Source code for rwskit.types_

# Python Modules
import importlib

from types import ModuleType, UnionType
from typing import *

# 3rd Party Modules
from icontract import require

# Project Modules


[docs] def is_union(type_: Any) -> bool: """Check if the given ``type_`` is a union of types. A type can be a union if it uses the :class:`~typing.Union` type hint or the ``|`` operator. Parameters ---------- type_ : Any The type to check. Returns ------- bool ``True`` if the given type is a union, otherwise ``False``. """ # Using `from __future__ import annotations` defers the loading of types # so `type_` might not be a `typing` Type object, but a string instead. # If it is a string we'll try to evaluate it into its actual class Type. # `eval` will raise an exception if the evaluated type is not already # imported (in globals). We only care about `typing.Optional` so if # a NameError is raised we can just ignore it and return False. # TODO Using `typing._eval_type` might be a better way to do this, # but it is a protected method and I have not looked closely # at the implementation. from typing import Optional # noqa try: type_ = eval(type_, globals()) if isinstance(type_, str) else type_ except NameError: return False else: return get_origin(type_) is Union or isinstance(type_, UnionType)
[docs] def is_optional(type_: Any) -> bool: """Check if a type is optional. An optional type is either defined by class:`typing.Optional` or by a :class:`~typing.Union` containing ``None``. Parameters ---------- type_ : Any The type to check. Returns ------- bool ``True`` if ``type_`` is optional. """ # See the note in `is_union` about lazy type evaluation. try: type_ = eval(type_, globals()) if isinstance(type_, str) else type_ except NameError: return False else: return is_union(type_) and (type(None) in get_args(type_))
@require(lambda cls: isinstance(cls, type), "The input must be a class type.")
[docs] def get_qualified_name(cls: type) -> str: """Get the fully qualified name of this class. The fully qualified name is the full path of package names, the module name, and class name. Parameters ---------- cls : Type[Any] Returns ------- str The fully qualified name of this class. Raises ------ icontract.ViolationError If the input ``cls`` is not a type. """ module, name = cls.__module__, cls.__qualname__ if module is not None and module != "__builtin__": name = module + "." + name return name
@require( lambda qualified_class_name: "." in qualified_class_name, "The 'qualified_class_name' must at least refer to a module (the " "provided name does not contain a '.').", ) @require( lambda qualified_class_name: bool(qualified_class_name), "The 'qualified_class_name' cannot be empty.", )
[docs] def type_from_string(qualified_class_name: str) -> type: """Import and return the type object from a fully qualified class name. Parameters ---------- qualified_class_name : str The fully qualified class name. Returns ------- type The type object corresponding to the fully qualified class name. Raises ------ icontract.ViolationError If the input ``qualified_class_name`` is ``None`` or empty. ModuleNotFoundError If the input ``qualified_class_name`` cannot be loaded. """ tokens = qualified_class_name.split(".") module_name, class_name = ".".join(tokens[:-1]), tokens[-1] module = importlib.import_module(module_name) return getattr(module, class_name)
[docs] def import_all_modules_in_path(module_path: str) -> dict[str, ModuleType]: """ Import all modules in a given path and return a dict of their names and modules. Parameters ---------- module_path : str The full path to the module, e.g. ``foo.bar.baz`` Returns ------- dict[str, ModuleType] A dictionary mapping the module path segments to their corresponding modules. """ result = {} parts = module_path.split(".") for i in range(1, len(parts) + 1): m = importlib.import_module(".".join(parts[:i])) result[m.__name__] = m return result