pyprotoloader 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from importlib.util import spec_from_file_location, module_from_spec
|
|
3
|
+
from inspect import getmembers, isclass
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PyProtoLoader:
|
|
8
|
+
"""
|
|
9
|
+
Dynamically loads Python modules from specified directories and provides utilities
|
|
10
|
+
to retrieve classes implementing a given protocol.
|
|
11
|
+
"""
|
|
12
|
+
def __init__(self, paths: List[str]):
|
|
13
|
+
"""
|
|
14
|
+
Initialize the loader with a list of directory paths to search for Python modules.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
paths (List[str]): List of directory paths to search for `.py` files.
|
|
18
|
+
"""
|
|
19
|
+
self.paths = paths
|
|
20
|
+
self.specs = {}
|
|
21
|
+
for path in paths:
|
|
22
|
+
for p, _, filenames in os.walk(path):
|
|
23
|
+
if p.endswith("__pycache__"):
|
|
24
|
+
continue
|
|
25
|
+
for filename in filenames:
|
|
26
|
+
if filename.endswith(".py"):
|
|
27
|
+
file_path = os.path.join(p, filename)
|
|
28
|
+
module_path = os.path.relpath(file_path, path)
|
|
29
|
+
spec = spec_from_file_location(filename[:-3], file_path)
|
|
30
|
+
if spec:
|
|
31
|
+
self.specs[module_path[:-3].replace(os.sep, ".")] = {"spec": spec,
|
|
32
|
+
"path": module_path,
|
|
33
|
+
"is_loaded": False, "module": None}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def load_module(self, module_name: str, reload: bool = False):
|
|
37
|
+
"""
|
|
38
|
+
Load a module by name. Optionally reload if already loaded.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
module_name (str): Name of the module to load (without `.py` extension).
|
|
42
|
+
reload (bool): Whether to reload the module if already loaded.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
module: The loaded Python module.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
ImportError: If the module is not found in the specified paths.
|
|
49
|
+
"""
|
|
50
|
+
if module_name in self.specs:
|
|
51
|
+
spec_info = self.specs[module_name]
|
|
52
|
+
spec = spec_info["spec"]
|
|
53
|
+
module = module_from_spec(spec)
|
|
54
|
+
if not reload and spec_info["is_loaded"]:
|
|
55
|
+
return spec_info["module"]
|
|
56
|
+
else:
|
|
57
|
+
spec.loader.exec_module(module)
|
|
58
|
+
spec_info["module"] = module
|
|
59
|
+
spec_info["is_loaded"] = True
|
|
60
|
+
return module
|
|
61
|
+
else:
|
|
62
|
+
raise ImportError(f"Module {module_name} not found in specified paths.")
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def get_protocol_class(module, protocol, class_name=None):
|
|
66
|
+
"""
|
|
67
|
+
Retrieve the first class in the module that implements the given protocol.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
module: The Python module to search.
|
|
71
|
+
protocol: The protocol class to check against.
|
|
72
|
+
class_name (str, optional): Specific class name to match. If None, returns the first match.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
class: The class implementing the protocol.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ValueError: If the protocol is invalid or no implementing class is found.
|
|
79
|
+
"""
|
|
80
|
+
if getattr(protocol, "_is_protocol", False) and getattr(protocol, "_is_runtime_protocol", False):
|
|
81
|
+
for name, obj in getmembers(module, isclass):
|
|
82
|
+
if isinstance(obj, protocol):
|
|
83
|
+
if class_name and name != class_name:
|
|
84
|
+
continue
|
|
85
|
+
return obj
|
|
86
|
+
raise ValueError(f"No class {class_name if class_name else ""} implementing {protocol} found in module {module.__name__}.")
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError(f"{protocol} is not a valid protocol class. "
|
|
89
|
+
f"Ensure it is decorated with @protocol and @runtime_protocol.")
|
pyprotoloader/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pyprotoloader
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author: Bharath Babu R
|
|
6
|
+
Author-email: Bharath Babu R <61000612+bharath93-byte@users.noreply.github.com>
|
|
7
|
+
Requires-Python: >=3.12.0
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# PyProtoLoader
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pyprotoloader/__init__.py,sha256=PURyFkkFLfGKk2Obr-vY5KWgf62WeGaHynOs4rRtzdQ,3717
|
|
2
|
+
pyprotoloader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
pyprotoloader-0.1.0.dist-info/WHEEL,sha256=eycQt0QpYmJMLKpE3X9iDk8R04v2ZF0x82ogq-zP6bQ,79
|
|
4
|
+
pyprotoloader-0.1.0.dist-info/METADATA,sha256=fIdFQHlT63_JLYmXnYmZLRAx2QIJPmUBf3lqlAQVLbU,277
|
|
5
|
+
pyprotoloader-0.1.0.dist-info/RECORD,,
|