import modułu Pythona - problem ze ścieżkami względnymi

Rozwijam własny moduł w Pythonie 2.7. Znajduje się w ~/Development/.../myModule zamiast /usr/lib/python2.7/dist-packages lub /usr/lib/python2.7/site-packages. Struktura wewnętrzna to:

/project-root-dir
  /server
    __init__.py
    service.py
    http.py
  /client
    __init__.py
    client.py

client/client.py obejmuje klasę PyCachedClient. Mam problem z importem:

project-root-dir$ python
Python 2.7.2+ (default, Jul 20 2012, 22:12:53) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from server import http
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "server/http.py", line 9, in <module>
    from client import PyCachedClient
ImportError: cannot import name PyCachedClient

Nie ustawiłem PythonPath, aby zawierał mój project-root-dir, dlatego gdy serwer.http próbuje dołączyć klienta.PyCachedClient, próbuje załadować go ze ścieżki względnej i nie powiedzie się. Moje pytanie brzmi - jak ustawić wszystkie ścieżki/ustawienia w dobry, pythoniczny sposób? I know I can run export PYTHONPATH=... in shell za każdym razem otwieram konsolę i próbuję uruchomić serwer, ale chyba nie jest to najlepszy sposób. Gdyby mój moduł był zainstalowany przez PyPi (lub coś podobnego), zainstalowałbym go w ścieżce /usr/lib/python... i ładowałby się automatycznie.

Byłbym wdzięczny za wskazówki dotyczące najlepszych praktyk w tworzeniu modułów Pythona.

Author: ducin, 2013-11-09

2 answers

My Python development workflow

Jest to podstawowy proces tworzenia pakietów Pythona, który zawiera to, co uważam za najlepsze praktyki w społeczności. Jest to podstawowe - jeśli naprawdę poważnie myślisz o rozwijaniu pakietów Pythona, jest jeszcze trochę więcej i każdy ma swoje własne preferencje, ale powinien służyć jako szablon, aby zacząć, a następnie dowiedzieć się więcej o zaangażowanych elementach. Podstawowe kroki to:

  • użycie virtualenv na isolation
  • setuptools do tworzenia pakietu instalowalnego i zarządzania zależnościami
  • python setup.py develop aby zainstalować pakiet w trybie deweloperskim

Virtualenv

Po pierwsze, polecam użycie virtualenv aby uzyskać odizolowane środowisko do rozwijania pakietu(pakietów) w. Podczas tworzenia pakietu należy zainstalować, uaktualnić, obniżyć i odinstalować zależności pakietu.]}

  • Twoje zależności rozwojowe aby zanieczyścić cały system site-packages
  • Twój systemowy wpływ na środowisko programistyczne]}
  • konflikty wersji

Zanieczyszczanie całego systemu site-packages jest złe, ponieważ każdy zainstalowany tam pakiet będzie dostępny dla wszystkich zainstalowanych aplikacji Pythona, które używają Pythona systemowego, nawet jeśli potrzebujesz tylko tej zależności dla swojego małego projektu. I właśnie został zainstalowany w nowej wersji, która przewyższa tę w całym systemie site-packages, i jest niezgodna z ${important_app}, która od niej zależy. Rozumiesz.

Wpływanie na środowisko programistyczne całego systemu site-packages jest złe, ponieważ być może twój projekt zależy od modułu, który masz już w systemie Pythona site-packages. Więc zapominasz prawidłowo zadeklarować, że twój projekt zależy od tego modułu, ale wszystko działa, ponieważ zawsze jest tam na twoim lokalnym polu rozwoju. Dopóki nie wypuścisz pakietu, a ludzie spróbują go zainstalować lub wypchnąć do produkcji, itd... Rozwój w czystym środowisku zmusza cię do prawidłowego deklarowania swoich zależności.

Tak więc, virtualenv jest izolowanym środowiskiem z własnym interpreterem Pythona i ścieżką wyszukiwania modułów. Jest on oparty na instalacji Pythona, którą wcześniej zainstalowałeś, ale odizolowany od niej.

Aby utworzyć virtualenv, zainstaluj pakiet virtualenv instalując go w Pythonie systemowym używając easy_install lub pip:

