Michael Merickel
2016-12-16 0c1c5872915d79f14b1af8c7c3e597d5e83f30bb
defer translation_dir resolution to allow asset overrides to execute first

fixes #2046
3 files modified
72 ■■■■ changed files
pyramid/config/__init__.py 13 ●●●● patch | view | raw | blame | history
pyramid/config/i18n.py 45 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_i18n.py 14 ●●●● patch | view | raw | blame | history
pyramid/config/__init__.py
@@ -451,9 +451,6 @@
            return filename # absolute filename
        return '%s:%s' % (package, filename)
    def _split_spec(self, path_or_spec):
        return resolve_asset_spec(path_or_spec, self.package_name)
    def _fix_registry(self):
        """ Fix up a ZCA component registry that is not a
        pyramid.registry.Registry by adding analogues of ``has_listeners``,
@@ -651,7 +648,11 @@
        of this error will be information about the source of the conflict,
        usually including file names and line numbers of the cause of the
        configuration conflicts."""
        self.action_state.execute_actions(introspector=self.introspector)
        self.begin()
        try:
            self.action_state.execute_actions(introspector=self.introspector)
        finally:
            self.end()
        self.action_state = ActionState() # old actions have been processed
    def include(self, callable, route_prefix=None):
@@ -992,11 +993,11 @@
        # Push the registry onto the stack in case any code that depends on
        # the registry threadlocal APIs used in listeners subscribed to the
        # IApplicationCreated event.
        self.manager.push({'registry': self.registry, 'request': None})
        self.begin()
        try:
            self.registry.notify(ApplicationCreated(app))
        finally:
            self.manager.pop()
            self.end()
        return app
pyramid/config/i18n.py
@@ -1,13 +1,10 @@
import os
import sys
from pyramid.interfaces import (
    ILocaleNegotiator,
    ITranslationDirectories,
    )
from pyramid.exceptions import ConfigurationError
from pyramid.path import package_path
from pyramid.path import AssetResolver
from pyramid.util import action_method
class I18NConfiguratorMixin(object):
@@ -69,32 +66,32 @@
        directories will be inserted into the beginning of the directory list
        in the order they're provided in the ``*specs`` list argument (items
        earlier in the list trump ones later in the list).
        """
        directories = []
        introspectables = []
        for spec in specs[::-1]: # reversed
            package_name, filename = self._split_spec(spec)
            if package_name is None: # absolute filename
                directory = filename
            else:
                __import__(package_name)
                package = sys.modules[package_name]
                directory = os.path.join(package_path(package), filename)
            if not os.path.isdir(os.path.realpath(directory)):
                raise ConfigurationError('"%s" is not a directory' %
                                         directory)
            intr = self.introspectable('translation directories', directory,
                                       spec, 'translation directory')
            intr['directory'] = directory
            intr['spec'] = spec
            introspectables.append(intr)
            directories.append(directory)
        resolver = AssetResolver(self.package_name)
        def register():
            for directory in directories:
            # defer spec resolution until register to allow for asset
            # overrides to take place in an earlier config phase
            for spec in specs[::-1]: # reversed
                # the trailing slash helps match asset overrides for folders
                if not spec.endswith('/'):
                    spec += '/'
                asset = resolver.resolve(spec)
                directory = asset.abspath()
                if not asset.isdir():
                    raise ConfigurationError('"%s" is not a directory' %
                                            directory)
                intr = self.introspectable('translation directories', directory,
                                        spec, 'translation directory')
                intr['directory'] = directory
                intr['spec'] = spec
                introspectables.append(intr)
                directories.append(directory)
            for directory in directories:
                tdirs = self.registry.queryUtility(ITranslationDirectories)
                if tdirs is None:
                    tdirs = []
pyramid/tests/test_config/test_i18n.py
@@ -36,9 +36,8 @@
    def test_add_translation_dirs_missing_dir(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne()
        self.assertRaises(ConfigurationError,
                          config.add_translation_dirs,
                          '/wont/exist/on/my/system')
        config.add_translation_dirs('/wont/exist/on/my/system')
        self.assertRaises(ConfigurationError, config.commit)
    def test_add_translation_dirs_no_specs(self):
        from pyramid.interfaces import ITranslationDirectories
@@ -87,3 +86,12 @@
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale])
    def test_add_translation_dirs_uses_override(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne()
        config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale')
        config.override_asset('pyramid.tests.pkgs.localeapp:locale/',
                              'pyramid.tests.pkgs.localeapp:locale2/')
        config.commit()
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                        [locale2])