Skip to content

Commit 3ed8ce6

Browse files
authored
Merge branch 'master' into 246-go-faster
2 parents 3f25565 + f6bef5d commit 3ed8ce6

5 files changed

Lines changed: 46 additions & 50 deletions

File tree

‎.github/workflows/test.yml‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ jobs:
2929
- name: Set up Python
3030
uses: actions/setup-python@v4
3131
with:
32-
python-version: "3.12"
32+
python-version: "3.14"
3333
- name: Install black
3434
run: |
35-
pip install black==25.1.0
35+
pip install black==26.5.1
3636
- name: Run black
3737
run: |
3838
black --check --diff .
@@ -73,9 +73,9 @@ jobs:
7373
INSTALL_OPENCV: "true"
7474

7575
# MacOS env with OpenMP installed through homebrew
76-
- name: py39_conda_homebrew_libomp
76+
- name: py310_conda_homebrew_libomp
7777
os: macos-latest
78-
PYTHON_VERSION: "3.9"
78+
PYTHON_VERSION: "3.10"
7979
PACKAGER: "conda"
8080
BLAS: "openblas"
8181
CC_OUTER_LOOP: "clang"
@@ -101,16 +101,16 @@ jobs:
101101

102102
# Linux environments to test that packages that comes with Ubuntu 22.04
103103
# are correctly handled.
104-
- name: py39_ubuntu_atlas_gcc_gcc
104+
- name: py310_ubuntu_atlas_gcc_gcc
105105
os: ubuntu-22.04
106-
PYTHON_VERSION: "3.9"
106+
PYTHON_VERSION: "3.10"
107107
PACKAGER: "ubuntu"
108108
APT_BLAS: "libatlas3-base libatlas-base-dev"
109109
CC_OUTER_LOOP: "gcc"
110110
CC_INNER_LOOP: "gcc"
111-
- name: py39_ubuntu_openblas_gcc_gcc
111+
- name: py310_ubuntu_openblas_gcc_gcc
112112
os: ubuntu-22.04
113-
PYTHON_VERSION: "3.9"
113+
PYTHON_VERSION: "3.10"
114114
PACKAGER: "ubuntu"
115115
APT_BLAS: "libopenblas-base libopenblas-dev"
116116
CC_OUTER_LOOP: "gcc"
@@ -125,11 +125,11 @@ jobs:
125125
CC_INNER_LOOP: "gcc"
126126
INSTALL_OPENCV: "true"
127127

128-
# Linux + Python 3.9 and homogeneous runtime nesting.
129-
- name: py39_conda_openblas_clang_clang
128+
# Linux + Python 3.10 and homogeneous runtime nesting.
129+
- name: py310_conda_openblas_clang_clang
130130
os: ubuntu-latest
131131
PACKAGER: "conda"
132-
PYTHON_VERSION: "3.9"
132+
PYTHON_VERSION: "3.10"
133133
BLAS: "openblas"
134134
CC_OUTER_LOOP: "clang-18"
135135
CC_INNER_LOOP: "clang-18"

‎CHANGES.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
closures that can abort after ``os.fork()`` on some libffi builds.
7878
https://github.com/joblib/threadpoolctl/pull/242
7979

80+
- Dropped official support for Python 3.9.
81+
https://github.com/joblib/threadpoolctl/pull/255
82+
8083
3.6.0 (2025-03-13)
8184
==================
8285

‎README.md‎

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,10 @@ in specific sections of your Python program:
176176
... a_squared = a @ a
177177
```
178178

179-
The threadpools can also be controlled via the object oriented API, which is especially
180-
useful to avoid searching through all the loaded shared libraries each time. It will
181-
however not act on libraries loaded after the instantiation of the
182-
`ThreadpoolController`:
179+
The threadpools can also be controlled via the object oriented API, which is
180+
especially useful to avoid searching through all the loaded shared libraries
181+
each time. **Note that it will not act on libraries loaded after the instantiation
182+
of the `ThreadpoolController`!**
183183

184184
```python
185185
>>> from threadpoolctl import ThreadpoolController
@@ -225,44 +225,40 @@ controlled libraries in that thread.** With Python's
225225
`concurrent.futures.ThreadPoolExecutor`, you can do so by passing in an
226226
initializer function that will get called on thread startup.
227227

