Skip to content

Commit 62701fe

Browse files
authored
Resolve symlinks in locker.lock path to real physical paths (#5850)
Fixes #5849
1 parent 83e6c31 commit 62701fe

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

src/poetry/packages/locker.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,8 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
602602
# The lock file should only store paths relative to the root project
603603
url = Path(
604604
os.path.relpath(
605-
Path(url).as_posix(), self._lock.path.parent.as_posix()
605+
Path(url).resolve().as_posix(),
606+
Path(self._lock.path.parent).resolve().as_posix(),
606607
)
607608
).as_posix()
608609

tests/packages/test_locker.py

+90
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import json
44
import logging
5+
import os
6+
import sys
57
import tempfile
68
import uuid
79

@@ -753,3 +755,91 @@ def test_content_hash_with_legacy_is_compatible(
753755
content_hash = locker._get_content_hash()
754756

755757
assert (content_hash == old_content_hash) or fresh
758+
759+
760+
def test_lock_file_resolves_file_url_symlinks(root: ProjectPackage):
761+
"""
762+
Create directories and file structure as follows:
763+
764+
d1/
765+
d1/testsymlink -> d1/d2/d3
766+
d1/d2/d3/lock_file
767+
d1/d4/source_file
768+
769+
Using the testsymlink as the Locker.lock file path should correctly resolve to
770+
the real physical path of the source_file when calculating the relative path
771+
from the lock_file, i.e. "../../d4/source_file" instead of the unresolved path
772+
from the symlink itself which would have been "../d4/source_file"
773+
774+
See https://github.com/python-poetry/poetry/issues/5849
775+
"""
776+
with tempfile.TemporaryDirectory() as d1:
777+
symlink_path = Path(d1).joinpath("testsymlink")
778+
with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirectory(
779+
dir=d1
780+
) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTemporaryFile(
781+
dir=d4
782+
) as source_file, tempfile.NamedTemporaryFile(
783+
dir=d3
784+
) as lock_file:
785+
lock_file.close()
786+
try:
787+
os.symlink(Path(d3), symlink_path)
788+
except OSError:
789+
if sys.platform == "win32":
790+
# os.symlink requires either administrative privileges or developer
791+
# mode on Win10, throwing an OSError if neither is active.
792+
# Test is not possible in that case.
793+
return
794+
raise
795+
locker = Locker(str(symlink_path) + os.sep + Path(lock_file.name).name, {})
796+
797+
package_local = Package(
798+
"local-package",
799+
"1.2.3",
800+
source_type="file",
801+
source_url=source_file.name,
802+
source_reference="develop",
803+
source_resolved_reference="123456",
804+
)
805+
packages = [
806+
package_local,
807+
]
808+
809+
locker.set_lock_data(root, packages)
810+
811+
with locker.lock.open(encoding="utf-8") as f:
812+
content = f.read()
813+
814+
expected = f"""\
815+
[[package]]
816+
name = "local-package"
817+
version = "1.2.3"
818+
description = ""
819+
category = "main"
820+
optional = false
821+
python-versions = "*"
822+
823+
[package.source]
824+
type = "file"
825+
url = "{
826+
Path(
827+
os.path.relpath(
828+
Path(source_file.name).resolve().as_posix(),
829+
Path(Path(lock_file.name).parent).resolve().as_posix(),
830+
)
831+
).as_posix()
832+
}"
833+
reference = "develop"
834+
resolved_reference = "123456"
835+
836+
[metadata]
837+
lock-version = "1.1"
838+
python-versions = "*"
839+
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
840+
841+
[metadata.files]
842+
local-package = []
843+
"""
844+
845+
assert content == expected

0 commit comments

Comments
 (0)