diff --git a/HISTORY.rst b/HISTORY.rst index 6b1de73037..76d0cf8f01 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,3 +1,8 @@ +3.3.2 (2019-XX-XX) +================== +- Improve setting migration method matching. Avoid executing + a migrations for settings with similar but shorter names. + 3.3.1 (2019-12-04) ================== - Update Celery broker environment variable in the docker installer. diff --git a/mayan/apps/smart_settings/classes.py b/mayan/apps/smart_settings/classes.py index ca6e8f0e6b..59595aeb02 100644 --- a/mayan/apps/smart_settings/classes.py +++ b/mayan/apps/smart_settings/classes.py @@ -5,6 +5,7 @@ import hashlib from importlib import import_module import logging import os +import re import sys import yaml @@ -122,12 +123,13 @@ class Namespace(object): class NamespaceMigration(object): + @staticmethod + def get_method_name(setting): + return setting.global_name.lower() + def __init__(self, namespace): self.namespace = namespace - def get_method_name(self, setting): - return setting.global_name.lower() - def get_method_name_full(self, setting, version): return '{}_{}'.format( self.get_method_name(setting=setting), @@ -136,18 +138,20 @@ class NamespaceMigration(object): def migrate(self, setting): if self.namespace.get_config_version() != self.namespace.version: - method_name = self.get_method_name(setting=setting) + setting_method_name = NamespaceMigration.get_method_name( + setting=setting + ) # Get methods for this setting - setting_methods = [ - method for method in dir(self) if method.startswith( - method_name - ) - ] + pattern = r'{}_\d{{4}}'.format(setting_method_name) + setting_methods = re.findall( + pattern=pattern, string='\n'.join(dir(self)) + ) + # Get order of execution of setting methods versions = [ method.replace( - '{}_'.format(method_name), '' + '{}_'.format(setting_method_name), '' ) for method in setting_methods ] try: diff --git a/mayan/apps/smart_settings/tests/mocks.py b/mayan/apps/smart_settings/tests/mocks.py index bf7f51af10..f282dc7dab 100644 --- a/mayan/apps/smart_settings/tests/mocks.py +++ b/mayan/apps/smart_settings/tests/mocks.py @@ -14,3 +14,13 @@ class TestNamespaceMigrationTwo(NamespaceMigration): def smart_settings_test_setting_0002(self, value): return '{}_0002'.format(value) + + +class TestNamespaceMigrationInvalid(NamespaceMigration): + def smart_settings_test_setting(self, value): + return 'invalid migration' + + +class TestNamespaceMigrationInvalidDual(NamespaceMigration): + def smart_settings_test_setting_with_longer_name(self, value): + return 'invalid migration' diff --git a/mayan/apps/smart_settings/tests/test_classes.py b/mayan/apps/smart_settings/tests/test_classes.py index 983fca3f59..7eae2eca3b 100644 --- a/mayan/apps/smart_settings/tests/test_classes.py +++ b/mayan/apps/smart_settings/tests/test_classes.py @@ -17,7 +17,10 @@ from .literals import ( TEST_SETTING_INITIAL_VALUE, TEST_SETTING_VALUE ) from .mixins import SmartSettingTestMixin -from .mocks import TestNamespaceMigrationOne, TestNamespaceMigrationTwo +from .mocks import ( + TestNamespaceMigrationOne, TestNamespaceMigrationTwo, + TestNamespaceMigrationInvalid, TestNamespaceMigrationInvalidDual +) class ClassesTestCase(EnvironmentTestCaseMixin, SmartSettingTestMixin, BaseTestCase): @@ -116,3 +119,43 @@ class NamespaceMigrationTestCase( self.assertEqual( self.test_setting.value, '{}_0001_0002'.format(TEST_SETTING_VALUE) ) + + def test_migration_invalid(self): + self._create_test_settings_namespace( + migration_class=TestNamespaceMigrationInvalid, version='0002' + ) + self._create_test_setting() + + with NamedTemporaryFile() as file_object: + settings.CONFIGURATION_FILEPATH = file_object.name + file_object.write( + force_bytes( + '{}: {}'.format(TEST_SETTING_GLOBAL_NAME, TEST_SETTING_VALUE) + ) + ) + file_object.seek(0) + Setting._config_file_cache = None + + self.assertEqual( + self.test_setting.value, TEST_SETTING_VALUE + ) + + def test_migration_invalid_dual(self): + self._create_test_settings_namespace( + migration_class=TestNamespaceMigrationInvalidDual, version='0002' + ) + self._create_test_setting() + + with NamedTemporaryFile() as file_object: + settings.CONFIGURATION_FILEPATH = file_object.name + file_object.write( + force_bytes( + '{}: {}'.format(TEST_SETTING_GLOBAL_NAME, TEST_SETTING_VALUE) + ) + ) + file_object.seek(0) + Setting._config_file_cache = None + + self.assertEqual( + self.test_setting.value, TEST_SETTING_VALUE + )