Browse Source

Merge pull request #1198 from acsone/8.0-module_auto_update-sbi

[MIG][8.0] module_auto_update backport from 9
pull/1213/head
Pedro M. Baeza 7 years ago
committed by GitHub
parent
commit
11ca573e54
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 90
      module_auto_update/README.rst
  2. 6
      module_auto_update/__init__.py
  3. 24
      module_auto_update/__openerp__.py
  4. 45
      module_auto_update/addon_hash.py
  5. 20
      module_auto_update/hooks.py
  6. 59
      module_auto_update/i18n/ca.po
  7. 59
      module_auto_update/i18n/de.po
  8. 59
      module_auto_update/i18n/es.po
  9. 59
      module_auto_update/i18n/es_MX.po
  10. 62
      module_auto_update/i18n/fr.po
  11. 59
      module_auto_update/i18n/hr.po
  12. 59
      module_auto_update/i18n/it.po
  13. 59
      module_auto_update/i18n/nl_NL.po
  14. 59
      module_auto_update/i18n/pt_BR.po
  15. 60
      module_auto_update/i18n/sl.po
  16. 60
      module_auto_update/i18n/tr.po
  17. 5
      module_auto_update/models/__init__.py
  18. 148
      module_auto_update/models/module.py
  19. 4
      module_auto_update/models/module_deprecated.py
  20. 6
      module_auto_update/tests/__init__.py
  21. 1
      module_auto_update/tests/sample_module/README.rst
  22. 1
      module_auto_update/tests/sample_module/data/f1.xml
  23. 1
      module_auto_update/tests/sample_module/data/f2.xml
  24. 1
      module_auto_update/tests/sample_module/i18n/en.po
  25. 1
      module_auto_update/tests/sample_module/i18n/en_US.po
  26. 1
      module_auto_update/tests/sample_module/i18n/fr.po
  27. 1
      module_auto_update/tests/sample_module/i18n/fr_BE.po
  28. 1
      module_auto_update/tests/sample_module/i18n/test.pot
  29. 1
      module_auto_update/tests/sample_module/i18n_extra/en.po
  30. 1
      module_auto_update/tests/sample_module/i18n_extra/fr.po
  31. 1
      module_auto_update/tests/sample_module/i18n_extra/nl_NL.po
  32. 1
      module_auto_update/tests/sample_module/models/stuff.py
  33. BIN
      module_auto_update/tests/sample_module/models/stuff.pyc
  34. BIN
      module_auto_update/tests/sample_module/models/stuff.pyo
  35. 1
      module_auto_update/tests/sample_module/static/src/some.js
  36. 68
      module_auto_update/tests/test_addon_hash.py
  37. 203
      module_auto_update/tests/test_module.py

90
module_auto_update/README.rst

