Skip to content

Commit db51ddd

Browse files
committed
[Types]: adding mypy type annotations to a lot of the repo
1 parent fc85ebb commit db51ddd

File tree

12 files changed

+159
-105
lines changed

12 files changed

+159
-105
lines changed

.prospector.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ test-warnings: false
66
ignore-patterns:
77
- (^|/)\..+
88
- .*\.html
9+
- docs/.*
10+
- tmp.py
911

1012
pylint:
1113
disable:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
repo = pyclickup
22
base_command = pytest
33
coverage = --cov-config setup.cfg --cov=$(repo)
4-
with_report = --cov-report html
4+
html_report = --cov-report html
55
term_report = --cov-report term
66
xml_report = --cov-report xml
77
reports = $(html_report) $(term_report) $(xml_report)

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Basic Usage
4949
from pyclickup import ClickUp
5050
5151
52-
clickup = ClickUp('$ACCESS_TOKEN')
52+
clickup = ClickUp("$ACCESS_TOKEN")
5353
5454
main_team = clickup.teams[0]
5555
main_space = main_team.spaces[0]

pyclickup/globals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55

6-
__version__ = "0.0.7"
6+
__version__ = "0.0.8"
77

88

99
LIBRARY = "pyclickup"

pyclickup/models/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
import json
55
from pyclickup.globals import DEFAULT_STATUSES, LIBRARY
66
from pyclickup.utils.text import snakeify, ts_to_datetime, datetime_to_ts
7+
from typing import Any
78

89

910
class BaseModel:
1011
"""basic model that just parses camelCase json to snake_case keys"""
1112

12-
def __init__(self, data, client=None, **kwargs):
13+
def __init__(self, data: dict, client: Any = None, **kwargs: Any) -> None:
1314
"""constructor"""
1415
self._data = {**data, **kwargs}
1516
self._json = self._jsond(data)
@@ -18,19 +19,19 @@ def __init__(self, data, client=None, **kwargs):
1819
for key in self._data:
1920
setattr(self, snakeify(key), self._data[key])
2021

21-
def _jsond(self, json_data):
22+
def _jsond(self, json_data: dict) -> str:
2223
"""json dumps"""
2324
return json.dumps(json_data)
2425

25-
def _jsonl(self, dictionary):
26+
def _jsonl(self, dictionary: str) -> dict:
2627
"""json loads"""
2728
return json.loads(dictionary)
2829

2930

3031
class User(BaseModel):
3132
"""user object"""
3233

33-
def __init__(self, data, **kwargs):
34+
def __init__(self, data: dict, **kwargs: Any) -> None:
3435
"""override"""
3536
if "user" in data.keys():
3637
data = data["user"]

pyclickup/models/client.py

