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

Interned strings are immortal, despite what the documentation says #113993

Closed
encukou opened this issue Jan 12, 2024 · 37 comments
Closed

Interned strings are immortal, despite what the documentation says #113993

encukou opened this issue Jan 12, 2024 · 37 comments
Labels
topic-subinterpreters type-bug An unexpected behavior, bug, or error

Comments

@encukou
Copy link
Member

encukou commented Jan 12, 2024

Bug report

Bug description:

The sys.intern documentation explicitly says:

Interned strings are not immortal; you must keep a reference to the return value of intern() around to benefit from it.

However, they were made immortal in #19474 (Implement Immortal Objects -- implementation of PEP 683), without a documentation update. The 3.12 What's New also doesn't mention the change. [edit: it's now clear that the PR author (@eduardo-elizondo) intended it but the reviewer (@markshannon) did not]
PEP-683 itself only mentions the change as a possible optimization.

Meanwhile, pathlib interns every path segment it processes, presumably depending on the documented behaviour. In CPython's test suite, that causes names of temporary directories to leak (stealthily, since interned strings are excluded from the total refcount).
[edit: Worse, type_setattro, PyObject_SetAttr or PyDict_SetItemString immortalize keys, so strings used for key/attribute names this way are not reclaimed until interpreter shutdown.]
On the other hand, free-threading (PEP-703) seems to rely [edit: relies] on these being immortal.

What's the correct behaviour? [edit: specifically, in 3.12 and non-free-threading 3.13, should this change be reverted or documented?]

@eduardo-elizondo @ericsnowcurrently

Linked PRs

Related fixes:

@encukou encukou added the type-bug An unexpected behavior, bug, or error label Jan 12, 2024
@nascheme
Copy link
Member

I would suggest that using "immortal" here could be confusing. The flagged checked by _Py_IsImmortal() is low-level implementation detail and should not be something that the library documentation discusses. Strings that you call intern() on may or may not have this flag set. Different Python runtimes might implement intern() some other way. The behavior that matters is that the string gets stored in an interned strings table and that future calls to intern() with the same string content will return an object with the same id(), even if the user program doesn't keep a reference to the string.

@encukou
Copy link
Member Author

encukou commented Jan 15, 2024

The behavior that matters is that the string gets stored in an interned strings table and that future calls to intern() with the same string content will return an object with the same id(), even if the user program doesn't keep a reference to the string.

No. That is not the behaviour on Python 3.11.
I get:

