highway-dsl 0.0.2__py3-none-any.whl → 0.0.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.

Potentially problematic release.


This version of highway-dsl might be problematic. Click here for more details.

highway_dsl/__init__.py CHANGED
@@ -6,6 +6,7 @@ from .workflow_dsl import (
6
6
  ParallelOperator,
7
7
  WaitOperator,
8
8
  ForEachOperator,
9
+ WhileOperator,
9
10
  RetryPolicy,
10
11
  TimeoutPolicy,
11
12
  OperatorType,
@@ -14,11 +15,13 @@ from .workflow_dsl import (
14
15
  __all__ = [
15
16
  "Workflow",
16
17
  "WorkflowBuilder",
18
+ "BaseOperator",
17
19
  "TaskOperator",
18
20
  "ConditionOperator",
19
21
  "ParallelOperator",
20
22
  "WaitOperator",
21
23
  "ForEachOperator",
24
+ "WhileOperator",
22
25
  "RetryPolicy",
23
26
  "TimeoutPolicy",
24
27
  "OperatorType",
@@ -16,6 +16,7 @@ class OperatorType(Enum):
16
16
  FOREACH = "foreach"
17
17
  SWITCH = "switch"
18
18
  TRY_CATCH = "try_catch"
19
+ WHILE = "while"
19
20
 
20
21
 
21
22
  class RetryPolicy(BaseModel):
@@ -52,8 +53,8 @@ class TaskOperator(BaseOperator):
52
53
 
53
54
  class ConditionOperator(BaseOperator):
54
55
  condition: str
55
- if_true: str
56
- if_false: str
56
+ if_true: Optional[str]
57
+ if_false: Optional[str]
57
58
  operator_type: OperatorType = Field(OperatorType.CONDITION, frozen=True)
58
59
 
59
60
 
@@ -94,6 +95,21 @@ class ForEachOperator(BaseOperator):
94
95
  operator_type: OperatorType = Field(OperatorType.FOREACH, frozen=True)
95
96
 
96
97
 
98
+ class WhileOperator(BaseOperator):
99
+ condition: str
100
+ loop_body: List[
101
+ Union[
102
+ TaskOperator,
103
+ ConditionOperator,
104
+ WaitOperator,
105
+ ParallelOperator,
106
+ ForEachOperator,
107
+ "WhileOperator",
108
+ ]
109
+ ] = Field(default_factory=list)
110
+ operator_type: OperatorType = Field(OperatorType.WHILE, frozen=True)
111
+
112
+
97
113
  class Workflow(BaseModel):
98
114
  name: str
99
115
  version: str = "1.0.0"
@@ -106,6 +122,7 @@ class Workflow(BaseModel):
106
122
  WaitOperator,
107
123
  ParallelOperator,
108
124
  ForEachOperator,
125
+ WhileOperator,
109
126
  ],
110
127
  ] = Field(default_factory=dict)
111
128
  variables: Dict[str, Any] = Field(default_factory=dict)
@@ -122,6 +139,7 @@ class Workflow(BaseModel):
122
139
  OperatorType.WAIT.value: WaitOperator,
123
140
  OperatorType.PARALLEL.value: ParallelOperator,
124
141
  OperatorType.FOREACH.value: ForEachOperator,
142
+ OperatorType.WHILE.value: WhileOperator,
125
143
  }
126
144
  for task_id, task_data in data["tasks"].items():
127
145
  operator_type = task_data.get("operator_type")
@@ -141,6 +159,7 @@ class Workflow(BaseModel):
141
159
  WaitOperator,
142
160
  ParallelOperator,
143
161
  ForEachOperator,
162
+ WhileOperator,
144
163
  ],
145
164
  ) -> "Workflow":
146
165
  self.tasks[task.task_id] = task
@@ -172,12 +191,18 @@ class Workflow(BaseModel):
172
191
 
173
192
 
174
193
  class WorkflowBuilder:
175
- def __init__(self, name: str, existing_workflow: Optional[Workflow] = None):
194
+ def __init__(
195
+ self,
196
+ name: str,
197
+ existing_workflow: Optional[Workflow] = None,
198
+ parent: Optional["WorkflowBuilder"] = None,
199
+ ):
176
200
  if existing_workflow:
177
201
  self.workflow = existing_workflow
178
202
  else:
179
203
  self.workflow = Workflow(name=name)
180
204
  self._current_task: Optional[str] = None
205
+ self.parent = parent
181
206
 
182
207
  def task(self, task_id: str, function: str, **kwargs) -> "WorkflowBuilder":
183
208
  task = TaskOperator(task_id=task_id, function=function, **kwargs)
@@ -188,18 +213,37 @@ class WorkflowBuilder:
188
213
  return self
189
214
 
190
215
  def condition(
191
- self, task_id: str, condition: str, if_true: str, if_false: str, **kwargs
216
+ self,
217
+ task_id: str,
218
+ condition: str,
219
+ if_true: Callable[["WorkflowBuilder"], "WorkflowBuilder"],
220
+ if_false: Callable[["WorkflowBuilder"], "WorkflowBuilder"],
221
+ **kwargs,
192
222
  ) -> "WorkflowBuilder":
223
+ true_builder = if_true(WorkflowBuilder(f"{{task_id}}_true", parent=self))
224
+ false_builder = if_false(WorkflowBuilder(f"{{task_id}}_false", parent=self))
225
+
226
+ true_tasks = list(true_builder.workflow.tasks.keys())
227
+ false_tasks = list(false_builder.workflow.tasks.keys())
228
+
193
229
  task = ConditionOperator(
194
230
  task_id=task_id,
195
231
  condition=condition,
196
- if_true=if_true,
197
- if_false=if_false,
232
+ if_true=true_tasks[0] if true_tasks else None,
233
+ if_false=false_tasks[0] if false_tasks else None,
198
234
  **kwargs,
199
235
  )
236
+
200
237
  if self._current_task:
201
238
  task.dependencies.append(self._current_task)
239
+
202
240
  self.workflow.add_task(task)
241
+
242
+ for task_obj in true_builder.workflow.tasks.values():
243
+ self.workflow.add_task(task_obj)
244
+ for task_obj in false_builder.workflow.tasks.values():
245
+ self.workflow.add_task(task_obj)
246
+
203
247
  self._current_task = task_id
204
248
  return self
205
249
 
@@ -214,12 +258,32 @@ class WorkflowBuilder:
214
258
  return self
215
259
 
216
260
  def parallel(
217
- self, task_id: str, branches: Dict[str, List[str]], **kwargs
261
+ self,
262
+ task_id: str,
263
+ branches: Dict[str, Callable[["WorkflowBuilder"], "WorkflowBuilder"]],
264
+ **kwargs,
218
265
  ) -> "WorkflowBuilder":
219
- task = ParallelOperator(task_id=task_id, branches=branches, **kwargs)
266
+ branch_builders = {
267
+ name: branch_func(WorkflowBuilder(f"{{task_id}}_{{name}}", parent=self))
268
+ for name, branch_func in branches.items()
269
+ }
270
+
271
+ branch_tasks = {
272
+ name: list(builder.workflow.tasks.keys())
273
+ for name, builder in branch_builders.items()
274
+ }
275
+
276
+ task = ParallelOperator(task_id=task_id, branches=branch_tasks, **kwargs)
277
+
220
278
  if self._current_task:
221
279
  task.dependencies.append(self._current_task)
280
+
222
281
  self.workflow.add_task(task)
282
+
283
+ for builder in branch_builders.values():
284
+ for task_obj in builder.workflow.tasks.values():
285
+ self.workflow.add_task(task_obj)
286
+
223
287
  self._current_task = task_id
224
288
  return self
225
289
 
@@ -235,6 +299,34 @@ class WorkflowBuilder:
235
299
  self._current_task = task_id
236
300
  return self
237
301
 
