fpathlib 0.1.4.dev12__tar.gz → 0.1.5__tar.gz

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 (35) hide show
  1. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/.gitignore +1 -0
  2. {fpathlib-0.1.4.dev12/src/fpathlib.egg-info → fpathlib-0.1.5}/PKG-INFO +1 -1
  3. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/conda-recipe/meta.yaml +2 -2
  4. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/scripts/pypi.sh +8 -1
  5. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/__init__.py +2 -0
  6. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/_version.py +3 -3
  7. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/expand.py +51 -14
  8. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/ext/polars.py +1 -1
  9. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/fpath.py +53 -14
  10. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5/src/fpathlib.egg-info}/PKG-INFO +1 -1
  11. fpathlib-0.1.5/src/fpathlib.egg-info/scm_version.json +8 -0
  12. fpathlib-0.1.4.dev12/src/fpathlib.egg-info/scm_version.json +0 -8
  13. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/LICENSE +0 -0
  14. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/MANIFEST.in +0 -0
  15. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/README.md +0 -0
  16. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/TODO.txt +0 -0
  17. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/docs/Makefile +0 -0
  18. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/docs/make.bat +0 -0
  19. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/docs/source/api.rst +0 -0
  20. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/docs/source/conf.py +0 -0
  21. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/docs/source/index.rst +0 -0
  22. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/pyproject.toml +0 -0
  23. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/scripts/conda.sh +0 -0
  24. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/scripts/deploy.sh +0 -0
  25. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/scripts/docs.sh +0 -0
  26. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/scripts/tag.sh +0 -0
  27. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/setup.cfg +0 -0
  28. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/ext/__init__.py +0 -0
  29. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/path.py +0 -0
  30. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib/utils.py +0 -0
  31. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib.egg-info/SOURCES.txt +0 -0
  32. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib.egg-info/dependency_links.txt +0 -0
  33. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib.egg-info/requires.txt +0 -0
  34. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib.egg-info/scm_file_list.json +0 -0
  35. {fpathlib-0.1.4.dev12 → fpathlib-0.1.5}/src/fpathlib.egg-info/top_level.txt +0 -0
@@ -5,3 +5,4 @@ docs/build
5
5
  dist
6
6
  build
7
7
  src/fpathlib/_version.py
8
+ *.swp
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fpathlib
3
- Version: 0.1.4.dev12
3
+ Version: 0.1.5
4
4
  Summary: A package to combine paths with metadata
5
5
  Author-email: "C. Lockhart" <clockha2@gmu.edu>
6
6
  Requires-Python: >=3.12
@@ -1,5 +1,5 @@
1
1
  {% set name = "fpathlib" %}
2
- {% set version = "0.1.3" %}
2
+ {% set version = "0.1.4" %}
3
3
 
4
4
  package:
5
5
  name: {{ name|lower }}
@@ -7,7 +7,7 @@ package:
7
7
 
8
8
  source:
9
9
  url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/fpathlib-{{ version }}.tar.gz
10
- sha256: 059dbf937c6e965ef33f15aa214b2f354550d5dfa26b9030357954cce8c51874
10
+ sha256: 8ff44332a465c2adb5c45f94d6ee79c958fb50c39e896b16734d0d8208026c77
11
11
 
12
12
  build:
13
13
  noarch: python
@@ -21,7 +21,14 @@ python3 -m build
21
21
  # 0.1.3/0.1.2 tag-collision incident slipped past a git-describe check even
22
22
  # though the build itself came out as a .devN), inspect what actually got
23
23
  # built. This is the real signal of whether the release is clean.
