cook-build 0.7.1__py3-none-any.whl → 0.7.3__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.
cook/__main__.py CHANGED
@@ -342,12 +342,10 @@ def __main__(cli_args: list[str] | None = None) -> None:
342
342
  ]
343
343
  )
344
344
  if args.module:
345
- # Temporarily add the current working directory to the path.
346
- try:
347
- sys.path.append(os.getcwd())
348
- importlib.import_module(args.module)
349
- finally:
350
- sys.path.pop()
345
+ # Add the current working directory to the path so local modules can be
346
+ # imported.
347
+ sys.path.append(os.getcwd())
348
+ importlib.import_module(args.module)
351
349
  elif args.recipe.is_file():
352
350
  # Parse the recipe.
353
351
  spec = importlib.util.spec_from_file_location("recipe", args.recipe)
cook/actions.py CHANGED
@@ -36,6 +36,7 @@ executor with a deprecation warning.
36
36
  """
37
37
 
38
38
  import asyncio
39
+ import functools
39
40
  import hashlib
40
41
  import logging
41
42
  import os
@@ -94,7 +95,9 @@ class FunctionAction(Action):
94
95
  else:
95
96
  # Run sync function in executor
96
97
  loop = asyncio.get_running_loop()
97
- await loop.run_in_executor(None, self.func, task, *self.args, **self.kwargs)
98
+ await loop.run_in_executor(
99
+ None, functools.partial(self.func, task, *self.args, **self.kwargs)
100
+ )
98
101
 
99
102
 
100
103
  class SubprocessAction(Action):
@@ -119,21 +122,22 @@ class SubprocessAction(Action):
119
122
  True
120
123
  """
121
124
 
122
- def __init__(self, *args, **kwargs) -> None:
125
+ def __init__(self, args: str | list[str], **kwargs) -> None:
123
126
  # Validate shell argument early
124
- if kwargs.get("shell", False) and args and not isinstance(args[0], str):
127
+ if kwargs.get("shell", False) and not isinstance(args, str):
125
128
  raise ValueError("shell=True requires string args")
126
129
  self.args = args
127
130
  self.kwargs = kwargs
128
131
 
129
132
  async def execute(self, task: "Task") -> None:
130
133
  # Get the command arguments
131
- (args,) = self.args
134
+ args = self.args
132
135
  shell = self.kwargs.get("shell", False)
133
136
  other_kwargs = {k: v for k, v in self.kwargs.items() if k != "shell"}
134
137
 
135
138
  # Create the subprocess
136
139
  if shell:
140
+ assert isinstance(args, str)
137
141
  process = await asyncio.create_subprocess_shell(args, **other_kwargs)
138
142
  else:
139
143
  # Exec mode: args can be a string (single command) or list
@@ -164,7 +168,7 @@ class SubprocessAction(Action):
164
168
  @property
165
169
  def hexdigest(self) -> str:
166
170
  hasher = hashlib.sha1()
167
- (args,) = self.args
171
+ args = self.args
168
172
  if isinstance(args, str):
169
173
  hasher.update(args.encode())
170
174
  else:
@@ -173,7 +177,7 @@ class SubprocessAction(Action):
173
177
  return hasher.hexdigest()
174
178
 
175
179
  def __repr__(self) -> str:
176
- args, *_ = self.args
180
+ args = self.args
177
181
  if not isinstance(args, str):
178
182
  args = " ".join(map(shlex.quote, args))
179
183
  return f"{self.__class__.__name__}({repr(args)})"
@@ -196,13 +200,13 @@ class CompositeAction(Action):
196
200
 
197
201
  @property
198
202
  def hexdigest(self) -> str | None:
199
- parts = []
203
+ hasher = hashlib.sha1()
200
204
  for action in self.actions:
201
205
  hexdigest = action.hexdigest
202
206
  if hexdigest is None:
203
207
  return None
204
- parts.append(hexdigest)
205
- return "".join(parts)
208
+ hasher.update(bytearray.fromhex(hexdigest))
209
+ return hasher.hexdigest()
206
210
 
207
211
 
208
212
  class ModuleAction(SubprocessAction):
cook/contexts.py CHANGED
@@ -152,7 +152,7 @@ class create_target_directories(Context):
152
152
  create = self.manager.create_task(
153
153
  name,
154
154
  action=actions.FunctionAction(
155
- lambda _: target.parent.mkdir(parents=True, exist_ok=True)
155
+ lambda _, p=target.parent: p.mkdir(parents=True, exist_ok=True)
156
156
  ),
157
157
  )
158
158
  task.task_dependencies.append(create)
cook/controller.py CHANGED
@@ -39,11 +39,6 @@ QUERIES = {
39
39
  "last_digested" TIMESTAMP NOT NULL
40
40
  );
41
41
  """,
42
- "select_task": """
43
- SELECT "digest"
44
- FROM "files"
45
- WHERE "name" = :name
46
- """,
47
42
  "upsert_task_completed": """
48
43
  INSERT INTO "tasks" ("name", "digest", "last_completed")
49
44
  VALUES (:name, :digest, :last_completed)
