Domanda

Sto sviluppando un framework Python che avrebbe " addons " scritto come pacchetti separati. Cioè:.

import myframework
from myframework.addons import foo, bar

Ora, quello che sto cercando di organizzare è che questi componenti aggiuntivi possano essere distribuiti separatamente dal framework principale e iniettati nello spazio dei nomi myframework.addons .

Attualmente la mia migliore soluzione a questa è la seguente. Un componente aggiuntivo verrebbe distribuito (molto probabilmente in {python_version} / site-pacchetti / in questo modo:

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

Il fooext / myframework / addons / __ init__.py avrebbe il codice di estensione del percorso pkgutil:

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

Il problema è che affinché questo funzioni, il PYTHONPATH deve contenere fooext / , tuttavia l'unica cosa che dovrebbe avere è la directory di installazione principale (molto probabilmente, la < code> site-packages ).

La soluzione a questo è di avere un codice extra in myframework / addons / __ init__.py che rintracciare sys.path e cercare eventuali moduli con un subframe myframework- pacchetto, nel qual caso lo aggiunge a sys.path e tutto funziona.

Un'altra idea che ho avuto è quella di scrivere i file dei componenti aggiuntivi direttamente nella posizione di installazione myframework / addons / , ma ciò renderebbe lo sviluppo e lo spazio dei nomi distribuito diversi

Esiste un modo migliore per realizzare questo o forse un approccio diverso al problema di distribuzione di cui sopra?

È stato utile?

Soluzione

  

Esiste un modo migliore per realizzare questo o forse un approccio diverso al problema di distribuzione di cui sopra?

È possibile. La configurazione del modulo / pacchetto di Python è generalmente difficile da manomettere dinamicamente in questo modo, ma il suo sistema oggetto / classe è aperto ed estensibile in un modo ben definito. Quando i moduli e i pacchetti non hanno abbastanza le caratteristiche di cui hai bisogno per incapsulare bene il tuo progetto, puoi invece usare le classi.

Ad esempio, potresti avere la funzionalità di estensione in un pacchetto completamente diverso, ma permettergli di iniettare classi nel tuo framework di base attraverso un'interfaccia specifica. per esempio. myframework / _ & # 8203; & # 8203; _ & # 8203; init & # 8203; _ & # 8203; & # 8203; _. py contenente un wrapper applicativo di base:

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

Quindi potresti avere la funzionalità di estensione in un myexts / helloer.py, che mantiene un riferimento all'istanza della classe MyFramework "proprietario" o "esterna":

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)

Quindi ora se " importa myframework " ;, otterrai solo le funzionalità di base. Ma se anche "importi myexts.helloer" hai anche la possibilità di chiamare MyFramework.helloer.hello (). Naturalmente è anche possibile definire protocolli per gli addon per interagire con il comportamento del framework di base e tra loro. Puoi anche fare cose come le classi interne che una sottoclasse del framework può sovrascrivere per personalizzare senza dover scansionare le classi di patch che potrebbero influenzare altre applicazioni, se hai bisogno di quel livello di complessità.

Un comportamento incapsulante come questo può essere utile, ma in genere è un lavoro fastidioso per adattare il codice a livello di modulo che devi già adattare a questo modello.

Altri suggerimenti

Setuptools ha la capacità di cercare il pacchetto "punti di ingresso" (funzioni, oggetti, qualunque cosa) per nome. Trac utilizza questo meccanismo per caricare i suoi plugin e funziona bene.

Esiste una configurazione completamente nuova per gli spazi dei nomi. Dai un'occhiata a Pacchetti dello spazio dei nomi di packaging . In breve, hai tre opzioni, in base a quanto compatibile con le versioni precedenti vuoi che il tuo codice sia. Esiste anche un PEP pertinente, che sostituisce quelli menzionati in altre risposte: PEP 420 .

Sembra che ciò che stai cercando possa essere realizzato abbastanza bene con gli hook di importazione.

Questo è un modo di scrivere codice di caricamento personalizzato, che può essere associato a un pacchetto (o nel tuo caso un framework), per eseguire il caricamento di tutti i sotto-pacchetti e moduli, piuttosto che usare il meccanismo di caricamento predefinito di Python. Quindi è possibile installare il caricatore nei pacchetti del sito come pacchetto base o nel proprio framework.

Quando viene trovato un pacchetto associato al caricatore (che può essere semplicemente codificato nel percorso relativo se necessario), utilizzerà sempre il caricatore per caricare tutti i componenti aggiuntivi, ad esempio. Ciò ha il vantaggio di non richiedere alcun armeggiamento del PYTHONPATH, che in genere vale la pena mantenere il più breve possibile.

L'alternativa a questo è utilizzare i file init per reindirizzare la chiamata di importazione di un sottomodulo a quello che si desidera raccogliere, ma questo è un po 'disordinato.

Ulteriori informazioni sugli hook di importazione sono disponibili qui:

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top