tasktree 0.0.20__py3-none-any.whl → 0.0.21__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/types.py CHANGED
@@ -10,7 +10,10 @@ import click
10
10
 
11
11
 
12
12
  class HostnameType(click.ParamType):
13
- """Validates hostname format (not DNS resolution)."""
13
+ """
14
+ Validates hostname format (not DNS resolution).
15
+ @athena: 84a721c40458
16
+ """
14
17
 
15
18
  name = "hostname"
16
19
 
@@ -20,6 +23,9 @@ class HostnameType(click.ParamType):
20
23
  )
21
24
 
22
25
  def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> str:
26
+ """
27
+ @athena: 8d921e52bcf2
28
+ """
23
29
  if isinstance(value, str):
24
30
  if self.HOSTNAME_PATTERN.match(value):
25
31
  return value
@@ -27,7 +33,10 @@ class HostnameType(click.ParamType):
27
33
 
28
34
 
29
35
  class EmailType(click.ParamType):
30
- """Validates email format (not deliverability)."""
36
+ """
37
+ Validates email format (not deliverability).
38
+ @athena: 95cfacc3f4cd
39
+ """
31
40
 
32
41
  name = "email"
33
42
 
@@ -37,6 +46,9 @@ class EmailType(click.ParamType):
37
46
  )
38
47
 
39
48
  def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> str:
49
+ """
50
+ @athena: 25046aeb6e6f
51
+ """
40
52
  if isinstance(value, str):
41
53
  if self.EMAIL_PATTERN.match(value):
42
54
  return value
@@ -44,11 +56,17 @@ class EmailType(click.ParamType):
44
56
 
45
57
 
46
58
  class IPType(click.ParamType):
47
- """Validates IP address (IPv4 or IPv6)."""
59
+ """
60
+ Validates IP address (IPv4 or IPv6).
61
+ @athena: dac837bf4894
62
+ """
48
63
 
49
64
  name = "ip"
50
65
 
51
66
  def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> str:
67
+ """
68
+ @athena: d57618e5ad89
69
+ """
52
70
  try:
53
71
  ip_address(value)
54
72
  return str(value)
@@ -57,11 +75,17 @@ class IPType(click.ParamType):
57
75
 
58
76
 
59
77
  class IPv4Type(click.ParamType):
60
- """Validates IPv4 address."""
78
+ """
79
+ Validates IPv4 address.
80
+ @athena: ea5957643fe5
81
+ """
61
82
 
62
83
  name = "ipv4"
63
84
 
64
85
  def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> str:
86
+ """
87
+ @athena: 7ed2d17d1f1a
88
+ """
65
89
  try:
66
90
  IPv4Address(value)
67
91
  return str(value)
@@ -70,11 +94,17 @@ class IPv4Type(click.ParamType):
70
94
 
71
95
 
72
96
  class IPv6Type(click.ParamType):
73
- """Validates IPv6 address."""
97
+ """
98
+ Validates IPv6 address.
99
+ @athena: 9bc5b38d4f23
100
+ """
74
101
 
75
102
  name = "ipv6"
76
103
 
77
104
  def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> str:
105
+ """
106
+ @athena: 4b101e4d54cf
107
+ """
78
108
  try:
79
109
  IPv6Address(value)
80
110
  return str(value)
@@ -83,11 +113,17 @@ class IPv6Type(click.ParamType):
83
113
 
84
114
 
85
115
  class DateTimeType(click.ParamType):
86
- """Validates datetime in format YYYY-MM-DDTHH:MM:SS."""
116
+ """
117
+ Validates datetime in format YYYY-MM-DDTHH:MM:SS.
118
+ @athena: c3bafa3e22e3
119
+ """
87
120
 
88
121
  name = "datetime"
89
122
 
90
123
  def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> str:
124
+ """
125
+ @athena: 13fa66adfe94
126
+ """
91
127
  if isinstance(value, str):
92
128
  try:
93
129
  datetime.fromisoformat(value)
