pyawe 0.1.0__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.
pyawe/__init__.py ADDED
@@ -0,0 +1,77 @@
1
+ """pyawe — Python client library for the AWE (Advanced Workflow Engine) API."""
2
+
3
+ from .client import AweClient
4
+ from .exceptions import (
5
+ AweAuthError,
6
+ AweError,
7
+ AweNotFoundError,
8
+ AweServerError,
9
+ AweValidationError,
10
+ )
11
+ from .models import (
12
+ CreateLoopBlockResponse,
13
+ DataBinding,
14
+ ExecutionProfile,
15
+ Job,
16
+ JobWithWorkflows,
17
+ LoginInfo,
18
+ LoopBlock,
19
+ LoopType,
20
+ Note,
21
+ NoteFolder,
22
+ PortDirection,
23
+ PortValueType,
24
+ ScheduleStatus,
25
+ ScriptType,
26
+ Status,
27
+ Task,
28
+ TaskLink,
29
+ TaskPortSpec,
30
+ TaskPortValues,
31
+ TaskRun,
32
+ TaskScript,
33
+ TaskTeamRole,
34
+ TaskType,
35
+ TaskWithContext,
36
+ Workflow,
37
+ WorkflowWithTasks,
38
+ )
39
+
40
+ __all__ = [
41
+ # Client
42
+ "AweClient",
43
+ # Exceptions
44
+ "AweError",
45
+ "AweAuthError",
46
+ "AweNotFoundError",
47
+ "AweValidationError",
48
+ "AweServerError",
49
+ # Enumerations
50
+ "Status",
51
+ "ScheduleStatus",
52
+ "TaskType",
53
+ "PortDirection",
54
+ "PortValueType",
55
+ "ScriptType",
56
+ "LoopType",
57
+ # Models
58
+ "LoginInfo",
59
+ "Workflow",
60
+ "WorkflowWithTasks",
61
+ "Task",
62
+ "TaskWithContext",
63
+ "TaskLink",
64
+ "TaskPortSpec",
65
+ "TaskPortValues",
66
+ "DataBinding",
67
+ "TaskScript",
68
+ "TaskRun",
69
+ "TaskTeamRole",
70
+ "Job",
71
+ "JobWithWorkflows",
72
+ "ExecutionProfile",
73
+ "LoopBlock",
74
+ "CreateLoopBlockResponse",
75
+ "Note",
76
+ "NoteFolder",
77
+ ]
pyawe/_http.py ADDED
@@ -0,0 +1,103 @@
1
+ """Internal HTTP session: request dispatch and error mapping."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from typing import Any, Dict, Optional
7
+
8
+ import requests
9
+
10
+ from .exceptions import AweAuthError, AweError, AweNotFoundError, AweServerError, AweValidationError
11
+
12
+
13
+ def _compact(d: Dict[str, Any]) -> Dict[str, Any]:
14
+ """Return a copy of *d* with all ``None``-valued keys removed."""
15
+ return {k: v for k, v in d.items() if v is not None}
16
+
17
+
18
+ def _str_id(value: Any) -> Optional[str]:
19
+ """Coerce a ``uuid.UUID`` or string identifier to ``str``, pass ``None`` through."""
20
+ if value is None:
21
+ return None
22
+ return str(value) if isinstance(value, uuid.UUID) else value
23
+
24
+
25
+ class _HttpSession:
26
+ """Thin wrapper around :class:`requests.Session` that handles auth headers and
27
+ maps non-2xx responses to typed exceptions."""
28
+
29
+ def __init__(self, api_url: str, auth_url: str) -> None:
30
+ self._api_url = api_url.rstrip("/")
31
+ self._auth_url = auth_url.rstrip("/")
32
+ self._session = requests.Session()
33
+ self._session.headers.update({"Content-Type": "application/json"})
34
+ self._token: Optional[str] = None
35
+
36
+ def set_token(self, token: str) -> None:
37
+ """Store the JWT and attach it to all subsequent requests."""
38
+ self._token = token
39
+ self._session.headers["Authorization"] = f"Bearer {token}"
40
+
41
+ def _require_auth(self) -> None:
42
+ if self._token is None:
43
+ raise AweAuthError("Not authenticated — call AweClient.login() first.")
44
+
45
+ def _raise_for_status(self, response: requests.Response) -> None:
46
+ if response.ok:
47
+ return
48
+ try:
49
+ body = response.json()
50
+ message = body.get("error") or body.get("message") or response.text
51
+ except Exception:
52
+ message = response.text
53
+ if response.status_code == 401:
54
+ raise AweAuthError(message)
55
+ if response.status_code == 403:
56
+ raise AweAuthError(f"Forbidden: {message}")
57
+ if response.status_code == 404:
58
+ raise AweNotFoundError(message)
59
+ if response.status_code == 400:
60
+ raise AweValidationError(message)
61
+ if response.status_code >= 500:
62
+ raise AweServerError(f"Server error {response.status_code}: {message}")
63
+ raise AweError(f"HTTP {response.status_code}: {message}")
64
+
65
+ # ── auth service ──────────────────────────────────────────────────────────
66
+
67
+ def post_auth(self, path: str, json: Any) -> Any:
68
+ """POST to the authentication service (no JWT header required)."""
69
+ response = self._session.post(f"{self._auth_url}{path}", json=json)
70
+ self._raise_for_status(response)
71
+ return response.json()
72
+
73
+ # ── AWE API ───────────────────────────────────────────────────────────────
74
+
75
+ def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
76
+ self._require_auth()
77
+ response = self._session.get(f"{self._api_url}{path}", params=params)
78
+ self._raise_for_status(response)
79
+ return response.json()
80
+
81
+ def post(self, path: str, json: Any = None) -> Any:
82
+ self._require_auth()
83
+ response = self._session.post(f"{self._api_url}{path}", json=json)
84
+ self._raise_for_status(response)
85
+ return response.json() if response.status_code != 204 else None
86
+
87
+ def put(self, path: str, json: Any = None) -> Any:
88
+ self._require_auth()
89
+ response = self._session.put(f"{self._api_url}{path}", json=json)
90
+ self._raise_for_status(response)
91
+ return response.json() if response.status_code != 204 else None
92
+
93
+ def patch(self, path: str, json: Any = None) -> Any:
94
+ self._require_auth()
95
+ response = self._session.patch(f"{self._api_url}{path}", json=json)
96
+ self._raise_for_status(response)
97
+ return response.json() if response.status_code != 204 else None
98
+
99
+ def delete(self, path: str, json: Any = None) -> Any:
100
+ self._require_auth()
101
+ response = self._session.delete(f"{self._api_url}{path}", json=json)
102
+ self._raise_for_status(response)
103
+ return response.json() if response.status_code != 204 else None