Repository: pdm-project/pdm
Pull request #3279 (2024-11-12 20:00:04)

feat: pass original working directory as env variable to pdm scripts
## Pull Request Checklist

- [X] A news fragment is added in `news/` describing what is new.
- [X] Test cases added for changed code.

## Describe what you have changed in this PR.

Implements #3179 

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pdm-project/pdm/pull/3279?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 85.29%. Comparing base [(`96c59f8`)](https://app.codecov.io/gh/pdm-project/pdm/commit/96c59f85723080eaa50725e17d4446bd53d9efc7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) to head [(`b548104`)](https://app.codecov.io/gh/pdm-project/pdm/commit/b548104810ae8cbf09edd93d1d52fbecfee4f9dc?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #3279      +/-   ##
==========================================
- Coverage   85.30%   85.29%   -0.01%     
==========================================
  Files         112      112              
  Lines       11399    11401       +2     
  Branches     2487     2487              
==========================================
+ Hits         9724     9725       +1     
- Misses       1146     1147       +1     
  Partials      529      529              
```

| [Flag](https://app.codecov.io/gh/pdm-project/pdm/pull/3279/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/pdm-project/pdm/pull/3279/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | `85.09% <100.00%> (-0.01%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pdm-project/pdm/pull/3279?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).


Patch:
diff --git a/docs/usage/scripts.md b/docs/usage/scripts.md
index 75693731..7290f011 100644
--- a/docs/usage/scripts.md
+++ b/docs/usage/scripts.md
@@ -284,6 +284,10 @@ start.working_dir = "subdir"
 
 Relative paths are resolved against the project root.
 
++++ 2.20.2
+
+To identify the original calling working directory, each script gets the environment variable `PDM_RUN_CWD` injected.
+
 ### `site_packages`
 
 To make sure the running environment is properly isolated from the outer Python interpreter,
diff --git a/news/3179.feat.md b/news/3179.feat.md
new file mode 100644
index 00000000..5d068992
--- /dev/null
+++ b/news/3179.feat.md
@@ -0,0 +1,1 @@
+Pass original working directory as env variable to pdm scripts
\ No newline at end of file
diff --git a/src/pdm/cli/commands/run.py b/src/pdm/cli/commands/run.py
index 4c013077..2fe9b063 100644
--- a/src/pdm/cli/commands/run.py
+++ b/src/pdm/cli/commands/run.py
@@ -290,9 +290,11 @@ class TaskRunner:
                 signum = signal.SIGTERM
             process.send_signal(signum)
 
+        process_env = os.environ.copy()
+        process_env.update({"PDM_RUN_CWD": str(Path.cwd())})
         handle_term = signal.signal(signal.SIGTERM, forward_signal)
         handle_int = signal.signal(signal.SIGINT, forward_signal)
-        process = subprocess.Popen(process_cmd, cwd=cwd, shell=shell, bufsize=0, close_fds=False)
+        process = subprocess.Popen(process_cmd, cwd=cwd, shell=shell, bufsize=0, close_fds=False, env=process_env)
         retcode = process.wait()
         signal.signal(signal.SIGTERM, handle_term)
         signal.signal(signal.SIGINT, handle_int)


Test patch:
diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py
index dec6cb04..b3eceab3 100644
--- a/tests/cli/test_run.py
+++ b/tests/cli/test_run.py
@@ -995,3 +995,36 @@ def test_run_script_with_inline_metadata(project, pdm, local_finder, local_finde
     with cd(project.root):
         result = pdm(["run", "test_script.py"], obj=project)
         assert result.exit_code == 0
+
+
+def test_run_script_pass_run_cwd(project, pdm, capfd):
+    project.pyproject.settings["scripts"] = {
+        "test_script": [
+            "python",
+            "-c",
+            "import os;print(os.getenv('PDM_RUN_CWD'))",
+        ]
+    }
+    project.pyproject.write()
+    capfd.readouterr()
+    result = pdm(["run", "test_script"], obj=project)
+    assert result.exit_code == 0
+    out, _ = capfd.readouterr()
+    assert Path(out.strip()) == Path.cwd()
+
+
+def test_run_script_pass_run_cwd_to_original_working_dir_when_working_dir_of_script_is_changed(project, pdm, capfd):
+    project.root.joinpath("subdir").mkdir()
+    project.root.joinpath("subdir", "test_script.py").write_text(
+        textwrap.dedent("""\
+           import os
+           print(os.getenv('PDM_RUN_CWD', ''))
+        """)
+    )
+    project.pyproject.settings["scripts"] = {
+        "test_script": {"working_dir": "subdir", "cmd": "python test_script.py"},
+    }
+    project.pyproject.write()
+    capfd.readouterr()
+    pdm(["run", "test_script"], obj=project, strict=True)
+    assert capfd.readouterr()[0].strip() == str(Path.cwd())