1515"""
1616Utilities to manage requirements files and call pip.
1717NOTE: this should use ONLY the standard library and not import anything else
18- because this is used for boostrapping with no requirements installed.
18+ because this is used for bootstrapping with no requirements installed.
1919"""
2020
2121
@@ -31,7 +31,7 @@ def load_requirements(requirements_file="requirements.txt", with_unpinned=False)
3131
3232def get_required_name_versions (requirement_lines , with_unpinned = False ):
3333 """
34- Yield required (name, version) tuples given a`requirement_lines` iterable of
34+ Yield required (name, version) tuples given a `requirement_lines` iterable of
3535 requirement text lines. Only accept requirements pinned to an exact version.
3636 """
3737
@@ -47,21 +47,26 @@ def get_required_name_versions(requirement_lines, with_unpinned=False):
4747
4848def get_required_name_version (requirement , with_unpinned = False ):
4949 """
50- Return a (name, version) tuple given a`requirement` specifier string.
50+ Return a (name, version) tuple given a `requirement` specifier string.
5151 Requirement version must be pinned. If ``with_unpinned`` is True, unpinned
5252 requirements are accepted and only the name portion is returned.
5353
54- For example:
55- >>> assert get_required_name_version("foo==1.2.3") == ("foo", "1.2.3")
56- >>> assert get_required_name_version("fooA==1.2.3.DEV1") == ("fooa", "1.2.3.dev1")
57- >>> assert get_required_name_version("foo==1.2.3", with_unpinned=False) == ("foo", "1.2.3")
58- >>> assert get_required_name_version("foo", with_unpinned=True) == ("foo", "")
59- >>> expected = ("foo", ""), get_required_name_version("foo>=1.2")
60- >>> assert get_required_name_version("foo>=1.2", with_unpinned=True) == expected
54+ Examples:
55+ >>> get_required_name_version("foo==1.2.3")
56+ ('foo', '1.2.3')
57+ >>> get_required_name_version("fooA==1.2.3.DEV1")
58+ ('fooa', '1.2.3.dev1')
59+ >>> get_required_name_version("foo==1.2.3", with_unpinned=False)
60+ ('foo', '1.2.3')
61+ >>> get_required_name_version("foo", with_unpinned=True)
62+ ('foo', '')
63+ >>> get_required_name_version("foo>=1.2", with_unpinned=True)
64+ ('foo', '')
6165 >>> try:
62- ... assert not get_required_name_version("foo", with_unpinned=False)
66+ ... get_required_name_version("foo", with_unpinned=False)
6367 ... except Exception as e:
64- ... assert "Requirement version must be pinned" in str(e)
68+ ... "Requirement version must be pinned" in str(e)
69+ True
6570 """
6671 requirement = requirement and "" .join (requirement .lower ().split ())
6772 if not requirement :
@@ -95,9 +100,7 @@ def lock_dev_requirements(
95100 """
96101 Freeze and lock current installed development-only requirements and save
97102 this to the `dev_requirements_file` requirements file. Development-only is
98- achieved by subtracting requirements from the `main_requirements_file`
99- requirements file from the current requirements using package names (and
100- ignoring versions).
103+ achieved by subtracting requirements from the main requirements using names only.
101104 """
102105 main_names = {n for n , _v in load_requirements (main_requirements_file )}
103106 all_reqs = get_installed_reqs (site_packages_dir = site_packages_dir )
@@ -112,13 +115,11 @@ def lock_dev_requirements(
112115
113116def get_installed_reqs (site_packages_dir ):
114117 """
115- Return the installed pip requirements as text found in `site_packages_dir`
116- as a text.
118+ Return the installed pip requirements as text found in `site_packages_dir`.
117119 """
118120 if not os .path .exists (site_packages_dir ):
119121 raise Exception (f"site_packages directory: { site_packages_dir !r} does not exists" )
120- # Also include these packages in the output with --all: wheel, distribute,
121- # setuptools, pip
122+
122123 args = ["pip" , "freeze" , "--exclude-editable" , "--all" , "--path" , site_packages_dir ]
123124 return subprocess .check_output (args , encoding = "utf-8" ) # noqa: S603
124125
@@ -140,23 +141,29 @@ def get_installed_reqs(site_packages_dir):
140141
141142def split_req (req ):
142143 """
143- Return a three-tuple of (name, comparator, version) given a ``req``
144- requirement specifier string. Each segment may be empty. Spaces are removed.
145-
146- For example:
147- >>> assert split_req("foo==1.2.3") == ("foo", "==", "1.2.3"), split_req("foo==1.2.3")
148- >>> assert split_req("foo") == ("foo", "", ""), split_req("foo")
149- >>> assert split_req("==1.2.3") == ("", "==", "1.2.3"), split_req("==1.2.3")
150- >>> assert split_req("foo >= 1.2.3 ") == ("foo", ">=", "1.2.3"), split_req("foo >= 1.2.3 ")
151- >>> assert split_req("foo>=1.2") == ("foo", ">=", "1.2"), split_req("foo>=1.2")
144+ Return a three-tuple of (name, comparator, version) given a requirement
145+ specifier string.
146+
147+ Examples:
148+ >>> split_req("foo==1.2.3")
149+ ('foo', '==', '1.2.3')
150+ >>> split_req("foo")
151+ ('foo', '', '')
152+ >>> split_req("==1.2.3")
153+ ('', '==', '1.2.3')
154+ >>> split_req("foo >= 1.2.3 ")
155+ ('foo', '>=', '1.2.3')
156+ >>> split_req("foo>=1.2")
157+ ('foo', '>=', '1.2')
152158 """
153159 if not req :
154160 raise ValueError ("req is required" )
155- # do not allow multiple constraints and tags
156161 if not any (c in req for c in ",;" ):
157162 raise Exception (f"complex requirements with : or ; not supported: { req } " )
163+
158164 req = "" .join (req .split ())
159165 if not any (c in req for c in comparators ):
160166 return req , "" , ""
167+
161168 segments = version_splitter .split (req , maxsplit = 1 )
162169 return tuple (segments )
0 commit comments