evo-objects 0.6.5__tar.gz → 0.6.6__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 (49) hide show
  1. {evo_objects-0.6.5 → evo_objects-0.6.6}/PKG-INFO +2 -2
  2. {evo_objects-0.6.5 → evo_objects-0.6.6}/pyproject.toml +2 -2
  3. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/exceptions.py +25 -1
  4. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/base.py +21 -7
  5. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/utils/data.py +29 -25
  6. {evo_objects-0.6.5 → evo_objects-0.6.6}/.gitignore +0 -0
  7. {evo_objects-0.6.5 → evo_objects-0.6.6}/LICENSE.md +0 -0
  8. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/__init__.py +0 -0
  9. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/_model_config.py +0 -0
  10. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/client/__init__.py +0 -0
  11. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/client/api_client.py +0 -0
  12. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/client/object_client.py +0 -0
  13. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/client/parse.py +0 -0
  14. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/data.py +0 -0
  15. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/__init__.py +0 -0
  16. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/api/__init__.py +0 -0
  17. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/api/data_api.py +0 -0
  18. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/api/metadata_api.py +0 -0
  19. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/api/objects_api.py +0 -0
  20. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/api/stages_api.py +0 -0
  21. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/endpoints/models.py +0 -0
  22. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/io.py +0 -0
  23. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/parquet/__init__.py +0 -0
  24. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/parquet/loader.py +0 -0
  25. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/py.typed +0 -0
  26. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/__init__.py +0 -0
  27. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/_data.py +0 -0
  28. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/_downhole.py +0 -0
  29. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/_grid.py +0 -0
  30. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/_model.py +0 -0
  31. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/_prefetch.py +0 -0
  32. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/_utils.py +0 -0
  33. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/attributes.py +0 -0
  34. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/block_model_ref.py +0 -0
  35. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/downhole_collection.py +0 -0
  36. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/downhole_intervals.py +0 -0
  37. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/exceptions.py +0 -0
  38. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/pointset.py +0 -0
  39. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/regular_grid.py +0 -0
  40. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/regular_masked_grid.py +0 -0
  41. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/spatial.py +0 -0
  42. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/tensor_grid.py +0 -0
  43. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/types.py +0 -0
  44. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/typed/variogram.py +0 -0
  45. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/utils/__init__.py +0 -0
  46. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/utils/downhole.py +0 -0
  47. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/utils/table_formats.py +0 -0
  48. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/utils/tables.py +0 -0
  49. {evo_objects-0.6.5 → evo_objects-0.6.6}/src/evo/objects/utils/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: evo-objects
3
- Version: 0.6.5
3
+ Version: 0.6.6
4
4
  Summary: Python SDK for using the Seequent Evo Geoscience Object API
5
5
  Project-URL: Source, https://github.com/SeequentEvo/evo-python-sdk
6
6
  Project-URL: Tracker, https://github.com/SeequentEvo/evo-python-sdk/issues
@@ -9,7 +9,7 @@ Project-URL: Documentation, https://developer.seequent.com/
9
9
  Author-email: Seequent <support@seequent.com>
10
10
  License-File: LICENSE.md
11
11
  Requires-Python: >=3.10
12
- Requires-Dist: evo-sdk-common[jmespath]>=0.5.28
12
+ Requires-Dist: evo-sdk-common[jmespath]>=0.5.31
13
13
  Requires-Dist: pydantic<3,>=2
14
14
  Provides-Extra: aiohttp
15
15
  Requires-Dist: evo-sdk-common[aiohttp]; extra == 'aiohttp'
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "evo-objects"
3
3
  description = "Python SDK for using the Seequent Evo Geoscience Object API"
4
- version = "0.6.5"
4
+ version = "0.6.6"
5
5
  requires-python = ">=3.10"
6
6
  license-files = ["LICENSE.md"]
7
7
  dynamic = ["readme"]
@@ -10,7 +10,7 @@ authors = [
10
10
  ]
11
11
 
12
12
  dependencies = [
13
- "evo-sdk-common[jmespath]>=0.5.28",
13
+ "evo-sdk-common[jmespath]>=0.5.31",
14
14
  "pydantic>=2,<3",
15
15
  ]
16
16
 
@@ -11,7 +11,7 @@
11
11
 
12
12
  from uuid import UUID
13
13
 
14
- from evo.common.exceptions import CustomTypedError, EvoClientException
14
+ from evo.common.exceptions import BadRequestException, CustomTypedError, EvoClientException
15
15
 
16
16
 
17
17
  class SchemaIDFormatError(EvoClientException):
@@ -41,6 +41,30 @@ class SchemaValidationError(EvoClientException):
41
41
  """Failed to validate data against a geoscience object schema"""
42
42
 
43
43
 
44
+ class InvalidGeoscienceObjectError(CustomTypedError, BadRequestException):
45
+ """The service rejected an object that does not match its geoscience object schema."""
46
+
47
+ TYPE_ID = "/geoscienceobject/schemas/invalid-object"
48
+
49
+ def __str__(self) -> str:
50
+ error_message = super().__str__()
51
+ detail = self.detail
52
+ if detail:
53
+ error_message = error_message.removesuffix(f"\nDetail: {detail}")
54
+ if json_path := self.content.get("json_path"):
55
+ error_message += f"\nInvalid value at: {json_path}"
56
+ if schema_path := self.content.get("schema_path"):
57
+ error_message += f"\nFailed schema rule: {schema_path}"
58
+ if detail:
59
+ if len(detail) > 500:
60
+ detail = (
61
+ f"{detail[:250]} ... [omitted] ... {detail[-250:]}\n"
62
+ "(For the full detail, catch this exception as exc and run print(exc.detail).)"
63
+ )
64
+ error_message += f"\nDetail: {detail}"
65
+ return error_message
66
+
67
+
44
68
  class ObjectAlreadyExistsError(CustomTypedError):
45
69
  TYPE_ID = "/geoscienceobject/objects/already-exists"
46
70
 
@@ -19,8 +19,9 @@ from dataclasses import dataclass
19
19
  from typing import Annotated, Any, ClassVar
20
20
  from uuid import UUID
21
21
 
22
- from evo import jmespath
22
+ from evo import jmespath, logging
23
23
  from evo.common import IContext, IFeedback, StaticContext
24
+ from evo.common.exceptions import error_context
24
25
  from evo.common.utils import NoFeedback
25
26
  from evo.objects import DownloadedObject, ObjectMetadata, ObjectReference, ObjectSchema, SchemaVersion
26
27
 
@@ -36,6 +37,8 @@ if sys.version_info >= (3, 11):
36
37
  else:
37
38
  from typing_extensions import Self
38
39
 
40
+ logger = logging.getLogger("object")
41
+
39
42
  __all__ = [
40
43
  "BaseObject",
41
44
  "BaseObjectData",
@@ -254,9 +257,15 @@ class _BaseObject(SchemaModel):
254
257
 
255
258
  # Take a copy to avoid the context changes affecting the object
256
259
  context = StaticContext.create_copy(context)
257
- object_dict = await cls._data_to_schema(data, context)
260
+ description = f"{cls.__name__} '{data.name}'"
261
+ with error_context(f"While preparing data for {description}", logger=logger):
262
+ object_dict = await cls._data_to_schema(data, context)
258
263
  object_dict["uuid"] = None # New UUID is generated by the service
259
- obj = await create_geoscience_object(context, object_dict, parent, path)
264
+ target = f"path '{path}'" if path is not None else f"parent path '{parent or ''}'"
265
+ with error_context(
266
+ f"While creating {description} at {target} (schema: {object_dict['schema']})", logger=logger
267
+ ):
268
+ obj = await create_geoscience_object(context, object_dict, parent, path)
260
269
  return cls(obj)
261
270
 
262
271
  @classmethod
@@ -285,10 +294,15 @@ class _BaseObject(SchemaModel):
285
294
  workspace_id=reference.workspace_id,
286
295
  )
287
296
 
288
- object_dict = await cls._data_to_schema(data, reference_context)
289
- obj = await replace_geoscience_object(
290
- reference_context, reference, object_dict, create_if_missing=create_if_missing
291
- )
297
+ description = f"{cls.__name__} '{data.name}'"
298
+ with error_context(f"While preparing replacement data for {description}", logger=logger):
299
+ object_dict = await cls._data_to_schema(data, reference_context)
300
+ with error_context(
301
+ f"While replacing {description} at '{reference}' (schema: {object_dict['schema']})", logger=logger
302
+ ):
303
+ obj = await replace_geoscience_object(
304
+ reference_context, reference, object_dict, create_if_missing=create_if_missing
305
+ )
292
306
  return cls(obj)
293
307
 
294
308
  @classmethod
@@ -19,7 +19,7 @@ import numpy as np
19
19
 
