8000 [IMP] core: support reinit modules · odoo/odoo@5c6ac5d · GitHub
[go: up one dir, main page]

Skip to content

Commit 5c6ac5d

Browse files
committed
[IMP] core: support reinit modules
Before #189000, -i an already installed module will reinitialize the module 1. reinstall the module without ``pre/post_init_hook`` 2. reinitialize some(but not all) of its direct or indirect dependent installed modules #189000 removed the behavior, because it is not correct. As a result, -i does nothing to an installed module. Although the behavior was not correct and had some side effects, it was useful for developers or a quick database fix. This commit re-supports the feature and defines the ```reinit`` as a special upgrade 1. doesn't execute ``migration scripts`` 2. load data in ``'init'`` mode
1 parent dfab9f5 commit 5c6ac5d

File tree

6 files changed

+63
-29
lines changed

6 files changed

+63
-29
lines changed

odoo/addons/base/tests/config/cli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
--init stock,hr
22
--update account,website
3+
--reinit account
34
--with-demo
45
--import-partial /tmp/import-partial
56
--pidfile /tmp/pidfile

odoo/addons/base/tests/test_configmanager.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def test_01_default_config(self):
7676
'save': False,
7777
'init': {},
7878
'update': {},
79+
'reinit': [],
7980
'with_demo': False,
8081
'import_partial': '',
8182
'pidfile': '',
@@ -193,6 +194,7 @@ def test_02_config_file(self):
193194
'save': False,
194195
'init': {}, # blacklist for save, ignored from the config file
195196
'update': {}, # blacklist for save, ignored from the config file
197+
'reinit': [],
196198
'with_demo': True,
197199
'import_partial': '',
198200
'pidfile': '/tmp/pidfile',
@@ -369,6 +371,7 @@ def test_04_odoo16_config_file(self):
369371
'translate_modules': ['all'],
370372
'unaccent': False,
371373
'update': {},
374+
'reinit': [],
372375
'upgrade_path': [],
373376
'pre_upgrade_scripts': [],
374377
'with_demo': True,
@@ -471,6 +474,7 @@ def test_06_cli(self):
471474
'save': False,
472475
'init': {'hr': True, 'stock': True},
473476
'update': {'account': True, 'website': True},
477+
'reinit': ['account'],
474478
'with_demo': True,
475479
'import_partial': '/tmp/import-partial',
476480
'pidfile': '/tmp/pidfile',
@@ -598,6 +602,7 @@ def test_07_environ(self):
598602
'save': False,
599603
'init': {},
600604
'update': {},
605+
'reinit': [],
601606
'with_demo': True,
602607
'import_partial': '',
603608
'pidfile': '/tmp/pidfile',

