Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/requirements.py: 18%
51 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
5import urllib.parse
6from typing import Any, List, Optional, Set
8from ._parser import parse_requirement
9from ._tokenizer import ParserSyntaxError
10from .markers import Marker, _normalize_extra_values
11from .specifiers import SpecifierSet
14class InvalidRequirement(ValueError):
15 """
16 An invalid requirement was found, users should refer to PEP 508.
17 """
20class Requirement:
21 """Parse a requirement.
23 Parse a given requirement string into its parts, such as name, specifier,
24 URL, and extras. Raises InvalidRequirement on a badly-formed requirement
25 string.
26 """
28 # TODO: Can we test whether something is contained within a requirement?
29 # If so how do we do that? Do we need to test against the _name_ of
30 # the thing as well as the version? What about the markers?
31 # TODO: Can we normalize the name and extra name?
33 def __init__(self, requirement_string: str) -> None:
34 try:
35 parsed = parse_requirement(requirement_string)
36 except ParserSyntaxError as e:
37 raise InvalidRequirement(str(e)) from e
39 self.name: str = parsed.name
40 if parsed.url:
41 parsed_url = urllib.parse.urlparse(parsed.url)
42 if parsed_url.scheme == "file":
43 if urllib.parse.urlunparse(parsed_url) != parsed.url:
44 raise InvalidRequirement("Invalid URL given")
45 elif not (parsed_url.scheme and parsed_url.netloc) or (
46 not parsed_url.scheme and not parsed_url.netloc
47 ):
48 raise InvalidRequirement(f"Invalid URL: {parsed.url}")
49 self.url: Optional[str] = parsed.url
50 else:
51 self.url = None
52 self.extras: Set[str] = set(parsed.extras if parsed.extras else [])
53 self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
54 self.marker: Optional[Marker] = None
55 if parsed.marker is not None:
56 self.marker = Marker.__new__(Marker)
57 self.marker._markers = _normalize_extra_values(parsed.marker)
59 def __str__(self) -> str:
60 parts: List[str] = [self.name]
62 if self.extras:
63 formatted_extras = ",".join(sorted(self.extras))
64 parts.append(f"[{formatted_extras}]")
66 if self.specifier:
67 parts.append(str(self.specifier))
69 if self.url:
70 parts.append(f"@ {self.url}")
71 if self.marker:
72 parts.append(" ")
74 if self.marker:
75 parts.append(f"; {self.marker}")
77 return "".join(parts)
79 def __repr__(self) -> str:
80 return f"<Requirement('{self}')>"
82 def __hash__(self) -> int:
83 return hash((self.__class__.__name__, str(self)))
85 def __eq__(self, other: Any) -> bool:
86 if not isinstance(other, Requirement):
87 return NotImplemented
89 return (
90 self.name == other.name
91 and self.extras == other.extras
92 and self.specifier == other.specifier
93 and self.url == other.url
94 and self.marker == other.marker
95 )