cuiman 0.1.1__tar.gz → 0.2.0.dev1__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.
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/.gitignore +1 -1
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/PKG-INFO +4 -7
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/__init__.py +3 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/__init__.py +3 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/async_client.py +5 -3
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/async_client_mixin.py +19 -4
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/client.py +6 -5
- cuiman-0.2.0.dev1/cuiman/api/client_app_mixin.py +104 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/client_mixin.py +18 -4
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/config.py +0 -134
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/exceptions.py +4 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/ishell.py +16 -7
- cuiman-0.2.0.dev1/cuiman/api/jobs.py +304 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/__init__.py +2 -0
- cuiman-0.2.0.dev1/cuiman/api/opener/impl/_image.py +48 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/openers.py +12 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/opener.py +15 -13
- cuiman-0.2.0.dev1/cuiman/api/service_client.py +372 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/args.py +21 -11
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/httpx.py +11 -6
- {cuiman-0.1.1/cuiman/gui/panels/jobs_panel → cuiman-0.2.0.dev1/cuiman/app}/__init__.py +4 -5
- cuiman-0.2.0.dev1/cuiman/app/app.py +124 -0
- cuiman-0.2.0.dev1/cuiman/app/display.py +110 -0
- cuiman-0.2.0.dev1/cuiman/app/dist/assets/index-CEQarI7F.js +84 -0
- cuiman-0.2.0.dev1/cuiman/app/dist/assets/index-DKE7nU5k.css +1 -0
- cuiman-0.2.0.dev1/cuiman/app/dist/eozilla-small.svg +5 -0
- cuiman-0.2.0.dev1/cuiman/app/dist/eozilla.svg +3 -0
- cuiman-0.2.0.dev1/cuiman/app/dist/index.html +14 -0
- cuiman-0.2.0.dev1/cuiman/app/dist/privacy.md +23 -0
- cuiman-0.2.0.dev1/cuiman/app/serve.py +68 -0
- cuiman-0.2.0.dev1/cuiman/app/service.py +96 -0
- cuiman-0.2.0.dev1/cuiman/app/url.py +77 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/cli.py +95 -21
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/config.py +22 -19
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/pyproject.toml +5 -7
- cuiman-0.1.1/cuiman/gui/__init__.py +0 -11
- cuiman-0.1.1/cuiman/gui/client.py +0 -134
- cuiman-0.1.1/cuiman/gui/ipy_helper.py +0 -63
- cuiman-0.1.1/cuiman/gui/jobs_event_bus.py +0 -76
- cuiman-0.1.1/cuiman/gui/jobs_observer.py +0 -32
- cuiman-0.1.1/cuiman/gui/panels/__init__.py +0 -15
- cuiman-0.1.1/cuiman/gui/panels/job_info_panel.py +0 -113
- cuiman-0.1.1/cuiman/gui/panels/jobs_panel/view.py +0 -99
- cuiman-0.1.1/cuiman/gui/panels/jobs_panel/viewmodel.py +0 -196
- cuiman-0.1.1/cuiman/gui/panels/main_panel/__init__.py +0 -11
- cuiman-0.1.1/cuiman/gui/panels/main_panel/settings.py +0 -21
- cuiman-0.1.1/cuiman/gui/panels/main_panel/view.py +0 -417
- cuiman-0.1.1/cuiman/gui/panels/main_panel/viewmodel.py +0 -307
- cuiman-0.1.1/cuiman/gui/panels/util.py +0 -259
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/LICENSE +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/README.md +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/__init__.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/config.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/login.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/login_async.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/defaults.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/__init__.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/context.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/errors.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/_gpd.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/_pd.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/_xr.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/base.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/registry.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/__init__.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/transport.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/__init__.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/client.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/output.py +0 -0
- {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cuiman
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0.dev1
|
|
4
4
|
Summary: Provides a client Python API, GUI, and CLI for servers compliant with OGC API - Processes
|
|
5
5
|
Project-URL: Documentation, https://eo-tools.github.io/eozilla
|
|
6
6
|
Project-URL: Repository, https://github.com/eo-tools/eozilla
|
|
@@ -27,16 +27,13 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
27
27
|
Classifier: Topic :: Software Development
|
|
28
28
|
Classifier: Typing :: Typed
|
|
29
29
|
Requires-Python: >=3.10
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist: gavicore>=0.1.1
|
|
30
|
+
Requires-Dist: gavicore>=0.2.0.dev1
|
|
32
31
|
Requires-Dist: httpx
|
|
33
|
-
Requires-Dist: ipyfilechooser
|
|
34
|
-
Requires-Dist: ipyleaflet
|
|
35
|
-
Requires-Dist: panel
|
|
36
32
|
Requires-Dist: pydantic
|
|
37
33
|
Requires-Dist: pydantic-settings
|
|
38
34
|
Requires-Dist: pyyaml
|
|
39
|
-
Requires-Dist:
|
|
35
|
+
Requires-Dist: remotestate<1,>=0.3.1
|
|
36
|
+
Requires-Dist: typer<1,>=0.26.0
|
|
40
37
|
Requires-Dist: uri-template
|
|
41
38
|
Description-Content-Type: text/markdown
|
|
42
39
|
|
|
@@ -8,6 +8,7 @@ from .api.async_client import AsyncClient
|
|
|
8
8
|
from .api.client import Client
|
|
9
9
|
from .api.config import ClientConfig
|
|
10
10
|
from .api.exceptions import ClientError
|
|
11
|
+
from .api.jobs import JobMonitor, JobOptions
|
|
11
12
|
|
|
12
13
|
__version__ = version("cuiman")
|
|
13
14
|
|
|
@@ -16,5 +17,7 @@ __all__ = [
|
|
|
16
17
|
"Client",
|
|
17
18
|
"ClientConfig",
|
|
18
19
|
"ClientError",
|
|
20
|
+
"JobMonitor",
|
|
21
|
+
"JobOptions",
|
|
19
22
|
"__version__",
|
|
20
23
|
]
|
|
@@ -6,10 +6,13 @@ from .async_client import AsyncClient
|
|
|
6
6
|
from .client import Client
|
|
7
7
|
from .config import ClientConfig
|
|
8
8
|
from .exceptions import ClientError
|
|
9
|
+
from .jobs import JobMonitor, JobOptions
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"AsyncClient",
|
|
12
13
|
"Client",
|
|
13
14
|
"ClientConfig",
|
|
14
15
|
"ClientError",
|
|
16
|
+
"JobMonitor",
|
|
17
|
+
"JobOptions",
|
|
15
18
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by gen_client.py:
|
|
2
2
|
# filename: async_client.py:
|
|
3
|
-
# timestamp:
|
|
3
|
+
# timestamp: 2026-07-15T17:16:03.217600
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
from typing import Any, Optional
|
|
@@ -18,13 +18,14 @@ from gavicore.models import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
from .async_client_mixin import AsyncClientMixin
|
|
21
|
+
from .client_app_mixin import ClientAppMixin
|
|
21
22
|
from .config import ClientConfig
|
|
22
23
|
from .ishell import has_ishell as _ # noqa F401
|
|
23
24
|
from .transport import AsyncTransport, TransportArgs
|
|
24
25
|
from .transport.httpx import HttpxTransport
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
class AsyncClient(AsyncClientMixin):
|
|
28
|
+
class AsyncClient(ClientAppMixin, AsyncClientMixin):
|
|
28
29
|
"""
|
|
29
30
|
The client API for the web service (asynchronous mode).
|
|
30
31
|
|
|
@@ -56,8 +57,9 @@ class AsyncClient(AsyncClientMixin):
|
|
|
56
57
|
raise ValueError("Required setting 'api_url' not configured")
|
|
57
58
|
self._transport = (
|
|
58
59
|
HttpxTransport(
|
|
59
|
-
api_url=self._config.api_url,
|
|
60
|
+
api_url=f"{self._config.api_url.rstrip('/')}/",
|
|
60
61
|
headers=self._config.auth_headers,
|
|
62
|
+
return_type_map=self._config.return_type_map,
|
|
61
63
|
async_token_refresher=self._config._make_async_token_refresher(),
|
|
62
64
|
debug=_debug,
|
|
63
65
|
)
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
# https://opensource.org/license/apache-2-0.
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import warnings
|
|
6
7
|
from abc import ABC, abstractmethod
|
|
7
|
-
from typing import Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
8
9
|
|
|
9
10
|
from gavicore.models import JobInfo, JobResults, JobStatus, ProcessDescription
|
|
10
11
|
from gavicore.util.request import ExecutionRequest
|
|
@@ -14,9 +15,14 @@ from .defaults import (
|
|
|
14
15
|
DEFAULT_OPEN_JOB_JOB_POLL_INTERVAL,
|
|
15
16
|
DEFAULT_OPEN_JOB_RESULT_TIMEOUT,
|
|
16
17
|
)
|
|
18
|
+
from .exceptions import ClientError, ClientWarning
|
|
17
19
|
from .opener import JobResultOpenContext, JobResultStatusError
|
|
18
20
|
from .opener.opener import open_job_result
|
|
19
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
20
26
|
# -----------------------------------------------------
|
|
21
27
|
# IMPORTANT: Sync changes here with ClientMixin!
|
|
22
28
|
# -----------------------------------------------------
|
|
@@ -121,9 +127,18 @@ class AsyncClientMixin(ABC):
|
|
|
121
127
|
if job_info.status != JobStatus.successful:
|
|
122
128
|
raise JobResultStatusError(job_info)
|
|
123
129
|
job_results = await self.get_job_results(job_id)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
process_id = job_info.processID
|
|
131
|
+
process_description: ProcessDescription | None = None
|
|
132
|
+
if process_id:
|
|
133
|
+
try:
|
|
134
|
+
process_description = await self.get_process(process_id)
|
|
135
|
+
except ClientError:
|
|
136
|
+
warnings.warn(
|
|
137
|
+
"An error occurred while getting process "
|
|
138
|
+
f"description for process ID {process_id!r}",
|
|
139
|
+
category=ClientWarning,
|
|
140
|
+
stacklevel=2,
|
|
141
|
+
)
|
|
127
142
|
ctx = JobResultOpenContext(
|
|
128
143
|
config=self.config,
|
|
129
144
|
job_id=job_id,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by gen_client.py:
|
|
2
2
|
# filename: client.py:
|
|
3
|
-
# timestamp:
|
|
3
|
+
# timestamp: 2026-07-15T17:16:02.527617
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
from typing import Any, Optional
|
|
@@ -17,6 +17,7 @@ from gavicore.models import (
|
|
|
17
17
|
ProcessRequest,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
+
from .client_app_mixin import ClientAppMixin
|
|
20
21
|
from .client_mixin import ClientMixin
|
|
21
22
|
from .config import ClientConfig
|
|
22
23
|
from .ishell import has_ishell as _ # noqa F401
|
|
@@ -24,7 +25,7 @@ from .transport import Transport, TransportArgs
|
|
|
24
25
|
from .transport.httpx import HttpxTransport
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
class Client(ClientMixin):
|
|
28
|
+
class Client(ClientAppMixin, ClientMixin):
|
|
28
29
|
"""
|
|
29
30
|
The client API for the web service (synchronous mode).
|
|
30
31
|
|
|
@@ -32,8 +33,8 @@ class Client(ClientMixin):
|
|
|
32
33
|
config: Optional client configuration object. If given,
|
|
33
34
|
other configuration arguments are ignored.
|
|
34
35
|
config_path: Optional path of the configuration file to be loaded
|
|
35
|
-
api_url: The service URL of the OGC API - Processes.
|
|
36
36
|
config_kwargs: Configuration settings as keyword arguments.
|
|
37
|
+
api_url: The service URL of the OGC API - Processes.
|
|
37
38
|
"""
|
|
38
39
|
|
|
39
40
|
def __init__(
|
|
@@ -44,7 +45,7 @@ class Client(ClientMixin):
|
|
|
44
45
|
api_url: Optional[str] = None,
|
|
45
46
|
_debug: bool = False,
|
|
46
47
|
_transport: Optional[Transport] = None,
|
|
47
|
-
**config_kwargs
|
|
48
|
+
**config_kwargs,
|
|
48
49
|
):
|
|
49
50
|
self._config = ClientConfig.create(
|
|
50
51
|
config=config,
|
|
@@ -56,7 +57,7 @@ class Client(ClientMixin):
|
|
|
56
57
|
raise ValueError("Required setting 'api_url' not configured")
|
|
57
58
|
self._transport = (
|
|
58
59
|
HttpxTransport(
|
|
59
|
-
api_url=self._config.api_url,
|
|
60
|
+
api_url=f"{self._config.api_url.rstrip('/')}/",
|
|
60
61
|
headers=self._config.auth_headers,
|
|
61
62
|
return_type_map=self._config.return_type_map,
|
|
62
63
|
token_refresher=self._config._maybe_make_token_refresher(),
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026 by the Eozilla team and contributors
|
|
2
|
+
# Permissions are hereby granted under the terms of the Apache 2.0 License:
|
|
3
|
+
# https://opensource.org/license/apache-2-0.
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import TYPE_CHECKING, Literal
|
|
7
|
+
|
|
8
|
+
from cuiman.api.ishell import has_ishell
|
|
9
|
+
|
|
10
|
+
from .config import ClientConfig
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from cuiman.app import App
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ClientAppMixin(ABC):
|
|
17
|
+
"""
|
|
18
|
+
Extra methods for the Client App.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def config(self) -> ClientConfig:
|
|
24
|
+
"""Will be overridden by the actual client class."""
|
|
25
|
+
|
|
26
|
+
def show_app(
|
|
27
|
+
self,
|
|
28
|
+
*,
|
|
29
|
+
compact: bool | None = None,
|
|
30
|
+
debug: bool = False,
|
|
31
|
+
scheme: Literal["dark", "light", "auto"] = "auto",
|
|
32
|
+
width: int | str = "100%",
|
|
33
|
+
height: int | str = 600,
|
|
34
|
+
display: Literal["browser", "notebook", "auto"] = "auto",
|
|
35
|
+
) -> "App":
|
|
36
|
+
"""
|
|
37
|
+
Show the Cuiman app for this client.
|
|
38
|
+
|
|
39
|
+
The app connects to this client's API configuration, renders the app GUI,
|
|
40
|
+
and returns an object that provides the serve result and a shared
|
|
41
|
+
[app state][cuiman.app.AppState], which you can interact with.
|
|
42
|
+
|
|
43
|
+
The app state currently only manages the process requests being
|
|
44
|
+
edited and executed by a user. The requests are a mapping from process IDs
|
|
45
|
+
to process requests.
|
|
46
|
+
|
|
47
|
+
Display the app and get the app instance:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
app = client.show_app()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
You can get and set process requests using the methods
|
|
54
|
+
|
|
55
|
+
- ``app.get_process_request(process_id)``
|
|
56
|
+
- ``app.set_process_request(process_id, process_request)``
|
|
57
|
+
|
|
58
|
+
where ``process_id`` is the process ID and ``process_request`` is a dict
|
|
59
|
+
or a ``ProcessRequest`` object that comprises basically two attributes:
|
|
60
|
+
|
|
61
|
+
- ``inputs``: a mapping from input name to an input's value.
|
|
62
|
+
- ``outputs``: the outputs to be generated. Just used to detect
|
|
63
|
+
whether an output is included or now.
|
|
64
|
+
|
|
65
|
+
Another convenient way to work with the nested app state is the
|
|
66
|
+
``process_requests`` property:
|
|
67
|
+
|
|
68
|
+
- ``app.process_requests.my_process.inputs.threshold = 0.75``
|
|
69
|
+
- ``app.process_requests.my_process = {"inputs": {...}}``
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
compact: Compact mode. Defaults to True, if ``display`` is "notebook".
|
|
73
|
+
debug: Enable app debug mode.
|
|
74
|
+
scheme: Color scheme to use in the app. ``"auto"`` follows the
|
|
75
|
+
surrounding notebook theme when the app is embedded.
|
|
76
|
+
width: Width of the notebook iframe.
|
|
77
|
+
height: Height of the notebook iframe.
|
|
78
|
+
display: Where to show the app. ``"auto"`` embeds it in notebooks
|
|
79
|
+
and opens it in a browser otherwise.
|
|
80
|
+
Return:
|
|
81
|
+
An ``App`` instance.
|
|
82
|
+
"""
|
|
83
|
+
from cuiman.app import App, serve
|
|
84
|
+
|
|
85
|
+
display_ = (
|
|
86
|
+
("notebook" if has_ishell else "browser") if display == "auto" else display
|
|
87
|
+
)
|
|
88
|
+
compact_ = compact if isinstance(compact, bool) else display_ == "notebook"
|
|
89
|
+
|
|
90
|
+
remote_store = App.create_remote_store()
|
|
91
|
+
|
|
92
|
+
# noinspection PyTypeChecker
|
|
93
|
+
serve_result = serve(
|
|
94
|
+
self.config,
|
|
95
|
+
remote_store,
|
|
96
|
+
compact=compact_,
|
|
97
|
+
debug=debug,
|
|
98
|
+
scheme=scheme,
|
|
99
|
+
width=width,
|
|
100
|
+
height=height,
|
|
101
|
+
display=display_,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return App(remote_store, serve_result)
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
# https://opensource.org/license/apache-2-0.
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
|
+
import warnings
|
|
6
7
|
from abc import ABC, abstractmethod
|
|
7
|
-
from typing import Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
8
9
|
|
|
9
10
|
from gavicore.models import JobInfo, JobResults, JobStatus, ProcessDescription
|
|
10
11
|
from gavicore.util.request import ExecutionRequest
|
|
@@ -15,9 +16,13 @@ from .defaults import (
|
|
|
15
16
|
DEFAULT_OPEN_JOB_JOB_POLL_INTERVAL,
|
|
16
17
|
DEFAULT_OPEN_JOB_RESULT_TIMEOUT,
|
|
17
18
|
)
|
|
19
|
+
from .exceptions import ClientError, ClientWarning
|
|
18
20
|
from .opener import JobResultOpenContext, JobResultStatusError
|
|
19
21
|
from .opener.opener import open_job_result
|
|
20
22
|
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
pass
|
|
25
|
+
|
|
21
26
|
# -----------------------------------------------------
|
|
22
27
|
# IMPORTANT: Sync changes here with AsyncClientMixin!
|
|
23
28
|
# -----------------------------------------------------
|
|
@@ -122,9 +127,18 @@ class ClientMixin(ABC):
|
|
|
122
127
|
if job_info.status != JobStatus.successful:
|
|
123
128
|
raise JobResultStatusError(job_info)
|
|
124
129
|
job_results = self.get_job_results(job_id)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
process_id = job_info.processID
|
|
131
|
+
process_description: ProcessDescription | None = None
|
|
132
|
+
if process_id:
|
|
133
|
+
try:
|
|
134
|
+
process_description = self.get_process(process_id)
|
|
135
|
+
except ClientError:
|
|
136
|
+
warnings.warn(
|
|
137
|
+
"An error occurred while getting process "
|
|
138
|
+
f"description for process ID {process_id!r}",
|
|
139
|
+
category=ClientWarning,
|
|
140
|
+
stacklevel=2,
|
|
141
|
+
)
|
|
128
142
|
ctx = JobResultOpenContext(
|
|
129
143
|
config=self.config,
|
|
130
144
|
job_id=job_id,
|
|
@@ -18,7 +18,6 @@ from pydantic import Field, HttpUrl, field_validator
|
|
|
18
18
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
19
19
|
|
|
20
20
|
from gavicore.models import InputDescription, ProcessDescription, ProcessSummary
|
|
21
|
-
from gavicore.ui import FieldFactoryRegistry, FieldMeta
|
|
22
21
|
|
|
23
22
|
from .auth import AuthConfig
|
|
24
23
|
from .defaults import DEFAULT_API_URL
|
|
@@ -154,112 +153,6 @@ class ClientConfig(AuthConfig, BaseSettings):
|
|
|
154
153
|
def validate_api_url(cls, v: str | None) -> str | None:
|
|
155
154
|
return None if v is None or v == "" else str(HttpUrl(v))
|
|
156
155
|
|
|
157
|
-
# noinspection PyUnusedLocal
|
|
158
|
-
@classmethod
|
|
159
|
-
def accept_process(
|
|
160
|
-
cls, process_summary: ProcessSummary, **filter_kwargs: Any
|
|
161
|
-
) -> bool:
|
|
162
|
-
"""
|
|
163
|
-
Predicate function that is used to filter the list of processes.
|
|
164
|
-
The function is intended to be overridden by subclasses in order to allow
|
|
165
|
-
for evaluating the given `process_summary` in an application-specific way.
|
|
166
|
-
This includes the using custom fields in the given
|
|
167
|
-
[ProcessSummary][gavicore.models.ProcessSummary] instance.
|
|
168
|
-
|
|
169
|
-
Applications may use the [extend_model()][gavicore.util.model.extend_model]
|
|
170
|
-
function to enhance existing model classes by their custom fields.
|
|
171
|
-
|
|
172
|
-
The default implementation unconditionally returns `True`.
|
|
173
|
-
|
|
174
|
-
Args:
|
|
175
|
-
process_summary: A process summary.
|
|
176
|
-
filter_kwargs: Implementation specific arguments passed
|
|
177
|
-
by a user of this class.
|
|
178
|
-
|
|
179
|
-
Returns:
|
|
180
|
-
`True` to accept the given process, otherwise `False`.
|
|
181
|
-
"""
|
|
182
|
-
return True
|
|
183
|
-
|
|
184
|
-
# noinspection PyUnusedLocal
|
|
185
|
-
@classmethod
|
|
186
|
-
def accept_input(
|
|
187
|
-
cls,
|
|
188
|
-
process_description: ProcessDescription,
|
|
189
|
-
input_name: str,
|
|
190
|
-
input_description: InputDescription,
|
|
191
|
-
**filter_kwargs: Any,
|
|
192
|
-
) -> bool:
|
|
193
|
-
"""
|
|
194
|
-
Predicate function that is used to filter the list of inputs of a process.
|
|
195
|
-
The function is intended to be overridden by subclasses in order to allow
|
|
196
|
-
for evaluating the given `input_description` in an application-specific way.
|
|
197
|
-
This includes the using custom fields in the given
|
|
198
|
-
[InputDescription][gavicore.models.InputDescription] instance.
|
|
199
|
-
|
|
200
|
-
Applications may use the [extend_model()][gavicore.util.model.extend_model]
|
|
201
|
-
function to enhance existing model classes by their custom fields.
|
|
202
|
-
|
|
203
|
-
The default implementation unconditionally returns `True`.
|
|
204
|
-
|
|
205
|
-
Args:
|
|
206
|
-
process_description: The process description.
|
|
207
|
-
input_name: The input's name.
|
|
208
|
-
input_description: A description of an
|
|
209
|
-
input of the given `process_description`.
|
|
210
|
-
filter_kwargs: Implementation specific arguments passed
|
|
211
|
-
by a user of this class.
|
|
212
|
-
|
|
213
|
-
Returns:
|
|
214
|
-
`True` to accept the given input, otherwise `False`.
|
|
215
|
-
"""
|
|
216
|
-
return True
|
|
217
|
-
|
|
218
|
-
# noinspection PyUnusedLocal
|
|
219
|
-
@classmethod
|
|
220
|
-
def is_advanced_input(
|
|
221
|
-
cls,
|
|
222
|
-
process_description: ProcessDescription,
|
|
223
|
-
input_name: str,
|
|
224
|
-
input_description: InputDescription,
|
|
225
|
-
) -> bool:
|
|
226
|
-
"""
|
|
227
|
-
Experimental method, do not use!
|
|
228
|
-
|
|
229
|
-
Designed to be overridden by a custom `ClientConfig` class
|
|
230
|
-
from which an instance will be assigned to `ClientConfig.default_config`
|
|
231
|
-
to become effective.
|
|
232
|
-
|
|
233
|
-
The default implementations checks if the given `input_description`
|
|
234
|
-
has `additionalParameters`, and if so, if a parameter with name
|
|
235
|
-
`"level"` has value `["advanced"]` (a list!).
|
|
236
|
-
|
|
237
|
-
Args:
|
|
238
|
-
process_description: The process description.
|
|
239
|
-
input_name: The input's name.
|
|
240
|
-
input_description: A description of an
|
|
241
|
-
input of the given `process_description`.
|
|
242
|
-
|
|
243
|
-
Returns:
|
|
244
|
-
`True` if the input is advanced
|
|
245
|
-
(e.g. for advanced process users only).
|
|
246
|
-
"""
|
|
247
|
-
# The following approach uses additionalParameters but its use
|
|
248
|
-
# is discouraged. (We receive such metadata currently from
|
|
249
|
-
# the TAO Process API).
|
|
250
|
-
additional_parameters = input_description.additionalParameters
|
|
251
|
-
if additional_parameters:
|
|
252
|
-
parameters = additional_parameters.parameters
|
|
253
|
-
if parameters:
|
|
254
|
-
for p in parameters:
|
|
255
|
-
if p.name == "level" and p.value == ["advanced"]:
|
|
256
|
-
return True
|
|
257
|
-
# The way it should be done is using "x-ui" properties.
|
|
258
|
-
# Ideally, we should pass an already parsed FieldMeta into the
|
|
259
|
-
# is_advanced_input() method.
|
|
260
|
-
field_meta = FieldMeta.from_input_description(input_name, input_description)
|
|
261
|
-
return field_meta.advanced or field_meta.level == "advanced"
|
|
262
|
-
|
|
263
156
|
@classmethod
|
|
264
157
|
def register_job_result_opener(
|
|
265
158
|
cls, opener_type: type[JobResultOpener]
|
|
@@ -286,33 +179,6 @@ class ClientConfig(AuthConfig, BaseSettings):
|
|
|
286
179
|
"""
|
|
287
180
|
return JobResultOpenerRegistry.create_default()
|
|
288
181
|
|
|
289
|
-
@classmethod
|
|
290
|
-
@cache
|
|
291
|
-
def get_field_factory_registry(cls) -> FieldFactoryRegistry:
|
|
292
|
-
"""
|
|
293
|
-
Get the registry for factories that generate UI fields from
|
|
294
|
-
process inputs.
|
|
295
|
-
|
|
296
|
-
Used in the GUI client `cuiman.gui.Client`. Use it to register
|
|
297
|
-
factories that customize the way the client GUI is generated.
|
|
298
|
-
|
|
299
|
-
The default registry contains factories that creates UI fields for
|
|
300
|
-
the [Panel](https://panel.holoviz.org/) library as this is the primary
|
|
301
|
-
library used in the GUI client.
|
|
302
|
-
|
|
303
|
-
The default registry type is
|
|
304
|
-
`gavicore.ui.panel.PanelFieldFactoryRegistry`.
|
|
305
|
-
|
|
306
|
-
The type of registered field factories is
|
|
307
|
-
`gavicore.ui.panel.PanelFieldFactory`.
|
|
308
|
-
|
|
309
|
-
The type of the field instances created by the factories must be
|
|
310
|
-
`gavicore.ui.panel.PanelField`.
|
|
311
|
-
"""
|
|
312
|
-
from gavicore.ui.panel import PanelFieldFactoryRegistry
|
|
313
|
-
|
|
314
|
-
return PanelFieldFactoryRegistry.create_default()
|
|
315
|
-
|
|
316
182
|
|
|
317
183
|
# Set Eozilla defaults.
|
|
318
184
|
# Cuiman applications might want to change them.
|
|
@@ -5,19 +5,31 @@
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
from typing import Any, Callable
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
has_ishell_module: bool
|
|
9
9
|
"""Whether `IPython.core.interactiveshell` is available."""
|
|
10
10
|
|
|
11
|
+
has_ishell: bool
|
|
12
|
+
"""Whether an `IPython.core.interactiveshell.InteractiveShell` is initialized."""
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
try:
|
|
13
|
-
|
|
16
|
+
has_ishell_module = (
|
|
17
|
+
importlib.util.find_spec("IPython.core.interactiveshell") is not None
|
|
18
|
+
)
|
|
14
19
|
except (ImportError, ModuleNotFoundError):
|
|
20
|
+
has_ishell_module = False
|
|
21
|
+
|
|
22
|
+
if has_ishell_module:
|
|
23
|
+
from IPython.core.interactiveshell import InteractiveShell
|
|
24
|
+
|
|
25
|
+
has_ishell = InteractiveShell.initialized()
|
|
26
|
+
else:
|
|
15
27
|
has_ishell = False
|
|
16
28
|
|
|
17
29
|
exception_handler: Callable[[Any, Any, Any, Any], None] | None = None
|
|
18
30
|
"""Custom exception handler for `IPython.core.interactiveshell.InteractiveShell`."""
|
|
19
31
|
|
|
20
|
-
__all__ = ["has_ishell", "exception_handler"]
|
|
32
|
+
__all__ = ["has_ishell_module", "has_ishell", "exception_handler"]
|
|
21
33
|
|
|
22
34
|
|
|
23
35
|
def _register_exception_handler() -> Callable[[Any, Any, Any, Any], None]:
|
|
@@ -55,7 +67,4 @@ def _register_exception_handler() -> Callable[[Any, Any, Any, Any], None]:
|
|
|
55
67
|
|
|
56
68
|
|
|
57
69
|
if has_ishell:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if InteractiveShell.initialized():
|
|
61
|
-
exception_handler = _register_exception_handler()
|
|
70
|
+
exception_handler = _register_exception_handler()
|