24
- dev_artifacts=$(ls dist/ | grep -c '\.dev[0-9]')
24
+ #
25
+ # `grep -c` exits 1 (not just prints "0") when it finds zero matches --
26
+ # with `set -e` active (deploy.sh sets it, and this script is sourced into
27
+ # that same shell), that silently killed the whole deploy right here for
28
+ # every *clean* release build (the exact case with 0 .dev files), before
29
+ # ever reaching twine upload. `|| true` keeps the count without letting
30
+ # grep's "no matches" exit status abort the script.
31
+ dev_artifacts=$(ls dist/ | grep -c '\.dev[0-9]' || true)
25
32
  if [ $allow_dev -eq 0 ] && [ "$dev_artifacts" != "0" ]
26
33
  then
27
34
  echo "built version is a dev version (tag doesn't point at a clean, distinct commit -- check 'git describe --tags --long' and 'git status'), not uploading to pypi"
@@ -2,6 +2,7 @@ from fpathlib.path import Path
2
2
  from fpathlib.fpath import FPath, ExpandedFPath
3
3
  from fpathlib.expand import (
4
4
  expand_fpath,
5
+ iexpand_fpath,
5
6
  expand_fpath_decorator,
6
7
  is_expandable,
7
8
  )
@@ -11,6 +12,7 @@ __all__ = [
11
12
  "FPath",
12
13
  "ExpandedFPath",
13
14
  "expand_fpath",
15
+ "iexpand_fpath",
14
16
  "expand_fpath_decorator",
15
17
  "is_expandable",
16
18
  ]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.4.dev12'
22
- __version_tuple__ = version_tuple = (0, 1, 4, 'dev12')
21
+ __version__ = version = '0.1.5'
22
+ __version_tuple__ = version_tuple = (0, 1, 5)
23
23
 
24
- __commit_id__ = commit_id = 'g2ae3f3b01'
24
+ __commit_id__ = commit_id = 'gae98b27f0'
@@ -31,7 +31,39 @@ def expand_fpath(fpath, *, exclude_path_patterns=None, require_metadata=True):
31
31
  )
32
32
 
33
33
 