@ -0,0 +1,90 @@
.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
==================
Module Auto Update
==================
This addon provides mechanisms to compute sha1 hashes of installed addons,
and save them in the database. It also provides a method that exploits these
mechanisms to update a database by upgrading only the modules for which the
hash has changed since the last successful upgrade.
Configuration
=============
This module supports the following system parameters:
* ``module_auto_update.exclude_patterns``: comma-separated list of file
name patterns to ignore when computing addon checksums. Defaults to
``*.pyc,*.pyo,i18n/*.pot,i18n_extra/*.pot,static/*``.
Filename patterns must be compatible with the python ``fnmatch`` function.
In addition to the above pattern, .po files corresponding to languages that
are not installed in the Odoo database are ignored when computing checksums.
Usage
=====
The main method provided by this module is ``upgrade_changed_checksum``
on ``ir.module.module``. It runs a database upgrade for all installed
modules for which the hash has changed since the last successful
run of this method. On success it saves the hashes in the database.
The first time this method is invoked after installing the module, it
runs an upgrade of all modules, because it has not saved the hashes yet.
This is by design, priviledging safety. Should this be an issue,
the method ``_save_installed_checksums`` can be invoked in a situation
where one is sure all modules on disk are installed and up-to-date in the
database.
An easy way to invoke this upgrade mechanism is by issuing the following
in an Odoo shell session::
env['ir.module.module'].upgrade_changed_checksum()
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/8.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Brent Hughes <brent.hughes@laslabs.com>
* Juan José Scarafía <jjs@adhoc.com.ar>
* Jairo Llopis <jairo.llopis@tecnativa.com>
* Stéphane Bidoul <stephane.bidoul@acsone.eu> (https://acsone.eu)
Do not contact contributors directly about support or help with technical issues.
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

6
module_auto_update/__init__.py

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from . import models
from .hooks import install_hook, uninstall_hook

24
module_auto_update/__openerp__.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
{
'name': 'Module Auto Update',
'summary': 'Automatically update Odoo modules',
'version': '8.0.2.0.1',
'category': 'Extra Tools',
'website': 'https://github.com/OCA/server-tools',
'author': 'LasLabs, '
'Juan José Scarafía, '
'Tecnativa, '
'ACSONE SA/NV, '
'Odoo Community Association (OCA)',
'license': 'LGPL-3',
'application': False,
'installable': True,
'install_hook': 'install_hook',
'uninstall_hook': 'uninstall_hook',
'depends': [
'base',
],
}

45
module_auto_update/addon_hash.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright 2018 ACSONE SA/NV.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from fnmatch import fnmatch
import hashlib
import os
def _fnmatch(filename, patterns):
for pattern in patterns:
if fnmatch(filename, pattern):
return True
return False
def _walk(top, exclude_patterns, keep_langs):
keep_langs = {l.split('_')[0] for l in keep_langs}
for dirpath, dirnames, filenames in os.walk(top):
dirnames.sort()
reldir = os.path.relpath(dirpath, top)
if reldir == '.':
reldir = ''
for filename in sorted(filenames):
filepath = os.path.join(reldir, filename)
if _fnmatch(filepath, exclude_patterns):
continue
if keep_langs and reldir in {'i18n', 'i18n_extra'}:
basename, ext = os.path.splitext(filename)
if ext == '.po':
if basename.split('_')[0] not in keep_langs:
continue
yield filepath
def addon_hash(top, exclude_patterns, keep_langs):
"""Compute a sha1 digest of file contents."""
m = hashlib.sha1()
for filepath in _walk(top, exclude_patterns, keep_langs):
# hash filename so empty files influence the hash
m.update(filepath.encode('utf-8'))
# hash file content
with open(os.path.join(top, filepath), 'rb') as f:
m.update(f.read())
return m.hexdigest()

20
module_auto_update/hooks.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from openerp import SUPERUSER_ID, api
from .models.module import PARAM_INSTALLED_CHECKSUMS
from .models.module_deprecated import PARAM_DEPRECATED
def install_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
# make sure migration to 9 does not enable deprecated features
env["ir.config_parameter"].set_param(PARAM_DEPRECATED, '0')
def uninstall_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
env["ir.config_parameter"].set_param(PARAM_INSTALLED_CHECKSUMS, False)
env["ir.config_parameter"].set_param(PARAM_DEPRECATED, False)

59
module_auto_update/i18n/ca.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2017\n"
"Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Mòdul"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

59
module_auto_update/i18n/de.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# Niki Waibel <niki.waibel@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: Niki Waibel <niki.waibel@gmail.com>, 2017\n"
"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Modul"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr "Modul aktualisieren"
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

59
module_auto_update/i18n/es.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# Pedro M. Baeza <pedro.baeza@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: Pedro M. Baeza <pedro.baeza@gmail.com>, 2017\n"
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Módulo"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr "Actualización de módulo"
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

59
module_auto_update/i18n/es_MX.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2017\n"
"Language-Team: Spanish (Mexico) (https://www.transifex.com/oca/teams/23907/es_MX/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es_MX\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Módulo"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

62
module_auto_update/i18n/fr.po

@ -0,0 +1,62 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
# Quentin THEURET <odoo@kerpeo.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-08-04 02:55+0000\n"
"PO-Revision-Date: 2017-08-04 02:55+0000\n"
"Last-Translator: Quentin THEURET <odoo@kerpeo.com>, 2017\n"
"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr "Rép. de checksum"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr "Checksum installé"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Module"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr "Mise à jour de module(s)"
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr "Modules"
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
"Ouvrir les mises à jour et l'action serveur de la liste des mises à jour des"
" applications"
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr "Mises à jour planifiées"
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr "Mises à jour"

59
module_auto_update/i18n/hr.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# Bole <bole@dajmi5.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: Bole <bole@dajmi5.com>, 2017\n"
"Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: hr\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Modul"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

59
module_auto_update/i18n/it.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2017\n"
"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Modulo"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

59
module_auto_update/i18n/nl_NL.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# Peter Hageman <hageman.p@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: Peter Hageman <hageman.p@gmail.com>, 2017\n"
"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/teams/23907/nl_NL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nl_NL\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Module"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

59
module_auto_update/i18n/pt_BR.po

@ -0,0 +1,59 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-21 02:43+0000\n"
"PO-Revision-Date: 2017-07-21 02:43+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2017\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/oca/teams/23907/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Módulo"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

60
module_auto_update/i18n/sl.po

@ -0,0 +1,60 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
# Matjaž Mozetič <m.mozetic@matmoz.si>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-21 22:54+0000\n"
"PO-Revision-Date: 2017-10-21 22:54+0000\n"
"Last-Translator: Matjaž Mozetič <m.mozetic@matmoz.si>, 2017\n"
"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Modul"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr "Nadgradnja modula"
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr "Posodobitve"

60
module_auto_update/i18n/tr.po

@ -0,0 +1,60 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_auto_update
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
# Ahmet Altinisik <aaltinisik@altinkaya.com.tr>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-08-04 02:55+0000\n"
"PO-Revision-Date: 2017-08-04 02:55+0000\n"
"Last-Translator: Ahmet Altinisik <aaltinisik@altinkaya.com.tr>, 2017\n"
"Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir
msgid "Checksum dir"
msgstr ""
#. module: module_auto_update
#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed
msgid "Checksum installed"
msgstr ""
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_ir_module_module
msgid "Module"
msgstr "Modül"
#. module: module_auto_update
#: model:ir.model,name:module_auto_update.model_base_module_upgrade
msgid "Module Upgrade"
msgstr "Modül Güncelle"
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.menu_default_modules
msgid "Modules"
msgstr ""
#. module: module_auto_update
#: model:ir.actions.server,name:module_auto_update.module_action_open_updates
msgid "Open Updates and Update Apps List Server Action"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.view,arch_db:module_auto_update.module_view_search
msgid "Scheduled Upgrades"
msgstr ""
#. module: module_auto_update
#: model:ir.ui.menu,name:module_auto_update.module_menu_updates
msgid "Updates"
msgstr ""

5
module_auto_update/models/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from . import module

148
module_auto_update/models/module.py

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# Copyright 2018 ACSONE SA/NV.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import json
import logging
import os
from openerp import api, exceptions, models, tools
from openerp.modules.module import get_module_path
from ..addon_hash import addon_hash
PARAM_INSTALLED_CHECKSUMS = \
'module_auto_update.installed_checksums'
PARAM_EXCLUDE_PATTERNS = \
'module_auto_update.exclude_patterns'
DEFAULT_EXCLUDE_PATTERNS = \
'*.pyc,*.pyo,i18n/*.pot,i18n_extra/*.pot,static/*'
_logger = logging.getLogger(__name__)
class IncompleteUpgradeError(exceptions.Warning):
pass
class Module(models.Model):
_inherit = 'ir.module.module'
@api.multi
def _get_checksum_dir(self):
self.ensure_one()
exclude_patterns = self.env["ir.config_parameter"].get_param(
PARAM_EXCLUDE_PATTERNS,
DEFAULT_EXCLUDE_PATTERNS,
)
exclude_patterns = [p.strip() for p in exclude_patterns.split(',')]
keep_langs = self.env['res.lang'].search([]).mapped('code')
module_path = get_module_path(self.name)
if module_path and os.path.isdir(module_path):
checksum_dir = addon_hash(
module_path,
exclude_patterns,
keep_langs,
)
else:
checksum_dir = False
return checksum_dir
@api.model
def _get_saved_checksums(self):
Icp = self.env['ir.config_parameter']
return json.loads(Icp.get_param(PARAM_INSTALLED_CHECKSUMS, '{}'))
@api.model
def _save_checksums(self, checksums):
Icp = self.env['ir.config_parameter']
Icp.set_param(PARAM_INSTALLED_CHECKSUMS, json.dumps(checksums))
@api.model
def _save_installed_checksums(self):
checksums = {}
installed_modules = self.search([('state', '=', 'installed')])
for module in installed_modules:
checksums[module.name] = module._get_checksum_dir()
self._save_checksums(checksums)
@api.model
def _get_modules_partially_installed(self):
return self.search([
('state', 'in', ('to install', 'to remove', 'to upgrade')),
])
@api.model
def _get_modules_with_changed_checksum(self):
saved_checksums = self._get_saved_checksums()
installed_modules = self.search([('state', '=', 'installed')])
return installed_modules.filtered(
lambda r: r._get_checksum_dir() != saved_checksums.get(r.name),
)
@api.model
def upgrade_changed_checksum(self, overwrite_existing_translations=False):
"""Run an upgrade of the database, upgrading only changed modules.
Installed modules for which the checksum has changed since the
last successful run of this method are marked "to upgrade",
then the normal Odoo scheduled upgrade process
is launched.
If there is no module with a changed checksum, and no module in state
other than installed, uninstalled, uninstallable, this method does
nothing, otherwise the normal Odoo upgrade process is launched.
After a successful upgrade, the checksums of installed modules are
saved.
In case of error during the upgrade, an exception is raised.
If any module remains to upgrade or to uninstall after the upgrade
process, an exception is raised as well.
Note: this method commits the current transaction at each important
step, it is therefore not intended to be run as part of a
larger transaction.
"""
_logger.info(
"Checksum upgrade starting (i18n-overwrite=%s)...",
overwrite_existing_translations
)
tools.config['overwrite_existing_translations'] = \
overwrite_existing_translations
_logger.info("Updating modules list...")
self.update_list()
changed_modules = self._get_modules_with_changed_checksum()
if not changed_modules and not self._get_modules_partially_installed():
_logger.info("No checksum change detected in installed modules "
"and all modules installed, nothing to do.")
return
_logger.info("Marking the following modules to upgrade, "
"for their checksums changed: %s...",
','.join(changed_modules.mapped('name')))
changed_modules.button_upgrade()
self.env.cr.commit() # pylint: disable=invalid-commit
_logger.info("Upgrading...")
self.env['base.module.upgrade'].upgrade_module()
self.env.cr.commit() # pylint: disable=invalid-commit
_logger.info("Upgrade successful, updating checksums...")
self._save_installed_checksums()
self.env.cr.commit() # pylint: disable=invalid-commit
partial_modules = self._get_modules_partially_installed()
if partial_modules:
raise IncompleteUpgradeError(
"Checksum upgrade successful "
"but incomplete for the following modules: %s" %
','.join(partial_modules.mapped('name'))
)
_logger.info("Checksum upgrade complete.")

4
module_auto_update/models/module_deprecated.py

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
PARAM_DEPRECATED = "module_auto_update.enable_deprecated"

6
module_auto_update/tests/__init__.py

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from . import test_addon_hash
from . import test_module

1
module_auto_update/tests/sample_module/README.rst

@ -0,0 +1 @@
Test data for addon_hash module.

1
module_auto_update/tests/sample_module/data/f1.xml

@ -0,0 +1 @@
<odoo/>

1
module_auto_update/tests/sample_module/data/f2.xml

@ -0,0 +1 @@
<odoo/>

1
module_auto_update/tests/sample_module/i18n/en.po

@ -0,0 +1 @@
en text

1
module_auto_update/tests/sample_module/i18n/en_US.po

@ -0,0 +1 @@
en_US

1
module_auto_update/tests/sample_module/i18n/fr.po

@ -0,0 +1 @@
fr

1
module_auto_update/tests/sample_module/i18n/fr_BE.po

@ -0,0 +1 @@
fr_BE

1
module_auto_update/tests/sample_module/i18n/test.pot

@ -0,0 +1 @@
...

1
module_auto_update/tests/sample_module/i18n_extra/en.po

@ -0,0 +1 @@
en

1
module_auto_update/tests/sample_module/i18n_extra/fr.po

@ -0,0 +1 @@
fr

1
module_auto_update/tests/sample_module/i18n_extra/nl_NL.po

@ -0,0 +1 @@
nl_NL

1
module_auto_update/tests/sample_module/models/stuff.py

@ -0,0 +1 @@
1+1

BIN
module_auto_update/tests/sample_module/models/stuff.pyc

BIN
module_auto_update/tests/sample_module/models/stuff.pyo

1
module_auto_update/tests/sample_module/static/src/some.js

@ -0,0 +1 @@
/* javascript */

