Skip to content

Commit 10ea0a6

Browse files
author
mvaught
committed
lint and formatting
1 parent 5ba232f commit 10ea0a6

File tree

5 files changed

+114
-49
lines changed

5 files changed

+114
-49
lines changed

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
from setuptools import find_packages, setup
1111

12-
1312
__author__ = "Mahmoud Hashemi and Glyph Lefkowitz"
1413
__version__ = "21.0.1dev"
1514
__contact__ = "mahmoud@hatnote.com"

src/hyperlink/_url.py

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
23
# -*- coding: utf-8 -*-
34
"""Hyperlink provides Pythonic URL parsing, construction, and rendering.
45
@@ -31,6 +32,7 @@
3132
cast,
3233
TYPE_CHECKING,
3334
)
35+
3436
if TYPE_CHECKING:
3537
from typing import (
3638
Type,
@@ -43,8 +45,11 @@
4345
TypeVar,
4446
Union,
4547
)
48+
4649
NoneType: Type[None] = type(None)
47-
QueryPairs = Tuple[Tuple[str, Optional[str]], ...] # internal representation
50+
QueryPairs = Tuple[
51+
Tuple[str, Optional[str]], ...
52+
] # internal representation
4853
QueryParameters = Union[
4954
Mapping[str, Optional[str]],
5055
QueryPairs,
@@ -177,7 +182,9 @@ def __bool__(self) -> bool:
177182
_QUERY_KEY_DELIMS = _ALL_DELIMS - _QUERY_KEY_SAFE
178183

179184

180-
def _make_decode_map(delims: Iterable[str], allow_percent: bool = False) -> Mapping[bytes, bytes]:
185+
def _make_decode_map(
186+
delims: Iterable[str], allow_percent: bool = False
187+
) -> Mapping[bytes, bytes]:
181188
ret = dict(_HEX_CHAR_MAP)
182189
if not allow_percent:
183190
delims = set(delims) | set(["%"])
@@ -262,9 +269,11 @@ def _encode_schemeless_path_part(text: str, maximal: bool = True) -> str:
262269
return "".join([_SCHEMELESS_PATH_PART_QUOTE_MAP[b] for b in bytestr])
263270
return "".join(
264271
[
265-
_SCHEMELESS_PATH_PART_QUOTE_MAP[t]
266-
if t in _SCHEMELESS_PATH_DELIMS
267-
else t
272+
(
273+
_SCHEMELESS_PATH_PART_QUOTE_MAP[t]
274+
if t in _SCHEMELESS_PATH_DELIMS
275+
else t
276+
)
268277
for t in text
269278
]
270279
)
@@ -449,7 +458,10 @@ def _encode_userinfo_part(text: str, maximal: bool = True) -> str:
449458

450459

451460
def register_scheme(
452-
text: str, uses_netloc: bool = True, default_port: Optional[int] = None, query_plus_is_space: bool = True
461+
text: str,
462+
uses_netloc: bool = True,
463+
default_port: Optional[int] = None,
464+
query_plus_is_space: bool = True,
453465
) -> None:
454466
"""Registers new scheme information, resulting in correct port and
455467
slash behavior from the URL object. There are dozens of standard
@@ -499,7 +511,9 @@ def register_scheme(
499511
return
500512

501513

502-
def scheme_uses_netloc(scheme: str, default: Optional[bool] = None) -> Optional[bool]:
514+
def scheme_uses_netloc(
515+
scheme: str, default: Optional[bool] = None
516+
) -> Optional[bool]:
503517
"""Whether or not a URL uses :code:`:` or :code:`://` to separate the
504518
scheme from the rest of the URL depends on the scheme's own
505519
standard definition. There is no way to infer this behavior
@@ -560,7 +574,12 @@ def _typecheck(name: str, value: T, *types: Type[Any]) -> T:
560574
return value
561575

562576

563-
def _textcheck(name: str, value: T, delims: Iterable[str] = frozenset(), nullable: bool = False) -> T:
577+
def _textcheck(
578+
name: str,
579+
value: T,
580+
delims: Iterable[str] = frozenset(),
581+
nullable: bool = False,
582+
) -> T:
564583
if not isinstance(value, str):
565584
if nullable and value is None:
566585
# used by query string values
@@ -590,7 +609,9 @@ def iter_pairs(iterable: Iterable[Any]) -> Iterator[Any]:
590609
return iter(iterable)
591610

592611

593-
def _decode_unreserved(text: str, normalize_case: bool = False, encode_stray_percents: bool = False) -> str:
612+
def _decode_unreserved(
613+
text: str, normalize_case: bool = False, encode_stray_percents: bool = False
614+
) -> str:
594615
return _percent_decode(
595616
text,
596617
normalize_case=normalize_case,
@@ -610,7 +631,9 @@ def _decode_userinfo_part(
610631
)
611632

612633

613-
def _decode_path_part(text: str, normalize_case: bool = False, encode_stray_percents: bool = False) -> str:
634+
def _decode_path_part(
635+
text: str, normalize_case: bool = False, encode_stray_percents: bool = False
636+
) -> str:
614637
"""
615638
>>> _decode_path_part(u'%61%77%2f%7a')
616639
u'aw%2fz'
@@ -625,7 +648,9 @@ def _decode_path_part(text: str, normalize_case: bool = False, encode_stray_perc
625648
)
626649

627650

628-
def _decode_query_key(text: str, normalize_case: bool = False, encode_stray_percents: bool = False) -> str:
651+
def _decode_query_key(
652+
text: str, normalize_case: bool = False, encode_stray_percents: bool = False
653+
) -> str:
629654
return _percent_decode(
630655
text,
631656
normalize_case=normalize_case,
@@ -1154,7 +1179,9 @@ def authority(self, with_password: bool = False, **kw: Any) -> str:
11541179
# first, a bit of twisted compat
11551180
with_password = kw.pop("includeSecrets", with_password)
11561181
if kw:
1157-
raise TypeError("got unexpected keyword arguments: %r" % list(kw.keys()))
1182+
raise TypeError(
1183+
"got unexpected keyword arguments: %r" % list(kw.keys())
1184+
)
11581185
host = self.host
11591186
if ":" in host:
11601187
hostport = ["[" + host + "]"]
@@ -1612,9 +1639,11 @@ def to_uri(self) -> URL:
16121639
[
16131640
(
16141641
_encode_query_key(k, maximal=True),
1615-
_encode_query_value(v, maximal=True)
1616-
if v is not None
1617-
else None,
1642+
(
1643+
_encode_query_value(v, maximal=True)
1644+
if v is not None
1645+
else None
1646+
),
16181647
)
16191648
for k, v in self.query
16201649
]
@@ -1954,19 +1983,29 @@ class DecodedURL(object):
19541983
.. versionadded:: 18.0.0
19551984
"""
19561985

1957-
def __init__(self, url: URL = _EMPTY_URL, lazy: bool = False, query_plus_is_space: Optional[bool] = None) -> None:
1986+
def __init__(
1987+
self,
1988+
url: URL = _EMPTY_URL,
1989+
lazy: bool = False,
1990+
query_plus_is_space: Optional[bool] = None,
1991+
) -> None:
19581992
self._url = url
19591993
if query_plus_is_space is None:
19601994
query_plus_is_space = url.scheme not in NO_QUERY_PLUS_SCHEMES
19611995
self._query_plus_is_space = query_plus_is_space
19621996
if not lazy:
19631997
# cache the following, while triggering any decoding
19641998
# issues with decodable fields
1965-
self.host, self.userinfo, self.path, self.query, self.fragment
1999+
_ = (self.host, self.userinfo, self.path, self.query, self.fragment)
19662000
return
19672001

19682002
@classmethod
1969-
def from_text(cls, text: str, lazy: bool = False, query_plus_is_space: Optional[bool] = None) -> DecodedURL:
2003+
def from_text(
2004+
cls,
2005+
text: str,
2006+
lazy: bool = False,
2007+
query_plus_is_space: Optional[bool] = None,
2008+
) -> DecodedURL:
19702009
"""\
19712010
Make a `DecodedURL` instance from any text string containing a URL.
19722011
@@ -2096,11 +2135,13 @@ def query(self) -> QueryPairs:
20962135
"QueryPairs",
20972136
tuple(
20982137
tuple(
2099-
_percent_decode(
2100-
predecode(x), raise_subencoding_exc=True
2138+
(
2139+
_percent_decode(
2140+
predecode(x), raise_subencoding_exc=True
2141+
)
2142+
if x is not None
2143+
else None
21012144
)
2102-
if x is not None
2103-
else None
21042145
for x in (k, v)
21052146
)
21062147
for k, v in self._url.query
@@ -2302,20 +2343,27 @@ def __dir__(self) -> Sequence[str]:
23022343

23032344
# Add some overloads so that parse gives a better return value.
23042345
if TYPE_CHECKING:
2346+
23052347
@overload
23062348
def parse(url: str, decoded: Literal[False], lazy: bool = False) -> URL:
23072349
"""Passing decoded=False returns URL."""
23082350

23092351
@overload
2310-
def parse(url: str, decoded: Literal[True] = True, lazy: bool = False) -> DecodedURL:
2352+
def parse(
2353+
url: str, decoded: Literal[True] = True, lazy: bool = False
2354+
) -> DecodedURL:
23112355
"""Passing decoded=True (or the default value) returns DecodedURL."""
23122356

23132357
@overload
2314-
def parse(url: str, decoded: bool = True, lazy: bool = False) -> Union[URL, DecodedURL]:
2358+
def parse(
2359+
url: str, decoded: bool = True, lazy: bool = False
2360+
) -> Union[URL, DecodedURL]:
23152361
"""If decoded is not a literal we don't know the return type."""
23162362

23172363

2318-
def parse(url: str, decoded: bool = True, lazy: bool = False) -> Union[URL, DecodedURL]:
2364+
def parse(
2365+
url: str, decoded: bool = True, lazy: bool = False
2366+
) -> Union[URL, DecodedURL]:
23192367
"""
23202368
Automatically turn text into a structured URL object.
23212369

src/hyperlink/hypothesis.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ def idna_characters() -> str:
111111
_idnaCharacters: str = ""
112112

113113
@composite
114-
def idna_text(draw: DrawCallable, min_size: int = 1, max_size: Optional[int] = None) -> str:
114+
def idna_text(
115+
draw: DrawCallable, min_size: int = 1, max_size: Optional[int] = None
116+
) -> str:
115117
"""
116118
A strategy which generates IDNA-encodable text.
117119
@@ -202,7 +204,11 @@ def hostname_labels(draw: DrawCallable, allow_idn: bool = True) -> str:
202204
return label
203205

204206
@composite
205-
def hostnames(draw: DrawCallable, allow_leading_digit: bool = True, allow_idn: bool = True) -> str:
207+
def hostnames(
208+
draw: DrawCallable,
209+
allow_leading_digit: bool = True,
210+
allow_idn: bool = True,
211+
) -> str:
206212
"""
207213
A strategy which generates host names.
208214
@@ -218,8 +224,10 @@ def hostnames(draw: DrawCallable, allow_leading_digit: bool = True, allow_idn: b
218224
str,
219225
draw(
220226
hostname_labels(allow_idn=allow_idn).filter(
221-
lambda l: (
222-
True if allow_leading_digit else l[0] not in digits
227+
lambda label: (
228+
True
229+
if allow_leading_digit
230+
else label[0] not in digits
223231
)
224232
)
225233
),
@@ -239,7 +247,24 @@ def hostnames(draw: DrawCallable, allow_leading_digit: bool = True, allow_idn: b
239247

240248
# Trim off labels until the total host name length fits in 252
241249
# characters. This avoids having to filter the data.
242-
while sum(len(label) for label in labels) + len(labels) - 1 > 252:
250+
# For IDNs, the length must also be checked after Punycode encoding,
251+
# because the encoded form may be much longer due to the 'xn--' prefix
252+
# and Unicode-to-ASCII expansion. This ensures compliance with RFC 1035
253+
# and IDNA's 255-byte limit.
254+
255+
def get_len(lbls: list[str]) -> int:
256+
d = ".".join(lbls)
257+
if allow_idn:
258+
try:
259+
return len(idna_encode(d))
260+
except IDNAError:
261+
# Encoding failed due to length or invalidity.
262+
# Return large number to force trimming.
263+
return 9999
264+
return len(d)
265+
266+
# Trim off labels until total hostname length fits IDNA/DNS limits
267+
while get_len(labels) > 253 and len(labels) > 1:
243268
labels = labels[:-1]
244269

245270
return ".".join(labels)

src/hyperlink/test/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
Tests for hyperlink
44
"""
55

6+
from __future__ import annotations
7+
68
__all = ()
79

810

9-
def _init_hypothesis():
10-
# type: () -> None
11+
def _init_hypothesis() -> None:
1112
from os import environ
1213

1314
if "CI" in environ:

src/hyperlink/test/test_common.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ def test_assertRaisesWithCallable(self):
3434
"""
3535
called_with = []
3636

37-
def raisesExpected(*args, **kwargs):
38-
# type: (Any, Any) -> None
37+
def raisesExpected(*args: Any, **kwargs: Any) -> None:
3938
called_with.append((args, kwargs))
4039
raise _ExpectedException
4140

@@ -44,15 +43,13 @@ def raisesExpected(*args, **kwargs):
4443
)
4544
self.assertEqual(called_with, [((1,), {"keyword": True})])
4645

47-
def test_assertRaisesWithCallableUnexpectedException(self):
48-
# type: () -> None
46+
def test_assertRaisesWithCallableUnexpectedException(self) -> None:
4947
"""When given a callable that raises an unexpected exception,
5048
HyperlinkTestCase.assertRaises raises that exception.
5149
5250
"""
5351

54-
def doesNotRaiseExpected(*args, **kwargs):
55-
# type: (Any, Any) -> None
52+
def doesNotRaiseExpected(*args: Any, **kwargs: Any) -> None:
5653
raise _UnexpectedException
5754

5855
try:
@@ -62,24 +59,21 @@ def doesNotRaiseExpected(*args, **kwargs):
6259
except _UnexpectedException:
6360
pass
6461

65-
def test_assertRaisesWithCallableDoesNotRaise(self):
66-
# type: () -> None
62+
def test_assertRaisesWithCallableDoesNotRaise(self) -> None:
6763
"""HyperlinkTestCase.assertRaises raises an AssertionError when given
6864
a callable that, when called, does not raise any exception.
6965
7066
"""
7167

72-
def doesNotRaise(*args, **kwargs):
73-
# type: (Any, Any) -> None
68+
def doesNotRaise(*args: Any, **kwargs: Any) -> None:
7469
pass
7570

7671
try:
7772
self.hyperlink_test.assertRaises(_ExpectedException, doesNotRaise)
7873
except AssertionError:
7974
pass
8075

81-
def test_assertRaisesContextManager(self):
82-
# type: () -> None
76+
def test_assertRaisesContextManager(self) -> None:
8377
"""HyperlinkTestCase.assertRaises does not raise an AssertionError
8478
when used as a context manager with a suite that raises the
8579
expected exception. The context manager stores the exception
@@ -93,8 +87,7 @@ def test_assertRaisesContextManager(self):
9387
isinstance(cm.exception, _ExpectedException)
9488
)
9589

96-
def test_assertRaisesContextManagerUnexpectedException(self):
97-
# type: () -> None
90+
def test_assertRaisesContextManagerUnexpectedException(self) -> None:
9891
"""When used as a context manager with a block that raises an
9992
unexpected exception, HyperlinkTestCase.assertRaises raises
10093
that unexpected exception.
@@ -106,8 +99,7 @@ def test_assertRaisesContextManagerUnexpectedException(self):
10699
except _UnexpectedException:
107100
pass
108101

109-
def test_assertRaisesContextManagerDoesNotRaise(self):
110-
# type: () -> None
102+
def test_assertRaisesContextManagerDoesNotRaise(self) -> None:
111103
"""HyperlinkTestcase.assertRaises raises an AssertionError when used
112104
as a context manager with a block that does not raise any
113105
exception.

0 commit comments

Comments
 (0)