Hello community, here is the log from the commit of package python-threadpoolctl for openSUSE:Factory checked in at 2020-07-26 16:19:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-threadpoolctl (Old) and /work/SRC/openSUSE:Factory/.python-threadpoolctl.new.3592 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-threadpoolctl" Sun Jul 26 16:19:06 2020 rev:2 rq:822657 version:2.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-threadpoolctl/python-threadpoolctl.changes 2020-03-05 23:24:40.297389756 +0100 +++ /work/SRC/openSUSE:Factory/.python-threadpoolctl.new.3592/python-threadpoolctl.changes 2020-07-26 16:19:51.536837182 +0200 @@ -1,0 +2,10 @@ +Fri Jul 24 11:21:21 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com> + +- update to 2.1.0 + * New commandline interface: 'python -m threadpoolctl -i numpy' + will try to import the `numpy` package and then return the output of + `threadpoolctl.threadpool_info()` on STDOUT formatted using the JSON + syntax. This makes it easier to quickly introspect a Python environment. +- Add patch python_executable.patch + +------------------------------------------------------------------- Old: ---- threadpoolctl-2.0.0.tar.gz New: ---- python_executable.patch threadpoolctl-2.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-threadpoolctl.spec ++++++ --- /var/tmp/diff_new_pack.jwAFGA/_old 2020-07-26 16:19:52.056837668 +0200 +++ /var/tmp/diff_new_pack.jwAFGA/_new 2020-07-26 16:19:52.060837672 +0200 @@ -19,13 +19,15 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-threadpoolctl -Version: 2.0.0 +Version: 2.1.0 Release: 0 Summary: Thread-pool Controls License: BSD-3-Clause Group: Development/Languages/Python URL: https://github.com/joblib/threadpoolctl Source: https://files.pythonhosted.org/packages/source/t/threadpoolctl/threadpoolctl-%{version}.tar.gz +# fix python executable in tests +Patch0: python_executable.patch BuildRequires: %{python_module devel} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} @@ -41,6 +43,7 @@ %prep %setup -q -n threadpoolctl-%{version} +%patch0 -p1 %build %python_build ++++++ python_executable.patch ++++++ Index: threadpoolctl-2.1.0/tests/test_threadpoolctl.py =================================================================== --- threadpoolctl-2.1.0.orig/tests/test_threadpoolctl.py +++ threadpoolctl-2.1.0/tests/test_threadpoolctl.py @@ -396,14 +396,14 @@ def test_libomp_libiomp_warning(recwarn) def test_command_line_empty(): output = subprocess.check_output( - "python -m threadpoolctl".split()) + (sys.executable + " -m threadpoolctl").split()) assert json.loads(output.decode("utf-8")) == [] def test_command_line_command_flag(): pytest.importorskip("numpy") output = subprocess.check_output( - ["python", "-m", "threadpoolctl", "-c", "import numpy"]) + [sys.executable, "-m", "threadpoolctl", "-c", "import numpy"]) cli_info = json.loads(output.decode("utf-8")) this_process_info = threadpool_info() @@ -415,7 +415,7 @@ def test_command_line_command_flag(): reason="need recent subprocess.run options") def test_command_line_import_flag(): result = subprocess.run([ - "python", "-m", "threadpoolctl", "-i", + sys.executable, "-m", "threadpoolctl", "-i", "numpy", "scipy.linalg", "invalid_package", ++++++ threadpoolctl-2.0.0.tar.gz -> threadpoolctl-2.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/CHANGES.md new/threadpoolctl-2.1.0/CHANGES.md --- old/threadpoolctl-2.0.0/CHANGES.md 2019-12-05 18:18:43.089723600 +0100 +++ new/threadpoolctl-2.1.0/CHANGES.md 2020-05-29 11:11:42.282364600 +0200 @@ -1,5 +1,17 @@ +2.1.0 (2020-05-29) +================== + +- New commandline interface: + + python -m threadpoolctl -i numpy + + will try to import the `numpy` package and then return the output of + `threadpoolctl.threadpool_info()` on STDOUT formatted using the JSON + syntax. This makes it easier to quickly introspect a Python environment. + + 2.0.0 (2019-12-05) -=========== +================== - Expose MKL, BLIS and OpenBLAS threading layer in informations displayed by `threadpool_info`. This information is referenced in the `threading_layer` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/PKG-INFO new/threadpoolctl-2.1.0/PKG-INFO --- old/threadpoolctl-2.0.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/threadpoolctl-2.1.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: threadpoolctl -Version: 2.0.0 +Version: 2.1.0 Summary: threadpoolctl Home-page: https://github.com/joblib/threadpoolctl Author: Thomas Moreau diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/README.md new/threadpoolctl-2.1.0/README.md --- old/threadpoolctl-2.0.0/README.md 2019-11-15 17:14:06.539986000 +0100 +++ new/threadpoolctl-2.1.0/README.md 2020-05-25 11:10:49.357650500 +0200 @@ -32,7 +32,38 @@ ## Usage -### Runtime Introspection +### Command Line Interface + +Get a JSON description of thread-pools initialized when importing python +packages such as numpy or scipy for instance: + +``` +python -m threadpoolctl -i numpy scipy.linalg +[ + { + "filepath": "/home/ogrisel/miniconda3/envs/tmp/lib/libmkl_rt.so", + "prefix": "libmkl_rt", + "user_api": "blas", + "internal_api": "mkl", + "version": "2019.0.4", + "num_threads": 2, + "threading_layer": "intel" + }, + { + "filepath": "/home/ogrisel/miniconda3/envs/tmp/lib/libiomp5.so", + "prefix": "libiomp", + "user_api": "openmp", + "internal_api": "openmp", + "version": null, + "num_threads": 4 + } +] +``` + +The JSON information is written on STDOUT. If some of the packages are missing, +a warning message is displayed on STDERR. + +### Python Runtime Programmatic Introspection Introspect the current state of the threadpool-enabled runtime libraries that are loaded when importing Python packages: @@ -45,28 +76,36 @@ >>> import numpy >>> pprint(threadpool_info()) -[{'filepath': '/opt/venvs/py37/lib/python3.7/site-packages/numpy/.libs/libopenblasp-r0-382c8f3a.3.5.dev.so', - 'internal_api': 'openblas', - 'num_threads': 4, - 'prefix': 'libopenblas', +[{'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libmkl_rt.so', + 'internal_api': 'mkl', + 'num_threads': 2, + 'prefix': 'libmkl_rt', + 'threading_layer': 'intel', 'user_api': 'blas', - 'version': '0.3.5.dev'}] + 'version': '2019.0.4'}, + {'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libiomp5.so', + 'internal_api': 'openmp', + 'num_threads': 4, + 'prefix': 'libiomp', + 'user_api': 'openmp', + 'version': None}] >>> import xgboost >>> pprint(threadpool_info()) -[{'filepath': '/opt/venvs/py37/lib/python3.7/site-packages/numpy/.libs/libopenblasp-r0-382c8f3a.3.5.dev.so', - 'internal_api': 'openblas', - 'num_threads': 4, - 'prefix': 'libopenblas', +[{'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libmkl_rt.so', + 'internal_api': 'mkl', + 'num_threads': 2, + 'prefix': 'libmkl_rt', + 'threading_layer': 'intel', 'user_api': 'blas', - 'version': '0.3.5.dev'}, - {'filepath': '/opt/venvs/py37/lib/python3.7/site-packages/scipy/.libs/libopenblasp-r0-8dca6697.3.0.dev.so', - 'internal_api': 'openblas', + 'version': '2019.0.4'}, + {'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libiomp5.so', + 'internal_api': 'openmp', 'num_threads': 4, - 'prefix': 'libopenblas', - 'user_api': 'blas', + 'prefix': 'libiomp', + 'user_api': 'openmp', 'version': None}, - {'filepath': '/usr/lib/x86_64-linux-gnu/libgomp.so.1', + {'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libgomp.so.1.0.0', 'internal_api': 'openmp', 'num_threads': 4, 'prefix': 'libgomp', @@ -74,7 +113,12 @@ 'version': None}] ``` -### Set the maximum size of thread-pools +In the above example, `numpy` was installed from the default anaconda channel and +comes with the MKL and its Intel OpenMP (`libiomp5`) implementation while +`xgboost` was installed from pypi.org and links against GNU OpenMP (`libgomp`) +so both OpenMP runtimes are loaded in the same Python program. + +### Setting the Maximum Size of Thread-Pools Control the number of threads used by the underlying runtime libraries in specific sections of your Python program: @@ -92,14 +136,28 @@ a_squared = a @ a ``` -### Known limitation +### Known Limitations -`threadpool_limits` does not act as expected in nested parallel loops -managed by distinct OpenMP runtime implementations (for instance libgomp -from GCC and libomp from clang/llvm or libiomp from ICC). +- `threadpool_limits` can fail to limit the number of inner threads when nesting + parallel loops managed by distinct OpenMP runtime implementations (for instance + libgomp from GCC and libomp from clang/llvm or libiomp from ICC). + + See the `test_openmp_nesting` function in [tests/test_threadpoolctl.py]( + https://github.com/joblib/threadpoolctl/blob/master/tests/test_threadpoolctl.py) + for an example. More information can be found at: + https://github.com/jeremiedbb/Nested_OpenMP + + Note however that this problem does not happen when `threadpool_limits` is + used to limit the number of threads used internally by BLAS calls that are + themselves nested under OpenMP parallel loops. `threadpool_limits` works as + expected, even if the inner BLAS implementation relies on a distinct OpenMP + implementation. + +- Using Intel OpenMP (ICC) and LLVM OpenMP (clang) in the same Python program + under Linux is known to cause problems. See the following guide for more details + and workarounds: + https://github.com/joblib/threadpoolctl/blob/master/multiple_openmp.md -See the `test_openmp_nesting()` function in `tests/test_threadpoolctl.py` -for an example. ## Maintainers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/continuous_integration/display_versions.py new/threadpoolctl-2.1.0/continuous_integration/display_versions.py --- old/threadpoolctl-2.0.0/continuous_integration/display_versions.py 2019-06-05 15:55:01.186587600 +0200 +++ new/threadpoolctl-2.1.0/continuous_integration/display_versions.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -from threadpoolctl import threadpool_info -from pprint import pprint - -try: - import numpy as np - print("numpy", np.__version__) -except ImportError: - pass - -try: - import scipy - import scipy.linalg - print("scipy", scipy.__version__) -except ImportError: - pass - -try: - from tests._openmp_test_helper import * # noqa -except ImportError: - pass - - -pprint(threadpool_info()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/continuous_integration/test_script.cmd new/threadpoolctl-2.1.0/continuous_integration/test_script.cmd --- old/threadpoolctl-2.0.0/continuous_integration/test_script.cmd 2019-12-05 18:07:10.028120500 +0100 +++ new/threadpoolctl-2.1.0/continuous_integration/test_script.cmd 2020-05-25 11:10:49.357650500 +0200 @@ -1,5 +1,7 @@ call activate %VIRTUALENV% -python continuous_integration/display_versions.py +# Use the CLI to display the effective runtime environment prior to +# launching the tests: +python -m threadpoolctl -i numpy scipy.linalg tests._openmp_test_helper pytest -vlrxXs --junitxml=%JUNITXML% --cov=threadpoolctl diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/continuous_integration/test_script.sh new/threadpoolctl-2.1.0/continuous_integration/test_script.sh --- old/threadpoolctl-2.0.0/continuous_integration/test_script.sh 2019-12-05 18:07:10.028120500 +0100 +++ new/threadpoolctl-2.1.0/continuous_integration/test_script.sh 2020-05-25 11:10:49.357650500 +0200 @@ -12,6 +12,9 @@ fi set -x -PYTHONPATH="." python continuous_integration/display_versions.py + +# Use the CLI to display the effective runtime environment prior to +# launching the tests: +python -m threadpoolctl -i numpy scipy.linalg tests._openmp_test_helper pytest -vlrxXs -W error -k "$TESTS" --junitxml=$JUNITXML --cov=threadpoolctl diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/multiple_openmp.md new/threadpoolctl-2.1.0/multiple_openmp.md --- old/threadpoolctl-2.0.0/multiple_openmp.md 2019-12-05 18:07:10.028120500 +0100 +++ new/threadpoolctl-2.1.0/multiple_openmp.md 2020-01-13 13:35:43.897076800 +0100 @@ -1,53 +1,85 @@ -# Multiple OpenMP runtimes +# Multiple OpenMP Runtimes + +## Context OpenMP is an API specification for parallel programming. There are many -implementations of it, tied to a compiler most of the time: the libgomp library -for GCC, libomp for LLVM/Clang, libiomp for ICC and vcomp for MSVC for example. -In the following, we refer to OpenMP without distinction between the -specification and an implementation. - -In general, it's not advised to have different OpenMP libraries (or even -different copies of the same library) loaded at the same time in a program. -It's considered an undefined behavior. Fortunately it's not as bad as it sounds -in most situations. - -However it can easily happen in the Python ecosystem since you can install -packages compiled with different compilers (hence linked to different OpenMP -implementations) and import them in a same Python program. - -A typical example is installing numpy from Anaconda which ships MKL -(Intel's math library) and a package using multi-threading with OpenMP -(Scikit-learn, LightGBM, XGBoost, ...). - -From our experience, most OpenMP libraries can seamlessly coexist in a same -program. For instance, on Linux, we never observed any issue between libgomp -and libiomp, which is the most common mix (numpy with MKL + a package compiled -with GCC, the most widely used C compiler). - -The only unrecoverable incompatibility we encountered is between libomp -(LLVM/Clang) and libiomp (ICC), on Linux, manifested by crashes or deadlocks. -It can happen even with the simplest OpenMP calls like getting the maximum -number of threads that will be used in a subsequent parallel region. A possible -explanation is that libomp is actually a fork of libiomp causing name colliding -for instance. Using threadpoolctl may crash your program in such a setting. +implementations of it, tied to a compiler most of the time: + +- `libgomp` for GCC (GNU C/C++ Compiler), +- `libomp` for Clang (LLVM C/C++ Compiler), +- `libiomp` for ICC (Intel C/C++ Compiler), +- `vcomp` for MSVC (Microsoft Visual Studio C/C++ Compiler). + +In general, it is not advised to have different OpenMP runtime libraries (or +even different copies of the same library) loaded at the same time in a +program. It's considered an undefined behavior. Fortunately it is not as bad as +it sounds in most situations. + +However this situation is frequent in the Python ecosystem since you can +install packages compiled with different compilers (hence linked to different +OpenMP implementations) and import them together in a Python program. + +A typical example is installing NumPy from Anaconda which is linked against MKL +(Intel's math library) and another package that uses multi-threading with OpenMP +directly in a compiled extension, as is the case in Scikit-learn (via Cython +`prange`), LightGBM and XGBoost (via pragmas in the C++ source code). + +From our experience, **most OpenMP libraries can seamlessly coexist in a same +program**. For instance, on Linux, we never observed any issue between +`libgomp` and `libiomp`, which is the most common mix (NumPy with MKL + a +package compiled with GCC, the most widely used C compiler on that platform). + +## Incompatibility between Intel OpenMP and LLVM OpenMP under Linux + +The only unrecoverable incompatibility we encountered happens when loading a +mix of compiled extensions linked with **`libomp` (LLVM/Clang) and `libiomp` +(ICC), on Linux**, manifested by crashes or deadlocks. It can happen even with +the simplest OpenMP calls like getting the maximum number of threads that will +be used in a subsequent parallel region. A possible explanation is that +`libomp` is actually a fork of `libiomp` causing name colliding for instance. +Using `threadpoolctl` may crash your program in such a setting. + +**Fortunately this problem is very rare**: at the time of writing, all major +binary distributions of Python packages for Linux use either GCC or ICC to +build the Python scientific packages. Therefore this problem would only happen +if some packagers decide to start shipping Python packages built with +LLVM/Clang instead of GCC. Surprisingly, we never encountered this kind of issue on macOS, where this mix is the most frequent (Clang being the default C compiler on macOS). -As far as we know, the only workaround consists in getting rid of one of the -OpenMP libraries. For example: +## Workarounds for Intel OpenMP and LLVM OpenMP case + +As far as we know, the only workaround consists in making sure only of one of +the two incompatible OpenMP libraries is loaded. For example: + +- Tell MKL (used by NumPy) to use the GNU OpenMP runtime instead of the Intel + OpenMP runtime by setting the following environment variable: + + export MKL_THREADING_LAYER=GNU + +- Install a build of NumPy and SciPy linked against OpenBLAS instead of MKL. + This can be done for instance by installing NumPy and SciPy from PyPI: + + pip install numpy scipy + + from the conda-forge conda channel: -- Build your OpenMP-enabled extensions with GCC (or ICC) instead of Clang. + conda install -c conda-forge numpy scipy -- Install a build of Numpy linked against OpenBLAS instead of MKL. This can be - done for instance by installing Numpy from PyPI:: + or from the default conda channel: - pip install numpy + conda install numpy scipy blas[build=openblas] - from the conda-forge conda channel:: +- Re-build your OpenMP-enabled extensions from source with GCC (or ICC) instead + of Clang if you want to keep on using NumPy/SciPy linked against MKL with the + default `libiomp`-based threading layer. - conda install -c conda-forge numpy +## References - or from the default conda channel:: +The above incompatibility has been reported upstream to the LLVM and Intel +developers on the following public issue trackers/forums along with a minimal +reproducer written in C: - conda install numpy blas[build=openblas] +- https://bugs.llvm.org/show_bug.cgi?id=43565 +- https://software.intel.com/en-us/forums/intel-c-compiler/topic/827607 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/setup.py new/threadpoolctl-2.1.0/setup.py --- old/threadpoolctl-2.0.0/setup.py 1970-01-01 01:00:00.000000000 +0100 +++ new/threadpoolctl-2.1.0/setup.py 1970-01-01 01:00:00.000000000 +0100 @@ -5,7 +5,7 @@ setup(name='threadpoolctl', - version='2.0.0', + version='2.1.0', description='threadpoolctl', author='Thomas Moreau', author_email='thomas.moreau.2...@gmail.com', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/tests/_openmp_test_helper/__init__.py new/threadpoolctl-2.1.0/tests/_openmp_test_helper/__init__.py --- old/threadpoolctl-2.0.0/tests/_openmp_test_helper/__init__.py 2019-09-25 10:35:31.332812000 +0200 +++ new/threadpoolctl-2.1.0/tests/_openmp_test_helper/__init__.py 2020-05-25 11:10:49.357650500 +0200 @@ -2,7 +2,12 @@ from .openmp_helpers_inner import get_compiler as get_inner_compiler from .openmp_helpers_outer import check_nested_openmp_loops from .openmp_helpers_outer import get_compiler as get_outer_compiler -from .nested_prange_blas import check_nested_prange_blas + +try: + from .nested_prange_blas import check_nested_prange_blas +except ImportError: + # Can happen if numpy and scipy are missing. + check_nested_prange_blas = None __all__ = ["check_openmp_num_threads", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/tests/test_threadpoolctl.py new/threadpoolctl-2.1.0/tests/test_threadpoolctl.py --- old/threadpoolctl-2.0.0/tests/test_threadpoolctl.py 2019-12-05 18:07:10.028120500 +0100 +++ new/threadpoolctl-2.1.0/tests/test_threadpoolctl.py 2020-05-25 11:10:49.357650500 +0200 @@ -1,7 +1,9 @@ +import json import os +import pytest import re +import subprocess import sys -import pytest from threadpoolctl import threadpool_limits, threadpool_info, _ThreadpoolInfo from threadpoolctl import _ALL_PREFIXES, _ALL_USER_APIS @@ -261,6 +263,8 @@ @pytest.mark.skipif(scipy is None, reason="requires scipy") +@pytest.mark.skipif(not cython_extensions_compiled, + reason='Requires cython extensions to be compiled') @pytest.mark.parametrize("nthreads_outer", [None, 1, 2, 4]) def test_nested_prange_blas(nthreads_outer): # Check that the BLAS linked to scipy effectively uses the number of @@ -371,7 +375,7 @@ from ._openmp_test_helper import check_nested_openmp_loops # noqa # Trigger the import of numpy to potentially import Intel OpenMP via MKL - import numpy.linalg # noqa + pytest.importorskip("numpy.linalg") # Check that a warning is raised when both libomp and libiomp are loaded # It should happen in one CI job (pylatest_conda_mkl_clang_gcc). @@ -388,3 +392,45 @@ assert "Found Intel" in str(wm.message) assert "LLVM" in str(wm.message) assert "multiple_openmp.md" in str(wm.message) + + +def test_command_line_empty(): + output = subprocess.check_output( + "python -m threadpoolctl".split()) + assert json.loads(output.decode("utf-8")) == [] + + +def test_command_line_command_flag(): + pytest.importorskip("numpy") + output = subprocess.check_output( + ["python", "-m", "threadpoolctl", "-c", "import numpy"]) + cli_info = json.loads(output.decode("utf-8")) + + this_process_info = threadpool_info() + for module in cli_info: + assert module in this_process_info + + +@pytest.mark.skipif(sys.version_info < (3, 7), + reason="need recent subprocess.run options") +def test_command_line_import_flag(): + result = subprocess.run([ + "python", "-m", "threadpoolctl", "-i", + "numpy", + "scipy.linalg", + "invalid_package", + "numpy.invalid_sumodule", + ], capture_output=True, check=True, encoding="utf-8") + cli_info = json.loads(result.stdout) + + this_process_info = threadpool_info() + for module in cli_info: + assert module in this_process_info + + warnings = [w.strip() for w in result.stderr.splitlines()] + assert "WARNING: could not import invalid_package" in warnings + assert "WARNING: could not import numpy.invalid_sumodule" in warnings + if scipy is None: + assert "WARNING: could not import scipy.linalg" in warnings + else: + assert "WARNING: could not import scipy.linalg" not in warnings diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/threadpoolctl-2.0.0/threadpoolctl.py new/threadpoolctl-2.1.0/threadpoolctl.py --- old/threadpoolctl-2.0.0/threadpoolctl.py 2019-12-05 18:18:04.565399200 +0100 +++ new/threadpoolctl-2.1.0/threadpoolctl.py 2020-05-29 11:13:31.525606200 +0200 @@ -19,7 +19,7 @@ from ctypes.util import find_library from abc import ABC, abstractmethod -__version__ = "2.0.0" +__version__ = "2.1.0" __all__ = ["threadpool_limits", "threadpool_info"] @@ -757,3 +757,40 @@ def _get_extra_info(self): pass + + +def _main(): + """Commandline interface to display thread-pool information and exit.""" + import argparse + import importlib + import json + import sys + + parser = argparse.ArgumentParser( + usage="python -m threadpoolctl -i numpy scipy.linalg xgboost", + description="Display thread-pool information and exit.", + ) + parser.add_argument( + "-i", "--import", dest="modules", nargs="*", default=(), + help="Python modules to import before introspecting thread-pools." + ) + parser.add_argument( + "-c", "--command", + help="a Python statement to execute before introspecting" + " thread-pools.") + + options = parser.parse_args(sys.argv[1:]) + for module in options.modules: + try: + importlib.import_module(module, package=None) + except ImportError: + print("WARNING: could not import", module, file=sys.stderr) + + if options.command: + exec(options.command) + + print(json.dumps(threadpool_info(), indent=2)) + + +if __name__ == "__main__": + _main()