Skip to content

GH-114781 potentially breaks gevent: threading becomes pre-imported at startup #117983

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

Closed
navytux opened this issue Apr 17, 2024 · 19 comments
Closed
Labels
type-bug An unexpected behavior, bug, or error

Comments

@navytux
Copy link

navytux commented Apr 17, 2024

Bug report

Bug description:

Hello up there.

I've discovered that starting from CPython >= 3.11.10 gevent-based applications
become potentially broken because, in a venv with gevent installed, threading
module becomes pre-imported by python stdlib itself. As gevent injects its own
implementation of threading primitives, it helps to know that there are no
instances of C-implemented locks at the time of the monkey-patching: if there
are no such instances(*) the program is known to have only instances of
gevent-provided locks and it will not run into deadlock scenarios
described at e.g. gevent/gevent#1865 where the code
tries to take a lock, that lock turns to be standard thread lock, but there are
only greenlets running and there is no other real thread to release that lock.

So not having threading pre-imported at startup is very desirable property in
the context of gevent. For this reason gpython from pygolang actually verifies
that invariant and it is through which I've discovered the issue when testing
pygolang on py3.11.10:

(py311-venv) kirr@deca:~/src/tools/go/pygolang-master$ gpython
Traceback (most recent call last):
  File "/home/kirr/src/tools/go/py311-venv/bin/gpython", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/kirr/src/tools/go/pygolang-master/gpython/__init__.py", line 368, in main
    raise RuntimeError('gpython: internal error: the following modules are pre-imported, but must be not:'
RuntimeError: gpython: internal error: the following modules are pre-imported, but must be not:

        ['threading']

sys.modules:

        ['__editable___pygolang_0_1_finder', '__future__', '__main__', '_abc', '_codecs', '_collections', '_collections_abc', '_distutils_hack', '_frozen_importlib', '_frozen_importlib_external', '_functools', '_imp', '_io', '_operator', '_signal', '_sitebuiltins', '_sre', '_stat', '_thread', '_warnings', '_weakref', '_weakrefset', 'abc', 'builtins', 'codecs', 'collections', 'contextlib', 'copyreg', 'encodings', 'encodings.aliases', 'encodings.utf_8', 'enum', 'errno', 'fnmatch', 'functools', 'genericpath', 'gpython', 'importlib', 'importlib._abc', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib.machinery', 'importlib.util', 'io', 'ipaddress', 'itertools', 'keyword', 'marshal', 'ntpath', 'operator', 'os', 'os.path', 'pathlib', 'posix', 'posixpath', 're', 're._casefix', 're._compiler', 're._constants', 're._parser', 'reprlib', 'site', 'stat', 'sys', 'threading', 'time', 'types', 'urllib', 'urllib.parse', 'warnings', 'zipimport', 'zope']

The problem is there because importlib.util started to import threading after
46f821d62b5a, and because setuptools
emits importlib.util usage in installed *-nspkg.pth and in generated finders for packages installed in editable mode:

https://github.com/pypa/setuptools/blob/92b45e9817ae829a5ca5a5962313a56b943cad91/setuptools/namespaces.py#L46-L61
https://github.com/pypa/setuptools/blob/92b45e98/setuptools/command/editable_wheel.py#L786-L790

So for example the following breaks because e.g. zope.event is installed via nspkg way:

kirr@deca:~/tmp/trashme/X$ python3.12 -m venv 1.venv
kirr@deca:~/tmp/trashme/X$ . 1.venv/bin/activate
(1.venv) kirr@deca:~/tmp/trashme/X$ pip list
Package Version
------- -------
pip     24.0

(1.venv) kirr@deca:~/tmp/trashme/X$ pip install zope.event
Collecting zope.event
  Using cached zope.event-5.0-py3-none-any.whl.metadata (4.4 kB)
Collecting setuptools (from zope.event)
  Using cached setuptools-69.5.1-py3-none-any.whl.metadata (6.2 kB)
Using cached zope.event-5.0-py3-none-any.whl (6.8 kB)
Using cached setuptools-69.5.1-py3-none-any.whl (894 kB)
Installing collected packages: setuptools, zope.event
Successfully installed setuptools-69.5.1 zope.event-5.0

(1.venv) kirr@deca:~/tmp/trashme/X$ python -c 'import sys; assert "threading" not in sys.modules, sys.modules'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError: {'threading': <module 'threading' ...>, 'importlib.util': <module 'importlib.util' (frozen)>, ...}

So would you please consider applying the following suggested patch to fix this problem:

--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -15,7 +15,7 @@
 import _imp
 import functools
 import sys
-import threading
+#import threading   delayed to avoid pre-importing threading early at startup
 import types
 import warnings
 
@@ -316,7 +316,7 @@ def exec_module(self, module):
         loader_state = {}
         loader_state['__dict__'] = module.__dict__.copy()
         loader_state['__class__'] = module.__class__
-        loader_state['lock'] = threading.RLock()
+        loader_state['lock'] = __import__('threading').RLock()
         loader_state['is_loading'] = False
         module.__spec__.loader_state = loader_state
         module.__class__ = _LazyModule

?

Thanks beforehand,
Kirill

/cc @effigies, @vstinner, @ericsnowcurrently, @doko42, @brettcannon
/cc @jamadden, @arcivanov, @maciejp-ro
/cc @eduardo-elizondo, @wilson3q, @vsajip

(*) or the list of C-implemented lock instances is limited and well defined -
for example gevent reinstantiates thrading._active_limbo_lock on the
monkey-patching.

CPython versions tested on:

3.11, 3.12, CPython main branch

Operating systems tested on:

Linux

Linked PRs

@navytux navytux added the type-bug An unexpected behavior, bug, or error label Apr 17, 2024
@jaraco
Copy link
Member

jaraco commented Apr 17, 2024

I note that zope is attempting to get away from pkg_resources-based namespace packages, which would bypass this issue, but only until another package came along that imported importlib.util early.

It seems not unreasonable to me to defer the import of threading to facilitate gevent.

@effigies
Copy link
Contributor

Just a quick note that I had imported threading locally in the first implementation, but committed 57fe084 in response to #114781 (comment).

Trivially revertible, and it does still seem cleaner to me not to import threading unless lazy loading is actually used.

@vstinner
Copy link
Member

Python doesn't support monkey patching on the standard library. This change has an impact on a core feature of Python, imports. I don't think that it's reasonable.

gevent should adapt its monkey patching mecanism for latest importlib changes.

@jaraco
Copy link
Member

jaraco commented Apr 17, 2024

Is it a negative impact on a core feature? Doesn't deferring the import of 'threading' actually improve import times of importlib.util? We've been doing a lot of deferred imports for performance reasons. What is the downside to deferring the threading import?

@vstinner
Copy link
Member

What is the downside to deferring the threading import?

It can slow down exec_module(). Also, it's kind of weird that executing a module during an import will import indirectly another import :-( The threading module is special and is injected as part of the importlib bootstrap process.

@navytux
Copy link
Author

navytux commented Apr 18, 2024

@jaraco, @effigies, @vstinner, thanks for feedback.

@vstinner, I can understand your arguments, and that was the reason I was hesitant to open this issue in the first place.

But also: here we are talking about LazyLoader - the thing that is intentionally deferring loading something:

class LazyLoader(Loader):
"""A loader that creates a module which defers loading until attribute access."""
@staticmethod
def __check_eager_loader(loader):
if not hasattr(loader, 'exec_module'):
raise TypeError('loader must define exec_module()')
@classmethod
def factory(cls, loader):
"""Construct a callable which returns the eager loader made lazy."""
cls.__check_eager_loader(loader)
return lambda *args, **kwargs: cls(loader(*args, **kwargs))
def __init__(self, loader):
self.__check_eager_loader(loader)
self.loader = loader
def create_module(self, spec):
return self.loader.create_module(spec)
def exec_module(self, module):
"""Make the module load lazily."""
module.__spec__.loader = self.loader
module.__loader__ = self.loader
# Don't need to worry about deep-copying as trying to set an attribute
# on an object would have triggered the load,
# e.g. ``module.__spec__.loader = None`` would trigger a load from
# trying to access module.__spec__.
loader_state = {}
loader_state['__dict__'] = module.__dict__.copy()
loader_state['__class__'] = module.__class__
loader_state['lock'] = threading.RLock()
loader_state['is_loading'] = False
module.__spec__.loader_state = loader_state
module.__class__ = _LazyModule

From this point of view to me it is ok to say that the functionality, that only that LazyLoader uses, is reasonable to import only when it is actually needed the first time. No?

Sure, gevent needs can be smashed very easily with the argument "python does not care about gevent monkey patching at all". If the level of required cooperation from the python side would be significant, that argument would indeed make sense, but here we are talking about insignificant amount of changes and it is really the attitude, not technical bits, which becomes decision driver.

Even though "python way" is all asyncio this days, there are still many users of gevent out there. It would be good if the needs of those python+gevent users are also heard.

Kirill

@navytux
Copy link
Author

navytux commented Apr 18, 2024

P.S. I have no problem with running v3.11.9-4-g3bc0d2b851b with my suggested patch locally: make test passes fully and every other application that I use also work ok.

@ericsnowcurrently
Copy link
Member

I might have suggested messing around with .pth files, but adding a new one won't necessarily help since they are loaded in the order they are identified by os.listdir(). Perhaps that could work via the "user site dir", but probably not. (I don't recall how the ordering works out in Lib/site.py, especially for venv.)

@serhiy-storchaka
Copy link
Member

Threading support is only needed in multithreaded program. What if make importlib.util checking whether threading has been imported and only use locks if it was imported? importlib.util could use already imported threading if it was imported and not import it itself.

@effigies
Copy link
Contributor

I believe this example would fail if you did that:

import lazy_loader as lazy  # Assume `lazy_loader` does not import threading itself

np = lazy.load('numpy')

import threading

def trigger():
    return np.__version__

def race():
    for _ in range(10):
        threading.Thread(target=trigger).start()

race()

(The most recent version of lazy_loader does in fact import threading, but there are many examples of ad hoc implementations based on the importlib.utils docs that do not import threading.)

The realistic scenario I see that would make conditionally using locks a problem would be a project that internally uses lazy loading to make its full API available with a single top-level import. If that gets imported before threading, we would be reintroducing potential races that are currently guarded against.

@effigies
Copy link
Contributor

effigies commented Jun 7, 2024

With another round of releases coming out, I feel like this needs resolution.

I understand that monkey-patching the stdlib is unsupported, but this feels like a weird hill to die on, given that deferred imports are accepted practice elsewhere (e.g., #114664):

My criterion for delayed imports is that they're only worth it if the majority of users of the module would benefit from it, otherwise you're just moving latency around unpredictably.

As the person who added the threading import, I can confidently assert that this is an unnecessary import in all cases that do not involve lazy loading. Given that lazy loading is not used in the stdlib, and there are many cases where a user cannot preempt importlib.util from loading, the majority of users will benefit from not adding an obligatory threading import.

On the other hand, lazy loading is used when there is a performance gain to deferring the third-party module load, and slightly adding latency to the first lazy-load simply adjusts the calculation of whether a module is worth loading eagerly or lazily. If the difference of an extra import threading shifts that balance, the difference probably doesn't matter.

I will open a PR that I hope will be backported, and I would ask the core developers to close this issue along with that PR if there is no intention of accepting a patch.

brettcannon pushed a commit that referenced this issue Jul 3, 2024
As noted in gh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 3, 2024
…ythonGH-120233)

As noted in pythongh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
(cherry picked from commit 94f50f8)

Co-authored-by: Chris Markiewicz <[email protected]>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 3, 2024
…ythonGH-120233)

