partis-pyproj 0.1.3rc4__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.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/__init__.py +9 -1
  2. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_legacy_setup.py +11 -11
  3. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_nonprintable.py +4 -3
  4. {partis_pyproj-0.1.3rc4.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.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cargo.py +5 -6
  7. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cmake.py +14 -25
  8. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/meson.py +10 -23
  9. partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/process.py +42 -0
  10. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/__init__.py +1 -1
  11. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_base.py +75 -86
  12. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_binary.py +6 -24
  13. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_copy.py +7 -18
  14. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_source.py +4 -21
  15. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_targz.py +5 -12
  16. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_zip.py +5 -14
  17. partis_pyproj-0.1.5.data/purelib/partis/pyproj/file.py +65 -0
  18. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/legacy.py +3 -2
  19. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/load_module.py +7 -6
  20. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/norms.py +35 -31
  21. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/__init__.py +2 -1
  22. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/match.py +42 -35
  23. {partis_pyproj-0.1.3rc4.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.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pep.py +36 -35
  26. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pkginfo.py +7 -16
  27. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pptoml.py +125 -120
  28. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pyproj.py +44 -39
  29. partis_pyproj-0.1.5.data/purelib/partis/pyproj/template.py +229 -0
  30. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/validate.py +279 -269
  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.3rc4.data/purelib/partis/pyproj/builder/builder.py +0 -153
  34. partis_pyproj-0.1.3rc4.data/purelib/partis/pyproj/builder/process.py +0 -78
  35. partis_pyproj-0.1.3rc4.data/purelib/partis/pyproj/path/utils.py +0 -40
  36. partis_pyproj-0.1.3rc4.dist-info/METADATA +0 -51
  37. partis_pyproj-0.1.3rc4.dist-info/RECORD +0 -35
  38. {partis_pyproj-0.1.3rc4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
  39. {partis_pyproj-0.1.3rc4.dist-info → partis_pyproj-0.1.5.dist-info}/LICENSE.txt +0 -0
  40. {partis_pyproj-0.1.3rc4.dist-info → partis_pyproj-0.1.5.dist-info}/WHEEL +0 -0
  41. {partis_pyproj-0.1.3rc4.dist-info → partis_pyproj-0.1.5.dist-info}/entry_points.txt +0 -0
  42. {partis_pyproj-0.1.3rc4.dist-info → partis_pyproj-0.1.5.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,18 @@
1
+ from __future__ import annotations
1
2
  import os
2
- import os.path as osp
3
3
  from pathlib import (
4
- Path,
5
- PurePath,
6
- PurePosixPath)
4
+ Path)
7
5
  import io
8
- import warnings
9
- import stat
10
-
11
6
  import tempfile
12
7
  import shutil
13
8
  import tarfile
14
-
15
9
  from .dist_base import dist_base
16
-
17
10
  from ..norms import (
18
11
  norm_path,
19
12
  norm_data,
20
13
  norm_mode )
21
14
 
22
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
15
+ #===============================================================================
23
16
  class dist_targz( dist_base ):
24
17
  """Builds a tar-file with gz compression
25
18
 
@@ -120,7 +113,7 @@ class dist_targz( dist_base ):
120
113
  # NOTE: the missing_ok parameter was not added until py38
121
114
  self.outpath.unlink()
122
115
 
123
- self.outdir.mkdir( parents = True, exist_ok = True )
116
+ self.outdir.mkdir(parents = True, exist_ok = True)
124
117
  shutil.copyfile( self._tmp_path, self.outpath )
125
118
 
126
119
  #-----------------------------------------------------------------------------
@@ -131,7 +124,7 @@ class dist_targz( dist_base ):
131
124
  # remove temporary file
132
125
  if self._tmp_path.exists():
133
126
  self._tmp_path.unlink()
134
-
127
+
135
128
  self._tmp_path = None
136
129
 
137
130
  #-----------------------------------------------------------------------------
@@ -1,26 +1,17 @@
1
+ from __future__ import annotations
1
2
  import os
2
- import os.path as osp
3
3
  from pathlib import (
4
- Path,
5
- PurePath,
6
- PurePosixPath)
7
- import io
8
- import warnings
9
- import stat
10
-
4
+ Path)
11
5
  import tempfile
12
6
  import shutil
13
7
  import zipfile
14
-
15
8
  from .dist_base import dist_base
16
-
17
9
  from ..norms import (
18
10
  norm_path,
19
11
  norm_data,
20
- norm_mode,
21
12
  norm_zip_external_attr )
22
13
 
23
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14
+ #===============================================================================
24
15
  class dist_zip( dist_base ):
25
16
  """Builds a zip file
26
17
 
@@ -118,7 +109,7 @@ class dist_zip( dist_base ):
118
109
  # NOTE: the missing_ok parameter was not added until py38
119
110
  self.outpath.unlink()
120
111
 
121
- self.outdir.mkdir( parents = True, exist_ok = True )
112
+ self.outdir.mkdir(parents = True, exist_ok = True )
122
113
  shutil.copyfile( self._tmp_path, self.outpath )
123
114
 
124
115
  #-----------------------------------------------------------------------------
@@ -129,7 +120,7 @@ class dist_zip( dist_base ):
129
120
  # remove temporary file
130
121
  if self._tmp_path.exists():
131
122
  self._tmp_path.unlink()
132
-
123
+
133
124
  self._tmp_path = None
134
125
 
135
126
  #-----------------------------------------------------------------------------
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+ import os
3
+
4
+ #===============================================================================
5
+ def tail(path, n, bufsize = 1024, encoding = 'utf-8'):
6
+ """Reads the last n lines from a file
7
+
8
+ Parameters
9
+ ----------
10
+ path : str
11
+ n : int
12
+ Max number of lines to read from the end of the file
13
+ bufsize : int
14
+ Number of bytes to buffer at a time.
15
+
16
+ Returns
17
+ -------
18
+ lines : List[str]
19
+ Up to ``n`` lines from the end of the file
20
+ """
21
+
22
+ bufsize = int(bufsize)
23
+ bufsize = max(1, bufsize)
24
+
25
+ n = int(n)
26
+ n = max( 0, n )
27
+
28
+ buf = bytes()
29
+ nlines = 0
30
+
31
+ head = 0
32
+
33
+ with open( path, 'rb' ) as fp:
34
+ # total number of bytes in the file
35
+ tot = fp.seek( 0, os.SEEK_END )
36
+
37
+ head = tot
38
+
39
+ while nlines < n and head > 0:
40
+ # NOTE: the number of newline characters is one less than number of 'lines'
41
+ nread = min( head, bufsize )
42
+ head -= nread
43
+
44
+ fp.seek( head, os.SEEK_SET )
45
+
46
+ _buf = fp.read( nread )
47
+
48
+ # NOTE: this is not exact, since pairs of '\r\n' may happen on the read
49
+ # boundary, but should count approximately the number of LF, CR, and CR+LF.
50
+ # LF -> 1 + 0 + 0 = 1
51
+ # CR -> 0 + 1 + 0 = 1
52
+ # CR+LF -> 1 + 1 - 1 = 1
53
+ nlines += _buf.count(b'\n') + _buf.count(b'\r') - _buf.count(b'\r\n')
54
+
55
+ buf = _buf + buf
56
+
57
+ if nlines > 0 and head > 0:
58
+ # remove everything before first newline to ensure only complete lines are kept
59
+ i = buf.index(b'\n')
60
+ buf = buf[(i+1):]
61
+
62
+ res = buf.decode(encoding, errors = 'replace')
63
+ lines = res.split('\n')[-n:]
64
+
65
+ return lines
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import os.path as osp
3
4
  from pathlib import PurePath
@@ -7,7 +8,7 @@ import logging
7
8
  import tempfile
8
9
 
9
10
 
10
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11
+ #===============================================================================
11
12
  def legacy_setup_content( pyproj, sdist ):
12
13
  """
13
14
 
@@ -34,7 +35,7 @@ def legacy_setup_content( pyproj, sdist ):
34
35
 
35
36
  sources = '\n'.join( [
36
37
  os.fspath(file)
37
- for file, hash, size in sdist.records ] ).encode('utf-8')
38
+ for file, (hash, size) in sdist.records.items() ] ).encode('utf-8')
38
39
 
39
40
  with open( osp.join( osp.dirname(__file__), '_legacy_setup.py' ), 'r' ) as fp:
40
41
  legacy_setup_py = fp.read()
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import os.path as osp
3
4
  import sys
@@ -22,11 +23,11 @@ from .validate import (
22
23
  restrict,
23
24
  mapget )
24
25
 
25
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
26
+ #===============================================================================
26
27
  class EntryPointError(ValueError):
27
28
  pass
28
29
 
29
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
30
+ #===============================================================================
30
31
  def module_name_from_path( path, root ):
31
32
  """Generates an importable module name from a file system path
32
33
 
@@ -45,7 +46,7 @@ def module_name_from_path( path, root ):
45
46
 
46
47
  return ".".join(path_parts)
47
48
 
48
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
49
+ #===============================================================================
49
50
  def module_path( mod_name, root ):
50
51
  path = root / Path(*mod_name.split('.'))
51
52
 
@@ -59,7 +60,7 @@ def module_path( mod_name, root ):
59
60
 
60
61
  return path
61
62
 
62
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
63
+ #===============================================================================
63
64
  def load_module( name, path, root ):
64
65
 
65
66
  path = str(path)
@@ -82,7 +83,7 @@ def load_module( name, path, root ):
82
83
 
83
84
  return mod
84
85
 
85
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
86
+ #===============================================================================
86
87
  def load_entrypoint( entry_point, root ):
87
88
  r"""
88
89
  Parameters
@@ -129,7 +130,7 @@ def load_entrypoint( entry_point, root ):
129
130
 
130
131
  return func
131
132
 
132
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
133
+ #===============================================================================
133
134
  class EntryPoint:
134
135
  r"""
135
136
 
@@ -1,7 +1,10 @@
1
+ from __future__ import annotations
1
2
  import sys
2
3
  import os
3
4
  import os.path as osp
4
- import io
5
+ from io import (
6
+ IOBase,
7
+ BytesIO)
5
8
  import warnings
6
9
  import stat
7
10
  import re
@@ -35,7 +38,7 @@ from .validate import (
35
38
  mapget,
36
39
  as_list)
37
40
 
38
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
41
+ #===============================================================================
39
42
  def marker_evaluated(marker):
40
43
  if isinstance(marker, bool):
41
44
  return marker
@@ -44,7 +47,7 @@ def marker_evaluated(marker):
44
47
 
45
48
  return marker.evaluate()
46
49
 
47
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
50
+ #===============================================================================
48
51
  def scalar(val):
49
52
  if isinstance(val, str):
50
53
  return val
@@ -54,11 +57,11 @@ def scalar(val):
54
57
 
55
58
  return val
56
59
 
57
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
60
+ #===============================================================================
58
61
  class scalar_list(valid_list):
59
- _value_valid = valid(scalar)
62
+ value_valid = valid(scalar)
60
63
 
61
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
64
+ #===============================================================================
62
65
  def norm_bool(val):
63
66
  t = [True, 'true', 'True', 'yes', 'y', 'enable', 'enabled']
64
67
  f = [False, 'false', 'False', 'no', 'n', 'disable', 'disabled']
@@ -71,7 +74,7 @@ def norm_bool(val):
71
74
 
72
75
  return val
73
76
 
74
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
77
+ #===============================================================================
75
78
  def empty_str(val):
76
79
  val = str(val)
77
80
 
@@ -81,7 +84,7 @@ def empty_str(val):
81
84
 
82
85
  return val
83
86
 
84
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
87
+ #===============================================================================
85
88
  def nonempty_str(val):
86
89
  val = str(val)
87
90
 
@@ -91,42 +94,43 @@ def nonempty_str(val):
91
94
 
92
95
  return val
93
96
 
94
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
97
+ #===============================================================================
95
98
  class str_list(valid_list):
96
- _value_valid = valid(str)
99
+ value_valid = valid(str)
97
100
 
98
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
101
+ #===============================================================================
99
102
  class nonempty_str_list(valid_list):
100
- _value_valid = valid(nonempty_str)
103
+ value_valid = valid(nonempty_str)
101
104
 
102
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
103
- def norm_data( data ):
105
+ #===============================================================================
106
+ def norm_data(data: str|bytes|IOBase) -> bytes:
104
107
  """Normalize data for writing into a distribution
105
108
 
106
109
  Parameters
107
110
  ----------
108
- data : bytes | str | io.IOBase
111
+ data:
112
+ Data to process
109
113
 
110
114
  Returns
111
115
  -------
112
- data : bytes
116
+ data:
113
117
 
114
118
  * If data is bytes, it will be returned un-modified.
115
119
  * If data is a str, it will be encoded as 'utf-8'.
116
120
  * If data is a stream, it will be read to EOF and apply one of the above.
117
121
  """
118
122
 
119
- if isinstance( data, io.IOBase ):
123
+ if isinstance(data, IOBase):
120
124
  # read from a stream, assumes readable
121
125
  data = data.read()
122
126
 
123
- if isinstance( data, str ):
127
+ if isinstance(data, str):
124
128
  # encode text as utf-8
125
129
  data = data.encode('utf-8')
126
130
 
127
131
  return data
128
132
 
129
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
133
+ #===============================================================================
130
134
  def norm_path( path ):
131
135
  """Normalizes a file path for writing into a distribution archive
132
136
 
@@ -168,7 +172,7 @@ def norm_path( path ):
168
172
 
169
173
  return path
170
174
 
171
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
175
+ #===============================================================================
172
176
  def norm_path_to_os( path ):
173
177
  """Converts a normalized or OS path to be an OS path
174
178
  """
@@ -184,7 +188,7 @@ def norm_path_to_os( path ):
184
188
 
185
189
  return str(pathlib.PurePath(path))
186
190
 
187
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
191
+ #===============================================================================
188
192
  def norm_mode( mode = None ):
189
193
  """Normalizes file permission mode for distribution archive
190
194
 
@@ -233,7 +237,7 @@ def norm_mode( mode = None ):
233
237
 
234
238
  return _mode
235
239
 
236
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
240
+ #===============================================================================
237
241
  def norm_zip_external_attr( mode = None ):
238
242
  """Converts the unix integer mode to zip external_attr
239
243
 
@@ -269,7 +273,7 @@ def norm_zip_external_attr( mode = None ):
269
273
 
270
274
  return xattr
271
275
 
272
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
276
+ #===============================================================================
273
277
  def b64_nopad( data ):
274
278
  """Encodes hash as urlsafe base64 encoding with no trailing '=' (:pep:`427`)
275
279
 
@@ -287,13 +291,13 @@ def b64_nopad( data ):
287
291
  """
288
292
  return urlsafe_b64encode( data ).decode("ascii").rstrip("=")
289
293
 
290
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
291
- def hash_sha256( stream ):
294
+ #===============================================================================
295
+ def hash_sha256(stream: BytesIO):
292
296
  """Computes SHA-256 hash
293
297
 
294
298
  Parameters
295
299
  ----------
296
- stream : bytes | io.BytesIO
300
+ stream:
297
301
 
298
302
  Returns
299
303
  -------
@@ -306,7 +310,7 @@ def hash_sha256( stream ):
306
310
  """
307
311
 
308
312
  if isinstance( stream, bytes ):
309
- with io.BytesIO( stream ) as _stream:
313
+ with BytesIO( stream ) as _stream:
310
314
  return hash_sha256( _stream )
311
315
 
312
316
  hasher = hashlib.sha256()
@@ -329,7 +333,7 @@ def hash_sha256( stream ):
329
333
 
330
334
  return digest_b64_nopad, size
331
335
 
332
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
336
+ #===============================================================================
333
337
  def email_encode_items(
334
338
  headers,
335
339
  payload = None ):
@@ -358,7 +362,7 @@ def email_encode_items(
358
362
  if payload is not None:
359
363
  msg.set_payload( payload )
360
364
 
361
- buffer = io.BytesIO()
365
+ buffer = BytesIO()
362
366
 
363
367
  gen = BytesGenerator(
364
368
  buffer,
@@ -373,7 +377,7 @@ def email_encode_items(
373
377
 
374
378
  return bytes
375
379
 
376
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
380
+ #===============================================================================
377
381
  class TimeEncode:
378
382
  digits = '0123456789abcdefghijklmnopqrstuvwxyz'
379
383
 
@@ -425,7 +429,7 @@ class TimeEncode:
425
429
 
426
430
  return f'{ts:0>{self.width}}'
427
431
 
428
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
432
+ #===============================================================================
429
433
  # File/directory names that are invalid on windows
430
434
  windows_invalid_filename = '|'.join([
431
435
  # filename ending with a period
@@ -1,6 +1,7 @@
1
1
  from .utils import (
2
2
  PathError,
3
- subdir )
3
+ subdir,
4
+ resolve)
4
5
 
5
6
  from .pattern import (
6
7
  PathPatternError,
@@ -1,5 +1,6 @@
1
- import os
1
+ from __future__ import annotations
2
2
  import os.path as osp
3
+ from os import PathLike
3
4
  import pathlib
4
5
  from pathlib import (
5
6
  PurePath,
@@ -13,19 +14,19 @@ from .pattern import (
13
14
  tr_path,
14
15
  tr_rel_join )
15
16
 
16
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17
+ #===============================================================================
17
18
  class PathMatcher:
18
19
  r"""Pattern matching similar to '.gitignore'
19
20
 
20
21
  Parameters
21
22
  ----------
22
- pattern: str
23
+ pattern:
23
24
  See notes below on pattern formatting.
24
- negate: bool
25
+ negate:
25
26
  A match to this pattern negates an existing match of the same name.
26
- dironly: bool
27
+ dironly:
27
28
  This pattern is to only match the name of a directory.
28
- relative: bool
29
+ relative:
29
30
  This pattern is to match relative paths instead of just the base name.
30
31
 
31
32
  Notes
@@ -82,10 +83,10 @@ class PathMatcher:
82
83
  """
83
84
  #-----------------------------------------------------------------------------
84
85
  def __init__(self,
85
- pattern,
86
- negate = False,
87
- dironly = False,
88
- relative = False ):
86
+ pattern: str,
87
+ negate: bool = False,
88
+ dironly: bool = False,
89
+ relative: bool = False):
89
90
 
90
91
  pattern = str(pattern).strip()
91
92
  _pattern = pattern
@@ -95,7 +96,7 @@ class PathMatcher:
95
96
  negate = True
96
97
  pattern = pattern[1:]
97
98
 
98
- elif pattern.startswith('\!'):
99
+ elif pattern.startswith(r'\!'):
99
100
  # Put a backslash ("\") in front of the first "!" for patterns that begin
100
101
  # with a literal "!", for example, "\!important!.txt".
101
102
  pattern = pattern[1:]
@@ -146,19 +147,15 @@ class PathMatcher:
146
147
  return f"{type(self).__name__}({args})"
147
148
 
148
149
  #-----------------------------------------------------------------------------
149
- def __call__(self, path):
150
- return self.match(path)
151
-
152
- #-----------------------------------------------------------------------------
153
- def match(self, path):
150
+ def match(self, path: PathLike) -> bool:
154
151
  """
155
152
  Parameters
156
153
  ----------
157
- path: str | pathlib.PurePath
154
+ path:
158
155
 
159
156
  Returns
160
157
  -------
161
- matched : bool
158
+ matched :
162
159
  True if the ``path`` matches this pattern
163
160
 
164
161
  """
@@ -171,28 +168,34 @@ class PathMatcher:
171
168
  return self._match(_path)
172
169
 
173
170
  #-----------------------------------------------------------------------------
174
- def nt(self, path):
171
+ __call__ = match
172
+
173
+ #-----------------------------------------------------------------------------
174
+ def nt(self, path: PathLike) -> bool:
175
175
  """Convenience method to force match as a Windows path
176
176
  """
177
177
  return self(PureWindowsPath(path))
178
178
 
179
179
  #-----------------------------------------------------------------------------
180
- def posix(self, path):
180
+ def posix(self, path: PathLike) -> bool:
181
181
  """Convenience method to force match as a POSIX path
182
182
  """
183
183
  return self(PurePosixPath(path))
184
184
 
185
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
185
+ #===============================================================================
186
186
  class PathFilter:
187
187
  """A combination of file patters applied relative to a given 'start' directory
188
188
 
189
189
  Parameters
190
190
  ----------
191
- patterns : list[str | PathMatcher]
192
- start : None | pathlib.PurePath
191
+ patterns:
192
+ start:
193
193
  """
194
194
  #-----------------------------------------------------------------------------
195
- def __init__(self, patterns = None, start = None):
195
+ def __init__(self,
196
+ patterns: list[str|PathMatcher] = None,
197
+ start: PathLike|None = None):
198
+
196
199
  if patterns is None:
197
200
  patterns = []
198
201
 
@@ -213,30 +216,33 @@ class PathFilter:
213
216
  self._start = _start
214
217
 
215
218
  #-----------------------------------------------------------------------------
216
- def filter(self, dir, fnames, dnames = None, feasible = None):
219
+ def filter(self,
220
+ dir: PathLike,
221
+ fnames: list[str],
222
+ dnames: list[str]|None = None,
223
+ feasible: set[str]|None = None) -> set[str]:
217
224
  """Filter a list of names in a directory
218
225
 
219
226
  Parameters
220
227
  ----------
221
- dir : PathLike | PurePath
228
+ dir:
222
229
  Directory containing ``dnames`` and ``fnames``.
223
- fnames : list[str]
230
+ fnames:
224
231
  List of file (non-directory) names in ``dir``.
225
-
226
- dnames : None | list[str]
232
+ dnames:
227
233
  List of directory names in ``dir``.
228
234
 
229
235
  .. note::
230
236
 
231
237
  If None, any fnames ending with '/' will be used as (directory) dnames.
232
238
 
233
- feasible : None | set[str]
239
+ feasible:
234
240
  The current feasible set of names (from either dnames or fnames) that have
235
241
  been matched.
236
242
 
237
243
  Returns
238
244
  -------
239
- feasible : set[str]
245
+ feasible:
240
246
  Updated feasible set of matched names. It is possible that the input
241
247
  feasible set contains names that are *not* in the output if a pattern
242
248
  negates an existing match.
@@ -290,13 +296,13 @@ class PathFilter:
290
296
  return feasible
291
297
 
292
298
 
293
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
299
+ #===============================================================================
294
300
  def contains(a, b):
295
301
  a = str(a)
296
302
  b = str(b)
297
303
  return a == osp.commonpath([a, b])
298
304
 
299
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
305
+ #===============================================================================
300
306
  def partition(test, vals):
301
307
  """Separates a single list into two lists
302
308
 
@@ -322,7 +328,7 @@ def partition(test, vals):
322
328
 
323
329
  return x, y
324
330
 
325
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
331
+ #===============================================================================
326
332
  def partition_dir(dir, names):
327
333
  """Separates a list of names into those that are directorys and all others.
328
334
  """
@@ -330,7 +336,7 @@ def partition_dir(dir, names):
330
336
  lambda name: not osp.isdir(osp.join(dir, name)),
331
337
  names )
332
338
 
333
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
339
+ #===============================================================================
334
340
  def combine_ignore_patterns(*patterns):
335
341
  """Creates a callable as ``ignore``
336
342
 
@@ -342,6 +348,7 @@ def combine_ignore_patterns(*patterns):
342
348
  -------
343
349
  callable(dir, names) -> matches
344
350
  """
351
+ # TODO: implement as callable object instead of closure for possible inspection
345
352
 
346
353
  def _ignore_patterns(dir, names):
347
354
  dir = PurePath(dir)