Source code for importloc.util

from base64 import b32encode
from enum import Enum
import inspect
from typing import Any, Callable, Optional, TypeVar, Union
from uuid import uuid4


#: Arbitrary type.
T = TypeVar('T', bound=object)


[docs] class OrderBy(str, Enum): """ Objects sorting method. """ #: Order by object name. NAME = 'name' #: Order by definition order in the source file, first by source filename path, #: then by first line number. This sorting method uses `inspect.getsourcefile` #: and `inspect.getsourcelines`, and inherits their requirements on objects #: and exceptions raised. SOURCE = 'source'
[docs] def get_instances( obj: object, cls: type[T], order: Union[OrderBy, str, Callable[[T], Any]] = 'name', ) -> list[T]: """ Get object members that are instances of specified type. Uses `inspect.getmembers` and `isinstance`. Args: obj (``object``): object to get members from. cls (``type``): type of members to be returned. order (`OrderBy` | ``str`` | ``Callable[[T], Any]``): sorting method or sort key function; defaults to ``'name'``. Raises: `TypeError`: or other `Exception` raised by `inspect.getfile` and `inspect.getsourcelines` when sorting ``order='source'``. Returns: ``list[object]``: Example: >>> import app.plugins >>> plugins = get_instances(app.plugins, Plugin) """ ret = [mem for name, mem in inspect.getmembers(obj) if isinstance(mem, cls)] order_key = get_sort_key_func(order) if order_key: ret.sort(key=order_key) return ret
[docs] def get_subclasses( obj: object, cls: type[T], order: Union[OrderBy, str, Callable[[T], Any]] = 'name', ) -> list[type[T]]: """ Get object members that are subclasses of specified class (excluding the class itself). Uses `inspect.getmembers` and `issubclass`. Args: obj (`object`): object to get members from. cls (`type`): base class for returned subclasses. order (`OrderBy` | ``str`` | ``Callable[[T], Any]``): sorting method or sort key function; defaults to ``'name'``. Raises: `TypeError`: or other `Exception` raised by `inspect.getfile` and `inspect.getsourcelines` when sorting ``order='source'``. Returns: ``list[object]``: Example: >>> from unittest import TestCase >>> from tests import test_usage >>> cases = get_subclasses(test_usage, TestCase, order='source') """ ret = [ m for name, m in inspect.getmembers(obj) if isinstance(m, type) and issubclass(m, cls) and m is not cls ] order_key = get_sort_key_func(order) if order_key: ret.sort(key=order_key) return ret
[docs] def getattr_nested( obj: object, name: str, default: Union[type[Exception], Any] = AttributeError, ) -> object: """ Get nested attribute value. If attribute chain does not exist, raise exception or return default value. Args: obj (`object`): object to get attribute value from. name (`str`): dot-separated nested attribute name. default (`Exception` | `Any`): raise exception if ``default`` is an `Exception` type (`AttributeError` by default); otherwise return ``default`` value. Raises: `AttributeError`: when nested attribute chain does not exist and ``default`` was not adjusted. `Exception`: when custom `Exception` passed as ``default``. Returns: nested attribute value or ``default`` value, if specified and not an `Exception` subclass. Example: >>> from app import config >>> options = getattr_nested(config, f'{config.primary}.options') >>> missing = getattr_nested(config, 'does.not.exist', None) """ current = obj for part in name.split('.'): try: current = getattr(current, part) except AttributeError as exc: if isinstance(default, type) and issubclass(default, Exception): raise default(f'object has no attribute {name!r}') from exc else: return default return current
[docs] def random_name(*args: Any, **kwargs: Any) -> str: """ Generate random module name based on UUID4. All arguments passed to this function will be ignored. Example: >>> random_name() 'ufoh3xjrrozfcvfheyktg62pzia' """ rand = b32encode(uuid4().bytes).decode('ascii').replace('=', '').lower() return f'u{rand}'
# undocumented helpers def get_sort_key_func( order: Union[OrderBy, str, Callable[[T], Any]], ) -> Optional[Callable[[Any], Any]]: if order == 'name': return None elif order == 'source': return lambda o: (inspect.getsourcefile(o), inspect.getsourcelines(o)[1]) elif callable(order): return order else: raise TypeError('Unexpected order type {}'.format(type(order)))