import adulib.reflection
reflection
Utilities for Python reflection.
is_valid_python_name
str) -> bool is_valid_python_name(name:
assert 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.
= find_module_root(adulib.reflection.__file__) module_root
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 tempfile
= """
py_code print('Hello...')
print(f'...{name}!')
"""
with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp_file:
'utf-8'))
temp_file.write(py_code.encode(= temp_file.name
temp_file_path
= get_function_from_py_file(temp_file_path, args=['name'])
func 'world') func(
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:
'utf-8'))
temp_file.write(py_code.encode(= temp_file.name
temp_file_path
= get_function_from_py_file(temp_file_path, is_async=True, args=['name'])
func await func('world')
Hello...
...world!
method_from_py_file
str) method_from_py_file(file_path:
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:
'utf-8'))
temp_file.write(py_code.encode(= temp_file.name
temp_file_path
class TestClass:
def __init__(self, name):
self.name = name
@method_from_py_file(temp_file_path)
def print_name(self): pass
"world").print_name() TestClass(
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 42
def 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
= MyClass(10)
obj print(obj.double()) # 20
print(obj.triple()) # 30
20
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'