it-swarm.com.de

Verwenden von setup.py zum Installieren von python project als systemd-Dienst

Ich habe ein Python-Projekt und möchte es mit etwas wie python setup.py install installieren können, damit bei der Installation automatisch ein systemd-Dienst erstellt wird.

Ich habe Probleme, höchstwahrscheinlich die Pfade richtig einstellen oder importieren.

Meine Umgebung:

  • Ubuntu 15.04
  • Python 2.7 (obwohl es toll wäre, es auch in py3 zum Laufen zu bringen).

Projektstruktur:

+ top-folder
  + super_project
    + folder1
      __init__.py
      file1.py
    + folder2
      __init__.py
      file2.py
    __init__.py
    main.py
  setup.py
  setup.cfg

setup.py:

from setuptools.command.install import install
from setuptools import setup, find_packages
import subprocess
import os


class CustomInstallCommand(install):

  def run(self):
    install.run(self)
    current_dir_path = os.path.dirname(os.path.realpath(__file__))
    create_service_script_path = os.path.join(current_dir_path, 'super_project', 'install_scripts', 'create_service.sh')
    subprocess.check_output([create_service_script_path])

setup(
  name='super-project',
  author='Myself',
  version='0.0.1',
  description='My Description',
  packages=find_packages(exclude=['contrib', 'docs']),
  # this will create the /usr/local/bin/super-project entrypoint script
  entry_points={
    'console_scripts': [
      'super-project = super_project.main:main'
    ]
  },
  cmdclass={'install': CustomInstallCommand}
)

main.py

from super_project.folder1.file1 import Class1
from super_project.folder2.file2 import Class2
import logging


def main():
  logging.info('Executing super-project...')
  (...)
  logging.info('super-project execution finished.')

if __== '__main__':
  main()

setup.cfg

[bdist_wheel]
universal=1

create_service.sh (mehr oder weniger):

SYSTEMD_SCRIPT_DIR=$( cd  $(dirname "${BASH_SOURCE:=$0}") && pwd)
cp -f "$SYSTEMD_SCRIPT_DIR/super-project.service" /lib/systemd/system
chown root:root /lib/systemd/system/super-project.service

systemctl daemon-reload
systemctl enable super-project.service

super-Projekt.Service

[Unit]
Description=Super Description

[Service]
Type=simple
ExecStart=/usr/local/bin/super-service
Restart=always

[Install]
WantedBy=multi-user.target

Die Installation des Pakets generiert die folgende Ausgabe:

$ Sudo python setup.py install --record files.txt
running install
running build
running build_py
copying super_project/main.py - build/lib.linux-x86_64-2.7/super_project
running install_lib
copying build/lib.linux-x86_64-2.7/super_project/__init__.py - /usr/local/lib/python2.7/dist-packages/super_project
copying build/lib.linux-x86_64-2.7/super_project/main.py - /usr/local/lib/python2.7/dist-packages/super_project
copying build/lib.linux-x86_64-2.7/super_project/db/__init__.py - /usr/local/lib/python2.7/dist-packages/super_project/db
copying build/lib.linux-x86_64-2.7/super_project/db/db_gateway.py - /usr/local/lib/python2.7/dist-packages/super_project/db
(...)
byte-compiling /usr/local/lib/python2.7/dist-packages/super_project/__init__.py to
__init__.pyc
byte-compiling /usr/local/lib/python2.7/dist-packages/super_project/main.py to
main.pyc
byte-compiling /usr/local/lib/python2.7/dist-packages/super_project/db/__init__.py to
__init__.pyc
byte-compiling /usr/local/lib/python2.7/dist-packages/super_project/db/db_gateway.py
to db_gateway.pyc
(...)
running install_Egg_info
running Egg_info
writing requirements to super_project.Egg-info/requires.txt
writing super_project.Egg-info/PKG-INFO
writing top-level names to super_project.Egg-info/top_level.txt
writing dependency_links to super_project.Egg-info/dependency_links.txt
writing entry points to super_project.Egg-info/entry_points.txt
reading manifest file 'super_project.Egg-info/SOURCES.txt'
writing manifest file 'super_project.Egg-info/SOURCES.txt'
Copying super_project.Egg-info to /usr/local/lib/python2.7/dist-packages/super_project-0.0.1.Egg-info
running install_scripts
Installing ai-scenario-qa script to /usr/local/bin
writing list of installed files to 'files.txt'

