mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-25 06:18:32 +07:00
106 lines
3.4 KiB
Python
106 lines
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright (c) 2015, by the Pockets team, see AUTHORS.
|
|
# Licensed under the BSD License, see LICENSE for details.
|
|
|
|
"""A pocket full of useful reflection functions!"""
|
|
|
|
from __future__ import absolute_import
|
|
import inspect
|
|
import functools
|
|
|
|
from pockets.collections import listify
|
|
from six import string_types
|
|
|
|
__all__ = ["resolve"]
|
|
|
|
|
|
def resolve(name, modules=None):
|
|
"""Resolve a dotted name to an object (usually class, module, or function).
|
|
|
|
If `name` is a string, attempt to resolve it according to Python
|
|
dot notation, e.g. "path.to.MyClass". If `name` is anything other than a
|
|
string, return it immediately:
|
|
|
|
>>> resolve("calendar.TextCalendar")
|
|
<class 'calendar.TextCalendar'>
|
|
>>> resolve(object()) #doctest: +ELLIPSIS
|
|
<object object at 0x...>
|
|
|
|
If `modules` is specified, then resolution of `name` is restricted
|
|
to the given modules. Leading dots are allowed in `name`, but they are
|
|
ignored. Resolution **will not** traverse up the module path if `modules`
|
|
is specified.
|
|
|
|
If `modules` is not specified and `name` has leading dots, then resolution
|
|
is first attempted relative to the calling function's module, and then
|
|
absolutely. Resolution **will** traverse up the module path. If `name` has
|
|
no leading dots, resolution is first attempted absolutely and then
|
|
relative to the calling module.
|
|
|
|
Warning:
|
|
Do not resolve strings supplied by an end user without specifying
|
|
`modules`. Instantiating an arbitrary object specified by an end user
|
|
can introduce a potential security risk.
|
|
|
|
To avoid this, restrict the search path by explicitly specifying
|
|
`modules`.
|
|
|
|
Restricting `name` resolution to a set of `modules`:
|
|
|
|
>>> resolve("pockets.camel") #doctest: +ELLIPSIS
|
|
<function camel at 0x...>
|
|
>>> resolve("pockets.camel", modules=["re", "six"]) #doctest: +ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Unable to resolve 'pockets.camel' in modules: ['re', 'six']
|
|
|
|
Args:
|
|
name (str or object): A dotted name.
|
|
|
|
modules (str or list, optional): A module or list of modules, under
|
|
which to search for `name`.
|
|
|
|
Returns:
|
|
object: The object specified by `name`.
|
|
|
|
Raises:
|
|
ValueError: If `name` can't be resolved.
|
|
|
|
"""
|
|
if not isinstance(name, string_types):
|
|
return name
|
|
|
|
obj_path = name.split('.')
|
|
search_paths = []
|
|
if modules:
|
|
while not obj_path[0]:
|
|
obj_path.pop(0)
|
|
for module_path in listify(modules):
|
|
search_paths.append(module_path.split('.') + obj_path)
|
|
else:
|
|
caller = inspect.getouterframes(inspect.currentframe())[1][0].f_globals
|
|
module_path = caller['__name__'].split('.')
|
|
if not obj_path[0]:
|
|
obj_path.pop(0)
|
|
while not obj_path[0]:
|
|
obj_path.pop(0)
|
|
if module_path:
|
|
module_path.pop()
|
|
|
|
search_paths.append(module_path + obj_path)
|
|
search_paths.append(obj_path)
|
|
else:
|
|
search_paths.append(obj_path)
|
|
search_paths.append(module_path + obj_path)
|
|
|
|
for path in search_paths:
|
|
try:
|
|
obj = functools.reduce(getattr, path[1:], __import__(path[0]))
|
|
except (AttributeError, ImportError):
|
|
pass
|
|
else:
|
|
return obj
|
|
|
|
raise ValueError("Unable to resolve '{0}' "
|
|
"in modules: {1}".format(name, modules))
|