sudo pip install virtualenv

Zauważ, że to będzie tylko czas zainstalować coś jako root( za pomocą sudo), do globalnej witryny-Pakiety. Wszystko po tym stanie się wewnątrz virtualenv, który masz zamiar utworzyć.

Teraz Utwórz virtualenv do rozwijania pakietu:

cd ~/pyprojects
virtualenv --no-site-packages foobar-env

Spowoduje to utworzenie drzewa katalogów ~/pyprojects/foobar-env, które jest Twoim virtualenv.

Aby aktywować virtualenv, cd do niego i source bin/activate script:

~/pyprojects $ cd foobar-env/
~/pyprojects/foobar-env $ . bin/activate
(foobar-env) ~/pyprojects/foobar-env $

Zwróć uwagę na kropkę wiodącą ., to skrót od source polecenia powłoki. Również zauważ, jak zmienia się monit: (foobar-env) oznacza Twoje wnętrze aktywowanego virtualenv (i zawsze będzie musiało być, aby izolacja zadziałała). Więc aktywuj swój env za każdym razem, gdy otwierasz nową kartę terminal lub sesję SSH itp..

Jeśli teraz uruchomisz python w tym aktywowanym env, będzie on używał ~/pyprojects/foobar-env/bin/python jako interpretera, z własną site-packages i izolowaną ścieżką wyszukiwania modułów.

Pakiet setuptools

Teraz do tworzenia pakietu. Zasadniczo będziesz chciał setuptools pakiet z setup.py aby poprawnie zadeklarować metadane i zależności pakietu. Możesz to zrobić samodzielnie, postępując zgodnie z dokumentacją setuptools, lub utworzyć pakiet skeletion przy użyciu szablonów Paster. Aby użyć szablonów Paster, zainstaluj PasteScript w swoim virtualenv:

pip install PasteScript

Stwórzmy katalog źródłowy dla naszego nowego pakietu, aby utrzymać porządek (może będziesz chciał podzielić swój projekt na kilka pakietów, lub później użyć zależności od źródło):

mkdir src
cd src/

Teraz do tworzenia pakietu, do

paster create -t basic_package foobar

I odpowiedzieć na wszystkie pytania w interaktywnym interfejsie. Większość z nich jest opcjonalna i można ją pozostawić domyślnie, naciskając klawisz ENTER.

Spowoduje to utworzenie pakietu (a dokładniej dystrybucji setuptools) o nazwie foobar. Jest to nazwa, która

  • ludzie będą używać do instalacji pakietu przy użyciu easy_install lub pip install foobar
  • nazwa innych pakietów będzie zależała od Twojej w setup.py
  • jak będzie się nazywał PyPi

