|
16 | 16 | from pip._vendor.packaging.markers import Marker
|
17 | 17 | from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
|
18 | 18 | from pip._vendor.packaging.specifiers import Specifier
|
19 |
| -from pip._vendor.pkg_resources import RequirementParseError, parse_requirements |
20 | 19 |
|
21 | 20 | from pip._internal.exceptions import InstallationError
|
22 | 21 | from pip._internal.models.index import PyPI, TestPyPI
|
@@ -113,31 +112,56 @@ def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
|
113 | 112 | return package_name, url, set()
|
114 | 113 |
|
115 | 114 |
|
| 115 | +def check_first_requirement_in_file(filename: str) -> None: |
| 116 | + """Check if file is parsable as a requirements file. |
| 117 | +
|
| 118 | + This is heavily based on ``pkg_resources.parse_requirements``, but |
| 119 | + simplified to just check the first meaningful line. |
| 120 | +
|
| 121 | + :raises InvalidRequirement: If the first meaningful line cannot be parsed |
| 122 | + as an requirement. |
| 123 | + """ |
| 124 | + with open(filename, encoding="utf-8", errors="ignore") as f: |
| 125 | + # Create a steppable iterator, so we can handle \-continuations. |
| 126 | + lines = ( |
| 127 | + line |
| 128 | + for line in (line.strip() for line in f) |
| 129 | + if line and not line.startswith("#") # Skip blank lines/comments. |
| 130 | + ) |
| 131 | + |
| 132 | + for line in lines: |
| 133 | + # Drop comments -- a hash without a space may be in a URL. |
| 134 | + if " #" in line: |
| 135 | + line = line[: line.find(" #")] |
| 136 | + # If there is a line continuation, drop it, and append the next line. |
| 137 | + if line.endswith("\\"): |
| 138 | + line = line[:-2].strip() + next(lines, "") |
| 139 | + Requirement(line) |
| 140 | + return |
| 141 | + |
| 142 | + |
116 | 143 | def deduce_helpful_msg(req: str) -> str:
|
117 | 144 | """Returns helpful msg in case requirements file does not exist,
|
118 | 145 | or cannot be parsed.
|
119 | 146 |
|
120 | 147 | :params req: Requirements file path
|
121 | 148 | """
|
122 |
| - msg = "" |
123 |
| - if os.path.exists(req): |
124 |
| - msg = " The path does exist. " |
125 |
| - # Try to parse and check if it is a requirements file. |
126 |
| - try: |
127 |
| - with open(req) as fp: |
128 |
| - # parse first line only |
129 |
| - next(parse_requirements(fp.read())) |
130 |
| - msg += ( |
131 |
| - "The argument you provided " |
132 |
| - "({}) appears to be a" |
133 |
| - " requirements file. If that is the" |
134 |
| - " case, use the '-r' flag to install" |
135 |
| - " the packages specified within it." |
136 |
| - ).format(req) |
137 |
| - except RequirementParseError: |
138 |
| - logger.debug("Cannot parse '%s' as requirements file", req, exc_info=True) |
| 149 | + if not os.path.exists(req): |
| 150 | + return f" File '{req}' does not exist." |
| 151 | + msg = " The path does exist. " |
| 152 | + # Try to parse and check if it is a requirements file. |
| 153 | + try: |
| 154 | + check_first_requirement_in_file(req) |
| 155 | + except InvalidRequirement: |
| 156 | + logger.debug("Cannot parse '%s' as requirements file", req) |
139 | 157 | else:
|
140 |
| - msg += f" File '{req}' does not exist." |
| 158 | + msg += ( |
| 159 | + f"The argument you provided " |
| 160 | + f"({req}) appears to be a" |
| 161 | + f" requirements file. If that is the" |
| 162 | + f" case, use the '-r' flag to install" |
| 163 | + f" the packages specified within it." |
| 164 | + ) |
141 | 165 | return msg
|
142 | 166 |
|
143 | 167 |
|
|
0 commit comments