Skip to content

Commit 2643f92

Browse files
committed
Convert to Python 3-style type hints
We also expand coverage, which highlights quite a few mistakes /o\ This still isn't complete (I got bored) but it's better. Signed-off-by: Stephen Finucane <stephen@that.guru>
1 parent bf118fb commit 2643f92

File tree

6 files changed

+133
-100
lines changed

6 files changed

+133
-100
lines changed

.pre-commit-config.yaml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
---
2+
default_language_version:
3+
# force all unspecified python hooks to run python3
4+
python: python3
15
repos:
2-
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v2.3.0
4-
hooks:
5-
- id: check-executables-have-shebangs
6-
- id: check-merge-conflict
7-
- id: check-yaml
8-
- id: end-of-file-fixer
9-
- id: flake8
10-
- id: trailing-whitespace
6+
- repo: https://github.com/pre-commit/pre-commit-hooks
7+
rev: v2.4.0
8+
hooks:
9+
- id: check-executables-have-shebangs
10+
- id: check-merge-conflict
11+
- id: check-yaml
12+
- id: end-of-file-fixer
13+
- id: flake8
14+
- id: trailing-whitespace

git_pw/api.py

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,42 @@
55
from functools import update_wrapper
66
import logging
77
import os.path
8-
import re
98
import pty
9+
import re
1010
import sys
1111
import tempfile
12+
import typing as ty
1213

1314
import click
1415
import requests
1516

1617
import git_pw
1718
from git_pw import config
1819

19-
if 0: # noqa
20-
from typing import Any # noqa
21-
from typing import Callable # noqa
22-
from typing import Dict # noqa
23-
from typing import IO # noqa
24-
from typing import List # noqa
25-
from typing import Optional # noqa
26-
from typing import Tuple # noqa
27-
from typing import Union # noqa
28-
29-
Filters = List[Tuple[str, str]]
30-
3120
CONF = config.CONF
3221
LOG = logging.getLogger(__name__)
3322

23+
Filters = ty.List[ty.Tuple[str, str]]
24+
3425

3526
class HTTPTokenAuth(requests.auth.AuthBase):
3627
"""Attaches HTTP Token Authentication to the given Request object."""
37-
def __init__(self, token):
28+
def __init__(self, token: str):
3829
self.token = token
3930

40-
def __call__(self, r):
31+
def __call__(
32+
self, r: requests.PreparedRequest,
33+
) -> requests.PreparedRequest:
4134
r.headers['Authorization'] = self._token_auth_str(self.token)
4235
return r
4336

4437
@staticmethod
45-
def _token_auth_str(token): # type: (str) -> str
38+
def _token_auth_str(token: str) -> str:
4639
"""Return a Token auth string."""
4740
return 'Token {}'.format(token.strip())
4841

4942

50-
def _get_auth(optional=False):
51-
# type: (bool) -> Optional[requests.auth.AuthBase]
43+
def _get_auth(optional: bool = False) -> ty.Optional[requests.auth.AuthBase]:
5244
if CONF.token:
5345
return HTTPTokenAuth(CONF.token)
5446
elif CONF.username and CONF.password:
@@ -61,13 +53,13 @@ def _get_auth(optional=False):
6153
return None
6254

6355

64-
def _get_headers(): # type: () -> Dict[str, str]
56+
def _get_headers() -> ty.Dict[str, str]:
6557
return {
6658
'User-Agent': 'git-pw ({})'.format(git_pw.__version__),
6759
}
6860

6961

70-
def _get_server(): # type: () -> str
62+
def _get_server() -> str:
7163
if CONF.server:
7264
server = CONF.server.rstrip('/')
7365

@@ -91,7 +83,7 @@ def _get_server(): # type: () -> str
9183
sys.exit(1)
9284

9385

94-
def _get_project(): # type: () -> str
86+
def _get_project() -> str:
9587
if CONF.project and CONF.project.strip() == '*':
9688
return '' # just don't bother filtering on project
9789
elif CONF.project:
@@ -104,7 +96,9 @@ def _get_project(): # type: () -> str
10496
sys.exit(1)
10597

10698

107-
def _handle_error(operation, exc):
99+
def _handle_error(
100+
operation: str, exc: requests.exceptions.RequestException,
101+
) -> None:
108102
if exc.response is not None and exc.response.content:
109103
# server errors should always be reported
110104
if exc.response.status_code in range(500, 512): # 5xx Server Error
@@ -128,8 +122,9 @@ def _handle_error(operation, exc):
128122
sys.exit(1)
129123

130124

131-
def _get(url, params=None, stream=False):
132-
# type: (str, Filters, bool) -> requests.Response
125+
def _get(
126+
url: str, params: Filters = None, stream: bool = False,
127+
) -> requests.Response:
133128
"""Make GET request and handle errors."""
134129
LOG.debug('GET %s', url)
135130

@@ -149,8 +144,9 @@ def _get(url, params=None, stream=False):
149144
return rsp
150145

151146

152-
def _post(url, data):
153-
# type: (str, dict) -> requests.Response
147+
def _post(
148+
url: str, data: ty.List[ty.Tuple[str, ty.Any]],
149+
) -> requests.Response:
154150
"""Make POST request and handle errors."""
155151
LOG.debug('POST %s, data=%r', url, data)
156152

@@ -166,14 +162,16 @@ def _post(url, data):
166162
return rsp
167163

168164

