cos-comparison 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.
- cos_comparison/__init__.py +146 -0
- cos_comparison/cos_comparison.py +1892 -0
- cos_comparison/cos_comparison_c/__init__.py +1 -0
- cos_comparison/cos_comparison_c/cos_comparison_c.py +558 -0
- cos_comparison/cos_comparison_numpy.py +498 -0
- cos_comparison/cos_comparison_old.py +89 -0
- cos_comparison-0.1.0.dist-info/METADATA +46 -0
- cos_comparison-0.1.0.dist-info/RECORD +11 -0
- cos_comparison-0.1.0.dist-info/WHEEL +5 -0
- cos_comparison-0.1.0.dist-info/licenses/LICENSE.txt +21 -0
- cos_comparison-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# cos_comparison package initializer
|
|
2
|
+
#
|
|
3
|
+
# Dynamic fallback through configured backends. Any attribute that is not
|
|
4
|
+
# already defined in this module is lazily resolved at call time by
|
|
5
|
+
# searching the backends listed in config.json in order. Once a backend
|
|
6
|
+
# returns a result (or raises an unexpected error), the process stops.
|
|
7
|
+
|
|
8
|
+
import importlib
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
# ----------------------------------------------------------------------
|
|
14
|
+
# File context manager (kept from original codebase)
|
|
15
|
+
# ----------------------------------------------------------------------
|
|
16
|
+
class file_context:
|
|
17
|
+
__slots__ = ("file",)
|
|
18
|
+
def __init__(self, *arg, **kwarg):
|
|
19
|
+
self.file = open(*arg, **kwarg)
|
|
20
|
+
def __enter__(self):
|
|
21
|
+
self.file = self.file.__enter__()
|
|
22
|
+
return self
|
|
23
|
+
def __exit__(self, *arg):
|
|
24
|
+
self.file.__exit__(*arg)
|
|
25
|
+
def __del__(self):
|
|
26
|
+
self.close()
|
|
27
|
+
def __getattr__(self, name):
|
|
28
|
+
return getattr(self.file, name)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ----------------------------------------------------------------------
|
|
32
|
+
# Backend discovery from config.json
|
|
33
|
+
# ----------------------------------------------------------------------
|
|
34
|
+
def _load_backend_names() -> list:
|
|
35
|
+
"""Return the ordered list of backend module names."""
|
|
36
|
+
conf_path = os.path.join(os.path.dirname(__file__), "config.json")
|
|
37
|
+
if not os.path.exists(conf_path):
|
|
38
|
+
return ["backend_python"] # sensible default
|
|
39
|
+
|
|
40
|
+
with file_context(conf_path, "r", encoding="utf-8") as f:
|
|
41
|
+
try:
|
|
42
|
+
meta, _ = json.load(f) # [backends, plugin_dir]
|
|
43
|
+
except Exception:
|
|
44
|
+
return ["backend_python"]
|
|
45
|
+
|
|
46
|
+
if isinstance(meta, list) and meta:
|
|
47
|
+
return meta
|
|
48
|
+
return ["backend_python"]
|
|
49
|
+
|
|
50
|
+
import os
|
|
51
|
+
|
|
52
|
+
def _get_backend_order():
|
|
53
|
+
try:
|
|
54
|
+
return _load_backend_names()
|
|
55
|
+
except:
|
|
56
|
+
pass
|
|
57
|
+
env_order = os.environ.get("COS_BACKEND", "").strip()
|
|
58
|
+
if env_order:
|
|
59
|
+
return [name.strip() for name in env_order.split(",")]
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
"cos_comparison_c",
|
|
63
|
+
"cos_comparison_numpy",
|
|
64
|
+
"cos_comparison",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
_BACKEND_NAMES = _get_backend_order()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ----------------------------------------------------------------------
|
|
72
|
+
# Module cache (avoid repeated imports)
|
|
73
|
+
# ----------------------------------------------------------------------
|
|
74
|
+
_module_cache: dict = {} # name -> module | None
|
|
75
|
+
|
|
76
|
+
def _import_backend(name: str) -> Any:
|
|
77
|
+
"""Import a backend module and cache it. Returns module or None."""
|
|
78
|
+
if name in _module_cache:
|
|
79
|
+
return _module_cache[name]
|
|
80
|
+
try:
|
|
81
|
+
mod = importlib.import_module(f".{name}", package="cos_comparison")
|
|
82
|
+
_module_cache[name] = mod
|
|
83
|
+
return mod
|
|
84
|
+
except ImportError:
|
|
85
|
+
_module_cache[name] = None
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ----------------------------------------------------------------------
|
|
90
|
+
# Callable object that searches backends on every invocation
|
|
91
|
+
# ----------------------------------------------------------------------
|
|
92
|
+
class _FallbackWrapper:
|
|
93
|
+
__slots__ = ("func_name",)
|
|
94
|
+
|
|
95
|
+
def __init__(self, func_name: str):
|
|
96
|
+
self.func_name = func_name
|
|
97
|
+
|
|
98
|
+
def __call__(self, *args, **kwargs):
|
|
99
|
+
last_exception = None
|
|
100
|
+
for bname in _BACKENDS:
|
|
101
|
+
mod = _import_backend(bname)
|
|
102
|
+
if mod is None:
|
|
103
|
+
continue # module not available
|
|
104
|
+
func = getattr(mod, self.func_name, None)
|
|
105
|
+
if func is None:
|
|
106
|
+
continue # function not provided
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
return func(*args, **kwargs)
|
|
110
|
+
except NotImplementedError:
|
|
111
|
+
# Backend explicitly refuses this call -- normal fallback
|
|
112
|
+
continue
|
|
113
|
+
except Exception as exc:
|
|
114
|
+
# Unexpected error – remember it but try next backend
|
|
115
|
+
last_exception = exc
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
if last_exception is not None:
|
|
119
|
+
raise last_exception
|
|
120
|
+
raise RuntimeError(
|
|
121
|
+
f"No backend among {_BACKENDS} provides a working "
|
|
122
|
+
f"implementation for '{self.func_name}'."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ----------------------------------------------------------------------
|
|
127
|
+
# Intercept missing attributes (Python 3.7+)
|
|
128
|
+
# ----------------------------------------------------------------------
|
|
129
|
+
def __getattr__(name: str):
|
|
130
|
+
"""Called only when `name` is not found in the module globals.
|
|
131
|
+
|
|
132
|
+
Returns a callable wrapper that searches backends on each call.
|
|
133
|
+
Once created, the wrapper is cached in the module's dictionary so
|
|
134
|
+
that subsequent lookups are fast.
|
|
135
|
+
"""
|
|
136
|
+
# No exclusion of private names – internal functions may be needed.
|
|
137
|
+
wrapper = _FallbackWrapper(name)
|
|
138
|
+
globals()[name] = wrapper
|
|
139
|
+
return wrapper
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ----------------------------------------------------------------------
|
|
143
|
+
# Provide a useful __dir__ for introspection
|
|
144
|
+
# ----------------------------------------------------------------------
|
|
145
|
+
def __dir__() -> list:
|
|
146
|
+
return sorted(set(globals().keys()))
|