mirror of
https://github.com/XuehaiPan/nvitop.git
synced 2026-05-15 14:15:55 -06:00
deps(python): drop Python 3.7 support (#150)
This commit is contained in:
parent
8d897bbb78
commit
cda0149e45
18 changed files with 55 additions and 37 deletions
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
- Operating system and version: [e.g. Ubuntu 20.04 LTS / Windows 10 Build 19043.1110]
|
||||
- Terminal emulator and version: [e.g. GNOME Terminal 3.36.2 / Windows Terminal 1.8.1521.0]
|
||||
- Python version: [e.g. `3.7.2` / `3.9.6`]
|
||||
- Python version: [e.g. `3.8.2` / `3.9.6`]
|
||||
- NVML version (driver version): [e.g. `460.84`]
|
||||
- `nvitop` version or commit: [e.g. `0.10.0` / `0.10.1.dev7+ga083321` / `main@75ae3c`]
|
||||
- `python-ml-py` version: [e.g. `11.450.51`]
|
||||
|
|
|
|||
4
.github/workflows/build.yaml
vendored
4
.github/workflows/build.yaml
vendored
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
id: py
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.7 - 3.13"
|
||||
python-version: "3.8 - 3.13"
|
||||
update-environment: true
|
||||
|
||||
- name: Upgrade build dependencies
|
||||
|
|
@ -129,7 +129,7 @@ jobs:
|
|||
uses: actions/setup-python@v5
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
python-version: "3.7 - 3.13"
|
||||
python-version: "3.8 - 3.13"
|
||||
update-environment: true
|
||||
|
||||
- name: Upgrade pip
|
||||
|
|
|
|||
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
id: py
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.7 - 3.13"
|
||||
python-version: "3.8 - 3.13"
|
||||
update-environment: true
|
||||
|
||||
- name: Upgrade pip
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ repos:
|
|||
- id: debug-statements
|
||||
- id: double-quote-string-fixer
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.9.3
|
||||
rev: v0.9.4
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
|
|
@ -34,7 +34,7 @@ repos:
|
|||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus] # sync with requires-python
|
||||
args: [--py38-plus] # sync with requires-python
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 7.1.1
|
||||
hooks:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Removed
|
||||
|
||||
-
|
||||
- Drop Python 3.7 support by [@XuehaiPan](https://github.com/XuehaiPan) in [#150](https://github.com/XuehaiPan/nvitop/pull/150).
|
||||
|
||||
------
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<!-- markdownlint-disable html -->
|
||||
|
||||

|
||||

|
||||
[](https://pypi.org/project/nvitop)
|
||||
[](https://anaconda.org/conda-forge/nvitop)
|
||||
[](https://nvitop.readthedocs.io)
|
||||
|
|
@ -111,7 +111,7 @@ An interactive NVIDIA-GPU process viewer and beyond, the one-stop solution for G
|
|||
|
||||
## Requirements
|
||||
|
||||
- Python 3.7+
|
||||
- Python 3.8+
|
||||
- NVIDIA Management Library (NVML)
|
||||
- nvidia-ml-py
|
||||
- psutil
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ An interactive NVIDIA-GPU process viewer and beyond, the one-stop solution for G
|
|||
.. |GitHub| image:: https://img.shields.io/badge/GitHub-Homepage-blue?logo=github
|
||||
.. _GitHub: https://github.com/XuehaiPan/nvitop
|
||||
|
||||
.. |Python Version| image:: https://img.shields.io/badge/Python-3.7%2B-brightgreen
|
||||
.. |Python Version| image:: https://img.shields.io/badge/Python-3.8%2B-brightgreen
|
||||
.. _Python Version: https://pypi.org/project/nvitop
|
||||
|
||||
.. |PyPI Package| image:: https://img.shields.io/pypi/v/nvitop?label=pypi&logo=pypi
|
||||
|
|
@ -56,7 +56,7 @@ Install from PyPI (|PyPI Package|_):
|
|||
|
||||
.. note::
|
||||
|
||||
Python 3.7+ is required, and Python versions lower than 3.7 is not supported.
|
||||
Python 3.8+ is required, and Python versions lower than 3.8 is not supported.
|
||||
|
||||
Install from conda-forge (|Conda-forge Package|_):
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|||
name = "nvitop-exporter"
|
||||
description = "Prometheus exporter built on top of `nvitop`."
|
||||
readme = "README.md"
|
||||
requires-python = ">= 3.7"
|
||||
requires-python = ">= 3.8"
|
||||
authors = [{ name = "Xuehai Pan", email = "XuehaiPan@pku.edu.cn" }]
|
||||
license = { text = "Apache License, Version 2.0 (Apache-2.0)" }
|
||||
keywords = [
|
||||
|
|
@ -27,7 +27,6 @@ classifiers = [
|
|||
"Development Status :: 5 - Production/Stable",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ class _TTLCacheLink: # pylint: disable=too-few-public-methods
|
|||
@overload
|
||||
def ttl_cache(
|
||||
maxsize: int | None = 128,
|
||||
*,
|
||||
ttl: float = 600.0,
|
||||
timer: Callable[[], float] = time.monotonic,
|
||||
typed: bool = False,
|
||||
|
|
@ -132,6 +133,7 @@ def ttl_cache(
|
|||
@overload
|
||||
def ttl_cache(
|
||||
maxsize: Callable[_P, _T],
|
||||
*,
|
||||
ttl: float = 600.0,
|
||||
timer: Callable[[], float] = time.monotonic,
|
||||
typed: bool = False,
|
||||
|
|
@ -141,6 +143,7 @@ def ttl_cache(
|
|||
# pylint: disable-next=too-many-statements
|
||||
def ttl_cache(
|
||||
maxsize: int | Callable[_P, _T] | None = 128,
|
||||
*,
|
||||
ttl: float = 600.0,
|
||||
timer: Callable[[], float] = time.monotonic,
|
||||
typed: bool = False,
|
||||
|
|
@ -165,7 +168,7 @@ def ttl_cache(
|
|||
return functools.lru_cache(maxsize=maxsize, typed=typed) # type: ignore[return-value]
|
||||
|
||||
# pylint: disable-next=too-many-statements,too-many-locals
|
||||
def wrapper(func: Callable[_P, _T]) -> Callable[_P, _T]:
|
||||
def wrapper(func: Callable[_P, _T], /) -> Callable[_P, _T]:
|
||||
cache: dict[Any, _TTLCacheLink] = {}
|
||||
cache_get = cache.get # bound method to lookup a key or return None
|
||||
cache_len = cache.__len__ # get cache size without calling len()
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ timer = time.monotonic
|
|||
_T = TypeVar('_T')
|
||||
|
||||
|
||||
def _unique(iterable: Iterable[_T]) -> list[_T]:
|
||||
def _unique(iterable: Iterable[_T], /) -> list[_T]:
|
||||
return list(OrderedDict.fromkeys(iterable).keys())
|
||||
|
||||
|
||||
|
|
@ -399,6 +399,7 @@ class ResourceMetricCollector: # pylint: disable=too-many-instance-attributes
|
|||
def __init__(
|
||||
self,
|
||||
devices: Iterable[Device] | None = None,
|
||||
*,
|
||||
root_pids: Iterable[int] | None = None,
|
||||
interval: float = 1.0,
|
||||
) -> None:
|
||||
|
|
@ -778,6 +779,7 @@ class _MetricBuffer: # pylint: disable=missing-class-docstring,missing-function
|
|||
self,
|
||||
tag: str,
|
||||
collector: ResourceMetricCollector,
|
||||
*,
|
||||
prev: _MetricBuffer | None = None,
|
||||
) -> None:
|
||||
self.collector: ResourceMetricCollector = collector
|
||||
|
|
|
|||
|
|
@ -1589,6 +1589,7 @@ class Device: # pylint: disable=too-many-instance-attributes,too-many-public-me
|
|||
return []
|
||||
|
||||
def query_nvlink_throughput_counters() -> tuple[tuple[int | NaType, int]]:
|
||||
assert self._handle is not None
|
||||
return tuple( # type: ignore[return-value]
|
||||
libnvml.nvmlQueryFieldValues(
|
||||
self._handle,
|
||||
|
|
@ -3149,7 +3150,7 @@ def _parse_cuda_visible_devices(
|
|||
) -> list[str]: ...
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
@functools.lru_cache
|
||||
def _parse_cuda_visible_devices( # pylint: disable=too-many-branches,too-many-statements
|
||||
cuda_visible_devices: str | None = None,
|
||||
format: Literal['index', 'uuid'] = 'index', # pylint: disable=redefined-builtin
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ class CUDAError(Exception):
|
|||
return CUDAError, (self.value,) # pylint: disable=no-member
|
||||
|
||||
|
||||
def cudaExceptionClass(cudaErrorCode: int) -> type[CUDAError]:
|
||||
def cudaExceptionClass(cudaErrorCode: int, /) -> type[CUDAError]:
|
||||
"""Map value to a proper subclass of :class:`CUDAError`.
|
||||
|
||||
Raises:
|
||||
|
|
@ -348,7 +348,7 @@ _extract_cuda_errors_as_classes()
|
|||
del _extract_cuda_errors_as_classes
|
||||
|
||||
|
||||
def _cudaCheckReturn(ret: _Any) -> _Any:
|
||||
def _cudaCheckReturn(ret: _Any, /) -> _Any:
|
||||
if ret != CUDA_SUCCESS:
|
||||
raise CUDAError(ret)
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ class cudaError(Exception):
|
|||
return cudaError, (self.value,) # pylint: disable=no-member
|
||||
|
||||
|
||||
def cudaExceptionClass(cudaErrorCode: int) -> type[cudaError]:
|
||||
def cudaExceptionClass(cudaErrorCode: int, /) -> type[cudaError]:
|
||||
"""Map value to a proper subclass of :class:`cudaError`.
|
||||
|
||||
Raises:
|
||||
|
|
@ -399,7 +399,7 @@ _extract_cuda_errors_as_classes()
|
|||
del _extract_cuda_errors_as_classes
|
||||
|
||||
|
||||
def _cudaCheckReturn(ret: _Any) -> _Any:
|
||||
def _cudaCheckReturn(ret: _Any, /) -> _Any:
|
||||
if ret != cudaSuccess:
|
||||
raise cudaError(ret)
|
||||
return ret
|
||||
|
|
@ -412,7 +412,7 @@ __libLoadLock: _threading.Lock = _threading.Lock()
|
|||
__cudaGetFunctionPointer_cache: dict[str, _ctypes._CFuncPtr] = {} # type: ignore[name-defined]
|
||||
|
||||
|
||||
def __cudaGetFunctionPointer(name: str) -> _ctypes._CFuncPtr: # type: ignore[name-defined]
|
||||
def __cudaGetFunctionPointer(name: str, /) -> _ctypes._CFuncPtr: # type: ignore[name-defined]
|
||||
"""Get the function pointer from the CUDA Runtime library.
|
||||
|
||||
Raises:
|
||||
|
|
|
|||
|
|
@ -174,7 +174,14 @@ del (
|
|||
|
||||
# 5. Add explicit references to appease linters
|
||||
# pylint: disable=no-member
|
||||
c_nvmlDevice_t: _TypeAlias = _pynvml.c_nvmlDevice_t # noqa: PYI042
|
||||
if _TYPE_CHECKING:
|
||||
# pylint: disable-next=missing-class-docstring,too-few-public-methods,function-redefined
|
||||
class c_nvmlDevice_t(_ctypes.c_void_p):
|
||||
pass
|
||||
|
||||
else:
|
||||
c_nvmlDevice_t: _TypeAlias = _pynvml.c_nvmlDevice_t # type: ignore[no-redef] # noqa: PYI042
|
||||
|
||||
c_nvmlFieldValue_t: _TypeAlias = _pynvml.c_nvmlFieldValue_t # noqa: PYI042
|
||||
NVML_SUCCESS: int = _pynvml.NVML_SUCCESS
|
||||
NVML_ERROR_INSUFFICIENT_SIZE: int = _pynvml.NVML_ERROR_INSUFFICIENT_SIZE
|
||||
|
|
@ -377,6 +384,7 @@ def nvmlShutdown() -> None: # pylint: disable=function-redefined
|
|||
|
||||
def nvmlQuery(
|
||||
func: _Callable[..., _Any] | str,
|
||||
/,
|
||||
*args: _Any,
|
||||
default: _Any = NA,
|
||||
ignore_errors: bool = True,
|
||||
|
|
@ -513,7 +521,7 @@ def nvmlQueryFieldValues(
|
|||
return values_with_timestamps
|
||||
|
||||
|
||||
def nvmlCheckReturn(retval: _Any, types: type | tuple[type, ...] | None = None) -> bool:
|
||||
def nvmlCheckReturn(retval: _Any, types: type | tuple[type, ...] | None = None, /) -> bool:
|
||||
"""Check whether the return value is not :const:`nvitop.NA` and is one of the given types."""
|
||||
if types is None:
|
||||
return retval != NA
|
||||
|
|
@ -643,6 +651,7 @@ if not _pynvml_installation_corrupted:
|
|||
|
||||
def __nvml_device_get_running_processes(
|
||||
func: str,
|
||||
/,
|
||||
handle: c_nvmlDevice_t,
|
||||
) -> list[c_nvmlProcessInfo_t]:
|
||||
"""Helper function for :func:`nvmlDeviceGet{Compute,Graphics,MPSCompute}RunningProcesses`.
|
||||
|
|
|
|||
|
|
@ -114,9 +114,9 @@ def auto_garbage_clean(
|
|||
raises an exception when falls.
|
||||
"""
|
||||
|
||||
def wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||
def wrapper(func: Callable[..., Any], /) -> Callable[..., Any]:
|
||||
@functools.wraps(func)
|
||||
def wrapped(self: GpuProcess, *args: Any, **kwargs: Any) -> Any:
|
||||
def wrapped(self: GpuProcess, /, *args: Any, **kwargs: Any) -> Any:
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
except host.PsutilError as ex:
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ def _can_do_color(
|
|||
# pylint: disable-next=too-many-arguments
|
||||
def colored(
|
||||
text: Any,
|
||||
/,
|
||||
color: Color | None = None,
|
||||
on_color: Highlight | None = None,
|
||||
attrs: Iterable[Attribute] | None = None,
|
||||
|
|
@ -258,6 +259,7 @@ def colored(
|
|||
# pylint: disable-next=too-many-arguments
|
||||
def cprint(
|
||||
text: object,
|
||||
/,
|
||||
color: Color | None = None,
|
||||
on_color: Highlight | None = None,
|
||||
attrs: Iterable[Attribute] | None = None,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import re
|
|||
import sys
|
||||
import time
|
||||
from collections.abc import KeysView
|
||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar, final
|
||||
|
||||
from nvitop.api import termcolor
|
||||
|
||||
|
|
@ -79,6 +79,7 @@ def set_color(value: bool) -> None:
|
|||
|
||||
def colored(
|
||||
text: Any,
|
||||
/,
|
||||
color: termcolor.Color | None = None,
|
||||
on_color: termcolor.Highlight | None = None,
|
||||
attrs: Iterable[termcolor.Attribute] | None = None,
|
||||
|
|
@ -103,6 +104,7 @@ def colored(
|
|||
return str(text)
|
||||
|
||||
|
||||
@final
|
||||
class NaType(str):
|
||||
"""A singleton (:const:`str: 'N/A'`) class represents a not applicable value.
|
||||
|
||||
|
|
@ -133,8 +135,7 @@ class NaType(str):
|
|||
nan
|
||||
"""
|
||||
|
||||
# NOTE: Decorate this class with `@final` and remove `noqa` when we drop Python 3.7 support.
|
||||
def __new__(cls) -> NaType: # noqa: PYI034
|
||||
def __new__(cls) -> NaType:
|
||||
"""Get the singleton instance (:const:`nvitop.NA`)."""
|
||||
if not hasattr(cls, '_instance'):
|
||||
cls._instance = super().__new__(cls, 'N/A')
|
||||
|
|
@ -510,6 +511,7 @@ SIZE_PATTERN: re.Pattern = re.compile(
|
|||
# pylint: disable-next=too-many-return-statements,too-many-branches
|
||||
def bytes2human(
|
||||
b: int | float | NaType, # noqa: PYI041
|
||||
/,
|
||||
*,
|
||||
min_unit: int = 1,
|
||||
) -> str:
|
||||
|
|
@ -546,7 +548,7 @@ def bytes2human(
|
|||
return f'{round(b / PiB, 1):.1f}PiB'
|
||||
|
||||
|
||||
def human2bytes(s: int | str) -> int:
|
||||
def human2bytes(s: int | str, /) -> int:
|
||||
"""Convert a human readable size string (*case insensitive*) to bytes.
|
||||
|
||||
Raises:
|
||||
|
|
@ -582,6 +584,7 @@ def human2bytes(s: int | str) -> int:
|
|||
|
||||
def timedelta2human(
|
||||
dt: int | float | datetime.timedelta | NaType, # noqa: PYI041
|
||||
/,
|
||||
*,
|
||||
round: bool = False, # pylint: disable=redefined-builtin
|
||||
) -> str:
|
||||
|
|
@ -601,7 +604,7 @@ def timedelta2human(
|
|||
return '{:d}:{:02d}'.format(*divmod(seconds, 60))
|
||||
|
||||
|
||||
def utilization2string(utilization: int | float | NaType) -> str: # noqa: PYI041
|
||||
def utilization2string(utilization: int | float | NaType, /) -> str: # noqa: PYI041
|
||||
"""Convert a utilization rate to string."""
|
||||
if utilization != NA:
|
||||
if isinstance(utilization, int):
|
||||
|
|
@ -611,7 +614,7 @@ def utilization2string(utilization: int | float | NaType) -> str: # noqa: PYI04
|
|||
return NA
|
||||
|
||||
|
||||
def boolify(string: str, default: Any = None) -> bool:
|
||||
def boolify(string: str, /, default: Any = None) -> bool:
|
||||
"""Convert the given value, usually a string, to boolean."""
|
||||
if string.lower() in {'true', 'yes', 'on', 'enabled', '1'}:
|
||||
return True
|
||||
|
|
@ -703,7 +706,7 @@ Method = TypeVar('Method', bound=Callable[..., Any])
|
|||
|
||||
|
||||
# Modified from psutil (https://github.com/giampaolo/psutil)
|
||||
def memoize_when_activated(method: Method) -> Method:
|
||||
def memoize_when_activated(method: Method, /) -> Method:
|
||||
"""A memoize decorator which is disabled by default.
|
||||
|
||||
It can be activated and deactivated on request. For efficiency reasons it can be used only
|
||||
|
|
@ -711,7 +714,7 @@ def memoize_when_activated(method: Method) -> Method:
|
|||
"""
|
||||
|
||||
@functools.wraps(method)
|
||||
def wrapped(self: object, *args: Any, **kwargs: Any) -> Any:
|
||||
def wrapped(self: object, /, *args: Any, **kwargs: Any) -> Any:
|
||||
try:
|
||||
# case 1: we previously entered oneshot() ctx
|
||||
# pylint: disable-next=protected-access
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|||
name = "nvitop"
|
||||
description = "An interactive NVIDIA-GPU process viewer and beyond, the one-stop solution for GPU process management."
|
||||
readme = "README.md"
|
||||
requires-python = ">= 3.7"
|
||||
requires-python = ">= 3.8"
|
||||
authors = [{ name = "Xuehai Pan", email = "XuehaiPan@pku.edu.cn" }]
|
||||
license = { text = "Apache License, Version 2.0 (Apache-2.0) & GNU General Public License, Version 3 (GPL-3.0)" }
|
||||
keywords = [
|
||||
|
|
@ -24,7 +24,6 @@ classifiers = [
|
|||
"License :: OSI Approved :: Apache Software License",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
|
|
@ -96,7 +95,7 @@ module = ['nvitop.callbacks.*', 'nvitop.tui.*']
|
|||
ignore_errors = true
|
||||
|
||||
[tool.pylint]
|
||||
main.py-version = "3.7"
|
||||
main.py-version = "3.8"
|
||||
basic.good-names = ["x", "y", "dx", "dy", "p", "s", "fg", "bg", "n", "ui", "tx", "rx"]
|
||||
format.max-line-length = 120
|
||||
"messages control".disable = ["consider-using-f-string", "duplicate-code", "wrong-import-order"]
|
||||
|
|
@ -108,7 +107,7 @@ ignore-words = "docs/source/spelling_wordlist.txt"
|
|||
|
||||
[tool.ruff]
|
||||
# Sync with requires-python
|
||||
target-version = "py37"
|
||||
target-version = "py38"
|
||||
line-length = 100
|
||||
output-format = "full"
|
||||
src = ["nvitop", "nvitop-exporter/nvitop_exporter"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue