Source code for dffml.util.python

"""
Python specific helper functions
"""
import sys
import types
import pathlib
import inspect
import importlib
from typing import Callable, Iterator, Optional, Tuple, Union


[docs]def modules( root: Union[str, pathlib.Path], package_name: str, *, skip: Optional[Callable[[str, pathlib.Path], bool]] = None, ) -> Iterator[Tuple[str, types.ModuleType]]: """ Import all modules (Python files) starting at the directory given by ``root`` of the package ``package_name``. Parameters ---------- root : str, pathlib.Path The path to the directory containing the top level ``__init__.py`` of the package. package_name : str Top level package name of the package your importing. There are no ``.`` characters in this. For example, ``dffml``, or ``dffml_config_yaml``. skip : callable, optional Function that will be called with ``(import_name, path)`` and should return a boolean, True if the module should not be imported and yielded to the caller. If it returns False, the Python file at ``path`` within ``package_name`` will be imported by passing ``import_name`` to :py:func`importlib.import_module`. Yields ------ import_name : str The ``package_name.subdir.file_stem`` used to import the module. module : types.ModuleType The imported module. Examples -------- You can get all the Python files imported individually in a package as follows. >>> import dffml >>> import pathlib >>> import importlib >>> >>> package_name = "xml" >>> top_level_module = importlib.import_module(package_name) >>> >>> root = pathlib.Path(top_level_module.__path__[0]) >>> >>> # Skip any files in dom/ subdirectory and __main__.py and __init__.py >>> def skip(_import_name, path) -> bool: ... return (root / "dom") in path.parents or path.name.startswith("__") ... >>> # Print the first module >>> for import_name, module in dffml.modules(root, package_name, skip=skip): ... print(import_name) ... break ... xml.etree.ElementInclude """ for path in sorted(pathlib.Path(root).rglob("*.py")): # Figure out name import_name = pathlib.Path(str(path)[len(str(root)) :]).parts[1:] import_name = ( package_name + "." + ".".join( list(import_name[:-1]) + [import_name[-1].replace(".py", "")] ) ) # Check if we should skip importing this file if skip and skip(import_name, path): continue # Import module yield import_name, importlib.import_module(import_name)
# See comment at beginning of within_method() IN_IPYTHON = False CHECKED_IN_IPYTHON = False IPYTHON_INSPECT_PATCHED = False
[docs]def within_method(obj: object, method_name: str, max_depth: int = -1) -> bool: """ Return True if a caller is being called from a given method of a given object. Parameters ---------- obj : object Check if we are within a method of this object. method_name : str Check if we are within a method by this name. max_depth : int, optional (-1) Stop checking stack frames after we have checked this many. Returns ------- within : boolean True if the calling function is being called from within the method given bound to the object given. Examples -------- >>> from dffml import within_method >>> >>> class FirstClass: ... def feedface(self): ... print(within_method(self, "__init__", max_depth=3)) ... >>> first = FirstClass() >>> first .feedface() False >>> >>> class SecondClass(FirstClass): ... def __init__(self): ... self.feedface() ... >>> second = SecondClass() True >>> >>> class ThirdClass(SecondClass): ... def __init__(self): ... self.deadbeef() ... ... def deadbeef(self): ... self.feedface() ... >>> third = ThirdClass() False """ # HACK Fix for if we are running in IPython notebook. Sometimes it doesn't # patch inspect.findsource as is intended # References: # - https://github.com/ipython/ipython/issues/1456 # - https://github.com/ipython/ipython/commit/298fdab5025745cd25f7f48147d8bc4c65be9d4a#diff-3a77d00d5690f670e9ac680f06b8ffe7ca902c6d325673f32e719d8e55b11ae3R209 global IN_IPYTHON global CHECKED_IN_IPYTHON global IPYTHON_INSPECT_PATCHED if ( not CHECKED_IN_IPYTHON and sys.version_info.major == 3 and sys.version_info.minor <= 7 ): try: get_ipython() IN_IPYTHON = True except: pass CHECKED_IN_IPYTHON = True if IN_IPYTHON and not IPYTHON_INSPECT_PATCHED: import IPython.core.ultratb inspect.findsource = IPython.core.ultratb.findsource IPYTHON_INSPECT_PATCHED = False # Grab stack frames try: frames = inspect.stack() except ImportError: # HACK ImportError Fix for lazy_import rasing on autosklearn/smac: emcee return True for i, frame_info in enumerate(frames): if max_depth != -1 and i >= max_depth: break if ( frame_info.function == method_name and "self" in frame_info.frame.f_locals and frame_info.frame.f_locals["self"] is obj ): return True return False