diff options
| author | Jason R. Coombs <jaraco@jaraco.com> | 2022-01-29 13:15:50 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-01-29 13:15:50 -0500 |
| commit | 19c1dc09056d534e523332889c96ccf946a216ca (patch) | |
| tree | 453d291e32c006fed104a24bc179a692156f9bfd /pkg_resources | |
| parent | aa3d9b93f4b46cad03d3eeb174430668d565bb06 (diff) | |
| parent | 3eca9923d34ffab7be96802eed8029d0d7ab1fbf (diff) | |
| download | python-setuptools-git-19c1dc09056d534e523332889c96ccf946a216ca.tar.gz | |
Merge pull request #3045 from pypa/feature/refactor-parse-requirements
Feature/refactor parse requirements
Diffstat (limited to 'pkg_resources')
| -rw-r--r-- | pkg_resources/__init__.py | 78 |
1 files changed, 63 insertions, 15 deletions
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 9933aad8..93db52d2 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2404,7 +2404,20 @@ def _nonblank(str): @functools.singledispatch def yield_lines(iterable): - """Yield valid lines of a string or iterable""" + r""" + Yield valid lines of a string or iterable. + + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ return itertools.chain.from_iterable(map(yield_lines, iterable)) @@ -3079,26 +3092,61 @@ def issue_warning(*args, **kw): warnings.warn(stacklevel=level + 1, *args, **kw) -def parse_requirements(strs): - """Yield ``Requirement`` objects for each specification in `strs` +def drop_comment(line): + """ + Drop comments. - `strs` must be a string, or a (possibly-nested) iterable thereof. + >>> drop_comment('foo # bar') + 'foo' + + A hash without a space may be in a URL. + + >>> drop_comment('http://example.com/foo#bar') + 'http://example.com/foo#bar' """ - # create a steppable iterator, so we can handle \-continuations - lines = iter(yield_lines(strs)) + return line.partition(' #')[0] + + +def join_continuation(lines): + r""" + Join lines continued by a trailing backslash. - for line in lines: - # Drop comments -- a hash without a space may be in a URL. - if ' #' in line: - line = line[:line.find(' #')] - # If there is a line continuation, drop it, and append the next line. - if line.endswith('\\'): - line = line[:-2].strip() + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar \\', 'baz'])) + ['foobarbaz'] + + Not sure why, but... + The character preceeding the backslash is also elided. + + >>> list(join_continuation(['goo\\', 'dly'])) + ['godly'] + + A terrible idea, but... + If no line is available to continue, suppress the lines. + + >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) + ['foo'] + """ + lines = iter(lines) + for item in lines: + while item.endswith('\\'): try: - line += next(lines) + item = item[:-2].strip() + next(lines) except StopIteration: return - yield Requirement(line) + yield item + + +def parse_requirements(strs): + """ + Yield ``Requirement`` objects for each specification in `strs`. + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs)))) class RequirementParseError(packaging.requirements.InvalidRequirement): |
