Ist __enter__ und __exit__ Verhalten für Verbindungsobjekte, die in der Python-Datenbank-API angegeben sind?

Hintergrund

Ich habe vor kurzem die Python with Keyword entdeckt und begann seine potenzielle Nützlichkeit für mehr hübsche Handhabung einige Szenarien, wo ich zuvor verwendet haben try: ... finally: ... konstruiert. Ich habe sofort beschlossen, es auszuprobieren auf dem MySQLdb-Verbindungsobjekt in irgendeinem Code, den ich schrieb.

Ich habe es nicht __enter__ wie __enter__ und __exit__ sich in Implementoren der Python-Datenbank-API verhalten und naiv erwartet, dass das Verhalten wie das von Datei-Objekten ist – alles, was ich erwartet hatte, war für den Ausgang, um connection.close() aufzurufen.

Stellen Sie sich meine Verwirrung vor, dann bei diesem Verhalten:

 >>> with util.get_db_connection() as conn: ... print conn ... <MySQLdb.cursors.Cursor object at 0xb6ca8b4c> 

get_db_connection() gibt ein MySQLdb-Verbindungsobjekt zurück, aber die __enter__ Methode dieses Verbindungsobjekts gibt ein __enter__ zurück, nicht das Verbindungsobjekt selbst, wie ich erwartet habe, wie __enter__ und __exit__ für __exit__ arbeiten. Ich glaube, ich sollte with util.get_db_connection() as cursor: , oder sonst nicht with überhaupt.

Fragen

Sofort macht mir diese Entdeckung ein paar Dinge:

  1. Was machen die __enter__ und __exit__ Methoden von MySQLdb-Verbindungsobjekten? Ist __exit__ zu begehen oder Rollback Änderungen für mich ohne mich explizit fragen, dass passieren? Gibt es sonst noch nichts offensichtlich, dass ich es wissen sollte?
  2. Ist dieses Verhalten bei anderen Implementatoren der Python-Datenbank-API gleich (wie sqlite3, django oder psycopg2)?
  3. Ist dieses Verhalten irgendwie irgendwie besprochen? ( PEP 249 – Python-Datenbank-API-Spezifikation v2.0 ) für 'enter', 'exit' und 'context manager' gibt nichts auf.

    2 Solutions collect form web for “Ist __enter__ und __exit__ Verhalten für Verbindungsobjekte, die in der Python-Datenbank-API angegeben sind?”

    Die Python-DBAPI wurde gut geschrieben, bevor Kontextmanager der Python-Sprache hinzugefügt wurden.

    Als solche haben verschiedene Datenbankbibliotheken ihre eigenen Entscheidungen getroffen, wie man die Kontextmanager-Unterstützung implementiert (wenn sie es überhaupt implementiert hat).

    In der Regel mit der Datenbank als Kontext Manager verbindet Sie eine Transaktion. Die Transaktion wird auf __enter__ gestartet und auf __enter__ oder abgebrochen, je nachdem, ob es eine Ausnahme gab oder nicht. Als solches solltest du die MySQL-Verbindung als Kontextmanager verwenden, nachdem du sie separat angeschlossen hast:

     connection = util.get_db_connection() with connection as cursor: cursor.execute(...) # connection commit is issued if no exceptions were raised. 

    Die sqlite3 Kontextmanager-Implementierung ist subtil unterschiedlich; Es verwaltet auch Transaktionen, gibt aber keinen Cursor aus der Methode __enter__ :

     con = sqlite3.connect(":memory:") with con: cursor = con.cursor() # or use the connection directly con.execute(...) 

    Technisch geht es einfach auf __enter__ .

    Siehe die Funktion __enter__ in diesem Link. https://github.com/PyMySQL/mysqlclient-python/blob/master/MySQLdb/connections.py

    __enter__ Funktion des Verbindungsobjekts gibt self.cursor() .

    Deshalb bekommst du anstelle von Connection-Objekt Cursor-Objekt.

    Python ist die beste Programmiersprache der Welt.