Packaged Python application cannot be executed

Hi

I am trying to create a package from an existing Python application such that it can be executed as a service in the background. To do this I have followed the instructions Package and development shell for a python project. The setup.py contains:

#!/usr/bin/env python

from setuptools import setup, find_packages

setup(name='Python 3D Viewer',
      version='1.0',
      # Modules to import from other scripts:
      packages=find_packages(),
      # Executables
      scripts=["myproject.py"],
)

The default.nix looks like this:

{ lib, python3Packages }:

with python3Packages;

buildPythonApplication {
  pname = "Python 3D Viewer";
  version = "1.0";

  src = builtins.path {
    name = "myPythonProject";
    path = ./.;
  };

  propagatedBuildInputs = [ mysql-connector pythonocc-core six flask werkzeug pycryptodome ];
}

The host.nix, which is called from the configuration.nix, contains:

...
let
  ...
  myPythonPackage = pkgs.callPackage /var/www/python_code {};
in {
  ...
  systemd.services.python3DViewer = {
    enable = true;
    description = "Python 3D Viewer";
    serviceConfig = {
      ExecStart = "${myPythonPackage}/bin/myproject.py";
      #Restart = "on-failure";
      #RestartSec = "1s";
    };
    wantedBy = ["multi-user.target"];
  };

When I execute a nixos-rebuild switch then the Python code is added to the nix store as a package. However, when I try to run this package then the application crashes and the output looks like:

File "/nix/store/dkna9ym9h2g1069nx4pqjmlznn2bdlrq-Python-3D-Viewer-1.0/bin/myproject.py", line 2
    PATH=${PATH:+':'$PATH':'}
         ^
SyntaxError: invalid syntax

If I change the myproject.py to a simple Hello World then everything works. I would assume that it has something to do with the Python code itself but I don’t know how to start debugging this. Any help is much appreciated!

Have a look at this file (e.g. with less).
This is not python code, but a small shell script that sets up the required path, then calls python and passes the actual script as an argument.
So if you do

python /nix/store/dkna9ym9h2g1069nx4pqjmlznn2bdlrq-Python-3D-Viewer-1.0/bin/myproject.py

you pass a shell script to the Python interpreter, resulting in the Syntax error.
Run it as

/nix/store/dkna9ym9h2g1069nx4pqjmlznn2bdlrq-Python-3D-Viewer-1.0/bin/myproject.py

like you do in the ExecStart line of your service.

I had already executed it as a shell script. My apologies for not making this clear. The output that I shared was only a part of the total output. The entire output looks like this:

* Serving Flask app '.myproject'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.1.248:5000
Press CTRL+C to quit
 * Restarting with watchdog (inotify)
  File "/nix/store/dkna9ym9h2g1069nx4pqjmlznn2bdlrq-Python-3D-Viewer-1.0/bin/myproject.py", line 2
    PATH=${PATH:+':'$PATH':'}
         ^
SyntaxError: invalid syntax

While the output should look like this:

* Serving Flask app 'myproject'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.1.248:5000
Press CTRL+C to quit
 * Restarting with watchdog (inotify)
 * Debugger is active!
 * Debugger PIN: 268-310-649

I don’t understand why I get the syntax error. Especially because a simple Hello World does work.

My guess is that Flask is reloading this file in the line “Restarting with watchdog” (presumably to support hot-reloading) but catches the wrapper-script, not the actual Python code. Since you run out of the Nix Store anyways, there is not much sense in having hot reloading enabled, maybe you can try if disabling it solves your issue. Can’t give concrete hints on how to disable this since I don’t know you code, but this page has some good hints.

1 Like

Thank you so much! Disabling the auto reloading fixed the issue. To disable it you can add the parameter use_reloader=False to the parameter list of the run function. In my case it looks like this:

app.run(port=5000,host='0.0.0.0', debug=True, use_reloader=False)
1 Like