nextmv 0.37.2__tar.gz → 0.38.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.
- {nextmv-0.37.2 → nextmv-0.38.0}/PKG-INFO +1 -1
- nextmv-0.38.0/nextmv/__about__.py +1 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/__init__.py +1 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/application.py +117 -0
- nextmv-0.38.0/nextmv/cloud/assets.py +48 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/output.py +10 -2
- nextmv-0.37.2/nextmv/__about__.py +0 -1
- {nextmv-0.37.2 → nextmv-0.38.0}/.gitignore +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/LICENSE +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/README.md +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/__entrypoint__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/_serialization.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/base_model.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/account.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/client.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/input_set.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/instance.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/integration.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/package.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/scenario.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/secrets.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/url.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/cloud/version.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/.gitignore +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/README.md +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/app.yaml +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/input.json +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/main.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/requirements.txt +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/src/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/default_app/src/visuals.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/deprecated.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/input.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/application.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/executor.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/geojson_handler.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/local.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/local/runner.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/logger.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/manifest.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/model.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/options.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/polling.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/run.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/safe.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/nextmv/status.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/pyproject.toml +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/cloud/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/cloud/app.yaml +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/cloud/test_client.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/cloud/test_instance.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/cloud/test_package.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/cloud/test_scenario.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/local/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/local/test_application.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/local/test_executor.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/local/test_runner.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options1.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options2.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options3.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options4.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options5.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options6.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options7.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/scripts/options_deprecated.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_base_model.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_input.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_inputs/test_data.json +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_logger.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_manifest.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_model.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_options.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_output.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_polling.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_run.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_safe.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_serialization.py +0 -0
- {nextmv-0.37.2 → nextmv-0.38.0}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v0.38.0"
|
|
@@ -57,6 +57,7 @@ from .account import Queue as Queue
|
|
|
57
57
|
from .account import QueuedRun as QueuedRun
|
|
58
58
|
from .application import Application as Application
|
|
59
59
|
from .application import poll as poll
|
|
60
|
+
from .assets import RunAsset as RunAsset
|
|
60
61
|
from .batch_experiment import BatchExperiment as BatchExperiment
|
|
61
62
|
from .batch_experiment import BatchExperimentInformation as BatchExperimentInformation
|
|
62
63
|
from .batch_experiment import BatchExperimentMetadata as BatchExperimentMetadata
|
|
@@ -22,8 +22,10 @@ poll
|
|
|
22
22
|
Function to poll for results with configurable options.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
+
import io
|
|
25
26
|
import json
|
|
26
27
|
import os
|
|
28
|
+
import pathlib
|
|
27
29
|
import shutil
|
|
28
30
|
import tarfile
|
|
29
31
|
import tempfile
|
|
@@ -37,6 +39,7 @@ from nextmv._serialization import deflated_serialize_json
|
|
|
37
39
|
from nextmv.base_model import BaseModel
|
|
38
40
|
from nextmv.cloud import package
|
|
39
41
|
from nextmv.cloud.acceptance_test import AcceptanceTest, Metric
|
|
42
|
+
from nextmv.cloud.assets import RunAsset
|
|
40
43
|
from nextmv.cloud.batch_experiment import (
|
|
41
44
|
BatchExperiment,
|
|
42
45
|
BatchExperimentInformation,
|
|
@@ -2539,6 +2542,120 @@ class Application:
|
|
|
2539
2542
|
except OSError as e:
|
|
2540
2543
|
raise Exception(f"error deleting output directory: {e}") from e
|
|
2541
2544
|
|
|
2545
|
+
def list_assets(self, run_id: str) -> list[RunAsset]:
|
|
2546
|
+
"""
|
|
2547
|
+
List the assets of a run.
|
|
2548
|
+
|
|
2549
|
+
Retrieves a list of assets associated with a specific run. This method ONLY
|
|
2550
|
+
returns the asset metadata, the content needs to be fetched via the
|
|
2551
|
+
`download_asset_content` method.
|
|
2552
|
+
|
|
2553
|
+
Parameters
|
|
2554
|
+
----------
|
|
2555
|
+
run_id : str
|
|
2556
|
+
ID of the run to list assets for.
|
|
2557
|
+
|
|
2558
|
+
Returns
|
|
2559
|
+
-------
|
|
2560
|
+
list[RunAsset]
|
|
2561
|
+
List of assets associated with the run.
|
|
2562
|
+
|
|
2563
|
+
Raises
|
|
2564
|
+
------
|
|
2565
|
+
requests.HTTPError
|
|
2566
|
+
If the response status code is not 2xx.
|
|
2567
|
+
|
|
2568
|
+
Examples
|
|
2569
|
+
--------
|
|
2570
|
+
>>> assets = app.list_assets("run-123")
|
|
2571
|
+
>>> for asset in assets:
|
|
2572
|
+
... print(asset.id, asset.name)
|
|
2573
|
+
b459daa6-1c13-48c6-b4c3-a262ea94cd04 clustering_polygons
|
|
2574
|
+
a1234567-89ab-cdef-0123-456789abcdef histogram
|
|
2575
|
+
"""
|
|
2576
|
+
response = self.client.request(
|
|
2577
|
+
method="GET",
|
|
2578
|
+
endpoint=f"{self.endpoint}/runs/{run_id}/assets",
|
|
2579
|
+
)
|
|
2580
|
+
assets_data = response.json().get("items", [])
|
|
2581
|
+
for asset_data in assets_data:
|
|
2582
|
+
asset_data["run_id"] = run_id
|
|
2583
|
+
return [RunAsset.from_dict(asset) for asset in assets_data]
|
|
2584
|
+
|
|
2585
|
+
def download_asset_content(
|
|
2586
|
+
self,
|
|
2587
|
+
asset: RunAsset,
|
|
2588
|
+
destination: str | pathlib.Path | io.BytesIO | None = None,
|
|
2589
|
+
) -> Any | None:
|
|
2590
|
+
"""
|
|
2591
|
+
Downloads an asset's content to a specified destination.
|
|
2592
|
+
|
|
2593
|
+
Parameters
|
|
2594
|
+
----------
|
|
2595
|
+
asset : RunAsset
|
|
2596
|
+
The asset to be downloaded.
|
|
2597
|
+
destination : Union[str, pathlib.Path, io.BytesIO, None]
|
|
2598
|
+
The destination where the asset will be saved. This can be a file path
|
|
2599
|
+
(as a string or pathlib.Path) or an io.BytesIO object. If None, the asset
|
|
2600
|
+
content will not be saved to a file, but returned immediately. If the asset
|
|
2601
|
+
type is JSON, the content will be returned as a dict.
|
|
2602
|
+
|
|
2603
|
+
Returns
|
|
2604
|
+
-------
|
|
2605
|
+
Any or None
|
|
2606
|
+
If `destination` is None, returns the asset content: for JSON assets, a
|
|
2607
|
+
`dict` parsed from the JSON response; for other asset types, the raw
|
|
2608
|
+
`bytes` content. If `destination` is provided, the content is written
|
|
2609
|
+
to the given destination and the method returns `None`.
|
|
2610
|
+
|
|
2611
|
+
Raises
|
|
2612
|
+
------
|
|
2613
|
+
requests.HTTPError
|
|
2614
|
+
If the response status code is not 2xx.
|
|
2615
|
+
|
|
2616
|
+
Examples
|
|
2617
|
+
--------
|
|
2618
|
+
>>> assets = app.list_assets("run-123")
|
|
2619
|
+
>>> asset = assets[0] # Assume we want to download the first asset
|
|
2620
|
+
>>> # Download to a file path
|
|
2621
|
+
>>> app.download_asset_content(asset, "polygons.geojson")
|
|
2622
|
+
>>> # Download to an in-memory bytes buffer
|
|
2623
|
+
>>> import io
|
|
2624
|
+
>>> buffer = io.BytesIO()
|
|
2625
|
+
>>> app.download_asset_content(asset, buffer)
|
|
2626
|
+
>>> # Download and get content directly (for JSON assets)
|
|
2627
|
+
>>> content = app.download_asset_content(asset)
|
|
2628
|
+
>>> print(content)
|
|
2629
|
+
{'type': 'FeatureCollection', 'features': [...]}
|
|
2630
|
+
"""
|
|
2631
|
+
# First, get the download_url for the asset.
|
|
2632
|
+
download_url_response = self.client.request(
|
|
2633
|
+
method="GET",
|
|
2634
|
+
endpoint=f"{self.endpoint}/runs/{asset.run_id}/assets/{asset.id}",
|
|
2635
|
+
).json()
|
|
2636
|
+
download_url = download_url_response["download_url"]
|
|
2637
|
+
asset_type = download_url_response.get("type", "json")
|
|
2638
|
+
|
|
2639
|
+
# Now, download the asset content using the download_url.
|
|
2640
|
+
download_response = self.client.request(
|
|
2641
|
+
method="GET",
|
|
2642
|
+
endpoint=download_url,
|
|
2643
|
+
headers={"Content-Type": "application/json" if asset_type == "json" else "application/octet-stream"},
|
|
2644
|
+
)
|
|
2645
|
+
|
|
2646
|
+
# Save the content to the specified destination.
|
|
2647
|
+
if destination is None:
|
|
2648
|
+
if asset_type == "json":
|
|
2649
|
+
return download_response.json()
|
|
2650
|
+
return download_response.content
|
|
2651
|
+
elif isinstance(destination, io.BytesIO):
|
|
2652
|
+
destination.write(download_response.content)
|
|
2653
|
+
return None
|
|
2654
|
+
else:
|
|
2655
|
+
with open(destination, "wb") as file:
|
|
2656
|
+
file.write(download_response.content)
|
|
2657
|
+
return None
|
|
2658
|
+
|
|
2542
2659
|
def run_input(self, run_id: str) -> dict[str, Any]:
|
|
2543
2660
|
"""
|
|
2544
2661
|
Get the input of a run.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from nextmv.base_model import BaseModel
|
|
2
|
+
from nextmv.output import Visual
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RunAsset(BaseModel):
|
|
6
|
+
"""
|
|
7
|
+
Represents an asset associated with a Nextmv Cloud run.
|
|
8
|
+
|
|
9
|
+
You can import the `RunAsset` class from `nextmv.cloud`:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from nextmv.cloud import RunAsset
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
A run asset represents metadata about an asset associated with a Nextmv Cloud run.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
id : str
|
|
20
|
+
Unique identifier of the asset.
|
|
21
|
+
run_id : str
|
|
22
|
+
Identifier of the run associated with the asset.
|
|
23
|
+
name : str
|
|
24
|
+
Name of the asset.
|
|
25
|
+
created_at : str
|
|
26
|
+
Timestamp of when the asset was created.
|
|
27
|
+
size : int
|
|
28
|
+
Size of the asset content in bytes.
|
|
29
|
+
content_type : str
|
|
30
|
+
Content type of the asset. Only `json` is allowed at the moment.
|
|
31
|
+
visual : Visual | None, optional
|
|
32
|
+
Visual schema of the asset, by default None.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
id: str
|
|
36
|
+
"""Unique identifier of the asset."""
|
|
37
|
+
run_id: str
|
|
38
|
+
"""Identifier of the run associated with the asset."""
|
|
39
|
+
name: str
|
|
40
|
+
"""Name of the asset."""
|
|
41
|
+
created_at: str
|
|
42
|
+
"""Timestamp of when the asset was created."""
|
|
43
|
+
size: int
|
|
44
|
+
"""Size of the asset content in bytes."""
|
|
45
|
+
content_type: str
|
|
46
|
+
"""Content type of the asset. Only `json` is allowed at the moment."""
|
|
47
|
+
visual: Visual | None = None
|
|
48
|
+
"""Visual schema of the asset."""
|
|
@@ -469,9 +469,17 @@ class Asset(BaseModel):
|
|
|
469
469
|
|
|
470
470
|
name: str
|
|
471
471
|
"""Name of the asset."""
|
|
472
|
-
content: Any
|
|
473
|
-
"""Content of the asset. The type must be serializable to JSON."""
|
|
474
472
|
|
|
473
|
+
id: str | None = None
|
|
474
|
+
"""
|
|
475
|
+
The ID of the asset. This ID will be populated by the Nextmv platform and can be used
|
|
476
|
+
to download the asset later.
|
|
477
|
+
"""
|
|
478
|
+
content: Any | None = None
|
|
479
|
+
"""
|
|
480
|
+
Content of the asset. The type must be serializable to JSON. Can be empty when
|
|
481
|
+
fetching the asset metadata only (e.g.: via the asset list endpoint).
|
|
482
|
+
"""
|
|
475
483
|
content_type: str | None = "json"
|
|
476
484
|
"""Content type of the asset. Only `json` is allowed"""
|
|
477
485
|
description: str | None = None
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "v0.37.2"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|