1

I have a Python script with a function I want to execute every X minutes.

from threading import Timer

x = 5

def control():
    Timer(x*60, control).start()
    rest_of_the_function()

control()

When I run the script in its Python virtual environment from the terminal, there is no problem; when I run it as systemd service, it calls control() function just once and then it doesn't do anything else.

If I check the service through systemctl status myservice its Active field says active (running). There are no errors in the log.

The content of the /lib/systemd/system/myservice.service file is the following.

[Unit]
Description=Short description

[Service]
Type=simple
WorkingDirectory=/home/user/myservice
ExecStart=/home/user/myservice/myvenv/bin/python main.py

[Install]
WantedBy=multi-user.target

Why does it just call control() once? Can I fix it?

  • You mean this works when you use python in interactive mode? Where do you call start() on the Timer object? Did you check with systemctl that your python program is actually running and it didn't exit? – sebasth Oct 1 '17 at 16:11
  • Before trying to create a systemd unit, you'll need to edit your program so that it works from the command line without exiting. – Johan Myréen Oct 1 '17 at 16:13
  • @sebasth I run it from bash as python main.py. I missed the start() here but I have it in my script (edited the post). How can I check if the python program is running? Only check I know is through journalctl -u myservice and there is no exit code or anything like that. – VaNa Oct 1 '17 at 16:19
  • @JohanMyréen I think it does not exit (at least not if I run it as mentioned in the previous comment). – VaNa Oct 1 '17 at 16:22
  • You can check the status with systemctl status myservice and look at the Active field. – sebasth Oct 1 '17 at 16:22
3

I think you are probably experiencing a simple buffering problem. If you are looking for messages on stdout, which by default is the journal for systemd units, then you may have to wait a long time for sufficient data to be printed before it gets flushed out.

A simple test for this is to add the -u option to python to make the output unbuffered. Remember to do systemctl daemon-reload after editing the service unit file.

You can also use strace to attach to the running python process. You will presumably find it waiting on a user futex, but after the appropriate time you should see the futex call return, and then entered again.

  • Indeed that was the problem. I haven't known any means to access the process other than what it logs by print(). When I added flush=True to the print arguments it works nicely. Thanks! – VaNa Oct 1 '17 at 18:27
  • @VaNa did you meant that you set flush=True in the systemd service unit ? – Ciasto piekarz Jan 26 '20 at 12:29
  • @Ciastopiekarz No. I had print("something") in the Python code and I just changed it to print("something", flush=True). – VaNa Jan 27 '20 at 8:13
  • Aaah , i have been using logger instead of print so didnt notice – Ciasto piekarz Jan 27 '20 at 8:28
2

When using print() or anything else which writes to a file descriptor (file, pipe, etc), you need to flush the buffers with flush() if you need the output right away.

Alternatively for stdin, stdout and stderr you can use unbuffered mode using -u option for python described in the man page.

  • That was the problem! Now I'm sorry for making such a stupid mistake... – VaNa Oct 1 '17 at 18:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.