From 447b0e733d6a08dda3547e8a6a9f682a2c4fbdfe Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Sun, 6 Dec 2020 13:55:18 +0100 Subject: [PATCH] [RFR] OpenUpgrade framework patches --- .pre-commit-config.yaml | 5 - openupgrade_framework/__init__.py | 1 - openupgrade_framework/__manifest__.py | 2 +- openupgrade_framework/odoo_patch/__init__.py | 1 - .../odoo_patch/addons/__init__.py | 0 .../odoo_patch/odoo/__init__.py | 6 +- .../odoo_patch/odoo/addons/__init__.py | 1 + .../odoo_patch/odoo/addons/base/__init__.py | 1 + .../odoo/addons/base/models/__init__.py | 2 + .../odoo/addons/base/models/ir_model.py | 55 ++ .../odoo/addons/base/models/ir_ui_view.py | 41 ++ openupgrade_framework/odoo_patch/odoo/http.py | 32 -- .../odoo_patch/odoo/models.py | 33 ++ .../odoo_patch/odoo/modules/__init__.py | 13 +- .../odoo_patch/odoo/modules/graph.py | 115 +--- .../odoo_patch/odoo/modules/loading.py | 527 ------------------ .../odoo_patch/odoo/modules/migration.py | 141 +---- .../odoo_patch/odoo/modules/registry.py | 58 -- .../odoo_patch/odoo/service/__init__.py | 4 - .../odoo_patch/odoo/service/server.py | 71 --- .../odoo_patch/odoo/tools/view_validation.py | 29 - openupgrade_framework/openupgrade/__init__.py | 0 .../openupgrade/openupgrade_loading.py | 318 ----------- .../odoo/addons/openupgrade_records | 1 - setup/openupgrade_records/setup.py | 6 - .../odoo/addons/openupgrade_scripts | 1 - setup/openupgrade_scripts/setup.py | 6 - 27 files changed, 173 insertions(+), 1297 deletions(-) delete mode 100644 openupgrade_framework/odoo_patch/addons/__init__.py create mode 100644 openupgrade_framework/odoo_patch/odoo/addons/__init__.py create mode 100644 openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py create mode 100644 openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py create mode 100644 openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py create mode 100644 openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py delete mode 100644 openupgrade_framework/odoo_patch/odoo/http.py create mode 100644 openupgrade_framework/odoo_patch/odoo/models.py delete mode 100644 openupgrade_framework/odoo_patch/odoo/modules/loading.py delete mode 100644 openupgrade_framework/odoo_patch/odoo/modules/registry.py delete mode 100644 openupgrade_framework/odoo_patch/odoo/service/__init__.py delete mode 100644 openupgrade_framework/odoo_patch/odoo/service/server.py delete mode 100644 openupgrade_framework/odoo_patch/odoo/tools/view_validation.py delete mode 100644 openupgrade_framework/openupgrade/__init__.py delete mode 100644 openupgrade_framework/openupgrade/openupgrade_loading.py delete mode 120000 setup/openupgrade_records/odoo/addons/openupgrade_records delete mode 100644 setup/openupgrade_records/setup.py delete mode 120000 setup/openupgrade_scripts/odoo/addons/openupgrade_scripts delete mode 100644 setup/openupgrade_scripts/setup.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f9b8bab..6dce3dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,6 @@ repos: rev: 20.8b1 hooks: - id: black - exclude: ^openupgrade_framework/odoo_patch/ - repo: https://github.com/prettier/pre-commit rev: v2.1.2 hooks: @@ -53,7 +52,6 @@ repos: - "@prettier/plugin-xml@0.12.0" args: - --plugin=@prettier/plugin-xml - exclude: ^openupgrade_framework/odoo_patch/ - repo: https://github.com/pre-commit/mirrors-eslint rev: v7.8.1 hooks: @@ -88,7 +86,6 @@ repos: rev: v2.7.2 hooks: - id: pyupgrade - exclude: ^openupgrade_framework/odoo_patch/ - repo: https://github.com/PyCQA/isort rev: 5.5.1 hooks: @@ -96,7 +93,6 @@ repos: name: isort except __init__.py args: - --settings=. - exclude: /__init__\.py$|^openupgrade_framework/odoo_patch/ - repo: https://github.com/acsone/setuptools-odoo rev: 2.6.0 hooks: @@ -106,7 +102,6 @@ repos: hooks: - id: flake8 name: flake8 except __init__.py - exclude: /__init__\.py$|^openupgrade_framework/odoo_patch/ additional_dependencies: ["flake8-bugbear==20.1.4"] - id: flake8 name: flake8 only __init__.py diff --git a/openupgrade_framework/__init__.py b/openupgrade_framework/__init__.py index 94ba6ff..4f09e91 100644 --- a/openupgrade_framework/__init__.py +++ b/openupgrade_framework/__init__.py @@ -1,2 +1 @@ from . import odoo_patch -from . import openupgrade diff --git a/openupgrade_framework/__manifest__.py b/openupgrade_framework/__manifest__.py index d63bd49..c4323fd 100644 --- a/openupgrade_framework/__manifest__.py +++ b/openupgrade_framework/__manifest__.py @@ -1,5 +1,5 @@ +# Copyright Odoo Community Association (OCA) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - { "name": "Openupgrade Framework", "summary": """Module to integrate in the server_wide_modules diff --git a/openupgrade_framework/odoo_patch/__init__.py b/openupgrade_framework/odoo_patch/__init__.py index 56a70db..3c691cd 100644 --- a/openupgrade_framework/odoo_patch/__init__.py +++ b/openupgrade_framework/odoo_patch/__init__.py @@ -1,2 +1 @@ from . import odoo -from . import addons diff --git a/openupgrade_framework/odoo_patch/addons/__init__.py b/openupgrade_framework/odoo_patch/addons/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openupgrade_framework/odoo_patch/odoo/__init__.py b/openupgrade_framework/odoo_patch/odoo/__init__.py index f13459f..c969456 100644 --- a/openupgrade_framework/odoo_patch/odoo/__init__.py +++ b/openupgrade_framework/odoo_patch/odoo/__init__.py @@ -1,5 +1 @@ -from . import modules -from . import service - -# Nothing todo the function, the function check_security didn't changed -from . import http +from . import addons, models, modules diff --git a/openupgrade_framework/odoo_patch/odoo/addons/__init__.py b/openupgrade_framework/odoo_patch/odoo/addons/__init__.py new file mode 100644 index 0000000..0e44449 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/__init__.py @@ -0,0 +1 @@ +from . import base diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py b/openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py new file mode 100644 index 0000000..9368777 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py @@ -0,0 +1,2 @@ +from . import ir_model +from . import ir_ui_view diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py new file mode 100644 index 0000000..a3f2d48 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py @@ -0,0 +1,55 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openupgradelib import openupgrade +from odoo import api, models +from odoo.addons.base.models.ir_model import IrModel, IrModelData, IrModelRelation +from odoo.tools import mute_logger + + +def _drop_table(self): + """ Never drop tables """ + for model in self: + if self.env.get(model.model) is not None: + openupgrade.message( + self.env.cr, "Unknown", False, False, + "Not dropping the table or view of model %s", model.model) + + +def _drop_column(self): + """ Never drop columns """ + for field in self: + if field.name in models.MAGIC_COLUMNS: + continue + openupgrade.message( + self.env.cr, "Unknown", False, False, + "Not dropping the column of field %s of model %s", field.name, + field.model, + ) + continue + + +IrModel._drop_column = _drop_column +IrModel._drop_table = _drop_table + + +@api.model +def _process_end(self, modules): + """ Don't warn about upgrade conventions from Odoo + ('fields should be explicitely removed by an upgrade script') + """ + with mute_logger('odoo.addons.base.models.ir_model'): + return IrModelData._process_end._original_method(self, modules) + + +_process_end._original_method = IrModelData._process_end +IrModelData._process_end = _process_end + + +def _module_data_uninstall(self): + """ Don't delete many2many relation tables. Only unlink the + ir.model.relation record itself. + """ + self.unlink() + + +IrModelRelation._module_data_uninstall = _module_data_uninstall diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py new file mode 100644 index 0000000..adfa557 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py @@ -0,0 +1,41 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging +from odoo import api +from odoo.tools import mute_logger +from odoo.addons.base.models.ir_ui_view import View + +_logger = logging.getLogger(__name__) + + +@api.constrains('arch_db') +def _check_xml(self): + """ Mute warnings about views which are common during migration """ + with mute_logger('odoo.addons.base.models.ir_ui_view'): + return View._check_xml._original_method(self) + + +def handle_view_error(self, message, *args, raise_exception=True, from_exception=None, from_traceback=None): + """ Don't raise or log exceptions in view validation unless explicitely requested """ + raise_exception = self.env.context.get('raise_view_error') + to_mute = 'odoo.addons.base.models.ir_ui_view' if raise_exception else 'not_muted' + with mute_logger(to_mute): + try: + return View.handle_view_error._original_method( + self, message, *args, + raise_exception=False, from_exception=from_exception, + from_traceback=from_traceback) + except ValueError: + _logger.warn( + "Can't render custom view %s for model %s. " + "Assuming you are migrating between major versions of " + "Odoo, this view is now set to inactive. Please " + "review the view contents manually after the migration.", + self.xml_id, self.model) + self.write({'active': False}) + + +_check_xml._original_method = View._check_xml +View._check_xml = _check_xml +handle_view_error._original_method = View.handle_view_error +View.handle_view_error = handle_view_error diff --git a/openupgrade_framework/odoo_patch/odoo/http.py b/openupgrade_framework/odoo_patch/odoo/http.py deleted file mode 100644 index e11c558..0000000 --- a/openupgrade_framework/odoo_patch/odoo/http.py +++ /dev/null @@ -1,32 +0,0 @@ -# flake8: noqa -# pylint: skip-file - -import odoo -from odoo.service import security -from odoo.http import SessionExpiredException, request, OpenERPSession - -if True: - def _check_security(self): - """ - Check the current authentication parameters to know if those are still - valid. This method should be called at each request. If the - authentication fails, a :exc:`SessionExpiredException` is raised. - """ - if not self.db or not self.uid: - raise SessionExpiredException("Session expired") - # We create our own environment instead of the request's one. - # to avoid creating it without the uid since request.uid isn't set yet - env = odoo.api.Environment(request.cr, self.uid, self.context) - # here we check if the session is still valid - if not security.check_session(self, env): - # - # When asking openupgrade_records to generate records - # over jsonrpc, a query on res_users in the call above locks this - # table for the sql operations that are triggered by the - # reinstallation of the base module - env.cr.rollback() - # - raise SessionExpiredException("Session expired") - - -OpenERPSession.check_security = _check_security diff --git a/openupgrade_framework/odoo_patch/odoo/models.py b/openupgrade_framework/odoo_patch/odoo/models.py new file mode 100644 index 0000000..bcb4597 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/models.py @@ -0,0 +1,33 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging +from uuid import uuid4 +from odoo.models import BaseModel +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + +_logger = logging.getLogger(__name__) + + +def unlink(self): + """ Don't break on unlink of obsolete records + when called from ir.model::_process_end() + + This only adapts the base unlink method. If overrides of this method + on individual models give problems, add patches for those as well. + """ + if not self.env.context.get(MODULE_UNINSTALL_FLAG): + return BaseModel.unlink._original_method(self) + savepoint = str(uuid4) + try: + self.env.cr.execute('SAVEPOINT "%s"' % savepoint) + return BaseModel.unlink._original_method(self) + except Exception as e: + self.env.cr.execute('ROLLBACK TO SAVEPOINT "%s"' % savepoint) + _logger.warning( + "Could not delete obsolete record with ids %s of model %s: %s", + self.ids, self._name, e) + return False + + +unlink._original_method = BaseModel.unlink +BaseModel.unlink = unlink diff --git a/openupgrade_framework/odoo_patch/odoo/modules/__init__.py b/openupgrade_framework/odoo_patch/odoo/modules/__init__.py index 90de5b4..614d705 100644 --- a/openupgrade_framework/odoo_patch/odoo/modules/__init__.py +++ b/openupgrade_framework/odoo_patch/odoo/modules/__init__.py @@ -1,12 +1 @@ -# Minor changes. (call to safe_eval changed) -# otherwise : adapted to V14 -from . import graph - -# A lot of changes in the core functions. -from . import loading - -# Adapted to V14 -from . import migration - -# Adapted to V14 -from . import registry +from . import graph, migration diff --git a/openupgrade_framework/odoo_patch/odoo/modules/graph.py b/openupgrade_framework/odoo_patch/odoo/modules/graph.py index b0bedef..5e454e5 100644 --- a/openupgrade_framework/odoo_patch/odoo/modules/graph.py +++ b/openupgrade_framework/odoo_patch/odoo/modules/graph.py @@ -1,108 +1,21 @@ -# flake8: noqa -# pylint: skip-file - -import logging +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import odoo -import odoo.tools as tools -from odoo.tools.safe_eval import safe_eval - from odoo.modules.graph import Graph -_logger = logging.getLogger(__name__) - - -if True: - - def _update_from_db(self, cr): - if not len(self): - return - # update the graph with values from the database (if exist) - ## First, we set the default values for each package in graph - additional_data = {key: {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None} for key in self.keys()} - ## Then we get the values from the database - cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version' - ' FROM ir_module_module' - ' WHERE name IN %s',(tuple(additional_data),) - ) - - ## and we update the default values with values from the database - additional_data.update((x['name'], x) for x in cr.dictfetchall()) - - # - # Prevent reloading of demo data from the new version on major upgrade - if ('base' in self and additional_data['base']['dbdemo'] and - additional_data['base']['installed_version'] < - odoo.release.major_version): - cr.execute("UPDATE ir_module_module SET demo = false") - for data in additional_data.values(): - data['dbdemo'] = False - # +def update_from_db(self, cr): + """ Prevent reloading of demo data from the new version on major upgrade """ + Graph.update_from_db._original_method(self, cr) + if ( + "base" in self + and self["base"].dbdemo + and self["base"].installed_version < odoo.release.major_version + ): + cr.execute("UPDATE ir_module_module SET demo = false") for package in self.values(): - for k, v in additional_data[package.name].items(): - setattr(package, k, v) - - - def _add_modules(self, cr, module_list, force=None): - if force is None: - force = [] - packages = [] - len_graph = len(self) - - # - # force additional dependencies for the upgrade process if given - # in config file - forced_deps = tools.config.get_misc('openupgrade', 'force_deps', '{}') - forced_deps = tools.config.get_misc('openupgrade', - 'force_deps_' + odoo.release.version, - forced_deps) - forced_deps = safe_eval(forced_deps) - # - - for module in module_list: - # This will raise an exception if no/unreadable descriptor file. - # NOTE The call to load_information_from_description_file is already - # done by db.initialize, so it is possible to not do it again here. - info = odoo.modules.module.load_information_from_description_file(module) - if info and info['installable']: - # - info['depends'].extend(forced_deps.get(module, [])) - # - packages.append((module, info)) # TODO directly a dict, like in get_modules_with_version - elif module != 'studio_customization': - _logger.warning('module %s: not installable, skipped', module) - - dependencies = dict([(p, info['depends']) for p, info in packages]) - current, later = set([p for p, info in packages]), set() - - while packages and current > later: - package, info = packages[0] - deps = info['depends'] - - # if all dependencies of 'package' are already in the graph, add 'package' in the graph - if all(dep in self for dep in deps): - if not package in current: - packages.pop(0) - continue - later.clear() - current.remove(package) - node = self.add_node(package, info) - for kind in ('init', 'demo', 'update'): - if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force: - setattr(node, kind, True) - else: - later.add(package) - packages.append((package, info)) - packages.pop(0) - - self.update_from_db(cr) - - for package in later: - unmet_deps = [p for p in dependencies[package] if p not in self] - _logger.info('module %s: Unmet dependencies: %s', package, ', '.join(unmet_deps)) - - return len(self) - len_graph + package.dbdemo = False -Graph.update_from_db = _update_from_db -Graph.add_modules = _add_modules +update_from_db._original_method = Graph.update_from_db +Graph.update_from_db = update_from_db diff --git a/openupgrade_framework/odoo_patch/odoo/modules/loading.py b/openupgrade_framework/odoo_patch/odoo/modules/loading.py deleted file mode 100644 index 6386567..0000000 --- a/openupgrade_framework/odoo_patch/odoo/modules/loading.py +++ /dev/null @@ -1,527 +0,0 @@ -# flake8: noqa -# pylint: skip-file - -import itertools -import logging -import sys -import time - -import odoo -import odoo.tools as tools -from odoo import api, SUPERUSER_ID -from odoo.modules import loading -from odoo.modules.module import adapt_version, load_openerp_module, initialize_sys_path - -from odoo.modules.loading import load_data, load_demo, _check_module_names -from odoo.addons.openupgrade_framework.openupgrade import openupgrade_loading - -import os - -_logger = logging.getLogger(__name__) -_test_logger = logging.getLogger('odoo.tests') - - -def _load_module_graph(cr, graph, status=None, perform_checks=True, - skip_modules=None, report=None, models_to_check=None): - """Migrates+Updates or Installs all module nodes from ``graph`` - :param graph: graph of module nodes to load - :param status: deprecated parameter, unused, left to avoid changing signature in 8.0 - :param perform_checks: whether module descriptors should be checked for validity (prints warnings - for same cases) - :param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped - :return: list of modules that were installed or updated - """ - if skip_modules is None: - skip_modules = [] - - if models_to_check is None: - models_to_check = set() - - processed_modules = [] - loaded_modules = [] - registry = odoo.registry(cr.dbname) - migrations = odoo.modules.migration.MigrationManager(cr, graph) - module_count = len(graph) - _logger.info('loading %d modules...', module_count) - - # - # suppress commits to have the upgrade of one module in just one transaction - cr.commit_org = cr.commit - cr.commit = lambda *args: None - cr.rollback_org = cr.rollback - cr.rollback = lambda *args: None - # - - # 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 - - models_updated = set() - - for index, package in enumerate(graph, 1): - module_name = package.name - module_id = package.id - - # - if module_name in skip_modules or module_name in loaded_modules: - # - continue - - module_t0 = time.time() - module_cursor_query_count = cr.sql_log_count - module_extra_query_count = odoo.sql_db.sql_counter - - needs_update = ( - hasattr(package, "init") - or hasattr(package, "update") - or package.state in ("to install", "to upgrade") - ) - module_log_level = logging.DEBUG - if needs_update: - module_log_level = logging.INFO - _logger.log(module_log_level, 'Loading module %s (%d/%d)', module_name, index, module_count) - - if needs_update: - if package.name != 'base': - registry.setup_models(cr) - migrations.migrate_module(package, 'pre') - if package.name != 'base': - env = api.Environment(cr, SUPERUSER_ID, {}) - env['base'].flush() - - load_openerp_module(package.name) - - new_install = package.state == 'to install' - if new_install: - 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) - - model_names = registry.load(cr, package) - - mode = 'update' - if hasattr(package, 'init') or package.state == 'to install': - mode = 'init' - - loaded_modules.append(package.name) - 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) - 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. - # This is because the extension may have changed the model, - # e.g. adding required=True to an existing field, but the schema has not been - # updated by this module because it's not marked as 'to upgrade/to install'. - models_to_check |= set(model_names) & models_updated - - 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) - - if perform_checks: - module._check() - - 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)) - module.invalidate_cache(['demo']) - - # - # add 'try' block for logging exceptions - # as errors in post scripts seem to be dropped - try: - migrations.migrate_module(package, 'post') - except Exception as exc: - _logger.error('Error executing post migration script for module %s: %s', - package, exc) - raise - # - - # Update translations for all installed languages - overwrite = odoo.tools.config["overwrite_existing_translations"] - module.with_context(overwrite=overwrite)._update_translations() - - if package.name is not None: - registry._init_modules.add(package.name) - - if needs_update: - if new_install: - post_init = package.info.get('post_init_hook') - if post_init: - getattr(py_module, post_init)(cr, registry) - - if mode == 'update': - # validate the views that have not been checked yet - env['ir.ui.view']._validate_module_views(module_name) - - # 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) - # - # commit after processing every module as well, for - # easier debugging and continuing an interrupted migration - cr.commit_org() - # - # run tests - if os.environ.get('OPENUPGRADE_TESTS') and package.name is not None: - prefix = '.migrations' - registry.openupgrade_test_prefixes[package.name] = prefix - report.record_result(odoo.modules.module.run_unit_tests(module_name, openupgrade_prefix=prefix)) - # - # commit module_n state and version immediatly - # to avoid invalid database state if module_n+1 raises an - # exception - cr.commit_org() - # - - package.load_state = package.state - package.load_version = package.installed_version - package.state = 'installed' - for kind in ('init', 'demo', 'update'): - if hasattr(package, kind): - delattr(package, kind) - module.flush() - - extra_queries = odoo.sql_db.sql_counter - module_extra_query_count - test_queries - extras = [] - if test_queries: - extras.append(f'+{test_queries} test') - if extra_queries: - extras.append(f'+{extra_queries} other') - _logger.log( - 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, - f' ({", ".join(extras)})' if extras else '' - ) - if test_results and not test_results.wasSuccessful(): - _logger.error( - "Module %s: %d failures, %d errors of %d tests", - module_name, len(test_results.failures), len(test_results.errors), - test_results.testsRun - ) - - _logger.runbot("%s modules loaded in %.2fs, %s queries (+%s extra)", - len(graph), - time.time() - t0, - 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 - - # - # restore commit method - cr.commit = cr.commit_org - cr.commit() - # - - return loaded_modules, processed_modules - - -def _load_marked_modules(cr, 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.""" - - if models_to_check is None: - models_to_check = set() - - 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] - # - module_list = openupgrade_loading.add_module_dependencies(cr, module_list) - # - if not module_list: - break - graph.add_modules(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, - perform_checks=perform_checks, models_to_check=models_to_check, - ) - processed_modules.extend(processed) - loaded_modules.extend(loaded) - if not processed: - break - return processed_modules - - -def _load_modules(db, force_demo=False, status=None, update_module=False): - initialize_sys_path() - - force = [] - if force_demo: - force.append('demo') - - models_to_check = set() - - with db.cursor() as cr: - if not odoo.modules.db.is_initialized(cr): - if not update_module: - _logger.error("Database %s not initialized, you can force it with `-i base`", cr.dbname) - return - _logger.info("init db") - odoo.modules.db.initialize(cr) - update_module = True # process auto-installed modules - tools.config["init"]["all"] = 1 - if not tools.config['without_demo']: - tools.config["demo"]['all'] = 1 - - # This is a brand new registry, just created in - # odoo.modules.registry.Registry.new(). - registry = odoo.registry(cr.dbname) - - if 'base' in tools.config['update'] or 'all' in tools.config['update']: - cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed')) - - # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) - graph = odoo.modules.graph.Graph() - graph.add_module(cr, 'base', force) - if not graph: - _logger.critical('module base cannot be loaded! (hint: verify addons-path)') - raise ImportError('Module `base` cannot be loaded! (hint: verify addons-path)') - - # processed_modules: for cleanup step after install - # loaded_modules: to avoid double loading - report = registry._assertion_report - loaded_modules, processed_modules = _load_module_graph( - cr, graph, status, perform_checks=update_module, - report=report, models_to_check=models_to_check) - - # - load_lang = tools.config.pop('load_language') - if load_lang or update_module: - # some base models are used below, so make sure they are set up - registry.setup_models(cr) - - if load_lang: - for lang in load_lang.split(','): - tools.load_language(cr, lang) - - # 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() - - _check_module_names(cr, itertools.chain(tools.config['init'], tools.config['update'])) - - module_names = [k for k, v in tools.config['init'].items() if v] - if module_names: - modules = Module.search([('state', '=', 'uninstalled'), ('name', 'in', module_names)]) - if modules: - modules.button_install() - - module_names = [k for k, v in tools.config['update'].items() if v] - if module_names: - # - # in standard Odoo, '--update all' just means: - # '--update base + upward (installed) dependencies. This breaks - # the chain when new glue modules are encountered. - # E.g. purchase in 8.0 depends on stock_account and report, - # both of which are new. They may be installed, but purchase as - # an upward dependency is not selected for upgrade. - # Therefore, explicitely select all installed modules for - # upgrading in OpenUpgrade in that case. - domain = [('state', '=', 'installed')] - if 'all' not in module_names: - domain.append(('name', 'in', module_names)) - modules = Module.search(domain) - # - if modules: - modules.button_upgrade() - - cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) - Module.invalidate_cache(['state']) - Module.flush() - - # STEP 3: Load marked modules (skipping base which was done in STEP 1) - # IMPORTANT: this is done in two parts, first loading all installed or - # partially installed modules (i.e. installed/to upgrade), to - # offer a consistent system to the second part: installing - # newly selected modules. - # We include the modules 'to remove' in the first step, because - # they are part of the "currently installed" modules. They will - # be dropped in STEP 6 later, before restarting the loading - # process. - # IMPORTANT 2: We have to loop here until all relevant modules have been - # processed, because in some rare cases the dependencies have - # changed, and modules that depend on an uninstalled module - # will not be processed on the first pass. - # It's especially useful for migrations. - previously_processed = -1 - while previously_processed < len(processed_modules): - previously_processed = len(processed_modules) - processed_modules += _load_marked_modules(cr, 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, - ['to install'], force, status, report, - loaded_modules, update_module, models_to_check) - # check that new module dependencies have been properly installed after a migration/upgrade - cr.execute("SELECT name from ir_module_module WHERE state IN ('to install', 'to upgrade')") - module_list = [name for (name,) in cr.fetchall()] - if module_list: - _logger.error("Some modules have inconsistent states, some dependencies may be missing: %s", sorted(module_list)) - - # check that all installed modules have been loaded by the registry after a migration/upgrade - cr.execute("SELECT name from ir_module_module WHERE state = 'installed' and name != 'studio_customization'") - module_list = [name for (name,) in cr.fetchall() if name not in graph] - if module_list: - _logger.error("Some modules are not loaded, some dependencies or manifest may be missing: %s", sorted(module_list)) - - registry.loaded = True - registry.setup_models(cr) - - # STEP 3.5: execute migration end-scripts - migrations = odoo.modules.migration.MigrationManager(cr, graph) - for package in graph: - migrations.migrate_module(package, 'end') - - # STEP 3.6: apply remaining constraints in case of an upgrade - registry.finalize_constraints() - - # STEP 4: Finish and cleanup installations - if processed_modules: - env = api.Environment(cr, SUPERUSER_ID, {}) - cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""") - for (model, name) in cr.fetchall(): - if model in registry and not registry[model]._abstract: - _logger.warning('The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,base.group_user,1,0,0,0', - model, model.replace('.', '_'), model.replace('.', '_'), model.replace('.', '_')) - - cr.execute("SELECT model from ir_model") - for (model,) in cr.fetchall(): - if model in registry: - env[model]._check_removed_columns(log=True) - elif _logger.isEnabledFor(logging.INFO): # more an info that a warning... - _logger.runbot("Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model) - - # Cleanup orphan records - env['ir.model.data']._process_end(processed_modules) - env['base'].flush() - - for kind in ('init', 'demo', 'update'): - tools.config[kind] = {} - - # STEP 5: Uninstall modules to remove - if update_module: - # Remove records referenced from ir_model_data for modules to be - # removed (and removed the references from ir_model_data). - 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') - if uninstall_hook: - py_module = sys.modules['odoo.addons.%s' % (pkg.name,)] - getattr(py_module, uninstall_hook)(cr, registry) - - Module = env['ir.module.module'] - Module.browse(modules_to_remove.values()).module_uninstall() - # Recursive reload, should only happen once, because there should be no - # modules to remove next time - cr.commit() - _logger.info('Reloading registry once more after uninstalling modules') - api.Environment.reset() - registry = odoo.modules.registry.Registry.new( - cr.dbname, force_demo, status, update_module - ) - registry.check_tables_exist(cr) - cr.commit() - return registry - - # STEP 5.5: Verify extended fields on every model - # This will fix the schema of all models in a situation such as: - # - module A is loaded and defines model M; - # - module B is installed/upgraded and extends model M; - # - module C is loaded and extends model M; - # - module B and C depend on A but not on each other; - # The changes introduced by module C are not taken into account by the upgrade of B. - if models_to_check: - registry.init_models(cr, list(models_to_check), {'models_to_check': True}) - - # 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: - try: - View._validate_custom_views(model) - except Exception as e: - _logger.warning('invalid custom view(s) for model %s: %s', model, tools.ustr(e)) - - if report.wasSuccessful(): - _logger.info('Modules loaded.') - else: - _logger.error('At least one test failed when loading the modules.') - - # STEP 8: call _register_hook on every model - # This is done *exactly once* when the registry is being loaded. See the - # 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['base'].flush() - - # STEP 9: save installed/updated modules for post-install tests - registry.updated_modules += processed_modules - -loading.load_module_graph = _load_module_graph -loading.load_marked_modules = _load_marked_modules -loading.load_modules = _load_modules -odoo.modules.load_modules = _load_modules diff --git a/openupgrade_framework/odoo_patch/odoo/modules/migration.py b/openupgrade_framework/odoo_patch/odoo/modules/migration.py index 0346c2b..65b4953 100644 --- a/openupgrade_framework/odoo_patch/odoo/modules/migration.py +++ b/openupgrade_framework/odoo_patch/odoo/modules/migration.py @@ -1,118 +1,23 @@ -# flake8: noqa -# pylint: skip-file - -import logging -import os -from os.path import join as opj -import odoo.release as release -from odoo.tools.parse_version import parse_version - -import odoo -from odoo.modules.migration import load_script -from odoo.modules import migration - -_logger = logging.getLogger(__name__) - - -if True: - def _migrate_module(self, pkg, stage): - assert stage in ('pre', 'post', 'end') - stageformat = { - 'pre': '[>%s]', - 'post': '[%s>]', - 'end': '[$%s]', - } - state = pkg.state if stage in ('pre', 'post') else getattr(pkg, 'load_state', None) - - # - # In openupgrade, also run migration scripts upon installation. - # We want to always pass in pre and post migration files and use a new - # argument in the migrate decorator (explained in the docstring) - # to decide if we want to do something if a new module is installed - # during the migration. - if not (hasattr(pkg, 'update') or state in ('to upgrade', 'to install')): - # - return - - def convert_version(version): - if version.count('.') >= 2: - return version # the version number already containt the server version - return "%s.%s" % (release.major_version, version) - - def _get_migration_versions(pkg, stage): - versions = sorted({ - ver - for lv in self.migrations[pkg.name].values() - for ver, lf in lv.items() - if lf - }, key=lambda k: parse_version(convert_version(k))) - if "0.0.0" in versions: - # reorder versions - versions.remove("0.0.0") - if stage == "pre": - versions.insert(0, "0.0.0") - else: - versions.append("0.0.0") - return versions - - def _get_migration_files(pkg, version, stage): - """ return a list of migration script files - """ - m = self.migrations[pkg.name] - lst = [] - - mapping = { - 'module': opj(pkg.name, 'migrations'), - 'module_upgrades': opj(pkg.name, 'upgrades'), - } - - for path in odoo.upgrade.__path__: - if os.path.exists(opj(path, pkg.name)): - mapping['upgrade'] = opj(path, pkg.name) - break - - for x in mapping: - if version in m.get(x): - for f in m[x][version]: - if not f.startswith(stage + '-'): - continue - lst.append(opj(mapping[x], version, f)) - lst.sort() - return lst - - installed_version = getattr(pkg, 'load_version', pkg.installed_version) or '' - parsed_installed_version = parse_version(installed_version) - current_version = parse_version(convert_version(pkg.data['version'])) - - versions = _get_migration_versions(pkg, stage) - - for version in versions: - if ((version == "0.0.0" and parsed_installed_version < current_version) - or parsed_installed_version < parse_version(convert_version(version)) <= current_version): - - strfmt = {'addon': pkg.name, - 'stage': stage, - 'version': stageformat[stage] % version, - } - - for pyfile in _get_migration_files(pkg, version, stage): - name, ext = os.path.splitext(os.path.basename(pyfile)) - if ext.lower() != '.py': - continue - mod = None - try: - mod = load_script(pyfile, name) - _logger.info('module %(addon)s: Running migration %(version)s %(name)s' % dict(strfmt, name=mod.__name__)) - migrate = mod.migrate - except ImportError: - _logger.exception('module %(addon)s: Unable to load %(stage)s-migration file %(file)s' % dict(strfmt, file=pyfile)) - raise - except AttributeError: - _logger.error('module %(addon)s: Each %(stage)s-migration file must have a "migrate(cr, installed_version)" function' % strfmt) - else: - migrate(self.cr, installed_version) - finally: - if mod: - del mod - -migration.migrate_module = _migrate_module +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.modules.migration import MigrationManager + + +def migrate_module(self, pkg, stage): + """In openupgrade, also run migration scripts upon installation. + We want to always pass in pre and post migration files and use a new + argument in the migrate decorator (explained in the docstring) + to decide if we want to do something if a new module is installed + during the migration. + We trick Odoo into running the scripts by setting the update attribute if necessary. + """ + has_update = hasattr(pkg, "update") + if not has_update: + pkg.update = True + MigrationManager.migrate_module._original_method(self, pkg, stage) + if not has_update: + delattr(pkg, "update") + + +migrate_module._original_method = MigrationManager.migrate_module +MigrationManager.migrate_module = migrate_module diff --git a/openupgrade_framework/odoo_patch/odoo/modules/registry.py b/openupgrade_framework/odoo_patch/odoo/modules/registry.py deleted file mode 100644 index 4c5f50d..0000000 --- a/openupgrade_framework/odoo_patch/odoo/modules/registry.py +++ /dev/null @@ -1,58 +0,0 @@ -# flake8: noqa -# pylint: skip-file - -from collections import deque -from contextlib import closing -import odoo -from odoo.tools.lru import LRU - -from odoo.modules import registry - - -if True: - - def _init(self, db_name): - self.models = {} # model name/model instance mapping - self._sql_constraints = set() - self._init = True - self._assertion_report = odoo.tests.runner.OdooTestResult() - self._fields_by_model = None - self._ordinary_tables = None - self._constraint_queue = deque() - self.__cache = LRU(8192) - - # modules fully loaded (maintained during init phase by `loading` module) - self._init_modules = set() - self.updated_modules = [] # installed/updated modules - # - self.openupgrade_test_prefixes = {} - # - self.loaded_xmlids = set() - - self.db_name = db_name - self._db = odoo.sql_db.db_connect(db_name) - - # cursor for test mode; None means "normal" mode - self.test_cr = None - self.test_lock = None - - # Indicates that the registry is - self.loaded = False # whether all modules are loaded - self.ready = False # whether everything is set up - - # Inter-process signaling: - # The `base_registry_signaling` sequence indicates the whole registry - # must be reloaded. - # The `base_cache_signaling sequence` indicates all caches must be - # invalidated (i.e. cleared). - self.registry_sequence = None - self.cache_sequence = None - - # Flags indicating invalidation of the registry or the cache. - self.registry_invalidated = False - self.cache_invalidated = False - - with closing(self.cursor()) as cr: - self.has_unaccent = odoo.modules.db.has_unaccent(cr) - -registry.init = _init diff --git a/openupgrade_framework/odoo_patch/odoo/service/__init__.py b/openupgrade_framework/odoo_patch/odoo/service/__init__.py deleted file mode 100644 index a96314d..0000000 --- a/openupgrade_framework/odoo_patch/odoo/service/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Import disabled, because the function run_unit_tests() -# disappeared in V14. -# TODO: OpenUpgrade Core maintainers : FIXME. -# from . import server diff --git a/openupgrade_framework/odoo_patch/odoo/service/server.py b/openupgrade_framework/odoo_patch/odoo/service/server.py deleted file mode 100644 index a2a998e..0000000 --- a/openupgrade_framework/odoo_patch/odoo/service/server.py +++ /dev/null @@ -1,71 +0,0 @@ -# flake8: noqa -# pylint: skip-file - -import logging -import os -import time - -import odoo -from odoo.tools import config -from odoo.modules.registry import Registry - -from odoo.service import server -from odoo.service.server import load_test_file_py - -_logger = logging.getLogger(__name__) - - -def preload_registries(dbnames): - """ Preload a registries, possibly run a test file.""" - # TODO: move all config checks to args dont check tools.config here - dbnames = dbnames or [] - rc = 0 - for dbname in dbnames: - try: - update_module = config['init'] or config['update'] - registry = Registry.new(dbname, update_module=update_module) - - # run test_file if provided - if config['test_file']: - test_file = config['test_file'] - if not os.path.isfile(test_file): - _logger.warning('test file %s cannot be found', test_file) - elif not test_file.endswith('py'): - _logger.warning('test file %s is not a python file', test_file) - else: - _logger.info('loading test file %s', test_file) - with odoo.api.Environment.manage(): - load_test_file_py(registry, test_file) - - # run post-install tests - if config['test_enable']: - t0 = time.time() - t0_sql = odoo.sql_db.sql_counter - module_names = (registry.updated_modules if update_module else - sorted(registry._init_modules)) - _logger.info("Starting post tests") - tests_before = registry._assertion_report.testsRun - with odoo.api.Environment.manage(): - for module_name in module_names: - result = loader.run_suite(loader.make_suite(module_name, 'post_install'), module_name) - registry._assertion_report.update(result) - # - # run deferred unit tests - for module_name, prefix in registry.openupgrade_test_prefixes: - result = run_unit_tests(module_name, position='post_install', openupgrade_prefix=prefix) - registry._assertion_report.record_result(result) - # - _logger.info("%d post-tests in %.2fs, %s queries", - registry._assertion_report.testsRun - tests_before, - time.time() - t0, - odoo.sql_db.sql_counter - t0_sql) - - if not registry._assertion_report.wasSuccessful(): - rc += 1 - except Exception: - _logger.critical('Failed to initialize database `%s`.', dbname, exc_info=True) - return -1 - return rc - - -server.preload_registries = preload_registries diff --git a/openupgrade_framework/odoo_patch/odoo/tools/view_validation.py b/openupgrade_framework/odoo_patch/odoo/tools/view_validation.py deleted file mode 100644 index e6c8243..0000000 --- a/openupgrade_framework/odoo_patch/odoo/tools/view_validation.py +++ /dev/null @@ -1,29 +0,0 @@ -# flake8: noqa -# pylint: skip-file - -# from odoo.addons.openupgrade_framework.openupgrade import openupgrade_log - -from odoo.tools import view_validation -from odoo.tools.view_validation import _validators, _logger - - -def _valid_view(arch, **kwargs): - for pred in _validators[arch.tag]: - # - # Do not raise blocking error, because it's normal to - # have inconsistent views in an openupgrade process - check = pred(arch, **kwargs) or 'Warning' - # - if not check: - _logger.error("Invalid XML: %s", pred.__doc__) - return False - if check == "Warning": - # - # Don't show this warning as useless and too much verbose - # _logger.warning("Invalid XML: %s", pred.__doc__) - # - return "Warning" - return True - - -view_validation.valid_view = _valid_view diff --git a/openupgrade_framework/openupgrade/__init__.py b/openupgrade_framework/openupgrade/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openupgrade_framework/openupgrade/openupgrade_loading.py b/openupgrade_framework/openupgrade/openupgrade_loading.py deleted file mode 100644 index ca3e1d4..0000000 --- a/openupgrade_framework/openupgrade/openupgrade_loading.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright 2011-2015 Therp BV -# Copyright 2016-2019 Opener B.V. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -# flake8: noqa: C901 - -import logging - -from openupgradelib.openupgrade_tools import table_exists - -from odoo import release -from odoo.modules.module import get_module_path -from odoo.tools.safe_eval import safe_eval -from odoo.tools.config import config - -# A collection of functions used in -# odoo/modules/loading.py - -logger = logging.getLogger("OpenUpgrade") - - -def add_module_dependencies(cr, module_list): - """ - Select (new) dependencies from the modules in the list - so that we can inject them into the graph at upgrade - time. Used in the modified OpenUpgrade Server, - not to be called from migration scripts - - Also take the OpenUpgrade configuration directives 'forced_deps' - and 'autoinstall' into account. From any additional modules - that these directives can add, the dependencies are added as - well (but these directives are not checked for the occurrence - of any of the dependencies). - """ - if not module_list: - return module_list - - modules_in = list(module_list) - forced_deps = safe_eval( - config.get_misc( - "openupgrade", - "forced_deps_" + release.version, - config.get_misc("openupgrade", "forced_deps", "{}"), - ) - ) - - autoinstall = safe_eval( - config.get_misc( - "openupgrade", - "autoinstall_" + release.version, - config.get_misc("openupgrade", "autoinstall", "{}"), - ) - ) - - for module in list(module_list): - module_list += forced_deps.get(module, []) - module_list += autoinstall.get(module, []) - - module_list = list(set(module_list)) - - dependencies = module_list - while dependencies: - cr.execute( - """ - SELECT DISTINCT dep.name - FROM - ir_module_module, - ir_module_module_dependency dep - WHERE - module_id = ir_module_module.id - AND ir_module_module.name in %s - AND dep.name not in %s - """, - ( - tuple(dependencies), - tuple(module_list), - ), - ) - - dependencies = [x[0] for x in cr.fetchall()] - module_list += dependencies - - # Select auto_install modules of which all dependencies - # are fulfilled based on the modules we know are to be - # installed - cr.execute( - """ - SELECT name from ir_module_module WHERE state IN %s - """, - (("installed", "to install", "to upgrade"),), - ) - modules = list(set(module_list + [row[0] for row in cr.fetchall()])) - cr.execute( - """ - SELECT name from ir_module_module m - WHERE auto_install IS TRUE - AND state = 'uninstalled' - AND NOT EXISTS( - SELECT id FROM ir_module_module_dependency d - WHERE d.module_id = m.id - AND name NOT IN %s) - """, - (tuple(modules),), - ) - auto_modules = [row[0] for row in cr.fetchall() if get_module_path(row[0])] - if auto_modules: - logger.info("Selecting autoinstallable modules %s", ",".join(auto_modules)) - module_list += auto_modules - - # Set proper state for new dependencies so that any init scripts are run - cr.execute( - """ - UPDATE ir_module_module SET state = 'to install' - WHERE name IN %s AND name NOT IN %s AND state = 'uninstalled' - """, - (tuple(module_list), tuple(modules_in)), - ) - return module_list - - -def log_model(model, local_registry): - """ - OpenUpgrade: Store the characteristics of the BaseModel and its fields - in the local registry, so that we can compare changes with the - main registry - """ - - if not model._name: - return - - typemap = {"monetary": "float"} - - # Deferred import to prevent import loop - from odoo import models - - # persistent models only - if isinstance(model, models.TransientModel): - return - - def isfunction(model, k): - if ( - model._fields[k].compute - and not model._fields[k].related - and not model._fields[k].company_dependent - ): - return "function" - return "" - - def isproperty(model, k): - if model._fields[k].company_dependent: - return "property" - return "" - - def isrelated(model, k): - if model._fields[k].related: - return "related" - return "" - - def _get_relation(v): - if v.type in ("many2many", "many2one", "one2many"): - return v.comodel_name - elif v.type == "many2one_reference": - return v.model_field - else: - return "" - - model_registry = local_registry.setdefault(model._name, {}) - if model._inherits: - model_registry["_inherits"] = {"_inherits": str(model._inherits)} - for k, v in model._fields.items(): - properties = { - "type": typemap.get(v.type, v.type), - "isfunction": isfunction(model, k), - "isproperty": isproperty(model, k), - "isrelated": isrelated(model, k), - "relation": _get_relation(v), - "table": v.relation if v.type == "many2many" else "", - "required": v.required and "required" or "", - "stored": v.store and "stored" or "", - "selection_keys": "", - "req_default": "", - "hasdefault": model._fields[k].default and "hasdefault" or "", - "inherits": "", - } - if v.type == "selection": - if isinstance(v.selection, (tuple, list)): - properties["selection_keys"] = str(sorted([x[0] for x in v.selection])) - else: - properties["selection_keys"] = "function" - elif v.type == "binary": - properties["attachment"] = str(getattr(v, "attachment", False)) - default = model._fields[k].default - if v.required and default: - if ( - callable(default) - or isinstance(default, str) - and getattr(model._fields[k], default, False) - and callable(getattr(model._fields[k], default)) - ): - # todo: in OpenERP 5 (and in 6 as well), - # literals are wrapped in a lambda function - properties["req_default"] = "function" - else: - properties["req_default"] = str(default) - for key, value in properties.items(): - if value: - model_registry.setdefault(k, {})[key] = value - - -def get_record_id(cr, module, model, field, mode): - """ - OpenUpgrade: get or create the id from the record table matching - the key parameter values - """ - cr.execute( - "SELECT id FROM openupgrade_record " - "WHERE module = %s AND model = %s AND " - "field = %s AND mode = %s AND type = %s", - (module, model, field, mode, "field"), - ) - record = cr.fetchone() - if record: - return record[0] - cr.execute( - "INSERT INTO openupgrade_record " - "(module, model, field, mode, type) " - "VALUES (%s, %s, %s, %s, %s)", - (module, model, field, mode, "field"), - ) - cr.execute( - "SELECT id FROM openupgrade_record " - "WHERE module = %s AND model = %s AND " - "field = %s AND mode = %s AND type = %s", - (module, model, field, mode, "field"), - ) - return cr.fetchone()[0] - - -def compare_registries(cr, module, registry, local_registry): - """ - OpenUpgrade: Compare the local registry with the global registry, - log any differences and merge the local registry with - the global one. - """ - if not table_exists(cr, "openupgrade_record"): - return - for model, flds in local_registry.items(): - registry.setdefault(model, {}) - for field, attributes in flds.items(): - old_field = registry[model].setdefault(field, {}) - mode = old_field and "modify" or "create" - record_id = False - for key, value in attributes.items(): - if key not in old_field or old_field[key] != value: - if not record_id: - record_id = get_record_id(cr, module, model, field, mode) - cr.execute( - "SELECT id FROM openupgrade_attribute " - "WHERE name = %s AND value = %s AND " - "record_id = %s", - (key, value, record_id), - ) - if not cr.fetchone(): - cr.execute( - "INSERT INTO openupgrade_attribute " - "(name, value, record_id) VALUES (%s, %s, %s)", - (key, value, record_id), - ) - old_field[key] = value - - -def update_field_xmlid(model, field): - """OpenUpgrade edit start: In rare cases, an old module defined a field - on a model that is not defined in another module earlier in the - chain of inheritance. Then we need to assign the ir.model.fields' - xmlid to this other module, otherwise the column would be dropped - when uninstalling the first module. - An example is res.partner#display_name defined in 7.0 by - account_report_company, but now the field belongs to the base - module - Given that we arrive here in order of inheritance, we simply check - if the field's xmlid belongs to a module already loaded, and if not, - update the record with the correct module name.""" - model.env.cr.execute( - "SELECT f.*, d.module, d.id as xmlid_id, d.name as xmlid " - "FROM ir_model_fields f LEFT JOIN ir_model_data d " - "ON f.id=d.res_id and d.model='ir.model.fields' WHERE f.model=%s", - (model._name,), - ) - for rec in model.env.cr.dictfetchall(): - if ( - "module" in model.env.context - and rec["module"] - and rec["name"] in model._fields.keys() - and rec["module"] != model.env.context["module"] - and rec["module"] not in model.env.registry._init_modules - ): - logging.getLogger(__name__).info( - "Moving XMLID for ir.model.fields record of %s#%s " "from %s to %s", - model._name, - rec["name"], - rec["module"], - model.env.context["module"], - ) - model.env.cr.execute( - "SELECT id FROM ir_model_data WHERE module=%(module)s " - "AND name=%(xmlid)s", - dict(rec, module=model.env.context["module"]), - ) - if model.env.cr.fetchone(): - logging.getLogger(__name__).info( - "Aborting, an XMLID for this module already exists." - ) - continue - model.env.cr.execute( - "UPDATE ir_model_data SET module=%(module)s " "WHERE id=%(xmlid_id)s", - dict(rec, module=model.env.context["module"]), - ) diff --git a/setup/openupgrade_records/odoo/addons/openupgrade_records b/setup/openupgrade_records/odoo/addons/openupgrade_records deleted file mode 120000 index e4e2ad8..0000000 --- a/setup/openupgrade_records/odoo/addons/openupgrade_records +++ /dev/null @@ -1 +0,0 @@ -../../../../openupgrade_records \ No newline at end of file diff --git a/setup/openupgrade_records/setup.py b/setup/openupgrade_records/setup.py deleted file mode 100644 index 28c57bb..0000000 --- a/setup/openupgrade_records/setup.py +++ /dev/null @@ -1,6 +0,0 @@ -import setuptools - -setuptools.setup( - setup_requires=['setuptools-odoo'], - odoo_addon=True, -) diff --git a/setup/openupgrade_scripts/odoo/addons/openupgrade_scripts b/setup/openupgrade_scripts/odoo/addons/openupgrade_scripts deleted file mode 120000 index 9215019..0000000 --- a/setup/openupgrade_scripts/odoo/addons/openupgrade_scripts +++ /dev/null @@ -1 +0,0 @@ -../../../../openupgrade_scripts \ No newline at end of file diff --git a/setup/openupgrade_scripts/setup.py b/setup/openupgrade_scripts/setup.py deleted file mode 100644 index 28c57bb..0000000 --- a/setup/openupgrade_scripts/setup.py +++ /dev/null @@ -1,6 +0,0 @@ -import setuptools - -setuptools.setup( - setup_requires=['setuptools-odoo'], - odoo_addon=True, -)