tasktree 0.0.11__py3-none-any.whl → 0.0.12__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.
tasktree/parser.py CHANGED
@@ -287,26 +287,35 @@ def _is_env_variable_reference(value: Any) -> bool:
287
287
  return isinstance(value, dict) and "env" in value
288
288
 
289
289
 
290
- def _validate_env_variable_reference(var_name: str, value: dict) -> str:
291
- """Validate and extract environment variable name from reference.
290
+ def _validate_env_variable_reference(var_name: str, value: dict) -> tuple[str, str | None]:
291
+ """Validate and extract environment variable name and optional default from reference.
292
292
 
293
293
  Args:
294
294
  var_name: Name of the variable being defined
295
- value: Dict that should be { env: ENV_VAR_NAME }
295
+ value: Dict that should be { env: ENV_VAR_NAME } or { env: ENV_VAR_NAME, default: "value" }
296
296
 
297
297
  Returns:
298
- Environment variable name
298
+ Tuple of (environment variable name, default value or None)
299
299
 
300
300
  Raises:
301
301
  ValueError: If reference is invalid
302
302
  """
303
- # Validate dict structure
304
- if len(value) != 1:
305
- extra_keys = [k for k in value.keys() if k != "env"]
303
+ # Validate dict structure - allow 'env' and optionally 'default'
304
+ valid_keys = {"env", "default"}
305
+ invalid_keys = set(value.keys()) - valid_keys
306
+ if invalid_keys:
306
307
  raise ValueError(
307
308
  f"Invalid environment variable reference in variable '{var_name}'.\n"
308
- f"Expected: {{ env: VARIABLE_NAME }}\n"
309
- f"Found extra keys: {', '.join(extra_keys)}"
309
+ f"Expected: {{ env: VARIABLE_NAME }} or {{ env: VARIABLE_NAME, default: \"value\" }}\n"
310
+ f"Found invalid keys: {', '.join(invalid_keys)}"
311
+ )
312
+
313
+ # Validate 'env' key is present
314
+ if "env" not in value:
315
+ raise ValueError(
316
+ f"Invalid environment variable reference in variable '{var_name}'.\n"
317
+ f"Missing required 'env' key.\n"
318
+ f"Expected: {{ env: VARIABLE_NAME }} or {{ env: VARIABLE_NAME, default: \"value\" }}"
310
319
  )
311
320
 
312
321
  env_var_name = value["env"]
@@ -315,7 +324,7 @@ def _validate_env_variable_reference(var_name: str, value: dict) -> str:
315
324
  if not env_var_name or not isinstance(env_var_name, str):
316
325
  raise ValueError(
317
326
  f"Invalid environment variable reference in variable '{var_name}'.\n"
318
- f"Expected: {{ env: VARIABLE_NAME }}\n"
327
+ f"Expected: {{ env: VARIABLE_NAME }} or {{ env: VARIABLE_NAME, default: \"value\" }}"
319
328
  f"Found: {{ env: {env_var_name!r} }}"
320
329
  )
321
330
 
@@ -327,23 +336,36 @@ def _validate_env_variable_reference(var_name: str, value: dict) -> str:
327
336
  f"and contain only alphanumerics and underscores."
328
337
  )
329
338
 
330
- return env_var_name
339
+ # Extract and validate default if present
340
+ default = value.get("default")
341
+ if default is not None:
342
+ # Default must be a string (env vars are always strings)
343
+ if not isinstance(default, str):
344
+ raise ValueError(
345
+ f"Invalid default value in variable '{var_name}'.\n"
346
+ f"Environment variable defaults must be strings.\n"
347
+ f"Got: {default!r} (type: {type(default).__name__})\n"
348
+ f"Use a quoted string: {{ env: {env_var_name}, default: \"{default}\" }}"
349
+ )
350
+
351
+ return env_var_name, default
331
352
 
332
353
 
333
- def _resolve_env_variable(var_name: str, env_var_name: str) -> str:
354
+ def _resolve_env_variable(var_name: str, env_var_name: str, default: str | None = None) -> str:
334
355
  """Resolve environment variable value.
335
356
 
336
357
  Args:
337
358
  var_name: Name of the variable being defined
338
359
  env_var_name: Name of environment variable to read
360
+ default: Optional default value to use if environment variable is not set
339
361
 
