Skip to content

Commit dd11c28

Browse files
nsa-softpayclaude
andcommitted
Add type annotations to public SDK surface
Annotate all function signatures and return types across the public API: Client, HTTPClient, all 10 model classes, utils, and exceptions. Uses Python 3.10+ syntax (str | None, built-in generics) with from __future__ import annotations for forward references. Closes #293 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bec4992 commit dd11c28

File tree

11 files changed

+214
-152
lines changed

11 files changed

+214
-152
lines changed

alertaclient/api.py

Lines changed: 107 additions & 100 deletions
Large diffs are not rendered by default.

alertaclient/models/alert.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
from __future__ import annotations
2+
13
from datetime import datetime
4+
from typing import Any
25

36
from alertaclient.utils import DateTime
47

8+
JSON = dict[str, Any]
9+
510

611
class Alert:
712

8-
def __init__(self, resource, event, **kwargs):
13+
def __init__(self, resource: str, event: str, **kwargs: Any) -> None:
914
if not resource:
1015
raise ValueError('Missing mandatory value for "resource"')
1116
if not event:
@@ -45,12 +50,12 @@ def __init__(self, resource, event, **kwargs):
4550
self.last_receive_time = kwargs.get('last_receive_time', None)
4651
self.history = kwargs.get('history', None) or list()
4752

48-
def __repr__(self):
53+
def __repr__(self) -> str:
4954
return 'Alert(id={!r}, environment={!r}, resource={!r}, event={!r}, severity={!r}, status={!r}, customer={!r})'.format(
5055
self.id, self.environment, self.resource, self.event, self.severity, self.status, self.customer)
5156

5257
@classmethod
53-
def parse(cls, json):
58+
def parse(cls, json: JSON) -> Alert:
5459
if not isinstance(json.get('correlate', []), list):
5560
raise ValueError('correlate must be a list')
5661
if not isinstance(json.get('service', []), list):
@@ -93,10 +98,10 @@ def parse(cls, json):
9398
history=json.get('history', None)
9499
)
95100

96-
def get_id(self, short=False):
101+
def get_id(self, short: bool = False) -> str | None:
97102
return self.id[:8] if short else self.id
98103

99-
def tabular(self, timezone=None):
104+
def tabular(self, timezone: str | None = None) -> dict[str, Any]:
100105
return {
101106
'id': self.get_id(short=True),
102107
'lastReceiveTime': DateTime.localtime(self.last_receive_time, timezone),

alertaclient/models/blackout.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
from __future__ import annotations
2+
13
from datetime import datetime, timedelta
4+
from typing import Any
25

36
from alertaclient.utils import DateTime
47

8+
JSON = dict[str, Any]
9+
510

611
class Blackout:
712

8-
def __init__(self, environment, **kwargs):
13+
def __init__(self, environment: str, **kwargs: Any) -> None:
914
if not environment:
1015
raise ValueError('Missing mandatory value for "environment"')
1116

@@ -62,7 +67,7 @@ def __init__(self, environment, **kwargs):
6267
self.status = 'expired'
6368
self.remaining = 0
6469

65-
def __repr__(self):
70+
def __repr__(self) -> str:
6671
more = ''
6772
if self.service:
6873
more += 'service=%r, ' % self.service
@@ -91,7 +96,7 @@ def __repr__(self):
9196
)
9297

9398
@classmethod
94-
def parse(cls, json):
99+
def parse(cls, json: JSON) -> Blackout:
95100
if not isinstance(json.get('service', []), list):
96101
raise ValueError('service must be a list')
97102
if not isinstance(json.get('tags', []), list):
@@ -115,7 +120,7 @@ def parse(cls, json):
115120
text=json.get('text', None)
116121
)
117122