302
+ def while_loop(
303
+ self,
304
+ task_id: str,
305
+ condition: str,
306
+ loop_body: Callable[["WorkflowBuilder"], "WorkflowBuilder"],
307
+ **kwargs,
308
+ ) -> "WorkflowBuilder":
309
+ loop_builder = loop_body(WorkflowBuilder(f"{{task_id}}_loop", parent=self))
310
+ loop_tasks = list(loop_builder.workflow.tasks.values())
311
+
312
+ task = WhileOperator(
313
+ task_id=task_id,
314
+ condition=condition,
315
+ loop_body=loop_tasks,
316
+ **kwargs,
317
+ )
318
+
319
+ if self._current_task:
320
+ task.dependencies.append(self._current_task)
321
+
322
+ self.workflow.add_task(task)
323
+
324
+ for task_obj in loop_tasks:
325
+ self.workflow.add_task(task_obj)
326
+
327
+ self._current_task = task_id
328
+ return self
329
+
238
330
  def retry(
239
331
  self,
240
332
  max_retries: int = 3,
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: highway_dsl
3
+ Version: 0.0.3
4
+ Summary: A domain specific language (DSL) for defining and managing data processing pipelines.
5
+ Author-email: Farseed Ashouri <farseed.ashouri@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/rodmena-limited/highway_dsl
8
+ Project-URL: Issues, https://github.com/rodmena-limited/highway_dsl/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: pydantic>=2.12.3
16
+ Requires-Dist: pyyaml>=6.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
19
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
20
+ Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
21
+ Requires-Dist: pytest-cov>=2.12.1; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ # Highway DSL
25
+
26
+ [![PyPI version](https://badge.fury.io/py/highway-dsl.svg)](https://badge.fury.io/py/highway-dsl)
27
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
28
+
29
+ **Highway DSL** is a Python-based domain-specific language for defining complex workflows in a clear, concise, and fluent manner. It is part of the larger **Highway** project, an advanced workflow engine capable of running complex DAG-based workflows.
30
+
31
+ ## Features
32
+
33
+ * **Fluent API:** A powerful and intuitive `WorkflowBuilder` for defining workflows programmatically.
34
+ * **Pydantic-based:** All models are built on Pydantic, providing robust data validation, serialization, and documentation.
35
+ * **Rich Operators:** A comprehensive set of operators for handling various workflow scenarios:
36
+ * `Task`
37
+ * `Condition` (if/else)
38
+ * `Parallel`
39
+ * `ForEach`
40
+ * `Wait`
41
+ * `While`
42
+ * **YAML/JSON Interoperability:** Workflows can be defined in Python and exported to YAML or JSON, and vice-versa.
43
+ * **Extensible:** The DSL is designed to be extensible with custom operators and policies.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install highway-dsl
49
+ ```
50
+
51
+ ## Quick Start
52
+
53
+ Here's a simple example of how to define a workflow using the `WorkflowBuilder`:
54
+
55
+ ```python
56
+ from datetime import timedelta
57
+ from highway_dsl import WorkflowBuilder
58
+
59
+ workflow = (
60
+ WorkflowBuilder("simple_etl")
61
+ .task("extract", "etl.extract_data", result_key="raw_data")
62
+ .task(
63
+ "transform",
64
+ "etl.transform_data",
65
+ args=["{{raw_data}}"],
66
+ result_key="transformed_data",
67
+ )
68
+ .retry(max_retries=3, delay=timedelta(seconds=10))
69
+ .task("load", "etl.load_data", args=["{{transformed_data}}"])
70
+ .timeout(timeout=timedelta(minutes=30))
71
+ .wait("wait_next", timedelta(hours=24))
72
+ .task("cleanup", "etl.cleanup")
73
+ .build()
74
+ )
75
+
76
+ print(workflow.to_yaml())
77
+ ```
78
+
79
+ ## Advanced Usage
80
+
81
+ ### Conditional Logic
82
+
83
+ ```python
84
+ from highway_dsl import WorkflowBuilder, RetryPolicy
85
+ from datetime import timedelta
86
+
87
+ builder = WorkflowBuilder("data_processing_pipeline")
88
+
89
+ builder.task("start", "workflows.tasks.initialize", result_key="init_data")
90
+ builder.task(
91
+ "validate",
92
+ "workflows.tasks.validate_data",
93
+ args=["{{init_data}}"],
94
+ result_key="validated_data",
95
+ )
96
+
97
+ builder.condition(
98
+ "check_quality",
99
+ condition="{{validated_data.quality_score}} > 0.8",
100
+ if_true=lambda b: b.task(
101
+ "high_quality_processing",
102
+ "workflows.tasks.advanced_processing",
103
+ args=["{{validated_data}}"],
104
+ retry_policy=RetryPolicy(max_retries=5, delay=timedelta(seconds=10), backoff_factor=2.0),
105
+ ),
106
+ if_false=lambda b: b.task(
107
+ "standard_processing",
108
+ "workflows.tasks.basic_processing",
109
+ args=["{{validated_data}}"],
110
+ ),
111
+ )
112
+
113
+ workflow = builder.build()
114
+ ```
115
+
116
+ ### While Loops
117
+
118
+ ```python
119
+ from highway_dsl import WorkflowBuilder
120
+
121
+ builder = WorkflowBuilder("qa_rework_workflow")
122
+
123
+ builder.task("start_qa", "workflows.tasks.start_qa", result_key="qa_results")
124
+
125
+ builder.while_loop(
126
+ "qa_rework_loop",
127
+ condition="{{qa_results.status}} == 'failed'",
128
+ loop_body=lambda b: b.task("perform_rework", "workflows.tasks.perform_rework").task(
129
+ "re_run_qa", "workflows.tasks.run_qa", result_key="qa_results"
130
+ ),
131
+ )
132
+
133
+ builder.task("finalize_product", "workflows.tasks.finalize_product", dependencies=["qa_rework_loop"])
134
+
135
+ workflow = builder.build()
136
+ ```
137
+
138
+ ## Development
139
+
140
+ To set up the development environment:
141
+
142
+ ```bash
143
+ git clone https://github.com/your-username/highway.git
144
+ cd highway
145
+ python -m venv .venv
146
+ source .venv/bin/activate
147
+ pip install -e .[dev]
148
+ ```
149
+
150
+ ### Running Tests
151
+
152
+ ```bash
153
+ pytest
154
+ ```
155
+
156
+ ### Type Checking
157
+
158
+ ```bash
159
+ mypy .
160
+ ```
@@ -0,0 +1,7 @@
1
+ highway_dsl/__init__.py,sha256=mr1oMylxliFwu2VO2qpyM3sVQwYIoPL2P6JE-6ZuF7M,507
2
+ highway_dsl/workflow_dsl.py,sha256=yMTmFr5bbjxfVTleCvSsDZ__n9C7qH39RdzajkUEmiI,11882
3
+ highway_dsl-0.0.3.dist-info/licenses/LICENSE,sha256=qdFq1H66BvKg67mf4-WGpFwtG2u_dNknxuJDQ1_ubaY,1072
4
+ highway_dsl-0.0.3.dist-info/METADATA,sha256=--TFErjeBDZ1mAyNHk30CQavLuKWAaoxgHK7xFpT-Ok,4612
5
+ highway_dsl-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ highway_dsl-0.0.3.dist-info/top_level.txt,sha256=_5uX-bbBsQ2rsi1XMr7WRyKbr6ack5GqVBcy-QjF1C8,12
7
+ highway_dsl-0.0.3.dist-info/RECORD,,
@@ -1,227 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: highway_dsl
3
- Version: 0.0.2
4
- Summary: A domain specific language (DSL) for defining and managing data processing pipelines.
5
- Author-email: Farseed Ashouri <farseed.ashouri@gmail.com>
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/rodmena-limited/highway_dsl
8
- Project-URL: Issues, https://github.com/rodmena-limited/highway_dsl/issues
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.9
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: pydantic>=2.12.3
16
- Requires-Dist: pyyaml>=6.0
17
- Provides-Extra: dev
18
- Requires-Dist: pytest>=7.0.0; extra == "dev"
19
- Requires-Dist: mypy>=1.0.0; extra == "dev"
20
- Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
21
- Requires-Dist: pytest-cov>=2.12.1; extra == "dev"
22
- Dynamic: license-file
23
-
24
- # Highway DSL
25
-
26
- Highway DSL is a Python-based Domain Specific Language (DSL) for defining and managing complex workflows. It allows users to declaratively specify tasks, dependencies, and execution parameters, supporting various control flow mechanisms like conditions, parallel execution, and retries.
27
-
28
- ## Features
29
-
30
- * **Declarative Workflow Definition:** Define workflows using a clear and concise Python API or through YAML/JSON configurations.
31
- * **Pydantic Models:** Leverages Pydantic for robust data validation and serialization/deserialization of workflow definitions.
32
- * **Rich Task Types:** Supports various operators including:
33
- * `TaskOperator`: Executes a Python function.
34
- * `ConditionOperator`: Enables conditional branching based on expressions.
35
- * `WaitOperator`: Pauses workflow execution for a specified duration or until a specific datetime.
36
- * `ParallelOperator`: Executes multiple branches of tasks concurrently.
37
- * `ForEachOperator`: Iterates over a collection, executing a chain of tasks for each item.
38
- * **Retry and Timeout Policies:** Define retry strategies and timeout limits for individual tasks.
39
- * **Serialization/Deserialization:** Seamless conversion of workflow definitions between Python objects, YAML, and JSON formats.
40
- * **Workflow Builder:** A fluent API for constructing workflows programmatically.
41
-
42
- ### Feature Overview
43
-
44
- ```mermaid
45
- graph TD
46
- A[Workflow] --> B{TaskOperator};
47
- A --> C{ConditionOperator};
48
- A --> D{WaitOperator};
49
- A --> E{ParallelOperator};
50
- A --> F{ForEachOperator};
51
-
52
- B --> G[Executes Python Function];
53
- C --> H{If/Else Branching};
54
- D --> I[Pauses Execution];
55
- E --> J[Concurrent Branches];
56
- F --> K[Iterates Over Items];
57
-
58
- subgraph Policies
59
- B --> L[RetryPolicy];
60
- B --> M[TimeoutPolicy];
61
- end
62
- ```
63
-
64
- ## Installation
65
-
66
- To install Highway DSL, you can use pip:
67
-
68
- ```bash
69
- pip install highway-dsl
70
- ```
71
-
72
- If you want to install it for development, including testing dependencies:
73
-
74
- ```bash
75
- pip install "highway-dsl[dev]"
76
- ```
77
-
78
- ## Usage
79
-
80
- ### Defining a Simple Workflow
81
-
82
- ```python
83
- from datetime import timedelta
84
- from highway_dsl import WorkflowBuilder
85
-
86
- def demonstrate_basic_workflow():
87
- """Show a simple complete workflow using just the builder"""
88
-
89
- workflow = (
90
- WorkflowBuilder("simple_etl")
91
- .task("extract", "etl.extract_data", result_key="raw_data")
92
- .task(
93
- "transform",
94
- "etl.transform_data",
95
- args=["{{raw_data}}"],
96
- result_key="transformed_data",
97
- )
98
- .retry(max_retries=3, delay=timedelta(seconds=10))
99
- .task("load", "etl.load_data", args=["{{transformed_data}}"])
100
- .timeout(timeout=timedelta(minutes=30))
101
- .wait("wait_next", timedelta(hours=24))
102
- .task("cleanup", "etl.cleanup")
103
- .build()
104
- )
105
-
106
- workflow.set_variables(
107
- {"database_url": "postgresql://localhost/mydb", "chunk_size": 1000}
108
- )
109
-
110
- return workflow
111
-
112
- if __name__ == "__main__":
113
- basic_workflow = demonstrate_basic_workflow()
114
- print(basic_workflow.to_yaml())
115
- ```
116
-
117
- ### Defining a Complex Workflow
118
-
119
- Refer to `example_usage.py` for a more complex example demonstrating conditional logic, parallel execution, and iteration.
120
-
121
- ### YAML Configuration
122
-
123
- You can also define workflows directly in YAML:
124
-
125
- ```yaml
126
- name: simple_etl
127
- version: 1.0.0
128
- description: Simple ETL workflow with retry and timeout
129
- variables:
130
- database_url: postgresql://localhost/mydb
131
- chunk_size: 1000
132
- start_task: extract
133
- tasks:
134
- extract:
135
- task_id: extract
136
- operator_type: task
137
- function: etl.extract_data
138
- result_key: raw_data
139
- dependencies: []
140
- metadata: {}
141
-
142
- transform:
143
- task_id: transform
144
- operator_type: task
145
- function: etl.transform_data
146
- args: ["{{raw_data}}"]
147
- result_key: transformed_data
148
- dependencies: ["extract"]
149
- retry_policy:
150
- max_retries: 3
151
- delay: PT10S
152
- backoff_factor: 2.0
153
- metadata: {}
154
-
155
- load:
156
- task_id: load
157
- operator_type: task
158
- function: etl.load_data
159
- args: ["{{transformed_data}}"]
160
- dependencies: ["transform"]
161
- timeout_policy:
162
- timeout: PT30M
163
- kill_on_timeout: true
164
- metadata: {}
165
-
166
- wait_next:
167
- task_id: wait_next
168
- operator_type: wait
169
- wait_for: "P1D"
170
- dependencies: ["load"]
171
- metadata: {}
172
-
173
- cleanup:
174
- task_id: cleanup
175
- operator_type: task
176
- function: etl.cleanup
177
- dependencies: ["wait_next"]
178
- metadata: {}
179
- ```
180
-
181
- To load this YAML:
182
-
183
- ```python
184
- from highway_dsl import Workflow
185
-
186
- yaml_content = """
187
- # ... (yaml content from above)
188
- """
189
-
190
- workflow = Workflow.from_yaml(yaml_content)
191
- print(workflow.name)
192
- ```
193
-
194
- ## Development
195
-
196
- ### Running Tests
197
-
198
- To run the unit tests, navigate to the project root and execute:
199
-
200
- ```bash
201
- pytest
202
- ```
203
-
204
- ### Type Checking
205
-
206
- To perform static type checking with MyPy:
207
-
208
- ```bash
209
- mypy .
210
- ```
211
-
212
- ## Project Structure
213
-
214
- ```
215
- .highway/
216
- ├── highway_dsl/
217
- │ ├── __init__.py # Exposes the public API
218
- │ └── workflow_dsl.py # Core DSL definitions (Pydantic models)
219
- ├── example_usage.py # Examples of how to use the DSL
220
- ├── tests/
221
- │ ├── __init__.py
222
- │ ├── conftest.py # Pytest configuration
223
- │ └── test_workflow_dsl.py # Unit and integration tests
224
- ├── pyproject.toml # Project metadata and dependencies
225
- ├── README.md # This file
226
- └── SUMMARY.md # Summary of changes and future instructions
227
- ```
@@ -1,7 +0,0 @@
1
- highway_dsl/__init__.py,sha256=8qmPd9ZZNgwPGZuWwPYvMOljg73BJIT2SSM7iIRycmw,447
2
- highway_dsl/workflow_dsl.py,sha256=2QWDhbXLPulq_kTZk_Yjs6L3BNwws_H6EDV0S1CjOXs,9205
3
- highway_dsl-0.0.2.dist-info/licenses/LICENSE,sha256=qdFq1H66BvKg67mf4-WGpFwtG2u_dNknxuJDQ1_ubaY,1072
4
- highway_dsl-0.0.2.dist-info/METADATA,sha256=uLLXSVlLWM8H6F5wR1huiAtgXfkIVdmLV-XsYwZkW6s,6390
5
- highway_dsl-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- highway_dsl-0.0.2.dist-info/top_level.txt,sha256=_5uX-bbBsQ2rsi1XMr7WRyKbr6ack5GqVBcy-QjF1C8,12
7
- highway_dsl-0.0.2.dist-info/RECORD,,