Skip to content

Commit 76f8fd3

Browse files
committed
Update typing
1 parent 01d71b0 commit 76f8fd3

File tree

4 files changed

+86
-54
lines changed

4 files changed

+86
-54
lines changed

toggl_python/api.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import typing
44
from functools import partial
5+
from typing import Any, Dict, Optional
56

67
import httpx
78

@@ -21,13 +22,13 @@ class Api:
2122
def __init__(
2223
self,
2324
base_url: typing.Optional[str] = None,
24-
auth: typing.Union[BasicAuth, TokenAuth] = None,
25+
auth: Optional[typing.Union[BasicAuth, TokenAuth]] = None,
2526
):
2627
if base_url:
2728
self.BASE_URL = httpx.URL(base_url)
2829
self.client = httpx.Client(base_url=self.BASE_URL, auth=auth)
2930

30-
def __getattr__(self, httpmethod):
31+
def __getattr__(self, httpmethod: str) -> Any:
3132
try:
3233
method = getattr(self.client, httpmethod)
3334
except AttributeError:
@@ -37,12 +38,12 @@ def __getattr__(self, httpmethod):
3738

3839
def api_method(
3940
self,
40-
method: typing.Callable,
41+
method: Any,
4142
url: str,
42-
params: dict = None,
43-
data: dict = None,
44-
files: dict = None,
45-
) -> httpx.Response:
43+
params: Optional[Dict[str, Any]] = None,
44+
data: Optional[Dict[str, Any]] = None,
45+
files: Optional[Dict[str, Any]] = None,
46+
) -> Any:
4647
"""
4748
Call http method with specified url and params
4849
"""

toggl_python/entities.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime
2-
from typing import Callable, List, Optional, Union
2+
from typing import Any, Callable, Dict, List, Optional, Union
33

44
from pydantic import BaseModel, EmailStr, HttpUrl
55

@@ -31,7 +31,7 @@ class Project(BaseEntity):
3131
billable: bool = True
3232
auto_estimates: Optional[bool] = False
3333
estimated_hours: Optional[int] = None
34-
color: Union[str, int] = None
34+
color: Union[str, int] = 0
3535
rate: Optional[float] = None
3636
created_at: Optional[datetime] = None
3737

@@ -66,8 +66,8 @@ class TimeEntry(BaseEntity):
6666
tid: Optional[int] = None
6767
description: Optional[str] = None
6868
billable: Optional[bool] = False
69-
start: Union[datetime, Callable] = datetime.now
70-
stop: Union[datetime, Callable] = None
69+
start: Union[datetime, Callable[[], datetime]] = datetime.now
70+
stop: Optional[Union[datetime, Callable[[], datetime]]] = None
7171
duration: int
7272
created_with: Optional[str]
7373
tags: List[str] = []
@@ -82,9 +82,9 @@ class ReportTimeEntry(BaseEntity):
8282
description: Optional[str] = None
8383
billable: Optional[int] = False
8484
is_billable: Optional[bool] = False
85-
cur: Optional[str] = False
86-
start: Union[datetime, Callable] = datetime.now
87-
end: Union[datetime, Callable] = None
85+
cur: Optional[Union[str, bool]] = False
86+
start: Union[datetime, Callable[[], datetime]] = datetime.now
87+
end: Optional[Union[datetime, Callable[[], datetime]]] = None
8888
dur: int
8989
tags: List[str] = []
9090

@@ -103,7 +103,7 @@ class User(BaseEntity):
103103
language: str
104104
image_url: HttpUrl
105105
sidebar_piechart: bool
106-
new_blog_post: dict
106+
new_blog_post: Dict[str, Any]
107107
send_product_emails: bool
108108
send_weekly_report: bool
109109
send_timer_notifications: bool

toggl_python/repository.py

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from functools import partial
2+
from typing import Any, Dict, Optional, Tuple, Type, Union
23

34
import httpx
45

@@ -10,72 +11,81 @@
1011
Group,
1112
Project,
1213
ProjectUser,
14+
ReportTimeEntry,
1315
Tag,
1416
Task,
1517
TimeEntry,
1618
User,
1719
Workspace,
1820
WorkspaceUser,
19-
ReportTimeEntry,
2021
)
2122
from .exceptions import MethodNotAllowed, NotSupported
22-
from .response import ReportTimeEntriesList
23+
from .response import ListResponse, ReportTimeEntriesList
24+
from .auth import BasicAuth, TokenAuth
2325

