import adulib.reflectionreflection
Utilities for Python reflection.
is_valid_python_name
is_valid_python_name(name: str) -> boolassert is_valid_python_name('a_python_name')
assert not is_valid_python_name('not a valid python name')find_module_root
find_module_root(path)Recursively finds the root directory of a Python module.
This function takes a file or directory path and determines the root directory of the module it belongs to. A directory is considered a module if it contains an ‘init.py’ file. The function will traverse upwards in the directory hierarchy until it finds the top-most module directory.
Parameters: path (str or Path): The file or directory path to start the search from.
Returns: Path or None: The root directory of the module if found, otherwise None.
module_root = find_module_root(adulib.reflection.__file__)get_module_path_hierarchy
get_module_path_hierarchy(path)Get the hierarchy of module paths starting from the given path.
This function constructs a list of tuples representing the module hierarchy starting from the specified path. Each tuple contains the module name and its corresponding path.
Parameters: path (str or Path): The file or directory path to start the hierarchy search from.
Returns: list: A list of tuples where each tuple contains a module name and its path.
get_function_from_py_file
get_function_from_py_file(file_path, func_name, args, is_async, return_func_key)Extracts and returns a function from a Python file.
This function reads a Python file, constructs a function from its contents, and returns it. It can handle both synchronous and asynchronous functions, and allows for optional argument specification and return value handling.
Parameters: file_path (str or Path): The path to the Python file containing the function. func_name (str, optional): The name of the function to extract. If not provided, the function name defaults to the file name without extension. args (list, optional): A list of argument names for the function. Defaults to an empty list. is_async (bool, optional): Indicates if the function is asynchronous. Defaults to False. return_func_key (str, optional): A key to handle return values within the function. Defaults to an empty string.
Returns: function: The extracted function, ready to be called with the specified arguments.
import tempfilepy_code = """
print('Hello...')
print(f'...{name}!')
"""
with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp_file:
temp_file.write(py_code.encode('utf-8'))
temp_file_path = temp_file.name
func = get_function_from_py_file(temp_file_path, args=['name'])
func('world')Hello...
...world!
py_code = """
import asyncio
await asyncio.sleep(0)
print('Hello...')
print(f'...{name}!')
"""
with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp_file:
temp_file.write(py_code.encode('utf-8'))
temp_file_path = temp_file.name
func = get_function_from_py_file(temp_file_path, is_async=True, args=['name'])
await func('world')Hello...
...world!
method_from_py_file
method_from_py_file(file_path: str)A decorator that replaces the functionality of a method with the code from a specified Python file.
This decorator reads a Python file, extracts a function with the same name as the decorated method, and replaces the original method’s functionality with the extracted function. It supports both synchronous and asynchronous functions.
Arguments: - file_path (str): The path to the Python file containing the function to be used as a replacement.
Returns: function: A decorator that wraps the original function, replacing its functionality with the function from the specified file.
py_code = """
print(f'Hello {self.name}')
"""
with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp_file:
temp_file.write(py_code.encode('utf-8'))
temp_file_path = temp_file.name
class TestClass:
def __init__(self, name):
self.name = name
@method_from_py_file(temp_file_path)
def print_name(self): pass
TestClass("world").print_name()Hello world
mod_property
mod_property(func, cached)Used to create module-level properties.
cached_mod_property
cached_mod_property(func)@mod_property
def my_prop():
print('my_prop called')
return 42def add_method(cls):
def decorator(func):
setattr(cls, func.__name__, func)
return func
return decorator
class MyClass:
def __init__(self, value):
self.value = value
@add_method(MyClass)
def double(self):
return self.value * 2
@add_method(MyClass)
def triple(self):
return self.value * 3
obj = MyClass(10)
print(obj.double()) # 20
print(obj.triple()) # 3020
30
patch_to
patch_to(cls, as_prop, cls_method, set_prop)Decorator: add f to cls
Define methods
class Foo:
...
@patch_to(Foo)
def bar(self):
return 'bar'
Foo().bar()'bar'
Define properties
class Foo:
def __init__(self):
self.value = "bar"
# Define a getter
@patch_to(Foo, as_prop=True)
def baz(self):
return self.value
# Define a setter
@patch_to(Foo, set_prop=True)
def baz(self, value):
self.value = value
foo = Foo()
assert foo.baz == "bar"
foo.baz = "???"
assert foo.baz == "???"Define a class method
@patch_to(Foo, cls_method=True)
def qux(self):
return 'bar'
Foo.qux()'bar'
patch
patch(f, as_prop, cls_method, set_prop)Decorator: add f to the first parameter’s class (based on f’s type annotations)
patch is similar to patch_to, except it uses type annotations in the signature to find the class to patch to.
class Foo:
...
@patch
def bar(self: Foo):
return 'bar'
Foo().bar()'bar'
class Foo:
def __init__(self):
self.value = "bar"
# Define a getter
@patch(as_prop=True)
def baz(self: Foo):
return self.value
# Define a setter
@patch(set_prop=True)
def baz(self: Foo, value):
self.value = value
foo = Foo()
assert foo.baz == "bar"
foo.baz = "???"
assert foo.baz == "???"@patch(cls_method=True)
def qux(cls: Foo):
return 'bar'
Foo.qux()'bar'