From 9ee7f4056c2741b624f190ceb423373307bb90a0 Mon Sep 17 00:00:00 2001
From: spoorn <spookump@gmail.com>
Date: Wed, 15 Jun 2022 01:31:51 -0700
Subject: [PATCH] Resolve symlinks in locker.lock path to real physical paths

Fixes https://github.com/python-poetry/poetry/issues/5849
---
 src/poetry/packages/locker.py |  3 +-
 tests/packages/test_locker.py | 75 +++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py
index e57e60b83b7..37f4afe29f1 100644
--- a/src/poetry/packages/locker.py
+++ b/src/poetry/packages/locker.py
@@ -603,7 +603,8 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
                 # The lock file should only store paths relative to the root project
                 url = Path(
                     os.path.relpath(
-                        Path(url).as_posix(), self._lock.path.parent.as_posix()
+                        Path(url).resolve().as_posix(),
+                        Path(self._lock.path.parent).resolve().as_posix(),
                     )
                 ).as_posix()
 
diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py
index 0e79817d4f3..9219425df96 100644
--- a/tests/packages/test_locker.py
+++ b/tests/packages/test_locker.py
@@ -2,6 +2,7 @@
 
 import json
 import logging
+import os
 import tempfile
 import uuid
 
@@ -753,3 +754,77 @@ def test_content_hash_with_legacy_is_compatible(
     content_hash = locker._get_content_hash()
 
     assert (content_hash == old_content_hash) or fresh
+
+
+def test_lock_file_resolves_file_url_symlinks(root: ProjectPackage):
+    """
+    Create directories and file structure as follows:
+
+    d1/
+    d1/testsymlink -> d1/d2/d3
+    d1/d2/d3/lock_file
+    d1/d4/source_file
+
+    Using the testsymlink as the Locker.lock file path should correctly resolve to
+    the real physical path of the source_file when calculating the relative path
+    from the lock_file, i.e. "../../d4/source_file" instead of the unresolved path
+    from the symlink itself which would have been "../d4/source_file"
+
+    See https://github.com/python-poetry/poetry/issues/5849
+    """
+    with tempfile.TemporaryDirectory() as d1:
+        symlink_path = Path(d1).joinpath("testsymlink")
+        with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirectory(
+            dir=d1
+        ) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTemporaryFile(
+            dir=d4
+        ) as source_file, tempfile.NamedTemporaryFile(
+            dir=d3
+        ) as lock_file:
+            lock_file.close()
+            os.symlink(Path(d3), symlink_path)
+            locker = Locker(str(symlink_path) + os.sep + Path(lock_file.name).name, {})
+
+            package_local = Package(
+                "local-package",
+                "1.2.3",
+                source_type="file",
+                source_url=source_file.name,
+                source_reference="develop",
+                source_resolved_reference="123456",
+            )
+            packages = [
+                package_local,
+            ]
+
+            locker.set_lock_data(root, packages)
+
+            with locker.lock.open(encoding="utf-8") as f:
+                content = f.read()
+
+            expected = f"""\
+[[package]]
+name = "local-package"
+version = "1.2.3"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.source]
+type = "file"
+url = "{Path(os.path.relpath(Path(source_file.name).resolve().as_posix(),
+         Path(Path(lock_file.name).parent).resolve().as_posix())).as_posix()}"
+reference = "develop"
+resolved_reference = "123456"
+
+[metadata]
+lock-version = "1.1"
+python-versions = "*"
+content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
+
+[metadata.files]
+local-package = []
+"""
+
+            assert content == expected