2014-04-06 5 views
7

Sto lavorando a uno strumento che trarrebbe vantaggio dalla possibilità di tenere traccia di tutti i riferimenti a un determinato oggetto all'interno di python.Da python, possiamo tenere traccia delle assegnazioni a livello di modulo prima che venga eseguito (altro) il codice utente?

In particolare, mi piacerebbe creare un sistema di test double che possa sostituire tutti gli attributi a livello di modulo di un determinato tipo. Si supponga ad esempio il seguente codice nel modulo c:

from a import b 

Se a è un modulo, b è un riferimento all'oggetto denominato a.b, ma è un separato riferimento . Se il mio doppio sistema di test successivamente sostituisce a.b, c.b farà comunque riferimento all'oggetto originale.

Mi piacerebbe che il mio strumento tenesse traccia di tutti i compiti di a.b in alias, ma l'aliasing a livello di modulo farebbe molto per raggiungere il mio obiettivo.

Metaforicamente, quello che vorrei è quello di ignorare Module.__setattribute__:

def __setattribute__(self, name, value): 
    if isinstance(value, interesting_types): 
     # remember this use of the interesting object and call super for normal processing. 

Si supponga che posso ottenere il mio codice caricato prima di moduli che possono essere monitorati possono essere caricati.

+0

Mi è venuto in mente che potrei essere in grado di utilizzare un importatore per fare il trucco, perché una volta importato (con i metodi normali), ho potuto ispezionare gli attributi dell'oggetto restituito e tracciarlo.Approfondirò questo approccio. –

+0

Probabilmente mi manca qualcosa ... 'da un'importazione b' crea un nuovo' b' nell'attuale scope che è diverso da 'a.b'. Se assegni un valore a 'b', non cambia il valore di' a.b'. Perchè vuoi fare questo? – Seth

+0

Sono interessato a puntare tutti i riferimenti all'originale b a un'altra implementazione di b. Nello specifico, sto cercando di costruire un sistema di test double che ti permetta di sostituire tutti i riferimenti alla normale implementazione con spionaggio, finto, stub o finto. –

risposta

2

Questo tipo di cose potrebbe funzionare per voi. In primo luogo, un po 'di codice:

a.py

b = 42 
# other module definitions 

fakery.py

class Fakery(object): 
    def __init__(self, mod): 
     self.module = __import__(mod) 
     import sys 
     sys.modules[self.module.__name__] = self 

    def __getattr__(self, name): 
     print "__getattr__ called with '%s'" % name 
     result = getattr(self.module, name) 
     if name == 'b': 
      result += 1 
     return result 

Esempio

>>> import fakery 
>>> fakery.Fakery('a') 
<fakery.Fakery object at 0x109007110> 
>>> from a import b 
__getattr__ called with '__path__' 
__getattr__ called with 'b' 
>>> print b 
43 
>>> import a 
>>> print a 
<fakery.Fakery object at 0x109007110> 

Tutto quello che avreste fare è modificare la classe Fakery per fare tutto ciò che si vuole fare. In questo caso, aggiungo solo 1 a a's b.

Spero sia chiaro come funziona, ma ecco una rapida spiegazione.

Quando un modulo viene importato, un record di esso viene inserito in sys.modules. Quando si crea un'istanza di un oggetto Fakery, ciò che importa è importare un modulo per nome (utilizzando __import__) e quindi sostituire la voce di quel modulo in sys.modules con se stesso.

Python importa solo i moduli una sola volta, memorizzando il modulo importato in sys.modules. Ogni importazione dopo la prima restituisce la voce in sys.modules. L'oggetto Fakery inserisce nello stesso in sys.modules['a'], sostituendo il modulo reale. Tutte le successive dichiarazioni import a o from a import <whatever> vengono ora indirizzate a un'istanza di Fakery. Dal momento che è solo una classe, puoi fare ogni genere di cose pazze usando metodi magici o metaprogrammazione.

__getattr__ è conveniente perché viene chiamato quando un attributo richiesto non esiste.

+0

Grazie Seth, è un po 'sporco - sono un po' preoccupato di ombreggiare il modulo normale senza ereditarlo - ma sono d'accordo che potrebbe funzionare abbastanza bene. Accetterò la risposta dopo che avrò del tempo per soffermarmi. –