partis-pyproj 0.1.4__py3-none-any.whl → 0.1.6__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.6.data}/purelib/partis/pyproj/__init__.py +9 -1
  2. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/_legacy_setup.py +11 -11
  3. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/_nonprintable.py +4 -3
  4. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/backend.py +44 -37
  5. partis_pyproj-0.1.6.data/purelib/partis/pyproj/builder/builder.py +351 -0
  6. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/cargo.py +2 -2
  7. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/cmake.py +9 -15
  8. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/builder/meson.py +5 -13
  9. partis_pyproj-0.1.6.data/purelib/partis/pyproj/builder/process.py +42 -0
  10. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/__init__.py +1 -1
  11. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_base.py +75 -86
  12. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_binary.py +6 -24
  13. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_copy.py +7 -18
  14. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_source.py +4 -21
  15. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_targz.py +5 -12
  16. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/dist_file/dist_zip.py +5 -14
  17. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/file.py +2 -1
  18. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/legacy.py +3 -2
  19. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/load_module.py +7 -6
  20. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/norms.py +35 -31
  21. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/path/__init__.py +2 -1
  22. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/path/match.py +42 -35
  23. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/path/pattern.py +60 -54
  24. partis_pyproj-0.1.6.data/purelib/partis/pyproj/path/utils.py +94 -0
  25. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pep.py +36 -35
  26. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pkginfo.py +7 -16
  27. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pptoml.py +125 -120
  28. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/pyproj.py +47 -36
  29. partis_pyproj-0.1.6.data/purelib/partis/pyproj/template.py +229 -0
  30. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.6.data}/purelib/partis/pyproj/validate.py +273 -268
  31. partis_pyproj-0.1.6.dist-info/METADATA +500 -0
  32. partis_pyproj-0.1.6.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.6.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
  39. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/LICENSE.txt +0 -0
  40. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/WHEEL +0 -0
  41. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/entry_points.txt +0 -0
  42. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.6.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,11 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import os.path as osp
3
4
  import sys
4
5
  import shutil
5
- import logging
6
+ from logging import (
7
+ getLogger,
8
+ Logger)
6
9
  import tempfile
7
10
  from copy import copy, deepcopy
8
11
  from collections.abc import (
@@ -27,7 +30,6 @@ except ImportError:
27
30
  from .pkginfo import (
28
31
  PkgInfoReq,
29
32
  PkgInfo )
30
-
31
33
  from .validate import (
32
34
  ValidationWarning,
33
35
  ValidationError,
@@ -38,17 +40,16 @@ from .validate import (
38
40
  valid,
39
41
  restrict,
40
42
  mapget )
41
-
42
43
  from .norms import (
43
44
  scalar_list,
44
45
  norm_bool,
45
46
  norm_path_to_os,
46
47
  norm_path )
47
-
48
48
  from .pep import (
49
49
  purelib_compat_tags,
50
50
  platlib_compat_tags )
51
-
51
+ from .path import (
52
+ resolve)
52
53
  from .load_module import (
53
54
  EntryPointError,
54
55
  EntryPoint,
@@ -68,7 +69,7 @@ from .builder import (
68
69
  from .dist_file import (
69
70
  dist_copy )
70
71
 
71
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
72
+ #===============================================================================
72
73
  class PyProjBase:
73
74
  """Minimal build system for a Python project
74
75
 
@@ -77,21 +78,21 @@ class PyProjBase:
77
78
 
78
79
  Parameters
79
80
  ----------
80
- root : str | pathlib.Path
81
+ root :
81
82
  Path to the root project directory containing 'pyproject.toml'.
82
- logger : logging.Logger
83
+ logger :
83
84
  Parent logger to use when processing project.
84
85
 
85
86
  """
86
87
  #-----------------------------------------------------------------------------
87
88
  def __init__( self, *,
88
- root,
89
- config_settings = None,
90
- logger = None ):
89
+ root: Path,
90
+ config_settings: dict|None = None,
91
+ logger: Logger|None = None ):
91
92
 
92
- self.logger = logger or logging.getLogger( __name__ )
93
+ self.logger = logger or getLogger( __name__ )
93
94
 
94
- self.root = Path(root).resolve()
95
+ self.root = resolve(Path(root))
95
96
 
96
97
  self.pptoml_file = self.root / 'pyproject.toml'
97
98
 
@@ -117,6 +118,7 @@ class PyProjBase:
117
118
 
118
119
  #...........................................................................
119
120
  # construct a validator from the tool.pyproj.config table
121
+ # NOTE: only really used in the event that "config_settings" are passed to the backend
120
122
  config_default = dict()
121
123
 
122
124
  for k,v in self.pyproj.config.items():
@@ -129,14 +131,12 @@ class PyProjBase:
129
131
  else:
130
132
  config_default[k] = valid(v, type(v))
131
133
 
132
- class valid_config(valid_dict):
133
- _allow_keys = list()
134
- _default = config_default
134
+ class valid_config_settings(valid_dict):
135
+ allow_keys = list()
136
+ default = config_default
135
137
 
136
138
  with validating( key = 'config_settings' ):
137
- _config_settings = valid_config(config_settings or dict())
138
-
139
- self.pyproj.config = _config_settings
139
+ self._config_settings = valid_config_settings(config_settings or dict())
140
140
 
141
141
  #...........................................................................
142
142
  self.build_backend = mapget( self.pptoml,
@@ -196,10 +196,14 @@ class PyProjBase:
196
196
 
197
197
  #-----------------------------------------------------------------------------
198
198
  @property
199
- def config(self):
200
- """:class:`partis.pyproj.pptoml.pyproj_config`
199
+ def config_settings(self):
200
+ """Config settings passed to backend, or defaults from ``pyproj.config``
201
201
  """
202
- return self._pptoml.tool.pyproj.config
202
+ return self._config_settings
203
+
204
+ #-----------------------------------------------------------------------------
205
+ # alias for backward compatibility
206
+ config = config_settings
203
207
 
204
208
  #-----------------------------------------------------------------------------
205
209
  @property
@@ -218,6 +222,9 @@ class PyProjBase:
218
222
  These are no longer restricted to meson, but this attribute kept for backward
219
223
  compatability.
220
224
 
225
+ Inplace changes to the returned object are not propagated back to the target
226
+ configuration.
227
+
221
228
  """
222
229
  targets = self._pptoml.tool.pyproj.targets
223
230
 
@@ -227,6 +234,7 @@ class PyProjBase:
227
234
  meson = dict(targets[0])
228
235
  meson.pop('entry')
229
236
  meson.pop('work_dir')
237
+ meson.pop('env')
230
238
  meson['compile'] = meson.pop('enabled')
231
239
  return pyproj_meson(meson)
232
240
 
@@ -297,9 +305,9 @@ class PyProjBase:
297
305
  dynamic = project.dynamic
298
306
 
299
307
  self.prep_entrypoint(
300
- name = f"tool.pyproj.prep",
308
+ name = "tool.pyproj.prep",
301
309
  obj = self.pyproj,
302
- logger = self.logger.getChild( f"prep" ) )
310
+ logger = self.logger.getChild("prep") )
303
311
 
304
312
  # NOTE: check that any dynamic meta-data is defined after prep
305
313
  for k in dynamic:
@@ -327,9 +335,9 @@ class PyProjBase:
327
335
  """
328
336
 
329
337
  self.prep_entrypoint(
330
- name = f"tool.pyproj.dist.prep",
338
+ name = "tool.pyproj.dist.prep",
331
339
  obj = self.dist,
332
- logger = self.logger.getChild( f"dist.prep" ) )
340
+ logger = self.logger.getChild("dist.prep") )
333
341
 
334
342
 
335
343
  #-----------------------------------------------------------------------------
@@ -338,9 +346,9 @@ class PyProjBase:
338
346
  """
339
347
 
340
348
  self.prep_entrypoint(
341
- name = f"tool.pyproj.dist.source.prep",
349
+ name = "tool.pyproj.dist.source.prep",
342
350
  obj = self.dist.source,
343
- logger = self.logger.getChild( f"dist.source.prep" ) )
351
+ logger = self.logger.getChild("dist.source.prep") )
344
352
 
345
353
  #-----------------------------------------------------------------------------
346
354
  def dist_source_copy( self, *, dist ):
@@ -352,7 +360,7 @@ class PyProjBase:
352
360
  Builder used to write out source distribution files
353
361
  """
354
362
 
355
- with validating( key = f'tool.pyproj.dist.source' ):
363
+ with validating( key = 'tool.pyproj.dist.source'):
356
364
  dist_copy(
357
365
  base_path = dist.named_dirs['root'],
358
366
  include = self.source.copy,
@@ -362,9 +370,9 @@ class PyProjBase:
362
370
  logger = self.logger )
363
371
 
364
372
  if self.add_legacy_setup:
365
- with validating( key = f'add_legacy_setup' ):
373
+ with validating(key = 'add_legacy_setup'):
366
374
 
367
- self.logger.info(f"generating legacy 'setup.py'")
375
+ self.logger.info("generating legacy 'setup.py'")
368
376
  legacy_setup_content( self, dist )
369
377
 
370
378
  #-----------------------------------------------------------------------------
@@ -372,16 +380,19 @@ class PyProjBase:
372
380
  """Prepares project files for a binary distribution
373
381
  """
374
382
 
375
- with Builder(
383
+ builder = Builder(
376
384
  pyproj = self,
377
385
  root = self.root,
378
386
  targets = self.targets,
379
- logger = self.logger.getChild( f"targets" ) ):
387
+ logger = self.logger.getChild("targets"))
388
+
389
+ with builder:
390
+ builder.build_targets()
380
391
 
381
392
  self.prep_entrypoint(
382
- name = f"tool.pyproj.dist.binary.prep",
393
+ name = "tool.pyproj.dist.binary.prep",
383
394
  obj = self.binary,
384
- logger = self.logger.getChild( f"dist.binary.prep" ) )
395
+ logger = self.logger.getChild("dist.binary.prep") )
385
396
 
386
397
  self.logger.debug(f"Compatibility tags after dist.binary.prep: {self.binary.compat_tags}")
387
398
 
@@ -396,7 +407,7 @@ class PyProjBase:
396
407
  """
397
408
 
398
409
 
399
- with validating( key = f'tool.pyproj.dist.binary' ):
410
+ with validating(key = 'tool.pyproj.dist.binary'):
400
411
  ignore = self.dist.ignore + self.dist.binary.ignore
401
412
 
402
413
  dist_copy(
@@ -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
+