As noted in pythongh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
(cherry picked from commit 94f50f8)

Co-authored-by: Chris Markiewicz <[email protected]>
brettcannon pushed a commit that referenced this issue Jul 3, 2024
…H-120233) (GH-121350)

gh-117983: Defer import of threading for lazy module loading (GH-120233)

As noted in gh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
(cherry picked from commit 94f50f8)

Co-authored-by: Chris Markiewicz <[email protected]>
brettcannon pushed a commit that referenced this issue Jul 3, 2024
…H-120233) (GH-121349)

gh-117983: Defer import of threading for lazy module loading (GH-120233)

As noted in gh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
(cherry picked from commit 94f50f8)

Co-authored-by: Chris Markiewicz <[email protected]>
@brettcannon
Copy link
Member

All merged!

@brettcannon
Copy link
Member

And thanks to @effigies for staying on top of this!

@navytux
Copy link
Author

navytux commented Jul 4, 2024

@brettcannon, thanks a lot for merging the fixes, and @effigies, thanks, once again, for improving things here.

I see that the patch landed to 3.12, 3.13 and main, but, as original description of the issue explains, the bug was introduced in 3.11.10, so is there a possibility to backport to 3.11 as well?

@vsajip
Copy link
Member

vsajip commented Jul 4, 2024

Probably not, as versions 3.11 and earlier receive security fixes only.

@navytux
Copy link
Author

navytux commented Jul 4, 2024

@vsajip, thanks for feedback. I understand your reasoning, but it was a regression introduced in 3.11.10. From this point of view this patch is not a new addition, but reverts that regression back. In other words it does not add something new, but removes what was added in a recent point release and found to cause problems. For me it goes as common sense to apply the fix to 3.11.x as well, because else 3.11 leaves the whole gevent ecosystem in broken state.

@effigies
Copy link
Contributor

effigies commented Jul 4, 2024

#120233 (comment)

@jaraco
Copy link
Member

jaraco commented Jul 4, 2024

@vsajip, thanks for feedback. I understand your reasoning, but it was a regression introduced in 3.11.10. From this point of view this patch is not a new addition, but reverts that regression back. In other words it does not add something new, but removes what was added in a recent point release and found to cause problems. For me it goes as common sense to apply the fix to 3.11.x as well, because else 3.11 leaves the whole gevent ecosystem in broken state.

@pablogsal Would you consider this request to backport to 3.11 one more time in light of this additional information (that the downstream regression was introduced in a security-fix release)? (apologies if that was already a factor in your consideration)

@brettcannon
Copy link
Member

@jaraco the change was actually made while 3.11 was open to bugfixes: 46f821d was committed on Feb 26 and Python 3.11 entered security-only on April 2 w/ the 3.11.9 release.

