Utilisation setup.py pour installer le projet python comme un service systemd

j'ai un projet python et je veux pouvoir l'installer en utilisant quelque chose comme python setup.py install de sorte que l'installation crée automatiquement un service systemd.

j'ai quelques problèmes, probablement pour définir les chemins ou les importations correctement.

Mon environnement:

  • Ubuntu 15.04
  • Python 2.7 (bien qu'il serait génial de le faire fonctionner dans py3 aussi).

Projet Structure:

+ 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 __name__ == '__main__':
  main()

le programme d'installation.cfg

[bdist_wheel]
universal=1

create_service.sh (plus ou moins):

SYSTEMD_SCRIPT_DIR=$( cd  $(dirname "${BASH_SOURCE:=}") && 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 projet.service

[Unit]
Description=Super Description

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

[Install]
WantedBy=multi-user.target

l'installation du paquet génère la sortie suivante:

$ 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'

super-project le fichier est créé dans /usr/local/bin:

#!/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 __name__ == '__main__':
    sys.exit(
        load_entry_point('super-project==0.0.1', 'console_scripts', 'super-project')()
    )

L'installation semble réussie, bien que:

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

l'erreur que je vois dans / var/log / syslog:

 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.

Comme on peut le voir, le module main ne peut pas être trouvé. C'est le principal problème.

en changeant le code / conf, je supprime le super-projet / service comme suit:

$ 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

d'autre part:

  • si j'exécute $ super-project/usr/local/bin/, le script démarre correctement (AUCUNE EXCEPTION d'importation) mais les fichiers de configuration ne peuvent pas être lu (très probablement en raison de problèmes relatifs/absolus de chemin).
  • si j'exécute $ super-projecttop-folder (dossier contenant le code/les fichiers du projet) le script tourne parfaitement

Ce qui me manque? J'ai passé beaucoup de temps à chercher quel pourrait être le problème. Il semble que le paquet soit correctement configuré dans le dist-packages répertoire et tous les fichiers de service sont créés correctement une fois que la configuration est exécutée.

j'ai lu les choses sur l'utilisation de from __future__ import absolute_import, mais je ne suis pas sûr si je dois ajouter cela à mon main.py (cela ne fonctionne pas) ou à tous les fichiers de mon projet.

24
demandé sur pylover 2016-02-17 08:40:42

2 réponses

assurez-vous que votre application peut être exécutée à partir d'autres répertoires, cela ressemble à un cas classique où vous supposez que le répertoire courant est l'endroit où le script de départ existe.

Il n'a rien à voir avec systemd. Essayez également d'exécuter la commande start à partir de l'extérieur de votre shell login (Votre .le profil n'est pas chargé par les services).

1
répondu sorin 2016-05-28 11:56:02

Vous recevez un ImportError, parce que le module en question n'est pas dans sys.path ou non accessible, à cause de certaines permissions du système de fichiers.

Voici un script pour vérifier les permissions système de fichiers d'une distribution, d'un groupe et d'un nom donnés.

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)))

Test;

$ 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
répondu Nizam Mohamed 2016-03-07 09:45:10