@@ -114,18 +150,20 @@ TYPE_MAPPING = {
114
150
 
115
151
 
116
152
  def get_click_type(type_name: str, min_val: int | float | None = None, max_val: int | float | None = None) -> click.ParamType:
117
- """Get Click parameter type by name with optional range constraints.
153
+ """
154
+ Get Click parameter type by name with optional range constraints.
118
155
 
119
156
  Args:
120
- type_name: Type name from task definition (e.g., 'str', 'int', 'hostname')
121
- min_val: Optional minimum value for numeric types
122
- max_val: Optional maximum value for numeric types
157
+ type_name: Type name from task definition (e.g., 'str', 'int', 'hostname')
158
+ min_val: Optional minimum value for numeric types
159
+ max_val: Optional maximum value for numeric types
123
160
 
124
161
  Returns:
125
- Click parameter type instance
162
+ Click parameter type instance
126
163
 
127
164
  Raises:
128
- ValueError: If type_name is not recognized
165
+ ValueError: If type_name is not recognized
166
+ @athena: d0912868676f
129
167
  """
130
168
  if type_name not in TYPE_MAPPING:
131
169
  raise ValueError(f"Unknown type: {type_name}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tasktree
3
- Version: 0.0.20
3
+ Version: 0.0.21
4
4
  Summary: A task automation tool with incremental execution
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: click>=8.1.0
@@ -835,7 +835,7 @@ Hint: Define named outputs like: outputs: [{ missing: 'path/to/file' }]
835
835
 
836
836
  ### Self-References
837
837
 
838
- Tasks can reference their own named inputs and outputs using `{{ self.inputs.name }}` and `{{ self.outputs.name }}` templates. This eliminates repetition when paths contain variables or when tasks have multiple inputs/outputs.
838
+ Tasks can reference their own inputs and outputs using `{{ self.inputs.name }}` (named access) or `{{ self.inputs.0 }}` (positional access) templates. This eliminates repetition when paths contain variables or when tasks have multiple inputs/outputs.
839
839
 
840
840
  **Named Inputs and Outputs:**
841
841
 
@@ -862,8 +862,10 @@ tasks:
862
862
 
863
863
  - **Defining named inputs**: `inputs: [{ name: "path/to/file" }]`
864
864
  - **Defining named outputs**: `outputs: [{ name: "path/to/file" }]`
865
- - **Referencing inputs**: `{{ self.inputs.input_name }}`
866
- - **Referencing outputs**: `{{ self.outputs.output_name }}`
865
+ - **Defining anonymous inputs**: `inputs: ["path/to/file"]`
866
+ - **Defining anonymous outputs**: `outputs: ["path/to/file"]`
867
+ - **Referencing by name**: `{{ self.inputs.input_name }}` or `{{ self.outputs.output_name }}`
868
+ - **Referencing by index**: `{{ self.inputs.0 }}` or `{{ self.outputs.1 }}` (0-based)
867
869
  - **Mixed format**: Can combine named and anonymous inputs/outputs in the same task
868
870
 
869
871
  **Why Use Self-References?**
@@ -972,6 +974,129 @@ tasks:
972
974
  cmd: build-tool --config {{ self.inputs.config }} --output {{ self.outputs.binary }}
973
975
  ```
974
976
 
977
+ **Positional Index References:**
978
+
979
+ In addition to named references, you can access inputs and outputs by their positional index using `{{ self.inputs.0 }}`, `{{ self.inputs.1 }}`, etc. This provides an alternative way to reference items, especially useful for:
980
+
981
+ - **Anonymous inputs/outputs**: Reference items that don't have names
982
+ - **Simple sequential access**: When order is more important than naming
983
+ - **Mixed with named access**: Use both styles in the same task
984
+
985
+ **Syntax:**
986
+
987
+ ```yaml
988
+ tasks:
989
+ process:
990
+ inputs: ["file1.txt", "file2.txt", "file3.txt"]
991
+ outputs: ["output1.txt", "output2.txt"]
992
+ cmd: |
993
+ cat {{ self.inputs.0 }} {{ self.inputs.1 }} > {{ self.outputs.0 }}
994
+ cat {{ self.inputs.2 }} > {{ self.outputs.1 }}
995
+ ```
996
+
997
+ Indices follow YAML declaration order, starting from 0 (zero-based indexing):
998
+ - First input/output = index 0
999
+ - Second input/output = index 1
1000
+ - Third input/output = index 2, etc.
1001
+
1002
+ **Works with Both Named and Anonymous:**
1003
+
1004
+ ```yaml
1005
+ tasks:
1006
+ build:
1007
+ inputs:
1008
+ - config: "build.yaml" # Index 0, also accessible as {{ self.inputs.config }}
1009
+ - "src/**/*.c" # Index 1, ONLY accessible as {{ self.inputs.1 }}
1010
+ - headers: "include/*.h" # Index 2, also accessible as {{ self.inputs.headers }}
1011
+ outputs:
1012
+ - "dist/app.js" # Index 0
1013
+ - bundle: "dist/bundle.js" # Index 1, also accessible as {{ self.outputs.bundle }}
1014
+ cmd: |
1015
+ # Mix positional and named references
1016
+ build-tool \
1017
+ --config {{ self.inputs.0 }} \
1018
+ --sources {{ self.inputs.1 }} \
1019
+ --headers {{ self.inputs.headers }} \
1020
+ --output {{ self.outputs.bundle }}
1021
+ ```
1022
+
1023
+ **Same Item, Multiple Ways:**
1024
+
1025
+ Named items can be accessed by both name and index:
1026
+
1027
+ ```yaml
1028
+ tasks:
1029
+ copy:
1030
+ inputs:
1031
+ - source: data.txt
1032
+ cmd: |
1033
+ # These are equivalent:
1034
+ cat {{ self.inputs.source }} > copy1.txt
1035
+ cat {{ self.inputs.0 }} > copy2.txt
1036
+ ```
1037
+
1038
+ **With Variables and Arguments:**
1039
+
1040
+ Positional references work with variable-expanded paths:
1041
+
1042
+ ```yaml
1043
+ variables:
1044
+ version: "1.0"
1045
+
1046
+ tasks:
1047
+ package:
1048
+ args: [platform]
1049
+ inputs:
1050
+ - "dist/app-{{ var.version }}.js"
1051
+ - "dist/lib-{{ arg.platform }}.so"
1052
+ outputs: ["release-{{ var.version }}-{{ arg.platform }}.tar.gz"]
1053
+ cmd: tar czf {{ self.outputs.0 }} {{ self.inputs.0 }} {{ self.inputs.1 }}
1054
+ ```
1055
+
1056
+ **Index Boundaries:**
1057
+
1058
+ Indices are validated before execution:
1059
+
1060
+ ```yaml
1061
+ tasks:
1062
+ build:
1063
+ inputs: ["file1.txt", "file2.txt"]
1064
+ cmd: cat {{ self.inputs.5 }} # Error: index 5 out of bounds!
1065
+ ```
1066
+
1067
+ Error message:
1068
+ ```
1069
+ Task 'build' references input index '5' but only has 2 inputs (indices 0-1)
1070
+ ```
1071
+
1072
+ **Empty Lists:**
1073
+
1074
+ Referencing an index when no inputs/outputs exist:
1075
+
1076
+ ```yaml
1077
+ tasks:
1078
+ generate:
1079
+ cmd: echo "test" > {{ self.outputs.0 }} # Error: no outputs defined!
1080
+ ```
1081
+
1082
+ Error message:
1083
+ ```
1084
+ Task 'generate' references output index '0' but has no outputs defined
1085
+ ```
1086
+
1087
+ **When to Use Index References:**
1088
+
1089
+ - **Anonymous items**: Only way to reference inputs/outputs without names
1090
+ - **Order-based processing**: When the sequence matters more than naming
1091
+ - **Simple tasks**: Quick access without defining names
1092
+ - **Compatibility**: Accessing items in legacy YAML that uses anonymous format
1093
+
1094
+ **When to Use Named References:**
1095
+
1096
+ - **Clarity**: Names make commands more readable (`{{ self.inputs.config }}` vs `{{ self.inputs.2 }}`)
1097
+ - **Maintainability**: Adding/removing items doesn't break indices
1098
+ - **Complex tasks**: Many inputs/outputs are easier to manage with names
1099
+
975
1100
  **Error Messages:**
976
1101
 
977
1102
  If you reference a non-existent input or output:
@@ -1011,18 +1136,21 @@ Hint: Define named inputs like: inputs: [{ src: 'file.txt' }]
1011
1136
 
1012
1137
  **Key Behaviors:**
1013
1138
 
1139
+ - **Two access methods**: Reference by name (`{{ self.inputs.name }}`) or by index (`{{ self.inputs.0 }}`)
1014
1140
  - **Template resolution**: Self-references are resolved during dependency graph planning
1015
1141
  - **Substitution order**: Variables → Dependency outputs → Self-references → Arguments/Environment
1016
- - **Fail-fast validation**: Errors are caught before execution begins
1017
- - **Clear error messages**: Lists available names if reference doesn't exist
1142
+ - **Fail-fast validation**: Errors are caught before execution begins (missing names, out-of-bounds indices)
1143
+ - **Clear error messages**: Lists available names/indices if reference doesn't exist
1018
1144
  - **Backward compatible**: Existing anonymous inputs/outputs work unchanged
1019
1145
  - **State tracking**: Works correctly with incremental execution and freshness checks
1146
+ - **Index order**: Positional indices follow YAML declaration order (0-based)
1020
1147
 
1021
1148
  **Limitations:**
1022
1149
 
1023
- - **Anonymous not referenceable**: Only named inputs/outputs can be referenced
1150
+ - **Anonymous not referenceable by name**: Anonymous inputs/outputs cannot be referenced by name (use positional index instead: `{{ self.inputs.0 }}`)
1024
1151
  - **Case sensitive**: `{{ self.inputs.Src }}` and `{{ self.inputs.src }}` are different
1025
1152
  - **Argument defaults**: Self-references in argument defaults are not supported (arguments are evaluated before self-references)
1153
+ - **No negative indices**: Python-style negative indexing (`{{ self.inputs.-1 }}`) is not supported
1026
1154
 
1027
1155
  **Use Cases:**
1028
1156
 
@@ -0,0 +1,14 @@
1
+ tasktree/__init__.py,sha256=ZfI6vy-TxinN7eK0_XjfHUFq83pFV1XiHFrai_SHNqw,1251
2
+ tasktree/cli.py,sha256=pyXMUENz1ldl8z9SPXBZ1zhOHzmvIRUGu2y5VwRUdgo,22960
3
+ tasktree/docker.py,sha256=EeIUqptvDqSXbjWKXwWGhA4hWvsBjOk0Z-3cS3cqEOM,14857
4
+ tasktree/executor.py,sha256=x0K-TIVRmiKuUlur1UTOpB2xOJOE_Cv3-3MlE7xUNEk,47271
5
+ tasktree/graph.py,sha256=BjpLaa7EE2vmOcUlqhvp1KK7yT5VbXHQCbxljOhF84k,23603
6
+ tasktree/hasher.py,sha256=UM-2dqRE-rspQUbhA-noGHc80-Hu-QaxCTPRb_UzPto,6661
7
+ tasktree/parser.py,sha256=sXgotDdDQiNSmo7FaESFQ4j1OytaheyplJSdFrFXTYk,99286
8
+ tasktree/state.py,sha256=0fxKbMNYbo-dCpCTTnKTmJhGRy72G5kLnh99FScMLGU,3985
9
+ tasktree/substitution.py,sha256=2ubFn6jIX5qmtzyWSCxTut-_Cn0zew8KnwxMqDIPq1o,18545
10
+ tasktree/types.py,sha256=2uHdUncfyfw4jNWbQTBUirew-frmTjuk1ZYNd15cyQQ,4908
11
+ tasktree-0.0.21.dist-info/METADATA,sha256=nyjDhb1hdQMEiLx0t_qqonqPoyFZVMCrIg7xFIKxMs8,54353
12
+ tasktree-0.0.21.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
13
+ tasktree-0.0.21.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
14
+ tasktree-0.0.21.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- tasktree/__init__.py,sha256=m7fLsPUft99oB_XXr4dOu2yUWu74zVutkw1-3zGrG5Y,1227
2
- tasktree/cli.py,sha256=S5ypqQlvCxdAvlBfO8TJZhvMoc086wqgvmOm8220678,21220
3
- tasktree/docker.py,sha256=qvja8G63uAcC73YMVY739egda1_CcBtoqzm0qIJU_Q8,14443
4
- tasktree/executor.py,sha256=g4mHtoO3wVIxyqNALIdJOEwlEqRkSY0eY-6sl2jF-IA,46463
5
- tasktree/graph.py,sha256=T78JH0whP7VquEvtOVN-8ePyHNcseTQoEouijDrgmkw,22663
6
- tasktree/hasher.py,sha256=o7Akd_AgGkAsnv9biK0AcbhlcqUQ9ne5y_6r4zoFaw0,5493
7
- tasktree/parser.py,sha256=R0swEkKBPGeijLHxD1CbQjtoKVn2gRJadsZuyKj1sdM,97922
8
- tasktree/state.py,sha256=Cktl4D8iDZVd55aO2LqVyPrc-BnljkesxxkcMcdcfOY,3541
9
- tasktree/substitution.py,sha256=FhtFI0ciK9bQxPdORvpf1coa59XxizKiBiUwHJp0PtI,16811
10
- tasktree/types.py,sha256=R_YAyO5bMLB6XZnkMRT7VAtlkA_Xx6xu0aIpzQjrBXs,4357
11
- tasktree-0.0.20.dist-info/METADATA,sha256=C8cdZtyz-dgiVoN1H2hy7wQNAA-K784WYFfqlZEOXnA,50237
12
- tasktree-0.0.20.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
13
- tasktree-0.0.20.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
14
- tasktree-0.0.20.dist-info/RECORD,,