68
module_auto_update/tests/test_addon_hash.py

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
# Copyright 2018 ACSONE SA/NV.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import os
import unittest
from .. import addon_hash
from ..models.module import DEFAULT_EXCLUDE_PATTERNS
class TestAddonHash(unittest.TestCase):
def setUp(self):
super(TestAddonHash, self).setUp()
self.sample_dir = os.path.join(
os.path.dirname(__file__),
'sample_module',
)
def test_basic(self):
files = list(addon_hash._walk(
self.sample_dir,
exclude_patterns=[],
keep_langs=[],
))
self.assertEqual(files, [
'README.rst',
'data/f1.xml',
'data/f2.xml',
'i18n/en.po',
'i18n/en_US.po',
'i18n/fr.po',
'i18n/fr_BE.po',
'i18n/test.pot',
'i18n_extra/en.po',
'i18n_extra/fr.po',
'i18n_extra/nl_NL.po',
'models/stuff.py',
'models/stuff.pyc',
'models/stuff.pyo',
'static/src/some.js',
])
def test_exclude(self):
files = list(addon_hash._walk(
self.sample_dir,
exclude_patterns=DEFAULT_EXCLUDE_PATTERNS.split(','),
keep_langs=['fr_FR', 'nl'],
))
self.assertEqual(files, [
'README.rst',
'data/f1.xml',
'data/f2.xml',
'i18n/fr.po',
'i18n/fr_BE.po',
'i18n_extra/fr.po',
'i18n_extra/nl_NL.po',
'models/stuff.py',
])
def test2(self):
checksum = addon_hash.addon_hash(
self.sample_dir,
exclude_patterns=['*.pyc', '*.pyo', '*.pot', 'static/*'],
keep_langs=['fr_FR', 'nl'],
)
self.assertEqual(checksum, 'fecb89486c8a29d1f760cbd01c1950f6e8421b14')

