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.
- {pyprod-0.5.0 → pyprod-0.7.0}/PKG-INFO +1 -1
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/releasenotes.rst +10 -0
- pyprod-0.7.0/src/pyprod/__init__.py +1 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/main.py +13 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/prod.py +29 -2
- {pyprod-0.5.0 → pyprod-0.7.0}/tests/test_prod.py +47 -2
- {pyprod-0.5.0 → pyprod-0.7.0}/tests/test_rule.py +0 -3
- pyprod-0.5.0/src/pyprod/__init__.py +0 -1
- {pyprod-0.5.0 → pyprod-0.7.0}/.github/workflows/publish.yml +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/.github/workflows/test.yml +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/.gitignore +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/LICENSE +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/README.rst +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/Makefile +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/commandline.rst +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/conf.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/index.rst +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/make.bat +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/prodfile.rst +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/pyprod2.png +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/quickstart.rst +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/docs/requirements.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/pyprod.webp +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/pyprod2.png +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/pyproject.toml +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/Makefile +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/Prodfile.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/hello.c +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/hello.h +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/build-c/main.c +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/Prodfile.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/a.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/b.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/c.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/inc1.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/generate-doc/inc2.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/Prodfile.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/doc.md +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/md_to_html.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/md-to-pdf/template.html +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/s3files/Prodfile.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/s3files/S3TEST.txt +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/tutorial-1/Prodfile.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/samples/tutorial-2/Prodfile.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/__main__.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/utils.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/src/pyprod/venv.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/tests/__init__.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/tests/conftest.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/tests/test_prodfuncs.py +0 -0
- {pyprod-0.5.0 → pyprod-0.7.0}/tests/utils.py +0 -0
@@ -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}: '*'
|
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
|
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
|
-
|
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 = """
|
@@ -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
|
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
|