Die super-project-Datei wird in/usr/local/bin erstellt:

#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'super-project==0.0.1','console_scripts','super-project'
__requires__ = 'super-project==0.0.1'
import sys
from pkg_resources import load_entry_point

if __== '__main__':
    sys.exit(
        load_entry_point('super-project==0.0.1', 'console_scripts', 'super-project')()
    )

Die Installation scheint erfolgreich zu sein, obwohl:

$ systemctl status super-project.service
● super-project.service
   Loaded: not-found (Reason: No such file or directory)
   Active: inactive (dead)

Den Fehler kann ich in/var/log/syslog sehen:

 Feb 16 20:48:34  systemd[1]: Starting  Super Description...
 Feb 16 20:48:34  super-project[22517]: Traceback (most recent call last):
 Feb 16 20:48:34  super-project[22517]: File "/usr/local/bin/super-project", line 9, in <module
 Feb 16 20:48:34  super-project[22517]: load_entry_point('super-project==0.0.1', 'console_scripts', 'super-project')()
 Feb 16 20:48:34  super-project[22517]: File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 521, in load_entry_point
 Feb 16 20:48:34  super-project[22517]: return get_distribution(dist).load_entry_point(group, name)
 Feb 16 20:48:34  super-project[22517]: File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2632, in load_entry_point
 Feb 16 20:48:34  super-project[22517]: return ep.load()
 Feb 16 20:48:34  super-project[22517]: File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2312, in load
 Feb 16 20:48:34  super-project[22517]: return self.resolve()
 Feb 16 20:48:34  super-project[22517]: File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2318, in resolve
 Feb 16 20:48:34  super-project[22517]: module = __import__(self.module_name, fromlist=['__name__'], level=0)
 Feb 16 20:48:34  super-project[22517]: ImportError: No module named main
 Feb 16 20:48:34  systemd[1]: super-project.service: main process exited, code=exited, status=1/FLURE
 Feb 16 20:48:34  systemd[1]: Unit super-project.service entered fled state.
 Feb 16 20:48:34  systemd[1]: super-project.service failed.
 Feb 16 20:48:34  systemd[1]: super-project.service holdoff time over, scheduling restart.
 Feb 16 20:48:34  systemd[1]: start request repeated too quickly for super-project.service
 Feb 16 20:48:34  systemd[1]: Failed to start Super Description.
 Feb 16 20:48:34  systemd[1]: Unit super-project.service entered fled state.
 Feb 16 20:48:34  systemd[1]: super-project.service failed.

Wie zu sehen ist, kann das Modul main nicht gefunden werden . Das ist das Hauptproblem.

Beim Ändern von Code/Conf entferne ich das Superprojekt/den Dienst wie folgt:

$ Sudo systemctl disable super-project.service
$ Sudo rm -f /lib/systemd/system/super-project.service
$ Sudo systemctl daemon-reload
$ su
# cat files.txt | xargs rm -r

Auf der anderen Seite:

  • Wenn ich $ super-project von /usr/local/bin/ ausführe, wird das Skript ordnungsgemäß gestartet (keine Importausnahme), die Konfigurationsdateien können jedoch nicht gelesen werden (höchstwahrscheinlich aufgrund relativer/absoluter Pfadprobleme).
  • Wenn ich $ super-project von top-folder (Ordner, der den Projektcode/die Dateien enthält) ausführen, läuft das Skript einwandfrei

Was vermisse ich? Ich habe viel Zeit damit verbracht, nach dem Problem zu suchen. Es scheint, dass das Paket im Verzeichnis dist-packages richtig eingerichtet ist und alle Servicedateien korrekt erstellt werden, sobald das Setup ausgeführt wird.

Ich habe Dinge über die Verwendung von from __future__ import absolute_import gelesen, aber ich bin nicht sicher, ob ich das zu meinem main.py hinzufügen muss (es funktioniert nicht) oder zu allen Dateien in meinem Projekt.

24
newlog

