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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.24
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any