2016-02-17 31 views
23

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 da top-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.

+0

Provare a registrare sys.path dallo script e confrontarlo con qualunque esso sia quando si avvia manualmente. – ther

+6

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 –

+3

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. –

risposta

0

Si ottiene un ImportError, perché il modulo in questione non è in sys.path o non è accessibile, a causa di alcune autorizzazioni del file system.
Ecco uno script per controllare le autorizzazioni del file system di una determinata distribuzione, gruppo e nome.

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

di prova;

$ 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

Il main.py nella mia directory dist-packages ha le seguenti autorizzazioni (-rw-rw-r-), quindi immagino che questo non sia il problema. Grazie comunque. – newlog

+1

La directory principale di newlog di main.py deve avere bit di esecuzione impostato per l'attraversamento della directory. –

+0

@Newlog potrebbe essere d'aiuto l'importazione del modulo 'main' all'interno del pacchetto. Prova ad aggiungere "da". importare main' in 'pkg/__ init __. py'. –

1

Assicurarsi di applicazione ca essere eseguito da altre directory, questo mi sembra un classico caso in cui si assume che directory corrente è dove esiste lo script di avvio.

Non ha nulla a che fare con systemd. Prova anche a eseguire il comando start dall'esterno della tua shell di login (il tuo profilo non viene caricato dai servizi).

Problemi correlati