pinexq-client 0.3.0.20240620.2__py3-none-any.whl → 0.4.0.2024.717.1__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 (32) hide show
  1. pinexq_client/core/enterapi.py +8 -9
  2. pinexq_client/core/exceptions.py +73 -2
  3. pinexq_client/core/hco/action_hco.py +12 -22
  4. pinexq_client/core/hco/action_with_parameters_hco.py +15 -21
  5. pinexq_client/core/hco/download_link_hco.py +2 -3
  6. pinexq_client/core/hco/hco_base.py +2 -3
  7. pinexq_client/core/hco/link_hco.py +10 -9
  8. pinexq_client/core/hco/unavailable.py +17 -0
  9. pinexq_client/core/hco/upload_action_hco.py +11 -10
  10. pinexq_client/core/model/error.py +5 -1
  11. pinexq_client/core/sirenaccess.py +18 -27
  12. pinexq_client/job_management/enterjma.py +2 -4
  13. pinexq_client/job_management/hcos/entrypoint_hco.py +36 -35
  14. pinexq_client/job_management/hcos/input_dataslot_hco.py +53 -51
  15. pinexq_client/job_management/hcos/job_hco.py +121 -120
  16. pinexq_client/job_management/hcos/job_query_result_hco.py +72 -47
  17. pinexq_client/job_management/hcos/jobsroot_hco.py +44 -42
  18. pinexq_client/job_management/hcos/output_dataslot_hco.py +1 -1
  19. pinexq_client/job_management/hcos/processing_step_hco.py +71 -70
  20. pinexq_client/job_management/hcos/processingstep_query_result_hco.py +76 -51
  21. pinexq_client/job_management/hcos/processingsteproot_hco.py +44 -43
  22. pinexq_client/job_management/hcos/workdata_hco.py +81 -80
  23. pinexq_client/job_management/hcos/workdata_query_result_hco.py +75 -52
  24. pinexq_client/job_management/hcos/workdataroot_hco.py +53 -52
  25. pinexq_client/job_management/tool/job.py +104 -7
  26. pinexq_client/job_management/tool/job_group.py +140 -0
  27. pinexq_client/job_management/tool/workdata.py +8 -0
  28. {pinexq_client-0.3.0.20240620.2.dist-info → pinexq_client-0.4.0.2024.717.1.dist-info}/METADATA +1 -1
  29. pinexq_client-0.4.0.2024.717.1.dist-info/RECORD +52 -0
  30. {pinexq_client-0.3.0.20240620.2.dist-info → pinexq_client-0.4.0.2024.717.1.dist-info}/WHEEL +1 -1
  31. pinexq_client-0.3.0.20240620.2.dist-info/RECORD +0 -50
  32. {pinexq_client-0.3.0.20240620.2.dist-info → pinexq_client-0.4.0.2024.717.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,17 +1,16 @@
1
- from typing import TypeVar, Type
2
-
3
1
  import httpx
4
2
 
5
- from pinexq_client.core import Entity
3
+ from typing import TypeVar, Type
4
+ from pinexq_client.core import Entity, get_resource, raise_exception_on_error
6
5
  from pinexq_client.core.hco.hco_base import Hco
7
6
 
8
7
  THco = TypeVar('THco', bound=Hco)
9
8
 
10
9
 
11
- def enter_api(client: httpx.Client, entrypoint_hco_type: Type[THco], entrypoint_entity_type: Type[Entity] = Entity,
10
+ def enter_api(client: httpx.Client,
11
+ entrypoint_hco_type: Type[THco],
12
+ entrypoint_entity_type: Type[Entity] = Entity,
12
13
  entrypoint: str = "api/EntryPoint") -> THco:
13
- entry_point_response = client.get(url=entrypoint)
14
- entry_point_response.raise_for_status()
15
- entrypoint_entity = entrypoint_entity_type.model_validate_json(entry_point_response.read())
16
-
17
- return entrypoint_hco_type.from_entity(entrypoint_entity, client)
14
+ entry_point_response = get_resource(client=client, href=entrypoint, parse_type=entrypoint_entity_type)
15
+ raise_exception_on_error("Error accessing the API", entry_point_response)
16
+ return entrypoint_hco_type.from_entity(entry_point_response, client)
@@ -1,2 +1,73 @@
1
- class SirenException(Exception):
2
- pass
1
+ from httpx import Response, URL
2
+
3
+ from pinexq_client.core.model.error import ProblemDetails
4
+ from .model.sirenmodels import TEntity
5
+
6
+
7
+ class ClientException(Exception):
8
+ """
9
+ Base class for all exceptions that are thrown by the PinexQ client.
10
+ """
11
+
12
+ def __init__(self, message: str):
13
+ self.message = message
14
+
15
+
16
+ class NotAvailableException(ClientException):
17
+ """
18
+ Exception that is thrown when an action or a link is not available.
19
+ """
20
+
21
+
22
+ class ApiException(Exception):
23
+ """
24
+ Base class for all exceptions that are thrown by the PinexQ API.
25
+ """
26
+ status: int = None # default status code, can be overridden in subclasses
27
+ problem_details: ProblemDetails | None = None
28
+
29
+ def __init__(self, message: str, problem_details: ProblemDetails | None = None):
30
+ super().__init__(message)
31
+ self.problem_details = problem_details
32
+
33
+ def __str__(self) -> str:
34
+ message = super().__str__()
35
+ if self.problem_details:
36
+ message += f"\n{self.problem_details}"
37
+ return message
38
+
39
+
40
+ class TemporarilyNotAvailableException(ApiException):
41
+ """
42
+ Exception that is thrown when the API returns a 503 status code.
43
+
44
+ """
45
+ status: int = 503
46
+
47
+
48
+ class TooManyRequestsException(ApiException):
49
+ """
50
+ Exception that is thrown when the API returns a 429 status code.
51
+
52
+ """
53
+ status: int = 429
54
+
55
+
56
+ def raise_exception_on_error(message: str, response: TEntity | Response | ProblemDetails | URL | None):
57
+ match response:
58
+ case ProblemDetails() as problem_details:
59
+ match problem_details.status:
60
+ case 429:
61
+ raise TooManyRequestsException(message, problem_details)
62
+ case 503:
63
+ raise TemporarilyNotAvailableException(message, problem_details)
64
+ case _:
65
+ raise ApiException(message, problem_details)
66
+ case Response() as http_response:
67
+ match http_response.status_code:
68
+ case 429:
69
+ raise TooManyRequestsException(message)
70
+ case 503:
71
+ raise TemporarilyNotAvailableException(message)
72
+ case _:
73
+ raise ApiException(message)
@@ -1,11 +1,11 @@
1
1
  from typing import TypeVar, Self
2
2
 
3
3
  import httpx
4
- from httpx import Response
5
4
  from httpx import URL
6
5
 
7
- from pinexq_client.core import SirenException, Entity, Action, ProblemDetails, execute_action
6
+ from pinexq_client.core import Entity, Action, execute_action, raise_exception_on_error, ClientException
8
7
  from pinexq_client.core.hco.hco_base import ClientContainer
8
+ from pinexq_client.core.hco.unavailable import UnavailableAction
9
9
 
10
10
  TEntity = TypeVar('TEntity', bound=Entity)
11
11
  THcoEntity = TypeVar('THcoEntity', bound=Entity)
@@ -16,21 +16,21 @@ class ActionHco(ClientContainer):
16
16
  _action: Action
17
17
 
18
18
  @classmethod
19
- def from_action_optional(cls, client: httpx.Client, action: Action | None) -> Self | None:
19
+ def from_action_optional(cls, client: httpx.Client, action: Action | None) -> Self | UnavailableAction:
20
20
  if action is None:
21
- return None
21
+ return UnavailableAction()
22
22
 
23
23
  if action.has_parameters():
24
- raise SirenException(f"Error while mapping action: expected action no parameters but got some")
24
+ raise ClientException(f"Error while mapping action: expected action no parameters but got some")
25
25
 
26
26
  instance = cls(client)
27
27
  instance._action = action
28
28
  return instance
29
29
 
30
30
  @classmethod
31
- def from_entity_optional(cls, client: httpx.Client, entity: Entity, name: str) -> Self | None:
31
+ def from_entity_optional(cls, client: httpx.Client, entity: Entity, name: str) -> Self | UnavailableAction:
32
32
  if entity is None:
33
- return None
33
+ return UnavailableAction()
34
34
 
35
35
  action = entity.find_first_action_with_name(name)
36
36
  return cls.from_action_optional(client, action)
@@ -38,33 +38,23 @@ class ActionHco(ClientContainer):
38
38
  @classmethod
39
39
  def from_action(cls, client: httpx.Client, action: Action) -> Self:
40
40
  action = cls.from_action_optional(client, action)
41
- if action is None:
42
- raise SirenException(
41
+ if isinstance(action, UnavailableAction):
42
+ raise ClientException(
43
43
  f"Error while mapping mandatory action: does not exist")
44
44
  return action
45
45
 
46
46
  @classmethod
47
47
  def from_entity(cls, client: httpx.Client, entity: Entity, name: str) -> Self:
48
48
  result = cls.from_entity_optional(client, entity, name)
49
- if result is None:
50
- raise SirenException(
49
+ if isinstance(result, UnavailableAction):
50
+ raise ClientException(
51
51
  f"Error while mapping mandatory action {name}: does not exist")
52
52
  return result
53
53
 
54
54
  def _execute_internal(self) -> None | URL:
55
55
  response = execute_action(self._client, self._action)
56
-
57
- if isinstance(response, ProblemDetails):
58
- raise SirenException(
59
- f"Error while executing action: {response}")
60
- if isinstance(response, Response):
61
- raise SirenException(
62
- f"Error while executing action, unexpected response: {response}")
56
+ raise_exception_on_error(f"Error while executing action, unexpected response", response)
63
57
  return response
64
58
 
65
59
  def __repr__(self):
66
60
  return f"<{self.__class__.__name__}: '{self._action.name}'>"
67
-
68
-
69
-
70
-
@@ -1,12 +1,12 @@
1
1
  from typing import TypeVar, Type, Self, Generic
2
2
 
3
3
  import httpx
4
- from httpx import Response
5
4
  from httpx import URL
6
5
  from pydantic import BaseModel
7
6
 
8
- from pinexq_client.core import SirenException, Entity, Action, ProblemDetails, execute_action
7
+ from pinexq_client.core import Entity, Action, execute_action, raise_exception_on_error, ClientException
9
8
  from pinexq_client.core.hco.hco_base import ClientContainer
9
+ from pinexq_client.core.hco.unavailable import UnavailableAction
10
10
 
11
11
  TParameters = TypeVar('TParameters', bound=BaseModel)
12
12
 
@@ -16,12 +16,12 @@ class ActionWithParametersHco(ClientContainer, Generic[TParameters]):
16
16
  _action: Action
17
17
 
18
18
  @classmethod
19
- def from_action_optional(cls, client: httpx.Client, action: Action | None) -> Self | None:
19
+ def from_action_optional(cls, client: httpx.Client, action: Action | None) -> Self | UnavailableAction:
20
20
  if action is None:
21
- return None
21
+ return UnavailableAction()
22
22
 
23
23
  if not action.has_parameters():
24
- raise SirenException(
24
+ raise ClientException(
25
25
  f"Error while mapping action: expected action with parameters but got none")
26
26
 
27
27
  instance = cls(client)
@@ -29,9 +29,9 @@ class ActionWithParametersHco(ClientContainer, Generic[TParameters]):
29
29
  return instance
30
30
 
31
31
  @classmethod
32
- def from_entity_optional(cls, client: httpx.Client, entity: Entity, name: str) -> Self | None:
32
+ def from_entity_optional(cls, client: httpx.Client, entity: Entity, name: str) -> Self | UnavailableAction:
33
33
  if entity is None:
34
- return None
34
+ return UnavailableAction()
35
35
 
36
36
  action = entity.find_first_action_with_name(name)
37
37
  return cls.from_action_optional(client, action)
@@ -39,43 +39,37 @@ class ActionWithParametersHco(ClientContainer, Generic[TParameters]):
39
39
  @classmethod
40
40
  def from_action(cls, client: httpx.Client, action: Action) -> Self:
41
41
  action = cls.from_action_optional(client, action)
42
- if action is None:
43
- raise SirenException(
42
+ if isinstance(action, UnavailableAction):
43
+ raise ClientException(
44
44
  f"Error while mapping mandatory action: action does not exist")
45
45
  return action
46
46
 
47
47
  @classmethod
48
48
  def from_entity(cls, client: httpx.Client, entity: Entity, name: str) -> Self:
49
49
  result = cls.from_entity_optional(client, entity, name)
50
- if result is None:
51
- raise SirenException(
50
+ if isinstance(result, UnavailableAction):
51
+ raise ClientException(
52
52
  f"Error while mapping mandatory action {name}: action does not exist")
53
53
  return result
54
54
 
55
55
  def _execute_internal(self, parameters: BaseModel) -> None | URL:
56
56
  if parameters is None:
57
- raise SirenException(f"Error while executing action: action requires parameters")
57
+ raise ClientException(f"Error while executing action: action requires parameters")
58
58
 
59
59
  response = execute_action(self._client, self._action, parameters)
60
-
61
- if isinstance(response, ProblemDetails):
62
- raise SirenException(
63
- f"Error while executing action: {response}")
64
- if isinstance(response, Response):
65
- raise SirenException(
66
- f"Error while executing action, unexpected response: {response}")
60
+ raise_exception_on_error(f"Error while executing action, unexpected response", response)
67
61
  return response
68
62
 
69
63
  def _execute(self, parameters: TParameters):
70
64
  result = self._execute_internal(parameters)
71
65
  if result is not None:
72
- raise SirenException("Action did respond with unexpected URL")
66
+ raise ClientException("Action did respond with unexpected URL")
73
67
  return
74
68
 
75
69
  def _execute_returns_url(self, parameters: TParameters) -> URL:
76
70
  result = self._execute_internal(parameters)
77
71
  if result is None:
78
- raise SirenException("Action did not respond with URL")
72
+ raise ClientException("Action did not respond with URL")
79
73
  return result
80
74
 
81
75
  def _get_default_parameters(self, parameter_type: Type[TParameters],
@@ -1,10 +1,9 @@
1
- from io import BytesIO
2
1
  from typing import Self
3
2
 
4
3
  import httpx
5
4
  from httpx import Response
6
5
 
7
- from pinexq_client.core import Link, Entity, get_resource, SirenException
6
+ from pinexq_client.core import Link, Entity, get_resource, ClientException
8
7
  from pinexq_client.core.hco.link_hco import LinkHco
9
8
 
10
9
 
@@ -29,7 +28,7 @@ class DownloadLinkHco(LinkHco):
29
28
  def download(self) -> bytes:
30
29
  response: Response = get_resource(self._client, self._link.href, self._link.type)
31
30
  if not isinstance(response, Response):
32
- raise SirenException(
31
+ raise ClientException(
33
32
  f"Error while downloading resource: did not get response type")
34
33
 
35
34
  return response.content
@@ -4,7 +4,7 @@ from typing import List, TypeVar, Generic, Callable, Any
4
4
  import httpx
5
5
  from httpx import URL
6
6
 
7
- from pinexq_client.core import SirenException, Entity, BaseRelations, Link
7
+ from pinexq_client.core import Entity, BaseRelations, Link, ClientException
8
8
 
9
9
  TEntity = TypeVar('TEntity', bound=Entity)
10
10
  THcoEntity = TypeVar('THcoEntity', bound=Entity)
@@ -64,7 +64,7 @@ class Hco(ClientContainer, Generic[THcoEntity]):
64
64
  def check_classes(existing_classes: List[str], expected_classes: List[str]):
65
65
  for expected_class in expected_classes:
66
66
  if expected_class not in existing_classes:
67
- raise SirenException(
67
+ raise ClientException(
68
68
  f"Error while mapping entity:expected hco class {expected_class} is not a class of generic entity "
69
69
  f"with classes {existing_classes}")
70
70
 
@@ -88,4 +88,3 @@ class Hco(ClientContainer, Generic[THcoEntity]):
88
88
  if hco_property.converter:
89
89
  property_value = hco_property.converter(property_value)
90
90
  setattr(self, var_name, property_value)
91
-
@@ -3,8 +3,9 @@ from typing import Self, Type
3
3
  import httpx
4
4
  from httpx import URL
5
5
 
6
- from pinexq_client.core import Link, SirenException, Entity, navigate, ensure_siren_response
6
+ from pinexq_client.core import Link, Entity, navigate, ensure_siren_response, ClientException
7
7
  from pinexq_client.core.hco.hco_base import ClientContainer, TEntity
8
+ from pinexq_client.core.hco.unavailable import UnavailableLink
8
9
 
9
10
 
10
11
  class LinkHco(ClientContainer):
@@ -12,18 +13,18 @@ class LinkHco(ClientContainer):
12
13
  _link: Link
13
14
 
14
15
  @classmethod
15
- def from_link_optional(cls, client: httpx.Client, link: Link | None) -> Self | None:
16
+ def from_link_optional(cls, client: httpx.Client, link: Link | None) -> Self | UnavailableLink:
16
17
  if link is None:
17
- return None
18
+ return UnavailableLink()
18
19
 
19
20
  instance = cls(client)
20
21
  instance._link = link
21
22
  return instance
22
23
 
23
24
  @classmethod
24
- def from_entity_optional(cls, client: httpx.Client, entity: Entity, link_relation: str) -> Self | None:
25
+ def from_entity_optional(cls, client: httpx.Client, entity: Entity, link_relation: str) -> Self | UnavailableLink:
25
26
  if entity is None:
26
- return None
27
+ return UnavailableLink()
27
28
 
28
29
  link = entity.find_first_link_with_relation(link_relation)
29
30
  return cls.from_link_optional(client, link)
@@ -31,16 +32,16 @@ class LinkHco(ClientContainer):
31
32
  @classmethod
32
33
  def from_link(cls, client: httpx.Client, link: Link) -> Self:
33
34
  result = cls.from_link_optional(client, link)
34
- if result is None:
35
- raise SirenException(f"Error while mapping mandatory link: link is None")
35
+ if isinstance(result, UnavailableLink):
36
+ raise ClientException(f"Error while mapping mandatory link: link is None")
36
37
 
37
38
  return result
38
39
 
39
40
  @classmethod
40
41
  def from_entity(cls, client: httpx.Client, entity: Entity, link_relation: str) -> Self:
41
42
  result = cls.from_entity_optional(client, entity, link_relation)
42
- if result is None:
43
- raise SirenException(
43
+ if isinstance(result, UnavailableLink):
44
+ raise ClientException(
44
45
  f"Error while mapping mandatory link: entity contains no link with relation {link_relation}")
45
46
 
46
47
  return result
@@ -0,0 +1,17 @@
1
+ from pinexq_client.core import NotAvailableException
2
+
3
+
4
+ class UnavailableAction:
5
+ """This class is used to represent an action that is not available. It is used to avoid None
6
+ checks in the code."""
7
+
8
+ def execute(self, *args, **kwargs):
9
+ raise NotAvailableException(f"Error while executing action: action is not available")
10
+
11
+
12
+ class UnavailableLink:
13
+ """This class is used to represent a link that is not available. It is used to avoid None
14
+ checks in the code."""
15
+
16
+ def navigate(self):
17
+ raise NotAvailableException(f"Error while navigating: link is not available")
@@ -1,13 +1,14 @@
1
1
  from io import IOBase
2
- from typing import BinaryIO, Iterable, AsyncIterable, Any, Self, List
2
+ from typing import Iterable, AsyncIterable, Any, Self
3
3
 
4
4
  import httpx
5
5
  from httpx import URL
6
6
  from pydantic import BaseModel, ConfigDict, Field, model_validator
7
7
 
8
- from pinexq_client.core import upload_file, upload_binary, upload_json, SirenException, MediaTypes, Action, Entity, \
9
- SirenClasses
8
+ from pinexq_client.core import upload_file, upload_binary, upload_json, MediaTypes, Action, Entity, \
9
+ SirenClasses, ClientException
10
10
  from pinexq_client.core.hco.action_with_parameters_hco import ActionWithParametersHco
11
+ from pinexq_client.core.hco.unavailable import UnavailableAction
11
12
 
12
13
 
13
14
  class UploadParameters(BaseModel):
@@ -42,7 +43,7 @@ class UploadAction(ActionWithParametersHco[UploadParameters]):
42
43
  def from_action_optional(cls, client: httpx.Client, action: Action | None) -> Self | None:
43
44
  instance = super(UploadAction, cls).from_action_optional(client, action)
44
45
 
45
- if instance:
46
+ if not isinstance(instance, UnavailableAction):
46
47
  cls.validate_file_upload_field(instance)
47
48
  cls.assign_upload_constraints(instance)
48
49
  return instance
@@ -51,7 +52,7 @@ class UploadAction(ActionWithParametersHco[UploadParameters]):
51
52
  def from_entity_optional(cls, client: httpx.Client, entity: Entity, name: str) -> Self | None:
52
53
  instance = super(UploadAction, cls).from_entity_optional(client, entity, name)
53
54
 
54
- if instance:
55
+ if not isinstance(instance, UnavailableAction):
55
56
  cls.validate_file_upload_field(instance)
56
57
  cls.assign_upload_constraints(instance)
57
58
  return instance
@@ -80,17 +81,17 @@ class UploadAction(ActionWithParametersHco[UploadParameters]):
80
81
  def validate_file_upload_field(self):
81
82
  action = self._action
82
83
  if SirenClasses.FileUploadAction not in action.class_:
83
- raise SirenException(
84
+ raise ClientException(
84
85
  f"Upload action does not have expected class: {str(SirenClasses.FileUploadAction)}. Got: {action.class_}")
85
86
 
86
87
  if action.type != MediaTypes.MULTIPART_FORM_DATA.value:
87
- raise SirenException(
88
+ raise ClientException(
88
89
  f"Upload action does not have expected type: {str(MediaTypes.MULTIPART_FORM_DATA)}. Got: {action.type}")
89
90
 
90
91
  upload_fields = self._action.fields[0]
91
92
 
92
93
  if upload_fields.type != "file":
93
- raise SirenException(
94
+ raise ClientException(
94
95
  f"Upload action does not have expected field type: 'file'. Got: {upload_fields.type}")
95
96
 
96
97
  def _upload(self, parameters: UploadParameters) -> URL:
@@ -106,8 +107,8 @@ class UploadAction(ActionWithParametersHco[UploadParameters]):
106
107
  elif parameters.json_ is not None:
107
108
  result = upload_json(self._client, self._action, parameters.json_, parameters.filename)
108
109
  else:
109
- raise SirenException("Did not execute upload, none selected")
110
+ raise ClientException("Did not execute upload, none selected")
110
111
 
111
112
  if not isinstance(result, URL):
112
- raise SirenException("Upload did not respond with location")
113
+ raise ClientException("Upload did not respond with location")
113
114
  return result
@@ -1,4 +1,4 @@
1
- from pydantic import BaseModel
1
+ from pydantic import BaseModel, Field, ConfigDict
2
2
 
3
3
 
4
4
  class ProblemDetails(BaseModel):
@@ -7,3 +7,7 @@ class ProblemDetails(BaseModel):
7
7
  status: int | None = None
8
8
  detail: str | None = None
9
9
  instance: str | None = None
10
+
11
+ def __str__(self):
12
+ message = [f" {key}: {value}" for key, value in self.model_dump().items() if value]
13
+ return str.join("\n", message)
@@ -1,22 +1,19 @@
1
1
  import json
2
- import logging
2
+ import warnings
3
3
  from io import BytesIO
4
4
  from typing import Any, BinaryIO, Iterable, AsyncIterable
5
5
 
6
6
  import httpx
7
- from httpx import Response
8
- from httpx import URL
7
+ from httpx import Response, URL
9
8
  from pydantic import BaseModel
10
9
 
11
- from .exceptions import SirenException
12
- from .http_headers import Headers
13
10
  from .base_relations import BaseRelations
11
+ from .exceptions import raise_exception_on_error, ClientException
12
+ from .http_headers import Headers
14
13
  from .media_types import MediaTypes
15
14
  from .model.error import ProblemDetails
16
15
  from .model.sirenmodels import Entity, Link, Action, TEntity
17
16
 
18
- logger = logging.getLogger(__name__)
19
-
20
17
 
21
18
  # for now, we do not support navigations to non siren links (e.g. external)
22
19
  def get_resource(client: httpx.Client, href: str, media_type: str = MediaTypes.SIREN,
@@ -25,13 +22,13 @@ def get_resource(client: httpx.Client, href: str, media_type: str = MediaTypes.S
25
22
  # assume get for links
26
23
  response = client.get(href)
27
24
  except (httpx.ConnectTimeout, httpx.ConnectError) as exc:
28
- raise SirenException(f"Http-client error requesting resource: {href}") from exc
25
+ raise ClientException(f"Http-client error requesting resource: {href}") from exc
29
26
  expected_type = media_type or MediaTypes.SIREN # if not specified expect siren
30
27
 
31
28
  if response.status_code == httpx.codes.OK:
32
29
  if (media_type := response.headers.get(Headers.CONTENT_TYPE, MediaTypes.SIREN)) != expected_type:
33
- logger.warning(f"Expected type {expected_type} not matched by response: "
34
- f"' got: '{media_type}'")
30
+ warnings.warn(f"Expected type {expected_type} not matched by response: "
31
+ f"' got: '{media_type}'")
35
32
 
36
33
  if media_type == MediaTypes.SIREN.value or media_type is None: # assume siren if not specified
37
34
  resp = response.content.decode()
@@ -43,7 +40,7 @@ def get_resource(client: httpx.Client, href: str, media_type: str = MediaTypes.S
43
40
  elif response.status_code >= 400:
44
41
  return handle_error_response(response)
45
42
  else:
46
- logger.warning(f"Unexpected return code: {response.status_code}")
43
+ warnings.warn(f"Unexpected return code: {response.status_code}")
47
44
  return response
48
45
 
49
46
 
@@ -53,12 +50,7 @@ def navigate(client: httpx.Client, link: Link,
53
50
 
54
51
 
55
52
  def ensure_siren_response(response: TEntity | ProblemDetails | Response) -> TEntity:
56
- if isinstance(response, ProblemDetails):
57
- raise SirenException(
58
- f"Error while navigating: {response}")
59
- if isinstance(response, Response):
60
- raise SirenException(
61
- f"Error while navigating, unexpected response: {response}")
53
+ raise_exception_on_error(f"Error while navigating, unexpected response", response)
62
54
  return response
63
55
 
64
56
 
@@ -70,7 +62,6 @@ def upload_json(client: httpx.Client, action: Action, json_payload: Any,
70
62
  def upload_binary(client: httpx.Client, action: Action, content: str | bytes | Iterable[bytes] | AsyncIterable[bytes],
71
63
  filename: str,
72
64
  mediatype: str = MediaTypes.OCTET_STREAM) -> None | URL | ProblemDetails | Response:
73
-
74
65
  if isinstance(content, str):
75
66
  payload = BytesIO(content.encode(encoding="utf-8"))
76
67
  elif isinstance(content, bytes):
@@ -86,21 +77,21 @@ def upload_binary(client: httpx.Client, action: Action, content: str | bytes | I
86
77
  def upload_file(client: httpx.Client, action: Action, file: BinaryIO, filename: str,
87
78
  mediatype: str = MediaTypes.OCTET_STREAM) -> None | URL | ProblemDetails | Response:
88
79
  if action.type != MediaTypes.MULTIPART_FORM_DATA:
89
- raise SirenException(
80
+ raise ClientException(
90
81
  f"Action with upload requires type: {MediaTypes.MULTIPART_FORM_DATA} but found: {action.type}")
91
82
 
92
83
  files = {'upload-file': (filename, file, mediatype)}
93
84
  try:
94
85
  response = client.request(method=action.method, url=action.href, files=files)
95
86
  except httpx.RequestError as exc:
96
- raise SirenException(f"Error from httpx while uploading data to: {action.href}") from exc
87
+ raise ClientException(f"Error from httpx while uploading data to: {action.href}") from exc
97
88
  return handle_action_result(response)
98
89
 
99
90
 
100
91
  def execute_action_on_entity(client: httpx.Client, entity: Entity, name: str, parameters: BaseModel | None = None):
101
92
  action = entity.find_first_action_with_name(name)
102
93
  if action is None:
103
- raise SirenException(f"Entity does not contain expected action: {name}")
94
+ raise ClientException(f"Entity does not contain expected action: {name}")
104
95
 
105
96
  return execute_action(client, action, parameters)
106
97
 
@@ -110,11 +101,11 @@ def execute_action(client: httpx.Client, action: Action,
110
101
  if action.has_parameters() is False:
111
102
  # no parameters required
112
103
  if parameters is not None:
113
- raise SirenException(f"Action requires no parameters but got some")
104
+ raise ClientException(f"Action requires no parameters but got some")
114
105
  else:
115
106
  # parameters required
116
107
  if parameters is None:
117
- raise SirenException(f"Action requires parameters but non provided")
108
+ raise ClientException(f"Action requires parameters but non provided")
118
109
 
119
110
  action_parameters = None
120
111
  if parameters is not None:
@@ -128,7 +119,7 @@ def execute_action(client: httpx.Client, action: Action,
128
119
  headers={Headers.CONTENT_TYPE.value: MediaTypes.APPLICATION_JSON.value}
129
120
  )
130
121
  except httpx.RequestError as exc:
131
- raise SirenException(f"Error from httpx while executing action: {action.href}") from exc
122
+ raise ClientException(f"Error from httpx while executing action: {action.href}") from exc
132
123
 
133
124
  return handle_action_result(response)
134
125
 
@@ -139,7 +130,7 @@ def handle_action_result(response: Response) -> None | URL | ProblemDetails | Re
139
130
  if response.status_code == httpx.codes.CREATED:
140
131
  location_header = response.headers.get(Headers.LOCATION_HEADER, None)
141
132
  if location_header is None:
142
- logger.warning(f"Got created response without location header")
133
+ warnings.warn(f"Got created response without location header")
143
134
  return
144
135
 
145
136
  return URL(location_header)
@@ -147,7 +138,7 @@ def handle_action_result(response: Response) -> None | URL | ProblemDetails | Re
147
138
  elif response.status_code >= 400:
148
139
  return handle_error_response(response)
149
140
  else:
150
- logger.warning(f"Unexpected return code: {response.status_code}")
141
+ warnings.warn(f"Unexpected return code: {response.status_code}")
151
142
  return response
152
143
 
153
144
 
@@ -156,7 +147,7 @@ def handle_error_response(response: Response) -> ProblemDetails | Response:
156
147
  if content_type.startswith(MediaTypes.PROBLEM_DETAILS.value):
157
148
  return ProblemDetails.model_validate(response.json())
158
149
  else:
159
- logger.warning(
150
+ warnings.warn(
160
151
  f"Error case did not return media type: '{MediaTypes.PROBLEM_DETAILS.value}', "
161
152
  f"got '{response.headers.get(Headers.CONTENT_TYPE, None)}' instead!")
162
153
  return response
@@ -2,7 +2,7 @@ from typing import TypeVar, Type
2
2
 
3
3
  import httpx
4
4
 
5
- import logging
5
+ import warnings
6
6
 
7
7
  from pinexq_client.core import Entity
8
8
  from pinexq_client.core.enterapi import enter_api
@@ -11,8 +11,6 @@ from pinexq_client.job_management.hcos.entrypoint_hco import EntryPointHco
11
11
  from pinexq_client.job_management.model.sirenentities import EntryPointEntity
12
12
  import pinexq_client.job_management
13
13
 
14
- LOG = logging.getLogger(__name__)
15
-
16
14
  THco = TypeVar("THco", bound=Hco)
17
15
 
18
16
 
@@ -34,7 +32,7 @@ def enter_jma(
34
32
  client_version = pinexq_client.job_management.__jma_version__
35
33
  jma_version = [int(i) for i in str.split(info.api_version, '.')]
36
34
  if not _version_match_major_minor(jma_version, client_version):
37
- LOG.warning(
35
+ warnings.warn(
38
36
  f"Version mismatch between 'pinexq_client' (v{'.'.join(map(str ,client_version))}) "
39
37
  f"and 'JobManagementAPI' (v{'.'.join(map(str, jma_version))})! "
40
38
  )