169-
def _patch(url, data):
170-
# type: (str, dict) -> requests.Response
165+
def _patch(
166+
url: str, data: ty.List[ty.Tuple[str, ty.Any]],
167+
) -> requests.Response:
171168
"""Make PATCH request and handle errors."""
172169
LOG.debug('PATCH %s, data=%r', url, data)
173170

174171
try:
175-
rsp = requests.patch(url, auth=_get_auth(), headers=_get_headers(),
176-
data=data)
172+
rsp = requests.patch(
173+
url, auth=_get_auth(), headers=_get_headers(), data=data,
174+
)
177175
rsp.raise_for_status()
178176
except requests.exceptions.RequestException as exc:
179177
_handle_error('update', exc)
@@ -183,8 +181,7 @@ def _patch(url, data):
183181
return rsp
184182

185183

186-
def _delete(url):
187-
# type: (str) -> requests.Response
184+
def _delete(url: str) -> requests.Response:
188185
"""Make DELETE request and handle errors."""
189186
LOG.debug('DELETE %s', url)
190187

@@ -199,8 +196,7 @@ def _delete(url):
199196
return rsp
200197

201198

202-
def version():
203-
# type: () -> Optional[Tuple[int, int]]
199+
def version() -> ty.Tuple[int, int]:
204200
"""Get the version of the server from the URL, if present."""
205201
server = _get_server()
206202

@@ -212,8 +208,9 @@ def version():
212208
return (1, 0)
213209

214210

215-
def download(url, params=None, output=None):
216-
# type: (str, Filters, IO) -> Optional[str]
211+
def download(
212+
url: str, params: Filters = None, output: ty.IO = None,
213+
) -> ty.Optional[str]:
217214
"""Retrieve a specific API resource and save it to a file/stdout.
218215
219216
The ``Content-Disposition`` header is assumed to be present and
@@ -261,8 +258,7 @@ def download(url, params=None, output=None):
261258
return output_path
262259

263260

264-
def index(resource_type, params=None):
265-
# type: (str, Filters) -> dict
261+
def index(resource_type: str, params: Filters = None) -> dict:
266262
"""List API resources.
267263
268264
GET /{resource}/
@@ -288,8 +284,11 @@ def index(resource_type, params=None):
288284
return _get(url, params).json()
289285

290286

291-
def detail(resource_type, resource_id, params=None):
292-
# type: (str, int, Filters) -> Dict
287+
def detail(
288+
resource_type: str,
289+
resource_id: ty.Union[str, int],
290+
params: Filters = None,
291+
) -> ty.Dict:
293292
"""Retrieve a specific API resource.
294293
295294
GET /{resource}/{resourceID}/
@@ -308,8 +307,9 @@ def detail(resource_type, resource_id, params=None):
308307
return _get(url, params, stream=False).json()
309308

310309

311-
def create(resource_type, data):
312-
# type: (str, dict) -> dict
310+
def create(
311+
resource_type: str, data: ty.List[ty.Tuple[str, ty.Any]],
312+
) -> dict:
313313
"""Create a new API resource.
314314
315315
POST /{resource}/
@@ -327,8 +327,7 @@ def create(resource_type, data):
327327
return _post(url, data).json()
328328

329329

330-
def delete(resource_type, resource_id):
331-
# type: (str, Union[str, int]) -> None
330+
def delete(resource_type: str, resource_id: ty.Union[str, int]) -> None:
332331
"""Delete a specific API resource.
333332
334333
DELETE /{resource}/{resourceID}/
@@ -346,8 +345,11 @@ def delete(resource_type, resource_id):
346345
_delete(url)
347346

348347

349-
def update(resource_type, resource_id, data):
350-
# type: (str, Union[int, str], dict) -> dict
348+
def update(
349+
resource_type: str,
350+
resource_id: ty.Union[str, int],
351+
data: ty.List[ty.Tuple[str, ty.Any]],
352+
) -> dict:
351353
"""Update a specific API resource.
352354
353355
PATCH /{resource}/{resourceID}/
@@ -366,8 +368,9 @@ def update(resource_type, resource_id, data):
366368
return _patch(url, data).json()
367369

368370

369-
def validate_minimum_version(min_version, msg):
370-
# type: (Tuple[int, int], str) -> Callable[[Any], Any]
371+
def validate_minimum_version(
372+
min_version: ty.Tuple[int, int], msg: str,
373+
) -> ty.Callable[[ty.Any], ty.Any]:
371374

372375
def inner(f):
373376
@click.pass_context
@@ -383,7 +386,7 @@ def new_func(ctx, *args, **kwargs):
383386
return inner
384387

385388

386-
def validate_multiple_filter_support(f):
389+
def validate_multiple_filter_support(f: ty.Callable) -> ty.Callable:
387390

388391
@click.pass_context
389392
def new_func(ctx, *args, **kwargs):
@@ -412,7 +415,9 @@ def new_func(ctx, *args, **kwargs):
412415
return update_wrapper(new_func, f)
413416

414417

415-
def retrieve_filter_ids(resource_type, filter_name, filter_value):
418+
def retrieve_filter_ids(
419+
resource_type: str, filter_name: str, filter_value: str,
420+
) -> ty.List[ty.Tuple[str, str]]:
416421
"""Retrieve IDs for items passed through by filter.
417422
418423
Some filters require client-side filtering, e.g. filtering patches by

0 commit comments

Comments
 (0)