Skip to content

pynvml_utils

NVML_VALUE_NOT_AVAILABLE_uint = c_uint(-1) module-attribute

Field Identifiers.

All Identifiers pertain to a device. Each ID is only used once and is guaranteed never to change.

NVMLError

Bases: Exception

Source code in omnigibson/utils/pynvml_utils.py
class NVMLError(Exception):
    _valClassMapping = dict()  # type: ignore
    # List of currently known error codes
    _errcode_to_string = {
        NVML_ERROR_UNINITIALIZED: "Uninitialized",
        NVML_ERROR_INVALID_ARGUMENT: "Invalid Argument",
        NVML_ERROR_NOT_SUPPORTED: "Not Supported",
        NVML_ERROR_NO_PERMISSION: "Insufficient Permissions",
        NVML_ERROR_ALREADY_INITIALIZED: "Already Initialized",
        NVML_ERROR_NOT_FOUND: "Not Found",
        NVML_ERROR_INSUFFICIENT_SIZE: "Insufficient Size",
        NVML_ERROR_INSUFFICIENT_POWER: "Insufficient External Power",
        NVML_ERROR_DRIVER_NOT_LOADED: "Driver Not Loaded",
        NVML_ERROR_TIMEOUT: "Timeout",
        NVML_ERROR_IRQ_ISSUE: "Interrupt Request Issue",
        NVML_ERROR_LIBRARY_NOT_FOUND: "NVML Shared Library Not Found",
        NVML_ERROR_FUNCTION_NOT_FOUND: "Function Not Found",
        NVML_ERROR_CORRUPTED_INFOROM: "Corrupted infoROM",
        NVML_ERROR_GPU_IS_LOST: "GPU is lost",
        NVML_ERROR_RESET_REQUIRED: "GPU requires restart",
        NVML_ERROR_OPERATING_SYSTEM: "The operating system has blocked the request.",
        NVML_ERROR_LIB_RM_VERSION_MISMATCH: "RM has detected an NVML/RM version mismatch.",
        NVML_ERROR_MEMORY: "Insufficient Memory",
        NVML_ERROR_UNKNOWN: "Unknown Error",
    }

    def __new__(typ, value):
        """
        Maps value to a proper subclass of NVMLError.
        See _extractNVMLErrorsAsClasses function for more details
        """
        if typ == NVMLError:
            typ = NVMLError._valClassMapping.get(value, typ)
        obj = Exception.__new__(typ)
        obj.value = value
        return obj

    def __str__(self):
        try:
            if self.value not in NVMLError._errcode_to_string:
                NVMLError._errcode_to_string[self.value] = str(nvmlErrorString(self.value))
            return NVMLError._errcode_to_string[self.value]
        except NVMLError:
            return "NVML Error with code %d" % self.value

    def __eq__(self, other):
        return self.value == other.value

__new__(typ, value)

Maps value to a proper subclass of NVMLError. See _extractNVMLErrorsAsClasses function for more details

Source code in omnigibson/utils/pynvml_utils.py
def __new__(typ, value):
    """
    Maps value to a proper subclass of NVMLError.
    See _extractNVMLErrorsAsClasses function for more details
    """
    if typ == NVMLError:
        typ = NVMLError._valClassMapping.get(value, typ)
    obj = Exception.__new__(typ)
    obj.value = value
    return obj

_PrintableStructure

Bases: Structure

Abstract class that produces nicer str output than ctypes.Structure. e.g. instead of:

print str(obj) this class will print class_name(field_name: formatted_value, field_name: formatted_value)

fmt dictionary of -> e.g. class that has field 'hex_value', c_uint could be formatted with fmt = {"hex_value" : "%08X"} to produce nicer output. Default fomratting string for all fields can be set with key "" like: fmt = {"" : "%d MHz"} # e.g all values are numbers in MHz. If not set it's assumed to be just "%s"

Exact format of returned str from this class is subject to change in the future.

Source code in omnigibson/utils/pynvml_utils.py
class _PrintableStructure(Structure):
    """
    Abstract class that produces nicer __str__ output than ctypes.Structure.
    e.g. instead of:
      >>> print str(obj)
      <class_name object at 0x7fdf82fef9e0>
    this class will print
      class_name(field_name: formatted_value, field_name: formatted_value)

    _fmt_ dictionary of <str _field_ name> -> <str format>
    e.g. class that has _field_ 'hex_value', c_uint could be formatted with
      _fmt_ = {"hex_value" : "%08X"}
    to produce nicer output.
    Default fomratting string for all fields can be set with key "<default>" like:
      _fmt_ = {"<default>" : "%d MHz"} # e.g all values are numbers in MHz.
    If not set it's assumed to be just "%s"

    Exact format of returned str from this class is subject to change in the future.
    """

    _fmt_ = {}  # type: ignore

    def __str__(self):
        result = []
        for x in self._fields_:
            key = x[0]
            value = getattr(self, key)
            fmt = "%s"
            if key in self._fmt_:
                fmt = self._fmt_[key]
            elif "<default>" in self._fmt_:
                fmt = self._fmt_["<default>"]
            result.append(("%s: " + fmt) % (key, value))
        return self.__class__.__name__ + "(" + ", ".join(result) + ")"

    def __getattribute__(self, name):
        res = super().__getattribute__(name)
        # need to convert bytes to unicode for python3 don't need to for python2
        # Python 2 strings are of both str and bytes
        # Python 3 strings are not of type bytes
        # ctypes should convert everything to the correct values otherwise
        if isinstance(res, bytes):
            if isinstance(res, str):
                return res
            return res.decode()
        return res

    def __setattr__(self, name, value):
        if isinstance(value, str):
            # encoding a python2 string returns the same value, since python2 strings are bytes already
            # bytes passed in python3 will be ignored.
            value = value.encode()
        super().__setattr__(name, value)

