mapres 2.4.dev5__tar.gz → 2.4.dev6__tar.gz
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.
- {mapres-2.4.dev5/src/mapres.egg-info → mapres-2.4.dev6}/PKG-INFO +1 -1
- {mapres-2.4.dev5 → mapres-2.4.dev6}/pyproject.toml +1 -1
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/resolver.py +40 -22
- {mapres-2.4.dev5 → mapres-2.4.dev6/src/mapres.egg-info}/PKG-INFO +1 -1
- {mapres-2.4.dev5 → mapres-2.4.dev6}/LICENSE +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/README.md +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/setup.cfg +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/__init__.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/cache.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/datamap.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/exceptions.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/layers.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/maps/__init__.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/maps/color.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres/maps/time.py +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres.egg-info/SOURCES.txt +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres.egg-info/dependency_links.txt +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres.egg-info/requires.txt +0 -0
- {mapres-2.4.dev5 → mapres-2.4.dev6}/src/mapres.egg-info/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ class MapResolver:
|
|
|
14
14
|
- syntax providers
|
|
15
15
|
- layered maps
|
|
16
16
|
- context substitution
|
|
17
|
-
-
|
|
17
|
+
- multi-pass resolution (controlled by `passes`)
|
|
18
18
|
- optional caching
|
|
19
19
|
'''
|
|
20
20
|
def __init__(
|
|
@@ -24,14 +24,16 @@ class MapResolver:
|
|
|
24
24
|
pipeline: list | None = None,
|
|
25
25
|
cache: bool = False,
|
|
26
26
|
cache_size: int = 1024,
|
|
27
|
-
|
|
27
|
+
passes_default: int = 5
|
|
28
28
|
):
|
|
29
29
|
self.layers = layers or LayerStack()
|
|
30
30
|
self.syntax = syntax or []
|
|
31
31
|
self.pipeline = pipeline or []
|
|
32
32
|
self.cache_enabled = cache
|
|
33
33
|
self.cache = LRUCache(cache_size) if cache else None
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
# unified recursion control
|
|
36
|
+
self.passes_default = passes_default
|
|
35
37
|
|
|
36
38
|
# pipeline
|
|
37
39
|
def _apply_pipeline(self, text: str, ctx: dict) -> str:
|
|
@@ -47,22 +49,26 @@ class MapResolver:
|
|
|
47
49
|
for provider in self.syntax:
|
|
48
50
|
pattern = provider.pattern
|
|
49
51
|
d = provider.get_map(ctx, self)
|
|
52
|
+
|
|
50
53
|
def repl(match: re.Match) -> str:
|
|
51
54
|
k = match.group(1)
|
|
52
55
|
if k not in d:
|
|
53
56
|
raise MissingKeyError(f'Missing key "{k}" in syntax provider {provider}')
|
|
54
57
|
return str(d[k])
|
|
58
|
+
|
|
55
59
|
try:
|
|
56
60
|
text = re.sub(pattern, repl, text)
|
|
57
61
|
except re.error as exc:
|
|
58
62
|
raise MapSyntaxError(
|
|
59
63
|
f'Regex error in syntax provider {provider}: {exc}'
|
|
60
64
|
) from exc
|
|
65
|
+
|
|
61
66
|
return text
|
|
62
67
|
|
|
63
68
|
# layered maps
|
|
64
69
|
def _apply_maps(self, text: str, ctx: dict, layerstack: LayerStack):
|
|
65
70
|
syntax_patterns = []
|
|
71
|
+
|
|
66
72
|
for m in layerstack:
|
|
67
73
|
if isinstance(m, type) and hasattr(m, 'as_map'):
|
|
68
74
|
m = m()
|
|
@@ -72,10 +78,13 @@ class MapResolver:
|
|
|
72
78
|
pattern = ctx.get('syntax')
|
|
73
79
|
if pattern:
|
|
74
80
|
syntax_patterns.append(pattern)
|
|
81
|
+
|
|
75
82
|
syntax_patterns = list(dict.fromkeys(syntax_patterns))
|
|
83
|
+
|
|
76
84
|
for pattern in syntax_patterns:
|
|
77
85
|
def repl(match: re.Match):
|
|
78
86
|
k = match.group(1)
|
|
87
|
+
|
|
79
88
|
for m in layerstack:
|
|
80
89
|
layer = m
|
|
81
90
|
if isinstance(layer, type) and hasattr(layer, 'as_map'):
|
|
@@ -86,6 +95,7 @@ class MapResolver:
|
|
|
86
95
|
d = layer
|
|
87
96
|
else:
|
|
88
97
|
continue
|
|
98
|
+
|
|
89
99
|
if k in d:
|
|
90
100
|
return str(d[k])
|
|
91
101
|
|
|
@@ -98,19 +108,23 @@ class MapResolver:
|
|
|
98
108
|
text = re.sub(pattern, repl, text)
|
|
99
109
|
except re.error as exc:
|
|
100
110
|
raise MapSyntaxError(f'Regex error in syntax pattern {pattern}: {exc}') from exc
|
|
111
|
+
|
|
101
112
|
return text
|
|
102
113
|
|
|
103
114
|
# context
|
|
104
115
|
def _apply_ctx(self, text: str, ctx: dict) -> str:
|
|
105
116
|
if not ctx:
|
|
106
117
|
return text
|
|
118
|
+
|
|
107
119
|
pattern = syntax.braces
|
|
108
120
|
d = ctx
|
|
121
|
+
|
|
109
122
|
def repl(match: re.Match) -> str:
|
|
110
123
|
k = match.group(1)
|
|
111
124
|
if k not in d:
|
|
112
125
|
raise MissingKeyError(f'Missing ctx key "{k}"')
|
|
113
126
|
return str(d[k])
|
|
127
|
+
|
|
114
128
|
try:
|
|
115
129
|
return re.sub(pattern, repl, text)
|
|
116
130
|
except re.error as exc:
|
|
@@ -124,21 +138,12 @@ class MapResolver:
|
|
|
124
138
|
text = self._apply_ctx(text, ctx)
|
|
125
139
|
return text
|
|
126
140
|
|
|
127
|
-
# recursion
|
|
128
|
-
def _recursive(self, text: str, ctx: dict, layerstack: LayerStack) -> str:
|
|
129
|
-
current = text
|
|
130
|
-
for _ in range(self.max_depth):
|
|
131
|
-
result = self._resolve_once(current, ctx, layerstack)
|
|
132
|
-
if result == current:
|
|
133
|
-
return result
|
|
134
|
-
current = result
|
|
135
|
-
return current
|
|
136
|
-
|
|
137
141
|
# public api
|
|
138
142
|
def res(
|
|
139
143
|
self,
|
|
140
144
|
text: str,
|
|
141
145
|
*,
|
|
146
|
+
passes: int | None = None,
|
|
142
147
|
extra_maps: dict | None = None,
|
|
143
148
|
override_maps: dict | None = None,
|
|
144
149
|
**ctx
|
|
@@ -147,22 +152,39 @@ class MapResolver:
|
|
|
147
152
|
Resolve a string using maps, syntax providers, pipeline, and context.
|
|
148
153
|
'''
|
|
149
154
|
try:
|
|
155
|
+
# determine number of passes
|
|
156
|
+
depth = passes if passes is not None else self.passes_default
|
|
157
|
+
|
|
158
|
+
if depth <= 0:
|
|
159
|
+
raise MapResError("passes must be >= 1")
|
|
160
|
+
|
|
161
|
+
# override maps
|
|
150
162
|
if override_maps is not None:
|
|
151
163
|
temp = LayerStack([Layer('override', override_maps, priority=0)])
|
|
152
|
-
return self.
|
|
164
|
+
return self._resolve_once(text, ctx, temp)
|
|
153
165
|
|
|
166
|
+
# extra maps
|
|
154
167
|
if extra_maps:
|
|
155
168
|
temp = self.layers.clone()
|
|
156
169
|
temp.add_layer(Layer('extra', extra_maps, priority=999))
|
|
157
|
-
return self.
|
|
170
|
+
return self._resolve_once(text, ctx, temp)
|
|
158
171
|
|
|
172
|
+
# caching
|
|
159
173
|
if self.cache_enabled:
|
|
160
174
|
key = (text, tuple(sorted(ctx.items())))
|
|
161
175
|
cached = self.cache.get(key)
|
|
162
176
|
if cached is not None:
|
|
163
177
|
return cached
|
|
164
178
|
|
|
165
|
-
|
|
179
|
+
# multi-pass resolution
|
|
180
|
+
current = text
|
|
181
|
+
for _ in range(depth):
|
|
182
|
+
new = self._resolve_once(current, ctx, self.layers)
|
|
183
|
+
if new == current:
|
|
184
|
+
break
|
|
185
|
+
current = new
|
|
186
|
+
|
|
187
|
+
result = current
|
|
166
188
|
|
|
167
189
|
if self.cache_enabled:
|
|
168
190
|
self.cache.set(key, result)
|
|
@@ -178,11 +200,11 @@ class MapResolver:
|
|
|
178
200
|
# global resolver
|
|
179
201
|
_DEFAULT = MapResolver()
|
|
180
202
|
|
|
181
|
-
def res(text: str, **ctx) -> str:
|
|
203
|
+
def res(text: str, passes=None, **ctx) -> str:
|
|
182
204
|
'''
|
|
183
205
|
Resolve using the global default resolver.
|
|
184
206
|
'''
|
|
185
|
-
return _DEFAULT.res(text, **ctx)
|
|
207
|
+
return _DEFAULT.res(text, passes=passes, **ctx)
|
|
186
208
|
|
|
187
209
|
def setGlobalMaps(maps, *, name=None, priority=0):
|
|
188
210
|
"""
|
|
@@ -193,9 +215,5 @@ def setGlobalMaps(maps, *, name=None, priority=0):
|
|
|
193
215
|
priority: layer priority (lower = earlier)
|
|
194
216
|
"""
|
|
195
217
|
layer_name = name or getattr(maps, "__name__", "global")
|
|
196
|
-
|
|
197
|
-
# Wrap datamap class or dict in a Layer
|
|
198
218
|
layer = Layer(layer_name, maps=[maps], priority=priority)
|
|
199
|
-
|
|
200
|
-
# Add to global resolver
|
|
201
219
|
_DEFAULT.layers.add_layer(layer)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|