Wewnątrz prawie zawsze tworzysz pakiet Pythona (jak w "katalogu z __init__.py), który nazywa się tak samo. Nie jest to wymagane, nazwa najwyższego poziomu pakietu Pythona może być dowolną poprawną nazwą pakietu, ale jest to powszechna konwencja nazywania go tak samo jak dystrybucji. I dlatego ważne, ale nie zawsze łatwe, jest oddzielenie ich od siebie. Ponieważ Nazwa pakietu Pythona najwyższego poziomu to co

  • ludzie (lub Ty) będą używać do importowania pakietu za pomocą import foobar lub from foobar import baz

Więc jeśli użyłeś szablonu Pastera, będzie on już utworzył ten katalog dla Ciebie:

cd foobar/foobar/

Teraz utwórz swój kod:

vim models.py

models.py

class Page(object):
    """A dumb object wrapping a webpage.
    """

    def __init__(self, content, url):
        self.content = content
        self.original_url = url

    def __repr__(self):
        return "<Page retrieved from '%s' (%s bytes)>" % (self.original_url, len(self.content))

I client.py w tym samym katalogu, który używa models.py:

client.py

import requests
from foobar.models import Page

url = 'http://www.stackoverflow.com'

response = requests.get(url)
page = Page(response.content, url)

print page

Zadeklaruj zależność od modułu requests w setup.py:

  install_requires=[
      # -*- Extra requirements: -*-
      'setuptools',
      'requests',
  ],

Wersja Kontrola

src/foobar/ jest katalogiem, który chcesz teraz umieścić pod kontrolą wersji:

cd src/foobar/
git init
vim .gitignore

.gitignore

*.egg-info
*.py[co]
git add .
git commit -m 'Create initial package structure.

Instalowanie pakietu jako jaja programistycznego

Teraz nadszedł czas, aby zainstalować pakiet w trybie deweloperskim:

python setup.py develop

To zainstaluje requests zależność i Twój pakiet jako jajko programistyczne. Więc jest połączony z Twoją stroną virtualenv-pakietami, ale nadal mieszka na src/foobar, gdzie możesz wprowadzać zmiany i niech będą natychmiast aktywni w virtualenv bez ponownej instalacji pakietu.

Teraz Twoje oryginalne pytanie, importowanie przy użyciu ścieżek względnych: moja rada jest taka, nie rób tego. Teraz, gdy masz odpowiedni pakiet setuptools, który jest zainstalowany i możliwy do zaimportowania, Twój bieżący katalog roboczy nie powinien mieć większego znaczenia. Po prostu zrób from foobar.models import Page lub podobne, deklarując w pełni kwalifikowaną nazwę, w której ten obiekt żyje. Dzięki temu Twój kod źródłowy jest o wiele bardziej czytelny i czytelny dla Ciebie i inni ludzie, którzy czytają Twój kod.

Możesz teraz uruchomić swój kod wykonując python client.py z dowolnego miejsca wewnątrz aktywowanego virtualenv. python src/foobar/foobar/client.py działa równie dobrze, Twój Pakiet jest poprawnie zainstalowany, a twój katalog roboczy nie ma już znaczenia.

Jeśli chcesz pójść o krok dalej, możesz nawet utworzyć punkt wejścia setuptools dla skryptów CLI. Spowoduje to utworzenie skryptu bin/something w virtualenv, który można uruchomić z powłoki.

Setuptools console_scripts punkt wejścia

setup.py

  entry_points='''
  # -*- Entry points: -*-    
  [console_scripts]
  run-fooobar = foobar.main:run_foobar
  ''',

client.py

def run_client():
    # ...

main.py

from foobar.client import run_client

def run_foobar():
    run_client()

Ponownie zainstaluj pakiet, aby aktywować punkt wejścia:

python setup.py develop

I proszę bardzo, bin/run-foo.

Gdy ty (lub ktoś inny) zainstalujesz pakiet na serio, poza virtualenv, punkt wejścia będzie w /usr/local/bin/run-foo lub gdzieś simiar, gdzie będzie automatycznie w $PATH.

Dalej kroki

  • Tworzenie wydania pakietu i wgrywanie go PyPi, na przykład za pomocą zest.releaser
  • prowadzenie dziennika zmian i wersjonowanie pakietu
  • dowiedz się o deklarowaniu zależności
  • Dowiedz się więcej o różnicach pomiędzy distribute, distutils, setuptools i distutils2]}

Sugerowana lektura:

 30
Author: Lukas Graf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:31

Więc masz dwa } Pakiety, pierwszy z modułami o nazwie:

server         # server/__init__.py
server.service # server/service.py
server.http    # server/http.py

Drugi z nazwami modułów:

client         # client/__init__.py
client.client  # client/client.py

Jeśli chcesz założyć, że oba pakiety są w importowanej ścieżce (sys.path), A klasa, którą chcesz mieć jest w client/client.py, to na twoim serwerze musisz zrobić:

from client.client import PyCachedClient

Prosiłeś o symbol z client, a nie client.client, a z twojego opisu wynika, że nie jest to miejsce, w którym ten symbol jest zdefiniowany.

Osobiście rozważyłbym zrobienie tego jednego pakietu (ie, umieszczenie __init__.py w folderze o jeden poziom wyżej i nadanie mu odpowiedniej nazwy pakietu Pythona), a client i server będą podpakietami tego pakietu. Następnie (a) możesz zaimportować relatywnie (from ...client.client import something), oraz (b) Twój projekt byłby bardziej odpowiedni do redystrybucji, nie umieszczając dwóch bardzo ogólnych nazw pakietów na najwyższym poziomie hierarchii modułów Pythona.

 2
Author: Matt Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-09 15:11:41