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
TypeError for threading.Lock | None #114315
Comments
Here are my 2c:
This does not seem to be the case anymore, right now Even the doc above says the same thing:
This rather simple patch fixes the problem: diff --git Lib/threading.py Lib/threading.py
index 85aff589680..075d7b0dce1 100644
--- Lib/threading.py
+++ Lib/threading.py
@@ -5,7 +5,6 @@
import _thread
import functools
import warnings
-import _weakref
from time import monotonic as _time
from _weakrefset import WeakSet
@@ -108,7 +107,7 @@ def gettrace():
# Synchronization classes
-Lock = _allocate_lock
+Lock = _thread.LockType
def RLock(*args, **kwargs):
"""Factory function that returns a new reentrant lock.
diff --git Modules/_threadmodule.c Modules/_threadmodule.c
index afcf646e3bc..4037e254d22 100644
--- Modules/_threadmodule.c
+++ Modules/_threadmodule.c
@@ -349,6 +349,18 @@ lock__at_fork_reinit(lockobject *self, PyObject *Py_UNUSED(args))
}
#endif /* HAVE_FORK */
+static lockobject *newlockobject(PyObject *module);
+
+static PyObject *
+lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *module = PyType_GetModuleByDef(type, &thread_module);
+ if (module == NULL) {
+ return NULL;
+ }
+ return (PyObject *)newlockobject(module);
+}
+
static PyMethodDef lock_methods[] = {
{"acquire_lock", _PyCFunction_CAST(lock_PyThread_acquire_lock),
@@ -398,6 +410,7 @@ static PyType_Slot lock_type_slots[] = {
{Py_tp_methods, lock_methods},
{Py_tp_traverse, lock_traverse},
{Py_tp_members, lock_type_members},
+ {Py_tp_new, lock_new},
{0, 0}
};
@@ -405,7 +418,7 @@ static PyType_Spec lock_type_spec = {
.name = "_thread.lock",
.basicsize = sizeof(lockobject),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
+ Py_TPFLAGS_IMMUTABLETYPE),
.slots = lock_type_slots,
};
@@ -1439,8 +1452,6 @@ A subthread can use this function to interrupt the main thread.\n\
Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``."
);
-static lockobject *newlockobject(PyObject *module);
-
static PyObject *
thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
{
@@ -1838,10 +1849,14 @@ thread_module_exec(PyObject *module)
}
// Lock
- state->lock_type = (PyTypeObject *)PyType_FromSpec(&lock_type_spec);
+ state->lock_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &lock_type_spec, NULL);
if (state->lock_type == NULL) {
return -1;
}
if (PyDict_SetItemString(d, "LockType", (PyObject *)state->lock_type) < 0) {
return -1;
} It might need some other changes, like restricting the extra arguments. But, generally it should be fine. Demo: >>> import threading
>>> threading.Lock()
<unlocked _thread.lock object at 0x1012899a0>
>>> threading.Lock | None
_thread.lock | None |
IMO it's reasonable to allow creating instances of
Should we allow subclassing or not? Does it make sense? Does it work? I don't know the rationale for only having a |
Lets not allow subclassing. Because we never have before. |
I will make a PR soon! 👍 |
…#114479) `threading.Lock` is now the underlying class and is constructable rather than the old factory function. This allows for type annotations to refer to it which had no non-ugly way to be expressed prior to this. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Gregory P. Smith <greg@krypto.org>
Fixed in 3.13. Thanks for the discussion & PR. Changing the type of a thing in a stdlib module is not something we can backport as a bugfix unfortunately so people who actually want to annotate something as being a Lock will need to come up with a workaround or just not annotate that. Ideally APIs aren't passing locks around so I hope the need is rare. (the question of why threading.Lock wasn't a type and how to annotate for it ironically came up from someone else unrelated to this issue or PR at work this morning) |
FWIW, in typeshed we already pretend that That means that type checkers will accept using So in the meantime, you should be able to simply do this if you need to annoate something as being a foo: threading.Lock And you should be able to do this to solve the original problem the issue author was reporting: foo: "threading.Lock | None" (It's obviously not ideal to have to put quotes around annotations, so I'm glad this will be fixed properly in Python 3.13!) |
…nction (python#114479) `threading.Lock` is now the underlying class and is constructable rather than the old factory function. This allows for type annotations to refer to it which had no non-ugly way to be expressed prior to this. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Gregory P. Smith <greg@krypto.org>
Bug report
Bug description:
I tried to search for a related ticket by various queries but did not find any, hopefully I haven't overlooked it.
While rewriting a codebase from
Optional[foo]
to the newerfoo | None
style, I encountered thatthreading.Lock
does not allow the latter.Example on MacOS 3.12.1:
Given that the documentation states the following, the error makes sense:
I actually didn't know it was a factory function. I would not intentionally create a
<function> | None
type. But since the documentation describesthreading.Lock
as a class, one would expect to be able to use it as any other type.CPython versions tested on:
3.11, 3.12, 3.13.0a3
Operating systems tested on:
Linux, macOS
Linked PRs
threading.Lock
a real class, not a factory function #114479The text was updated successfully, but these errors were encountered: