stlib-ext 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.
- morefunctools/__init__.py +17 -0
- morefunctools/cache.py +59 -0
- morefunctools/pyexperimental.py +48 -0
- moretyping/__init__.py +4 -0
- moretyping/data/__init__.py +1 -0
- moretyping/data/_meta_process.py +35 -0
- moretyping/data/number.py +27 -0
- moretyping/meta/__init__.py +0 -0
- moretyping/visualise/__init__.py +2 -0
- moretyping/visualise/lists/__init__.py +1 -0
- moretyping/visualise/lists/link.py +16 -0
- moretyping/visualise/other/__init__.py +1 -0
- moretyping/visualise/other/showjson.py +12 -0
- moretyping/visualise/shared.py +16 -0
- stlib_ext/__init__.py +2 -0
- stlib_ext-0.1.0.dist-info/METADATA +8 -0
- stlib_ext-0.1.0.dist-info/RECORD +18 -0
- stlib_ext-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
def importDefaults(module) -> dict[str, Any]:
|
|
5
|
+
if not hasattr(module, "__defaults__"):
|
|
6
|
+
return {}
|
|
7
|
+
vals = {}
|
|
8
|
+
for obj in module.__defaults__:
|
|
9
|
+
if obj.__name__ in globals().keys():
|
|
10
|
+
warnings.warn(f"Warning: importDefaults may overwrite globals()[{repr(obj.__name__)}]")
|
|
11
|
+
vals[obj.__name__] = obj
|
|
12
|
+
return vals
|
|
13
|
+
|
|
14
|
+
from . import cache
|
|
15
|
+
from . import pyexperimental as exp
|
|
16
|
+
|
|
17
|
+
globals().update(importDefaults(exp))
|
morefunctools/cache.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Optional, Any
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from moretyping.visualise import VisLink
|
|
7
|
+
from .pyexperimental import experimental
|
|
8
|
+
|
|
9
|
+
type MaxSize = int | bool
|
|
10
|
+
|
|
11
|
+
class CacheInfo:
|
|
12
|
+
def __init__(self, hits: int, misses: int, maxsize: MaxSize, currsize: int) -> None:
|
|
13
|
+
self.hits = hits
|
|
14
|
+
self.misses = misses
|
|
15
|
+
self.maxsize = maxsize
|
|
16
|
+
self.currsize = currsize
|
|
17
|
+
self.locked = False
|
|
18
|
+
|
|
19
|
+
def lock(self) -> None:
|
|
20
|
+
self.locked = True
|
|
21
|
+
|
|
22
|
+
def __setattr__(self, name, value):
|
|
23
|
+
if self.locked == False:
|
|
24
|
+
self.__dict__[name] = value
|
|
25
|
+
else:
|
|
26
|
+
raise RuntimeError("Attempted to modify immutable object: CacheInfo")
|
|
27
|
+
|
|
28
|
+
# TODO: Implement cache_clear()
|
|
29
|
+
# TODO: Implement cache_info()
|
|
30
|
+
# TODO: Implement cache_entries()
|
|
31
|
+
@experimental
|
|
32
|
+
def fifo_cache(maxsize: Optional[MaxSize]) -> Callable[[Callable], Callable]:
|
|
33
|
+
if isinstance(maxsize, bool):
|
|
34
|
+
maxsize = math.inf if maxsize else 0
|
|
35
|
+
|
|
36
|
+
def decorator(function: Callable):
|
|
37
|
+
cache = {}
|
|
38
|
+
cacheList = [] # TODO: Switch to deque
|
|
39
|
+
|
|
40
|
+
@wraps(function)
|
|
41
|
+
def wrapper(*args, **kwargs) -> Any:
|
|
42
|
+
key = VisLink((tuple(args), tuple(kwargs.items()))).string
|
|
43
|
+
if key in set(cache.keys()): # TODO: Remove set()
|
|
44
|
+
return cache.get(key)
|
|
45
|
+
|
|
46
|
+
value = function(*args, **kwargs)
|
|
47
|
+
cacheList.append(key)
|
|
48
|
+
cache[key] = value
|
|
49
|
+
|
|
50
|
+
if len(cacheList) > maxsize:
|
|
51
|
+
removalKey = cacheList.pop(0)
|
|
52
|
+
cache.pop(removalKey)
|
|
53
|
+
print(f"Removing key: {removalKey}") # TODO: Remove debug print statements
|
|
54
|
+
|
|
55
|
+
return value
|
|
56
|
+
|
|
57
|
+
return wrapper
|
|
58
|
+
|
|
59
|
+
return decorator
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any, Optional, overload
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
class ExperimentalWarning(Warning):
|
|
7
|
+
def __init__(self, message: str) -> None:
|
|
8
|
+
self.msg = message
|
|
9
|
+
super().__init__(message)
|
|
10
|
+
|
|
11
|
+
@overload
|
|
12
|
+
def experimental(message: Optional[str] = None, category: type[Warning] = ExperimentalWarning, stacklevel: int = 2) -> Callable[[Callable], Callable]:
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
@overload
|
|
16
|
+
def experimental(fn: Callable) -> Callable:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
# TODO: Clean up a little
|
|
20
|
+
def experimental(message: Optional[str] | Callable = None, category: type[Warning] = ExperimentalWarning, stacklevel: int = 2) -> Callable:
|
|
21
|
+
newMessage = None if callable(message) else message
|
|
22
|
+
if callable(message):
|
|
23
|
+
fn = message
|
|
24
|
+
message = None
|
|
25
|
+
|
|
26
|
+
@wraps(fn)
|
|
27
|
+
def wrapper(*args, **kwargs) -> Any:
|
|
28
|
+
warnings.warn(newMessage or f"Function {'{'}{fn.__name__}{'}'} is experimental. Expect bugs or incomplete behaviour.", category = category, stacklevel = stacklevel)
|
|
29
|
+
return fn(*args, **kwargs)
|
|
30
|
+
|
|
31
|
+
return wrapper
|
|
32
|
+
|
|
33
|
+
def decorator(fn: Callable) -> Callable:
|
|
34
|
+
@wraps(fn)
|
|
35
|
+
def wrapper(*args, **kwargs) -> Any:
|
|
36
|
+
warnings.warn(newMessage or f"Function {'{'}{fn.__name__}{'}'} is experimental. Expect bugs or incomplete behaviour.", category = category, stacklevel = stacklevel)
|
|
37
|
+
return fn(*args, **kwargs)
|
|
38
|
+
return wrapper
|
|
39
|
+
|
|
40
|
+
return decorator
|
|
41
|
+
|
|
42
|
+
@experimental
|
|
43
|
+
def test():
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
__defaults__ = (
|
|
47
|
+
experimental,
|
|
48
|
+
)
|
moretyping/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .number import Number
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
class ProcessMeta(type):
|
|
4
|
+
__output_classes__: tuple[type, ...]
|
|
5
|
+
|
|
6
|
+
def __new__(mcls, name: str, bases: tuple[type, ...], namespace: dict[str, Any], *, output_classes: tuple[type, ...], **kwargs: Any) -> type:
|
|
7
|
+
oldnew = namespace["__new__"]
|
|
8
|
+
|
|
9
|
+
def __new__(cls: type, *args: Any, **kwargs: Any) -> Any:
|
|
10
|
+
value = oldnew(cls, *args, **kwargs)
|
|
11
|
+
|
|
12
|
+
for t in output_classes:
|
|
13
|
+
if isinstance(value, t):
|
|
14
|
+
break
|
|
15
|
+
else:
|
|
16
|
+
raise TypeError(f"Data processor `{name}` returned a value of type {type(value)} -- not one of {output_classes}")
|
|
17
|
+
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
def __init__(self: Any, *_: Any, **__: Any) -> None:
|
|
21
|
+
raise RuntimeError(f"Attempted to initialise instance of {type(self)} (data processor). Invalid operation.")
|
|
22
|
+
|
|
23
|
+
namespace["__init__"] = __init__
|
|
24
|
+
namespace["__new__"] = __new__
|
|
25
|
+
namespace["__output_classes__"] = output_classes
|
|
26
|
+
|
|
27
|
+
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
|
|
28
|
+
return cls
|
|
29
|
+
|
|
30
|
+
def __instancecheck__(cls: "ProcessMeta", instance: object) -> bool:
|
|
31
|
+
for t in cls.__output_classes__:
|
|
32
|
+
if isinstance(instance, t):
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
return False
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from ._meta_process import ProcessMeta
|
|
3
|
+
|
|
4
|
+
type Numeric = int | float
|
|
5
|
+
|
|
6
|
+
class Number(metaclass=ProcessMeta, output_classes=(int, float)):
|
|
7
|
+
"""
|
|
8
|
+
Convert an object of unknown type to a Numeric (`int|float`) value.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
value: Numeric | Any — The value to cast to a Numeric type.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
Numeric — The value cast to a Numeric type.
|
|
15
|
+
|
|
16
|
+
Raises:
|
|
17
|
+
ValueError: If the value can't be cast to a numeric type.
|
|
18
|
+
|
|
19
|
+
Details:
|
|
20
|
+
If the value is an integer, return it unchanged.
|
|
21
|
+
|
|
22
|
+
Otherwise, return `float(value)`
|
|
23
|
+
"""
|
|
24
|
+
def __new__(cls, value: Numeric | Any) -> int | float: # type: ignore[misc]
|
|
25
|
+
if isinstance(value, int):
|
|
26
|
+
return value
|
|
27
|
+
return float(value)
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .link import VisLink
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ..shared import VisualiseMeta
|
|
2
|
+
from typing import Any
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
|
|
6
|
+
class VisLink(metaclass=VisualiseMeta):
|
|
7
|
+
def __init__(self, data: list[Any]) -> None:
|
|
8
|
+
self.data = tuple(data)
|
|
9
|
+
|
|
10
|
+
@cached_property
|
|
11
|
+
def strdata(self) -> Iterable[str]:
|
|
12
|
+
return map(lambda x: str(x), self.data)
|
|
13
|
+
|
|
14
|
+
@cached_property
|
|
15
|
+
def string(self) -> str:
|
|
16
|
+
return f"[ {" > ".join(self.strdata)} ]"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .showjson import ViewJSON
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from ..shared import VisualiseMeta
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
class ViewJSON(metaclass=VisualiseMeta):
|
|
7
|
+
def __init__(self, data: Optional[Any] = {}) -> None:
|
|
8
|
+
self.data = data
|
|
9
|
+
|
|
10
|
+
@cached_property
|
|
11
|
+
def string(self) -> str:
|
|
12
|
+
return json.dumps(self.data, indent = 4)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
class VisualiseMeta(type):
|
|
5
|
+
def __new__(mcls: type[VisualiseMeta], name: str, bases: tuple[type, ...], namespace: dict[str, Any]) -> "VisualiseMeta":
|
|
6
|
+
def __repr__(self: Any) -> str:
|
|
7
|
+
return f"<{type(self).__name__} object> {self.__str__()}"
|
|
8
|
+
|
|
9
|
+
def __str__(self: Any) -> str:
|
|
10
|
+
return str(self.string)
|
|
11
|
+
|
|
12
|
+
namespace["__repr__"] = namespace.get("__repr__") or __repr__
|
|
13
|
+
namespace["__str__"] = namespace.get("__str__") or __str__
|
|
14
|
+
cls = super().__new__(mcls, name, bases, namespace)
|
|
15
|
+
return cls
|
|
16
|
+
|
stlib_ext/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
morefunctools/__init__.py,sha256=pntkIUGrDS0GLSo6gpSOis5yHzyN5RR45yPpNjVuplY,488
|
|
2
|
+
morefunctools/cache.py,sha256=tB7wLl0JQE1vU-v5cSiIyb7xQ5J8R8YeXVVNjUD_INc,1807
|
|
3
|
+
morefunctools/pyexperimental.py,sha256=kBO3J1wml4gaWyiMXhUyqfdUOBdidb0C3zHGfBN3PiY,1559
|
|
4
|
+
moretyping/__init__.py,sha256=y4oDRCAX70hi5l4AlHajG1NLLx9wYnAGKuLM2jMMvVc,60
|
|
5
|
+
moretyping/data/__init__.py,sha256=1VwgRodn7CEgyix_yyTEPLEsFSNw1HahvZPTnEU5aJw,27
|
|
6
|
+
moretyping/data/_meta_process.py,sha256=8Zq-50OZtXEfiYQMPXG-3WSfEI0T5nI9MUHQO785X4g,1342
|
|
7
|
+
moretyping/data/number.py,sha256=q94wYTPNyqP1-0jnVvamDBY9dpAXjfMyeXlm9CRcQeM,763
|
|
8
|
+
moretyping/meta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
moretyping/visualise/__init__.py,sha256=D0d0F9kfpxuuBD2tTINmdXCFtsOa9oCzAeZX1lb2X8I,41
|
|
10
|
+
moretyping/visualise/lists/__init__.py,sha256=hEfBxoBKPOV3oaPiE-Td2tGkxAuSNiwiWP8GhFBp__E,25
|
|
11
|
+
moretyping/visualise/lists/link.py,sha256=Cdu_2gLc-Su3SsLv3foqnVq9bpWCw0d_lmjdUslIhpg,465
|
|
12
|
+
moretyping/visualise/other/__init__.py,sha256=Jq9DZTNQGhLfnjste45_ekTIn7w26iBUxGXFXNVeadA,30
|
|
13
|
+
moretyping/visualise/other/showjson.py,sha256=C_LRFqE0_Nw7EpDZkKvDUgaqR5evf2na4qTldoRUnHM,343
|
|
14
|
+
moretyping/visualise/shared.py,sha256=SkLR53iY1lqXBoMq4jY_LXPBhoNWMltZ2J04ptfB_v4,627
|
|
15
|
+
stlib_ext/__init__.py,sha256=ndgh7JT2wTwGdkUdTwUJPc1-WOGy_qfDzTqikmKStUg,39
|
|
16
|
+
stlib_ext-0.1.0.dist-info/WHEEL,sha256=CoDSoyhtC_eO_tlxRYzsTraPv1fPJRXFx91k6ISeAvA,81
|
|
17
|
+
stlib_ext-0.1.0.dist-info/METADATA,sha256=jgXS9mgg6UeH2Z5X9g32WYPmZn0S24t54Fju-_gDqAo,164
|
|
18
|
+
stlib_ext-0.1.0.dist-info/RECORD,,
|