cornflow 1.1.5a1__py3-none-any.whl → 2.0.0a7__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.
- cornflow/cli/service.py +53 -21
- cornflow/commands/dag.py +6 -6
- cornflow/commands/permissions.py +12 -10
- cornflow/config.py +16 -3
- cornflow/endpoints/__init__.py +1 -1
- cornflow/endpoints/case.py +4 -4
- cornflow/endpoints/dag.py +5 -5
- cornflow/endpoints/data_check.py +12 -12
- cornflow/endpoints/example_data.py +2 -2
- cornflow/endpoints/execution.py +12 -12
- cornflow/endpoints/execution_databricks.py +830 -0
- cornflow/endpoints/health.py +41 -7
- cornflow/endpoints/instance.py +3 -3
- cornflow/endpoints/schemas.py +2 -2
- cornflow/models/__init__.py +1 -1
- cornflow/models/dag.py +5 -4
- cornflow/models/dag_permissions.py +2 -2
- cornflow/models/execution.py +2 -1
- cornflow/orchestrator_constants.py +12 -0
- cornflow/schemas/health.py +1 -1
- cornflow/shared/const.py +23 -0
- cornflow/shared/databricks.py +111 -0
- cornflow/shared/exceptions.py +3 -1
- cornflow/tests/integration/test_commands.py +2 -2
- cornflow/tests/unit/test_commands.py +2 -2
- cornflow/tests/unit/test_dags.py +2 -2
- cornflow/tests/unit/test_example_data.py +1 -1
- cornflow/tests/unit/tools.py +3 -3
- {cornflow-1.1.5a1.dist-info → cornflow-2.0.0a7.dist-info}/METADATA +3 -2
- {cornflow-1.1.5a1.dist-info → cornflow-2.0.0a7.dist-info}/RECORD +33 -30
- {cornflow-1.1.5a1.dist-info → cornflow-2.0.0a7.dist-info}/WHEEL +0 -0
- {cornflow-1.1.5a1.dist-info → cornflow-2.0.0a7.dist-info}/entry_points.txt +0 -0
- {cornflow-1.1.5a1.dist-info → cornflow-2.0.0a7.dist-info}/top_level.txt +0 -0
cornflow/endpoints/health.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
Endpoint to check the health of the services.
|
3
3
|
It performs a health check to airflow and a health check to cornflow database
|
4
4
|
"""
|
5
|
+
|
5
6
|
import os
|
6
7
|
|
7
8
|
# Import from libraries
|
@@ -13,7 +14,14 @@ from flask_apispec import marshal_with, doc
|
|
13
14
|
from cornflow.endpoints.meta_resource import BaseMetaResource
|
14
15
|
from cornflow.models import UserModel
|
15
16
|
from cornflow.schemas.health import HealthResponse
|
16
|
-
from cornflow.shared.const import
|
17
|
+
from cornflow.shared.const import (
|
18
|
+
AIRFLOW_BACKEND,
|
19
|
+
DATABRICKS_BACKEND,
|
20
|
+
STATUS_HEALTHY,
|
21
|
+
STATUS_UNHEALTHY,
|
22
|
+
)
|
23
|
+
from cornflow.shared.databricks import Databricks
|
24
|
+
from cornflow.shared.exceptions import EndpointNotImplemented
|
17
25
|
|
18
26
|
|
19
27
|
class HealthEndpoint(BaseMetaResource):
|
@@ -27,11 +35,11 @@ class HealthEndpoint(BaseMetaResource):
|
|
27
35
|
:rtype: dict
|
28
36
|
:doc-author: baobab soluciones
|
29
37
|
"""
|
30
|
-
|
31
|
-
|
38
|
+
|
39
|
+
backend_status = self.check_backend_status()
|
40
|
+
|
32
41
|
cornflow_status = STATUS_UNHEALTHY
|
33
|
-
|
34
|
-
airflow_status = STATUS_HEALTHY
|
42
|
+
|
35
43
|
|
36
44
|
if (
|
37
45
|
UserModel.get_one_user_by_username(os.getenv("CORNFLOW_SERVICE_USER"))
|
@@ -40,6 +48,32 @@ class HealthEndpoint(BaseMetaResource):
|
|
40
48
|
cornflow_status = STATUS_HEALTHY
|
41
49
|
|
42
50
|
current_app.logger.info(
|
43
|
-
f"Health check: cornflow {cornflow_status},
|
51
|
+
f"Health check: cornflow {cornflow_status}, backend {backend_status}"
|
44
52
|
)
|
45
|
-
return {"cornflow_status": cornflow_status, "
|
53
|
+
return {"cornflow_status": cornflow_status, "backend_status": backend_status}
|
54
|
+
|
55
|
+
def check_backend_status(self):
|
56
|
+
if current_app.config["CORNFLOW_BACKEND"] == AIRFLOW_BACKEND:
|
57
|
+
return self._check_airflow_status()
|
58
|
+
elif current_app.config["CORNFLOW_BACKEND"] == DATABRICKS_BACKEND:
|
59
|
+
return self._check_databricks_status()
|
60
|
+
else:
|
61
|
+
raise EndpointNotImplemented()
|
62
|
+
|
63
|
+
@staticmethod
|
64
|
+
def _check_airflow_status():
|
65
|
+
af_client = Airflow.from_config(current_app.config)
|
66
|
+
airflow_status = STATUS_UNHEALTHY
|
67
|
+
if af_client.is_alive():
|
68
|
+
airflow_status = STATUS_HEALTHY
|
69
|
+
|
70
|
+
return airflow_status
|
71
|
+
|
72
|
+
@staticmethod
|
73
|
+
def _check_databricks_status():
|
74
|
+
db_client = Databricks.from_config(current_app.config)
|
75
|
+
databricks_status = STATUS_UNHEALTHY
|
76
|
+
if db_client.is_alive():
|
77
|
+
databricks_status = STATUS_HEALTHY
|
78
|
+
|
79
|
+
return databricks_status
|
cornflow/endpoints/instance.py
CHANGED
@@ -16,7 +16,7 @@ from werkzeug.utils import secure_filename
|
|
16
16
|
|
17
17
|
# Import from internal modules
|
18
18
|
from cornflow.endpoints.meta_resource import BaseMetaResource
|
19
|
-
from cornflow.models import InstanceModel,
|
19
|
+
from cornflow.models import InstanceModel, DeployedOrch
|
20
20
|
from cornflow.schemas.instance import (
|
21
21
|
InstanceSchema,
|
22
22
|
InstanceEndpointResponse,
|
@@ -92,7 +92,7 @@ class InstanceEndpoint(BaseMetaResource):
|
|
92
92
|
# We validate the instance data
|
93
93
|
config = current_app.config
|
94
94
|
|
95
|
-
instance_schema =
|
95
|
+
instance_schema = DeployedOrch.get_one_schema(config, data_schema, INSTANCE_SCHEMA)
|
96
96
|
instance_errors = json_schema_validate_as_string(instance_schema, kwargs["data"])
|
97
97
|
|
98
98
|
if instance_errors:
|
@@ -163,7 +163,7 @@ class InstanceDetailsEndpoint(InstanceDetailsEndpointBase):
|
|
163
163
|
|
164
164
|
config = current_app.config
|
165
165
|
|
166
|
-
instance_schema =
|
166
|
+
instance_schema = DeployedOrch.get_one_schema(config, schema, INSTANCE_SCHEMA)
|
167
167
|
instance_errors = json_schema_validate_as_string(instance_schema, kwargs["data"])
|
168
168
|
|
169
169
|
if instance_errors:
|
cornflow/endpoints/schemas.py
CHANGED
@@ -8,7 +8,7 @@ from flask_apispec import marshal_with, doc
|
|
8
8
|
|
9
9
|
# Import from internal modules
|
10
10
|
from cornflow.endpoints.meta_resource import BaseMetaResource
|
11
|
-
from cornflow.models import PermissionsDAG,
|
11
|
+
from cornflow.models import PermissionsDAG, DeployedOrch
|
12
12
|
from cornflow.schemas.schemas import SchemaOneApp, SchemaListApp
|
13
13
|
from cornflow.shared.authentication import Auth, authenticate
|
14
14
|
from cornflow.shared.const import ALL_DEFAULT_ROLES
|
@@ -63,7 +63,7 @@ class SchemaDetailsEndpoint(BaseMetaResource):
|
|
63
63
|
)
|
64
64
|
|
65
65
|
if permission:
|
66
|
-
deployed_dag =
|
66
|
+
deployed_dag = DeployedOrch.get_one_object(dag_name)
|
67
67
|
current_app.logger.info("User gets schema {}".format(dag_name))
|
68
68
|
return {
|
69
69
|
"instance": deployed_dag.instance_schema,
|
cornflow/models/__init__.py
CHANGED
@@ -4,7 +4,7 @@ Initialization file for the models module
|
|
4
4
|
from .action import ActionModel
|
5
5
|
from .alarms import AlarmsModel
|
6
6
|
from .case import CaseModel
|
7
|
-
from .dag import
|
7
|
+
from .dag import DeployedOrch
|
8
8
|
from .dag_permissions import PermissionsDAG
|
9
9
|
from .execution import ExecutionModel
|
10
10
|
from .instance import InstanceModel
|
cornflow/models/dag.py
CHANGED
@@ -16,8 +16,9 @@ from cornflow.models.meta_models import TraceAttributesModel
|
|
16
16
|
from cornflow.shared import db
|
17
17
|
from cornflow.shared.exceptions import ObjectDoesNotExist
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
# TODO AGA: cambiar nombre a la clase para que sea general
|
20
|
+
# Aí como a los permisos etc
|
21
|
+
class DeployedOrch(TraceAttributesModel):
|
21
22
|
"""
|
22
23
|
This model contains the registry of the DAGs that are deployed on the corresponding Airflow server
|
23
24
|
"""
|
@@ -35,7 +36,7 @@ class DeployedDAG(TraceAttributesModel):
|
|
35
36
|
"PermissionsDAG",
|
36
37
|
cascade="all,delete",
|
37
38
|
backref="deployed_dags",
|
38
|
-
primaryjoin="and_(
|
39
|
+
primaryjoin="and_(DeployedOrch.id==PermissionsDAG.dag_id)",
|
39
40
|
)
|
40
41
|
|
41
42
|
def __init__(self, data):
|
@@ -53,7 +54,7 @@ class DeployedDAG(TraceAttributesModel):
|
|
53
54
|
|
54
55
|
@staticmethod
|
55
56
|
def get_one_schema(config, dag_name, schema=INSTANCE_SCHEMA):
|
56
|
-
item =
|
57
|
+
item = DeployedOrch.get_one_object(dag_name)
|
57
58
|
|
58
59
|
if item is None:
|
59
60
|
err = f"The DAG {dag_name} does not exist in the database."
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from cornflow.models.dag import
|
1
|
+
from cornflow.models.dag import DeployedOrch
|
2
2
|
from cornflow.models.meta_models import TraceAttributesModel
|
3
3
|
from cornflow.shared import db
|
4
4
|
|
@@ -29,7 +29,7 @@ class PermissionsDAG(TraceAttributesModel):
|
|
29
29
|
|
30
30
|
@staticmethod
|
31
31
|
def add_all_permissions_to_user(user_id):
|
32
|
-
dags =
|
32
|
+
dags = DeployedOrch.get_all_objects()
|
33
33
|
permissions = [
|
34
34
|
PermissionsDAG({"dag_id": dag.id, "user_id": user_id}) for dag in dags
|
35
35
|
]
|
cornflow/models/execution.py
CHANGED
@@ -78,7 +78,7 @@ class ExecutionModel(BaseDataModel):
|
|
78
78
|
+ str(self.instance_id)
|
79
79
|
).encode()
|
80
80
|
).hexdigest()
|
81
|
-
|
81
|
+
# TODO AGA: modificar a run_id, tanto la columna como el parámetro.
|
82
82
|
self.dag_run_id = data.get("dag_run_id")
|
83
83
|
self.state = data.get("state", DEFAULT_EXECUTION_CODE)
|
84
84
|
self.state_message = EXECUTION_STATE_MESSAGE_DICT[self.state]
|
@@ -117,6 +117,7 @@ class ExecutionModel(BaseDataModel):
|
|
117
117
|
:param str message: Message for the error
|
118
118
|
:return: nothing
|
119
119
|
"""
|
120
|
+
print("Updating state to ", code)
|
120
121
|
self.state = code
|
121
122
|
if message is None:
|
122
123
|
self.state_message = EXECUTION_STATE_MESSAGE_DICT[code]
|
cornflow/schemas/health.py
CHANGED
cornflow/shared/const.py
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
In this files we import the values for different constants on cornflow server
|
3
3
|
"""
|
4
4
|
|
5
|
+
# CORNFLOW BACKEND
|
6
|
+
AIRFLOW_BACKEND = 1
|
7
|
+
DATABRICKS_BACKEND = 2
|
8
|
+
|
9
|
+
|
5
10
|
# endpoints responses for health check
|
6
11
|
STATUS_HEALTHY = "healthy"
|
7
12
|
STATUS_UNHEALTHY = "unhealthy"
|
@@ -42,7 +47,24 @@ AIRFLOW_TO_STATE_MAP = dict(
|
|
42
47
|
failed=EXEC_STATE_ERROR,
|
43
48
|
queued=EXEC_STATE_QUEUED,
|
44
49
|
)
|
50
|
+
# TODO AGA : revisar si la correspondencia de estados es correcta
|
51
|
+
DATABRICKS_TO_STATE_MAP = dict(
|
52
|
+
BLOCKED=EXEC_STATE_QUEUED,
|
53
|
+
PENDING=EXEC_STATE_QUEUED,
|
54
|
+
QUEUED=EXEC_STATE_QUEUED,
|
55
|
+
RUNNING=EXEC_STATE_RUNNING,
|
56
|
+
TERMINATING=EXEC_STATE_RUNNING,
|
57
|
+
SUCCESS=EXEC_STATE_CORRECT,
|
58
|
+
USER_CANCELED=EXEC_STATE_STOPPED,
|
59
|
+
OTHER_FINISH_ERROR=EXEC_STATE_ERROR,
|
60
|
+
)
|
45
61
|
|
62
|
+
DATABRICKS_FINISH_TO_STATE_MAP = dict(
|
63
|
+
SUCCESS=EXEC_STATE_CORRECT,
|
64
|
+
USER_CANCELED=EXEC_STATE_STOPPED,
|
65
|
+
)
|
66
|
+
|
67
|
+
DATABRICKS_TERMINATE_STATE = "TERMINATED"
|
46
68
|
# These codes and names are inherited from flask app builder in order to have the same names and values
|
47
69
|
# as this library that is the base of airflow
|
48
70
|
AUTH_DB = 1
|
@@ -55,6 +77,7 @@ OID_NONE = 0
|
|
55
77
|
OID_AZURE = 1
|
56
78
|
OID_GOOGLE = 2
|
57
79
|
|
80
|
+
|
58
81
|
# AZURE OPEN ID URLS
|
59
82
|
OID_AZURE_DISCOVERY_COMMON_URL = (
|
60
83
|
"https://login.microsoftonline.com/common/.well-known/openid-configuration"
|
@@ -0,0 +1,111 @@
|
|
1
|
+
"""
|
2
|
+
Python class to implement the Databricks client wrapper
|
3
|
+
"""
|
4
|
+
import requests
|
5
|
+
from databricks.sdk import WorkspaceClient
|
6
|
+
from flask import current_app
|
7
|
+
from cornflow.orchestrator_constants import config_orchestrator
|
8
|
+
# TODO AGA: CODIGO REPETIDO
|
9
|
+
# TODO AGA: revisar si el import está bien
|
10
|
+
from cornflow_client.constants import DatabricksError
|
11
|
+
from cornflow.shared.const import DATABRICKS_TO_STATE_MAP,DATABRICKS_TERMINATE_STATE, DATABRICKS_FINISH_TO_STATE_MAP
|
12
|
+
|
13
|
+
class Databricks:
|
14
|
+
def __init__(self, url, auth_secret, token_endpoint, ep_clusters, client_id):
|
15
|
+
self.url = url
|
16
|
+
self.constants=config_orchestrator["databricks"]
|
17
|
+
self.auth_secret=auth_secret
|
18
|
+
self.token_endpoint=token_endpoint
|
19
|
+
self.ep_clusters=ep_clusters
|
20
|
+
self.client_id = client_id
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_config(cls, config):
|
24
|
+
data = dict(
|
25
|
+
url=config["DATABRICKS_URL"],
|
26
|
+
auth_secret=config["DATABRICKS_AUTH_SECRET"],
|
27
|
+
token_endpoint=config["DATABRICKS_TOKEN_ENDPOINT"],
|
28
|
+
ep_clusters=config["DATABRICKS_EP_CLUSTERS"],
|
29
|
+
client_id=config["DATABRICKS_CLIENT_ID"],
|
30
|
+
)
|
31
|
+
return cls(**data)
|
32
|
+
|
33
|
+
def get_token(self):
|
34
|
+
import requests
|
35
|
+
url = f'{self.url}{self.token_endpoint}'
|
36
|
+
data = {
|
37
|
+
"grant_type": "client_credentials",
|
38
|
+
"scope": "all-apis"
|
39
|
+
}
|
40
|
+
auth = (self.client_id,self.auth_secret)
|
41
|
+
oauth_response = requests.post(url,data=data,auth=auth)
|
42
|
+
oauth_response.json()
|
43
|
+
oauth_token = oauth_response.json()["access_token"]
|
44
|
+
return oauth_token
|
45
|
+
|
46
|
+
def is_alive(self):
|
47
|
+
try:
|
48
|
+
# TODO: this url is project specific. Either it has to be a config option or some other way has to be found
|
49
|
+
path="/Workspace/Repos/nippon/nippon_production_scheduling/requirements.txt"
|
50
|
+
url = f"{self.url}/api/2.0/workspace/get-status?path={path}"
|
51
|
+
response = self.request_headers_auth(method="GET", url=url)
|
52
|
+
if "error_code" in response.json().keys():
|
53
|
+
return False
|
54
|
+
return True
|
55
|
+
|
56
|
+
except Exception as err:
|
57
|
+
current_app.logger.error(f"Error: {err}")
|
58
|
+
return False
|
59
|
+
|
60
|
+
def get_orch_info(self, orch_name, method="GET"):
|
61
|
+
"""
|
62
|
+
Get information about a job in Databricks
|
63
|
+
https://docs.databricks.com/api/workspace/jobs/get
|
64
|
+
"""
|
65
|
+
url = f"{self.url}/api/2.1/jobs/get/?job_id={orch_name}"
|
66
|
+
schema_info = self.request_headers_auth(method=method, url=url)
|
67
|
+
if "error_code" in schema_info.json().keys():
|
68
|
+
raise DatabricksError("JOB not available")
|
69
|
+
return schema_info
|
70
|
+
# TODO AGA: incluir un id de job por defecto o hacer obligatorio el uso el parámetro.
|
71
|
+
# Revisar los efectos secundarios de eliminar execution_id y usar el predeterminado
|
72
|
+
def run_workflow(
|
73
|
+
self, execution_id, orch_name=config_orchestrator["databricks"]["def_schema"], checks_only=False, case_id=None
|
74
|
+
):
|
75
|
+
"""
|
76
|
+
Run a job in Databricks
|
77
|
+
"""
|
78
|
+
# TODO AGA: revisar si la url esta bien/si acepta asi los parámetros
|
79
|
+
url = f"{self.url}/api/2.1/jobs/run-now/"
|
80
|
+
# TODO AGA: revisar si deben ser notebook parameters o job parameters.
|
81
|
+
# Entender cómo se usa checks_only
|
82
|
+
payload = dict(job_id=orch_name, notebook_parameters=dict(checks_only=checks_only))
|
83
|
+
return self.request_headers_auth(method="POST", url=url, json=payload)
|
84
|
+
|
85
|
+
def get_run_status(self, run_id):
|
86
|
+
"""
|
87
|
+
Get the status of a run in Databricks
|
88
|
+
"""
|
89
|
+
print( "asking for run id ", run_id)
|
90
|
+
url = f"{self.url}/api/2.1/jobs/runs/get"
|
91
|
+
payload = dict(run_id=run_id)
|
92
|
+
info = self.request_headers_auth(method="GET", url=url, json=payload)
|
93
|
+
info = info.json()
|
94
|
+
print("info is ", info)
|
95
|
+
state = info["status"]["state"]
|
96
|
+
if state == DATABRICKS_TERMINATE_STATE:
|
97
|
+
if info["status"]["termination_details"]["code"] in DATABRICKS_FINISH_TO_STATE_MAP.keys():
|
98
|
+
return info["status"]["termination_details"]["code"]
|
99
|
+
else:
|
100
|
+
return "OTHER_FINISH_ERROR"
|
101
|
+
return state
|
102
|
+
def request_headers_auth(self, status=200, **kwargs):
|
103
|
+
token =self.get_token()
|
104
|
+
def_headers = {"Authorization": "Bearer "+ str(token)}
|
105
|
+
headers = kwargs.get("headers", def_headers)
|
106
|
+
response = requests.request(headers=headers, **kwargs)
|
107
|
+
if status is None:
|
108
|
+
return response
|
109
|
+
if response.status_code != status:
|
110
|
+
raise DatabricksError(error=response.text, status_code=response.status_code)
|
111
|
+
return response
|
cornflow/shared/exceptions.py
CHANGED
@@ -5,7 +5,7 @@ on a flask REST API server
|
|
5
5
|
|
6
6
|
from flask import jsonify
|
7
7
|
from webargs.flaskparser import parser
|
8
|
-
from cornflow_client.constants import AirflowError
|
8
|
+
from cornflow_client.constants import AirflowError, DatabricksError
|
9
9
|
from werkzeug.exceptions import HTTPException
|
10
10
|
import traceback
|
11
11
|
|
@@ -146,6 +146,8 @@ def initialize_errorhandlers(app):
|
|
146
146
|
@app.errorhandler(InvalidCredentials)
|
147
147
|
@app.errorhandler(EndpointNotImplemented)
|
148
148
|
@app.errorhandler(AirflowError)
|
149
|
+
# TODO AGA: revisar si está bien implementado el nuevo error
|
150
|
+
@app.errorhandler(DatabricksError)
|
149
151
|
@app.errorhandler(InvalidData)
|
150
152
|
@app.errorhandler(InvalidPatch)
|
151
153
|
@app.errorhandler(ConfigurationError)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from flask import current_app
|
2
2
|
|
3
3
|
from cornflow.commands.dag import register_deployed_dags_command
|
4
|
-
from cornflow.models import
|
4
|
+
from cornflow.models import DeployedOrch
|
5
5
|
from cornflow.tests.const import PUBLIC_DAGS
|
6
6
|
from cornflow.tests.custom_liveServer import CustomTestCaseLive
|
7
7
|
|
@@ -15,7 +15,7 @@ class TestCornflowCommands(CustomTestCaseLive):
|
|
15
15
|
register_deployed_dags_command(
|
16
16
|
config["AIRFLOW_URL"], config["AIRFLOW_USER"], config["AIRFLOW_PWD"], False
|
17
17
|
)
|
18
|
-
dags =
|
18
|
+
dags = DeployedOrch.get_all_objects()
|
19
19
|
|
20
20
|
for dag in PUBLIC_DAGS:
|
21
21
|
self.assertIn(dag, [d.id for d in dags])
|
@@ -39,7 +39,7 @@ from cornflow.models import (
|
|
39
39
|
ViewModel,
|
40
40
|
)
|
41
41
|
from cornflow.models import (
|
42
|
-
|
42
|
+
DeployedOrch,
|
43
43
|
PermissionsDAG,
|
44
44
|
UserModel,
|
45
45
|
)
|
@@ -356,7 +356,7 @@ class TestCommands(TestCase):
|
|
356
356
|
- Presence of required DAGs
|
357
357
|
"""
|
358
358
|
register_deployed_dags_command_test(verbose=True)
|
359
|
-
dags =
|
359
|
+
dags = DeployedOrch.get_all_objects()
|
360
360
|
for dag in ["solve_model_dag", "gc", "timer"]:
|
361
361
|
self.assertIn(dag, [d.id for d in dags])
|
362
362
|
|
cornflow/tests/unit/test_dags.py
CHANGED
@@ -24,7 +24,7 @@ from cornflow.commands.access import access_init_command
|
|
24
24
|
from cornflow.commands.dag import register_deployed_dags_command_test
|
25
25
|
from cornflow.commands.permissions import register_dag_permissions_command
|
26
26
|
from cornflow.shared.const import ADMIN_ROLE, SERVICE_ROLE
|
27
|
-
from cornflow.models import
|
27
|
+
from cornflow.models import DeployedOrch, PermissionsDAG, UserModel, UserRoleModel
|
28
28
|
from cornflow.shared.const import EXEC_STATE_CORRECT, EXEC_STATE_MANUAL
|
29
29
|
from cornflow.shared import db
|
30
30
|
from cornflow.tests.const import (
|
@@ -351,7 +351,7 @@ class TestDeployedDAG(TestCase):
|
|
351
351
|
"""
|
352
352
|
before = PermissionsDAG.get_user_dag_permissions(self.admin["id"])
|
353
353
|
self.assertIsNotNone(before)
|
354
|
-
dag =
|
354
|
+
dag = DeployedOrch.query.get("solve_model_dag")
|
355
355
|
dag.delete()
|
356
356
|
after = PermissionsDAG.get_user_dag_permissions(self.admin["id"])
|
357
357
|
self.assertNotEqual(before, after)
|
@@ -35,7 +35,7 @@ class TestExampleDataEndpoint(CustomTestCase):
|
|
35
35
|
def patch_af_client(self, Airflow_mock):
|
36
36
|
af_client = Airflow_mock.return_value
|
37
37
|
af_client.is_alive.return_value = True
|
38
|
-
af_client.
|
38
|
+
af_client.get_orch_info.return_value = {}
|
39
39
|
af_client.get_one_variable.return_value = {
|
40
40
|
"value": json.dumps(self.example),
|
41
41
|
"key": self.schema_name,
|
cornflow/tests/unit/tools.py
CHANGED
@@ -6,8 +6,8 @@ def patch_af_client(af_client_class):
|
|
6
6
|
responses_mock = Mock()
|
7
7
|
responses_mock.json.return_value = {"is_paused": False, "dag_run_id": "12345", "state": "success"}
|
8
8
|
af_client_mock.is_alive.return_value = True
|
9
|
-
af_client_mock.
|
10
|
-
af_client_mock.
|
11
|
-
af_client_mock.
|
9
|
+
af_client_mock.get_orch_info.return_value = responses_mock
|
10
|
+
af_client_mock.run_workflow.return_value = responses_mock
|
11
|
+
af_client_mock.get_run_status.return_value = responses_mock
|
12
12
|
af_client_mock.set_dag_run_to_fail.return_value = None
|
13
13
|
af_client_class.from_config.return_value = af_client_mock
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: cornflow
|
3
|
-
Version:
|
3
|
+
Version: 2.0.0a7
|
4
4
|
Summary: Cornflow is an open source multi-solver optimization server with a REST API built using flask.
|
5
5
|
Home-page: https://github.com/baobabsoluciones/cornflow
|
6
6
|
Author: baobab soluciones
|
@@ -13,8 +13,9 @@ Requires-Python: >=3.9
|
|
13
13
|
Requires-Dist: alembic==1.9.2
|
14
14
|
Requires-Dist: apispec<=6.3.0
|
15
15
|
Requires-Dist: click<=8.1.7
|
16
|
-
Requires-Dist: cornflow-client
|
16
|
+
Requires-Dist: cornflow-client==2.0.0a6
|
17
17
|
Requires-Dist: cryptography<=42.0.5
|
18
|
+
Requires-Dist: databricks-sdk==0.29.0
|
18
19
|
Requires-Dist: disposable-email-domains>=0.0.86
|
19
20
|
Requires-Dist: Flask==2.3.2
|
20
21
|
Requires-Dist: flask-apispec<=0.11.4
|
@@ -6,8 +6,9 @@ airflow_config/plugins/XCom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
6
6
|
airflow_config/plugins/XCom/gce_xcom_backend.py,sha256=vCGvF2jbfZt5bOv-pk5Q_kUR6LomFUojIymimSJmj3o,1795
|
7
7
|
cornflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
cornflow/app.py,sha256=X73N64o8OGEqVIRWbC13e_4xb1lxzOH_BV3F3fzAmXE,7312
|
9
|
-
cornflow/config.py,sha256=
|
9
|
+
cornflow/config.py,sha256=x7Eu4gOaxdMD9_hzc1m2TcwTtOyFnXXRUjb49_Uc1Y4,6107
|
10
10
|
cornflow/gunicorn.py,sha256=uO-Yk7w7nvQSWh12iDxsVvlG-_2BiKIIjm2UiTk4P9E,480
|
11
|
+
cornflow/orchestrator_constants.py,sha256=VO6EGcHhovH6nFpp3QGY_orYVJbjFrMavmemL-Gx_Vs,269
|
11
12
|
cornflow/cli/__init__.py,sha256=5jBmSMpaE1S9rDaQjS8VHJ6x4FfJG8MhKzMzfw7G4Zc,743
|
12
13
|
cornflow/cli/actions.py,sha256=BdTFucT6gZ0QJqo96Zu0C2G9acZ578tLkktKSfTybJ8,414
|
13
14
|
cornflow/cli/arguments.py,sha256=9EEyyny5cJJ1t3WAs6zMgTDvTre0JdQ2N_oZfFQmixs,657
|
@@ -16,7 +17,7 @@ cornflow/cli/migrations.py,sha256=Stc8H99rG8vgo3yRJcck11zBY_EA4WqyVybglfl8zJE,16
|
|
16
17
|
cornflow/cli/permissions.py,sha256=4KXKysH4g8YYQIZcPuXFS2g0xEErp-e8I_FAqMGaV7U,1006
|
17
18
|
cornflow/cli/roles.py,sha256=NFG__qrlyOT0h4L4nwo9FSV4DKjGtMVh3gwiJxwM37w,411
|
18
19
|
cornflow/cli/schemas.py,sha256=sxuJOZf12SBZAXDiAYNPB-n9LSxzSwkB3xyhgS_4K9A,6086
|
19
|
-
cornflow/cli/service.py,sha256=
|
20
|
+
cornflow/cli/service.py,sha256=lCFOQXtBMYOm8sjntuyLhY_TshFOMMnEtysFy6zrgc8,10817
|
20
21
|
cornflow/cli/users.py,sha256=nPnu8rQNLtwmeXLwYtJ_hjlsa_24XOnQLgBJRBP9bJw,2104
|
21
22
|
cornflow/cli/utils.py,sha256=0tF41gTt6LL9XGOizTQg2GXuOXbqLg6gapCr-HWjJ0Q,733
|
22
23
|
cornflow/cli/views.py,sha256=Xyx2l-Sm7panxQEfR3qksCIUoqF7woMKsYgZALkxUXM,636
|
@@ -31,30 +32,31 @@ cornflow/commands/__init__.py,sha256=_7mi2Sd8bnaSujU-L78z8Zrswz68NJ2xoocYpsEYmPM
|
|
31
32
|
cornflow/commands/access.py,sha256=NTZJFF9la8TDuMcD_ISQtJTj-wtM2p1dddokQJHtkj0,748
|
32
33
|
cornflow/commands/actions.py,sha256=4AwgAmyI6VeaugkISvTlNGrIzMMU_-ZB3MhwDD_CIEA,1544
|
33
34
|
cornflow/commands/cleanup.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
|
-
cornflow/commands/dag.py,sha256=
|
35
|
-
cornflow/commands/permissions.py,sha256=
|
35
|
+
cornflow/commands/dag.py,sha256=4c74pqtAg9y_qw_mOSQU1WULwdd9PnV0SmVqbY4T-uw,3721
|
36
|
+
cornflow/commands/permissions.py,sha256=TtKJcX219RecAytm8PCdVctaVLZSA7ilhBlPnWxYuFQ,6934
|
36
37
|
cornflow/commands/roles.py,sha256=Oux-UkswkQ74zqaMEJYIEsZpQZGBcGaSahVzx9feAHU,1516
|
37
38
|
cornflow/commands/schemas.py,sha256=QjLXLw5So3f8ZqTg5_uvXxwpo4vE0dMT4_gFMKZHGvQ,1828
|
38
39
|
cornflow/commands/users.py,sha256=MEfqMm2ujso0NQgdUm-crOet-G0M43GNqVCx2Ls-2HY,2591
|
39
40
|
cornflow/commands/views.py,sha256=K2Ld1-l1ZKn9m6e2W1LCxmN44QokwR-8u8rIrviiEf8,2276
|
40
|
-
cornflow/endpoints/__init__.py,sha256=
|
41
|
+
cornflow/endpoints/__init__.py,sha256=JeETje5NDIoOohofogwQr_Q8NUxsK5FcI9tWN-UcDjE,7357
|
41
42
|
cornflow/endpoints/action.py,sha256=ksHK3F919cjkONLcFV2tUIbG-eZw5XbYkqVjYx9iq5I,1359
|
42
43
|
cornflow/endpoints/alarms.py,sha256=3FN7mosBFP_DcJQxfg1h5_phy755FYESXyQ62XpvFbs,1956
|
43
44
|
cornflow/endpoints/apiview.py,sha256=cpxZFkWy6yrRHiAq2tseyVAK1r8uvjnFuOgJjGT0rKI,1370
|
44
|
-
cornflow/endpoints/case.py,sha256=
|
45
|
-
cornflow/endpoints/dag.py,sha256=
|
46
|
-
cornflow/endpoints/data_check.py,sha256=
|
47
|
-
cornflow/endpoints/example_data.py,sha256=
|
48
|
-
cornflow/endpoints/execution.py,sha256=
|
49
|
-
cornflow/endpoints/
|
50
|
-
cornflow/endpoints/
|
45
|
+
cornflow/endpoints/case.py,sha256=7mopDXp8RJzOcEvokvTGhT5YYGl2gZlhKry5K8DNya4,18674
|
46
|
+
cornflow/endpoints/dag.py,sha256=x_F9gGc1_9-zoAErPGPFBhwQKqIgGEoCjDVMmRKgZRg,10395
|
47
|
+
cornflow/endpoints/data_check.py,sha256=FrsBCzwXD_E5qCYkXt0Byc_UPC0APBSxbj8EriPVKxI,16520
|
48
|
+
cornflow/endpoints/example_data.py,sha256=e3Y_lZTKVQTkk_pzOnmd-VisuKo7kE-7IqhhLHV-6yw,4374
|
49
|
+
cornflow/endpoints/execution.py,sha256=2tX4UJ23e-phAFfgRpjFASr54OWIROuA6a9Uum2Cg48,28010
|
50
|
+
cornflow/endpoints/execution_databricks.py,sha256=m9hrSyMTC01xU45BYuKL-yUf8udAKaSmBx_S9PT7cyQ,34945
|
51
|
+
cornflow/endpoints/health.py,sha256=K1l8YK7t5nfVRi0WXMn124l8-ezvYAYqHlCtitLU9AY,2558
|
52
|
+
cornflow/endpoints/instance.py,sha256=YuB0TTs32eKFxd2GJc7WeVTVWZdv7tqNAtWLrOPsuXo,11618
|
51
53
|
cornflow/endpoints/licenses.py,sha256=82hHWGYvVIiyw9mlwGtMwJMDJ-ShHOi9rvuM6KvfE4U,873
|
52
54
|
cornflow/endpoints/login.py,sha256=rchsQBL60FQXoub4dznB_UjQ5r9CmJBGnI-HnQY37Mk,9413
|
53
55
|
cornflow/endpoints/main_alarms.py,sha256=GUB-UdnvEFi7n6FGFKO9VtZeZb4Ox3NvBMhB7rdqNyI,2006
|
54
56
|
cornflow/endpoints/meta_resource.py,sha256=eqC6U8IpY65Cbk2WpdphRtE6o5kes2lB4LhezfUB7xI,8471
|
55
57
|
cornflow/endpoints/permission.py,sha256=FpEBIucfUl89UaJ80SC0VR6pFAdqdSsS43SdNkcXWtI,3751
|
56
58
|
cornflow/endpoints/roles.py,sha256=54ra4MQ9JmrHDsiGczDAVqHgAT4zwhdTA1dLBOy66v8,6105
|
57
|
-
cornflow/endpoints/schemas.py,sha256=
|
59
|
+
cornflow/endpoints/schemas.py,sha256=gyyTgD5mQb7Nsacm8Mdjcz4GXNy9IY7ZEUW9myN-Nbo,2935
|
58
60
|
cornflow/endpoints/signup.py,sha256=4Xle2aTd6fiblb2pFcTaBP3ykXSuXsrc7qD0JjpqeZY,3513
|
59
61
|
cornflow/endpoints/tables.py,sha256=KI4sgkBHdiHbOnIRR_yoZ859ea3rnp_6ji_XvQbwsZ8,3071
|
60
62
|
cornflow/endpoints/token.py,sha256=UEnsNNQAd6lJi2aF972d8uUWNJHT4ZcRr0eYpN458R4,1193
|
@@ -79,14 +81,14 @@ cornflow/migrations/versions/e1a50dae1ac9_.py,sha256=NIRKFg1hvo4F-YqMdsYoX9VwzKl
|
|
79
81
|
cornflow/migrations/versions/e937a5234ce4_.py,sha256=460JyCKPN6XL5DDcJfC51WKr9V342ovVhuo8F7fwuo0,710
|
80
82
|
cornflow/migrations/versions/ebdd955fcc5e_.py,sha256=MzLbzmiwMWWVkJWJ8EMmmBnCIOzvlwXKGFWxELnOQpE,1848
|
81
83
|
cornflow/migrations/versions/f3bee20314a2_.py,sha256=pgfAeiPvFvPJXhWlFHq6Y7bjYFGvapsfHEFXXX8UJlE,1782
|
82
|
-
cornflow/models/__init__.py,sha256=
|
84
|
+
cornflow/models/__init__.py,sha256=8ETJtAalmLu0yjv_B8dt8tNmg-NqlsngoZlhMTuftoE,501
|
83
85
|
cornflow/models/action.py,sha256=8MYzQ2qX5bG0zk28OufypzThkR7AU1J1el-5ABoTurg,1200
|
84
86
|
cornflow/models/alarms.py,sha256=R_g3tkWNSJaAG4gSvthgJlyrueY9VDuIZPoVHk5lDvU,1682
|
85
87
|
cornflow/models/base_data_model.py,sha256=mVMHJpEoJeH6Wly_ZIfzLfTPd39nSYpCgmtA_ft2VPs,5577
|
86
88
|
cornflow/models/case.py,sha256=GEs-xeo0bJ5qJETDnIur-2q2IyR3NSj1K0jP3Arz4Xs,9572
|
87
|
-
cornflow/models/dag.py,sha256=
|
88
|
-
cornflow/models/dag_permissions.py,sha256=
|
89
|
-
cornflow/models/execution.py,sha256=
|
89
|
+
cornflow/models/dag.py,sha256=a3X_WSuLj9nNv-c2qziQb9RSOULprTNbWm4G3k5jGAo,3021
|
90
|
+
cornflow/models/dag_permissions.py,sha256=LM2CacGyflwYbG8fbjRaaUry7pQDtvOXjfJpY9jj5EQ,1718
|
91
|
+
cornflow/models/execution.py,sha256=63wECEMQKehozS0Fnh6-bxnwe2BFCZ0qLDljnRbVxTI,6140
|
90
92
|
cornflow/models/instance.py,sha256=2E9kBKv1a8soaEAvG8X4qXQ4BVC-IWYD5WQcPmZQw00,3979
|
91
93
|
cornflow/models/main_alarms.py,sha256=9S-Ohr2kYFFWB0HomrpSdDIoUr85Eu1rt90Om_Pa8VY,1748
|
92
94
|
cornflow/models/meta_models.py,sha256=qeliGdpw0_q0GCeZzansF-09Ay5pueaT-QQPVPZ5aj4,12000
|
@@ -103,7 +105,7 @@ cornflow/schemas/common.py,sha256=QYuxWcOl4smXFZr_vL07OVgH9H50ZywCrXxycVNr1qA,47
|
|
103
105
|
cornflow/schemas/dag.py,sha256=0ENA75X9L8YqjJW6ZO1Sb4zE8OxB15_O49_nwA6eAVw,901
|
104
106
|
cornflow/schemas/example_data.py,sha256=hbE8TJakFqOweHXiA3mduNETM6FCX6xLTiQuH3EkSTc,281
|
105
107
|
cornflow/schemas/execution.py,sha256=GSRHzikVPlhxMdiKrGnTuGfen8_Lf4wSfheJwvcavTs,4718
|
106
|
-
cornflow/schemas/health.py,sha256=
|
108
|
+
cornflow/schemas/health.py,sha256=8ptyAJc23HTgb-dgtFFEtowFrlwILgkZdgezJ-JSMDY,141
|
107
109
|
cornflow/schemas/instance.py,sha256=qr4km0AlAhoNf9G1Il-pfHphT_vAiiLDpv7A9S3FKAw,1870
|
108
110
|
cornflow/schemas/main_alarms.py,sha256=cC1_Vb1dmo_vdZpZQrA7jH-hRCjVtLRy6Z2JFBlTrlo,604
|
109
111
|
cornflow/schemas/model_json.py,sha256=qUsdd1XmxhcsAmb1JB0xAsvucZoAeQhAQD_3wiY-rVo,202
|
@@ -119,9 +121,10 @@ cornflow/schemas/user_role.py,sha256=e5y6RgdZZtLqD-h2B3sa5WokI5-pT78tWw85IG34I74
|
|
119
121
|
cornflow/schemas/view.py,sha256=ctq9Y1TmjrWdyOqgDYeEx7qbbuNLKfSiNOlFTlXmpaw,429
|
120
122
|
cornflow/shared/__init__.py,sha256=1ahcBwWOsSjGI4FEm77JBQjitBdBszOncKcEMjzwGYE,29
|
121
123
|
cornflow/shared/compress.py,sha256=pohQaGs1xbH8CN6URIH6BAHA--pFq7Hmjz8oI3c3B5c,1347
|
122
|
-
cornflow/shared/const.py,sha256=
|
124
|
+
cornflow/shared/const.py,sha256=FQQQz-LekFN81YPh1KTHwyLaikddPYqQJHdzTHhvTfw,4034
|
125
|
+
cornflow/shared/databricks.py,sha256=DYhF99DJCxAcTQjB3ih4w9qc_yb_3iG1bpx-eerumvE,4731
|
123
126
|
cornflow/shared/email.py,sha256=QNDDMv86LZObkevSCyUbLQeR2UD3zWScPIr82NDzYHQ,3437
|
124
|
-
cornflow/shared/exceptions.py,sha256=
|
127
|
+
cornflow/shared/exceptions.py,sha256=bD_Eo1T2baSrgfGMKb6r9GZ1fCJbitykEEMy9wOnk4A,7033
|
125
128
|
cornflow/shared/licenses.py,sha256=Lc71Jw2NxVTFWtoXdQ9wJX_o3BDfYg1xVoehDXvnCkQ,1328
|
126
129
|
cornflow/shared/log_config.py,sha256=FM2ajjp2MB4BlFbUHklnWInT7-LLjtrqQ0mo3k_HRmE,621
|
127
130
|
cornflow/shared/query_tools.py,sha256=6yGLCWjv-I2a_ZU4A0IymyJq67fZPZdRcCGOGQQpSXg,1199
|
@@ -137,7 +140,7 @@ cornflow/tests/const.py,sha256=_5BYFGN42Xg0PXMR8UU5DBL6dYmYn5rgRBgPyptrKso,2499
|
|
137
140
|
cornflow/tests/custom_liveServer.py,sha256=I_0YNrcKIwVmRov3zCQMWwcCWkMe5V246Hpa4gS8AZE,3079
|
138
141
|
cornflow/tests/custom_test_case.py,sha256=X1j-cy9QKhF4W6_7jcJsTm-0Jn6lluq6gj-g126dFpQ,35945
|
139
142
|
cornflow/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
140
|
-
cornflow/tests/integration/test_commands.py,sha256=
|
143
|
+
cornflow/tests/integration/test_commands.py,sha256=mGiMfqIqwvRx08Al6LcHXEKPgEQEJ33EoPIZhGcReX0,697
|
141
144
|
cornflow/tests/integration/test_cornflowclient.py,sha256=ioAQmQKWW6mXVJhdF4LECZcGIOa_N0xPkFaGWGtxOO8,20963
|
142
145
|
cornflow/tests/ldap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
143
146
|
cornflow/tests/ldap/test_ldap_authentication.py,sha256=6Gu1WkF7MQmcV_10IJkpo2qEloZZ9zjpV18ANDD0HRw,4286
|
@@ -148,10 +151,10 @@ cornflow/tests/unit/test_apiview.py,sha256=ws74KU4O1VvKWsgLeFFpgDJxrTFf0cvB4NBX6
|
|
148
151
|
cornflow/tests/unit/test_application.py,sha256=ZVmTQDUOkPRxHqt6mWU9G_lQ3jJNMJR0cx7IkLMFGrU,1715
|
149
152
|
cornflow/tests/unit/test_cases.py,sha256=KnkvLsEOZXvosuE2fH8_i6iETlvWF3u04363rmmj8mM,38089
|
150
153
|
cornflow/tests/unit/test_cli.py,sha256=0l1_fYvJg-f_wA1SFgtMTzdBRy6uLhp58xWkNIR-tXQ,18602
|
151
|
-
cornflow/tests/unit/test_commands.py,sha256=
|
152
|
-
cornflow/tests/unit/test_dags.py,sha256
|
154
|
+
cornflow/tests/unit/test_commands.py,sha256=HjeR7vytN_IQhkmKFJw5St4bSEOIXuTeLPuu75ZGsaY,14724
|
155
|
+
cornflow/tests/unit/test_dags.py,sha256=Q8l0K1FoMI6Yn4tuzxfiZp2azCpIWclbUneQnkOattA,13403
|
153
156
|
cornflow/tests/unit/test_data_checks.py,sha256=6s50d1iuRTUcAYn14oEcRS39ZZ6E9ussU4YpkpYhtC4,8612
|
154
|
-
cornflow/tests/unit/test_example_data.py,sha256=
|
157
|
+
cornflow/tests/unit/test_example_data.py,sha256=rCj3wNW4OMHyLfuPdIKxBa7-eRuFsymij-9Nk27_Z3o,4128
|
155
158
|
cornflow/tests/unit/test_executions.py,sha256=_hIaiZri7Blyx4DYhBDHh-0peU1HQh66RSPqQJFveE8,17501
|
156
159
|
cornflow/tests/unit/test_generate_from_schema.py,sha256=L1EdnASbDJ8SjrX1V4WnUKKwV0sRTwVnNYnxSpyeSeQ,15376
|
157
160
|
cornflow/tests/unit/test_health.py,sha256=0E0HXMb63_Z8drbLZdxnJwtTbQyaZS9ZEHut6qsDbh8,1033
|
@@ -168,9 +171,9 @@ cornflow/tests/unit/test_sign_up.py,sha256=-i6VO9z1FwqRHFvaSrpWAzOZx6qa8mHUEmmsj
|
|
168
171
|
cornflow/tests/unit/test_tables.py,sha256=dY55YgaCkyqwJnqn0LbZHNeXBoL4ZxXWwKkCoTF4WVE,8947
|
169
172
|
cornflow/tests/unit/test_token.py,sha256=OEVPgG8swSMkUbuGJGfGF5Z27utMLICn1eIyma1cM9E,3760
|
170
173
|
cornflow/tests/unit/test_users.py,sha256=WfaMcybPpR7rspXyvzHGgw25p751hMPAV0DOp_caSPM,22430
|
171
|
-
cornflow/tests/unit/tools.py,sha256=
|
172
|
-
cornflow-
|
173
|
-
cornflow-
|
174
|
-
cornflow-
|
175
|
-
cornflow-
|
176
|
-
cornflow-
|
174
|
+
cornflow/tests/unit/tools.py,sha256=BCAm_KGVgZO-CCb_rkaZlbK4SID_F2ab8FiBJzGwKtc,587
|
175
|
+
cornflow-2.0.0a7.dist-info/METADATA,sha256=5hDnLkcu17S91I6f3jFMFkxf9DkQG6AAIjeS8k542J0,9536
|
176
|
+
cornflow-2.0.0a7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
177
|
+
cornflow-2.0.0a7.dist-info/entry_points.txt,sha256=q9cPKAFBsmHkERCqQ2JcOTM-tVBLHTl-DGxwCXowAWM,46
|
178
|
+
cornflow-2.0.0a7.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
|
179
|
+
cornflow-2.0.0a7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|