2Requirements file parsing
11from optparse
import Values
35 from typing
import NoReturn
39__all__ = [
"parse_requirements"]
41ReqFileLines = Iterable[Tuple[int, str]]
43LineParser = Callable[[str], Tuple[str, Values]]
52ENV_VAR_RE =
re.compile(
r"(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})")
79SUPPORTED_OPTIONS_REQ_DEST = [str(
o().dest)
for o
in SUPPORTED_OPTIONS_REQ]
91 options: Optional[Dict[str, Any]] =
None,
92 line_source: Optional[str] =
None,
129def parse_requirements(
132 finder: Optional[
"PackageFinder"] =
None,
134 constraint: bool =
False,
135) -> Generator[ParsedRequirement,
None,
None]:
136 """Parse a requirements file and yield ParsedRequirement instances.
138 :param filename: Path or url of requirements file.
139 :param session: PipSession instance.
140 :param finder: Instance of pip.index.PackageFinder.
141 :param options: cli options.
142 :param constraint: If true, parsing a constraint file rather than
150 parsed_line, options=options, finder=finder, session=session
152 if parsed_req
is not None:
157 """Split, filter, and join lines, and return a line iterator
159 :param content: the content of the requirements file
171) -> ParsedRequirement:
173 line_comes_from =
"{} {} (line {})".format(
187 comes_from=line_comes_from,
193 for dest
in SUPPORTED_OPTIONS_REQ_DEST:
197 line_source = f
"line {line.lineno} of {line.filename}"
201 comes_from=line_comes_from,
204 line_source=line_source,
212 finder: Optional[
"PackageFinder"] =
None,
214 session: Optional[PipSession] =
None,
218 "%s line %s has --hash but no requirement, and will be ignored.",
252 value = relative_to_reqs_file
260 find_links=find_links,
261 index_urls=index_urls,
274 source = f
"line {lineno} of {filename}"
281 finder: Optional[
"PackageFinder"] =
None,
282 session: Optional[PipSession] =
None,
283) -> Optional[ParsedRequirement]:
284 """Handle a single parsed requirements line; This can result in
285 creating/yielding requirements, or updating the finder.
287 :param line: The parsed line to be processed.
288 :param options: CLI options.
289 :param finder: The finder - updated by non-requirement lines.
290 :param session: The session - updated by non-requirement lines.
292 Returns a ParsedRequirement object if the line is a requirement line,
293 otherwise returns None.
295 For lines that contain requirements, the only options that have an effect
296 are from SUPPORTED_OPTIONS_REQ, and they are scoped to the
297 requirement. Other options from SUPPORTED_OPTIONS may be present, but are
300 For lines that do not contain requirements, the only options that have an
301 effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may
302 be present, but are ignored. These lines may contain multiple options
303 (although our docs imply only one is supported), and all our parsed and
326 line_parser: LineParser,
332 self, filename: str, constraint: bool
333 ) -> Generator[ParsedLine,
None,
None]:
334 """Parse a given file, yielding parsed lines."""
338 self, filename: str, constraint: bool
339 ) -> Generator[ParsedLine,
None,
None]:
340 for line
in self.
_parse_file(filename, constraint):
347 nested_constraint =
False
350 nested_constraint =
True
369 self, filename: str, constraint: bool
370 ) -> Generator[ParsedLine,
None,
None]:
375 for line_number, line
in lines_enum:
378 except OptionParsingError
as e:
380 msg = f
"Invalid requirement: {line}\n{e.msg}"
393 def parse_line(line: str) -> Tuple[str, Values]:
406 except ValueError
as e:
411 return args_str, opts
417 """Break up the line into an args and options string. We only want to shlex
418 (and then optparse) the options, not the args. args can contain markers
419 which are corrupted by shlex.
430 return " ".join(args),
" ".join(options)
440 Return a parser for parsing requirement lines
444 option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ
445 for option_factory
in option_factories:
451 def parser_exit(self: Any, msg: str) ->
"NoReturn":
462 """Joins a line ending in '\' with the previous line (except when following
463 comments). The joined line takes on the index of the first line.
465 primary_line_number =
None
466 new_line: List[str] = []
467 for line_number, line
in lines_enum:
474 assert primary_line_number
is not None
475 yield primary_line_number,
"".join(new_line)
478 yield line_number, line
481 primary_line_number = line_number
486 assert primary_line_number
is not None
487 yield primary_line_number,
"".join(new_line)
494 Strips comments and filter empty lines.
496 for line_number, line
in lines_enum:
500 yield line_number, line
504 """Replace all environment variables that can be retrieved via `os.getenv`.
506 The only allowed format for environment variables defined in the
507 requirement file is `${MY_VARIABLE_1}` to ensure two things:
509 1. Strings that contain a `$` aren't accidentally (partially) expanded.
510 2. Ensure consistency across platforms for requirement files.
512 These points are the result of a discussion on the `github pull
513 request #3514 <https://github.com/pypa/pip/pull/3514>`_.
515 Valid characters in variable names follow the `POSIX standard
516 <http://pubs.opengroup.org/onlinepubs/9699919799/>`_ and are limited
517 to uppercase letter, digits and the `_` (underscore).
519 for line_number, line
in lines_enum:
527 yield line_number, line
531 """Gets the content of a file; it may be a filename, file: URL, or
532 http: URL. Returns (location, content). Content is unicode.
533 Respects # -*- coding: declarations on the retrieved files.
535 :param url: File path or url.
536 :param session: PipSession instance.
538 scheme = get_url_scheme(url)
541 if scheme
in [
"http",
"https",
"file"]:
543 raise_for_status(resp)
548 with open(url,
"rb")
as f:
549 content = auto_decode(
f.read())
550 except OSError
as exc:
None __init__(self, str msg)
None __init__(self, str filename, int lineno, str args, Values opts, bool constraint)
None __init__(self, str requirement, bool is_editable, str comes_from, bool constraint, Optional[Dict[str, Any]] options=None, Optional[str] line_source=None)
None __init__(self, PipSession session, LineParser line_parser)
Generator[ParsedLine, None, None] _parse_and_recurse(self, str filename, bool constraint)
Generator[ParsedLine, None, None] _parse_file(self, str filename, bool constraint)
ReqFileLines join_lines(ReqFileLines lines_enum)
Tuple[str, str] get_file_content(str url, PipSession session)
ParsedRequirement handle_requirement_line(ParsedLine line, Optional[optparse.Values] options=None)
optparse.OptionParser build_parser()
None handle_option_line(Values opts, str filename, int lineno, Optional["PackageFinder"] finder=None, Optional[optparse.Values] options=None, Optional[PipSession] session=None)
ReqFileLines expand_env_variables(ReqFileLines lines_enum)
Tuple[str, str] break_args_options(str line)
Optional[ParsedRequirement] handle_line(ParsedLine line, Optional[optparse.Values] options=None, Optional["PackageFinder"] finder=None, Optional[PipSession] session=None)
ReqFileLines preprocess(str content)
LineParser get_line_parser(Optional["PackageFinder"] finder)
ReqFileLines ignore_comments(ReqFileLines lines_enum)