summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2025-12-27 10:44:10 +0100
committerDaniel Schadt <kingdread@gmx.de>2025-12-27 10:44:10 +0100
commit8b025bad44dcdf4d373b8202780fb1847d83240f (patch)
tree58772f360adb0e12077cecf5e80fcff9797190ed
parenteb0e01a0fd7aff84c345b1c43bd4fa9d2fe04441 (diff)
parentbb7833b040b3ca720467c4327cc618b8b107783f (diff)
downloadpyramid-8b025bad44dcdf4d373b8202780fb1847d83240f.tar.gz
pyramid-8b025bad44dcdf4d373b8202780fb1847d83240f.tar.bz2
pyramid-8b025bad44dcdf4d373b8202780fb1847d83240f.zip
Merge remote-tracking branch 'luhn/asset-overrides'
-rw-r--r--src/pyramid/asset.py5
-rw-r--r--src/pyramid/config/__init__.py3
-rw-r--r--src/pyramid/config/i18n.py2
-rw-r--r--src/pyramid/path.py376
-rw-r--r--src/pyramid/resolver.py330
-rw-r--r--src/pyramid/util.py2
-rw-r--r--tests/pkgs/assets/__init__.py0
-rw-r--r--tests/pkgs/assets/dir/fizz.txt1
-rw-r--r--tests/pkgs/assets/foo.txt1
-rw-r--r--tests/test_path.py319
-rw-r--r--tests/test_resolver.py312
11 files changed, 724 insertions, 627 deletions
diff --git a/src/pyramid/asset.py b/src/pyramid/asset.py
index bdf90ef5e..75c577e51 100644
--- a/src/pyramid/asset.py
+++ b/src/pyramid/asset.py
@@ -1,7 +1,6 @@
import os
-import pkg_resources
-from pyramid.path import package_name, package_path
+from pyramid.path import package_name, package_path, resource_filename
def resolve_asset_spec(spec, pname='__main__'):
@@ -40,4 +39,4 @@ def abspath_from_asset_spec(spec, pname='__main__'):
pname, filename = resolve_asset_spec(spec, pname)
if pname is None:
return filename
- return pkg_resources.resource_filename(pname, filename)
+ return resource_filename(pname, filename)
diff --git a/src/pyramid/config/__init__.py b/src/pyramid/config/__init__.py
index a49c15cba..1d48221d9 100644
--- a/src/pyramid/config/__init__.py
+++ b/src/pyramid/config/__init__.py
@@ -36,8 +36,9 @@ from pyramid.interfaces import (
IDebugLogger,
IExceptionResponse,
)
-from pyramid.path import DottedNameResolver, caller_package, package_of
+from pyramid.path import caller_package, package_of
from pyramid.registry import Introspectable, Introspector, Registry
+from pyramid.resolver import DottedNameResolver
from pyramid.router import Router
from pyramid.settings import aslist
from pyramid.threadlocal import manager
diff --git a/src/pyramid/config/i18n.py b/src/pyramid/config/i18n.py
index d70a8f25a..089834671 100644
--- a/src/pyramid/config/i18n.py
+++ b/src/pyramid/config/i18n.py
@@ -1,7 +1,7 @@
from pyramid.config.actions import action_method
from pyramid.exceptions import ConfigurationError
from pyramid.interfaces import ILocaleNegotiator, ITranslationDirectories
-from pyramid.path import AssetResolver
+from pyramid.resolver import AssetResolver
class I18NConfiguratorMixin:
diff --git a/src/pyramid/path.py b/src/pyramid/path.py
index 160d97abe..3bc87b5f3 100644
--- a/src/pyramid/path.py
+++ b/src/pyramid/path.py
@@ -1,6 +1,8 @@
+import atexit
+from contextlib import ExitStack
import functools
-from importlib import import_module
from importlib.machinery import SOURCE_SUFFIXES
+import importlib.resources
import os
import pkg_resources
import sys
@@ -11,6 +13,30 @@ from pyramid.interfaces import IAssetDescriptor
init_names = ['__init__%s' % x for x in SOURCE_SUFFIXES]
+@functools.lru_cache(maxsize=None)
+def resource_filename(package, name):
+ """
+ Return a filename on the filesystem for the given resource. If the
+ resource does not exist in the filesystem (e.g. in a zipped egg), it will
+ be extracted to a temporary directory and cleaned up when the application
+ exits.
+
+ This function is equivalent to the now-deprecated
+ ``pkg_resources.resource_filename``.
+
+ This function is only included in order to provide legacy functionality;
+ use should be avoided. Instead prefer to use ``importlib.resource`` APIs
+ directly.
+
+ """
+ ref = importlib.resources.files(package) / name
+
+ manager = ExitStack()
+ atexit.register(manager.close)
+ path = manager.enter_context(importlib.resources.as_file(ref))
+ return str(path)
+
+
def caller_path(path, level=2):
if not os.path.isabs(path):
module = caller_module(level + 1)
@@ -70,7 +96,7 @@ def package_path(package):
# the result
prefix = getattr(package, '__abspath__', None)
if prefix is None:
- prefix = pkg_resources.resource_filename(package.__name__, '')
+ prefix = resource_filename(package.__name__, '')
# pkg_resources doesn't care whether we feed it a package
# name or a module name within the package, the result
# will be the same: a directory name to the package itself
@@ -90,340 +116,56 @@ class _CALLER_PACKAGE:
CALLER_PACKAGE = _CALLER_PACKAGE()
-class Resolver:
- def __init__(self, package=CALLER_PACKAGE):
- if package in (None, CALLER_PACKAGE):
- self.package = package
- else:
- if isinstance(package, str):
- try:
- __import__(package)
- except ImportError:
- raise ValueError(
- f'The dotted name {package!r} cannot be imported'
- )
- package = sys.modules[package]
- self.package = package_of(package)
-
- def get_package_name(self):
- if self.package is CALLER_PACKAGE:
- package_name = caller_package().__name__
- else:
- package_name = self.package.__name__
- return package_name
-
- def get_package(self):
- if self.package is CALLER_PACKAGE:
- package = caller_package()
- else:
- package = self.package
- return package
-
-
-class AssetResolver(Resolver):
- """A class used to resolve an :term:`asset specification` to an
- :term:`asset descriptor`.
-
- .. versionadded:: 1.3
-
- The constructor accepts a single argument named ``package`` which may be
- any of:
-
- - A fully qualified (not relative) dotted name to a module or package
-
- - a Python module or package object
-
- - The value ``None``
-
- - The constant value :attr:`pyramid.path.CALLER_PACKAGE`.
-
- The default value is :attr:`pyramid.path.CALLER_PACKAGE`.
-
- The ``package`` is used when a relative asset specification is supplied
- to the :meth:`~pyramid.path.AssetResolver.resolve` method. An asset
- specification without a colon in it is treated as relative.
-
- If ``package`` is ``None``, the resolver will
- only be able to resolve fully qualified (not relative) asset
- specifications. Any attempt to resolve a relative asset specification
- will result in an :exc:`ValueError` exception.
-
- If ``package`` is :attr:`pyramid.path.CALLER_PACKAGE`,
- the resolver will treat relative asset specifications as
- relative to the caller of the :meth:`~pyramid.path.AssetResolver.resolve`
- method.
-
- If ``package`` is a *module* or *module name* (as opposed to a package or
- package name), its containing package is computed and this
- package is used to derive the package name (all names are resolved relative
- to packages, never to modules). For example, if the ``package`` argument
- to this type was passed the string ``xml.dom.expatbuilder``, and
- ``template.pt`` is supplied to the
- :meth:`~pyramid.path.AssetResolver.resolve` method, the resulting absolute
- asset spec would be ``xml.minidom:template.pt``, because
- ``xml.dom.expatbuilder`` is a module object, not a package object.
-
- If ``package`` is a *package* or *package name* (as opposed to a module or
- module name), this package will be used to compute relative
- asset specifications. For example, if the ``package`` argument to this
- type was passed the string ``xml.dom``, and ``template.pt`` is supplied
- to the :meth:`~pyramid.path.AssetResolver.resolve` method, the resulting
- absolute asset spec would be ``xml.minidom:template.pt``.
- """
-
- def resolve(self, spec):
- """
- Resolve the asset spec named as ``spec`` to an object that has the
- attributes and methods described in
- :class:`pyramid.interfaces.IAssetDescriptor`.
-
- If ``spec`` is an absolute filename
- (e.g. ``/path/to/myproject/templates/foo.pt``) or an absolute asset
- spec (e.g. ``myproject:templates.foo.pt``), an asset descriptor is
- returned without taking into account the ``package`` passed to this
- class' constructor.
-
- If ``spec`` is a *relative* asset specification (an asset
- specification without a ``:`` in it, e.g. ``templates/foo.pt``), the
- ``package`` argument of the constructor is used as the package
- portion of the asset spec. For example:
-
- .. code-block:: python
-
- a = AssetResolver('myproject')
- resolver = a.resolve('templates/foo.pt')
- print(resolver.abspath())
- # -> /path/to/myproject/templates/foo.pt
-
- If the AssetResolver is constructed without a ``package`` argument of
- ``None``, and a relative asset specification is passed to
- ``resolve``, an :exc:`ValueError` exception is raised.
- """
- if os.path.isabs(spec):
- return FSAssetDescriptor(spec)
- path = spec
- if ':' in path:
- package_name, path = spec.split(':', 1)
- else:
- if self.package is CALLER_PACKAGE:
- package_name = caller_package().__name__
- else:
- package_name = getattr(self.package, '__name__', None)
- if package_name is None:
- raise ValueError(
- f'relative spec {spec!r} irresolveable without package'
- )
- return PkgResourcesAssetDescriptor(package_name, path)
-
-
-class DottedNameResolver(Resolver):
- """A class used to resolve a :term:`dotted Python name` to a package or
- module object.
-
- .. versionadded:: 1.3
-
- The constructor accepts a single argument named ``package`` which may be
- any of:
-
- - A fully qualified (not relative) dotted name to a module or package
-
- - a Python module or package object
-
- - The value ``None``
-
- - The constant value :attr:`pyramid.path.CALLER_PACKAGE`.
-
- The default value is :attr:`pyramid.path.CALLER_PACKAGE`.
-
- The ``package`` is used when a relative dotted name is supplied to the
- :meth:`~pyramid.path.DottedNameResolver.resolve` method. A dotted name
- which has a ``.`` (dot) or ``:`` (colon) as its first character is
- treated as relative.
-
- If ``package`` is ``None``, the resolver will only be able to resolve
- fully qualified (not relative) names. Any attempt to resolve a
- relative name will result in an :exc:`ValueError` exception.
-
- If ``package`` is :attr:`pyramid.path.CALLER_PACKAGE`,
- the resolver will treat relative dotted names as relative to
- the caller of the :meth:`~pyramid.path.DottedNameResolver.resolve`
- method.
-
- If ``package`` is a *module* or *module name* (as opposed to a package or
- package name), its containing package is computed and this
- package used to derive the package name (all names are resolved relative
- to packages, never to modules). For example, if the ``package`` argument
- to this type was passed the string ``xml.dom.expatbuilder``, and
- ``.mindom`` is supplied to the
- :meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
- import would be for ``xml.minidom``, because ``xml.dom.expatbuilder`` is
- a module object, not a package object.
-
- If ``package`` is a *package* or *package name* (as opposed to a module or
- module name), this package will be used to relative compute
- dotted names. For example, if the ``package`` argument to this type was
- passed the string ``xml.dom``, and ``.minidom`` is supplied to the
- :meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
- import would be for ``xml.minidom``.
- """
-
- def resolve(self, dotted):
- """
- This method resolves a dotted name reference to a global Python
- object (an object which can be imported) to the object itself.
-
- Two dotted name styles are supported:
-
- - ``pkg_resources``-style dotted names where non-module attributes
- of a package are separated from the rest of the path using a ``:``
- e.g. ``package.module:attr``.
-
- - ``zope.dottedname``-style dotted names where non-module
- attributes of a package are separated from the rest of the path
- using a ``.`` e.g. ``package.module.attr``.
-
- These styles can be used interchangeably. If the supplied name
- contains a ``:`` (colon), the ``pkg_resources`` resolution
- mechanism will be chosen, otherwise the ``zope.dottedname``
- resolution mechanism will be chosen.
-
- If the ``dotted`` argument passed to this method is not a string, a
- :exc:`ValueError` will be raised.
-
- When a dotted name cannot be resolved, a :exc:`ValueError` error is
- raised.
-
- Example:
-
- .. code-block:: python
-
- r = DottedNameResolver()
- v = r.resolve('xml') # v is the xml module
-
- """
- if not isinstance(dotted, str):
- raise ValueError(f'{dotted!r} is not a string')
- package = self.package
- if package is CALLER_PACKAGE:
- package = caller_package()
- return self._resolve(dotted, package)
-
- def maybe_resolve(self, dotted):
- """
- This method behaves just like
- :meth:`~pyramid.path.DottedNameResolver.resolve`, except if the
- ``dotted`` value passed is not a string, it is simply returned. For
- example:
-
- .. code-block:: python
-
- import xml
- r = DottedNameResolver()
- v = r.maybe_resolve(xml)
- # v is the xml module; no exception raised
- """
- if isinstance(dotted, str):
- package = self.package
- if package is CALLER_PACKAGE:
- package = caller_package()
- return self._resolve(dotted, package)
- return dotted
-
- def _resolve(self, dotted, package):
- if ':' in dotted:
- return self._pkg_resources_style(dotted, package)
- else:
- return self._zope_dottedname_style(dotted, package)
-
- def _pkg_resources_style(self, value, package):
- """package.module:attr style"""
- if value.startswith(('.', ':')):
- if not package:
- raise ValueError(
- f'relative name {value!r} irresolveable without package'
- )
- if value in ['.', ':']:
- value = package.__name__
- else:
- value = package.__name__ + value
- # logic below is similar to importlib.metadata.EntryPoint.load()
- module = value
- attrs = []
- parts = value.split(':', 1)
- if len(parts) == 2:
- module, attrs = parts
- attrs = attrs.split('.')
- module = import_module(module)
- try:
- return functools.reduce(getattr, attrs, module)
- except AttributeError as ex:
- raise ImportError(str(ex))
-
- def _zope_dottedname_style(self, value, package):
- """package.module.attr style"""
- module = getattr(package, '__name__', None) # package may be None
- if not module:
- module = None
- if value == '.':
- if module is None:
- raise ValueError(
- f'relative name {value!r} irresolveable without package'
- )
- name = module.split('.')
- else:
- name = value.split('.')
- if not name[0]:
- if module is None:
- raise ValueError(
- 'relative name %r irresolveable without '
- 'package' % (value,)
- )
- module = module.split('.')
- name.pop(0)
- while not name[0]:
- module.pop()
- name.pop(0)
- name = module + name
-
- used = name.pop(0)
- found = __import__(used)
- for n in name:
- used += '.' + n
- try:
- found = getattr(found, n)
- except AttributeError:
- __import__(used)
- found = getattr(found, n) # pragma: no cover
-
- return found
-
-
@implementer(IAssetDescriptor)
class PkgResourcesAssetDescriptor:
pkg_resources = pkg_resources
- def __init__(self, pkg_name, path):
+ def __init__(self, pkg_name, path, overrides=None):
self.pkg_name = pkg_name
self.path = path
+ self.overrides = overrides
def absspec(self):
return f'{self.pkg_name}:{self.path}'
def abspath(self):
- return os.path.abspath(
- self.pkg_resources.resource_filename(self.pkg_name, self.path)
- )
+ if self.overrides is not None:
+ filename = self.overrides.get_filename(self.path)
+ else:
+ filename = None
+ if filename is None:
+ filename = self.pkg_resources.resource_filename(
+ self.pkg_name,
+ self.path,
+ )
+ return os.path.abspath(filename)
def stream(self):
+ if self.overrides is not None:
+ stream = self.overrides.get_stream(self.path)
+ if stream is not None:
+ return stream
return self.pkg_resources.resource_stream(self.pkg_name, self.path)
def isdir(self):
+ if self.overrides is not None:
+ result = self.overrides.isdir(self.path)
+ if result is not None:
+ return result
return self.pkg_resources.resource_isdir(self.pkg_name, self.path)
def listdir(self):
+ if self.overrides is not None:
+ result = self.overrides.listdir(self.path)
+ if result is not None:
+ return result
return self.pkg_resources.resource_listdir(self.pkg_name, self.path)
def exists(self):
+ if self.overrides is not None:
+ result = self.overrides.exists(self.path)
+ if result is not None:
+ return result
return self.pkg_resources.resource_exists(self.pkg_name, self.path)
@@ -449,3 +191,9 @@ class FSAssetDescriptor:
def exists(self):
return os.path.exists(self.path)
+
+
+# We're importing these classes for backwards compatibility, because these used
+# to exist in `pyramid.path`.
+# This must be at the bottom of the file to avoid a circular import.
+from .resolver import AssetResolver, DottedNameResolver, Resolver # noqa
diff --git a/src/pyramid/resolver.py b/src/pyramid/resolver.py
new file mode 100644
index 000000000..6792540f4
--- /dev/null
+++ b/src/pyramid/resolver.py
@@ -0,0 +1,330 @@
+import functools
+from importlib import import_module
+import os
+import sys
+
+from pyramid.interfaces import IPackageOverrides
+
+from .path import (
+ CALLER_PACKAGE,
+ FSAssetDescriptor,
+ PkgResourcesAssetDescriptor,
+ caller_package,
+ package_of,
+)
+from .threadlocal import get_current_registry
+
+
+class Resolver:
+ def __init__(self, package=CALLER_PACKAGE):
+ if package in (None, CALLER_PACKAGE):
+ self.package = package
+ else:
+ if isinstance(package, str):
+ try:
+ __import__(package)
+ except ImportError:
+ raise ValueError(
+ f'The dotted name {package!r} cannot be imported'
+ )
+ package = sys.modules[package]
+ self.package = package_of(package)
+
+ def get_package_name(self):
+ if self.package is CALLER_PACKAGE:
+ package_name = caller_package().__name__
+ else:
+ package_name = self.package.__name__
+ return package_name
+
+ def get_package(self):
+ if self.package is CALLER_PACKAGE:
+ package = caller_package()
+ else:
+ package = self.package
+ return package
+
+
+class AssetResolver(Resolver):
+ """A class used to resolve an :term:`asset specification` to an
+ :term:`asset descriptor`.
+
+ .. versionadded:: 1.3
+
+ The constructor accepts a single argument named ``package`` which may be
+ any of:
+
+ - A fully qualified (not relative) dotted name to a module or package
+
+ - a Python module or package object
+
+ - The value ``None``
+
+ - The constant value :attr:`pyramid.path.CALLER_PACKAGE`.
+
+ The default value is :attr:`pyramid.path.CALLER_PACKAGE`.
+
+ The ``package`` is used when a relative asset specification is supplied
+ to the :meth:`~pyramid.path.AssetResolver.resolve` method. An asset
+ specification without a colon in it is treated as relative.
+
+ If ``package`` is ``None``, the resolver will
+ only be able to resolve fully qualified (not relative) asset
+ specifications. Any attempt to resolve a relative asset specification
+ will result in an :exc:`ValueError` exception.
+
+ If ``package`` is :attr:`pyramid.path.CALLER_PACKAGE`,
+ the resolver will treat relative asset specifications as
+ relative to the caller of the :meth:`~pyramid.path.AssetResolver.resolve`
+ method.
+
+ If ``package`` is a *module* or *module name* (as opposed to a package or
+ package name), its containing package is computed and this
+ package is used to derive the package name (all names are resolved relative
+ to packages, never to modules). For example, if the ``package`` argument
+ to this type was passed the string ``xml.dom.expatbuilder``, and
+ ``template.pt`` is supplied to the
+ :meth:`~pyramid.path.AssetResolver.resolve` method, the resulting absolute
+ asset spec would be ``xml.minidom:template.pt``, because
+ ``xml.dom.expatbuilder`` is a module object, not a package object.
+
+ If ``package`` is a *package* or *package name* (as opposed to a module or
+ module name), this package will be used to compute relative
+ asset specifications. For example, if the ``package`` argument to this
+ type was passed the string ``xml.dom``, and ``template.pt`` is supplied
+ to the :meth:`~pyramid.path.AssetResolver.resolve` method, the resulting
+ absolute asset spec would be ``xml.minidom:template.pt``.
+ """
+
+ def __init__(self, package=CALLER_PACKAGE, registry=None):
+ self.registry = registry
+ super().__init__(package=package)
+
+ def resolve(self, spec):
+ """
+ Resolve the asset spec named as ``spec`` to an object that has the
+ attributes and methods described in
+ :class:`pyramid.interfaces.IAssetDescriptor`.
+
+ If ``spec`` is an absolute filename
+ (e.g. ``/path/to/myproject/templates/foo.pt``) or an absolute asset
+ spec (e.g. ``myproject:templates.foo.pt``), an asset descriptor is
+ returned without taking into account the ``package`` passed to this
+ class' constructor.
+
+ If ``spec`` is a *relative* asset specification (an asset
+ specification without a ``:`` in it, e.g. ``templates/foo.pt``), the
+ ``package`` argument of the constructor is used as the package
+ portion of the asset spec. For example:
+
+ .. code-block:: python
+
+ a = AssetResolver('myproject')
+ resolver = a.resolve('templates/foo.pt')
+ print(resolver.abspath())
+ # -> /path/to/myproject/templates/foo.pt
+
+ If the AssetResolver is constructed without a ``package`` argument of
+ ``None``, and a relative asset specification is passed to
+ ``resolve``, an :exc:`ValueError` exception is raised.
+ """
+ if os.path.isabs(spec):
+ return FSAssetDescriptor(spec)
+ path = spec
+ if ':' in path:
+ package_name, path = spec.split(':', 1)
+ else:
+ if self.package is CALLER_PACKAGE:
+ package_name = caller_package().__name__
+ else:
+ package_name = getattr(self.package, '__name__', None)
+ if package_name is None:
+ raise ValueError(
+ f'relative spec {spec!r} irresolveable without package'
+ )
+
+ registry = self.registry or get_current_registry()
+ overrides = registry.queryUtility(IPackageOverrides, package_name)
+ return PkgResourcesAssetDescriptor(package_name, path, overrides)
+
+
+class DottedNameResolver(Resolver):
+ """A class used to resolve a :term:`dotted Python name` to a package or
+ module object.
+
+ .. versionadded:: 1.3
+
+ The constructor accepts a single argument named ``package`` which may be
+ any of:
+
+ - A fully qualified (not relative) dotted name to a module or package
+
+ - a Python module or package object
+
+ - The value ``None``
+
+ - The constant value :attr:`pyramid.path.CALLER_PACKAGE`.
+
+ The default value is :attr:`pyramid.path.CALLER_PACKAGE`.
+
+ The ``package`` is used when a relative dotted name is supplied to the
+ :meth:`~pyramid.path.DottedNameResolver.resolve` method. A dotted name
+ which has a ``.`` (dot) or ``:`` (colon) as its first character is
+ treated as relative.
+
+ If ``package`` is ``None``, the resolver will only be able to resolve
+ fully qualified (not relative) names. Any attempt to resolve a
+ relative name will result in an :exc:`ValueError` exception.
+
+ If ``package`` is :attr:`pyramid.path.CALLER_PACKAGE`,
+ the resolver will treat relative dotted names as relative to
+ the caller of the :meth:`~pyramid.path.DottedNameResolver.resolve`
+ method.
+
+ If ``package`` is a *module* or *module name* (as opposed to a package or
+ package name), its containing package is computed and this
+ package used to derive the package name (all names are resolved relative
+ to packages, never to modules). For example, if the ``package`` argument
+ to this type was passed the string ``xml.dom.expatbuilder``, and
+ ``.mindom`` is supplied to the
+ :meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
+ import would be for ``xml.minidom``, because ``xml.dom.expatbuilder`` is
+ a module object, not a package object.
+
+ If ``package`` is a *package* or *package name* (as opposed to a module or
+ module name), this package will be used to relative compute
+ dotted names. For example, if the ``package`` argument to this type was
+ passed the string ``xml.dom``, and ``.minidom`` is supplied to the
+ :meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
+ import would be for ``xml.minidom``.
+ """
+
+ def resolve(self, dotted):
+ """
+ This method resolves a dotted name reference to a global Python
+ object (an object which can be imported) to the object itself.
+
+ Two dotted name styles are supported:
+
+ - ``pkg_resources``-style dotted names where non-module attributes
+ of a package are separated from the rest of the path using a ``:``
+ e.g. ``package.module:attr``.
+
+ - ``zope.dottedname``-style dotted names where non-module
+ attributes of a package are separated from the rest of the path
+ using a ``.`` e.g. ``package.module.attr``.
+
+ These styles can be used interchangeably. If the supplied name
+ contains a ``:`` (colon), the ``pkg_resources`` resolution
+ mechanism will be chosen, otherwise the ``zope.dottedname``
+ resolution mechanism will be chosen.
+
+ If the ``dotted`` argument passed to this method is not a string, a
+ :exc:`ValueError` will be raised.
+
+ When a dotted name cannot be resolved, a :exc:`ValueError` error is
+ raised.
+
+ Example:
+
+ .. code-block:: python
+
+ r = DottedNameResolver()
+ v = r.resolve('xml') # v is the xml module
+
+ """
+ if not isinstance(dotted, str):
+ raise ValueError(f'{dotted!r} is not a string')
+ package = self.package
+ if package is CALLER_PACKAGE:
+ package = caller_package()
+ return self._resolve(dotted, package)
+
+ def maybe_resolve(self, dotted):
+ """
+ This method behaves just like
+ :meth:`~pyramid.path.DottedNameResolver.resolve`, except if the
+ ``dotted`` value passed is not a string, it is simply returned. For
+ example:
+
+ .. code-block:: python
+
+ import xml
+ r = DottedNameResolver()
+ v = r.maybe_resolve(xml)
+ # v is the xml module; no exception raised
+ """
+ if isinstance(dotted, str):
+ package = self.package
+ if package is CALLER_PACKAGE:
+ package = caller_package()
+ return self._resolve(dotted, package)
+ return dotted
+
+ def _resolve(self, dotted, package):
+ if ':' in dotted:
+ return self._pkg_resources_style(dotted, package)
+ else:
+ return self._zope_dottedname_style(dotted, package)
+
+ def _pkg_resources_style(self, value, package):
+ """package.module:attr style"""
+ if value.startswith(('.', ':')):
+ if not package:
+ raise ValueError(
+ f'relative name {value!r} irresolveable without package'
+ )
+ if value in ['.', ':']:
+ value = package.__name__
+ else:
+ value = package.__name__ + value
+ # logic below is similar to importlib.metadata.EntryPoint.load()
+ module = value
+ attrs = []
+ parts = value.split(':', 1)
+ if len(parts) == 2:
+ module, attrs = parts
+ attrs = attrs.split('.')
+ module = import_module(module)
+ try:
+ return functools.reduce(getattr, attrs, module)
+ except AttributeError as ex:
+ raise ImportError(str(ex))
+
+ def _zope_dottedname_style(self, value, package):
+ """package.module.attr style"""
+ module = getattr(package, '__name__', None) # package may be None
+ if not module:
+ module = None
+ if value == '.':
+ if module is None:
+ raise ValueError(
+ f'relative name {value!r} irresolveable without package'
+ )
+ name = module.split('.')
+ else:
+ name = value.split('.')
+ if not name[0]:
+ if module is None:
+ raise ValueError(
+ 'relative name %r irresolveable without '
+ 'package' % (value,)
+ )
+ module = module.split('.')
+ name.pop(0)
+ while not name[0]:
+ module.pop()
+ name.pop(0)
+ name = module + name
+
+ used = name.pop(0)
+ found = __import__(used)
+ for n in name:
+ used += '.' + n
+ try:
+ found = getattr(found, n)
+ except AttributeError:
+ __import__(used)
+ found = getattr(found, n) # pragma: no cover
+
+ return found
diff --git a/src/pyramid/util.py b/src/pyramid/util.py
index c71528a49..5a7169ef7 100644
--- a/src/pyramid/util.py
+++ b/src/pyramid/util.py
@@ -5,7 +5,7 @@ import inspect
import platform
import weakref
-from pyramid.path import DottedNameResolver as _DottedNameResolver
+from pyramid.resolver import DottedNameResolver as _DottedNameResolver
_marker = object()
diff --git a/tests/pkgs/assets/__init__.py b/tests/pkgs/assets/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/pkgs/assets/__init__.py
diff --git a/tests/pkgs/assets/dir/fizz.txt b/tests/pkgs/assets/dir/fizz.txt
new file mode 100644
index 000000000..99f1ea4a2
--- /dev/null
+++ b/tests/pkgs/assets/dir/fizz.txt
@@ -0,0 +1 @@
+buzz
diff --git a/tests/pkgs/assets/foo.txt b/tests/pkgs/assets/foo.txt
new file mode 100644
index 000000000..5716ca598
--- /dev/null
+++ b/tests/pkgs/assets/foo.txt
@@ -0,0 +1 @@
+bar
diff --git a/tests/test_path.py b/tests/test_path.py
index 6f12303f7..f93b0ae90 100644
--- a/tests/test_path.py
+++ b/tests/test_path.py
@@ -4,6 +4,18 @@ import unittest
here = os.path.abspath(os.path.dirname(__file__))
+class TestResourceFilename(unittest.TestCase):
+ def _callFUT(self, package, name):
+ from pyramid.path import resource_filename
+
+ return resource_filename(package, name)
+
+ def test_returns_path(self):
+ path = self._callFUT('tests.pkgs.assets', 'foo.txt')
+ expected = os.path.join(here, 'pkgs/assets/foo.txt')
+ self.assertEqual(path, expected)
+
+
class TestCallerPath(unittest.TestCase):
def tearDown(self):
from . import test_path
@@ -214,97 +226,6 @@ class TestPackageName(unittest.TestCase):
self.assertEqual(result, '__main__')
-class TestResolver(unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.path import Resolver
-
- return Resolver
-
- def _makeOne(self, package):
- return self._getTargetClass()(package)
-
- def test_get_package_caller_package(self):
- from pyramid.path import CALLER_PACKAGE
- import tests
-
- self.assertEqual(self._makeOne(CALLER_PACKAGE).get_package(), tests)
-
- def test_get_package_name_caller_package(self):
- from pyramid.path import CALLER_PACKAGE
-
- self.assertEqual(
- self._makeOne(CALLER_PACKAGE).get_package_name(), 'tests'
- )
-
- def test_get_package_string(self):
- import tests
-
- self.assertEqual(self._makeOne('tests').get_package(), tests)
-
- def test_get_package_name_string(self):
- self.assertEqual(self._makeOne('tests').get_package_name(), 'tests')
-
-
-class TestAssetResolver(unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.path import AssetResolver
-
- return AssetResolver
-
- def _makeOne(self, package='tests'):
- return self._getTargetClass()(package)
-
- def test_ctor_as_package(self):
- import sys
-
- tests = sys.modules['tests']
- inst = self._makeOne(tests)
- self.assertEqual(inst.package, tests)
-
- def test_ctor_as_str(self):
- import sys
-
- tests = sys.modules['tests']
- inst = self._makeOne('tests')
- self.assertEqual(inst.package, tests)
-
- def test_resolve_abspath(self):
- from pyramid.path import FSAssetDescriptor
-
- inst = self._makeOne(None)
- r = inst.resolve(os.path.join(here, 'test_asset.py'))
- self.assertEqual(r.__class__, FSAssetDescriptor)
- self.assertTrue(r.exists())
-
- def test_resolve_absspec(self):
- from pyramid.path import PkgResourcesAssetDescriptor
-
- inst = self._makeOne(None)
- r = inst.resolve('tests:test_asset.py')
- self.assertEqual(r.__class__, PkgResourcesAssetDescriptor)
- self.assertTrue(r.exists())
-
- def test_resolve_relspec_with_pkg(self):
- from pyramid.path import PkgResourcesAssetDescriptor
-
- inst = self._makeOne('tests')
- r = inst.resolve('test_asset.py')
- self.assertEqual(r.__class__, PkgResourcesAssetDescriptor)
- self.assertTrue(r.exists())
-
- def test_resolve_relspec_no_package(self):
- inst = self._makeOne(None)
- self.assertRaises(ValueError, inst.resolve, 'test_asset.py')
-
- def test_resolve_relspec_caller_package(self):
- from pyramid.path import CALLER_PACKAGE, PkgResourcesAssetDescriptor
-
- inst = self._makeOne(CALLER_PACKAGE)
- r = inst.resolve('test_asset.py')
- self.assertEqual(r.__class__, PkgResourcesAssetDescriptor)
- self.assertTrue(r.exists())
-
-
class TestPkgResourcesAssetDescriptor(unittest.TestCase):
def _getTargetClass(self):
from pyramid.path import PkgResourcesAssetDescriptor
@@ -423,222 +344,6 @@ class TestFSAssetDescriptor(unittest.TestCase):
self.assertTrue(inst.exists())
-class TestDottedNameResolver(unittest.TestCase):
- def _makeOne(self, package=None):
- from pyramid.path import DottedNameResolver
-
- return DottedNameResolver(package)
-
- def config_exc(self, func, *arg, **kw):
- try:
- func(*arg, **kw)
- except ValueError as e:
- return e
- else:
- raise AssertionError('Invalid not raised') # pragma: no cover
-
- def test_zope_dottedname_style_resolve_builtin(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style('builtins.str', None)
- self.assertEqual(result, str)
-
- def test_zope_dottedname_style_resolve_absolute(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- 'tests.test_path.TestDottedNameResolver', None
- )
- self.assertEqual(result, self.__class__)
-
- def test_zope_dottedname_style_irrresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(
- ImportError,
- typ._zope_dottedname_style,
- 'pyramid.test_path.nonexisting_name',
- None,
- )
-
- def test__zope_dottedname_style_resolve_relative(self):
- import tests
-
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- '.test_path.TestDottedNameResolver', tests
- )
- self.assertEqual(result, self.__class__)
-
- def test__zope_dottedname_style_resolve_relative_leading_dots(self):
- import tests.test_path
-
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- '..tests.test_path.TestDottedNameResolver', tests
- )
- self.assertEqual(result, self.__class__)
-
- def test__zope_dottedname_style_resolve_relative_is_dot(self):
- import tests
-
- typ = self._makeOne()
- result = typ._zope_dottedname_style('.', tests)
- self.assertEqual(result, tests)
-
- def test__zope_dottedname_style_irresolveable_relative_is_dot(self):
- typ = self._makeOne()
- e = self.config_exc(typ._zope_dottedname_style, '.', None)
- self.assertEqual(
- e.args[0], "relative name '.' irresolveable without package"
- )
-
- def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
- typ = self._makeOne()
- e = self.config_exc(typ._zope_dottedname_style, '.whatever', None)
- self.assertEqual(
- e.args[0],
- "relative name '.whatever' irresolveable without package",
- )
-
- def test_zope_dottedname_style_irrresolveable_relative(self):
- import tests
-
- typ = self._makeOne()
- self.assertRaises(
- ImportError, typ._zope_dottedname_style, '.notexisting', tests
- )
-
- def test__zope_dottedname_style_resolveable_relative(self):
- import tests
-
- typ = self._makeOne()
- result = typ._zope_dottedname_style('.', tests)
- self.assertEqual(result, tests)
-
- def test__zope_dottedname_style_irresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(
- ImportError, typ._zope_dottedname_style, 'pyramid.fudge.bar', None
- )
-
- def test__zope_dottedname_style_resolveable_absolute(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- 'tests.test_path.TestDottedNameResolver', None
- )
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_absolute(self):
- typ = self._makeOne()
- result = typ._pkg_resources_style(
- 'tests.test_path:TestDottedNameResolver', None
- )
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_irrresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(
- ImportError, typ._pkg_resources_style, 'tests:nonexisting', None
- )
-
- def test__pkg_resources_style_resolve_relative(self):
- import tests
-
- typ = self._makeOne()
- result = typ._pkg_resources_style(
- '.test_path:TestDottedNameResolver', tests
- )
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_relative_is_dot(self):
- import tests
-
- typ = self._makeOne()
- result = typ._pkg_resources_style('.', tests)
- self.assertEqual(result, tests)
-
- def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
- typ = self._makeOne()
- self.assertRaises(
- ValueError, typ._pkg_resources_style, '.whatever', None
- )
-
- def test__pkg_resources_style_irrresolveable_relative(self):
- import pyramid
-
- typ = self._makeOne()
- self.assertRaises(
- ImportError, typ._pkg_resources_style, ':notexisting', pyramid
- )
-
- def test_resolve_not_a_string(self):
- typ = self._makeOne()
- e = self.config_exc(typ.resolve, None)
- self.assertEqual(e.args[0], 'None is not a string')
-
- def test_resolve_using_pkgresources_style(self):
- typ = self._makeOne()
- result = typ.resolve('tests.test_path:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_resolve_using_zope_dottedname_style(self):
- typ = self._makeOne()
- result = typ.resolve('tests.test_path:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_resolve_missing_raises(self):
- typ = self._makeOne()
- self.assertRaises(ImportError, typ.resolve, 'cant.be.found')
-
- def test_resolve_caller_package(self):
- from pyramid.path import CALLER_PACKAGE
-
- typ = self._makeOne(CALLER_PACKAGE)
- self.assertEqual(
- typ.resolve('.test_path.TestDottedNameResolver'), self.__class__
- )
-
- def test_maybe_resolve_caller_package(self):
- from pyramid.path import CALLER_PACKAGE
-
- typ = self._makeOne(CALLER_PACKAGE)
- self.assertEqual(
- typ.maybe_resolve('.test_path.TestDottedNameResolver'),
- self.__class__,
- )
-
- def test_ctor_string_module_resolveable(self):
- import tests
-
- typ = self._makeOne('tests.test_path')
- self.assertEqual(typ.package, tests)
-
- def test_ctor_string_package_resolveable(self):
- import tests
-
- typ = self._makeOne('tests')
- self.assertEqual(typ.package, tests)
-
- def test_ctor_string_irresolveable(self):
- self.assertRaises(ValueError, self._makeOne, 'cant.be.found')
-
- def test_ctor_module(self):
- import tests
-
- from . import test_path
-
- typ = self._makeOne(test_path)
- self.assertEqual(typ.package, tests)
-
- def test_ctor_package(self):
- import tests
-
- typ = self._makeOne(tests)
- self.assertEqual(typ.package, tests)
-
- def test_ctor_None(self):
- typ = self._makeOne(None)
- self.assertEqual(typ.package, None)
-
-
class DummyPkgResource:
pass
diff --git a/tests/test_resolver.py b/tests/test_resolver.py
new file mode 100644
index 000000000..77800a74a
--- /dev/null
+++ b/tests/test_resolver.py
@@ -0,0 +1,312 @@
+import os
+import unittest
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestResolver(unittest.TestCase):
+ def _getTargetClass(self):
+ from pyramid.resolver import Resolver
+
+ return Resolver
+
+ def _makeOne(self, package):
+ return self._getTargetClass()(package)
+
+ def test_get_package_caller_package(self):
+ from pyramid.path import CALLER_PACKAGE
+ import tests
+
+ self.assertEqual(self._makeOne(CALLER_PACKAGE).get_package(), tests)
+
+ def test_get_package_name_caller_package(self):
+ from pyramid.path import CALLER_PACKAGE
+
+ self.assertEqual(
+ self._makeOne(CALLER_PACKAGE).get_package_name(), 'tests'
+ )
+
+ def test_get_package_string(self):
+ import tests
+
+ self.assertEqual(self._makeOne('tests').get_package(), tests)
+
+ def test_get_package_name_string(self):
+ self.assertEqual(self._makeOne('tests').get_package_name(), 'tests')
+
+
+class TestAssetResolver(unittest.TestCase):
+ def _getTargetClass(self):
+ from pyramid.resolver import AssetResolver
+
+ return AssetResolver
+
+ def _makeOne(self, package='tests'):
+ return self._getTargetClass()(package)
+
+ def test_ctor_as_package(self):
+ import sys
+
+ tests = sys.modules['tests']
+ inst = self._makeOne(tests)
+ self.assertEqual(inst.package, tests)
+
+ def test_ctor_as_str(self):
+ import sys
+
+ tests = sys.modules['tests']
+ inst = self._makeOne('tests')
+ self.assertEqual(inst.package, tests)
+
+ def test_resolve_abspath(self):
+ from pyramid.path import FSAssetDescriptor
+
+ inst = self._makeOne(None)
+ r = inst.resolve(os.path.join(here, 'test_asset.py'))
+ self.assertEqual(r.__class__, FSAssetDescriptor)
+ self.assertTrue(r.exists())
+
+ def test_resolve_absspec(self):
+ from pyramid.path import PkgResourcesAssetDescriptor
+
+ inst = self._makeOne(None)
+ r = inst.resolve('tests:test_asset.py')
+ self.assertEqual(r.__class__, PkgResourcesAssetDescriptor)
+ self.assertTrue(r.exists())
+
+ def test_resolve_relspec_with_pkg(self):
+ from pyramid.path import PkgResourcesAssetDescriptor
+
+ inst = self._makeOne('tests')
+ r = inst.resolve('test_asset.py')
+ self.assertEqual(r.__class__, PkgResourcesAssetDescriptor)
+ self.assertTrue(r.exists())
+
+ def test_resolve_relspec_no_package(self):
+ inst = self._makeOne(None)
+ self.assertRaises(ValueError, inst.resolve, 'test_asset.py')
+
+ def test_resolve_relspec_caller_package(self):
+ from pyramid.path import CALLER_PACKAGE, PkgResourcesAssetDescriptor
+
+ inst = self._makeOne(CALLER_PACKAGE)
+ r = inst.resolve('test_asset.py')
+ self.assertEqual(r.__class__, PkgResourcesAssetDescriptor)
+ self.assertTrue(r.exists())
+
+
+class TestDottedNameResolver(unittest.TestCase):
+ def _makeOne(self, package=None):
+ from pyramid.resolver import DottedNameResolver
+
+ return DottedNameResolver(package)
+
+ def config_exc(self, func, *arg, **kw):
+ try:
+ func(*arg, **kw)
+ except ValueError as e:
+ return e
+ else:
+ raise AssertionError('Invalid not raised') # pragma: no cover
+
+ def test_zope_dottedname_style_resolve_builtin(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style('builtins.str', None)
+ self.assertEqual(result, str)
+
+ def test_zope_dottedname_style_resolve_absolute(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ 'tests.test_resolver.TestDottedNameResolver', None
+ )
+ self.assertEqual(result, self.__class__)
+
+ def test_zope_dottedname_style_irrresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError,
+ typ._zope_dottedname_style,
+ 'pyramid.test_resolver.nonexisting_name',
+ None,
+ )
+
+ def test__zope_dottedname_style_resolve_relative(self):
+ import tests
+
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ '.test_resolver.TestDottedNameResolver', tests
+ )
+ self.assertEqual(result, self.__class__)
+
+ def test__zope_dottedname_style_resolve_relative_leading_dots(self):
+ import tests.test_resolver
+
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ '..tests.test_resolver.TestDottedNameResolver', tests
+ )
+ self.assertEqual(result, self.__class__)
+
+ def test__zope_dottedname_style_resolve_relative_is_dot(self):
+ import tests
+
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style('.', tests)
+ self.assertEqual(result, tests)
+
+ def test__zope_dottedname_style_irresolveable_relative_is_dot(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ._zope_dottedname_style, '.', None)
+ self.assertEqual(
+ e.args[0], "relative name '.' irresolveable without package"
+ )
+
+ def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ._zope_dottedname_style, '.whatever', None)
+ self.assertEqual(
+ e.args[0],
+ "relative name '.whatever' irresolveable without package",
+ )
+
+ def test_zope_dottedname_style_irrresolveable_relative(self):
+ import tests
+
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError, typ._zope_dottedname_style, '.notexisting', tests
+ )
+
+ def test__zope_dottedname_style_resolveable_relative(self):
+ import tests
+
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style('.', tests)
+ self.assertEqual(result, tests)
+
+ def test__zope_dottedname_style_irresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError, typ._zope_dottedname_style, 'pyramid.fudge.bar', None
+ )
+
+ def test__zope_dottedname_style_resolveable_absolute(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ 'tests.test_resolver.TestDottedNameResolver', None
+ )
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_resolve_absolute(self):
+ typ = self._makeOne()
+ result = typ._pkg_resources_style(
+ 'tests.test_resolver:TestDottedNameResolver', None
+ )
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_irrresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError, typ._pkg_resources_style, 'tests:nonexisting', None
+ )
+
+ def test__pkg_resources_style_resolve_relative(self):
+ import tests
+
+ typ = self._makeOne()
+ result = typ._pkg_resources_style(
+ '.test_resolver:TestDottedNameResolver', tests
+ )
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_resolve_relative_is_dot(self):
+ import tests
+
+ typ = self._makeOne()
+ result = typ._pkg_resources_style('.', tests)
+ self.assertEqual(result, tests)
+
+ def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
+ typ = self._makeOne()
+ self.assertRaises(
+ ValueError, typ._pkg_resources_style, '.whatever', None
+ )
+
+ def test__pkg_resources_style_irrresolveable_relative(self):
+ import pyramid
+
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError, typ._pkg_resources_style, ':notexisting', pyramid
+ )
+
+ def test_resolve_not_a_string(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ.resolve, None)
+ self.assertEqual(e.args[0], 'None is not a string')
+
+ def test_resolve_using_pkgresources_style(self):
+ typ = self._makeOne()
+ result = typ.resolve('tests.test_resolver:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_resolve_using_zope_dottedname_style(self):
+ typ = self._makeOne()
+ result = typ.resolve('tests.test_resolver:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_resolve_missing_raises(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ.resolve, 'cant.be.found')
+
+ def test_resolve_caller_package(self):
+ from pyramid.path import CALLER_PACKAGE
+
+ typ = self._makeOne(CALLER_PACKAGE)
+ self.assertEqual(
+ typ.resolve('.test_resolver.TestDottedNameResolver'),
+ self.__class__,
+ )
+
+ def test_maybe_resolve_caller_package(self):
+ from pyramid.path import CALLER_PACKAGE
+
+ typ = self._makeOne(CALLER_PACKAGE)
+ self.assertEqual(
+ typ.maybe_resolve('.test_resolver.TestDottedNameResolver'),
+ self.__class__,
+ )
+
+ def test_ctor_string_module_resolveable(self):
+ import tests
+
+ typ = self._makeOne('tests.test_resolver')
+ self.assertEqual(typ.package, tests)
+
+ def test_ctor_string_package_resolveable(self):
+ import tests
+
+ typ = self._makeOne('tests')
+ self.assertEqual(typ.package, tests)
+
+ def test_ctor_string_irresolveable(self):
+ self.assertRaises(ValueError, self._makeOne, 'cant.be.found')
+
+ def test_ctor_module(self):
+ import tests
+
+ from . import test_resolver
+
+ typ = self._makeOne(test_resolver)
+ self.assertEqual(typ.package, tests)
+
+ def test_ctor_package(self):
+ import tests
+
+ typ = self._makeOne(tests)
+ self.assertEqual(typ.package, tests)
+
+ def test_ctor_None(self):
+ typ = self._makeOne(None)
+ self.assertEqual(typ.package, None)