PyProd 0.5.0__tar.gz → 0.7.0__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 (51) hide show
  1. {pyprod-0.5.0 → pyprod-0.7.0}/PKG-INFO +1 -1
  2. {pyprod-0.5.0 → pyprod-0.7.0}/docs/releasenotes.rst +10 -0
  3. pyprod-0.7.0/src/pyprod/__init__.py +1 -0
  4. {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/main.py +13 -0
  5. {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/prod.py +29 -2
  6. {pyprod-0.5.0 → pyprod-0.7.0}/tests/test_prod.py +47 -2
  7. {pyprod-0.5.0 → pyprod-0.7.0}/tests/test_rule.py +0 -3
  8. pyprod-0.5.0/src/pyprod/__init__.py +0 -1
  9. {pyprod-0.5.0 → pyprod-0.7.0}/.github/workflows/publish.yml +0 -0
  10. {pyprod-0.5.0 → pyprod-0.7.0}/.github/workflows/test.yml +0 -0
  11. {pyprod-0.5.0 → pyprod-0.7.0}/.gitignore +0 -0
  12. {pyprod-0.5.0 → pyprod-0.7.0}/LICENSE +0 -0
  13. {pyprod-0.5.0 → pyprod-0.7.0}/README.rst +0 -0
  14. {pyprod-0.5.0 → pyprod-0.7.0}/docs/Makefile +0 -0
  15. {pyprod-0.5.0 → pyprod-0.7.0}/docs/commandline.rst +0 -0
  16. {pyprod-0.5.0 → pyprod-0.7.0}/docs/conf.py +0 -0
  17. {pyprod-0.5.0 → pyprod-0.7.0}/docs/index.rst +0 -0
  18. {pyprod-0.5.0 → pyprod-0.7.0}/docs/make.bat +0 -0
  19. {pyprod-0.5.0 → pyprod-0.7.0}/docs/prodfile.rst +0 -0
  20. {pyprod-0.5.0 → pyprod-0.7.0}/docs/pyprod2.png +0 -0
  21. {pyprod-0.5.0 → pyprod-0.7.0}/docs/quickstart.rst +0 -0
  22. {pyprod-0.5.0 → pyprod-0.7.0}/docs/requirements.txt +0 -0
  23. {pyprod-0.5.0 → pyprod-0.7.0}/pyprod.webp +0 -0
  24. {pyprod-0.5.0 → pyprod-0.7.0}/pyprod2.png +0 -0
  25. {pyprod-0.5.0 → pyprod-0.7.0}/pyproject.toml +0 -0
  26. {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/Makefile +0 -0
  27. {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/Prodfile.py +0 -0
  28. {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/hello.c +0 -0
  29. {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/hello.h +0 -0
  30. {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/main.c +0 -0
  31. {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/Prodfile.py +0 -0
  32. {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/a.txt +0 -0
  33. {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/b.txt +0 -0
  34. {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/c.txt +0 -0
  35. {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/inc1.txt +0 -0
  36. {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/inc2.txt +0 -0
  37. {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/Prodfile.py +0 -0
  38. {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/doc.md +0 -0
  39. {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/md_to_html.py +0 -0
  40. {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/template.html +0 -0
  41. {pyprod-0.5.0 → pyprod-0.7.0}/samples/s3files/Prodfile.py +0 -0
  42. {pyprod-0.5.0 → pyprod-0.7.0}/samples/s3files/S3TEST.txt +0 -0
  43. {pyprod-0.5.0 → pyprod-0.7.0}/samples/tutorial-1/Prodfile.py +0 -0
  44. {pyprod-0.5.0 → pyprod-0.7.0}/samples/tutorial-2/Prodfile.py +0 -0
  45. {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/__main__.py +0 -0
  46. {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/utils.py +0 -0
  47. {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/venv.py +0 -0
  48. {pyprod-0.5.0 → pyprod-0.7.0}/tests/__init__.py +0 -0
  49. {pyprod-0.5.0 → pyprod-0.7.0}/tests/conftest.py +0 -0
  50. {pyprod-0.5.0 → pyprod-0.7.0}/tests/test_prodfuncs.py +0 -0
  51. {pyprod-0.5.0 → pyprod-0.7.0}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyProd
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: PyProd: More Makeable than Make
5
5
  Project-URL: Homepage, https://github.com/atsuoishimoto/pyprod
6
6
  Project-URL: Documentation, https://pyprod.readthedocs.io/en/latest/
@@ -1,6 +1,16 @@
1
1
  Release Notes
2
2
  ================
3
3
 
4
+ 0.7.0 (2025-4-6)
5
+ -----------------------------
6
+
7
+ - Allow wildcard in the ``depends``.
8
+
9
+ 0.6.0 (2025-4-6)
10
+ -----------------------------
11
+
12
+ - Ignore None or falsy values in the ``depends`` and ``uses`` in the rule.
13
+
4
14
  0.5.0 (2025-2-12)
5
15
  -----------------------------
6
16
 
@@ -0,0 +1 @@
1
+ __version__ = "0.7.0"
@@ -53,6 +53,15 @@ parser.add_argument(
53
53
  help="Increase verbosity level (default: 0)",
54
54
  )
55
55
 
56
+ parser.add_argument(
57
+ "-V",
58
+ "--version",
59
+ dest="version",
60
+ action="store_true",
61
+ default=0,
62
+ help="Show version",
63
+ )
64
+
56
65
 
57
66
  parser.add_argument("targets", nargs="*", help="Build targets")
58
67
 
@@ -74,6 +83,10 @@ def init_args(args=None):
74
83
 
75
84
  def main():
76
85
  args = init_args()
86
+ if args.version:
87
+ print(f"PyProd {pyprod.__version__}")
88
+ sys.exit(0)
89
+
77
90
  pyprod.verbose = args.verbose
78
91
  chdir = args.directory
79
92
  if chdir:
@@ -17,7 +17,9 @@ from dataclasses import dataclass, field
17
17
  from fnmatch import fnmatch, translate
18
18
  from functools import wraps
19
19
  from pathlib import Path
20
+
20
21
  import dateutil.parser
22
+
21
23
  import pyprod
22
24
 
23
25
  from .utils import flatten, unique_list
@@ -164,7 +166,7 @@ def _check_pattern(pattern):
164
166
 
165
167
  def _check_wildcard(path):
166
168
  if "*" in path:
167
- raise RuleError(f"{path}: '*' directory is not allowed")
169
+ raise RuleError(f"{path}: '*' is not allowed")
168
170
 
169
171
 
170
172
  def _name_to_str(name):
@@ -183,6 +185,25 @@ def _name_to_str(name):
183
185
  return name
184
186
 
185
187
 
188
+ def _expand_glob(name):
189
+ if not isinstance(name, (str, Path)):
190
+ return name
191
+
192
+ if "*" not in str(name):
193
+ return name
194
+
195
+ # split path to support absolute path
196
+ p = Path(name)
197
+ parts = p.parts
198
+ for i, part in enumerate(parts):
199
+ if "*" in part:
200
+ root = Path(*parts[:i])
201
+ rest = "/".join(parts[i:])
202
+ break
203
+
204
+ return list(root.glob(rest))
205
+
206
+
186
207
  class Rule:
187
208
  def __init__(self, targets, pattern=None, depends=(), uses=(), builder=None):
188
209
  self.targets = []
@@ -216,13 +237,18 @@ class Rule:
216
237
 
217
238
  self.depends = []
218
239
  for depend in flatten(depends or ()):
240
+ if not depend:
241
+ continue
242
+
219
243
  depend = _name_to_str(depend)
220
244
  _check_pattern_count(depend)
221
- _check_wildcard(depend)
222
245
  self.depends.append(depend)
223
246
 
224
247
  self.uses = []
225
248
  for use in flatten(uses or ()):
249
+ if not use:
250
+ continue
251
+
226
252
  use = _name_to_str(use)
227
253
  _check_pattern_count(use)
228
254
  _check_wildcard(use)
@@ -346,6 +372,7 @@ class Rules:
346
372
  depends = dep.depends[:]
347
373
  uses = dep.uses[:]
348
374
 
375
+ depends = list(flatten(_expand_glob(depend) for depend in depends))
349
376
  yield depends, uses, dep
350
377
  break
351
378
 
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import os
2
3
  from contextlib import contextmanager
3
4
  from pathlib import Path
@@ -71,7 +72,7 @@ async def test_prod(tmp_path, jobs):
71
72
  p = prod.Prod("Prodfile.py", 4)
72
73
  await p.start(["app.exe"])
73
74
 
74
- assert mtime < (tmp_path / "app.exe").stat().st_mtime
75
+ assert mtime <= (tmp_path / "app.exe").stat().st_mtime
75
76
 
76
77
 
77
78
  @pytest.mark.asyncio
@@ -95,7 +96,7 @@ def build_app(target, a, b):
95
96
  Path(target).write_text(f"{target}, {a}, {b}")
96
97
  """
97
98
 
98
- Path(tmp_path / "Prodfile.py").write_text(src)
99
+ (tmp_path / "Prodfile.py").write_text(src)
99
100
 
100
101
  with chdir(tmp_path):
101
102
  p = prod.Prod("Prodfile.py", 4)
@@ -108,6 +109,50 @@ def build_app(target, a, b):
108
109
  assert (tmp_path / "b.c").read_text() == "b.c"
109
110
 
110
111
 
112
+ @pytest.mark.asyncio
113
+ async def test_dep_glob(tmp_path):
114
+ src = """
115
+ @rule(targets=("%.o"), depends=Path("deps/%/*/dep"))
116
+ def build(target, *deps):
117
+ assert isinstance(target, str)
118
+ p = Path(target)
119
+ assert set(deps) == {Path("deps/aaa/bbb/dep"), Path("deps/aaa/ccc/dep")}
120
+ p.write_text(repr([str(p) for p in deps]))
121
+ """
122
+
123
+ (tmp_path / "Prodfile.py").write_text(src)
124
+
125
+ (tmp_path / "deps/aaa/bbb/").mkdir(parents=True, exist_ok=True)
126
+ (tmp_path / "deps/aaa/bbb/dep").write_text("1")
127
+
128
+ (tmp_path / "deps/aaa/ccc/").mkdir(parents=True, exist_ok=True)
129
+ (tmp_path / "deps/aaa/ccc/dep").write_text("2")
130
+
131
+ (tmp_path / "deps/aaa/ddd/").mkdir(parents=True, exist_ok=True)
132
+
133
+ with chdir(tmp_path):
134
+ p = prod.Prod("Prodfile.py")
135
+ await p.start(["aaa.o"])
136
+ print((tmp_path / "aaa.o").read_text())
137
+
138
+
139
+ @pytest.mark.asyncio
140
+ async def test_dep_glob_no_match(tmp_path):
141
+ src = """
142
+ @rule(targets=("%.o"), depends=Path("deps/%/*/dep"))
143
+ def build(target, *deps):
144
+ assert isinstance(target, str)
145
+ p = Path(target)
146
+ p.write_text(str(target)+"-"+str(deps))
147
+ """
148
+
149
+ (tmp_path / "Prodfile.py").write_text(src)
150
+ with chdir(tmp_path):
151
+ p = prod.Prod("Prodfile.py")
152
+ await p.start(["aaa.o"])
153
+ assert (tmp_path / "aaa.o").read_text() == "aaa.o-()"
154
+
155
+
111
156
  @pytest.mark.asyncio
112
157
  async def test_preserve_pathobj(tmp_path):
113
158
  src = """
@@ -126,9 +126,6 @@ def test_validate_depends():
126
126
  with pytest.raises(prod.RuleError):
127
127
  prod.Rule("a.%", None, "%.%", "")
128
128
 
129
- with pytest.raises(prod.RuleError):
130
- prod.Rule("a.%", None, "*/x.y", "")
131
-
132
129
  prod.Rule("a.b", None, "x.y", "")
133
130
 
134
131
 
@@ -1 +0,0 @@
1
- __version__ = "0.5.0"
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
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes