Python's try yield finally + async is a bit pit
The try finally block in Python is a means of exception capture.At the same time, there is also a function in Python that the finally block in the try finally block will always be executed, and the variables in the try block will be used. The following sample code and its running results:
#Example 1 def test (): try : print ( "a" ) yield 1 return 2 finally : print ( "bbbb" ) return 0
a = test () print ( a ) print ( "-" * 30 ) print ( next ( a )) print ( a ) print ( "=" * 30 ) print ( next ( a )) #Example 2 def test2 () : try : print ( "a" ) return 2 finally : print ( "b" ) #return 0
a = test2 () print ( '-' * 30 ) print ( a )
The execution result is shown in the figure:
In Example 1, the execution principle of yield is tested.When test is called for the first time, print is not performed, but a generator
Traceback ( most recent call last ): File "test/try.py" , line 18 , in < module >
print ( next ( a )) StopIteration : 0
Example 2 shows the execution logic of try finally more clearly. First output a and then output b, and then the return in the finally will override the return in the try if it exists. It should be noted that the finally print in test is also executed after the finally in test2 is executed. So the finally block in the try finally block in Python will always be executed.
Use loops to demonstrate the function of yield, and the function of yield is clear at a glance.
DEF Demo (): Print ( "Start" ) the while.1 : the yield 'yield_value' Print ( "return the yield" )
fun = demo () print ( fun ) print ( next ( fun )) print ( "-" * 30 ) print ( next ( fun )) print ( next ( fun )) print ( next ( fun )) print ( next ( fun )) print ( "!" * 30 )
#When the above program is executed, the output:
W : \ green_soft \python - 3.8.6rc1 - embed - amd64 > python test / try.Py < generator object demo at 0x0000000001DB5E40 >startyield_value------------------------------ return yieldyield_valuereturn yieldyield_valuereturn yieldyield_valuereturn yieldyield_value!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
The matter is not over yet.From the above, it is concluded that finally will be executed, and because of this, some program frameworks will use this point as database processing, let finally operate the database shutdown, etc.Thereby closing the database connection at the last execution time of the program. For example, the sample program in the fastapi framework: https://fastapi.tiangolo.com/tutorial/sql-databases/ :
# Dependency def get_db ():
db = SessionLocal () try : yield db finally :
db.Close ()
The program gets the database connection through Dependency, and closes the database in the last execution time of the program. But in actual applications, I found that this is not the case.I use uvicorn as a local back-end service.The program in finally will be executed, but every time the finally log is printed, it will be printed in the next request, that is, the database connection and It is not closed when the current request is completed, but when the next request starts. This will cause the overlap of process execution programs in a single process. It is possible that the request made later but closed first will cause the database connection of the first request to be lost (closed). This is a problem that I have investigated based on the company's business.After my test, I can solve this problem by using return db, and it will not cause the waste of database connections and other situations.
todo: Our problem may also be related to the difference between the database operation module we use and the fastapi official website example.We use the pymysql connection class, while the official website uses SessionLocal. I didn't verify it anymore.We welcome any additional attempts and no feedback.
from.database import SessionLocal , engine # Dependency def get_db ():
db = SessionLocal () try : yield db finally :
db.close ()
0 Comments