Skip to content

Commit 8ea155e

Browse files
authored
fix: qualifiers type annotation (#172)
* fix: qualifiers type annotation * fix: miscellaneous type annotation issues * style: remove quotes from forward references * refactor: add AnyStr type alias * revert: required argument check Signed-off-by: Jonathan Howard <jonathan.w.howard@lmco.com>
1 parent 16bd3a2 commit 8ea155e

File tree

1 file changed

+50
-52
lines changed

1 file changed

+50
-52
lines changed

src/packageurl/__init__.py

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
# Visit https://github.com/package-url/packageurl-python for support and
2525
# download.
2626

27+
from __future__ import annotations
28+
2729
import string
2830
from collections import namedtuple
2931
from typing import TYPE_CHECKING
3032
from typing import Any
31-
from typing import AnyStr
3233
from typing import Dict
3334
from typing import Optional
3435
from typing import Tuple
@@ -43,12 +44,15 @@
4344
from collections.abc import Iterable
4445

4546
from typing_extensions import Literal
47+
from typing_extensions import Self
48+
49+
AnyStr = Union[str, bytes]
4650

4751
# Python 3
4852
basestring = (
4953
bytes,
5054
str,
51-
) # NOQA
55+
)
5256

5357
"""
5458
A purl (aka. Package URL) implementation as specified at:
@@ -84,16 +88,16 @@ def unquote(s: AnyStr) -> str:
8488

8589

8690
@overload
87-
def get_quoter(encode: bool = True) -> "Callable[[AnyStr], str]": ...
91+
def get_quoter(encode: bool = True) -> Callable[[AnyStr], str]: ...
8892

8993

9094
@overload
91-
def get_quoter(encode: None) -> "Callable[[str], str]": ...
95+
def get_quoter(encode: None) -> Callable[[str], str]: ...
9296

9397

9498
def get_quoter(
9599
encode: Optional[bool] = True,
96-
) -> "Union[Callable[[AnyStr], str], Callable[[str], str]]":
100+
) -> Union[Callable[[AnyStr], str], Callable[[str], str]]:
97101
"""
98102
Return quoting callable given an `encode` tri-boolean (True, False or None)
99103
"""
@@ -105,22 +109,22 @@ def get_quoter(
105109
return lambda x: x
106110

107111

108-
def normalize_type(type: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]: # NOQA
112+
def normalize_type(type: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]:
109113
if not type:
110114
return None
111115
if not isinstance(type, str):
112-
type_str = type.decode("utf-8") # NOQA
116+
type_str = type.decode("utf-8")
113117
else:
114118
type_str = type
115119

116120
quoter = get_quoter(encode)
117-
type_str = quoter(type_str) # NOQA
121+
type_str = quoter(type_str)
118122
return type_str.strip().lower() or None
119123

120124

121125
def normalize_namespace(
122126
namespace: Optional[AnyStr], ptype: Optional[str], encode: Optional[bool] = True
123-
) -> Optional[str]: # NOQA
127+
) -> Optional[str]:
124128
if not namespace:
125129
return None
126130
if not isinstance(namespace, str):
@@ -138,7 +142,7 @@ def normalize_namespace(
138142

139143
def normalize_name(
140144
name: Optional[AnyStr], ptype: Optional[str], encode: Optional[bool] = True
141-
) -> Optional[str]: # NOQA
145+
) -> Optional[str]:
142146
if not name:
143147
return None
144148
if not isinstance(name, str):
@@ -156,9 +160,7 @@ def normalize_name(
156160
return name_str or None
157161

158162

159-
def normalize_version(
160-
version: Optional[AnyStr], encode: Optional[bool] = True
161-
) -> Optional[str]: # NOQA
163+
def normalize_version(version: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]:
162164
if not version:
163165
return None
164166
if not isinstance(version, str):
@@ -173,25 +175,25 @@ def normalize_version(
173175

174176
@overload
175177
def normalize_qualifiers(
176-
qualifiers: Union[AnyStr, Dict[str, str], None], encode: "Literal[True]" = ...
178+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Literal[True] = ...
177179
) -> Optional[str]: ...
178180

179181

180182
@overload
181183
def normalize_qualifiers(
182-
qualifiers: Union[AnyStr, Dict[str, str], None], encode: "Optional[Literal[False]]"
183-
) -> Optional[Dict[str, str]]: ...
184+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Optional[Literal[False]]
185+
) -> Dict[str, str]: ...
184186

185187

186188
@overload
187189
def normalize_qualifiers(
188-
qualifiers: Union[AnyStr, Dict[str, str], None], encode: Optional[bool] = ...
189-
) -> Union[str, Dict[str, str], None]: ...
190+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Optional[bool] = ...
191+
) -> Optional[Union[str, Dict[str, str]]]: ...
190192

191193

192194
def normalize_qualifiers(
193-
qualifiers: Union[AnyStr, Dict[str, str], None], encode: Optional[bool] = True
194-
) -> Union[str, Dict[str, str], None]: # NOQA
195+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Optional[bool] = True
196+
) -> Optional[Union[str, Dict[str, str]]]:
195197
"""
196198
Return normalized `qualifiers` as a mapping (or as a string if `encode` is
197199
True). The `qualifiers` arg is either a mapping or a string.
@@ -213,7 +215,7 @@ def normalize_qualifiers(
213215
f"Invalid qualifier. Must be a string of key=value pairs:{repr(qualifiers_list)}"
214216
)
215217
qualifiers_parts = [kv.partition("=") for kv in qualifiers_list]
216-
qualifiers_pairs: "Iterable[Tuple[str, str]]" = [(k, v) for k, _, v in qualifiers_parts]
218+
qualifiers_pairs: Iterable[Tuple[str, str]] = [(k, v) for k, _, v in qualifiers_parts]
217219
elif isinstance(qualifiers, dict):
218220
qualifiers_pairs = qualifiers.items()
219221
else:
@@ -255,9 +257,7 @@ def normalize_qualifiers(
255257
return qualifiers_map
256258

257259

258-
def normalize_subpath(
259-
subpath: Optional[AnyStr], encode: Optional[bool] = True
260-
) -> Optional[str]: # NOQA
260+
def normalize_subpath(subpath: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]:
261261
if not subpath:
262262
return None
263263
if not isinstance(subpath, str):
@@ -278,9 +278,9 @@ def normalize(
278278
namespace: Optional[AnyStr],
279279
name: Optional[AnyStr],
280280
version: Optional[AnyStr],
281-
qualifiers: Union[AnyStr, Dict[str, str], None],
281+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]],
282282
subpath: Optional[AnyStr],
283-
encode: "Literal[True]" = ...,
283+
encode: Literal[True] = ...,
284284
) -> Tuple[str, Optional[str], str, Optional[str], Optional[str], Optional[str]]: ...
285285

