Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
829aeed
impr: Added type annotation in `_utils` module (Iteration 1)
DebanKsahu Sep 4, 2026
fe055a7
impr: Added type annotation in `_utils` module (Iteration 2)
DebanKsahu Sep 5, 2026
047052e
impr: Added type annotation in `_formatter` module (Iteration 1)
DebanKsahu Sep 6, 2026
363a97b
impr: Resolved merge conflict in `_formatter` module
DebanKsahu Sep 12, 2026
0a4afdd
feat: Added `pyrefly` and `mypy` settings to `pyproject.toml`. It wil…
DebanKsahu Sep 15, 2026
ff10a3a
impr: Added type annotation in `_formatter` module (Iteration 2)
DebanKsahu Sep 15, 2026
3c2c3fa
chore: Updated `ruff` rules to automate some fixes for `mypy`'s missi…
DebanKsahu Sep 16, 2026
d593e5d
impr: Added type annotation in `_hooks` module (Iteration 1)
DebanKsahu Sep 16, 2026
7a538cc
chore: Revert `ANN201` and `ANN202` rule for now as a lot of files st…
DebanKsahu Sep 16, 2026
e9d9af7
impr: Added type annotation in `_win_memory` module (Iteration 1)
DebanKsahu Sep 25, 2026
6db29f3
impr: Added type annotation in `_metadata` module (Iteration 1)
DebanKsahu Sep 26, 2026
162af55
impr: Added type annotation in `_cpu_utils` module (Iteration 1)
DebanKsahu Sep 28, 2026
7624e87
impr: Added type annotation in `_linux_memory` module (Iteration 1)
DebanKsahu Sep 28, 2026
b336f48
impr: Added type annotation in `_psutil_memory` module (Iteration 1)
DebanKsahu Sep 28, 2026
f4c51c5
impr: Added type annotation in `_process_time` module (Iteration 1)
DebanKsahu Sep 29, 2026
5af8e52
impr: Added type annotation in `_metadata` module (Iteration 2)
DebanKsahu Sep 30, 2026
591320e
fix: Fixed the `override` decorator import error as it is in `typing_…
DebanKsahu Sep 30, 2026
5a9ef51
fix: Fixed the wrong import error of `TypedDict` and import error of …
DebanKsahu Sep 30, 2026
912390b
fix: Fixed error of unsuported multiple inheritance in `NameTuple` in…
DebanKsahu Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 35 additions & 35 deletions pyperf/_cpu_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Literal
from collections.abc import Sequence
import collections
import os
import re
Expand All @@ -6,14 +8,13 @@

try:
if not USE_PSUTIL:
psutil = None
else:
import psutil
raise ImportError
import psutil
except ImportError:
psutil = None
psutil = None # type: ignore[assignment]


def get_logical_cpu_count():
def get_logical_cpu_count() -> int | None:
if psutil is not None:
# Number of logical CPUs
cpu_count = psutil.cpu_count()
Expand All @@ -28,15 +29,15 @@ def get_logical_cpu_count():
return cpu_count


def format_cpu_list(cpus):
def format_cpu_list(cpus: Sequence[int]) -> str:
if len(cpus)==0:
return "None"
cpus = sorted(cpus)
parts = []
first = None
last = None
for cpu in cpus:
if first is None:
first = cpu
elif cpu != last + 1:
first = cpus[0]
last = cpus[0]
for cpu in cpus[1:]:
if cpu != last + 1:
if first != last:
parts.append('%s-%s' % (first, last))
else:
Expand All @@ -50,7 +51,7 @@ def format_cpu_list(cpus):
return ','.join(parts)


def format_cpu_infos(infos):
def format_cpu_infos(infos: dict[int, str]) -> list[str]:
groups = collections.defaultdict(list)
for cpu, info in infos.items():
groups[info].append(cpu)
Expand All @@ -59,19 +60,19 @@ def format_cpu_infos(infos):
items.sort()
text = []
for cpus, info in items:
cpus = format_cpu_list(cpus)
text.append('%s=%s' % (cpus, info))
cpu_str = format_cpu_list(cpus)
text.append('%s=%s' % (cpu_str, info))
return text


def parse_cpu_list(cpu_list):
def parse_cpu_list(cpu_list: str) -> list[int] | None:
cpu_list = cpu_list.strip(' \x00')
# /sys/devices/system/cpu/nohz_full returns ' (null)\n' when NOHZ full
# is not used
if cpu_list == '(null)':
return
return None
if not cpu_list:
return
return None

cpus = []
for part in cpu_list.split(','):
Expand All @@ -88,15 +89,15 @@ def parse_cpu_list(cpu_list):
return cpus


def parse_cpu_mask(line):
def parse_cpu_mask(line: str) -> int:
mask = 0
for part in line.split(','):
mask <<= 32
mask |= int(part, 16)
return mask


def format_cpu_mask(mask):
def format_cpu_mask(mask: int) -> str:
parts = []
while 1:
part = "%08x" % (mask & 0xffffffff)
Expand All @@ -107,14 +108,14 @@ def format_cpu_mask(mask):
return ','.join(reversed(parts))


def format_cpus_as_mask(cpus):
def format_cpus_as_mask(cpus: Sequence[int]) -> str:
mask = 0
for cpu in cpus:
mask |= (1 << cpu)
return format_cpu_mask(mask)


def get_isolated_cpus():
def get_isolated_cpus() -> list[int] | None:
"""Get the list of isolated CPUs.

Return a sorted list of CPU identifiers, or return None if no CPU is
Expand All @@ -137,48 +138,47 @@ def get_isolated_cpus():
return None


def set_cpu_affinity(cpus):
def set_cpu_affinity(cpus: list[int]) -> Literal[True] | None:
# Availability: some Unix platforms
if hasattr(os, 'sched_setaffinity'):
os.sched_setaffinity(0, cpus)
return True

try:
if not USE_PSUTIL:
return
else:
import psutil
raise ImportError
import psutil
except ImportError:
return
return None

# Availability: Linux, Windows, FreeBSD (psutil 2.2.0+)
# https://psutil.rtfd.io/en/latest/index.html#psutil.Process.cpu_affinity
proc = psutil.Process()
if not hasattr(proc, 'cpu_affinity'):
return
return None

proc.cpu_affinity(cpus)
return True


def set_highest_priority():
def set_highest_priority() -> Literal[True] | None:
try:
if not USE_PSUTIL:
return
else:
import psutil
raise ImportError
import psutil
except ImportError:
return
return None

proc = psutil.Process()
if not hasattr(proc, 'nice'):
return
return None

# Want to set realtime on Windows.
# Fail hard for anything else right now, so it is obvious what to fix
# when adding other OS support.
try:
proc.nice(psutil.REALTIME_PRIORITY_CLASS)
proc.nice(psutil.REALTIME_PRIORITY_CLASS) # type: ignore[attr-defined,unused-ignore]
return True
except psutil.AccessDenied:
pass
return None
50 changes: 29 additions & 21 deletions pyperf/_formatter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from typing import Literal, cast
from collections.abc import Sequence
import datetime
_TIMEDELTA_UNITS = ('sec', 'ms', 'us', 'ns')


def format_timedeltas(values):
def format_timedeltas(values: Sequence[float]) -> tuple[str, ...]:
ref_value = abs(values[0])
for i in range(2, -9, -1):
if ref_value >= 10.0 ** i:
Expand All @@ -18,11 +21,11 @@ def format_timedeltas(values):
return tuple(fmt % (value * factor,) for value in values)


def format_timedelta(value):
def format_timedelta(value: float) -> str:
return format_timedeltas((value,))[0]


def format_filesize(size):
def format_filesize(size: float) -> str:
if size < 10 * 1024:
if size != 1:
return '%.0f bytes' % size
Expand All @@ -35,11 +38,11 @@ def format_filesize(size):
return '%.1f KiB' % (size / 1024.0)


def format_filesizes(sizes):
def format_filesizes(sizes: Sequence[float]) -> tuple[str, ...]:
return tuple(format_filesize(size) for size in sizes)


def format_seconds(seconds):
def format_seconds(seconds: float | None) -> str:
# Coarse but human readable duration
if not seconds:
return '0 sec'
Expand All @@ -64,8 +67,9 @@ def format_seconds(seconds):
return ' '.join(parts)


def format_number(number, unit=None, units=None):
def format_number(number: int, unit: str | None = None, units: str | None = None) -> str:
plural = (not number or abs(number) > 1)
str_number = str(number)
if number >= 10000:
pow10 = 0
x = number
Expand All @@ -75,47 +79,51 @@ def format_number(number, unit=None, units=None):
if r:
break
if not r:
number = '10^%s' % pow10
str_number = '10^%s' % pow10

if isinstance(number, int) and number > 8192:
if str_number == str(number) and number > 8192:
if number & (number-1) == 0:
number = '2^%s' % (number.bit_length() - 1)
str_number = '2^%s' % (number.bit_length() - 1)

if not unit:
return str(number)
return str_number

if plural:
if not units:
units = unit + 's'
return '%s %s' % (number, units)
return '%s %s' % (str_number, units)
else:
return '%s %s' % (number, unit)
return '%s %s' % (str_number, unit)


def format_integers(numbers):
def format_integers(numbers: Sequence[int]) -> tuple[str, ...]:
return tuple(format_number(number) for number in numbers)


DEFAULT_UNIT = 'second'

UNIT_FORMATTERS = {
'second': format_timedeltas,
'byte': format_filesizes,
'integer': format_integers,
}


def format_values(unit, values):
_UNIT_TYPE = Literal['second', 'byte', 'integer']
DEFAULT_UNIT: _UNIT_TYPE = 'second'
def format_values(unit: _UNIT_TYPE | None, values: Sequence[float | int]) -> tuple[str, ...]:
if not unit:
unit = DEFAULT_UNIT
formatter = UNIT_FORMATTERS[unit]
return formatter(values)
if unit == 'second':
return format_timedeltas(values)
elif unit == 'byte':
return format_filesizes(values)
elif unit == 'integer':
return format_integers(cast(Sequence[int],values))


def format_value(unit, value):
def format_value(unit: _UNIT_TYPE, value: float | int) -> str:
return format_values(unit, (value,))[0]


def format_datetime(dt, microsecond=True):
def format_datetime(dt: datetime.datetime, microsecond: bool = True) -> str:
if not microsecond:
dt = dt.replace(microsecond=0)
return dt.isoformat(' ')
Loading
Loading