Tres Seaver
2013-05-09 8ebdc05e58ea5dc90fe0f2beff315095e61851ee
backport pull #1015 to 1.4 branch
4 files modified
185 ■■■■■ changed files
CHANGES.txt 7 ●●●●● patch | view | raw | blame | history
pyramid/config/assets.py 38 ●●●● patch | view | raw | blame | history
pyramid/interfaces.py 44 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_assets.py 96 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -7,6 +7,13 @@
  Python 3.2 (although the combination of MarkupSafe 0.17 and Python 3.3 or any
  supported Python 2 version will work OK).
- Make the ``pyramid.config.assets.PackageOverrides`` object implement the API
  for ``__loader__`` objects specified in PEP 302.  Proxies to the
  ``__loader__`` set by the importer, if present; otherwise, raises
  ``NotImplementedError``.  This makes Pyramid static view overrides work
  properly under Python 3.3 (previously they would not).  See
  https://github.com/Pylons/pyramid/pull/1015 for more information.
1.4.1 (2013-04-23)
==================
pyramid/config/assets.py
@@ -81,14 +81,12 @@
            self, resource_name)
        
@implementer(IPackageOverrides)
class PackageOverrides:
class PackageOverrides(object):
    # pkg_resources arg in kw args below for testing
    def __init__(self, package, pkg_resources=pkg_resources):
        if hasattr(package, '__loader__') and not isinstance(package.__loader__,
                                                             self.__class__):
            raise TypeError('Package %s already has a non-%s __loader__ '
                            '(probably a module in a zipped egg)' %
                            (package, self.__class__))
        loader = self._real_loader = getattr(package, '__loader__', None)
        if isinstance(loader, self.__class__):
            self._real_loader = None
        # We register ourselves as a __loader__ *only* to support the
        # setuptools _find_adapter adapter lookup; this class doesn't
        # actually support the PEP 302 loader "API".  This is
@@ -150,7 +148,33 @@
        for package, rname in self.search_path(resource_name):
            if pkg_resources.resource_exists(package, rname):
                return pkg_resources.resource_listdir(package, rname)
    @property
    def real_loader(self):
        if self._real_loader is None:
            raise NotImplementedError()
        return self._real_loader
    def get_data(self, path):
        """ See IPEP302Loader.
        """
        return self.real_loader.get_data(path)
    def is_package(self, fullname):
        """ See IPEP302Loader.
        """
        return self.real_loader.is_package(fullname)
    def get_code(self, fullname):
        """ See IPEP302Loader.
        """
        return self.real_loader.get_code(fullname)
    def get_source(self, fullname):
        """ See IPEP302Loader.
        """
        return self.real_loader.get_source(fullname)
class DirectoryOverride:
    def __init__(self, path, package, prefix):
pyramid/interfaces.py
@@ -795,7 +795,49 @@
    'See the "What\'s new In Pyramid 1.3" document for a further description.'
    )
class IPackageOverrides(Interface):
class IPEP302Loader(Interface):
    """ See http://www.python.org/dev/peps/pep-0302/#id30.
    """
    def get_data(path):
        """ Retrieve data for and arbitrary "files" from storage backend.
        Raise IOError for not found.
        Data is returned as bytes.
        """
    def is_package(fullname):
        """ Return True if the module specified by 'fullname' is a package.
        """
    def get_code(fullname):
        """ Return the code object for the module identified by 'fullname'.
        Return 'None' if it's a built-in or extension module.
        If the loader doesn't have the code object but it does have the source
        code, return the compiled source code.
        Raise ImportError if the module can't be found by the importer at all.
        """
    def get_source(fullname):
        """ Return the source code for the module identified by 'fullname'.
        Return a string, using newline characters for line endings, or None
        if the source is not available.
        Raise ImportError if the module can't be found by the importer at all.
        """
    def get_filename(fullname):
        """ Return the value of '__file__' if the named module was loaded.
        If the module is not found, raise ImportError.
        """
class IPackageOverrides(IPEP302Loader):
    """ Utility for pkg_resources overrides """
