Ho un progetto python e voglio essere in grado di installarlo utilizzando qualcosa come python setup.py install
in modo che l'installazione crei automaticamente un servizio systemd.Utilizzo di setup.py per installare il progetto python come servizio systemd
Ho qualche problema, molto probabilmente impostando i percorsi o le importazioni correttamente.
Il mio ambiente:
- Ubuntu 15.04
- Python 2.7 (anche se sarebbe bello farlo funzionare in PY3 troppo). Struttura
Progetto:
+ 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()
setup.cfg
012.351.641.061.[bdist_wheel]
universal=1
create_service.sh (più o meno):
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-project.service
[Unit]
Description=Super Description
[Service]
Type=simple
ExecStart=/usr/local/bin/super-service
Restart=always
[Install]
WantedBy=multi-user.target
L'installazione del pacchetto genera il seguente risultato:
$ 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'
Il file super-project
viene creato in/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'installazione sembra riuscita, anche se:
$ systemctl status super-project.service
● super-project.service
Loaded: not-found (Reason: No such file or directory)
Active: inactive (dead)
L'errore che posso vedere in/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.
Come si può vedere, il modulo main
non può essere trovato. Questo è il problema principale.
Quando si modifica il codice/conf, tolgo il super-progetto/servizio come segue:
$ 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'altra parte:
- Se eseguo
$ super-project
dal/usr/local/bin/
, lo script si avvia correttamente (nessuna eccezione di importazione) ma i file di configurazione non possono essere letti (molto probabilmente a causa di problemi relativi/assoluti del percorso). - Se eseguo
$ super-project
datop-folder
(cartella che contiene il codice del progetto/file) lo script viene eseguito perfettamente
Che cosa mi manca? Ho passato molto tempo a cercare quale potrebbe essere il problema. Sembra che il pacchetto sia impostato correttamente nella directory dist-packages
e tutti i file di servizio vengano creati correttamente una volta eseguita l'installazione.
Ho letto cose sull'utilizzo di from __future__ import absolute_import
, ma non sono sicuro di doverlo aggiungere al mio main.py (non funziona) oa tutti i file nel mio progetto.
Provare a registrare sys.path dallo script e confrontarlo con qualunque esso sia quando si avvia manualmente. – ther
Suggerisco un approccio diverso; I pacchetti Python dovrebbero rimanere il più neutri possibile e non forzare alcuna manipolazione di sistema che implichi privilegi di root durante l'esecuzione di 'setup.py'. Cosa succede se l'utente desidera installare il pacchetto su virtualenv per lo sviluppo? Invece, suggerisco che la tua applicazione abbia uno script separato in 'bin' che può fare l'installazione systemd: https://packaging.python.org/en/latest/distributing/#entry-points –
In alternativa, crea un pacchetto per il tuo sistema operativo ('deb',' rpm', ecc.) dato che è il posto giusto dove inserire i comandi di installazione specifici del sistema operativo. –