IronLanguages / ironpython3

Implementation of Python 3.x for .NET Framework that is built on top of the Dynamic Language Runtime.
Apache License 2.0
2.51k stars 290 forks source link

Incorrect value of sys.executable in dotnet tool IronPython.Console #1756

Open BCSharp opened 1 year ago

BCSharp commented 1 year ago

Description

When IronPython console executable is installed as a dotnet tool, the value of sys.executable is path to ipy.dll rather than ipy.exe (or ipy on POSIX systems), which is the executable that has been run. The value of sys.executable is used by some Python code to recursively run Python in a subprocess.

This issue exists regardless whether the tool was installed as global, or into a dedicated directory through --tool-path.

When the tool is installed as a project tool, there is no executable available (as far as I know) and the program has to be invoked through dotnet, e.g. dotnet ipy or dotnet tool run ipy. Hard to say what should be a correct value of sys.executable in such a case. Perhaps IronPython could create a launcher script on the fly and report that script.

Note that the value of sys.path is correct (i.e. it contains subdirectories of the directory containing ipy.dll).

The example below is for Windows, but it is the same behavior on other OSes.

Steps to Reproduce

On Windows + PowerShell:

  1. dotnet tool install ironpython.console --global
  2. ~\.dotnet\tools\ipy.exe -c "import sys; print(sys.executable)"

Expected behavior:

Output: C:\Users\USERNAME\.dotnet\tools\ipy.exe

Actual behavior:

C:\Users\USERNAME\.dotnet\tools\.store\ironpython.console\3.4.1\ironpython.console\3.4.1\tools\net6.0\any\ipy.dll

Version Information

IronPython 3.4.1 (3.4.1.1000) [.NETCoreApp,Version=v6.0 on .NET 6.0.25 (64-bit)]

Microsoft Windows [Version 10.0.22631.2715]

slozier commented 1 year ago

Looks like we could use Environment.ProcessPath on .NET 6 to get the name of the exe. Although it's a bit tricky because someone could call dotnet ipy.dll and then we'll get the dotnet.exe. Also hosted scenarios, .NET Standard, etc...

Just so I don't have to look it up, if needed Process.GetCurrentProcess().MainModule.FileName can be used for pre .NET 6 targets.

BCSharp commented 1 year ago

My primary concern is ipy installed as a dotnet tool (global or via tool-path) or from the zip file (using Install-IronPython.ps1). I envisioned these two cases as canonical ways to install ipy on .NET, and was hoping it could be a drop-in replacement for ipy installed through msiexec or chocolatey.

For other scenarios, like using dotnet directly or hosted runs, I expect the user to be able to wire in a launcher if needed. sys.executable is writable so can be set appropriately manually.

I'm now thinking about the following heuristics to handle the dotnet tool case: if the Environment.ProcessPath executable is in the directory directly above sys.executable, use ProcessPath. This should filter out dotnet.exe and still offer a decent behaviour out of the box.

In case of installations from the zip file, I see that IronPython.Hosting.PythonCommandLine already searches for launchers (called "runners" there) in the directory of ipy.dll and that Install-IronPython.ps1 creates those launchers, so there should be no problem there.