203
module_auto_update/tests/test_module.py

@ -0,0 +1,203 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# Copyright 2018 ACSONE SA/NV.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import os
import tempfile
import mock
from openerp.modules import get_module_path
from openerp.tests import common
from openerp.tests.common import TransactionCase
from ..addon_hash import addon_hash
from ..models.module import IncompleteUpgradeError, DEFAULT_EXCLUDE_PATTERNS
MODULE_NAME = 'module_auto_update'
class TestModule(TransactionCase):
def setUp(self):
super(TestModule, self).setUp()
self.own_module = self.env['ir.module.module'].search([
('name', '=', MODULE_NAME),
])
self.own_dir_path = get_module_path(MODULE_NAME)
keep_langs = self.env['res.lang'].search([]).mapped('code')
self.own_checksum = addon_hash(
self.own_dir_path,
exclude_patterns=DEFAULT_EXCLUDE_PATTERNS.split(','),
keep_langs=keep_langs,
)
self.own_writeable = os.access(self.own_dir_path, os.W_OK)
def test_compute_checksum_dir(self):
"""It should compute the directory's SHA-1 hash"""
self.assertEqual(
self.own_module._get_checksum_dir(), self.own_checksum,
'Module directory checksum not computed properly',
)
def test_compute_checksum_dir_ignore_excluded(self):
"""It should exclude .pyc/.pyo extensions from checksum
calculations"""
if not self.own_writeable:
self.skipTest("Own directory not writeable")
with tempfile.NamedTemporaryFile(suffix='.pyc', dir=self.own_dir_path):
self.assertEqual(
self.own_module._get_checksum_dir(), self.own_checksum,
'SHA1 checksum does not ignore excluded extensions',
)
def test_compute_checksum_dir_recomputes_when_file_added(self):
"""It should return a different value when a non-.pyc/.pyo file is
added to the module directory"""
if not self.own_writeable:
self.skipTest("Own directory not writeable")
with tempfile.NamedTemporaryFile(suffix='.py', dir=self.own_dir_path):
self.assertNotEqual(
self.own_module._get_checksum_dir(), self.own_checksum,
'SHA1 checksum not recomputed',
)
def test_saved_checksums(self):
Imm = self.env['ir.module.module']
base_module = Imm.search([('name', '=', 'base')])
self.assertEqual(base_module.state, 'installed')
self.assertFalse(Imm._get_saved_checksums())
Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums()
self.assertTrue(saved_checksums)
self.assertTrue(saved_checksums['base'])
def test_get_modules_with_changed_checksum(self):
Imm = self.env['ir.module.module']
self.assertTrue(Imm._get_modules_with_changed_checksum())
Imm._save_installed_checksums()
self.assertFalse(Imm._get_modules_with_changed_checksum())
@common.at_install(False)
@common.post_install(True)
class TestModuleAfterInstall(TransactionCase):
def setUp(self):
super(TestModuleAfterInstall, self).setUp()
Imm = self.env['ir.module.module']
self.own_module = Imm.search([('name', '=', MODULE_NAME)])
self.base_module = Imm.search([('name', '=', 'base')])
def test_get_modules_partially_installed(self):
Imm = self.env['ir.module.module']
self.assertTrue(
self.own_module not in Imm._get_modules_partially_installed())
self.own_module.button_upgrade()
self.assertTrue(
self.own_module in Imm._get_modules_partially_installed())
self.own_module.button_upgrade_cancel()
self.assertTrue(
self.own_module not in Imm._get_modules_partially_installed())
def test_upgrade_changed_checksum(self):
Imm = self.env['ir.module.module']
Bmu = self.env['base.module.upgrade']
# check modules are in installed state
installed_modules = Imm.search([('state', '=', 'installed')])
self.assertTrue(self.own_module in installed_modules)
self.assertTrue(self.base_module in installed_modules)
self.assertTrue(len(installed_modules) > 2)
# change the checksum of 'base'
Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums()
saved_checksums['base'] = False
Imm._save_checksums(saved_checksums)
changed_modules = Imm._get_modules_with_changed_checksum()
self.assertEqual(len(changed_modules), 1)
self.assertTrue(self.base_module in changed_modules)
def upgrade_module_mock(self_model):
upgrade_module_mock.call_count += 1
# since we are upgrading base, all installed module
# must have been marked to upgrade at this stage
self.assertEqual(self.base_module.state, 'to upgrade')
self.assertEqual(self.own_module.state, 'to upgrade')
installed_modules.write({'state': 'installed'})
upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'):
# we simulate an install by setting module states
Bmu._patch_method('upgrade_module', upgrade_module_mock)
try:
Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 1)
self.assertEqual(self.base_module.state, 'installed')
self.assertEqual(self.own_module.state, 'installed')
saved_checksums = Imm._get_saved_checksums()
self.assertTrue(saved_checksums['base'])
self.assertTrue(saved_checksums[MODULE_NAME])
finally:
Bmu._revert_method('upgrade_module')
def test_incomplete_upgrade(self):
Imm = self.env['ir.module.module']
Bmu = self.env['base.module.upgrade']
installed_modules = Imm.search([('state', '=', 'installed')])
# change the checksum of 'base'
Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums()
saved_checksums['base'] = False
Imm._save_checksums(saved_checksums)
def upgrade_module_mock(self_model):
upgrade_module_mock.call_count += 1
# since we are upgrading base, all installed module
# must have been marked to upgrade at this stage
self.assertEqual(self.base_module.state, 'to upgrade')
self.assertEqual(self.own_module.state, 'to upgrade')
installed_modules.write({'state': 'installed'})
# simulate partial upgrade
self.own_module.write({'state': 'to upgrade'})
upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'):
# we simulate an install by setting module states
Bmu._patch_method('upgrade_module', upgrade_module_mock)
try:
with self.assertRaises(IncompleteUpgradeError):
Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 1)
finally:
Bmu._revert_method('upgrade_module')
def test_nothing_to_upgrade(self):
Imm = self.env['ir.module.module']
Bmu = self.env['base.module.upgrade']
Imm._save_installed_checksums()
def upgrade_module_mock(self_model):
upgrade_module_mock.call_count += 1
upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'):
# we simulate an install by setting module states
Bmu._patch_method('upgrade_module', upgrade_module_mock)
try:
Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 0)
finally:
Bmu._revert_method('upgrade_module')
Loading…
Cancel
Save