Lines changed: 88 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
"""
44
import requests
55
import urllib.parse
6+
from datetime import datetime
7+
from requests.models import Response
68
from pyclickup.globals import __version__, API_URL, LIBRARY, TEST_TOKEN, TEST_API_URL
79
from pyclickup.models import User, Task, Team
810
from pyclickup.models.error import RateLimited
911
from pyclickup.utils.text import datetime_to_ts, filter_locals
10-
11-
12-
def test_client():
13-
"""returns a test client"""
14-
return ClickUp(TEST_TOKEN, api_url=TEST_API_URL, debug=True)
12+
from typing import Any, Dict, List, Optional, Union # noqa
1513

1614

1715
class ClickUp:
@@ -26,7 +24,13 @@ class ClickUp:
2624
"assignees",
2725
]
2826

29-
def __init__(self, token, api_url=API_URL, cache=True, debug=False):
27+
def __init__(
28+
self,
29+
token: str,
30+
api_url: str = API_URL,
31+
cache: bool = True,
32+
debug: bool = False,
33+
) -> None:
3034
"""creates a new client"""
3135
if not token:
3236
raise Exception("no token specified!")
@@ -37,11 +41,11 @@ def __init__(self, token, api_url=API_URL, cache=True, debug=False):
3741
self.debug = debug
3842

3943
# cache
40-
self._user = None
41-
self._teams = None
44+
self._user = None # type: Optional[User]
45+
self._teams = None # type: Optional[List[Team]]
4246

4347
@property
44-
def headers(self):
48+
def headers(self) -> dict:
4549
"""forms the headers required for the API calls"""
4650
return {
4751
"Accept": "application/json",
@@ -51,30 +55,36 @@ def headers(self):
5155
}
5256

5357
@property
54-
def user(self):
58+
def user(self) -> User:
5559
"""get the user associated with this token"""
5660
if not self._user or not self.cache:
5761
self._user = User(self.get("user"), client=self)
5862
return self._user
5963

6064
@property
61-
def teams(self):
65+
def teams(self) -> List[Team]:
6266
"""get authorized teams"""
6367
if not self._teams or not self.cache:
64-
self._teams = [Team(x, client=self) for x in self.get("team")["teams"]]
68+
teams_data = self.get("team")
69+
if not isinstance(teams_data, dict):
70+
raise Exception("invalid response while looking up teams")
71+
self._teams = [Team(x, client=self) for x in teams_data["teams"]]
6572
return self._teams
6673

67-
def get_team_by_id(self, team_id):
74+
def get_team_by_id(self, team_id: str) -> Team:
6875
"""given an team_id, return the team if it exists"""
69-
return Team(self.get("team/{}".format(team_id))["team"], client=self)
76+
team_data = self.get("team/{}".format(team_id))
77+
if not isinstance(team_data, dict):
78+
raise Exception("no team found")
79+
return Team(team_data["team"], client=self)
7080

71-
def _log(self, *args):
81+
def _log(self, *args: Any) -> None:
7282
"""logging method"""
7383
if not self.debug:
7484
return
7585
print(*args)
7686

77-
def _req(self, path, method="get", **kwargs):
87+
def _req(self, path: str, method: str = "get", **kwargs: Any) -> Response:
7888
"""requests wrapper"""
7989
full_path = urllib.parse.urljoin(self.api_url, path)
8090
self._log("[{}]: {}".format(method.upper(), full_path))
@@ -83,39 +93,48 @@ def _req(self, path, method="get", **kwargs):
8393
raise RateLimited()
8494
return request
8595

86-
def get(self, path, **kwargs):
96+
def get(
97+
self, path: str, raw: bool = False, **kwargs: Any
98+
) -> Union[list, dict, Response]:
8799
"""makes a get request to the API"""
88-
return self._req(path, **kwargs).json()
100+
request = self._req(path, **kwargs)
101+
return request if raw else request.json()
89102

90-
def post(self, path, **kwargs):
103+
def post(
104+
self, path: str, raw: bool = False, **kwargs: Any
105+
) -> Union[list, dict, Response]:
91106
"""makes a post request to the API"""
92-
return self._req(path, method="post", **kwargs).json()
107+
request = self._req(path, method="post", **kwargs)
108+
return request if raw else request.json()
93109

94-
def put(self, path, **kwargs):
110+
def put(
111+
self, path: str, raw: bool = False, **kwargs: Any
112+
) -> Union[list, dict, Response]:
95113
"""makes a put request to the API"""
96-
return self._req(path, method="put", **kwargs).json()
114+
request = self._req(path, method="put", **kwargs)
115+
return request if raw else request.json()
97116

98117
def _get_tasks(
99118
self,
100-
team_id,
101-
page=None, # integer - it appears to fetch 100 at a time
102-
order_by=None, # string, [id, created, updated, due_date]
103-
reverse=None, # bool
104-
subtasks=None, # bool
105-
space_ids=None, # List
106-
project_ids=None, # List
107-
list_ids=None, # List
108-
statuses=None, # List
109-
include_closed=False, # bool
110-
assignees=None, # List
111-
due_date_gt=None, # integer, posix time
112-
due_date_lt=None, # integer, posix time
113-
date_created_gt=None, # integer, posix time
114-
date_created_lt=None, # integer, posix time
115-
date_updated_gt=None, # integer, posix time
116-
date_updated_lt=None, # integer, posix time
117-
**kwargs
118-
):
119+
team_id: str,
120+
page: int = None, # integer - it appears to fetch 100 at a time
121+
order_by: str = None, # string, [id, created, updated, due_date]
122+
reverse: bool = None, # bool
123+
subtasks: bool = None, # bool
124+
space_ids: list = None, # List
125+
project_ids: list = None, # List
126+
list_ids: list = None, # List
127+
statuses: list = None, # List
128+
include_closed: bool = False, # bool
129+
assignees: list = None, # List
130+
due_date_gt: int = None, # integer, posix time
131+
due_date_lt: int = None, # integer, posix time
132+
date_created_gt: int = None, # integer, posix time
133+
date_created_lt: int = None, # integer, posix time
134+
date_updated_gt: int = None, # integer, posix time
135+
date_updated_lt: int = None, # integer, posix time
136+
**kwargs: Any
137+
) -> List[Task]:
119138
"""fetches the tasks according to the given options"""
120139
params = filter_locals(locals(), extras=["team_id"])
121140

@@ -132,31 +151,40 @@ def _get_tasks(
132151
for x in params
133152
]
134153
path = "team/{}/task?{}".format(team_id, "&".join(options))
135-
return [Task(x, client=self) for x in self.get(path)["tasks"]]
136-
137-
def _get_all_tasks(self, team_id, **kwargs):
154+
task_list = self.get(path)
155+
if not isinstance(task_list, dict):
156+
return []
157+
return [Task(x, client=self) for x in task_list["tasks"]]
158+
159+
def _get_all_tasks(
160+
self, team_id: str, page_limit: int = -1, **kwargs: Any
161+
) -> List[Task]:
138162
"""get all tasks wrapper"""
139-
tasks = []
163+
tasks = [] # type: List[Task]
140164
page_count = 0
141165
task_page = self._get_tasks(team_id, page=page_count, **kwargs)
142-
while task_page:
166+
while task_page and (page_limit == -1 or page_count < page_limit):
143167
tasks += task_page
144168
page_count += 1
145169
task_page = self._get_tasks(team_id, page=page_count, **kwargs)
146170
return tasks
147171

148172
def _create_task(
149173
self,
150-
list_id,
151-
name, # string
152-
content, # string
153-
status, # string
154-
assignees=None, # array
155-
priority=None, # integer
156-
due_date=None, # integer posix time, or python datetime
157-
):
174+
list_id: str,
175+
name: str, # string
176+
content: str, # string
177+
status: str, # string
178+
assignees: list = None, # array
179+
priority: int = None, # integer
180+
due_date: Union[int, datetime] = None, # integer posix time, or python datetime
181+
) -> Any:
158182
"""creates a task in the specified list"""
159-
data = {"name": name, "content": content, "status": status}
183+
data = {
184+
"name": name,
185+
"content": content,
186+
"status": status,
187+
} # type: Dict[str, Any]
160188
if assignees:
161189
data["assignees"] = assignees
162190
if priority:
@@ -166,3 +194,8 @@ def _create_task(
166194
due_date if isinstance(due_date, int) else datetime_to_ts(due_date)
167195
)
168196
return self.post("list/{}/task".format(list_id), data=data)
197+
198+
199+
def test_client() -> ClickUp:
200+
"""returns a test client"""
201+
return ClickUp(TEST_TOKEN, api_url=TEST_API_URL, debug=True)

pyclickup/models/error.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""
22
error models for pyclickup
33
"""
4+
from typing import Any
45

