Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add aarch64 manylinux wheels #2425

Merged
merged 6 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 15 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,20 @@ concurrency:
jobs:
# Linux + macOS + Windows Python 3
py3:
name: py3-${{ matrix.os }}-${{ startsWith(matrix.os, 'ubuntu') && 'all' || matrix.archs }}
name: "py3-${{ matrix.os }}-${{ matrix.arch }}"
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
archs: "x86_64 i686"
- os: macos-12
archs: "x86_64"
- os: macos-14
archs: "arm64"
- os: windows-2019
archs: "AMD64"
- os: windows-2019
archs: "x86"
- {os: ubuntu-latest, arch: x86_64}
- {os: ubuntu-latest, arch: i686}
- {os: ubuntu-latest, arch: aarch64}
- {os: macos-12, arch: x86_64}
- {os: macos-14, arch: arm64}
- {os: windows-2019, arch: AMD64}
- {os: windows-2019, arch: x86}
steps:
- uses: actions/checkout@v4

Expand All @@ -51,16 +48,20 @@ jobs:
with:
python-version: 3.11

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
if: matrix.arch == 'aarch64'

- name: Create wheels + run tests
uses: pypa/cibuildwheel@v2.18.0
uses: pypa/cibuildwheel@v2.19.1
env:
CIBW_ARCHS: "${{ matrix.archs }}"
CIBW_ARCHS: "${{ matrix.arch }}"
CIBW_PRERELEASE_PYTHONS: True

- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-py3-${{ matrix.os }}-${{ startsWith(matrix.os, 'ubuntu') && 'all' || matrix.archs }}
name: wheels-py3-${{ matrix.os }}-${{ matrix.arch }}
path: wheelhouse

- name: Generate .tar.gz
Expand Down
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- 2407_: `Process.connections()`_ was renamed to `Process.net_connections()`_.
The old name is still available, but it's deprecated (triggers a
``DeprecationWarning``) and will be removed in the future.
- 2425_: [Linux]: provide aarch64 wheels. (patch by Matthieu Darbois / Ben Raz)

**Bug fixes**

Expand Down
8 changes: 7 additions & 1 deletion psutil/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT",
"HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS",
"HAS_SENSORS_TEMPERATURES", "HAS_NET_CONNECTIONS_UNIX", "MACOS_11PLUS",
"MACOS_12PLUS", "COVERAGE",
"MACOS_12PLUS", "COVERAGE", 'AARCH64', "QEMU_USER",
# subprocesses
'pyrun', 'terminate', 'reap_children', 'spawn_testproc', 'spawn_zombie',
'spawn_children_pair',
Expand Down Expand Up @@ -128,8 +128,14 @@
GITHUB_ACTIONS = 'GITHUB_ACTIONS' in os.environ or 'CIBUILDWHEEL' in os.environ
CI_TESTING = APPVEYOR or GITHUB_ACTIONS
COVERAGE = 'COVERAGE_RUN' in os.environ
if LINUX and GITHUB_ACTIONS:
with open('/proc/1/cmdline') as f:
QEMU_USER = "/bin/qemu-" in f.read()
else:
QEMU_USER = False
# are we a 64 bit process?
IS_64BIT = sys.maxsize > 2**32
AARCH64 = platform.machine() == "aarch64"


@memoize
Expand Down
2 changes: 2 additions & 0 deletions psutil/tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from psutil.tests import HAS_SENSORS_FANS
from psutil.tests import HAS_SENSORS_TEMPERATURES
from psutil.tests import PYPY
from psutil.tests import QEMU_USER
from psutil.tests import SKIP_SYSCONS
from psutil.tests import PsutilTestCase
from psutil.tests import create_sockets
Expand Down Expand Up @@ -277,6 +278,7 @@ def test_net_if_addrs(self):
self.assertIsInstance(addr.netmask, (str, type(None)))
self.assertIsInstance(addr.broadcast, (str, type(None)))