34
- def expand_fpath_decorator(f=None, require_expandable=True, post_process=None):
34
+ def iexpand_fpath(fpath, *, exclude_path_patterns=None, require_metadata=True, errors="raise"):
35
+ """
36
+ Generator equivalent of :func:`.expand_fpath` -- lazily yields each
37
+ matching :obj:`.Path` instead of building the whole
38
+ :obj:`.ExpandedFPath` up front. This is a convenience function that
39
+ simply creates an :obj:`FPath` and calls its :meth:`.FPath.iexpand`
40
+ method.
41
+
42
+ Parameters
43
+ ----------
44
+ fpath : :obj:`str`
45
+ An f-string path, where the variables are captured and stored along the path
46
+ name.
47
+ exclude_path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
48
+ Exclude paths that match the supplied pattern. (Default: None).
49
+ require_metadata : :obj:`bool`
50
+ Require that all paths identified must have found metadata. (Default: True).
51
+ errors : :obj:`str`
52
+ How to handle the "no matches" case. See :meth:`.FPath.iexpand`. (Default: "raise").
53
+
54
+ Yields
55
+ ------
56
+ :obj:`.Path`
57
+ """
58
+
59
+ return FPath(fpath).iexpand(
60
+ exclude_path_patterns=exclude_path_patterns,
61
+ require_metadata=require_metadata,
62
+ errors=errors,
63
+ )
64
+
65
+
66
+ def expand_fpath_decorator(f=None, require_expandable=False, post_process=None):
35
67
  """
36
68
  Decorator for :func:`.expand_fpath`.
37
69
 
@@ -39,6 +71,11 @@ def expand_fpath_decorator(f=None, require_expandable=True, post_process=None):
39
71
  ----------
40
72
  f : :obj:`callable`
41
73
  A function that takes an :obj:`.ExpandedFPath` as its first argument.
74
+ require_expandable : :obj:`bool`
75
+ Whether to require `fpath` to have {} named captures. If False
76
+ (the default), a plain literal path or glob is passed straight
77
+ through to `f` unexpanded, with `post_process` skipped -- there's
78
+ no ExpandedFPath to hand it in that case. (Default: False)
42
79
  post_process : :obj:`callable`
43
80
  A function that takes the output of `f` and the :obj:`.ExpandedFPath`.
44
81
  (Default: None).
@@ -52,18 +89,8 @@ def expand_fpath_decorator(f=None, require_expandable=True, post_process=None):
52
89
  expanded_fpath = fpath
53
90
  result = f(fpath, *args, **kwargs)
54
91
 
55
- # Otherwise, expand fpath and call f with the result.
56
- else:
57
- # What happens if fpath is not expandable?
58
- # If require_expandable is True, raise an error.
59
- # Otherwise, just call f with the original fpath.
60
- if not is_expandable(fpath):
61
- if require_expandable:
62
- msg = f"fpath not expandable: '{fpath}'"
63
- raise ValueError(msg)
64
- return f(fpath, *args, **kwargs)
65
-
66
- # We know fpath is expandable, so we can expand it and call f
92
+ # Expand fpath if it is expandable.
93
+ elif is_expandable(fpath):
67
94
  exclude_path_patterns = kwargs.pop("exclude_path_patterns", None)
68
95
  require_metadata = kwargs.pop("require_metadata", True)
69
96
  expanded_fpath = expand_fpath(
@@ -73,7 +100,17 @@ def expand_fpath_decorator(f=None, require_expandable=True, post_process=None):
73
100
  )
74
101
  result = f(expanded_fpath, *args, **kwargs)
75
102
 
76
- # If post_process is provided, call it with the result and the expanded_fpath.
103
+ # fpath has no {} captures, so there's no ExpandedFPath to
104
+ # build -- nothing for post_process (e.g. join_metadata) to
105
+ # join metadata from. Call f directly and return immediately,
106
+ # skipping post_process entirely, rather than falling through
107
+ # to it with no expanded_fpath to give it.
108
+ else:
109
+ if require_expandable:
110
+ msg = f"fpath not expandable: '{fpath}'"
111
+ raise ValueError(msg)
112
+ return f(fpath, *args, **kwargs)
113
+
77
114
  if post_process is not None:
78
115
  result = post_process(result, expanded_fpath)
79
116
 
@@ -160,7 +160,7 @@ def scan_parquet(expanded_fpath, *args, **kwargs):
160
160
 
161
161
 
162
162
  # TODO rename expanded_fpath as source
163
- @expand_fpath_decorator(require_expandable=False, post_process=join_metadata)
163
+ @expand_fpath_decorator(post_process=join_metadata)
164
164
  def scan_txt(
165
165
  expanded_fpath,
166
166
  filter_expr=None,
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Sequence
2
- from glob import glob
2
+ from glob import glob, iglob
3
3
  import parse
4
4
  import re
5
5
 
@@ -31,10 +31,19 @@ class FPath:
31
31
  def __repr__(self):
32
32
  return "FPath({!r})".format(self.fpath)
33
33
 
34
- def expand(self, exclude_path_patterns=None, require_metadata=True, errors="raise"):
34
+ def iexpand(self, exclude_path_patterns=None, require_metadata=True, errors="raise"):
35
35
  """
36
- Use an f-string to extract out a collection of paths, where the f-string
37
- variables are captured and stored along the path name.
36
+ Lazily yield each :obj:`.Path` matching the f-string pattern, one at
37
+ a time, instead of building the whole :obj:`.ExpandedFPath` up
38
+ front. Useful when a pattern could match a very large number of
39
+ files and you don't want them all held in memory at once, or want
40
+ to start processing before the full glob finishes walking the
41
+ filesystem. :meth:`.expand` is built on top of this generator.
42
+
43
+ Like any generator function, nothing in the body runs -- including
44
+ argument validation and the wildcard/{} capture check -- until the
45
+ result is first iterated (a `for` loop, `list(...)`, etc.), not at
46
+ the moment :meth:`.iexpand` is called.
38
47
 
39
48
  Parameters
40
49
  ----------
@@ -43,13 +52,13 @@ class FPath:
43
52
  require_metadata : :obj:`bool`
