2012-05-24 21 views
5

Algunos antecedentes: trabajo en un gran banco y estoy tratando de reutilizar algunos módulos de Python, que no puedo modificar, solo importo. Tampoco tengo la opción de instalar nuevas utilidades/funciones, etc. (ejecutando Python 2.6 en Linux).Python missing __exit__ method

tengo esta en la actualidad:

En mi módulo:

from common.databaseHelper import BacktestingDatabaseHelper 

class mfReportProcess(testingResource): 
    def __init__(self): 
     self.db = BacktestingDatabaseHelper.fromConfig('db_name') 

Uno de los métodos llamados dentro de la clase 'testingResource' tiene esto:

with self.db as handler: 

que cae terminado con esto:

with self.db as handler: 
AttributeError: 'BacktestingDatabaseHelper' object has no attribute '__exit__' 

y, de hecho, no existe el método __exit__ en la clase 'BacktestingDatabaseHelper', una clase que no puedo cambiar.

Sin embargo, este código que estoy tratando de reutilizar funciona perfectamente para otras aplicaciones. ¿Alguien sabe por qué me sale este error y nadie más? ¿Hay alguna forma de definir localmente el __exit__?

Muchas gracias de antemano.

Editado para añadir:

He intentado añadir mi propia clase para configurar el acceso DB pero no puedo conseguir que funcione - añadido esto a mi módulo:

class myDB(BacktestingDatabaseHelper): 
    def __enter__(self): 
     self.db = fromConfig('db_name') 
    def __exit__(self): 
     self.db.close() 

y ha añadido:

self.db = myDB 

en mi init atributo para mi clase principal, pero me sale este error:

with self.db as handler: 
TypeError: unbound method __enter__() must be called with myDB instance as first argument (got nothing instead) 

¿Alguna sugerencia sobre cómo hacer esto correctamente?

+3

Están usando una versión diferente del módulo o no están usando 'BacktestingDatabaseHelper' como administrador de contexto –

+2

. Tendría que saber qué real '__enter__' y' __exit__' do, y haz lo mismo. Si el mismo código funciona para otras personas en su empresa, realmente le sugiero que compruebe la versión y el contenido de cada uno de los módulos comunes. –

Respuesta

4

El error significa que BacktestingDatabaseHelper no está diseñado para ser utilizado en una declaración with. Parece que las clases testingResource y BacktestingDatabaseHelper no son compatibles entre sí (quizás su versión de common.databaseHelper está desactualizada).

+0

Me estoy dando la vuelta a la idea de que mi versión es incorrecta/vieja, pero estoy esperando que un miembro del equipo de desarrollo le aconseje. – Nelmo

+2

Aaggh: ¡el desarrollador ahora me dice que instaló el paquete equivocado para mí! Lo siento mucho y muchas gracias por todas sus respuestas. – Nelmo

+1

@ user996166; así que pon eso como ** 'EDIT:' ** en la primera línea de tu pregunta !! – smci

11

El uso del protocolo with asume que el objeto utilizado en with implementa el context manager protocol.

Básicamente esto significa que la definición de clase debe tener __enter__() y __exit__() métodos definidos. Si usa un objeto sin estos, python lanzará un AttributeError quejándose del atributo __exit__ que falta.

2

El 'con' palabra clave es básicamente un shortcut para escribir:

try: 
    // Do something 
finally: 
    hander.__exit__() 

que es útil si el objeto handler está consumiendo recursos (como, por ejemplo, una secuencia de archivo abierto). Se asegura de que no importa lo que suceda en la parte 'hacer algo', el recurso se libera limpiamente.

En su caso, su objeto controlador no tiene un método __exit__, por lo que with falla. Supongo que otras personas pueden usar BacktestingDatabaseHelper porque no están usando with.

En cuanto a lo que puede hacer ahora, le sugiero que olvide with y use try ... finally, en lugar de intentar agregar su propia versión de __exit__ al objeto. Deberá asegurarse de liberar el controlador correctamente (la forma de hacerlo dependerá de cómo se supone que se usa BacktestingDatabaseHelper), p.

try: 
    handler = self.db 
    // do stuff 
finally: 
    handler.close() 

Editar: Puesto que no puede cambiarlo, usted debe hacer algo como @ Daniel Roseman suggests para envolver BacktestingDatabaseHelper. Dependiendo de cuál es la mejor para limpiar BacktestingDatabaseHelper (como el anterior), se puede escribir algo como:

from contextlib import contextmanager 

@contextmanager 
def closing(thing): 
    try: 
     yield thing 
    finally: 
     thing.close() 

y utilizar esto como:

class mfReportProcess(testingResource): 
    def __init__(self): 
     self.db = closing(BacktestingDatabaseHelper.fromConfig('db_name')) 

(esto es directamente desde el documentation).

+0

Desafortunadamente, esa línea "con" es parte del código común que no puedo cambiar, así que tengo que usarlo. – Nelmo

+0

(Mi respuesta fue demasiado larga para un comentario, así que he editado mi respuesta.) – karaken12

+0

Además, no estoy seguro si solo está cerrando lo que el administrador de contexto está haciendo, o si se trata de algo diferente (transacción gestión, etc.) – glglgl

2

Es posible que desee probar el decorador contextlib.contextmanager para ajustar su objeto para que admita el protocolo del gestor de contexto.

3

Como no se puede cambiar el estado de with, debe agregar una clase que deriva de BacktestingDatabaseHelper que añade apropiadas __enter__() y __exit__() funciones y utilizar esto en su lugar.

Este es un ejemplo que trata de estar lo más cerca posible al original:

class myDB(BacktestingDatabaseHelper): 
    def __enter__(self): 
     return self 
    def __exit__(self): 
     self.db.close() 
    def fromConfig(self, name): 
     x = super(myDB, self).fromConfig(name) 
     assert isinstance(x, BacktestingDatabaseHelper) 
     x.__class__ = myDB # not sure if that really works 
[...] 
self.db=myDB.fromConfig('tpbp') 

El problema es, sin embargo, que no estoy seguro de lo que se supone que el __enter__ volver. Si toma MySQLdb, por ejemplo, el administrador de contexto de la conexión crea un cursor que representa una transacción. Si ese es el caso aquí también, tendrá que buscar algo más ...

+0

** Vea el bit editado en OP *** – Nelmo

+0

@ user996166 Agregué un ejemplo de cómo podría funcionar, quizás. 'self.db = myDB' simplemente asigna la clase. Tal vez podrías intentar con 'self.db = myDB()', aunque dudo si eso funciona. – glglgl

+0

Desafortunadamente, tengo que usar la versión común de 'fromConfig' (que no puedo cambiar) ya que tiene parámetros/variables importantes para configurar. – Nelmo