nextmv 0.37.0__tar.gz → 0.37.2__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.
- {nextmv-0.37.0 → nextmv-0.37.2}/PKG-INFO +3 -2
- nextmv-0.37.2/nextmv/__about__.py +1 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/__init__.py +4 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/application.py +25 -24
- nextmv-0.37.2/nextmv/cloud/integration.py +533 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/package.py +3 -3
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/README.md +1 -1
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/input.py +1 -1
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/application.py +1 -1
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/model.py +1 -1
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/output.py +1 -1
- {nextmv-0.37.0 → nextmv-0.37.2}/pyproject.toml +3 -1
- nextmv-0.37.0/nextmv/__about__.py +0 -1
- {nextmv-0.37.0 → nextmv-0.37.2}/.gitignore +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/LICENSE +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/README.md +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/__entrypoint__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/_serialization.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/base_model.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/account.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/client.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/input_set.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/instance.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/scenario.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/secrets.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/url.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/cloud/version.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/.gitignore +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/app.yaml +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/input.json +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/main.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/requirements.txt +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/src/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/default_app/src/visuals.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/deprecated.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/executor.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/geojson_handler.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/local.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/local/runner.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/logger.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/manifest.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/options.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/polling.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/run.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/safe.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/nextmv/status.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/cloud/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/cloud/app.yaml +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/cloud/test_client.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/cloud/test_instance.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/cloud/test_package.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/cloud/test_scenario.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/local/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/local/test_application.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/local/test_executor.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/local/test_runner.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options1.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options2.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options3.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options4.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options5.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options6.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options7.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/scripts/options_deprecated.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_base_model.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_input.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_inputs/test_data.json +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_logger.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_manifest.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_model.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_options.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_output.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_polling.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_run.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_safe.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_serialization.py +0 -0
- {nextmv-0.37.0 → nextmv-0.37.2}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nextmv
|
|
3
|
-
Version: 0.37.
|
|
3
|
+
Version: 0.37.2
|
|
4
4
|
Summary: The all-purpose Python SDK for Nextmv
|
|
5
5
|
Project-URL: Homepage, https://www.nextmv.io
|
|
6
6
|
Project-URL: Documentation, https://nextmv-py.docs.nextmv.io/en/latest/nextmv/
|
|
@@ -224,7 +224,6 @@ Requires-Dist: requests>=2.31.0
|
|
|
224
224
|
Requires-Dist: urllib3>=2.1.0
|
|
225
225
|
Provides-Extra: all
|
|
226
226
|
Requires-Dist: folium>=0.20.0; extra == 'all'
|
|
227
|
-
Requires-Dist: mlflow>=2.17.2; extra == 'all'
|
|
228
227
|
Requires-Dist: plotly>=6.0.1; extra == 'all'
|
|
229
228
|
Provides-Extra: dev
|
|
230
229
|
Requires-Dist: build>=1.0.3; extra == 'dev'
|
|
@@ -240,6 +239,8 @@ Requires-Dist: requests>=2.31.0; extra == 'dev'
|
|
|
240
239
|
Requires-Dist: ruff>=0.1.7; extra == 'dev'
|
|
241
240
|
Requires-Dist: twine>=4.0.2; extra == 'dev'
|
|
242
241
|
Requires-Dist: urllib3>=2.1.0; extra == 'dev'
|
|
242
|
+
Provides-Extra: notebook
|
|
243
|
+
Requires-Dist: mlflow>=2.19.0; extra == 'notebook'
|
|
243
244
|
Description-Content-Type: text/markdown
|
|
244
245
|
|
|
245
246
|
# Nextmv Python SDK
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v0.37.2"
|
|
@@ -74,6 +74,10 @@ from .input_set import InputSet as InputSet
|
|
|
74
74
|
from .input_set import ManagedInput as ManagedInput
|
|
75
75
|
from .instance import Instance as Instance
|
|
76
76
|
from .instance import InstanceConfiguration as InstanceConfiguration
|
|
77
|
+
from .integration import Integration as Integration
|
|
78
|
+
from .integration import IntegrationProvider as IntegrationProvider
|
|
79
|
+
from .integration import IntegrationType as IntegrationType
|
|
80
|
+
from .integration import list_integrations as list_integrations
|
|
77
81
|
from .scenario import Scenario as Scenario
|
|
78
82
|
from .scenario import ScenarioConfiguration as ScenarioConfiguration
|
|
79
83
|
from .scenario import ScenarioInput as ScenarioInput
|
|
@@ -3215,7 +3215,7 @@ class Application:
|
|
|
3215
3215
|
name: str | None = None,
|
|
3216
3216
|
version_id: str | None = None,
|
|
3217
3217
|
description: str | None = None,
|
|
3218
|
-
configuration: InstanceConfiguration | None = None,
|
|
3218
|
+
configuration: InstanceConfiguration | dict[str, Any] | None = None,
|
|
3219
3219
|
) -> Instance:
|
|
3220
3220
|
"""
|
|
3221
3221
|
Update an instance.
|
|
@@ -3230,7 +3230,7 @@ class Application:
|
|
|
3230
3230
|
Optional ID of the version to associate the instance with.
|
|
3231
3231
|
description : Optional[str], default=None
|
|
3232
3232
|
Optional description of the instance.
|
|
3233
|
-
configuration : Optional[InstanceConfiguration], default=None
|
|
3233
|
+
configuration : Optional[InstanceConfiguration | dict[str, Any]], default=None
|
|
3234
3234
|
Optional configuration to use for the instance.
|
|
3235
3235
|
|
|
3236
3236
|
Returns
|
|
@@ -3247,13 +3247,7 @@ class Application:
|
|
|
3247
3247
|
# Get the instance as it currently exsits.
|
|
3248
3248
|
instance = self.instance(id)
|
|
3249
3249
|
instance_dict = instance.to_dict()
|
|
3250
|
-
|
|
3251
|
-
payload = {
|
|
3252
|
-
"name": instance_dict["name"],
|
|
3253
|
-
"version_id": instance_dict["version_id"],
|
|
3254
|
-
"description": instance_dict["description"],
|
|
3255
|
-
"configuration": instance_dict["configuration"],
|
|
3256
|
-
}
|
|
3250
|
+
payload = instance_dict
|
|
3257
3251
|
|
|
3258
3252
|
if name is not None:
|
|
3259
3253
|
payload["name"] = name
|
|
@@ -3262,7 +3256,14 @@ class Application:
|
|
|
3262
3256
|
if description is not None:
|
|
3263
3257
|
payload["description"] = description
|
|
3264
3258
|
if configuration is not None:
|
|
3265
|
-
|
|
3259
|
+
if isinstance(configuration, dict):
|
|
3260
|
+
config_dict = configuration
|
|
3261
|
+
elif isinstance(configuration, InstanceConfiguration):
|
|
3262
|
+
config_dict = configuration.to_dict()
|
|
3263
|
+
else:
|
|
3264
|
+
raise TypeError("configuration must be either a dict or InstanceConfiguration object")
|
|
3265
|
+
|
|
3266
|
+
payload["configuration"] = config_dict
|
|
3266
3267
|
|
|
3267
3268
|
response = self.client.request(
|
|
3268
3269
|
method="PUT",
|
|
@@ -3303,11 +3304,7 @@ class Application:
|
|
|
3303
3304
|
|
|
3304
3305
|
managed_input = self.managed_input(managed_input_id)
|
|
3305
3306
|
managed_input_dict = managed_input.to_dict()
|
|
3306
|
-
|
|
3307
|
-
payload = {
|
|
3308
|
-
"name": managed_input_dict["name"],
|
|
3309
|
-
"description": managed_input_dict["description"],
|
|
3310
|
-
}
|
|
3307
|
+
payload = managed_input_dict
|
|
3311
3308
|
|
|
3312
3309
|
if name is not None:
|
|
3313
3310
|
payload["name"] = name
|
|
@@ -3376,7 +3373,7 @@ class Application:
|
|
|
3376
3373
|
secrets_collection_id: str,
|
|
3377
3374
|
name: str | None = None,
|
|
3378
3375
|
description: str | None = None,
|
|
3379
|
-
secrets: list[Secret] | None = None,
|
|
3376
|
+
secrets: list[Secret | dict[str, Any]] | None = None,
|
|
3380
3377
|
) -> SecretsCollectionSummary:
|
|
3381
3378
|
"""
|
|
3382
3379
|
Update a secrets collection.
|
|
@@ -3393,7 +3390,7 @@ class Application:
|
|
|
3393
3390
|
Optional new name for the secrets collection.
|
|
3394
3391
|
description : Optional[str], default=None
|
|
3395
3392
|
Optional new description for the secrets collection.
|
|
3396
|
-
secrets : Optional[list[Secret]], default=None
|
|
3393
|
+
secrets : Optional[list[Secret | dict[str, Any]]], default=None
|
|
3397
3394
|
Optional list of secrets to update. Each secret should be an
|
|
3398
3395
|
instance of the Secret class containing a key and value.
|
|
3399
3396
|
|
|
@@ -3429,19 +3426,23 @@ class Application:
|
|
|
3429
3426
|
|
|
3430
3427
|
collection = self.secrets_collection(secrets_collection_id)
|
|
3431
3428
|
collection_dict = collection.to_dict()
|
|
3432
|
-
|
|
3433
|
-
payload = {
|
|
3434
|
-
"name": collection_dict["name"],
|
|
3435
|
-
"description": collection_dict["description"],
|
|
3436
|
-
"secrets": collection_dict["secrets"],
|
|
3437
|
-
}
|
|
3429
|
+
payload = collection_dict
|
|
3438
3430
|
|
|
3439
3431
|
if name is not None:
|
|
3440
3432
|
payload["name"] = name
|
|
3441
3433
|
if description is not None:
|
|
3442
3434
|
payload["description"] = description
|
|
3443
3435
|
if secrets is not None and len(secrets) > 0:
|
|
3444
|
-
|
|
3436
|
+
secrets_dicts = []
|
|
3437
|
+
for ix, secret in enumerate(secrets):
|
|
3438
|
+
if isinstance(secret, dict):
|
|
3439
|
+
secrets_dicts.append(secret)
|
|
3440
|
+
elif isinstance(secret, Secret):
|
|
3441
|
+
secrets_dicts.append(secret.to_dict())
|
|
3442
|
+
else:
|
|
3443
|
+
raise ValueError(f"secret at index {ix} must be either a Secret or dict object")
|
|
3444
|
+
|
|
3445
|
+
payload["secrets"] = secrets_dicts
|
|
3445
3446
|
|
|
3446
3447
|
response = self.client.request(
|
|
3447
3448
|
method="PUT",
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration module for interacting with Nextmv Cloud integrations.
|
|
3
|
+
|
|
4
|
+
This module provides functionality to interact with integrations in Nextmv
|
|
5
|
+
Cloud, including integration management.
|
|
6
|
+
|
|
7
|
+
Classes
|
|
8
|
+
-------
|
|
9
|
+
IntegrationType
|
|
10
|
+
Enum representing the type of an integration.
|
|
11
|
+
IntegrationProvider
|
|
12
|
+
Enum representing the provider of an integration.
|
|
13
|
+
Integration
|
|
14
|
+
Class representing an integration in Nextmv Cloud.
|
|
15
|
+
|
|
16
|
+
Functions
|
|
17
|
+
---------
|
|
18
|
+
list_integrations
|
|
19
|
+
Function to list all integrations in Nextmv Cloud.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from enum import Enum
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from pydantic import AliasChoices, Field
|
|
27
|
+
|
|
28
|
+
from nextmv.base_model import BaseModel
|
|
29
|
+
from nextmv.cloud.client import Client
|
|
30
|
+
from nextmv.manifest import ManifestType
|
|
31
|
+
from nextmv.safe import safe_id
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class IntegrationType(str, Enum):
|
|
35
|
+
"""
|
|
36
|
+
The type of an integration.
|
|
37
|
+
|
|
38
|
+
You can import the `IntegrationType` class directly from `cloud`:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from nextmv.cloud import IntegrationType
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Attributes
|
|
45
|
+
----------
|
|
46
|
+
RUNTIME : str
|
|
47
|
+
Indicates a runtime integration.
|
|
48
|
+
DATA : str
|
|
49
|
+
Indicates a data integration.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
RUNTIME = "runtime"
|
|
53
|
+
"""Indicates a runtime integration."""
|
|
54
|
+
DATA = "data"
|
|
55
|
+
"""Indicates a data integration."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class IntegrationProvider(str, Enum):
|
|
59
|
+
"""
|
|
60
|
+
The provider of an integration.
|
|
61
|
+
|
|
62
|
+
You can import the `IntegrationProvider` class directly from `cloud`:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from nextmv.cloud import IntegrationProvider
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Attributes
|
|
69
|
+
----------
|
|
70
|
+
DBX : str
|
|
71
|
+
Indicates a Databricks integration.
|
|
72
|
+
UNKNOWN : str
|
|
73
|
+
Indicates an unknown integration provider.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
DBX = "dbx"
|
|
77
|
+
"""Indicates a Databricks integration."""
|
|
78
|
+
UNKNOWN = "unknown"
|
|
79
|
+
"""Indicates an unknown integration provider."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Integration(BaseModel):
|
|
83
|
+
"""
|
|
84
|
+
Represents an integration in Nextmv Cloud. An integration allows Nextmv
|
|
85
|
+
Cloud to communicate with external systems or services.
|
|
86
|
+
|
|
87
|
+
You can import the `Integration` class directly from `cloud`:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from nextmv.cloud import Integration
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
You can use the `Integration.get` class method to retrieve an existing
|
|
94
|
+
integration from Nextmv Cloud, to ensure that all fields are properly
|
|
95
|
+
populated.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
integration_id : str
|
|
100
|
+
The unique identifier of the integration.
|
|
101
|
+
client : Client
|
|
102
|
+
Client to use for interacting with the Nextmv Cloud API.
|
|
103
|
+
name : str, optional
|
|
104
|
+
The name of the integration.
|
|
105
|
+
description : str, optional
|
|
106
|
+
An optional description of the integration.
|
|
107
|
+
is_global : bool, optional
|
|
108
|
+
Indicates whether the integration is global (available to all
|
|
109
|
+
applications in the account).
|
|
110
|
+
application_ids : list[str], optional
|
|
111
|
+
List of application IDs that have access to this integration.
|
|
112
|
+
integration_type : IntegrationType, optional
|
|
113
|
+
The type of the integration (runtime or data).
|
|
114
|
+
exec_types : list[ManifestType], optional
|
|
115
|
+
List of execution types supported by the integration.
|
|
116
|
+
provider : IntegrationProvider, optional
|
|
117
|
+
The provider of the integration.
|
|
118
|
+
provider_config : dict[str, Any], optional
|
|
119
|
+
Configuration specific to the integration provider.
|
|
120
|
+
created_at : datetime, optional
|
|
121
|
+
The timestamp when the integration was created.
|
|
122
|
+
updated_at : datetime, optional
|
|
123
|
+
The timestamp when the integration was last updated.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
integration_id: str = Field(
|
|
127
|
+
serialization_alias="id",
|
|
128
|
+
validation_alias=AliasChoices("id", "integration_id"),
|
|
129
|
+
)
|
|
130
|
+
"""The unique identifier of the integration."""
|
|
131
|
+
client: Client = Field(exclude=True)
|
|
132
|
+
"""Client to use for interacting with the Nextmv Cloud API."""
|
|
133
|
+
|
|
134
|
+
name: str | None = None
|
|
135
|
+
"""The name of the integration."""
|
|
136
|
+
description: str | None = None
|
|
137
|
+
"""An optional description of the integration."""
|
|
138
|
+
is_global: bool = Field(
|
|
139
|
+
serialization_alias="global",
|
|
140
|
+
validation_alias=AliasChoices("global", "is_global"),
|
|
141
|
+
default=False,
|
|
142
|
+
)
|
|
143
|
+
"""
|
|
144
|
+
Indicates whether the integration is global (available to all
|
|
145
|
+
applications in the account).
|
|
146
|
+
"""
|
|
147
|
+
application_ids: list[str] | None = None
|
|
148
|
+
"""
|
|
149
|
+
List of application IDs that have access to this integration.
|
|
150
|
+
"""
|
|
151
|
+
integration_type: IntegrationType | None = Field(
|
|
152
|
+
serialization_alias="type",
|
|
153
|
+
validation_alias=AliasChoices("type", "integration_type"),
|
|
154
|
+
default=None,
|
|
155
|
+
)
|
|
156
|
+
"""The type of the integration (runtime or data)."""
|
|
157
|
+
exec_types: list[ManifestType] | None = None
|
|
158
|
+
"""List of execution types supported by the integration."""
|
|
159
|
+
provider: IntegrationProvider | None = None
|
|
160
|
+
"""The provider of the integration."""
|
|
161
|
+
provider_config: dict[str, Any] | None = None
|
|
162
|
+
"""Configuration specific to the integration provider."""
|
|
163
|
+
created_at: datetime | None = None
|
|
164
|
+
"""The timestamp when the integration was created."""
|
|
165
|
+
updated_at: datetime | None = None
|
|
166
|
+
"""The timestamp when the integration was last updated."""
|
|
167
|
+
endpoint: str = Field(
|
|
168
|
+
exclude=True,
|
|
169
|
+
default="v1/integrations/{id}",
|
|
170
|
+
)
|
|
171
|
+
"""Base endpoint for the integration."""
|
|
172
|
+
|
|
173
|
+
def model_post_init(self, __context) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Validations done after model initialization.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
self.endpoint = self.endpoint.format(id=self.integration_id)
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def get(cls, client: Client, integration_id: str) -> "Integration":
|
|
182
|
+
"""
|
|
183
|
+
Retrieve an existing integration from Nextmv Cloud.
|
|
184
|
+
|
|
185
|
+
This method should be used for validating that the integration exists,
|
|
186
|
+
and not rely simply on instantiating the `Integration` class. Using
|
|
187
|
+
this method ensures that all the fields of the `Integration` class are
|
|
188
|
+
properly populated.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
client : Client
|
|
193
|
+
Client to use for interacting with the Nextmv Cloud API.
|
|
194
|
+
integration_id : str
|
|
195
|
+
The unique identifier of the integration to retrieve.
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
Integration
|
|
200
|
+
The retrieved integration instance.
|
|
201
|
+
|
|
202
|
+
Raises
|
|
203
|
+
------
|
|
204
|
+
requests.HTTPError
|
|
205
|
+
If the response status code is not 2xx.
|
|
206
|
+
|
|
207
|
+
Examples
|
|
208
|
+
--------
|
|
209
|
+
>>> from nextmv.cloud import Client, Integration
|
|
210
|
+
>>> client = Client(api_key="your_api_key")
|
|
211
|
+
>>> integration = Integration.get(client=client, integration_id="your_integration_id")
|
|
212
|
+
>>> print(integration.to_dict())
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
response = client.request(
|
|
216
|
+
method="GET",
|
|
217
|
+
endpoint=f"v1/integrations/{integration_id}",
|
|
218
|
+
)
|
|
219
|
+
response_dict = response.json()
|
|
220
|
+
response_dict["client"] = client
|
|
221
|
+
|
|
222
|
+
return cls.from_dict(response_dict)
|
|
223
|
+
|
|
224
|
+
@classmethod
|
|
225
|
+
def new( # noqa: C901
|
|
226
|
+
cls,
|
|
227
|
+
client: Client,
|
|
228
|
+
name: str,
|
|
229
|
+
integration_type: IntegrationType | str,
|
|
230
|
+
exec_types: list[ManifestType | str],
|
|
231
|
+
provider: IntegrationProvider | str,
|
|
232
|
+
provider_config: dict[str, Any],
|
|
233
|
+
integration_id: str | None = None,
|
|
234
|
+
description: str | None = None,
|
|
235
|
+
is_global: bool = False,
|
|
236
|
+
application_ids: list[str] | None = None,
|
|
237
|
+
exist_ok: bool = False,
|
|
238
|
+
) -> "Integration":
|
|
239
|
+
"""
|
|
240
|
+
Create a new integration directly in Nextmv Cloud.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
client : Client
|
|
245
|
+
Client to use for interacting with the Nextmv Cloud API.
|
|
246
|
+
name : str
|
|
247
|
+
The name of the integration.
|
|
248
|
+
integration_type : IntegrationType | str
|
|
249
|
+
The type of the integration. Please refer to the `IntegrationType`
|
|
250
|
+
enum for possible values.
|
|
251
|
+
exec_types : list[ManifestType | str]
|
|
252
|
+
List of execution types supported by the integration. Please refer
|
|
253
|
+
to the `ManifestType` enum for possible values.
|
|
254
|
+
provider : IntegrationProvider | str
|
|
255
|
+
The provider of the integration. Please refer to the
|
|
256
|
+
`IntegrationProvider` enum for possible values.
|
|
257
|
+
provider_config : dict[str, Any]
|
|
258
|
+
Configuration specific to the integration provider.
|
|
259
|
+
integration_id : str, optional
|
|
260
|
+
The unique identifier of the integration. If not provided,
|
|
261
|
+
it will be generated automatically.
|
|
262
|
+
description : str, optional
|
|
263
|
+
An optional description of the integration.
|
|
264
|
+
is_global : bool, optional, default=False
|
|
265
|
+
Indicates whether the integration is global (available to all
|
|
266
|
+
applications in the account). Default is False.
|
|
267
|
+
application_ids : list[str], optional
|
|
268
|
+
List of application IDs that have access to this integration.
|
|
269
|
+
exist_ok : bool, default=False
|
|
270
|
+
If True and an integration with the same ID already exists,
|
|
271
|
+
return the existing integration instead of creating a new one.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
Integration
|
|
276
|
+
The created integration instance.
|
|
277
|
+
|
|
278
|
+
Raises
|
|
279
|
+
------
|
|
280
|
+
requests.HTTPError
|
|
281
|
+
If the response status code is not 2xx.
|
|
282
|
+
ValueError
|
|
283
|
+
If both `is_global` is True and `application_ids` is provided.
|
|
284
|
+
|
|
285
|
+
Examples
|
|
286
|
+
--------
|
|
287
|
+
>>> from nextmv.cloud import Client, Integration, IntegrationType, IntegrationProvider, ManifestType
|
|
288
|
+
>>> client = Client(api_key="your_api_key")
|
|
289
|
+
>>> integration = Integration.new(
|
|
290
|
+
... client=client,
|
|
291
|
+
... name="my_integration",
|
|
292
|
+
... integration_type=IntegrationType.RUNTIME,
|
|
293
|
+
... exec_types=[ManifestType.PYTHON],
|
|
294
|
+
... provider=IntegrationProvider.DBX,
|
|
295
|
+
... provider_config={"config_key": "config_value"},
|
|
296
|
+
... )
|
|
297
|
+
>>> print(integration.to_dict())
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
if is_global and application_ids is not None:
|
|
301
|
+
raise ValueError("An integration cannot be global and have specific application IDs.")
|
|
302
|
+
elif not is_global and application_ids is None:
|
|
303
|
+
raise ValueError("A non-global integration must have specific application IDs.")
|
|
304
|
+
|
|
305
|
+
if integration_id is None:
|
|
306
|
+
integration_id = safe_id("integration")
|
|
307
|
+
|
|
308
|
+
if exist_ok:
|
|
309
|
+
try:
|
|
310
|
+
integration = cls.get(client=client, integration_id=integration_id)
|
|
311
|
+
return integration
|
|
312
|
+
except Exception:
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
if not isinstance(integration_type, IntegrationType):
|
|
316
|
+
integration_type = IntegrationType(integration_type)
|
|
317
|
+
|
|
318
|
+
if not all(isinstance(exec_type, ManifestType) for exec_type in exec_types):
|
|
319
|
+
exec_types = [ManifestType(exec_type) for exec_type in exec_types]
|
|
320
|
+
|
|
321
|
+
if not isinstance(provider, IntegrationProvider):
|
|
322
|
+
provider = IntegrationProvider(provider)
|
|
323
|
+
|
|
324
|
+
payload = {
|
|
325
|
+
"id": integration_id,
|
|
326
|
+
"name": name,
|
|
327
|
+
"global": is_global,
|
|
328
|
+
"type": integration_type.value,
|
|
329
|
+
"exec_types": [exec_type.value for exec_type in exec_types],
|
|
330
|
+
"provider": provider.value,
|
|
331
|
+
"provider_config": provider_config,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if description is not None:
|
|
335
|
+
payload["description"] = description
|
|
336
|
+
|
|
337
|
+
if application_ids is not None:
|
|
338
|
+
payload["application_ids"] = application_ids
|
|
339
|
+
|
|
340
|
+
response = client.request(
|
|
341
|
+
method="POST",
|
|
342
|
+
endpoint="v1/integrations",
|
|
343
|
+
payload=payload,
|
|
344
|
+
)
|
|
345
|
+
response_dict = response.json()
|
|
346
|
+
response_dict["client"] = client
|
|
347
|
+
integration = cls.from_dict(response_dict)
|
|
348
|
+
|
|
349
|
+
return integration
|
|
350
|
+
|
|
351
|
+
def delete(self) -> None:
|
|
352
|
+
"""
|
|
353
|
+
Deletes the integration from Nextmv Cloud.
|
|
354
|
+
|
|
355
|
+
Raises
|
|
356
|
+
------
|
|
357
|
+
requests.HTTPError
|
|
358
|
+
If the response status code is not 2xx.
|
|
359
|
+
|
|
360
|
+
Examples
|
|
361
|
+
--------
|
|
362
|
+
>>> from nextmv.cloud import Client, Integration
|
|
363
|
+
>>> client = Client(api_key="your_api_key")
|
|
364
|
+
>>> integration = Integration.get(client=client, integration_id="your_integration_id")
|
|
365
|
+
>>> integration.delete()
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
_ = self.client.request(
|
|
369
|
+
method="DELETE",
|
|
370
|
+
endpoint=self.endpoint,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def update( # noqa: C901
|
|
374
|
+
self,
|
|
375
|
+
name: str | None = None,
|
|
376
|
+
integration_type: IntegrationType | str | None = None,
|
|
377
|
+
exec_types: list[ManifestType | str] | None = None,
|
|
378
|
+
provider: IntegrationProvider | str | None = None,
|
|
379
|
+
provider_config: dict[str, Any] | None = None,
|
|
380
|
+
description: str | None = None,
|
|
381
|
+
is_global: bool | None = None,
|
|
382
|
+
application_ids: list[str] | None = None,
|
|
383
|
+
) -> "Integration":
|
|
384
|
+
"""
|
|
385
|
+
Updates the integration in Nextmv Cloud.
|
|
386
|
+
|
|
387
|
+
Parameters
|
|
388
|
+
----------
|
|
389
|
+
name : str, optional
|
|
390
|
+
The new name of the integration.
|
|
391
|
+
integration_type : IntegrationType | str, optional
|
|
392
|
+
The new type of the integration. Please refer to the `IntegrationType`
|
|
393
|
+
enum for possible values.
|
|
394
|
+
exec_types : list[ManifestType | str], optional
|
|
395
|
+
New list of execution types supported by the integration. Please refer
|
|
396
|
+
to the `ManifestType` enum for possible values.
|
|
397
|
+
provider : IntegrationProvider | str, optional
|
|
398
|
+
The new provider of the integration. Please refer to the
|
|
399
|
+
`IntegrationProvider` enum for possible values.
|
|
400
|
+
provider_config : dict[str, Any], optional
|
|
401
|
+
New configuration specific to the integration provider.
|
|
402
|
+
description : str, optional
|
|
403
|
+
The new description of the integration.
|
|
404
|
+
is_global : bool, optional
|
|
405
|
+
Indicates whether the integration is global (available to all
|
|
406
|
+
applications in the account). If not provided, the current value
|
|
407
|
+
is preserved.
|
|
408
|
+
application_ids : list[str], optional
|
|
409
|
+
New list of application IDs that have access to this integration.
|
|
410
|
+
|
|
411
|
+
Returns
|
|
412
|
+
-------
|
|
413
|
+
Integration
|
|
414
|
+
The updated integration instance.
|
|
415
|
+
|
|
416
|
+
Raises
|
|
417
|
+
------
|
|
418
|
+
requests.HTTPError
|
|
419
|
+
If the response status code is not 2xx.
|
|
420
|
+
|
|
421
|
+
Examples
|
|
422
|
+
--------
|
|
423
|
+
>>> from nextmv.cloud import Client, Integration
|
|
424
|
+
>>> client = Client(api_key="your_api_key")
|
|
425
|
+
>>> integration = Integration.get(client=client, integration_id="your_integration_id")
|
|
426
|
+
>>> updated_integration = integration.update(name="new_name")
|
|
427
|
+
>>> print(updated_integration.to_dict())
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
integration = self.get(client=self.client, integration_id=self.integration_id)
|
|
431
|
+
integration_dict = integration.to_dict()
|
|
432
|
+
payload = integration_dict
|
|
433
|
+
|
|
434
|
+
if name is not None:
|
|
435
|
+
payload["name"] = name
|
|
436
|
+
|
|
437
|
+
if integration_type is not None:
|
|
438
|
+
if not isinstance(integration_type, IntegrationType):
|
|
439
|
+
integration_type = IntegrationType(integration_type)
|
|
440
|
+
payload["type"] = integration_type.value
|
|
441
|
+
|
|
442
|
+
if exec_types is not None:
|
|
443
|
+
if not all(isinstance(exec_type, ManifestType) for exec_type in exec_types):
|
|
444
|
+
exec_types = [ManifestType(exec_type) for exec_type in exec_types]
|
|
445
|
+
payload["exec_types"] = [exec_type.value for exec_type in exec_types]
|
|
446
|
+
|
|
447
|
+
if provider is not None:
|
|
448
|
+
if not isinstance(provider, IntegrationProvider):
|
|
449
|
+
provider = IntegrationProvider(provider)
|
|
450
|
+
payload["provider"] = provider.value
|
|
451
|
+
|
|
452
|
+
if provider_config is not None:
|
|
453
|
+
payload["provider_config"] = provider_config
|
|
454
|
+
|
|
455
|
+
if description is not None:
|
|
456
|
+
payload["description"] = description
|
|
457
|
+
|
|
458
|
+
if is_global is not None:
|
|
459
|
+
payload["global"] = is_global
|
|
460
|
+
|
|
461
|
+
if application_ids is not None:
|
|
462
|
+
payload["application_ids"] = application_ids
|
|
463
|
+
|
|
464
|
+
# Final validation: ensure invariants are met.
|
|
465
|
+
if payload["global"] is True and payload.get("application_ids"):
|
|
466
|
+
raise ValueError(
|
|
467
|
+
"An integration cannot be global and have application_ids. "
|
|
468
|
+
"To make an integration global, call update(is_global=True, application_ids=[])."
|
|
469
|
+
)
|
|
470
|
+
if payload["global"] is False and not payload.get("application_ids"):
|
|
471
|
+
raise ValueError(
|
|
472
|
+
"A non-global integration must have specific application IDs. "
|
|
473
|
+
"Provide application_ids with at least one ID, or set is_global=True."
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
response = self.client.request(
|
|
477
|
+
method="PUT",
|
|
478
|
+
endpoint=self.endpoint,
|
|
479
|
+
payload=payload,
|
|
480
|
+
)
|
|
481
|
+
response_dict = response.json()
|
|
482
|
+
response_dict["client"] = self.client
|
|
483
|
+
integration = self.from_dict(response_dict)
|
|
484
|
+
|
|
485
|
+
return integration
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def list_integrations(client: Client) -> list[Integration]:
|
|
489
|
+
"""
|
|
490
|
+
List all integrations in Nextmv Cloud for the given client.
|
|
491
|
+
|
|
492
|
+
You can import the `list_integrations` method directly from `cloud`:
|
|
493
|
+
|
|
494
|
+
```python
|
|
495
|
+
from nextmv.cloud import list_integrations
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Parameters
|
|
499
|
+
----------
|
|
500
|
+
client : Client
|
|
501
|
+
Client to use for interacting with the Nextmv Cloud API.
|
|
502
|
+
|
|
503
|
+
Returns
|
|
504
|
+
-------
|
|
505
|
+
list[Integration]
|
|
506
|
+
List of integrations.
|
|
507
|
+
|
|
508
|
+
Raises
|
|
509
|
+
------
|
|
510
|
+
requests.HTTPError
|
|
511
|
+
If the response status code is not 2xx.
|
|
512
|
+
|
|
513
|
+
Examples
|
|
514
|
+
--------
|
|
515
|
+
>>> from nextmv.cloud import Client, list_integrations
|
|
516
|
+
>>> client = Client(api_key="your_api_key")
|
|
517
|
+
>>> integrations = list_integrations(client=client)
|
|
518
|
+
>>> for integration in integrations:
|
|
519
|
+
... print(integration.to_dict())
|
|
520
|
+
"""
|
|
521
|
+
|
|
522
|
+
response = client.request(
|
|
523
|
+
method="GET",
|
|
524
|
+
endpoint="v1/integrations",
|
|
525
|
+
)
|
|
526
|
+
response_dict = response.json()
|
|
527
|
+
integrations = []
|
|
528
|
+
for integration_data in response_dict.get("items", []):
|
|
529
|
+
integration_data["client"] = client
|
|
530
|
+
integration = Integration.from_dict(integration_data)
|
|
531
|
+
integrations.append(integration)
|
|
532
|
+
|
|
533
|
+
return integrations
|
|
@@ -330,12 +330,12 @@ def __install_dependencies( # noqa: C901 # complexity
|
|
|
330
330
|
result = subprocess.run(
|
|
331
331
|
command,
|
|
332
332
|
cwd=app_dir,
|
|
333
|
+
stdout=subprocess.PIPE,
|
|
334
|
+
stderr=subprocess.STDOUT, # Merge stderr into stdout
|
|
333
335
|
text=True,
|
|
334
|
-
capture_output=True,
|
|
335
|
-
check=True,
|
|
336
336
|
)
|
|
337
337
|
if result.returncode != 0:
|
|
338
|
-
raise Exception(f"error installing dependencies: {result.
|
|
338
|
+
raise Exception(f"error installing dependencies: {os.linesep}{result.stdout}")
|
|
339
339
|
|
|
340
340
|
|
|
341
341
|
def __run_command(binary: str, dir: str, redirect_out_err: bool, *arguments: str) -> str:
|
|
@@ -284,7 +284,7 @@ class Model:
|
|
|
284
284
|
import mlflow as mlflow
|
|
285
285
|
except ImportError as e:
|
|
286
286
|
raise ImportError(
|
|
287
|
-
"mlflow is not installed. Please install optional dependencies with `pip install nextmv[
|
|
287
|
+
"mlflow is not installed. Please install optional dependencies with `pip install nextmv[notebook]`"
|
|
288
288
|
) from e
|
|
289
289
|
|
|
290
290
|
finally:
|
|
@@ -51,10 +51,12 @@ Repository = "https://github.com/nextmv-io/nextmv-py"
|
|
|
51
51
|
|
|
52
52
|
[project.optional-dependencies]
|
|
53
53
|
all = [
|
|
54
|
-
"mlflow>=2.17.2",
|
|
55
54
|
"folium>=0.20.0",
|
|
56
55
|
"plotly>=6.0.1",
|
|
57
56
|
]
|
|
57
|
+
notebook = [
|
|
58
|
+
"mlflow>=2.19.0",
|
|
59
|
+
]
|
|
58
60
|
dev = [
|
|
59
61
|
"build>=1.0.3",
|
|
60
62
|
"pydantic>=2.5.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "v0.37.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|