$ python3.11
Python 3.11.7 (main, Dec 18 2023, 00:00:00) [GCC 13.2.1 20231205 (Red Hat 13.2.1-6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> id1 = id(sys.intern('some string'))
>>> other = sys.intern('other string')
>>> id2 = id(sys.intern('some string'))
>>> id1 == id2
False

IMO, the guarantee is that if two strings are interned, they can be compared by identity.
CPython uses this for a fast path in string comparison; so some users call intern on strings that are likely to be compared to one another -- even ones that srent expected to live until interpreter shutdown. We can say that's optmizing for a specific implementation, but it's common -- even pathlib and the compiler (e.g. eval) do it.

The new behaviour can leak strings that are not likely to be used again.

@erlend-aasland
Copy link
Contributor

$ cat test.py
import sys
id1 = id(sys.intern('some string'))
other = sys.intern('other string')
id2 = id(sys.intern('some string'))
assert id1 == id2
$ python3.8 ./test.py
$ python3.9 ./test.py
$ python3.10 ./test.py
$ python3.11 ./test.py
$ python3.12 ./test.py
$ python3.13 ./test.py

No assertions triggered.

@encukou
Copy link
Member Author

encukou commented Jan 15, 2024

The the new string is allowed to reuse the old one's memory; I guess this is build-specific. What platform are you on?
Instead of other = sys.intern('other string'), try putting in gc.collect and something more intensive, like import asyncio?

@erlend-aasland
Copy link
Contributor

I'm on macOS using the official build. Strange thing: I can reproduce the behaviour you're seeing in the REPL.

@erlend-aasland
Copy link
Contributor

Instead of other = sys.intern('other string'), try putting in gc.collect and something more intensive, like import asyncio?

Nope, cannot reproduce using $ python3.11 ./test.py as above.

@encukou
Copy link
Member Author

encukou commented Jan 15, 2024

Ah! Right, literal constants are part the code object, which does normally stay around. You'll need something that's not folded to a constant, like:

$ cat test.py 
import sys
def intern_and_report(string):
    string = sys.intern(string)
    the_id = id(string)
    print(f'{string!r} at {id(string)}: refcount {sys.getrefcount(string):x}')
    return the_id

id1 = intern_and_report('a' + __name__)
str_b = sys.intern('b' + __name__)
id2 = intern_and_report('a' + __name__)
assert id1 == id2
$ python3.10 ./test.py
'a__main__' at 139775546163056: refcount 3
'a__main__' at 139775546294640: refcount 3
Traceback (most recent call last):
  File "/tmp/rttair/./test.py", line 11, in <module>
    assert id1 == id2
AssertionError

$ python3.12 ./test.py
'a__main__' at 139964910181616: refcount ffffffff
'a__main__' at 139964910181616: refcount ffffffff

@erlend-aasland
Copy link
Contributor

Yes; thanks!

@erlend-aasland
Copy link
Contributor

See also #113601

@eduardo-elizondo
Copy link
Contributor

@encukou just getting to this! You're right, as of now, all interned strings should be immortal, so the sys.intern docs should probably be updated to update that they are indeed immortal and the fact that we don't need to keep a reference to benefit from it anymore.

As for the leaks, as @erlend-aasland already called out, we have a PR to correctly clean them up in #113601!

@nascheme
Copy link
Member

The behavior that matters is that the string gets stored in an interned strings table and that future calls to intern() with the same string content will return an object with the same id(), even if the user program doesn't keep a reference to the string.

No. That is not the behaviour on Python 3.11.

Sorry, I was not very clear. I was describing the behaviour we have with Python 3.12 and we had with older versions of Python (version 2.2 and before, it seems). It looks like this commit changed it: 45ec02a.

Since 3.12 changed this, definitely the documentation should be updated. Further, I think we should have had a discussion and explicitly decide if this is desirable behaviour, since it reverts the effect of 45ec02a. Maybe there was one and I missed it? Perhaps free-threading doesn't require that every interned string become immortal but only some of them (e.g. symbols used by code objects etc). That might preserve free-threaded performance (e.g. interned strings shared between threads) while avoid the "leak" like encountered by pathlib (e.g. interned strings only used for a short time and then discarded).

@encukou
Copy link
Member Author

encukou commented Jan 16, 2024

@eduardo-elizondo

as of now, all interned strings should be immortal

To be clear, I think this is bad. Some interned strings are temporary, and they now leak. They should be cleaned up sooner that at interpreter shutdown.

@jvs
Copy link

jvs commented May 25, 2024

The documentation for sys.intern explains, "Interned strings are not immortal; you must keep a reference to the return value of intern() around to benefit from it."

Some open source libraries depend on this behavior, like msgpack-python. Changing the behavior of sys.intern will cause problems for some applications. I work on an application that is experiencing out-of-memory errors because of this issue.

One solution might be to add a separate function called sys.immortalize(string). Alternately, an optional argument to sys.intern could also do the trick. For example, sys.intern(s, immortalize=True).

Restoring the old, documented behavior of sys.intern would save people a lot of trouble. Making the new behavior opt-in (with a separate function or an optional argument) might be a way to have our cake and eat it too.

@jvs
Copy link

jvs commented May 28, 2024

Does this mean that PyDict_SetItemString will also immortalize its key argument? Will every key passed to PyDict_SetItemString live forever?

It seems like that could also be a source of leaks. Maybe the documentation for PyDict_SetItemString should be updated to indicate that key will be kept alive forever.

Alternately, maybe PyDict_SetItemString should not call PyUnicode_InternInPlace. I notice a comment on that line asks, XXX Should we really?. Seems like a good question.

@markshannon
Copy link
Member

I don't see a good reason why interning strings should make them immortal.
The two things, immortality and interning, are separate concepts.

Why were interned strings changed to be immortal in 3.12?

Since interned strings that are tied to code objects are effectively immortal, making those actually immortal seems fine if necessary for performance.

But strings interned with sys.intern() should not be made immortal implicitly, IMO.
@jvs suggestion of adding an immortalize argument to sys.intern() seems a good one.

@markshannon
Copy link
Member

The immortal objects PR seems to allow interned, but not immortals strings:
https://github.com/python/cpython/pull/19474/files#diff-6be7f081fe6c5e9cbc89323a00399b291d1cda855bcb4c6eeaee0fac89c2f8ddR105
so it is a bit surprising that they aren't supported.

@markshannon
Copy link
Member

It might also be worth making the distinction between "interpreter immortal" and "process immortal" strings, as the latter can be shared between multiple interpreters.

@colesbury
Copy link
Contributor

The free-threaded build needs every interned string to be immortal, not just symbols, if you don't want to introduce bottlenecks that prevent scaling in multi-threaded programs.

The documentation can describe the possible behaviors without promising a specific implementation. Something like:

"Depending on the Python version and implementation, interned strings may or may not be immortal; you should keep a reference to the return value of intern() around to benefit from it."

@markshannon
Copy link
Member

markshannon commented May 29, 2024

Why do all interned strings need to be immortal?
Strings where there is potentially high contention on the refcount need to be immortal, to allow scaling.
But that only implies that interned strings must be immortal if there is a strong correlation between contention and interning.
Is there such a strong correlation?

Are there not strings that are contended and are not interned, or does the free-threading build intern so many strings that all possibly contended strings are interned?

@colesbury
Copy link
Contributor

Interning implies sharing and sharing between threads leads to reference count contention. To take the pathlib example, I'd much rather pathlib did not intern strings, but if it does intern the components, it's important that those strings are immortalized, because otherwise you are likely to have a bunch of strings shared between threads that otherwise would not be shared.

Are there not strings that are contended and are not interned...?

It's definitely possible, but from what I've seen it's not the most common source of reference count contention. If you don't intern a string, it's much less likely to be incidentally shared between threads.

@jvs
Copy link

jvs commented May 29, 2024

This seems like a breaking change that is kind of flying under the radar. The documentation explains, "Interned strings are not immortal; you must keep a reference to the return value of intern() around to benefit from it."

It looks like it been that way for 20 years

I'd be worried that there are many applications that depend on the old behavior. If there's a way to maintain backwards compatibility, while also finding a way to avoid bottlenecks around shared strings, then that might be the best option.

@methane
Copy link
Member

methane commented May 30, 2024

Can we make it mortal again on --enable-GIL build?

@encukou
Copy link
Member Author

encukou commented May 30, 2024

Right. It looks like free-threading needs this, and the ecosystem (and stdlib) will need to adapt, so the question is how soon.
Specifically, what should we do in 3.12 -- revert the change, or document it?

I guess ultimately the RM should decide that.

encukou added a commit to encukou/cpython that referenced this issue Jul 4, 2024
…sImmortal

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
noahbkim pushed a commit to hudson-trading/cpython that referenced this issue Jul 11, 2024
… issues (pythonGH-120520)


* Add an InternalDocs file describing how interning should work and how to use it.

* Add internal functions to *explicitly* request what kind of interning is done:
  - `_PyUnicode_InternMortal`
  - `_PyUnicode_InternImmortal`
  - `_PyUnicode_InternStatic`

* Switch uses of `PyUnicode_InternInPlace` to those.

* Disallow using `_Py_SetImmortal` on strings directly.
  You should use `_PyUnicode_InternImmortal` instead:
  - Strings should be interned before immortalization, otherwise you're possibly
    interning a immortalizing copy.
  - `_Py_SetImmortal` doesn't handle the `SSTATE_INTERNED_MORTAL` to
    `SSTATE_INTERNED_IMMORTAL` update, and those flags can't be changed in
    backports, as they are now part of public API and version-specific ABI.

* Add private `_only_immortal` argument for `sys.getunicodeinternedsize`, used in refleak test machinery.

* Make sure the statically allocated string singletons are unique. This means these sets are now disjoint:
  - `_Py_ID`
  - `_Py_STR` (including the empty string)
  - one-character latin-1 singletons

  Now, when you intern a singleton, that exact singleton will be interned.

* Add a `_Py_LATIN1_CHR` macro, use it instead of `_Py_ID`/`_Py_STR` for one-character latin-1 singletons everywhere (including Clinic).

* Intern `_Py_STR` singletons at startup.

* For free-threaded builds, intern `_Py_LATIN1_CHR` singletons at startup.

* Beef up the tests. Cover internal details (marked with `@cpython_only`).

* Add lots of assertions

Co-Authored-By: Eric Snow <ericsnowcurrently@gmail.com>
encukou added a commit that referenced this issue Jul 16, 2024
…ortal (GH-121358)

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 16, 2024
…_IsImmortal (pythonGH-121358)

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
(cherry picked from commit 956270d)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
encukou added a commit that referenced this issue Jul 16, 2024
…lizing in other API (#121364)

* Switch PyUnicode_InternInPlace to _PyUnicode_InternMortal, clarify docs

* Document immortality in some functions that take `const char *`

This is PyUnicode_InternFromString;
PyDict_SetItemString, PyObject_SetAttrString;
PyObject_DelAttrString; PyUnicode_InternFromString;
and the PyModule_Add convenience functions.

Always point out a non-immortalizing alternative.

* Don't immortalize user-provided attr names in _ctypes
encukou added a commit that referenced this issue Jul 16, 2024
…y_IsImmortal (GH-121358) (GH-121851)

gh-113993: For string interning, do not rely on (or assert) _Py_IsImmortal (GH-121358)

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
(cherry picked from commit 956270d)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
encukou added a commit to encukou/cpython that referenced this issue Jul 16, 2024
… keep immortalizing in other API (pythonGH-121364)

* Switch PyUnicode_InternInPlace to _PyUnicode_InternMortal, clarify docs

* Document immortality in some functions that take `const char *`

This is PyUnicode_InternFromString;
PyDict_SetItemString, PyObject_SetAttrString;
PyObject_DelAttrString; PyUnicode_InternFromString;
and the PyModule_Add convenience functions.

Always point out a non-immortalizing alternative.

* Don't immortalize user-provided attr names in _ctypes
(cherry picked from commit b4aedb2)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
… issues (pythonGH-120520)


* Add an InternalDocs file describing how interning should work and how to use it.

* Add internal functions to *explicitly* request what kind of interning is done:
  - `_PyUnicode_InternMortal`
  - `_PyUnicode_InternImmortal`
  - `_PyUnicode_InternStatic`

* Switch uses of `PyUnicode_InternInPlace` to those.

* Disallow using `_Py_SetImmortal` on strings directly.
  You should use `_PyUnicode_InternImmortal` instead:
  - Strings should be interned before immortalization, otherwise you're possibly
    interning a immortalizing copy.
  - `_Py_SetImmortal` doesn't handle the `SSTATE_INTERNED_MORTAL` to
    `SSTATE_INTERNED_IMMORTAL` update, and those flags can't be changed in
    backports, as they are now part of public API and version-specific ABI.

* Add private `_only_immortal` argument for `sys.getunicodeinternedsize`, used in refleak test machinery.

* Make sure the statically allocated string singletons are unique. This means these sets are now disjoint:
  - `_Py_ID`
  - `_Py_STR` (including the empty string)
  - one-character latin-1 singletons

  Now, when you intern a singleton, that exact singleton will be interned.

* Add a `_Py_LATIN1_CHR` macro, use it instead of `_Py_ID`/`_Py_STR` for one-character latin-1 singletons everywhere (including Clinic).

* Intern `_Py_STR` singletons at startup.

* For free-threaded builds, intern `_Py_LATIN1_CHR` singletons at startup.

* Beef up the tests. Cover internal details (marked with `@cpython_only`).

* Add lots of assertions

Co-Authored-By: Eric Snow <ericsnowcurrently@gmail.com>
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
…_IsImmortal (pythonGH-121358)

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
…mmortalizing in other API (python#121364)

* Switch PyUnicode_InternInPlace to _PyUnicode_InternMortal, clarify docs

* Document immortality in some functions that take `const char *`

This is PyUnicode_InternFromString;
PyDict_SetItemString, PyObject_SetAttrString;
PyObject_DelAttrString; PyUnicode_InternFromString;
and the PyModule_Add convenience functions.

Always point out a non-immortalizing alternative.

* Don't immortalize user-provided attr names in _ctypes
encukou added a commit that referenced this issue Jul 17, 2024
…immortalizing in other API (GH-121364) (GH-121854)

* Switch PyUnicode_InternInPlace to _PyUnicode_InternMortal, clarify docs

* Document immortality in some functions that take `const char *`

This is PyUnicode_InternFromString;
PyDict_SetItemString, PyObject_SetAttrString;
PyObject_DelAttrString; PyUnicode_InternFromString;
and the PyModule_Add convenience functions.

Always point out a non-immortalizing alternative.

* Don't immortalize user-provided attr names in _ctypes
(cherry picked from commit b4aedb2)
encukou added a commit to encukou/cpython that referenced this issue Aug 16, 2024
…related issues (pythonGH-120520)

* Add an InternalDocs file describing how interning should work and how to use it.

* Add internal functions to *explicitly* request what kind of interning is done:
  - `_PyUnicode_InternMortal`
  - `_PyUnicode_InternImmortal`
  - `_PyUnicode_InternStatic`

* Switch uses of `PyUnicode_InternInPlace` to those.

* Disallow using `_Py_SetImmortal` on strings directly.
  You should use `_PyUnicode_InternImmortal` instead:
  - Strings should be interned before immortalization, otherwise you're possibly
    interning a immortalizing copy.
  - `_Py_SetImmortal` doesn't handle the `SSTATE_INTERNED_MORTAL` to
    `SSTATE_INTERNED_IMMORTAL` update, and those flags can't be changed in
    backports, as they are now part of public API and version-specific ABI.

* Add private `_only_immortal` argument for `sys.getunicodeinternedsize`, used in refleak test machinery.

* Make sure the statically allocated string singletons are unique. This means these sets are now disjoint:
  - `_Py_ID`
  - `_Py_STR` (including the empty string)
  - one-character latin-1 singletons

  Now, when you intern a singleton, that exact singleton will be interned.

* Add a `_Py_LATIN1_CHR` macro, use it instead of `_Py_ID`/`_Py_STR` for one-character latin-1 singletons everywhere (including Clinic).

* Intern `_Py_STR` singletons at startup.

* Beef up the tests. Cover internal details (marked with `@cpython_only`).

* Add lots of assertions

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
encukou added a commit to encukou/cpython that referenced this issue Aug 16, 2024
… keep immortalizing in other API (pythonGH-121364)

* Switch PyUnicode_InternInPlace to _PyUnicode_InternMortal, clarify docs

* Document immortality in some functions that take `const char *`

This is PyUnicode_InternFromString;
PyDict_SetItemString, PyObject_SetAttrString;
PyObject_DelAttrString; PyUnicode_InternFromString;
and the PyModule_Add convenience functions.

Always point out a non-immortalizing alternative.

* Don't immortalize user-provided attr names in _ctypes
(cherry picked from commit b4aedb2)
@encukou
Copy link
Member Author

encukou commented Aug 28, 2024

The 3.12 backport is at #123065.

@Yhg1s, re:

Yes, I agree this should be reverted in 3.12, and conditional on free threading in 3.13. That's my opinion as RM, but considering the discussions the DC had about the immortal objects PEP, I'm sure they all agree.

Unfortunately it's far from a straightforward revert.
Do you still want this in 3.12? Should I ask the SC?

Yhg1s pushed a commit that referenced this issue Sep 27, 2024
…H-121903, GH-122303) (#123065)

This backports several PRs for gh-113993, making interned strings mortal so they can be garbage-collected when no longer needed.

* Allow interned strings to be mortal, and fix related issues (GH-120520)

  * Add an InternalDocs file describing how interning should work and how to use it.

  * Add internal functions to *explicitly* request what kind of interning is done:
    - `_PyUnicode_InternMortal`
    - `_PyUnicode_InternImmortal`
    - `_PyUnicode_InternStatic`

  * Switch uses of `PyUnicode_InternInPlace` to those.

  * Disallow using `_Py_SetImmortal` on strings directly.
    You should use `_PyUnicode_InternImmortal` instead:
    - Strings should be interned before immortalization, otherwise you're possibly
      interning a immortalizing copy.
    - `_Py_SetImmortal` doesn't handle the `SSTATE_INTERNED_MORTAL` to
      `SSTATE_INTERNED_IMMORTAL` update, and those flags can't be changed in
      backports, as they are now part of public API and version-specific ABI.

  * Add private `_only_immortal` argument for `sys.getunicodeinternedsize`, used in refleak test machinery.

   Make sure the statically allocated string singletons are unique. This means these sets are now disjoint:
    - `_Py_ID`
    - `_Py_STR` (including the empty string)
    - one-character latin-1 singletons

    Now, when you intern a singleton, that exact singleton will be interned.

  * Add a `_Py_LATIN1_CHR` macro, use it instead of `_Py_ID`/`_Py_STR` for one-character latin-1 singletons everywhere (including Clinic).

  * Intern `_Py_STR` singletons at startup.

  * Beef up the tests. Cover internal details (marked with `@cpython_only`).

  * Add lots of assertions

* Don't immortalize in PyUnicode_InternInPlace; keep immortalizing in other API (GH-121364)

  * Switch PyUnicode_InternInPlace to _PyUnicode_InternMortal, clarify docs

  * Document immortality in some functions that take `const char *`

  This is PyUnicode_InternFromString;
  PyDict_SetItemString, PyObject_SetAttrString;
  PyObject_DelAttrString; PyUnicode_InternFromString;
  and the PyModule_Add convenience functions.

  Always point out a non-immortalizing alternative.

  * Don't immortalize user-provided attr names in _ctypes

* Immortalize names in code objects to avoid crash (GH-121903)

* Intern latin-1 one-byte strings at startup (GH-122303)

There are some 3.12-specific changes, mainly to allow statically allocated strings in deepfreeze. (In 3.13, deepfreeze switched to the general `_Py_ID`/`_Py_STR`.)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
@encukou encukou closed this as completed Sep 27, 2024
@github-project-automation github-project-automation bot moved this from Todo to Done in Subinterpreters Sep 27, 2024
encukou added a commit to encukou/cpython that referenced this issue Oct 3, 2024
…rt) _Py_IsImmortal (pythonGH-121358) (pythonGH-121851)

pythongh-113993: For string interning, do not rely on (or assert) _Py_IsImmortal (pythonGH-121358)

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
(cherry picked from commit 956270d)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
encukou added a commit that referenced this issue Oct 4, 2024
…y_IsImmortal (GH-121358) (GH-124938)

gh-113993: For string interning, do not rely on (or assert) _Py_IsImmortal (GH-121358)

Older stable ABI extensions are allowed to make immortal objects mortal.
Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`).
(cherry picked from commit 956270d)

Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-subinterpreters type-bug An unexpected behavior, bug, or error
Projects
Status: Done
Development

No branches or pull requests