diff -r 2626a8bb7615 Doc/library/multiprocessing.rst --- a/Doc/library/multiprocessing.rst Sun May 12 19:57:26 2013 +0200 +++ b/Doc/library/multiprocessing.rst Mon May 13 01:52:09 2013 +0200 @@ -702,6 +702,8 @@ Miscellaneous Return the number of CPUs in the system. May raise :exc:`NotImplementedError`. + See also :func:`os.cpu_count` and :func:`shutil.cpu_count`. + .. function:: current_process() Return the :class:`Process` object corresponding to the current process. diff -r 2626a8bb7615 Doc/library/os.rst --- a/Doc/library/os.rst Sun May 12 19:57:26 2013 +0200 +++ b/Doc/library/os.rst Mon May 13 01:52:09 2013 +0200 @@ -3155,9 +3155,18 @@ operating system. Return the set of CPUs the process with PID *pid* (or the current process if zero) is restricted to. - .. seealso:: - :func:`multiprocessing.cpu_count` returns the number of CPUs in the - system. + +.. function:: cpu_count() + + Return the number of CPUs in the system. On error, an :exc:`OSError` is + raised. + + :func:`shutil.cpu_count` is the high-level function which should normally + be used, :func:`os.cpu_count` is the low-level implementation. + + Availability: Windows, Linux, Mac OS X, FreeBSD, NetBSD, OpenBSD, HPUX. + + .. versionadded:: 3.4 .. _os-path: diff -r 2626a8bb7615 Doc/library/shutil.rst --- a/Doc/library/shutil.rst Sun May 12 19:57:26 2013 +0200 +++ b/Doc/library/shutil.rst Mon May 13 01:52:09 2013 +0200 @@ -601,3 +601,17 @@ Querying the size of the output terminal .. _`Other Environment Variables`: http://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html#tag_002_003 + +Miscellaneous System Information +-------------------------------- + + +.. function:: cpu_count(fallback=1) + + Return the number of CPUs in the system. Returns *fallback* if the number of + CPUs is unknown or invalid (smaller than 1), and on error. + + This function calls :class:`os.cpu_count`. + + .. versionadded:: 3.4 + diff -r 2626a8bb7615 Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst Sun May 12 19:57:26 2013 +0200 +++ b/Doc/whatsnew/3.4.rst Mon May 13 01:52:09 2013 +0200 @@ -174,6 +174,13 @@ Added ``FAIL_FAST`` flag to halt test ru detected. (Contributed by R. David Murray and Daniel Urban in :issue:`16522`.) +os +-- + +The :mod:`os` module has a new :func:`~os.cpu_count` function to get the number +of CPUs in the system. + + smtplib ------- @@ -183,6 +190,13 @@ try/except statement by code that only c (:issue:`2118`). +shutil +------ + +The :mod:`shutil` module has a new :func:`~os.cpu_count` function to get the +number of CPUs in the system. + + wave ---- @@ -250,4 +264,4 @@ that may require changes to your code. * The module type now initializes the :attr:`__package__` and :attr:`__loader__` attributes to ``None`` by default. To determine if these attributes were set in a backwards-compatible fashion, use e.g. - ``getattr(module, '__loader__', None) is not None``. \ No newline at end of file + ``getattr(module, '__loader__', None) is not None``. diff -r 2626a8bb7615 Lib/multiprocessing/__init__.py --- a/Lib/multiprocessing/__init__.py Sun May 12 19:57:26 2013 +0200 +++ b/Lib/multiprocessing/__init__.py Mon May 13 01:52:09 2013 +0200 @@ -28,6 +28,7 @@ # import os +import shutil import sys from multiprocessing.process import Process, current_process, active_children @@ -85,30 +86,10 @@ def cpu_count(): ''' Returns the number of CPUs in the system ''' - if sys.platform == 'win32': - try: - num = int(os.environ['NUMBER_OF_PROCESSORS']) - except (ValueError, KeyError): - num = 0 - elif 'bsd' in sys.platform or sys.platform == 'darwin': - comm = '/sbin/sysctl -n hw.ncpu' - if sys.platform == 'darwin': - comm = '/usr' + comm - try: - with os.popen(comm) as p: - num = int(p.read()) - except ValueError: - num = 0 - else: - try: - num = os.sysconf('SC_NPROCESSORS_ONLN') - except (ValueError, OSError, AttributeError): - num = 0 - - if num >= 1: - return num - else: + num = shutil.cpu_count(None) + if num is None: raise NotImplementedError('cannot determine number of cpus') + return num def freeze_support(): ''' diff -r 2626a8bb7615 Lib/shutil.py --- a/Lib/shutil.py Sun May 12 19:57:26 2013 +0200 +++ b/Lib/shutil.py Mon May 13 01:52:09 2013 +0200 @@ -1126,3 +1126,23 @@ def which(cmd, mode=os.F_OK | os.X_OK, p if _access_check(name, mode): return name return None + +def cpu_count(fallback=1): + """ + Return the number of CPUs in the system. + Returns fallback if the number of CPUs is unknown or invalid + (smaller than 1), and on error. + """ + if not hasattr(os, 'cpu_count'): + return fallback + + try: + count = os.cpu_count() + except OSError: + return fallback + + if count < 1: + return fallback + + return count + diff -r 2626a8bb7615 Lib/test/test_os.py --- a/Lib/test/test_os.py Sun May 12 19:57:26 2013 +0200 +++ b/Lib/test/test_os.py Mon May 13 01:52:09 2013 +0200 @@ -2216,6 +2216,15 @@ class OSErrorTests(unittest.TestCase): else: self.fail("No exception thrown by {}".format(func)) +class CPUCountTests(unittest.TestCase): + @unittest.skipUnless(hasattr('os', 'cpu_count'), + 'need os.cpu_count()') + def test_cpu_count(self): + cpus = os.cpu_count() + self.assertIsInstance(cpus, int) + self.assertGreater(cpus, 0) + + @support.reap_threads def test_main(): support.run_unittest( @@ -2246,6 +2255,7 @@ def test_main(): TermsizeTests, OSErrorTests, RemoveDirsTests, + CPUCountTests, ) if __name__ == "__main__": diff -r 2626a8bb7615 Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py Sun May 12 19:57:26 2013 +0200 +++ b/Lib/test/test_shutil.py Mon May 13 01:52:09 2013 +0200 @@ -1,6 +1,7 @@ # Copyright (C) 2003 Python Software Foundation import unittest +import unittest.mock import shutil import tempfile import sys @@ -1715,9 +1716,49 @@ class TermsizeTests(unittest.TestCase): self.assertEqual(expected, actual) +class TestCpuCount(unittest.TestCase): + def test_valid(self): + count = shutil.cpu_count() + self.assertEqual(type(count), int) + self.assertGreaterEqual(count, 1) + + def test_missing_os_cpu_count(self): + with unittest.mock.patch('shutil.os') as osmod: + if hasattr(osmod, 'cpu_count'): + del osmod.cpu_count + + self.assertIsNone(shutil.cpu_count(fallback=None)) + self.assertEqual(shutil.cpu_count(), 1) + + def test_failing_os_cpu_count(self): + def failing_cpu_count(): + raise OSError() + + with unittest.mock.patch('shutil.os') as osmod: + osmod.cpu_count = failing_cpu_count + + self.assertIsNone(shutil.cpu_count(fallback=None)) + self.assertEqual(shutil.cpu_count(), 1) + + def test_invalid_os_cpu_count(self): + def invalid_cpu_count(): + return 0 + + with unittest.mock.patch('shutil.os') as osmod: + osmod.cpu_count = invalid_cpu_count + + self.assertIsNone(shutil.cpu_count(fallback=None)) + self.assertEqual(shutil.cpu_count(), 1) + + def test_main(): - support.run_unittest(TestShutil, TestMove, TestCopyFile, - TermsizeTests, TestWhich) + support.run_unittest( + TestShutil, + TestMove, + TestCopyFile, + TermsizeTests, + TestWhich, + TestCpuCount) if __name__ == '__main__': test_main() diff -r 2626a8bb7615 Misc/NEWS --- a/Misc/NEWS Sun May 12 19:57:26 2013 +0200 +++ b/Misc/NEWS Mon May 13 01:52:09 2013 +0200 @@ -91,6 +91,9 @@ Core and Builtins Library ------- +- Issue #17914: Add os.cpu_count() and shutil.cpu_count() functions to get + the number of CPUs in the system. + - Issue #17606: Fixed support of encoded byte strings in the XMLGenerator .characters() and ignorableWhitespace() methods. Original patch by Sebastian Ortiz Vasquez. diff -r 2626a8bb7615 Modules/posixmodule.c --- a/Modules/posixmodule.c Sun May 12 19:57:26 2013 +0200 +++ b/Modules/posixmodule.c Mon May 13 01:52:09 2013 +0200 @@ -113,6 +113,18 @@ corresponding Unix manual entries for mo #include #endif +#ifdef __hpux +#include +#endif + +#if defined(__DragonFly__) || \ + defined(__OpenBSD__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__APPLE__) +#include +#endif + #if defined(MS_WINDOWS) # define TERMSIZE_USE_CONIO #elif defined(HAVE_SYS_IOCTL_H) @@ -10299,6 +10311,100 @@ get_terminal_size(PyObject *self, PyObje } #endif /* defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) */ +PyDoc_STRVAR(posix_cpu_count__doc__, +"cpu_count() -> integer\n\n\ +Return an integer representing the number of online logical CPUs,\n\ +or Py_None if this value cannot be established."); + +#if defined(MS_WINDOWS) \ + || defined(__hpux) \ + || defined(__APPLE__) \ + || defined(__DragonFly__) \ + || defined(__OpenBSD__) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || (defined(HAVE_SYSCONF) \ + && (defined(_SC_NPROCESSORS_ONLN) || defined(_SC_NPROC_ONLN))) +# define HAVE_CPU_COUNT +#endif + +#ifdef HAVE_CPU_COUNT +static PyObject * +posix_cpu_count(PyObject *self) +{ +#ifdef MS_WINDOWS + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return PyLong_FromUnsignedLong(sysinfo.dwNumberOfProcessors); + +#elif defined(__hpux) + int ncpu; + + ncpu = mpctl(MPC_GETNUMSPUS, NULL, NULL); + if (ncpu < 0) + return posix_error(); + + return PyLong_FromLong(ncpu); + +#elif defined(__APPLE__) + int mib[2]; + size_t length; + size_t size; + int err; + int ncpu; + + mib[0] = CTL_HW; + length = PY_ARRAY_LENGTH(mib); + err = sysctlnametomib("hw.logicalcpu", mib, &length); + if (err == -1) + return posix_error(); + assert(length == 2); + + ncpu = -1; + size = sizeof(ncpu); + err = sysctl(mib, length, &ncpu, &size, NULL, 0); + if (err == -1) + return posix_error(); + + return PyLong_FromLong(ncpu); + + +#elif defined(__DragonFly__) || \ + defined(__OpenBSD__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) + int err; + size_t length; + int ncpu; + int mib[4]; + size_t size; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + length = PY_ARRAY_LENGTH(mib); + ncpu = -1; + size = sizeof(ncpu); + err = sysctl(mib, length, &ncpu, &size, NULL, 0) + if (err == -1) + return posix_error(); + + return PyLong_FromLong(ncpu); + +#else + long ncpu; +#ifdef _SC_NPROC_ONLN + ncpu = sysconf(_SC_NPROC_ONLN); +#else + ncpu = sysconf(_SC_NPROCESSORS_ONLN); +#endif + if (ncpu == -1) + return posix_error(); + + return PyLong_FromLong(ncpu); +#endif +} +#endif + static PyMethodDef posix_methods[] = { {"access", (PyCFunction)posix_access, @@ -10744,6 +10850,10 @@ static PyMethodDef posix_methods[] = { #if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) {"get_terminal_size", get_terminal_size, METH_VARARGS, termsize__doc__}, #endif +#ifdef HAVE_CPU_COUNT + {"cpu_count", (PyCFunction)posix_cpu_count, + METH_NOARGS, posix_cpu_count__doc__}, +#endif {NULL, NULL} /* Sentinel */ };