286286

@@ -290,10 +290,10 @@ def normalize(
290290
namespace: Optional[AnyStr],
291291
name: Optional[AnyStr],
292292
version: Optional[AnyStr],
293-
qualifiers: Union[AnyStr, Dict[str, str], None],
293+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]],
294294
subpath: Optional[AnyStr],
295-
encode: "Optional[Literal[False]]",
296-
) -> Tuple[str, Optional[str], str, Optional[str], Optional[Dict[str, str]], Optional[str]]: ...
295+
encode: Optional[Literal[False]],
296+
) -> Tuple[str, Optional[str], str, Optional[str], Dict[str, str], Optional[str]]: ...
297297

298298

299299
@overload
@@ -302,11 +302,11 @@ def normalize(
302302
namespace: Optional[AnyStr],
303303
name: Optional[AnyStr],
304304
version: Optional[AnyStr],
305-
qualifiers: Union[AnyStr, Dict[str, str], None],
305+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]],
306306
subpath: Optional[AnyStr],
307307
encode: Optional[bool] = ...,
308308
) -> Tuple[
309-
str, Optional[str], str, Optional[str], Union[str, Dict[str, str], None], Optional[str]
309+
str, Optional[str], str, Optional[str], Optional[Union[str, Dict[str, str]]], Optional[str]
310310
]: ...
311311

312312

