Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No type checking for thread local variables? #2388

Open
jgarvin opened this issue Nov 2, 2016 · 4 comments
Open

No type checking for thread local variables? #2388

jgarvin opened this issue Nov 2, 2016 · 4 comments

Comments

@jgarvin
Copy link

jgarvin commented Nov 2, 2016

The following type checks without errors even though I am running with "--disallow-untyped-defs --disallow-untyped-calls".

import threading
_parameter_values = threading.local()
_parameter_values.data = {}
_parameter_values.data[3] = "hello"
_parameter_values.data["hello"] = 3

and if I deliberately try to add annotations:

import threading
_parameter_values = threading.local()
_parameter_values.data: Dict[int, str] = {}
_parameter_values.data[3] = "hello"

then I get the following error:
error: Type cannot be declared in assignment to non-self attribute

@gvanrossum gvanrossum added this to the Future milestone Nov 2, 2016
@gvanrossum
Copy link
Member

The former is blocked by #521 (it even says so in the stub for threading.local).

The latter would be a pretty advanced feature -- while PEP 526 allows this syntax it doesn't mandate that type checkers should support it. But IIRC you can subclass threading.local and then you can use annotations in the class definition.

@rwbarton
Copy link
Contributor

rwbarton commented Nov 2, 2016

I don't see how we can sensibly assign a type to the result of threading.local() since it is truly global, i.e., not scoped to any individual module. There's no way that we can verify that separate modules (which will execute in the same thread) access threading.local() in a well-typed fashion, because there's nowhere to put a type declaration.

Within a module, you could adopt something like the following style.

import threading
_parameter_values = threading.local()
local_data: Dict[int, str] = {}
_parameter_values.data = local_data
local_data[3] = "hello"

and then only access local_data, not _parameter_values.data directly.

@gvanrossum
Copy link
Member

But see the docstring of _threading_local.py (which apparently is the only reference, the official docs for threading.oocal point there):

You can create custom local objects by subclassing the local class:

  >>> class MyLocal(local):
  ...     number = 2
  ...     initialized = False
  ...     def __init__(self, **kw):
  ...         if self.initialized:
  ...             raise SystemError('__init__ called too many times')
  ...         self.initialized = True
  ...         self.__dict__.update(kw)
  ...     def squared(self):
  ...         return self.number ** 2

This can be useful to support default values, methods and
initialization.  Note that if you define an __init__ method, it will be
called each time the local object is used in a separate thread.  This
is necessary to initialize each thread's dictionary.

Now if we create a local object:

  >>> mydata = MyLocal(color='red')

Now we have a default number:

  >>> mydata.number
  2

an initial color:

  >>> mydata.color
  'red'
  >>> del mydata.color

And a method that operates on the data:

  >>> mydata.squared()
  4

As before, we can access the data in a separate thread:

  >>> log = []
  >>> thread = threading.Thread(target=f)
  >>> thread.start()
  >>> thread.join()
  >>> log
  [[('color', 'red'), ('initialized', True)], 11]

without affecting this thread's data:

  >>> mydata.number
  2
  >>> mydata.color
  Traceback (most recent call last):
  ...
  AttributeError: 'MyLocal' object has no attribute 'color'

@gvanrossum gvanrossum removed this from the Future milestone Mar 29, 2017
@dimaqq
Copy link

dimaqq commented Apr 27, 2022

I'd love the latter.

Not so much for thread-local variables, rather to allow 3rd party library extensions, like pyropy/fastapi-socketio#27

Or, perhaps, what I'm thinking of could be done in some other way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants