Repository: giampaolo/psutil (language: python)

Issue:
ARM64 support
As mentioned in #1782 -- arm64 and it's variants (armv7, armv8, aarch64) are here to stay -- and likely in much higher usage these days with IoT (ie esp32, etc), Raspberry PIs and it's alternatives, and of course AWS with it's Graviton 2 & 3 processors (which have been steadily & rapidly growing in usage especially given higher performance and lower cost compared to their non-Graviton instance types).

Opening this request hoping that `psutil` will reconsider providing wheels for these types of processors. Many companies don't allow software such as gcc, devel packages, etc on production servers and so for many, including myself, it is impossible to deploy `psutil` into a production environment.

Given the high value of `psutil` and that it is often embedded into other packages (meaning you can't use alternatives), hopefully you will reconsider.

While I realize #1972 exists, this request is to support more then just the `aarch64` variant.
wheels: add arm64 builds
## Summary

* OS: windows, arm, mac
* Bug fix: no
* Type: wheels
* Fixes: #2206, #1972, #1966 

## Description

- add arm64 builds via qemu
- add macos universal2 build
- add windows arm64 build
- bump cancel workflow action to latest to remove nodejs warnings
- bump cibuildwheel action to latest



Discussion / hints:

(everything related to musllinux was copied from #2126 yet not even mentioned in the PR description...)
@giampaolo This PR looks very promising.
I use psutil on an arm64 device, but since I don't have a prebuilt arm64 wheel, I have to go to the trouble of installing build-essential and building it each time.
Considering the extra dependencies required to build psutil, and the fact that arm64 devices are often underpowered and take a lot of time to build, I would prefer to have a pre-built arm64 wheel available from PyPI.
Could you please merge this PR? Please consider it.
Ran into the same issue just now when debugging a Docker image on a MacBook Air M1. Would be great if there were arm64 wheels on PyPI.
> (everything related to musllinux was copied from #2126 yet not even mentioned in the PR description...)

@ben9923 I didn't copy anything from your PR but happy to add attribution and combine them since yours clearly came before mine.

You probably should have reached out to me privately though before making false accusations of this kind though!
> > (everything related to musllinux was copied from #2126 yet not even mentioned in the PR description...)
> 
> 
> 
> @ben9923 I didn't copy anything from your PR but happy to add attribution and combine them since yours clearly came before mine.
> 
> 
> 
> You probably should have reached out to me privately though before making false accusations of this kind though!

I highly doubt those package install commands made to run, for example, some missing network tests (on already supported platforms) came from nowhere, but anyway mostly said it because your PR description doesn't even mention any of the changes that are highlighted in my PR and are included in yours.

I don't really care about the credit for some yaml lines, just want some wheels 😛
@ben9923 anyone who was trying to get this to work with tests passing etc would have to add the same stuff. So it's hardly a surprise.

I was originally trying to get PyPy to work too as well as some builds for py2 on Windows but ran out of time and gave up on those bits as they were non essential for what I was doing at the time.

In any case, as you say, just need more wheels so hopefully this gets pulled in one way or another 👍😎
Yes, wheels on PyPI would be great. In my book you're both stars for working on fixing this @ben9923 @shawaj
I have also run into this issue.
Hey this looks promising! I can't tell from the diff. Would this yield an arm64 wheel for python 3.12 or just some older versions?
Adding my upvote to get this merged.

In my experience, lots of tech companies now supply devs with Mac laptops that are `arm64`. Building a linux-based docker image that uses `psutil` doesn't work naturally without the linux+arm64 wheels. This PR would make things work "out of the box" for that common scenario.

For anyone looking for workarounds in the meantime, I've found a few options:
- make sure your docker image has system packages like `build-essential` installed so it can build `psutil` from source (hopefully). I'd recommend doing a multi-stage dockerfile so the `build-essential` system packages aren't in your final image
- build the docker image with non-native architecture so a pre-built wheel can be used. example command: `docker build --platform=linux/amd64 -t my-docker-image .`
- build your own wheel of `psutil` for linux+arm64, and make sure it's accessible to your docker build. for example, you could host the wheel(s) in your own private python package index and use an arg like `--extra-index-url` to point pip to the private index
Just FWIW, GitHub now offers free standard GHA runners for macOS arm64, and to my understanding there are active plans to offer Linux and Windows arm64 support within the next ≈year.
Hello, may I ask what the current status of this is? Do you need any more work or testing to be done before this PR will be considered for a merge? Thanks!

cc @larsoner
@hoechenberger I'm not sure. Happy to do more work to get it merged but haven't heard anything on this from @giampaolo since opening the PR so unclear what the status is sadly 😕
Hello @giampaolo, what are your thoughts on this? This is something that's important e.g. to users on Apple Silicon.

Fix patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c4e9c745e..d23bd179c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -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
 
@@ -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
diff --git a/HISTORY.rst b/HISTORY.rst
index ae664900f..5107b1345 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -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**
 
diff --git a/pyproject.toml b/pyproject.toml
index b03392071..ba13f2829 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -207,6 +207,7 @@ trailing_comma_inline_array = true
 skip = [
     "*-musllinux*",
     "cp313-win*",  # pywin32 is not available on cp313 yet
+    "cp3{7,8,9,10,11,12}-*linux_{aarch64,ppc64le,s390x}",  # Only test cp36/cp313 on qemu tested architectures
     "pp*",
 ]
 test-command = [
@@ -218,6 +219,9 @@ test-extras = "test"
 [tool.cibuildwheel.macos]
 archs = ["arm64", "x86_64"]
 
+[tool.cibuildwheel.linux]
+before-all = "yum install -y net-tools"
+
 [build-system]
 build-backend = "setuptools.build_meta"
 requires = ["setuptools>=43", "wheel"]


Test patch:
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 684c68ae2..dccb9e4b2 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -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',
@@ -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
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index e768e7d52..a5469ac8a 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -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
@@ -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():
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index be264ae1d..d8faac968 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -28,6 +28,7 @@
 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
@@ -35,6 +36,7 @@
 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
@@ -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(
@@ -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(
@@ -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.
@@ -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):
@@ -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)
@@ -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(')')]
diff --git a/psutil/tests/test_memleaks.py b/psutil/tests/test_memleaks.py
index 8232b18ad..6506497c9 100755
--- a/psutil/tests/test_memleaks.py
+++ b/psutil/tests/test_memleaks.py
@@ -19,6 +19,7 @@
 import functools
 import os
 import platform
+import sys
 import unittest
 
 import psutil
@@ -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
@@ -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)
 
@@ -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)
 
@@ -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__)
diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py
index 59416592e..5c05d1764 100755
--- a/psutil/tests/test_misc.py
+++ b/psutil/tests/test_misc.py
@@ -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
@@ -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()
@@ -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')
 
diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py
index 5203c2707..941f0fac1 100755
--- a/psutil/tests/test_posix.py
+++ b/psutil/tests/test_posix.py
@@ -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
@@ -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):
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index 1ee5393c2..363474c78 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -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
@@ -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
@@ -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]
@@ -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:
@@ -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()
@@ -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")
@@ -771,13 +780,14 @@ 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()
@@ -785,7 +795,8 @@ def test_name(self):
         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 = [
@@ -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:
@@ -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)
@@ -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()
diff --git a/psutil/tests/test_process_all.py b/psutil/tests/test_process_all.py
index d1f476bb5..48833a105 100755
--- a/psutil/tests/test_process_all.py
+++ b/psutil/tests/test_process_all.py
@@ -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
@@ -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)
 
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index 554fbffb2..e228f6d32 100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -48,6 +48,7 @@
 from psutil.tests import IS_64BIT
 from psutil.tests import MACOS_12PLUS
 from psutil.tests import PYPY
+from psutil.tests import QEMU_USER
 from psutil.tests import UNICODE_SUFFIX
 from psutil.tests import PsutilTestCase
 from psutil.tests import check_net_address
@@ -806,6 +807,7 @@ def test_net_io_counters_no_nics(self):
             self.assertEqual(psutil.net_io_counters(pernic=True), {})
             assert m.called
 
+    @unittest.skipIf(QEMU_USER, 'QEMU user not supported')
     def test_net_if_addrs(self):
         nics = psutil.net_if_addrs()
         assert nics, nics
@@ -893,6 +895,7 @@ def test_net_if_addrs_mac_null_bytes(self):
             else:
                 self.assertEqual(addr.address, '06-3d-29-00-00-00')
 
+    @unittest.skipIf(QEMU_USER, 'QEMU user not supported')
     def test_net_if_stats(self):
         nics = psutil.net_if_stats()
         assert nics, nics