[IMP] base, *: re-use env during registry loading

The goal of this revision is to re-use the environment among the
different steps of the registry loading,
instead of creating a new environment for each step.

1. Simply To avoid to repeat the line
   `env = api.Environment(cr, SUPERUSER_ID, {})`
   multiple times in the code
2. This also allows to share the context among the different
   steps. This is not yet used in this revision, but it could
   be, for instance to avoid the current repetition to add the keys
   `install_module`, in `convert_csv_import` and `xml_import._tag_record`

Part-of: odoo/odoo#108254
This commit is contained in:
Denis Ledoux
2023-02-01 10:25:01 +01:00
parent 74b73cee85
commit 5bf1207c8c
11 changed files with 64 additions and 76 deletions
@@ -83,7 +83,7 @@ class IrModule(models.Model):
noupdate = True
pathname = opj(path, filename)
idref = {}
convert_file(self.env.cr, module, filename, idref, mode=mode, noupdate=noupdate, kind=kind, pathname=pathname)
convert_file(self.env, module, filename, idref, mode=mode, noupdate=noupdate, kind=kind, pathname=pathname)
path_static = opj(path, 'static')
IrAttachment = self.env['ir.attachment']
+1 -1
View File
@@ -99,7 +99,7 @@ class TemplateResetMixin(models.AbstractModel):
# We don't have a way to pass context while loading record from a file, so we use this hack
# to pass the context key that is needed to reset the fields not available in data file
rec.set('context', json.dumps({'reset_template': 'True'}))
obj = xml_import(template.env.cr, module, {}, mode='init', xml_filename=fullpath)
obj = xml_import(template.env, module, {}, mode='init', xml_filename=fullpath)
obj._tag_record(rec)
template._override_translation_term(module, [xml_id, external_id])
else:
+1 -1
View File
@@ -152,7 +152,7 @@ class TestMailTemplate(MailCommon):
class TestMailTemplateReset(MailCommon):
def _load(self, module, *args):
convert_file(self.cr, module='mail',
convert_file(self.env, module='mail',
filename=get_module_resource(module, *args),
idref={}, mode='init', noupdate=False, kind='test')
@@ -10,7 +10,7 @@ class PosSessionCheckProductWizard(models.TransientModel):
_description = 'Verify if there are any products for the PoS'
def load_demo_products(self):
convert.convert_file(self.env.cr, 'point_of_sale', 'data/point_of_sale_onboarding.xml', None, mode='init', kind='data')
convert.convert_file(self.env, 'point_of_sale', 'data/point_of_sale_onboarding.xml', None, mode='init', kind='data')
return self.open_ui()
def open_ui(self):
+1 -1
View File
@@ -114,7 +114,7 @@ class TestSmsTemplateAccessRights(TransactionCase):
class TestSMSTemplateReset(TransactionCase):
def _load(self, module, *args):
convert_file(self.cr, module='sms',
convert_file(self.env, module='sms',
filename=get_module_resource(module, *args),
idref={}, mode='init', noupdate=False, kind='test')
+1 -1
View File
@@ -12,7 +12,7 @@ from odoo.modules.module import get_module_resource
class TestQweb(TransactionCaseWithUserDemo):
def _load(self, module, *args):
tools.convert_file(
self.cr, 'test_website',
self.env, 'test_website',
get_module_resource(module, *args),
{}, 'init', False, 'test'
)
+1 -1
View File
@@ -13,7 +13,7 @@ class IrDemo(models.TransientModel):
@assert_log_admin_access
def install_demo(self):
force_demo(self.env.cr)
force_demo(self.env)
return {
'type': 'ir.actions.act_url',
'target': 'self',
@@ -77,7 +77,7 @@ class TestEvalXML(common.TransactionCase):
self.eval_xml(Field('test_nofile.txt', type='file'), obj)
def test_function(self):
obj = xml_import(self.cr, 'test_convert', None, 'init')
obj = xml_import(self.env, 'test_convert', None, 'init')
# pass args in eval
xml = E.function(
@@ -126,7 +126,7 @@ class TestEvalXML(common.TransactionCase):
self.assertEqual(kwargs, {})
def test_function_kwargs(self):
obj = xml_import(self.cr, 'test_convert', None, 'init')
obj = xml_import(self.env, 'test_convert', None, 'init')
# pass args and kwargs in child elements
xml = E.function(
@@ -175,7 +175,7 @@ class TestEvalXML(common.TransactionCase):
self.assertEqual(kwargs, {})
def test_function_function(self):
obj = xml_import(self.cr, 'test_convert', None, 'init')
obj = xml_import(self.env, 'test_convert', None, 'init')
xml = E.function(
E.function(model="test_convert.usered", name="search", eval="[[]]"),
@@ -210,7 +210,7 @@ class TestEvalXML(common.TransactionCase):
</field>
</record>
""".strip())
obj = xml_import(self.cr, 'test_convert', None, 'init')
obj = xml_import(self.env, 'test_convert', None, 'init')
obj._tag_record(xml)
# check that field 'usered_ids' is not passed
+1 -1
View File
@@ -21,7 +21,7 @@ function = E.function
class TestEnv(common.TransactionCase):
def setUp(self):
super().setUp()
self._importer = xml_import(self.env.cr, 'test_convert', None, 'init')
self._importer = xml_import(self.env, 'test_convert', None, 'init')
def importer(self, doc):
etree.RelaxNG(
+40 -51
View File
@@ -23,7 +23,7 @@ _logger = logging.getLogger(__name__)
_test_logger = logging.getLogger('odoo.tests')
def load_data(cr, idref, mode, kind, package):
def load_data(env, idref, mode, kind, package):
"""
kind: data, demo, test, init_xml, update_xml, demo_xml.
@@ -68,14 +68,14 @@ def load_data(cr, idref, mode, kind, package):
noupdate = False
if kind in ('demo', 'demo_xml') or (filename.endswith('.csv') and kind in ('init', 'init_xml')):
noupdate = True
tools.convert_file(cr, package.name, filename, idref, mode, noupdate, kind)
tools.convert_file(env, package.name, filename, idref, mode, noupdate, kind)
finally:
if kind in ('demo', 'test'):
threading.current_thread().testing = False
return bool(filename)
def load_demo(cr, package, idref, mode):
def load_demo(env, package, idref, mode):
"""
Loads demo data for the specified package.
"""
@@ -85,8 +85,8 @@ def load_demo(cr, package, idref, mode):
try:
if package.data.get('demo') or package.data.get('demo_xml'):
_logger.info("Module %s: loading demo", package.name)
with cr.savepoint(flush=False):
load_data(cr, idref, mode, kind='demo', package=package)
with env.cr.savepoint(flush=False):
load_data(env, idref, mode, kind='demo', package=package)
return True
except Exception as e:
# If we could not install demo data for this module
@@ -94,7 +94,6 @@ def load_demo(cr, package, idref, mode):
"Module %s demo data failed to install, installed without demo data",
package.name, exc_info=True)
env = api.Environment(cr, SUPERUSER_ID, {})
todo = env.ref('base.demo_failure_todo', raise_if_not_found=False)
Failure = env.get('ir.demo_failure')
if todo and Failure is not None:
@@ -103,27 +102,26 @@ def load_demo(cr, package, idref, mode):
return False
def force_demo(cr):
def force_demo(env):
"""
Forces the `demo` flag on all modules, and installs demo data for all installed modules.
"""
graph = odoo.modules.graph.Graph()
cr.execute('UPDATE ir_module_module SET demo=True')
cr.execute(
env.cr.execute('UPDATE ir_module_module SET demo=True')
env.cr.execute(
"SELECT name FROM ir_module_module WHERE state IN ('installed', 'to upgrade', 'to remove')"
)
module_list = [name for (name,) in cr.fetchall()]
graph.add_modules(cr, module_list, ['demo'])
module_list = [name for (name,) in env.cr.fetchall()]
graph.add_modules(env.cr, module_list, ['demo'])
for package in graph:
load_demo(cr, package, {}, 'init')
load_demo(env, package, {}, 'init')
env = api.Environment(cr, SUPERUSER_ID, {})
env['ir.module.module'].invalidate_model(['demo'])
env['res.groups']._update_user_groups_view()
def load_module_graph(cr, graph, status=None, perform_checks=True,
def load_module_graph(env, graph, status=None, perform_checks=True,
skip_modules=None, report=None, models_to_check=None):
"""Migrates+Updates or Installs all module nodes from ``graph``
@@ -142,15 +140,15 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
processed_modules = []
loaded_modules = []
registry = odoo.registry(cr.dbname)
migrations = odoo.modules.migration.MigrationManager(cr, graph)
registry = env.registry
migrations = odoo.modules.migration.MigrationManager(env.cr, graph)
module_count = len(graph)
_logger.info('loading %d modules...', module_count)
# register, instantiate and initialize models for each modules
t0 = time.time()
loading_extra_query_count = odoo.sql_db.sql_counter
loading_cursor_query_count = cr.sql_log_count
loading_cursor_query_count = env.cr.sql_log_count
models_updated = set()
@@ -162,7 +160,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
continue
module_t0 = time.time()
module_cursor_query_count = cr.sql_log_count
module_cursor_query_count = env.cr.sql_log_count
module_extra_query_count = odoo.sql_db.sql_counter
needs_update = (
@@ -179,10 +177,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
if needs_update:
if not new_install:
if package.name != 'base':
registry.setup_models(cr)
registry.setup_models(env.cr)
migrations.migrate_module(package, 'pre')
if package.name != 'base':
env = api.Environment(cr, SUPERUSER_ID, {})
env.flush_all()
load_openerp_module(package.name)
@@ -191,9 +188,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
py_module = sys.modules['odoo.addons.%s' % (module_name,)]
pre_init = package.info.get('pre_init_hook')
if pre_init:
getattr(py_module, pre_init)(cr)
getattr(py_module, pre_init)(env.cr)
model_names = registry.load(cr, package)
model_names = registry.load(env.cr, package)
mode = 'update'
if hasattr(package, 'init') or package.state == 'to install':
@@ -203,8 +200,8 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
if needs_update:
models_updated |= set(model_names)
models_to_check -= set(model_names)
registry.setup_models(cr)
registry.init_models(cr, model_names, {'module': package.name}, new_install)
registry.setup_models(env.cr)
registry.init_models(env.cr, model_names, {'module': package.name}, new_install)
elif package.state != 'to remove':
# The current module has simply been loaded. The models extended by this module
# and for which we updated the schema, must have their schema checked again.
@@ -216,7 +213,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
idref = {}
if needs_update:
env = api.Environment(cr, SUPERUSER_ID, {})
# Can't put this line out of the loop: ir.module.module will be
# registered by init_models() above.
module = env['ir.module.module'].browse(module_id)
@@ -227,9 +223,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
if package.state == 'to upgrade':
# upgrading the module information
module.write(module.get_values_from_terp(package.data))
load_data(cr, idref, mode, kind='data', package=package)
demo_loaded = package.dbdemo = load_demo(cr, package, idref, mode)
cr.execute('update ir_module_module set demo=%s where id=%s', (demo_loaded, module_id))
load_data(env, idref, mode, kind='data', package=package)
demo_loaded = package.dbdemo = load_demo(env, package, idref, mode)
env.cr.execute('update ir_module_module set demo=%s where id=%s', (demo_loaded, module_id))
module.invalidate_model(['demo'])
migrations.migrate_module(package, 'post')
@@ -245,7 +241,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
if new_install:
post_init = package.info.get('post_init_hook')
if post_init:
getattr(py_module, post_init)(cr, registry)
getattr(py_module, post_init)(env.cr, registry)
if mode == 'update':
# validate the views that have not been checked yet
@@ -254,14 +250,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
# need to commit any modification the module's installation or
# update made to the schema or data so the tests can run
# (separately in their own transaction)
cr.commit()
env.cr.commit()
concrete_models = [model for model in model_names if not registry[model]._abstract]
if concrete_models:
cr.execute("""
env.cr.execute("""
SELECT model FROM ir_model
WHERE id NOT IN (SELECT DISTINCT model_id FROM ir_model_access) AND model IN %s
""", [tuple(concrete_models)])
models = [model for [model] in cr.fetchall()]
models = [model for [model] in env.cr.fetchall()]
if models:
lines = [
f"The models {models} have no access rules in module {module_name}, consider adding some, like:",
@@ -276,12 +272,11 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
test_time = test_queries = 0
test_results = None
if tools.config.options['test_enable'] and (needs_update or not updating):
env = api.Environment(cr, SUPERUSER_ID, {})
loader = odoo.tests.loader
suite = loader.make_suite([module_name], 'at_install')
if suite.countTestCases():
if not needs_update:
registry.setup_models(cr)
registry.setup_models(env.cr)
# Python tests
env['ir.http']._clear_routing_map() # force routing map to be rebuilt
@@ -292,7 +287,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
test_queries = odoo.sql_db.sql_counter - tests_q0
# tests may have reset the environment
env = api.Environment(cr, SUPERUSER_ID, {})
module = env['ir.module.module'].browse(module_id)
if needs_update:
@@ -320,7 +314,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
module_log_level, "Module %s loaded in %.2fs%s, %s queries%s",
module_name, time.time() - module_t0,
f' (incl. {test_time:.2f}s test)' if test_time else '',
cr.sql_log_count - module_cursor_query_count,
env.cr.sql_log_count - module_cursor_query_count,
f' ({", ".join(extras)})' if extras else ''
)
if test_results and not test_results.wasSuccessful():
@@ -333,7 +327,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
_logger.runbot("%s modules loaded in %.2fs, %s queries (+%s extra)",
len(graph),
time.time() - t0,
cr.sql_log_count - loading_cursor_query_count,
env.cr.sql_log_count - loading_cursor_query_count,
odoo.sql_db.sql_counter - loading_extra_query_count) # extra queries: testes, notify, any other closed cursor
return loaded_modules, processed_modules
@@ -352,7 +346,7 @@ def _check_module_names(cr, module_names):
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
_logger.warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
def load_marked_modules(cr, graph, states, force, progressdict, report,
def load_marked_modules(env, graph, states, force, progressdict, report,
loaded_modules, perform_checks, models_to_check=None):
"""Loads modules marked with ``states``, adding them to ``graph`` and
``loaded_modules`` and returns a list of installed/upgraded modules."""
@@ -362,14 +356,14 @@ def load_marked_modules(cr, graph, states, force, progressdict, report,
processed_modules = []
while True:
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),))
module_list = [name for (name,) in cr.fetchall() if name not in graph]
env.cr.execute("SELECT name from ir_module_module WHERE state IN %s", (tuple(states),))
module_list = [name for (name,) in env.cr.fetchall() if name not in graph]
if not module_list:
break
graph.add_modules(cr, module_list, force)
graph.add_modules(env.cr, module_list, force)
_logger.debug('Updating graph with %d more modules', len(module_list))
loaded, processed = load_module_graph(
cr, graph, progressdict, report=report, skip_modules=loaded_modules,
env, graph, progressdict, report=report, skip_modules=loaded_modules,
perform_checks=perform_checks, models_to_check=models_to_check
)
processed_modules.extend(processed)
@@ -420,8 +414,9 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
# processed_modules: for cleanup step after install
# loaded_modules: to avoid double loading
report = registry._assertion_report
env = api.Environment(cr, SUPERUSER_ID, {})
loaded_modules, processed_modules = load_module_graph(
cr, graph, status, perform_checks=update_module,
env, graph, status, perform_checks=update_module,
report=report, models_to_check=models_to_check)
load_lang = tools.config.pop('load_language')
@@ -435,7 +430,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
# STEP 2: Mark other modules to be loaded/updated
if update_module:
env = api.Environment(cr, SUPERUSER_ID, {})
Module = env['ir.module.module']
_logger.info('updating modules list')
Module.update_list()
@@ -475,11 +469,11 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
previously_processed = -1
while previously_processed < len(processed_modules):
previously_processed = len(processed_modules)
processed_modules += load_marked_modules(cr, graph,
processed_modules += load_marked_modules(env, graph,
['installed', 'to upgrade', 'to remove'],
force, status, report, loaded_modules, update_module, models_to_check)
if update_module:
processed_modules += load_marked_modules(cr, graph,
processed_modules += load_marked_modules(env, graph,
['to install'], force, status, report,
loaded_modules, update_module, models_to_check)
@@ -487,7 +481,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
registry.setup_models(cr)
# check that all installed modules have been loaded by the registry
env = api.Environment(cr, SUPERUSER_ID, {})
Module = env['ir.module.module']
modules = Module.search(Module._get_modules_to_load_domain(), order='name')
missing = [name for name in modules.mapped('name') if name not in graph]
@@ -510,7 +503,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
# STEP 4: Finish and cleanup installations
if processed_modules:
env = api.Environment(cr, SUPERUSER_ID, {})
cr.execute("SELECT model from ir_model")
for (model,) in cr.fetchall():
@@ -533,7 +525,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
cr.execute("SELECT name, id FROM ir_module_module WHERE state=%s", ('to remove',))
modules_to_remove = dict(cr.fetchall())
if modules_to_remove:
env = api.Environment(cr, SUPERUSER_ID, {})
pkgs = reversed([p for p in graph if p.name in modules_to_remove])
for pkg in pkgs:
uninstall_hook = pkg.info.get('uninstall_hook')
@@ -567,7 +558,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
# STEP 6: verify custom views on every model
if update_module:
env = api.Environment(cr, SUPERUSER_ID, {})
env['res.groups']._update_user_groups_view()
View = env['ir.ui.view']
for model in registry:
@@ -590,7 +580,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
# management of those hooks in `Registry.setup_models`: all the calls to
# setup_models() done here do not mess up with hooks, as registry.ready
# is False.
env = api.Environment(cr, SUPERUSER_ID, {})
for model in env.values():
model._register_hook()
env.flush_all()
+12 -13
View File
@@ -577,10 +577,10 @@ form: module.record_id""" % (xml_id,)
def noupdate(self):
return self._noupdate[-1]
def __init__(self, cr, module, idref, mode, noupdate=False, xml_filename=None):
def __init__(self, env, module, idref, mode, noupdate=False, xml_filename=None):
self.mode = mode
self.module = module
self.envs = [odoo.api.Environment(cr, SUPERUSER_ID, {})]
self.envs = [env]
self.idref = {} if idref is None else idref
self._noupdate = [noupdate]
self.xml_filename = xml_filename
@@ -599,27 +599,27 @@ form: module.record_id""" % (xml_id,)
self._tag_root(de)
DATA_ROOTS = ['odoo', 'data', 'openerp']
def convert_file(cr, module, filename, idref, mode='update', noupdate=False, kind=None, pathname=None):
def convert_file(env, module, filename, idref, mode='update', noupdate=False, kind=None, pathname=None):
if pathname is None:
pathname = os.path.join(module, filename)
ext = os.path.splitext(filename)[1].lower()
with file_open(pathname, 'rb') as fp:
if ext == '.csv':
convert_csv_import(cr, module, pathname, fp.read(), idref, mode, noupdate)
convert_csv_import(env, module, pathname, fp.read(), idref, mode, noupdate)
elif ext == '.sql':
convert_sql_import(cr, fp)
convert_sql_import(env, fp)
elif ext == '.xml':
convert_xml_import(cr, module, fp, idref, mode, noupdate)
convert_xml_import(env, module, fp, idref, mode, noupdate)
elif ext == '.js':
pass # .js files are valid but ignored here.
else:
raise ValueError("Can't load unknown file type %s.", filename)
def convert_sql_import(cr, fp):
cr.execute(fp.read()) # pylint: disable=sql-injection
def convert_sql_import(env, fp):
env.cr.execute(fp.read()) # pylint: disable=sql-injection
def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
def convert_csv_import(env, module, fname, csvcontent, idref=None, mode='init',
noupdate=False):
'''Import csv file :
quote: "
@@ -647,14 +647,13 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
'install_filename': fname,
'noupdate': noupdate,
}
env = odoo.api.Environment(cr, SUPERUSER_ID, context)
result = env[model].load(fields, datas)
result = env[model].with_context(**context).load(fields, datas)
if any(msg['type'] == 'error' for msg in result['messages']):
# Report failed import and abort module install
warning_msg = "\n".join(msg['message'] for msg in result['messages'])
raise Exception(_('Module loading %s failed: file %s could not be processed:\n %s') % (module, fname, warning_msg))
def convert_xml_import(cr, module, xmlfile, idref=None, mode='init', noupdate=False, report=None):
def convert_xml_import(env, module, xmlfile, idref=None, mode='init', noupdate=False, report=None):
doc = etree.parse(xmlfile)
schema = os.path.join(config['root_path'], 'import_xml.rng')
relaxng = etree.RelaxNG(etree.parse(schema))
@@ -675,5 +674,5 @@ def convert_xml_import(cr, module, xmlfile, idref=None, mode='init', noupdate=Fa
xml_filename = xmlfile
else:
xml_filename = xmlfile.name
obj = xml_import(cr, module, idref, mode, noupdate=noupdate, xml_filename=xml_filename)
obj = xml_import(env, module, idref, mode, noupdate=noupdate, xml_filename=xml_filename)
obj.parse(doc.getroot())