44
53
  Require that all paths identified must have found metadata. (Default: True).
45
54
  errors : :obj:`str`
46
- How to handle errors. If "raise", then raise an error. If "warn", then warn
47
- and return an empty collection. If "ignore", then ignore the error and
48
- return an empty collection. (Default: "raise").
55
+ How to handle the "no matches" case, checked once the pattern is
56
+ fully exhausted. If "raise", then raise an error. If "warn",
57
+ then warn. If "ignore", then do nothing. (Default: "raise").
49
58
 
50
- Returns
51
- -------
52
- :obj:`.ExpandedFPath`
59
+ Yields
60
+ ------
61
+ :obj:`.Path`
53
62
  """
54
63
 
55
64
  if errors not in {"raise", "warn", "ignore"}:
@@ -84,8 +93,8 @@ class FPath:
84
93
  )
85
94
  raise ValueError(msg)
86
95
 
87
- paths = []
88
- for fname in glob(re.sub(r"\{.*?\}", "*", self.fpath)):
96
+ n = 0
97
+ for fname in iglob(re.sub(r"\{.*?\}", "*", self.fpath)):
89
98
  path = Path(fname)
90
99
  if exclude_path_patterns and path.match_any(exclude_path_patterns):
91
100
  continue
@@ -93,9 +102,10 @@ class FPath:
93
102
  if require_metadata and path.metadata is None:
94
103
  msg = f"metadata not found for '{fname}' with '{self.fpath}'"
95
104
  raise AttributeError(msg)
96
- paths.append(path)
105
+ n += 1
106
+ yield path
97
107
 
98
- if len(paths) == 0 and errors != "ignore":
108
+ if n == 0 and errors != "ignore":
99
109
  msg = f"no paths found for {self.fpath.__repr__()}"
100
110
  if errors == "raise":
101
111
  raise IOError(msg)
@@ -104,6 +114,35 @@ class FPath:
104
114
 
105
115
  warnings.warn(msg)
106
116
 
117
+ def expand(self, exclude_path_patterns=None, require_metadata=True, errors="raise"):
118
+ """
119
+ Use an f-string to extract out a collection of paths, where the f-string
120
+ variables are captured and stored along the path name.
121
+
122
+ Parameters
123
+ ----------
124
+ exclude_path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
125
+ Exclude paths that match the supplied pattern. (Default: None).
126
+ require_metadata : :obj:`bool`
127
+ Require that all paths identified must have found metadata. (Default: True).
128
+ errors : :obj:`str`
129
+ How to handle errors. If "raise", then raise an error. If "warn", then warn
130
+ and return an empty collection. If "ignore", then ignore the error and
131
+ return an empty collection. (Default: "raise").
132
+
133
+ Returns
134
+ -------
135
+ :obj:`.ExpandedFPath`
136
+ """
137
+
138
+ paths = list(
139
+ self.iexpand(
140
+ exclude_path_patterns=exclude_path_patterns,
141
+ require_metadata=require_metadata,
142
+ errors=errors,
143
+ )
144
+ )
145
+
107
146
  return ExpandedFPath(paths=paths, fpath=self)
108
147
 
109
148
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fpathlib
3
- Version: 0.1.4.dev12
3
+ Version: 0.1.5
4
4
  Summary: A package to combine paths with metadata
5
5
  Author-email: "C. Lockhart" <clockha2@gmu.edu>
6
6
  Requires-Python: >=3.12
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.1.5",
3
+ "distance": 0,
4
+ "node": "gae98b27f09095479f5ae8272b52f73655b3c8a4b",
5
+ "dirty": false,
6
+ "branch": "main",
7
+ "node_date": "2026-09-17"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.1.3",
3
- "distance": 12,
4
- "node": "g2ae3f3b01d310e4bf9308863ebc666476069caf8",
5
- "dirty": false,
6
- "branch": "main",
7
- "node_date": "2026-09-16"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes