oceanprotocol-job-details 0.3.13__py3-none-any.whl → 0.3.14__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.
@@ -8,7 +8,7 @@ from oceanprotocol_job_details.loaders.impl.job_details import JobDetailsLoader
8
8
  from oceanprotocol_job_details.domain import Paths
9
9
 
10
10
 
11
- InputParametersT = TypeVar("InputParametersT", BaseModel, None)
11
+ InputParametersT = TypeVar("InputParametersT", bound=BaseModel)
12
12
 
13
13
 
14
14
  class Container(containers.DeclarativeContainer, Generic[InputParametersT]):
@@ -26,7 +26,7 @@ class Container(containers.DeclarativeContainer, Generic[InputParametersT]):
26
26
 
27
27
  files = providers.Factory(lambda loader: loader.load(), loader=file_loader)
28
28
  ddo_loader = providers.Factory(DDOLoader, files=files)
29
- ddos = providers.Factory(lambda loader: loader.load(), loader=ddo_loader)
29
+ metadata = providers.Factory(lambda loader: loader.load(), loader=ddo_loader)
30
30
 
31
31
  job_details_loader: providers.Factory[JobDetailsLoader[InputParametersT]] = (
32
32
  providers.Factory(
@@ -34,6 +34,6 @@ class Container(containers.DeclarativeContainer, Generic[InputParametersT]):
34
34
  files=files,
35
35
  secret=config.secret,
36
36
  paths=paths,
37
- ddos=ddos,
37
+ metadata=metadata,
38
38
  )
39
39
  )
@@ -0,0 +1,10 @@
1
+ from oceanprotocol_job_details.domain.ddo import DDO
2
+ from oceanprotocol_job_details.domain.derived import (
3
+ DID,
4
+ DDOMetadata,
5
+ DIDPaths,
6
+ Files,
7
+ Paths,
8
+ )
9
+
10
+ __all__ = ["DDO", "DID", "DDOMetadata", "Paths", "DIDPaths", "Files"]
@@ -1,7 +1,5 @@
1
1
  # mypy: disable-error-code=explicit-any
2
- from dataclasses import InitVar, dataclass, field
3
- from pathlib import Path
4
- from typing import Generator, List, Optional, Sequence, TypeAlias
2
+ from typing import Optional
5
3
 
6
4
  from pydantic import BaseModel, ConfigDict, Field, JsonValue
7
5
 
@@ -124,64 +122,3 @@ class DDO(BaseModel):
124
122
  purgatory: Purgatory
125
123
 
126
124
  model_config = ConfigDict(populate_by_name=True)
127
-
128
-
129
- @dataclass(frozen=True)
130
- class DIDPaths:
131
- did: str
132
- ddo: Path = field(repr=False)
133
-
134
- files: InitVar[Generator[Path, None, None]]
135
-
136
- _input: List[Path] = field(init=False, repr=False)
137
-
138
- def __post_init__(self, files: Generator[Path, None, None]) -> None:
139
- assert self.ddo.exists(), f"DDO {self.ddo} does not exist"
140
-
141
- object.__setattr__(self, "_input", list(files))
142
-
143
- @property
144
- def input_files(self) -> List[Path]:
145
- return self._input
146
-
147
- def __len__(self) -> int:
148
- return len(self._input)
149
-
150
-
151
- Files: TypeAlias = Sequence[DIDPaths]
152
-
153
-
154
- @dataclass(frozen=True)
155
- class Paths:
156
- """Configuration class for the Ocean Protocol Job Details"""
157
-
158
- base_dir: InitVar[Path | None] = None
159
-
160
- _base: Path = field(init=False, repr=False)
161
-
162
- def __post_init__(self, base_dir: Path | None) -> None:
163
- object.__setattr__(self, "_base", base_dir if base_dir else Path("/data"))
164
-
165
- @property
166
- def data(self) -> Path:
167
- return self._base
168
-
169
- @property
170
- def inputs(self) -> Path:
171
- return self.data / "inputs"
172
-
173
- @property
174
- def ddos(self) -> Path:
175
- return self.data / "ddos"
176
-
177
- @property
178
- def outputs(self) -> Path:
179
- return self.data / "outputs"
180
-
181
- @property
182
- def logs(self) -> Path:
183
- return self.data / "logs"
184
-
185
- @property
186
- def algorithm_custom_parameters(self) -> Path:
187
- return self.inputs / "algoCustomData.json"
@@ -0,0 +1,71 @@
1
+ from dataclasses import InitVar, dataclass, field
2
+ from pathlib import Path
3
+ from typing import Dict, Generator, List, Sequence, TypeAlias
4
+
5
+ from oceanprotocol_job_details.domain.ddo import DDO
6
+
7
+ DID: TypeAlias = str
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class DIDPaths:
12
+ did: DID
13
+ ddo: Path = field(repr=False)
14
+
15
+ files: InitVar[Generator[Path, None, None]]
16
+
17
+ _input: List[Path] = field(init=False, repr=False)
18
+
19
+ def __post_init__(self, files: Generator[Path, None, None]) -> None:
20
+ assert self.ddo.exists(), f"DDO {self.ddo} does not exist"
21
+
22
+ object.__setattr__(self, "_input", list(files))
23
+
24
+ @property
25
+ def input_files(self) -> List[Path]:
26
+ return self._input
27
+
28
+ def __len__(self) -> int:
29
+ return len(self._input)
30
+
31
+
32
+ Files: TypeAlias = Sequence[DIDPaths]
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class Paths:
37
+ """Configuration class for the Ocean Protocol Job Details"""
38
+
39
+ base_dir: InitVar[Path | None] = None
40
+
41
+ _base: Path = field(init=False, repr=False)
42
+
43
+ def __post_init__(self, base_dir: Path | None) -> None:
44
+ object.__setattr__(self, "_base", base_dir if base_dir else Path("/data"))
45
+
46
+ @property
47
+ def data(self) -> Path:
48
+ return self._base
49
+
50
+ @property
51
+ def inputs(self) -> Path:
52
+ return self.data / "inputs"
53
+
54
+ @property
55
+ def ddos(self) -> Path:
56
+ return self.data / "ddos"
57
+
58
+ @property
59
+ def outputs(self) -> Path:
60
+ return self.data / "outputs"
61
+
62
+ @property
63
+ def logs(self) -> Path:
64
+ return self.data / "logs"
65
+
66
+ @property
67
+ def algorithm_custom_parameters(self) -> Path:
68
+ return self.inputs / "algoCustomData.json"
69
+
70
+
71
+ DDOMetadata: TypeAlias = Dict[DID, DDO]
@@ -1,25 +1,40 @@
1
+ # mypy: disable-error-code=explicit-any
1
2
  import asyncio
2
3
  import inspect
3
- from typing import Any, Callable, Coroutine, TypeVar
4
+ from functools import partial
5
+ from typing import Any, Callable, Coroutine, TypeGuard, TypeVar, cast
4
6
 
5
7
  T = TypeVar("T")
6
8
 
7
9
 
10
+ def is_coro_function(
11
+ obj: Any,
12
+ ) -> TypeGuard[Callable[..., Coroutine[Any, Any, T]]]:
13
+ return inspect.iscoroutinefunction(obj)
14
+
15
+
16
+ def is_coro(obj: Any) -> TypeGuard[Coroutine[Any, Any, T]]:
17
+ return inspect.iscoroutine(obj)
18
+
19
+
8
20
  async def run_in_executor(
9
21
  obj: Callable[..., T]
10
22
  | Callable[..., Coroutine[Any, Any, T]]
11
23
  | Coroutine[Any, Any, T],
12
- *args,
13
- **kwargs,
24
+ *args: Any,
25
+ **kwargs: Any,
14
26
  ) -> T:
15
- if inspect.iscoroutinefunction(obj):
27
+ if is_coro_function(obj):
16
28
  return await obj(*args, **kwargs)
17
29
 
18
- if inspect.iscoroutine(obj):
30
+ if is_coro(obj):
19
31
  return await obj
20
32
 
21
33
  if callable(obj):
22
34
  loop = asyncio.get_running_loop()
23
- return await loop.run_in_executor(None, obj, *args, **kwargs)
24
35
 
25
- return obj
36
+ # just to comply with mypy
37
+ func = partial(obj, *args, **kwargs)
38
+ return await loop.run_in_executor(None, cast(Callable[[], T], func))
39
+
40
+ return cast(T, obj)
@@ -6,7 +6,7 @@ from oceanprotocol_job_details.di import Container
6
6
  from oceanprotocol_job_details.ocean import JobDetails
7
7
  from oceanprotocol_job_details.settings import JobSettings
8
8
 
9
- InputParametersT = TypeVar("InputParametersT", BaseModel, None)
9
+ InputParametersT = TypeVar("InputParametersT", bound=BaseModel)
10
10
 
11
11
 
12
12
  def create_container(config: Dict[str, Any]) -> Container[InputParametersT]: # type: ignore[explicit-any]
@@ -1,10 +1,10 @@
1
- from __future__ import annotations
2
-
3
1
  from dataclasses import InitVar, dataclass, field
4
2
  from pathlib import Path
5
- from typing import final
3
+ from typing import List, Tuple, final
4
+
5
+ import orjson
6
6
 
7
- from oceanprotocol_job_details.domain import DDO, Files
7
+ from oceanprotocol_job_details.domain import DDO, DID, DDOMetadata, Files
8
8
 
9
9
 
10
10
  @final
@@ -13,12 +13,14 @@ class DDOLoader:
13
13
  files: InitVar[Files]
14
14
  """The files to load the DDOs from"""
15
15
 
16
- _ddo_paths: list[Path] = field(init=False)
16
+ _files: List[Tuple[DID, Path]] = field(init=False)
17
17
 
18
18
  def __post_init__(self, files: Files) -> None:
19
19
  assert files is not None and len(files) != 0, "Missing files"
20
+ object.__setattr__(self, "_files", [(f.did, f.ddo) for f in files])
20
21
 
21
- object.__setattr__(self, "_ddo_paths", [f.ddo for f in files])
22
-
23
- def load(self) -> list[DDO]:
24
- return [DDO.model_validate_json(p.read_text()) for p in self._ddo_paths]
22
+ def load(self) -> DDOMetadata:
23
+ return {
24
+ did: DDO.model_validate(orjson.loads(path.read_bytes()))
25
+ for did, path in self._files
26
+ }
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import InitVar, dataclass, field
3
+ from dataclasses import dataclass, field
4
4
  from logging import Logger
5
5
  from pathlib import Path
6
6
  from typing import Literal, final
@@ -1,29 +1,28 @@
1
- from dataclasses import dataclass, field
2
- from types import NoneType
1
+ from dataclasses import dataclass
3
2
  from typing import Generic, Type, TypeVar, final
4
3
 
5
- from pydantic import BaseModel
4
+ from pydantic import BaseModel, Secret
6
5
 
7
- from oceanprotocol_job_details.domain import DDO, Files, Paths
6
+ from oceanprotocol_job_details.domain import DDOMetadata, Files, Paths
8
7
  from oceanprotocol_job_details.ocean import JobDetails
9
8
 
10
- T = TypeVar("T", BaseModel, None)
9
+ InputParameterT = TypeVar("InputParameterT", bound=BaseModel)
11
10
 
12
11
 
13
12
  @final
14
13
  @dataclass(frozen=True)
15
- class JobDetailsLoader(Generic[T]):
16
- input_type: Type[T] = field(repr=False)
14
+ class JobDetailsLoader(Generic[InputParameterT]):
15
+ input_type: Type[InputParameterT] | None
17
16
  files: Files
18
- secret: str
17
+ secret: Secret[str] | None
19
18
  paths: Paths
20
- ddos: list[DDO]
19
+ metadata: DDOMetadata
21
20
 
22
- def load(self) -> JobDetails[T]:
23
- return JobDetails[T](
21
+ def load(self) -> JobDetails[InputParameterT]:
22
+ return JobDetails[InputParameterT](
24
23
  files=self.files,
25
24
  secret=self.secret,
26
- ddos=self.ddos,
25
+ metadata=self.metadata,
27
26
  paths=self.paths,
28
27
  input_type=self.input_type,
29
28
  )
@@ -1,26 +1,22 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- from functools import cached_property
5
1
  from pathlib import Path
6
2
  from typing import Generator, Generic, Tuple, Type, TypeVar, final
7
3
 
8
4
  import aiofiles
5
+ import orjson
9
6
  from pydantic import BaseModel, ConfigDict, Secret
10
7
 
11
- from oceanprotocol_job_details.domain import DDO, Files, Paths
12
- from oceanprotocol_job_details.executors import run_in_executor
8
+ from oceanprotocol_job_details.domain import DDOMetadata, Files, Paths
13
9
 
14
- InputParametersT = TypeVar("InputParametersT", BaseModel, None)
10
+ InputParametersT = TypeVar("InputParametersT", bound=BaseModel)
15
11
 
16
12
 
17
13
  @final
18
14
  class JobDetails(BaseModel, Generic[InputParametersT]): # type: ignore[explicit-any]
19
15
  files: Files
20
- ddos: list[DDO]
16
+ metadata: DDOMetadata
21
17
  paths: Paths
22
18
  input_type: Type[InputParametersT] | None
23
- secret: Secret[str] | None = None
19
+ secret: Secret[str] | None
24
20
 
25
21
  model_config = ConfigDict(arbitrary_types_allowed=True, frozen=True)
26
22
 
@@ -33,13 +29,16 @@ class JobDetails(BaseModel, Generic[InputParametersT]): # type: ignore[explicit
33
29
  for file in files.input_files
34
30
  )
35
31
 
36
- async def input_parameters(self) -> InputParametersT:
37
- if self._input_parameters is None:
38
- input_parameters = await self.ainput_parameters()
39
- object.__setattr__(self, "_input_parameters", input_parameters)
40
- return self._input_parameters
32
+ async def input_parameters(self) -> InputParametersT | None:
33
+ current = self._input_parameters
34
+
35
+ if current is None:
36
+ current = await self.ainput_parameters()
37
+ object.__setattr__(self, "_input_parameters", current)
38
+
39
+ return current
41
40
 
42
- async def ainput_parameters(self) -> InputParametersT:
41
+ async def ainput_parameters(self) -> InputParametersT | None:
43
42
  if self.input_type is None:
44
43
  return None
45
44
 
@@ -49,4 +48,4 @@ class JobDetails(BaseModel, Generic[InputParametersT]): # type: ignore[explicit
49
48
 
50
49
  raw = raw.strip()
51
50
  assert raw is not None, f"Empty file {path}"
52
- return self.input_type.model_validate_json(raw) # type: ignore
51
+ return self.input_type.model_validate(orjson.loads(raw))
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from typing import Self
5
5
 
6
6
  import orjson
7
- from pydantic import Field, field_validator, model_validator
7
+ from pydantic import Field, Secret, field_validator, model_validator
8
8
  from pydantic_settings import BaseSettings, SettingsConfigDict
9
9
 
10
10
 
@@ -12,7 +12,7 @@ class JobSettings(BaseSettings): # type: ignore[explicit-any]
12
12
  base_dir: Path = Field(alias="BASE_DIR")
13
13
  dids: list[str] = Field(default_factory=list, alias="DIDS")
14
14
  transformation_did: str = Field(alias="TRANSFORMATION_DID")
15
- secret: str | None = Field(default=None, alias="SECRET")
15
+ secret: Secret[str] | None = Field(default=None, alias="SECRET")
16
16
  logger: Logger = Field(default_factory=lambda: getLogger(__name__))
17
17
 
