Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implement PEP302 API in PackageOverrides as '__loader__'. #1015

Merged
merged 1 commit into from

3 participants

@tseaver
Owner

Proxy to the 'loader' set by the importer, if present. Otherwise,
raise NotImplementedError.

We cannot raise in the presence of an existing 'loader', because Python 3.3.x sets one for any module. So, we need to pretend to be one by proxy.

@tseaver tseaver Implement PEP302 API in PackageOverrides as '__loader__'.
Proxy to the '__loader__' set by the importer, if present.  Otherwise,
raise NotImplementedError.
bea48e2
@sontek

Was just working on porting an application over to 3.3 and this is holding us back. Think this will make it in to Pyramid soon?

@tseaver tseaver referenced this pull request in Pylons/substanced
Merged

Py3k compatibility #31

@mcdonc mcdonc merged commit bea48e2 into from
@tseaver tseaver referenced this pull request from a commit
@tseaver tseaver backport pull #1015 to 1.4 branch 8ebdc05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 9, 2013
  1. @tseaver

    Implement PEP302 API in PackageOverrides as '__loader__'.

    tseaver authored
    Proxy to the '__loader__' set by the importer, if present.  Otherwise,
    raise NotImplementedError.
This page is out of date. Refresh to see the latest.
View
5 CHANGES.txt
@@ -4,6 +4,11 @@ next release
Features
--------
+- 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``.
+
- ``ACLAuthorizationPolicy`` supports ``__acl__`` as a callable. This
removes the ambiguity between the potential ``AttributeError`` that would
be raised on the ``context`` when the property was not defined and the
View
38 pyramid/config/assets.py
@@ -81,14 +81,12 @@ def resource_listdir(self, resource_name):
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 @@ def listdir(self, resource_name):
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):
View
44 pyramid/interfaces.py
@@ -793,7 +793,49 @@ def __call__():
'See the "What\'s new In Pyramid 1.3" document for more details.'
)
-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.
View
96 pyramid/tests/test_config/test_assets.py
@@ -314,16 +314,40 @@ def _getTargetClass(self):
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 @@ def test_listdir_doesnt_exist(self):
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 __init__(self):
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,
Something went wrong with that request. Please try again.