@@ -80,7 +75,6 @@ class Controller:
80
75
  def __init__(self, dependencies: nx.DiGraph, connection: Connection) -> None:
81
76
  self.dependencies = dependencies
82
77
  self.connection = connection
83
- self._digest_cache: dict[Path, tuple[float, bytes]] = {}
84
78
 
85
79
  def resolve_stale_tasks(self, tasks: list["Task"] | None = None) -> set["Task"]:
86
80
  self.is_stale(tasks or list(self.dependencies))
@@ -88,7 +82,7 @@ class Controller:
88
82
  node for node, data in self.dependencies.nodes(True) if data.get("is_stale")
89
83
  }
90
84
 
91
- def _evaluate_task_hexdigest(self, task: "Task") -> str | None:
85
+ def _evaluate_task_hexdigest(self, task: "Task") -> str:
92
86
  """
93
87
  Evaluate the digest of a task by combining the digest of all its dependencies.
94
88
  """
@@ -102,8 +96,7 @@ class Controller:
102
96
  )
103
97
  dependency = Path(dependency).resolve()
104
98
  if not dependency.is_file():
105
- LOGGER.debug("dependency %s of %s is missing", dependency, task)
106
- return None
99
+ raise FileNotFoundError(f"dependency {dependency} of {task} is missing")
107
100
  dependencies.append(dependency)
108
101
 
109
102
  hasher = hashlib.sha1()
@@ -198,9 +191,11 @@ class Controller:
198
191
  return True
199
192
 
200
193
  # If one of the dependencies is missing, the task is stale.
201
- current_digest = self._evaluate_task_hexdigest(task)
202
- if current_digest is None:
194
+ try:
195
+ current_digest = self._evaluate_task_hexdigest(task)
196
+ except FileNotFoundError:
203
197
  LOGGER.debug("%s is stale because one of its dependencies is missing", task)
198
+ return True
204
199
 
205
200
  # If the digest has changed, the task is stale.
206
201
  (cached_digest,) = cached_digest
@@ -290,8 +285,10 @@ class Controller:
290
285
  dep_futures = [task_futures[dep] for dep in dep_tasks]
291
286
  await asyncio.gather(*dep_futures)
292
287
 
293
- start = datetime.now()
294
- digest = self._evaluate_task_hexdigest(task)
288
+ digest: str | None = None
289
+ if not dry_run:
290
+ digest = self._evaluate_task_hexdigest(task)
291
+ start: datetime | None = None
295
292
 
296
293
  try:
297
294
  # Log what we're doing
@@ -307,22 +304,19 @@ class Controller:
307
304
  " action: %s",
308
305
  task.action,
309
306
  )
310
- else:
311
- LOGGER.log(
312
- logging.DEBUG if task.name.startswith("_") else logging.INFO,
313
- "executing %s ...",
314
- task,
315
- )
316
-
317
- # Update DB for start
318
- if not dry_run:
319
- params = {"name": task.name, "last_started": start}
320
- self.connection.execute(QUERIES["upsert_task_started"], params)
321
- self.connection.commit()
322
307
 
323
308
  # Execute the task
324
309
  if not dry_run:
325
310
  async with semaphore:
311
+ start = datetime.now()
312
+ LOGGER.log(
313
+ logging.DEBUG if task.name.startswith("_") else logging.INFO,
314
+ "executing %s ...",
315
+ task,
316
+ )
317
+ params = {"name": task.name, "last_started": start}
318
+ self.connection.execute(QUERIES["upsert_task_started"], params)
319
+ self.connection.commit()
326
320
  await task.execute()
327
321
 
328
322
  # Check that all targets were created
@@ -335,6 +329,7 @@ class Controller:
335
329
 
336
330
  # Update DB for completion
337
331
  if not dry_run:
