partis-pyproj 0.1.8__py3-none-any.whl → 0.2.0__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 (40) hide show
  1. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/backend.py +2 -2
  2. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/builder.py +19 -1
  3. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/download.py +31 -6
  4. partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_copy.py +187 -0
  5. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/norms.py +5 -3
  6. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/pep.py +78 -36
  7. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/pkginfo.py +2 -1
  8. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/pptoml.py +47 -2
  9. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/pyproj.py +3 -3
  10. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/validate.py +49 -31
  11. {partis_pyproj-0.1.8.dist-info → partis_pyproj-0.2.0.dist-info}/METADATA +17 -11
  12. partis_pyproj-0.2.0.dist-info/RECORD +38 -0
  13. partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_copy.py +0 -138
  14. partis_pyproj-0.1.8.dist-info/RECORD +0 -38
  15. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/__init__.py +0 -0
  16. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/_legacy_setup.py +0 -0
  17. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/_nonprintable.py +0 -0
  18. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
  19. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/cargo.py +0 -0
  20. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/cmake.py +0 -0
  21. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/meson.py +0 -0
  22. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/builder/process.py +0 -0
  23. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/dist_file/__init__.py +0 -0
  24. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/dist_file/dist_base.py +0 -0
  25. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/dist_file/dist_binary.py +0 -0
  26. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/dist_file/dist_source.py +0 -0
  27. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/dist_file/dist_targz.py +0 -0
  28. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/dist_file/dist_zip.py +0 -0
  29. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/file.py +0 -0
  30. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/legacy.py +0 -0
  31. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/load_module.py +0 -0
  32. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/path/__init__.py +0 -0
  33. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/path/match.py +0 -0
  34. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/path/pattern.py +0 -0
  35. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/path/utils.py +0 -0
  36. {partis_pyproj-0.1.8.data → partis_pyproj-0.2.0.data}/purelib/partis/pyproj/template.py +0 -0
  37. {partis_pyproj-0.1.8.dist-info → partis_pyproj-0.2.0.dist-info}/LICENSE.txt +0 -0
  38. {partis_pyproj-0.1.8.dist-info → partis_pyproj-0.2.0.dist-info}/WHEEL +0 -0
  39. {partis_pyproj-0.1.8.dist-info → partis_pyproj-0.2.0.dist-info}/entry_points.txt +0 -0
  40. {partis_pyproj-0.1.8.dist-info → partis_pyproj-0.2.0.dist-info}/top_level.txt +0 -0
@@ -53,7 +53,7 @@ def backend_init(
53
53
 
54
54
  if not root_logger.handlers:
55
55
  basicConfig(
56
- level = logging.INFO,
56
+ level = os.environ.get('PARTIS_PYPROJ_LOGLEVEL', 'INFO').upper(),
57
57
  format = "{message}",
58
58
  style = "{" )
59
59
 
@@ -232,7 +232,7 @@ def build_wheel(
232
232
 
233
233
  except ValidationError as e:
234
234
  known_exception_type = copy(e)
235
- raise known_exception_type from None
235
+ raise known_exception_type from e.__cause__
236
236
 
237
237
  return dist.outname
238
238
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  import os
3
3
  import os.path as osp
4
+ import sys
4
5
  import tempfile
5
6
  import sysconfig
6
7
  import re
@@ -30,6 +31,23 @@ from ..pptoml import pyproj_targets
30
31
 
31
32
  ERROR_REC = re.compile(r"error:", re.I)
32
33
 
34
+ pyexe = sys.executable
35
+
36
+ try:
37
+ pyexe = osp.realpath(pyexe)
38
+ except Exception:
39
+ ...
40
+
41
+ # fallback for commonly needed config. variables, but sometimes are not set
42
+ _sysconfig_vars_alt = {
43
+ 'LIBDEST': sysconfig.get_path('stdlib'),
44
+ 'BINLIBDEST': sysconfig.get_path('platstdlib'),
45
+ 'INCLUDEPY': sysconfig.get_path('include'),
46
+ 'EXENAME': pyexe,
47
+ 'BINDIR': osp.dirname(pyexe)}
48
+
49
+ _sysconfig_vars = _sysconfig_vars_alt|sysconfig.get_config_vars()
50
+
33
51
  #===============================================================================
34
52
  class BuildCommandError(ValidationError):
35
53
  pass
@@ -71,7 +89,7 @@ class Builder:
71
89
  'targets': targets,
72
90
  'env': os.environ,
73
91
  'tmpdir': self.tmpdir,
74
- 'config_vars': sysconfig.get_config_vars()},
92
+ 'config_vars': _sysconfig_vars},
75
93
  root=root,
76
94
  # better way for builders to whitelist templated directories?
77
95
  dirs=[self.tmpdir, Path(tempfile.gettempdir())/'partis-pyproj-downloads'])
@@ -20,6 +20,18 @@ from ..norms import b64_nopad, nonempty_str
20
20
  # replace runs of non-alphanumeric, dot, dash, or underscore
21
21
  _filename_subs = re.compile(r'[^a-z0-9\.\-\_]+', re.I)
22
22
 
23
+ try:
24
+ # prefer user home directory to avoid clashing in global "tmp" directory
25
+ CACHE_DIR = Path.home()/'.cache'/'partis-pyproj'
26
+
27
+ except RuntimeError:
28
+ # use global temporary directory, suffixed by username to try to avoid conficts
29
+ # between users
30
+ import getpass
31
+ username = getpass.getuser()
32
+ tmp_dir = tempfile.gettempdir()
33
+ CACHE_DIR = Path(tmp_dir)/f'.cache-partis-pyproj-{username}'
34
+
23
35
  #===============================================================================
