[Python-Dev] PEP 567 pre v3

Yury Selivanov yselivanov.ml at gmail.com
Thu Jan 11 01:23:43 EST 2018


On Thu, Jan 11, 2018 at 4:44 AM, Nathaniel Smith <njs at pobox.com> wrote:
[..]
> It may have gotten lost in that email, but my actual favorite approach
> is that we make the signatures:
>
> ContextVar(name, *, initial_value)  # or even (*, name, initial_value)
> ContextVar.get()
> ContextVar.set(value)
>
> so that when you create a ContextVar you always state the initial
> value, whatever makes sense in a particular case. (Obviously None will
> be a very popular choice, but this way it won't be implicit, and
> no-one will be surprised to see it returned from get().)

Alright, you've shown that most of the time when we use
threading.local in the standard library we subclass it in order to
provide a default value (and avoid AttributeError being thrown).  This
is a solid argument in favour of keeping the 'default' parameter for
the ContextVar constructor.  Let's keep it.

However I still don't like the idea of making defaults mandatory.  I
have at least one exemplary use case (I can come up with more of such
examples, btw) which shows that it's not always desired to have a None
default: getting the current request object in a web application.
With the current PEP 567 semantics:

    request_var: ContextVar[Request] = ContextVar('current_request')

and later:

    request : Request = request_var.get()

'request_var.get()' will throw a LookupError, which will indicate that
something went wrong in the framework layer.  The user should never
see this error, and they can just rely on the fact that the current
request is always available (cannot be None).

With mandatory defaults, the type of 'request' variable will be
'Optional[Request]', and the user will be forced to add an 'if'
statement to guard against None values.  Otherwise the user risks
having occasional AttributeErrors that don't really explain what
actually happened.  I would prefer them to see a LookupError('cannot
lookup current_request context variable') instead.

I think that when you have an int stored in a context variable it
would usually make sense to give it a 0 default (or some other
number). However, for a complex object (like current request object)
there is *no* sensible default value sometimes.  Forcing the user to
set it to None feels like a badly designed API that forces the user to
work around it.

Therefore I'm still in favour of keeping the current PEP 567
behaviour.  It feels very consistent with how variable lookups and
threading.local objects work in Python now.

Yury


More information about the Python-Dev mailing list