noahbkim pushed a commit to hudson-trading/cpython that referenced this issue Jul 11, 2024
…ython#120233)

As noted in pythongh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
…ython#120233)

As noted in pythongh-117983, the import importlib.util can be triggered at
interpreter startup under some circumstances, so adding threading makes
it a potentially obligatory load.
Lazy loading is not used in the stdlib, so this removes an unnecessary
load for the majority of users and slightly increases the cost of the
first lazily loaded module.

An obligatory threading load breaks gevent, which monkeypatches the
stdlib. Although unsupported, there doesn't seem to be an offsetting
benefit to breaking their use case.

For reference, here are benchmarks for the current main branch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       9.7 ms ±   0.7 ms    [User: 7.7 ms, System: 1.8 ms]
  Range (min … max):     8.4 ms …  13.1 ms    313 runs
```

And with this patch:

```
❯ hyperfine -w 8 './python -c "import importlib.util"'
Benchmark 1: ./python -c "import importlib.util"
  Time (mean ± σ):       8.4 ms ±   0.7 ms    [User: 6.8 ms, System: 1.4 ms]
  Range (min … max):     7.2 ms …  11.7 ms    352 runs
```

Compare to:

```
❯ hyperfine -w 8 './python -c pass'
Benchmark 1: ./python -c pass
  Time (mean ± σ):       7.6 ms ±   0.6 ms    [User: 5.9 ms, System: 1.6 ms]
  Range (min … max):     6.7 ms …  11.3 ms    390 runs