Stellen Sie sicher, dass Ihre Anwendung von anderen Verzeichnissen aus ausgeführt werden kann. Dies scheint ein klassischer Fall zu sein, in dem Sie davon ausgehen, dass das aktuelle Verzeichnis das Startskript ist.

Es hat nichts mit systemd zu tun. Versuchen Sie auch, den Startbefehl außerhalb Ihrer Login-Shell auszuführen (Ihr .profile wird nicht von Diensten geladen).

1
sorin

Ich benutze für alle meine Projekte einen anderen Ansatz, der am besten zu tun ist, indem ich kurz beschreibt, wie ich das mache.
Achtung: Ich arbeite ausschließlich an Linux, ich weiß nichts über Öffnungen in den mit Glas oder runden Früchten ausgestatteten Wänden ... :)

Erstellen Sie ein Skript wie
Datei : scripts/super-project.bash_completion 

eval "$(register-python-argcomplete super-project)"

Fügen Sie dann in der MANIFEST.in ein Include hinzu 

include scripts/super-project.bash_completion

in der Regel ist die "Superprojekt" -Datei (ohne ext) ein Einstiegspunkt auf diese Weise:
Datei: bin/Superprojekt 

#!/usr/bin/env python2.7
from super_project.main import main

if __== '__main__':
    main()
else:
    raise NotImplementedError

Ich denke, dieses bin/super-Projekt kann verwendet werden, um den Einstiegspunkt zu laden.

Grüße.

0
LittleEaster

Sie erhalten eine ImportError, da sich das betreffende Modul aufgrund einiger Dateisystemberechtigungen nicht im sys.path befindet oder nicht zugänglich ist.
Hier ist ein Skript zum Überprüfen der Dateisystemberechtigungen einer bestimmten Verteilung, Gruppe und eines bestimmten Namens. 

chk_perm.py

from pkg_resources import get_distribution
import os
import sys

dist, group, name = sys.argv[1:]
dist = get_distribution(dist)
location = dist.location
einfo = dist.get_entry_info(group, name)
if not einfo:
    print('No such group "{}" or name "{}"'.format(group, name))
    sys.exit(1)
m_name = einfo.module_name
path = format(os.path.join(location, *m_name.split('.')))
path = path if os.access(path, os.F_OK) else '{}.py'.format(path)
print('If path "{}" exists: {}'.format(path, os.access(path, os.F_OK) if path.endswith('.py') else True))
print('If path "{}" readable: {}'.format(path, os.access(path, os.R_OK)))

Prüfung;

$ python chk_perm.py setuptools console_scripts easy_install
If path "lib/python2.7/site-packages/setuptools/command/easy_install.py" exists: True
If path "lib/python2.7/site-packages/setuptools/command/easy_install.py" readable: True

$ foo
Traceback (most recent call last):
  File "bin/foo", line 9, in <module>
    load_entry_point('mypkg==0.0.4', 'console_scripts', 'foo')()
  File "lib/python2.7/site-packages/pkg_resources/__init__.py", line 549, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "lib/python2.7/site-packages/pkg_resources/__init__.py", line 2542, in load_entry_point
    return ep.load()
  File "lib/python2.7/site-packages/pkg_resources/__init__.py", line 2202, in load
    return self.resolve()
  File "lib/python2.7/site-packages/pkg_resources/__init__.py", line 2208, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
ImportError: No module named main

$ python chk_perm.py mypkg console_scripts foo
If path "lib/python2.7/site-packages/pkg/main.py" exists: True
If path "lib/python2.7/site-packages/pkg/main.py" readable: False

$ ls -l lib/python2.7/site-packages/pkg/main.py 
-rw-rw---- 1 root root 104 Mar  6 22:52 lib/python2.7/site-packages/pkg/main.py

$ Sudo chmod o+r lib/python2.7/site-packages/pkg/main.py
$ ls -l lib/python2.7/site-packages/pkg/main.py 
-rw-rw-r-- 1 root root 104 Mar  6 22:52 lib/python2.7/site-packages/pkg/main.py

$ python chk_perm.py mypkg console_scripts foo
If path "lib/python2.7/site-packages/pkg/main.py" exists: True
If path "lib/python2.7/site-packages/pkg/main.py" readable: True

$ foo
App is running
0
Nizam Mohamed