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.
Files changed (70) hide show
  1. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/.gitignore +1 -1
  2. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/PKG-INFO +4 -7
  3. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/__init__.py +3 -0
  4. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/__init__.py +3 -0
  5. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/async_client.py +5 -3
  6. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/async_client_mixin.py +19 -4
  7. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/client.py +6 -5
  8. cuiman-0.2.0.dev1/cuiman/api/client_app_mixin.py +104 -0
  9. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/client_mixin.py +18 -4
  10. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/config.py +0 -134
  11. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/exceptions.py +4 -0
  12. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/ishell.py +16 -7
  13. cuiman-0.2.0.dev1/cuiman/api/jobs.py +304 -0
  14. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/__init__.py +2 -0
  15. cuiman-0.2.0.dev1/cuiman/api/opener/impl/_image.py +48 -0
  16. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/openers.py +12 -0
  17. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/opener.py +15 -13
  18. cuiman-0.2.0.dev1/cuiman/api/service_client.py +372 -0
  19. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/args.py +21 -11
  20. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/httpx.py +11 -6
  21. {cuiman-0.1.1/cuiman/gui/panels/jobs_panel → cuiman-0.2.0.dev1/cuiman/app}/__init__.py +4 -5
  22. cuiman-0.2.0.dev1/cuiman/app/app.py +124 -0
  23. cuiman-0.2.0.dev1/cuiman/app/display.py +110 -0
  24. cuiman-0.2.0.dev1/cuiman/app/dist/assets/index-CEQarI7F.js +84 -0
  25. cuiman-0.2.0.dev1/cuiman/app/dist/assets/index-DKE7nU5k.css +1 -0
  26. cuiman-0.2.0.dev1/cuiman/app/dist/eozilla-small.svg +5 -0
  27. cuiman-0.2.0.dev1/cuiman/app/dist/eozilla.svg +3 -0
  28. cuiman-0.2.0.dev1/cuiman/app/dist/index.html +14 -0
  29. cuiman-0.2.0.dev1/cuiman/app/dist/privacy.md +23 -0
  30. cuiman-0.2.0.dev1/cuiman/app/serve.py +68 -0
  31. cuiman-0.2.0.dev1/cuiman/app/service.py +96 -0
  32. cuiman-0.2.0.dev1/cuiman/app/url.py +77 -0
  33. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/cli.py +95 -21
  34. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/config.py +22 -19
  35. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/pyproject.toml +5 -7
  36. cuiman-0.1.1/cuiman/gui/__init__.py +0 -11
  37. cuiman-0.1.1/cuiman/gui/client.py +0 -134
  38. cuiman-0.1.1/cuiman/gui/ipy_helper.py +0 -63
  39. cuiman-0.1.1/cuiman/gui/jobs_event_bus.py +0 -76
  40. cuiman-0.1.1/cuiman/gui/jobs_observer.py +0 -32
  41. cuiman-0.1.1/cuiman/gui/panels/__init__.py +0 -15
  42. cuiman-0.1.1/cuiman/gui/panels/job_info_panel.py +0 -113
  43. cuiman-0.1.1/cuiman/gui/panels/jobs_panel/view.py +0 -99
  44. cuiman-0.1.1/cuiman/gui/panels/jobs_panel/viewmodel.py +0 -196
  45. cuiman-0.1.1/cuiman/gui/panels/main_panel/__init__.py +0 -11
  46. cuiman-0.1.1/cuiman/gui/panels/main_panel/settings.py +0 -21
  47. cuiman-0.1.1/cuiman/gui/panels/main_panel/view.py +0 -417
  48. cuiman-0.1.1/cuiman/gui/panels/main_panel/viewmodel.py +0 -307
  49. cuiman-0.1.1/cuiman/gui/panels/util.py +0 -259
  50. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/LICENSE +0 -0
  51. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/README.md +0 -0
  52. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/__init__.py +0 -0
  53. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/config.py +0 -0
  54. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/login.py +0 -0
  55. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/auth/login_async.py +0 -0
  56. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/defaults.py +0 -0
  57. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/__init__.py +0 -0
  58. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/context.py +0 -0
  59. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/errors.py +0 -0
  60. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/_gpd.py +0 -0
  61. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/_pd.py +0 -0
  62. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/_xr.py +0 -0
  63. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/impl/base.py +0 -0
  64. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/opener/registry.py +0 -0
  65. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/__init__.py +0 -0
  66. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/api/transport/transport.py +0 -0
  67. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/__init__.py +0 -0
  68. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/client.py +0 -0
  69. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/cli/output.py +0 -0
  70. {cuiman-0.1.1 → cuiman-0.2.0.dev1}/cuiman/py.typed +0 -0
@@ -17,7 +17,7 @@ __pycache__/
17
17
  .Python
18
18
  build/
19
19
  develop-eggs/
20
- dist/
20
+ /*/dist/
21
21
  downloads/
22
22
  eggs/
23
23
  .eggs/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuiman
3
- Version: 0.1.1
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: click
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: typer
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: 2025-11-27T12:56:53.114835
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
- process_description = (
125
- (await self.get_process(job_info.processID)) if job_info.processID else None
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: 2025-11-27T12:56:52.899826
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: Any,
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
- process_description = (
126
- self.get_process(job_info.processID) if job_info.processID else None
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.
@@ -22,3 +22,7 @@ class ClientError(Exception):
22
22
  def __init__(self, message: str, api_error: ApiError):
23
23
  super().__init__(message)
24
24
  self.api_error = api_error
25
+
26
+
27
+ class ClientWarning(UserWarning):
28
+ """Special warning type used by the client."""
@@ -5,19 +5,31 @@
5
5
  import importlib.util
6
6
  from typing import Any, Callable
7
7
 
8
- has_ishell: bool
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
- has_ishell = importlib.util.find_spec("IPython.core.interactiveshell") is not None
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
- from IPython.core.interactiveshell import InteractiveShell
59
-
60
- if InteractiveShell.initialized():
61
- exception_handler = _register_exception_handler()
70
+ exception_handler = _register_exception_handler()