```

This roughly halves the import time of importlib.util.
taegyunkim added a commit to DataDog/dd-trace-py that referenced this issue Aug 28, 2024
1. Upgrade testrunner image to the one generated by #10373 
2. testrunner now runs python 3.11.9 (upgraded from 3.11.3) and gevent
isn't compatible with python 3.11.9,
python/cpython#117983, so update the test to
run when version < 3.11.9
3. Regenerate riot file for appsec_integrations using python 3.12, as
outdated pydantic doesn't work with 3.12.4, see
pydantic/pydantic#9637. This also resulted in
updating pytest version and `setup_module` is used instead of `setup`.
4. Add `CMAKE_BUILD_PARALLEL_LEVEL` to a few places


## Checklist
- [x] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [x] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- Backport labels are set in a manner that is consistent with the
[release branch maintenance
policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
markbhasawut added a commit to markbhasawut/sapling that referenced this issue Oct 27, 2024
…g` has no attribute `Rlock`

Summary:

When using a recent version of python (3.12), running a sapling command returns the error `unknown python exception`:
AttributeError: partially initialized module 'threading' has no attribute 'RLock' (most likely due to a circular import).

This is caused by cpython breaking demandimport by `importing` threading locally in `importlib.util.LazyLoader.exec_module`.

Adding `threading` along with `warnings`, and `_weakrefset` (which are imported by threading) to demandimport's ignore list resolves the issue.

Refs: python/cpython#117983
      https://repo.mercurial-scm.org/hg/file/63ede7a43a37/hgdemandimport/__init__.py
      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1076449
      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1076747
markbhasawut added a commit to markbhasawut/sapling that referenced this issue Oct 27, 2024
…g` has no attribute `Rlock`

Summary:

When using a recent version of python (3.12), running a sapling command returns the error `unknown python exception`:
AttributeError: partially initialized module 'threading' has no attribute 'RLock' (most likely due to a circular import).

This is caused by cpython breaking demandimport by importing `threading` locally in `importlib.util.LazyLoader.exec_module`.

Adding `threading` along with `warnings`, and `_weakrefset` (which are imported by threading) to demandimport's ignore list resolves the issue.

Refs: python/cpython#117983
      https://repo.mercurial-scm.org/hg/file/63ede7a43a37/hgdemandimport/__init__.py
      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1076449
      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1076747
facebook-github-bot pushed a commit to facebook/sapling that referenced this issue Oct 29, 2024
#977)

Summary:
…g` has no attribute `Rlock`

When using a recent version of python (3.12), running a sapling command returns the error `unknown python exception`: AttributeError: partially initialized module 'threading' has no attribute 'RLock' (most likely due to a circular import).

This is caused by cpython breaking demandimport by importing `threading` locally in `importlib.util.LazyLoader.exec_module`.

Adding `threading` along with `warnings`, and `_weakrefset` (which are imported by threading) to demandimport's ignore list resolves the issue.

Refs: python/cpython#117983
      https://repo.mercurial-scm.org/hg/file/63ede7a43a37/hgdemandimport/__init__.py
      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1076449
      https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1076747

Pull Request resolved: #977

Reviewed By: ahornby

Differential Revision: D65039760

fbshipit-source-id: 134258735005cb6710cb3f33e3c23eb3a000bcb6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

8 participants