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.
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
lubpip 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
lubfrom 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:
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.
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