20
20
  from evo import logging
21
21
  from evo.common import APIConnector, Environment, ICache, IFeedback, StaticContext
22
- from evo.common.exceptions import StorageFileNotFoundError
22
+ from evo.common.exceptions import StorageFileNotFoundError, error_context
23
23
  from evo.common.io.exceptions import DataExistsError
24
24
  from evo.common.utils import NoFeedback, PartialFeedback, split_feedback
25
25
 
@@ -143,22 +143,24 @@ class ObjectDataClient:
143
143
  n_names = len(names)
144
144
  logger.debug(f"Found {n_names} data reference{'' if n_names == 1 else 's'}")
145
145
  n_uploaded = 0
146
- async for ctx in ObjectDataUpload._create_multiple(
147
- connector=self._connector,
148
- environment=self._environment,
149
- names=names,
150
- ):
151
- if fb is not NoFeedback:
152
- fb_part = PartialFeedback(parent=fb, start=n_uploaded / n_names, end=(n_uploaded + 1) / n_names)
153
- else:
154
- fb_part = NoFeedback
155
-
156
- data_file = self.cache_location / ctx.name
157
- logger.debug(f"Uploading data: {ctx.name}")
158
- if not data_file.exists():
159
- raise StorageFileNotFoundError(f"Object data file not found in cache: {data_file}")
160
- await ctx.upload_from_path(filename=data_file, transport=self._connector.transport, fb=fb_part)
161
- n_uploaded += 1
146
+ with error_context("While uploading referenced data", logger=logger):
147
+ async for ctx in ObjectDataUpload._create_multiple(
148
+ connector=self._connector,
149
+ environment=self._environment,
150
+ names=names,
151
+ ):
152
+ if fb is not NoFeedback:
153
+ fb_part = PartialFeedback(parent=fb, start=n_uploaded / n_names, end=(n_uploaded + 1) / n_names)
154
+ else:
155
+ fb_part = NoFeedback
156
+
157
+ with error_context(f"While uploading referenced data '{ctx.name}'", logger=logger):
158
+ data_file = self.cache_location / ctx.name
159
+ logger.debug(f"Uploading data: {ctx.name}")
160
+ if not data_file.exists():
161
+ raise StorageFileNotFoundError(f"Object data file not found in cache: {data_file}")
162
+ await ctx.upload_from_path(filename=data_file, transport=self._connector.transport, fb=fb_part)
163
+ n_uploaded += 1
162
164
 
163
165
  logger.debug(
164
166
  f"Uploaded {n_uploaded} data reference{'' if n_uploaded == 1 else 's'}, skipped {n_names - n_uploaded}"
@@ -183,9 +185,10 @@ class ObjectDataClient:
183
185
  """
184
186
  from .table_formats import get_known_format
185
187
 
186
- if not isinstance(table_format, KnownTableFormat):
187
- table_format = get_known_format(table, table_formats=table_format)
188
- table_info = table_format.save_table(table=table, destination=self.cache_location)
188
+ with error_context("While preparing an Arrow table for Evo Parquet storage", logger=logger):
189
+ if not isinstance(table_format, KnownTableFormat):
190
+ table_format = get_known_format(table, table_formats=table_format)
191
+ table_info = table_format.save_table(table=table, destination=self.cache_location)
189
192
  return table_info
190
193
 
191
194
  async def upload_table(
@@ -210,11 +213,12 @@ class ObjectDataClient:
210
213
  """
211
214
  table_info = self.save_table(table, table_format)
212
215
  upload = ObjectDataUpload(connector=self._connector, environment=self._environment, name=table_info["data"])
213
- try:
214
- await upload.upload_from_cache(cache=self._cache, transport=self._connector.transport, fb=fb)
215
- except DataExistsError:
216
- logger.debug(f"Data not uploaded because data already exists (label: {table_info['data']})")
217
- fb.progress(1)
216
+ with error_context(f"While uploading table data '{table_info['data']}'", logger=logger):
217
+ try:
218
+ await upload.upload_from_cache(cache=self._cache, transport=self._connector.transport, fb=fb)
219
+ except DataExistsError:
220
+ logger.debug(f"Data not uploaded because data already exists (label: {table_info['data']})")
221
+ fb.progress(1)
218
222
  return table_info
219
223
 
220
224
  async def upload_category_table(self, table: pa.Table, fb: IFeedback = NoFeedback) -> CategoryInfo:
File without changes
File without changes