cuiman 0.1.2__tar.gz → 0.2.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.
Files changed (71) hide show
  1. {cuiman-0.1.2 → cuiman-0.2.0}/PKG-INFO +4 -8
  2. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/__init__.py +3 -0
  3. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/__init__.py +3 -0
  4. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/async_client.py +5 -3
  5. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/client.py +5 -3
  6. cuiman-0.2.0/cuiman/api/client_app_mixin.py +110 -0
  7. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/config.py +0 -147
  8. cuiman-0.2.0/cuiman/api/jobs.py +304 -0
  9. cuiman-0.2.0/cuiman/api/service_client.py +372 -0
  10. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/transport/args.py +21 -11
  11. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/transport/httpx.py +11 -6
  12. {cuiman-0.1.2/cuiman/gui/panels/jobs_panel → cuiman-0.2.0/cuiman/app}/__init__.py +4 -5
  13. cuiman-0.2.0/cuiman/app/app.py +124 -0
  14. cuiman-0.2.0/cuiman/app/display.py +104 -0
  15. cuiman-0.2.0/cuiman/app/dist/assets/index-9NSgOSt1.js +85 -0
  16. cuiman-0.2.0/cuiman/app/dist/assets/index-DKE7nU5k.css +1 -0
  17. cuiman-0.2.0/cuiman/app/dist/eozilla-small.svg +5 -0
  18. cuiman-0.2.0/cuiman/app/dist/eozilla.svg +3 -0
  19. cuiman-0.2.0/cuiman/app/dist/index.html +14 -0
  20. cuiman-0.2.0/cuiman/app/dist/privacy.md +23 -0
  21. cuiman-0.2.0/cuiman/app/notebook-display.js +196 -0
  22. cuiman-0.2.0/cuiman/app/serve.py +123 -0
  23. cuiman-0.2.0/cuiman/app/service.py +96 -0
  24. cuiman-0.2.0/cuiman/app/url.py +77 -0
  25. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/cli/cli.py +89 -21
  26. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/cli/config.py +22 -19
  27. {cuiman-0.1.2 → cuiman-0.2.0}/pyproject.toml +4 -8
  28. cuiman-0.1.2/cuiman/gui/__init__.py +0 -11
  29. cuiman-0.1.2/cuiman/gui/client.py +0 -151
  30. cuiman-0.1.2/cuiman/gui/ipy_helper.py +0 -63
  31. cuiman-0.1.2/cuiman/gui/jobs_event_bus.py +0 -76
  32. cuiman-0.1.2/cuiman/gui/jobs_observer.py +0 -32
  33. cuiman-0.1.2/cuiman/gui/panels/__init__.py +0 -15
  34. cuiman-0.1.2/cuiman/gui/panels/job_info_panel.py +0 -113
  35. cuiman-0.1.2/cuiman/gui/panels/jobs_panel/view.py +0 -99
  36. cuiman-0.1.2/cuiman/gui/panels/jobs_panel/viewmodel.py +0 -196
  37. cuiman-0.1.2/cuiman/gui/panels/main_panel/__init__.py +0 -11
  38. cuiman-0.1.2/cuiman/gui/panels/main_panel/settings.py +0 -21
  39. cuiman-0.1.2/cuiman/gui/panels/main_panel/view.py +0 -417
  40. cuiman-0.1.2/cuiman/gui/panels/main_panel/viewmodel.py +0 -307
  41. cuiman-0.1.2/cuiman/gui/panels/util.py +0 -259
  42. {cuiman-0.1.2 → cuiman-0.2.0}/.gitignore +0 -0
  43. {cuiman-0.1.2 → cuiman-0.2.0}/LICENSE +0 -0
  44. {cuiman-0.1.2 → cuiman-0.2.0}/README.md +0 -0
  45. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/async_client_mixin.py +0 -0
  46. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/auth/__init__.py +0 -0
  47. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/auth/config.py +0 -0
  48. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/auth/login.py +0 -0
  49. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/auth/login_async.py +0 -0
  50. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/client_mixin.py +0 -0
  51. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/defaults.py +0 -0
  52. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/exceptions.py +0 -0
  53. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/ishell.py +0 -0
  54. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/__init__.py +0 -0
  55. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/context.py +0 -0
  56. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/errors.py +0 -0
  57. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/__init__.py +0 -0
  58. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/_gpd.py +0 -0
  59. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/_image.py +0 -0
  60. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/_pd.py +0 -0
  61. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/_xr.py +0 -0
  62. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/base.py +0 -0
  63. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/impl/openers.py +0 -0
  64. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/opener.py +0 -0
  65. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/opener/registry.py +0 -0
  66. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/transport/__init__.py +0 -0
  67. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/api/transport/transport.py +0 -0
  68. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/cli/__init__.py +0 -0
  69. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/cli/client.py +0 -0
  70. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/cli/output.py +0 -0
  71. {cuiman-0.1.2 → cuiman-0.2.0}/cuiman/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuiman
3
- Version: 0.1.2
3
+ Version: 0.2.0
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,17 +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.2
30
+ Requires-Dist: gavicore>=0.2.0
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: remotestate<1,>=0.3.1
40
- Requires-Dist: typer
35
+ Requires-Dist: remotestate<1,>=0.3.4
36
+ Requires-Dist: typer<1,>=0.26.0
41
37
  Requires-Dist: uri-template
42
38
  Description-Content-Type: text/markdown
43
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: 2026-07-08T13:06:46.295496
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
  )
@@ -1,6 +1,6 @@
1
1
  # generated by gen_client.py:
2
2
  # filename: client.py:
3
- # timestamp: 2026-07-08T13:06:43.275607
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
 
@@ -56,8 +57,9 @@ 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,
62
+ return_type_map=self._config.return_type_map,
61
63
  token_refresher=self._config._maybe_make_token_refresher(),
62
64
  debug=_debug,
63
65
  )
@@ -0,0 +1,110 @@
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
+ from cuiman.app.serve import ProxyMode
10
+
11
+ from .config import ClientConfig
12
+
13
+ if TYPE_CHECKING:
14
+ from cuiman.app import App
15
+
16
+
17
+ class ClientAppMixin(ABC):
18
+ """
19
+ Extra methods for the Client App.
20
+ """
21
+
22
+ @property
23
+ @abstractmethod
24
+ def config(self) -> ClientConfig:
25
+ """Will be overridden by the actual client class."""
26
+
27
+ def show_app(
28
+ self,
29
+ *,
30
+ compact: bool | None = None,
31
+ debug: bool = False,
32
+ scheme: Literal["dark", "light", "auto"] = "auto",
33
+ width: int | str = "100%",
34
+ height: int | str = 600,
35
+ display: Literal["browser", "notebook", "auto"] = "auto",
36
+ proxy: ProxyMode = "auto",
37
+ ) -> "App":
38
+ """
39
+ Show the Cuiman app for this client.
40
+
41
+ The app connects to this client's API configuration, renders the app GUI,
42
+ and returns an object that provides the serve result and a shared
43
+ [app state][cuiman.app.AppState], which you can interact with.
44
+
45
+ The app state currently only manages the process requests being
46
+ edited and executed by a user. The requests are a mapping from process IDs
47
+ to process requests.
48
+
49
+ Display the app and get the app instance:
50
+
51
+ ```
52
+ app = client.show_app()
53
+ ```
54
+
55
+ You can get and set process requests using the methods
56
+
57
+ - ``app.get_process_request(process_id)``
58
+ - ``app.set_process_request(process_id, process_request)``
59
+
60
+ where ``process_id`` is the process ID and ``process_request`` is a dict
61
+ or a ``ProcessRequest`` object that comprises basically two attributes:
62
+
63
+ - ``inputs``: a mapping from input name to an input's value.
64
+ - ``outputs``: the outputs to be generated. Just used to detect
65
+ whether an output is included or now.
66
+
67
+ Another convenient way to work with the nested app state is the
68
+ ``process_requests`` property:
69
+
70
+ - ``app.process_requests.my_process.inputs.threshold = 0.75``
71
+ - ``app.process_requests.my_process = {"inputs": {...}}``
72
+
73
+ Args:
74
+ compact: Compact mode. Defaults to True, if ``display`` is "notebook".
75
+ debug: Enable app debug mode.
76
+ scheme: Color scheme to use in the app. ``"auto"`` follows the
77
+ surrounding notebook theme when the app is embedded.
78
+ width: Width of the notebook iframe.
79
+ height: Height of the notebook iframe.
80
+ display: Where to show the app. ``"auto"`` embeds it in notebooks
81
+ and opens it in a browser otherwise.
82
+ proxy: Whether notebook traffic uses ``jupyter-server-proxy``. ``"auto"``
83
+ uses it when available, ``"never"`` disables it, and ``"always"``
84
+ requires it.
85
+ Return:
86
+ An ``App`` instance.
87
+ """
88
+ from cuiman.app import App, serve
89
+
90
+ display_ = (
91
+ ("notebook" if has_ishell else "browser") if display == "auto" else display
92
+ )
93
+ compact_ = compact if isinstance(compact, bool) else display_ == "notebook"
94
+
95
+ remote_store = App.create_remote_store()
96
+
97
+ # noinspection PyTypeChecker
98
+ serve_result = serve(
99
+ self.config,
100
+ remote_store,
101
+ compact=compact_,
102
+ debug=debug,
103
+ scheme=scheme,
104
+ width=width,
105
+ height=height,
106
+ display=display_,
107
+ proxy=proxy,
108
+ )
109
+
110
+ return App(remote_store, serve_result)
@@ -16,10 +16,8 @@ from typing import (
16
16
  import yaml
17
17
  from pydantic import Field, HttpUrl, field_validator
18
18
  from pydantic_settings import BaseSettings, SettingsConfigDict
19
- from typing_extensions import deprecated
20
19
 
21
20
  from gavicore.models import InputDescription, ProcessDescription, ProcessSummary
22
- from gavicore.ui import FieldFactoryRegistry, FieldMeta
23
21
 
24
22
  from .auth import AuthConfig
25
23
  from .defaults import DEFAULT_API_URL
@@ -155,124 +153,6 @@ class ClientConfig(AuthConfig, BaseSettings):
155
153
  def validate_api_url(cls, v: str | None) -> str | None:
156
154
  return None if v is None or v == "" else str(HttpUrl(v))
157
155
 
158
- # noinspection PyUnusedLocal
159
- @classmethod
160
- @deprecated(
161
- "ClientConfig.accept_process() is deprecated, only used by the legacy "
162
- "cuiman.gui client, and will not work in newer Eozilla versions."
163
- )
164
- def accept_process(
165
- cls, process_summary: ProcessSummary, **filter_kwargs: Any
166
- ) -> bool:
167
- """
168
- Predicate function that is used to filter the list of processes.
169
- The function is intended to be overridden by subclasses in order to allow
170
- for evaluating the given `process_summary` in an application-specific way.
171
- This includes the using custom fields in the given
172
- [ProcessSummary][gavicore.models.ProcessSummary] instance.
173
-
174
- Applications may use the [extend_model()][gavicore.util.model.extend_model]
175
- function to enhance existing model classes by their custom fields.
176
-
177
- The default implementation unconditionally returns `True`.
178
-
179
- Args:
180
- process_summary: A process summary.
181
- filter_kwargs: Implementation specific arguments passed
182
- by a user of this class.
183
-
184
- Returns:
185
- `True` to accept the given process, otherwise `False`.
186
- """
187
- return True
188
-
189
- # noinspection PyUnusedLocal
190
- @classmethod
191
- @deprecated(
192
- "ClientConfig.accept_input() is deprecated, only used by the legacy "
193
- "cuiman.gui client, and will not work in newer Eozilla versions."
194
- )
195
- def accept_input(
196
- cls,
197
- process_description: ProcessDescription,
198
- input_name: str,
199
- input_description: InputDescription,
200
- **filter_kwargs: Any,
201
- ) -> bool:
202
- """
203
- Predicate function that is used to filter the list of inputs of a process.
204
- The function is intended to be overridden by subclasses in order to allow
205
- for evaluating the given `input_description` in an application-specific way.
206
- This includes the using custom fields in the given
207
- [InputDescription][gavicore.models.InputDescription] instance.
208
-
209
- Applications may use the [extend_model()][gavicore.util.model.extend_model]
210
- function to enhance existing model classes by their custom fields.
211
-
212
- The default implementation unconditionally returns `True`.
213
-
214
- Args:
215
- process_description: The process description.
216
- input_name: The input's name.
217
- input_description: A description of an
218
- input of the given `process_description`.
219
- filter_kwargs: Implementation specific arguments passed
220
- by a user of this class.
221
-
222
- Returns:
223
- `True` to accept the given input, otherwise `False`.
224
- """
225
- return True
226
-
227
- # noinspection PyUnusedLocal
228
- @classmethod
229
- @deprecated(
230
- "ClientConfig.is_advanced_input() is deprecated, only used by the legacy "
231
- "cuiman.gui client, and will not work in newer Eozilla versions."
232
- )
233
- def is_advanced_input(
234
- cls,
235
- process_description: ProcessDescription,
236
- input_name: str,
237
- input_description: InputDescription,
238
- ) -> bool:
239
- """
240
- Experimental method, do not use!
241
-
242
- Designed to be overridden by a custom `ClientConfig` class
243
- from which an instance will be assigned to `ClientConfig.default_config`
244
- to become effective.
245
-
246
- The default implementations checks if the given `input_description`
247
- has `additionalParameters`, and if so, if a parameter with name
248
- `"level"` has value `["advanced"]` (a list!).
249
-
250
- Args:
251
- process_description: The process description.
252
- input_name: The input's name.
253
- input_description: A description of an
254
- input of the given `process_description`.
255
-
256
- Returns:
257
- `True` if the input is advanced
258
- (e.g. for advanced process users only).
259
- """
260
- # The following approach uses additionalParameters but its use
261
- # is discouraged. (We receive such metadata currently from
262
- # the TAO Process API).
263
- additional_parameters = input_description.additionalParameters
264
- if additional_parameters:
265
- parameters = additional_parameters.parameters
266
- if parameters:
267
- for p in parameters:
268
- if p.name == "level" and p.value == ["advanced"]:
269
- return True
270
- # The way it should be done is using "x-ui" properties.
271
- # Ideally, we should pass an already parsed FieldMeta into the
272
- # is_advanced_input() method.
273
- field_meta = FieldMeta.from_input_description(input_name, input_description)
274
- return field_meta.advanced or field_meta.level == "advanced"
275
-
276
156
  @classmethod
277
157
  def register_job_result_opener(
278
158
  cls, opener_type: type[JobResultOpener]
@@ -299,33 +179,6 @@ class ClientConfig(AuthConfig, BaseSettings):
299
179
  """
300
180
  return JobResultOpenerRegistry.create_default()
301
181
 
302
- @classmethod
303
- @cache
304
- def get_field_factory_registry(cls) -> FieldFactoryRegistry:
305
- """
306
- Get the registry for factories that generate UI fields from
307
- process inputs.
308
-
309
- Used in the GUI client `cuiman.gui.Client`. Use it to register
310
- factories that customize the way the client GUI is generated.
311
-
312
- The default registry contains factories that creates UI fields for
313
- the [Panel](https://panel.holoviz.org/) library as this is the primary
314
- library used in the GUI client.
315
-
316
- The default registry type is
317
- `gavicore.ui.panel.PanelFieldFactoryRegistry`.
318
-
319
- The type of registered field factories is
320
- `gavicore.ui.panel.PanelFieldFactory`.
321
-
322
- The type of the field instances created by the factories must be
323
- `gavicore.ui.panel.PanelField`.
324
- """
325
- from gavicore.ui.panel import PanelFieldFactoryRegistry
326
-
327
- return PanelFieldFactoryRegistry.create_default()
328
-
329
182
 
330
183
  # Set Eozilla defaults.
331
184
  # Cuiman applications might want to change them.