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.
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/__init__.py +9 -1
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_legacy_setup.py +11 -11
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_nonprintable.py +4 -3
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/backend.py +44 -37
- partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/builder.py +351 -0
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cargo.py +2 -2
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cmake.py +9 -15
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/meson.py +5 -13
- partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/process.py +42 -0
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/__init__.py +1 -1
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_base.py +75 -86
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_binary.py +6 -24
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_copy.py +7 -18
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_source.py +4 -21
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_targz.py +5 -12
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_zip.py +5 -14
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/file.py +2 -1
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/legacy.py +3 -2
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/load_module.py +7 -6
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/norms.py +35 -31
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/__init__.py +2 -1
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/match.py +42 -35
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/pattern.py +60 -54
- partis_pyproj-0.1.5.data/purelib/partis/pyproj/path/utils.py +94 -0
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pep.py +36 -35
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pkginfo.py +7 -16
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pptoml.py +125 -120
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pyproj.py +39 -36
- partis_pyproj-0.1.5.data/purelib/partis/pyproj/template.py +229 -0
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/validate.py +273 -268
- partis_pyproj-0.1.5.dist-info/METADATA +500 -0
- partis_pyproj-0.1.5.dist-info/RECORD +37 -0
- partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/builder.py +0 -267
- partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/process.py +0 -75
- partis_pyproj-0.1.4.data/purelib/partis/pyproj/path/utils.py +0 -40
- partis_pyproj-0.1.4.dist-info/METADATA +0 -51
- partis_pyproj-0.1.4.dist-info/RECORD +0 -36
- {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
- {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/LICENSE.txt +0 -0
- {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/WHEEL +0 -0
- {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/entry_points.txt +0 -0
- {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
|
+
|