228-
```python
229-
from threadpoolctl import threadpool_limits
230-
from concurrent.futures import ThreadPoolExecutor
231-
232-
# This top-level limiter doesn't actually change the limits initially; it is
233-
# there to ensure the limits are reset _after_ the Python thread pool is done.
234-
# This is necessary because some underlying limiting APIs operate on a
235-
# process-wide basis.
236-
with threadpool_limits():
237-
# Make sure each Python worker thread also calls threadpool_limits(). If
238-
# you're using another thread pool class, you will need to do so some other
239-
# way.
240-
with ThreadPoolExecutor(4, initializer=lambda: threadpool_limits(limits=1)) as pool:
241-
# ... run some BLAS-using code in the thread pool ...
242-
pool.map(somefunc, someargs)
243-
```
244-
245-
Whenever `threadpool_limits` is called, it needs to do some work (inspecting and getting access to third-party shared libraries) that can take some time.
246-
To prevent the performance cost of doing this work every time, you can reuse a
247-
`ThreadpoolController` object:
228+
Whenever `threadpool_limits` is called, it creates a new `ThreadpoolController`,
229+
which needs to do some work (inspecting and getting access to third-party shared
230+
libraries) that can take some time. To prevent the performance cost of doing
231+
this work in all the threads, you can reuse a `ThreadpoolController` object
232+
across the threads.
248233

249234
```python
250235
from threadpoolctl import ThreadpoolController
251236

252-
# This won't have any side-effects:
253-
CONTROLLER = ThreadpoolController()
237+
# This won't have any side-effects. Because it caches its list of loaded
238+
# libraries, you need to create a new one if you've imported or loaded any
239+
# relevant libraries in the interim. So storing this on module level may not be
240+
# a good idea if you e.g. only do `import numpy` later on.
241+
controller = ThreadpoolController()
254242

255243
with (
256-
CONTROLLER.limit(),
257-
ThreadPoolExecutor(4, initializer=lambda: CONTROLLER.limit(limits=1)) as pool,
244+
# This top-level limiter doesn't actually change the limits initially; it
245+
# is there to ensure the limits are reset _after_ the Python thread pool is
246+
# done. This is necessary because some underlying limiting APIs operate on
247+
# a process-wide basis.
248+
controller.limit(),
249+
# Make sure each Python worker thread also calls threadpool_limits(). If
250+
# you're using another thread pool class, you will need to do so some other
251+
# way.
252+
ThreadPoolExecutor(4, initializer=lambda: controller.limit(limits=1)) as pool,
258253
):
259254
# ... run some BLAS-using code in the thread pool ...
260255
pool.map(somefunc, someargs)
261256

262257
# Later...
258+
controller = ThreadpoolController()
263259
with (
264-
CONTROLLER.limit(),
265-
ThreadPoolExecutor(4, initializer=lambda: CONTROLLER.limit(limits=2)) as pool,
260+
controller.limit(),
261+
ThreadPoolExecutor(4, initializer=lambda: controller.limit(limits=2)) as pool,
266262
):
267263
# ... run some BLAS-using code in the thread pool ...
268264
pool.map(somefunc, someargs)
@@ -273,11 +269,12 @@ You can also operate without a context manager:
273269
```python
274270
from threadpoolctl import ThreadpoolController
275271

276-
CONTROLLER = ThreadpoolController()
272+
controller = ThreadpoolController()
277273
try:
278-
limiter = CONTROLLER.limit()
274+
limiter = controller.limit()
279275
with ThreadPoolExecutor(
280-
4, initializer=lambda: CONTROLLER.limit(limits=1)) as pool:
276+
4, initializer=lambda: controller.limit(limits=1)
277+
) as pool:
281278
# ... run some BLAS-using code in the thread pool ...
282279
pool.map(somefunc, someargs)
283280
finally:

‎pyproject.toml‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ build-backend = "flit_core.buildapi"
66
name = "threadpoolctl"
77
authors = [{name = "Thomas Moreau", email = "thomas.moreau.2010@gmail.com"}]
88
readme = "README.md"
9-
requires-python = ">=3.9"
9+
requires-python = ">=3.10"
1010
license = "BSD-3-Clause"
1111
dynamic = ["version", "description"]
1212
classifiers = [
1313
"Intended Audience :: Developers",
1414
"Programming Language :: Python :: 3",
15-
"Programming Language :: Python :: 3.9",
1615
"Programming Language :: Python :: 3.10",
1716
"Programming Language :: Python :: 3.11",
1817
"Programming Language :: Python :: 3.12",
@@ -27,7 +26,7 @@ homepage = "https://github.com/joblib/threadpoolctl"
2726

2827
[tool.black]
2928
line-length = 88
30-
target_version = ['py39', 'py310', 'py311', 'py312', 'py313']
29+
target_version = ['py310', 'py311', 'py312', 'py313', 'py314']
3130
preview = true
3231

3332
[tool.ruff]

‎tests/test_threadpoolctl.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,6 @@ def test_command_line_command_flag():
595595
assert lib_info in this_process_info
596596

597597

598-
@pytest.mark.skipif(
599-
sys.version_info < (3, 7), reason="need recent subprocess.run options"
600-
)
601598
def test_command_line_import_flag():
602599
result = subprocess.run(
603600
[

0 commit comments

Comments
 (0)