ds-resource-plugin-py-lib 0.1.0a1__py3-none-any.whl

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 (31) hide show
  1. ds_resource_plugin_py_lib/__init__.py +27 -0
  2. ds_resource_plugin_py_lib/common/__init__.py +6 -0
  3. ds_resource_plugin_py_lib/common/resource/__init__.py +8 -0
  4. ds_resource_plugin_py_lib/common/resource/client.py +239 -0
  5. ds_resource_plugin_py_lib/common/resource/dataset/__init__.py +11 -0
  6. ds_resource_plugin_py_lib/common/resource/dataset/base.py +158 -0
  7. ds_resource_plugin_py_lib/common/resource/dataset/errors.py +150 -0
  8. ds_resource_plugin_py_lib/common/resource/dataset/storage_format.py +52 -0
  9. ds_resource_plugin_py_lib/common/resource/errors.py +35 -0
  10. ds_resource_plugin_py_lib/common/resource/linked_service/__init__.py +7 -0
  11. ds_resource_plugin_py_lib/common/resource/linked_service/base.py +80 -0
  12. ds_resource_plugin_py_lib/common/resource/linked_service/errors.py +98 -0
  13. ds_resource_plugin_py_lib/common/serde/__init__.py +6 -0
  14. ds_resource_plugin_py_lib/common/serde/deserialize/__init__.py +9 -0
  15. ds_resource_plugin_py_lib/common/serde/deserialize/awswrangler.py +80 -0
  16. ds_resource_plugin_py_lib/common/serde/deserialize/base.py +23 -0
  17. ds_resource_plugin_py_lib/common/serde/deserialize/pandas.py +56 -0
  18. ds_resource_plugin_py_lib/common/serde/serialize/__init__.py +9 -0
  19. ds_resource_plugin_py_lib/common/serde/serialize/awswrangler.py +52 -0
  20. ds_resource_plugin_py_lib/common/serde/serialize/base.py +19 -0
  21. ds_resource_plugin_py_lib/common/serde/serialize/pandas.py +45 -0
  22. ds_resource_plugin_py_lib/libs/__init__.py +5 -0
  23. ds_resource_plugin_py_lib/libs/models/__init__.py +5 -0
  24. ds_resource_plugin_py_lib/libs/models/serializable.py +544 -0
  25. ds_resource_plugin_py_lib/libs/utils/import_string.py +26 -0
  26. ds_resource_plugin_py_lib/py.typed +0 -0
  27. ds_resource_plugin_py_lib-0.1.0a1.dist-info/METADATA +137 -0
  28. ds_resource_plugin_py_lib-0.1.0a1.dist-info/RECORD +31 -0
  29. ds_resource_plugin_py_lib-0.1.0a1.dist-info/WHEEL +5 -0
  30. ds_resource_plugin_py_lib-0.1.0a1.dist-info/licenses/LICENSE-APACHE +201 -0
  31. ds_resource_plugin_py_lib-0.1.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,27 @@
1
+ """
2
+ A Python package from the ds-resource-plugin library.
3
+
4
+ **File:** ``__init__.py``
5
+ **Region:** ``ds-resource-plugin-py-lib``
6
+
7
+ Example:
8
+
9
+ .. code-block:: python
10
+
11
+ from ds_resource_plugin_py_lib import __version__
12
+
13
+ print(f"Package version: {__version__}")
14
+ """
15
+
16
+ from pathlib import Path
17
+
18
+ from . import common, libs
19
+
20
+ _VERSION_FILE = Path(__file__).parent.parent.parent / "VERSION.txt"
21
+ __version__ = _VERSION_FILE.read_text().strip() if _VERSION_FILE.exists() else "0.0.0"
22
+
23
+ __all__ = [
24
+ "__version__",
25
+ "common",
26
+ "libs",
27
+ ]
@@ -0,0 +1,6 @@
1
+ from . import resource, serde
2
+
3
+ __all__ = [
4
+ "resource",
5
+ "serde",
6
+ ]
@@ -0,0 +1,8 @@
1
+ from . import dataset, linked_service
2
+ from .client import ResourceClient
3
+
4
+ __all__ = [
5
+ "ResourceClient",
6
+ "dataset",
7
+ "linked_service",
8
+ ]
@@ -0,0 +1,239 @@
1
+ """
2
+ File: client.py
3
+ Description: Resource client for discovering and managing datasets and linked services using entry points
4
+ Region: packages/shared
5
+ """
6
+
7
+ import logging
8
+ from functools import lru_cache
9
+ from importlib import import_module
10
+ from importlib.metadata import entry_points
11
+ from pathlib import Path
12
+ from typing import Any, cast
13
+
14
+ import yaml
15
+ from ds_common_logger_py_lib.mixin import LoggingMixin
16
+
17
+ from ...libs.utils.import_string import import_string
18
+ from ..resource.dataset.base import Dataset, DatasetInfo
19
+ from ..resource.errors import DeserializationException
20
+ from ..resource.linked_service.base import LinkedService, LinkedServiceInfo
21
+
22
+
23
+ class ResourceClient(LoggingMixin):
24
+ log_level = logging.DEBUG
25
+
26
+ PROTOCOL_GROUP = "ds.protocols"
27
+ PROVIDER_GROUP = "ds.providers"
28
+
29
+ def __init__(self) -> None:
30
+ super().__init__()
31
+ self._resource_dict: dict[str, dict[str, Any]] = {}
32
+ self._linked_services: dict[tuple[str, str], LinkedServiceInfo] = {}
33
+ self._datasets: dict[tuple[str, str], DatasetInfo] = {}
34
+ self._discover_resources(self.PROTOCOL_GROUP)
35
+ self._discover_resources(self.PROVIDER_GROUP)
36
+ self.log.debug(f"Loaded {len(self._resource_dict)} resources")
37
+ self.log.debug(f"Loaded {len(self._linked_services)} linked services")
38
+ self.log.debug(f"Loaded {len(self._datasets)} datasets")
39
+
40
+ @classmethod
41
+ @lru_cache(maxsize=1)
42
+ def get_instance(cls) -> "ResourceClient":
43
+ """Get the singleton instance of ResourceClient."""
44
+ return cls()
45
+
46
+ @property
47
+ def resources(self) -> dict[str, dict[str, Any]]:
48
+ return self._resource_dict
49
+
50
+ @property
51
+ def linked_services(self) -> dict[tuple[str, str], LinkedServiceInfo]:
52
+ return self._linked_services
53
+
54
+ @property
55
+ def datasets(self) -> dict[tuple[str, str], DatasetInfo]:
56
+ return self._datasets
57
+
58
+ def _discover_resources(self, group: str) -> None:
59
+ """
60
+ Discover protocol/provider packages via entry points.
61
+ Each entry point must target a Python package that contains resource.yaml in its root.
62
+ """
63
+ try:
64
+ eps = entry_points(group=group)
65
+ except Exception as exc:
66
+ self.log.warning(f"Failed to read entry points for {group}: {exc}")
67
+ return
68
+
69
+ for ep in eps:
70
+ try:
71
+ module = import_module(ep.module)
72
+ module_path = getattr(module, "__file__", None)
73
+ if not module_path:
74
+ self.log.warning(f"Entry point {ep.name} has no __file__; skipping.")
75
+ continue
76
+
77
+ real_path = str(Path(module_path).parent.resolve())
78
+ self._scan_resource_directory(real_path)
79
+
80
+ except Exception as exc:
81
+ self.log.error(f"Error when loading entry point {ep.name} ({group}): {exc}")
82
+
83
+ def _scan_resource_directory(self, resource_dir: str) -> None:
84
+ """
85
+ Scan a resource directory for resource.yaml.
86
+ Checks root first (new behavior), then subdirectories (old behavior).
87
+ Args:
88
+ resource_dir: Path to the resource directory.
89
+ """
90
+ resource_path = Path(resource_dir)
91
+ if not resource_path.exists():
92
+ self.log.debug(f"Resource directory {resource_dir} does not exist")
93
+ return
94
+
95
+ self._load_resource_from_path(str(resource_path))
96
+
97
+ def _load_resource_from_path(self, path: str) -> None:
98
+ """
99
+ Load resource configuration from a directory path.
100
+
101
+ Args:
102
+ path: Path to the resource directory.
103
+ """
104
+ resource_dir = Path(path)
105
+ resource_yaml = resource_dir / "resource.yaml"
106
+ if not resource_yaml.exists():
107
+ self.log.debug(f"No resource.yaml found in {path}")
108
+ return
109
+
110
+ try:
111
+ with Path(resource_yaml).open() as f:
112
+ resource_config = yaml.safe_load(f)
113
+ if not resource_config:
114
+ self.log.warning(f"Empty resource configuration in {resource_yaml}")
115
+ return
116
+
117
+ resource_name = resource_config.get("name", resource_dir.name)
118
+ self._resource_dict[resource_name] = resource_config
119
+ self._parse_linked_services(resource_config)
120
+ self._parse_datasets(resource_config)
121
+ except Exception as exc:
122
+ self.log.error(f"Error loading resource configuration from {resource_yaml}: {exc}")
123
+
124
+ def _parse_linked_services(self, config: dict[str, Any]) -> None:
125
+ """
126
+ Parse linked services from resource configuration.
127
+
128
+ Args:
129
+ config: Resource configuration dictionary.
130
+ """
131
+ linked_services = config.get("linked_service", [])
132
+ for service in linked_services:
133
+ service_name = service.get("name")
134
+ if service_name:
135
+ kind = service.get("kind")
136
+ version = service.get("version", "1.0.0")
137
+ service_info = LinkedServiceInfo(
138
+ kind=kind,
139
+ name=service_name,
140
+ class_name=service.get("class_name"),
141
+ version=version,
142
+ description=service.get("description"),
143
+ )
144
+ # Store by composite key (kind, version) to support multiple versions
145
+ self._linked_services[service_info.key] = service_info
146
+
147
+ def _parse_datasets(self, config: dict[str, Any]) -> None:
148
+ """
149
+ Parse datasets from resource configuration.
150
+
151
+ Args:
152
+ config: Resource configuration dictionary.
153
+ """
154
+ datasets = config.get("dataset", [])
155
+ for dataset in datasets:
156
+ dataset_name = dataset.get("name")
157
+ if dataset_name:
158
+ kind = dataset.get("kind")
159
+ version = dataset.get("version", "1.0.0")
160
+ dataset_info = DatasetInfo(
161
+ kind=kind,
162
+ name=dataset_name,
163
+ class_name=dataset.get("class_name"),
164
+ version=version,
165
+ description=dataset.get("description"),
166
+ )
167
+ self._datasets[dataset_info.key] = dataset_info
168
+
169
+ def _get_dataset_model_cls(self, kind: str, version: str) -> type[Dataset[Any, Any]]:
170
+ """
171
+ Get a dataset model class by kind and optionally version.
172
+
173
+ Args:
174
+ kind: str
175
+ version: str
176
+ Returns:
177
+ Type[Dataset]
178
+ """
179
+ cls_name = self.datasets[(kind, version)].class_name
180
+ self.log.debug("Dataset Class Name: %s", cls_name)
181
+ return cast("type[Dataset[Any, Any]]", import_string(cls_name))
182
+
183
+ def _get_linked_service_model_cls(self, kind: str, version: str) -> type[LinkedService[Any]]:
184
+ """
185
+ Get a linked service model class by kind and version.
186
+
187
+ Args:
188
+ kind: The kind of the linked service.
189
+ version: str version of the linked service.
190
+ Returns:
191
+ Type[LinkedService]
192
+ """
193
+ cls_name = self.linked_services[(kind, version)].class_name
194
+ self.log.debug("Linked Service Class Name: %s", cls_name)
195
+ return cast("type[LinkedService[Any]]", import_string(cls_name))
196
+
197
+ def linked_service(self, config: dict[str, Any]) -> LinkedService[Any]:
198
+ """
199
+ Get a linked service instance by configuration.
200
+
201
+ Args:
202
+ config: dict containing at least 'kind' and 'version'
203
+ Returns:
204
+ LinkedService
205
+ """
206
+ try:
207
+ kind = config["kind"]
208
+ version = config["version"]
209
+ model_cls = self._get_linked_service_model_cls(kind, version)
210
+ linked_service: LinkedService[Any] = model_cls.deserialize(config)
211
+ return linked_service
212
+ except (TypeError, KeyError) as exc:
213
+ self.log.exception(f"Error deserializing linked service: {exc}")
214
+ raise DeserializationException(
215
+ message=f"Error deserializing linked service: {exc}",
216
+ details={"config": config, "error": str(exc)},
217
+ ) from exc
218
+
219
+ def dataset(self, config: dict[str, Any]) -> Dataset[Any, Any]:
220
+ """
221
+ Get a dataset instance by configuration.
222
+
223
+ Args:
224
+ config: dict containing at least 'kind' and 'version'
225
+ Returns:
226
+ Dataset
227
+ """
228
+ try:
229
+ kind = config["kind"]
230
+ version = config["version"]
231
+ dataset_cls = self._get_dataset_model_cls(kind, version)
232
+ dataset: Dataset[Any, Any] = dataset_cls.deserialize(config)
233
+ return dataset
234
+ except (TypeError, KeyError) as exc:
235
+ self.log.exception(f"Error deserializing dataset: {exc}")
236
+ raise DeserializationException(
237
+ message=f"Error deserializing dataset: {exc}",
238
+ details={"config": config, "error": str(exc)},
239
+ ) from exc
@@ -0,0 +1,11 @@
1
+ from .base import Dataset, DatasetInfo, DatasetTypedProperties, TabularDataset
2
+ from .storage_format import DatasetStorageFormat, DatasetStorageFormatType
3
+
4
+ __all__ = [
5
+ "Dataset",
6
+ "DatasetInfo",
7
+ "DatasetStorageFormat",
8
+ "DatasetStorageFormatType",
9
+ "DatasetTypedProperties",
10
+ "TabularDataset",
11
+ ]
@@ -0,0 +1,158 @@
1
+ import io
2
+ from abc import ABC, abstractmethod
3
+ from collections.abc import Callable
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Generic, NamedTuple, TypeVar
6
+
7
+ import pandas as pd
8
+
9
+ from ....libs.models.serializable import Serializable
10
+ from ...resource.linked_service.base import LinkedService
11
+ from ...serde.deserialize.base import DataDeserializer
12
+ from ...serde.serialize.base import DataSerializer
13
+
14
+
15
+ class DatasetInfo(NamedTuple):
16
+ kind: str
17
+ name: str
18
+ class_name: str
19
+ version: str
20
+ description: str | None = None
21
+
22
+ def __str__(self) -> str:
23
+ """Return a string representation of the dataset info."""
24
+ return f"{self.kind}:v{self.version}"
25
+
26
+ @property
27
+ def key(self) -> tuple[str, str]:
28
+ """Return the composite key (kind, version) for dictionary lookups."""
29
+ return (self.kind, self.version)
30
+
31
+
32
+ @dataclass(kw_only=True)
33
+ class DatasetTypedProperties(Serializable):
34
+ """
35
+ The object containing the typed properties of the dataset.
36
+ """
37
+
38
+ pass
39
+
40
+
41
+ DatasetTypedPropertiesType = TypeVar("DatasetTypedPropertiesType", bound=DatasetTypedProperties)
42
+ LinkedServiceType = TypeVar("LinkedServiceType", bound=LinkedService[Any])
43
+
44
+
45
+ @dataclass(kw_only=True)
46
+ class Dataset(
47
+ ABC,
48
+ Serializable,
49
+ Generic[LinkedServiceType, DatasetTypedPropertiesType],
50
+ ):
51
+ """
52
+ The ds workflow nested object which identifies data within a data store,
53
+ such as table, files, folders and documents.
54
+
55
+ You probably want to use the subclasses and not this class directly.
56
+ """
57
+
58
+ typed_properties: DatasetTypedPropertiesType
59
+ linked_service: LinkedServiceType
60
+
61
+ serializer: DataSerializer | None = None
62
+ deserializer: DataDeserializer | None = None
63
+
64
+ post_fetch_callback: Callable[..., Any] | None = None
65
+ prepare_write_callback: Callable[..., Any] | None = None
66
+
67
+ content: Any | None = None
68
+ schema: dict[str, Any] | None = None
69
+ next: bool | None = True
70
+ cursor: str | None = None
71
+
72
+ @property
73
+ @abstractmethod
74
+ def kind(self) -> str:
75
+ """
76
+ Get the kind of the dataset.
77
+ """
78
+ raise NotImplementedError("Method (kind) not implemented")
79
+
80
+ @abstractmethod
81
+ def create(self, **kwargs: Any) -> Any:
82
+ """
83
+ Create the dataset.
84
+ :param kwargs: dict
85
+ :return: None
86
+ """
87
+ raise NotImplementedError("Method (create) not implemented")
88
+
89
+ @abstractmethod
90
+ def read(self, **kwargs: Any) -> Any:
91
+ """
92
+ Read the dataset.
93
+ :param kwargs: dict
94
+ :return: None
95
+ """
96
+ raise NotImplementedError("Method (read) not implemented")
97
+
98
+ @abstractmethod
99
+ def delete(self, **kwargs: Any) -> Any:
100
+ """
101
+ Delete the dataset.
102
+ :param kwargs: dict
103
+ :return: None
104
+ """
105
+ raise NotImplementedError("Method (delete) not implemented")
106
+
107
+ @abstractmethod
108
+ def update(self, **kwargs: Any) -> Any:
109
+ """
110
+ Update the dataset.
111
+ :param kwargs: dict
112
+ :return: None
113
+ """
114
+ raise NotImplementedError("Method (update) not implemented")
115
+
116
+ @abstractmethod
117
+ def rename(self, **kwargs: Any) -> Any:
118
+ """
119
+ Move the dataset.
120
+ :param kwargs: dict
121
+ :return: None
122
+ """
123
+ raise NotImplementedError("Method (move) not implemented")
124
+
125
+
126
+ @dataclass(kw_only=True)
127
+ class BinaryDataset(
128
+ Dataset[LinkedServiceType, DatasetTypedPropertiesType],
129
+ Generic[LinkedServiceType, DatasetTypedPropertiesType],
130
+ ):
131
+ """
132
+ Binary dataset object which identifies data within a data store,
133
+ such as files, folders and documents.
134
+
135
+ The content of the dataset is a binary file.
136
+ """
137
+
138
+ content: io.BytesIO = field(default_factory=io.BytesIO)
139
+ next: bool | None = True
140
+ cursor: str | None = None
141
+
142
+
143
+ @dataclass(kw_only=True)
144
+ class TabularDataset(
145
+ Dataset[LinkedServiceType, DatasetTypedPropertiesType],
146
+ Generic[LinkedServiceType, DatasetTypedPropertiesType],
147
+ ):
148
+ """
149
+ Tabular dataset object which identifies data within a data store,
150
+ such as table/csv/json/parquet/parquetdataset/ and other documents.
151
+
152
+ The content of the dataset is a pandas DataFrame.
153
+ """
154
+
155
+ schema: dict[str, Any] | None = None
156
+ content: pd.DataFrame = field(default_factory=pd.DataFrame)
157
+ next: bool | None = True
158
+ cursor: str | None = None
@@ -0,0 +1,150 @@
1
+ """
2
+ Exceptions for Datasets.
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ from ..errors import ResourceException
8
+
9
+
10
+ class DatasetException(ResourceException):
11
+ """Base exception for all dataset-related errors."""
12
+
13
+ def __init__(
14
+ self,
15
+ message: str = "Dataset operation failed",
16
+ code: str = "DATASET_ERROR",
17
+ status_code: int = 500,
18
+ details: dict[str, Any] | None = None,
19
+ ) -> None:
20
+ super().__init__(message, code, status_code, details)
21
+
22
+
23
+ class MismatchedLinkedServiceException(DatasetException):
24
+ """Raised when a linked service does not match the dataset type."""
25
+
26
+ def __init__(
27
+ self,
28
+ message: str = "Mismatched linked service",
29
+ code: str = "MISMATCHED_LINKED_SERVICE",
30
+ status_code: int = 400,
31
+ details: dict[str, Any] | None = None,
32
+ ) -> None:
33
+ super().__init__(message, code, status_code, details)
34
+
35
+
36
+ class UnsupportedDatasetType(DatasetException):
37
+ """Raised when a dataset type is not supported."""
38
+
39
+ def __init__(
40
+ self,
41
+ message: str = "Dataset type is not supported",
42
+ code: str = "UNSUPPORTED_DATASET_TYPE",
43
+ status_code: int = 400,
44
+ details: dict[str, Any] | None = None,
45
+ ) -> None:
46
+ super().__init__(message, code, status_code, details)
47
+
48
+
49
+ class InvalidDatasetClass(DatasetException):
50
+ """Raised when a dataset type is invalid."""
51
+
52
+ def __init__(
53
+ self,
54
+ message: str = "Invalid dataset type",
55
+ code: str = "INVALID_DATASET_TYPE",
56
+ status_code: int = 400,
57
+ details: dict[str, Any] | None = None,
58
+ ) -> None:
59
+ super().__init__(message, code, status_code, details)
60
+
61
+
62
+ class FileNotFound(DatasetException):
63
+ """Raised when file not found."""
64
+
65
+ def __init__(
66
+ self,
67
+ message: str = "File not found",
68
+ code: str = "NOT_FOUND",
69
+ status_code: int = 404,
70
+ details: dict[str, Any] | None = None,
71
+ ) -> None:
72
+ super().__init__(message, code, status_code, details)
73
+
74
+
75
+ class WriteException(DatasetException):
76
+ """Raised when a write operation fails."""
77
+
78
+ def __init__(
79
+ self,
80
+ message: str = "Write operation failed",
81
+ code: str = "WRITE_ERROR",
82
+ status_code: int = 500,
83
+ details: dict[str, Any] | None = None,
84
+ ) -> None:
85
+ super().__init__(message, code, status_code, details)
86
+
87
+
88
+ class UpdateException(DatasetException):
89
+ """Raised when a update operation fails."""
90
+
91
+ def __init__(
92
+ self,
93
+ message: str = "Update operation failed",
94
+ code: str = "UPDATE_ERROR",
95
+ status_code: int = 500,
96
+ details: dict[str, Any] | None = None,
97
+ ) -> None:
98
+ super().__init__(message, code, status_code, details)
99
+
100
+
101
+ class ReadException(DatasetException):
102
+ """Raised when a read operation fails."""
103
+
104
+ def __init__(
105
+ self,
106
+ message: str = "Read operation failed",
107
+ code: str = "READ_ERROR",
108
+ status_code: int = 500,
109
+ details: dict[str, Any] | None = None,
110
+ ) -> None:
111
+ super().__init__(message, code, status_code, details)
112
+
113
+
114
+ class DeleteException(DatasetException):
115
+ """Raised when a delete operation fails."""
116
+
117
+ def __init__(
118
+ self,
119
+ message: str = "Delete operation failed",
120
+ code: str = "DELETE_ERROR",
121
+ status_code: int = 500,
122
+ details: dict[str, Any] | None = None,
123
+ ) -> None:
124
+ super().__init__(message, code, status_code, details)
125
+
126
+
127
+ class JsonDecodeException(DatasetException):
128
+ """Raised when a operation fails due to JSONDecodeError."""
129
+
130
+ def __init__(
131
+ self,
132
+ message: str = "Failed to decode JSON.",
133
+ code: str = "JSON_DECODE_ERROR",
134
+ status_code: int = 400,
135
+ details: dict[str, Any] | None = None,
136
+ ) -> None:
137
+ super().__init__(message, code, status_code, details)
138
+
139
+
140
+ class BadRequestException(DatasetException):
141
+ """Raised when an operation fails due to invalid input."""
142
+
143
+ def __init__(
144
+ self,
145
+ message: str = "Bad Request.",
146
+ code: str = "BAD_REQUEST",
147
+ status_code: int = 400,
148
+ details: dict[str, Any] | None = None,
149
+ ) -> None:
150
+ super().__init__(message, code, status_code, details)
@@ -0,0 +1,52 @@
1
+ from dataclasses import dataclass, field
2
+ from enum import StrEnum
3
+ from typing import Any
4
+
5
+ from ....libs.models.serializable import Serializable
6
+
7
+
8
+ class DatasetStorageFormatType(StrEnum):
9
+ """
10
+ Enum to define the storage format types.
11
+ """
12
+
13
+ PARQUET = "PARQUET"
14
+ CSV = "CSV"
15
+ JSON = "JSON"
16
+ EXCEL = "EXCEL"
17
+ SEMI_STRUCTURED_JSON = "SEMI_STRUCTURED_JSON"
18
+ XML = "XML"
19
+
20
+
21
+ @dataclass(kw_only=True)
22
+ class DatasetStorageFormat(Serializable):
23
+ """
24
+ The object containing the storage format of the dataset.
25
+ """
26
+
27
+ type: DatasetStorageFormatType
28
+ args: dict[str, Any] = field(default_factory=dict)
29
+
30
+
31
+ @dataclass(kw_only=True)
32
+ class ParquetFormat(DatasetStorageFormat):
33
+ type: DatasetStorageFormatType = DatasetStorageFormatType.PARQUET
34
+ args: dict[str, Any] = field(default_factory=dict)
35
+
36
+
37
+ @dataclass(kw_only=True)
38
+ class CsvFormat(DatasetStorageFormat):
39
+ type: DatasetStorageFormatType = DatasetStorageFormatType.CSV
40
+ args: dict[str, Any] = field(default_factory=lambda: {"delimiter": ","})
41
+
42
+
43
+ @dataclass(kw_only=True)
44
+ class SemiStructuredJsonFormat(DatasetStorageFormat):
45
+ type: DatasetStorageFormatType = DatasetStorageFormatType.SEMI_STRUCTURED_JSON
46
+ args: dict[str, Any] = field(default_factory=lambda: {"record_path": None})
47
+
48
+
49
+ @dataclass(kw_only=True)
50
+ class XMLFormat(DatasetStorageFormat):
51
+ type: DatasetStorageFormatType = DatasetStorageFormatType.XML
52
+ args: dict[str, Any] = field(default_factory=dict)
@@ -0,0 +1,35 @@
1
+ """
2
+ Exceptions for Resources.
3
+ """
4
+
5
+ from typing import Any
6
+
7
+
8
+ class ResourceException(Exception):
9
+ """Base exception for all resource-related errors."""
10
+
11
+ def __init__(
12
+ self,
13
+ message: str = "Resource operation failed",
14
+ code: str = "RESOURCE_ERROR",
15
+ status_code: int = 500,
16
+ details: dict[str, Any] | None = None,
17
+ ) -> None:
18
+ self.code = code
19
+ self.status_code = status_code
20
+ self.message = message
21
+ self.details = details or {}
22
+ super().__init__(self.message)
23
+
24
+
25
+ class DeserializationException(ResourceException):
26
+ """Raised when a deserialization operation fails."""
27
+
28
+ def __init__(
29
+ self,
30
+ message: str = "Deserialization operation failed",
31
+ code: str = "DESERIALIZATION_ERROR",
32
+ status_code: int = 400,
33
+ details: dict[str, Any] | None = None,
34
+ ) -> None:
35
+ super().__init__(message, code, status_code, details)
@@ -0,0 +1,7 @@
1
+ from .base import LinkedService, LinkedServiceInfo, LinkedServiceTypedProperties
2
+
3
+ __all__ = [
4
+ "LinkedService",
5
+ "LinkedServiceInfo",
6
+ "LinkedServiceTypedProperties",
7
+ ]