2009-01-18 36 views
12

Pracuję nad strukturą Pythona, która miałaby "dodatki" napisane jako osobne pakiety. Tj .:Umieszczanie oddzielnych pakietów Pythona w tej samej przestrzeni nazw?

import myframework 
from myframework.addons import foo, bar 

Teraz, co staram się zorganizować to tak, że te dodatki mogą być rozpowszechniane oddzielnie od ramy bazowej i wstrzyknięto do myframework.addons nazw.

Obecnie moje najlepsze rozwiązanie jest następujące. Add-on będzie wdrożony (najprawdopodobniej w {python_version}/site-packages/ tak:

fooext/ 
fooext/__init__.py 
fooext/myframework/ 
fooext/myframework/__init__.py 
fooext/myframework/addons/ 
fooext/myframework/addons/__init__.py 
fooext/myframework/addons/foo.py 

fooext/myframework/addons/__init__.py miałby kod pkgutil przedłużacza Ścieżka:

import pkgutil 
__path__ = pkgutil.extend_path(__path__, __name__) 

Problem polega na tym, aby to działało, PYTHONPATH musi mieć w sobie fooext/, jednak jedyną rzeczą, którą mógłby mieć, jest katalog instalacji nadrzędnej (najprawdopodobniej wspomniany wyżej) .

Rozwiązaniem tego problemu jest posiadanie dodatkowego kodu w myframework/addons/__init__.py, który przechodziłby pod numerem sys.path i szukał dowolnego modułu z podpakietem myframework, w takim przypadku dodaje go do sys.path i wszystko działa.

Innym pomysłem było napisanie plików dodatków bezpośrednio do lokalizacji instalacji myframework/addons/, ale wtedy różnice w rozmieszczaniu i wdrażaniu przestrzeni nazw byłyby inne.

Czy istnieje lepszy sposób na osiągnięcie tego lub może inne podejście do powyższego problemu z dystrybucją w ogóle?

Odpowiedz

4

Czy istnieje lepszy sposób, aby osiągnąć ten cel, a może inne podejście do powyższego problemu dystrybucji w ogóle?

Prawdopodobnie. Konfiguracja modułu/pakietu Pythona jest na ogół trudna do manipulowania dynamicznie w ten sposób, ale jego system obiekt/klasa jest otwarty i rozszerzalny w dobrze określony sposób.Kiedy moduły i pakiety nie mają dość funkcji, które są potrzebne do estetyzacji projektu, możesz zamiast tego używać klas.

Na przykład można mieć funkcjonalność rozszerzenia w zupełnie innym pakiecie, ale pozwolić mu na wstrzykiwanie klas do podstawowego szkieletu za pośrednictwem określonego interfejsu. na przykład. myframework/_ _ _ _.py startowych zawierających podstawowe opakowanie aplikacji:

class MyFramework(object): 
    """A bare MyFramework, I only hold a person's name 
    """ 
    _addons= {} 
    @staticmethod 
    def addAddon(name, addon): 
     MyFramework._addons[name]= addon 

    def __init__(self, person): 
     self.person= person 
     for name, addon in MyFramework._addons.items(): 
      setattr(self, name, addon(self)) 

Wtedy można mieć rozszerzenie funkcjonalności w myexts/helloer.py, który utrzymuje odniesienie do jego „właściciela "lub«zewnętrzna»klasa myFramework instancja:

class Helloer(object): 
    def __init__(self, owner): 
     self.owner= owner 
    def hello(self): 
     print 'hello '+self.owner.person 

import myframework 
myframework.MyFramework.addAddon('helloer', Helloer) 

Więc teraz, jeśli tylko«import myframework», tylko uzyskać podstawową funkcjonalność. Ale jeśli również "importujesz myexts.helloer", możesz także wywołać MyFramework.helloer.hello(). Oczywiście można również definiować protokoły dla dodatków do interakcji z podstawowymi zachowaniami struktury i nawzajem. Można również wykonywać czynności takie jak wewnętrzne klasy, które podklasa architektury może przesłonić w celu dostosowania bez konieczności stosowania klas łatek małp, które mogą wpływać na inne aplikacje, jeśli potrzebujesz takiego poziomu złożoności.

Zachowanie kapsułkujące w ten sposób może być użyteczne, ale zwykle irytujące jest przystosowywanie kodu na poziomie modułu, który już został dopasowany do tego modelu.

+0

Zobacz odpowiedź Aleca poniżej. Setuptools ma coś dokładnie takiego, zwane punktami wejścia. –

0

Wygląda na to, że to, o co prosisz, można całkiem sprawnie przeprowadzić za pomocą haków importu.

Jest to sposób pisania niestandardowego kodu ładującego, który może być powiązany z pakietem (lub ramą), aby wykonać ładowanie wszystkich podpakietów i modułów, zamiast używania domyślnego mechanizmu ładowania Pythona. Następnie możesz zainstalować program ładujący w pakietach witryny jako pakiet podstawowy lub w ramach.

Po znalezieniu pakietu powiązanego z programem ładującym (który w razie potrzeby może zostać zakodowany na stałe w ścieżce względnej), wówczas zawsze będzie używał programu ładującego do ładowania wszystkich dodatków na przykład. Ma tę zaletę, że nie wymaga żadnego manipulowania PYTHONPATH, co jest na ogół warte zachowania jak najkrótszego.

Alternatywą dla tego jest użycie plików init do przekierowania wywołania importu dla podmodułu do tego, który ma zostać odebrany, ale jest to nieco kłopotliwe.

Więcej informacji na hakach importu można znaleźć tutaj:

http://www.python.org/dev/peps/pep-0302/

3

Setuptools ma możliwość wyszukiwania pakietów "punktów wejścia" (funkcje, obiekty, cokolwiek) według nazwy. Trac używa tego mechanizmu do load its plugins i działa dobrze.

+0

Głosowałbym za rekomendacją Aleca powyżej którejkolwiek z pozostałych. punkty wejściowe zostały zaprojektowane dokładnie w tym celu, a dokumentacja ma ładny przykład ładowania wtyczek do barwnika: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services- i-plugins –

0

Jest to całkowicie nowa konfiguracja dla przestrzeni nazw. Spójrz na Packaging namespace packages. Krótko mówiąc, masz trzy opcje, w zależności od tego, ile kompatybilnego wstecznego chcesz, aby twój kod był. Istnieje również odpowiednia PEP, która zastępuje te wymienione w innych odpowiedziach: PEP 420.

Powiązane problemy