2426

2527
class BaseRepository(Api):
2628
LIST_URL = ""
27-
DETAIL_URL = None
28-
ENTITY_CLASS = BaseEntity
29-
ADDITIONAL_METHODS = {}
30-
EXCLUDED_METHODS = ()
31-
ADDITIONAL_PARAMS = {}
32-
DATA_CONTAINER = {}
33-
LIST_RESPONSE = None
34-
35-
def __init__(self, base_url=None, auth=None):
29+
DETAIL_URL: Optional[str] = None
30+
ENTITY_CLASS: Type[BaseEntity] = BaseEntity
31+
ADDITIONAL_METHODS: Dict[str, Dict[str, Any]] = {}
32+
EXCLUDED_METHODS: Optional[Tuple[str, ...]] = None
33+
ADDITIONAL_PARAMS: Dict[str, Dict[str, Any]] = {}
34+
DATA_CONTAINER: Dict[str, Optional[str]] = {}
35+
LIST_RESPONSE: Optional[Type[ListResponse]] = None
36+
37+
def __init__(
38+
self,
39+
base_url: Optional[str] = None,
40+
auth: Optional[Union[BasicAuth, TokenAuth]] = None,
41+
) -> None:
3642
super().__init__(base_url=base_url, auth=auth)
3743
if not self.DETAIL_URL:
3844
self.DETAIL_URL = self.LIST_URL + "/{id}"
3945

40-
def __getattr__(self, method: str):
41-
if method in self.EXCLUDED_METHODS:
46+
def __getattr__(self, attr: str) -> Any:
47+
if self.EXCLUDED_METHODS and attr in self.EXCLUDED_METHODS:
4248
raise MethodNotAllowed
4349
try:
44-
method = super().__getattr__(method)
50+
method = super().__getattr__(attr)
4551
except AttributeError:
46-
if method in self.ADDITIONAL_METHODS.keys():
52+
if attr in self.ADDITIONAL_METHODS.keys():
4753
method = partial(
48-
self.additionat_method, **self.ADDITIONAL_METHODS[method]
54+
self.additionat_method, **self.ADDITIONAL_METHODS[attr]
4955
)
5056
else:
51-
raise AttributeError(f"No such method ({method})!")
57+
raise AttributeError(f"No such method ({attr})!")
5258
return method
5359

5460
def additionat_method(
5561
self,
5662
url: str,
57-
_id: int = None,
58-
additional_id: int = None,
59-
entity: object = None,
63+
_id: Optional[int] = None,
64+
additional_id: Optional[int] = None,
65+
entity: Any = None,
6066
detail: bool = False,
6167
single_item: bool = False,
62-
data_key: str = None,
63-
params: dict = None,
64-
data: dict = None,
65-
files: dict = None,
66-
) -> httpx.Response:
68+
data_key: Optional[str] = None,
69+
params: Optional[Dict[str, Any]] = None,
70+
data: Optional[Dict[str, Any]] = None,
71+
files: Optional[Dict[str, Any]] = None,
72+
) -> Any:
6773
"""
6874
Call additional method with specified url and params
6975
"""
7076

