qctrl-workflow-client 2.4.1__tar.gz → 3.1.0__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.
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/PKG-INFO +2 -2
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/pyproject.toml +3 -3
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/__init__.py +1 -1
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/defaults.py +13 -1
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/functions.py +67 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/api.py +24 -9
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/LICENSE +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/README.md +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/globals.py +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/products.py +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/__init__.py +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/base.py +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/local.py +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/settings.py +0 -0
- {qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qctrl-workflow-client
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Q-CTRL Client
|
|
5
5
|
Home-page: https://q-ctrl.com
|
|
6
6
|
License: https://q-ctrl.com/terms
|
|
@@ -33,7 +33,7 @@ Requires-Dist: importlib-metadata (>=6.8.0,<7.0.0)
|
|
|
33
33
|
Requires-Dist: packaging (>=23.1.0,<24.0.0)
|
|
34
34
|
Requires-Dist: qctrl-client (>=9.1.0,<10.0.0)
|
|
35
35
|
Requires-Dist: qctrl-commons (>=22.0.0,<23.0.0)
|
|
36
|
-
Requires-Dist: requests (>=2.
|
|
36
|
+
Requires-Dist: requests (>=2.32.0,<3.0.0)
|
|
37
37
|
Requires-Dist: tenacity (>=8.1.0,<9.0.0)
|
|
38
38
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
|
39
39
|
Project-URL: Documentation, https://docs.q-ctrl.com
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "qctrl-workflow-client"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.1.0"
|
|
4
4
|
description = "Q-CTRL Client"
|
|
5
5
|
license = "https://q-ctrl.com/terms"
|
|
6
6
|
authors = ["Q-CTRL <support@q-ctrl.com>"]
|
|
@@ -79,13 +79,13 @@ tomli = "^2.0.1"
|
|
|
79
79
|
importlib-metadata = "^6.8.0"
|
|
80
80
|
gql = "^3.4.0"
|
|
81
81
|
tenacity = "^8.1.0"
|
|
82
|
-
requests = "^2.
|
|
82
|
+
requests = "^2.32.0"
|
|
83
83
|
packaging = "^23.1.0"
|
|
84
84
|
qctrl-commons = { version = "^22.0.0", source = "PyPI" }
|
|
85
85
|
qctrl-client = { version = "^9.1.0", source = "PyPI" }
|
|
86
86
|
|
|
87
87
|
[tool.poetry.group.dev.dependencies]
|
|
88
|
-
black = "^
|
|
88
|
+
black = "^24.3.0"
|
|
89
89
|
isort = "^5.12.0"
|
|
90
90
|
pre-commit = "^3.3.3"
|
|
91
91
|
pylint = "^2.14.4"
|
|
@@ -24,8 +24,10 @@ from qctrlclient.client import (
|
|
|
24
24
|
GraphQLClient,
|
|
25
25
|
GraphQLClientError,
|
|
26
26
|
)
|
|
27
|
+
from qctrlcommons.utils import generate_user_agent
|
|
27
28
|
|
|
28
29
|
from .globals import global_value
|
|
30
|
+
from .utils import get_installed_version
|
|
29
31
|
|
|
30
32
|
_DEFAULT_API_URL = "https://federation-service.q-ctrl.com"
|
|
31
33
|
_DEFAULT_OIDC_URL = "https://id.q-ctrl.com"
|
|
@@ -58,6 +60,7 @@ def get_default_cli_auth() -> CliAuth:
|
|
|
58
60
|
|
|
59
61
|
def get_authenticated_client_for_product(
|
|
60
62
|
product_access_required: str,
|
|
63
|
+
package_name: str,
|
|
61
64
|
api_url: Optional[str] = None,
|
|
62
65
|
auth: Optional[BaseAuth | str] = None,
|
|
63
66
|
invalid_access_error_message: Optional[str] = None,
|
|
@@ -70,6 +73,8 @@ def get_authenticated_client_for_product(
|
|
|
70
73
|
----------
|
|
71
74
|
product_access_required : str
|
|
72
75
|
The product access required for the user to use the CLI.
|
|
76
|
+
package_name : str
|
|
77
|
+
The package name to include in the User-Agent header.
|
|
73
78
|
api_url : str, optional
|
|
74
79
|
The API URL to use. If not provided, the default API URL will be used.
|
|
75
80
|
auth : BaseAuth or str, optional
|
|
@@ -82,8 +87,15 @@ def get_authenticated_client_for_product(
|
|
|
82
87
|
if isinstance(auth, str):
|
|
83
88
|
auth = CliAuth(auth)
|
|
84
89
|
|
|
90
|
+
headers = {
|
|
91
|
+
"User-Agent": generate_user_agent(
|
|
92
|
+
package_name, get_installed_version(package_name)
|
|
93
|
+
),
|
|
94
|
+
}
|
|
85
95
|
client = GraphQLClient(
|
|
86
|
-
api_url or get_default_oidc_url(),
|
|
96
|
+
url=api_url or get_default_oidc_url(),
|
|
97
|
+
headers=headers,
|
|
98
|
+
auth=auth or get_default_cli_auth(),
|
|
87
99
|
)
|
|
88
100
|
|
|
89
101
|
try:
|
{qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/functions.py
RENAMED
|
@@ -20,6 +20,8 @@ from typing import (
|
|
|
20
20
|
)
|
|
21
21
|
from warnings import warn
|
|
22
22
|
|
|
23
|
+
from qctrlcommons.exceptions import QctrlArgumentsValueError
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
def core_workflow(
|
|
25
27
|
get_config: Callable, workflow: str, formatter: Optional[Callable] = None
|
|
@@ -61,6 +63,71 @@ def core_workflow(
|
|
|
61
63
|
# the function
|
|
62
64
|
config = get_config()
|
|
63
65
|
router = config.get_router()
|
|
66
|
+
|
|
67
|
+
router.set_async_state(kwargs.pop("is_async", False))
|
|
68
|
+
|
|
69
|
+
data = func(*args, **kwargs)
|
|
70
|
+
result = router(workflow, data)
|
|
71
|
+
|
|
72
|
+
if formatter:
|
|
73
|
+
result = formatter(result)
|
|
74
|
+
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
return customized_decorator
|
|
78
|
+
|
|
79
|
+
return decorator
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def async_core_workflow(
|
|
83
|
+
get_config: Callable, workflow: str, formatter: Optional[Callable] = None
|
|
84
|
+
):
|
|
85
|
+
"""
|
|
86
|
+
Decorator for a function which will execute asynchronously workflow.
|
|
87
|
+
The decorated function should return the data to be used
|
|
88
|
+
during async workflow execution. When being used in a client
|
|
89
|
+
package, it is recommended to use a partial to provide
|
|
90
|
+
a default value for `get_config` e.g.
|
|
91
|
+
|
|
92
|
+
async_fire_opal_workflow = partial(
|
|
93
|
+
async_core_workflow,
|
|
94
|
+
get_fire_opal_config
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
@async_fire_opal_workflow("execute")
|
|
98
|
+
def execute(...):
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
get_config : Callable
|
|
103
|
+
Returns a `CoreClientSettings` instance. The configured
|
|
104
|
+
router will be used to execute the workflow.
|
|
105
|
+
workflow : str
|
|
106
|
+
The registered name of the workflow to be executed.
|
|
107
|
+
formatter : Callable, optional
|
|
108
|
+
Optional callable which can be used to format the workflow
|
|
109
|
+
result. The callable should accept exactly one argument
|
|
110
|
+
which is the raw result fo the workflow. If used, the
|
|
111
|
+
decorated function will return the result of this callable.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def decorator(func: Callable):
|
|
115
|
+
@wraps(func)
|
|
116
|
+
def customized_decorator(*args, **kwargs):
|
|
117
|
+
# router is instantiated before function is called
|
|
118
|
+
# so any alteration to settings is visible within
|
|
119
|
+
# the function
|
|
120
|
+
config = get_config()
|
|
121
|
+
router = config.get_router()
|
|
122
|
+
|
|
123
|
+
# Set the async state to True
|
|
124
|
+
router.set_async_state(True)
|
|
125
|
+
if "is_async" in kwargs:
|
|
126
|
+
raise QctrlArgumentsValueError(
|
|
127
|
+
description="The asynchronous state of this function cannot be altered.",
|
|
128
|
+
arguments={"is_async": kwargs.get("is_async")},
|
|
129
|
+
)
|
|
130
|
+
|
|
64
131
|
data = func(*args, **kwargs)
|
|
65
132
|
result = router(workflow, data)
|
|
66
133
|
|
{qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/api.py
RENAMED
|
@@ -199,6 +199,13 @@ class ApiRouter(BaseRouter):
|
|
|
199
199
|
self._settings = settings
|
|
200
200
|
self._validate()
|
|
201
201
|
self._parallel_task_collector = None
|
|
202
|
+
self._async = False
|
|
203
|
+
|
|
204
|
+
def set_async_state(self, is_async: bool):
|
|
205
|
+
"""
|
|
206
|
+
Toggle asynchronous state of workflow execution.
|
|
207
|
+
"""
|
|
208
|
+
self._async = is_async
|
|
202
209
|
|
|
203
210
|
def _validate(self):
|
|
204
211
|
"""
|
|
@@ -384,6 +391,9 @@ class ApiRouter(BaseRouter):
|
|
|
384
391
|
errors=action_data["errors"],
|
|
385
392
|
)
|
|
386
393
|
|
|
394
|
+
if self._async:
|
|
395
|
+
return {"async_result": action}
|
|
396
|
+
|
|
387
397
|
if self._parallel_task_collector is not None:
|
|
388
398
|
async_result = {"async_result": action}
|
|
389
399
|
self._parallel_task_collector.add(async_result)
|
|
@@ -404,7 +414,6 @@ class ApiRouter(BaseRouter):
|
|
|
404
414
|
|
|
405
415
|
return {
|
|
406
416
|
"package_versions": package_versions,
|
|
407
|
-
"organization_slug": self._settings.organization,
|
|
408
417
|
"organization": self._get_configured_organization().to_dict(),
|
|
409
418
|
}
|
|
410
419
|
|
|
@@ -429,16 +438,11 @@ class ApiRouter(BaseRouter):
|
|
|
429
438
|
message = warning_data["message"]
|
|
430
439
|
warn(Warning(message))
|
|
431
440
|
|
|
432
|
-
|
|
433
|
-
wait=_POLL_WAIT_CHAIN,
|
|
434
|
-
retry=retry_if_result(lambda action: not action.is_finished()),
|
|
435
|
-
)
|
|
436
|
-
def _poll_for_completion(self, action: Action) -> Action:
|
|
441
|
+
def update_action_status(self, action: Action) -> Action:
|
|
437
442
|
"""
|
|
438
|
-
|
|
439
|
-
|
|
443
|
+
Update the action status. When finished, an updated
|
|
444
|
+
`Action` object is returned.
|
|
440
445
|
"""
|
|
441
|
-
|
|
442
446
|
_query = gql.gql(
|
|
443
447
|
"""
|
|
444
448
|
query($modelId: String!) {
|
|
@@ -468,6 +472,17 @@ class ApiRouter(BaseRouter):
|
|
|
468
472
|
|
|
469
473
|
return action
|
|
470
474
|
|
|
475
|
+
@retry(
|
|
476
|
+
wait=_POLL_WAIT_CHAIN,
|
|
477
|
+
retry=retry_if_result(lambda action: not action.is_finished()),
|
|
478
|
+
)
|
|
479
|
+
def _poll_for_completion(self, action: Action) -> Action:
|
|
480
|
+
"""
|
|
481
|
+
Poll the API waiting for the action to be finished.
|
|
482
|
+
When finished, an updated `Action` object is returned.
|
|
483
|
+
"""
|
|
484
|
+
return self.update_action_status(action)
|
|
485
|
+
|
|
471
486
|
def get_result(self, action: Action) -> DecodedResult:
|
|
472
487
|
"""
|
|
473
488
|
Return the result of the action.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/__init__.py
RENAMED
|
File without changes
|
{qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/base.py
RENAMED
|
File without changes
|
{qctrl_workflow_client-2.4.1 → qctrl_workflow_client-3.1.0}/qctrlworkflowclient/router/local.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|