@@ -315,21 +315,21 @@ def normalize(
315315
namespace: Optional[AnyStr],
316316
name: Optional[AnyStr],
317317
version: Optional[AnyStr],
318-
qualifiers: Union[AnyStr, Dict[str, str], None],
318+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]],
319319
subpath: Optional[AnyStr],
320320
encode: Optional[bool] = True,
321321
) -> Tuple[
322322
Optional[str],
323323
Optional[str],
324324
Optional[str],
325325
Optional[str],
326-
Union[str, Dict[str, str], None],
326+
Optional[Union[str, Dict[str, str]]],
327327
Optional[str],
328-
]: # NOQA
328+
]:
329329
"""
330330
Return normalized purl components
331331
"""
332-
type_norm = normalize_type(type, encode) # NOQA
332+
type_norm = normalize_type(type, encode)
333333
namespace_norm = normalize_namespace(namespace, type_norm, encode)
334334
name_norm = normalize_name(name, type_norm, encode)
335335
version_norm = normalize_version(version, encode)
@@ -346,22 +346,22 @@ class PackageURL(
346346
https://github.com/package-url/purl-spec
347347
"""
348348

349-
name: str
350-
namespace: Optional[str]
351-
qualifiers: Union[str, Dict[str, str], None]
352-
subpath: Optional[str]
353349
type: str
350+
namespace: Optional[str]
351+
name: str
354352
version: Optional[str]
353+
qualifiers: Dict[str, str]
354+
subpath: Optional[str]
355355

356356
def __new__(
357-
self,
357+
cls,
358358
type: Optional[AnyStr] = None,
359359
namespace: Optional[AnyStr] = None,
360-
name: Optional[AnyStr] = None, # NOQA
360+
name: Optional[AnyStr] = None,
361361
version: Optional[AnyStr] = None,
362-
qualifiers: Union[AnyStr, Dict[str, str], None] = None,
362+
qualifiers: Optional[Union[AnyStr, Dict[str, str]]] = None,
363363
subpath: Optional[AnyStr] = None,
364-
) -> "PackageURL": # this should be 'Self' https://github.com/python/mypy/pull/13133
364+
) -> Self:
365365
required = dict(type=type, name=name)
366366
for key, value in required.items():
367367
if value:
@@ -399,12 +399,10 @@ def __new__(
399399
version_norm,
400400
qualifiers_norm,
401401
subpath_norm,
402-
) = normalize( # NOQA
403-
type, namespace, name, version, qualifiers, subpath, encode=None
404-
)
402+
) = normalize(type, namespace, name, version, qualifiers, subpath, encode=None)
405403

406404
return super().__new__(
407-
PackageURL,
405+
cls,
408406
type=type_norm,
409407
namespace=namespace_norm,
410408
name=name_norm,
@@ -439,7 +437,7 @@ def to_string(self) -> str:
439437
"""
440438
Return a purl string built from components.
441439
"""
442-
type, namespace, name, version, qualifiers, subpath = normalize( # NOQA
440+
type, namespace, name, version, qualifiers, subpath = normalize(
443441
self.type,
444442
self.namespace,
445443
self.name,
@@ -472,7 +470,7 @@ def to_string(self) -> str:
472470
return "".join(purl)
473471

474472
@classmethod
475-
def from_string(cls, purl: str) -> "PackageURL":
473+
def from_string(cls, purl: str) -> Self:
476474
"""
477475
Return a PackageURL object parsed from a string.
478476
Raise ValueError on errors.
@@ -490,7 +488,7 @@ def from_string(cls, purl: str) -> "PackageURL":
490488
version: Optional[str] # this line is just for type hinting
491489
subpath: Optional[str] # this line is just for type hinting
492490

493-
type, sep, remainder = remainder.partition("/") # NOQA
491+
type, sep, remainder = remainder.partition("/")
494492
if not type or not sep:
495493
raise ValueError(f"purl is missing the required type component: {repr(purl)}.")
496494

@@ -536,7 +534,7 @@ def from_string(cls, purl: str) -> "PackageURL":
536534
if not name:
537535
raise ValueError(f"purl is missing the required name component: {repr(purl)}")
538536

539-
type, namespace, name, version, qualifiers, subpath = normalize( # NOQA
537+
type, namespace, name, version, qualifiers, subpath = normalize(
540538
type,
541539
namespace,
542540
name,
@@ -546,4 +544,4 @@ def from_string(cls, purl: str) -> "PackageURL":
546544
encode=False,
547545
)
548546

549-
return PackageURL(type, namespace, name, version, qualifiers, subpath)
547+
return cls(type, namespace, name, version, qualifiers, subpath)

0 commit comments

Comments
 (0)