omdev 0.0.0.dev29__py3-none-any.whl → 0.0.0.dev30__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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

omdev/cache/comp/cache.py DELETED
@@ -1,137 +0,0 @@
1
- """
2
- TODO:
3
- - decorator
4
- - thread local cache instance - but shared
5
- - arbitrary user-specified cache keys
6
- - filesystem OPTIONAL
7
- - locking
8
- - Keyer scheme
9
- - per-module-ish CACHE_VERSION convention
10
- - are pickles stable?
11
- - ComputeCache class
12
- - Cacheable - fn is one
13
- - ttl
14
- - nice to have: np mmap
15
- - compress?
16
- - decos, descriptors, etc
17
- - overlap w/ jobs/dags/batches/whatever
18
- - joblib
19
- - keep src anyway, but just for warn
20
- - strip comments?
21
- - ** INPUTS **
22
- - if underlying impl changes, bust
23
- - kinda reacty/reffy/signally
24
- - decorator unwrapping and shit
25
- - proactive deep invalidate
26
- - tracked and versioned 'ops' but not result cached
27
- - 'Versioned'
28
-
29
- manifest stuff
30
- - serialization_version
31
- - lib_version
32
- - lib_revision
33
-
34
- fn manifest stuff
35
- - source
36
- - qualname
37
- - location
38
-
39
- See:
40
- - https://github.com/amakelov/mandala
41
- - https://jax.readthedocs.io/en/latest/autodidax.html
42
- - tinyjit
43
- - https://docs.python.org/3/library/pickle.html#pickle.Pickler.dispatch_table
44
-
45
- names:
46
- - CacheKey = unambiguous, fully qualified, unhashed map key - usually Cacheable + args
47
- - Cacheable = usually a fn
48
- - CacheableName = qualname of a cacheable
49
- - dir structure: __package__/__qualname__/... ?
50
- """
51
- import copy
52
- import typing as ta
53
-
54
- from omlish import collections as col
55
- from omlish import dataclasses as dc
56
-
57
- from .types import CacheableName
58
- from .types import CacheableResolver
59
- from .types import CacheableVersionMap
60
- from .types import CacheKey
61
- from .types import CacheResult
62
-
63
-
64
- class Cache:
65
- def __init__(
66
- self,
67
- resolver: CacheableResolver,
68
- ) -> None:
69
- super().__init__()
70
-
71
- self._resolver = resolver
72
-
73
- self._dct: dict[CacheKey, Cache.Entry] = {}
74
-
75
- self._stats = Cache.Stats()
76
-
77
- @dc.dataclass()
78
- class Stats:
79
- num_hits: int = 0
80
- num_misses: int = 0
81
- num_invalidates: int = 0
82
- num_puts: int = 0
83
-
84
- @property
85
- def stats(self) -> Stats:
86
- return copy.deepcopy(self._stats)
87
-
88
- @dc.dataclass(frozen=True)
89
- class Entry:
90
- key: CacheKey
91
- versions: CacheableVersionMap
92
- value: ta.Any
93
-
94
- @dc.validate
95
- def _check_types(self) -> bool:
96
- return (
97
- isinstance(self.key, CacheKey) and
98
- isinstance(self.versions, col.frozendict)
99
- )
100
-
101
- def _build_version_map(self, names: ta.Iterable[CacheableName]) -> CacheableVersionMap:
102
- dct = {}
103
- for n in names:
104
- c = self._resolver.resolve(n)
105
- dct[n] = c.version
106
- return col.frozendict(dct)
107
-
108
- def get(self, key: CacheKey) -> CacheResult | None:
109
- try:
110
- entry = self._dct[key]
111
- except KeyError:
112
- self._stats.num_misses += 1
113
- return None
114
-
115
- new_versions = self._build_version_map(entry.versions)
116
- if entry.versions != new_versions:
117
- del self._dct[key]
118
- self._stats.num_invalidates += 1
119
- return None
120
-
121
- self._stats.num_hits += 1
122
- return CacheResult(
123
- True,
124
- entry.versions,
125
- entry.value,
126
- )
127
-
128
- def put(self, key: CacheKey, versions: CacheableVersionMap, val: ta.Any) -> None:
129
- if key in self._dct:
130
- raise KeyError(key)
131
-
132
- self._dct[key] = Cache.Entry(
133
- key,
134
- versions,
135
- val,
136
- )
137
- self._stats.num_puts += 1
@@ -1,136 +0,0 @@
1
- import contextlib
2
- import typing as ta
3
-
4
- from omlish import check
5
- from omlish import lang
6
-
7
- from .cache import Cache
8
- from .types import Cacheable
9
- from .types import CacheableVersionMap
10
- from .types import CacheKey
11
- from .types import CacheResult
12
- from .types import merge_version_maps
13
-
14
-
15
- CacheT = ta.TypeVar('CacheT', bound='Cache')
16
-
17
-
18
- ##
19
-
20
-
21
- _CURRENT_CACHE: Cache | None = None
22
-
23
-
24
- @contextlib.contextmanager
25
- def cache_context(cache: CacheT) -> ta.Iterator[CacheT]:
26
- global _CURRENT_CACHE
27
- prev = _CURRENT_CACHE
28
- try:
29
- _CURRENT_CACHE = cache
30
- yield cache
31
- finally:
32
- check.is_(_CURRENT_CACHE, cache)
33
- _CURRENT_CACHE = prev
34
-
35
-
36
- def get_current_cache() -> Cache | None:
37
- return _CURRENT_CACHE
38
-
39
-
40
- ##
41
-
42
-
43
- class CacheableContext(lang.Final):
44
- def __init__(
45
- self,
46
- cacheable: Cacheable,
47
- key: CacheKey,
48
- *,
49
- parent: ta.Optional['CacheableContext'] = None,
50
- ) -> None:
51
- super().__init__()
52
- self._cacheable = cacheable
53
- self._key = key
54
- self._parent = parent
55
-
56
- self._result: CacheResult | None = None
57
- self._children: list[CacheableContext] = []
58
-
59
- if parent is not None:
60
- check.state(not parent.has_result)
61
- parent._children.append(self) # noqa
62
-
63
- #
64
-
65
- @property
66
- def cacheable(self) -> Cacheable:
67
- return self._cacheable
68
-
69
- @property
70
- def key(self) -> CacheKey:
71
- return self._key
72
-
73
- @property
74
- def parent(self) -> ta.Optional['CacheableContext']:
75
- return self._parent
76
-
77
- @property
78
- def children(self) -> ta.Sequence['CacheableContext']:
79
- return self._children
80
-
81
- #
82
-
83
- @property
84
- def has_result(self) -> bool:
85
- return self._result is not None
86
-
87
- def result(self) -> CacheResult:
88
- return check.not_none(self._result)
89
-
90
- def set_hit(self, result: CacheResult) -> None:
91
- check.state(result.hit)
92
- self._result = check.replacing_none(self._result, result)
93
- self.result_versions()
94
-
95
- def set_miss(self, val: ta.Any) -> None:
96
- self._result = check.replacing_none(self._result, CacheResult(
97
- False,
98
- CacheableVersionMap(),
99
- val,
100
- ))
101
- self.result_versions()
102
-
103
- @lang.cached_function
104
- def result_versions(self) -> CacheableVersionMap:
105
- r = check.not_none(self._result)
106
- return merge_version_maps(
107
- self._cacheable.as_version_map,
108
- r.versions,
109
- *[c.result_versions() for c in self._children],
110
- )
111
-
112
-
113
- #
114
-
115
-
116
- _CURRENT_CACHEABLE_CONTEXT: CacheableContext | None = None
117
-
118
-
119
- @contextlib.contextmanager
120
- def cacheable_context(
121
- cacheable: Cacheable,
122
- key: CacheKey,
123
- ) -> ta.Iterator[CacheableContext]:
124
- global _CURRENT_CACHEABLE_CONTEXT
125
- prev = _CURRENT_CACHEABLE_CONTEXT
126
- ctx = CacheableContext(
127
- cacheable,
128
- key,
129
- parent=prev,
130
- )
131
- try:
132
- _CURRENT_CACHEABLE_CONTEXT = ctx
133
- yield ctx
134
- finally:
135
- check.is_(_CURRENT_CACHEABLE_CONTEXT, ctx)
136
- _CURRENT_CACHEABLE_CONTEXT = prev
omdev/cache/comp/fns.py DELETED
@@ -1,115 +0,0 @@
1
- import functools
2
- import importlib
3
- import typing as ta
4
-
5
- from omlish import cached
6
- from omlish import check
7
- from omlish import collections as col
8
- from omlish import dataclasses as dc
9
- from omlish import lang
10
-
11
- from .contexts import cacheable_context
12
- from .contexts import get_current_cache
13
- from .types import Cacheable
14
- from .types import CacheableName
15
- from .types import CacheableResolver
16
- from .types import CacheKey
17
-
18
-
19
- T = ta.TypeVar('T')
20
-
21
-
22
- ##
23
-
24
-
25
- @dc.dataclass(frozen=True)
26
- class FnCacheableName(CacheableName, lang.Final):
27
- module: str
28
- qualname: str
29
-
30
-
31
- @dc.dataclass(frozen=True)
32
- class FnCacheable(Cacheable, lang.Final):
33
- fn: ta.Callable
34
- version: int = dc.xfield(override=True)
35
-
36
- @cached.property
37
- def name(self) -> FnCacheableName:
38
- return FnCacheableName(self.fn.__module__, self.fn.__qualname__) # noqa
39
-
40
-
41
- class FnCacheableResolver(CacheableResolver):
42
- def resolve(self, name: CacheableName) -> Cacheable:
43
- fname = check.isinstance(name, FnCacheableName)
44
-
45
- mod = importlib.import_module(fname.module)
46
- obj = mod
47
- for a in fname.qualname.split('.'):
48
- obj = getattr(obj, a)
49
-
50
- check.callable(obj)
51
- fc = check.isinstance(obj.__cacheable__, FnCacheable)
52
-
53
- return fc
54
-
55
-
56
- @dc.dataclass(frozen=True)
57
- class FnCacheKey(CacheKey[FnCacheableName], lang.Final):
58
- args: tuple
59
- kwargs: col.frozendict[str, ta.Any]
60
-
61
- @dc.validate
62
- def _check_fn_types(self) -> bool:
63
- return (
64
- isinstance(self.name, FnCacheableName) and
65
- isinstance(self.args, tuple) and
66
- isinstance(self.kwargs, col.frozendict)
67
- )
68
-
69
-
70
- ##
71
-
72
-
73
- def cached_fn(version: int) -> ta.Callable[[T], T]:
74
- def outer(fn):
75
- @functools.wraps(fn)
76
- def inner(*args, **kwargs):
77
- # NOTE: just for testing :x allows updating
78
- # TODO: proper wrapper obj probably (enforce name resolution)
79
- cacheable = inner.__cacheable__ # type: ignore
80
-
81
- if (cache := get_current_cache()) is not None:
82
- key = FnCacheKey(
83
- cacheable.name,
84
- args,
85
- col.frozendict(kwargs),
86
- )
87
-
88
- with cacheable_context(
89
- cacheable,
90
- key,
91
- ) as ctx:
92
- if (hit := cache.get(key)) is not None:
93
- ctx.set_hit(hit)
94
- return hit.value
95
-
96
- val = fn(*args, **kwargs)
97
- ctx.set_miss(val)
98
- cache.put(
99
- key,
100
- ctx.result_versions(),
101
- val,
102
- )
103
- return val
104
-
105
- else:
106
- return fn(*args, **kwargs)
107
-
108
- inner.__cacheable__ = FnCacheable( # type: ignore
109
- fn,
110
- version,
111
- )
112
-
113
- return inner
114
-
115
- return outer # noqa
@@ -1,23 +0,0 @@
1
- from .types import Cacheable
2
- from .types import CacheableName
3
- from .types import CacheableResolver
4
-
5
-
6
- class CachingCacheableResolver(CacheableResolver):
7
- def __init__(self, child: CacheableResolver) -> None:
8
- super().__init__()
9
-
10
- self._child = child
11
- self._dct: dict[CacheableName, Cacheable] = {}
12
-
13
- def clear(self) -> None:
14
- self._dct.clear()
15
-
16
- def resolve(self, name: CacheableName) -> Cacheable:
17
- try:
18
- return self._dct[name]
19
- except KeyError:
20
- pass
21
- ret = self._child.resolve(name)
22
- self._dct[name] = ret
23
- return ret
omdev/cache/comp/types.py DELETED
@@ -1,92 +0,0 @@
1
- import abc
2
- import typing as ta
3
-
4
- from omlish import cached
5
- from omlish import collections as col
6
- from omlish import dataclasses as dc
7
- from omlish import lang
8
-
9
-
10
- T = ta.TypeVar('T')
11
-
12
-
13
- ##
14
-
15
-
16
- CacheableNameT = ta.TypeVar('CacheableNameT', bound='CacheableName')
17
-
18
-
19
- class CacheableName(lang.Abstract):
20
- pass
21
-
22
-
23
- ##
24
-
25
-
26
- CacheableVersion: ta.TypeAlias = ta.Hashable
27
- CacheableVersionMap: ta.TypeAlias = col.frozendict['CacheableName', CacheableVersion]
28
-
29
-
30
- def merge_version_maps(
31
- *dcts: ta.Mapping[CacheableName, CacheableVersion],
32
- ) -> CacheableVersionMap:
33
- out: dict[CacheableName, CacheableVersion] = {}
34
- for dct in dcts:
35
- for name, version in dct.items():
36
- try:
37
- ex = out[name]
38
- except KeyError:
39
- out[name] = version
40
- else:
41
- if ex != version:
42
- raise Exception(f'Version mismatch: {ex} {version}')
43
- return col.frozendict(out)
44
-
45
-
46
- ##
47
-
48
-
49
- class Cacheable(lang.Abstract):
50
- @property
51
- @abc.abstractmethod
52
- def name(self) -> CacheableName:
53
- raise NotImplementedError
54
-
55
- @property
56
- @abc.abstractmethod
57
- def version(self) -> CacheableVersion:
58
- raise NotImplementedError
59
-
60
- @cached.property
61
- def as_version_map(self) -> CacheableVersionMap:
62
- return col.frozendict({self.name: self.version})
63
-
64
-
65
- ##
66
-
67
-
68
- @dc.dataclass(frozen=True)
69
- @dc.extra_params(cache_hash=True)
70
- class CacheKey(lang.Abstract, ta.Generic[CacheableNameT]):
71
- name: CacheableNameT
72
-
73
- @dc.validate
74
- def _check_types(self) -> bool:
75
- hash(self)
76
- return isinstance(self.name, CacheableName)
77
-
78
-
79
- @dc.dataclass(frozen=True)
80
- class CacheResult(ta.Generic[T], lang.Final):
81
- hit: bool
82
- versions: CacheableVersionMap
83
- value: T
84
-
85
-
86
- ##
87
-
88
-
89
- class CacheableResolver(lang.Abstract):
90
- @abc.abstractmethod
91
- def resolve(self, name: CacheableName) -> Cacheable:
92
- raise NotImplementedError
File without changes