mapres 2.4.dev6__tar.gz → 3a2__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.
Files changed (30) hide show
  1. mapres-3a2/PKG-INFO +33 -0
  2. mapres-3a2/README.md +7 -0
  3. {mapres-2.4.dev6 → mapres-3a2}/pyproject.toml +6 -11
  4. {mapres-2.4.dev6 → mapres-3a2}/src/mapres/__init__.py +9 -1
  5. mapres-3a2/src/mapres/ast.py +29 -0
  6. mapres-3a2/src/mapres/datamap.py +160 -0
  7. mapres-3a2/src/mapres/evaluator.py +212 -0
  8. mapres-3a2/src/mapres/exceptions.py +8 -0
  9. mapres-3a2/src/mapres/layers.py +54 -0
  10. {mapres-2.4.dev6 → mapres-3a2}/src/mapres/maps/color.py +3 -9
  11. {mapres-2.4.dev6 → mapres-3a2}/src/mapres/maps/time.py +2 -2
  12. mapres-3a2/src/mapres/parser.py +112 -0
  13. mapres-3a2/src/mapres/resolver.py +161 -0
  14. mapres-3a2/src/mapres/tokenizer.py +325 -0
  15. mapres-3a2/src/mapres.egg-info/PKG-INFO +33 -0
  16. {mapres-2.4.dev6 → mapres-3a2}/src/mapres.egg-info/SOURCES.txt +4 -1
  17. mapres-2.4.dev6/PKG-INFO +0 -47
  18. mapres-2.4.dev6/README.md +0 -18
  19. mapres-2.4.dev6/src/mapres/datamap.py +0 -64
  20. mapres-2.4.dev6/src/mapres/exceptions.py +0 -8
  21. mapres-2.4.dev6/src/mapres/layers.py +0 -43
  22. mapres-2.4.dev6/src/mapres/resolver.py +0 -219
  23. mapres-2.4.dev6/src/mapres.egg-info/PKG-INFO +0 -47
  24. mapres-2.4.dev6/src/mapres.egg-info/requires.txt +0 -3
  25. {mapres-2.4.dev6 → mapres-3a2}/LICENSE +0 -0
  26. {mapres-2.4.dev6 → mapres-3a2}/setup.cfg +0 -0
  27. {mapres-2.4.dev6 → mapres-3a2}/src/mapres/cache.py +0 -0
  28. {mapres-2.4.dev6 → mapres-3a2}/src/mapres/maps/__init__.py +0 -0
  29. {mapres-2.4.dev6 → mapres-3a2}/src/mapres.egg-info/dependency_links.txt +0 -0
  30. {mapres-2.4.dev6 → mapres-3a2}/src/mapres.egg-info/top_level.txt +0 -0
mapres-3a2/PKG-INFO ADDED
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: mapres
3
+ Version: 3a2
4
+ Summary: A powerfull mapping utility
5
+ Author: iFamished
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/iFamished/mapres
8
+ Project-URL: Documentation, https://github.com/iFamished/mapres
9
+ Project-URL: Source, https://github.com/iFamished/mapres
10
+ Project-URL: Issues, https://github.com/iFamished/mapres/issues
11
+ Keywords: templating,automation,server-management
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Development Status :: 4 - Beta
19
+ Classifier: Intended Audience :: Developers
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Classifier: Topic :: System :: Systems Administration
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # MapRes
28
+ A powerful string-mapping utility for many use cases.
29
+
30
+ ## Installation
31
+ ```bash
32
+ pip install mapres
33
+ ```
mapres-3a2/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # MapRes
2
+ A powerful string-mapping utility for many use cases.
3
+
4
+ ## Installation
5
+ ```bash
6
+ pip install mapres
7
+ ```
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "mapres"
10
- version = "2.4-dev.6"
10
+ version = "3-alpha.2"
11
11
  description = "A powerfull mapping utility"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.9"
@@ -18,13 +18,8 @@ keywords = [
18
18
  "templating",
19
19
  "automation",
20
20
  "server-management",
21
- "hungerlib",
22
- ]
23
- dependencies = [
24
- "requests",
25
- "mcrcon",
26
- "PyYaml",
27
21
  ]
22
+ dependencies = []
28
23
  classifiers = [
29
24
  "Programming Language :: Python :: 3",
30
25
  "Programming Language :: Python :: 3.10",
@@ -42,10 +37,10 @@ classifiers = [
42
37
  text = "MIT"
43
38
 
44
39
  [project.urls]
45
- Homepage = "https://github.com/iFamishedX/mapres"
46
- Documentation = "https://github.com/iFamishedX/mapres"
47
- Source = "https://github.com/iFamishedX/mapres"
48
- Issues = "https://github.com/iFamishedX/mapres/issues"
40
+ Homepage = "https://github.com/iFamished/mapres"
41
+ Documentation = "https://github.com/iFamished/mapres"
42
+ Source = "https://github.com/iFamished/mapres"
43
+ Issues = "https://github.com/iFamished/mapres/issues"
49
44
 
50
45
  [tool.setuptools.packages.find]
51
46
  where = [
@@ -7,7 +7,7 @@ try:
7
7
  except PackageNotFoundError:
8
8
  __version__ = '0.0.0'
9
9
 
10
- from .resolver import MapResolver, res, setGlobalMaps
10
+ from .resolver import MapResolver, res, rprint, setGlobalMaps
11
11
  from .datamap import DataMap, datamap, syntax
12
12
  from .layers import Layer, LayerStack
13
13
 
@@ -28,6 +28,10 @@ maps = SimpleNamespace(
28
28
  time = time,
29
29
  )
30
30
 
31
+ utils = SimpleNamespace(
32
+ rprint = rprint,
33
+ )
34
+
31
35
  __all__ = [
32
36
  # core modules
33
37
  'MapResolver',
@@ -39,6 +43,9 @@ __all__ = [
39
43
  'LayerStack',
40
44
  'setGlobalMaps',
41
45
 
46
+ # utils
47
+ 'rprint',
48
+
42
49
  # maps
43
50
  'ColorMap',
44
51
  'ascii_colors',
@@ -49,4 +56,5 @@ __all__ = [
49
56
 
50
57
  # namespaces
51
58
  'maps',
59
+ 'utils',
52
60
  ]
@@ -0,0 +1,29 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class Node:
6
+ pass
7
+
8
+
9
+ @dataclass
10
+ class TemplateNode(Node):
11
+ children: list[Node]
12
+
13
+
14
+ @dataclass
15
+ class TextNode(Node):
16
+ text: str
17
+
18
+
19
+ @dataclass
20
+ class IdentNode(Node):
21
+ name: str
22
+ syntax: str
23
+
24
+
25
+ @dataclass
26
+ class CallNode(Node):
27
+ name: str # outer identifier
28
+ arg: Node # nested TemplateNode
29
+ syntax: str # which delimiter type produced it
@@ -0,0 +1,160 @@
1
+ from dataclasses import dataclass, fields, is_dataclass
2
+
3
+ # syntax identifiers
4
+ class syntax:
5
+ braces = 'braces' # {{value}}
6
+ dollars = 'dollars' # ${value}
7
+ angles = 'angles' # <value>
8
+ percents = 'percents' # %value%
9
+ pipes = 'pipes' # |value|
10
+ colons = 'colons' # :value:
11
+ at_tags = 'at_tags' # @value@
12
+ hash_tags = 'hash_tags' # #value#
13
+ paren_dollars = 'paren_dollars' # $(value)
14
+
15
+
16
+ # base datamap class
17
+ @dataclass
18
+ class DataMap:
19
+ '''
20
+ Core datamap class for mapres 3.
21
+
22
+ Responsibilities:
23
+ - hold fields
24
+ - optionally evaluate dynamic providers
25
+ - return a dict via as_map()
26
+
27
+ Syntax metadata (__syntax__) determines which delimiters
28
+ this map responds to (angle, brace, colon, percent, pipe, etc).
29
+
30
+ Mode metadata (__mode__) controls behavior:
31
+ - None: plain static map
32
+ - 'dynamic': values may be provided by callables/providers
33
+ - 'config': config-style maps (no dynamic evaluation)
34
+ - 'callable': map exposes callable methods for nested calls
35
+ '''
36
+ __syntax__: str = None
37
+ __mode__: str | None = None # 'dynamic', 'config', 'callable', or None
38
+ __missing_key__: str = 'error' # 'error', 'silent', 'placeholder', or 'raw'
39
+ __recursive__: bool = False
40
+ __max_depth__: int = 10
41
+
42
+ def as_map(self):
43
+ '''
44
+ Convert datamap fields into a dictionary.
45
+ If mode='dynamic', evaluate provider functions.
46
+
47
+ Callable mode does NOT change as_map() behavior; methods are
48
+ used by the evaluator, not exported as map entries.
49
+ '''
50
+ result = {}
51
+
52
+ # dataclass fields
53
+ for f in fields(self):
54
+ if not f.init or f.name.startswith('__'):
55
+ continue
56
+
57
+ val = getattr(self, f.name)
58
+
59
+ # dynamic provider support
60
+ if self.__mode__ == 'dynamic':
61
+ providers = getattr(self, 'providers', None)
62
+ if isinstance(providers, dict) and f.name in providers:
63
+ val = providers[f.name]
64
+ if callable(val):
65
+ val = val()
66
+
67
+ result[f.name] = val
68
+
69
+ # nested classes > nested dicts
70
+ for name, obj in self.__class__.__dict__.items():
71
+ if name.startswith('__'):
72
+ continue
73
+ if isinstance(obj, type):
74
+ result[name] = self._class_to_dict(obj)
75
+
76
+ return result
77
+
78
+ def _class_to_dict(self, cls):
79
+ out = {}
80
+
81
+ if is_dataclass(cls):
82
+ dummy = cls()
83
+ for f in fields(dummy):
84
+ if not f.init:
85
+ continue
86
+ out[f.name] = getattr(dummy, f.name)
87
+
88
+ for name, obj in cls.__dict__.items():
89
+ if name.startswith('__'):
90
+ continue
91
+ if isinstance(obj, type):
92
+ out[name] = self._class_to_dict(obj)
93
+
94
+ return out
95
+
96
+
97
+ def datamap(_cls=None, *, syntax=None, mode=None, missing_key=None, recursive=None, max_depth=None):
98
+ def wrap(cls):
99
+ cls.__syntax__ = syntax
100
+ cls.__mode__ = mode
101
+ cls.__missing_key__ = missing_key or 'error'
102
+ cls.__recursive__ = recursive or False
103
+ cls.__max_depth__ = max_depth or 10
104
+
105
+ namespace = dict(cls.__dict__)
106
+ namespace['__dict__'] = {}
107
+ rules_obj = namespace.get('rules', None)
108
+
109
+ cls = type(cls.__name__, (DataMap,), namespace)
110
+
111
+ if rules_obj is not None:
112
+ setattr(cls, 'rules', rules_obj)
113
+
114
+ return dataclass(frozen=False)(cls)
115
+
116
+ if _cls is not None and isinstance(_cls, type):
117
+ return wrap(_cls)
118
+
119
+ return wrap
120
+
121
+
122
+ # syntax-bound decorator factories
123
+ class _SyntaxFactory:
124
+ def __init__(self, syntax_name):
125
+ self._syntax = syntax_name
126
+
127
+ def __call__(self, _cls=None, **kwargs):
128
+ if _cls is not None and isinstance(_cls, type):
129
+ return datamap(_cls, syntax=self._syntax)
130
+ return datamap(syntax=self._syntax, **kwargs)
131
+
132
+ def config(self, _cls=None, **kwargs):
133
+ if _cls is not None and isinstance(_cls, type):
134
+ return datamap(_cls, syntax=self._syntax, mode='config')
135
+ return datamap(syntax=self._syntax, mode='config', **kwargs)
136
+
137
+ def dynamic(self, _cls=None, **kwargs):
138
+ if _cls is not None and isinstance(_cls, type):
139
+ return datamap(_cls, syntax=self._syntax, mode='dynamic')
140
+ return datamap(syntax=self._syntax, mode='dynamic', **kwargs)
141
+
142
+ def callable(self, _cls=None, **kwargs):
143
+ if _cls is not None and isinstance(_cls, type):
144
+ return datamap(_cls, syntax=self._syntax, mode='callable')
145
+ return datamap(syntax=self._syntax, mode='callable', **kwargs)
146
+
147
+
148
+ datamap.braces = _SyntaxFactory(syntax.braces)
149
+ datamap.dollars = _SyntaxFactory(syntax.dollars)
150
+ datamap.angles = _SyntaxFactory(syntax.angles)
151
+ datamap.percents = _SyntaxFactory(syntax.percents)
152
+ datamap.pipes = _SyntaxFactory(syntax.pipes)
153
+ datamap.colons = _SyntaxFactory(syntax.colons)
154
+ datamap.at_tags = _SyntaxFactory(syntax.at_tags)
155
+ datamap.hash_tags = _SyntaxFactory(syntax.hash_tags)
156
+ datamap.paren_dollars = _SyntaxFactory(syntax.paren_dollars)
157
+
158
+ datamap.config = datamap(mode='config')
159
+ datamap.dynamic = datamap(mode='dynamic')
160
+ datamap.callable = datamap(mode='callable')
@@ -0,0 +1,212 @@
1
+ from .ast import TemplateNode, TextNode, IdentNode, CallNode
2
+ from .tokenizer import Tokenizer
3
+ from .parser import Parser
4
+ from .exceptions import MapResError, MissingKeyError
5
+
6
+
7
+ class EvaluationError(MapResError):
8
+ pass
9
+
10
+
11
+ class Evaluator:
12
+ def __init__(self, layerstack, ctx=None, missing_key_mode: str = 'error'):
13
+ '''
14
+ layerstack: LayerStack instance
15
+ ctx: dict used as highest-priority map
16
+ missing_key_mode:
17
+ - 'error' → raise MissingKeyError
18
+ - 'silent' → return raw token unchanged
19
+ - 'placeholder' → return <missing:key>
20
+ - 'raw' → return raw token unchanged (alias of 'silent' at token level)
21
+ '''
22
+ self.layerstack = layerstack
23
+ self.ctx = ctx or {}
24
+ self._seen = set() # cycle detection
25
+ self.missing_key_mode = missing_key_mode
26
+
27
+ # public api
28
+ def evaluate(self, node):
29
+ return self._eval(node)
30
+
31
+ # internal dispatch
32
+ def _eval(self, node):
33
+ if isinstance(node, TemplateNode):
34
+ return self._eval_template(node)
35
+
36
+ if isinstance(node, TextNode):
37
+ return node.text
38
+
39
+ if isinstance(node, IdentNode):
40
+ return self._eval_ident(node)
41
+
42
+ if isinstance(node, CallNode):
43
+ return self._eval_call(node)
44
+
45
+ raise EvaluationError(f'Unknown AST node: {node}')
46
+
47
+ # re-resolve
48
+ def _re_resolve(self, text: str) -> str:
49
+ tokens = Tokenizer(text).tokenize()
50
+ ast = Parser(tokens).parse()
51
+ return self.evaluate(ast)
52
+
53
+ # template
54
+ def _eval_template(self, node: TemplateNode):
55
+ parts = []
56
+ for child in node.children:
57
+ parts.append(self._eval(child))
58
+ out = ''.join(parts)
59
+
60
+ # per-map recursion (syntax-scoped)
61
+ recursive_maps = [
62
+ m for m in self.layerstack.all_maps()
63
+ if getattr(m, '__recursive__', False) is True
64
+ ]
65
+
66
+ if not recursive_maps:
67
+ return out
68
+
69
+ max_depth = max(getattr(m, '__max_depth__', 10) for m in recursive_maps)
70
+
71
+ seen = set()
72
+ depth = 0
73
+
74
+ while depth < max_depth:
75
+ new_out = self._re_resolve(out)
76
+
77
+ if new_out == out:
78
+ break
79
+
80
+ if new_out in seen:
81
+ raise EvaluationError('Cycle detected in recursive map resolution')
82
+
83
+ seen.add(new_out)
84
+ out = new_out
85
+ depth += 1
86
+
87
+ return out
88
+
89
+ # identifier
90
+ def _eval_ident(self, node: IdentNode):
91
+ name = node.name
92
+
93
+ # cycle detection
94
+ if name in self._seen:
95
+ raise EvaluationError(f'Cycle detected in identifier \'{name}\'')
96
+ self._seen.add(name)
97
+
98
+ parts = name.split('.')
99
+
100
+ # 1. ctx first
101
+ val = self._lookup_hierarchical(self.ctx, parts)
102
+ if val is not None:
103
+ self._seen.remove(name)
104
+ return str(val)
105
+
106
+ # 2. syntax-bound maps
107
+ for m in self.layerstack.all_maps():
108
+ if getattr(m, '__syntax__', None) not in (None, node.syntax):
109
+ continue
110
+
111
+ d = self._map_to_dict(m)
112
+ val = self._lookup_hierarchical(d, parts)
113
+ if val is not None:
114
+ self._seen.remove(name)
115
+ return str(val)
116
+
117
+ self._seen.remove(name)
118
+ return self._handle_missing_ident(node)
119
+
120
+ # call (nested token)
121
+ def _eval_call(self, node: CallNode):
122
+ outer = node.name
123
+
124
+ if outer in self._seen:
125
+ raise EvaluationError(f'Cycle detected in call \'{outer}\'')
126
+ self._seen.add(outer)
127
+
128
+ arg_value = self._eval(node.arg)
129
+ parts = [outer, arg_value]
130
+
131
+ # ctx first
132
+ val = self._lookup_hierarchical(self.ctx, parts)
133
+ if val is not None:
134
+ self._seen.remove(outer)
135
+ return str(val)
136
+
137
+ # syntax-bound maps
138
+ for m in self.layerstack.all_maps():
139
+ if getattr(m, '__syntax__', None) not in (None, node.syntax):
140
+ continue
141
+
142
+ d = self._map_to_dict(m)
143
+ val = self._lookup_hierarchical(d, parts)
144
+ if val is not None:
145
+ self._seen.remove(outer)
146
+ return str(val)
147
+
148
+ self._seen.remove(outer)
149
+ return self._handle_missing_call(node, arg_value)
150
+
151
+ # helpers
152
+ def _map_to_dict(self, m):
153
+ if hasattr(m, 'as_map'):
154
+ return m.as_map()
155
+ if isinstance(m, dict):
156
+ return m
157
+ raise EvaluationError(f'Invalid map object: {m}')
158
+
159
+ def _lookup_hierarchical(self, root, parts):
160
+ cur = root
161
+ for p in parts:
162
+ if not isinstance(cur, dict):
163
+ return None
164
+ if p not in cur:
165
+ return None
166
+ cur = cur[p]
167
+ return cur
168
+
169
+ # missing-key handling (token-level)
170
+ def _delims_for_syntax(self, syntax_name: str):
171
+ table = {
172
+ 'angles': ('<', '>'),
173
+ 'braces': ('{{', '}}'),
174
+ 'dollars': ('${', '}'),
175
+ 'percents': ('%', '%'),
176
+ 'pipes': ('|', '|'),
177
+ 'colons': (':', ':'),
178
+ }
179
+ return table.get(syntax_name, ('<', '>'))
180
+
181
+ def _handle_missing_ident(self, node: IdentNode):
182
+ name = node.name
183
+ open_, close_ = self._delims_for_syntax(node.syntax)
184
+ raw_token = f'{open_}{name}{close_}'
185
+
186
+ mode = self.missing_key_mode or 'error'
187
+
188
+ if mode in ('silent', 'raw'):
189
+ # token-level fallback: leave the unresolved token as-is
190
+ return raw_token
191
+
192
+ if mode == 'placeholder':
193
+ return f'<missing:{name}>'
194
+
195
+ # default: error
196
+ raise MissingKeyError(name, token=raw_token)
197
+
198
+ def _handle_missing_call(self, node: CallNode, arg_value: str):
199
+ outer = node.name
200
+ open_, close_ = self._delims_for_syntax(node.syntax)
201
+ raw_token = f'{open_}{outer}({arg_value}){close_}'
202
+ key_repr = f'{outer}({arg_value})'
203
+
204
+ mode = self.missing_key_mode or 'error'
205
+
206
+ if mode in ('silent', 'raw'):
207
+ return raw_token
208
+
209
+ if mode == 'placeholder':
210
+ return f'<missing:{key_repr}>'
211
+
212
+ raise MissingKeyError(key_repr, token=raw_token)
@@ -0,0 +1,8 @@
1
+ class MapResError(Exception):
2
+ pass
3
+
4
+ class MissingKeyError(MapResError):
5
+ def __init__(self, key: str, token: str | None = None):
6
+ super().__init__(f"Missing key {key!r}")
7
+ self.key = key
8
+ self.token = token or key
@@ -0,0 +1,54 @@
1
+ class Layer:
2
+ """
3
+ A single layer containing one or more maps.
4
+ Priority determines resolution order.
5
+ Lower priority = earlier lookup.
6
+ """
7
+ def __init__(self, maps=None, priority=0):
8
+ self.maps = maps or []
9
+ self.priority = priority
10
+
11
+ def add(self, m):
12
+ self.maps.append(m)
13
+
14
+ def __iter__(self):
15
+ return iter(self.maps)
16
+
17
+
18
+ class LayerStack:
19
+ """
20
+ A simple ordered list of layers.
21
+ No names, no dicts, no complexity.
22
+ Just priority-sorted layers.
23
+ """
24
+ def __init__(self):
25
+ self.layers = [] # list of Layer objects
26
+
27
+ def add_layer(self, maps, priority=0):
28
+ """
29
+ Add a new layer containing one or more maps.
30
+ maps: datamap instance, datamap class, or dict
31
+ priority: lower = earlier lookup
32
+ """
33
+ layer = Layer(
34
+ maps=[maps] if not isinstance(maps, list) else maps,
35
+ priority=priority
36
+ )
37
+ self.layers.append(layer)
38
+ self.layers.sort(key=lambda l: l.priority)
39
+
40
+ def all_maps(self):
41
+ """
42
+ Yield all maps in priority order.
43
+ """
44
+ for layer in self.layers:
45
+ for m in layer:
46
+ yield m
47
+
48
+ def clone(self):
49
+ """
50
+ Return a shallow copy of the layerstack.
51
+ """
52
+ new = LayerStack()
53
+ new.layers = list(self.layers)
54
+ return new
@@ -1,6 +1,6 @@
1
- from mapres.datamap import datamap, syntax
1
+ from mapres.datamap import datamap
2
2
 
3
- @datamap(syntax=syntax.angles) # produces <>
3
+ @datamap.angles
4
4
  class ColorMap:
5
5
  black: str = '\033[30m'
6
6
  dark_blue: str = '\033[34m'
@@ -22,13 +22,8 @@ class ColorMap:
22
22
  bold: str = '\033[1m'
23
23
  italic: str = '\033[3m'
24
24
 
25
- def as_dict(self):
26
- return {f'<{k}>': getattr(self, k) for k in self.__dataclass_fields__}
27
-
28
- # ASCII color map
29
25
  ascii_colors = ColorMap()
30
26
 
31
- # Minecraft color map
32
27
  mc_colors = ColorMap(
33
28
  black='§0',
34
29
  dark_blue='§1',
@@ -51,7 +46,6 @@ mc_colors = ColorMap(
51
46
  italic='§o'
52
47
  )
53
48
 
54
- # Color strip map
55
49
  strip_colors = ColorMap(
56
50
  black='',
57
51
  dark_blue='',
@@ -72,4 +66,4 @@ strip_colors = ColorMap(
72
66
  reset='',
73
67
  bold='',
74
68
  italic=''
75
- )
69
+ )
@@ -1,6 +1,6 @@
1
1
  from datetime import datetime, timezone
2
2
  from zoneinfo import ZoneInfo
3
- from mapres.datamap import datamap, syntax
3
+ from mapres.datamap import datamap
4
4
 
5
5
 
6
6
  def safe_zoneinfo(tz: str):
@@ -10,7 +10,7 @@ def safe_zoneinfo(tz: str):
10
10
  return timezone.utc
11
11
 
12
12
 
13
- @datamap(syntax=syntax.percents, mode='dynamic')
13
+ @datamap.percents.dynamic
14
14
  class TimeMap:
15
15
  _default_tz = safe_zoneinfo("America/Chicago")
16
16