"""System-level utilities for the `pyrseus` package."""importimportlibimportmultiprocessingimportsignalfromcontextlibimportcontextmanagerfromfunctoolsimportcachefromtypingimportOptionalimportpsutilLOGICAL_CORES=psutil.cpu_count(logical=True)PHYSICAL_CORES=psutil.cpu_count(logical=False)# None on some platforms
[docs]defget_num_available_cores(pid:Optional[int]=None,physical:bool=False):""" Returns the number of cores that are available to the given process for scheduling threads, respecting the CPU affinity mask when possible. Think of this as a better version of `multiprocessing.cpu_count`. .. note: If you are calling this in code that already depends on the |loky|_ package, consider using `.loky.backend.context.cpu_count` instead of this function. It is even more sophisticated. :param pid: process ID to test, on systems that have affinity masks. Defaults to the current process. :param physical: whether to try only counting the number of physical cores instead of logical cores. Silently ignored on platforms that don't support querying the physical core count. """ifhasattr(psutil.Process,"cpu_affinity"):# Use psutil on any platform that supports it (Linux, Windows, FreeBSD).proc=psutil.Process(pid)logical_available=len(proc.cpu_affinity())ifphysical:if(PHYSICAL_CORESisnotNone)and(PHYSICAL_CORES>=0):# On most platforms, we can do the right thing.returnint((PHYSICAL_CORES*logical_available)//LOGICAL_CORES)else:# On OpenBSD and NetBSD, physical is always None, so give up and# just assume it's okay to return the number of available logical# cores.returnlogical_availableelse:returnlogical_availableelse:# Fallback to just counting the number of visible cores on platforms# that don't have Process.cpu_affinity, like macOS.returnmultiprocessing.cpu_count()
[docs]@cachedefis_mp_start_method_supported(start_method:str)->bool:""" Returns whether the given ``start_method`` is supported by the `multiprocessing` library, in the current process. """importmultiprocessingasmptry:mp.get_context(start_method)exceptValueError:returnFalseelse:returnTrue
[docs]defmodule_exists(name):""" Tells whether the given Python module or package exists and looks importable, without actually importing it. """try:returnimportlib.util.find_spec(name)isnotNoneexceptModuleNotFoundError:returnFalse
[docs]@contextmanagerdefSignalHandlerCtx(signum,handler):""" Safely makes a temporary change to one signal handler. """unset=object()old_handler=unsettry:old_handler=signal.signal(signum,handler)yieldfinally:ifold_handlerisnotunset:signal.signal(signum,old_handler)