340
362
  Returns:
341
- Environment variable value as string
363
+ Environment variable value as string, or default if not set and default provided
342
364
 
343
365
  Raises:
344
- ValueError: If environment variable is not set
366
+ ValueError: If environment variable is not set and no default provided
345
367
  """
346
- value = os.environ.get(env_var_name)
368
+ value = os.environ.get(env_var_name, default)
347
369
 
348
370
  if value is None:
349
371
  raise ValueError(
@@ -730,11 +752,11 @@ def _resolve_variable_value(
730
752
 
731
753
  # Check if this is an environment variable reference
732
754
  if _is_env_variable_reference(raw_value):
733
- # Validate and extract env var name
734
- env_var_name = _validate_env_variable_reference(name, raw_value)
755
+ # Validate and extract env var name and optional default
756
+ env_var_name, default = _validate_env_variable_reference(name, raw_value)
735
757
 
736
- # Resolve from os.environ
737
- string_value = _resolve_env_variable(name, env_var_name)
758
+ # Resolve from os.environ (with optional default)
759
+ string_value = _resolve_env_variable(name, env_var_name, default)
738
760
 
739
761
  # Still perform variable-in-variable substitution
740
762
  from tasktree.substitution import substitute_variables
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tasktree
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: A task automation tool with incremental execution
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: click>=8.1.0
@@ -718,10 +718,13 @@ For more complex scenarios, define environment variables in the `variables` sect
718
718
 
719
719
  ```yaml
720
720
  variables:
721
- # Direct env reference (resolved at parse time)
721
+ # Required env var (error if not set)
722
722
  api_key: { env: API_KEY }
723
- db_host: { env: DATABASE_HOST }
724
-
723
+
724
+ # Optional env var with default
725
+ port: { env: PORT, default: "8080" }
726
+ log_level: { env: LOG_LEVEL, default: "info" }
727
+
725
728
  # Or using string substitution
726
729
  deploy_user: "{{ env.DEPLOY_USER }}"
727
730
 
@@ -4,12 +4,12 @@ tasktree/docker.py,sha256=qvja8G63uAcC73YMVY739egda1_CcBtoqzm0qIJU_Q8,14443
4
4
  tasktree/executor.py,sha256=Q7Bks5B88i-IyZDpxGSps9MM3uflz0U3yn4Rtq_uHMM,42266
5
5
  tasktree/graph.py,sha256=oXLxX0Ix4zSkVBg8_3x9K7WxSFpg136sp4MF-d2mDEQ,9682
6
6
  tasktree/hasher.py,sha256=0GrnCfwAXnwq_kpnHFFb12B5_2VFNXx6Ng7hTdcCyXo,4415
7
- tasktree/parser.py,sha256=rHJuYMM4AUjM1E-Jh3SpUpBRKGkciYOvgfgERoXylSE,67364
7
+ tasktree/parser.py,sha256=XdFuELqrrhHc45HeMpo6-gflopZM7kYTqO1lQcFwtFk,68782
8
8
  tasktree/state.py,sha256=Cktl4D8iDZVd55aO2LqVyPrc-BnljkesxxkcMcdcfOY,3541
9
9
  tasktree/substitution.py,sha256=M_qcP0NKJATrKcNShSqHJatneuth1RVwTk1ci8-ZuxQ,6473
10
10
  tasktree/tasks.py,sha256=2QdQZtJAX2rSGbyXKG1z9VF_siz1DUzdvzCgPkykxtU,173
11
11
  tasktree/types.py,sha256=R_YAyO5bMLB6XZnkMRT7VAtlkA_Xx6xu0aIpzQjrBXs,4357
12
- tasktree-0.0.11.dist-info/METADATA,sha256=a41OmLVRm4BbIpZT4e7v-l3g3gmw3DD1Eg0OXnrVsYA,37151
13
- tasktree-0.0.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- tasktree-0.0.11.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
15
- tasktree-0.0.11.dist-info/RECORD,,
12
+ tasktree-0.0.12.dist-info/METADATA,sha256=fdh62_US58sghqn46-f0072dEnFECrJccRa6tL7m2DU,37234
13
+ tasktree-0.0.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ tasktree-0.0.12.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
15
+ tasktree-0.0.12.dist-info/RECORD,,