7177
if detail:
78+
if not self.DETAIL_URL:
79+
raise AttributeError("Not defined DETAIL_URL")
7280
_url = (self.DETAIL_URL + "/" + url + "/{additional_id}").format(
7381
id=_id, additional_id=additional_id
7482
)
7583
return self._retrieve(
7684
_url, entity, headers=self.HEADERS, params=params
7785
)
7886
elif _id:
87+
if not self.DETAIL_URL:
88+
raise AttributeError("Not defined DETAIL_URL")
7989
_url = (self.DETAIL_URL + "/" + url).format(id=_id)
8090
return self._list(_url, entity, headers=self.HEADERS, param=params)
8191
elif single_item:
@@ -90,7 +100,13 @@ def additionat_method(
90100
else:
91101
raise NotSupported
92102

93-
def _retrieve(self, _url, entity_class, data_key: str = "data", **kwargs):
103+
def _retrieve(
104+
self,
105+
_url: Union[str, httpx.URL],
106+
entity_class: Any,
107+
data_key: Optional[str] = "data",
108+
**kwargs: Any,
109+
) -> Any:
94110
params = kwargs
95111
params.update(self.ADDITIONAL_PARAMS.get("retrieve", {}))
96112

@@ -102,11 +118,19 @@ def _retrieve(self, _url, entity_class, data_key: str = "data", **kwargs):
102118
if data:
103119
return entity_class(**data)
104120

105-
def retrieve(self, id: int = None, **kwargs):
121+
def retrieve(self, id: Optional[int] = None) -> Any:
122+
if not self.DETAIL_URL:
123+
raise AttributeError("Not defined DETAIL_URL")
106124
full_url = self.BASE_URL.join(self.DETAIL_URL.format(id=id))
107125
return self._retrieve(full_url, self.ENTITY_CLASS)
108126

109-
def _list(self, _url, entity_class, data_key: str = None, **kwargs):
127+
def _list(
128+
self,
129+
_url: Union[str, httpx.URL],
130+
entity_class: Any,
131+
data_key: Optional[str] = None,
132+
**kwargs: Any,
133+
) -> Any:
110134
params = kwargs
111135
params.update(self.ADDITIONAL_PARAMS.get("list", {}))
112136

@@ -123,29 +147,33 @@ def _list(self, _url, entity_class, data_key: str = None, **kwargs):
123147
value = self.LIST_RESPONSE(value, response_body)
124148
return value
125149

126-
def list(self, **kwargs):
127-
if "list" in self.EXCLUDED_METHODS:
150+
def list(self, **kwargs: Any) -> Any:
151+
if self.EXCLUDED_METHODS and "list" in self.EXCLUDED_METHODS:
128152
raise MethodNotAllowed
129153
full_url = self.BASE_URL.join(self.LIST_URL)
130154
return self._list(full_url, self.ENTITY_CLASS, **kwargs)
131155

132-
def create(self, entity: ENTITY_CLASS, **kwargs):
133-
if "create" in self.EXCLUDED_METHODS:
156+
def create(self, entity: Any, **kwargs: Any) -> Any:
157+
if self.EXCLUDED_METHODS and "create" in self.EXCLUDED_METHODS:
134158
raise MethodNotAllowed
135159
full_url = self.BASE_URL.join(self.LIST_URL)
136160
response = self.post(full_url, data=entity.dict(), **kwargs)
137161
return self.ENTITY_CLASS(**response.json())
138162

139-
def update(self, entity: ENTITY_CLASS, **kwargs):
140-
if "update" in self.EXCLUDED_METHODS:
163+
def update(self, entity: Any, **kwargs: Any) -> Any:
164+
if self.EXCLUDED_METHODS and "update" in self.EXCLUDED_METHODS:
141165
raise MethodNotAllowed
166+
if not self.DETAIL_URL:
167+
raise AttributeError("Not defined DETAIL_URL")
142168
full_url = self.BASE_URL.join(self.DETAIL_URL.format(id=entity.id))
143169
response = self.put(full_url, data=entity.dict(), **kwargs)
144170
return self.ENTITY_CLASS(**response.json())
145171

146-
def partial_update(self, entity: ENTITY_CLASS, **kwargs):
147-
if "partial_update" in self.EXCLUDED_METHODS:
172+
def partial_update(self, entity: Any, **kwargs: Any) -> Any:
173+
if self.EXCLUDED_METHODS and "partial_update" in self.EXCLUDED_METHODS:
148174
raise MethodNotAllowed
175+
if not self.DETAIL_URL:
176+
raise AttributeError("Not defined DETAIL_URL")
149177
full_url = self.BASE_URL.join(self.DETAIL_URL.format(id=entity.id))
150178
response = self.patch(full_url, data=entity.dict(), **kwargs)
151179
return self.ENTITY_CLASS(**response.json())

toggl_python/response.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
class ListResponse(list):
2-
response_parameters = ()
1+
from typing import Any, List, Tuple
32

4-
def __init__(self, value, response_body):
3+
4+
class ListResponse(List[Any]):
5+
response_parameters: Tuple[str, ...] = ()
6+
7+
def __init__(self, value: Any, response_body: Any):
58
super(ListResponse, self).__init__(value)
69

710
for parameter in self.response_parameters:

0 commit comments

Comments
 (0)