Skip to content

Commit

Permalink
Fallback to default vcs ref and determine package name from the pip l…
Browse files Browse the repository at this point in the history
…ine where possible (#5921)

* More proactively determine package name from the pip line where possible, fallback to the file scanning logics.
  • Loading branch information
matteius committed Sep 7, 2023
1 parent 91743a6 commit 4a85f0b
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 42 deletions.
2 changes: 2 additions & 0 deletions news/5921.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fallback to default vcs ref when no ref is supplied.
More proactively determine package name from the pip line where possible, fallback to the existing file scanning logics when unable to determine name.
13 changes: 10 additions & 3 deletions pipenv/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1134,11 +1134,17 @@ def remove_packages_from_pipfile(self, packages):
self.write_toml(parsed)

def generate_package_pipfile_entry(self, package, pip_line, category=None):
"""Generate a package entry from pip install line
given the installreq package and the pip line that generated it.
"""
# Don't re-capitalize file URLs or VCSs.
if not isinstance(package, InstallRequirement):
package = expansive_install_req_from_line(package.strip())
package, req_name = expansive_install_req_from_line(package.strip())
else:
_, req_name = expansive_install_req_from_line(pip_line.strip())

req_name = determine_package_name(package)
if req_name is None:
req_name = determine_package_name(package)
path_specifier = determine_path_specifier(package)
vcs_specifier = determine_vcs_specifier(package)
name = self.get_package_name_in_pipfile(req_name, category=category)
Expand Down Expand Up @@ -1173,7 +1179,8 @@ def generate_package_pipfile_entry(self, package, pip_line, category=None):
else:
vcs_part = pip_line
vcs_parts = vcs_part.rsplit("@", 1)
entry["ref"] = vcs_parts[1].split("#", 1)[0].strip()
if len(vcs_parts) > 1:
entry["ref"] = vcs_parts[1].split("#", 1)[0].strip()
entry[vcs] = vcs_parts[0].strip()

# Check and extract subdirectory fragment
Expand Down
2 changes: 1 addition & 1 deletion pipenv/routines/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def do_install(
del os.environ["PYTHONHOME"]
st.console.print(f"Resolving {pkg_line}...", markup=False)
try:
pkg_requirement = expansive_install_req_from_line(
pkg_requirement, _ = expansive_install_req_from_line(
pkg_line, expand_env=True
)
except ValueError as e:
Expand Down
2 changes: 1 addition & 1 deletion pipenv/routines/outdated.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def do_outdated(project, pypi_mirror=None, pre=False, clear=False):
for name, deps in project.environment.reverse_dependencies().items()
}
for result in installed_packages:
dep = expansive_install_req_from_line(
dep, _ = expansive_install_req_from_line(
str(result.as_requirement()), expand_env=True
)
packages.update(as_pipfile(dep))
Expand Down
8 changes: 5 additions & 3 deletions pipenv/routines/uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ def do_uninstall(
raise exceptions.PipenvUsageError("No package provided!", ctx=ctx)
if not categories:
categories = project.get_package_categories(for_lockfile=True)
editable_pkgs = [
expansive_install_req_from_line(f"-e {p}").name for p in editable_packages if p
]
editable_pkgs = []
for p in editable_packages:
if p:
install_req, name = expansive_install_req_from_line(f"-e {p}")
editable_pkgs.append(name)
packages += editable_pkgs
package_names = {p for p in packages if p}
package_map = {canonicalize_name(p): p for p in packages if p}
Expand Down
2 changes: 1 addition & 1 deletion pipenv/routines/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def upgrade(
pipfile_category = get_pipfile_category_using_lockfile_section(category)

for package in package_args[:]:
install_req = expansive_install_req_from_line(package, expand_env=True)
install_req, _ = expansive_install_req_from_line(package, expand_env=True)
if index_name:
install_req.index = index_name
name, normalized_name, pipfile_entry = project.generate_package_pipfile_entry(
Expand Down
75 changes: 45 additions & 30 deletions pipenv/utils/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@ def replace_with_env(match):


def expansive_install_req_from_line(
name: str,
pip_line: str,
comes_from: Optional[Union[str, InstallRequirement]] = None,
*,
use_pep517: Optional[bool] = None,
Expand All @@ -923,30 +923,43 @@ def expansive_install_req_from_line(
user_supplied: bool = False,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
expand_env: bool = False,
) -> InstallRequirement:
"""Creates an InstallRequirement from a name, which might be a
requirement, directory containing 'setup.py', filename, or URL.
:param line_source: An optional string describing where the line is from,
for logging purposes in case of an error.
) -> (InstallRequirement, str):
"""Create an InstallRequirement from a pip-style requirement line.
InstallRequirement is a pip internal construct that represents an installable requirement,
and is used as an intermediary between the pip command and the resolver.
:param pip_line: A pip-style requirement line.
:param comes_from: The path to the requirements file the line was found in.
:param use_pep517: Whether to use PEP 517/518 when installing the
requirement.
:param isolated: Whether to isolate the requirements when installing them. (likely unused)
:param global_options: Extra global options to be used when installing the install req (likely unused)
:param hash_options: Extra hash options to be used when installing the install req (likely unused)
:param constraint: Whether the requirement is a constraint.
:param line_source: The source of the line (e.g. "requirements.txt").
:param user_supplied: Whether the requirement was directly provided by the user.
:param config_settings: Configuration settings to be used when installing the install req (likely unused)
:param expand_env: Whether to expand environment variables in the line. (definitely used)
:return: A tuple of the InstallRequirement and the name of the package (if determined).
"""
name = name.strip("'")
if name.startswith("-e "): # Editable requirements
name = name.split("-e ")[1]
return install_req_from_editable(name, line_source)
if has_name_with_extras(name):
name = name.split(" @ ", 1)[1]
name = None
pip_line = pip_line.strip("'")
for new_req_symbol in ("@ ", " @ "): # Check for new style pip lines
if new_req_symbol in pip_line:
pip_line_parts = pip_line.split(new_req_symbol, 1)
name = pip_line_parts[0]
pip_line = pip_line_parts[1]
if pip_line.startswith("-e "): # Editable requirements
pip_line = pip_line.split("-e ")[1]
return install_req_from_editable(pip_line, line_source), name

if expand_env:
name = expand_env_variables(name)
pip_line = expand_env_variables(pip_line)

vcs_part = name
if "@ " in name: # Check for new style vcs lines
vcs_part = name.split("@ ", 1)[1]
vcs_part = pip_line
for vcs in VCS_LIST:
if vcs_part.startswith(f"{vcs}+"):
link = get_link_from_line(vcs_part)
return InstallRequirement(
install_req = InstallRequirement(
None,
comes_from,
link=link,
Expand All @@ -957,22 +970,23 @@ def expansive_install_req_from_line(
constraint=constraint,
user_supplied=user_supplied,
)
if urlparse(name).scheme in ("http", "https", "file") or any(
name.endswith(s) for s in INSTALLABLE_EXTENSIONS
return install_req, name
if urlparse(pip_line).scheme in ("http", "https", "file") or any(
pip_line.endswith(s) for s in INSTALLABLE_EXTENSIONS
):
parts = parse_req_from_line(name, line_source)
parts = parse_req_from_line(pip_line, line_source)
else:
# It's a requirement
if "--index" in name:
name = name.split("--index")[0]
if " -i " in name:
name = name.split(" -i ")[0]
if "--index" in pip_line:
pip_line = pip_line.split("--index")[0]
if " -i " in pip_line:
pip_line = pip_line.split(" -i ")[0]
# handle local version identifiers (like the ones torch uses in their public index)
if "+" in name:
name = name.split("+")[0]
parts = parse_req_from_line(name, line_source)
if "+" in pip_line:
pip_line = pip_line.split("+")[0]
parts = parse_req_from_line(pip_line, line_source)

return InstallRequirement(
install_req = InstallRequirement(
parts.requirement,
comes_from,
link=parts.link,
Expand All @@ -986,6 +1000,7 @@ def expansive_install_req_from_line(
extras=parts.extras,
user_supplied=user_supplied,
)
return install_req, name


def _file_path_from_pipfile(path_obj, pipfile_entry):
Expand Down Expand Up @@ -1068,7 +1083,7 @@ def install_req_from_pipfile(name, pipfile):
version = ""
req_str = f"{name}{extras_str}{version}"

install_req = expansive_install_req_from_line(
install_req, _ = expansive_install_req_from_line(
req_str,
comes_from=None,
use_pep517=False,
Expand Down
3 changes: 2 additions & 1 deletion pipenv/utils/locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,8 @@ def get_requirements(
pip_line_specified = requirement_from_lockfile(
package_name, package_info, include_hashes=True, include_markers=True
)
yield expansive_install_req_from_line(pip_line), pip_line_specified
install_req, _ = expansive_install_req_from_line(pip_line)
yield install_req, pip_line_specified

def requirements_list(self, category: str) -> List[Dict]:
if self.lockfile.get(category):
Expand Down
7 changes: 5 additions & 2 deletions pipenv/utils/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,11 @@ def create(
if not dep:
continue
is_constraint = True
install_req = expansive_install_req_from_line(dep, expand_env=True)
package_name = determine_package_name(install_req)
install_req, package_name = expansive_install_req_from_line(
dep, expand_env=True
)
if package_name is None:
package_name = determine_package_name(install_req)
original_deps[package_name] = dep
install_reqs[package_name] = install_req
index, extra_index, trust_host, remainder = parse_indexes(dep)
Expand Down

0 comments on commit 4a85f0b

Please sign in to comment.