ContextVars across modules

…衆ロ難τιáo~ 提交于 2020-05-29 06:37:47

问题


I am completely newb on asyncio and ContextVars, I just read up what's new in 3.7 and discovered ContextVars, I struggle to understand it's usage, all I know it's helpful in coroutines, instead of using thread.local should use ContextVars. But none of the official doc and top google search result could help me truely understand its purpose.

So is convextvars shared across modules? I tried:

example.py

from contextvars import ContextVar

number = ContextVar('number', default=100)
number.set(1)

then I try to import number.py

(playground) Jamess-MacBook-Pro-2:playground jlin$ python3.7
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> from contextvars import ContextVar
>>> number = ContextVar('number', default=200)
>>> number.get()
200

I was expecting number.get() would return 1, but obviously I have understood its purpose wrong.

Could someone please help me understand this?


回答1:


Assume your use case is to use python thread.local to store your thread global variable inside multi thread application.

For example you store django request globally in thread.local

  • all your code has access to current (and correct) request instance
  • it works, because each django HTTP request handled in it's own python thread

No imagine you handle HTTP requests in asyncio as non blocking code executed in the same python thread.

  • HTTP request stored in thread.local isn't going to work, because multiple concurrent requests get processed in the same python thread
  • What happens is you overwrite same thread.local variable and all your code has access to latest (incorrect) request instance

In this case you use ContextVars designed to be used for this use case inside non blocking concurrent jobs running in same python thread.




回答2:


You have assigned the value for the number again. Which is why it shows 200 from the latest update.




回答3:


You are reassigning the value of number. Directly call the variable from the module, e.g. example.number.get().


A simple application is substituting a global variable.

Given

import random
import contextvars as cv

Code

Here we'll imitate a random walk. Like a global variable, we are able to share state between functions:

move = cv.ContextVar("move", default="")


def go_left():
    value = move.get()
    move.set(value + "L")


def go_right():
    value = move.get()
    move.set(value + "R")


def random_walk(steps):
    directions = [go_left, go_right]
    while steps:
        random.choice(directions)()
        steps -= 1
    return move.get()

Demo

The ContextVar acts as a global variable that is updated by random events:

random_walk(1)
# 'R'
random_walk(2)
# 'RLL'
random_walk(3)
# 'RLLLRL'

Beyond a regular global variable, ContextVar:

  • permits default values
  • works with context managers
  • works with coroutines



回答4:


Not completely sure if my approach is correct but I will add my findings to answer, so I tried to use this in my django project to set the request var without using thread local.

from contextvars import ContextVar
request_var = ContextVar('request', default=None)


def request_middleware(get_response):
    """
    Sets the request into contextvar
    """
    def middleware(request):
        ctx = copy_context()
        return ctx.run(wrap_get_response, request)

    def wrap_get_response(request):
        request_var.set(request)
        return get_response(request)
    return middleware

Then in somewhere else that need to use the request var:

from core.middlewares import request_var
request = request_var.get()

Not sure if this is the correct usage, but it seems working fine for me.

I don't know how to get the request_var from context.get() eg.

from contextvars import copy_context
ctx = copy_context()
# the key here is `request_var` obj in middlewares.py, 
# might as well just import `request_var` and use it directly?
request = ctx.get(...)  


来源:https://stackoverflow.com/questions/56371373/contextvars-across-modules

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!