cornflow-client 2.0.0a23__tar.gz → 2.0.0rc2__tar.gz
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.
- {cornflow_client-2.0.0a23/cornflow_client.egg-info → cornflow_client-2.0.0rc2}/PKG-INFO +9 -9
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/airflow/api.py +182 -2
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/application.py +4 -6
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/experiment.py +5 -50
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/instance.py +4 -41
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/instance_solution.py +61 -1
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_core.py +146 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2/cornflow_client.egg-info}/PKG-INFO +9 -9
- cornflow_client-2.0.0rc2/cornflow_client.egg-info/requires.txt +7 -0
- cornflow_client-2.0.0rc2/requirements.txt +7 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/setup.py +2 -2
- cornflow_client-2.0.0a23/cornflow_client.egg-info/requires.txt +0 -7
- cornflow_client-2.0.0a23/requirements.txt +0 -7
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/LICENSE +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/MANIFEST.in +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/README.rst +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/airflow/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/airflow/dag_utilities.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/constants.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/read_tools.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/solution.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/tools.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/cornflow_client.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/empty_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/pulp_json_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/schema_validator.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/solver_config.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/vrp_solution_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/databricks/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/databricks/api.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/raw_cornflow_client.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/schema/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/schema/dictSchema.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/schema/dict_functions.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/schema/manager.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/schema/tools.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/const.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/data_input_bad.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/data_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/endpoints_access.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/endpoints_methods.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/gc_input.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/gc_output.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/graph_coloring_input.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/graph_coloring_output.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/hk_data_input.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/hk_data_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/hk_solution_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/instance-hackathon2.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/name_problem_schema.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/pulp_example_data.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/schema_with_fk.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/schema_without_fk.json +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/test_mps.mps +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_with_access.xlsx +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_with_fk.xlsx +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_with_methods.xlsx +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_without_fk.xlsx +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/integration/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/integration/test_airflow_integration.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/integration/test_cornflow_integration.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/integration/test_raw_cornflow_integration.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/__init__.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_abc.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_dag_utilities.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_instance_solution_methods.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_schema_from_excel.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_schema_manager.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_varnames.py +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client.egg-info/SOURCES.txt +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client.egg-info/dependency_links.txt +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client.egg-info/top_level.txt +0 -0
- {cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cornflow-client
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0rc2
|
|
4
4
|
Summary: Client to connect to a cornflow server
|
|
5
5
|
Home-page: https://github.com/baobabsoluciones/cornflow
|
|
6
6
|
Author: baobab soluciones
|
|
@@ -9,16 +9,16 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/x-rst
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: requests
|
|
16
|
-
Requires-Dist: genson
|
|
17
|
-
Requires-Dist: jsonschema
|
|
18
|
-
Requires-Dist: marshmallow
|
|
19
|
-
Requires-Dist: pytups
|
|
20
|
-
Requires-Dist: ortools
|
|
21
|
-
Requires-Dist: PuLP
|
|
15
|
+
Requires-Dist: requests==2.32.4
|
|
16
|
+
Requires-Dist: genson==1.2.2
|
|
17
|
+
Requires-Dist: jsonschema==4.21.1
|
|
18
|
+
Requires-Dist: marshmallow==3.26.2
|
|
19
|
+
Requires-Dist: pytups==0.86.2
|
|
20
|
+
Requires-Dist: ortools==9.8.3296
|
|
21
|
+
Requires-Dist: PuLP==2.9.0
|
|
22
22
|
Dynamic: author
|
|
23
23
|
Dynamic: author-email
|
|
24
24
|
Dynamic: classifier
|
|
@@ -31,6 +31,12 @@ class Airflow(object):
|
|
|
31
31
|
|
|
32
32
|
@classmethod
|
|
33
33
|
def from_config(cls, config):
|
|
34
|
+
"""
|
|
35
|
+
Create an Airflow client from a configuration dictionary.
|
|
36
|
+
|
|
37
|
+
:param config: The configuration dictionary
|
|
38
|
+
:return: The Airflow client
|
|
39
|
+
"""
|
|
34
40
|
data = dict(
|
|
35
41
|
url=config["AIRFLOW_URL"],
|
|
36
42
|
user=config["AIRFLOW_USER"],
|
|
@@ -39,6 +45,12 @@ class Airflow(object):
|
|
|
39
45
|
return cls(**data)
|
|
40
46
|
|
|
41
47
|
def is_alive(self, config=None):
|
|
48
|
+
"""
|
|
49
|
+
Check if the Airflow server is alive.
|
|
50
|
+
|
|
51
|
+
:param config: The configuration dictionary
|
|
52
|
+
:return: True if the Airflow server is alive, False otherwise
|
|
53
|
+
"""
|
|
42
54
|
try:
|
|
43
55
|
response = requests.get(f"{self.url}/health")
|
|
44
56
|
except (ConnectionError, HTTPError):
|
|
@@ -55,6 +67,13 @@ class Airflow(object):
|
|
|
55
67
|
return database and scheduler
|
|
56
68
|
|
|
57
69
|
def request_headers_auth(self, status=200, **kwargs):
|
|
70
|
+
"""
|
|
71
|
+
Request headers with authentication.
|
|
72
|
+
|
|
73
|
+
:param status: The status code to check
|
|
74
|
+
:param kwargs: The keyword arguments to pass to the request
|
|
75
|
+
:return: The response
|
|
76
|
+
"""
|
|
58
77
|
def_headers = {"Content-type": "application/json", "Accept": "application/json"}
|
|
59
78
|
headers = kwargs.get("headers", def_headers)
|
|
60
79
|
response = requests.request(headers=headers, auth=self.auth, **kwargs)
|
|
@@ -63,6 +82,16 @@ class Airflow(object):
|
|
|
63
82
|
return response
|
|
64
83
|
|
|
65
84
|
def consume_dag_run(self, dag_name, payload, dag_run_id=None, method="POST"):
|
|
85
|
+
"""
|
|
86
|
+
Consume a DAG run.
|
|
87
|
+
|
|
88
|
+
:param dag_name: The name of the DAG
|
|
89
|
+
:param payload: The payload to pass to the request
|
|
90
|
+
:param dag_run_id: The ID of the DAG run
|
|
91
|
+
:param method: The method to use to consume the DAG run
|
|
92
|
+
:return: The response
|
|
93
|
+
"""
|
|
94
|
+
# TODO: cleanup method input arguments
|
|
66
95
|
url = f"{self.url}/dags/{dag_name}/dagRuns"
|
|
67
96
|
if dag_run_id is not None:
|
|
68
97
|
url = url + f"/{dag_run_id}"
|
|
@@ -70,6 +99,13 @@ class Airflow(object):
|
|
|
70
99
|
return response
|
|
71
100
|
|
|
72
101
|
def set_dag_run_state(self, dag_name, payload):
|
|
102
|
+
"""
|
|
103
|
+
Set the state of a DAG run.
|
|
104
|
+
|
|
105
|
+
:param dag_name: The name of the DAG
|
|
106
|
+
:param payload: The payload to pass to the request
|
|
107
|
+
:return: The response
|
|
108
|
+
"""
|
|
73
109
|
url = f"{self.url}/dags/{dag_name}/updateTaskInstancesState"
|
|
74
110
|
return self.request_headers_auth(method="POST", url=url, json=payload)
|
|
75
111
|
|
|
@@ -80,6 +116,15 @@ class Airflow(object):
|
|
|
80
116
|
checks_only=False,
|
|
81
117
|
case_id=None,
|
|
82
118
|
):
|
|
119
|
+
"""
|
|
120
|
+
Run a workflow.
|
|
121
|
+
|
|
122
|
+
:param execution_id: The ID of the execution
|
|
123
|
+
:param workflow_name: The name of the DAG
|
|
124
|
+
:param checks_only: Whether to run the checks only
|
|
125
|
+
:param case_id: The ID of the case
|
|
126
|
+
:return: The response
|
|
127
|
+
"""
|
|
83
128
|
conf = dict(exec_id=execution_id, checks_only=checks_only)
|
|
84
129
|
if case_id is not None:
|
|
85
130
|
conf["case_id"] = case_id
|
|
@@ -90,10 +135,21 @@ class Airflow(object):
|
|
|
90
135
|
self, execution_id, dag_name="solve_model_dag", checks_only=False, case_id=None
|
|
91
136
|
):
|
|
92
137
|
"""
|
|
93
|
-
Run workflow
|
|
138
|
+
Run workflow.
|
|
139
|
+
|
|
140
|
+
DEPRECATION: This method is deprecated and will be removed in version 2.0.0.
|
|
141
|
+
|
|
142
|
+
:param execution_id: The ID of the execution
|
|
143
|
+
:param dag_name: The name of the DAG
|
|
144
|
+
:param checks_only: Whether to run the checks only
|
|
145
|
+
:param case_id: The ID of the case
|
|
146
|
+
:return: The response
|
|
94
147
|
"""
|
|
95
148
|
warnings.warn(
|
|
96
|
-
"This method
|
|
149
|
+
"This method is deprecated and will be removed in version 2.0.0 "
|
|
150
|
+
"Please use run_workflow() instead.",
|
|
151
|
+
DeprecationWarning,
|
|
152
|
+
stacklevel=2,
|
|
97
153
|
)
|
|
98
154
|
return self.run_workflow(
|
|
99
155
|
execution_id,
|
|
@@ -103,17 +159,62 @@ class Airflow(object):
|
|
|
103
159
|
)
|
|
104
160
|
|
|
105
161
|
def update_schemas(self, dag_name="update_all_schemas"):
|
|
162
|
+
"""
|
|
163
|
+
Update the schemas.
|
|
164
|
+
|
|
165
|
+
:param dag_name: The name of the DAG
|
|
166
|
+
:return: The response
|
|
167
|
+
"""
|
|
106
168
|
return self.consume_dag_run(dag_name, payload={}, method="POST")
|
|
107
169
|
|
|
108
170
|
def update_dag_registry(self, dag_name="update_dag_registry"):
|
|
171
|
+
"""
|
|
172
|
+
Update the DAG registry.
|
|
173
|
+
|
|
174
|
+
:param dag_name: The name of the DAG
|
|
175
|
+
:return: The response
|
|
176
|
+
"""
|
|
109
177
|
return self.consume_dag_run(dag_name, payload={}, method="POST")
|
|
110
178
|
|
|
111
179
|
def get_run_status(self, schema, run_id):
|
|
180
|
+
"""
|
|
181
|
+
Get the status of a DAG run.
|
|
182
|
+
|
|
183
|
+
:param schema: The name of the DAG
|
|
184
|
+
:param run_id: The ID of the DAG run
|
|
185
|
+
:return: The status of the DAG run
|
|
186
|
+
"""
|
|
112
187
|
return self.consume_dag_run(
|
|
113
188
|
schema, payload=None, dag_run_id=run_id, method="GET"
|
|
114
189
|
)
|
|
115
190
|
|
|
191
|
+
def get_dag_run_status(self, dag_name, dag_run_id):
|
|
192
|
+
"""
|
|
193
|
+
Get the status of a DAG run.
|
|
194
|
+
|
|
195
|
+
DEPRECATION: This method is deprecated and will be removed in version 2.0.0.
|
|
196
|
+
|
|
197
|
+
:param dag_name: The name of the DAG
|
|
198
|
+
:param dag_run_id: The ID of the DAG run
|
|
199
|
+
:return: The status of the DAG run
|
|
200
|
+
"""
|
|
201
|
+
warnings.warn(
|
|
202
|
+
"This method is deprecated and will be removed in version 2.0.0. "
|
|
203
|
+
"Please use get_run_status() instead.",
|
|
204
|
+
DeprecationWarning,
|
|
205
|
+
stacklevel=2,
|
|
206
|
+
)
|
|
207
|
+
return self.get_run_status(dag_name, dag_run_id)
|
|
208
|
+
|
|
116
209
|
def set_dag_run_to_fail(self, dag_name, run_id, new_status="failed"):
|
|
210
|
+
"""
|
|
211
|
+
Set the status of a DAG run to failed.
|
|
212
|
+
|
|
213
|
+
:param dag_name: The name of the DAG
|
|
214
|
+
:param run_id: The ID of the DAG run
|
|
215
|
+
:param new_status: The new status of the DAG run
|
|
216
|
+
:return: The response
|
|
217
|
+
"""
|
|
117
218
|
# here, two calls have to be done:
|
|
118
219
|
# first we get information on the dag_run
|
|
119
220
|
dag_run = self.consume_dag_run(
|
|
@@ -135,45 +236,124 @@ class Airflow(object):
|
|
|
135
236
|
return self.set_dag_run_state(dag_name, payload=payload)
|
|
136
237
|
|
|
137
238
|
def get_all_dag_runs(self, dag_name):
|
|
239
|
+
"""
|
|
240
|
+
Get all the DAG runs.
|
|
241
|
+
|
|
242
|
+
:param dag_name: The name of the DAG
|
|
243
|
+
:return: The response
|
|
244
|
+
"""
|
|
138
245
|
return self.consume_dag_run(dag_name=dag_name, payload=None, method="GET")
|
|
139
246
|
|
|
140
247
|
def get_workflow_info(self, workflow_name, method="GET"):
|
|
248
|
+
"""
|
|
249
|
+
Get the information of a DAG.
|
|
250
|
+
|
|
251
|
+
:param workflow_name: The name of the DAG
|
|
252
|
+
:param method: The method to use to get the information
|
|
253
|
+
:return: The information of the DAG
|
|
254
|
+
"""
|
|
255
|
+
# TODO: cleanup method input arguments
|
|
141
256
|
url = f"{self.url}/dags/{workflow_name}"
|
|
142
257
|
schema_info = self.request_headers_auth(method=method, url=url)
|
|
143
258
|
if schema_info.status_code != 200:
|
|
144
259
|
raise AirflowError("DAG not available")
|
|
145
260
|
return schema_info
|
|
146
261
|
|
|
262
|
+
def get_dag_info(self, dag_name, method="GET"):
|
|
263
|
+
"""
|
|
264
|
+
Get the information of a DAG.
|
|
265
|
+
|
|
266
|
+
DEPRECATION: This method is deprecated and will be removed in version 2.0.0.
|
|
267
|
+
|
|
268
|
+
:param dag_name: The name of the DAG
|
|
269
|
+
:param method: The method to use to get the information
|
|
270
|
+
:return: The information of the DAG
|
|
271
|
+
"""
|
|
272
|
+
warnings.warn(
|
|
273
|
+
"This method is deprecated and will be removed in version 2.0.0. "
|
|
274
|
+
"Please use get_workflow_info() instead.",
|
|
275
|
+
DeprecationWarning,
|
|
276
|
+
stacklevel=2,
|
|
277
|
+
)
|
|
278
|
+
return self.get_workflow_info(dag_name, method)
|
|
279
|
+
|
|
147
280
|
def get_one_variable(self, variable):
|
|
281
|
+
"""
|
|
282
|
+
Get one variable.
|
|
283
|
+
|
|
284
|
+
:param variable: The name of the variable
|
|
285
|
+
:return: The variable
|
|
286
|
+
"""
|
|
148
287
|
url = f"{self.url}/variables/{variable}"
|
|
149
288
|
return self.request_headers_auth(method="GET", url=url).json()
|
|
150
289
|
|
|
151
290
|
def get_all_variables(self):
|
|
291
|
+
"""
|
|
292
|
+
Get all variables.
|
|
293
|
+
|
|
294
|
+
:return: The variables
|
|
295
|
+
"""
|
|
152
296
|
return self.request_headers_auth(
|
|
153
297
|
method="GET", url=f"{self.url}/variables"
|
|
154
298
|
).json()
|
|
155
299
|
|
|
156
300
|
def get_one_schema(self, dag_name, schema):
|
|
301
|
+
"""
|
|
302
|
+
Get one schema.
|
|
303
|
+
|
|
304
|
+
:param dag_name: The name of the DAG
|
|
305
|
+
:param schema: The name of the schema
|
|
306
|
+
:return: The schema
|
|
307
|
+
"""
|
|
157
308
|
return self.get_schemas_for_dag_name(dag_name)[schema]
|
|
158
309
|
|
|
159
310
|
def get_schemas_for_dag_name(self, dag_name):
|
|
311
|
+
"""
|
|
312
|
+
Get all schemas for a DAG name.
|
|
313
|
+
|
|
314
|
+
:param dag_name: The name of the DAG
|
|
315
|
+
:return: The schemas
|
|
316
|
+
"""
|
|
160
317
|
response = self.get_one_variable(dag_name)
|
|
161
318
|
result = json.loads(response["value"])
|
|
162
319
|
result["name"] = response["key"]
|
|
163
320
|
return result
|
|
164
321
|
|
|
165
322
|
def get_all_schemas(self):
|
|
323
|
+
"""
|
|
324
|
+
Get all schemas.
|
|
325
|
+
|
|
326
|
+
:return: The schemas
|
|
327
|
+
"""
|
|
166
328
|
response = self.get_all_variables()
|
|
167
329
|
return [dict(name=variable["key"]) for variable in response["variables"]]
|
|
168
330
|
|
|
169
331
|
def get_all_dags(self, method="GET"):
|
|
332
|
+
"""
|
|
333
|
+
Get all DAGs.
|
|
334
|
+
|
|
335
|
+
:param method: The method to use to get the DAGs
|
|
336
|
+
:return: The DAGs
|
|
337
|
+
"""
|
|
170
338
|
url = f"{self.url}/dags"
|
|
171
339
|
return self.request_headers_auth(method=method, url=url)
|
|
172
340
|
|
|
173
341
|
def get_internal_dags(self, method="GET"):
|
|
342
|
+
"""
|
|
343
|
+
Get all internal DAGs.
|
|
344
|
+
|
|
345
|
+
:param method: The method to use to get the DAGs
|
|
346
|
+
:return: The DAGs
|
|
347
|
+
"""
|
|
174
348
|
url = f"{self.url}/dags?tags=internal"
|
|
175
349
|
return self.request_headers_auth(method=method, url=url)
|
|
176
350
|
|
|
177
351
|
def get_model_dags(self, method="GET"):
|
|
352
|
+
"""
|
|
353
|
+
Get all model DAGs.
|
|
354
|
+
|
|
355
|
+
:param method: The method to use to get the DAGs
|
|
356
|
+
:return: The DAGs
|
|
357
|
+
"""
|
|
178
358
|
url = f"{self.url}/dags?tags=model"
|
|
179
359
|
return self.request_headers_auth(method=method, url=url)
|
|
@@ -8,6 +8,7 @@ from timeit import default_timer as timer
|
|
|
8
8
|
from typing import Type, Dict, List, Tuple, Union
|
|
9
9
|
|
|
10
10
|
from jsonschema import Draft7Validator
|
|
11
|
+
from cornflow_client.constants import BadSolutionChecks
|
|
11
12
|
from pytups import SuperDict
|
|
12
13
|
|
|
13
14
|
from cornflow_client.constants import (
|
|
@@ -263,12 +264,9 @@ class ApplicationCore(ABC):
|
|
|
263
264
|
|
|
264
265
|
# Perform data checks only if a valid solution dict was obtained
|
|
265
266
|
# and the solver implements data_checks
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
except AttributeError:
|
|
270
|
-
# Solver doesn't implement data_checks, which is acceptable.
|
|
271
|
-
solution_checks = None
|
|
267
|
+
# Checks for non-None and non-empty dict
|
|
268
|
+
if final_sol_dict:
|
|
269
|
+
solution_checks = algo.data_checks()
|
|
272
270
|
|
|
273
271
|
return final_sol_dict, solution_checks
|
|
274
272
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Base code for the experiment template.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import logging as log
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
7
|
from typing import List, Dict, Union
|
|
8
8
|
|
|
@@ -14,10 +14,11 @@ from cornflow_client.constants import (
|
|
|
14
14
|
BadSolutionChecks,
|
|
15
15
|
)
|
|
16
16
|
from .instance import InstanceCore
|
|
17
|
+
from .instance_solution import CheckCore
|
|
17
18
|
from .solution import SolutionCore
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class ExperimentCore(ABC):
|
|
21
|
+
class ExperimentCore(CheckCore, ABC):
|
|
21
22
|
"""
|
|
22
23
|
The solver template.
|
|
23
24
|
"""
|
|
@@ -74,17 +75,8 @@ class ExperimentCore(ABC):
|
|
|
74
75
|
"""
|
|
75
76
|
Method that executes the ExperimentCore.check() method and validates the result against the schema_checks
|
|
76
77
|
"""
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if checks is None:
|
|
80
|
-
checks = self.check_solution()
|
|
81
|
-
except NotImplementedError:
|
|
82
|
-
warnings.warn(
|
|
83
|
-
"The check_solution() method is deprecated. Please use check() instead. "
|
|
84
|
-
"Support for check_solution() will be removed on cornflow-client 2.0.0",
|
|
85
|
-
DeprecationWarning,
|
|
86
|
-
)
|
|
87
|
-
checks = self.check_solution()
|
|
78
|
+
# Check method always exists since it is implemented in the ExperimentCore class
|
|
79
|
+
checks = self.check()
|
|
88
80
|
validator = Draft7Validator(self.schema_checks)
|
|
89
81
|
if not validator.is_valid(checks):
|
|
90
82
|
raise BadSolutionChecks(
|
|
@@ -118,43 +110,6 @@ class ExperimentCore(ABC):
|
|
|
118
110
|
"""
|
|
119
111
|
return self.launch_all_checks()
|
|
120
112
|
|
|
121
|
-
def get_check_methods(self) -> list:
|
|
122
|
-
"""
|
|
123
|
-
Finds all class methods starting with check_ and returns them in a list.
|
|
124
|
-
|
|
125
|
-
:return: A list of check methods.
|
|
126
|
-
"""
|
|
127
|
-
check_methods = [
|
|
128
|
-
m
|
|
129
|
-
for m in dir(self)
|
|
130
|
-
if m.startswith("check_")
|
|
131
|
-
and callable(getattr(self, m))
|
|
132
|
-
and m != "check_schema"
|
|
133
|
-
and m != "check_solution"
|
|
134
|
-
]
|
|
135
|
-
return check_methods
|
|
136
|
-
|
|
137
|
-
def launch_all_checks(self) -> Dict[str, Union[List, Dict]]:
|
|
138
|
-
"""
|
|
139
|
-
Launch every check method and return a dict with the check method name as key
|
|
140
|
-
and the list/dict of errors / warnings as value.
|
|
141
|
-
|
|
142
|
-
It will only return those checks that return a non-empty list/dict.
|
|
143
|
-
"""
|
|
144
|
-
check_methods = {m: getattr(self, m)() for m in self.get_check_methods()}
|
|
145
|
-
failed_checks = {}
|
|
146
|
-
for k, v in check_methods.items():
|
|
147
|
-
if v is None:
|
|
148
|
-
continue
|
|
149
|
-
|
|
150
|
-
try:
|
|
151
|
-
if len(v) > 0:
|
|
152
|
-
failed_checks[k.split("check_")[1]] = v
|
|
153
|
-
except TypeError:
|
|
154
|
-
failed_checks[k.split("check_")[1]] = v
|
|
155
|
-
|
|
156
|
-
return dict(failed_checks)
|
|
157
|
-
|
|
158
113
|
@property
|
|
159
114
|
@abstractmethod
|
|
160
115
|
def schema_checks(self) -> dict:
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
"""
|
|
1
|
+
""" """
|
|
4
2
|
|
|
3
|
+
import logging as log
|
|
5
4
|
from abc import ABC, abstractmethod
|
|
6
5
|
from typing import List, Dict, Union
|
|
7
6
|
|
|
8
7
|
from jsonschema import Draft7Validator
|
|
9
8
|
|
|
10
9
|
from cornflow_client.constants import BadInstanceChecks
|
|
11
|
-
from .instance_solution import InstanceSolutionCore
|
|
10
|
+
from .instance_solution import InstanceSolutionCore, CheckCore
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
class InstanceCore(InstanceSolutionCore, ABC):
|
|
13
|
+
class InstanceCore(InstanceSolutionCore, CheckCore, ABC):
|
|
15
14
|
"""
|
|
16
15
|
The instance template.
|
|
17
16
|
"""
|
|
@@ -54,39 +53,3 @@ class InstanceCore(InstanceSolutionCore, ABC):
|
|
|
54
53
|
the method Instance.check()
|
|
55
54
|
"""
|
|
56
55
|
raise NotImplementedError()
|
|
57
|
-
|
|
58
|
-
def get_check_methods(self) -> list:
|
|
59
|
-
"""
|
|
60
|
-
Finds all class methods starting with check_ and returns them in a list.
|
|
61
|
-
|
|
62
|
-
:return: A list of check methods.
|
|
63
|
-
"""
|
|
64
|
-
check_methods = [
|
|
65
|
-
m
|
|
66
|
-
for m in dir(self)
|
|
67
|
-
if m.startswith("check_")
|
|
68
|
-
and callable(getattr(self, m))
|
|
69
|
-
and m != "check_schema"
|
|
70
|
-
]
|
|
71
|
-
return check_methods
|
|
72
|
-
|
|
73
|
-
def launch_all_checks(self) -> Dict[str, Union[List, Dict]]:
|
|
74
|
-
"""
|
|
75
|
-
Launch every check method and return a dict with the check method name as key
|
|
76
|
-
and the list/dict of errors / warnings as value.
|
|
77
|
-
|
|
78
|
-
It will only return those checks that return a non-empty list/dict.
|
|
79
|
-
"""
|
|
80
|
-
check_methods = {m: getattr(self, m)() for m in self.get_check_methods()}
|
|
81
|
-
failed_checks = {}
|
|
82
|
-
for k, v in check_methods.items():
|
|
83
|
-
if v is None:
|
|
84
|
-
continue
|
|
85
|
-
|
|
86
|
-
try:
|
|
87
|
-
if len(v) > 0:
|
|
88
|
-
failed_checks[k.split("check_")[1]] = v
|
|
89
|
-
except TypeError:
|
|
90
|
-
failed_checks[k.split("check_")[1]] = v
|
|
91
|
-
|
|
92
|
-
return dict(failed_checks)
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/core/instance_solution.py
RENAMED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import json
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from datetime import datetime, timedelta
|
|
6
|
-
from typing import List
|
|
6
|
+
from typing import List, Dict, Union
|
|
7
|
+
import logging as log
|
|
7
8
|
|
|
8
9
|
from genson import SchemaBuilder
|
|
9
10
|
from jsonschema import Draft7Validator
|
|
@@ -431,3 +432,62 @@ class InstanceSolutionCore(ABC):
|
|
|
431
432
|
"""Returns the integer value of the hour (in number) from ts string in format 'YYYY-MM-DDTh:m:s'"""
|
|
432
433
|
datetime_obj = datetime.strptime(string, DATETIME_FORMAT)
|
|
433
434
|
return datetime_obj.hour
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class CheckCore:
|
|
438
|
+
def get_check_methods(self) -> list:
|
|
439
|
+
"""
|
|
440
|
+
Finds all class methods starting with check_ and returns them in a list.
|
|
441
|
+
|
|
442
|
+
:return: A list of check methods.
|
|
443
|
+
"""
|
|
444
|
+
check_methods = [
|
|
445
|
+
m
|
|
446
|
+
for m in dir(self)
|
|
447
|
+
if m.startswith("check_")
|
|
448
|
+
and callable(getattr(self, m))
|
|
449
|
+
and m != "check_schema"
|
|
450
|
+
and m != "check_solution"
|
|
451
|
+
]
|
|
452
|
+
return check_methods
|
|
453
|
+
|
|
454
|
+
def launch_all_checks(self) -> Dict[str, Union[List, Dict]]:
|
|
455
|
+
"""
|
|
456
|
+
Launch every check method and return a dict with the check method name as key
|
|
457
|
+
and the list/dict of errors / warnings as value.
|
|
458
|
+
|
|
459
|
+
It will only return those checks that return a non-empty list/dict.
|
|
460
|
+
If a check method raises an exception, it will be caught and a generic error
|
|
461
|
+
message will be added to the checks dictionary.
|
|
462
|
+
"""
|
|
463
|
+
check_method_names = self.get_check_methods()
|
|
464
|
+
check_methods = {}
|
|
465
|
+
|
|
466
|
+
# Execute each check method and catch any exceptions
|
|
467
|
+
for method_name in check_method_names:
|
|
468
|
+
try:
|
|
469
|
+
check_methods[method_name] = getattr(self, method_name)()
|
|
470
|
+
except Exception:
|
|
471
|
+
# If a check fails, add a generic error message
|
|
472
|
+
check_methods[method_name] = [
|
|
473
|
+
{
|
|
474
|
+
"error_type": "Check execution error",
|
|
475
|
+
"error_message": "The execution of the check has failed, please contact support",
|
|
476
|
+
}
|
|
477
|
+
]
|
|
478
|
+
log.warning(
|
|
479
|
+
f"The execution of the check {method_name} has failed, please contact support"
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
failed_checks = {}
|
|
483
|
+
for k, v in check_methods.items():
|
|
484
|
+
if v is None:
|
|
485
|
+
continue
|
|
486
|
+
|
|
487
|
+
try:
|
|
488
|
+
if len(v) > 0:
|
|
489
|
+
failed_checks[k.split("check_")[1]] = v
|
|
490
|
+
except TypeError:
|
|
491
|
+
failed_checks[k.split("check_")[1]] = v
|
|
492
|
+
|
|
493
|
+
return dict(failed_checks)
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_core.py
RENAMED
|
@@ -85,6 +85,39 @@ class TestCore(TestCase):
|
|
|
85
85
|
|
|
86
86
|
self.config = SuperDict(msg=False, timeLimit=1, solver="default", seconds=10)
|
|
87
87
|
|
|
88
|
+
@property
|
|
89
|
+
def error_check_schema(self):
|
|
90
|
+
"""Common schema for error check testing used across multiple test methods."""
|
|
91
|
+
return get_empty_schema(
|
|
92
|
+
properties=dict(
|
|
93
|
+
test_check=dict(
|
|
94
|
+
type="array",
|
|
95
|
+
objects=dict(
|
|
96
|
+
type="object",
|
|
97
|
+
properties=dict(
|
|
98
|
+
error_type=dict(type="string"),
|
|
99
|
+
error_message=dict(type="string"),
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
working_check=dict(
|
|
104
|
+
type="array",
|
|
105
|
+
objects=dict(type="object"),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def error_test_cases(self):
|
|
112
|
+
"""Common test cases for error handling used across multiple test methods."""
|
|
113
|
+
return [
|
|
114
|
+
(RuntimeError, "Runtime error occurred"),
|
|
115
|
+
(AttributeError, "Attribute not found"),
|
|
116
|
+
(ValueError, "Invalid value provided"),
|
|
117
|
+
(TypeError, "Type mismatch error"),
|
|
118
|
+
(KeyError, "Missing key in dictionary"),
|
|
119
|
+
]
|
|
120
|
+
|
|
88
121
|
def tearDown(self):
|
|
89
122
|
pass
|
|
90
123
|
|
|
@@ -293,3 +326,116 @@ class TestCore(TestCase):
|
|
|
293
326
|
"b_equal_c": [{"b": 2, "c": 1}],
|
|
294
327
|
},
|
|
295
328
|
)
|
|
329
|
+
|
|
330
|
+
def test_solution_check_method_exception_handling(self):
|
|
331
|
+
"""Test that when a check_* method raises an exception, a generic error message is added."""
|
|
332
|
+
|
|
333
|
+
class DummySolution(SolutionCore):
|
|
334
|
+
schema = get_empty_schema(properties=dict(sleep=dict(type="number")))
|
|
335
|
+
|
|
336
|
+
class ErrorCheckSolver(ExperimentCore):
|
|
337
|
+
schema_checks = self.error_check_schema
|
|
338
|
+
|
|
339
|
+
def __init__(
|
|
340
|
+
self,
|
|
341
|
+
instance,
|
|
342
|
+
solution=None,
|
|
343
|
+
error_class=RuntimeError,
|
|
344
|
+
error_message="Error",
|
|
345
|
+
):
|
|
346
|
+
super().__init__(instance, solution)
|
|
347
|
+
self.error_class = error_class
|
|
348
|
+
self.error_message = error_message
|
|
349
|
+
|
|
350
|
+
def solve(self, options):
|
|
351
|
+
self.solution = DummySolution({"sleep": 1})
|
|
352
|
+
return dict(status=STATUS_OPTIMAL, status_sol=SOLUTION_STATUS_FEASIBLE)
|
|
353
|
+
|
|
354
|
+
def check_test_check(self):
|
|
355
|
+
# Simulate different types of unexpected errors in a check method
|
|
356
|
+
raise self.error_class(self.error_message)
|
|
357
|
+
|
|
358
|
+
def check_working_check(self):
|
|
359
|
+
# This check should work correctly and return empty list (no errors)
|
|
360
|
+
return []
|
|
361
|
+
|
|
362
|
+
def get_objective(self):
|
|
363
|
+
return 0
|
|
364
|
+
|
|
365
|
+
class ErrorApp(self.app.__class__):
|
|
366
|
+
solvers = dict(default=ErrorCheckSolver)
|
|
367
|
+
solution = DummySolution
|
|
368
|
+
|
|
369
|
+
error_app = ErrorApp()
|
|
370
|
+
|
|
371
|
+
for error_class, error_message in self.error_test_cases:
|
|
372
|
+
with self.subTest(error_type=error_class.__name__):
|
|
373
|
+
# Create instance and solution for the solver
|
|
374
|
+
instance = error_app.instance.from_dict({"seconds": 1})
|
|
375
|
+
solution = error_app.solution.from_dict({"sleep": 1})
|
|
376
|
+
error_solver = ErrorCheckSolver(
|
|
377
|
+
instance, solution, error_class, error_message
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Should NOT raise an exception, but return the checks with generic error
|
|
381
|
+
checks = error_solver.launch_all_checks()
|
|
382
|
+
|
|
383
|
+
# Verify the generic error message is in the checks for the failing check
|
|
384
|
+
self.assertIn("test_check", checks)
|
|
385
|
+
self.assertEqual(
|
|
386
|
+
checks["test_check"],
|
|
387
|
+
[
|
|
388
|
+
{
|
|
389
|
+
"error_type": "Check execution error",
|
|
390
|
+
"error_message": "The execution of the check has failed, please contact support",
|
|
391
|
+
}
|
|
392
|
+
],
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# Verify that the working check is NOT in the failed checks (empty list means no errors)
|
|
396
|
+
self.assertNotIn("working_check", checks)
|
|
397
|
+
|
|
398
|
+
def test_instance_check_method_exception_handling(self):
|
|
399
|
+
"""Test that when a check_* method of an instance raises an exception, a generic error message is added."""
|
|
400
|
+
|
|
401
|
+
class ErrorCheckInstance(InstanceCore):
|
|
402
|
+
schema = get_empty_schema(properties=dict(seconds=dict(type="number")))
|
|
403
|
+
schema_checks = self.error_check_schema
|
|
404
|
+
|
|
405
|
+
def __init__(self, data, error_class=RuntimeError, error_message="Error"):
|
|
406
|
+
super().__init__(data)
|
|
407
|
+
self.error_class = error_class
|
|
408
|
+
self.error_message = error_message
|
|
409
|
+
|
|
410
|
+
def check_test_check(self):
|
|
411
|
+
# Simulate different types of unexpected errors in a check method
|
|
412
|
+
raise self.error_class(self.error_message)
|
|
413
|
+
|
|
414
|
+
def check_working_check(self):
|
|
415
|
+
# This check should work correctly and return empty list (no errors)
|
|
416
|
+
return []
|
|
417
|
+
|
|
418
|
+
for error_class, error_message in self.error_test_cases:
|
|
419
|
+
with self.subTest(error_type=error_class.__name__):
|
|
420
|
+
# Create instance
|
|
421
|
+
error_instance = ErrorCheckInstance(
|
|
422
|
+
{"seconds": 1}, error_class, error_message
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# Should NOT raise an exception, but return the checks with generic error
|
|
426
|
+
checks = error_instance.launch_all_checks()
|
|
427
|
+
|
|
428
|
+
# Verify the generic error message is in the checks for the failing check
|
|
429
|
+
self.assertIn("test_check", checks)
|
|
430
|
+
self.assertEqual(
|
|
431
|
+
checks["test_check"],
|
|
432
|
+
[
|
|
433
|
+
{
|
|
434
|
+
"error_type": "Check execution error",
|
|
435
|
+
"error_message": "The execution of the check has failed, please contact support",
|
|
436
|
+
}
|
|
437
|
+
],
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
# Verify that the working check is NOT in the failed checks (empty list means no errors)
|
|
441
|
+
self.assertNotIn("working_check", checks)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cornflow-client
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0rc2
|
|
4
4
|
Summary: Client to connect to a cornflow server
|
|
5
5
|
Home-page: https://github.com/baobabsoluciones/cornflow
|
|
6
6
|
Author: baobab soluciones
|
|
@@ -9,16 +9,16 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/x-rst
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: requests
|
|
16
|
-
Requires-Dist: genson
|
|
17
|
-
Requires-Dist: jsonschema
|
|
18
|
-
Requires-Dist: marshmallow
|
|
19
|
-
Requires-Dist: pytups
|
|
20
|
-
Requires-Dist: ortools
|
|
21
|
-
Requires-Dist: PuLP
|
|
15
|
+
Requires-Dist: requests==2.32.4
|
|
16
|
+
Requires-Dist: genson==1.2.2
|
|
17
|
+
Requires-Dist: jsonschema==4.21.1
|
|
18
|
+
Requires-Dist: marshmallow==3.26.2
|
|
19
|
+
Requires-Dist: pytups==0.86.2
|
|
20
|
+
Requires-Dist: ortools==9.8.3296
|
|
21
|
+
Requires-Dist: PuLP==2.9.0
|
|
22
22
|
Dynamic: author
|
|
23
23
|
Dynamic: author-email
|
|
24
24
|
Dynamic: classifier
|
|
@@ -12,7 +12,7 @@ extra_required = {"excel": ["openpyxl", "pandas"]}
|
|
|
12
12
|
|
|
13
13
|
setuptools.setup(
|
|
14
14
|
name="cornflow-client",
|
|
15
|
-
version="2.0.
|
|
15
|
+
version="2.0.0rc2",
|
|
16
16
|
author="baobab soluciones",
|
|
17
17
|
author_email="sistemas@baobabsoluciones.es",
|
|
18
18
|
description="Client to connect to a cornflow server",
|
|
@@ -26,7 +26,7 @@ setuptools.setup(
|
|
|
26
26
|
"Operating System :: OS Independent",
|
|
27
27
|
"Development Status :: 5 - Production/Stable",
|
|
28
28
|
],
|
|
29
|
-
python_requires=">=3.
|
|
29
|
+
python_requires=">=3.10",
|
|
30
30
|
include_package_data=True,
|
|
31
31
|
install_requires=required,
|
|
32
32
|
extra_require=extra_required,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/airflow/dag_utilities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/empty_schema.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/pulp_json_schema.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/schema_validator.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/solver_config.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/data/vrp_solution_schema.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/databricks/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/raw_cornflow_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/schema/dict_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/__init__.py
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/data_input_bad.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/data_schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/gc_input.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/gc_output.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/hk_data_input.json
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/hk_data_schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/schema_with_fk.json
RENAMED
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/test_mps.mps
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_with_access.xlsx
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_with_fk.xlsx
RENAMED
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/data/xl_without_fk.xlsx
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/integration/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/__init__.py
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_abc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client/tests/unit/test_varnames.py
RENAMED
|
File without changes
|
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{cornflow_client-2.0.0a23 → cornflow_client-2.0.0rc2}/cornflow_client.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|