18
18
  model_config = SettingsConfigDict(
@@ -35,7 +35,7 @@ class JobSettings(BaseSettings): # type: ignore[explicit-any]
35
35
 
36
36
  @model_validator(mode="after")
37
37
  def validate_dids(self) -> Self:
38
- if not self.dids:
38
+ if len(self.dids) == 0:
39
39
  self.dids.extend(
40
40
  [f.name for f in (self.base_dir / "ddos").glob("*") if f.is_file()]
41
41
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oceanprotocol-job-details
3
- Version: 0.3.13
3
+ Version: 0.3.14
4
4
  Summary: A Python package to get details from OceanProtocol jobs
5
5
  Project-URL: Homepage, https://github.com/AgrospAI/oceanprotocol-job-details
6
6
  Project-URL: Issues, https://github.com/AgrospAI/oceanprotocol-job-details/issues
@@ -97,7 +97,7 @@ _, file_path = next(job_details.inputs())
97
97
 
98
98
  ```bash
99
99
  data # Root /data directory
100
- ├── ddos # Contains the loaded dataset's DDO
100
+ ├── ddos # Contains the loaded dataset's DDO (metadata)
101
101
  │ ├── 17feb...e42 # DDO file
102
102
  │ └── ... # One DDO per loaded dataset
103
103
  ├── inputs # Datasets dir
@@ -0,0 +1,20 @@
1
+ oceanprotocol_job_details/__init__.py,sha256=sq_C8Ey4_4oHiX9cO1wne7ZsWqs86WWgHfKQ5PMwwak,218
2
+ oceanprotocol_job_details/di.py,sha256=QQ7Gs1zbDdGRJ6MrJsW3VmaFLL90edcQd-x2DNR_I5A,1328
3
+ oceanprotocol_job_details/executors.py,sha256=CTkJkFmNYg50Z7U6JO6Jc85biKE7yhWBHByfPMOT0wo,962
4
+ oceanprotocol_job_details/helpers.py,sha256=nRmIu53eX3kzOdivcTCjr06xofjo-Z88_hMt7tcJzYg,1064
5
+ oceanprotocol_job_details/ocean.py,sha256=8J2fChXmVevJGm8EWTi0QrnqGM8iOoOqJPqyM4PTJlQ,1572
6
+ oceanprotocol_job_details/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ oceanprotocol_job_details/settings.py,sha256=MFrZXc1tPXyUsFCE9v2xqEamU3kBHhjzH2qLi0ov384,1367
8
+ oceanprotocol_job_details/domain/__init__.py,sha256=GA0Fl0GB4_ioMNfdLAPHpGjo9oImy_1DqNWX9yMJqRo,243
9
+ oceanprotocol_job_details/domain/ddo.py,sha256=sLNuxue6PYGs1fXR7FCuzS1Ny0gybJL1kwZF_aptkzE,2307
10
+ oceanprotocol_job_details/domain/derived.py,sha256=GBq_HDWJnVbyj0KejYfOEb6FL3kVrn6may4cKQYtsws,1677
11
+ oceanprotocol_job_details/loaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ oceanprotocol_job_details/loaders/loader.py,sha256=36X2s_0lN89kCUpItxEXfIzuBBNJySebP2B_tdWK2E0,186
13
+ oceanprotocol_job_details/loaders/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ oceanprotocol_job_details/loaders/impl/ddo.py,sha256=jKesMAOTtbHeXtOM_aWyOiX4GDfT3IRdmiub1FkuMCs,748
15
+ oceanprotocol_job_details/loaders/impl/files.py,sha256=01wZAhR41lh3lAiJrMMjNguWI_JlY28oCix2zN9CNXo,1136
16
+ oceanprotocol_job_details/loaders/impl/job_details.py,sha256=06Zy8SdIpEmrqbmA8de-InMPrrh3PygzcgOVBPr5Ks8,806
17
+ oceanprotocol_job_details-0.3.14.dist-info/METADATA,sha256=sNjiZklJFJoGCakjHdeei9e69lKhBFuBipBvYIMPCYs,4520
18
+ oceanprotocol_job_details-0.3.14.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
19
+ oceanprotocol_job_details-0.3.14.dist-info/licenses/LICENSE,sha256=ni3ix7P_GxK1W3VGC4fJ3o6QoCngCEpSuTJwO4nkpbw,1055
20
+ oceanprotocol_job_details-0.3.14.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- oceanprotocol_job_details/__init__.py,sha256=sq_C8Ey4_4oHiX9cO1wne7ZsWqs86WWgHfKQ5PMwwak,218
2
- oceanprotocol_job_details/di.py,sha256=H4_n9NrNuRB2s9uarSkwDYzjD1i7sV8dySRfHt21QWM,1316
3
- oceanprotocol_job_details/domain.py,sha256=ifw-hKFAJj0Gl_wJuH-51LQx4KPsCyEVDsT10xg9uBw,3842
4
- oceanprotocol_job_details/executors.py,sha256=Csc1MaUf0-Ve4Wp80qIanL7reRTT2qA3W3QrSE2jQO4,556
5
- oceanprotocol_job_details/helpers.py,sha256=ABm3oIRwPd-4XeCOIszCbfL2wkUJqVJJ2bqy3hR4jyw,1064
6
- oceanprotocol_job_details/ocean.py,sha256=kq7tAHJ36s7piJ_WpXVaTmSAMe87tuwHDeMQB-xS0T4,1702
7
- oceanprotocol_job_details/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- oceanprotocol_job_details/settings.py,sha256=o_1Hn2vl5hMk7bAkdS7GjE4nKOAyHm7dScO2_o2sPuY,1345
9
- oceanprotocol_job_details/loaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- oceanprotocol_job_details/loaders/loader.py,sha256=36X2s_0lN89kCUpItxEXfIzuBBNJySebP2B_tdWK2E0,186
11
- oceanprotocol_job_details/loaders/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- oceanprotocol_job_details/loaders/impl/ddo.py,sha256=XthrQFhmP85XSVzVjBlLePtTowGR3BAsmVp3jngiQ08,668
13
- oceanprotocol_job_details/loaders/impl/files.py,sha256=Y2vFBT2T9w9zrdpmf550-LQJxwtNPUGa0UU6bBzk9AU,1145
14
- oceanprotocol_job_details/loaders/impl/job_details.py,sha256=QwlUaG9KozkI1wX66oDTPg4TjGkvSsi8O-TctF6eWvo,724
15
- oceanprotocol_job_details-0.3.13.dist-info/METADATA,sha256=9jFrlbS9oWNQXrHj_jY3ia16bA3HvdBeMt6nMdUguHk,4509
16
- oceanprotocol_job_details-0.3.13.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
17
- oceanprotocol_job_details-0.3.13.dist-info/licenses/LICENSE,sha256=ni3ix7P_GxK1W3VGC4fJ3o6QoCngCEpSuTJwO4nkpbw,1055
18
- oceanprotocol_job_details-0.3.13.dist-info/RECORD,,