@unittest.skipIf(QEMU_USER, 'QEMU user not supported')
def test_net_if_stats(self):
# Duplicate of test_system.py. Keep it anyway.
for ifname, info in psutil.net_if_stats().items():
Expand Down
23 changes: 20 additions & 3 deletions psutil/tests/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
from psutil._compat import PY3
from psutil._compat import FileNotFoundError
from psutil._compat import basestring
from psutil.tests import AARCH64
from psutil.tests import GITHUB_ACTIONS
from psutil.tests import GLOBAL_TIMEOUT
from psutil.tests import HAS_BATTERY
from psutil.tests import HAS_CPU_FREQ
from psutil.tests import HAS_GETLOADAVG
from psutil.tests import HAS_RLIMIT
from psutil.tests import PYPY
from psutil.tests import QEMU_USER
from psutil.tests import TOLERANCE_DISK_USAGE
from psutil.tests import TOLERANCE_SYS_MEM
from psutil.tests import PsutilTestCase
Expand Down Expand Up @@ -277,8 +279,14 @@ def test_used(self):
# This got changed in:
# https://gitlab.com/procps-ng/procps/commit/
# 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
# Newer versions of procps are using yet another way to compute used
# memory.
# https://gitlab.com/procps-ng/procps/commit/
# 2184e90d2e7cdb582f9a5b706b47015e56707e4d
if get_free_version_info() < (3, 3, 12):
raise unittest.SkipTest("old free version")
raise unittest.SkipTest("free version too old")
if get_free_version_info() >= (4, 0, 0):
raise unittest.SkipTest("free version too recent")
cli_value = free_physmem().used
psutil_value = psutil.virtual_memory().used
self.assertAlmostEqual(
Expand Down Expand Up @@ -341,8 +349,14 @@ def test_used(self):
# This got changed in:
# https://gitlab.com/procps-ng/procps/commit/
# 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
# Newer versions of procps are using yet another way to compute used
# memory.
# https://gitlab.com/procps-ng/procps/commit/
# 2184e90d2e7cdb582f9a5b706b47015e56707e4d
if get_free_version_info() < (3, 3, 12):
raise unittest.SkipTest("old free version")
raise unittest.SkipTest("free version too old")
if get_free_version_info() >= (4, 0, 0):
raise unittest.SkipTest("free version too recent")
vmstat_value = vmstat('used memory') * 1024
psutil_value = psutil.virtual_memory().used
self.assertAlmostEqual(
Expand Down Expand Up @@ -830,6 +844,7 @@ def path_exists_mock(path):
assert psutil.cpu_freq()

@unittest.skipIf(not HAS_CPU_FREQ, "not supported")
@unittest.skipIf(AARCH64, "aarch64 does not report mhz in /proc/cpuinfo")
def test_emulate_use_cpuinfo(self):
# Emulate a case where /sys/devices/system/cpu/cpufreq* does not
# exist and /proc/cpuinfo is used instead.
Expand Down Expand Up @@ -1037,6 +1052,7 @@ def test_ips(self):


@unittest.skipIf(not LINUX, "LINUX only")
@unittest.skipIf(QEMU_USER, "QEMU user not supported")
class TestSystemNetIfStats(PsutilTestCase):
@unittest.skipIf(not which("ifconfig"), "ifconfig utility not available")
def test_against_ifconfig(self):
Expand Down Expand Up @@ -1596,7 +1612,7 @@ def test_issue_687(self):
with ThreadTask():
p = psutil.Process()
threads = p.threads()
self.assertEqual(len(threads), 2)
self.assertEqual(len(threads), 3 if QEMU_USER else 2)
tid = sorted(threads, key=lambda x: x.id)[1].id
self.assertNotEqual(p.pid, tid)
pt = psutil.Process(tid)
Expand Down Expand Up @@ -2276,6 +2292,7 @@ def test_name(self):
value = self.read_status_file("Name:")
self.assertEqual(self.proc.name(), value)

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_status(self):
value = self.read_status_file("State:")
value = value[value.find('(') + 1 : value.rfind(')')]
Expand Down
9 changes: 9 additions & 0 deletions psutil/tests/test_memleaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import functools
import os
import platform
import sys
import unittest

import psutil
Expand All @@ -43,6 +44,7 @@
from psutil.tests import HAS_SENSORS_BATTERY
from psutil.tests import HAS_SENSORS_FANS
from psutil.tests import HAS_SENSORS_TEMPERATURES
from psutil.tests import QEMU_USER
from psutil.tests import TestMemoryLeak
from psutil.tests import create_sockets
from psutil.tests import get_testfn
Expand Down Expand Up @@ -398,6 +400,7 @@ def test_disk_usage(self):
times = FEW_TIMES if POSIX else self.times
self.execute(lambda: psutil.disk_usage('.'), times=times)

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_disk_partitions(self):
self.execute(psutil.disk_partitions)

Expand Down Expand Up @@ -435,6 +438,7 @@ def test_net_if_addrs(self):
tolerance = 80 * 1024 if WINDOWS else self.tolerance
self.execute(psutil.net_if_addrs, tolerance=tolerance)

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_net_if_stats(self):
self.execute(psutil.net_if_stats)

Expand Down Expand Up @@ -491,6 +495,11 @@ def test_win_service_get_description(self):


if __name__ == '__main__':
from psutil.tests.runner import cprint
from psutil.tests.runner import run_from_name

if QEMU_USER:
cprint("skipping %s tests under QEMU_USER" % __file__, "brown")
sys.exit(0)

run_from_name(__file__)
5 changes: 5 additions & 0 deletions psutil/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from psutil.tests import HAS_SENSORS_TEMPERATURES
from psutil.tests import PYTHON_EXE
from psutil.tests import PYTHON_EXE_ENV
from psutil.tests import QEMU_USER
from psutil.tests import SCRIPTS_DIR
from psutil.tests import PsutilTestCase
from psutil.tests import mock
Expand Down Expand Up @@ -288,6 +289,9 @@ def check(ret):
for fun, name in ns.iter(ns.getters):
if name in {"win_service_iter", "win_service_get"}:
continue
if QEMU_USER and name == "net_if_stats":
# OSError: [Errno 38] ioctl(SIOCETHTOOL) not implemented
continue
with self.subTest(name=name):
try:
ret = fun()
Expand Down Expand Up @@ -1008,6 +1012,7 @@ def test_pstree(self):
def test_netstat(self):
self.assert_stdout('netstat.py')

@unittest.skipIf(QEMU_USER, 'QEMU user not supported')
def test_ifconfig(self):
self.assert_stdout('ifconfig.py')

Expand Down
7 changes: 6 additions & 1 deletion psutil/tests/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from psutil import SUNOS
from psutil.tests import HAS_NET_IO_COUNTERS
from psutil.tests import PYTHON_EXE
from psutil.tests import QEMU_USER
from psutil.tests import PsutilTestCase
from psutil.tests import mock
from psutil.tests import retry_on_failure
Expand Down Expand Up @@ -102,7 +103,11 @@ def ps_name(pid):
field = "command"
if SUNOS:
field = "comm"
return ps(field, pid).split()[0]
command = ps(field, pid).split()
if QEMU_USER:
assert "/bin/qemu-" in command[0]
return command[1]
return command[0]


def ps_args(pid):
Expand Down
24 changes: 19 additions & 5 deletions psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
from psutil.tests import PYPY
from psutil.tests import PYTHON_EXE
from psutil.tests import PYTHON_EXE_ENV
from psutil.tests import QEMU_USER
from psutil.tests import PsutilTestCase
from psutil.tests import ThreadTask
from psutil.tests import call_until
Expand Down Expand Up @@ -253,6 +254,7 @@ def test_cpu_percent_numcpus_none(self):
psutil.Process().cpu_percent()
assert m.called

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_cpu_times(self):
times = psutil.Process().cpu_times()
assert times.user >= 0.0, times
Expand All @@ -265,6 +267,7 @@ def test_cpu_times(self):
for name in times._fields:
time.strftime("%H:%M:%S", time.localtime(getattr(times, name)))

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_cpu_times_2(self):
user_time, kernel_time = psutil.Process().cpu_times()[:2]
utime, ktime = os.times()[:2]
Expand Down Expand Up @@ -633,6 +636,8 @@ def test_memory_maps(self):

for nt in maps:
if not nt.path.startswith('['):
if QEMU_USER and "/bin/qemu-" in nt.path:
continue
assert os.path.isabs(nt.path), nt.path
if POSIX:
try:
Expand Down Expand Up @@ -698,6 +703,7 @@ def test_is_running(self):
assert not p.is_running()
assert not p.is_running()

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_exe(self):
p = self.spawn_psproc()
exe = p.exe()
Expand Down Expand Up @@ -754,6 +760,9 @@ def test_cmdline(self):
' '.join(p.cmdline()[1:]), ' '.join(cmdline[1:])
)
return
if QEMU_USER:
self.assertEqual(' '.join(p.cmdline()[2:]), ' '.join(cmdline))
return
self.assertEqual(' '.join(p.cmdline()), ' '.join(cmdline))

@unittest.skipIf(PYPY, "broken on PYPY")
Expand All @@ -771,21 +780,23 @@ def test_long_cmdline(self):
self.assertEqual(p.cmdline(), cmdline)
except psutil.ZombieProcess:
raise unittest.SkipTest("OPENBSD: process turned into zombie")
elif NETBSD:
elif QEMU_USER:
self.assertEqual(p.cmdline()[2:], cmdline)
else:
ret = p.cmdline()
if ret == []:
if NETBSD and ret == []:
# https://github.com/giampaolo/psutil/issues/2250
raise unittest.SkipTest("OPENBSD: returned EBUSY")

self.assertEqual(p.cmdline(), cmdline)
self.assertEqual(ret, cmdline)

def test_name(self):
p = self.spawn_psproc()
name = p.name().lower()
pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()
assert pyexe.startswith(name), (pyexe, name)

@unittest.skipIf(PYPY, "unreliable on PYPY")
@unittest.skipIf(PYPY or QEMU_USER, "unreliable on PYPY")
@unittest.skipIf(QEMU_USER, "unreliable on QEMU user")
def test_long_name(self):
pyexe = create_py_exe(self.get_testfn(suffix="0123456789" * 2))
cmdline = [
Expand Down Expand Up @@ -816,6 +827,7 @@ def test_long_name(self):
@unittest.skipIf(SUNOS, "broken on SUNOS")
@unittest.skipIf(AIX, "broken on AIX")
@unittest.skipIf(PYPY, "broken on PYPY")
@unittest.skipIf(QEMU_USER, "broken on QEMU user")
def test_prog_w_funky_name(self):
# Test that name(), exe() and cmdline() correctly handle programs
# with funky chars such as spaces and ")", see:
Expand Down Expand Up @@ -922,6 +934,7 @@ def cleanup(init):
except psutil.AccessDenied:
pass

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
def test_status(self):
p = psutil.Process()
self.assertEqual(p.status(), psutil.STATUS_RUNNING)
Expand Down Expand Up @@ -1149,6 +1162,7 @@ def test_parent_multi(self):
self.assertEqual(grandchild.parent(), child)
self.assertEqual(child.parent(), parent)

@unittest.skipIf(QEMU_USER, "QEMU user not supported")
@retry_on_failure()
def test_parents(self):
parent = psutil.Process()
Expand Down
4 changes: 4 additions & 0 deletions psutil/tests/test_process_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from psutil._compat import long
from psutil._compat import unicode
from psutil.tests import CI_TESTING
from psutil.tests import QEMU_USER
from psutil.tests import VALID_PROC_STATUSES
from psutil.tests import PsutilTestCase
from psutil.tests import check_connection_ntuple
Expand Down Expand Up @@ -235,6 +236,9 @@ def username(self, ret, info):
def status(self, ret, info):
self.assertIsInstance(ret, str)
assert ret, ret
if QEMU_USER:
# status does not work under qemu user
return
self.assertNotEqual(ret, '?') # XXX
self.assertIn(ret, VALID_PROC_STATUSES)

Expand Down
Loading
Loading