Skip to content
This repository

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

Merged
merged 1 commit into from about 1 year ago

3 participants

Tres Seaver John Anderson Chris McDonough
Tres Seaver
Owner
tseaver commented

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.

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

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?

Tres Seaver tseaver referenced this pull request in Pylons/substanced
Merged

Py3k compatibility #31

Chris McDonough mcdonc merged commit bea48e2 into from
Chris McDonough mcdonc closed this
Tres Seaver tseaver referenced this pull request from a commit
Tres Seaver 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

Showing 1 unique commit by 1 author.

May 09, 2013
Tres Seaver tseaver Implement PEP302 API in PackageOverrides as '__loader__'.
Proxy to the '__loader__' set by the importer, if present.  Otherwise,
raise NotImplementedError.
bea48e2
This page is out of date. Refresh to see the latest.
5 CHANGES.txt
@@ -4,6 +4,11 @@ next release
4 4 Features
5 5 --------
6 6
  7 +- Make the ``pyramid.config.assets.PackageOverrides`` object implement the
  8 + API for ``__loader__`` objects specified in PEP 302. Proxies to the
  9 + ``__loader__`` set by the importer, if present; otherwise, raises
  10 + ``NotImplementedError``.
  11 +
7 12 - ``ACLAuthorizationPolicy`` supports ``__acl__`` as a callable. This
8 13 removes the ambiguity between the potential ``AttributeError`` that would
9 14 be raised on the ``context`` when the property was not defined and the
38 pyramid/config/assets.py
@@ -81,14 +81,12 @@ def resource_listdir(self, resource_name):
81 81 self, resource_name)
82 82
83 83 @implementer(IPackageOverrides)
84   -class PackageOverrides:
  84 +class PackageOverrides(object):
85 85 # pkg_resources arg in kw args below for testing
86 86 def __init__(self, package, pkg_resources=pkg_resources):
87   - if hasattr(package, '__loader__') and not isinstance(package.__loader__,
88   - self.__class__):
89   - raise TypeError('Package %s already has a non-%s __loader__ '
90   - '(probably a module in a zipped egg)' %
91   - (package, self.__class__))
  87 + loader = self._real_loader = getattr(package, '__loader__', None)
  88 + if isinstance(loader, self.__class__):
  89 + self._real_loader = None
92 90 # We register ourselves as a __loader__ *only* to support the
93 91 # setuptools _find_adapter adapter lookup; this class doesn't
94 92 # actually support the PEP 302 loader "API". This is
@@ -150,7 +148,33 @@ def listdir(self, resource_name):
150 148 for package, rname in self.search_path(resource_name):
151 149 if pkg_resources.resource_exists(package, rname):
152 150 return pkg_resources.resource_listdir(package, rname)
153   -
  151 +
  152 + @property
  153 + def real_loader(self):
  154 + if self._real_loader is None:
  155 + raise NotImplementedError()
  156 + return self._real_loader
  157 +
  158 + def get_data(self, path):
  159 + """ See IPEP302Loader.
  160 + """
  161 + return self.real_loader.get_data(path)
  162 +
  163 + def is_package(self, fullname):
  164 + """ See IPEP302Loader.
  165 + """
  166 + return self.real_loader.is_package(fullname)
  167 +
  168 + def get_code(self, fullname):
  169 + """ See IPEP302Loader.
  170 + """
  171 + return self.real_loader.get_code(fullname)
  172 +
  173 + def get_source(self, fullname):
  174 + """ See IPEP302Loader.
  175 + """
  176 + return self.real_loader.get_source(fullname)
  177 +
154 178
155 179 class DirectoryOverride:
156 180 def __init__(self, path, package, prefix):
44 pyramid/interfaces.py
@@ -793,7 +793,49 @@ def __call__():
793 793 'See the "What\'s new In Pyramid 1.3" document for more details.'
794 794 )
795 795
796   -class IPackageOverrides(Interface):
  796 +class IPEP302Loader(Interface):
  797 + """ See http://www.python.org/dev/peps/pep-0302/#id30.
  798 + """
  799 + def get_data(path):
  800 + """ Retrieve data for and arbitrary "files" from storage backend.
  801 +
  802 + Raise IOError for not found.
  803 +
  804 + Data is returned as bytes.
  805 + """
  806 +
  807 + def is_package(fullname):
  808 + """ Return True if the module specified by 'fullname' is a package.
  809 + """
  810 +
  811 + def get_code(fullname):
  812 + """ Return the code object for the module identified by 'fullname'.
  813 +
  814 + Return 'None' if it's a built-in or extension module.
  815 +
  816 + If the loader doesn't have the code object but it does have the source
  817 + code, return the compiled source code.
  818 +
  819 + Raise ImportError if the module can't be found by the importer at all.
  820 + """
  821 +
  822 + def get_source(fullname):
  823 + """ Return the source code for the module identified by 'fullname'.
  824 +
  825 + Return a string, using newline characters for line endings, or None
  826 + if the source is not available.
  827 +
  828 + Raise ImportError if the module can't be found by the importer at all.
  829 + """
  830 +
  831 + def get_filename(fullname):
  832 + """ Return the value of '__file__' if the named module was loaded.
  833 +
  834 + If the module is not found, raise ImportError.
  835 + """
  836 +
  837 +
  838 +class IPackageOverrides(IPEP302Loader):
797 839 """ Utility for pkg_resources overrides """
798 840
799 841 # VH_ROOT_KEY is an interface; its imported from other packages (e.g.
96 pyramid/tests/test_config/test_assets.py
@@ -314,16 +314,40 @@ def _getTargetClass(self):
314 314 from pyramid.config.assets import PackageOverrides
315 315 return PackageOverrides
316 316
317   - def _makeOne(self, package, pkg_resources=None):
  317 + def _makeOne(self, package=None, pkg_resources=None):
  318 + if package is None:
  319 + package = DummyPackage('package')
