diff --git a/src/packageurl/__init__.py b/src/packageurl/__init__.py index 170038b..f1e5551 100644 --- a/src/packageurl/__init__.py +++ b/src/packageurl/__init__.py @@ -24,11 +24,12 @@ # Visit https://github.com/package-url/packageurl-python for support and # download. +from __future__ import annotations + import string from collections import namedtuple from typing import TYPE_CHECKING from typing import Any -from typing import AnyStr from typing import Dict from typing import Optional from typing import Tuple @@ -43,12 +44,15 @@ from collections.abc import Iterable from typing_extensions import Literal + from typing_extensions import Self + + AnyStr = Union[str, bytes] # Python 3 basestring = ( bytes, str, -) # NOQA +) """ A purl (aka. Package URL) implementation as specified at: @@ -84,16 +88,16 @@ def unquote(s: AnyStr) -> str: @overload -def get_quoter(encode: bool = True) -> "Callable[[AnyStr], str]": ... +def get_quoter(encode: bool = True) -> Callable[[AnyStr], str]: ... @overload -def get_quoter(encode: None) -> "Callable[[str], str]": ... +def get_quoter(encode: None) -> Callable[[str], str]: ... def get_quoter( encode: Optional[bool] = True, -) -> "Union[Callable[[AnyStr], str], Callable[[str], str]]": +) -> Union[Callable[[AnyStr], str], Callable[[str], str]]: """ Return quoting callable given an `encode` tri-boolean (True, False or None) """ @@ -105,22 +109,22 @@ def get_quoter( return lambda x: x -def normalize_type(type: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]: # NOQA +def normalize_type(type: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]: if not type: return None if not isinstance(type, str): - type_str = type.decode("utf-8") # NOQA + type_str = type.decode("utf-8") else: type_str = type quoter = get_quoter(encode) - type_str = quoter(type_str) # NOQA + type_str = quoter(type_str) return type_str.strip().lower() or None def normalize_namespace( namespace: Optional[AnyStr], ptype: Optional[str], encode: Optional[bool] = True -) -> Optional[str]: # NOQA +) -> Optional[str]: if not namespace: return None if not isinstance(namespace, str): @@ -138,7 +142,7 @@ def normalize_namespace( def normalize_name( name: Optional[AnyStr], ptype: Optional[str], encode: Optional[bool] = True -) -> Optional[str]: # NOQA +) -> Optional[str]: if not name: return None if not isinstance(name, str): @@ -156,9 +160,7 @@ def normalize_name( return name_str or None -def normalize_version( - version: Optional[AnyStr], encode: Optional[bool] = True -) -> Optional[str]: # NOQA +def normalize_version(version: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]: if not version: return None if not isinstance(version, str): @@ -173,25 +175,25 @@ def normalize_version( @overload def normalize_qualifiers( - qualifiers: Union[AnyStr, Dict[str, str], None], encode: "Literal[True]" = ... + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Literal[True] = ... ) -> Optional[str]: ... @overload def normalize_qualifiers( - qualifiers: Union[AnyStr, Dict[str, str], None], encode: "Optional[Literal[False]]" -) -> Optional[Dict[str, str]]: ... + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Optional[Literal[False]] +) -> Dict[str, str]: ... @overload def normalize_qualifiers( - qualifiers: Union[AnyStr, Dict[str, str], None], encode: Optional[bool] = ... -) -> Union[str, Dict[str, str], None]: ... + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Optional[bool] = ... +) -> Optional[Union[str, Dict[str, str]]]: ... def normalize_qualifiers( - qualifiers: Union[AnyStr, Dict[str, str], None], encode: Optional[bool] = True -) -> Union[str, Dict[str, str], None]: # NOQA + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], encode: Optional[bool] = True +) -> Optional[Union[str, Dict[str, str]]]: """ Return normalized `qualifiers` as a mapping (or as a string if `encode` is True). The `qualifiers` arg is either a mapping or a string. @@ -213,7 +215,7 @@ def normalize_qualifiers( f"Invalid qualifier. Must be a string of key=value pairs:{repr(qualifiers_list)}" ) qualifiers_parts = [kv.partition("=") for kv in qualifiers_list] - qualifiers_pairs: "Iterable[Tuple[str, str]]" = [(k, v) for k, _, v in qualifiers_parts] + qualifiers_pairs: Iterable[Tuple[str, str]] = [(k, v) for k, _, v in qualifiers_parts] elif isinstance(qualifiers, dict): qualifiers_pairs = qualifiers.items() else: @@ -255,9 +257,7 @@ def normalize_qualifiers( return qualifiers_map -def normalize_subpath( - subpath: Optional[AnyStr], encode: Optional[bool] = True -) -> Optional[str]: # NOQA +def normalize_subpath(subpath: Optional[AnyStr], encode: Optional[bool] = True) -> Optional[str]: if not subpath: return None if not isinstance(subpath, str): @@ -278,9 +278,9 @@ def normalize( namespace: Optional[AnyStr], name: Optional[AnyStr], version: Optional[AnyStr], - qualifiers: Union[AnyStr, Dict[str, str], None], + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], subpath: Optional[AnyStr], - encode: "Literal[True]" = ..., + encode: Literal[True] = ..., ) -> Tuple[str, Optional[str], str, Optional[str], Optional[str], Optional[str]]: ... @@ -290,10 +290,10 @@ def normalize( namespace: Optional[AnyStr], name: Optional[AnyStr], version: Optional[AnyStr], - qualifiers: Union[AnyStr, Dict[str, str], None], + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], subpath: Optional[AnyStr], - encode: "Optional[Literal[False]]", -) -> Tuple[str, Optional[str], str, Optional[str], Optional[Dict[str, str]], Optional[str]]: ... + encode: Optional[Literal[False]], +) -> Tuple[str, Optional[str], str, Optional[str], Dict[str, str], Optional[str]]: ... @overload @@ -302,11 +302,11 @@ def normalize( namespace: Optional[AnyStr], name: Optional[AnyStr], version: Optional[AnyStr], - qualifiers: Union[AnyStr, Dict[str, str], None], + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], subpath: Optional[AnyStr], encode: Optional[bool] = ..., ) -> Tuple[ - str, Optional[str], str, Optional[str], Union[str, Dict[str, str], None], Optional[str] + str, Optional[str], str, Optional[str], Optional[Union[str, Dict[str, str]]], Optional[str] ]: ... @@ -315,7 +315,7 @@ def normalize( namespace: Optional[AnyStr], name: Optional[AnyStr], version: Optional[AnyStr], - qualifiers: Union[AnyStr, Dict[str, str], None], + qualifiers: Optional[Union[AnyStr, Dict[str, str]]], subpath: Optional[AnyStr], encode: Optional[bool] = True, ) -> Tuple[ @@ -323,13 +323,13 @@ def normalize( Optional[str], Optional[str], Optional[str], - Union[str, Dict[str, str], None], + Optional[Union[str, Dict[str, str]]], Optional[str], -]: # NOQA +]: """ Return normalized purl components """ - type_norm = normalize_type(type, encode) # NOQA + type_norm = normalize_type(type, encode) namespace_norm = normalize_namespace(namespace, type_norm, encode) name_norm = normalize_name(name, type_norm, encode) version_norm = normalize_version(version, encode) @@ -346,22 +346,22 @@ class PackageURL( https://github.com/package-url/purl-spec """ - name: str - namespace: Optional[str] - qualifiers: Union[str, Dict[str, str], None] - subpath: Optional[str] type: str + namespace: Optional[str] + name: str version: Optional[str] + qualifiers: Dict[str, str] + subpath: Optional[str] def __new__( - self, + cls, type: Optional[AnyStr] = None, namespace: Optional[AnyStr] = None, - name: Optional[AnyStr] = None, # NOQA + name: Optional[AnyStr] = None, version: Optional[AnyStr] = None, - qualifiers: Union[AnyStr, Dict[str, str], None] = None, + qualifiers: Optional[Union[AnyStr, Dict[str, str]]] = None, subpath: Optional[AnyStr] = None, - ) -> "PackageURL": # this should be 'Self' https://github.com/python/mypy/pull/13133 + ) -> Self: required = dict(type=type, name=name) for key, value in required.items(): if value: @@ -399,12 +399,10 @@ def __new__( version_norm, qualifiers_norm, subpath_norm, - ) = normalize( # NOQA - type, namespace, name, version, qualifiers, subpath, encode=None - ) + ) = normalize(type, namespace, name, version, qualifiers, subpath, encode=None) return super().__new__( - PackageURL, + cls, type=type_norm, namespace=namespace_norm, name=name_norm, @@ -439,7 +437,7 @@ def to_string(self) -> str: """ Return a purl string built from components. """ - type, namespace, name, version, qualifiers, subpath = normalize( # NOQA + type, namespace, name, version, qualifiers, subpath = normalize( self.type, self.namespace, self.name, @@ -472,7 +470,7 @@ def to_string(self) -> str: return "".join(purl) @classmethod - def from_string(cls, purl: str) -> "PackageURL": + def from_string(cls, purl: str) -> Self: """ Return a PackageURL object parsed from a string. Raise ValueError on errors. @@ -490,7 +488,7 @@ def from_string(cls, purl: str) -> "PackageURL": version: Optional[str] # this line is just for type hinting subpath: Optional[str] # this line is just for type hinting - type, sep, remainder = remainder.partition("/") # NOQA + type, sep, remainder = remainder.partition("/") if not type or not sep: raise ValueError(f"purl is missing the required type component: {repr(purl)}.") @@ -536,7 +534,7 @@ def from_string(cls, purl: str) -> "PackageURL": if not name: raise ValueError(f"purl is missing the required name component: {repr(purl)}") - type, namespace, name, version, qualifiers, subpath = normalize( # NOQA + type, namespace, name, version, qualifiers, subpath = normalize( type, namespace, name, @@ -546,4 +544,4 @@ def from_string(cls, purl: str) -> "PackageURL": encode=False, ) - return PackageURL(type, namespace, name, version, qualifiers, subpath) + return cls(type, namespace, name, version, qualifiers, subpath)