0

I’m looking for some advice about the best way to distribute Python.Net with my application. It seems that I have two options:

1. My application installer runs pip install pythonnet

The downside is that the customer needs to have internet access at the time of the install.

2. Bundle the Python.Net files with my application to allow offline installation

I like the idea of a ‘standalone’ installer that can be run offline, however is this a good idea? It needs to install the correct version of Python.Net that matches the version of Python that the customer has installed.

I tried the following steps:

C:\mkdir download pythonnet
cd download pythonnet
pip download pythonnet     <-- this gets two files: pythonnet-2.5.2.tar.gz and pycparser-2.20-py2.py3-none-any.whl

<disconnect from the internet>

pip install C:\pythonnet_download\pythonnet-2.5.2.tar.gz

However, this gives the error:

Processing c:\pythonnet_download\pythonnet-2.5.2.tar.gz
Requirement already satisfied: pycparser in c:\program files\python39\lib\site-packages (from pythonnet==2.5.2) (2.20)
Building wheels for collected packages: pythonnet
  Building wheel for pythonnet (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: 'c:\program files\python39\python.exe' -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\vincluff\\AppData\\Local\\Temp\\pip-req-build-bgue1v76\\setup.py'"'"'; __file__='"'"'C:\\Users\\vincluff\\AppData\\Local\\Temp\\pip-req-build-bgue1v76\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d 'C:\Users\vincluff\AppData\Local\Temp\pip-wheel-s9qap8ze'
       cwd: C:\Users\vincluff\AppData\Local\Temp\pip-req-build-bgue1v76\
  Complete output (44 lines):
  running bdist_wheel
  running build
  running build_ext
  Checking for updates from https://www.nuget.org/api/v2/.
  The remote name could not be resolved: 'www.nuget.org'
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "C:\Users\vincluff\AppData\Local\Temp\pip-req-build-bgue1v76\setup.py", line 630, in <module>
      setup(
    File "c:\program files\python39\lib\site-packages\setuptools\__init__.py", line 165, in setup
      return distutils.core.setup(**attrs)
    File "c:\program files\python39\lib\distutils\core.py", line 148, in setup
      dist.run_commands()
    File "c:\program files\python39\lib\distutils\dist.py", line 966, in run_commands
      self.run_command(cmd)
    File "c:\program files\python39\lib\distutils\dist.py", line 985, in run_command
      cmd_obj.run()
    File "C:\Users\vincluff\AppData\Local\Temp\pip-req-build-bgue1v76\setup.py", line 612, in run
      return bdist_wheel.bdist_wheel.run(self)
    File "C:\Users\vincluff\AppData\Roaming\Python\Python39\site-packages\wheel\bdist_wheel.py", line 290, in run
      self.run_command('build')
    File "c:\program files\python39\lib\distutils\cmd.py", line 313, in run_command
      self.distribution.run_command(command)
    File "c:\program files\python39\lib\distutils\dist.py", line 985, in run_command
      cmd_obj.run()
    File "c:\program files\python39\lib\distutils\command\build.py", line 135, in run
      self.run_command(cmd_name)
    File "c:\program files\python39\lib\distutils\cmd.py", line 313, in run_command
      self.distribution.run_command(command)
    File "c:\program files\python39\lib\distutils\dist.py", line 985, in run_command
      cmd_obj.run()
    File "c:\program files\python39\lib\distutils\command\build_ext.py", line 340, in run
      self.build_extensions()
    File "c:\program files\python39\lib\distutils\command\build_ext.py", line 449, in build_extensions
      self._build_extensions_serial()
    File "c:\program files\python39\lib\distutils\command\build_ext.py", line 474, in _build_extensions_serial
      self.build_extension(ext)
    File "C:\Users\vincluff\AppData\Local\Temp\pip-req-build-bgue1v76\setup.py", line 249, in build_extension
      self._install_packages()
    File "C:\Users\vincluff\AppData\Local\Temp\pip-req-build-bgue1v76\setup.py", line 438, in _install_packages
      subprocess.check_call(cmd, shell=use_shell)
    File "c:\program files\python39\lib\subprocess.py", line 373, in check_call
      raise CalledProcessError(retcode, cmd)
  subprocess.CalledProcessError: Command 'tools\nuget\nuget.exe update -self' returned non-zero exit status 1.
  ----------------------------------------
  ERROR: Failed building wheel for pythonnet
  Running setup.py clean for pythonnet
Failed to build pythonnet
Installing collected packages: pythonnet
    Running setup.py install for pythonnet ... error
    ERROR: Command errored out with exit status 1:
     command: 'c:\program files\python39\python.exe' -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\vincluff\\AppData\\Local\\Temp\\pip-req-build-bgue1v76\\setup.py'"'"'; __file__='"'"'C:\\Users\\vincluff\\AppData\\Local\\Temp\\pip-req-build-bgue1v76\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record 'C:\Users\vincluff\AppData\Local\Temp\pip-record-lcx9op7p\install-record.txt' --single-version-externally-managed --compile --install-headers 'c:\program files\python39\Include\pythonnet'
         cwd: C:\Users\vincluff\AppData\Local\Temp\pip-req-build-bgue1v76\
    Complete output (6 lines):
    usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
       or: setup.py --help [cmd1 cmd2 ...]
       or: setup.py --help-commands
       or: setup.py cmd --help

    error: option --single-version-externally-managed not recognized
    ----------------------------------------
ERROR: Command errored out with exit status 1: 'c:\program files\python39\python.exe' -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\vincluff\\AppData\\Local\\Temp\\pip-req-build-bgue1v76\\setup.py'"'"'; __file__='"'"'C:\\Users\\vincluff\\AppData\\Local\\Temp\\pip-req-build-bgue1v76\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record 'C:\Users\vincluff\AppData\Local\Temp\pip-record-lcx9op7p\install-record.txt' --single-version-externally-managed --compile --install-headers 'c:\program files\python39\Include\pythonnet' Check the logs for full command output
  • Why do the above steps fail?
  • If I get this working, will it allow the creation of a Python.Net wheel which matches the installed Python version?

Note: I was able to get offline install working with the following steps but this is using a wheel that is specific to my installed version of Python. I want it to work with whatever Python version the customer has.

pip install pythonnet

< search in C:\Users\vince\AppData\Local\pip\cache\wheels for pythonnet-2.5.2-cp39-cp39-win_amd64.whl and copy it to C:\copy_of_pythonnet_wheel >

pip uninstall pythonnet
pip install C:\copy_of_pythonnet_wheel\pythonnet-2.5.2-cp39-cp39-win_amd64.whl

Related question about Python.Runtime.dll:

If I go with an online installer (pip install pythonnet), it places Python.Runtime.dll in the site-packages directory, e.g: C:\Program Files\Python39\Lib\site-packages. Should my .Net application load this Python.Runtime.dll, or should it use the Python.Runtime.dll that I compiled my .Net project against, and so is already installed in C:\Program Files\MyDotNetExeLocation ?

If the former, how do I specify the location of Python.Runtime.dll? The location depends on where the user decides to install Python so I don’t know where to find it. One solution I can think of: After my installer has installed Python.Net, it checks location of site-packages using:

py.exe -c "import site; print(next(item for item in site.getsitepackages() if 'site-packages' in item))"

And then copies Python.Runtime.dll from this directory into C:\Program Files\MyDotNetExeLocation

Vince
  • 25
  • 6

1 Answers1

0

The upcoming Python.NET 3.0 won't need to be installed using pip to enable embedding of Python into .NET projects. You can build it from GitHub src/runtime directory or get the latest monthly preview build from NuGet.org.

Your app will need to set Runtime.PythonDLL to the full path of python39.dll and PythonEngine.PythonHome to Python home directory (on Windows this is usually the directory where python39.dll is located).

LOST
  • 2,956
  • 3
  • 25
  • 40
  • Thanks @LOST. That's good to know about the upcoming changes in Python.NET 3.0. Until v3.0 is release ready, I will continue to use v2.5.2. With regards to my question 1: Should my application perform online or offline installation of Python.Net: I found the following question which concludes that it's not possible to perform offline installation of Python.Net (without hacks): https://stackoverflow.com/questions/60116746/installing-pythonnet-with-no-internet-access Are there any plans to allow offline install? – Vince Feb 19 '21 at 17:51
  • About my question: Question about Python.Runtime.dll (Using Python.NET v2.5.2) I don't know which version of Python3 that my customer has. Is it safe for my application to bundle a copy of Python.Runtime.dll that I got from running pip install on Python 3.9? The dll that I got from pip install on Python 3.9 is different to the dll got from pip install on Python 3.8. However I ran a test where my .Net application uses the dll from pip install on Python 3.9, and then ran my Python application on Python 3.8 and the two applications communicated with each other. So am I'm worrying unnecessarily? – Vince Feb 19 '21 at 17:53
  • @Vince prior to 3.0 DLLs built for different Python versions are incompatible. I suspect that you go lucky somehow. – LOST Feb 19 '21 at 22:42
  • @Vince wheel should be ok for offline install. In 3.0 we will remove the need to run nuget. – LOST Feb 19 '21 at 22:43
  • Thanks for the help @LOST. Marking this as solved. – Vince Feb 22 '21 at 10:30