# VH_ROOT_KEY is an interface; its imported from other packages (e.g.
pyramid/tests/test_config/test_assets.py
@@ -314,16 +314,40 @@
        from pyramid.config.assets import PackageOverrides
        return PackageOverrides
    def _makeOne(self, package, pkg_resources=None):
    def _makeOne(self, package=None, pkg_resources=None):
        if package is None:
            package = DummyPackage('package')
        klass = self._getTargetClass()
        if pkg_resources is None:
            pkg_resources = DummyPkgResources()
        return klass(package, pkg_resources=pkg_resources)
    def test_class_conforms_to_IPackageOverrides(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IPackageOverrides
        verifyClass(IPackageOverrides, self._getTargetClass())
    def test_instance_conforms_to_IPackageOverrides(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IPackageOverrides
        verifyObject(IPackageOverrides, self._makeOne())
    def test_class_conforms_to_IPEP302Loader(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IPEP302Loader
        verifyClass(IPEP302Loader, self._getTargetClass())
    def test_instance_conforms_to_IPEP302Loader(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IPEP302Loader
        verifyObject(IPEP302Loader, self._makeOne())
    def test_ctor_package_already_has_loader_of_different_type(self):
        package = DummyPackage('package')
        package.__loader__ = True
        self.assertRaises(TypeError, self._makeOne, package)
        loader = package.__loader__ = DummyLoader()
        po = self._makeOne(package)
        self.assertTrue(package.__loader__ is po)
        self.assertTrue(po.real_loader is loader)
    def test_ctor_package_already_has_loader_of_same_type(self):
        package = DummyPackage('package')
@@ -502,6 +526,55 @@
        po.overrides= overrides
        self.assertEqual(po.listdir('whatever'), None)
    # PEP 302 __loader__ extensions:  use the "real" __loader__, if present.
    def test_get_data_pkg_has_no___loader__(self):
        package = DummyPackage('package')
        po = self._makeOne(package)
        self.assertRaises(NotImplementedError, po.get_data, 'whatever')
    def test_get_data_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        po = self._makeOne(package)
        self.assertEqual(po.get_data('whatever'), b'DEADBEEF')
        self.assertEqual(loader._got_data, 'whatever')
    def test_is_package_pkg_has_no___loader__(self):
        package = DummyPackage('package')
        po = self._makeOne(package)
        self.assertRaises(NotImplementedError, po.is_package, 'whatever')
    def test_is_package_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        po = self._makeOne(package)
        self.assertTrue(po.is_package('whatever'))
        self.assertEqual(loader._is_package, 'whatever')
    def test_get_code_pkg_has_no___loader__(self):
        package = DummyPackage('package')
        po = self._makeOne(package)
        self.assertRaises(NotImplementedError, po.get_code, 'whatever')
    def test_get_code_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        po = self._makeOne(package)
        self.assertEqual(po.get_code('whatever'), b'DEADBEEF')
        self.assertEqual(loader._got_code, 'whatever')
    def test_get_source_pkg_has_no___loader__(self):
        package = DummyPackage('package')
        po = self._makeOne(package)
        self.assertRaises(NotImplementedError, po.get_source, 'whatever')
    def test_get_source_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        po = self._makeOne(package)
        self.assertEqual(po.get_source('whatever'), 'def foo():\n    pass')
        self.assertEqual(loader._got_source, 'whatever')
class TestDirectoryOverride(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.config.assets import DirectoryOverride
@@ -570,10 +643,25 @@
    def register_loader_type(self, typ, inst):
        self.registered.append((typ, inst))
class DummyPackage:
    def __init__(self, name):
        self.__name__ = name
class DummyLoader:
    _got_data = _is_package = None
    def get_data(self, path):
        self._got_data = path
        return b'DEADBEEF'
    def is_package(self, fullname):
        self._is_package = fullname
        return True
    def get_code(self, fullname):
        self._got_code = fullname
        return b'DEADBEEF'
    def get_source(self, fullname):
        self._got_source = fullname
        return 'def foo():\n    pass'
class DummyUnderOverride:
    def __call__(self, package, path, override_package, override_prefix,