24
36
  def download(
25
37
  pyproj,
@@ -94,7 +106,12 @@ def download(
94
106
  try:
95
107
  logger.info(f"- downloading: {url} -> {tmp_file}")
96
108
 
97
- with requests.get(url, stream=True) as req, tmp_file.open('wb') as fp:
109
+ req = requests.get(url, stream=True)
110
+
111
+ if not req.ok:
112
+ req.raise_for_status()
113
+
114
+ with req, tmp_file.open('wb') as fp:
98
115
  for chunk in req.iter_content(chunk_size=chunk_size):
99
116
  if chunk:
100
117
  fp.write(chunk)
@@ -107,6 +124,9 @@ def download(
107
124
  logger.info(f"- {size/1e6:,.1f} MB")
108
125
  last_size = size
109
126
 
127
+ if size == 0:
128
+ raise ValidationError(f"Downloaded file had zero size: {url}")
129
+
110
130
  logger.info(f"- complete {size/1e6:,.1f} MB")
111
131
 
112
132
  if hash:
@@ -137,18 +157,24 @@ def download(
137
157
  out_file.symlink_to(cache_file)
138
158
 
139
159
  if extract:
140
- logger.info(f"- extracting: {cache_file} -> {build_dir}")
160
+ out_dir = build_dir
161
+
162
+ if isinstance(extract, (str,Path)):
163
+ out_dir = extract
164
+
165
+ logger.info(f"- extracting: {cache_file} -> {out_dir}")
166
+
141
167
  with tarfile.open(cache_file, 'r:*') as fp:
142
168
  if sys.version_info >= (3, 12):
143
169
  # 'filter' argument added, controls behavior of extract
144
170
  fp.extractall(
145
- path=build_dir,
171
+ path=out_dir,
146
172
  members=None,
147
173
  numeric_owner=False,
148
174
  filter='tar')
149
175
  else:
150
176
  fp.extractall(
151
- path=build_dir,
177
+ path=out_dir,
152
178
  members=None,
153
179
  numeric_owner=False)
154
180
 
@@ -161,7 +187,6 @@ def _cached_download(url: str, checksum: str) -> Path:
161
187
  if not checksum:
162
188
  checksum = '0'
163
189
 
164
- cache_dir = Path(tempfile.gettempdir())/'partis-pyproj-downloads'
165
190
  name = url.split('/')[-1]
166
191
  _url = url
167
192
 
@@ -178,7 +203,7 @@ def _cached_download(url: str, checksum: str) -> Path:
178
203
  url_dirname = _filename_subs.sub('_', _url)
179
204
  url_filename = f"{short}-" + _filename_subs.sub('_', name)
180
205
 
181
- url_dir = cache_dir/url_dirname
206
+ url_dir = CACHE_DIR/url_dirname
182
207
  url_dir.mkdir(exist_ok=True, parents=True)
183
208
 
184
209
  file = url_dir/url_filename
@@ -0,0 +1,187 @@
1
+ from __future__ import annotations
2
+ import os
3
+ import glob
4
+ from pathlib import (
5
+ Path)
6
+ import logging
7
+ from ..validate import (
8
+ FileOutsideRootError,
9
+ ValidationError,
10
+ validating )
11
+ from ..path import (
12
+ PathFilter,
13
+ subdir,
14
+ combine_ignore_patterns,
15
+ resolve)
16
+ from ..pptoml import (
17
+ pyproj_dist_copy)
18
+
19
+ #===============================================================================
20
+ def _rglob(pattern: str, *, root_dir: Path) -> list[Path]:
21
+ # detect if glob will be recursive by finding '**' in the pattern
22
+ recursive = '**' in pattern
23
+
24
+ # NOTE: root_dir added in Python 3.10
25
+ cwd = Path.cwd()
26
+
27
+ try:
28
+ os.chdir(root_dir)
29
+ matches = glob.glob(pattern, recursive = recursive)
30
+
31
+ matches = [Path(m) for m in matches]
32
+
33
+ if recursive:
34
+ # don't match directories in recursive mode, since copying a parent
35
+ # directory negates the need to recurse
36
+ matches = [m for m in matches if not m.is_dir()]
37
+
38
+ finally:
39
+ os.chdir(cwd)
40
+
41
+ return matches
42
+
43
+ #===============================================================================
44
+ def dist_iter(*,
45
+ copy_items: list[pyproj_dist_copy],
46
+ ignore: list[str],
47
+ root: Path,
48
+ logger: logging.Logger):
49
+
50
+ patterns = PathFilter(
51
+ patterns = ignore )
52
+
53
+
54
+
55
+ for i, incl in enumerate(copy_items):
56
+ src = incl.src
57
+ dst = incl.dst
58
+ _ignore = incl.ignore
59
+
60
+ _ignore_patterns = combine_ignore_patterns(
61
+ patterns,
62
+ PathFilter(
63
+ patterns = _ignore,
64
+ start = src ) )
65
+
66
+ if not incl.include:
67
+ # each copy specifies a single path
68
+ # logger.debug(f" - from: {src}\n - to: {dst}")
69
+ yield ( i, src, dst, _ignore_patterns, True )
70
+
71
+ else:
72
+ # each copy can result in many paths
73
+ for incl_pattern in incl.include:
74
+ # produce list of possible copies by glob pattern, relative to 'src'
75
+ matches = _rglob(incl_pattern.glob, root_dir=src)
76
+ # logger.debug(f"- glob: {len(matches)} matches with pattern {incl_pattern.glob!r} in {str(src)!r}")
77
+
78
+ if not matches:
79
+ logger.warning(f"Copy pattern did not yield any files: {incl_pattern.glob!r}")
80
+ continue
81
+
82
+ for i, match in enumerate(matches):
83
+ parent = match.parent
84
+ src_filename = match.name
85
+
86
+ if _ignore_patterns(src/parent, [src_filename]):
87
+ # Only filter by ignore pattern if this path was part of a glob
88
+ # logger.debug(f" - ignored: {match}")
89
+ continue
90
+
91
+ # logger.debug(f" - match: {match}")
92
+
93
+ if incl_pattern.strip:
94
+ # remove leading path components
95
+ dst_parent = type(parent)(*parent.parts[incl_pattern.strip:])
96
+ # logger.debug(f" - stripped: {parent.parts[:incl_pattern.strip]}")
97
+ else:
98
+ dst_parent = parent
99
+
100
+ # match to regular expression
101
+ m = incl_pattern.rematch.fullmatch(src_filename)
102
+
103
+ if not m:
104
+ # logger.debug(f" - !rematch: {src_filename!r} (pattern = {incl_pattern.rematch})")
105
+ continue
106
+
107
+ # apply replacement
108
+ if incl_pattern.replace == '{0}':
109
+ dst_filename = src_filename
110
+
111
+ else:
112
+ args = (m.group(0), *m.groups())
113
+ kwargs = m.groupdict()
114
+
115
+ try:
116
+ dst_filename = incl_pattern.replace.format(*args, **kwargs)
117
+ # logger.debug(f" - renamed: {dst_filename!r} (template = {incl_pattern.replace!r})")
118
+
119
+ except (IndexError, KeyError) as e:
120
+ raise ValidationError(
121
+ f"Replacement '{incl_pattern.replace}' failed for"
122
+ f" '{incl_pattern.rematch.pattern}':"
123
+ f" {args}, {kwargs}") from None
124
+
125
+ _src = src/parent/src_filename
126
+ # re-base the dst path, (path relative to src) == (path relative to dst)
127
+ _dst = dst/dst_parent/dst_filename
128
+
129
+ # logger.debug(f" - from: {str(_src)!r}\n - to: {str(_dst)!r}")
130
+
131
+ yield (i, _src, _dst, _ignore_patterns, False)
132
+
133
+
134
+ #===============================================================================
135
+ def dist_copy(*,
136
+ base_path: Path,
137
+ copy_items: list[pyproj_dist_copy],
138
+ ignore,
139
+ dist,
140
+ root = None,
141
+ logger = None ):
142
+
143
+ if len(copy_items) == 0:
144
+ return
145
+
146
+ logger = logger or logging.getLogger( __name__ )
147
+
148
+ history: dict[Path, Path] = {}
149
+
150
+ with validating(key = 'copy'):
151
+
152
+ for i, src, dst, ignore_patterns, individual in dist_iter(
153
+ copy_items = copy_items,
154
+ ignore = ignore,
155
+ root = root,
156
+ logger = logger):
157
+
158
+ with validating(key = i):
159
+
160
+ dst = base_path.joinpath(dst)
161
+ src_abs = resolve(src)
162
+
163
+ if root and not subdir(root, src_abs, check = False):
164
+ raise FileOutsideRootError(
165
+ f"Must have common path with root:\n file = \"{src_abs}\"\n root = \"{root}\"")
166
+
167
+ _src = history.get(dst)
168
+
169
+ if _src == src:
170
+ continue
171
+
172
+ if _src is not None:
173
+ raise ValidationError(
174
+ f"Overwriting destination {str(dst)!r} from {str(_src)!r} with {str(src)!r}")
175
+
176
+ history[dst] = src
177
+
178
+ if src.is_dir():
179
+ dist.copytree(
180
+ src = src,
181
+ dst = dst,
182
+ ignore = ignore_patterns )
183
+
184
+ else:
185
+ dist.copyfile(
186
+ src = src,
187
+ dst = dst )
@@ -391,12 +391,14 @@ class TimeEncode:
391
391
  Parameters
392
392
  ----------
393
393
  resolution : int
394
- Number of seconds that are resolved
394
+ Number of seconds that are resolved.
395
+ The default resolution is 1 minute
395
396
  rollover : int
396
397
  Number of seconds upper bound before wrapping.
397
- The number of distinct values is ``rollover // resolution``
398
+ The number of distinct values is ``rollover // resolution``.
399
+ The default rollover is 324 days
398
400
  width : int
399
- Minimal witdth of encoded number
401
+ Minimal width of encoded number
400
402
  base : int
401
403
  Numeric base used to encode number
402
404
  """
@@ -1,41 +1,18 @@
1
1
  from __future__ import annotations
2
2
  import sys
3
- import os
4
- import os.path as osp
5
- import io
6
- import warnings
7
- import stat
8
3
  import re
9
- import pathlib
10
4
  import inspect
11
- from copy import copy
12
- from collections.abc import (
13
- Mapping,
14
- Sequence,
15
- Iterable )
16
5
 
17
6
  from collections import namedtuple
18
- import hashlib
19
- from base64 import urlsafe_b64encode
20
- from email.message import Message
21
- from email.generator import BytesGenerator
22
7
  from email.utils import parseaddr, formataddr
23
8
  from urllib.parse import urlparse
24
9
  import keyword
25
10
 
26
11
  from packaging.tags import sys_tags
27
- from packaging.requirements import Requirement
28
- from packaging.specifiers import SpecifierSet
29
12
 
30
13
  from .validate import (
31
14
  ValidationError,
32
- validating,
33
- valid_type,
34
- valid_keys,
35
- valid_dict,
36
- valid_list,
37
- mapget,
38
- as_list)
15
+ validating)
39
16
 
40
17
  #===============================================================================
41
18
  CompatibilityTags = namedtuple('CompatibilityTags', ['py_tag', 'abi_tag', 'plat_tag'])
@@ -151,7 +128,7 @@ def norm_dist_name( name ):
151
128
 
152
129
  # > The name should be lowercased with all runs of the
153
130
  # > characters ., -, or _ replaced with a single - character.
154
- name = re.sub( r'[\-\_\.]+', "-", name )
131
+ name = pep_503_name_norm.sub('-', name)
155
132
 
156
133
  return name
157
134
 
@@ -164,12 +141,14 @@ def norm_dist_filename( name ):
164
141
  Each component of the filename is escaped by replacing runs of
165
142
  non-alphanumeric characters with an underscore '_'
166
143
 
144
+ Addendum - It seems that "local" versions require '+'
145
+
167
146
  See Also
168
147
  --------
169
148
  * https://www.python.org/dev/peps/pep-0427/#file-name-convention
170
149
  """
171
150
 
172
- return re.sub( r"[^\w\d\.]+", "_", name )
151
+ return re.sub( r"[^\w\d\.\+]+", "_", name )
173
152
 
174
153
  #===============================================================================
175
154
  def join_dist_filename( parts ):
@@ -194,6 +173,12 @@ def join_dist_filename( parts ):
194
173
  def norm_dist_version( version ):
195
174
  """Checks for valid distribution version (:pep:`440`)
196
175
 
176
+ .. versionchanged:: 0.1.9
177
+
178
+ Allow local version identifiers ``<public version identifier>[+<local version label>]``,
179
+ in addition to public versions.
180
+ Version pattern now uses :ref:`~packaging.version.VERSION_PATTERN`.
181
+
197
182
  See Also
198
183
  --------
199
184
  * https://www.python.org/dev/peps/pep-0440/#version-scheme
@@ -390,16 +375,30 @@ def norm_dist_url( label, url ):
390
375
  def norm_dist_extra( extra ):
391
376
  """Normalize distribution 'extra' requirement
392
377
 
378
+ .. versionchanged:: 0.2.0
379
+
380
+ Extra names are normalized according to PEP-685 and validated according to
381
+ Core Metadata 2.3.
382
+ Previously, extra names "must be a valid Python identifier" (Core Metadata 2.1)
383
+
384
+
393
385
  Note
394
386
  ----
395
- * No known PEP specifies this format, but is treated as
387
+ * MUST write out extra names in their normalized form.
388
+ * This applies to the Provides-Extra field and the extra marker when used
389
+ in the Requires-Dist field.
390
+
391
+ See Also
392
+ --------
393
+ * https://peps.python.org/pep-0685/#specification
396
394
  """
397
395
 
398
- extra = norm_printable( extra )
396
+ extra = norm_printable(extra).lower()
397
+ extra = pep_503_name_norm.sub('-', extra)
399
398
 
400
- if not pep_621_extra.fullmatch( extra ):
399
+ if not pep_685_extra.fullmatch(extra):
401
400
  raise PEPValidationError(
402
- pep = 621,
401
+ pep = 685,
403
402
  msg = "Invalid extra",
404
403
  val = extra )
405
404
 
@@ -590,6 +589,8 @@ def norm_entry_point_name( name ):
590
589
  See Also
591
590
  --------
592
591
  * https://packaging.python.org/en/latest/specifications/entry-points/
592
+ * The name may contain any characters except =, but it cannot start or end with
593
+ any whitespace character, or start with [
593
594
  """
594
595
 
595
596
  name = norm_printable( name )
@@ -635,14 +636,49 @@ def norm_entry_point_ref( ref ):
635
636
  or 'importable.module:object.attr': {ref}""") from e
636
637
 
637
638
  #===============================================================================
639
+ # https://packaging.python.org/en/latest/specifications/name-normalization/#name-format
638
640
  pep426_dist_name = re.compile(
639
641
  r'^([A-Z0-9]|[A-Z0-9][A-Z0-9._\-]*[A-Z0-9])$',
640
642
  re.IGNORECASE )
641
643
 
642
- pep440_version = re.compile(
643
- r'^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*'
644
- r'((a|b|rc)(0|[1-9][0-9]*))?'
645
- r'(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$' )
644
+ # https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization
645
+ # > runs of characters ., -, or _ replaced with a single - character.
646
+ pep_503_name_norm = re.compile(r'[\-\_\.]+', re.IGNORECASE)
647
+
648
+ # value of packaging.version.VERSION_PATTERN, as of 'packaging == 25.0'
649
+ # just in case the variable is ever deprecated
650
+ VERSION_PATTERN = r"""
651
+ v?
652
+ (?:
653
+ (?:(?P<epoch>[0-9]+)!)? # epoch
654
+ (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
655
+ (?P<pre> # pre-release
656
+ [-_\.]?
657
+ (?P<pre_l>alpha|a|beta|b|preview|pre|c|rc)
658
+ [-_\.]?
659
+ (?P<pre_n>[0-9]+)?
660
+ )?
661
+ (?P<post> # post release
662
+ (?:-(?P<post_n1>[0-9]+))
663
+ |
664
+ (?:
665
+ [-_\.]?
666
+ (?P<post_l>post|rev|r)
667
+ [-_\.]?
668
+ (?P<post_n2>[0-9]+)?
669
+ )
670
+ )?
671
+ (?P<dev> # dev release
672
+ [-_\.]?
673
+ (?P<dev_l>dev)
674
+ [-_\.]?
675
+ (?P<dev_n>[0-9]+)?
676
+ )?
677
+ )
678
+ (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
679
+ """
680
+
681
+ pep440_version = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)
646
682
 
647
683
  # NOTE: PEP 427 does not specify any constraints on the string following the
648
684
  # digits, but given the form it is used in the filenames it really cannot
@@ -716,16 +752,22 @@ pep_301_classifier = re.compile(
716
752
  pep_621_keyword = re.compile( r'^[^\s\,]+$' )
717
753
 
718
754
  #===============================================================================
719
- pep_621_extra = re.compile( r'^([A-Z0-9_]+)?$', re.IGNORECASE )
755
+ # https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata-provides-extra
756
+ pep_685_extra = re.compile( r'^[a-z0-9]+(-[a-z0-9]+)*$', re.IGNORECASE)
757
+
720
758
 
721
759
  #===============================================================================
722
760
  # https://packaging.python.org/en/latest/specifications/entry-points/
723
761
  # Group names must be one or more groups of letters, numbers and underscores,
724
762
  # separated by dots
725
763
  entry_point_group = re.compile( r'^[A-Z0-9_]+(\.[A-Z0-9_]+)*$', re.IGNORECASE )
764
+
765
+ # The name may contain any characters except =, but it cannot start or end with
766
+ # any whitespace character, or start with [
726
767
  # For new entry points (names), it is recommended to use only letters, numbers,
727
768
  # underscores, dots and dashes
728
- entry_point_name = re.compile( r'^([A-Z0-9_\.\-]+)?$', re.IGNORECASE )
769
+ # entry_point_name = re.compile(r'^([A-Z0-9_\.\-]+)?$', re.IGNORECASE)
770
+ entry_point_name = re.compile(r'^([^\[\]\=\s]+)?$', re.IGNORECASE)
729
771
 
730
772
  #===============================================================================
731
773
  py_keyword = re.compile( '^(' + '|'.join(keyword.kwlist) + ')$' )
@@ -121,9 +121,10 @@ class PkgInfoReq:
121
121
  self.req = Requirement( norm_printable(req) )
122
122
 
123
123
  marker = str( self.req.marker ) if self.req.marker else ''
124
- extra = norm_dist_extra(extra)
125
124
 
126
125
  if extra:
126
+ extra = norm_dist_extra(extra)
127
+
127
128
  if marker:
128
129
  self.req.marker = Marker(f'extra == "{extra}" and ( {marker} )')
129
130
  else:
@@ -130,6 +130,49 @@ class optional_dependencies(valid_dict):
130
130
  key_valid = valid(norm_dist_extra)
131
131
  value_valid = valid(optional_dependency_group)
132
132
 
133
+ #===============================================================================
134
+ class dependency_group_include(valid_dict):
135
+ allow_keys = list()
136
+ default = {
137
+ 'include-group': valid(norm_dist_extra)}
138
+
139
+ #===============================================================================
140
+ class dependency_group(valid_list):
141
+ value_valid = union(dependency_group_include, valid(norm_printable, Requirement, str))
142
+
143
+ #===============================================================================
144
+ def _check_dependency_groups(groups):
145
+ for group,v in groups.items():
146
+ for dep in v:
147
+ if isinstance(dep, Mapping):
148
+ _group = dep.get('include-group')
149
+
150
+ if _group not in groups:
151
+ raise ValidationError(f"'include-group' must be one of {set(groups)}: got {_group!r}")
152
+
153
+ elif _group == group:
154
+ raise ValidationError(f"'include-group' cannot be recursive: {_group!r}")
155
+
156
+ return groups
157
+
158
+ #===============================================================================
159
+ class dependency_groups(valid_dict):
160
+ r"""Dependency Groups
161
+
162
+ * https://packaging.python.org/en/latest/specifications/dependency-groups/
163
+
164
+ .. code-block:: toml
165
+ :caption: Example Dependency Groups
166
+
167
+ [dependency-groups]
168
+ coverage = ["coverage[toml]"]
169
+ test = ["pytest>7", {include-group = "coverage"}]
170
+
171
+ """
172
+ key_valid = valid(norm_dist_extra)
173
+ value_valid = valid(dependency_group)
174
+ validator = valid(_check_dependency_groups)
175
+
133
176
  #===============================================================================
134
177
  class entry_point_group(valid_dict):
135
178
  key_valid = valid(norm_entry_point_name)
@@ -317,9 +360,10 @@ class include(valid_dict):
317
360
  proxy_key = 'glob'
318
361
  # TODO: how to normalize patterns?
319
362
  default = {
320
- 'glob': valid(nonempty_str),
363
+ 'glob': valid(r'**', nonempty_str),
321
364
  'rematch': valid(r'.*', nonempty_str, re.compile),
322
- 'replace': valid('{0}', nonempty_str)}
365
+ 'replace': valid('{0}', nonempty_str),
366
+ 'strip': valid(int)}
323
367
 
324
368
  #===============================================================================
325
369
  class include_list(valid_list):
@@ -420,4 +464,5 @@ class pptoml(valid_dict):
420
464
  default = {
421
465
  'project': valid(REQUIRED, project),
422
466
  'build-system': valid(REQUIRED, build_system),
467
+ 'dependency-groups': valid(OPTIONAL, dependency_groups),
423
468
  'tool': valid(OPTIONAL, tool) }
@@ -364,7 +364,7 @@ class PyProjBase:
364
364
  with validating( key = 'tool.pyproj.dist.source'):
365
365
  dist_copy(
366
366
  base_path = dist.named_dirs['root'],
367
- include = self.source.copy,
367
+ copy_items = self.source.copy,
368
368
  ignore = self.dist.ignore + self.source.ignore,
369
369
  dist = dist,
370
370
  root = self.root,
@@ -413,7 +413,7 @@ class PyProjBase:
413
413
 
414
414
  dist_copy(
415
415
  base_path = dist.named_dirs['root'],
416
- include = self.binary.copy,
416
+ copy_items = self.binary.copy,
417
417
  ignore = ignore,
418
418
  dist = dist,
419
419
  root = self.root,
@@ -437,7 +437,7 @@ class PyProjBase:
437
437
  with validating( key = k ):
438
438
  dist_copy(
439
439
  base_path = dist.named_dirs[k],
440
- include = _include,
440
+ copy_items = _include,
441
441
  ignore = _ignore,
442
442
  dist = dist,
443
443
  root = self.root,
@@ -276,6 +276,10 @@ def validate(val, default, validators):
276
276
  if not isinstance(validators, Sequence):
277
277
  validators = [validators]
278
278
 
279
+ if not isinstance(validators, (list,tuple)):
280
+ raise ValidDefinitionError(
281
+ "Validators must be list or tuple")
282
+
279
283
  for validator in validators:
280
284
  if isinstance(validator, type):
281
285
  # cast to valid type (if needed)
@@ -755,7 +759,8 @@ class _ValidDictMeta(ABCMeta):
755
759
  'deprecate_keys',
756
760
  'forbid_keys',
757
761
  'default',
758
- 'proxy_keys')
762
+ 'proxy_keys',
763
+ 'validator')
759
764
 
760
765
  schema = {}
761
766
 
@@ -820,21 +825,24 @@ class _ValidDictMeta(ABCMeta):
820
825
  all_keys = tuple(all_keys)
821
826
 
822
827
  # compose validator with parameterized valid_keys method
823
- validator = valid(
824
- validator or (lambda v: v),
825
- partial(valid_keys,
826
- key_valid = key_valid,
827
- value_valid = value_valid,
828
- item_valid = item_valid,
829
- allow_keys = allow_keys,
830
- require_keys = require_keys,
831
- min_keys = min_keys,
832
- wedge_keys = wedge_keys,
833
- mutex_keys = mutex_keys,
834
- deprecate_keys = deprecate_keys,
835
- forbid_keys = forbid_keys,
836
- default = default,
837
- proxy_keys = proxy_keys))
828
+ validators = [partial(valid_keys,
829
+ key_valid = key_valid,
830
+ value_valid = value_valid,
831
+ item_valid = item_valid,
832
+ allow_keys = allow_keys,
833
+ require_keys = require_keys,
834
+ min_keys = min_keys,
835
+ wedge_keys = wedge_keys,
836
+ mutex_keys = mutex_keys,
837
+ deprecate_keys = deprecate_keys,
838
+ forbid_keys = forbid_keys,
839
+ default = default,
840
+ proxy_keys = proxy_keys)]
841
+
842
+ if validator:
843
+ validators.append(validator)
844
+
845
+ validator = valid(*validators)
838
846
 
839
847
  namespace['_all_keys'] = all_keys
840
848
  namespace['_validator'] = validator
@@ -925,6 +933,9 @@ class valid_dict(Mapping, metaclass = _ValidDictMeta):
925
933
  elif cls.proxy_key:
926
934
  args = [{ cls.proxy_key : v }]
927
935
 
936
+ else:
937
+ raise ValidationError(f"Expected mapping: got {v!r}")
938
+
928
939
  self._data = dict(*args, **kwargs)
929
940
  self._validate()
930
941
 
@@ -1042,19 +1053,23 @@ class valid_list(list):
1042
1053
  #---------------------------------------------------------------------------#
1043
1054
  def __init__( self, vals = None ):
1044
1055
  cls = type(self)
1045
- self._as_list = cls._as_list or list
1046
- self.value_valid = valid(
1047
- cls.value_valid or (lambda v: v))
1048
1056
 
1049
1057
  if vals is None:
1050
1058
  vals = list()
1051
1059
 
1052
- elif self._as_list:
1053
- vals = self._as_list(vals)
1060
+ elif cls._as_list:
1061
+ vals = cls._as_list(vals)
1062
+
1063
+ elif not isinstance(vals, Iterable) or isinstance(vals, (str,Mapping)):
1064
+ raise ValidationError(f"Excpected sequence: got {vals!r}")
1054
1065
 
1055
- for i,v in enumerate(vals):
1056
- with validating(key = i):
1057
- vals[i] = self.value_valid(v)
1066
+ else:
1067
+ vals = list(vals)
1068
+
1069
+ if cls.value_valid:
1070
+ for i,v in enumerate(vals):
1071
+ with validating(key = i):
1072
+ vals[i] = cls.value_valid(v)
1058
1073
 
1059
1074
  super().__init__(vals)
1060
1075
  self._validate()
@@ -1072,8 +1087,9 @@ class valid_list(list):
1072
1087
 
1073
1088
  #---------------------------------------------------------------------------#
1074
1089
  def append(self, val ):
1075
- with validating(key = len(self)):
1076
- val = self.value_valid(val)
1090
+ if self.value_valid:
1091
+ with validating(key = len(self)):
1092
+ val = self.value_valid(val)
1077
1093
 
1078
1094
  super().append(val)
1079
1095
 
@@ -1081,16 +1097,18 @@ class valid_list(list):
1081
1097
  def extend(self, vals ):
1082
1098
  vals = list(vals)
1083
1099
 
1084
- for i,v in enumerate(vals):
1085
- with validating(key = len(self) + i):
1086
- vals[i] = self.value_valid(v)
1100
+ if self.value_valid:
1101
+ for i,v in enumerate(vals):
1102
+ with validating(key = len(self) + i):
1103
+ vals[i] = self.value_valid(v)
1087
1104
 
1088
1105
  super().extend(vals)
1089
1106
 
1090
1107
  #-----------------------------------------------------------------------------
1091
1108
  def __setitem__( self, key, val ):
1092
- with validating(key = key):
1093
- val = self.value_valid(val)
1109
+ if self.value_valid:
1110
+ with validating(key = key):
1111
+ val = self.value_valid(val)
1094
1112
 
1095
1113
  super().__setitem__(key, val)
1096
1114
 
@@ -1,28 +1,29 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: partis-pyproj
3
- Version: 0.1.8
3
+ Version: 0.2.0
4
4
  Requires-Python: >=3.8
5
5
  Author-email: "Nanohmics Inc." <software.support@nanohmics.com>
6
6
  Maintainer-email: "Nanohmics Inc." <software.support@nanohmics.com>
7
7
  Summary: Minimal set of Python project utilities (PEP-517/621)
8
8
  License-File: LICENSE.txt
9
+ Project-URL: homepage, https://github.com/kcdodd/partis-pyproj/
9
10
  Classifier: Programming Language :: Python
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Topic :: Software Development :: Build Tools
12
- Classifier: License :: OSI Approved :: BSD License
13
11
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Operating System :: POSIX :: Linux
15
12
  Classifier: Operating System :: Microsoft :: Windows
13
+ Classifier: License :: OSI Approved :: BSD License
14
+ Classifier: Topic :: Software Development :: Build Tools
15
+ Classifier: Operating System :: POSIX :: Linux
16
16
  Classifier: Intended Audience :: Developers
17
+ Classifier: Development Status :: 4 - Beta
17
18
  Provides-Extra: meson
18
19
  Provides-Extra: cmake
20
+ Requires-Dist: requests>=2.32.3
19
21
  Requires-Dist: packaging>=24.2
20
22
  Requires-Dist: tomli>=2.0.1
21
- Requires-Dist: requests>=2.32.3
22
- Requires-Dist: ninja>=1.10.2.3; extra == "meson"
23
23
  Requires-Dist: meson>=0.61.3; extra == "meson"
24
- Requires-Dist: cmake>=3.24.3; extra == "cmake"
24
+ Requires-Dist: ninja>=1.10.2.3; extra == "meson"
25
25
  Requires-Dist: ninja>=1.10.2.3; extra == "cmake"
26
+ Requires-Dist: cmake>=3.24.3; extra == "cmake"
26
27
  Description-Content-Type: text/markdown
27
28
 
28
29
  [![tests](https://github.com/kcdodd/partis-pyproj/actions/workflows/tests.yaml/badge.svg)](https://github.com/kcdodd/partis-pyproj/actions/workflows/tests.yaml)
@@ -87,10 +88,15 @@ formats and behaviors.
87
88
 
88
89
  * An `include` list is used to filter files or directories to be copied, expanded
89
90
  to zero or more matches relative to `src`.
90
- * `glob` follows the format of [Path.glob](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob), with recursion.
91
- * `rematch` may further discriminate files (already matched by `glob`) using [Regular Expression Syntax](https://docs.python.org/3/library/re.html#regular-expression-syntax).
92
- * `replace` can change destination filenames using
91
+ * `glob` follows the format of [Path.glob](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob).
92
+ If recursive pattern `**` is used, the glob will *not* match directories,
93
+ since the resulting `copytree` would end up copying all files in the directory.
94
+ * `rematch` may further discriminate filenames (already matched by `glob`) using [Regular Expression Syntax](https://docs.python.org/3/library/re.html#regular-expression-syntax). Directories are *not* considered by `rematch`.
95
+ * `replace` can change destination *filenames* using
93
96
  [Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax), with values supplied by any groups defined in `rematch`.
97
+ This cannot rename directories.
98
+ * `strip` can remove (up to) the given number of path components from the relative
99
+ `src` path.
94
100
 
95
101
  **Ignore patterns**
96
102
 
@@ -0,0 +1,38 @@
1
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/file.py,sha256=cU327vzXGfuMWlqY-LdfGqGs7LOhVd0MOUOIdAL5od4,1581
2
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/__init__.py,sha256=-CibR9jSfwRxNn4gHe-7_ZrQnH3FY4JYTi3Mz-Q66cU,1560
3
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/validate.py,sha256=FS-x15lErV6_y5WLOD2pv5j6jWsXXalA4xdYeazi140,32587
4
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/_legacy_setup.py,sha256=tAE0xlD9RVhhMn1kcUPSccbDFE1ReuH99jfrw8q8MiM,5301
5
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/norms.py,sha256=d6SkBBSST87smwD27YZ1lhicuw6rHdRQfXb_xnBsCGk,10990
6
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/cargo.py,sha256=z9yj7QNZqCV_hFRBOsBBWo1GgOWZF0Oqag0kLpfN-2I,1144
7
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/__init__.py,sha256=IRQk2gA5mK6BjewVHQ1IcVLVENV2vBMrwS4JO8wGPYc,164
8
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/meson.py,sha256=V39hN5bJ4kc9JBTXwT0XIc9uMtnKtrYczWC28jqu-GU,1939
9
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/process.py,sha256=8tUuFQrnks6Q-0G6Ow8G7LOycc2Z5olqLBzGhRCAulg,1002
10
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/download.py,sha256=m0F5lTC7AjPaZGPp6xkFCt8ziMjqrVMUZfzXo_XTwdw,5552
11
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/cmake.py,sha256=soKtbHkLttRq96NdLTD8dlD5SgzxrFYqJadC9V8HSjw,1915
12
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/builder/builder.py,sha256=iTU_GFWmyk0yNoFdG7nL76_SP0x-Y4Ej6h4RrkZuVjE,11909
13
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/pyproj.py,sha256=G8dbbqpBjfCyd9o9fpaP8Lflz9P207UeTQ-2D_APOnM,12767
14
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/pptoml.py,sha256=U3lJUJk9GPSmwvQsVb8lqtA8nxD5nXUeZLjd9jX3zIM,15111
15
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/legacy.py,sha256=JXSg-5Y65PUOtlEgkIgNj3vwZtJFi9CVsRvnpar2xKs,1749
16
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/template.py,sha256=1OmPPAEOLz0kquypFEb_c2aLysTGOwC2zqw1_eXYutY,6908
17
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/pkginfo.py,sha256=PtW1J6NSkjJ2en4l5sxo7FkpTaaWd_kLjtZU1NNybYo,15151
18
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/_nonprintable.py,sha256=2-JeNY6prFM4cfZKeFuv-aZW7ZvPI8FYO7dJ0CL3RD8,1893
19
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/pep.py,sha256=ukhfWr5Nd80L1VEOZkcVGzKstKt19UcoSqd1V3axYpo,34661
20
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/load_module.py,sha256=6y3oMqCvycS4EhcOn1NtuXWyix0smCnogEH6W0bWiPg,4396
21
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/backend.py,sha256=E4IxPkrNTvgJ3-RqFa40deXfjeeJrvCa_CGtPV3Uj5I,7055
22
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/path/__init__.py,sha256=458RSB1Lr63sY3CP_uqxaHkEUQMgeqZgNXFzpUIbsn4,236
23
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/path/utils.py,sha256=lJUyTGecZDMoTphSItr99_4b_yp_4zMRd8IAFiXgsI4,2364
24
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/path/match.py,sha256=zyeGJtVQ-gWLGx1NBlDhJ8cWa6mJyU0JBWFUdhz7yik,11812
25
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/path/pattern.py,sha256=d3SO_bencbojbf3zkvaR6bxzT83VFrMpI-wEfc26Qk0,16107
26
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/__init__.py,sha256=7SK7yeLehYJCbeHgnRFb4tPi2bEP2_ouA-qNxh_ofJI,379
27
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_targz.py,sha256=csb8NckhfScIUNY6LnQlUM31Jio69RwndBLuGD45Kls,3867
28
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_base.py,sha256=0LbWUitfuunpU0azUMD9xvapfn3_waUzZJVLwHxhD3E,11997
29
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_copy.py,sha256=gE0ZY68Th-Ym2609KN9ebsulhyQsYEflL3x2Pau4ZKU,5300
30
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_binary.py,sha256=COfEivLDvHk2ycX9xTSnNnIsc5EyXRTznaJPWOrx4Os,7749
31
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_source.py,sha256=_TzLbzLSn6TIwO98zwdvllkGCJgvr6BKnjNM-URKiCw,4362
32
+ partis_pyproj-0.2.0.data/purelib/partis/pyproj/dist_file/dist_zip.py,sha256=ILAIz1myBgAR4xFRNgcJ1b2otcdUscyRViEbbArzL1M,3810
33
+ partis_pyproj-0.2.0.dist-info/METADATA,sha256=WG7rbmkjVNSGi2ewvctnGgXuaECl259CX4cHKhKECjM,22424
34
+ partis_pyproj-0.2.0.dist-info/LICENSE.txt,sha256=6LYtpan0H8F0reCrEa1ZUf_Nr9Vu93A3bqunX2J5Et0,3320
35
+ partis_pyproj-0.2.0.dist-info/top_level.txt,sha256=T72LF-bUK7nMDf1GQgbzvgZ2YrJElwwRd9h7HwVQBIQ,6
36
+ partis_pyproj-0.2.0.dist-info/entry_points.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ partis_pyproj-0.2.0.dist-info/WHEEL,sha256=4YeLUbbRU8D0xzIlG3rm8IFLwHKcY8YaY_wiNxswKJg,126
38
+ partis_pyproj-0.2.0.dist-info/RECORD,,
@@ -1,138 +0,0 @@
1
- from __future__ import annotations
2
- import os
3
- import glob
4
- from pathlib import (
5
- Path)
6
- import logging
7
- from ..validate import (
8
- FileOutsideRootError,
9
- validating )
10
- from ..path import (
11
- PathFilter,
12
- subdir,
13
- combine_ignore_patterns,
14
- resolve)
15
-
16
- # #===============================================================================
17
- # def rematch_replace(rematch, replace, name):
18
-
19
- # m = rematch.fullmatch(path)
20
- # if not m:
21
- # continue
22
-
23
- # args = (m.group(0), *m.groups())
24
- # kwargs = m.groupdict()
25
- # try:
26
- # replace.format(*args, **kwargs))
27
- # except (IndexError, KeyError) as e:
28
- # raise ValueError(
29
- # f"Replacement '{replace}' failed to format match '{m.group(0)}': "
30
- # f"{args}, {kwargs}") from None
31
-
32
-
33
-
34
- #===============================================================================
35
- def dist_iter(*,
36
- include,
37
- ignore,
38
- root ):
39
-
40
- patterns = PathFilter(
41
- patterns = ignore )
42
-
43
- for i, incl in enumerate(include):
44
- src = incl.src
45
- dst = incl.dst
46
- _ignore = incl.ignore
47
-
48
- _ignore_patterns = combine_ignore_patterns(
49
- patterns,
50
- PathFilter(
51
- patterns = _ignore,
52
- start = src ) )
53
-
54
- if not incl.include:
55
- yield ( i, src, dst, _ignore_patterns, True )
56
- else:
57
- for incl_pattern in incl.include:
58
- cwd = Path.cwd()
59
- try:
60
- # TODO: better way of globing *relative* to src directory
61
- os.chdir(src)
62
- matches = glob.glob(incl_pattern.glob, recursive = True)
63
- finally:
64
- os.chdir(cwd)
65
-
66
- for match in matches:
67
- match = Path(match)
68
- basename = match.parent
69
- src_filename = match.name
70
-
71
- m = incl_pattern.rematch.fullmatch(src_filename)
72
- if not m:
73
- continue
74
-
75
- args = (m.group(0), *m.groups())
76
- kwargs = m.groupdict()
77
- try:
78
- dst_filename = incl_pattern.replace.format(*args, **kwargs)
79
- except (IndexError, KeyError) as e:
80
- raise ValueError(
81
- f"Replacement '{incl_pattern.replace}' failed for"
82
- f" '{incl_pattern.rematch.pattern}':"
83
- f" {args}, {kwargs}") from None
84
-
85
- _src = src/basename/src_filename
86
- # re-base the dst path, path relative to src == path relative to dst
87
- _dst = dst/basename/dst_filename
88
-
89
- yield (i, _src, _dst, _ignore_patterns, False)
90
-
91
-
92
- #===============================================================================
93
- def dist_copy(*,
94
- base_path,
95
- include,
96
- ignore,
97
- dist,
98
- root = None,
99
- logger = None ):
100
-
101
- if len(include) == 0:
102
- return
103
-
104
- logger = logger or logging.getLogger( __name__ )
105
-
106
- with validating(key = 'copy'):
107
-
108
- for i, src, dst, ignore_patterns, individual in dist_iter(
109
- include = include,
110
- ignore = ignore,
111
- root = root ):
112
-
113
- with validating(key = i):
114
-
115
- dst = base_path.joinpath(dst)
116
-
117
- if not individual and ignore_patterns( src.parent, [src.name]):
118
- logger.debug( f'ignoring: {src}' )
119
- continue
120
-
121
- src_abs = resolve(src)
122
-
123
- if root and not subdir(root, src_abs, check = False):
124
- raise FileOutsideRootError(
125
- f"Must have common path with root:\n file = \"{src_abs}\"\n root = \"{root}\"")
126
-
127
- logger.debug(f"dist copy: {src} -> {dst}")
128
-
129
- if src.is_dir():
130
- dist.copytree(
131
- src = src,
132
- dst = dst,
133
- ignore = ignore_patterns )
134
-
135
- else:
136
- dist.copyfile(
137
- src = src,
138
- dst = dst )
@@ -1,38 +0,0 @@
1
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/file.py,sha256=cU327vzXGfuMWlqY-LdfGqGs7LOhVd0MOUOIdAL5od4,1581
2
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/__init__.py,sha256=-CibR9jSfwRxNn4gHe-7_ZrQnH3FY4JYTi3Mz-Q66cU,1560
3
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/validate.py,sha256=ZYzaiHaWWkw9RLL-AgVfch-3gacNg5s1Vl0yy6gX4-U,32168
4
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/_legacy_setup.py,sha256=tAE0xlD9RVhhMn1kcUPSccbDFE1ReuH99jfrw8q8MiM,5301
5
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/norms.py,sha256=uyF3Pi9NbptleTabdZlMkaKJB5ymfDOSrcpD6bygQeM,10909
6
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/cargo.py,sha256=z9yj7QNZqCV_hFRBOsBBWo1GgOWZF0Oqag0kLpfN-2I,1144
7
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/__init__.py,sha256=IRQk2gA5mK6BjewVHQ1IcVLVENV2vBMrwS4JO8wGPYc,164
8
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/meson.py,sha256=V39hN5bJ4kc9JBTXwT0XIc9uMtnKtrYczWC28jqu-GU,1939
9
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/process.py,sha256=8tUuFQrnks6Q-0G6Ow8G7LOycc2Z5olqLBzGhRCAulg,1002
10
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/download.py,sha256=RfDeAsGXMCAYct5REXroF7QwncQdrliYhMzm8ePxE94,4981
11
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/cmake.py,sha256=soKtbHkLttRq96NdLTD8dlD5SgzxrFYqJadC9V8HSjw,1915
12
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/builder/builder.py,sha256=pOIBUG5iEVsjh9SglIky41kgWLcAyHdEDAWzym2CM2g,11467
13
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/pyproj.py,sha256=Ra7DOj2Pq5peC_pT82xQoAxCDX3sHukpbKgNSqNeI8c,12758
14
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/pptoml.py,sha256=uThSRs6EMA51nFo38XufFCVEWifC83prM85oCKqxliQ,13565
15
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/legacy.py,sha256=JXSg-5Y65PUOtlEgkIgNj3vwZtJFi9CVsRvnpar2xKs,1749
16
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/template.py,sha256=1OmPPAEOLz0kquypFEb_c2aLysTGOwC2zqw1_eXYutY,6908
17
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/pkginfo.py,sha256=6rz06s19MX7zQIWt2wE-HEg-1WA2LZogk9kdqKFFcVE,15148
18
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/_nonprintable.py,sha256=2-JeNY6prFM4cfZKeFuv-aZW7ZvPI8FYO7dJ0CL3RD8,1893
19
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/pep.py,sha256=XzZYnYKaB4V3wOXUsrGNav9H_ziMoaQb9wGEKlciOms,32694
20
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/load_module.py,sha256=6y3oMqCvycS4EhcOn1NtuXWyix0smCnogEH6W0bWiPg,4396
21
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/backend.py,sha256=sKubLkwD30n_nwFnIqQ-T4Dh5UFZUXf5r5yrl5oNiXs,7004
22
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/path/__init__.py,sha256=458RSB1Lr63sY3CP_uqxaHkEUQMgeqZgNXFzpUIbsn4,236
23
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/path/utils.py,sha256=lJUyTGecZDMoTphSItr99_4b_yp_4zMRd8IAFiXgsI4,2364
24
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/path/match.py,sha256=zyeGJtVQ-gWLGx1NBlDhJ8cWa6mJyU0JBWFUdhz7yik,11812
25
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/path/pattern.py,sha256=d3SO_bencbojbf3zkvaR6bxzT83VFrMpI-wEfc26Qk0,16107
26
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/__init__.py,sha256=7SK7yeLehYJCbeHgnRFb4tPi2bEP2_ouA-qNxh_ofJI,379
27
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_targz.py,sha256=csb8NckhfScIUNY6LnQlUM31Jio69RwndBLuGD45Kls,3867
28
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_base.py,sha256=0LbWUitfuunpU0azUMD9xvapfn3_waUzZJVLwHxhD3E,11997
29
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_copy.py,sha256=MXUfoOXhH2eTetgkkz-5linJkqzX7_q4gIvCLLlB-7c,3519
30
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_binary.py,sha256=COfEivLDvHk2ycX9xTSnNnIsc5EyXRTznaJPWOrx4Os,7749
31
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_source.py,sha256=_TzLbzLSn6TIwO98zwdvllkGCJgvr6BKnjNM-URKiCw,4362
32
- partis_pyproj-0.1.8.data/purelib/partis/pyproj/dist_file/dist_zip.py,sha256=ILAIz1myBgAR4xFRNgcJ1b2otcdUscyRViEbbArzL1M,3810
33
- partis_pyproj-0.1.8.dist-info/METADATA,sha256=5VkYF6-weoj2QFLR2MPHR8Quv6F4m2DCSUsSgW9nTy0,22034
34
- partis_pyproj-0.1.8.dist-info/LICENSE.txt,sha256=6LYtpan0H8F0reCrEa1ZUf_Nr9Vu93A3bqunX2J5Et0,3320
35
- partis_pyproj-0.1.8.dist-info/top_level.txt,sha256=T72LF-bUK7nMDf1GQgbzvgZ2YrJElwwRd9h7HwVQBIQ,6
36
- partis_pyproj-0.1.8.dist-info/entry_points.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- partis_pyproj-0.1.8.dist-info/WHEEL,sha256=4YeLUbbRU8D0xzIlG3rm8IFLwHKcY8YaY_wiNxswKJg,126
38
- partis_pyproj-0.1.8.dist-info/RECORD,,