From 48b51e67c93d16c6030edaaaf36d4fbcafc4b3b4 Mon Sep 17 00:00:00 2001 From: fw-bot Date: Fri, 24 Jan 2020 17:29:50 +0000 Subject: [PATCH] [FIX] module.py: --upgrades-paths dynamic hooks Migration have long been only accessible thanks to a symlink from `odoo.base.maintenance` to our private migration repository. Thank to the change of bbb1a8f it is now possible to give a load the migrations scripts from a path given in options. The `initialize_sys_path` function has been updated to hooks the new paths or the legacy symlink and to provide aliases to the previous import logic to ensure backward compatibility. `odoo.upgrades` (`community/odoo/upgrades`) is a new namespace that hook all `--upgrades-paths` directories or the `community/odoo/base/maintenance/migrations` symlink if none is previded. `odoo.addons.base.maintenance.migrations` has been made an alias to `odoo.upgrades`. The `odoo.upgrades` is the desired method for accessing migrations scripts and should be used by all new scripts. closes odoo/odoo#44117 Task: 2178274 X-original-commit: d963cc05acd882729c4eb5ab940dae2a2197e55a Signed-off-by: Raphael Collet (rco) --- odoo/__init__.py | 1 + odoo/modules/migration.py | 10 +++------- odoo/modules/module.py | 23 ++++++++++++++++++++++- odoo/tools/config.py | 23 ++++++++++++++++------- odoo/upgrades.py | 24 ------------------------ odoo/upgrades/__init__.py | 6 ++++++ 6 files changed, 48 insertions(+), 39 deletions(-) delete mode 100644 odoo/upgrades.py create mode 100644 odoo/upgrades/__init__.py diff --git a/odoo/__init__.py b/odoo/__init__.py index 8eb764a712d..804db5d937c 100644 --- a/odoo/__init__.py +++ b/odoo/__init__.py @@ -106,6 +106,7 @@ def registry(database_name=None): #---------------------------------------------------------- # Imports #---------------------------------------------------------- +from . import upgrades # this namespace must be imported first from . import addons from . import conf from . import loglevels diff --git a/odoo/modules/migration.py b/odoo/modules/migration.py index 8faf8735a8d..f1b00a6def7 100644 --- a/odoo/modules/migration.py +++ b/odoo/modules/migration.py @@ -12,7 +12,7 @@ from os.path import join as opj from odoo.modules.module import get_resource_path import odoo.release as release -import odoo.tools as tools +import odoo.upgrades from odoo.tools.parse_version import parse_version _logger = logging.getLogger(__name__) @@ -63,7 +63,7 @@ class MigrationManager(object): def _get_files(self): def _get_upgrades_paths(pkg): - for path in tools.config['upgrades_paths'].split(','): + for path in odoo.upgrades.__path__: upgrade_path = opj(path, pkg) if os.path.exists(upgrade_path): return upgrade_path @@ -86,8 +86,6 @@ class MigrationManager(object): self.migrations[pkg.name] = { 'module': get_scripts(get_resource_path(pkg.name, 'migrations')), 'module_upgrades': get_scripts(get_resource_path(pkg.name, 'upgrades')), - 'maintenance': get_scripts(get_resource_path('base', 'maintenance', 'migrations', pkg.name)), - 'maintenance_upgrades': get_scripts(get_resource_path('base', 'maintenance', 'upgrades', pkg.name)), 'upgrades': get_scripts(_get_upgrades_paths(pkg.name)), } @@ -133,11 +131,9 @@ class MigrationManager(object): mapping = { 'module': opj(pkg.name, 'migrations'), 'module_upgrades': opj(pkg.name, 'upgrades'), - 'maintenance': opj('base', 'maintenance', 'migrations', pkg.name), - 'maintenance_upgrades': opj('base', 'maintenance', 'upgrades', pkg.name), } - for path in tools.config['upgrades_paths'].split(','): + for path in odoo.upgrades.__path__: if os.path.exists(opj(path, pkg.name)): mapping['upgrades'] = opj(path, pkg.name) break diff --git a/odoo/modules/module.py b/odoo/modules/module.py index 851395ba280..dd8fde100f2 100644 --- a/odoo/modules/module.py +++ b/odoo/modules/module.py @@ -39,20 +39,41 @@ def initialize_sys_path(): and explicit directories. """ + # hook odoo.addons on data dir dd = os.path.normcase(tools.config.addons_data_dir) if os.access(dd, os.R_OK) and dd not in odoo.addons.__path__: odoo.addons.__path__.append(dd) + # hook odoo.addons on addons paths for ad in tools.config['addons_path'].split(','): ad = os.path.normcase(os.path.abspath(tools.ustr(ad.strip()))) if ad not in odoo.addons.__path__: odoo.addons.__path__.append(ad) - # add base module path + # hook odoo.addons on base module path base_path = os.path.normcase(os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons'))) if base_path not in odoo.addons.__path__ and os.path.isdir(base_path): odoo.addons.__path__.append(base_path) + # hook odoo.upgrades on upgrades paths + from odoo import upgrades + for up in tools.config['upgrades_paths'].split(','): + up = os.path.normcase(os.path.abspath(tools.ustr(up.strip()))) + if up not in upgrades.__path__: + upgrades.__path__.append(up) + + # hook odoo.upgrades on legacy odoo/addons/base/maintenance/migrations symlink + if not tools.config['upgrades_paths']: + upgrades.__path__.append(os.path.join( + base_path, 'base', 'maintenance', 'migrations')) + + # create decrecated module alias from odoo.addons.base.maintenance.migrations to odoo.upgrades + spec = importlib.machinery.ModuleSpec("odoo.addons.base.maintenance", None, is_package=True) + maintenance_pkg = importlib.util.module_from_spec(spec) + maintenance_pkg.migrations = upgrades + sys.modules["odoo.addons.base.maintenance"] = maintenance_pkg + sys.modules["odoo.addons.base.maintenance.migrations"] = upgrades + def get_module_path(module, downloaded=False, display_warning=True): """Return the path of the given module. diff --git a/odoo/tools/config.py b/odoo/tools/config.py index 194da756ef8..81d1f448f7e 100644 --- a/odoo/tools/config.py +++ b/odoo/tools/config.py @@ -9,10 +9,12 @@ except ImportError: import errno import logging import optparse +import glob import os import sys import tempfile import odoo +from os.path import expandvars, expanduser, abspath, realpath from .. import release, conf, loglevels from . import appdirs @@ -488,7 +490,7 @@ class configmanager(object): self._normalize(x) for x in self.options['addons_path'].split(',')) - self.options['upgrades_paths'] = ( + self.options["upgrades_paths"] = ( ",".join(self._normalize(x) for x in self.options['upgrades_paths'].split(',')) if self.options['upgrades_paths'] @@ -546,7 +548,7 @@ class configmanager(object): if not os.path.isdir(res): raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path)) if not self._is_addons_path(res): - raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path)) + raise optparse.OptionValueError("option %s: the path %r is not a valid addons directory" % (opt, path)) ad_paths.append(res) setattr(parser.values, option.dest, ",".join(ad_paths)) @@ -558,9 +560,18 @@ class configmanager(object): res = self._normalize(path) if not os.path.isdir(res): raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path)) - upgrades_paths.append(res) + if not self._is_upgrades_path(res): + raise optparse.OptionValueError("option %s: the path %r is not a valid upgrade directory" % (opt, path)) + if res not in upgrades_paths: + upgrades_paths.append(res) setattr(parser.values, option.dest, ",".join(upgrades_paths)) + def _is_upgrades_path(self, res): + return any( + glob.glob(os.path.join(res, f"*/*/{prefix}-*.py")) + for prefix in ["pre", "post", "end"] + ) + def _test_enable_callback(self, option, opt, value, parser): if not parser.values.test_tags: parser.values.test_tags = "+standard" @@ -700,9 +711,7 @@ class configmanager(object): def _normalize(self, path): if not path: return path - return os.path.abspath( - os.path.expanduser( - os.path.expandvars( - path.strip()))) + return realpath(abspath(expanduser(expandvars(path.strip())))) + config = configmanager() diff --git a/odoo/upgrades.py b/odoo/upgrades.py deleted file mode 100644 index c83dae7b2e5..00000000000 --- a/odoo/upgrades.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -import importlib.util -import os -import sys - -from odoo.tools import config -from odoo.modules.module import get_resource_path - -for path in config.get("upgrades_paths", "").split(","): - if os.path.exists(os.path.join(path, "__init__.py")): - break -else: - # failback to legacy "maintenance/migrations" package - path = get_resource_path("base", "maintenance", "migrations") - -if not path: - raise ImportError("No package found in `upgrades_paths`") - -spec = importlib.util.spec_from_file_location("odoo.upgrades", os.path.join(path, "__init__.py")) -module = importlib.util.module_from_spec(spec) -spec.loader.exec_module(module) - -# shadow module and register under legacy name -sys.modules["odoo.upgrades"] = sys.modules["odoo.addons.base.maintenance.migrations"] = module diff --git a/odoo/upgrades/__init__.py b/odoo/upgrades/__init__.py new file mode 100644 index 00000000000..5fb18447da2 --- /dev/null +++ b/odoo/upgrades/__init__.py @@ -0,0 +1,6 @@ +import pkgutil +import os.path +__path__ = [ + os.path.abspath(path) + for path in pkgutil.extend_path(__path__, __name__) +]