_LoadNvmlLibrary()

Load the library if it isn't loaded already

Source code in omnigibson/utils/pynvml_utils.py
def _LoadNvmlLibrary():
    """
    Load the library if it isn't loaded already
    """
    global nvmlLib

    if nvmlLib is None:
        # lock to ensure only one caller loads the library
        libLoadLock.acquire()

        try:
            # ensure the library still isn't loaded
            if nvmlLib is None:
                if sys.platform[:3] == "win":
                    # cdecl calling convention
                    # First, check for nvml.dll in System32 first for DCH drivers.
                    # If nvml.dll is not found in System32, it should be in ProgramFiles
                    # load nvml.dll from %ProgramFiles%/NVIDIA Corporation/NVSMI/nvml.dll
                    search_paths = [
                        os.path.join(os.getenv("WINDIR", "C:/Windows"), "System32/nvml.dll"),
                        os.path.join(
                            os.getenv("ProgramFiles", "C:/Program Files"),
                            "NVIDIA Corporation/NVSMI/nvml.dll",
                        ),
                    ]
                    # Finally, it may be overridden by an environment variable
                    nvml_path = os.getenv("NVML_DLL_PATH")
                    if nvml_path is not None:
                        search_paths.append(nvml_path)
                    for dll_path in search_paths:
                        try:
                            nvmlLib = CDLL(dll_path)
                        except OSError as ose:
                            continue
                        break
                else:
                    # assume linux
                    try:
                        nvmlLib = CDLL("libnvidia-ml.so.1")
                    except OSError as ose:
                        _nvmlCheckReturn(NVML_ERROR_LIBRARY_NOT_FOUND)
                if nvmlLib is None:
                    _nvmlCheckReturn(NVML_ERROR_LIBRARY_NOT_FOUND)
        finally:
            # lock is always freed
            libLoadLock.release()

_extractNVMLErrorsAsClasses()

Generates a hierarchy of classes on top of NVMLError class.

Each NVML Error gets a new NVMLError subclass. This way try,except blocks can filter appropriate exceptions more easily.

NVMLError is a parent class. Each NVML_ERROR_* gets it's own subclass. e.g. NVML_ERROR_ALREADY_INITIALIZED will be turned into NVMLError_AlreadyInitialized

Source code in omnigibson/utils/pynvml_utils.py
def _extractNVMLErrorsAsClasses():
    """
    Generates a hierarchy of classes on top of NVMLError class.

    Each NVML Error gets a new NVMLError subclass. This way try,except blocks can filter appropriate
    exceptions more easily.

    NVMLError is a parent class. Each NVML_ERROR_* gets it's own subclass.
    e.g. NVML_ERROR_ALREADY_INITIALIZED will be turned into NVMLError_AlreadyInitialized
    """
    this_module = sys.modules[__name__]
    nvmlErrorsNames = [x for x in dir(this_module) if x.startswith("NVML_ERROR_")]
    for err_name in nvmlErrorsNames:
        # e.g. Turn NVML_ERROR_ALREADY_INITIALIZED into NVMLError_AlreadyInitialized
        class_name = "NVMLError_" + string.capwords(err_name.replace("NVML_ERROR_", ""), "_").replace("_", "")
        err_val = getattr(this_module, err_name)

        def gen_new(val):
            def new(typ):
                obj = NVMLError.__new__(typ, val)
                return obj

            return new

        new_error_class = type(class_name, (NVMLError,), {"__new__": gen_new(err_val)})
        new_error_class.__module__ = __name__
        setattr(this_module, class_name, new_error_class)
        NVMLError._valClassMapping[err_val] = new_error_class

convertStrBytes(func)

In python 3, strings are unicode instead of bytes, and need to be converted for ctypes Args from caller: (1, 'string', <main.c_nvmlDevice_t at 0xFFFFFFFF>) Args passed to function: (1, b'string', <main.c_nvmlDevice_t at 0xFFFFFFFF)>


Returned from function: b'returned string' Returned to caller: 'returned string'

Source code in omnigibson/utils/pynvml_utils.py
def convertStrBytes(func):
    """
    In python 3, strings are unicode instead of bytes, and need to be converted for ctypes
    Args from caller: (1, 'string', <__main__.c_nvmlDevice_t at 0xFFFFFFFF>)
    Args passed to function: (1, b'string', <__main__.c_nvmlDevice_t at 0xFFFFFFFF)>
    ----
    Returned from function: b'returned string'
    Returned to caller: 'returned string'
    """

    @wraps(func)
    def wrapper(*args, **kwargs):
        # encoding a str returns bytes in python 2 and 3
        args = [arg.encode() if isinstance(arg, str) else arg for arg in args]
        res = func(*args, **kwargs)
        # In python 2, str and bytes are the same
        # In python 3, str is unicode and should be decoded.
        # Ctypes handles most conversions, this only effects c_char and char arrays.
        if isinstance(res, bytes):
            if isinstance(res, str):
                return res
            return res.decode()
        return res

    return wrapper
    return func