terrakio-core 0.3.6__tar.gz → 0.3.7__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.
Potentially problematic release.
This version of terrakio-core might be problematic. Click here for more details.
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/PKG-INFO +7 -1
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/pyproject.toml +10 -1
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/__init__.py +1 -1
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/async_client.py +47 -47
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/endpoints/dataset_management.py +6 -4
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/endpoints/mass_stats.py +5 -5
- terrakio_core-0.3.7/terrakio_core/endpoints/model_management.py +790 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/exceptions.py +4 -2
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core.egg-info/PKG-INFO +7 -1
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core.egg-info/requires.txt +7 -0
- terrakio_core-0.3.6/terrakio_core/endpoints/model_management.py +0 -385
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/README.md +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/setup.cfg +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/client.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/config.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/convenience_functions/convenience_functions.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/endpoints/auth.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/endpoints/group_management.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/endpoints/space_management.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/endpoints/user_management.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/helper/bounded_taskgroup.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/helper/decorators.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/helper/tiles.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core/sync_client.py +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core.egg-info/SOURCES.txt +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core.egg-info/dependency_links.txt +0 -0
- {terrakio_core-0.3.6 → terrakio_core-0.3.7}/terrakio_core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: terrakio-core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: Core components for Terrakio API clients
|
|
5
5
|
Author-email: Yupeng Chao <yupeng@haizea.com.au>
|
|
6
6
|
Project-URL: Homepage, https://github.com/HaizeaAnalytics/terrakio-python-api
|
|
@@ -23,6 +23,12 @@ Requires-Dist: shapely>=2.0.0
|
|
|
23
23
|
Requires-Dist: geopandas>=0.13.0
|
|
24
24
|
Requires-Dist: google-cloud-storage>=2.0.0
|
|
25
25
|
Requires-Dist: nest_asyncio
|
|
26
|
+
Provides-Extra: ml
|
|
27
|
+
Requires-Dist: torch>=2.7.1; extra == "ml"
|
|
28
|
+
Requires-Dist: scikit-learn>=1.7.0; extra == "ml"
|
|
29
|
+
Requires-Dist: skl2onnx>=1.19.1; extra == "ml"
|
|
30
|
+
Requires-Dist: onnx>=1.18.0; extra == "ml"
|
|
31
|
+
Requires-Dist: onnxruntime>=1.10.0; extra == "ml"
|
|
26
32
|
|
|
27
33
|
# Terrakio Core
|
|
28
34
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "terrakio-core"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.7"
|
|
8
8
|
authors = [
|
|
9
9
|
{name = "Yupeng Chao", email = "yupeng@haizea.com.au"},
|
|
10
10
|
]
|
|
@@ -32,6 +32,15 @@ dependencies = [
|
|
|
32
32
|
"nest_asyncio",
|
|
33
33
|
]
|
|
34
34
|
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
ml = [
|
|
37
|
+
"torch>=2.7.1",
|
|
38
|
+
"scikit-learn>=1.7.0",
|
|
39
|
+
"skl2onnx>=1.19.1",
|
|
40
|
+
"onnx>=1.18.0",
|
|
41
|
+
"onnxruntime>=1.10.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
35
44
|
[project.urls]
|
|
36
45
|
"Homepage" = "https://github.com/HaizeaAnalytics/terrakio-python-api"
|
|
37
46
|
"Bug Tracker" = "https://github.com/HaizeaAnalytics/terrakio-python-api/issues"
|
|
@@ -46,65 +46,65 @@ class AsyncClient(BaseClient):
|
|
|
46
46
|
else:
|
|
47
47
|
return await self._make_request_with_retry(self._session, method, endpoint, **kwargs)
|
|
48
48
|
|
|
49
|
-
|
|
50
49
|
async def _make_request_with_retry(self, session: aiohttp.ClientSession, method: str, endpoint: str, **kwargs) -> Dict[Any, Any]:
|
|
51
50
|
url = f"{self.url}/{endpoint.lstrip('/')}"
|
|
51
|
+
last_exception = None
|
|
52
|
+
|
|
52
53
|
for attempt in range(self.retry + 1):
|
|
53
|
-
try:
|
|
54
|
+
try:
|
|
54
55
|
async with session.request(method, url, **kwargs) as response:
|
|
55
|
-
|
|
56
|
+
if not response.ok and self._should_retry(response.status, attempt):
|
|
57
|
+
self.logger.info(f"Request failed (attempt {attempt+1}/{self.retry+1}): {response.status}. Retrying...")
|
|
58
|
+
continue
|
|
56
59
|
if not response.ok:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if response.status == 400:
|
|
60
|
-
should_retry = False
|
|
61
|
-
else:
|
|
62
|
-
if response.status in [408, 502, 503, 504]:
|
|
63
|
-
should_retry = True
|
|
64
|
-
elif response.status == 500:
|
|
65
|
-
try:
|
|
66
|
-
response_text = await response.text()
|
|
67
|
-
if "Internal server error" not in response_text:
|
|
68
|
-
should_retry = True
|
|
69
|
-
except:
|
|
70
|
-
should_retry = True
|
|
71
|
-
|
|
72
|
-
if should_retry and attempt < self.retry:
|
|
73
|
-
self.logger.info(f"Request failed (attempt {attempt+1}/{self.retry+1}): {response.status} {response.reason}. Retrying...")
|
|
74
|
-
continue
|
|
75
|
-
else:
|
|
76
|
-
error_msg = f"API request failed: {response.status} {response.reason}"
|
|
77
|
-
try:
|
|
78
|
-
error_data = await response.json()
|
|
79
|
-
if "detail" in error_data:
|
|
80
|
-
error_msg += f" - {error_data['detail']}"
|
|
81
|
-
except:
|
|
82
|
-
pass
|
|
83
|
-
raise APIError(error_msg, status_code=response.status)
|
|
84
|
-
|
|
85
|
-
content_type = response.headers.get('content-type', '').lower()
|
|
86
|
-
content = await response.read()
|
|
87
|
-
if 'json' in content_type:
|
|
88
|
-
return json.loads(content.decode('utf-8'))
|
|
89
|
-
elif 'csv' in content_type:
|
|
90
|
-
return pd.read_csv(BytesIO(content))
|
|
91
|
-
elif 'image/' in content_type:
|
|
92
|
-
return content
|
|
93
|
-
elif 'text' in content_type:
|
|
94
|
-
return content.decode('utf-8')
|
|
95
|
-
else:
|
|
60
|
+
error_msg = f"API request failed: {response.status} {response.reason}"
|
|
96
61
|
try:
|
|
97
|
-
|
|
62
|
+
error_data = await response.json()
|
|
63
|
+
if "detail" in error_data:
|
|
64
|
+
error_msg += f" - {error_data['detail']}"
|
|
98
65
|
except:
|
|
99
|
-
|
|
66
|
+
pass
|
|
67
|
+
raise APIError(error_msg, status_code=response.status)
|
|
68
|
+
return await self._parse_response(response)
|
|
69
|
+
|
|
100
70
|
except aiohttp.ClientError as e:
|
|
71
|
+
last_exception = e
|
|
101
72
|
if attempt < self.retry:
|
|
102
|
-
self.logger.info(f"
|
|
73
|
+
self.logger.info(f"Networking error (attempt {attempt+1}/{self.retry+1}): {e}. Retrying...")
|
|
103
74
|
continue
|
|
104
75
|
else:
|
|
105
|
-
|
|
106
|
-
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
raise APIError(f"Networking error, request failed after {self.retry+1} attempts: {last_exception}", status_code=None)
|
|
79
|
+
|
|
80
|
+
def _should_retry(self, status_code: int, attempt: int) -> bool:
|
|
81
|
+
"""Determine if the request should be retried based on status code."""
|
|
82
|
+
if attempt >= self.retry:
|
|
83
|
+
return False
|
|
84
|
+
elif status_code in [408, 502, 503, 504]:
|
|
85
|
+
return True
|
|
86
|
+
else:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
async def _parse_response(self, response) -> Any:
|
|
90
|
+
"""Parse response based on content type."""
|
|
91
|
+
content_type = response.headers.get('content-type', '').lower()
|
|
92
|
+
content = await response.read()
|
|
107
93
|
|
|
94
|
+
if 'json' in content_type:
|
|
95
|
+
return json.loads(content.decode('utf-8'))
|
|
96
|
+
elif 'csv' in content_type:
|
|
97
|
+
return pd.read_csv(BytesIO(content))
|
|
98
|
+
elif 'image/' in content_type:
|
|
99
|
+
return content
|
|
100
|
+
elif 'text' in content_type:
|
|
101
|
+
return content.decode('utf-8')
|
|
102
|
+
else:
|
|
103
|
+
try:
|
|
104
|
+
return xr.open_dataset(BytesIO(content))
|
|
105
|
+
except:
|
|
106
|
+
raise APIError(f"Unknown response format: {content_type}", status_code=response.status)
|
|
107
|
+
|
|
108
108
|
async def _regular_request(self, method: str, endpoint: str, **kwargs):
|
|
109
109
|
url = endpoint.lstrip('/')
|
|
110
110
|
if self._session is None:
|
|
@@ -42,7 +42,7 @@ class DatasetManagement:
|
|
|
42
42
|
return self._client._terrakio_request("GET", f"/datasets/{name}", params = params)
|
|
43
43
|
|
|
44
44
|
@require_api_key
|
|
45
|
-
def create_dataset(
|
|
45
|
+
async def create_dataset(
|
|
46
46
|
self,
|
|
47
47
|
name: str,
|
|
48
48
|
collection: str = "terrakio-datasets",
|
|
@@ -59,7 +59,8 @@ class DatasetManagement:
|
|
|
59
59
|
proj4: Optional[str] = None,
|
|
60
60
|
abstract: Optional[str] = None,
|
|
61
61
|
geotransform: Optional[List[float]] = None,
|
|
62
|
-
padding: Optional[Any] = None
|
|
62
|
+
padding: Optional[Any] = None,
|
|
63
|
+
input: Optional[str] = None
|
|
63
64
|
) -> Dict[str, Any]:
|
|
64
65
|
"""
|
|
65
66
|
Create a new dataset.
|
|
@@ -104,12 +105,13 @@ class DatasetManagement:
|
|
|
104
105
|
"proj4": proj4,
|
|
105
106
|
"abstract": abstract,
|
|
106
107
|
"geotransform": geotransform,
|
|
107
|
-
"padding": padding
|
|
108
|
+
"padding": padding,
|
|
109
|
+
"input": input
|
|
108
110
|
}
|
|
109
111
|
for param, value in param_mapping.items():
|
|
110
112
|
if value is not None:
|
|
111
113
|
payload[param] = value
|
|
112
|
-
return self._client._terrakio_request("POST", "/datasets", params = params, json = payload)
|
|
114
|
+
return await self._client._terrakio_request("POST", "/datasets", params = params, json = payload)
|
|
113
115
|
|
|
114
116
|
@require_api_key
|
|
115
117
|
def update_dataset(
|
|
@@ -12,7 +12,7 @@ class MassStats:
|
|
|
12
12
|
self._client = client
|
|
13
13
|
|
|
14
14
|
@require_api_key
|
|
15
|
-
async def
|
|
15
|
+
async def _upload_request(
|
|
16
16
|
self,
|
|
17
17
|
name: str,
|
|
18
18
|
size: int,
|
|
@@ -220,7 +220,7 @@ class MassStats:
|
|
|
220
220
|
return self._client._terrakio_request("GET", "mass_stats/download", params=params)
|
|
221
221
|
|
|
222
222
|
@require_api_key
|
|
223
|
-
async def
|
|
223
|
+
async def _upload_file(self, file_path: str, url: str, use_gzip: bool = False):
|
|
224
224
|
"""
|
|
225
225
|
Helper method to upload a JSON file to a signed URL.
|
|
226
226
|
|
|
@@ -427,7 +427,7 @@ class MassStats:
|
|
|
427
427
|
return e
|
|
428
428
|
except json.JSONDecodeError as e:
|
|
429
429
|
return e
|
|
430
|
-
upload_result = await self.
|
|
430
|
+
upload_result = await self._upload_request(name = name, size = size, region = region, output = output, config = config, location = location, force_loc = force_loc, overwrite = overwrite, server = server, skip_existing = skip_existing)
|
|
431
431
|
requests_url = upload_result.get('requests_url')
|
|
432
432
|
manifest_url = upload_result.get('manifest_url')
|
|
433
433
|
if not requests_url:
|
|
@@ -436,7 +436,7 @@ class MassStats:
|
|
|
436
436
|
try:
|
|
437
437
|
# in this place we are uploading the request json file, we need to check whether the json is in the correct format or not
|
|
438
438
|
self.validate_request(request_json)
|
|
439
|
-
requests_response = await self.
|
|
439
|
+
requests_response = await self._upload_file(request_json, requests_url, use_gzip=True)
|
|
440
440
|
if requests_response.status not in [200, 201, 204]:
|
|
441
441
|
self._client.logger.error(f"Requests upload error: {requests_response.text()}")
|
|
442
442
|
raise Exception(f"Failed to upload request JSON: {requests_response.text()}")
|
|
@@ -447,7 +447,7 @@ class MassStats:
|
|
|
447
447
|
raise ValueError("No manifest_url returned from server for manifest JSON upload")
|
|
448
448
|
|
|
449
449
|
try:
|
|
450
|
-
manifest_response = await self.
|
|
450
|
+
manifest_response = await self._upload_file(manifest_json, manifest_url, use_gzip=False)
|
|
451
451
|
if manifest_response.status not in [200, 201, 204]:
|
|
452
452
|
self._client.logger.error(f"Manifest upload error: {manifest_response.text()}")
|
|
453
453
|
raise Exception(f"Failed to upload manifest JSON: {manifest_response.text()}")
|