partis-pyproj 0.1.4__py3-none-any.whl → 0.1.5__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.
Files changed (42) hide show
  1. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/__init__.py +9 -1
  2. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_legacy_setup.py +11 -11
  3. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_nonprintable.py +4 -3
  4. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/backend.py +44 -37
  5. partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/builder.py +351 -0
  6. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cargo.py +2 -2
  7. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cmake.py +9 -15
  8. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/meson.py +5 -13
  9. partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/process.py +42 -0
  10. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/__init__.py +1 -1
  11. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_base.py +75 -86
  12. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_binary.py +6 -24
  13. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_copy.py +7 -18
  14. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_source.py +4 -21
  15. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_targz.py +5 -12
  16. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_zip.py +5 -14
  17. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/file.py +2 -1
  18. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/legacy.py +3 -2
  19. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/load_module.py +7 -6
  20. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/norms.py +35 -31
  21. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/__init__.py +2 -1
  22. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/match.py +42 -35
  23. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/pattern.py +60 -54
  24. partis_pyproj-0.1.5.data/purelib/partis/pyproj/path/utils.py +94 -0
  25. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pep.py +36 -35
  26. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pkginfo.py +7 -16
  27. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pptoml.py +125 -120
  28. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pyproj.py +39 -36
  29. partis_pyproj-0.1.5.data/purelib/partis/pyproj/template.py +229 -0
  30. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/validate.py +273 -268
  31. partis_pyproj-0.1.5.dist-info/METADATA +500 -0
  32. partis_pyproj-0.1.5.dist-info/RECORD +37 -0
  33. partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/builder.py +0 -267
  34. partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/process.py +0 -75
  35. partis_pyproj-0.1.4.data/purelib/partis/pyproj/path/utils.py +0 -40
  36. partis_pyproj-0.1.4.dist-info/METADATA +0 -51
  37. partis_pyproj-0.1.4.dist-info/RECORD +0 -36
  38. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
  39. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/LICENSE.txt +0 -0
  40. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/WHEEL +0 -0
  41. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/entry_points.txt +0 -0
  42. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,229 @@
1
+ from __future__ import annotations
2
+ import re
3
+ from copy import copy
4
+ from pathlib import Path
5
+ from collections.abc import (
6
+ Sequence,
7
+ Mapping)
8
+ # from string import Template
9
+
10
+ from .validate import (
11
+ ValidationError,
12
+ FileOutsideRootError)
13
+ from .path import (
14
+ subdir,
15
+ resolve)
16
+
17
+ namespace_sep = re.compile(r"[\.\[\]]")
18
+
19
+ _idpattern = re.compile(r"""(?:
20
+ # references a template variable
21
+ [A-Z_][A-Z0-9_]* # Python identifier
22
+ (?:
23
+ \.[A-Z_][A-Z0-9_]* # attribute access by Python identifier
24
+ |
25
+ \[-?[0-9]+\])* # integer index (potentially negative)
26
+ |
27
+ \/ # forward slash separate for building path
28
+ |
29
+ (?<=\/)\.\. # double-dot (parent directory), must follow a slash
30
+ |
31
+ '[A-Z0-9\-_\.]+' # string literal, quotes to be removed
32
+ )+
33
+ """,
34
+ re.IGNORECASE|re.VERBOSE)
35
+
36
+ _group_pattern = re.compile(
37
+ # NOTE: handles escaped '$' and missing closing brace
38
+ r"\$(?:(?P<escaped>\$)|{ *(?P<braced>[^\s\}]+) *(?:}|(?P<unterminated>$|[^}])))",
39
+ re.IGNORECASE)
40
+
41
+ #===============================================================================
42
+ class TemplateError(ValidationError):
43
+ ...
44
+
45
+ #===============================================================================
46
+ class NamespaceError(ValidationError):
47
+ ...
48
+
49
+ #===============================================================================
50
+ class Template:
51
+ r"""Template support nested mappings and paths using :class:`Namespace`
52
+ """
53
+
54
+ #-----------------------------------------------------------------------------
55
+ def __init__(self, template):
56
+ self.template = template
57
+
58
+ #-----------------------------------------------------------------------------
59
+ def substitute(self, namespace: Mapping = None, /, **kwargs):
60
+ if namespace is None:
61
+ namespace = kwargs
62
+
63
+ elif kwargs:
64
+ raise TypeError("Cannot use both namespace and kwargs")
65
+
66
+ if not isinstance(namespace, Namespace):
67
+ namespace = Namespace(namespace)
68
+
69
+ def _handler(m):
70
+ if m.group('escaped'):
71
+ return '$'
72
+
73
+ if m.group('unterminated') is not None:
74
+ raise TemplateError(f"Unterminated template substitution: {m.group()}")
75
+
76
+ name = m.group('braced').strip()
77
+
78
+ if not _idpattern.fullmatch(name):
79
+ raise TemplateError(f"Invalid template substitution: {name}")
80
+
81
+ return str(namespace[name])
82
+
83
+ return _group_pattern.sub(_handler, self.template)
84
+
85
+ #===============================================================================
86
+ class Namespace(Mapping):
87
+ r"""Namespace (potentially nested) mapping for using with :class:`Template`
88
+
89
+ Parameters
90
+ ----------
91
+ data:
92
+ Mapping for names to values. Note that changes to the namespace will also
93
+ change the data. Making a shallow copy of the namespace also make a shallow
94
+ copy of the data.
95
+ root:
96
+ If given, absolute path to project root, used to resolve relative paths and ensure
97
+ any derived paths are within this parent directory.
98
+ """
99
+ __slots__ = ['data', 'root']
100
+
101
+ #-----------------------------------------------------------------------------
102
+ def __init__(self, data: Mapping, *, root: Path = None):
103
+ self.data = data
104
+ self.root = root
105
+
106
+ #-----------------------------------------------------------------------------
107
+ def __iter__(self):
108
+ return iter(self.data)
109
+
110
+ #-----------------------------------------------------------------------------
111
+ def __len__(self):
112
+ return len(self.data)
113
+
114
+ #-----------------------------------------------------------------------------
115
+ def __setitem__(self, name, value):
116
+ self.data[name] = value
117
+
118
+ #-----------------------------------------------------------------------------
119
+ def __getitem__(self, key):
120
+ raw_segments = key.split('/')
121
+ segments = []
122
+
123
+ for name in raw_segments:
124
+ if len(name) == 0 or name == '..':
125
+ # empty segment
126
+ segments.append(name)
127
+
128
+ elif name.startswith("'"):
129
+ # string literal, remove quotes
130
+ segments.append(name[1:-1])
131
+
132
+ else:
133
+ # variable name lookup
134
+ segments.append(self.lookup(name))
135
+
136
+ if len(segments) == 1:
137
+ return segments[0]
138
+
139
+ if self.root is None:
140
+ out = Path(*segments)
141
+
142
+ else:
143
+ root = self.root
144
+ out = type(root)(*segments)
145
+
146
+ if not out.is_absolute():
147
+ out = root/out
148
+
149
+ if isinstance(root, Path):
150
+ # NOTE: ignored if root is a pure path
151
+ out = resolve(out)
152
+
153
+ if not subdir(root, out, check = False):
154
+ raise FileOutsideRootError(
155
+ f"Must be within project root directory:"
156
+ f"\n file = \"{out}\"\n root = \"{root}\"")
157
+
158
+ return out
159
+
160
+ #-----------------------------------------------------------------------------
161
+ def __copy__(self):
162
+ cls = type(self)
163
+ obj = cls.__new__(cls)
164
+ obj.data = copy(self.data)
165
+ obj.root = self.root
166
+ return obj
167
+
168
+ #-----------------------------------------------------------------------------
169
+ def lookup(self, name):
170
+ parts = namespace_sep.split(name)
171
+ data = self.data
172
+
173
+ try:
174
+ cur = []
175
+
176
+ for k in parts:
177
+ if k:
178
+ if isinstance(data, Mapping):
179
+ data = data[k]
180
+ elif not isinstance(data, (str,bytes)) and isinstance(data, Sequence):
181
+ i = int(k)
182
+ data = data[i]
183
+ else:
184
+ raise NamespaceError(f"Expected mapping or sequence for '{k}': {type(data).__name__}")
185
+
186
+ cur.append(k)
187
+
188
+ except (KeyError,TypeError,IndexError) as e:
189
+ raise NamespaceError(f"Invalid key '{k}' of name '{name}': {str(e)}") from None
190
+
191
+ return data
192
+
193
+ #===============================================================================
194
+ def template_substitute(
195
+ value: bool|int|str|Path|Mapping|Sequence,
196
+ namespace: Mapping):
197
+ r"""Recursively performs template substitution based on type of value
198
+ """
199
+
200
+ if not isinstance(namespace, Namespace):
201
+ namespace = Namespace(namespace)
202
+
203
+ if isinstance(value, (bool,int)):
204
+ # just handles case where definitely not a template
205
+ return value
206
+
207
+ cls = type(value)
208
+
209
+ if isinstance(value, str):
210
+ return cls(Template(value).substitute(namespace))
211
+
212
+ if isinstance(value, Path):
213
+ return cls(*(
214
+ Template(v).substitute(namespace)
215
+ for v in value.parts))
216
+
217
+ if isinstance(value, Mapping):
218
+ return cls({
219
+ k: template_substitute(v, namespace)
220
+ for k,v in value.items()})
221
+
222
+ if isinstance(value, Sequence):
223
+ return cls([
224
+ template_substitute(v, namespace)
225
+ for v in value])
226
+
227
+
228
+ raise TypeError(f"Unknown template value type: {value}")
229
+