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/__init__.py +4 -1
- tasktree/cli.py +107 -29
- tasktree/docker.py +87 -53
- tasktree/executor.py +194 -129
- tasktree/graph.py +123 -72
- tasktree/hasher.py +68 -19
- tasktree/parser.py +340 -229
- tasktree/state.py +46 -17
- tasktree/substitution.py +162 -103
- tasktree/types.py +50 -12
- {tasktree-0.0.20.dist-info → tasktree-0.0.21.dist-info}/METADATA +135 -7
- tasktree-0.0.21.dist-info/RECORD +14 -0
- tasktree-0.0.20.dist-info/RECORD +0 -14
- {tasktree-0.0.20.dist-info → tasktree-0.0.21.dist-info}/WHEEL +0 -0
- {tasktree-0.0.20.dist-info → tasktree-0.0.21.dist-info}/entry_points.txt +0 -0
tasktree/types.py
CHANGED
|
@@ -10,7 +10,10 @@ import click
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class HostnameType(click.ParamType):
|
|
13
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
153
|
+
"""
|
|
154
|
+
Get Click parameter type by name with optional range constraints.
|
|
118
155
|
|
|
119
156
|
Args:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
162
|
+
Click parameter type instance
|
|
126
163
|
|
|
127
164
|
Raises:
|
|
128
|
-
|
|
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.
|
|
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
|
|
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
|
-
- **
|
|
866
|
-
- **
|
|
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**:
|
|
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,,
|
tasktree-0.0.20.dist-info/RECORD
DELETED
|
@@ -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,,
|
|
File without changes
|
|
File without changes
|