1818from .regex import URL_REGEX , HASH_REGEX
1919
2020from .dependencies import DependencyFile , Dependency
21- from pkg_resources import parse_requirements
21+ from packaging .requirements import Requirement as PackagingRequirement , InvalidRequirement
22+ import six
2223from . import filetypes
2324
2425
26+ # this is a backport from setuptools 26.1
27+ def setuptools_parse_requirements_backport (strs ): # pragma: no cover
28+ # Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com>
29+ #
30+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
31+ # this software and associated documentation files (the "Software"), to deal in
32+ # the Software without restriction, including without limitation the rights to
33+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
34+ # of the Software, and to permit persons to whom the Software is furnished to do
35+ # so, subject to the following conditions:
36+ #
37+ # The above copyright notice and this permission notice shall be included in all
38+ # copies or substantial portions of the Software.
39+ #
40+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46+ # SOFTWARE.
47+ """Yield ``Requirement`` objects for each specification in `strs`
48+
49+ `strs` must be a string, or a (possibly-nested) iterable thereof.
50+ """
51+ # create a steppable iterator, so we can handle \-continuations
52+ def yield_lines (strs ):
53+ """Yield non-empty/non-comment lines of a string or sequence"""
54+ if isinstance (strs , six .string_types ):
55+ for s in strs .splitlines ():
56+ s = s .strip ()
57+ # skip blank lines/comments
58+ if s and not s .startswith ('#' ):
59+ yield s
60+ else :
61+ for ss in strs :
62+ for s in yield_lines (ss ):
63+ yield s
64+ lines = iter (yield_lines (strs ))
65+
66+ for line in lines :
67+ # Drop comments -- a hash without a space may be in a URL.
68+ if ' #' in line :
69+ line = line [:line .find (' #' )]
70+ # If there is a line continuation, drop it, and append the next line.
71+ if line .endswith ('\\ ' ):
72+ line = line [:- 2 ].strip ()
73+ line += next (lines )
74+ yield PackagingRequirement (line )
75+
76+
2577class RequirementsTXTLineParser (object ):
2678 """
2779
@@ -34,14 +86,17 @@ def parse(cls, line):
3486 :param line:
3587 :return:
3688 """
37- # setuptools requires a space before the comment. If this isn't the case, add it.
38- if "\t #" in line :
39- parsed , = parse_requirements (line .replace ("\t #" , "\t #" ))
40- else :
41- parsed , = parse_requirements (line )
89+ try :
90+ # setuptools requires a space before the comment. If this isn't the case, add it.
91+ if "\t #" in line :
92+ parsed , = setuptools_parse_requirements_backport (line .replace ("\t #" , "\t #" ))
93+ else :
94+ parsed , = setuptools_parse_requirements_backport (line )
95+ except InvalidRequirement :
96+ return None
4297 dep = Dependency (
43- name = parsed .project_name ,
44- specs = parsed .specs ,
98+ name = parsed .name ,
99+ specs = parsed .specifier ,
45100 line = line ,
46101 extras = parsed .extras ,
47102 dependency_type = filetypes .requirements_txt
@@ -200,11 +255,12 @@ def parse(self):
200255 parseable_line , hashes = Parser .parse_hashes (parseable_line )
201256
202257 req = RequirementsTXTLineParser .parse (parseable_line )
203- req .hashes = hashes
204- req .index_server = index_server
205- # replace the requirements line with the 'real' line
206- req .line = line
207- self .obj .dependencies .append (req )
258+ if req :
259+ req .hashes = hashes
260+ req .index_server = index_server
261+ # replace the requirements line with the 'real' line
262+ req .line = line
263+ self .obj .dependencies .append (req )
208264 except ValueError :
209265 continue
210266
@@ -229,8 +285,9 @@ def parse(self):
229285 continue
230286 if line :
231287 req = RequirementsTXTLineParser .parse (line )
232- req .dependency_type = self .obj .file_type
233- self .obj .dependencies .append (req )
288+ if req :
289+ req .dependency_type = self .obj .file_type
290+ self .obj .dependencies .append (req )
234291 except NoOptionError :
235292 pass
236293
@@ -254,8 +311,9 @@ def parse(self):
254311 if self .is_marked_line (line ):
255312 continue
256313 req = RequirementsTXTLineParser .parse (line )
257- req .dependency_type = self .obj .file_type
258- self .obj .dependencies .append (req )
314+ if req :
315+ req .dependency_type = self .obj .file_type
316+ self .obj .dependencies .append (req )
259317 except yaml .YAMLError :
260318 pass
261319
0 commit comments