56

67
class PyClickUpException(Exception):
78
"""base pyclickup exception class"""
89

9-
def __init__(self, *args, **kwargs):
10+
def __init__(self, *args: Any, **kwargs: Any) -> None:
1011
extra = ""
1112
if args:
1213
extra = '\n| extra info: "{extra}"'.format(extra=args[0])

pyclickup/test/conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
"""
44
import pytest
55
from pyclickup.test.helpers import dbg
6+
from typing import Any
67

78

89
@pytest.fixture(scope="session", autouse=True)
9-
def before_all(request):
10+
def before_all(request: Any) -> None:
1011
"""test setup"""
1112
dbg("[+] begin pyclickup tests")
1213
request.addfinalizer(after_all)
1314

1415

15-
def after_all():
16+
def after_all() -> None:
1617
"""tear down"""
1718
dbg("[+] end pyclickup tests")

pyclickup/test/helpers.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@
99
init()
1010

1111

12-
def manual_raise():
13-
"""manually raise an exception"""
14-
raise SystemExit(1)
15-
16-
17-
def dbg(text):
12+
def dbg(text: str) -> None:
1813
"""debug printer for tests"""
1914
if isinstance(text, dict):
2015
text = json.dumps(text, sort_keys=True, indent=2)

0 commit comments

Comments
 (0)