Skip to content

Commit aef9e6f

Browse files
committed
Fix editable installation from relative paths on Windows
When running `pipenv install -e .\..\mypackage` the pip requirement line `-e .\..\mypackage` is written to a temporary requirements file which is passed to pip to install the package. pip fails to install the package from the requirements file as it incorrectly interprets the relative path, treating the slashes as escapes. To work around this issue replace all back slashes (windows directory separators) with forward slashes (POSIX directory separators) in pip requirements for editable package installs.
1 parent 218f1a8 commit aef9e6f

File tree

3 files changed

+26
-7
lines changed

3 files changed

+26
-7
lines changed

pipenv/project.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from urllib.parse import unquote, urljoin
1616

1717
from pipenv.utils.constants import VCS_LIST
18-
from pipenv.utils.dependencies import extract_vcs_url
18+
from pipenv.utils.dependencies import extract_vcs_url, normalize_editable_path_for_pip
1919
from pipenv.vendor.tomlkit.items import SingleKey, Table
2020

2121
try:
@@ -1187,9 +1187,14 @@ def generate_package_pipfile_entry(
11871187
if extras:
11881188
entry["extras"] = list(extras)
11891189
if path_specifier:
1190-
entry["file"] = unquote(str(path_specifier))
1191-
if pip_line.startswith("-e"):
1192-
entry["editable"] = True
1190+
editable = pip_line.startswith("-e")
1191+
entry["file"] = unquote(
1192+
normalize_editable_path_for_pip(path_specifier)
1193+
if editable
1194+
else str(path_specifier)
1195+
)
1196+
if editable:
1197+
entry["editable"] = editable
11931198
elif vcs_specifier:
11941199
for vcs in VCS_LIST:
11951200
if vcs in package.link.scheme:

pipenv/routines/install.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
expansive_install_req_from_line,
1414
get_lockfile_section_using_pipfile_category,
1515
install_req_from_pipfile,
16+
normalize_editable_path_for_pip,
1617
)
1718
from pipenv.utils.indexes import get_source_list
1819
from pipenv.utils.internet import download_file, is_valid_url
@@ -44,7 +45,9 @@ def handle_new_packages(
4445
new_packages = []
4546
if packages or editable_packages:
4647

47-
pkg_list = packages + [f"-e {pkg}" for pkg in editable_packages]
48+
pkg_list = packages + [
49+
f"-e {normalize_editable_path_for_pip(pkg)}" for pkg in editable_packages
50+
]
4851

4952
for pkg_line in pkg_list:
5053
console.print(f"Installing {pkg_line}...", style="bold green")
@@ -262,7 +265,11 @@ def do_install(
262265
)
263266
warnings.filterwarnings("ignore", category=ResourceWarning)
264267
packages = packages if packages else []
265-
editable_packages = editable_packages if editable_packages else []
268+
editable_packages = (
269+
[normalize_editable_path_for_pip(p) for p in editable_packages]
270+
if editable_packages
271+
else []
272+
)
266273
package_args = [p for p in packages if p] + [p for p in editable_packages if p]
267274
new_packages = []
268275
if dev and not pipfile_categories:

pipenv/utils/dependencies.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,13 @@ def expansive_install_req_from_line(
10681068
return install_req, name
10691069

10701070

1071+
def normalize_editable_path_for_pip(path_str):
1072+
"""Normalize an editable package path for pip."""
1073+
# Windows paths need to be converted to POSIX paths otherwise path
1074+
# separators (back slashes) are interpreted as escape characters.
1075+
return path_str.replace(os.path.sep, "/")
1076+
1077+
10711078
def file_path_from_pipfile(path_str, pipfile_entry):
10721079
"""Creates an installable file path from a pipfile entry.
10731080
Handles local and remote paths, files and directories;
@@ -1082,7 +1089,7 @@ def file_path_from_pipfile(path_str, pipfile_entry):
10821089
if pipfile_entry.get("extras"):
10831090
req_str = f"{req_str}[{','.join(pipfile_entry['extras'])}]"
10841091
if pipfile_entry.get("editable", False):
1085-
req_str = f"-e {req_str}"
1092+
req_str = f"-e {normalize_editable_path_for_pip(req_str)}"
10861093

10871094
return req_str
10881095

0 commit comments

Comments
 (0)