332
+ assert digest is not None
338
333
  params = {
339
334
  "name": task.name,
340
335
  "digest": digest,
@@ -344,6 +339,7 @@ class Controller:
344
339
  self.connection.commit()
345
340
 
346
341
  # Log completion
342
+ assert start is not None
347
343
  delta = util.format_timedelta(datetime.now() - start)
348
344
  LOGGER.log(
349
345
  logging.DEBUG if task.name.startswith("_") else logging.INFO,
@@ -362,7 +358,7 @@ class Controller:
362
358
  self.connection.execute(QUERIES["upsert_task_failed"], params)
363
359
  self.connection.commit()
364
360
 
365
- delta = util.format_timedelta(datetime.now() - start)
361
+ delta = util.format_timedelta(datetime.now() - start) if start else "?"
366
362
  LOGGER.exception("failed to execute %s after %s", task, delta)
367
363
  raise util.FailedTaskError(ex, task=task) from ex
368
364
 
cook/manager.py CHANGED
@@ -63,7 +63,7 @@ class Manager:
63
63
  raise ValueError(f"{context} did not return a task")
64
64
  self.tasks[task.name] = task
65
65
  return task
66
- except:
66
+ except Exception:
67
67
  filename, lineno = util.get_location()
68
68
  LOGGER.exception(
69
69
  "failed to create task with name '%s' at %s:%d", name, filename, lineno
@@ -150,7 +150,7 @@ def create_task(
150
150
 
151
151
  Args:
152
152
  name: Name of the new task. Defaults to the string representation of the first
153
- dependency if not provided.
153
+ target if not provided.
154
154
  action: Action to execute or a string for shell commands.
155
155
  targets: Paths for files to be generated.
156
156
  dependencies: Paths to files on which this task depends.
cook/util.py CHANGED
@@ -32,6 +32,7 @@ def evaluate_hexdigest(path: PathOrStr, size=2**16, hasher: str = "sha1") -> str
32
32
  class Timer:
33
33
  def __init__(self):
34
34
  self.start = None
35
+ self.end = None
35
36
 
36
37
  def __enter__(self) -> Timer:
37
38
  self.start = time()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cook-build
3
- Version: 0.7.1
3
+ Version: 0.7.3
4
4
  Summary: A task-centric build system with simple declarative recipes specified in Python
5
5
  Author: Till Hoffmann
6
6
  License: BSD-3-Clause
@@ -0,0 +1,14 @@
1
+ cook/__init__.py,sha256=SCa9_i6B84IzSAwq0wnSQqvycyL4dvTO7dRIysJXZj4,179
2
+ cook/__main__.py,sha256=3vnDZOJLTMJGRQzPolqurVzKJ7KCUjosBuuUkW22tKY,13192
3
+ cook/actions.py,sha256=yxo_LQ42iejq0K92OnYs7x2GaTuyNK3ZmqnRikQUgWs,7537
4
+ cook/contexts.py,sha256=-L4o_b_XPNZ_MBuYAgR1LYbptWPankDyj-00Pji7EVg,10710
5
+ cook/controller.py,sha256=vs9wBfvdQzE5RGZxZtKef6hb8rUOKKMp_kuPnmVAf9s,13432
6
+ cook/manager.py,sha256=sLlqPjsi81UTSHNhNj-ZTDilCdVmEF992N94yxWPOlU,6247
7
+ cook/task.py,sha256=-LNMwHdFlTUG45DF_QyWlIHw55_n_u2Xf5beKB7LrdY,2308
8
+ cook/util.py,sha256=Ssk_79cB7tvxEk_I9VAjjzmEp1bJ95ELZHCAx6sybyo,2459
9
+ cook_build-0.7.3.dist-info/licenses/LICENSE,sha256=3Nuj_WTTcz7JDg4-9EzNf6vHlKRWpdLUccg-pvoZ3WE,1500
10
+ cook_build-0.7.3.dist-info/METADATA,sha256=M83L-Uar5tKKLB2OyqQ1IS3wxe-idBZIvH2foLTXxAE,4586
11
+ cook_build-0.7.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ cook_build-0.7.3.dist-info/entry_points.txt,sha256=5UP0ZmmxSNKevTVISUJxmdXEQsKrI4n54OQYkjrdX2c,48
13
+ cook_build-0.7.3.dist-info/top_level.txt,sha256=ewNQIn2oRSYV98vAsUnw88u2Q8XHKhAz70ed2PEdR2c,5
14
+ cook_build-0.7.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- cook/__init__.py,sha256=SCa9_i6B84IzSAwq0wnSQqvycyL4dvTO7dRIysJXZj4,179
2
- cook/__main__.py,sha256=4sO22TsNTt3oirT71dJjZJwmynsRyUGT-CHJNlDgCkk,13242
3
- cook/actions.py,sha256=1VLyWL-pACuWpAjIyNWPBeu6wlKkoqi6t3lHr6hV0EQ,7399
4
- cook/contexts.py,sha256=AMKO7Uz-nI52OsdPQ_zCLXjOf77V4pgzvDyj6-N8Ods,10705
5
- cook/controller.py,sha256=vpF3QhiM3HImaI0rHJ58T8i5AQi5_P4Z2p42qFvG1po,13426
6
- cook/manager.py,sha256=Y-QGVw9x8ZpSBMRALJIHgcMmIZulAV9KxwtBJz35Gpw,6241
7
- cook/task.py,sha256=-LNMwHdFlTUG45DF_QyWlIHw55_n_u2Xf5beKB7LrdY,2308
8
- cook/util.py,sha256=15MMG07CYZZ-YdFE_2jzRRTaqHMsw83UFg0s7e72MhI,2435
9
- cook_build-0.7.1.dist-info/licenses/LICENSE,sha256=3Nuj_WTTcz7JDg4-9EzNf6vHlKRWpdLUccg-pvoZ3WE,1500
10
- cook_build-0.7.1.dist-info/METADATA,sha256=8CK_rxir23FBjEo5H3IhxB1tkzXlMrolpAuhwqYLP4g,4586
11
- cook_build-0.7.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
12
- cook_build-0.7.1.dist-info/entry_points.txt,sha256=5UP0ZmmxSNKevTVISUJxmdXEQsKrI4n54OQYkjrdX2c,48
13
- cook_build-0.7.1.dist-info/top_level.txt,sha256=ewNQIn2oRSYV98vAsUnw88u2Q8XHKhAz70ed2PEdR2c,5
14
- cook_build-0.7.1.dist-info/RECORD,,