318 320 klass = self._getTargetClass()
319 321 if pkg_resources is None:
320 322 pkg_resources = DummyPkgResources()
321 323 return klass(package, pkg_resources=pkg_resources)
322 324
  325 + def test_class_conforms_to_IPackageOverrides(self):
  326 + from zope.interface.verify import verifyClass
  327 + from pyramid.interfaces import IPackageOverrides
  328 + verifyClass(IPackageOverrides, self._getTargetClass())
  329 +
  330 + def test_instance_conforms_to_IPackageOverrides(self):
  331 + from zope.interface.verify import verifyObject
  332 + from pyramid.interfaces import IPackageOverrides
  333 + verifyObject(IPackageOverrides, self._makeOne())
  334 +
  335 + def test_class_conforms_to_IPEP302Loader(self):
  336 + from zope.interface.verify import verifyClass
  337 + from pyramid.interfaces import IPEP302Loader
  338 + verifyClass(IPEP302Loader, self._getTargetClass())
  339 +
  340 + def test_instance_conforms_to_IPEP302Loader(self):
  341 + from zope.interface.verify import verifyObject
  342 + from pyramid.interfaces import IPEP302Loader
  343 + verifyObject(IPEP302Loader, self._makeOne())
  344 +
323 345 def test_ctor_package_already_has_loader_of_different_type(self):
324 346 package = DummyPackage('package')
325   - package.__loader__ = True
326   - self.assertRaises(TypeError, self._makeOne, package)
  347 + loader = package.__loader__ = DummyLoader()
  348 + po = self._makeOne(package)
  349 + self.assertTrue(package.__loader__ is po)
  350 + self.assertTrue(po.real_loader is loader)
327 351
328 352 def test_ctor_package_already_has_loader_of_same_type(self):
329 353 package = DummyPackage('package')
@@ -502,6 +526,55 @@ def test_listdir_doesnt_exist(self):
502 526 po.overrides= overrides
503 527 self.assertEqual(po.listdir('whatever'), None)
504 528
  529 + # PEP 302 __loader__ extensions: use the "real" __loader__, if present.
  530 + def test_get_data_pkg_has_no___loader__(self):
  531 + package = DummyPackage('package')
  532 + po = self._makeOne(package)
  533 + self.assertRaises(NotImplementedError, po.get_data, 'whatever')
  534 +
  535 + def test_get_data_pkg_has___loader__(self):
  536 + package = DummyPackage('package')
  537 + loader = package.__loader__ = DummyLoader()
  538 + po = self._makeOne(package)
  539 + self.assertEqual(po.get_data('whatever'), b'DEADBEEF')
  540 + self.assertEqual(loader._got_data, 'whatever')
  541 +
  542 + def test_is_package_pkg_has_no___loader__(self):
  543 + package = DummyPackage('package')
  544 + po = self._makeOne(package)
  545 + self.assertRaises(NotImplementedError, po.is_package, 'whatever')
  546 +
  547 + def test_is_package_pkg_has___loader__(self):
  548 + package = DummyPackage('package')
  549 + loader = package.__loader__ = DummyLoader()
  550 + po = self._makeOne(package)
  551 + self.assertTrue(po.is_package('whatever'))
  552 + self.assertEqual(loader._is_package, 'whatever')
  553 +
  554 + def test_get_code_pkg_has_no___loader__(self):
  555 + package = DummyPackage('package')
  556 + po = self._makeOne(package)
  557 + self.assertRaises(NotImplementedError, po.get_code, 'whatever')
  558 +
  559 + def test_get_code_pkg_has___loader__(self):
  560 + package = DummyPackage('package')
  561 + loader = package.__loader__ = DummyLoader()
  562 + po = self._makeOne(package)
  563 + self.assertEqual(po.get_code('whatever'), b'DEADBEEF')
  564 + self.assertEqual(loader._got_code, 'whatever')
  565 +
  566 + def test_get_source_pkg_has_no___loader__(self):
  567 + package = DummyPackage('package')
  568 + po = self._makeOne(package)
  569 + self.assertRaises(NotImplementedError, po.get_source, 'whatever')
  570 +
  571 + def test_get_source_pkg_has___loader__(self):
  572 + package = DummyPackage('package')
  573 + loader = package.__loader__ = DummyLoader()
  574 + po = self._makeOne(package)
  575 + self.assertEqual(po.get_source('whatever'), 'def foo():\n pass')
  576 + self.assertEqual(loader._got_source, 'whatever')
  577 +
505 578 class TestDirectoryOverride(unittest.TestCase):
506 579 def _getTargetClass(self):
507 580 from pyramid.config.assets import DirectoryOverride
@@ -570,10 +643,25 @@ def __init__(self):
570 643
571 644 def register_loader_type(self, typ, inst):
572 645 self.registered.append((typ, inst))
573   -
  646 +
574 647 class DummyPackage:
575 648 def __init__(self, name):
576 649 self.__name__ = name
  650 +
  651 +class DummyLoader:
  652 + _got_data = _is_package = None
  653 + def get_data(self, path):
  654 + self._got_data = path
  655 + return b'DEADBEEF'
  656 + def is_package(self, fullname):
  657 + self._is_package = fullname
  658 + return True
  659 + def get_code(self, fullname):
  660 + self._got_code = fullname
  661 + return b'DEADBEEF'
  662 + def get_source(self, fullname):
  663 + self._got_source = fullname
  664 + return 'def foo():\n pass'
577 665
578 666 class DummyUnderOverride:
579 667 def __call__(self, package, path, override_package, override_prefix,

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.