PyProd 0.2.0.post1__py3-none-any.whl → 0.3.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- pyprod/prod.py +147 -62
- {pyprod-0.2.0.post1.dist-info → pyprod-0.3.0.dist-info}/METADATA +1 -1
- pyprod-0.3.0.dist-info/RECORD +11 -0
- pyprod-0.2.0.post1.dist-info/RECORD +0 -11
- {pyprod-0.2.0.post1.dist-info → pyprod-0.3.0.dist-info}/WHEEL +0 -0
- {pyprod-0.2.0.post1.dist-info → pyprod-0.3.0.dist-info}/entry_points.txt +0 -0
- {pyprod-0.2.0.post1.dist-info → pyprod-0.3.0.dist-info}/licenses/LICENSE +0 -0
pyprod/prod.py
CHANGED
@@ -33,7 +33,7 @@ class NoRuleToMakeTargetError(Exception):
|
|
33
33
|
pass
|
34
34
|
|
35
35
|
|
36
|
-
class
|
36
|
+
class RuleError(Exception):
|
37
37
|
pass
|
38
38
|
|
39
39
|
|
@@ -109,13 +109,11 @@ def glob(path, dir="."):
|
|
109
109
|
|
110
110
|
def rule_to_re(rule):
|
111
111
|
if not isinstance(rule, (str, Path)):
|
112
|
-
raise
|
112
|
+
raise RuleError(rule)
|
113
113
|
|
114
114
|
srule = str(rule)
|
115
115
|
srule = translate(srule)
|
116
116
|
srule = replace_pattern(srule, "(?P<stem>.*)", maxreplace=1)
|
117
|
-
if isinstance(rule, Path):
|
118
|
-
return Path(srule)
|
119
117
|
return srule
|
120
118
|
|
121
119
|
|
@@ -132,38 +130,97 @@ def replace_pattern(rule, replaceto, *, maxreplace=None):
|
|
132
130
|
if maxreplace is not None:
|
133
131
|
if n > maxreplace:
|
134
132
|
# contains multiple '%'
|
135
|
-
raise
|
133
|
+
raise RuleError(f"{s_rule} contains multiple '%'")
|
134
|
+
|
136
135
|
return replaceto
|
137
136
|
|
138
137
|
s_rule = re.sub("%%|%", f, s_rule)
|
139
|
-
if isinstance(rule, Path):
|
140
|
-
return Path(s_rule)
|
141
138
|
return s_rule
|
142
139
|
|
143
140
|
|
144
|
-
|
141
|
+
def _check_pattern_count(pattern):
|
142
|
+
"""Counts number of '%' in the pattern"""
|
143
|
+
matches = re.finditer(r"%%|%", pattern)
|
144
|
+
num = len([m for m in matches if len(m[0]) == 1])
|
145
|
+
if num > 1:
|
146
|
+
raise RuleError(f"{pattern}: Multiple '%' is not allowed")
|
147
|
+
return num
|
148
|
+
|
149
|
+
|
150
|
+
def _check_pattern(pattern):
|
151
|
+
matches = re.finditer(r"%%|%", pattern)
|
152
|
+
singles = [m for m in matches if len(m[0]) == 1]
|
153
|
+
if len(singles) > 1:
|
154
|
+
raise RuleError(f"{pattern}: Multiple '%' is not allowed")
|
155
|
+
if not len(singles):
|
156
|
+
raise RuleError(f"{pattern}: Pattern should contain a '%'.")
|
157
|
+
|
158
|
+
|
159
|
+
def _strip_dot(path):
|
160
|
+
if not path:
|
161
|
+
return path
|
162
|
+
path = Path(path) # ./aaa/ -> aaa
|
163
|
+
parts = path.parts
|
164
|
+
if ".." in parts:
|
165
|
+
raise RuleError(f"{path}: '..' directory is not allowed")
|
166
|
+
return str(path)
|
167
|
+
|
168
|
+
|
169
|
+
def _check_wildcard(path):
|
170
|
+
if "*" in path:
|
171
|
+
raise RuleError(f"{path}: '*' directory is not allowed")
|
172
|
+
|
173
|
+
|
145
174
|
class Rule:
|
146
|
-
targets:
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
175
|
+
def __init__(self, targets, pattern, depends, uses, builder=None):
|
176
|
+
self.targets = []
|
177
|
+
for target in flatten(targets or ()):
|
178
|
+
target = str(target)
|
179
|
+
_check_pattern_count(target)
|
180
|
+
target = _strip_dot(target)
|
181
|
+
target = rule_to_re(target)
|
182
|
+
self.targets.append(target)
|
183
|
+
|
184
|
+
self.first_target = None
|
185
|
+
for target in flatten(targets or ()):
|
186
|
+
target = str(target)
|
187
|
+
if not target:
|
188
|
+
continue
|
189
|
+
|
190
|
+
if "*" in target:
|
191
|
+
continue
|
192
|
+
|
193
|
+
if _check_pattern_count(target) == 0:
|
194
|
+
# not contain one %
|
195
|
+
self.first_target = target
|
196
|
+
break
|
197
|
+
|
198
|
+
if pattern:
|
199
|
+
pattern = _strip_dot(pattern)
|
200
|
+
if _check_pattern_count(pattern) != 1:
|
201
|
+
raise RuleError(f"{pattern}: Pattern should contain a '%'")
|
202
|
+
|
203
|
+
self.pattern = rule_to_re(pattern)
|
204
|
+
else:
|
205
|
+
self.pattern = None
|
206
|
+
|
207
|
+
self.depends = []
|
208
|
+
for depend in flatten(depends or ()):
|
209
|
+
depend = str(depend)
|
210
|
+
_check_pattern_count(depend)
|
211
|
+
_check_wildcard(depend)
|
212
|
+
depend = _strip_dot(depend)
|
213
|
+
self.depends.append(depend)
|
214
|
+
|
215
|
+
self.uses = []
|
216
|
+
for use in flatten(uses or ()):
|
217
|
+
use = str(use)
|
218
|
+
_check_pattern_count(use)
|
219
|
+
_check_wildcard(use)
|
220
|
+
use = _strip_dot(use)
|
221
|
+
self.uses.append(use)
|
222
|
+
|
223
|
+
self.builder = builder
|
167
224
|
|
168
225
|
def __call__(self, f):
|
169
226
|
self.builder = f
|
@@ -211,12 +268,15 @@ class Rules:
|
|
211
268
|
stem = d
|
212
269
|
elif dep.pattern:
|
213
270
|
m = re.fullmatch(str(dep.pattern), name)
|
214
|
-
|
215
|
-
|
216
|
-
stem = d
|
271
|
+
if m:
|
272
|
+
stem = m.groupdict().get("stem", None)
|
217
273
|
|
218
|
-
|
219
|
-
|
274
|
+
if stem is not None:
|
275
|
+
depends = [replace_pattern(r, stem) for r in dep.depends]
|
276
|
+
uses = [replace_pattern(r, stem) for r in dep.uses]
|
277
|
+
else:
|
278
|
+
depends = dep.depends[:]
|
279
|
+
uses = dep.uses[:]
|
220
280
|
|
221
281
|
yield depends, uses, dep
|
222
282
|
break
|
@@ -237,14 +297,8 @@ class Rules:
|
|
237
297
|
|
238
298
|
def select_first_target(self):
|
239
299
|
for dep in self.rules:
|
240
|
-
|
241
|
-
|
242
|
-
# pattern?
|
243
|
-
matches = re.finditer(r"%%|%", s_target)
|
244
|
-
if any((len(m[0]) == 1) for m in matches):
|
245
|
-
# has %
|
246
|
-
continue
|
247
|
-
return target
|
300
|
+
if dep.first_target:
|
301
|
+
return dep.first_target
|
248
302
|
|
249
303
|
def select_builder(self, name):
|
250
304
|
for depends, uses, dep in self.iter_rule(name):
|
@@ -356,6 +410,30 @@ class Envs:
|
|
356
410
|
return os.environ.get(name, default=default)
|
357
411
|
|
358
412
|
|
413
|
+
def read(filename):
|
414
|
+
with open(filename, "r") as f:
|
415
|
+
return f.read()
|
416
|
+
|
417
|
+
|
418
|
+
def write(filename, s, append=False):
|
419
|
+
mode = "a" if append else "w"
|
420
|
+
with open(filename, mode) as f:
|
421
|
+
f.write(s)
|
422
|
+
|
423
|
+
|
424
|
+
def quote(s):
|
425
|
+
return shlex.quote(str(s))
|
426
|
+
|
427
|
+
|
428
|
+
def squote(*s):
|
429
|
+
ret = [shlex.quote(str(x)) for x in flatten(s)]
|
430
|
+
return ret
|
431
|
+
|
432
|
+
|
433
|
+
def makedirs(path):
|
434
|
+
os.makedirs(path, exist_ok=True)
|
435
|
+
|
436
|
+
|
359
437
|
class Prod:
|
360
438
|
def __init__(self, modulefile, njobs=1, params=None):
|
361
439
|
self.modulefile = Path(modulefile)
|
@@ -372,18 +450,25 @@ class Prod:
|
|
372
450
|
|
373
451
|
def get_module_globals(self):
|
374
452
|
globals = {
|
453
|
+
"capture": capture,
|
454
|
+
"check": self.checkers.check,
|
455
|
+
"environ": Envs(),
|
456
|
+
"glob": glob,
|
457
|
+
"makedirs": makedirs,
|
458
|
+
"os": os,
|
459
|
+
"params": self.params,
|
375
460
|
"pip": pip,
|
461
|
+
"quote": quote,
|
462
|
+
"q": quote,
|
463
|
+
"squote": squote,
|
464
|
+
"sq": squote,
|
465
|
+
"read": read,
|
376
466
|
"rule": self.rules.rule,
|
377
|
-
"check": self.checkers.check,
|
378
|
-
"Path": Path,
|
379
467
|
"run": run,
|
380
|
-
"capture": capture,
|
381
|
-
"glob": glob,
|
382
|
-
"MAX_TS": MAX_TS,
|
383
|
-
"environ": Envs(),
|
384
468
|
"shutil": shutil,
|
385
|
-
"
|
386
|
-
"
|
469
|
+
"write": write,
|
470
|
+
"MAX_TS": MAX_TS,
|
471
|
+
"Path": Path,
|
387
472
|
}
|
388
473
|
return globals
|
389
474
|
|
@@ -432,13 +517,13 @@ class Prod:
|
|
432
517
|
if isinstance(obj, str | Path):
|
433
518
|
builds.append(obj)
|
434
519
|
elif isinstance(obj, Rule):
|
435
|
-
raise
|
520
|
+
raise RuleError(obj)
|
436
521
|
elif callable(obj):
|
437
522
|
self.built += 1
|
438
523
|
task = asyncio.create_task(self.run_in_executor(obj))
|
439
524
|
waitings.append(task)
|
440
525
|
else:
|
441
|
-
raise
|
526
|
+
raise RuleError(obj)
|
442
527
|
|
443
528
|
await self.build(builds)
|
444
529
|
await asyncio.gather(*waitings)
|
@@ -497,11 +582,11 @@ class Prod:
|
|
497
582
|
return Exists(name, True, ret)
|
498
583
|
|
499
584
|
async def run(self, name): # -> Any | int:
|
500
|
-
|
585
|
+
name = str(name)
|
501
586
|
|
502
|
-
self.rules.build_tree(
|
503
|
-
deps, uses = self.rules.get_dep_names(
|
504
|
-
selected = self.rules.select_builder(
|
587
|
+
self.rules.build_tree(name)
|
588
|
+
deps, uses = self.rules.get_dep_names(name)
|
589
|
+
selected = self.rules.select_builder(name)
|
505
590
|
if selected:
|
506
591
|
build_deps, build_uses, builder = selected
|
507
592
|
deps = deps + build_deps
|
@@ -513,19 +598,19 @@ class Prod:
|
|
513
598
|
if uses:
|
514
599
|
await self.build(uses)
|
515
600
|
|
516
|
-
exists = await self.is_exists(
|
601
|
+
exists = await self.is_exists(name)
|
517
602
|
|
518
603
|
if not exists.exists:
|
519
|
-
logger.debug("%r does not exists",
|
604
|
+
logger.debug("%r does not exists", name)
|
520
605
|
elif (ts >= MAX_TS) or (exists.ts < ts):
|
521
|
-
logger.debug("%r should be updated",
|
606
|
+
logger.debug("%r should be updated", name)
|
522
607
|
else:
|
523
|
-
logger.debug("%r already exists",
|
608
|
+
logger.debug("%r already exists", name)
|
524
609
|
|
525
610
|
if not exists.exists and not selected:
|
526
|
-
raise NoRuleToMakeTargetError(
|
611
|
+
raise NoRuleToMakeTargetError(name)
|
527
612
|
elif selected and ((not exists.exists) or (ts >= MAX_TS) or (exists.ts < ts)):
|
528
|
-
logger.warning("building: %r",
|
613
|
+
logger.warning("building: %r", name)
|
529
614
|
await self.run_in_executor(builder.builder, name, *build_deps)
|
530
615
|
self.built += 1
|
531
616
|
return MAX_TS
|
@@ -0,0 +1,11 @@
|
|
1
|
+
pyprod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
pyprod/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
|
3
|
+
pyprod/main.py,sha256=nS0mcLy3Ix7ydnX1Pu5g7ceLzhh6DndwJWO5sjO_qWg,2580
|
4
|
+
pyprod/prod.py,sha256=r2e-JSHiTw2SOcv-tYFx4aRpVkQbOb5j9vEVrjT0meE,16694
|
5
|
+
pyprod/utils.py,sha256=oiiUkSbeqTazbtJ6gz7ZKqG1OvAeV-nV9u_3Y0DCOOM,401
|
6
|
+
pyprod/venv.py,sha256=_riw56YQvUOSd55u_1m9ElsqPdjM5qVvIZP6dr9Fzt4,1051
|
7
|
+
pyprod-0.3.0.dist-info/METADATA,sha256=t9TMYeBveYE1LFZNpcYJAtOdUo7gPARFHmIZ9p0bnag,2477
|
8
|
+
pyprod-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
9
|
+
pyprod-0.3.0.dist-info/entry_points.txt,sha256=zFycf8BYSMRDTiI0jftmcvtkf9XM4MZ4BL3JaIer_ZM,44
|
10
|
+
pyprod-0.3.0.dist-info/licenses/LICENSE,sha256=OtPgwnlLrsVEYPnTraun5AqftAT5vUv4rIan-qYj7nE,1071
|
11
|
+
pyprod-0.3.0.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
pyprod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
pyprod/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
|
3
|
-
pyprod/main.py,sha256=nS0mcLy3Ix7ydnX1Pu5g7ceLzhh6DndwJWO5sjO_qWg,2580
|
4
|
-
pyprod/prod.py,sha256=poLehE5Gs_vEZvKrSdMicF016tQeaMMAnFc76ssPQQY,14586
|
5
|
-
pyprod/utils.py,sha256=oiiUkSbeqTazbtJ6gz7ZKqG1OvAeV-nV9u_3Y0DCOOM,401
|
6
|
-
pyprod/venv.py,sha256=_riw56YQvUOSd55u_1m9ElsqPdjM5qVvIZP6dr9Fzt4,1051
|
7
|
-
pyprod-0.2.0.post1.dist-info/METADATA,sha256=WnpzS2zWCnRZxR9cHjrfkI7yUd742pp5idOzdNyJ7Fk,2483
|
8
|
-
pyprod-0.2.0.post1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
9
|
-
pyprod-0.2.0.post1.dist-info/entry_points.txt,sha256=zFycf8BYSMRDTiI0jftmcvtkf9XM4MZ4BL3JaIer_ZM,44
|
10
|
-
pyprod-0.2.0.post1.dist-info/licenses/LICENSE,sha256=OtPgwnlLrsVEYPnTraun5AqftAT5vUv4rIan-qYj7nE,1071
|
11
|
-
pyprod-0.2.0.post1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|