118-
def tabular(self, timezone=None):
123+
def tabular(self, timezone: str | None = None) -> dict[str, Any]:
119124
return {
120125
'id': self.id,
121126
'priority': self.priority,

alertaclient/models/customer.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
JSON = dict[str, Any]
6+
7+
18
class Customer:
29

3-
def __init__(self, match, customer, **kwargs):
10+
def __init__(self, match: str, customer: str, **kwargs: Any) -> None:
411
self.id = kwargs.get('id', None)
512
self.match = match
613
self.customer = customer
714

8-
def __repr__(self):
15+
def __repr__(self) -> str:
916
return 'Customer(id={!r}, match={!r}, customer={!r})'.format(
1017
self.id, self.match, self.customer)
1118

1219
@classmethod
13-
def parse(cls, json):
20+
def parse(cls, json: JSON) -> Customer:
1421
return Customer(
1522
id=json.get('id', None),
1623
match=json.get('match', None),
1724
customer=json.get('customer', None)
1825
)
1926

20-
def tabular(self):
27+
def tabular(self) -> dict[str, Any]:
2128
return {
2229
'id': self.id,
2330
'match': self.match,

alertaclient/models/heartbeat.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
from __future__ import annotations
2+
13
from datetime import datetime, timedelta
4+
from typing import Any
25

36
from alertaclient.utils import DateTime
47

8+
JSON = dict[str, Any]
9+
510
DEFAULT_MAX_LATENCY = 2000 # ms
611

712

813
class Heartbeat:
914

10-
def __init__(self, origin=None, tags=None, create_time=None, timeout=None, customer=None, **kwargs):
15+
def __init__(self, origin: str | None = None, tags: list[str] | None = None, create_time: datetime | None = None, timeout: int | None = None, customer: str | None = None, **kwargs: Any) -> None:
1116
if any(['.' in key for key in kwargs.get('attributes', dict()).keys()])\
1217
or any(['$' in key for key in kwargs.get('attributes', dict()).keys()]):
1318
raise ValueError('Attribute keys must not contain "." or "$"')
@@ -25,20 +30,20 @@ def __init__(self, origin=None, tags=None, create_time=None, timeout=None, custo
2530
self.customer = customer
2631

2732
@property
28-
def latency(self):
33+
def latency(self) -> int:
2934
return int((self.receive_time - self.create_time).total_seconds() * 1000)
3035

3136
@property
32-
def since(self):
37+
def since(self) -> timedelta:
3338
since = datetime.utcnow() - self.receive_time
3439
return since - timedelta(microseconds=since.microseconds)
3540

36-
def __repr__(self):
41+
def __repr__(self) -> str:
3742
return 'Heartbeat(id={!r}, origin={!r}, create_time={!r}, timeout={!r}, customer={!r})'.format(
3843
self.id, self.origin, self.create_time, self.timeout, self.customer)
3944

4045
@classmethod
41-
def parse(cls, json):
46+
def parse(cls, json: JSON) -> Heartbeat:
4247
if not isinstance(json.get('tags', []), list):
4348
raise ValueError('tags must be a list')
4449
if not isinstance(json.get('attributes', {}), dict):
@@ -60,7 +65,7 @@ def parse(cls, json):
6065
customer=json.get('customer', None)
6166
)
6267

63-
def tabular(self, timezone=None):
68+
def tabular(self, timezone: str | None = None) -> dict[str, Any]:
6469
return {
6570
'id': self.id,
6671
'origin': self.origin,

alertaclient/models/history.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
from __future__ import annotations
2+
13
from datetime import datetime
4+
from typing import Any
25

36
from alertaclient.utils import DateTime
47

8+
JSON = dict[str, Any]
9+
510

611
class History:
712

8-
def __init__(self, id, event, **kwargs):
13+
def __init__(self, id: str, event: str, **kwargs: Any) -> None:
914
self.id = id
1015
self.event = event
1116
self.severity = kwargs.get('severity', None)
@@ -16,14 +21,14 @@ def __init__(self, id, event, **kwargs):
1621
self.update_time = kwargs.get('update_time', None) or datetime.utcnow()
1722
self.user = kwargs.get('user', None)
1823

19-
def __repr__(self):
24+
def __repr__(self) -> str:
2025
return 'History(id={!r}, event={!r}, severity={!r}, status={!r}, type={!r})'.format(
2126
self.id, self.event, self.severity, self.status, self.change_type)
2227

2328

2429
class RichHistory:
2530

26-
def __init__(self, resource, event, **kwargs):
31+
def __init__(self, resource: str, event: str, **kwargs: Any) -> None:
2732

2833
self.id = kwargs.get('id', None)
2934
self.resource = resource
@@ -43,12 +48,12 @@ def __init__(self, resource, event, **kwargs):
4348
self.change_type = kwargs.get('change_type', kwargs.get('type', None))
4449
self.customer = kwargs.get('customer', None)
4550

46-
def __repr__(self):
51+
def __repr__(self) -> str:
4752
return 'RichHistory(id={!r}, environment={!r}, resource={!r}, event={!r}, severity={!r}, status={!r}, type={!r}, customer={!r})'.format(
4853
self.id, self.environment, self.resource, self.event, self.severity, self.status, self.change_type, self.customer)
4954

5055
@classmethod
51-
def parse(cls, json):
56+
def parse(cls, json: JSON) -> RichHistory:
5257
if not isinstance(json.get('service', []), list):
5358
raise ValueError('service must be a list')
5459
if not isinstance(json.get('tags', []), list):
@@ -73,7 +78,7 @@ def parse(cls, json):
7378
customer=json.get('customer', None)
7479
)
7580

76-
def tabular(self, timezone=None):
81+
def tabular(self, timezone: str | None = None) -> dict[str, Any]:
7782
data = {
7883
'id': self.id,
7984
'resource': self.resource,

alertaclient/models/key.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
from __future__ import annotations
2+
3+
from datetime import datetime
4+
from typing import Any
5+
16
from alertaclient.utils import DateTime
27

8+
JSON = dict[str, Any]
9+
310

411
class ApiKey:
512

6-
def __init__(self, user, scopes, text='', expire_time=None, customer=None, **kwargs):
13+
def __init__(self, user: str, scopes: list[str], text: str | None = '', expire_time: datetime | None = None, customer: str | None = None, **kwargs: Any) -> None:
714
self.id = kwargs.get('id', None)
815
self.key = kwargs.get('key', None)
916
self.user = user
@@ -15,15 +22,15 @@ def __init__(self, user, scopes, text='', expire_time=None, customer=None, **kwa
1522
self.customer = customer
1623

1724
@property
18-
def type(self):
25+
def type(self) -> str:
1926
return self.scopes_to_type(self.scopes)
2027

21-
def __repr__(self):
28+
def __repr__(self) -> str:
2229
return 'ApiKey(key={!r}, user={!r}, scopes={!r}, expireTime={!r}, customer={!r})'.format(
2330
self.key, self.user, self.scopes, self.expire_time, self.customer)
2431

2532
@classmethod
26-
def parse(cls, json):
33+
def parse(cls, json: JSON) -> ApiKey:
2734
if not isinstance(json.get('scopes', []), list):
2835
raise ValueError('scopes must be a list')
2936

@@ -39,13 +46,13 @@ def parse(cls, json):
3946
customer=json.get('customer', None)
4047
)
4148

42-
def scopes_to_type(self, scopes):
49+
def scopes_to_type(self, scopes: list[str]) -> str:
4350
for scope in scopes:
4451
if scope.startswith('write') or scope.startswith('admin'):
4552
return 'read-write'
4653
return 'read-only'
4754

48-
def tabular(self, timezone=None):
55+
def tabular(self, timezone: str | None = None) -> dict[str, Any]:
4956
return {
5057
'id': self.id,
5158
'key': self.key,

alertaclient/models/note.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
from __future__ import annotations
2+
13
from datetime import datetime
4+
from typing import Any
25

36
from alertaclient.utils import DateTime
47

8+
JSON = dict[str, Any]
9+
510

611
class Note:
712

8-
def __init__(self, text, user, note_type, **kwargs):
13+
def __init__(self, text: str, user: str, note_type: str | None, **kwargs: Any) -> None:
914

1015
self.id = kwargs.get('id', None)
1116
self.text = text
@@ -18,7 +23,7 @@ def __init__(self, text, user, note_type, **kwargs):
1823
self.customer = kwargs.get('customer')
1924

2025
@classmethod
21-
def parse(cls, json):
26+
def parse(cls, json: JSON) -> Note:
2227
return Note(
2328
id=json.get('id', None),
2429
text=json.get('text', None),
@@ -31,12 +36,12 @@ def parse(cls, json):
3136
customer=json.get('customer', None)
3237
)
3338

34-
def __repr__(self):
39+
def __repr__(self) -> str:
3540
return 'Note(id={!r}, text={!r}, user={!r}, type={!r}, customer={!r})'.format(
3641
self.id, self.text, self.user, self.note_type, self.customer
3742
)
3843

39-
def tabular(self, timezone=None):
44+
def tabular(self, timezone: str | None = None) -> dict[str, Any]:
4045
note = {
4146
'id': self.id,
4247
'text': self.text,

alertaclient/models/permission.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
JSON = dict[str, Any]
6+
7+
18
class Permission:
29

3-
def __init__(self, match, scopes, **kwargs):
10+
def __init__(self, match: str, scopes: list[str] | None, **kwargs: Any) -> None:
411
self.id = kwargs.get('id', None)
512
self.match = match
613
self.scopes = scopes or list()
714

8-
def __repr__(self):
15+
def __repr__(self) -> str:
916
return 'Perm(id={!r}, match={!r}, scopes={!r})'.format(
1017
self.id, self.match, self.scopes)
1118

1219
@classmethod
13-
def parse(cls, json):
20+
def parse(cls, json: JSON) -> Permission:
1421
if not isinstance(json.get('scopes', []), list):
1522
raise ValueError('scopes must be a list')
1623

@@ -20,7 +27,7 @@ def parse(cls, json):
2027
scopes=json.get('scopes', list())
2128
)
2229

23-
def tabular(self):
30+
def tabular(self) -> dict[str, Any]:
2431
return {
2532
'id': self.id,
2633
'match': self.match,

0 commit comments

Comments
 (0)