odoo/modules/loading.py

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,19 @@ def load_module_graph(
167167
module_cursor_query_count = env.cr.sql_log_count
168168
module_extra_query_count = odoo.sql_db.sql_counter
169169

170-
needs_update = update_module and package.state in ("to install", "to upgrade")
170+
update_operation = (
171+
'install' if package.state == 'to install' else
172+
'upgrade' if package.state == 'to upgrade' else
173+
'reinit' if module_name in registry._reinit_modules else
174+
None
175+
) if update_module else None
171176
module_log_level = logging.DEBUG
172-
if needs_update:
177+
if update_operation:
173178
module_log_level = logging.INFO
174179
_logger.log(module_log_level, 'Loading module %s (%d/%d)', module_name, index, module_count)
175180

176-
new_install = package.state == 'to install'
177-
if needs_update:
178-
if not new_install or package.name in registry._force_upgrade_scripts:
181+
if update_operation:
182+
if update_operation == 'upgrade' or module_name in registry._force_upgrade_scripts:
179183
if package.name != 'base':
180184
registry._setup_models__(env.cr)
181185
migrations.migrate_module(package, 'pre')
@@ -184,7 +188,7 @@ def load_module_graph(
184188

185189
load_openerp_module(package.name)
186190

187-
if new_install:
191+
if update_operation == 'install':
188192
py_module = sys.modules['odoo.addons.%s' % (module_name,)]
189193
pre_init = package.manifest.get('pre_init_hook')
190194
if pre_init:
@@ -193,12 +197,12 @@ def load_module_graph(
193197

194198
model_names = registry.load(package)
195199

196-
if needs_update:
200+
if update_operation:
197201
model_names = registry.descendants(model_names, '_inherit', '_inherits')
198202
models_updated |= set(model_names)
199203
models_to_check -= set(model_names)
200204
registry._setup_models__(env.cr)
201-
registry.init_models(env.cr, model_names, {'module': package.name}, new_install)
205+
registry.init_models(env.cr, model_names, {'module': package.name}, update_operation == 'install')
202206
elif package.state != 'to remove':
203207
# The current module has simply been loaded. The models extended by this module
204208
# and for which we updated the schema, must have their schema checked again.
@@ -208,24 +212,25 @@ def load_module_graph(
208212
model_names = registry.descendants(model_names, '_inherit', '_inherits')
209213
models_to_check |= set(model_names) & models_updated
210214

211-
if needs_update:
215+
if update_operation:
212216
# Can't put this line out of the loop: ir.module.module will be
213217
# registered by init_models() above.
214218
module = env['ir.module.module'].browse(module_id)
215219
module._check()
216220

217221
idref: dict = {}
218222

219-
if new_install: # 'to install'
223+
if update_operation == 'install':
220224
load_data(env, idref, 'init', kind='data', package=package)
221225
if install_demo and package.demo_installable:
222226
package.demo = load_demo(env, package, idref, 'init')
223-
else: # 'to upgrade'
227+
else: # 'upgrade' or 'reinit'
224228
# upgrading the module information
225229
module.write(module.get_values_from_terp(package.manifest))
226-
load_data(env, idref, 'update', kind='data', package=package)
230+
mode = 'update' if update_operation == 'upgrade' else 'init'
231+
load_data(env, idref, mode, kind='data', package=package)
227232
if package.demo:
228-
package.demo = load_demo(env, package, idref, 'update')
233+
package.demo = load_demo(env, package, idref, mode)
229234
env.cr.execute('UPDATE ir_module_module SET demo = %s WHERE id = %s', (package.demo, module_id))
230235
module.invalidate_model(['demo'])
231236

@@ -238,12 +243,12 @@ def load_module_graph(
238243
if package.name is not None:
239244
registry._init_modules.add(package.name)
240245

241-
if needs_update:
242-
if new_install:
246+
if update_operation:
247+
if update_operation == 'install':
243248
post_init = package.manifest.get('post_init_hook')
244249
if post_init:
245250
getattr(py_module, post_init)(env)
246-
else: # 'to upgrade'
251+
elif update_operation == 'upgrade':
247252
# validate the views that have not been checked yet
248253
env['ir.ui.view']._validate_module_views(module_name)
249254

@@ -278,12 +283,12 @@ def load_module_graph(
278283
test_queries = 0
279284
test_results = None
280285

281-
update_from_config = tools.config['update'] or tools.config['init']
282-
if tools.config['test_enable'] and (needs_update or not update_from_config):
286+
update_from_config = tools.config['update'] or tools.config['init'] or tools.config['reinit']
287+
if tools.config['test_enable'] and (update_operation or not update_from_config):
283288
from odoo.tests import loader # noqa: PLC0415
284289
suite = loader.make_suite([module_name], 'at_install')
285290
if suite.countTestCases():
286-
if not needs_update:
291+
if not update_operation:
287292
registry._setup_models__(env.cr)
288293
registry.check_null_constraints(env.cr)
289294
# Python tests
@@ -345,6 +350,7 @@ def load_modules(
345350
update_module: bool = False,
346351
upgrade_modules: Collection[str] = (),
347352
install_modules: Collection[str] = (),
353+
reinit_modules: Collection[str] = (),
348354
new_db_demo: bool = False,
349355
) -> None:
350356
""" Load the modules for a registry object that has just been created. This
@@ -354,6 +360,7 @@ def load_modules(
354360
:param update_module: Whether to update (install, upgrade, or uninstall) modules. Defaults to ``False``
355361
:param upgrade_modules: A collection of module names to upgrade.
356362
:param install_modules: A collection of module names to install.
363+
:param reinit_modules: A collection of module names to reinitialize.
357364
:param new_db_demo: Whether to install demo data for new database. Defaults to ``False``
358365
"""
359366
initialize_sys_path()
@@ -372,6 +379,8 @@ def load_modules(
372379
return
373380
_logger.info("init db")
374381
modules_db.initialize(cr)
382+
elif 'base' in reinit_modules:
383+
registry._reinit_modules.add('base')
375384

376385
if 'base' in upgrade_modules:
377386
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
@@ -429,6 +438,11 @@ def load_modules(
429438
if modules:
430439
modules.button_upgrade()
431440

441+
if reinit_modules:
442+
modules = Module.search([('state', 'in', ('installed', 'to upgrade')), ('name', 'in', tuple(reinit_modules))])
443+
reinit_modules = modules.downstream_dependencies(exclude_states=('uninstalled', 'uninstallable', 'to remove', 'to install')) + modules
444+
registry._reinit_modules.update(m for m in reinit_modules.mapped('name') if m not in graph._imported_modules)
445+
432446
env.flush_all()
433447
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
434448
Module.invalidate_model(['state'])

odoo/orm/registry.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,30 @@ def new(
128128
update_module: bool = False,
129129
install_modules: Collection[str] = (),
130130
upgrade_modules: Collection[str] = (),
131+
reinit_modules: Collection[str] = (),
131132
new_db_demo: bool | None = None,
132133
) -> Registry:
133134
"""Create and return a new registry for the given database name.
134135
135136
:param db_name: The name of the database to associate with the Registry instance.
137+
:param update_module: If ``True``, update modules while loading the registry. Defaults to ``False``.
138+
:param install_modules: Names of modules to install.
136139
137-
:param update_module: If True, update modules while loading the registry. Defaults to ``False``.
140+
* If a specified module is **not installed**, it and all of its direct and indirect
141+
dependencies will be installed.
138142
139-
:param install_modules: Names of modules to install. Their direct or indirect dependency
140-
modules will also be installed. Defaults to an empty tuple.
143+
Defaults to an empty tuple.
141144
142145
:param upgrade_modules: Names of modules to upgrade. Their direct or indirect dependent
143-
modules will also be upgraded. Defaults to an empty tuple.
146+
modules will also be upgraded. Defaults to an empty tuple.
147+
:param reinit_modules: Names of modules to reinitialize.
148+
149+
* If a specified module is **already installed**, it and all of its installed direct and
150+
indirect dependents will be re-initialized. Re-initialization means the module will be
151+
upgraded without running upgrade scripts, but with data loaded in ``'init'`` mode.
144152
145153
:param new_db_demo: Whether to install demo data for the new database. If set to ``None``, the value will be
146-
determined by the ``config['with_demo']``. Defaults to ``None``
154+
determined by the ``config['with_demo']``. Defaults to ``None``
147155
"""
148156
t0 = time.time()
149157
registry: Registry = object.__new__(cls)
@@ -165,9 +173,10 @@ def new(
165173
new_db_demo = config['with_demo']
166174
load_modules(
167175
registry,
168-
update_module=update_module or bool(upgrade_modules or install_modules),
176+
update_module=update_module or bool(upgrade_modules or install_modules or reinit_modules),
169177
upgrade_modules=upgrade_modules,
170178
install_modules=install_modules,
179+
reinit_modules=reinit_modules,
171180
new_db_demo=new_db_demo,
172181
)
173182
except Exception:
@@ -178,6 +187,8 @@ def new(
178187
del cls.registries[db_name] # pylint: disable=unsupported-delete-operation
179188
raise
180189

190+
del registry._reinit_modules
191+
181192
# load_modules() above can replace the registry by calling
182193
# indirectly new() again (when modules have to be uninstalled).
183194
# Yeah, crazy.
@@ -209,10 +220,11 @@ def init(self, db_name: str) -> None:
209220
self.__caches: dict[str, LRU] = {cache_name: LRU(cache_size) for cache_name, cache_size in _REGISTRY_CACHES.items()}
210221

211222
# update context during loading modules
212-
self._force_upgrade_scripts = set() # force the execution of the upgrade script for these modules
223+
self._force_upgrade_scripts: set[str] = set() # force the execution of the upgrade script for these modules
224+
self._reinit_modules: set[str] = set() # modules to reinitialize
213225

214226
# modules fully loaded (maintained during init phase by `loading` module)
215-
self._init_modules: set[str] = set()
227+
self._init_modules: set[str] = set() # modules have been initialized
216228
self.updated_modules: list[str] = [] # installed/updated modules
217229
self.loaded_xmlids: set[str] = set()
218230

odoo/service/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,8 +1388,8 @@ def preload_registries(dbnames):
13881388
for dbname in dbnames:
13891389
try:
13901390
threading.current_thread().dbname = dbname
1391-
update_module = config['init'] or config['update']
1392-
registry = Registry.new(dbname, update_module=update_module, install_modules=config['init'], upgrade_modules=config['update'])
1391+
update_module = config['init'] or config['update'] or config['reinit']
1392+
registry = Registry.new(dbname, update_module=update_module, install_modules=config['init'], upgrade_modules=config['update'], reinit_modules=config['reinit'])
13931393

13941394
# run post-install tests
13951395
if config['test_enable']:

odoo/tools/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ def _build_cli(self):
214214
help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
215215
group.add_option("-u", "--update", dest="update", type='comma', metavar="MODULE,...", my_default=[], file_loadable=False,
216216
help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
217+
group.add_option("--reinit", dest="reinit", type='comma', metavar="MODULE,...", my_default=[], file_loadable=False,
218+
help="reinitialize one or more modules (comma-separated list), requires -d")
217219
group.add_option("--with-demo", dest="with_demo", action='store_true', my_default=False,
218220
help="install demo data in new databases")
219221
group.add_option("--without-demo", dest="with_demo", type='without_demo', metavar='BOOL', nargs='?', const=True,

0 commit comments

Comments
 (0)
0