[
  {
    "path": ".clang-format",
    "content": "# Re-adapted from: https://gist.github.com/JPHutchins/6ef33a52cc92fc4a71996b32b11724b4\n# clang-format doc: https://clang.llvm.org/docs/ClangFormatStyleOptions.html\n\nBasedOnStyle: Google\nAlignAfterOpenBracket: BlockIndent\nAlignTrailingComments: false\nAllowAllArgumentsOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortEnumsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLoopsOnASingleLine: false\nBinPackArguments: false\nBinPackParameters: false\nBraceWrapping:\n  AfterCaseLabel: false\n  AfterClass: false\n  AfterControlStatement: MultiLine\n  AfterEnum: false\n  AfterExternBlock: false\n  AfterFunction: false\n  AfterNamespace: false\n  AfterObjCDeclaration: false\n  AfterStruct: false\n  AfterUnion: false\n  BeforeCatch: false\n  BeforeElse: true\n  BeforeLambdaBody: false\n  BeforeWhile: false\n  IndentBraces: false\n  SplitEmptyFunction: false\n  SplitEmptyNamespace: false\n  SplitEmptyRecord: false\nBitFieldColonSpacing: After\nBreakBeforeBinaryOperators: NonAssignment\nBreakBeforeBraces: Custom\nBreakStringLiterals: true\nColumnLimit: 79\nDerivePointerAlignment: false\nIndentCaseBlocks: true\nIndentCaseLabels: true\nIndentWidth: 4\nMaxEmptyLinesToKeep: 2\nPointerAlignment: Right\nSortIncludes: false\nSpaceBeforeParens: ControlStatementsExceptControlMacros\nUseTab: Never\n\n# Force fun return type and fun definition to stay on 2 different lines:\n#    static int\n#    foo() {\n#       printf();\n#    }\nAlwaysBreakAfterReturnType: TopLevelDefinitions\n\n# Prevents:\n#    foo =\n#        Bar(...)\nPenaltyBreakAssignment: 400\nPenaltyBreakBeforeFirstCallParameter: 0\n\n# Handle macros with no `;` at EOL, so that they don't include the next line\n# into them.\nStatementMacros:\n  - Py_BEGIN_ALLOW_THREADS\n  - Py_END_ALLOW_THREADS\n"
  },
  {
    "path": ".dprint.jsonc",
    "content": "{\n    \"markdown\": {\n        \"lineWidth\": 79,\n        \"textWrap\": \"always\",\n    },\n    \"json\": {\n        \"indentWidth\": 4,\n        \"associations\": [\n            \"**/*.json\",\n            \"**/*.jsonc\",\n        ],\n    },\n    \"yaml\": {\n        \"associations\": [\n            \"**/*.yml\",\n            \"**/*.yaml\",\n            \"**/.clang-format\",\n        ],\n    },\n    \"excludes\": [\n        \"**/*-lock.json\",\n        \".github/ISSUE_TEMPLATE/bug.md\",\n        \".github/ISSUE_TEMPLATE/enhancement.md\",\n        \".github/PULL_REQUEST_TEMPLATE.md\",\n    ],\n    \"plugins\": [\n        \"https://plugins.dprint.dev/markdown-0.21.1.wasm\",\n        \"https://plugins.dprint.dev/json-0.21.1.wasm\",\n        \"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.6.0.wasm\",\n    ],\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ntidelift: \"pypi/psutil\"\ngithub: giampaolo\npatreon: # Replace with a single Patreon username\nopen_collective: psutil\nko_fi: # Replace with a single Ko-fi username\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\ncustom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.md",
    "content": "---\nname: Bug\nabout: Report a bug\ntitle: \"[OS] title\"\nlabels: 'bug'\n---\n\n## Summary\n\n- OS: { type-or-version }\n- Architecture: { 64bit, 32bit, ARM, PowerPC, s390 }\n- Psutil version: { pip3 show psutil }\n- Python version: { python3 -V }\n- Type: { core, doc, performance, scripts, tests, wheels, new-api, installation }\n\n## Description\n\n{{{\n  A clear explanation of the bug, including traceback message (if any). Please read the contributing guidelines before submit:\n  https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md\n}}}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question\n    url: https://groups.google.com/g/psutil\n    about: Use this to ask for support\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/enhancement.md",
    "content": "---\nname: Enhancement\nabout: Propose an enhancement\nlabels: 'enhancement'\ntitle: \"[OS] title\"\n---\n\n## Summary\n\n- OS: { type-or-version }\n- Type: { core, doc, performance, scripts, tests, wheels, new-api }\n\n## Description\n\n{{{\n  A clear explanation of your proposal. Please read the contributing guidelines before submit:\n  https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md\n}}}\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## Summary\n\n- OS: { type-or-version }\n- Bug fix: { yes/no }\n- Type: { core, doc, performance, scripts, tests, wheels, new-api }\n- Fixes: { comma-separated list of issues fixed by this PR, if any }\n\n## Description\n\n{{{\n  A clear explanation of your bugfix or enhancement. Please read the contributing guidelines before submit:\n  https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md\n}}}\n"
  },
  {
    "path": ".github/no-response.yml",
    "content": "# Configuration for probot-no-response: https://github.com/probot/no-response\n\n# Number of days of inactivity before an issue is closed for lack of response\ndaysUntilClose: 14\n# Label requiring a response\nresponseRequiredLabel: need-more-info\n# Comment to post when closing an Issue for lack of response.\n# Set to `false` to disable\ncloseComment: >\n  This issue has been automatically closed because there has been no response for more information from the original author. Please reach out if you have or find the answers requested so that this can be investigated further.\n"
  },
  {
    "path": ".github/placeholder",
    "content": ""
  },
  {
    "path": ".github/workflows/bsd.yml",
    "content": "# Execute tests on *BSD platforms. Does not produce wheels.\n# Useful URLs:\n# https://github.com/vmactions/freebsd-vm\n# https://github.com/vmactions/openbsd-vm\n# https://github.com/vmactions/netbsd-vm\n\non:\n  push:\n    # only run this job if the following files are modified\n    paths: &bsd_paths\n      - \".github/workflows/bsd.yml\"\n      - \"psutil/__init__.py\"\n      - \"psutil/_common.py\"\n      - \"psutil/_ntuples.py\"\n      - \"psutil/_psbsd.py\"\n      - \"psutil/_psposix.py\"\n      - \"psutil/_psutil_bsd.c\"\n      - \"psutil/arch/bsd/**\"\n      - \"psutil/arch/freebsd/**\"\n      - \"psutil/arch/netbsd/**\"\n      - \"psutil/arch/openbsd/**\"\n      - \"psutil/arch/posix/**\"\n      - \"setup.py\"\n      - \"tests/**\"\n  pull_request:\n    paths: *bsd_paths\nname: bsd\nconcurrency:\n  group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.sha || '' }}\n  cancel-in-progress: true\njobs:\n  freebsd:\n    # if: false\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Run tests\n        uses: vmactions/freebsd-vm@v1\n        with:\n          usesh: true\n          run: |\n            make ci-test\n\n  openbsd:\n    # if: false\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Run tests\n        uses: vmactions/openbsd-vm@v1\n        with:\n          usesh: true\n          run: |\n            make ci-test\n\n  netbsd:\n    # if: false\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Run tests\n        uses: vmactions/netbsd-vm@v1\n        with:\n          usesh: true\n          run: |\n            make ci-test\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "# Runs CI tests and generates wheels on the following platforms:\n# * Linux\n# * macOS\n# * Windows\n#\n# Useful URLs:\n# * https://github.com/pypa/cibuildwheel\n# * https://github.com/actions/checkout\n# * https://github.com/actions/setup-python\n# * https://github.com/actions/upload-artifact\n\non: [push, pull_request]\nname: build\nconcurrency:\n  # Cancel build if a new one starts, but don't interrupt all jobs on the first\n  # failure.\n  group: build-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n\n  # Run tests on Linux, macOS, Windows\n  tests:\n    name: \"${{ matrix.os }}, ${{ matrix.arch }}\"\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 15\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - { os: ubuntu-latest, arch: x86_64 }\n          - { os: ubuntu-24.04-arm, arch: aarch64 }\n          - { os: macos-15, arch: x86_64 }\n          - { os: macos-15, arch: arm64 }\n          - { os: windows-2025, arch: AMD64 }\n          - { os: windows-11-arm, arch: ARM64 }\n    steps:\n      - uses: actions/checkout@v5\n\n      # Install Python 3.8 on macOS ARM64 for universal2 support, else 3.11\n      - name: Install Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ runner.os == 'macOS' && runner.arch == 'ARM64' && '3.8' || '3.11' }}\n\n      - name: Build wheels + run tests\n        uses: pypa/cibuildwheel@v3.2.1\n        env:\n          CIBW_ARCHS: \"${{ matrix.arch }}\"\n          CIBW_ENABLE: \"cpython-freethreading ${{ startsWith(github.ref, 'refs/tags/') && '' || 'cpython-prerelease' }}\"\n\n      - name: Upload wheels\n        uses: actions/upload-artifact@v4\n        with:\n          name: wheels-${{ matrix.os }}-${{ matrix.arch }}\n          path: wheelhouse\n\n  # Run linters and build doc.\n  linters-and-doc:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - uses: actions/setup-python@v6\n        with:\n          python-version: 3.x\n      - name: \"Run linters\"\n        run: |\n          make ci-lint\n      - name: \"Build doc\"\n        run: |\n          pip install -r docs/requirements.txt\n          cd docs && make html\n\n  # Merge wheels and check sanity of the package distribution.\n  merge-and-check-dist:\n    needs: [tests]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - uses: actions/setup-python@v6\n        with:\n          python-version: 3.x\n      - uses: actions/upload-artifact/merge@v4\n        with:\n          name: wheels\n          pattern: wheels-*\n          separate-directories: false\n          delete-merged: true\n      - uses: actions/download-artifact@v4\n        with:\n          name: wheels\n          path: wheelhouse\n      - run: |\n          make ci-check-dist\n"
  },
  {
    "path": ".github/workflows/changelog_bot.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Activated when commenting \"/changelog\" on a PR.\n\nThis bot will ask Claude to add an entry into docs/changelog.rst based\non the changes introduced in the PR, and also add an entry to\ndocs/credits.rst.\n\nRequires:\n\n- A subscription to Claude API\n- The \"ANTHROPIC_API_KEY\" environment variable to be set via GitHub\n  web interface (Settings -> Secrets & Variables)\n\"\"\"\n\nimport argparse\nimport datetime\nimport json\nimport os\nimport re\nimport sys\nimport urllib.request\n\nimport anthropic\n\n# CLI args\nPR_NUMBER = None\nREPO = None\nTOKEN = None\n\nCHANGELOG_FILE = \"docs/changelog.rst\"\nCREDITS_FILE = \"docs/credits.rst\"\nMAX_DIFF_CHARS = 20_000\nMAX_TOKENS = 1024\n\nPROMPT = \"\"\"\\\nYou are helping maintain the official changelog for psutil, a Python\nsystem monitoring library.\n\nYour task is to generate ONE changelog entry describing the user-visible\nchange introduced by a pull request. Do not generate more than one entry.\n\nPR #{number}: {title}\nAuthor: @{author} (full name: {author_name})\n\nDescription:\n{body}\n\nDiff:\n{diff}\n\nISSUE NUMBER SELECTION\n\nThe changelog should reference the GitHub ISSUE number when one exists,\nnot the pull request number.\n\nDetermine the correct issue number using these rules:\n\n1. If the PR title or description references an issue such as:\n   - \"Fixes #1234\"\n   - \"Closes #1234\"\n   - \"Refs #1234\"\n   - \"#1234\"\n   then use that number.\n\n2. If multiple issues are referenced, choose the primary one most\n   closely related to the change.\n\n3. If no issue reference exists, fall back to the PR number.\n\nThe chosen number will be used in the :gh:`...` role.\n\nSTYLE\n\n- Write concise entries (1-2 sentences max).\n- Focus on the user-visible behavior change.\n- Avoid implementation details unless relevant.\n- Prefer imperative verbs: \"fix\", \"add\", \"improve\", \"avoid\", \"detect\".\n- Do not repeat the PR title verbatim.\n- Do not mention \"PR\" in the text.\n- Wrap lines around ~79 characters.\n\nCLASSIFICATION\n\nClassify the change into one of:\n\nBug fixes\nEnhancements\n\nBug fixes include:\n- crashes\n- incorrect behavior\n- race conditions\n- compilation failures\n- incorrect return values\n- memory leaks\n- platform regressions\n\nEnhancements include:\n- new features\n- performance improvements\n- better detection\n- improved error handling\n- new platform support\n- packaging improvements (e.g. wheels)\n\nPLATFORM TAGS\n\nIf the change is platform specific, add tags immediately after the issue\nreference.\n\nExamples:\n\n:gh:`1234`, [Linux]:\n:gh:`1234`, [Windows]:\n:gh:`1234`, [macOS], [BSD]:\n\nOnly add platform tags if the change clearly affects specific OSes.\n\nRST FORMATTING\n\nUse Sphinx roles for psutil APIs:\n\nFunctions:\n:func:`function_name`\n\nMethods:\n:meth:`Class.method`\n\nClasses:\n:class:`ClassName`\n\nExceptions:\n:exc:`ExceptionName`\n\nC functions or identifiers must use double backticks:\n\n``function_name()``\n\nFORMAT\n\nEach entry must follow this structure:\n\n- :gh:`ISSUE_NUMBER`: <description>.\n\nOr with platforms:\n\n- :gh:`ISSUE_NUMBER`, [Linux]: <description>.\n\nDESCRIPTION GUIDELINES\n\n- Describe the user-visible change.\n- Mention affected psutil APIs when applicable.\n- Avoid mentioning internal helper functions.\n- If fixing incorrect behavior, describe the previous issue.\n- If fixing a crash or compilation error, state it clearly.\n\nEXAMPLES\n\n- :gh:`2708`, [macOS]: :meth:`Process.cmdline()` and\n  :meth:`Process.environ()` may fail with ``OSError: [Errno 0]``.\n  They now raise :exc:`AccessDenied` instead.\n\n- :gh:`2674`, [Windows]: :func:`disk_usage()` could truncate values on\n  32-bit systems for drives larger than 4GB.\n\n- :gh:`2705`, [Linux]: :meth:`Process.wait()` now uses\n  ``pidfd_open()`` + ``poll()`` for waiting, avoiding busy loops\n  and improving response times.\n\nCREDITS\n\npsutil maintains a list of contributors in docs/credits.rst under\n\"Code contributors by year\".\n\nGenerate a credits entry unless the author is @giampaolo, in which\ncase set credits_entry to null.\n\nUse the contributor's full name from their GitHub profile ({author_name})\nunless it is not set, in which case fall back to their username (@{author}).\n\nThe format used in docs/credits.rst is:\n\n* `Name or username`_ - :gh:`ISSUE_NUMBER`\n\nExamples:\n\n* `Sergey Fedorov`_ - :gh:`2701`\n* `someuser`_ - :gh:`2710`\n\nUse the same issue number used in the changelog entry.\n\"\"\"\n\nSUBMIT_TOOL = {\n    \"name\": \"submit\",\n    \"description\": \"Submit the changelog and credits entries.\",\n    \"input_schema\": {\n        \"type\": \"object\",\n        \"properties\": {\n            \"section\": {\n                \"type\": \"string\",\n                \"enum\": [\"Bug fixes\", \"Enhancements\"],\n            },\n            \"changelog_entry\": {\n                \"type\": \"string\",\n                \"description\": \"The RST changelog entry.\",\n            },\n            \"credits_entry\": {\n                \"type\": [\"string\", \"null\"],\n                \"description\": (\n                    \"The RST credits line, or null if author is @giampaolo.\"\n                ),\n            },\n        },\n        \"required\": [\"section\", \"changelog_entry\", \"credits_entry\"],\n    },\n}\n\n\ndef gh_request(path, accept=\"application/vnd.github+json\"):\n    url = f\"https://api.github.com{path}\"\n    req = urllib.request.Request(\n        url,\n        headers={\n            \"Authorization\": f\"Bearer {TOKEN}\",\n            \"Accept\": accept,\n            \"X-GitHub-Api-Version\": \"2022-11-28\",\n        },\n    )\n    with urllib.request.urlopen(req) as resp:\n        return resp.read()\n\n\ndef fetch_pr_metadata():\n    pr = json.loads(gh_request(f\"/repos/{REPO}/pulls/{PR_NUMBER}\"))\n    author = pr[\"user\"][\"login\"]\n    # Fetch the user profile to get the full name.\n    user = json.loads(gh_request(f\"/users/{author}\"))\n    author_name = user.get(\"name\") or author\n    return {\n        \"number\": pr[\"number\"],\n        \"title\": pr[\"title\"],\n        \"body\": pr.get(\"body\") or \"\",\n        \"author\": author,\n        \"author_name\": author_name,\n    }\n\n\ndef fetch_pr_diff():\n    return gh_request(\n        f\"/repos/{REPO}/pulls/{PR_NUMBER}\",\n        accept=\"application/vnd.github.v3.diff\",\n    ).decode(\"utf-8\", errors=\"replace\")\n\n\ndef ask_claude(pr, diff):\n    prompt = PROMPT.format(\n        number=pr[\"number\"],\n        title=pr[\"title\"],\n        author=pr[\"author\"],\n        author_name=pr[\"author_name\"],\n        body=pr[\"body\"],\n        diff=diff[:MAX_DIFF_CHARS],\n    )\n    api_key = os.environ.get(\"ANTHROPIC_API_KEY\", \"\").strip()\n    client = anthropic.Anthropic(api_key=api_key)\n    message = client.messages.create(\n        model=\"claude-sonnet-4-6\",\n        max_tokens=MAX_TOKENS,\n        tools=[SUBMIT_TOOL],\n        tool_choice={\"type\": \"tool\", \"name\": \"submit\"},\n        messages=[{\"role\": \"user\", \"content\": prompt}],\n    )\n    tool_use = next(b for b in message.content if b.type == \"tool_use\")\n    return tool_use.input\n\n\ndef insert_changelog_entry(section, entry):\n    with open(CHANGELOG_FILE) as f:\n        lines = f.readlines()\n\n    version_re = re.compile(r\"^(\\d+\\.\\d+\\.\\d+|X\\.X\\.X).*$\")\n    version_idx = next(\n        (i for i, ln in enumerate(lines) if version_re.match(ln.rstrip())),\n        None,\n    )\n    if version_idx is None:\n        sys.exit(f\"Could not find version block in {CHANGELOG_FILE}\")\n    next_version_idx = next(\n        (\n            i\n            for i in range(version_idx + 1, len(lines))\n            if version_re.match(lines[i].rstrip())\n        ),\n        len(lines),\n    )\n    block = lines[version_idx:next_version_idx]\n\n    # Skip if this issue is already referenced in the version block\n    gh_ref = re.search(r\":gh:`\\d+`\", entry)\n    if gh_ref and any(gh_ref.group(0) in ln for ln in block):\n        print(\n            f\"Changelog entry for {gh_ref.group(0)} already exists, skipping\"\n        )\n        return\n\n    header = f\"**{section}**\"\n    header_idx = next(\n        (i for i, ln in enumerate(block) if ln.rstrip() == header), None\n    )\n\n    def _entry_gh_number(line):\n        \"\"\"Extract the ticket number from a :gh:`N` reference.\"\"\"\n        m = re.search(r\":gh:`(\\d+)`\", line)\n        return int(m.group(1)) if m else None\n\n    new_entry_num = _entry_gh_number(entry)\n\n    if header_idx is None:\n        insert_at = next(\n            (\n                i\n                for i, ln in enumerate(block)\n                if ln.startswith(\"**\") and ln.rstrip() != header\n            ),\n            len(block),\n        )\n        new_block = (\n            block[:insert_at]\n            + [f\"{header}\\n\", \"\\n\", f\"{entry}\\n\", \"\\n\"]\n            + block[insert_at:]\n        )\n    else:\n        # Find the end of this section (next ** header or end of block).\n        section_end = next(\n            (\n                i\n                for i in range(header_idx + 1, len(block))\n                if block[i].startswith(\"**\")\n            ),\n            len(block),\n        )\n        # Skip the blank line after the header.\n        first_entry = header_idx + 1\n        if first_entry < len(block) and not block[first_entry].strip():\n            first_entry += 1\n        # Find the right position sorted by ticket number.\n        insert_at = section_end\n        if new_entry_num is not None:\n            for i in range(first_entry, section_end):\n                num = _entry_gh_number(block[i])\n                if num is not None and num > new_entry_num:\n                    insert_at = i\n                    break\n        else:\n            insert_at = first_entry\n        new_block = block[:insert_at] + [f\"{entry}\\n\"] + block[insert_at:]\n\n    lines[version_idx:next_version_idx] = new_block\n    with open(CHANGELOG_FILE, \"w\") as f:\n        f.writelines(lines)\n\n\ndef update_credits(credits_entry, author, author_name):\n    \"\"\"Insert credits entry and link definition into CREDITS_FILE.\"\"\"\n    with open(CREDITS_FILE) as f:\n        lines = f.readlines()\n\n    year = str(datetime.date.today().year)\n    year_re = re.compile(r\"^\\d{4}$\")\n\n    def sort_key(e):\n        m = re.match(r\"\\*\\s+`([^`]+)`_\", e.strip())\n        return m.group(1).lower() if m else e.strip().lower()\n\n    # Insert year entry\n    section_idx = next(\n        (\n            i\n            for i, ln in enumerate(lines)\n            if ln.rstrip() == \"Code contributors by year\"\n        ),\n        None,\n    )\n    if section_idx is None:\n        sys.exit(\n            f\"Could not find 'Code contributors by year' in {CREDITS_FILE}\"\n        )\n\n    year_idx = next(\n        (\n            i\n            for i in range(section_idx, len(lines))\n            if lines[i].rstrip() == year\n        ),\n        None,\n    )\n    if year_idx is None:\n        first_year = next(\n            (\n                i\n                for i in range(section_idx, len(lines))\n                if year_re.match(lines[i].rstrip())\n            ),\n            len(lines),\n        )\n        lines[first_year:first_year] = [\n            f\"{year}\\n\",\n            \"~\" * len(year) + \"\\n\",\n            \"\\n\",\n            f\"{credits_entry}\\n\",\n            \"\\n\",\n        ]\n    else:\n        year_end = next(\n            (\n                i\n                for i in range(year_idx + 2, len(lines))\n                if year_re.match(lines[i].rstrip())\n            ),\n            len(lines),\n        )\n        new_key = sort_key(credits_entry)\n        insert_idx = year_end\n        skip = False\n        for i in range(year_idx + 2, year_end):\n            if lines[i].startswith(\"* \"):\n                k = sort_key(lines[i])\n                if k == new_key:\n                    print(\n                        f\"Credits entry for {new_key!r} already exists,\"\n                        \" skipping\"\n                    )\n                    skip = True\n                    break\n                if k > new_key:\n                    insert_idx = i\n                    break\n        if not skip:\n            # Don't back up past the blank line after the year\n            # underline (year_idx + 2 = \"~~~~\\n\", + 3 = first\n            # content line).\n            min_idx = year_idx + 3\n            while insert_idx > min_idx and not lines[insert_idx - 1].strip():\n                insert_idx -= 1\n            lines.insert(insert_idx, f\"{credits_entry}\\n\")\n\n    # Insert link definition if missing\n    target = f\".. _`{author_name}`:\"\n    if not any(ln.startswith(target) for ln in lines):\n        link_section = next(\n            (\n                i\n                for i, ln in enumerate(lines)\n                if ln.rstrip() == \".. Code contributors\"\n            ),\n            None,\n        )\n        if link_section is None:\n            sys.exit(\n                \"Could not find code contributors link section in\"\n                f\" {CREDITS_FILE}\"\n            )\n        definition = f\".. _`{author_name}`: https://github.com/{author}\\n\"\n        insert_idx = len(lines)\n        for i in range(link_section, len(lines)):\n            m = re.match(r\"\\.\\. _`([^`]+)`:\", lines[i])\n            if m and m.group(1).lower() > author_name.lower():\n                insert_idx = i\n                break\n        lines.insert(insert_idx, definition)\n\n    with open(CREDITS_FILE, \"w\") as f:\n        f.writelines(lines)\n\n\ndef post_comment(body):\n    url = f\"https://api.github.com/repos/{REPO}/issues/{PR_NUMBER}/comments\"\n    data = json.dumps({\"body\": body}).encode()\n    req = urllib.request.Request(\n        url,\n        data=data,\n        headers={\n            \"Authorization\": f\"Bearer {TOKEN}\",\n            \"Accept\": \"application/vnd.github+json\",\n            \"Content-Type\": \"application/json\",\n            \"X-GitHub-Api-Version\": \"2022-11-28\",\n        },\n    )\n    with urllib.request.urlopen(req):\n        pass\n\n\ndef parse_cli():\n    global PR_NUMBER, REPO, TOKEN\n    p = argparse.ArgumentParser(description=__doc__)\n    p.add_argument(\"--pr-number\", type=int, required=True)\n    p.add_argument(\n        \"--repo\", type=str, required=True, help=\"e.g. giampaolo/psutil\"\n    )\n    p.add_argument(\"--token\", type=str, required=True, help=\"GitHub token\")\n    args = p.parse_args()\n    PR_NUMBER = args.pr_number\n    REPO = args.repo\n    TOKEN = args.token\n\n\ndef main():\n    parse_cli()\n    print(f\"Fetching PR #{PR_NUMBER} from {REPO}...\")\n    pr = fetch_pr_metadata()\n    diff = fetch_pr_diff()\n    print(\"Asking Claude for changelog entry...\")\n    result = ask_claude(pr, diff)\n    section = result[\"section\"]\n    changelog_entry = result[\"changelog_entry\"]\n    credits_entry = result[\"credits_entry\"]\n    print(f\"Section: {section}\")\n    print(f\"Entry:   {changelog_entry}\")\n    insert_changelog_entry(section, changelog_entry)\n    print(f\"Inserted entry into {CHANGELOG_FILE}\")\n    comment = (\n        f\"`{CHANGELOG_FILE}` entry added under **{section}**:\\n\\n\"\n        f\"```rst\\n{changelog_entry}\\n```\"\n    )\n    if credits_entry:\n        print(f\"Credits: {credits_entry}\")\n        update_credits(credits_entry, pr[\"author\"], pr[\"author_name\"])\n        print(f\"Updated {CREDITS_FILE}\")\n        comment += (\n            f\"\\n\\n`{CREDITS_FILE}` entry added:\\n\\n\"\n            f\"```rst\\n{credits_entry}\\n```\"\n        )\n    post_comment(comment)\n    print(\"Posted confirmation comment on PR\")\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": ".github/workflows/changelog_bot.yml",
    "content": "name: changelog-bot\n\non:\n  issue_comment:\n    types: [created]\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  changelog:\n    if: >\n      github.event.comment.body == '/changelog' &&\n      github.event.issue.pull_request != null\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Check commenter permissions\n        id: check-perms\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          PERMISSION=$(gh api \\\n            repos/${{ github.repository }}/collaborators/${{ github.event.comment.user.login }}/permission \\\n            --jq '.permission')\n          echo \"permission=$PERMISSION\"\n          if [[ \"$PERMISSION\" != \"write\" && \"$PERMISSION\" != \"admin\" ]]; then\n            echo \"User ${{ github.event.comment.user.login }} does not have write/admin permission\"\n            exit 1\n          fi\n\n      - name: Get PR info\n        id: pr-info\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          PR_NUMBER=${{ github.event.issue.number }}\n          PR_JSON=$(gh pr view $PR_NUMBER \\\n            --repo ${{ github.repository }} \\\n            --json headRefName,headRepository,headRepositoryOwner)\n          HEAD_BRANCH=$(echo \"$PR_JSON\" | jq -r '.headRefName')\n          HEAD_OWNER=$(echo \"$PR_JSON\" | jq -r '.headRepositoryOwner.login')\n          HEAD_REPO=$(echo \"$PR_JSON\" | jq -r '.headRepository.name')\n          echo \"branch=$HEAD_BRANCH\" >> \"$GITHUB_OUTPUT\"\n          echo \"head_repo=$HEAD_OWNER/$HEAD_REPO\" >> \"$GITHUB_OUTPUT\"\n\n      - name: Checkout PR branch\n        uses: actions/checkout@v5\n        with:\n          ref: refs/pull/${{ github.event.issue.number }}/head\n          token: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Install dependencies\n        run: pip install anthropic\n\n      - name: Run changelog bot\n        env:\n          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}\n        run: |\n          python .github/workflows/changelog_bot.py \\\n            --pr-number ${{ github.event.issue.number }} \\\n            --repo ${{ github.repository }} \\\n            --token ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Commit and push if changelog changed\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"github-actions[bot]@users.noreply.github.com\"\n          git add docs/changelog.rst docs/credits.rst\n          if git diff --cached --quiet; then\n            echo \"No changes, skipping commit\"\n          else\n            git commit -m \"Update changelog for PR #${{ github.event.issue.number }}\"\n            HEAD_REPO=\"${{ steps.pr-info.outputs.head_repo }}\"\n            HEAD_BRANCH=\"${{ steps.pr-info.outputs.branch }}\"\n            PUSH_URL=\"https://x-access-token:${GH_TOKEN}@github.com/${HEAD_REPO}.git\"\n            git push \"$PUSH_URL\" \"HEAD:refs/heads/${HEAD_BRANCH}\"\n          fi\n"
  },
  {
    "path": ".github/workflows/issues.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Bot triggered by Github Actions every time a new issue, PR or comment\nis created. Assign labels, provide replies, closes issues, etc. depending\non the situation.\n\"\"\"\n\nimport functools\nimport json\nimport os\nimport pathlib\nimport re\nfrom pprint import pprint as pp\n\nfrom github import Github\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent\nSCRIPTS_DIR = ROOT_DIR / 'scripts'\n\n\n# --- constants\n\n# fmt: off\nLABELS_MAP = {\n    # platforms\n    \"linux\": [\n        \"linux\", \"ubuntu\", \"redhat\", \"mint\", \"centos\", \"red hat\", \"archlinux\",\n        \"debian\", \"alpine\", \"gentoo\", \"fedora\", \"slackware\", \"suse\", \"RHEL\",\n        \"opensuse\", \"manylinux\", \"apt \", \"apt-\", \"rpm\", \"yum\", \"kali\",\n        \"/sys/class\", \"/proc/net\", \"/proc/disk\", \"/proc/smaps\",\n        \"/proc/vmstat\",\n    ],\n    \"windows\": [\n        \"windows\", \"win32\", \"WinError\", \"WindowsError\", \"win10\", \"win7\",\n        \"win \", \"mingw\", \"msys\", \"studio\", \"microsoft\",\n        \"CloseHandle\", \"GetLastError\", \"NtQuery\", \"DLL\", \"MSVC\", \"TCHAR\",\n        \"WCHAR\", \".bat\", \"OpenProcess\", \"TerminateProcess\",\n        \"windows error\", \"NtWow64\", \"NTSTATUS\", \"Visual Studio\",\n    ],\n    \"macos\": [\n        \"macos\", \"mac \", \"osx\", \"os x\", \"mojave\", \"sierra\", \"capitan\",\n        \"yosemite\", \"catalina\", \"mojave\", \"big sur\", \"xcode\", \"darwin\",\n        \"dylib\", \"m1\",\n    ],\n    \"aix\": [\"aix\"],\n    \"cygwin\": [\"cygwin\"],\n    \"freebsd\": [\"freebsd\"],\n    \"netbsd\": [\"netbsd\"],\n    \"openbsd\": [\"openbsd\"],\n    \"sunos\": [\"sunos\", \"solaris\"],\n    \"wsl\": [\"wsl\"],\n    \"unix\": [\n        \"psposix\", \"waitpid\", \"statvfs\", \"/dev/tty\",\n        \"/dev/pts\", \"posix\",\n    ],\n    \"pypy\": [\"pypy\"],\n    \"docker\": [\"docker\", \"docker-compose\"],\n    \"vm\": [\n        \"docker\", \"docker-compose\", \"vmware\", \"lxc\", \"hyperv\", \"virtualpc\",\n        \"virtualbox\", \"bhyve\", \"openvz\", \"lxc\", \"xen\", \"kvm\", \"qemu\", \"heroku\",\n    ],\n    # types\n    \"enhancement\": [\"enhancement\"],\n    \"memleak\": [\"memory leak\", \"leaks memory\", \"memleak\", \"mem leak\"],\n    \"api\": [\"idea\", \"proposal\", \"api\", \"feature\"],\n    \"performance\": [\"performance\", \"speedup\", \"speed up\", \"slow\", \"fast\"],\n    \"wheels\": [\"wheel\", \"wheels\"],\n    \"scripts\": [\n        \"example script\", \"examples script\", \"example dir\", \"scripts/\",\n    ],\n    # bug\n    \"bug\": [\n        \"fail\", \"can't execute\", \"can't install\", \"cannot execute\",\n        \"cannot install\", \"install error\", \"crash\", \"critical\",\n    ],\n    # doc\n    \"doc\": [\n        \"doc \", \"document \", \"documentation\", \"readthedocs\", \"pythonhosted\",\n        \"HISTORY\", \"README\", \"dev guide\", \"devguide\", \"sphinx\", \"docfix\",\n        \"index.rst\",\n    ],\n    # tests\n    \"tests\": [\n        \" test \", \"tests\", \"travis\", \"coverage\", \"cirrus\",\n        \"continuous integration\", \"unittest\", \"pytest\", \"unit test\",\n    ],\n    # critical errors\n    \"critical\": [\n        \"WinError\", \"WindowsError\", \"RuntimeError\", \"ZeroDivisionError\",\n        \"SystemError\", \"MemoryError\", \"core dump\", \"segfault\",\n        \"segmentation fault\",\n    ],\n}\n\nOS_LABELS = [\n    \"linux\", \"windows\", \"macos\", \"freebsd\", \"openbsd\", \"netbsd\", \"openbsd\",\n    \"bsd\", \"sunos\", \"unix\", \"wsl\", \"aix\", \"cygwin\",\n]\n# fmt: on\n\nLABELS_MAP['scripts'].extend(\n    [x for x in os.listdir(SCRIPTS_DIR) if x.endswith('.py')]\n)\n\nILLOGICAL_PAIRS = [\n    ('bug', 'enhancement'),\n    ('doc', 'tests'),\n    ('scripts', 'doc'),\n    ('scripts', 'tests'),\n    ('bsd', 'freebsd'),\n    ('bsd', 'openbsd'),\n    ('bsd', 'netbsd'),\n]\n\n# --- replies\n\nREPLY_MISSING_PYTHON_HEADERS = \"\"\"\\\nIt looks like you're missing `Python.h` headers. This usually means you have \\\nto install them first, then retry psutil installation.\nPlease read \\\n[install](https://psutil.readthedocs.io/install) \\\ninstructions for your platform. \\\nThis is an auto-generated response based on the text you submitted. \\\nIf this was a mistake or you think there's a bug with psutil installation \\\nprocess, please add a comment to reopen this issue.\n\"\"\"\n\n# REPLY_UPDATE_CHANGELOG = \"\"\"\\\n# \"\"\"\n\n\n# --- github API utils\n\n\ndef is_pr(issue):\n    return issue.pull_request is not None\n\n\ndef has_label(issue, label):\n    assigned = [x.name for x in issue.labels]\n    return label in assigned\n\n\ndef get_repo():\n    repo = os.environ['GITHUB_REPOSITORY']\n    token = os.environ['GITHUB_TOKEN']\n    return Github(token).get_repo(repo)\n\n\n# --- event utils\n\n\n@functools.lru_cache()\ndef _get_event_data():\n    with open(os.environ[\"GITHUB_EVENT_PATH\"]) as f:\n        ret = json.load(f)\n        pp(ret)\n        return ret\n\n\ndef is_event_new_issue():\n    data = _get_event_data()\n    try:\n        return data['action'] == 'opened' and 'issue' in data\n    except KeyError:\n        return False\n\n\ndef is_event_new_pr():\n    data = _get_event_data()\n    try:\n        return data['action'] == 'opened' and 'pull_request' in data\n    except KeyError:\n        return False\n\n\ndef get_issue():\n    data = _get_event_data()\n    try:\n        num = data['issue']['number']\n    except KeyError:\n        num = data['pull_request']['number']\n    return get_repo().get_issue(number=num)\n\n\n# --- actions\n\n\ndef log(msg):\n    if '\\n' in msg or \"\\r\\n\" in msg:\n        print(f\">>>\\n{msg}\\n<<<\", flush=True)\n    else:\n        print(f\">>> {msg} <<<\", flush=True)\n\n\ndef add_label(issue, label):\n    def should_add(issue, label):\n        if has_label(issue, label):\n            log(f\"already has label {label!r}\")\n            return False\n\n        for left, right in ILLOGICAL_PAIRS:\n            if label == left and has_label(issue, right):\n                log(f\"already has label f{label}\")\n                return False\n\n        return not has_label(issue, label)\n\n    if not should_add(issue, label):\n        log(f\"should not add label {label!r}\")\n        return\n\n    log(f\"add label {label!r}\")\n    issue.add_to_labels(label)\n\n\ndef _guess_labels_from_text(text):\n    assert isinstance(text, str), text\n    for label, keywords in LABELS_MAP.items():\n        for keyword in keywords:\n            if keyword.lower() in text.lower():\n                yield (label, keyword)\n\n\ndef add_labels_from_text(issue, text):\n    assert isinstance(text, str), text\n    for label, keyword in _guess_labels_from_text(text):\n        add_label(issue, label)\n\n\ndef add_labels_from_new_body(issue, text):\n    assert isinstance(text, str), text\n    log(\"start searching for template lines in new issue/PR body\")\n    # add os label\n    r = re.search(r\"\\* OS:.*?\\n\", text)\n    log(\"search for 'OS: ...' line\")\n    if r:\n        log(\"found\")\n        add_labels_from_text(issue, r.group(0))\n    else:\n        log(\"not found\")\n\n    # add bug/enhancement label\n    log(\"search for 'Bug fix: y/n' line\")\n    r = re.search(r\"\\* Bug fix:.*?\\n\", text)\n    if (\n        is_pr(issue)\n        and r is not None\n        and not has_label(issue, \"bug\")\n        and not has_label(issue, \"enhancement\")\n    ):\n        log(\"found\")\n        s = r.group(0).lower()\n        if 'yes' in s:\n            add_label(issue, 'bug')\n        else:\n            add_label(issue, 'enhancement')\n    else:\n        log(\"not found\")\n\n    # add type labels\n    log(\"search for 'Type: ...' line\")\n    r = re.search(r\"\\* Type:.*?\\n\", text)\n    if r:\n        log(\"found\")\n        s = r.group(0).lower()\n        if 'doc' in s:\n            add_label(issue, 'doc')\n        if 'performance' in s:\n            add_label(issue, 'performance')\n        if 'scripts' in s:\n            add_label(issue, 'scripts')\n        if 'tests' in s:\n            add_label(issue, 'tests')\n        if 'wheels' in s:\n            add_label(issue, 'wheels')\n        if 'new-api' in s:\n            add_label(issue, 'new-api')\n        if 'new-platform' in s:\n            add_label(issue, 'new-platform')\n    else:\n        log(\"not found\")\n\n\n# --- events\n\n\ndef on_new_issue(issue):\n    def has_text(text):\n        return text in issue.title.lower() or (\n            issue.body and text in issue.body.lower()\n        )\n\n    def body_mentions_python_h():\n        if not issue.body:\n            return False\n        body = issue.body.replace(' ', '')\n        return (\n            \"#include<Python.h>\\n^~~~\" in body\n            or \"#include<Python.h>\\r\\n^~~~\" in body\n        )\n\n    log(\"searching for missing Python.h\")\n    if (\n        has_text(\"missing python.h\")\n        or has_text(\"python.h: no such file or directory\")\n        or body_mentions_python_h()\n    ):\n        log(\"found mention of Python.h\")\n        issue.create_comment(REPLY_MISSING_PYTHON_HEADERS)\n        issue.edit(state='closed')\n        return\n\n\ndef on_new_pr(issue):\n    pass\n    # pr = get_repo().get_pull(issue.number)\n    # files = [x.filename for x in list(pr.get_files())]\n    # if \"changelog.rst\" not in files:\n    #     issue.create_comment(REPLY_UPDATE_CHANGELOG)\n\n\ndef main():\n    issue = get_issue()\n    stype = \"PR\" if is_pr(issue) else \"issue\"\n    log(f\"running issue bot for {stype} {issue!r}\")\n\n    if is_event_new_issue():\n        log(f\"created new issue {issue}\")\n        add_labels_from_text(issue, issue.title)\n        if issue.body:\n            add_labels_from_new_body(issue, issue.body)\n        on_new_issue(issue)\n    elif is_event_new_pr():\n        log(f\"created new PR {issue}\")\n        add_labels_from_text(issue, issue.title)\n        if issue.body:\n            add_labels_from_new_body(issue, issue.body)\n        on_new_pr(issue)\n    else:\n        log(\"unhandled event\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".github/workflows/issues.yml",
    "content": "# Fired by Github Actions every time an issue, PR or comment is created.\n\nname: issues\non:\n  issues:\n    types: [opened]\n  pull_request_target:\n    types: [opened]\n  issue_comment:\n    types: [created]\npermissions:\n  issues: write\n  pull-requests: write\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v5\n\n      - name: Install Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: \"3.x\"\n\n      - name: Install deps\n        run: python3 -m pip install PyGithub\n\n      - name: Run\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          PYTHONUNBUFFERED=1 PYTHONWARNINGS=always python3 .github/workflows/issues.py\n"
  },
  {
    "path": ".github/workflows/sunos.yml",
    "content": "# Execute tests on SunOS\n# https://github.com/vmactions/solaris-vm\n\nname: sunos\non:\n  push:\n    # only run this job if the following files are modified\n    paths: &sunos_paths\n      - \".github/workflows/sunos.yml\"\n      - \"psutil/_pssunos.py\"\n      - \"psutil/_psutil_sunos.c\"\n      - \"psutil/arch/all/**\"\n      - \"psutil/arch/posix/**\"\n      - \"psutil/arch/sunos/**\"\n      - \"setup.py\"\n  pull_request:\n    paths: *sunos_paths\nconcurrency:\n  group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.sha || '' }}\n  cancel-in-progress: true\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Run tests\n        id: test\n        uses: vmactions/solaris-vm@v1\n        with:\n          release: \"11.4-gcc\"\n          usesh: true\n          run: |\n            set -x\n            python3 setup.py build_ext -i --parallel 4\n            python3 -c \"import psutil\"\n            python3 scripts/internal/install_pip.py\n            python3 -m pip install --break-system-packages --user .[test]\n            PYTHONMALLOC=malloc PYTHONUNBUFFERED=1 python3 -m pytest tests/test_memleaks.py\n"
  },
  {
    "path": ".gitignore",
    "content": "syntax: glob\n*.al\n*.bak\n*.egg-info\n*.la\n*.lo\n*.o\n*.orig\n*.pyc\n*.pyd\n*.rej\n*.so\n*.swp\n.failed-tests.txt\n.cache/\n.idea/\n.tox/\nbuild/\ndocs/_build/\ndist/\nwheelhouse/\n.tests/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to psutil project\n\n## Issues\n\n- The issue tracker is for reporting problems or proposing enhancements related\n  to the **program code**.\n- Please do not open issues **asking for support**. Instead, use the forum at:\n  https://groups.google.com/g/psutil.\n- Before submitting a new issue, **search** if there are existing issues for\n  the same topic.\n- **Be clear** in describing what the problem is and try to be accurate in\n  editing the default issue **template**. There is a bot which automatically\n  assigns **labels** based on issue's title and body format. Labels help\n  keeping the issues properly organized and searchable (by OS, issue type,\n  etc.).\n- When reporting a malfunction, consider enabling\n  [debug mode](https://psutil.readthedocs.io/en/latest/#debug-mode) first.\n- To report a **security vulnerability**, use the\n  [Tidelift security contact](https://tidelift.com/security). Tidelift will\n  coordinate the fix and the disclosure of the reported problem.\n\n## Pull Requests\n\n- The PR system is for fixing bugs or make enhancements related to the\n  **program code**.\n- If you wish to implement a new feature or add support for a new platform it's\n  better to **discuss it first**, either on the issue tracker, the forum or via\n  private email.\n- In order to get acquainted with the code base and tooling, take a look at the\n  **[Development Guide](https://psutil.readthedocs.io/devguide)**.\n- If you can, remember to update\n  [changelog.rst](https://github.com/giampaolo/psutil/blob/master/docs/changelog.rst)\n  and [CREDITS](https://github.com/giampaolo/psutil/blob/master/CREDITS) file.\n"
  },
  {
    "path": "HISTORY.rst",
    "content": "History has moved to:\n\n- https://psutil.readthedocs.io/changelog\n- https://github.com/giampaolo/psutil/blob/master/docs/changelog.rst (source)\n"
  },
  {
    "path": "INSTALL.rst",
    "content": "Installation instructions have moved to:\n\n- https://psutil.readthedocs.io/install\n- https://github.com/giampaolo/psutil/blob/master/docs/install.rst (source)\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n * Neither the name of the psutil authors nor the names of its contributors\n   may be used to endorse or promote products derived from this software without\n   specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include .clang-format\ninclude .dprint.jsonc\ninclude .gitignore\ninclude CONTRIBUTING.md\ninclude HISTORY.rst\ninclude INSTALL.rst\ninclude LICENSE\ninclude MANIFEST.in\ninclude Makefile\ninclude README.rst\ninclude SECURITY.md\ninclude _bootstrap.py\ninclude docs/.readthedocs.yaml\ninclude docs/DEVNOTES\ninclude docs/Makefile\ninclude docs/README\ninclude docs/_ext/add_home_link.py\ninclude docs/_ext/availability.py\ninclude docs/_ext/changelog_anchors.py\ninclude docs/_ext/check_python_syntax.py\ninclude docs/_links.rst\ninclude docs/_static/copybutton.js\ninclude docs/_static/css/custom.css\ninclude docs/_static/favicon.ico\ninclude docs/_static/sidebar.js\ninclude docs/_templates/layout.html\ninclude docs/adoption.rst\ninclude docs/alternatives.rst\ninclude docs/api.rst\ninclude docs/changelog.rst\ninclude docs/conf.py\ninclude docs/credits.rst\ninclude docs/devguide.rst\ninclude docs/faq.rst\ninclude docs/glossary.rst\ninclude docs/index.rst\ninclude docs/install.rst\ninclude docs/migration.rst\ninclude docs/platform.rst\ninclude docs/recipes.rst\ninclude docs/requirements.txt\ninclude docs/shell_equivalents.rst\ninclude docs/timeline.rst\ninclude psutil/__init__.py\ninclude psutil/_common.py\ninclude psutil/_enums.py\ninclude psutil/_ntuples.py\ninclude psutil/_psaix.py\ninclude psutil/_psbsd.py\ninclude psutil/_pslinux.py\ninclude psutil/_psosx.py\ninclude psutil/_psposix.py\ninclude psutil/_pssunos.py\ninclude psutil/_psutil_aix.c\ninclude psutil/_psutil_bsd.c\ninclude psutil/_psutil_linux.c\ninclude psutil/_psutil_osx.c\ninclude psutil/_psutil_sunos.c\ninclude psutil/_psutil_windows.c\ninclude psutil/_pswindows.py\ninclude psutil/arch/aix/common.c\ninclude psutil/arch/aix/common.h\ninclude psutil/arch/aix/ifaddrs.c\ninclude psutil/arch/aix/ifaddrs.h\ninclude psutil/arch/aix/net_connections.c\ninclude psutil/arch/aix/net_connections.h\ninclude psutil/arch/aix/net_kernel_structs.h\ninclude psutil/arch/all/errors.c\ninclude psutil/arch/all/init.c\ninclude psutil/arch/all/init.h\ninclude psutil/arch/all/pids.c\ninclude psutil/arch/all/str.c\ninclude psutil/arch/all/utils.c\ninclude psutil/arch/bsd/cpu.c\ninclude psutil/arch/bsd/disk.c\ninclude psutil/arch/bsd/heap.c\ninclude psutil/arch/bsd/init.c\ninclude psutil/arch/bsd/init.h\ninclude psutil/arch/bsd/mem.c\ninclude psutil/arch/bsd/net.c\ninclude psutil/arch/bsd/proc.c\ninclude psutil/arch/bsd/proc_utils.c\ninclude psutil/arch/bsd/sys.c\ninclude psutil/arch/freebsd/cpu.c\ninclude psutil/arch/freebsd/disk.c\ninclude psutil/arch/freebsd/init.h\ninclude psutil/arch/freebsd/mem.c\ninclude psutil/arch/freebsd/pids.c\ninclude psutil/arch/freebsd/proc.c\ninclude psutil/arch/freebsd/proc_socks.c\ninclude psutil/arch/freebsd/sensors.c\ninclude psutil/arch/freebsd/sys_socks.c\ninclude psutil/arch/linux/disk.c\ninclude psutil/arch/linux/heap.c\ninclude psutil/arch/linux/init.h\ninclude psutil/arch/linux/mem.c\ninclude psutil/arch/linux/net.c\ninclude psutil/arch/linux/proc.c\ninclude psutil/arch/netbsd/cpu.c\ninclude psutil/arch/netbsd/disk.c\ninclude psutil/arch/netbsd/init.h\ninclude psutil/arch/netbsd/mem.c\ninclude psutil/arch/netbsd/pids.c\ninclude psutil/arch/netbsd/proc.c\ninclude psutil/arch/netbsd/socks.c\ninclude psutil/arch/openbsd/cpu.c\ninclude psutil/arch/openbsd/disk.c\ninclude psutil/arch/openbsd/init.h\ninclude psutil/arch/openbsd/mem.c\ninclude psutil/arch/openbsd/pids.c\ninclude psutil/arch/openbsd/proc.c\ninclude psutil/arch/openbsd/socks.c\ninclude psutil/arch/openbsd/users.c\ninclude psutil/arch/osx/cpu.c\ninclude psutil/arch/osx/disk.c\ninclude psutil/arch/osx/heap.c\ninclude psutil/arch/osx/init.c\ninclude psutil/arch/osx/init.h\ninclude psutil/arch/osx/mem.c\ninclude psutil/arch/osx/net.c\ninclude psutil/arch/osx/pids.c\ninclude psutil/arch/osx/proc.c\ninclude psutil/arch/osx/proc_utils.c\ninclude psutil/arch/osx/sensors.c\ninclude psutil/arch/osx/sys.c\ninclude psutil/arch/posix/init.c\ninclude psutil/arch/posix/init.h\ninclude psutil/arch/posix/net.c\ninclude psutil/arch/posix/pids.c\ninclude psutil/arch/posix/proc.c\ninclude psutil/arch/posix/sysctl.c\ninclude psutil/arch/posix/users.c\ninclude psutil/arch/sunos/cpu.c\ninclude psutil/arch/sunos/disk.c\ninclude psutil/arch/sunos/environ.c\ninclude psutil/arch/sunos/init.h\ninclude psutil/arch/sunos/mem.c\ninclude psutil/arch/sunos/net.c\ninclude psutil/arch/sunos/proc.c\ninclude psutil/arch/sunos/sys.c\ninclude psutil/arch/windows/cpu.c\ninclude psutil/arch/windows/disk.c\ninclude psutil/arch/windows/heap.c\ninclude psutil/arch/windows/init.c\ninclude psutil/arch/windows/init.h\ninclude psutil/arch/windows/mem.c\ninclude psutil/arch/windows/net.c\ninclude psutil/arch/windows/ntextapi.h\ninclude psutil/arch/windows/pids.c\ninclude psutil/arch/windows/proc.c\ninclude psutil/arch/windows/proc_handles.c\ninclude psutil/arch/windows/proc_info.c\ninclude psutil/arch/windows/proc_utils.c\ninclude psutil/arch/windows/security.c\ninclude psutil/arch/windows/sensors.c\ninclude psutil/arch/windows/services.c\ninclude psutil/arch/windows/socks.c\ninclude psutil/arch/windows/sys.c\ninclude psutil/arch/windows/wmi.c\ninclude pyproject.toml\ninclude scripts/battery.py\ninclude scripts/cpu_distribution.py\ninclude scripts/disk_usage.py\ninclude scripts/fans.py\ninclude scripts/free.py\ninclude scripts/ifconfig.py\ninclude scripts/internal/README\ninclude scripts/internal/bench_oneshot.py\ninclude scripts/internal/bench_oneshot_2.py\ninclude scripts/internal/convert_readme.py\ninclude scripts/internal/download_wheels.py\ninclude scripts/internal/find_adopters.py\ninclude scripts/internal/find_broken_links.py\ninclude scripts/internal/generate_manifest.py\ninclude scripts/internal/git_pre_commit.py\ninclude scripts/internal/install-sysdeps.sh\ninclude scripts/internal/install_pip.py\ninclude scripts/internal/print_access_denied.py\ninclude scripts/internal/print_announce.py\ninclude scripts/internal/print_api_speed.py\ninclude scripts/internal/print_dist.py\ninclude scripts/internal/print_downloads.py\ninclude scripts/internal/print_hashes.py\ninclude scripts/internal/print_sysinfo.py\ninclude scripts/internal/purge_installation.py\ninclude scripts/internal/rst_check_dead_refs.py\ninclude scripts/iotop.py\ninclude scripts/killall.py\ninclude scripts/meminfo.py\ninclude scripts/netstat.py\ninclude scripts/nettop.py\ninclude scripts/pidof.py\ninclude scripts/pmap.py\ninclude scripts/procinfo.py\ninclude scripts/procsmem.py\ninclude scripts/ps.py\ninclude scripts/pstree.py\ninclude scripts/sensors.py\ninclude scripts/temperatures.py\ninclude scripts/top.py\ninclude scripts/who.py\ninclude scripts/winservices.py\ninclude setup.py\ninclude tests/README.md\ninclude tests/__init__.py\ninclude tests/test_aix.py\ninclude tests/test_bsd.py\ninclude tests/test_connections.py\ninclude tests/test_contracts.py\ninclude tests/test_heap.py\ninclude tests/test_linux.py\ninclude tests/test_memleaks.py\ninclude tests/test_misc.py\ninclude tests/test_osx.py\ninclude tests/test_posix.py\ninclude tests/test_process.py\ninclude tests/test_process_all.py\ninclude tests/test_scripts.py\ninclude tests/test_sudo.py\ninclude tests/test_sunos.py\ninclude tests/test_system.py\ninclude tests/test_testutils.py\ninclude tests/test_type_hints.py\ninclude tests/test_unicode.py\ninclude tests/test_windows.py\nrecursive-exclude docs/_static *\n"
  },
  {
    "path": "Makefile",
    "content": "# Shortcuts for various development tasks.\n#\n# - To use this on Windows install Git For Windows first, then launch a Git\n#   Bash Shell.\n# - To use a specific Python version run: `make install PYTHON=python3.3`.\n# - To append an argument to a command use ARGS, e.g: `make test ARGS=\"-k\n#   some_test`.\n\n# Configurable\nPYTHON = python3\nARGS =\nFILES =\n\nPIP_INSTALL_ARGS = --trusted-host files.pythonhosted.org --trusted-host pypi.org --upgrade --upgrade-strategy eager\nPYTHON_ENV_VARS = PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 PSUTIL_TESTING=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1\nSUDO = $(if $(filter $(OS),Windows_NT),,sudo -E)\nDPRINT = ~/.dprint/bin/dprint\n\n# if make is invoked with no arg, default to `make help`\n.DEFAULT_GOAL := help\n\n# install git hook\n_ := $(shell mkdir -p .git/hooks/ && ln -sf ../../scripts/internal/git_pre_commit.py .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit)\n\n# ===================================================================\n# Install\n# ===================================================================\n\nclean:  ## Remove all build files.\n\t@rm -rfv `find . \\\n\t\t-type d -name __pycache__ \\\n\t\t-o -type f -name \\*.bak \\\n\t\t-o -type f -name \\*.orig \\\n\t\t-o -type f -name \\*.pyc \\\n\t\t-o -type f -name \\*.pyd \\\n\t\t-o -type f -name \\*.pyo \\\n\t\t-o -type f -name \\*.rej \\\n\t\t-o -type f -name \\*.so \\\n\t\t-o -type f -name \\*.~ \\\n\t\t-o -type f -name \\*\\$testfn`\n\t@rm -rfv \\\n\t\t*.core \\\n\t\t*.egg-info \\\n\t\t*\\@psutil-* \\\n\t\t.coverage \\\n\t\t.failed-tests.txt \\\n\t\t.pytest_cache \\\n\t\t.ruff_cache/ \\\n\t\t.tests \\\n\t\tbuild/ \\\n\t\tdist/ \\\n\t\tdocs/_build/ \\\n\t\thtmlcov/ \\\n\t\tpytest-cache-files* \\\n\t\twheelhouse\n\n.PHONY: build\nbuild:  ## Compile (in parallel) without installing.\n\t@# \"build_ext -i\" copies compiled *.so files in ./psutil directory in order\n\t@# to allow \"import psutil\" when using the interactive interpreter from\n\t@# within  this directory.\n\t$(PYTHON_ENV_VARS) $(PYTHON) setup.py build_ext -i --parallel 4\n\t$(PYTHON_ENV_VARS) $(PYTHON) -c \"import psutil\"  # make sure it actually worked\n\ninstall:  ## Install this package as current user in \"edit\" mode.\n\t$(MAKE) build\n\t# If not in a virtualenv, add --user to the install command.\n\t$(PYTHON_ENV_VARS) $(PYTHON) setup.py develop $(SETUP_INSTALL_ARGS) `$(PYTHON) -c \\\n\t\t\"import sys; print('' if hasattr(sys, 'real_prefix') or hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix else '--user')\"`\n\nuninstall:  ## Uninstall this package via pip.\n\tcd ..; $(PYTHON_ENV_VARS) $(PYTHON) -m pip uninstall -y -v psutil || true\n\t$(PYTHON_ENV_VARS) $(PYTHON) scripts/internal/purge_installation.py\n\ninstall-pip:  ## Install pip (no-op if already installed).\n\t$(PYTHON) scripts/internal/install_pip.py\n\ninstall-sysdeps:\n\t./scripts/internal/install-sysdeps.sh\n\ninstall-pydeps-test:  ## Install python deps necessary to run unit tests.\n\t$(MAKE) install-pip\n\tPIP_BREAK_SYSTEM_PACKAGES=1 $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) setuptools\n\tPIP_BREAK_SYSTEM_PACKAGES=1 $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) .[test]\n\ninstall-pydeps-lint:  ## Install python deps necessary to run linters.\n\t$(MAKE) install-pip\n\tPIP_BREAK_SYSTEM_PACKAGES=1 $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) setuptools\n\tPIP_BREAK_SYSTEM_PACKAGES=1 $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) .[lint]\n\ninstall-pydeps-dev:  ## Install python deps meant for local development.\n\t$(MAKE) install-pip\n\tPIP_BREAK_SYSTEM_PACKAGES=1 $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) setuptools\n\tPIP_BREAK_SYSTEM_PACKAGES=1 $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) .[dev]\n\n# ===================================================================\n# Tests\n# ===================================================================\n\n# Cache dir on Windows often causes \"Permission denied\" errors\n_PYTEST_EXTRA != if [ \"$$OS\" = \"Windows_NT\" ]; then printf '%s' '-o cache_dir=/tmp/pytest-psutil-cache'; fi\nRUN_TEST = $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(_PYTEST_EXTRA)\n\ntest:  ## Run all tests (except memleak tests).\n\t# To run a specific test do `make test ARGS=tests/test_process.py::TestProcess::test_cmdline`\n\t$(RUN_TEST) $(ARGS)\n\ntest-parallel:  ## Run all tests (except memleak tests) in parallel.\n\t$(RUN_TEST) -p xdist -n auto --dist loadgroup $(ARGS)\n\ntest-process:  ## Run process-related tests.\n\t$(RUN_TEST) -k \"test_process.py or test_proc or test_pid or Process or pids or pid_exists\" $(ARGS)\n\ntest-process-all:  ## Run tests which iterate over all process PIDs.\n\t$(RUN_TEST) -k test_process_all.py $(ARGS)\n\ntest-system:  ## Run system-related API tests.\n\t$(RUN_TEST) -k \"test_system.py or test_sys or System or disk or sensors or net_io_counters or net_if_addrs or net_if_stats or users or pids or win_service_ or boot_time\" $(ARGS)\n\ntest-misc:  ## Run miscellaneous tests.\n\t$(RUN_TEST) -k \"test_misc.py or Misc\" $(ARGS)\n\ntest-scripts:  ## Run scripts tests.\n\t$(RUN_TEST) tests/test_scripts.py $(ARGS)\n\ntest-testutils:  ## Run test utils tests.\n\t$(RUN_TEST) tests/test_testutils.py $(ARGS)\n\ntest-unicode:  ## Test APIs dealing with strings.\n\t$(RUN_TEST) tests/test_unicode.py $(ARGS)\n\ntest-contracts:  ## APIs sanity tests.\n\t$(RUN_TEST) tests/test_contracts.py $(ARGS)\n\ntest-type-hints:  ## Test type hints\n\t$(RUN_TEST) tests/test_type_hints.py $(ARGS)\n\ntest-connections:  ## Test psutil.net_connections() and Process.net_connections().\n\t$(RUN_TEST) -k \"test_connections.py or net_\" $(ARGS)\n\ntest-heap:  ## Test psutil.heap_*() APIs.\n\t$(RUN_TEST) -k \"test_heap.py or heap_\" $(ARGS)\n\ntest-posix:  ## POSIX specific tests.\n\t$(RUN_TEST) -k \"test_posix.py or posix_ or Posix\" $(ARGS)\n\ntest-platform:  ## Run specific platform tests only.\n\t$(RUN_TEST) -k test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in (\"LINUX\", \"BSD\", \"OSX\", \"SUNOS\", \"WINDOWS\", \"AIX\") if getattr(psutil, x)][0])'`.py $(ARGS)\n\ntest-memleaks:  ## Memory leak tests.\n\tPYTHONMALLOC=malloc $(RUN_TEST) -k test_memleaks.py $(ARGS)\n\ntest-sudo:  ## Run tests requiring root privileges.\n\t# Use unittest runner because pytest may not be installed as root.\n\t$(SUDO) $(PYTHON_ENV_VARS) $(PYTHON) -m unittest -v tests.test_sudo\n\ntest-last-failed:  ## Re-run tests which failed on last run\n\t$(RUN_TEST) --last-failed $(ARGS)\n\ncoverage:  ## Run test coverage.\n\trm -rf .coverage htmlcov\n\t$(PYTHON_ENV_VARS) $(PYTHON) -m coverage run -m pytest $(ARGS)\n\t$(PYTHON) -m coverage report\n\t@echo \"writing results to htmlcov/index.html\"\n\t$(PYTHON) -m coverage html\n\t$(PYTHON) -m webbrowser -t htmlcov/index.html\n\n# ===================================================================\n# Linters\n# ===================================================================\n\n# Return a shell pipeline that outputs one file per line. Uses\n# $(FILES) if set, else \"git ls-files\" with given pattern(s).\n_ls = $(if $(FILES), printf '%s\\n' $(FILES), git ls-files $(1))\n\nruff:  ## Run ruff linter.\n\t@$(call _ls,'*.py') | xargs $(PYTHON) -m ruff check --output-format=concise\n\nblack:  ## Run black formatter.\n\t@$(call _ls,'*.py') | xargs $(PYTHON) -m black --check --safe\n\nlint-c:  ## Run C linter.\n\t@$(call _ls,'*.c' '*.h') | xargs -P0 -I{} clang-format --dry-run --Werror {}\n\ndprint:\n\t@$(DPRINT) check\n\nlint-rst:  ## Run linter for .rst files.\n\t@$(call _ls,'*.rst') | xargs python3 scripts/internal/rst_check_dead_refs.py\n\t@$(call _ls,'*.rst') | xargs sphinx-lint\n\nlint-toml:  ## Run linter for pyproject.toml.\n\t@$(call _ls,'*.toml') | xargs toml-sort --check\n\nlint-all:  ## Run all linters\n\t$(MAKE) black\n\t$(MAKE) ruff\n\t$(MAKE) lint-c\n\t$(MAKE) dprint\n\t$(MAKE) lint-rst\n\t$(MAKE) lint-toml\n\n# --- not mandatory linters (just run from time to time)\n\npylint:  ## Python pylint\n\t@git ls-files '*.py' | xargs $(PYTHON) -m pylint --rcfile=pyproject.toml --jobs=0 $(ARGS)\n\nvulture:  ## Find unused code\n\t@git ls-files '*.py' | xargs $(PYTHON) -m vulture $(ARGS)\n\n# ===================================================================\n# Fixers\n# ===================================================================\n\nfix-black:\n\t@git ls-files '*.py' | xargs $(PYTHON) -m black\n\nfix-ruff:\n\t@git ls-files '*.py' | xargs $(PYTHON) -m ruff check --fix --output-format=concise $(ARGS)\n\nfix-c:\n\t@git ls-files '*.c' '*.h' | xargs -P0 -I{} clang-format -i {}  # parallel exec\n\nfix-toml:  ## Fix pyproject.toml\n\t@git ls-files '*.toml' | xargs toml-sort\n\nfix-dprint:\n\t@$(DPRINT) fmt\n\nfix-all:  ## Run all code fixers.\n\t$(MAKE) fix-ruff\n\t$(MAKE) fix-black\n\t$(MAKE) fix-toml\n\t$(MAKE) fix-dprint\n\n# ===================================================================\n# CI jobs\n# ===================================================================\n\nci-lint:  ## Run all linters on GitHub CI.\n\t$(MAKE) install-pydeps-lint\n\tcurl -fsSL https://dprint.dev/install.sh | sh\n\t$(DPRINT) --version\n\tclang-format --version\n\t$(MAKE) lint-all\n\nci-test:  ## Run tests on GitHub CI. Used by BSD runners.\n\t$(MAKE) install-sysdeps\n\t$(MAKE) install-pydeps-test\n\t$(MAKE) build\n\t$(MAKE) print-sysinfo\n\t$(MAKE) test\n\t$(MAKE) test-memleaks\n\nci-test-cibuildwheel:  ## Run CI tests for the built wheels.\n\t$(MAKE) install-sysdeps\n\t$(MAKE) install-pydeps-test\n\t$(MAKE) print-sysinfo\n\t# Tests must be run from a separate directory so pytest does not import\n\t# from the source tree and instead exercises only the installed wheel.\n\trm -rf .tests tests/__pycache__\n\tmkdir -p .tests\n\tcp -r tests .tests/\n\tcd .tests/ && PYTHONPATH=$$(pwd) $(PYTHON_ENV_VARS) $(PYTHON) -m pytest\n\tcd .tests/ && PYTHONPATH=$$(pwd) $(PYTHON_ENV_VARS) PYTHONMALLOC=malloc $(PYTHON) -m pytest -k test_memleaks.py\n\nci-check-dist:  ## Run all sanity checks re. to the package distribution.\n\t$(PYTHON) -m pip install -U setuptools virtualenv twine check-manifest validate-pyproject[all] abi3audit\n\t$(MAKE) create-sdist\n\tmv wheelhouse/* dist/\n\t$(MAKE) check-dist\n\t$(MAKE) install\n\t$(MAKE) print-dist\n\n# ===================================================================\n# Distribution\n# ===================================================================\n\n# --- create\n\ngenerate-manifest:  ## Generates MANIFEST.in file.\n\t$(PYTHON) scripts/internal/generate_manifest.py > MANIFEST.in\n\ncreate-sdist:  ## Create tar.gz source distribution.\n\t$(MAKE) generate-manifest\n\t$(PYTHON_ENV_VARS) $(PYTHON) setup.py sdist\n\ncreate-wheels:  ## Create .whl files\n\t$(PYTHON_ENV_VARS) $(PYTHON) setup.py bdist_wheel\n\ndownload-wheels:  ## Download latest wheels hosted on github.\n\t$(PYTHON) scripts/internal/download_wheels.py --tokenfile=~/.github.token\n\t$(MAKE) print-dist\n\ncreate-dist:  ## Create .tar.gz + .whl distribution.\n\t$(MAKE) create-sdist\n\t$(MAKE) download-wheels\n\n# --- check\n\ncheck-manifest:  ## Check sanity of MANIFEST.in file.\n\t$(PYTHON) -m check_manifest -v\n\ncheck-pyproject:  ## Check sanity of pyproject.toml file.\n\t$(PYTHON) -m validate_pyproject -v pyproject.toml\n\ncheck-sdist:  ## Check sanity of source distribution.\n\t$(PYTHON_ENV_VARS) $(PYTHON) -m virtualenv --clear --no-wheel --quiet build/venv\n\t$(PYTHON_ENV_VARS) build/venv/bin/python -m pip install -v --isolated --quiet dist/*.tar.gz\n\t$(PYTHON_ENV_VARS) build/venv/bin/python -c \"import os; os.chdir('build/venv'); import psutil\"\n\t$(PYTHON) -m twine check --strict dist/*.tar.gz\n\ncheck-wheels:  ## Check sanity of wheels.\n\t$(PYTHON) -m abi3audit --verbose --strict dist/*-abi3-*.whl\n\t$(PYTHON) -m twine check --strict dist/*.whl\n\ncheck-dist:  ## Run all sanity checks re. to the package distribution.\n\t$(MAKE) check-manifest\n\t$(MAKE) check-pyproject\n\t$(MAKE) check-sdist\n\t$(MAKE) check-wheels\n\n# --- release\n\npre-release:  ## Check if we're ready to produce a new release.\n\t$(MAKE) clean\n\t$(MAKE) create-dist\n\t$(MAKE) check-dist\n\t$(MAKE) install\n\t@$(PYTHON) -c \\\n\t\t\"import requests, sys; \\\n\t\tfrom packaging.version import parse; \\\n\t\tfrom psutil import __version__; \\\n\t\tres = requests.get('https://pypi.org/pypi/psutil/json', timeout=5); \\\n\t\tversions = sorted(res.json()['releases'], key=parse, reverse=True); \\\n\t\tsys.exit('version %r already exists on PYPI' % __version__) if __version__ in versions else 0\"\n\t@ver=$$($(PYTHON) -c \"from psutil import __version__; print(__version__)\"); \\\n\t\tgrep -q \"$$ver\" docs/changelog.rst || { echo \"ERR: version $$ver not found in docs/changelog.rst\"; exit 1; }; \\\n\t\tgrep -q \"$$ver\" docs/timeline.rst || { echo \"ERR: version $$ver not found in docs/timeline.rst\"; exit 1; }\n\t$(MAKE) print-hashes\n\t$(MAKE) print-dist\n\nrelease:  ## Upload a new release.\n\t$(PYTHON) -m twine upload dist/*.tar.gz\n\t$(PYTHON) -m twine upload dist/*.whl\n\t$(MAKE) git-tag-release\n\ngit-tag-release:  ## Git-tag a new release.\n\tgit tag -a release-`python3 -c \"import setup; print(setup.get_version())\"` -m `git rev-list HEAD --count`:`git rev-parse --short HEAD`\n\tgit push --follow-tags\n\n# ===================================================================\n# Printers\n# ===================================================================\n\nprint-announce:  ## Print announce of new release.\n\t@$(PYTHON) scripts/internal/print_announce.py\n\nprint-access-denied: ## Print AD exceptions\n\t$(PYTHON) scripts/internal/print_access_denied.py\n\nprint-api-speed:  ## Benchmark all API calls\n\t$(PYTHON) scripts/internal/print_api_speed.py $(ARGS)\n\nprint-downloads:  ## Print PYPI download statistics\n\t$(PYTHON) scripts/internal/print_downloads.py\n\nprint-hashes:  ## Prints hashes of files in dist/ directory\n\t$(PYTHON) scripts/internal/print_hashes.py\n\nprint-sysinfo:  ## Prints system info\n\t$(PYTHON) scripts/internal/print_sysinfo.py\n\nprint-dist:  ## Print downloaded wheels / tar.gz\n\t$(PYTHON) scripts/internal/print_dist.py\n\n# ===================================================================\n# Misc\n# ===================================================================\n\ngrep-todos:  ## Look for TODOs in the source files.\n\tgit grep -EIn \"TODO|FIXME|XXX\"\n\nbench-oneshot:  ## Benchmarks for oneshot() ctx manager (see #799).\n\t$(PYTHON) scripts/internal/bench_oneshot.py\n\nbench-oneshot-2:  ## Same as above but using perf module (supposed to be more precise)\n\t$(PYTHON) scripts/internal/bench_oneshot_2.py\n\nfind-broken-links:  ## Look for broken links in source files.\n\tgit ls-files | xargs $(PYTHON) -Wa scripts/internal/find_broken_links.py\n\nhelp: ## Display callable targets.\n\t@awk -F':.*?## ' '/^[a-zA-Z0-9_.-]+:.*?## / {printf \"\\033[36m%-24s\\033[0m %s\\n\", $$1, $$2}' $(MAKEFILE_LIST) | sort\n"
  },
  {
    "path": "README.rst",
    "content": "|  |downloads| |stars| |forks| |contributors| |packages|\n|  |version| |license| |stackoverflow| |twitter| |tidelift|\n|  |github-actions-wheels| |github-actions-bsd|\n\n.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg\n    :target: https://clickpy.clickhouse.com/dashboard/psutil\n    :alt: Downloads\n\n.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg\n    :target: https://github.com/giampaolo/psutil/stargazers\n    :alt: Github stars\n\n.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg\n    :target: https://github.com/giampaolo/psutil/network/members\n    :alt: Github forks\n\n.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg\n    :target: https://github.com/giampaolo/psutil/graphs/contributors\n    :alt: Contributors\n\n.. |stackoverflow| image:: https://img.shields.io/badge/stackoverflow-Ask%20questions-blue.svg\n    :target: https://stackoverflow.com/questions/tagged/psutil\n    :alt: Stackoverflow\n\n.. |github-actions-wheels| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/build.yml.svg?label=Linux%2C%20macOS%2C%20Windows\n    :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Abuild\n    :alt: Linux, macOS, Windows\n\n.. |github-actions-bsd| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/bsd.yml.svg?label=BSD\n    :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests\n    :alt: FreeBSD, NetBSD, OpenBSD\n\n.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi\n    :target: https://pypi.org/project/psutil\n    :alt: Latest version\n\n.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg\n    :target: https://repology.org/metapackage/python:psutil/versions\n    :alt: Binary packages\n\n.. |license| image:: https://img.shields.io/pypi/l/psutil.svg\n    :target: https://github.com/giampaolo/psutil/blob/master/LICENSE\n    :alt: License\n\n.. |twitter| image:: https://img.shields.io/twitter/follow/grodola?style=flat\n    :target: https://twitter.com/grodola\n    :alt: Twitter Follow\n\n.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat\n    :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme\n    :alt: Tidelift\n\n-----\n\n.. raw:: html\n\n    <div align=\"center\">\n        <a href=\"https://github.com/giampaolo/psutil\"><img src=\"https://github.com/giampaolo/psutil/raw/master/docs/_static/psutil-logo.png\" /></a>\n        <br />\n        <br />\n        <a href=\"https://github.com/giampaolo/psutil\"><b>Home</b></a>&nbsp;&nbsp;&nbsp;\n        <a href=\"https://psutil.readthedocs.io/\"><b>Documentation</b></a>&nbsp;&nbsp;&nbsp;\n        <a href=\"https://psutil.readthedocs.io/adoption\"><b>Who uses psutil</b></a>&nbsp;&nbsp;&nbsp;\n        <a href=\"https://pypi.org/project/psutil/#files\"><b>Download</b></a>&nbsp;&nbsp;&nbsp;\n        <a href=\"https://gmpy.dev/tags/psutil\"><b>Blog</b></a>&nbsp;&nbsp;&nbsp;\n        <a href=\"#funding\"><b>Funding</b></a>&nbsp;&nbsp;&nbsp;\n    </div>\n\nSummary\n=======\n\npsutil (process and system utilities) is a cross-platform library for\nretrieving information about **running processes** and **system utilization**\n(CPU, memory, disks, network, sensors) in Python. It is useful mainly for\n**system monitoring**, **profiling**, **limiting process resources**, and\n**managing running processes**. It implements many functionalities offered by\nclassic UNIX command line tools such as\n*ps, top, free, iotop, netstat, ifconfig, lsof*\nand others (see `shell equivalents`_).\npsutil currently supports the following platforms:\n\n- **Linux**\n- **Windows**\n- **macOS**\n- **FreeBSD, OpenBSD**, **NetBSD**\n- **Sun Solaris**\n- **AIX**\n\n.. <PYPI-EXCLUDE>\n\nSponsors\n========\n\n.. raw:: html\n\n    <table border=\"0\" cellpadding=\"10\" cellspacing=\"0\">\n      <tr>\n        <td align=\"center\">\n          <a href=\"https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme\">\n            <img width=\"200\" src=\"https://github.com/giampaolo/psutil/raw/master/docs/_static/tidelift-logo.svg\">\n          </a>\n        </td>\n        <td align=\"center\">\n          <a href=\"https://sansec.io/\">\n            <img src=\"https://sansec.io/assets/images/logo.svg\">\n          </a>\n        </td>\n        <td align=\"center\">\n          <a href=\"https://www.apivoid.com/\">\n            <img width=\"180\" src=\"https://gmpy.dev/images/apivoid-logo.svg\">\n          </a>\n        </td>\n      </tr>\n    </table>\n\n    <sup><a href=\"https://github.com/sponsors/giampaolo\">add your logo</a></sup>\n\n.. </PYPI-EXCLUDE>\n\nFunding\n=======\n\nWhile psutil is free software and will always remain so, the project would benefit\nimmensely from some funding. psutil is among the `top 100`_ most-downloaded\nPython packages, and keeping up with bug reports, user support, and ongoing\nmaintenance has become increasingly difficult to sustain as a one-person\neffort. If you're a company that's making significant use of psutil you can\nconsider becoming a sponsor via `GitHub\n<https://github.com/sponsors/giampaolo>`_, `Open Collective\n<https://opencollective.com/psutil>`_ or `PayPal\n<https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8>`_.\nSponsors can have their logo displayed here and in the psutil `documentation\n<https://psutil.readthedocs.io>`_.\n\nProjects using psutil\n=====================\n\npsutil is one of the `top 100`_ most-downloaded packages on PyPI, with 280+\nmillion downloads per month, `760,000+ GitHub repositories\n<https://github.com/giampaolo/psutil/network/dependents>`_ using it, and\n14,000+ packages depending on it. Some notable projects using psutil:\n\n- `TensorFlow <https://github.com/tensorflow/tensorflow>`_,\n  `PyTorch <https://github.com/pytorch/pytorch>`_,\n- `Home Assistant <https://github.com/home-assistant/core>`_,\n  `Ansible <https://github.com/ansible/ansible>`_,\n  `Apache Airflow <https://github.com/apache/airflow>`_,\n  `Sentry <https://github.com/getsentry/sentry>`_\n- `Celery <https://github.com/celery/celery>`_,\n  `Dask <https://github.com/dask/dask>`_\n- `Glances <https://github.com/nicolargo/glances>`_,\n  `bpytop <https://github.com/aristocratos/bpytop>`_,\n  `Ajenti <https://github.com/ajenti/ajenti>`_,\n  `GRR <https://github.com/google/grr>`_\n- `psleak`_\n\n`Full list <https://psutil.readthedocs.io/adoption>`_\n\n\n\nPorts\n=====\n\n- Go: `gopsutil <https://github.com/shirou/gopsutil>`_\n- C: `cpslib <https://github.com/hamon-in/cpslib>`_\n- Rust: `rust-psutil <https://github.com/rust-psutil/rust-psutil>`_\n- Nim: `psutil-nim <https://github.com/johnscillieri/psutil-nim>`_\n\n.. <PYPI-EXCLUDE>\n\nSupporters\n==========\n\nPeople who donated money over the years:\n\n.. raw:: html\n\n    <div>\n      <a href=\"https://github.com/codingjoe\"><img height=\"35\" width=\"35\" title=\"Johannes Maron\" src=\"https://avatars.githubusercontent.com/u/1772890?v=4\" /></a>\n      <a href=\"https://github.com/eallrich\"><img height=\"35\" width=\"35\" title=\"Evan Allrich\" src=\"https://avatars.githubusercontent.com/u/17393?v=4\" /></a>\n      <a href=\"https://github.com/ofek\"><img height=\"35\" width=\"35\" title=\"Ofek Lev\" src=\"https://avatars.githubusercontent.com/u/9677399?v=4\" /></a>\n      <a href=\"https://github.com/roboflow\"><img height=\"35\" width=\"35\" title=\"roboflow.com\" src=\"https://avatars.githubusercontent.com/u/53104118?s=200&v=4\" /></a>\n      <a href=\"https://github.com/sansecio\"><img height=\"35\" width=\"35\" title=\"sansec.io\" src=\"https://avatars.githubusercontent.com/u/60706188?s=200&v=4\" /></a>\n      <a href=\"https://github.com/Trash-Nothing\"><img height=\"35\" width=\"35\" title=\"trashnothing.com\" src=\"https://avatars.githubusercontent.com/u/230028908?s=200&v=4\" /></a>\n\n      <a href=\"https://github.com/abramov-v\"><img height=\"35\" width=\"35\" title=\"Valeriy Abramov\" src=\"https://avatars.githubusercontent.com/u/76448042?v=4\" /></a>\n      <a href=\"https://github.com/alexdlaird\"><img height=\"35\" width=\"35\" title=\"Alex Laird\" src=\"https://avatars.githubusercontent.com/u/1660326?v=4\" /></a>\n      <a href=\"https://github.com/aristocratos\"><img height=\"35\" width=\"35\" title=\"aristocratos\" src=\"https://avatars3.githubusercontent.com/u/59659483?s=96&amp;v=4\" /></a>\n      <a href=\"https://github.com/ArtyomVancyan\"><img height=\"35\" width=\"35\" title=\"Artyom Vancyan\" src=\"https://avatars.githubusercontent.com/u/44609997?v=4\" /></a>\n      <a href=\"https://github.com/c0m4r\"><img height=\"35\" width=\"35\" title=\"c0m4r\" src=\"https://avatars.githubusercontent.com/u/6292788?v=4\" /></a>\n      <a href=\"https://github.com/coskundeniz\"><img height=\"35\" width=\"35\" title=\"Coşkun Deniz\" src=\"https://avatars.githubusercontent.com/u/4516210?v=4\" /></a>\n      <a href=\"https://github.com/cybersecgeek\"><img height=\"35\" width=\"35\" title=\"cybersecgeek\" src=\"https://avatars.githubusercontent.com/u/12847926?v=4\" /></a>\n      <a href=\"https://github.com/dbwiddis\"><img height=\"35\" width=\"35\" title=\"Daniel Widdis\" src=\"https://avatars1.githubusercontent.com/u/9291703?s=88&amp;v=4\" /></a>\n      <a href=\"https://github.com/getsentry\"><img height=\"35\" width=\"35\" title=\"getsentry\" src=\"https://avatars.githubusercontent.com/u/1396951?s=200&v=4\" /></a>\n      <a href=\"https://github.com/great-work-told-is\"><img height=\"35\" width=\"35\" title=\"great-work-told-is\" src=\"https://avatars.githubusercontent.com/u/113922084?v=4\" /></a>\n      <a href=\"https://github.com/guilt\"><img height=\"35\" width=\"35\" title=\"Karthik Kumar Viswanathan\" src=\"https://avatars.githubusercontent.com/u/195178?v=4\" /></a>\n      <a href=\"https://github.com/inarikami\"><img height=\"35\" width=\"35\" title=\"inarikami\" src=\"https://avatars.githubusercontent.com/u/22864465?v=4\" /></a>\n      <a href=\"https://github.com/indeedeng\"><img height=\"35\" width=\"35\" title=\"indeedeng\" src=\"https://avatars.githubusercontent.com/u/2905043?s=200&v=4\" /></a>\n      <a href=\"https://github.com/JeremyGrosser\"><img height=\"35\" width=\"35\" title=\"JeremyGrosser\" src=\"https://avatars.githubusercontent.com/u/2151?v=4\" /></a>\n      <a href=\"https://github.com/maxesisn\"><img height=\"35\" width=\"35\" title=\"Maximilian Wu\" src=\"https://avatars.githubusercontent.com/u/20412597?v=4\" /></a>\n      <a href=\"https://github.com/Mehver\"><img height=\"35\" width=\"35\" title=\"Mehver\" src=\"https://avatars.githubusercontent.com/u/75297777?v=4\" /></a>\n      <a href=\"https://github.com/mirbyte\"><img height=\"35\" width=\"35\" title=\"mirko\" src=\"https://avatars.githubusercontent.com/u/83219244?v=4\" /></a>\n      <a href=\"https://github.com/PySimpleGUI\"><img height=\"35\" width=\"35\" title=\"PySimpleGUI\" src=\"https://avatars.githubusercontent.com/u/46163555?v=4\" /></a>\n      <a href=\"https://github.com/robusta-dev\"><img height=\"35\" width=\"35\" title=\"Robusta\" src=\"https://avatars.githubusercontent.com/u/82757710?s=200&v=4\" /></a>\n      <a href=\"https://github.com/sasozivanovic\"><img height=\"35\" width=\"35\" title=\"Sašo Živanović\" src=\"https://avatars.githubusercontent.com/u/3317028?v=4\" /></a>\n      <a href=\"https://github.com/scoutapm-sponsorships\"><img height=\"35\" width=\"35\" title=\"scoutapm-sponsorships\" src=\"https://avatars.githubusercontent.com/u/71095532?v=4\" /></a>\n      <a href=\"https://github.com/u93\"><img height=\"35\" width=\"35\" title=\"Eugenio E Breijo\" src=\"https://avatars.githubusercontent.com/u/16807302?v=4\" /></a>\n      <a href=\"https://opencollective.com/alexey-vazhnov\"><img height=\"35\" width=\"35\" title=\"Alexey Vazhnov\" src=\"https://images.opencollective.com/alexey-vazhnov/daed334/avatar/40.png\" /></a>\n      <a href=\"https://opencollective.com/chenyoo-hao\"><img height=\"35\" width=\"35\" title=\"Chenyoo Hao\" src=\"https://images.opencollective.com/chenyoo-hao/avatar/40.png\" /></a>\n    </div>\n\n    <sup><a href=\"https://github.com/sponsors/giampaolo\">add your avatar</a></sup>\n\n.. </PYPI-EXCLUDE>\n\n----\n\nExample usages\n==============\n\nBelow are interactive examples demonstrating all parts of the psutil API,\nincluding CPU, memory, disks, network, sensors, and process management.\n\nCPU\n---\n\n.. code-block:: python\n\n    >>> import psutil\n    >>>\n    >>> psutil.cpu_times()\n    scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, guest_nice=0.0)\n    >>>\n    >>> for x in range(3):\n    ...     psutil.cpu_percent(interval=1)\n    ...\n    4.0\n    5.9\n    3.8\n    >>>\n    >>> for x in range(3):\n    ...     psutil.cpu_percent(interval=1, percpu=True)\n    ...\n    [4.0, 6.9, 3.7, 9.2]\n    [7.0, 8.5, 2.4, 2.1]\n    [1.2, 9.0, 9.9, 7.2]\n    >>>\n    >>> for x in range(3):\n    ...     psutil.cpu_times_percent(interval=1, percpu=False)\n    ...\n    scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)\n    scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)\n    scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)\n    >>>\n    >>> psutil.cpu_count()\n    4\n    >>> psutil.cpu_count(logical=False)\n    2\n    >>>\n    >>> psutil.cpu_stats()\n    scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)\n    >>>\n    >>> psutil.cpu_freq()\n    scpufreq(current=931.42925, min=800.0, max=3500.0)\n    >>>\n    >>> psutil.getloadavg()  # also on Windows (emulated)\n    (3.14, 3.89, 4.67)\n\nMemory\n------\n\n.. code-block:: python\n\n    >>> psutil.virtual_memory()\n    svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)\n    >>> psutil.swap_memory()\n    sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)\n    >>>\n\nDisks\n-----\n\n.. code-block:: python\n\n    >>> psutil.disk_partitions()\n    [sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),\n     sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext', opts='rw')]\n    >>>\n    >>> psutil.disk_usage('/')\n    sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)\n    >>>\n    >>> psutil.disk_io_counters(perdisk=False)\n    sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412)\n    >>>\n\nNetwork\n-------\n\n.. code-block:: python\n\n    >>> psutil.net_io_counters(pernic=True)\n    {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),\n     'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}\n    >>>\n    >>> psutil.net_connections(kind='tcp')\n    [sconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED', pid=1254),\n     sconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING', pid=2987),\n     ...]\n    >>>\n    >>> psutil.net_if_addrs()\n    {'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),\n            snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),\n            snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],\n     'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),\n               snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),\n               snicaddr(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}\n    >>>\n    >>> psutil.net_if_stats()\n    {'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536, flags='up,loopback,running'),\n     'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500, flags='up,broadcast,running,multicast')}\n    >>>\n\nSensors\n-------\n\n.. code-block:: python\n\n    >>> import psutil\n    >>> psutil.sensors_temperatures()\n    {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)],\n     'asus': [shwtemp(label='', current=47.0, high=None, critical=None)],\n     'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0),\n                  shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0)]}\n    >>>\n    >>> psutil.sensors_fans()\n    {'asus': [sfan(label='cpu_fan', current=3200)]}\n    >>>\n    >>> psutil.sensors_battery()\n    sbattery(percent=93, secsleft=16628, power_plugged=False)\n    >>>\n\nOther system info\n-----------------\n\n.. code-block:: python\n\n    >>> import psutil\n    >>> psutil.users()\n    [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),\n     suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]\n    >>>\n    >>> psutil.boot_time()\n    1365519115.0\n    >>>\n\nProcess management\n------------------\n\n.. code-block:: python\n\n    >>> import psutil\n    >>> psutil.pids()\n    [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215,\n     1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, 2637, 2774, 3932,\n     4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282, 4306, 4311,\n     4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433,\n     4443, 4445, 4446, 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054,\n     7055, 7071]\n    >>>\n    >>> p = psutil.Process(7055)\n    >>> p\n    psutil.Process(pid=7055, name='python3', status=<ProcessStatus.STATUS_RUNNING: 'running'>, started='09:04:44')\n    >>> p.pid\n    7055\n    >>> p.name()\n    'python3'\n    >>> p.exe()\n    '/usr/bin/python3'\n    >>> p.cwd()\n    '/home/giampaolo'\n    >>> p.cmdline()\n    ['/usr/bin/python3', 'main.py']\n    >>>\n    >>> p.ppid()\n    7054\n    >>> p.parent()\n    psutil.Process(pid=4699, name='bash', status=<ProcessStatus.STATUS_SLEEPING: 'sleeping'>, started='09:06:44')\n    >>> p.parents()\n    [psutil.Process(pid=4699, name='bash', started='09:06:44'),\n     psutil.Process(pid=4689, name='gnome-terminal-server', status=<ProcessStatus.STATUS_SLEEPING: 'sleeping'>, started='0:06:44'),\n     psutil.Process(pid=1, name='systemd', status=<ProcessStatus.STATUS_SLEEPING: 'sleeping'>, started='05:56:55')]\n    >>> p.children(recursive=True)\n    [psutil.Process(pid=29835, name='python3', status=<ProcessStatus.STATUS_SLEEPING: 'sleeping'>, started='11:45:38'),\n     psutil.Process(pid=29836, name='python3', status=<ProcessStatus.STATUS_WAKING: 'waking'>, started='11:43:39')]\n    >>>\n    >>> p.status()\n    <ProcessStatus.STATUS_RUNNING: 'running'>\n    >>> p.create_time()\n    1267551141.5019531\n    >>> p.terminal()\n    '/dev/pts/0'\n    >>>\n    >>> p.username()\n    'giampaolo'\n    >>> p.uids()\n    puids(real=1000, effective=1000, saved=1000)\n    >>> p.gids()\n    pgids(real=1000, effective=1000, saved=1000)\n    >>>\n    >>> p.cpu_times()\n    pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0)\n    >>> p.cpu_percent(interval=1.0)\n    12.1\n    >>> p.cpu_affinity()\n    [0, 1, 2, 3]\n    >>> p.cpu_affinity([0, 1])  # set\n    >>> p.cpu_num()\n    1\n    >>>\n    >>> p.memory_info()\n    pmem(rss=3164160, vms=4410163, shared=897433, text=302694, data=2422374)\n    >>> p.memory_info_ex()\n    pmem_ex(rss=3164160, vms=4410163, shared=897433, text=302694, data=2422374, peak_rss=4172190, peak_vms=6399001, rss_anon=2266726, rss_file=897433, rss_shmem=0, swap=0, hugetlb=0)\n    >>> p.memory_footprint()  # \"real\" USS memory usage\n    pfootprint(uss=2355200, pss=2483712, swap=0)\n    >>> p.memory_percent()\n    0.7823\n    >>> p.memory_maps()\n    [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),\n     pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),\n     pmmap_grouped(path='[heap]',  rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),\n     pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),\n     ...]\n    >>>\n    >>> p.page_faults()\n    ppagefaults(minor=5905, major=3)\n    >>>\n    >>> p.io_counters()\n    pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543)\n    >>>\n    >>> p.open_files()\n    [popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768),\n     popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)]\n    >>>\n    >>> p.net_connections(kind='tcp')\n    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status=<ConnectionStatus.CONN_ESTABLISHED: 'ESTABLISHED'>),\n     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status=<ConnectionStatus.CONN_CLOSING: 'CLOSING'>)]\n    >>>\n    >>> p.threads()\n    [pthread(id=5234, user_time=22.5, system_time=9.2891),\n     pthread(id=5237, user_time=0.0707, system_time=1.1)]\n    >>>\n    >>> p.num_threads()\n    4\n    >>> p.num_fds()\n    8\n    >>> p.num_ctx_switches()\n    pctxsw(voluntary=78, involuntary=19)\n    >>>\n    >>> p.nice()\n    0\n    >>> p.nice(10)  # set\n    >>>\n    >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # IO priority (Win and Linux only)\n    >>> p.ionice()\n    pionice(ioclass=<ProcessIOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)\n    >>>\n    >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))  # set resource limits (Linux only)\n    >>> p.rlimit(psutil.RLIMIT_NOFILE)\n    (5, 5)\n    >>>\n    >>> p.environ()\n    {'LC_PAPER': 'it_IT.UTF-8', 'SHELL': '/bin/bash', 'GREP_OPTIONS': '--color=auto',\n    'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg',\n     ...}\n    >>>\n    >>> p.as_dict()\n    {'status': <ProcessStatus.STATUS_RUNNING: 'running'>, 'num_ctx_switches': pctxsw(voluntary=63, involuntary=1), 'pid': 5457, ...}\n    >>> p.is_running()\n    True\n    >>> p.suspend()\n    >>> p.resume()\n    >>>\n    >>> p.terminate()\n    >>> p.kill()\n    >>> p.wait(timeout=3)\n    <Exitcode.EX_OK: 0>\n    >>>\n    >>> psutil.test()\n    USER         PID %CPU %MEM     VSZ     RSS TTY        START    TIME  COMMAND\n    root           1  0.0  0.0   24584    2240            Jun17   00:00  init\n    root           2  0.0  0.0       0       0            Jun17   00:00  kthreadd\n    ...\n    giampaolo  31475  0.0  0.0   20760    3024 /dev/pts/0 Jun19   00:00  python2.4\n    giampaolo  31721  0.0  2.2  773060  181896            00:04   10:30  chrome\n    root       31763  0.0  0.0       0       0            00:05   00:00  kworker/0:1\n    >>>\n\nFurther process APIs\n--------------------\n\n.. code-block:: python\n\n    >>> import psutil\n    >>> for proc in psutil.process_iter(['pid', 'name']):\n    ...     print(proc.info)\n    ...\n    {'pid': 1, 'name': 'systemd'}\n    {'pid': 2, 'name': 'kthreadd'}\n    {'pid': 3, 'name': 'ksoftirqd/0'}\n    ...\n    >>>\n    >>> psutil.pid_exists(3)\n    True\n    >>>\n    >>> def on_terminate(proc):\n    ...     print(\"process {} terminated\".format(proc))\n    ...\n    >>> # waits for multiple processes to terminate\n    >>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate)\n    >>>\n\nHeap info\n---------\n\n.. code-block:: python\n\n    >>> import psutil\n    >>> psutil.heap_info()\n    pheap(heap_used=5177792, mmap_used=819200)\n    >>> psutil.heap_trim()\n\nSee also `psleak`_.\n\nWindows services\n----------------\n\n.. code-block:: python\n\n    >>> list(psutil.win_service_iter())\n    [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,\n     <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,\n     <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,\n     <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,\n     ...]\n    >>> s = psutil.win_service_get('alg')\n    >>> s.as_dict()\n    {'binpath': 'C:\\\\Windows\\\\System32\\\\alg.exe',\n     'description': 'Provides support for 3rd party protocol plug-ins for Internet Connection Sharing',\n     'display_name': 'Application Layer Gateway Service',\n     'name': 'alg',\n     'pid': None,\n     'start_type': 'manual',\n     'status': 'stopped',\n     'username': 'NT AUTHORITY\\\\LocalService'}\n\n\n.. _`psleak`: https://github.com/giampaolo/psleak\n.. _`shell equivalents`: https://psutil.readthedocs.io/en/latest/shell_equivalents.html\n.. _`top 100`: https://clickpy.clickhouse.com/dashboard/psutil\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\nIf you have discovered a security vulnerability in this project, please report\nit privately. **Do not disclose it as a public issue**. This gives me time to\nfix the issue before public exposure, reducing the chance that an exploit will\nbe used before a patch is released.\n\nTo report a security vulnerability use the\n[Tidelift security contact](https://tidelift.com/security). Tidelift will\ncoordinate the fix and the disclosure of the reported problem.\n"
  },
  {
    "path": "_bootstrap.py",
    "content": "# Copyright (c) 2009 Giampaolo Rodola. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Bootstrap utilities for loading psutil modules without psutil\nbeing installed.\n\"\"\"\n\nimport ast\nimport importlib.util\nimport os\nimport pathlib\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent\n\n\ndef load_module(path):\n    \"\"\"Load a Python module by file path without importing it\n    as part of a package.\n    \"\"\"\n    name = os.path.splitext(os.path.basename(path))[0]\n    spec = importlib.util.spec_from_file_location(name, path)\n    mod = importlib.util.module_from_spec(spec)\n    spec.loader.exec_module(mod)\n    return mod\n\n\ndef get_version():\n    \"\"\"Extract __version__ from psutil/__init__.py using AST\n    (no imports needed).\n    \"\"\"\n    path = ROOT_DIR / \"psutil\" / \"__init__.py\"\n    with open(path, encoding=\"utf-8\") as f:\n        mod = ast.parse(f.read())\n    for node in mod.body:\n        if isinstance(node, ast.Assign):\n            for target in node.targets:\n                if getattr(target, \"id\", None) == \"__version__\":\n                    return ast.literal_eval(node.value)\n    msg = \"could not find __version__\"\n    raise RuntimeError(msg)\n"
  },
  {
    "path": "docs/.readthedocs.yaml",
    "content": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\nversion: 2\n\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.11\"\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n  configuration: docs/conf.py\n\n# RTD recommends specifying your dependencies to enable reproducible builds:\n# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html\npython:\n  install:\n    - requirements: docs/requirements.txt\n"
  },
  {
    "path": "docs/DEVNOTES",
    "content": "TODO\n====\n\nA collection of ideas and notes about stuff to implement in future versions.\n\"#NNN\" occurrences refer to bug tracker issues at:\nhttps://github.com/giampaolo/psutil/issues\n\nFEATURES\n========\n\n- (UNIX) process root (different from cwd)\n\n- (Linux) locked files via /proc/locks:\n  https://www.centos.org/docs/5/html/5.2/Deployment_Guide/s2-proc-locks.html\n\n- #269: NIC rx/tx queue. This should probably go into net_if_stats().\n  Figure out on what platforms this is supported:\n  Linux: yes\n  Others: ?\n\n- Asynchronous psutil.Popen (see http://bugs.python.org/issue1191964)\n\n- (Windows) fall back on using WMIC for Process methods returning AccessDenied\n\n- #613: thread names; patch for macOS available at:\n  https://code.google.com/p/plcrashreporter/issues/detail?id=65\n  Sample code:\n  https://github.com/janmojzis/pstree/blob/master/proc_kvm.c\n\n- scripts/taskmgr-gui.py (using tk).\n\n- system-wide number of open file descriptors:\n  - https://jira.hyperic.com/browse/SIGAR-30\n\n- Number of system threads.\n  - Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684824(v=vs.85).aspx\n\n- Doc / wiki which compares similarities between UNIX cli tools and psutil.\n  Example:\n  ```\n  df -a  ->  psutil.disk_partitions\n  lsof  ->  psutil.Process.open_files() and psutil.Process.net_connections()\n  killall->  (actual script)\n  tty  ->  psutil.Process.terminal()\n  who  ->  psutil.users()\n  ```\n\n- psutil.proc_tree() something which obtains a {pid:ppid, ...} dict for\n  all running processes in one shot. This can be factored out from\n  Process.children() and exposed as a first class function.\n  PROS: on Windows we can take advantage of _psutil_windows.ppid_map()\n  which is faster than iterating over all pids and calling ppid().\n  CONS: scripts/pstree.py shows this can be easily done in the user code\n  so maybe it's not worth the addition.\n\n- advanced cmdline interface exposing the whole API and providing different\n  kind of outputs (e.g. pprinted, colorized, json).\n\n- [Linux]: process cgroups (http://en.wikipedia.org/wiki/Cgroups). They look\n  similar to prlimit() in terms of functionality but uglier (they should allow\n  limiting per-process network IO resources though, which is great). Needs\n  further reading.\n\n- Python 3.3. exposed different sched.h functions:\n  http://docs.python.org/dev/whatsnew/3.3.html#os\n  http://bugs.python.org/issue12655\n  http://docs.python.org/dev/library/os.html#interface-to-the-scheduler\n  It might be worth to take a look and figure out whether we can include some\n  of those in psutil.\n  Also, we can probably reimplement wait_pid() on POSIX which is currently\n  implemented as a busy-loop.\n\n- os.times() provides 'elapsed' times (cpu_times() might).\n\n- ...also guest_time and cguest_time on Linux.\n\n- Enrich exception classes hierarchy on Python >= 3.3 / post PEP-3151 so that:\n   - NoSuchProcess inherits from ProcessLookupError\n   - AccessDenied inherits from PermissionError\n   - TimeoutExpired inherits from TimeoutError (debatable)\n   See: http://docs.python.org/3/library/exceptions.html#os-exceptions\n\n- Process.threads() might grow an extra \"id\" parameter so that it can be\n   used as such:\n  ```\n  >>> p = psutil.Process(os.getpid())\n  >>> p.threads(id=psutil.current_thread_id())\n  thread(id=2539, user_time=0.03, system_time=0.02)\n  >>>\n  ```\n  Note: this leads to questions such as \"should we have a custom NoSuchThread\n  exception? Also see issue #418.\n  Note #2: this would work with os.getpid() only.\n  psutil.current_thread_id() might be desirable as per issue #418 though.\n\n- should psutil.TimeoutExpired exception have a 'msg' kwarg similar to\n  NoSuchProcess and AccessDenied? Not that we need it, but currently we\n  cannot raise a TimeoutExpired exception with a specific error string.\n\n- round Process.memory_percent() result?\n\nBUGFIXES\n========\n\n- #600: windows / open_files(): support network file handles.\n\nREJECTED IDEAS\n==============\n\n- #550: threads per core\n- #1667: process_iter(new_only=True)\n\nINCONSISTENCIES\n===============\n\n- PROCFS_PATH should have been set_procfs_path().\n- `virtual_memory()` should have been `memory_virtual()`.\n- `swap_memory()` should have been `memory_swap()`.\n\nRESOURCES\n=========\n\n- conky: https://github.com/brndnmtthws/conky/\n- sigar: https://github.com/hyperic/sigar (Java)\n- zabbix: https://zabbix.org/wiki/Get_Zabbix\n- libstatgrab: http://www.i-scream.org/libstatgrab/\n- top: http://www.unixtop.org/\n- oshi: https://github.com/oshi/oshi\n- netdata: https://github.com/netdata/netdata\n\nSTATS\n=====\n\n- https://pepy.tech/projects/psutil\n- https://clickpy.clickhouse.com/dashboard/psutil\n- https://pypistats.org/packages/psutil\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n\nPYTHON = python3\nSPHINXBUILD = $(PYTHON) -m sphinx\nBUILDDIR = _build\nALLSPHINXOPTS = --fail-on-warning -d $(BUILDDIR)/doctrees .\n\nclean:  ## Remove all build files\n\trm -rf $(BUILDDIR)\n\nhtml:  ## Generate doc in HTML format\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\nsinglehtml:  ## Generate doc as a single HTML page\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\ntext:  ## Generate doc in .txt format\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nauto-build:  ## Rebuild HTML on file changes (requires inotify-tools)\n\t$(MAKE) clean\n\t$(MAKE) html\n\twhile inotifywait -r -e modify,create,delete,move .; do $(MAKE) html; done\n\ncheck-links:  ## Check links\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\nhelp: ## Display callable targets.\n\t@awk -F':.*?## ' '/^[a-zA-Z0-9_.-]+:.*?## / {printf \"\\033[36m%-24s\\033[0m %s\\n\", $$1, $$2}' $(MAKEFILE_LIST) | sort\n"
  },
  {
    "path": "docs/README",
    "content": "About\n=====\n\nThis directory contains the reStructuredText (reST) sources to the psutil\ndocumentation.  You don't need to build them yourself, prebuilt versions are\navailable at http://psutil.readthedocs.io.\nIn case you want, you need to install sphinx first:\n\n    $ pip install sphinx\n\nThen run:\n\n    $ make html\n\nYou'll then have an HTML version of the doc at _build/html/index.html.\n"
  },
  {
    "path": "docs/_ext/add_home_link.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Sphinx extension that prepends a 'Home' link to the TOC sidebar.\nThis script gets called on `make html`.\n\"\"\"\n\nfrom sphinx.application import Sphinx\n\n\ndef add_home_link(app: Sphinx, pagename, templatename, context, doctree):\n    if \"toctree\" in context:\n        toctree_func = context[\"toctree\"]\n        toc_html = toctree_func(\n            maxdepth=2, collapse=False, includehidden=False\n        )\n        # prepend Home link manually\n        home_link = '<li class=\"toctree-l1\"><a href=\"index.html\">Home</a></li>'\n        context[\"toctree\"] = lambda **_kw: home_link + toc_html\n\n\ndef setup(app: Sphinx):\n    app.connect(\"html-page-context\", add_home_link)\n"
  },
  {
    "path": "docs/_ext/availability.py",
    "content": "# noqa: CPY001\n\n# Slightly adapted from CPython's:\n# https://github.com/python/cpython/blob/main/Doc/tools/extensions/availability.py\n# Copyright (c) PSF\n# Licensed under the Python Software Foundation License Version 2.\n\n\"\"\"Support for `.. availability:: …` directive, to document platform\navailability.\n\"\"\"\n\nfrom docutils import nodes\nfrom sphinx.locale import _ as sphinx_gettext\nfrom sphinx.util import logging\nfrom sphinx.util.docutils import SphinxDirective\n\nlogger = logging.getLogger(__name__)\n\n_PLATFORMS = frozenset({\n    \"AIX\",\n    \"BSD\",\n    \"FreeBSD\",\n    \"Linux\",\n    \"Linux with glibc\",\n    \"macOS\",\n    \"NetBSD\",\n    \"OpenBSD\",\n    \"POSIX\",\n    \"SunOS\",\n    \"UNIX\",\n    \"Windows\",\n})\n\n_LIBC = frozenset({\n    \"glibc\",\n    \"musl\",\n})\n\nKNOWN_PLATFORMS = _PLATFORMS | _LIBC\n\n\nclass Availability(SphinxDirective):\n    has_content = True\n    required_arguments = 1\n    optional_arguments = 0\n    final_argument_whitespace = True\n\n    def run(self):\n        title = sphinx_gettext(\"Availability\")\n        sep = nodes.Text(\": \")\n        parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno)\n        pnode = nodes.paragraph(\n            title, \"\", nodes.emphasis(title, title), sep, *parsed, *msgs\n        )\n        self.set_source_info(pnode)\n        cnode = nodes.container(\"\", pnode, classes=[\"availability\"])\n        self.set_source_info(cnode)\n        if self.content:\n            self.state.nested_parse(self.content, self.content_offset, cnode)\n        self.parse_platforms()\n\n        return [cnode]\n\n    def parse_platforms(self):\n        \"\"\"Parse platform information from arguments\n\n        Arguments is a comma-separated string of platforms. A platform may\n        be prefixed with \"not \" to indicate that a feature is not available.\n        Example::\n\n           .. availability:: Windows, Linux >= 4.2, not glibc\n        \"\"\"\n        platforms = {}\n        for arg in self.arguments[0].rstrip(\".\").split(\",\"):\n            arg = arg.strip()\n            platform, _, version = arg.partition(\" >= \")\n            if platform.startswith(\"not \"):\n                version = False\n                platform = platform.removeprefix(\"not \")\n            elif not version:\n                version = True\n            platforms[platform] = version\n\n        unknown = set(platforms).difference(KNOWN_PLATFORMS)\n        if unknown:\n            logger.warning(\n                \"Unknown platform%s or syntax '%s' in '.. availability:: %s', \"\n                \"see %s:KNOWN_PLATFORMS for a set of known platforms.\",\n                \"s\" if len(platforms) != 1 else \"\",\n                \" \".join(sorted(unknown)),\n                self.arguments[0],\n                __file__,\n                location=self.get_location(),\n            )\n\n        return platforms\n\n\ndef setup(app):\n    app.add_directive(\"availability\", Availability)\n    return {\n        \"version\": \"1.0\",\n        \"parallel_read_safe\": True,\n        \"parallel_write_safe\": True,\n    }\n"
  },
  {
    "path": "docs/_ext/changelog_anchors.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Sphinx extension for adding anchors to each section in\ndocs/changelog.rst.\n\nThis script gets called on `make html`, and adds numeric anchors for\nversion titles (e.g., 7.2.3 -> #723).\n\"\"\"\n\nimport re\n\nfrom docutils import nodes\n\nVERSION_RE = re.compile(r\"^(\\d+\\.\\d+\\.\\d+)\")\n\n\ndef add_version_anchors(app, doctree, docname):\n    if docname != \"changelog\":\n        return\n\n    for node in doctree.traverse(nodes.section):\n        title = node.next_node(nodes.title)\n        if not title:\n            continue\n\n        text = title.astext()\n        m = VERSION_RE.match(text)\n        if m:\n            anchor = m.group(1).replace(\".\", \"\")\n            if anchor not in node[\"ids\"]:\n                node[\"ids\"].insert(0, anchor)\n\n\ndef setup(app):\n    app.connect(\"doctree-resolved\", add_version_anchors)\n"
  },
  {
    "path": "docs/_ext/check_python_syntax.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Sphinx extension that checks the Python syntax of code blocks in the\ndocumentation. This script gets called on `make html`.\n\"\"\"\n\nimport ast\n\nimport docutils.nodes\nimport sphinx.errors\n\n\ndef check_python_blocks(app, doctree, docname):\n    path = app.env.doc2path(docname)\n\n    for node in doctree.traverse(docutils.nodes.literal_block):\n        lang = node.get(\"language\")\n        if lang not in {\"python\", \"py\"}:\n            continue\n\n        code = node.astext()\n\n        # skip empty blocks\n        if not code.strip():\n            continue\n\n        # skip REPL examples containing >>>\n        if \">>>\" in code:\n            continue\n\n        try:\n            ast.parse(code)\n        except SyntaxError as err:\n            lineno = node.line or \"?\"\n            msg = (\n                f\"invalid Python syntax in {path}:{lineno}:\\n\\n{code}\\n\\n{err}\"\n            )\n            raise sphinx.errors.SphinxError(msg) from None\n\n\ndef setup(app):\n    app.connect(\"doctree-resolved\", check_python_blocks)\n"
  },
  {
    "path": "docs/_links.rst",
    "content": ""
  },
  {
    "path": "docs/_static/copybutton.js",
    "content": "$(document).ready(function() {\n    /* Add a [>>>] button on the top-right corner of code samples to hide\n     * the >>> and ... prompts and the output and thus make the code\n     * copyable. */\n    var div = $('.highlight-python .highlight,' +\n                '.highlight-python3 .highlight')\n    var pre = div.find('pre');\n\n    // get the styles from the current theme\n    pre.parent().parent().css('position', 'relative');\n    var hide_text = 'Hide the prompts and output';\n    var show_text = 'Show the prompts and output';\n    var border_width = pre.css('border-top-width');\n    var border_style = pre.css('border-top-style');\n    var border_color = pre.css('border-top-color');\n    var button_styles = {\n        'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0',\n        'border-color': border_color, 'border-style': border_style,\n        'border-width': border_width, 'color': border_color, 'text-size': '75%',\n        'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em',\n        'border-radius': '0 3px 0 0'\n    }\n\n    // create and add the button to all the code blocks that contain >>>\n    div.each(function(index) {\n        var jthis = $(this);\n        if (jthis.find('.gp').length > 0) {\n            var button = $('<span class=\"copybutton\">&gt;&gt;&gt;</span>');\n            button.css(button_styles)\n            button.attr('title', hide_text);\n            jthis.prepend(button);\n        }\n        // tracebacks (.gt) contain bare text elements that need to be\n        // wrapped in a span to work with .nextUntil() (see later)\n        jthis.find('pre:has(.gt)').contents().filter(function() {\n            return ((this.nodeType == 3) && (this.data.trim().length > 0));\n        }).wrap('<span>');\n    });\n\n    // define the behavior of the button when it's clicked\n    $('.copybutton').toggle(\n        function() {\n            var button = $(this);\n            button.parent().find('.go, .gp, .gt').hide();\n            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');\n            button.css('text-decoration', 'line-through');\n            button.attr('title', show_text);\n        },\n        function() {\n            var button = $(this);\n            button.parent().find('.go, .gp, .gt').show();\n            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');\n            button.css('text-decoration', 'none');\n            button.attr('title', hide_text);\n        });\n});\n\n"
  },
  {
    "path": "docs/_static/css/custom.css",
    "content": "/* ================================================================== */\n/* Layout                                                             */\n/* ================================================================== */\n\n@media (min-width: 1200px) {\n    .wy-nav-content {\n        max-width: 1000px;\n        padding-left: 20px !important;\n    }\n}\n\n/* ================================================================== */\n/* Navigation                                                         */\n/* ================================================================== */\n\n/* hide top navigation, keep footer navigation */\n.rst-content > div[role=\"navigation\"] {\n    display: none;\n}\n\nfooter div[role=\"navigation\"][aria-label=\"Footer\"] {\n    display: block;\n}\n\n/* ================================================================== */\n/* Sidebar                                                            */\n/* ================================================================== */\n\n.wy-side-nav-search {\n    background-color: grey !important\n}\n\n/* ================================================================== */\n/* Headings                                                           */\n/* ================================================================== */\n\nh1, h2, h3 {\n    background: #eee;\n    padding: 5px;\n    border-bottom: 1px solid #ccc;\n}\n\nh1 {\n    font-size: 35px;\n}\n\n/* ================================================================== */\n/* Tables                                                             */\n/* ================================================================== */\n\n.wy-table-responsive table thead {\n    background-color: #eeeeee;\n}\n\n.document th {\n    padding: 4px 8px !important;\n    font-weight: 600;\n}\n\n.document th p {\n    margin-bottom: 0px !important;\n}\n\n.document td {\n    padding: 4px 8px !important;\n}\n\n.document td p {\n    margin-bottom: 0px !important;\n}\n\n/* \"longtable\" class (used by alternatives.rst): fixed layout\n   with extra padding for multi-line cells */\n.wy-table-responsive table.longtable {\n    table-layout: fixed;\n    width: 100%;\n}\n\n.wy-table-responsive table.longtable th,\n.wy-table-responsive table.longtable td {\n    padding: 10px !important;\n    white-space: normal !important;\n    word-wrap: break-word;\n}\n\n/* adoption page logos */\n.document td img[alt$=\"-logo\"] {\n    height: 20px !important;\n    width: 20px !important;\n    vertical-align: middle;\n}\n\n/* ================================================================== */\n/* Lists                                                              */\n/* ================================================================== */\n\n.rst-content ul {\n    margin-top: 0px !important;\n}\n\n.rst-content ul p {\n    margin-bottom: 0px !important;\n}\n\n.rst-content li {\n    list-style: outside;\n    margin-left: 15px;\n}\n\n/* ================================================================== */\n/* API signatures                                                     */\n/* ================================================================== */\n\n.rst-content dl:not(.docutils) {\n    margin: 0px 0px 0px 0px !important;\n}\n\n.rst-content dl:not(.docutils) dt {\n    color: #555;\n}\n\n.data dd {\n    margin-bottom: 0px !important;\n}\n\n.data .descname {\n    border-right:10px !important;\n}\n\n.function .descclassname,\n.class .descclassname {\n    font-weight: normal !important;\n}\n\n.sig-paren {\n    padding-left: 2px;\n    padding-right: 2px;\n}\n\n/* ================================================================== */\n/* Code blocks                                                        */\n/* ================================================================== */\n\npre {\n    padding: 5px !important;\n}\n\n.highlight {\n    background: #eeffcc;\n}\n\n.highlight .hll {\n    background-color: #ffffcc\n}\n\n.highlight .c {\n    color: #408090;\n    font-style: italic\n}\n\n.codeblock div[class^='highlight'],\npre.literal-block div[class^='highlight'],\n.rst-content .literal-block div[class^='highlight'],\ndiv[class^='highlight'] div[class^='highlight'] {\n    background-color: #eeffcc !important;\n}\n\n/* ================================================================== */\n/* Links                                                              */\n/* ================================================================== */\n\n/* external links: dotted underline (except intersphinx) */\na.reference.external:not([href*=\"docs.python.org\"]) {\n    text-decoration: underline dotted;\n}\n\na.reference.external:not([href*=\"docs.python.org\"]):hover {\n    text-decoration: underline solid;\n}\n\na.external[href^=\"#\"] {\n    text-decoration: none;\n    color: inherit;\n}\n\n/* ================================================================== */\n/* Admonitions (note, warning, tip) - styled like python doc          */\n/* ================================================================== */\n\ndiv.admonition {\n    margin-top: 10px !important;\n    margin-bottom: 10px !important;\n}\n\ndiv.warning {\n    background-color: #ffe4e4 !important;\n    border: 1px solid #f66 !important;\n    border-radius: 3px !important;\n}\n\ndiv.note {\n    background-color: #eee !important;\n    border: 1px solid #ccc !important;\n    border-radius: 3px !important;\n}\n\ndiv.tip {\n    background-color: #dfd !important;\n    border: 1px solid green !important;\n    border-radius: 3px !important;\n}\n\ndiv.admonition p.admonition-title + p {\n    display: inline !important;\n}\n\np.admonition-title {\n    display: inline !important;\n    background: none !important;\n    color: black !important;\n}\n\np.admonition-title:after {\n    content: \":\" !important;\n}\n\n/* hide admonition icons */\n.fa-exclamation-circle:before,\n.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,\n.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,\n.rst-content .admonition-title:before {\n    display: none !important;\n}\n\n.admonition.warning {\n    padding-top: 5px !important;\n    padding-bottom: 5px !important;\n}\n\n.admonition.warning p {\n    margin-bottom: 5px !important;\n}\n\n.admonition.note {\n    padding-top: 5px !important;\n    padding-bottom: 5px !important;\n}\n\n.admonition.note p {\n    margin-bottom: 5px !important;\n}\n\n.note code {\n    background: #d6d6d6 !important;\n}\n\n/* ================================================================== */\n/* Version directives (versionadded / versionchanged / deprecated)    */\n/* ================================================================== */\n\ndiv.versionadded,\ndiv.versionchanged,\ndiv.deprecated {\n    border-left: 3px solid;\n    padding: 0 1rem;\n}\n\ndiv.versionadded {\n    border-left-color: rgb(79, 196, 100);\n}\n\ndiv.versionchanged {\n    border-left-color: rgb(244, 227, 76);\n}\n\ndiv.deprecated {\n    border-left-color: rgb(244, 76, 78);\n}\n\ndiv.versionadded .versionmodified {\n    color: rgb(41, 100, 51);\n}\n\ndiv.versionchanged .versionmodified {\n    color: rgb(133, 72, 38);\n}\n\ndiv.deprecated .versionmodified {\n    color: rgb(159, 49, 51);\n}\n"
  },
  {
    "path": "docs/_static/sidebar.js",
    "content": "/*\n * sidebar.js\n * ~~~~~~~~~~\n *\n * This script makes the Sphinx sidebar collapsible.\n *\n * .sphinxsidebar contains .sphinxsidebarwrapper.  This script adds in\n * .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton used to\n * collapse and expand the sidebar.\n *\n * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden and the\n * width of the sidebar and the margin-left of the document are decreased.\n * When the sidebar is expanded the opposite happens.  This script saves a\n * per-browser/per-session cookie used to remember the position of the sidebar\n * among the pages.  Once the browser is closed the cookie is deleted and the\n * position reset to the default (expanded).\n *\n * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.\n * :license: BSD, see LICENSE for details.\n *\n */\n\n$(function() {\n  // global elements used by the functions.\n  // the 'sidebarbutton' element is defined as global after its\n  // creation, in the add_sidebar_button function\n  var bodywrapper = $('.bodywrapper');\n  var sidebar = $('.sphinxsidebar');\n  var sidebarwrapper = $('.sphinxsidebarwrapper');\n\n  // original margin-left of the bodywrapper and width of the sidebar\n  // with the sidebar expanded\n  var bw_margin_expanded = bodywrapper.css('margin-left');\n  var ssb_width_expanded = sidebar.width();\n\n  // margin-left of the bodywrapper and width of the sidebar\n  // with the sidebar collapsed\n  var bw_margin_collapsed = '.8em';\n  var ssb_width_collapsed = '.8em';\n\n  // colors used by the current theme\n  var dark_color = '#AAAAAA';\n  var light_color = '#CCCCCC';\n\n  function sidebar_is_collapsed() {\n    return sidebarwrapper.is(':not(:visible)');\n  }\n\n  function toggle_sidebar() {\n    if (sidebar_is_collapsed())\n      expand_sidebar();\n    else\n      collapse_sidebar();\n  }\n\n  function collapse_sidebar() {\n    sidebarwrapper.hide();\n    sidebar.css('width', ssb_width_collapsed);\n    bodywrapper.css('margin-left', bw_margin_collapsed);\n    sidebarbutton.css({\n        'margin-left': '0',\n        //'height': bodywrapper.height(),\n        'height': sidebar.height(),\n        'border-radius': '5px'\n    });\n    sidebarbutton.find('span').text('»');\n    sidebarbutton.attr('title', _('Expand sidebar'));\n    document.cookie = 'sidebar=collapsed';\n  }\n\n  function expand_sidebar() {\n    bodywrapper.css('margin-left', bw_margin_expanded);\n    sidebar.css('width', ssb_width_expanded);\n    sidebarwrapper.show();\n    sidebarbutton.css({\n        'margin-left': ssb_width_expanded-12,\n        //'height': bodywrapper.height(),\n        'height': sidebar.height(),\n        'border-radius': '0 5px 5px 0'\n    });\n    sidebarbutton.find('span').text('«');\n    sidebarbutton.attr('title', _('Collapse sidebar'));\n    //sidebarwrapper.css({'padding-top':\n    //  Math.max(window.pageYOffset - sidebarwrapper.offset().top, 10)});\n    document.cookie = 'sidebar=expanded';\n  }\n\n  function add_sidebar_button() {\n    sidebarwrapper.css({\n        'float': 'left',\n        'margin-right': '0',\n        'width': ssb_width_expanded - 28\n    });\n    // create the button\n    sidebar.append(\n      '<div id=\"sidebarbutton\"><span>&laquo;</span></div>'\n    );\n    var sidebarbutton = $('#sidebarbutton');\n    // find the height of the viewport to center the '<<' in the page\n    var viewport_height;\n    if (window.innerHeight)\n \t  viewport_height = window.innerHeight;\n    else\n\t  viewport_height = $(window).height();\n    var sidebar_offset = sidebar.offset().top;\n\n    var sidebar_height = sidebar.height();\n    //var sidebar_height = Math.max(bodywrapper.height(), sidebar.height());\n    sidebarbutton.find('span').css({\n        'display': 'block',\n        'margin-top': sidebar_height/2 - 10\n        //'margin-top': (viewport_height - sidebar.position().top - 20) / 2\n        //'position': 'fixed',\n        //'top': Math.min(viewport_height/2, sidebar_height/2 + sidebar_offset) - 10\n    });\n\n    sidebarbutton.click(toggle_sidebar);\n    sidebarbutton.attr('title', _('Collapse sidebar'));\n    sidebarbutton.css({\n        'border-radius': '0 5px 5px 0',\n        'color': '#444444',\n        'background-color': '#CCCCCC',\n        'font-size': '1.2em',\n        'cursor': 'pointer',\n        'height': sidebar_height,\n        'padding-top': '1px',\n        'padding-left': '1px',\n        'margin-left': ssb_width_expanded - 12\n    });\n\n    sidebarbutton.hover(\n      function () {\n          $(this).css('background-color', dark_color);\n      },\n      function () {\n          $(this).css('background-color', light_color);\n      }\n    );\n  }\n\n  function set_position_from_cookie() {\n    if (!document.cookie)\n      return;\n    var items = document.cookie.split(';');\n    for(var k=0; k<items.length; k++) {\n      var key_val = items[k].split('=');\n      var key = key_val[0];\n      if (key == 'sidebar') {\n        var value = key_val[1];\n        if ((value == 'collapsed') && (!sidebar_is_collapsed()))\n          collapse_sidebar();\n        else if ((value == 'expanded') && (sidebar_is_collapsed()))\n          expand_sidebar();\n      }\n    }\n  }\n\n  add_sidebar_button();\n  var sidebarbutton = $('#sidebarbutton');\n  set_position_from_cookie();\n});\n"
  },
  {
    "path": "docs/_templates/layout.html",
    "content": "{% extends \"!layout.html\" %}\n{% block extrahead %}\n{{ super() }}\n<!-- Google tag (gtag.js) -->\n<script async src=\"https://www.googletagmanager.com/gtag/js?id=G-G7374TFB11\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n  gtag('config', 'G-G7374TFB11');\n</script>\n{% endblock %}\n"
  },
  {
    "path": "docs/adoption.rst",
    "content": ".. currentmodule:: psutil\n\nWho uses psutil\n===============\n\npsutil is among the `top 100 <https://clickpy.clickhouse.com/dashboard/psutil>`__\nmost-downloaded packages on PyPI, with 280+ million downloads per month,\n`760,000+ GitHub repositories <https://github.com/giampaolo/psutil/network/dependents>`__ using\nit, and 14,000+ packages depending on it. The projects below are a small\nsample of notable software that depends on it.\n\nSee also :doc:`alternatives` for related Python libraries and equivalents in\nother languages.\n\nInfrastructure / automation\n---------------------------\n\n.. list-table::\n   :header-rows: 1\n   :widths: 28 32 14 26\n\n   * - Project\n     - Description\n     - Stars\n     - psutil usage\n   * - |homeassistant-logo| `Home Assistant <https://github.com/home-assistant/core>`__\n     - Open source home automation platform\n     - |homeassistant-stars|\n     - system monitor integration\n   * - |ansible-logo| `Ansible <https://github.com/ansible/ansible>`__\n     - IT automation platform\n     - |ansible-stars|\n     - system fact gathering\n   * - |airflow-logo| `Apache Airflow <https://github.com/apache/airflow>`__\n     - Workflow orchestration platform\n     - |airflow-stars|\n     - process supervisor, unit testing\n   * - |celery-logo| `Celery <https://github.com/celery/celery>`__\n     - Distributed task queue\n     - |celery-stars|\n     - worker process monitoring, memleak detection\n   * - |salt-logo| `Salt <https://github.com/saltstack/salt>`__\n     - Infrastructure automation at scale\n     - |salt-stars|\n     - deep system data collection (grains)\n   * - |dask-logo| `Dask <https://github.com/dask/dask>`__\n     - Parallel computing with task scheduling\n     - |dask-stars|\n     - `metrics dashboard <https://github.com/dask/dask/blob/0d40a604284e021d098682b71c55d0387a414925/docs/source/dashboard.rst#L147>`__, profiling\n   * - |ajenti-logo| `Ajenti <https://github.com/ajenti/ajenti>`__\n     - Web-based server administration panel\n     - |ajenti-stars|\n     - monitoring plugins, deep integration\n\nAI / machine learning\n---------------------\n\n.. list-table::\n   :header-rows: 1\n   :widths: 28 32 14 26\n\n   * - Project\n     - Description\n     - Stars\n     - psutil usage\n   * - |tensorflow-logo| `TensorFlow <https://github.com/tensorflow/tensorflow>`__\n     - Open source machine learning framework by Google\n     - |tensorflow-stars|\n     - unit tests\n   * - |pytorch-logo| `PyTorch <https://github.com/pytorch/pytorch>`__\n     - Tensors and dynamic neural networks with GPU acceleration\n     - |pytorch-stars|\n     - benchmark scripts\n   * - |ray-logo| `Ray <https://github.com/ray-project/ray>`__\n     - AI compute engine with distributed runtime\n     - |ray-stars|\n     - metrics dashboard\n   * - |mlflow-logo| `MLflow <https://github.com/mlflow/mlflow>`__\n     - AI/ML engineering platform\n     - |mlflow-stars|\n     - deep system monitoring integration\n\nDeveloper tools\n---------------\n\n.. list-table::\n   :header-rows: 1\n   :widths: 28 32 14 26\n\n   * - Project\n     - Description\n     - Stars\n     - psutil usage\n   * - |sentry-logo| `Sentry <https://github.com/getsentry/sentry>`__\n     - Error tracking and performance monitoring\n     - |sentry-stars|\n     - send telemetry metrics\n   * - |locust-logo| `Locust <https://github.com/locustio/locust>`__\n     - Scalable load testing in Python\n     - |locust-stars|\n     - monitoring of the Locust process\n   * - |spyder-logo| `Spyder <https://github.com/spyder-ide/spyder>`__\n     - Scientific Python IDE\n     - |spyder-stars|\n     - deep integration, UI stats, process management\n   * - |psleak-logo| `psleak <https://github.com/giampaolo/psleak>`__\n     - Test framework to detect memory leaks in Python C extensions\n     - |psleak-stars|\n     - heap process memory (:func:`heap_info()`)\n\nSystem monitoring\n-----------------\n\n.. list-table::\n   :header-rows: 1\n   :widths: 28 32 14 26\n\n   * - Project\n     - Description\n     - Stars\n     - psutil usage\n   * - |glances-logo| `Glances <https://github.com/nicolargo/glances>`__\n     - System monitoring tool (top/htop alternative)\n     - |glances-stars|\n     - core dependency for all metrics\n   * - |bpytop-logo| `bpytop <https://github.com/aristocratos/bpytop>`__\n     - Terminal resource monitor\n     - |bpytop-stars|\n     - core dependency for all metrics\n   * - |auto-cpufreq-logo| `auto-cpufreq <https://github.com/AdnanHodzic/auto-cpufreq>`__\n     - Automatic CPU speed and power optimizer for Linux\n     - |auto-cpufreq-stars|\n     - core dependency for CPU monitoring\n   * - |grr-logo| `GRR <https://github.com/google/grr>`__\n     - Remote live forensics by Google\n     - |grr-stars|\n     - endpoint system data collection, deep integration\n   * - |stui-logo| `s-tui <https://github.com/amanusk/s-tui>`__\n     - Terminal CPU stress and monitoring utility\n     - |stui-stars|\n     - core dependency for metrics\n   * - |asitop-logo| `asitop <https://github.com/tlkh/asitop>`__\n     - Apple Silicon performance monitoring CLI\n     - |asitop-stars|\n     - core dependency for system metrics\n   * - |psdash-logo| `psdash <https://github.com/Jahaja/psdash>`__\n     - Web dashboard using psutil and Flask\n     - |psdash-stars|\n     - core dependency for all metrics\n   * - |dd-agent-logo| `dd-agent <https://github.com/DataDog/dd-agent>`__\n     - Original monitoring agent by Datadog\n     - |dd-agent-stars|\n     - system metrics collection\n   * - |ddtrace-logo| `dd-trace-py <https://github.com/DataDog/dd-trace-py>`__\n     - Python tracing and profiling library\n     - |ddtrace-stars|\n     - system metrics collection\n\nHow this list was compiled\n--------------------------\n\n- `GitHub dependency graph <https://github.com/giampaolo/psutil/network/dependents>`__\n  was used to identify packages and repositories that depend on psutil.\n- GitHub code search with query \"psutil in:readme language:Python\", sorted\n  by stars, was used to find additional projects that mention psutil in their\n  README.\n- Each candidate was then manually verified by checking the project's\n  pyproject.toml, setup.py, setup.cfg or requirements.txt to confirm that\n  psutil is an actual dependency (direct, build-time, or optional), not just\n  a passing mention.\n- Projects were excluded if they only mention psutil in documentation or\n  examples without declaring it as a dependency.\n- Star counts are pulled dynamically from `shields.io <https://shields.io/>`__ badges.\n- The final list was manually curated to include notable projects and\n  meaningful usages of psutil across different areas of the Python ecosystem.\n\n.. ============================================================================\n.. ============================================================================\n.. ============================================================================\n\n.. Star badges\n.. ============================================================================\n\n.. |airflow-stars| image:: https://img.shields.io/github/stars/apache/airflow.svg?style=social&label=%20\n.. |ajenti-stars| image:: https://img.shields.io/github/stars/ajenti/ajenti.svg?style=social&label=%20\n.. |ansible-stars| image:: https://img.shields.io/github/stars/ansible/ansible.svg?style=social&label=%20\n.. |asitop-stars| image:: https://img.shields.io/github/stars/tlkh/asitop.svg?style=social&label=%20\n.. |auto-cpufreq-stars| image:: https://img.shields.io/github/stars/AdnanHodzic/auto-cpufreq.svg?style=social&label=%20\n.. |bpytop-stars| image:: https://img.shields.io/github/stars/aristocratos/bpytop.svg?style=social&label=%20\n.. |celery-stars| image:: https://img.shields.io/github/stars/celery/celery.svg?style=social&label=%20\n.. |dask-stars| image:: https://img.shields.io/github/stars/dask/dask.svg?style=social&label=%20\n.. |ddtrace-stars| image:: https://img.shields.io/github/stars/DataDog/dd-trace-py.svg?style=social&label=%20\n.. |dd-agent-stars| image:: https://img.shields.io/github/stars/DataDog/dd-agent.svg?style=social&label=%20\n.. |glances-stars| image:: https://img.shields.io/github/stars/nicolargo/glances.svg?style=social&label=%20\n.. |grr-stars| image:: https://img.shields.io/github/stars/google/grr.svg?style=social&label=%20\n.. |homeassistant-stars| image:: https://img.shields.io/github/stars/home-assistant/core.svg?style=social&label=%20\n.. |locust-stars| image:: https://img.shields.io/github/stars/locustio/locust.svg?style=social&label=%20\n.. |mlflow-stars| image:: https://img.shields.io/github/stars/mlflow/mlflow.svg?style=social&label=%20\n.. |psdash-stars| image:: https://img.shields.io/github/stars/Jahaja/psdash.svg?style=social&label=%20\n.. |psleak-stars| image:: https://img.shields.io/github/stars/giampaolo/psleak.svg?style=social&label=%20\n.. |pytorch-stars| image:: https://img.shields.io/github/stars/pytorch/pytorch.svg?style=social&label=%20\n.. |ray-stars| image:: https://img.shields.io/github/stars/ray-project/ray.svg?style=social&label=%20\n.. |salt-stars| image:: https://img.shields.io/github/stars/saltstack/salt.svg?style=social&label=%20\n.. |sentry-stars| image:: https://img.shields.io/github/stars/getsentry/sentry.svg?style=social&label=%20\n.. |spyder-stars| image:: https://img.shields.io/github/stars/spyder-ide/spyder.svg?style=social&label=%20\n.. |stui-stars| image:: https://img.shields.io/github/stars/amanusk/s-tui.svg?style=social&label=%20\n.. |tensorflow-stars| image:: https://img.shields.io/github/stars/tensorflow/tensorflow.svg?style=social&label=%20\n\n.. Logo images\n.. ============================================================================\n\n.. |airflow-logo| image:: https://github.com/apache.png?s=28 :height: 28\n.. |ajenti-logo| image:: https://github.com/ajenti.png?s=28 :height: 28\n.. |ansible-logo| image:: https://github.com/ansible.png?s=28 :height: 28\n.. |asitop-logo| image:: https://github.com/tlkh.png?s=28 :height: 28\n.. |auto-cpufreq-logo| image:: https://github.com/AdnanHodzic.png?s=28 :height: 28\n.. |bpytop-logo| image:: https://github.com/aristocratos.png?s=28 :height: 28\n.. |celery-logo| image:: https://github.com/celery.png?s=28 :height: 28\n.. |dask-logo| image:: https://github.com/dask.png?s=28 :height: 28\n.. |ddtrace-logo| image:: https://github.com/DataDog.png?s=28 :height: 28\n.. |dd-agent-logo| image:: https://github.com/DataDog.png?s=28 :height: 28\n.. |glances-logo| image:: https://github.com/nicolargo.png?s=28 :height: 28\n.. |grr-logo| image:: https://github.com/google.png?s=28 :height: 28\n.. |homeassistant-logo| image:: https://github.com/home-assistant.png?s=28 :height: 28\n.. |locust-logo| image:: https://github.com/locustio.png?s=28 :height: 28\n.. |mlflow-logo| image:: https://github.com/mlflow.png?s=28 :height: 28\n.. |osquery-logo| image:: https://github.com/osquery.png?s=28 :height: 28\n.. |psdash-logo| image:: https://github.com/Jahaja.png?s=28 :height: 28\n.. |psleak-logo| image:: https://github.com/giampaolo.png?s=28 :height: 28\n.. |pytorch-logo| image:: https://github.com/pytorch.png?s=28 :height: 28\n.. |ray-logo| image:: https://github.com/ray-project.png?s=28 :height: 28\n.. |salt-logo| image:: https://github.com/saltstack.png?s=28 :height: 28\n.. |sentry-logo| image:: https://github.com/getsentry.png?s=28 :height: 28\n.. |spyder-logo| image:: https://github.com/spyder-ide.png?s=28 :height: 28\n.. |stui-logo| image:: https://github.com/amanusk.png?s=28 :height: 28\n.. |tensorflow-logo| image:: https://github.com/tensorflow.png?s=28 :height: 28\n\n.. --- Notes\n.. Stars shield:\n.. https://shields.io/badges/git-hub-repo-stars\n"
  },
  {
    "path": "docs/alternatives.rst",
    "content": ".. currentmodule:: psutil\n\nAlternatives\n============\n\nThis page describes Python tools and modules that overlap with psutil,\nto help you pick the right tool for the job.\nSee also :doc:`adoption` for notable projects that use psutil.\n\nPython standard library\n-----------------------\n\nos module\n^^^^^^^^^\n\nThe :mod:`os` module provides a handful of process-related functions:\n:func:`os.getpid`, :func:`os.getppid`, :func:`os.getuid`,\n:func:`os.cpu_count`, :func:`os.getloadavg` (UNIX only). These are\ncheap wrappers around POSIX syscalls and are perfectly fine when you\nonly need information about the *current* process and don't need\ncross-platform code.\n\npsutil goes further in several directions. Its primary goal is to provide a\n**single portable interface** for concepts that are natively UNIX-only. Things\nlike process CPU and memory usage, open file descriptors, network connections,\nsignals, nice levels, and I/O counters exist as first-class OS primitives on\nLinux and macOS, but have no direct equivalent on Windows. psutil implements\nall of them on Windows too (using Win32 APIs, ``NtQuerySystemInformation`` and\nWMI) so that code written against psutil runs unmodified on every supported\nplatform. Beyond portability, it also exposes the same information for *any*\nprocess (not just the current one), and returns structured named tuples instead\nof raw integers.\n\nresource module\n^^^^^^^^^^^^^^^\n\n:mod:`resource` (UNIX only) lets you read and set resource limits\n(``RLIMIT_*``) and get basic usage counters (user/system time, page\nfaults, I/O ops) for the *current* process or its children via\n:func:`resource.getrusage`. It is the right tool when you specifically\nwant to enforce or inspect ``ulimit``-style limits.\n\npsutil's :meth:`Process.rlimit` exposes the same interface but extends it\nto all processes, not just the caller.\n\nsubprocess module\n^^^^^^^^^^^^^^^^^\n\nCalling ``ps``, ``top``, ``netstat``, ``vmstat`` via\n:mod:`subprocess` and parsing the text output is fragile: output\nformats differ across OS versions and locales, parsing is error-prone,\nand spawning a subprocess per sample is slow. psutil reads the same\nkernel data sources directly without spawning any external processes.\n\nplatform module\n^^^^^^^^^^^^^^^\n\n:mod:`platform` provides information about the OS and Python runtime,\nsuch as OS name, kernel version, architecture, and machine type.\nIt is useful for identifying the environment, but does not expose\nruntime metrics or process information like psutil. Overlaps with\npsutil's OS constants (:data:`LINUX`, :data:`WINDOWS`, :data:`MACOS`,\netc.).\n\n/proc filesystem\n^^^^^^^^^^^^^^^^\n\nOn Linux, ``/proc`` exposes process and system information as virtual files.\nReading ``/proc/pid/status`` or ``/proc/meminfo`` directly is fast and has no\ndependencies, which is why some minimal containers or scripts do this. The\ndownsides are that it is Linux-only, the format may vary across kernel\nversions, and you have to parse raw text yourself. psutil parses ``/proc``\ninternally, exposes the same information through a consistent\ncross-platform API and handles edge cases (numeric overflow, compatibility\nwith old kernels, graceful fallbacks, etc.) transparently.\n\nThird-party libraries\n---------------------\n\nLibraries that cover areas psutil does not, or that go deeper on a\nspecific platform or subsystem.\n\n.. list-table::\n   :header-rows: 1\n   :widths: 5 25\n   :class: longtable\n\n   * - Library\n     - Focus\n\n   * - `distro <https://github.com/python-distro/distro>`_\n     - Linux distro info (name, version, codename). psutil does not\n       expose OS details.\n\n   * - `GPUtil <https://github.com/anderskm/gputil>`_ /\n       `pynvml <https://github.com/gpuopenanalytics/pynvml>`_\n     - NVIDIA GPU utilization and VRAM usage.\n\n   * - `ifaddr <https://github.com/ifaddr/ifaddr>`_\n     - Network interface address enumeration.\n       Overlaps with :func:`net_if_addrs`.\n\n   * - `libvirt-python <https://github.com/libvirt/libvirt-python>`_\n     - Manage KVM/QEMU/Xen VMs: enumerate guests, query\n       CPU/memory allocation. Complements psutil's host-level view.\n\n   * - `prometheus_client <https://github.com/prometheus/client_python>`_\n     - Export metrics to Prometheus. Use *alongside* psutil.\n\n   * - `py-cpuinfo <https://github.com/workhorsy/py-cpuinfo>`_\n     - CPU brand string, microarchitecture, feature flags.\n\n   * - `pyroute2 <https://github.com/svinota/pyroute2>`_\n     - Linux netlink (interfaces, routes, connections).\n       Overlaps with :func:`net_if_addrs`, :func:`net_if_stats`,\n       :func:`net_connections`.\n\n   * - `pywifi <https://github.com/awkman/pywifi>`_\n     - WiFi scanning, signal strength, SSID. Exposes wireless\n       details that :func:`net_if_addrs` does not.\n\n   * - `pySMART <https://github.com/truenas/py-SMART>`_\n     - S.M.A.R.T. disk health data. Complements\n       :func:`disk_io_counters`.\n\n   * - `pywin32 <https://github.com/mhammond/pywin32>`_\n     - Win32 API bindings (Windows only).\n\n   * - `setproctitle <https://github.com/dvarrazzo/py-setproctitle>`_\n     - Set process title shown by ``ps``/``top``. Writes what\n       :meth:`Process.name` reads.\n\n   * - `wmi <https://github.com/tjguk/wmi>`_\n     - WMI interface (Windows only).\n\nOther languages\n---------------\n\nEquivalent libraries in other languages providing cross-platform system and\nprocess information.\n\n.. list-table::\n   :header-rows: 1\n   :widths: 5 5 20\n   :class: longtable\n\n   * - Library\n     - Language\n     - Focus\n\n   * - `gopsutil <https://github.com/shirou/gopsutil>`_\n     - Go\n     - CPU, memory, disk, network, processes. Directly inspired\n       by psutil and follows a similar API.\n\n   * - `Hardware.Info <https://github.com/Jinjinov/Hardware.Info>`_\n     - C# / .NET\n     - CPU, RAM, GPU, disk, network, battery.\n\n   * - `hwinfo <https://github.com/lfreist/hwinfo>`_\n     - C++\n     - CPU, RAM, GPU, disks, mainboard. More hardware-focused.\n\n   * - `OSHI <https://github.com/oshi/oshi>`_\n     - Java\n     - OS and hardware information: CPU, memory, disk, network,\n       processes, sensors, USB devices.\n\n   * - `sysinfo <https://github.com/GuillaumeGomez/sysinfo>`_\n     - Rust\n     - CPU, memory, disk, network, processes, components.\n\n   * - `systeminformation <https://github.com/sebhildebrandt/systeminformation>`_\n     - Node.js\n     - CPU, memory, disk, network, processes, battery, Docker.\n"
  },
  {
    "path": "docs/api.rst",
    "content": ".. currentmodule:: psutil\n.. include:: _links.rst\n.. _availability:\n\nAPI reference\n=============\n\n.. note::\n   psutil 8.0 introduces breaking API changes. See the\n   :ref:`migration guide <migration-8.0>` if upgrading from 7.x.\n\n.. contents::\n   :local:\n   :depth: 5\n\nSystem related functions\n------------------------\n\nCPU\n^^^\n\n.. function:: cpu_times(percpu=False)\n\n  Return system CPU times as a named tuple. All fields are\n  :term:`cumulative counters <cumulative counter>` (seconds) representing time\n  the CPU has spent in each mode since boot.\n  The attributes availability varies depending on the platform.\n  Cross-platform fields:\n\n  - **user**: time spent by normal processes executing in user mode; on Linux\n    this also includes **guest** time\n\n  - **system**: time spent by processes executing in kernel mode\n\n  - **idle**: time spent doing nothing\n\n  Platform-specific fields:\n\n  - **nice** *(Linux, macOS, BSD)*: time spent by :term:`niced <nice>`\n    (lower-priority) processes executing in user mode; on Linux this also\n    includes **guest_nice** time.\n\n  - **iowait** *(Linux, SunOS, AIX)*: time spent waiting for I/O to complete\n    (:term:`iowait`). This is *not* accounted in **idle** time counter.\n\n  - **irq** *(Linux, Windows, BSD)*: time spent for servicing\n    :term:`hardware interrupts <hardware interrupt>`\n\n  - **softirq** *(Linux)*: time spent for servicing\n    :term:`soft interrupts <soft interrupt>`\n\n  - **steal** *(Linux)*: CPU time the virtual machine wanted to run but was\n    used by other virtual machines or the host. A sustained non-zero steal rate\n    indicates CPU contention.\n\n  - **guest** *(Linux)*: time the host CPU spent running a guest operating\n    system (virtual machine). Already included in **user** time.\n\n  - **guest_nice** *(Linux)*: like **guest**, but for virtual CPUs running at a\n    lower :term:`nice` priority. Already included in **nice** time.\n\n  - **dpc** *(Windows)*: time spent servicing deferred procedure calls (DPCs);\n    DPCs are interrupts that run at a lower priority than standard interrupts.\n\n  When *percpu* is ``True`` return a list of named tuples for each logical CPU\n  on the system.\n  The list is ordered by CPU index.\n  The order of the list is consistent across calls.\n  Example output on Linux:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.cpu_times()\n     scputimes(user=17411.7, system=3797.02, idle=51266.57, nice=77.99, iowait=732.58, irq=0.01, softirq=142.43, steal=0.0, guest=0.0, guest_nice=0.0)\n\n  .. note::\n    CPU times are always supposed to increase over time, or at least remain the\n    same, and that's because time cannot go backwards. Surprisingly sometimes\n    this might not be the case (at least on Windows and Linux), see `#1210\n    <https://github.com/giampaolo/psutil/issues/1210#issuecomment-363046156>`_.\n\n  .. versionchanged:: 4.1.0\n     added *irq* and *dpc* fields on Windows (*irq* was called *interrupt*\n     before 8.0.0).\n\n  .. versionchanged:: 8.0.0\n     *interrupt* field on Windows was renamed to *irq*; *interrupt* still\n     works but raises :exc:`DeprecationWarning`.\n\n  .. versionchanged:: 8.0.0\n     ``cpu_times()`` field order was standardized: ``user``, ``system``,\n     ``idle`` are now always the first three fields. Previously on Linux,\n     macOS, and BSD the first three were ``user``, ``nice``, ``system``.\n     See :ref:`migration guide <migration-8.0>`.\n\n.. function:: cpu_percent(interval=None, percpu=False)\n\n  Return a float representing the current system-wide CPU utilization as a\n  percentage. When *interval* is > ``0.0`` compares system CPU times elapsed\n  before and after the interval (blocking).\n  When *interval* is ``0.0`` or ``None`` compares system CPU times elapsed\n  since last call or module import, returning immediately.\n  That means the first time this is called it will return a meaningless ``0.0``\n  value which you are supposed to ignore.\n  In this case it is recommended for accuracy that this function be called with\n  at least ``0.1`` seconds between calls.\n  When *percpu* is ``True`` returns a list of floats representing the\n  utilization as a percentage for each CPU.\n  The list is ordered by CPU index. The order of the list is consistent across\n  calls.\n  Internally this function maintains a global map (a dict) where each key is\n  the ID of the calling thread (:func:`threading.get_ident`). This means it can be\n  called from different threads, at different intervals, and still return\n  meaningful and independent results.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> # blocking\n     >>> psutil.cpu_percent(interval=1)\n     2.0\n     >>> # non-blocking (percentage since last call)\n     >>> psutil.cpu_percent(interval=None)\n     2.9\n     >>> # blocking, per-cpu\n     >>> psutil.cpu_percent(interval=1, percpu=True)\n     [2.0, 1.0]\n     >>>\n\n  .. note::\n    the first time this function is called with *interval* = ``0.0`` or ``None``\n    it will return a meaningless ``0.0`` value which you are supposed to\n    ignore. See also :ref:`faq_cpu_percent` FAQ.\n\n  .. versionchanged:: 5.9.6\n     the function is now thread safe.\n\n.. function:: cpu_times_percent(interval=None, percpu=False)\n\n  Same as :func:`cpu_percent` but provides utilization percentages for each\n  specific CPU time as is returned by\n  :func:`psutil.cpu_times(percpu=True)<cpu_times()>`.\n  *interval* and\n  *percpu* arguments have the same meaning as in :func:`cpu_percent`.\n  On Linux \"guest\" and \"guest_nice\" percentages are not accounted in \"user\"\n  and \"user_nice\" percentages.\n\n  .. note::\n    the first time this function is called with *interval* = ``0.0`` or\n    ``None`` it will return a meaningless ``0.0`` value which you are supposed\n    to ignore. See also :ref:`faq_cpu_percent` FAQ.\n\n  .. versionchanged:: 4.1.0\n     two new *irq* and *dpc* fields are returned on Windows (*irq* was\n     called *interrupt* before 8.0.0).\n\n  .. versionchanged:: 5.9.6\n     function is now thread safe.\n\n.. function:: cpu_count(logical=True)\n\n  Return the number of :term:`logical CPUs <logical CPU>` in the system\n  (similar to :func:`os.cpu_count`) or ``None`` if undetermined.\n  Unlike :func:`os.cpu_count`, this is not influenced by the ``PYTHON_CPU_COUNT``\n  environment variable introduced in Python 3.13.\n  \"logical CPUs\" means the number of physical cores multiplied by the number\n  of threads that can run on each core (this is known as Hyper Threading).\n  This is what cloud providers often refer to as vCPUs.\n  If *logical* is ``False`` return the number of physical cores only, or\n  ``None`` if undetermined.\n  On OpenBSD and NetBSD ``psutil.cpu_count(logical=False)`` always return\n  ``None``.\n  Example on a system having 2 cores + Hyper Threading:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.cpu_count()\n     4\n     >>> psutil.cpu_count(logical=False)\n     2\n\n  Note that ``psutil.cpu_count()`` may not necessarily be equivalent to the\n  actual number of CPUs the current process can use.\n  That can vary in case process CPU affinity has been changed, Linux cgroups\n  are being used or (in case of Windows) on systems using processor groups or\n  having more than 64 CPUs.\n  The number of usable CPUs can be obtained with:\n\n  .. code-block:: pycon\n\n     >>> len(psutil.Process().cpu_affinity())\n     1\n\n.. function:: cpu_stats()\n\n  Return various CPU statistics as a named tuple. All fields are\n  :term:`cumulative counters <cumulative counter>` since boot.\n\n  - **ctx_switches**: number of :term:`context switches <context switch>`\n    (voluntary + involuntary).\n  - **interrupts**:\n    number of :term:`hardware interrupts <hardware interrupt>`.\n  - **soft_interrupts**:\n    number of :term:`soft interrupts <soft interrupt>`. Always set to ``0`` on\n    Windows and SunOS.\n  - **syscalls**: number of system calls. Always set to ``0`` on Linux.\n\n  Example (Linux):\n\n  .. code-block:: python\n\n     >>> import psutil\n     >>> psutil.cpu_stats()\n     scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)\n\n  .. versionadded:: 4.1.0\n\n\n.. function:: cpu_freq(percpu=False)\n\n    Return CPU frequency as a named tuple including *current*, *min* and *max*\n    frequencies expressed in Mhz. On Linux *current* frequency reports the\n    real-time value, on all other platforms this usually represents the\n    nominal \"fixed\" value (never changing). If *percpu* is ``True`` and the\n    system supports per-cpu frequency retrieval (Linux and FreeBSD), a list of\n    frequencies is returned for each CPU, if not, a list with a single element\n    is returned. If *min* and *max* cannot be determined they are set to\n    ``0.0``.\n\n    Example (Linux):\n\n    .. code-block:: python\n\n       >>> import psutil\n       >>> psutil.cpu_freq()\n       scpufreq(current=931.42925, min=800.0, max=3500.0)\n       >>> psutil.cpu_freq(percpu=True)\n       [scpufreq(current=2394.945, min=800.0, max=3500.0),\n        scpufreq(current=2236.812, min=800.0, max=3500.0),\n        scpufreq(current=1703.609, min=800.0, max=3500.0),\n        scpufreq(current=1754.289, min=800.0, max=3500.0)]\n\n    .. availability:: Linux, macOS, Windows, FreeBSD, OpenBSD.\n\n    .. versionadded:: 5.1.0\n\n    .. versionchanged:: 5.5.1\n       added FreeBSD support.\n\n    .. versionchanged:: 5.9.1\n       added OpenBSD support.\n\n.. function:: getloadavg()\n\n    Return the average system load over the last 1, 5 and 15 minutes as a tuple.\n    The \"load\" represents the processes which are in a runnable state, either\n    using the CPU or waiting to use the CPU (e.g. waiting for disk I/O).\n    On UNIX systems this relies on :func:`os.getloadavg`. On Windows this is emulated\n    by using a Windows API that spawns a thread which keeps running in\n    background and updates results every 5 seconds, mimicking the UNIX behavior.\n    Thus, on Windows, the first time this is called and for the next 5 seconds\n    it will return a meaningless ``(0.0, 0.0, 0.0)`` tuple.\n    The numbers returned only make sense when compared to the number of CPU cores\n    installed on the system. So, for instance, a value of `3.14` on a system\n    with 10 logical CPUs means that the system load was 31.4% percent over the\n    last N minutes.\n\n    .. code-block:: python\n\n       >>> import psutil\n       >>> psutil.getloadavg()\n       (3.14, 3.89, 4.67)\n       >>> psutil.cpu_count()\n       10\n       >>> # percentage representation\n       >>> [x / psutil.cpu_count() * 100 for x in psutil.getloadavg()]\n       [31.4, 38.9, 46.7]\n\n    .. versionadded:: 5.6.2\n\nMemory\n^^^^^^\n\n.. function:: virtual_memory()\n\n  Return statistics about system memory usage as a named tuple including the\n  following fields, expressed in bytes.\n\n  - **total**: total physical memory (exclusive swap).\n  - **available**: memory that can be given instantly to processes without the\n    system going into swap. On Linux it uses the ``MemAvailable`` field from\n    ``/proc/meminfo`` *(kernel 3.14+)*; on older kernels it falls back to an\n    estimate. This is the recommended field for monitoring actual memory usage\n    in a cross-platform fashion. See :term:`available memory`.\n  - **percent**: the percentage usage calculated as\n    ``(total - available) / total * 100``.\n  - **used**: memory in use, calculated differently depending on the platform\n    (see the table below). It is meant for informational purposes. Neither\n    ``total - free`` nor ``total - available`` necessarily equals ``used``.\n  - **free**: memory not currently allocated to anything. This is\n    typically much lower than **available** because the OS keeps recently\n    freed memory as reclaimable cache (see **cached** and **buffers**)\n    rather than zeroing it immediately. Do not use this to check for\n    memory pressure; use **available** instead.\n  - **active** *(Linux, macOS, BSD)*: memory currently mapped by processes\n    or recently accessed, held in RAM. It is unlikely to be reclaimed unless\n    the system is under significant memory pressure.\n  - **inactive** *(Linux, macOS, BSD)*: memory not recently accessed. It\n    still holds valid data (:term:`page cache`, old allocations) but is a\n    candidate for reclamation or swapping. On BSD systems it is counted in\n    **available**.\n  - **buffers** *(Linux, BSD)*: memory used by the kernel to cache disk\n    metadata (e.g. filesystem structures). Reclaimable by the OS when needed.\n  - **cached** *(Linux, BSD, Windows)*: RAM used by the kernel to cache file\n    contents (data read from or written to disk). Reclaimable by the OS when\n    needed. See :term:`page cache`.\n  - **shared** *(Linux, BSD)*: memory accessible by multiple processes\n    simultaneously, such as in-memory ``tmpfs`` and POSIX shared memory objects\n    (``shm_open``). On Linux this corresponds to ``Shmem`` in ``/proc/meminfo``\n    and is already counted within **active** / **inactive**.\n  - **slab** *(Linux)*: memory used by the kernel's internal object caches\n    (e.g. inode and dentry caches). The reclaimable portion\n    (``SReclaimable``) is already included in **cached**.\n  - **wired** *(macOS, BSD, Windows)*: memory pinned in RAM by the kernel (e.g.\n    kernel code and critical data structures). It can never be moved to disk.\n\n  Below is a table showing implementation details. All info on Linux is\n  retrieved from `/proc/meminfo`_. On macOS via ``host_statistics64()``. On\n  Windows via `GetPerformanceInfo`_.\n\n  .. list-table::\n     :header-rows: 1\n     :widths: 9 15 14 14 26\n\n     * - Field\n       - Linux\n       - macOS\n       - Windows\n       - FreeBSD\n     * - total\n       - ``MemTotal``\n       - ``sysctl() hw.memsize``\n       - ``PhysicalTotal``\n       - ``sysctl() hw.physmem``\n     * - available\n       - ``MemAvailable``\n       - ``inactive + free``\n       - ``PhysicalAvailable``\n       - ``inactive + cached + free``\n     * - used\n       - ``total - available``\n       - ``active + wired``\n       - ``total - available``\n       - ``active + wired + cached``\n     * - free\n       - ``MemFree``\n       - ``free - speculative``\n       - same as ``available``\n       - ``sysctl() vm.stats.vm.v_free_count``\n     * - active\n       - ``Active``\n       - ``active``\n       -\n       - ``sysctl() vm.stats.vm.v_active_count``\n     * - inactive\n       - ``Inactive``\n       - ``inactive``\n       -\n       - ``sysctl() vm.stats.vm.v_inactive_count``\n     * - buffers\n       - ``Buffers``\n       -\n       -\n       - ``sysctl() vfs.bufspace``\n     * - cached\n       - ``Cached + SReclaimable``\n       -\n       - ``SystemCache``\n       - ``sysctl() vm.stats.vm.v_cache_count``\n     * - shared\n       - ``Shmem``\n       -\n       -\n       - ``sysctl(CTL_VM/VM_METER) t_vmshr + t_rmshr``\n     * - slab\n       - ``Slab``\n       -\n       -\n       -\n     * - wired\n       -\n       - ``wired``\n       - ``KernelNonpaged``\n       - ``sysctl() vm.stats.vm.v_wire_count``\n\n  Example on Linux:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> mem = psutil.virtual_memory()\n     >>> mem\n     svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304, slab=199348224)\n     >>>\n     >>> THRESHOLD = 500 * 1024 * 1024  # 500MB\n     >>> if mem.available <= THRESHOLD:\n     ...     print(\"warning\")\n     ...\n     >>>\n\n  .. note:: if you just want to know how much physical memory is left in a\n    cross-platform manner, simply rely on **available** and **percent**\n    fields.\n\n  .. note::\n     - On Linux, **total**, **free**, **used**, **shared**, and **available**\n       match the output of the ``free`` command.\n     - On macOS, **free**, **active**, **inactive**, and **wired** match\n       ``vm_stat`` output.\n     - On Windows, **total**, **used** (\"In use\"), and **available** match\n       the Task Manager (Performance > Memory tab).\n\n  .. note::  see also `scripts/meminfo.py`_.\n\n  .. versionchanged:: 4.2.0\n     added *shared* metric on Linux.\n\n  .. versionchanged:: 5.4.4\n     added *slab* metric on Linux.\n\n  .. versionchanged:: 8.0.0\n     added *cached* and *wired* metric on Windows.\n\n.. function:: swap_memory()\n\n  Return system swap memory statistics as a named tuple including the following\n  fields:\n\n  * **total**: total swap space. On Windows this is derived as\n    ``CommitLimit - PhysicalTotal``, representing virtual memory backed by\n    the page file rather than the raw page-file size.\n  * **used**: swap space currently in use.\n  * **free**: swap space not in use (``total - used``).\n  * **percent**: swap usage as a percentage, calculated as\n    ``used / total * 100``.\n  * **sin**: number of bytes the system has paged *in* from disk (pages moved\n    from swap space back into RAM) since boot. See :term:`swap-in`.\n  * **sout**: number of bytes the system has paged *out* to disk (pages moved\n    from RAM into swap space) since boot. A continuously increasing **sout**\n    is a sign of memory pressure. See :term:`swap-out`.\n\n  **sin** and **sout** are :term:`cumulative counters <cumulative counter>` since boot; monitor\n  their rate of change rather than the absolute value to detect active\n  swapping. See :term:`swap-in` and :term:`swap-out`.\n  On Windows both are always ``0``.\n  See `scripts/meminfo.py`_ script providing an example on how to convert bytes in a\n  human readable form.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.swap_memory()\n     sswap(total=2097147904L, used=886620160L, free=1210527744L, percent=42.3, sin=1050411008, sout=1906720768)\n\n  .. versionchanged:: 5.2.3\n     on Linux this function relies on /proc fs instead of sysinfo() syscall so\n     that it can be used in conjunction with\n     :const:`psutil.PROCFS_PATH` in order to retrieve memory info about\n     Linux containers such as Docker and Heroku.\n\nDisks\n^^^^^\n\n.. function:: disk_partitions(all=False)\n\n  Return all mounted disk partitions as a list of named tuples including device,\n  mount point and filesystem type, similarly to \"df\" command on UNIX. If *all*\n  parameter is ``False`` it tries to distinguish and return physical devices\n  only (e.g. hard disks, cd-rom drives, USB keys) and ignore all others\n  (e.g. pseudo, memory, duplicate, inaccessible filesystems).\n  Note that this may not be fully reliable on all systems (e.g. on BSD this\n  parameter is ignored).\n  See `scripts/disk_usage.py`_ script providing an example usage.\n  Returns a list of named tuples with the following fields:\n\n  * **device**: the device path (e.g. ``\"/dev/hda1\"``). On Windows this is the\n    drive letter (e.g. ``\"C:\\\\\"``).\n  * **mountpoint**: the mount point path (e.g. ``\"/\"``). On Windows this is the\n    drive letter (e.g. ``\"C:\\\\\"``).\n  * **fstype**: the partition filesystem (e.g. ``\"ext3\"`` on UNIX or ``\"NTFS\"``\n    on Windows).\n  * **opts**: a comma-separated string indicating different mount options for\n    the drive/partition. Platform-dependent.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.disk_partitions()\n     [sdiskpart(device='/dev/sda3', mountpoint='/', fstype='ext4', opts='rw,errors=remount-ro'),\n      sdiskpart(device='/dev/sda7', mountpoint='/home', fstype='ext4', opts='rw')]\n\n  .. versionchanged:: 5.7.4\n     added *maxfile* and *maxpath* fields.\n\n  .. versionchanged:: 6.0.0\n     removed *maxfile* and *maxpath* fields.\n\n.. function:: disk_usage(path)\n\n  Return disk usage statistics about the partition which contains the given\n  *path* as a named tuple including **total**, **used** and **free** space\n  expressed in bytes, plus the **percentage** usage.\n  ``OSError`` is raised if *path* does not exist.\n  Starting from Python 3.3 this is also available as :func:`shutil.disk_usage`\n  (see `BPO-12442`_).\n  See `scripts/disk_usage.py`_ script providing an example usage.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.disk_usage('/')\n     sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)\n\n  .. note::\n    UNIX usually reserves 5% of the total disk space for the root user.\n    *total* and *used* fields on UNIX refer to the overall total and used\n    space, whereas *free* represents the space available for the **user** and\n    *percent* represents the **user** utilization (see\n    `source code <https://github.com/giampaolo/psutil/blob/3dea30d583b8c1275057edb1b3b720813b4d0f60/psutil/_psposix.py#L123>`_).\n    That is why *percent* value may look 5% bigger than what you would expect\n    it to be.\n    Also note that both 4 values match \"df\" cmdline utility.\n\n  .. versionchanged:: 4.3.0\n     *percent* value takes root reserved space into account.\n\n.. function:: disk_io_counters(perdisk=False, nowrap=True)\n\n  Return system-wide disk I/O statistics as a named tuple including the\n  following fields:\n\n  - **read_count**: number of reads\n  - **write_count**: number of writes\n  - **read_bytes**: number of bytes read\n  - **write_bytes**: number of bytes written\n\n  Platform-specific fields:\n\n  - **read_time**: (all except *NetBSD* and *OpenBSD*) time spent reading from\n    disk (in milliseconds)\n  - **write_time**: (all except *NetBSD* and *OpenBSD*) time spent writing to disk\n    (in milliseconds)\n  - **busy_time**: (*Linux*, *FreeBSD*) time spent doing actual I/Os (in\n    milliseconds). See :term:`busy_time`.\n  - **read_merged_count** (*Linux*): number of merged reads (see `iostats doc`_)\n  - **write_merged_count** (*Linux*): number of merged writes (see `iostats doc`_)\n\n  If *perdisk* is ``True`` return the same information for every physical disk\n  installed on the system as a dictionary with partition names as the keys and\n  the named tuple described above as the values.\n  See `scripts/iotop.py`_ for an example application.\n  On some systems such as Linux, on a very busy or long-lived system, the\n  numbers returned by the kernel may overflow and wrap (restart from zero).\n  If *nowrap* is ``True`` psutil will detect and adjust those numbers across\n  function calls and add \"old value\" to \"new value\" so that the returned\n  numbers will always be increasing or remain the same, but never decrease.\n  ``disk_io_counters.cache_clear()`` can be used to invalidate the *nowrap*\n  cache.\n  On Windows it may be necessary to issue ``diskperf -y`` command from cmd.exe\n  first in order to enable IO counters.\n  On diskless machines this function will return ``None`` or ``{}`` if\n  *perdisk* is ``True``.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.disk_io_counters()\n     sdiskio(read_count=8141, write_count=2431, read_bytes=290203, write_bytes=537676, read_time=5868, write_time=94922)\n     >>>\n     >>> psutil.disk_io_counters(perdisk=True)\n     {'sda1': sdiskio(read_count=920, write_count=1, read_bytes=2933248, write_bytes=512, read_time=6016, write_time=4),\n      'sda2': sdiskio(read_count=18707, write_count=8830, read_bytes=6060, write_bytes=3443, read_time=24585, write_time=1572),\n      'sdb1': sdiskio(read_count=161, write_count=0, read_bytes=786432, write_bytes=0, read_time=44, write_time=0)}\n\n  .. note::\n    on Windows ``\"diskperf -y\"`` command may need to be executed first\n    otherwise this function won't find any disk.\n\n  .. versionchanged:: 5.3.0\n     numbers no longer wrap (restart from zero) across calls thanks to new\n    *nowrap* argument.\n\n  .. versionchanged:: 4.0.0\n     added *busy_time* (Linux, FreeBSD), *read_merged_count* and\n    *write_merged_count* (Linux) fields.\n\n  .. versionchanged:: 4.0.0\n     NetBSD no longer has *read_time* and *write_time* fields.\n\nNetwork\n^^^^^^^\n\n.. function:: net_io_counters(pernic=False, nowrap=True)\n\n  Return system-wide network I/O statistics as a named tuple including the\n  following attributes:\n\n  - **bytes_sent**: number of bytes sent\n  - **bytes_recv**: number of bytes received\n  - **packets_sent**: number of packets sent\n  - **packets_recv**: number of packets received\n  - **errin**: total number of errors while receiving\n  - **errout**: total number of errors while sending\n  - **dropin**: total number of incoming packets which were dropped\n  - **dropout**: total number of outgoing packets which were dropped (always 0\n    on macOS and BSD). See :term:`dropin / dropout`.\n\n  If *pernic* is ``True`` return the same information for every network\n  interface installed on the system as a dictionary with network interface\n  names as the keys and the named tuple described above as the values.\n  On some systems such as Linux, on a very busy or long-lived system, the\n  numbers returned by the kernel may overflow and wrap (restart from zero).\n  If *nowrap* is ``True`` psutil will detect and adjust those numbers across\n  function calls and add \"old value\" to \"new value\" so that the returned\n  numbers will always be increasing or remain the same, but never decrease.\n  ``net_io_counters.cache_clear()`` can be used to invalidate the *nowrap*\n  cache.\n  On machines with no network interfaces this function will return ``None`` or\n  ``{}`` if *pernic* is ``True``.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.net_io_counters()\n     snetio(bytes_sent=14508483, bytes_recv=62749361, packets_sent=84311, packets_recv=94888, errin=0, errout=0, dropin=0, dropout=0)\n     >>>\n     >>> psutil.net_io_counters(pernic=True)\n     {'lo': snetio(bytes_sent=547971, bytes_recv=547971, packets_sent=5075, packets_recv=5075, errin=0, errout=0, dropin=0, dropout=0),\n     'wlan0': snetio(bytes_sent=13921765, bytes_recv=62162574, packets_sent=79097, packets_recv=89648, errin=0, errout=0, dropin=0, dropout=0)}\n\n  Also see `scripts/nettop.py`_ and `scripts/ifconfig.py`_ for an example application.\n\n  .. versionchanged:: 5.3.0\n     numbers no longer wrap (restart from zero) across calls thanks to new\n     *nowrap* argument.\n\n.. function:: net_connections(kind='inet')\n\n  Return system-wide socket connections as a list of named tuples.\n  Every named tuple provides 7 attributes:\n\n  - **fd**: the socket file descriptor. If the connection refers to the current\n    process this may be passed to :func:`socket.fromfd`\n    to obtain a usable socket object.\n    On Windows and SunOS this is always set to ``-1``.\n  - **family**: the address family, either :data:`socket.AF_INET`, :data:`socket.AF_INET6` or :data:`socket.AF_UNIX`.\n  - **type**: the address type, either :data:`socket.SOCK_STREAM`, :data:`socket.SOCK_DGRAM` or\n    :data:`socket.SOCK_SEQPACKET`.\n  - **laddr**: the local address as a ``(ip, port)`` named tuple or a ``path``\n    in case of AF_UNIX sockets. For UNIX sockets see notes below.\n  - **raddr**: the remote address as a ``(ip, port)`` named tuple or an\n    absolute ``path`` in case of UNIX sockets.\n    When the remote endpoint is not connected you'll get an empty tuple\n    (AF_INET*) or ``\"\"`` (AF_UNIX). For UNIX sockets see notes below.\n  - **status**: represents the status of a TCP connection. The return value\n    is one of the `psutil.CONN_* <#connections-constants>`_ constants\n    (a string).\n    For UDP and UNIX sockets this is always going to be\n    :const:`psutil.CONN_NONE`.\n  - **pid**: the PID of the process which opened the socket, if retrievable,\n    else ``None``. On some platforms (e.g. Linux) the availability of this\n    field changes depending on process privileges (root is needed).\n\n  The *kind* parameter is a string which filters for connections matching the\n  following criteria:\n\n  .. table::\n\n   +----------------+-----------------------------------------------------+\n   | Kind value     | Connections using                                   |\n   +================+=====================================================+\n   | ``\"inet\"``     | IPv4 and IPv6                                       |\n   +----------------+-----------------------------------------------------+\n   | ``\"inet4\"``    | IPv4                                                |\n   +----------------+-----------------------------------------------------+\n   | ``\"inet6\"``    | IPv6                                                |\n   +----------------+-----------------------------------------------------+\n   | ``\"tcp\"``      | TCP                                                 |\n   +----------------+-----------------------------------------------------+\n   | ``\"tcp4\"``     | TCP over IPv4                                       |\n   +----------------+-----------------------------------------------------+\n   | ``\"tcp6\"``     | TCP over IPv6                                       |\n   +----------------+-----------------------------------------------------+\n   | ``\"udp\"``      | UDP                                                 |\n   +----------------+-----------------------------------------------------+\n   | ``\"udp4\"``     | UDP over IPv4                                       |\n   +----------------+-----------------------------------------------------+\n   | ``\"udp6\"``     | UDP over IPv6                                       |\n   +----------------+-----------------------------------------------------+\n   | ``\"unix\"``     | UNIX socket (both UDP and TCP protocols)            |\n   +----------------+-----------------------------------------------------+\n   | ``\"all\"``      | the sum of all the possible families and protocols  |\n   +----------------+-----------------------------------------------------+\n\n  On macOS and AIX this function requires root privileges.\n  To get per-process connections use :meth:`Process.net_connections`.\n  Also, see `scripts/netstat.py`_ example script.\n  Example:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.net_connections()\n     [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status=<ConnectionStatus.CONN_ESTABLISHED: 'ESTABLISHED'>, pid=1254),\n      pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status=<ConnectionStatus.CONN_CLOSING: 'CLOSING'>, pid=2987),\n      pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status=<ConnectionStatus.CONN_ESTABLISHED: 'ESTABLISHED'>, pid=None),\n      pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status=<ConnectionStatus.CONN_SYN_SENT: 'SYN_SENT'>, pid=None)\n      ...]\n\n  .. warning::\n    On Linux, retrieving some connections requires root privileges. If psutil is\n    not run as root, those connections are silently skipped instead of raising\n    :exc:`PermissionError`. That means the returned list may be incomplete.\n\n  .. note::\n    (macOS and AIX) :exc:`psutil.AccessDenied` is always raised unless running\n    as root. This is a limitation of the OS and ``lsof`` does the same.\n\n  .. note::\n    (Solaris) UNIX sockets are not supported.\n\n  .. note::\n     (Linux, FreeBSD, OpenBSD) *raddr* field for UNIX sockets is always set to\n     ``\"\"`` (empty string). This is a limitation of the OS.\n\n  .. versionadded:: 2.1.0\n\n  .. versionchanged:: 5.3.0\n     socket \"fd\" is now set for real instead of being ``-1``.\n\n  .. versionchanged:: 5.3.0\n    *laddr* and *raddr* are named tuples.\n\n  .. versionchanged:: 5.9.5\n     OpenBSD: retrieve *laddr* path for AF_UNIX sockets (before it was an empty\n     string).\n\n  .. versionchanged:: 8.0.0\n     *status* field is now a :class:`psutil.ConnectionStatus` enum member\n     instead of a plain ``str``.\n     See :ref:`migration guide <migration-8.0>`.\n\n.. function:: net_if_addrs()\n\n  Return the addresses associated to each :term:`NIC` (network interface card)\n  installed on the system as a dictionary whose keys are the NIC names and\n  value is a list of named tuples for each address assigned to the NIC.\n  Each named tuple includes 5 fields:\n\n  - **family**: the address family, either :data:`socket.AF_INET` or :data:`socket.AF_INET6`\n    or :const:`psutil.AF_LINK`, which refers to a MAC address.\n  - **address**: the primary NIC address (always set).\n  - **netmask**: the netmask address (may be ``None``).\n  - **broadcast**: the broadcast address (may be ``None``).\n  - **ptp**: stands for \"point to point\"; it's the destination address on a\n    point to point interface (typically a VPN). *broadcast* and *ptp* are\n    mutually exclusive. May be ``None``.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.net_if_addrs()\n     {'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),\n             snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),\n             snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],\n      'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),\n                snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),\n                snicaddr(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}\n     >>>\n\n  See also `scripts/nettop.py`_ and `scripts/ifconfig.py`_ for an example application.\n\n  .. note::\n    if you're interested in others families (e.g. AF_BLUETOOTH) you can use\n    the more powerful `netifaces <https://pypi.org/project/netifaces/>`_\n    extension.\n\n  .. note::\n    you can have more than one address of the same family associated with each\n    interface (that's why dict values are lists).\n\n  .. note::\n    *broadcast* and *ptp* are not supported on Windows and are always ``None``.\n\n  .. versionadded:: 3.0.0\n\n  .. versionchanged:: 3.2.0\n     *ptp* field was added.\n\n  .. versionchanged:: 4.4.0\n     added support for *netmask* field on Windows which is no longer ``None``.\n\n  .. versionchanged:: 7.0.0\n     added support for *broadcast* field on Windows which is no longer ``None``.\n\n.. function:: net_if_stats()\n\n  Return information about each :term:`NIC` (network interface card) installed on the\n  system as a dictionary whose keys are the NIC names and value is a named tuple\n  with the following fields:\n\n  - **isup**: a bool indicating whether the NIC is up and running (meaning\n    ethernet cable or Wi-Fi is connected).\n  - **duplex**: the duplex communication type;\n    it can be either :const:`NIC_DUPLEX_FULL`, :const:`NIC_DUPLEX_HALF` or\n    :const:`NIC_DUPLEX_UNKNOWN`.\n  - **speed**: the NIC speed expressed in megabits (Mbps), if it can't be\n    determined (e.g. 'localhost') it will be set to ``0``.\n  - **mtu**: NIC's maximum transmission unit expressed in bytes.\n  - **flags**: a string of comma-separated flags on the interface (may be an empty string).\n    Possible flags are: ``up``, ``broadcast``, ``debug``, ``loopback``,\n    ``pointopoint``, ``notrailers``, ``running``, ``noarp``, ``promisc``,\n    ``allmulti``, ``master``, ``slave``, ``multicast``, ``portsel``,\n    ``dynamic``, ``oactive``, ``simplex``, ``link0``, ``link1``, ``link2``,\n    and ``d2`` (some flags are only available on certain platforms).\n\n  Also see `scripts/nettop.py`_ and `scripts/ifconfig.py`_ for an example application.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.net_if_stats()\n     {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500, flags='up,broadcast,running,multicast'),\n      'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536, flags='up,loopback,running')}\n\n  .. availability:: UNIX\n\n  .. versionadded:: 3.0.0\n\n  .. versionchanged:: 5.7.3\n     `isup` on UNIX also checks whether the NIC is running.\n\n  .. versionchanged:: 5.9.3\n     *flags* field was added on POSIX.\n\nSensors\n^^^^^^^\n\n.. function:: sensors_temperatures(fahrenheit=False)\n\n  Return hardware temperatures. Each entry is a named tuple representing a\n  certain hardware temperature sensor (it may be a CPU, an hard disk or\n  something else, depending on the OS and its configuration).\n  All temperatures are expressed in celsius unless *fahrenheit* is set to\n  ``True``.\n  If sensors are not supported by the OS an empty dict is returned.\n  Each named tuple includes 4 fields:\n\n  - **label**: a string label for the sensor, if available, else ``\"\"``.\n  - **current**: current temperature, or ``None`` if not available.\n  - **high**: temperature at which the system will throttle, or ``None``\n    if not available.\n  - **critical**: temperature at which the system will shut down, or\n    ``None`` if not available.\n\n  See also `scripts/temperatures.py`_ and `scripts/sensors.py`_ for an example application.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.sensors_temperatures()\n     {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)],\n      'asus': [shwtemp(label='', current=47.0, high=None, critical=None)],\n      'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0),\n                   shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0),\n                   shwtemp(label='Core 1', current=52.0, high=100.0, critical=100.0),\n                   shwtemp(label='Core 2', current=45.0, high=100.0, critical=100.0),\n                   shwtemp(label='Core 3', current=47.0, high=100.0, critical=100.0)]}\n\n  .. availability:: Linux, FreeBSD\n\n  .. versionadded:: 5.1.0\n\n  .. versionchanged:: 5.5.0\n     added FreeBSD support.\n\n.. function:: sensors_fans()\n\n  Return hardware fans speed. Each entry is a named tuple representing a\n  certain hardware sensor fan.\n  Fan speed is expressed in RPM (revolutions per minute).\n  If sensors are not supported by the OS an empty dict is returned.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.sensors_fans()\n     {'asus': [sfan(label='cpu_fan', current=3200)]}\n\n  See also `scripts/fans.py`_  and `scripts/sensors.py`_ for an example application.\n\n  .. availability:: Linux\n\n  .. versionadded:: 5.2.0\n\n.. function:: sensors_battery()\n\n  Return battery status information as a named tuple including the following\n  values. If no battery is installed or metrics can't be determined ``None``\n  is returned.\n\n  - **percent**: battery power left as a percentage.\n  - **secsleft**: a rough approximation of how many seconds are left before the\n    battery runs out of power.\n    If the AC power cable is connected this is set to\n    :data:`psutil.POWER_TIME_UNLIMITED <psutil.POWER_TIME_UNLIMITED>`.\n    If it can't be determined it is set to\n    :data:`psutil.POWER_TIME_UNKNOWN <psutil.POWER_TIME_UNKNOWN>`.\n  - **power_plugged**: ``True`` if the AC power cable is connected, ``False``\n    if not or ``None`` if it can't be determined.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>>\n     >>> def secs2hours(secs):\n     ...     mm, ss = divmod(secs, 60)\n     ...     hh, mm = divmod(mm, 60)\n     ...     return \"%d:%02d:%02d\" % (hh, mm, ss)\n     ...\n     >>> battery = psutil.sensors_battery()\n     >>> battery\n     sbattery(percent=93, secsleft=16628, power_plugged=False)\n     >>> print(\"charge = %s%%, time left = %s\" % (battery.percent, secs2hours(battery.secsleft)))\n     charge = 93%, time left = 4:37:08\n\n  See also `scripts/battery.py`_  and `scripts/sensors.py`_ for an example application.\n\n  .. availability:: Linux, Windows, macOS, FreeBSD\n\n  .. versionadded:: 5.1.0\n\n  .. versionchanged:: 5.4.2\n     added macOS support.\n\n----\n\nOther system info\n^^^^^^^^^^^^^^^^^\n\n.. function:: boot_time()\n\n  Return the system boot time expressed in seconds since the epoch (seconds\n  since January 1, 1970, at midnight UTC). The returned value is based on the\n  system clock, which means it may be affected by changes such as manual\n  adjustments or time synchronization (e.g. NTP).\n\n  .. code-block:: python\n\n     >>> import psutil, datetime\n     >>> psutil.boot_time()\n     1389563460.0\n     >>> datetime.datetime.fromtimestamp(psutil.boot_time()).strftime(\"%Y-%m-%d %H:%M:%S\")\n     '2014-01-12 22:51:00'\n\n  .. note::\n    on Windows this function may return a time which is off by 1 second if it's\n    used across different processes (see issue :gh:`1007`).\n\n.. function:: users()\n\n  Return users currently connected on the system as a list of named tuples\n  including the following fields:\n\n  - **name**: the name of the user.\n  - **terminal**: the tty or pseudo-tty associated with the user, if any,\n    else ``None``.\n  - **host**: the host name associated with the entry, if any, else ``None``.\n  - **started**: the creation time as a floating point number expressed in\n    seconds since the epoch.\n  - **pid**: the PID of the login process (like sshd, tmux, gdm-session-worker,\n    ...). On Windows and OpenBSD this is always set to ``None``.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.users()\n     [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),\n      suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]\n\n  .. versionchanged:: 5.3.0\n     added \"pid\" field.\n\n----\n\nProcesses\n---------\n\nFunctions\n^^^^^^^^^\n\n.. function:: pids()\n\n  Return a sorted list of current running PIDs.\n  To iterate over all processes and avoid race conditions :func:`process_iter`\n  should be preferred.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> psutil.pids()\n     [1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, ..., 32498]\n\n\n  .. versionchanged:: 5.6.0\n     PIDs are returned in sorted order.\n\n.. function:: process_iter(attrs=None, ad_value=None)\n\n  Return an iterator yielding a :class:`Process` class instance for all running\n  processes on the local machine.\n  This should be preferred over :func:`psutil.pids` to iterate over\n  processes, as retrieving info is safe from race conditions.\n\n  Every :class:`Process` instance is only created once, and then cached for the\n  next time :func:`psutil.process_iter` is called (if PID is still alive).\n  Cache can optionally be cleared via ``process_iter.cache_clear()``.\n\n  *attrs* and *ad_value* have the same meaning as in :meth:`Process.as_dict`.\n  If *attrs* is specified :meth:`Process.as_dict` result will be stored as a\n  ``info`` attribute attached to the returned :class:`Process` instances.\n  If *attrs* is an empty list it will retrieve all process info (slow).\n\n  Sorting order in which processes are returned is based on their PID.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> for proc in psutil.process_iter(['pid', 'name', 'username']):\n     ...     print(proc.info)\n     ...\n     {'name': 'systemd', 'pid': 1, 'username': 'root'}\n     {'name': 'kthreadd', 'pid': 2, 'username': 'root'}\n     {'name': 'ksoftirqd/0', 'pid': 3, 'username': 'root'}\n     ...\n\n  A dict comprehensions to create a ``{pid: info, ...}`` data structure:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> procs = {p.pid: p.info for p in psutil.process_iter(['name', 'username'])}\n     >>> procs\n     {1: {'name': 'systemd', 'username': 'root'},\n      2: {'name': 'kthreadd', 'username': 'root'},\n      3: {'name': 'ksoftirqd/0', 'username': 'root'},\n      ...}\n\n  Clear internal cache:\n\n  .. code-block:: pycon\n\n     >>> psutil.process_iter.cache_clear()\n\n  .. versionchanged:: 5.3.0\n     added \"attrs\" and \"ad_value\" parameters.\n\n  .. versionchanged:: 6.0.0\n     no longer checks whether each yielded process PID has been reused.\n\n  .. versionchanged:: 6.0.0\n     added ``psutil.process_iter.cache_clear()`` API.\n\n.. function:: pid_exists(pid)\n\n  Check whether the given PID exists in the current process list. This is\n  faster than doing ``pid in psutil.pids()`` and should be preferred.\n\n.. function:: wait_procs(procs, timeout=None, callback=None)\n\n  Convenience function which waits for a list of :class:`Process` instances to\n  terminate. Return a ``(gone, alive)`` tuple indicating which processes are\n  gone and which ones are still alive. The *gone* ones will have a new\n  *returncode* attribute indicating process exit status as returned by\n  :meth:`Process.wait`.\n  ``callback`` is a function which gets called when one of the processes being\n  waited on is terminated and a :class:`Process` instance is passed as callback\n  argument (the instance will also have a *returncode* attribute set).\n  This function will return as soon as all processes terminate or when\n  *timeout* (seconds) occurs.\n  Differently from :meth:`Process.wait` it will not raise\n  :exc:`TimeoutExpired` if timeout occurs.\n  A typical use case may be:\n\n  - send SIGTERM to a list of processes\n  - give them some time to terminate\n  - send SIGKILL to those ones which are still alive\n\n  Example which terminates and waits all the children of this process::\n\n    import psutil\n\n    def on_terminate(proc):\n        print(\"process {} terminated with exit code {}\".format(proc, proc.returncode))\n\n    procs = psutil.Process().children()\n    for p in procs:\n        p.terminate()\n    gone, alive = psutil.wait_procs(procs, timeout=3, callback=on_terminate)\n    for p in alive:\n        p.kill()\n\nExceptions\n^^^^^^^^^^\n\n.. exception:: Error()\n\n  Base exception class. All other exceptions inherit from this one.\n\n.. exception:: NoSuchProcess(pid, name=None, msg=None)\n\n  Raised by :class:`Process` class methods when no process with the given\n  *pid* is found in the current process list, or when a process no longer\n  exists. *name* is the name the process had before disappearing\n  and gets set only if :meth:`Process.name` was previously called.\n\n  See also :ref:`faq_no_such_process` FAQ.\n\n.. exception:: ZombieProcess(pid, name=None, ppid=None, msg=None)\n\n  This may be raised by :class:`Process` class methods when querying a\n  :term:`zombie process` on UNIX (Windows doesn't have zombie processes).\n  *name* and *ppid* attributes are available if :meth:`Process.name` or\n  :meth:`Process.ppid` methods were called before the process turned into a\n  zombie.\n\n  See also :ref:`faq_zombie_process` FAQ.\n\n  .. note::\n\n    this is a subclass of :exc:`NoSuchProcess` so if you're not interested\n    in retrieving zombies (e.g. when using :func:`process_iter`) you can\n    ignore this exception and just catch :exc:`NoSuchProcess`.\n\n  .. versionadded:: 3.0.0\n\n.. exception:: AccessDenied(pid=None, name=None, msg=None)\n\n  Raised by :class:`Process` class methods when permission to perform an\n  action is denied due to insufficient privileges.\n  *name* attribute is available if :meth:`Process.name` was previously called.\n\n  See also :ref:`faq_access_denied` FAQ.\n\n.. exception:: TimeoutExpired(seconds, pid=None, name=None, msg=None)\n\n  Raised by :meth:`Process.wait` method if timeout expires and the process is\n  still alive.\n  *name* attribute is available if :meth:`Process.name` was previously called.\n\nProcess class\n^^^^^^^^^^^^^\n\n.. class:: Process(pid=None)\n\n  Represents an OS process with the given *pid*.\n  If *pid* is omitted current process *pid* (:func:`os.getpid`) is used.\n  Raise :exc:`NoSuchProcess` if *pid* does not exist.\n  On Linux *pid* can also refer to a thread ID (the *id* field returned by\n  :meth:`threads` method).\n  When calling methods of this class, always be prepared to catch\n  :exc:`NoSuchProcess` and :exc:`AccessDenied` exceptions.\n  :func:`hash` builtin can be used against instances of this class in order to\n  identify a process univocally over time (the hash is determined by mixing\n  process PID + creation time). As such it can also be used with :class:`set`.\n\n  .. note::\n\n    In order to efficiently fetch more than one information about the process\n    at the same time, make sure to use either :meth:`oneshot` context manager\n    or :meth:`as_dict` utility method.\n\n  .. note::\n\n    the way this class is bound to a process is via its **PID**.\n    That means that if the process terminates and the OS reuses its PID you may\n    inadvertently end up interacting with another process. To prevent this\n    problem you can use :meth:`is_running` first.\n    Some methods (e.g. setters and signal-related methods) perform an\n    additional check based on PID + creation time and will raise\n    :exc:`NoSuchProcess` if the PID has been reused. See :ref:`faq_pid_reuse`\n    FAQ for details.\n\n  .. method:: oneshot()\n\n    Utility context manager which considerably speeds up the retrieval of\n    multiple process information at the same time.\n    Internally different process info (e.g. :meth:`name`, :meth:`ppid`,\n    :meth:`uids`, :meth:`create_time`, ...) may be fetched by using the same\n    routine, but only one value is returned and the others are discarded.\n    When using this context manager the internal routine is executed once (in\n    the example below on :meth:`name`) the value of interest is returned and\n    the others are cached.\n    The subsequent calls sharing the same internal routine will return the\n    cached value.\n    The cache is cleared when exiting the context manager block.\n    The advice is to use this every time you retrieve more than one information\n    about the process. If you're lucky, you'll get a hell of a speedup.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> with p.oneshot():\n       ...     p.name()  # execute internal routine once collecting multiple info\n       ...     p.cpu_times()  # return cached value\n       ...     p.cpu_percent()  # return cached value\n       ...     p.create_time()  # return cached value\n       ...     p.ppid()  # return cached value\n       ...     p.status()  # return cached value\n       ...\n       >>>\n\n    Here's a list of methods which can take advantage of the speedup depending\n    on what platform you're on.\n    In the table below horizontal empty rows indicate what process methods can\n    be efficiently grouped together internally.\n    The last column (speedup) shows an approximation of the speedup you can get\n    if you call all the methods together (best case scenario).\n\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | Linux                        | Windows                       | macOS                        | BSD                          | SunOS                    | AIX                      |\n    +==============================+===============================+==============================+==============================+==========================+==========================+\n    | :meth:`cpu_num`              | :meth:`~Process.cpu_percent`  | :meth:`~Process.cpu_percent` | :meth:`cpu_num`              | :meth:`name`             | :meth:`name`             |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`~Process.cpu_percent` | :meth:`cpu_times`             | :meth:`cpu_times`            | :meth:`~Process.cpu_percent` | :meth:`cmdline`          | :meth:`cmdline`          |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`cpu_times`            | :meth:`io_counters`           | :meth:`memory_info`          | :meth:`cpu_times`            | :meth:`create_time`      | :meth:`create_time`      |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`create_time`          | :meth:`memory_info`           | :meth:`memory_percent`       | :meth:`create_time`          |                          |                          |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`name`                 | :meth:`memory_info_ex`        | :meth:`num_ctx_switches`     | :meth:`gids`                 | :meth:`memory_info`      | :meth:`memory_info`      |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`ppid`                 | :meth:`num_ctx_switches`      | :meth:`num_threads`          | :meth:`io_counters`          | :meth:`memory_percent`   | :meth:`memory_percent`   |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`status`               | :meth:`num_handles`           |                              | :meth:`name`                 | :meth:`num_threads`      | :meth:`num_threads`      |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`terminal`             | :meth:`num_threads`           | :meth:`create_time`          | :meth:`memory_info`          | :meth:`ppid`             | :meth:`ppid`             |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    |                              |                               | :meth:`gids`                 | :meth:`memory_percent`       | :meth:`status`           | :meth:`status`           |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`gids`                 | :meth:`exe`                   | :meth:`name`                 | :meth:`num_ctx_switches`     | :meth:`terminal`         | :meth:`terminal`         |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`memory_info_ex`       | :meth:`name`                  | :meth:`ppid`                 | :meth:`ppid`                 |                          |                          |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`num_ctx_switches`     |                               | :meth:`status`               | :meth:`status`               | :meth:`gids`             | :meth:`gids`             |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`num_threads`          |                               | :meth:`terminal`             | :meth:`terminal`             | :meth:`uids`             | :meth:`uids`             |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`uids`                 |                               | :meth:`terminal`             | :meth:`terminal`             | :meth:`uids`             | :meth:`uids`             |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`username`             |                               | :meth:`uids`                 | :meth:`uids`                 | :meth:`username`         | :meth:`username`         |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    |                              |                               | :meth:`username`             | :meth:`username`             |                          |                          |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`memory_footprint`     |                               |                              |                              |                          |                          |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | :meth:`memory_maps`          |                               |                              |                              |                          |                          |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n    | *speedup: +2.6x*             | *speedup: +1.8x / +6.5x*      | *speedup: +1.9x*             | *speedup: +2.0x*             | *speedup: +1.3x*         | *speedup: +1.3x*         |\n    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+\n\n    .. versionadded:: 5.0.0\n\n  .. attribute:: pid\n\n     The process PID. This is the only (read-only) attribute of the class.\n\n  .. method:: ppid()\n\n    The process parent PID. On Windows the return value is cached after the\n    first call. On POSIX it is not cached because the ppid may change if the\n    process becomes a :term:`zombie process`.\n    See also :meth:`parent` and :meth:`parents` methods.\n\n  .. method:: name()\n\n    The process name.  On Windows the return value is cached after first\n    call. Not on POSIX because the process name may change.\n    See also how to `find a process by name <#find-process-by-name>`_.\n\n  .. method:: exe()\n\n    The process executable as an absolute path. On some systems, if exe cannot\n    be determined for some internal reason (e.g. system process or path no\n    longer exists), this may be an empty string. The return value is cached\n    after first call.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> psutil.Process().exe()\n       '/usr/bin/python3'\n\n  .. method:: cmdline()\n\n    The command line used to start this process, as a list of strings.\n    The return value is not cached because the cmdline of a process may change.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> psutil.Process().cmdline()\n       ['python', 'manage.py', 'runserver']\n\n  .. method:: environ()\n\n    The environment variables of the process as a dict.  Note: this might not\n    reflect changes made after the process started.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> psutil.Process().environ()\n       {'LC_NUMERIC': 'it_IT.UTF-8', 'QT_QPA_PLATFORMTHEME': 'appmenu-qt5', 'IM_CONFIG_PHASE': '1', 'XDG_GREETER_DATA_DIR': '/var/lib/lightdm-data/giampaolo', 'XDG_CURRENT_DESKTOP': 'Unity', 'UPSTART_EVENTS': 'started starting', 'GNOME_KEYRING_PID': '', 'XDG_VTNR': '7', 'QT_IM_MODULE': 'ibus', 'LOGNAME': 'giampaolo', 'USER': 'giampaolo', 'PATH': '/home/giampaolo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/giampaolo/svn/sysconf/bin', 'LC_PAPER': 'it_IT.UTF-8', 'GNOME_KEYRING_CONTROL': '', 'GTK_IM_MODULE': 'ibus', 'DISPLAY': ':0', 'LANG': 'en_US.UTF-8', 'LESS_TERMCAP_se': '\\x1b[0m', 'TERM': 'xterm-256color', 'SHELL': '/bin/bash', 'XDG_SESSION_PATH': '/org/freedesktop/DisplayManager/Session0', 'XAUTHORITY': '/home/giampaolo/.Xauthority', 'LANGUAGE': 'en_US', 'COMPIZ_CONFIG_PROFILE': 'ubuntu', 'LC_MONETARY': 'it_IT.UTF-8', 'QT_LINUX_ACCESSIBILITY_ALWAYS_ON': '1', 'LESS_TERMCAP_me': '\\x1b[0m', 'LESS_TERMCAP_md': '\\x1b[01;38;5;74m', 'LESS_TERMCAP_mb': '\\x1b[01;31m', 'HISTSIZE': '100000', 'UPSTART_INSTANCE': '', 'CLUTTER_IM_MODULE': 'xim', 'WINDOWID': '58786407', 'EDITOR': 'vim', 'SESSIONTYPE': 'gnome-session', 'XMODIFIERS': '@im=ibus', 'GPG_AGENT_INFO': '/home/giampaolo/.gnupg/S.gpg-agent:0:1', 'HOME': '/home/giampaolo', 'HISTFILESIZE': '100000', 'QT4_IM_MODULE': 'xim', 'GTK2_MODULES': 'overlay-scrollbar', 'XDG_SESSION_DESKTOP': 'ubuntu', 'SHLVL': '1', 'XDG_RUNTIME_DIR': '/run/user/1000', 'INSTANCE': 'Unity', 'LC_ADDRESS': 'it_IT.UTF-8', 'SSH_AUTH_SOCK': '/run/user/1000/keyring/ssh', 'VTE_VERSION': '4205', 'GDMSESSION': 'ubuntu', 'MANDATORY_PATH': '/usr/share/gconf/ubuntu.mandatory.path', 'VISUAL': 'vim', 'DESKTOP_SESSION': 'ubuntu', 'QT_ACCESSIBILITY': '1', 'XDG_SEAT_PATH': '/org/freedesktop/DisplayManager/Seat0', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'XDG_SESSION_ID': 'c2', 'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-9GAJpvnt8r', '_': '/usr/bin/python', 'DEFAULTS_PATH': '/usr/share/gconf/ubuntu.default.path', 'LC_IDENTIFICATION': 'it_IT.UTF-8', 'LESS_TERMCAP_ue': '\\x1b[0m', 'UPSTART_SESSION': 'unix:abstract=/com/ubuntu/upstart-session/1000/1294', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', 'GTK_MODULES': 'gail:atk-bridge:unity-gtk-module', 'XDG_SESSION_TYPE': 'x11', 'PYTHONSTARTUP': '/home/giampaolo/.pythonstart', 'LC_NAME': 'it_IT.UTF-8', 'OLDPWD': '/home/giampaolo/svn/curio_giampaolo/tests', 'GDM_LANG': 'en_US', 'LC_TELEPHONE': 'it_IT.UTF-8', 'HISTCONTROL': 'ignoredups:erasedups', 'LC_MEASUREMENT': 'it_IT.UTF-8', 'PWD': '/home/giampaolo/svn/curio_giampaolo', 'JOB': 'gnome-session', 'LESS_TERMCAP_us': '\\x1b[04;38;5;146m', 'UPSTART_JOB': 'unity-settings-daemon', 'LC_TIME': 'it_IT.UTF-8', 'LESS_TERMCAP_so': '\\x1b[38;5;246m', 'PAGER': 'less', 'XDG_DATA_DIRS': '/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'XDG_SEAT': 'seat0'}\n\n\n    .. note::\n      on macOS Big Sur this function returns something meaningful only for the\n      current process or in\n      `other specific circumstances <https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/kern_sysctl.c#L1315-L1321>`_.\n\n    .. versionadded:: 4.0.0\n\n    .. versionchanged:: 5.3.0\n       added SunOS support.\n\n    .. versionchanged:: 5.6.3\n       added AIX support.\n\n    .. versionchanged:: 5.7.3\n       added BSD support.\n\n  .. method:: create_time()\n\n    The process creation time as a floating point number expressed in seconds\n    since the epoch (seconds since January 1, 1970, at midnight UTC). The\n    return value, which is cached after first call, is based on the system\n    clock, which means it may be affected by changes such as manual adjustments\n    or time synchronization (e.g. NTP).\n\n    .. code-block:: pycon\n\n       >>> import psutil, datetime\n       >>> p = psutil.Process()\n       >>> p.create_time()\n       1307289803.47\n       >>> datetime.datetime.fromtimestamp(p.create_time()).strftime(\"%Y-%m-%d %H:%M:%S\")\n       '2011-03-05 18:03:52'\n\n  .. method:: as_dict(attrs=None, ad_value=None)\n\n    Utility method retrieving multiple process information as a dictionary.\n    If *attrs* is specified it must be a list of strings reflecting available\n    :class:`Process` class's attribute names. Here's a list of possible string\n    values:\n    ``'cmdline'``, ``'net_connections'``, ``'cpu_affinity'``, ``'cpu_num'``, ``'cpu_percent'``, ``'cpu_times'``, ``'create_time'``, ``'cwd'``, ``'environ'``, ``'exe'``, ``'gids'``, ``'io_counters'``, ``'ionice'``, ``'memory_footprint'``, ``'memory_full_info'``, ``'memory_info'``, ``'memory_info_ex'``, ``'memory_maps'``, ``'memory_percent'``, ``'name'``, ``'nice'``, ``'num_ctx_switches'``, ``'num_fds'``, ``'num_handles'``, ``'num_threads'``, ``'open_files'``, ``'pid'``, ``'ppid'``, ``'status'``, ``'terminal'``, ``'threads'``, ``'uids'``, ``'username'```.\n    If *attrs* argument is not passed all public read only attributes are\n    assumed.\n    *ad_value* is the value which gets assigned to a dict key in case\n    :exc:`AccessDenied` or :exc:`ZombieProcess` exception is raised when\n    retrieving that particular process information.\n    Internally, :meth:`as_dict` uses :meth:`oneshot` context manager so\n    there's no need you use it also.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.as_dict(attrs=['pid', 'name', 'username'])\n       {'username': 'giampaolo', 'pid': 12366, 'name': 'python'}\n       >>>\n       >>> # get a list of valid attrs names\n       >>> list(psutil.Process().as_dict().keys())\n       ['cmdline', 'connections', 'cpu_affinity', 'cpu_num', 'cpu_percent', 'cpu_times', 'create_time', 'cwd', 'environ', 'exe', 'gids', 'io_counters', 'ionice', 'memory_footprint', 'memory_full_info', 'memory_info', 'memory_info_ex', 'memory_maps', 'memory_percent', 'name', 'net_connections', 'nice', 'num_ctx_switches', 'num_fds', 'num_threads', 'open_files', 'pid', 'ppid', 'status', 'terminal', 'threads', 'uids', 'username']\n\n    .. versionchanged:: 3.0.0\n       *ad_value* is used also when incurring into :exc:`ZombieProcess`\n       exception, not only :exc:`AccessDenied`.\n\n    .. versionchanged:: 4.5.0\n       :meth:`as_dict` is considerably faster thanks to :meth:`oneshot`\n       context manager.\n\n  .. method:: parent()\n\n    Utility method which returns the parent process as a :class:`Process`\n    object, preemptively checking whether PID has been reused. If no parent\n    PID is known return ``None``.\n    See also :meth:`ppid` and :meth:`parents` methods.\n\n  .. method:: parents()\n\n    Utility method which returns the parents of this process as a list of\n    :class:`Process` instances. If no parents are known return an empty list.\n    See also :meth:`ppid` and :meth:`parent` methods.\n\n    .. versionadded:: 5.6.0\n\n  .. method:: status()\n\n    The current process status as a :class:`psutil.ProcessStatus` enum member.\n    The returned value is one of the\n    `psutil.STATUS_* <#process-status-constants>`_ constants.\n    A common use case is detecting :term:`zombie processes <zombie process>`\n    (``p.status() == psutil.STATUS_ZOMBIE``).\n\n    .. versionchanged:: 8.0.0\n       return value is now a :class:`psutil.ProcessStatus` enum member instead\n       of a plain ``str``.\n       See :ref:`migration guide <migration-8.0>`.\n\n  .. method:: cwd()\n\n    The process current working directory as an absolute path. If cwd cannot be\n    determined for some internal reason (e.g. system process or directory no\n    longer exists) it may return an empty string.\n\n    .. versionchanged:: 5.6.4\n       added support for NetBSD.\n\n  .. method:: username()\n\n    The name of the user that owns the process. On UNIX this is calculated by\n    using real process uid.\n\n  .. method:: uids()\n\n    The real, effective and saved user ids of this process as a named tuple.\n    This is the same as :func:`os.getresuid` but can be used for any process PID.\n\n    .. availability:: UNIX\n\n  .. method:: gids()\n\n    The real, effective and saved group ids of this process as a named tuple.\n    This is the same as :func:`os.getresgid` but can be used for any process PID.\n\n    .. availability:: UNIX\n\n  .. method:: terminal()\n\n    The terminal associated with this process, if any, else ``None``. This is\n    similar to \"tty\" command but can be used for any process PID.\n\n    .. availability:: UNIX\n\n  .. method:: nice(value=None)\n\n    Get or set process niceness (priority).\n    On UNIX this is a number which usually goes from ``-20`` to ``20``.\n    The higher the nice value, the lower the priority of the process.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.nice(10)  # set\n       >>> p.nice()  # get\n       10\n       >>>\n\n    Starting from Python 3.3 this functionality is also available as\n    :func:`os.getpriority` and :func:`os.setpriority` (see `BPO-10784`_).\n    On Windows this is implemented via `GetPriorityClass`_ and\n    `SetPriorityClass`_ Windows APIs and *value* is one of the\n    :data:`psutil.*_PRIORITY_CLASS <psutil.ABOVE_NORMAL_PRIORITY_CLASS>`\n    constants reflecting the MSDN documentation.\n    The return value on Windows is a :class:`psutil.ProcessPriority` enum member.\n    Example which increases process priority on Windows:\n\n    .. code-block:: pycon\n\n       >>> p.nice(psutil.HIGH_PRIORITY_CLASS)\n\n    .. versionchanged:: 8.0.0\n       on Windows, return value is now a :class:`psutil.ProcessPriority` enum\n       member.\n       See :ref:`migration guide <migration-8.0>`.\n\n  .. method:: ionice(ioclass=None, value=None)\n\n    Get or set process I/O niceness (priority).\n    If no argument is provided it acts as a get, returning a ``(ioclass, value)``\n    tuple on Linux and a *ioclass* integer on Windows.\n    If *ioclass* is provided it acts as a set. In this case an additional\n    *value* can be specified on Linux only in order to increase or decrease the\n    I/O priority even further.\n    Here's the possible platform-dependent *ioclass* values.\n\n    Linux (see `ioprio_get`_ manual):\n\n    * :const:`IOPRIO_CLASS_RT`: (high) the process gets first access to the disk\n      every time. Use it with care as it can starve the entire\n      system. Additional priority *level* can be specified and ranges from\n      ``0`` (highest) to ``7`` (lowest).\n    * :const:`IOPRIO_CLASS_BE`: (normal) the default for any process that hasn't set\n      a specific I/O priority. Additional priority *level* ranges from\n      ``0`` (highest) to ``7`` (lowest).\n    * :const:`IOPRIO_CLASS_IDLE`: (low) get I/O time when no-one else needs the disk.\n      No additional *value* is accepted.\n    * :const:`IOPRIO_CLASS_NONE`: returned when no priority was previously set.\n\n    Windows:\n\n    * :const:`IOPRIO_HIGH`: highest priority.\n    * :const:`IOPRIO_NORMAL`: default priority.\n    * :const:`IOPRIO_LOW`: low priority.\n    * :const:`IOPRIO_VERYLOW`: lowest priority.\n\n    Here's an example on how to set the highest I/O priority depending on what\n    platform you're on:\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> if psutil.LINUX:\n       ...     p.ionice(psutil.IOPRIO_CLASS_RT, value=7)\n       ... else:\n       ...     p.ionice(psutil.IOPRIO_HIGH)\n       ...\n       >>> p.ionice()  # get\n       pionice(ioclass=<ProcessIOPriority.IOPRIO_CLASS_RT: 1>, value=7)\n\n    .. availability:: Linux, Windows\n\n    .. versionchanged:: 5.6.2\n       Windows accepts new :data:`IOPRIO_* <psutil.IOPRIO_VERYLOW>` constants.\n\n    .. versionchanged:: 8.0.0\n       *ioclass* is now a :class:`psutil.ProcessIOPriority` enum member.\n       See :ref:`migration guide <migration-8.0>`.\n\n  .. method:: rlimit(resource, limits=None)\n\n    Get or set process :term:`resource limits <resource limit>` (see `man prlimit`_).\n    *resource* is one of the :data:`psutil.RLIMIT_* <psutil.RLIM_INFINITY>`\n    constants.\n    *limits* is a ``(soft, hard)`` tuple.\n    This is the same as :func:`resource.getrlimit` and :func:`resource.setrlimit`\n    but can be used for any process PID, not only :func:`os.getpid`.\n    For get, return value is a ``(soft, hard)`` tuple. Each value may be either\n    an integer or :data:`psutil.RLIMIT_* <psutil.RLIM_INFINITY>`.\n    Also see `scripts/procinfo.py`_ script.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.rlimit(psutil.RLIMIT_NOFILE, (128, 128))   # process can open max 128 file descriptors\n       >>> p.rlimit(psutil.RLIMIT_FSIZE, (1024, 1024))  # can create files no bigger than 1024 bytes\n       >>> p.rlimit(psutil.RLIMIT_FSIZE)                # get\n       (1024, 1024)\n       >>>\n\n    .. availability:: Linux, FreeBSD\n\n    .. versionchanged:: 5.7.3\n       added FreeBSD support.\n\n  .. method:: io_counters()\n\n    Return process I/O statistics as a named tuple.\n    For Linux you can refer to\n    `/proc filesystem documentation <https://stackoverflow.com/questions/3633286/>`_.\n\n    All fields are :term:`cumulative counters <cumulative counter>` since process creation.\n\n    - **read_count**: the number of read operations performed.\n      This is supposed to count the number of read-related syscalls such as\n      ``read()`` and ``pread()`` on UNIX.\n    - **write_count**: the number of write operations performed.\n      This is supposed to count the number of write-related syscalls such as\n      ``write()`` and ``pwrite()`` on UNIX.\n    - **read_bytes**: the number of bytes read. Always ``-1`` on BSD.\n    - **write_bytes**: the number of bytes written. Always ``-1`` on BSD.\n\n    Linux specific:\n\n    - **read_chars** *(Linux)*: the amount of bytes which this process passed\n      to ``read()`` and ``pread()`` syscalls. Differently from *read_bytes*\n      it doesn't care whether or not actual physical disk I/O occurred.\n    - **write_chars** *(Linux)*: the amount of bytes which this process passed\n      to ``write()`` and ``pwrite()`` syscalls. Differently from *write_bytes*\n      it doesn't care whether or not actual physical disk I/O occurred.\n\n    Windows specific:\n\n    - **other_count** *(Windows)*: the number of I/O operations performed\n      other than read and write operations.\n    - **other_bytes** *(Windows)*: the number of bytes transferred during\n      operations other than read and write operations.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.io_counters()\n       pio(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0, read_chars=769931, write_chars=203)\n\n\n    .. availability:: Linux, BSD, Windows, AIX\n\n    .. versionchanged:: 5.2.0\n       added *read_chars* + *write_chars* on Linux and *other_count* +\n       *other_bytes* on Windows.\n\n  .. method:: num_ctx_switches()\n\n    The number of :term:`context switches <context switch>` performed by this process\n    (:term:`cumulative counter`).\n\n    .. note::\n      (Windows, macOS) *involuntary* value is always set to 0, while\n      *voluntary* value reflect the total number of context switches (voluntary\n      + involuntary). This is a limitation of the OS.\n\n    .. versionchanged:: 5.4.1\n       added AIX support.\n\n  .. method:: num_fds()\n\n    The number of :term:`file descriptors <file descriptor>` currently opened by this process\n    (non cumulative).\n\n    .. availability:: UNIX\n\n  .. method:: num_handles()\n\n    The number of :term:`handles <handle>` currently used by this process (non cumulative).\n\n    .. availability:: Windows\n\n  .. method:: num_threads()\n\n    The number of threads currently used by this process (non cumulative).\n\n  .. method:: threads()\n\n    Return threads opened by process as a list of named tuples. On OpenBSD this\n    method requires root privileges.\n\n    - **id**: the native thread ID assigned by the kernel. If :attr:`pid` refers\n      to the current process, this matches the\n      `native_id <https://docs.python.org/3/library/threading.html#threading.Thread.native_id>`_\n      attribute of the :class:`threading.Thread` class, and can be used to reference\n      individual Python threads running within your own Python app.\n    - **user_time**: time spent in user mode.\n    - **system_time**: time spent in kernel mode.\n\n  .. method:: cpu_times()\n\n    Return a named tuple of :term:`cumulative counters <cumulative counter>` (seconds)\n    representing the accumulated process CPU times\n    (see `explanation <http://stackoverflow.com/questions/556405/>`_).\n    This is similar to :func:`os.times` but can be used for any process PID.\n\n    - **user**: time spent in user mode.\n    - **system**: time spent in kernel mode.\n    - **children_user**: user time of all child processes (always ``0`` on\n      Windows and macOS).\n    - **children_system**: system time of all child processes (always ``0`` on\n      Windows and macOS).\n    - **iowait**: (Linux) time spent waiting for blocking I/O to complete (:term:`iowait`).\n      This value is excluded from `user` and `system` times count (because the\n      CPU is not doing any work).\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.cpu_times()\n       pcputimes(user=0.03, system=0.67, children_user=0.0, children_system=0.0, iowait=0.08)\n       >>> sum(p.cpu_times()[:2])  # cumulative, excluding children and iowait\n       0.70\n\n\n    .. versionchanged:: 4.1.0\n       return two extra fields: *children_user* and *children_system*.\n\n    .. versionchanged:: 5.6.4\n       added *iowait* on Linux.\n\n  .. method:: cpu_percent(interval=None)\n\n    Return a float representing the process CPU utilization as a percentage\n    which can also be ``> 100.0`` in case of a process running multiple threads\n    on different CPUs.\n    When *interval* is > ``0.0`` compares process times to system CPU times\n    elapsed before and after the interval (blocking). When interval is ``0.0``\n    or ``None`` compares process times to system CPU times elapsed since last\n    call, returning immediately. That means the first time this is called it\n    will return a meaningless ``0.0`` value which you are supposed to ignore.\n    For accuracy, it is recommended to call this function a second time with\n    at least ``0.1`` seconds between calls.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> # blocking\n       >>> p.cpu_percent(interval=1)\n       2.0\n       >>> # non-blocking (percentage since last call)\n       >>> p.cpu_percent(interval=None)\n       2.9\n\n    .. note::\n      the first time this method is called with interval = ``0.0`` or\n      ``None`` it will return a meaningless ``0.0`` value which you are\n      supposed to ignore. See also :ref:`faq_cpu_percent` FAQ.\n\n    .. note::\n      the returned value can be > 100.0 in case of a process running multiple\n      threads on different CPU cores.\n\n    .. note::\n      the returned value is explicitly *not* split evenly between all available\n      CPUs (differently from :func:`psutil.cpu_percent`).\n      This means that a busy loop process running on a system with 2 logical\n      CPUs will be reported as having 100% CPU utilization instead of 50%.\n      This was done in order to be consistent with ``top`` UNIX utility,\n      and also to make it easier to identify processes hogging CPU resources\n      independently from the number of CPUs.\n      It must be noted that ``taskmgr.exe`` on Windows does not behave like\n      this (it would report 50% usage instead).\n      To emulate Windows ``taskmgr.exe`` behavior you can do:\n      ``p.cpu_percent() / psutil.cpu_count()``.\n\n  .. method:: cpu_affinity(cpus=None)\n\n    Get or set process current\n    `CPU affinity <http://www.linuxjournal.com/article/6799?page=0,0>`_.\n    CPU affinity consists in telling the OS to run a process on a limited set\n    of CPUs only (on Linux cmdline, ``taskset`` command is typically used).\n    If no argument is passed it returns the current CPU affinity as a list\n    of integers.\n    If passed it must be a list of integers specifying the new CPUs affinity.\n    If an empty list is passed all eligible CPUs are assumed (and set).\n    On some systems such as Linux this may not necessarily mean all available\n    logical CPUs as in ``list(range(psutil.cpu_count()))``).\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> psutil.cpu_count()\n       4\n       >>> p = psutil.Process()\n       >>> # get\n       >>> p.cpu_affinity()\n       [0, 1, 2, 3]\n       >>> # set; from now on, process will run on CPU #0 and #1 only\n       >>> p.cpu_affinity([0, 1])\n       >>> p.cpu_affinity()\n       [0, 1]\n       >>> # reset affinity against all eligible CPUs\n       >>> p.cpu_affinity([])\n\n    .. availability:: Linux, Windows, FreeBSD\n\n    .. versionchanged:: 2.2.0\n       added support for FreeBSD.\n\n    .. versionchanged:: 5.1.0\n       an empty list can be passed to set affinity against all eligible CPUs.\n\n  .. method:: cpu_num()\n\n    Return what CPU this process is currently running on.\n    The returned number should be ``<=`` :func:`psutil.cpu_count`.\n    On FreeBSD certain kernel process may return ``-1``.\n    It may be used in conjunction with ``psutil.cpu_percent(percpu=True)`` to\n    observe the system workload distributed across multiple CPUs as shown by\n    `scripts/cpu_distribution.py`_ example script.\n\n    .. availability:: Linux, FreeBSD, SunOS\n\n    .. versionadded:: 5.1.0\n\n  .. method:: memory_info()\n\n    Return a named tuple with variable fields depending on the platform\n    representing memory information about the process.\n    The \"portable\" fields available on all platforms are `rss` and `vms`.\n    All numbers are expressed in bytes.\n\n    +---------+---------+----------+---------+-----+-----------------+\n    | Linux   | macOS   | BSD      | Solaris | AIX | Windows         |\n    +=========+=========+==========+=========+=====+=================+\n    | rss     | rss     | rss      | rss     | rss | rss             |\n    +---------+---------+----------+---------+-----+-----------------+\n    | vms     | vms     | vms      | vms     | vms | vms             |\n    +---------+---------+----------+---------+-----+-----------------+\n    | shared  |         | text     |         |     |                 |\n    +---------+---------+----------+---------+-----+-----------------+\n    | text    |         | data     |         |     |                 |\n    +---------+---------+----------+---------+-----+-----------------+\n    | data    |         | stack    |         |     |                 |\n    +---------+---------+----------+---------+-----+-----------------+\n    |         |         | peak_rss |         |     | peak_rss        |\n    +---------+---------+----------+---------+-----+-----------------+\n    |         |         |          |         |     | peak_vms        |\n    +---------+---------+----------+---------+-----+-----------------+\n\n    - **rss**: aka :term:`RSS`. The portion of physical memory\n      currently held by this process (code, data, stack, and mapped files that\n      are resident). Pages swapped out to disk are not counted. On UNIX it\n      matches the ``top`` RES column. On Windows it maps to ``WorkingSetSize``.\n      See also :ref:`faq_memory_rss_vs_vms` FAQ.\n\n    - **vms**: aka :term:`VMS`. The total address space reserved by\n      the process, including pages not yet touched, pages in swap, and\n      memory-mapped files not yet accessed. Typically much larger than\n      **rss**. On UNIX it matches the ``top`` VIRT column. On Windows this\n      maps to ``PrivateUsage`` (private committed pages only), which differs\n      from the UNIX definition; use ``virtual`` from :meth:`memory_info_ex`\n      for the true virtual address space size.\n\n    - **shared** *(Linux)*: memory backed by a file or device (shared\n      libraries, mmap'd files, POSIX shared memory) that *could* be shared\n      with other processes. A page is counted here even if no other process\n      is currently mapping it. Matches ``top``'s SHR column.\n\n    - **text** *(Linux, BSD)*: aka TRS (Text Resident Set). Resident memory\n      devoted to executable code. These pages are read-only and typically\n      shared across all processes running the same binary. Matches ``top``'s\n      CODE column.\n\n    - **data** *(Linux, BSD)*: aka DRS (Data Resident Set). On Linux this\n      covers the data **and** stack segments combined (from\n      ``/proc/<pid>/statm``). On BSD it covers the data segment only (see\n      **stack**). Matches ``top``'s DATA column.\n\n    - **stack** *(BSD)*: size of the process stack segment. Reported\n      separately from **data** (unlike Linux where both are combined).\n\n    - **peak_rss** *(BSD, Windows)*: the highest :term:`RSS` value (high water mark)\n      the process has ever reached. See :term:`peak_rss`. On BSD this may be\n      ``0`` for kernel PIDs.\n      On Windows it maps to ``PeakWorkingSetSize``.\n\n    - **peak_vms** *(Windows)*: peak private committed (page-file-backed)\n      virtual memory. Maps to ``PeakPagefileUsage``.\n\n    For the full definitions of Windows fields see\n    `PROCESS_MEMORY_COUNTERS_EX`_.\n\n    Example on Linux:\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.memory_info()\n       pmem(rss=15491072, vms=84025344, shared=5206016, text=2555904, data=9891840)\n\n    .. versionchanged:: 4.0.0\n       multiple fields are returned, not only *rss* and *vms*.\n\n    .. versionchanged:: 8.0.0\n       Linux: *lib* and *dirty* removed (always 0 since Linux 2.6). Deprecated\n       aliases returning 0 and emitting `DeprecationWarning` are kept.\n       See :ref:`migration guide <migration-8.0>`.\n\n    .. versionchanged:: 8.0.0\n       macOS: *pfaults* and *pageins* removed with **no backward-compatible\n       aliases**. Use :meth:`page_faults` instead.\n       See :ref:`migration guide <migration-8.0>`.\n\n    .. versionchanged:: 8.0.0\n       Windows: eliminated old aliases: *wset* → *rss*, *peak_wset* →\n       *peak_rss*, *pagefile* / *private* → *vms*, *peak_pagefile* →\n       *peak_vms*, *num_page_faults* → :meth:`page_faults` method. At the same\n       time *paged_pool*, *nonpaged_pool*, *peak_paged_pool*,\n       *peak_nonpaged_pool* were moved to :meth:`memory_info_ex`. All these old\n       names still work but raise `DeprecationWarning`.\n       See :ref:`migration guide <migration-8.0>`.\n\n    .. versionchanged:: 8.0.0\n       BSD: added *peak_rss*.\n\n  .. method:: memory_info_ex()\n\n    Return a named tuple extending :meth:`memory_info` with additional\n    platform-specific memory metrics. On platforms where extra fields are not\n    implemented this returns the same result as :meth:`memory_info`. All\n    numbers are expressed in bytes.\n\n    +-------------+----------------+--------------------+\n    | Linux       | macOS          | Windows            |\n    +=============+================+====================+\n    | peak_rss    | peak_rss       | virtual            |\n    +-------------+----------------+--------------------+\n    | peak_vms    |                | peak_virtual       |\n    +-------------+----------------+--------------------+\n    | rss_anon    | rss_anon       | paged_pool         |\n    +-------------+----------------+--------------------+\n    | rss_file    | rss_file       | nonpaged_pool      |\n    +-------------+----------------+--------------------+\n    | rss_shmem   | wired          | peak_paged_pool    |\n    +-------------+----------------+--------------------+\n    | swap        | compressed     | peak_nonpaged_pool |\n    +-------------+----------------+--------------------+\n    | hugetlb     | phys_footprint |                    |\n    +-------------+----------------+--------------------+\n\n    - **peak_rss** *(Linux, macOS)*: the highest :term:`RSS` value (high water\n      mark) the process has reached since it started. See :term:`peak_rss`.\n    - **peak_vms** *(Linux)*: the highest VMS value the process has reached\n      since it started.\n    - **rss_anon** *(Linux, macOS)*: resident :term:`anonymous memory` pages\n      (heap, stack, private mappings) not backed by any file. Set to 0\n      on Linux < 4.5.\n    - **rss_file** *(Linux, macOS)*: resident file-backed memory; pages mapped\n      from files (shared libraries, mmap'd files). Set to 0 on Linux < 4.5.\n    - **rss_shmem** *(Linux)*: resident shared memory pages (``tmpfs``,\n      ``shm_open``). ``rss_anon + rss_file + rss_shmem`` equals **rss**. Set to\n      0 on Linux < 4.5.\n    - **wired** *(macOS)*: memory pinned in RAM by the kernel on behalf of this\n      process; cannot be compressed or paged out.\n    - **swap** *(Linux)*: process memory currently in swap. Equivalent to\n      ``memory_footprint().swap`` but cheaper, as it reads from\n      ``/proc/<pid>/status`` instead of ``/proc/<pid>/smaps``.\n    - **compressed** *(macOS)*: pages held in the in-RAM memory compressor; not\n      counted in **rss**. A large value signals memory pressure but has not yet\n      triggered swapping.\n    - **hugetlb** *(Linux)*: resident memory backed by huge pages. Set to 0 on\n      Linux < 4.4.\n    - **phys_footprint** *(macOS)*: total physical memory impact including\n      compressed pages. What Xcode and ``footprint(1)`` report; prefer this\n      over **rss** macOS memory monitoring.\n    - **virtual** *(Windows)*: true virtual address space size, including\n      reserved-but-uncommitted regions (unlike **vms** in\n      :meth:`memory_info`).\n    - **peak_virtual** *(Windows)*: peak virtual address space size.\n    - **paged_pool** *(Windows)*: kernel memory used for objects created by\n      this process (open file handles, registry keys, etc.) that the OS may\n      swap to disk under memory pressure.\n    - **nonpaged_pool** *(Windows)*: kernel memory used for objects that must\n      stay in RAM at all times (I/O request packets, device driver buffers,\n      etc.). A large or growing value may indicate a driver memory leak.\n    - **peak_paged_pool** *(Windows)*: peak paged-pool usage.\n    - **peak_nonpaged_pool** *(Windows)*: peak non-paged-pool usage.\n\n    For the full definitions of Windows fields see\n    `PROCESS_MEMORY_COUNTERS_EX`_.\n\n    .. versionadded:: 8.0.0\n\n  .. method:: memory_footprint()\n\n    Return a named tuple with USS, PSS and swap memory metrics. These give\n    a more accurate picture of actual memory consumption than\n    :meth:`memory_info`, as explained in this\n    `blog post <https://gmpy.dev/blog/2016/real-process-memory-and-environ-in-python>`_.\n    It works by walking the full process address space, so it is\n    considerably slower than :meth:`memory_info` and may require elevated\n    privileges.\n\n    - **uss** *(Linux, macOS, Windows)*: aka :term:`USS`. This is the\n      memory which is unique to a process and which would be freed if the\n      process were terminated right now. The most representative metric for\n      actual memory usage.\n\n    - **pss** *(Linux)*: aka :term:`PSS`, is the amount of memory\n      shared with other processes, accounted in a way that the amount is\n      divided evenly between the processes that share it. I.e. if a process has\n      10 MBs all to itself, and 10 MBs shared with another process, its PSS\n      will be 15 MBs.\n\n    - **swap** *(Linux)*: process memory currently in swap, counted per-mapping\n      (slower, but may be more accurate than ``memory_info_ex().swap``).\n\n    Example on Linux:\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.memory_footprint()\n       pfootprint(uss=6545408, pss=6872064, swap=0)\n\n    See also `scripts/procsmem.py`_ for an example application.\n\n    .. versionadded:: 8.0.0\n\n    .. availability:: Linux, macOS, Windows\n\n  .. method:: memory_full_info()\n\n    This deprecated method returns the same information as :meth:`memory_info`\n    plus :meth:`memory_footprint` in a single named tuple.\n\n    .. versionadded:: 4.0.0\n\n    .. deprecated:: 8.0.0\n       use :meth:`memory_footprint` instead.\n       See :ref:`migration guide <migration-8.0>`.\n\n  .. method:: memory_percent(memtype=\"rss\")\n\n    Return process memory usage as a percentage of total physical memory\n    (``process.memory_info().rss / virtual_memory().total * 100``).\n    *memtype* can be any field name from :meth:`memory_info`,\n    :meth:`memory_info_ex`, or :meth:`memory_footprint` and controls which\n    memory value is used in the calculation (defaults to ``\"rss\"``).\n\n    .. versionchanged:: 4.0.0\n       added `memtype` parameter.\n\n  .. method:: memory_maps(grouped=True)\n\n    Return process's memory-mapped file regions as a list of named tuples whose\n    fields vary by platform (all values in bytes). If *grouped* is ``True``\n    regions with the same *path* are merged and their numeric fields summed.\n    If *grouped* is ``False`` each region is listed individually and the\n    tuple also includes *addr* (address range) and *perms* (permission\n    string e.g. ``\"r-xp\"``).\n    See `scripts/pmap.py`_ for an example application.\n\n    +---------------+---------+--------------+-----------+\n    | Linux         | Windows | FreeBSD      | Solaris   |\n    +===============+=========+==============+===========+\n    | rss           | rss     | rss          | rss       |\n    +---------------+---------+--------------+-----------+\n    | size          |         | private      | anonymous |\n    +---------------+---------+--------------+-----------+\n    | pss           |         | ref_count    | locked    |\n    +---------------+---------+--------------+-----------+\n    | shared_clean  |         | shadow_count |           |\n    +---------------+---------+--------------+-----------+\n    | shared_dirty  |         |              |           |\n    +---------------+---------+--------------+-----------+\n    | private_clean |         |              |           |\n    +---------------+---------+--------------+-----------+\n    | private_dirty |         |              |           |\n    +---------------+---------+--------------+-----------+\n    | referenced    |         |              |           |\n    +---------------+---------+--------------+-----------+\n    | anonymous     |         |              |           |\n    +---------------+---------+--------------+-----------+\n    | swap          |         |              |           |\n    +---------------+---------+--------------+-----------+\n\n    Linux fields (from ``/proc/<pid>/smaps``):\n\n    - **rss**: resident pages in this mapping.\n    - **size**: total virtual size; may far exceed **rss** for sparse or\n      reserved-but-unaccessed mappings.\n    - **pss**: proportional RSS. **rss** divided by the number of processes\n      sharing this mapping. Useful for fair per-process accounting.\n    - **shared_clean**: shared pages not modified (e.g. shared library code);\n      can be dropped from RAM without writing to swap.\n    - **shared_dirty**: shared pages that have been written to.\n    - **private_clean**: private unmodified pages; can be dropped without\n      writing to swap.\n    - **private_dirty**: private modified pages; must be written to swap\n      before they can be reclaimed. The key indicator of a mapping's real\n      memory cost.\n    - **referenced**: pages recently accessed.\n    - **anonymous**: :term:`anonymous memory` pages not backed by a file (heap, stack allocations).\n    - **swap**: pages from this mapping currently in swap.\n\n    FreeBSD fields:\n\n    - **private**: pages in this mapping private to this process.\n    - **ref_count**: reference count on the VM object backing this mapping.\n    - **shadow_count**: depth of the copy-on-write shadow object chain.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.memory_maps()\n       [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),\n        pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),\n        ...]\n\n    .. availability:: Linux, Windows, FreeBSD, SunOS\n\n    .. versionchanged:: 5.6.0\n       removed macOS support because inherently broken (see issue `#1291\n       <https://github.com/giampaolo/psutil/issues/1291>`_)\n\n  .. method:: children(recursive=False)\n\n    Return the children of this process as a list of :class:`Process`\n    instances.\n    If recursive is ``True`` return all the parent descendants.\n    Pseudo code example assuming *A == this process*:\n\n    ::\n\n      A ─┐\n         │\n         ├─ B (child) ─┐\n         │             └─ X (grandchild) ─┐\n         │                                └─ Y (great grandchild)\n         ├─ C (child)\n         └─ D (child)\n\n      .. code-block:: pycon\n\n         >>> p.children()\n         B, C, D\n         >>> p.children(recursive=True)\n         B, X, Y, C, D\n\n    Note that in the example above if process X disappears process Y won't be\n    returned either as the reference to process A is lost.\n    This concept is well illustrated by this\n    `unit test <https://github.com/giampaolo/psutil/blob/65a52341b55faaab41f68ebc4ed31f18f0929754/psutil/tests/test_process.py#L1064-L1075>`_.\n    See also how to :ref:`kill a process tree <recipe_kill_proc_tree>`.\n\n  .. method:: page_faults()\n\n    Return the number of :term:`page faults <page fault>` for this process as a\n    ``(minor, major)`` named tuple.\n\n    - **minor** (a.k.a. *soft* faults): occur when a memory page is not\n      currently mapped into the process address space, but is already present\n      in physical RAM (e.g. a shared library loaded by another process). The\n      kernel resolves these without disk I/O.\n    - **major** (a.k.a. *hard* faults): occur when the page must be fetched\n      from disk. These are expensive because they stall the process until I/O\n      completes.\n\n    Both counters are :term:`cumulative counters <cumulative counter>` since process creation.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process()\n       >>> p.page_faults()\n       ppagefaults(minor=5905, major=3)\n\n    .. versionadded:: 8.0.0\n\n  .. method:: open_files()\n\n    Return regular files opened by process as a list of named tuples including\n    the following fields:\n\n    - **path**: the absolute file name.\n    - **fd**: the file descriptor number; on Windows this is always ``-1``.\n\n    Linux only:\n\n    - **position** (*Linux*): the file (offset) position.\n    - **mode** (*Linux*): a string indicating how the file was opened, similarly\n      to :func:`open` builtin ``mode`` argument.\n      Possible values are ``'r'``, ``'w'``, ``'a'``, ``'r+'`` and ``'a+'``.\n      There's no distinction between files opened in binary or text mode\n      (``\"b\"`` or ``\"t\"``).\n    - **flags** (*Linux*): the flags which were passed to the underlying\n      :func:`os.open` C call when the file was opened (e.g. :data:`os.O_RDONLY`,\n      :data:`os.O_TRUNC`, etc).\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> f = open('file.ext', 'w')\n       >>> p = psutil.Process()\n       >>> p.open_files()\n       [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3, position=0, mode='w', flags=32769)]\n\n\n    .. warning::\n      on Windows this method is not reliable due to some limitations of the\n      underlying Windows API which may hang when retrieving certain file\n      handles.\n      In order to work around that psutil spawns a thread to determine the file\n      handle name and kills it if it's not responding after 100ms.\n      That implies that this method on Windows is not guaranteed to enumerate\n      all regular file handles (see\n      `issue 597 <https://github.com/giampaolo/psutil/pull/597>`_).\n      Tools like ProcessHacker have the same limitation.\n\n    .. warning::\n      on BSD this method can return files with a null path (\"\") due to a\n      kernel bug, hence it's not reliable\n      (see `issue 595 <https://github.com/giampaolo/psutil/pull/595>`_).\n\n    .. versionchanged:: 3.1.0\n       no longer hangs on Windows.\n\n    .. versionchanged:: 4.1.0\n       new *position*, *mode* and *flags* fields on Linux.\n\n  .. method:: net_connections(kind=\"inet\")\n\n    Return socket connections opened by process as a list of named tuples.\n    To get system-wide connections use :func:`psutil.net_connections`.\n    Every named tuple provides 6 attributes:\n\n    - **fd**: the socket file descriptor. If the connection refers to the\n      current process this may be passed to :func:`socket.fromfd` to obtain a usable\n      socket object.\n      On Windows, FreeBSD and SunOS this is always set to ``-1``.\n    - **family**: the address family, either :data:`socket.AF_INET`, :data:`socket.AF_INET6` or\n      :data:`socket.AF_UNIX`.\n    - **type**: the address type, either :data:`socket.SOCK_STREAM`, :data:`socket.SOCK_DGRAM` or\n      :data:`socket.SOCK_SEQPACKET`.\n    - **laddr**: the local address as a ``(ip, port)`` named tuple or a ``path``\n      in case of AF_UNIX sockets. For UNIX sockets see notes below.\n    - **raddr**: the remote address as a ``(ip, port)`` named tuple or an\n      absolute ``path`` in case of UNIX sockets.\n      When the remote endpoint is not connected you'll get an empty tuple\n      (AF_INET*) or ``\"\"`` (AF_UNIX). For UNIX sockets see notes below.\n    - **status**: represents the status of a TCP connection. The return value\n      is one of the :data:`psutil.CONN_* <psutil.CONN_ESTABLISHED>` constants.\n      For UDP and UNIX sockets this is always going to be\n      :const:`psutil.CONN_NONE`.\n\n    The *kind* parameter is a string which filters for connections that fit the\n    following criteria:\n\n    +----------------+-----------------------------------------------------+\n    | Kind value     | Connections using                                   |\n    +================+=====================================================+\n    | ``\"inet\"``     | IPv4 and IPv6                                       |\n    +----------------+-----------------------------------------------------+\n    | ``\"inet4\"``    | IPv4                                                |\n    +----------------+-----------------------------------------------------+\n    | ``\"inet6\"``    | IPv6                                                |\n    +----------------+-----------------------------------------------------+\n    | ``\"tcp\"``      | TCP                                                 |\n    +----------------+-----------------------------------------------------+\n    | ``\"tcp4\"``     | TCP over IPv4                                       |\n    +----------------+-----------------------------------------------------+\n    | ``\"tcp6\"``     | TCP over IPv6                                       |\n    +----------------+-----------------------------------------------------+\n    | ``\"udp\"``      | UDP                                                 |\n    +----------------+-----------------------------------------------------+\n    | ``\"udp4\"``     | UDP over IPv4                                       |\n    +----------------+-----------------------------------------------------+\n    | ``\"udp6\"``     | UDP over IPv6                                       |\n    +----------------+-----------------------------------------------------+\n    | ``\"unix\"``     | UNIX socket (both UDP and TCP protocols)            |\n    +----------------+-----------------------------------------------------+\n    | ``\"all\"``      | the sum of all the possible families and protocols  |\n    +----------------+-----------------------------------------------------+\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process(1694)\n       >>> p.name()\n       'firefox'\n       >>> p.net_connections()\n       [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status=<ConnectionStatus.CONN_ESTABLISHED: 'ESTABLISHED'>),\n        pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status=<ConnectionStatus.CONN_CLOSING: 'CLOSING'>),\n        pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status=<ConnectionStatus.CONN_ESTABLISHED: 'ESTABLISHED'>),\n        pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status=<ConnectionStatus.CONN_SYN_SENT: 'SYN_SENT'>)]\n\n    .. warning::\n      On Linux, retrieving connections for certain processes requires root\n      privileges. If psutil is not run as root, those connections are silently\n      skipped instead of raising :exc:`psutil.AccessDenied`. That means\n      the returned list may be incomplete.\n\n    .. note::\n      (Solaris) UNIX sockets are not supported.\n\n    .. note::\n       (Linux, FreeBSD) *raddr* field for UNIX sockets is always set to \"\".\n       This is a limitation of the OS.\n\n    .. note::\n       (OpenBSD) *laddr* and *raddr* fields for UNIX sockets are always set to\n       \"\". This is a limitation of the OS.\n\n    .. note::\n      (AIX) :exc:`psutil.AccessDenied` is always raised unless running\n      as root (lsof does the same).\n\n    .. versionchanged:: 5.3.0\n       *laddr* and *raddr* are named tuples.\n\n    .. versionchanged:: 6.0.0\n       method renamed from `connections` to `net_connections`.\n\n    .. versionchanged:: 8.0.0\n       *status* field is now a :class:`psutil.ConnectionStatus` enum member\n       instead of a plain ``str``.\n       See :ref:`migration guide <migration-8.0>`.\n\n  .. method:: connections()\n\n    Same as :meth:`net_connections` (deprecated).\n\n    .. deprecated:: 6.0.0\n       use :meth:`net_connections` instead.\n\n  .. method:: is_running()\n\n    Return whether the current process is running in the current process list.\n    Differently from ``psutil.pid_exists(p.pid)``, this is reliable also in\n    case the process is gone and its PID reused by another process (:ref:`PID reuse <faq_pid_reuse>`).\n\n    If PID has been reused, this method will also remove the process from\n    :func:`process_iter` internal cache.\n\n    .. note::\n      this will return ``True`` also if the process is a :term:`zombie process`\n      (``p.status() == psutil.STATUS_ZOMBIE``).\n\n    .. versionchanged:: 6.0.0\n       automatically remove process from :func:`process_iter` internal cache\n       if PID has been reused by another process.\n\n  .. method:: send_signal(signal)\n\n    Send a signal to process (see :mod:`signal` module constants)\n    preemptively checking whether PID has been reused.\n    On UNIX this is the same as ``os.kill(pid, sig)``.\n    On Windows only *SIGTERM*, *CTRL_C_EVENT* and *CTRL_BREAK_EVENT* signals\n    are supported and *SIGTERM* is treated as an alias for :meth:`kill`.\n    See also how to :ref:`kill a process tree <recipe_kill_proc_tree>`.\n\n    .. versionchanged:: 3.2.0\n       support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals on Windows was\n       added.\n\n  .. method:: suspend()\n\n    Suspend process execution with *SIGSTOP* signal preemptively\n    checking whether PID has been reused.\n    On UNIX this is the same as ``os.kill(pid, signal.SIGSTOP)``.\n    On Windows this is done by suspending all process threads execution.\n\n  .. method:: resume()\n\n    Resume process execution with *SIGCONT* signal preemptively\n    checking whether PID has been reused.\n    On UNIX this is the same as ``os.kill(pid, signal.SIGCONT)``.\n    On Windows this is done by resuming all process threads execution.\n\n  .. method:: terminate()\n\n    Terminate the process with *SIGTERM* signal preemptively checking\n    whether PID has been reused.\n    On UNIX this is the same as ``os.kill(pid, signal.SIGTERM)``.\n    On Windows this is an alias for :meth:`kill`.\n    See also how to :ref:`kill a process tree <recipe_kill_proc_tree>`.\n\n  .. method:: kill()\n\n    Kill the current process by using *SIGKILL* signal preemptively\n    checking whether PID has been reused.\n    On UNIX this is the same as ``os.kill(pid, signal.SIGKILL)``.\n    On Windows this is done by using `TerminateProcess`_.\n    See also how to :ref:`kill a process tree <recipe_kill_proc_tree>`.\n\n  .. method:: wait(timeout=None)\n\n    Wait for a process PID to terminate. The details about the return value\n    differ on UNIX and Windows.\n\n    *On UNIX*: if the process terminated normally, the return value is a\n    positive integer >= 0 indicating the exit code.\n    If the process was terminated by a signal return the negated value of the\n    signal which caused the termination (e.g. ``-SIGTERM``).\n    If PID is not a child of :func:`os.getpid` (current process) just wait until\n    the process disappears and return ``None``.\n    If PID does not exist return ``None`` immediately.\n\n    *On Windows*: always return the exit code, which is a positive integer as\n    returned by `GetExitCodeProcess`_.\n\n    *timeout* is expressed in seconds. If specified and the process is still\n    alive raise :exc:`TimeoutExpired` exception.\n    ``timeout=0`` can be used in non-blocking apps: it will either return\n    immediately or raise :exc:`TimeoutExpired`.\n\n    The return value is cached.\n    To wait for multiple processes use :func:`psutil.wait_procs`.\n\n    .. code-block:: pycon\n\n       >>> import psutil\n       >>> p = psutil.Process(9891)\n       >>> p.terminate()\n       >>> p.wait()\n       <Negsignal.SIGTERM: -15>\n\n    .. note::\n\n      When ``timeout`` is not ``None`` and the platform supports it, an\n      efficient event-driven mechanism is used to wait for process termination:\n\n      - Linux >= 5.3 with Python >= 3.9 uses :func:`os.pidfd_open` + :func:`select.poll`\n      - macOS and other BSD variants use :func:`select.kqueue` + ``KQ_FILTER_PROC``\n        + ``KQ_NOTE_EXIT``\n      - Windows uses `WaitForSingleObject`_\n\n      If none of these mechanisms are available, the function falls back to a\n      busy loop (non-blocking call and short sleeps).\n\n    .. versionchanged:: 5.7.2\n       if *timeout* is not ``None``, use efficient event-driven implementation\n       on Linux >= 5.3 and macOS / BSD.\n\n    .. versionchanged:: 5.7.1\n       return value is cached (instead of returning ``None``).\n\n    .. versionchanged:: 5.7.1\n       on POSIX, in case of negative signal, return it as a human readable\n       :mod:`enum`.\n\n    .. versionchanged:: 7.2.2\n       on Linux >= 5.3 + Python >= 3.9 and macOS/BSD, use :func:`os.pidfd_open` and\n       :func:`select.kqueue` respectively, instead of less efficient busy-loop\n       polling.\n\n----\n\nPopen class\n^^^^^^^^^^^\n\n.. class:: Popen(*args, **kwargs)\n\n  Same as :class:`subprocess.Popen` but in addition it provides all\n  :class:`psutil.Process` methods in a single class.\n  For the following methods which are common to both classes, psutil\n  implementation takes precedence:\n  :meth:`send_signal() <psutil.Process.send_signal()>`,\n  :meth:`terminate() <psutil.Process.terminate()>`,\n  :meth:`kill() <psutil.Process.kill()>`.\n  This is done in order to avoid killing another process in case its PID has\n  been reused, fixing `BPO-6973`_.\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> from subprocess import PIPE\n     >>>\n     >>> p = psutil.Popen([\"/usr/bin/python\", \"-c\", \"print('hello')\"], stdout=PIPE)\n     >>> p.name()\n     'python'\n     >>> p.username()\n     'giampaolo'\n     >>> p.communicate()\n     ('hello\\n', None)\n     >>> p.wait(timeout=2)\n     0\n     >>>\n\n\n  .. versionchanged:: 4.4.0\n     added context manager support.\n\n----\n\nC heap introspection\n--------------------\n\nThe following functions provide direct access to the platform's native C heap\nallocator (such as glibc's ``malloc`` on Linux or ``jemalloc`` on BSD). They\nare low-level interfaces intended for detecting memory leaks in C extensions,\nwhich are usually not revealed via standard RSS/VMS metrics. These functions do\nnot reflect Python object memory; they operate solely on allocations made in C\nvia ``malloc()``, ``free()``, and related calls.\n\nThe general idea behind these functions is straightforward: capture the state\nof the C heap before and after repeatedly invoking a function implemented in a\nC extension, and compare the results. If ``heap_used`` or ``mmap_used`` grows\nsteadily across iterations, the C code is likely retaining memory it should be\nreleasing. This provides an allocator-level way to spot native leaks that\nPython's memory tracking misses.\n\n.. tip::\n\n  Check out `psleak`_ project to see a practical example of how these APIs can be\n  used to detect memory leaks in C extensions.\n\n.. function:: heap_info()\n\n  Return low-level heap statistics from the system's C allocator. On Linux,\n  this exposes ``uordblks`` and ``hblkhd`` fields from glibc's `mallinfo2`_.\n  Returns a named tuple containing:\n\n  - ``heap_used``: total number of bytes currently allocated via ``malloc()``\n    (small allocations).\n  - ``mmap_used``: total number of bytes currently allocated via ``mmap()`` or\n    via large ``malloc()`` allocations. Always set to 0 on macOS.\n  - ``heap_count``: (Windows only) number of private heaps created via\n    ``HeapCreate()``.\n\n   .. code-block:: pycon\n\n      >>> import psutil\n      >>> psutil.heap_info()\n      pheap(heap_used=5177792, mmap_used=819200)\n\n  These fields reflect how unreleased C allocations affect the heap:\n\n  +---------------+------------------------------------------------------------------------------------+-----------------+\n  | Platform      | Allocation type                                                                    | Affected field  |\n  +===============+====================================================================================+=================+\n  | UNIX / glibc  | small ``malloc()`` ≤128KB without ``free()``                                       | ``heap_used``   |\n  +---------------+------------------------------------------------------------------------------------+-----------------+\n  | UNIX / glibc  | large ``malloc()`` >128KB without ``free()`` , or ``mmap()`` without ``munmap()``  | ``mmap_used``   |\n  +---------------+------------------------------------------------------------------------------------+-----------------+\n  | Windows       | ``HeapAlloc()`` without ``HeapFree()``                                             | ``heap_used``   |\n  +---------------+------------------------------------------------------------------------------------+-----------------+\n  | Windows       | ``VirtualAlloc()`` without ``VirtualFree()``                                       | ``mmap_used``   |\n  +---------------+------------------------------------------------------------------------------------+-----------------+\n  | Windows       | ``HeapCreate()`` without ``HeapDestroy()``                                         | ``heap_count``  |\n  +---------------+------------------------------------------------------------------------------------+-----------------+\n\n  .. availability:: Linux with glibc, Windows, macOS, FreeBSD, NetBSD\n\n  .. versionadded:: 7.2.0\n\n.. function:: heap_trim()\n\n  Request that the underlying allocator free any unused memory it's holding in\n  the heap (typically small ``malloc()`` allocations).\n\n  In practice, modern allocators rarely comply, so this is not a\n  general-purpose memory-reduction tool and won't meaningfully shrink RSS in\n  real programs. Its primary value is in **leak detection tools**.\n\n  Calling ``heap_trim()`` before taking measurements helps reduce allocator\n  noise, giving you a cleaner baseline so that changes in ``heap_used`` come\n  from the code you're testing, not from internal allocator caching or\n  fragmentation. Its effectiveness depends on allocator behavior and\n  fragmentation patterns.\n\n  .. availability:: Linux with glibc, Windows, macOS, FreeBSD, NetBSD\n\n  .. versionadded:: 7.2.0\n\n----\n\nWindows services\n----------------\n\n.. function:: win_service_iter()\n\n  Return an iterator yielding a :class:`WindowsService` class instance for all\n  Windows services installed.\n\n  .. versionadded:: 4.2.0\n\n  .. availability:: Windows\n\n.. function:: win_service_get(name)\n\n  Get a Windows service by name, returning a :class:`WindowsService` instance.\n  Raise :exc:`psutil.NoSuchProcess` if no service with such name exists.\n\n  .. versionadded:: 4.2.0\n\n  .. availability:: Windows\n\n.. class:: WindowsService\n\n  Represents a Windows service with the given *name*. This class is returned\n  by :func:`win_service_iter` and :func:`win_service_get` functions and it is\n  not supposed to be instantiated directly.\n\n  .. method:: name()\n\n    The service name. This string is how a service is referenced and can be\n    passed to :func:`win_service_get` to get a new :class:`WindowsService`\n    instance.\n\n  .. method:: display_name()\n\n    The service display name. The value is cached when this class is\n    instantiated.\n\n  .. method:: binpath()\n\n    The fully qualified path to the service binary/exe file as a string,\n    including command line arguments.\n\n  .. method:: username()\n\n    The name of the user that owns this service.\n\n  .. method:: start_type()\n\n    A string which can either be `\"automatic\"`, `\"manual\"` or `\"disabled\"`.\n\n  .. method:: pid()\n\n    The process PID, if any, else `None`. This can be passed to\n    :class:`Process` class to control the service's process.\n\n  .. method:: status()\n\n    Service status as a string, which may be either `\"running\"`, `\"paused\"`,\n    `\"start_pending\"`, `\"pause_pending\"`, `\"continue_pending\"`,\n    `\"stop_pending\"` or `\"stopped\"`.\n\n  .. method:: description()\n\n    Service long description.\n\n  .. method:: as_dict()\n\n    Utility method retrieving all the information above as a dictionary.\n\n  Example code:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> list(psutil.win_service_iter())\n     [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,\n      <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,\n      <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,\n      <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,\n      ...]\n     >>> s = psutil.win_service_get('alg')\n     >>> s.as_dict()\n     {'binpath': 'C:\\\\Windows\\\\System32\\\\alg.exe',\n      'description': 'Provides support for 3rd party protocol plug-ins for Internet Connection Sharing',\n      'display_name': 'Application Layer Gateway Service',\n      'name': 'alg',\n      'pid': None,\n      'start_type': 'manual',\n      'status': 'stopped',\n      'username': 'NT AUTHORITY\\\\LocalService'}\n\n  .. availability:: Windows\n\n  .. versionadded:: 4.2.0\n\n----\n\nConstants\n---------\n\nThe following enum classes group related constants and are useful for type\nannotations and introspection. The individual constants (e.g.\n:data:`psutil.STATUS_RUNNING`) are also accessible directly from the psutil\nnamespace as aliases for the enum members and should be preferred over\naccessing them via the enum class (e.g. prefer ``psutil.STATUS_RUNNING`` over\n``psutil.ProcessStatus.STATUS_RUNNING``).\n\n.. class:: psutil.ProcessStatus\n\n  :class:`enum.StrEnum` collection of :data:`STATUS_* <psutil.STATUS_RUNNING>`\n  constants. Returned by :meth:`Process.status`.\n\n  .. versionadded:: 8.0.0\n\n.. class:: psutil.ProcessPriority\n\n  :class:`enum.IntEnum` collection of\n  :data:`*_PRIORITY_CLASS <psutil.ABOVE_NORMAL_PRIORITY_CLASS>` constants for\n  :meth:`Process.nice` on Windows.\n\n  .. availability:: Windows\n\n  .. versionadded:: 8.0.0\n\n.. class:: psutil.ProcessIOPriority\n\n  :class:`enum.IntEnum` collection of I/O priority constants for\n  :meth:`Process.ionice`.\n\n  :data:`IOPRIO_CLASS_* <psutil.IOPRIO_CLASS_NONE>` on Linux,\n  :data:`IOPRIO_* <psutil.IOPRIO_VERYLOW>` on Windows.\n\n  .. availability:: Linux, Windows\n\n  .. versionadded:: 8.0.0\n\n.. class:: psutil.ProcessRlimit\n\n  :class:`enum.IntEnum` collection of :data:`RLIMIT_* <psutil.RLIMIT_NOFILE>`\n  constants for :meth:`Process.rlimit`.\n\n  .. availability:: Linux, FreeBSD\n\n  .. versionadded:: 8.0.0\n\n.. class:: psutil.ConnectionStatus\n\n  :class:`enum.StrEnum` collection of :data:`CONN_* <psutil.CONN_ESTABLISHED>`\n  constants. Returned in the *status* field of\n  :func:`psutil.net_connections` and :meth:`Process.net_connections`.\n\n  .. versionadded:: 8.0.0\n\n.. class:: psutil.NicDuplex\n\n  :class:`enum.IntEnum` collection of :data:`NIC_DUPLEX_* <psutil.NIC_DUPLEX_FULL>`\n  constants. Returned in the *duplex* field of :func:`psutil.net_if_stats`.\n\n  .. versionadded:: 3.0.0\n\n.. class:: psutil.BatteryTime\n\n  :class:`enum.IntEnum` collection of :data:`POWER_TIME_* <psutil.POWER_TIME_UNKNOWN>`\n  constants. May appear in the *secsleft* field of :func:`psutil.sensors_battery`.\n\n  .. versionadded:: 5.1.0\n\nOperating system constants\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. _const-oses:\n\n.. data:: POSIX\n.. data:: LINUX\n.. data:: WINDOWS\n.. data:: MACOS\n.. data:: FREEBSD\n.. data:: NETBSD\n.. data:: OPENBSD\n.. data:: BSD\n.. data:: SUNOS\n.. data:: AIX\n\n  ``bool`` constants which define what platform you're on. E.g. if on Windows,\n  :const:`WINDOWS` constant will be ``True``, all others will be ``False``.\n\n  .. versionadded:: 4.0.0\n\n  .. versionchanged:: 5.4.0\n     added AIX.\n\n.. data:: OSX\n\n  Alias for :const:`MACOS`.\n\n  .. deprecated:: 5.4.7\n     use :const:`MACOS` instead.\n\nProcess status constants\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. _const-pstatus:\n\n.. data:: STATUS_RUNNING\n.. data:: STATUS_SLEEPING\n.. data:: STATUS_DISK_SLEEP\n.. data:: STATUS_STOPPED\n.. data:: STATUS_TRACING_STOP\n.. data:: STATUS_ZOMBIE\n.. data:: STATUS_DEAD\n.. data:: STATUS_WAKE_KILL\n.. data:: STATUS_WAKING\n.. data:: STATUS_PARKED (Linux)\n.. data:: STATUS_IDLE (Linux, macOS, FreeBSD)\n.. data:: STATUS_LOCKED (FreeBSD)\n.. data:: STATUS_WAITING (FreeBSD)\n.. data:: STATUS_SUSPENDED (NetBSD)\n\n  Represent a process status. Returned by :meth:`Process.status`.\n  These constants are members of the :class:`psutil.ProcessStatus` enum.\n\n  .. versionadded:: 3.4.1 ``STATUS_SUSPENDED`` (NetBSD)\n\n  .. versionadded:: 5.4.7 ``STATUS_PARKED`` (Linux)\n\n  .. versionchanged:: 8.0.0\n     constants are now :class:`psutil.ProcessStatus` enum members (were plain\n     strings).\n     See :ref:`migration guide <migration-8.0>`.\n\nProcess priority constants\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. _const-prio:\n\n.. data:: REALTIME_PRIORITY_CLASS\n.. data:: HIGH_PRIORITY_CLASS\n.. data:: ABOVE_NORMAL_PRIORITY_CLASS\n.. data:: NORMAL_PRIORITY_CLASS\n.. data:: IDLE_PRIORITY_CLASS\n.. data:: BELOW_NORMAL_PRIORITY_CLASS\n\n  Represent the priority of a process on Windows (see `SetPriorityClass`_).\n  They can be used in conjunction with :meth:`Process.nice` to get or\n  set process priority.\n  These constants are members of the :class:`psutil.ProcessPriority` enum.\n\n  .. availability:: Windows\n\n  .. versionchanged:: 8.0.0\n     constants are now :class:`psutil.ProcessPriority` enum members (were plain\n     integers).\n     See :ref:`migration guide <migration-8.0>`.\n\n.. _const-ioprio-linux:\n\n.. data:: IOPRIO_CLASS_NONE\n.. data:: IOPRIO_CLASS_RT\n.. data:: IOPRIO_CLASS_BE\n.. data:: IOPRIO_CLASS_IDLE\n\n  A set of integers representing the I/O priority of a process on Linux. They\n  can be used in conjunction with :meth:`Process.ionice` to get or set\n  process I/O priority.\n  These constants are members of the :class:`psutil.ProcessIOPriority`\n  enum.\n  :const:`IOPRIO_CLASS_NONE` and :const:`IOPRIO_CLASS_BE` (best effort) is the\n  default for any process that hasn't set a specific I/O priority.\n  :const:`IOPRIO_CLASS_RT` (real time) means the process is given first access\n  to the disk, regardless of what else is going on in the system.\n  :const:`IOPRIO_CLASS_IDLE` means the process will get I/O time when no-one else\n  needs the disk.\n  For further information refer to manuals of\n  `ionice <http://linux.die.net/man/1/ionice>`_ command line utility or\n  `ioprio_get`_ system call.\n\n  .. availability:: Linux\n\n  .. versionchanged:: 8.0.0\n     constants are now :class:`psutil.ProcessIOPriority` enum members\n     (previously ``IOPriority`` enum).\n     See :ref:`migration guide <migration-8.0>`.\n\n.. _const-ioprio-windows:\n\n.. data:: IOPRIO_VERYLOW\n.. data:: IOPRIO_LOW\n.. data:: IOPRIO_NORMAL\n.. data:: IOPRIO_HIGH\n\n  A set of integers representing the I/O priority of a process on Windows.\n  They can be used in conjunction with :meth:`Process.ionice` to get\n  or set process I/O priority.\n  These constants are members of the :class:`psutil.ProcessIOPriority`\n  enum.\n\n  .. availability:: Windows\n\n  .. versionadded:: 5.6.2\n\n  .. versionchanged:: 8.0.0\n     constants are now :class:`psutil.ProcessIOPriority` enum members\n     (previously ``IOPriority`` enum).\n     See :ref:`migration guide <migration-8.0>`.\n\nProcess resource constants\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. _const-rlimit:\n\nLinux / FreeBSD:\n\n  .. data:: RLIM_INFINITY\n  .. data:: RLIMIT_AS\n  .. data:: RLIMIT_CORE\n  .. data:: RLIMIT_CPU\n  .. data:: RLIMIT_DATA\n  .. data:: RLIMIT_FSIZE\n  .. data:: RLIMIT_MEMLOCK\n  .. data:: RLIMIT_NOFILE\n  .. data:: RLIMIT_NPROC\n  .. data:: RLIMIT_RSS\n  .. data:: RLIMIT_STACK\n\nLinux specific:\n\n  .. data:: RLIMIT_LOCKS\n  .. data:: RLIMIT_MSGQUEUE\n  .. data:: RLIMIT_NICE\n  .. data:: RLIMIT_RTPRIO\n  .. data:: RLIMIT_RTTIME\n  .. data:: RLIMIT_SIGPENDING\n\nFreeBSD specific:\n\n  .. data:: RLIMIT_SWAP\n  .. data:: RLIMIT_SBSIZE\n  .. data:: RLIMIT_NPTS\n\nConstants used for getting and setting process resource limits to be used in\nconjunction with :meth:`Process.rlimit`. See :func:`resource.getrlimit`\nfor further information.\nThese constants are members of the :class:`psutil.ProcessRlimit` enum.\n\n.. availability:: Linux, FreeBSD\n\n.. versionchanged:: 5.7.3\n   added FreeBSD support, added ``RLIMIT_SWAP``, ``RLIMIT_SBSIZE``,\n   ``RLIMIT_NPTS``.\n\n.. versionchanged:: 8.0.0\n   constants are now :class:`psutil.ProcessRlimit` enum members (were plain\n   integers).\n   See :ref:`migration guide <migration-8.0>`.\n\nConnections constants\n^^^^^^^^^^^^^^^^^^^^^\n\n.. _const-conn:\n\n.. data:: CONN_ESTABLISHED\n.. data:: CONN_SYN_SENT\n.. data:: CONN_SYN_RECV\n.. data:: CONN_FIN_WAIT1\n.. data:: CONN_FIN_WAIT2\n.. data:: CONN_TIME_WAIT\n.. data:: CONN_CLOSE\n.. data:: CONN_CLOSE_WAIT\n.. data:: CONN_LAST_ACK\n.. data:: CONN_LISTEN\n.. data:: CONN_CLOSING\n.. data:: CONN_NONE\n.. data:: CONN_DELETE_TCB (Windows)\n.. data:: CONN_IDLE (Solaris)\n.. data:: CONN_BOUND (Solaris)\n\n  A set of strings representing the status of a TCP connection.\n  Returned by :meth:`Process.net_connections` and\n  :func:`psutil.net_connections` (`status` field).\n  These constants are members of the :class:`psutil.ConnectionStatus` enum.\n\n  .. versionchanged:: 8.0.0\n     constants are now :class:`psutil.ConnectionStatus` enum members (were\n     plain strings).\n     See :ref:`migration guide <migration-8.0>`.\n\nHardware constants\n^^^^^^^^^^^^^^^^^^\n\n.. _const-aflink:\n\n.. data:: AF_LINK\n\n  Constant which identifies a MAC address associated with a network interface.\n  To be used in conjunction with :func:`psutil.net_if_addrs`.\n\n  .. versionadded:: 3.0.0\n\n.. _const-duplex:\n\n.. data:: NIC_DUPLEX_FULL\n.. data:: NIC_DUPLEX_HALF\n.. data:: NIC_DUPLEX_UNKNOWN\n\n  Constants which identifies whether a :term:`NIC` (network interface card) has\n  full or half mode speed. NIC_DUPLEX_FULL means the NIC is able to send and\n  receive data (files) simultaneously, NIC_DUPLEX_HALF means the NIC can either\n  send or receive data at a time.\n  To be used in conjunction with :func:`psutil.net_if_stats`.\n\n  .. versionadded:: 3.0.0\n\n.. _const-power:\n\n.. data:: POWER_TIME_UNKNOWN\n.. data:: POWER_TIME_UNLIMITED\n\n  Whether the remaining time of the battery cannot be determined or is\n  unlimited.\n  May be assigned to :func:`psutil.sensors_battery`'s *secsleft* field.\n\n  .. versionadded:: 5.1.0\n\nOther constants\n^^^^^^^^^^^^^^^\n\n.. _const-procfs_path:\n\n.. data:: PROCFS_PATH\n\n  The path of the /proc filesystem on Linux, Solaris and AIX (defaults to\n  ``\"/proc\"``).\n  You may want to re-set this constant right after importing psutil in case\n  your /proc filesystem is mounted elsewhere or if you want to retrieve\n  information about Linux containers such as Docker, Heroku or LXC (see\n  `here <https://fabiokung.com/2014/03/13/memory-inside-linux-containers/>`_\n  for more info).\n  It must be noted that this trick works only for APIs which rely on /proc\n  filesystem (e.g. memory-related APIs and many (but not all)\n  :class:`Process` class methods).\n\n  .. availability:: Linux, SunOS, AIX\n\n  .. versionadded:: 3.2.3\n\n  .. versionchanged:: 3.4.2\n     also available on Solaris.\n\n  .. versionchanged:: 5.4.0\n     also available on AIX.\n\n.. _const-version-info:\n\n.. data:: version_info\n\n  A tuple to check psutil installed version. Example:\n\n  .. code-block:: pycon\n\n     >>> import psutil\n     >>> if psutil.version_info >= (4, 5):\n     ...    pass\n\n.. _`BPO-10784`: https://bugs.python.org/issue10784\n.. _`BPO-12442`: https://bugs.python.org/issue12442\n.. _`BPO-6973`: https://bugs.python.org/issue6973\n.. _`ioprio_get`: https://linux.die.net/man/2/ioprio_get\n.. _`iostats doc`: https://www.kernel.org/doc/Documentation/iostats.txt\n.. _`mallinfo2`: https://man7.org/linux/man-pages/man3/mallinfo.3.html\n.. _`man prlimit`: https://linux.die.net/man/2/prlimit\n.. _`psleak`: https://github.com/giampaolo/psleak\n.. _`/proc/meminfo`: https://man7.org/linux/man-pages/man5/proc_meminfo.5.html\n\n.. === scripts\n\n.. _`scripts/battery.py`: https://github.com/giampaolo/psutil/blob/master/scripts/battery.py\n.. _`scripts/cpu_distribution.py`: https://github.com/giampaolo/psutil/blob/master/scripts/cpu_distribution.py\n.. _`scripts/disk_usage.py`: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py\n.. _`scripts/fans.py`: https://github.com/giampaolo/psutil/blob/master/scripts/fans.py\n.. _`scripts/ifconfig.py`: https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py\n.. _`scripts/iotop.py`: https://github.com/giampaolo/psutil/blob/master/scripts/iotop.py\n.. _`scripts/meminfo.py`: https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py\n.. _`scripts/netstat.py`: https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py\n.. _`scripts/nettop.py`: https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py\n.. _`scripts/pmap.py`: https://github.com/giampaolo/psutil/blob/master/scripts/pmap.py\n.. _`scripts/procinfo.py`: https://github.com/giampaolo/psutil/blob/master/scripts/procinfo.py\n.. _`scripts/procsmem.py`: https://github.com/giampaolo/psutil/blob/master/scripts/procsmem.py\n.. _`scripts/sensors.py`: https://github.com/giampaolo/psutil/blob/master/scripts/sensors.py\n.. _`scripts/temperatures.py`: https://github.com/giampaolo/psutil/blob/master/scripts/temperatures.py\n\n.. === Windows API\n\n.. _`GetExitCodeProcess`: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess\n.. _`GetPerformanceInfo`: https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getperformanceinfo\n.. _`GetPriorityClass`: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getpriorityclass\n.. _`PROCESS_MEMORY_COUNTERS_EX`: https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters_ex\n.. _`SetPriorityClass`: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass\n.. _`TerminateProcess`: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess\n.. _`WaitForSingleObject`: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject\n"
  },
  {
    "path": "docs/changelog.rst",
    "content": ".. currentmodule:: psutil\n\nChangelog\n=========\n\n8.0.0 (IN DEVELOPMENT)\n^^^^^^^^^^^^^^^^^^^^^^\n\n**Compatibility notes**\n\n psutil 8.0 introduces breaking API changes and drops support for Python 3.6.\n See the :ref:`migration guide <migration-8.0>` if upgrading from 7.x.\n\n**Enhancements**\n\nDoc improvements (:gh:`2761`, :gh:`2757`, :gh:`2760`, :gh:`2745`, :gh:`2763`,\n:gh:`2764`, :gh:`2767`, :gh:`2768`, :gh:`2769`, :gh:`2771`, :gh:`2774`,\n:gh:`2775`)\n\n- Split docs from a single HTML file into multiple sections (API reference,\n  install, etc.).\n\n- Added new sections:\n\n  - `/recipes <https://psutil.readthedocs.io/en/latest/recipes.html>`__:\n    show code samples\n  - `/adoption <https://psutil.readthedocs.io/en/latest/adoption.html>`__:\n    notable software using psutil\n  - `/glossary <https://psutil.readthedocs.io/en/latest/glossary.html>`__:\n    a section explaining the core concepts\n  - `/shell_equivalents <https://psutil.readthedocs.io/en/latest/shell_equivalents.html>`__:\n    maps each psutil API to native CLI commands\n  - `/install <https://psutil.readthedocs.io/en/latest/install.html>`__\n    (was old ``INSTALL.rst`` in root dir)\n  - `/credits <https://psutil.readthedocs.io/en/latest/credits.html>`__:\n    list contributors and donors (was old ``CREDITS`` in root dir)\n  - `/platform <https://psutil.readthedocs.io/en/latest/platform.html>`__:\n    summary of OSes and architectures support\n  - `/faq <https://psutil.readthedocs.io/en/latest/faq.html>`__:\n    extended FAQ section.\n  - `/alternatives <https://psutil.readthedocs.io/en/latest/alternatives.html>`__:\n    list of alternative Python libraries and tools that overlap with psutil.\n  - `/migration <https://psutil.readthedocs.io/en/latest/migration.html>`__: a\n    section explaining how to migrate to newer psutil versions that break\n    backward compatibility.\n\n- Usability:\n\n  - Show a clickable COPY button to copy code snippets.\n  - Show ``psutil.`` prefix for all APIs.\n  - Use sphinx extension to validate Python code snippets syntax at build-time.\n\n- Testing:\n\n  - Replace ``rstcheck`` with ``sphinx-lint`` for RST linting.\n  - Add custom script to detect dead reference links in ``.rst`` files.\n  - Build doc as part of CI process.\n\n- Greatly improved :func:`virtual_memory` doc.\n\nType hints / enums:\n\n- :gh:`1946`: Add inline type hints to all public APIs in `psutil/__init__.py`.\n  Type checkers (mypy, pyright, etc.) can now statically verify code that uses\n  psutil. No runtime behavior is changed; the annotations are purely\n  informational.\n- :gh:`2751`: Convert all named tuples from ``collections.namedtuple`` to\n  ``typing.NamedTuple`` classes with **type annotations**. This makes the\n  classes self-documenting, effectively turning this module into a readable\n  API reference.\n- :gh:`2753`: Introduce enum classes (:class:`ProcessStatus`,\n  :class:`ConnectionStatus`,\n  :class:`ProcessIOPriority`, :class:`ProcessPriority`, :class:`ProcessRlimit`)\n  grouping related constants. The individual top-level constants (e.g.\n  ``psutil.STATUS_RUNNING``) remain the primary API, and are now aliases\n  for the corresponding enum members.\n\n- New APIs:\n\n- :gh:`1541`: New :meth:`Process.page_faults` method, returning a ``(minor,\n  major)`` named tuple.\n- Reorganization of process memory APIs (:gh:`2731`, :gh:`2736`, :gh:`2723`,\n  :gh:`2733`).\n\n  - Add new :meth:`Process.memory_info_ex` method, which extends\n    :meth:`Process.memory_info` with platform-specific metrics:\n\n    - Linux: *peak_rss*, *peak_vms*, *rss_anon*, *rss_file*, *rss_shmem*,\n      *swap*, *hugetlb*\n    - macOS: *peak_rss*, *rss_anon*, *rss_file*, *wired*, *compressed*,\n      *phys_footprint*\n    - Windows: *virtual*, *peak_virtual*\n\n  - Add new :meth:`Process.memory_footprint` method, which returns *uss*,\n    *pss* and *swap* metrics (what :meth:`Process.memory_full_info` used to\n    return, which is now **deprecated**, see\n    :ref:`migration guide <migration-8.0>`).\n\n  - :meth:`Process.memory_info` named tuple changed:\n\n    - BSD: added *peak_rss*.\n\n    - Linux: *lib* and *dirty* removed (always 0 since Linux 2.6). Deprecated\n      aliases returning 0 and emitting `DeprecationWarning` are kept.\n\n    - macOS: *pfaults* and *pageins* removed with **no\n      backward-compatible aliases**. Use :meth:`Process.page_faults` instead.\n\n    - Windows: eliminated old aliases: *wset* → *rss*, *peak_wset* →\n      *peak_rss*, *pagefile* / *private* → *vms*, *peak_pagefile* → *peak_vms*.\n      At the same time *paged_pool*, *nonpaged_pool*, *peak_paged_pool*,\n      *peak_nonpaged_pool* were moved to :meth:`Process.memory_info_ex`. All\n      these old names still work but raise `DeprecationWarning`.\n      See :ref:`migration guide <migration-8.0>`.\n\n  - :meth:`Process.memory_full_info` is **deprecated**. Use the new\n    :meth:`Process.memory_footprint` instead.\n    See :ref:`migration guide <migration-8.0>`.\n\nOthers\n\n- :gh:`2747`: the field order of the named tuple returned by :func:`cpu_times`\n  has been normalized on all platforms, and the first 3 fields are now always\n  ``user, system, idle``. See compatibility notes below.\n- :gh:`2754`: standardize :func:`sensors_battery`'s `percent` so that it\n  returns a `float` instead of `int` on all systems, not only Linux.\n- :gh:`2765`: add a PR bot that uses Claude to summarize PR changes and update\n  ``changelog.rst`` and ``credits.rst`` when commenting with /changelog.\n- :gh:`2766`: remove remaining Python 2.7 compatibility shims from\n  ``setup.py``, simplifying the build infrastructure.\n- :gh:`2772`, [Windows]: :func:`cpu_times` ``interrupt`` field renamed to\n  ``irq`` to match the field name used on Linux and BSD. ``interrupt`` still\n  works but raises :exc:`DeprecationWarning`.\n  See :ref:`migration guide <migration-8.0>`.\n- :gh:`2776`: Windows: :func:`virtual_memory` now includes ``cached`` and\n  ``wired`` fields.\n\n**Bug fixes**\n\n- :gh:`2770`, [Linux]: fix :func:`cpu_count_cores` raising ``ValueError``\n  on s390x architecture, where ``/proc/cpuinfo`` uses spaces before the\n  colon separator instead of a tab.\n- :gh:`2726`, [macOS]: :meth:`Process.num_ctx_switches` return an unusual\n  high number due to a C type precision issue.\n- :gh:`2411` [macOS]: :meth:`Process.cpu_times` and\n  :meth:`Process.cpu_percent` calculation on macOS x86_64 (arm64 is fine) was\n  highly inaccurate (41.67x lower).\n- :gh:`2732`, [Linux]: net_if_duplex_speed: handle EBUSY from\n  ioctl(SIOCETHTOOL).\n- :gh:`2744`, [NetBSD]: fix possible double `free()` in :func:`swap_memory`.\n- :gh:`2746`, [FreeBSD]: :meth:`Process.memory_maps`, `rss` and `private`\n  fields, are erroneously reported in memory pages instead of bytes. Other\n  platforms (Linux, macOS, Windows) return bytes.\n\n7.2.3 — 2026-02-08\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`2715`, [Linux]: ``wait_pid_pidfd_open()`` (from :meth:`Process.wait`)\n  crashes with ``EINVAL`` due to kernel race condition.\n\n7.2.2 — 2026-01-28\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2705`: [Linux]: :meth:`Process.wait` now uses ``pidfd_open()`` +\n  ``poll()`` for waiting, resulting in no busy loop and faster response times.\n  Requires Linux >= 5.3 and Python >= 3.9. Falls back to traditional polling if\n  unavailable.\n- :gh:`2705`: [macOS], [BSD]: :meth:`Process.wait` now uses ``kqueue()`` for\n  waiting, resulting in no busy loop and faster response times.\n\n**Bug fixes**\n\n- :gh:`2701`, [macOS]: fix compilation error on macOS < 10.7.  (patch by Sergey\n  Fedorov)\n- :gh:`2707`, [macOS]: fix potential memory leaks in error paths of\n  `Process.memory_full_info()` and `Process.threads()`.\n- :gh:`2708`, [macOS]: :meth:`Process.cmdline` and :meth:`Process.environ`\n  may fail with ``OSError: [Errno 0] Undefined error`` (from\n  ``sysctl(KERN_PROCARGS2)``). They now raise :exc:`AccessDenied` instead.\n\n7.2.1 — 2025-12-29\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`2699`, [FreeBSD], [NetBSD]: :func:`heap_info` does not detect small\n  allocations (<= 1K). In order to fix that, we now flush internal jemalloc\n  cache before fetching the metrics.\n\n7.2.0 — 2025-12-23\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1275`: new :func:`heap_info` and :func:`heap_trim` functions,\n  providing direct access to the platform's native C heap allocator (glibc,\n  mimalloc, libmalloc). Useful to create tools to detect memory leaks.\n- :gh:`2403`, [Linux]: publish wheels for Linux musl.\n- :gh:`2680`: unit tests are no longer installed / part of the distribution.\n  They now live under `tests/` instead of `psutil/tests`.\n\n**Bug fixes**\n\n* :gh:`2684`, [FreeBSD], [critical]: compilation fails on FreeBSD 14 due to\n  missing include.\n* :gh:`2691`, [Windows]: fix memory leak in :func:`net_if_stats` due to\n  missing ``Py_CLEAR``.\n\n**Compatibility notes**\n\n- :gh:`2680`: `import psutil.tests` no longer works (but it was never\n  documented to begin with).\n\n7.1.3 — 2025-11-02\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2667`: enforce `clang-format` on all C and header files. It is now the\n  mandatory formatting style for all C sources.\n- :gh:`2672`, [macOS], [BSD]: increase the chances to recognize zombie\n  processes and raise the appropriate exception (:exc:`ZombieProcess`).\n- :gh:`2676`, :gh:`2678`: replace unsafe `sprintf` / `snprintf` / `sprintf_s`\n  calls with `str_format()`. Replace `strlcat` / `strlcpy` with safe `str_copy`\n  / `str_append`. This unifies string handling across platforms and reduces\n  unsafe usage of standard string functions, improving robustness.\n\n**Bug fixes**\n\n- :gh:`2674`, [Windows]: :func:`disk_usage` could truncate values on 32-bit\n  platforms, potentially reporting incorrect total/free/used space for drives\n  larger than 4GB.\n- :gh:`2675`, [macOS]: :meth:`Process.status` incorrectly returns \"running\"\n  for 99% of the processes.\n- :gh:`2677`, [Windows]: fix MAC address string construction in\n  :func:`net_if_addrs`. Previously, the MAC address buffer was incorrectly\n  updated using a fixed increment and `sprintf_s`, which could overflow or\n  misformat the string if the MAC length or formatting changed. Also, the\n  final '\\n' was inserted unnecessarily.\n- :gh:`2679`, [OpenBSD], [NetBSD], [critical]: can't build due to C syntax\n  error.\n\n7.1.2 — 2025-10-25\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2657`: stop publishing prebuilt Linux and Windows wheels for 32-bit\n  Python. 32-bit CPython is still supported, but psutil must now be built from\n  source.\n  :gh:`2565`: produce wheels for free-thread cPython 3.13 and 3.14 (patch by\n  Lysandros Nikolaou)\n\n**Bug fixes**\n\n- :gh:`2650`, [macOS]: :meth:`Process.cmdline` and :meth:`Process.environ`\n  may incorrectly raise :exc:`NoSuchProcess` instead of :exc:`ZombieProcess`.\n- :gh:`2658`, [macOS]: double ``free()`` in :meth:`Process.environ` when it\n  fails internally. This posed a risk of segfault.\n- :gh:`2662`, [macOS]: massive C code cleanup to guard against possible\n  segfaults which were (not so) sporadically spotted on CI.\n\n**Compatibility notes**\n\n- :gh:`2657`: stop publishing prebuilt Linux and Windows wheels for 32-bit\n  Python.\n\n7.1.1 — 2025-10-19\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2645`, [SunOS]: dropped support for SunOS 10.\n- :gh:`2646`, [SunOS]: add CI test runner for SunOS.\n\n**Bug fixes**\n\n- :gh:`2641`, [SunOS]: cannot compile psutil from sources due to missing C\n  include.\n- :gh:`2357`, [SunOS]: :meth:`Process.cmdline` does not handle spaces\n  properly. (patch by Ben Raz)\n\n**Compatibility notes**\n\n* :gh:`2645`: SunOS 10 is no longer supported.\n\n7.1.0 — 2025-09-17\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2581`, [Windows]: publish ARM64 wheels.  (patch by Matthieu Darbois)\n- :gh:`2571`, [FreeBSD]: Dropped support for FreeBSD 8 and earlier. FreeBSD 8\n  was maintained from 2009 to 2013.\n- :gh:`2575`: introduced `dprint` CLI tool to format .yml and .md files.\n\n**Bug fixes**\n\n- :gh:`2473`, [macOS]: Fix build issue on macOS 11 and lower.\n- :gh:`2494`, [Windows]: All APIs dealing with paths, such as\n  :meth:`Process.memory_maps`, :meth:`Process.exe` and\n  :meth:`Process.open_files` does not properly handle UNC paths. Paths such\n  as ``\\\\??\\\\C:\\\\Windows\\\\Temp`` and\n  ``'\\\\Device\\\\HarddiskVolume1\\\\Windows\\\\Temp'`` are now converted to\n  ``C:\\\\Windows\\\\Temp``.  (patch by Ben Peddell)\n- :gh:`2506`, [Windows]: Windows service APIs had issues with unicode services\n  using special characters in their name.\n- :gh:`2514`, [Linux]: :meth:`Process.cwd` sometimes fail with\n  `FileNotFoundError` due to a race condition.\n- :gh:`2526`, [Linux]: :meth:`Process.create_time`, which is used to\n  univocally identify a process over time, is subject to system clock updates,\n  and as such can lead to :meth:`Process.is_running` returning a wrong\n  result. A monotonic creation time is now used instead.  (patch by Jonathan\n  Kohler)\n- :gh:`2528`, [Linux]: :meth:`Process.children` may raise\n  ``PermissionError``. It will now raise :exc:`AccessDenied` instead.\n- :gh:`2540`, [macOS]: :func:`boot_time` is off by 45 seconds (C precision\n  issue).\n- :gh:`2541`, :gh:`2570`, :gh:`2578` [Linux], [macOS], [NetBSD]:\n  :meth:`Process.create_time` does not reflect system clock updates.\n- :gh:`2542`: if system clock is updated :meth:`Process.children` and\n  :meth:`Process.parent` may not be able to return the right information.\n- :gh:`2545`: [Illumos]: Fix handling of MIB2_UDP_ENTRY in\n  :func:`net_connections`.\n- :gh:`2552`, [Windows]: :func:`boot_time` didn't take into account the time\n  spent during suspend / hibernation.\n- :gh:`2560`, [Linux]: :meth:`Process.memory_maps` may crash with\n  `IndexError` on RISCV64 due to a malformed `/proc/{PID}/smaps` file.  (patch\n  by Julien Stephan)\n- :gh:`2586`, [macOS], [CRITICAL]: fixed different places in C code which can\n  trigger a segfault.\n- :gh:`2604`, [Linux]: :func:`virtual_memory` \"used\" memory does not match\n  recent versions of ``free`` CLI utility.  (patch by Isaac K. Ko)\n- :gh:`2605`, [Linux]: :func:`sensors_battery` reports a negative amount for\n  seconds left.\n- :gh:`2607`, [Windows]: ``WindowsService.description()`` method may fail with\n  ``ERROR_NOT_FOUND``. Now it returns an empty string instead.\n- 2610:, [macOS], [CRITICAL]: fix :func:`cpu_freq` segfault on ARM\n  architectures.\n\n**Compatibility notes**\n\n- :gh:`2571`: dropped support for FreeBSD 8 and earlier.\n\n7.0.0 — 2025-02-13\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`669`, [Windows]: :func:`net_if_addrs` also returns the ``broadcast``\n  address instead of ``None``.\n- :gh:`2480`: Python 2.7 is no longer supported. Latest version supporting\n  Python 2.7 is psutil 6.1.X. Install it with: ``pip2 install psutil==6.1.*``.\n- :gh:`2490`: removed long deprecated ``Process.memory_info_ex()`` method. It\n  was deprecated in psutil 4.0.0, released 8 years ago. Substitute is\n  ``Process.memory_full_info()``.\n\n**Bug fixes**\n\n- :gh:`2496`, [Linux]: Avoid segfault (a cPython bug) on\n  ``Process.memory_maps()`` for processes that use hundreds of GBs of memory.\n- :gh:`2502`, [macOS]: :func:`virtual_memory` now relies on\n  ``host_statistics64`` instead of ``host_statistics``. This is the same\n  approach used by ``vm_stat`` CLI tool, and should grant more accurate\n  results.\n\n**Compatibility notes**\n\n- :gh:`2480`: Python 2.7 is no longer supported.\n- :gh:`2490`: removed long deprecated ``Process.memory_info_ex()`` method.\n\n6.1.1 — 2024-12-19\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2471`: use Vulture CLI tool to detect dead code.\n\n**Bug fixes**\n\n- :gh:`2418`, [Linux]: fix race condition in case /proc/PID/stat does not\n  exist, but /proc/PID does, resulting in FileNotFoundError.\n- :gh:`2470`, [Linux]: :func:`users` may return \"localhost\" instead of the\n  actual IP address of the user logged in.\n\n6.1.0 — 2024-10-17\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2366`, [Windows]: drastically speedup :func:`process_iter`. We now\n  determine process unique identity by using process \"fast\" create time method.\n  This will considerably speedup those apps which use :func:`process_iter`\n  only once, e.g. to look for a process with a certain name.\n- :gh:`2446`: use pytest instead of unittest.\n- :gh:`2448`: add ``make install-sysdeps`` target to install the necessary\n  system dependencies (python-dev, gcc, etc.) on all supported UNIX flavors.\n- :gh:`2449`: add ``make install-pydeps-test`` and ``make install-pydeps-dev``\n  targets. They can be used to install dependencies meant for running tests and\n  for local development. They can also be installed via ``pip install .[test]``\n  and ``pip install .[dev]``.\n- :gh:`2456`: allow to run tests via ``python3 -m psutil.tests`` even if\n  ``pytest`` module is not installed. This is useful for production\n  environments that don't have pytest installed, but still want to be able to\n  test psutil installation.\n\n**Bug fixes**\n\n- :gh:`2427`: psutil (segfault) on import in the free-threaded (no GIL) version\n  of Python 3.13.  (patch by Sam Gross)\n- :gh:`2455`, [Linux]: ``IndexError`` may occur when reading /proc/pid/stat and\n  field 40 (blkio_ticks) is missing.\n- :gh:`2457`, [AIX]: significantly improve the speed of\n  :meth:`Process.open_files` for some edge cases.\n- :gh:`2460`, [OpenBSD]: :meth:`Process.num_fds` and\n  :meth:`Process.open_files` may fail with :exc:`NoSuchProcess` for PID 0.\n  Instead, we now return \"null\" values (0 and [] respectively).\n\n6.0.0 — 2024-06-18\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2109`: ``maxfile`` and ``maxpath`` fields were removed from the\n  named tuple returned by :func:`disk_partitions`. Reason: on network\n  filesystems (NFS) this can potentially take a very long time to complete.\n- :gh:`2366`, [Windows]: log debug message when using slower process APIs.\n- :gh:`2375`, [macOS]: provide arm64 wheels.  (patch by Matthieu Darbois)\n- :gh:`2396`: :func:`process_iter` no longer pre-emptively checks whether\n  PIDs have been reused. This makes :func:`process_iter` around 20x times\n  faster.\n- :gh:`2396`: a new ``psutil.process_iter.cache_clear()`` API can be used the\n  clear\n  :func:`process_iter` internal cache.\n- :gh:`2401`, Support building with free-threaded CPython 3.13. (patch by Sam\n  Gross)\n- :gh:`2407`: :meth:`Process.connections` was renamed to\n  :meth:`Process.net_connections`. The old name is still available, but it's\n  deprecated (triggers a ``DeprecationWarning``) and will be removed in the\n  future.\n- :gh:`2425`: [Linux]: provide aarch64 wheels.  (patch by Matthieu Darbois /\n  Ben Raz)\n\n**Bug fixes**\n\n- :gh:`2250`, [NetBSD]: :meth:`Process.cmdline` sometimes fail with EBUSY. It\n  usually happens for long cmdlines with lots of arguments. In this case retry\n  getting the cmdline for up to 50 times, and return an empty list as last\n  resort.\n- :gh:`2254`, [Linux]: offline cpus raise NotImplementedError in cpu_freq()\n  (patch by Shade Gladden)\n- :gh:`2272`: Add pickle support to psutil Exceptions.\n- :gh:`2359`, [Windows], [CRITICAL]: :func:`pid_exists` disagrees with\n  :class:`Process` on whether a pid exists when ERROR_ACCESS_DENIED.\n- :gh:`2360`, [macOS]: can't compile on macOS < 10.13.  (patch by Ryan Schmidt)\n- :gh:`2362`, [macOS]: can't compile on macOS 10.11.  (patch by Ryan Schmidt)\n- :gh:`2365`, [macOS]: can't compile on macOS < 10.9.  (patch by Ryan Schmidt)\n- :gh:`2395`, [OpenBSD]: :func:`pid_exists` erroneously return True if the\n  argument is a thread ID (TID) instead of a PID (process ID).\n- :gh:`2412`, [macOS]: can't compile on macOS 10.4 PowerPC due to missing\n  `MNT_` constants.\n\n**Porting notes**\n\nVersion 6.0.0 introduces some changes which affect backward compatibility:\n\n- :gh:`2109`: the named tuple returned by :func:`disk_partitions`' no longer\n  has ``maxfile`` and ``maxpath`` fields.\n- :gh:`2396`: :func:`process_iter` no longer pre-emptively checks whether\n  PIDs have been reused. If you want to check for PID reusage you are supposed\n  to use\n  :meth:`Process.is_running` against the yielded :class:`Process` instances.\n  That will also automatically remove reused PIDs from :func:`process_iter`\n  internal cache.\n- :gh:`2407`: :meth:`Process.connections` was renamed to\n  :meth:`Process.net_connections`. The old name is still available, but it's\n  deprecated (triggers a ``DeprecationWarning``) and will be removed in the\n  future.\n\n5.9.8 — 2024-01-19\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2343`, [FreeBSD]: filter :func:`net_connections` returned list in C\n  instead of Python, and avoid to retrieve unnecessary connection types unless\n  explicitly asked. E.g., on an IDLE system with few IPv6 connections this will\n  run around 4 times faster. Before all connection types (TCP, UDP, UNIX) were\n  retrieved internally, even if only a portion was returned.\n- :gh:`2342`, [NetBSD]: same as above (#2343) but for NetBSD.\n- :gh:`2349`: adopted black formatting style.\n\n**Bug fixes**\n\n- :gh:`930`, [NetBSD], [critical]: :func:`net_connections` implementation was\n  broken. It could either leak memory or core dump.\n- :gh:`2340`, [NetBSD]: if process is terminated, :meth:`Process.cwd` will\n  return an empty string instead of raising :exc:`NoSuchProcess`.\n- :gh:`2345`, [Linux]: fix compilation on older compiler missing\n  DUPLEX_UNKNOWN.\n- :gh:`2222`, [macOS]: `cpu_freq()` now returns fixed values for `min` and\n  `max` frequencies in all Apple Silicon chips.\n\n5.9.7 — 2023-12-17\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2324`: enforce Ruff rule `raw-string-in-exception`, which helps\n  providing clearer tracebacks when exceptions are raised by psutil.\n\n**Bug fixes**\n\n- :gh:`2325`, [PyPy]: psutil did not compile on PyPy due to missing\n  `PyErr_SetExcFromWindowsErrWithFilenameObject` cPython API.\n\n5.9.6 — 2023-10-15\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1703`: :func:`cpu_percent` and :func:`cpu_times_percent` are now\n  thread safe, meaning they can be called from different threads and still\n  return meaningful and independent results. Before, if (say) 10 threads called\n  ``cpu_percent(interval=None)`` at the same time, only 1 thread out of 10\n  would get the right result.\n- :gh:`2266`: if :class:`Process` class is passed a very high PID, raise\n  :exc:`NoSuchProcess` instead of OverflowError.  (patch by Xuehai Pan)\n- :gh:`2246`: drop python 3.4 & 3.5 support.  (patch by Matthieu Darbois)\n- :gh:`2290`: PID reuse is now pre-emptively checked for :meth:`Process.ppid`\n  and\n  :meth:`Process.parents`.\n- :gh:`2312`: use ``ruff`` Python linter instead of ``flake8 + isort``. It's an\n  order of magnitude faster + it adds a ton of new code quality checks.\n\n**Bug fixes**\n\n- :gh:`2195`, [Linux]: no longer print exception at import time in case\n  /proc/stat can't be read due to permission error. Redirect it to\n  ``PSUTIL_DEBUG`` instead.\n- :gh:`2241`, [NetBSD]: can't compile On NetBSD 10.99.3/amd64.  (patch by\n  Thomas Klausner)\n- :gh:`2245`, [Windows]: fix var unbound error on possibly in\n  :func:`swap_memory` (patch by student_2333)\n- :gh:`2268`: ``bytes2human()`` utility function was unable to properly\n  represent negative values.\n- :gh:`2252`, [Windows]: :func:`disk_usage` fails on Python 3.12+.  (patch by\n  Matthieu Darbois)\n- :gh:`2284`, [Linux]: :meth:`Process.memory_full_info` may incorrectly raise\n  :exc:`ZombieProcess` if it's determined via ``/proc/pid/smaps_rollup``.\n  Instead we now fallback on reading ``/proc/pid/smaps``.\n- :gh:`2287`, [OpenBSD], [NetBSD]: :meth:`Process.is_running` erroneously\n  return ``False`` for zombie processes, because creation time cannot be\n  determined.\n- :gh:`2288`, [Linux]: correctly raise :exc:`ZombieProcess` on\n  :meth:`Process.exe`,\n  :meth:`Process.cmdline` and :meth:`Process.memory_maps` instead of\n  returning a \"null\" value.\n- :gh:`2290`: differently from what stated in the doc, PID reuse is not\n  pre-emptively checked for :meth:`Process.nice` (set),\n  :meth:`Process.ionice`, (set), :meth:`Process.cpu_affinity` (set),\n  :meth:`Process.rlimit` (set), :meth:`Process.parent`.\n- :gh:`2308`, [OpenBSD]: :meth:`Process.threads` always fail with\n  :exc:`AccessDenied` (also as root).\n\n5.9.5 — 2023-04-17\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2196`: in case of exception, display a cleaner error traceback by hiding\n  the `KeyError` bit deriving from a missed cache hit.\n- :gh:`2217`: print the full traceback when a `DeprecationWarning` or\n  `UserWarning` is raised.\n- :gh:`2230`, [OpenBSD]: :func:`net_connections` implementation was rewritten\n  from scratch:\n\n  - We're now able to retrieve the path of AF_UNIX sockets (before it was an\n    empty string)\n  - The function is faster since it no longer iterates over all processes.\n  - No longer produces duplicate connection entries.\n- :gh:`2238`: there are cases where :meth:`Process.cwd` cannot be determined\n  (e.g. directory no longer exists), in which case we returned either ``None``\n  or an empty string. This was consolidated and we now return ``\"\"`` on all\n  platforms.\n- :gh:`2239`, [UNIX]: if process is a zombie, and we can only determine part of\n  the its truncated :meth:`Process.name` (15 chars), don't fail with\n  :exc:`ZombieProcess` when we try to guess the full name from the\n  :meth:`Process.cmdline`. Just return the truncated name.\n- :gh:`2240`, [NetBSD], [OpenBSD]: add CI testing on every commit for NetBSD\n  and OpenBSD platforms (python 3 only).\n\n**Bug fixes**\n\n- :gh:`1043`, [OpenBSD] :func:`net_connections` returns duplicate entries.\n- :gh:`1915`, [Linux]: on certain kernels, ``\"MemAvailable\"`` field from\n  ``/proc/meminfo`` returns ``0`` (possibly a kernel bug), in which case we\n  calculate an approximation for ``available`` memory which matches \"free\" CLI\n  utility.\n- :gh:`2164`, [Linux]: compilation fails on kernels < 2.6.27 (e.g. CentOS 5).\n- :gh:`2186`, [FreeBSD]: compilation fails with Clang 15.  (patch by Po-Chuan\n  Hsieh)\n- :gh:`2191`, [Linux]: :func:`disk_partitions`: do not unnecessarily read\n  /proc/filesystems and raise :exc:`AccessDenied` unless user specified\n  `all=False` argument.\n- :gh:`2216`, [Windows]: fix tests when running in a virtual environment (patch\n  by Matthieu Darbois)\n- :gh:`2225`, [POSIX]: :func:`users` loses precision for ``started``\n  attribute (off by 1 minute).\n- :gh:`2229`, [OpenBSD]: unable to properly recognize zombie processes.\n  :exc:`NoSuchProcess` may be raised instead of :exc:`ZombieProcess`.\n- :gh:`2231`, [NetBSD]: *available*  :func:`virtual_memory` is higher than\n  *total*.\n- :gh:`2234`, [NetBSD]: :func:`virtual_memory` metrics are wrong: *available*\n  and *used* are too high. We now match values shown by *htop* CLI utility.\n- :gh:`2236`, [NetBSD]: :meth:`Process.num_threads` and\n  :meth:`Process.threads` return threads that are already terminated.\n- :gh:`2237`, [OpenBSD], [NetBSD]: :meth:`Process.cwd` may raise\n  ``FileNotFoundError`` if cwd no longer exists. Return an empty string\n  instead.\n\n5.9.4 — 2022-11-07\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2102`: use Limited API when building wheels with CPython 3.6+ on Linux,\n  macOS and Windows. This allows to use pre-built wheels in all future versions\n  of cPython 3.  (patch by Matthieu Darbois)\n\n**Bug fixes**\n\n- :gh:`2077`, [Windows]: Use system-level values for :func:`virtual_memory`.\n  (patch by Daniel Widdis)\n- :gh:`2156`, [Linux]: compilation may fail on very old gcc compilers due to\n  missing ``SPEED_UNKNOWN`` definition.  (patch by Amir Rossert)\n- :gh:`2010`, [macOS]: on MacOS, arm64 ``IFM_1000_TX`` and ``IFM_1000_T`` are\n  the same value, causing a build failure.  (patch by Lawrence D'Anna)\n- :gh:`2160`, [Windows]: Get Windows percent swap usage from performance\n  counters. (patch by Daniel Widdis)\n\n5.9.3 — 2022-10-18\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`2040`, [macOS]: provide wheels for arm64 architecture.  (patch by\n  Matthieu Darbois)\n\n**Bug fixes**\n\n- :gh:`2116`, [macOS], [critical]: :func:`net_connections` fails with\n  RuntimeError.\n- :gh:`2135`, [macOS]: :meth:`Process.environ` may contain garbage data. Fix\n  out-of-bounds read around ``sysctl_procargs``.  (patch by Bernhard\n  Urban-Forster)\n- :gh:`2138`, [Linux], **[critical]**: can't compile psutil on Android due to\n  undefined ``ethtool_cmd_speed`` symbol.\n- :gh:`2142`, [POSIX]: :func:`net_if_stats` 's ``flags`` on Python 2 returned\n  unicode instead of str.  (patch by Matthieu Darbois)\n- :gh:`2147`, [macOS] Fix disk usage report on macOS 12+.  (patch by Matthieu\n  Darbois)\n- :gh:`2150`, [Linux] :meth:`Process.threads` may raise ``NoSuchProcess``.\n  Fix race condition.  (patch by Daniel Li)\n- :gh:`2153`, [macOS] Fix race condition in\n  test_posix.TestProcess.test_cmdline. (patch by Matthieu Darbois)\n\n5.9.2 — 2022-09-04\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`2093`, [FreeBSD], **[critical]**: :func:`pids` may fail with ENOMEM.\n  Dynamically increase the ``malloc()`` buffer size until it's big enough.\n- :gh:`2095`, [Linux]: :func:`net_if_stats` returns incorrect interface speed\n  for 100GbE network cards.\n- :gh:`2113`, [FreeBSD], **[critical]**: :func:`virtual_memory` may raise\n  ENOMEM due to missing ``#include <sys/param.h>`` directive.  (patch by Peter\n  Jeremy)\n- :gh:`2128`, [NetBSD]: :func:`swap_memory` was miscalculated.  (patch by\n  Thomas Klausner)\n\n5.9.1 — 2022-05-20\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1053`: drop Python 2.6 support.  (patches by Matthieu Darbois and Hugo\n  van Kemenade)\n- :gh:`2037`: Add additional flags to net_if_stats.\n- :gh:`2050`, [Linux]: increase ``read(2)`` buffer size from 1k to 32k when\n  reading ``/proc`` pseudo files line by line. This should help having more\n  consistent results.\n- :gh:`2057`, [OpenBSD]: add support for :func:`cpu_freq`.\n- :gh:`2107`, [Linux]: :meth:`Process.memory_full_info` (reporting process\n  USS/PSS/Swap memory) now reads ``/proc/pid/smaps_rollup`` instead of\n  ``/proc/pids/smaps``, which makes it 5 times faster.\n\n**Bug fixes**\n\n- :gh:`2048`: ``AttributeError`` is raised if ``psutil.Error`` class is raised\n  manually and passed through ``str``.\n- :gh:`2049`, [Linux]: :func:`cpu_freq` erroneously returns ``curr`` value in\n  GHz while ``min`` and ``max`` are in MHz.\n- :gh:`2050`, [Linux]: :func:`virtual_memory` may raise ``ValueError`` if\n  running in a LCX container.\n\n5.9.0 — 2021-12-29\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1851`, [Linux]: :func:`cpu_freq` is slow on systems with many CPUs.\n  Read current frequency values for all CPUs from ``/proc/cpuinfo`` instead of\n  opening many files in ``/sys`` fs.  (patch by marxin)\n- :gh:`1992`: :exc:`NoSuchProcess` message now specifies if the PID has been\n  reused.\n- :gh:`1992`: error classes (:exc:`NoSuchProcess`, :exc:`AccessDenied`, etc.)\n  now have a better formatted and separated ``__repr__`` and ``__str__``\n  implementations.\n- :gh:`1996`, [BSD]: add support for MidnightBSD.  (patch by Saeed Rasooli)\n- :gh:`1999`, [Linux]: :func:`disk_partitions`: convert ``/dev/root`` device\n  (an alias used on some Linux distros) to real root device path.\n- :gh:`2005`: ``PSUTIL_DEBUG`` mode now prints file name and line number of the\n  debug messages coming from C extension modules.\n- :gh:`2042`: rewrite HISTORY.rst to use hyperlinks pointing to psutil API doc.\n\n**Bug fixes**\n\n- :gh:`1456`, [macOS], **[critical]**: :func:`cpu_freq` ``min`` and ``max``\n  are set to 0 if can't be determined (instead of crashing).\n- :gh:`1512`, [macOS]: sometimes :meth:`Process.connections` will crash with\n  ``EOPNOTSUPP`` for one connection; this is now ignored.\n- :gh:`1598`, [Windows]: :func:`disk_partitions` only returns mountpoints on\n  drives where it first finds one.\n- :gh:`1874`, [SunOS]: swap output error due to incorrect range.\n- :gh:`1892`, [macOS]: :func:`cpu_freq` broken on Apple M1.\n- :gh:`1901`, [macOS]: different functions, especially\n  :meth:`Process.open_files` and\n  :meth:`Process.connections`, could randomly raise :exc:`AccessDenied`\n  because the internal buffer of ``proc_pidinfo(PROC_PIDLISTFDS)`` syscall\n  was not big enough. We now dynamically increase the buffer size until\n  it's big enough instead of giving up and raising :exc:`AccessDenied`,\n  which was a fallback to avoid crashing.\n- :gh:`1904`, [Windows]: ``OpenProcess`` fails with ``ERROR_SUCCESS`` due to\n  ``GetLastError()`` called after ``sprintf()``.  (patch by alxchk)\n- :gh:`1913`, [Linux]: :func:`wait_procs` should catch\n  ``subprocess.TimeoutExpired`` exception.\n- :gh:`1919`, [Linux]: :func:`sensors_battery` can raise ``TypeError`` on\n  PureOS.\n- :gh:`1921`, [Windows]: :func:`swap_memory` shows committed memory instead\n  of swap.\n- :gh:`1940`, [Linux]: psutil does not handle ``ENAMETOOLONG`` when accessing\n  process file descriptors in procfs.  (patch by Nikita Radchenko)\n- :gh:`1948`, **[critical]**: ``memoize_when_activated`` decorator is not\n  thread-safe. (patch by Xuehai Pan)\n- :gh:`1953`, [Windows], **[critical]**: :func:`disk_partitions` crashes due\n  to insufficient buffer len. (patch by MaWe2019)\n- :gh:`1965`, [Windows], **[critical]**: fix \"Fatal Python error: deallocating\n  None\" when calling :func:`users` multiple times.\n- :gh:`1980`, [Windows]: 32bit / WoW64 processes fails to read\n  :meth:`Process.name` longer than 128 characters resulting in\n  :exc:`AccessDenied`. This is now fixed.  (patch by PetrPospisil)\n- :gh:`1991`, **[critical]**: :func:`process_iter` is not thread safe and can\n  raise ``TypeError`` if invoked from multiple threads.\n- :gh:`1956`, [macOS]: :meth:`Process.cpu_times` reports incorrect timings on\n  M1 machines. (patch by Olivier Dormond)\n- :gh:`2023`, [Linux]: :func:`cpu_freq` return order is wrong on systems with\n  more than 9 CPUs.\n\n5.8.0 — 2020-12-19\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1863`: :func:`disk_partitions` exposes 2 extra fields: ``maxfile`` and\n  ``maxpath``, which are the maximum file name and path name length.\n- :gh:`1872`, [Windows]: added support for PyPy 2.7.\n- :gh:`1879`: provide pre-compiled wheels for Linux and macOS (yey!).\n- :gh:`1880`: get rid of Travis and Cirrus CI services (they are no longer\n  free). CI testing is now done by GitHub Actions on Linux, macOS and FreeBSD\n  (yes). AppVeyor is still being used for Windows CI.\n\n**Bug fixes**\n\n- :gh:`1708`, [Linux]: get rid of :func:`sensors_temperatures` duplicates.\n  (patch by Tim Schlueter).\n- :gh:`1839`, [Windows], **[critical]**: always raise :exc:`AccessDenied`\n  instead of ``WindowsError`` when failing to query 64 processes from 32 bit\n  ones by using ``NtWoW64`` APIs.\n- :gh:`1866`, [Windows], **[critical]**: :meth:`Process.exe`,\n  :meth:`Process.cmdline`,\n  :meth:`Process.environ` may raise \"[WinError 998] Invalid access to memory\n  location\" on Python 3.9 / VS 2019.\n- :gh:`1874`, [SunOS]: wrong swap output given when encrypted column is\n  present.\n- :gh:`1875`, [Windows], **[critical]**: :meth:`Process.username` may raise\n  ``ERROR_NONE_MAPPED`` if the SID has no corresponding account name. In this\n  case :exc:`AccessDenied` is now raised.\n- :gh:`1886`, [macOS]: ``EIO`` error may be raised on :meth:`Process.cmdline`\n  and :meth:`Process.environ`. Now it gets translated into\n  :exc:`AccessDenied`.\n- :gh:`1887`, [Windows], **[critical]**: ``OpenProcess`` may fail with\n  \"[WinError 0] The operation completed successfully\".\" Turn it into\n  :exc:`AccessDenied` or :exc:`NoSuchProcess` depending on whether the PID is\n  alive.\n- :gh:`1891`, [macOS]: get rid of deprecated ``getpagesize()``.\n\n5.7.3 — 2020-10-23\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`809`, [FreeBSD]: add support for :meth:`Process.rlimit`.\n- :gh:`893`, [BSD]: add support for :meth:`Process.environ` (patch by Armin\n  Gruner)\n- :gh:`1830`, [POSIX]: :func:`net_if_stats` ``isup`` also checks whether the\n  NIC is running (meaning Wi-Fi or ethernet cable is connected).  (patch by\n  Chris Burger)\n- :gh:`1837`, [Linux]: improved battery detection and charge ``secsleft``\n  calculation (patch by aristocratos)\n\n**Bug fixes**\n\n- :gh:`1620`, [Linux]: :func:`cpu_count` with ``logical=False`` result is\n  incorrect on systems with more than one CPU socket.  (patch by Vincent A.\n  Arcila)\n- :gh:`1738`, [macOS]: :meth:`Process.exe` may raise ``FileNotFoundError`` if\n  process is still alive but the exe file which launched it got deleted.\n- :gh:`1791`, [macOS]: fix missing include for ``getpagesize()``.\n- :gh:`1823`, [Windows], **[critical]**: :meth:`Process.open_files` may cause\n  a segfault due to a NULL pointer.\n- :gh:`1838`, [Linux]: :func:`sensors_battery`: if `percent` can be\n  determined but not the remaining values, still return a result instead of\n  ``None``. (patch by aristocratos)\n\n5.7.2 — 2020-07-15\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- wheels for 2.7 were inadvertently deleted.\n\n5.7.1 — 2020-07-15\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1729`: parallel tests on POSIX (``make test-parallel``). They're twice\n  as fast!\n- :gh:`1741`, [POSIX]: ``make build`` now runs in parallel on Python >= 3.6 and\n  it's about 15% faster.\n- :gh:`1747`: :meth:`Process.wait` return value is cached so that the exit\n  code can be retrieved on then next call.\n- :gh:`1747`, [POSIX]: :meth:`Process.wait` on POSIX now returns an enum,\n  showing the negative signal which was used to terminate the process. It\n  returns something like ``<Negsignal.SIGTERM: -15>``.\n- :gh:`1747`: :class:`Process` class provides more info about the process on\n  ``str()`` and ``repr()`` (status and exit code).\n- :gh:`1757`: memory leak tests are now stable.\n- :gh:`1768`, [Windows]: added support for Windows Nano Server. (contributed by\n  Julien Lebot)\n\n**Bug fixes**\n\n- :gh:`1726`, [Linux]: :func:`cpu_freq` parsing should use spaces instead of\n  tabs on ia64. (patch by Michał Górny)\n- :gh:`1760`, [Linux]: :meth:`Process.rlimit` does not handle long long type\n  properly.\n- :gh:`1766`, [macOS]: :exc:`NoSuchProcess` may be raised instead of\n  :exc:`ZombieProcess`.\n- :gh:`1781`, **[critical]**: :func:`getloadavg` can crash the Python\n  interpreter. (patch by Ammar Askar)\n\n5.7.0 — 2020-02-18\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1637`, [SunOS]: add partial support for old SunOS 5.10 Update 0 to 3.\n- :gh:`1648`, [Linux]: :func:`sensors_temperatures` looks into an additional\n  ``/sys/device/`` directory for additional data.  (patch by Javad Karabi)\n- :gh:`1652`, [Windows]: dropped support for Windows XP and Windows Server\n  2003. Minimum supported Windows version now is Windows Vista.\n- :gh:`1671`, [FreeBSD]: add CI testing/service for FreeBSD (Cirrus CI).\n- :gh:`1677`, [Windows]: :meth:`Process.exe` will succeed for all process\n  PIDs (instead of raising :exc:`AccessDenied`).\n- :gh:`1679`, [Windows]: :func:`net_connections` and\n  :meth:`Process.connections` are 10% faster.\n- :gh:`1682`, [PyPy]: added CI / test integration for PyPy via Travis.\n- :gh:`1686`, [Windows]: added support for PyPy on Windows.\n- :gh:`1693`, [Windows]: :func:`boot_time`, :meth:`Process.create_time` and\n  :func:`users`'s login time now have 1 micro second precision (before the\n  precision was of 1 second).\n\n**Bug fixes**\n\n- :gh:`1538`, [NetBSD]: :meth:`Process.cwd` may return ``ENOENT`` instead of\n  :exc:`NoSuchProcess`.\n- :gh:`1627`, [Linux]: :meth:`Process.memory_maps` can raise ``KeyError``.\n- :gh:`1642`, [SunOS]: querying basic info for PID 0 results in\n  ``FileNotFoundError``.\n- :gh:`1646`, [FreeBSD], **[critical]**: many :class:`Process` methods may\n  cause a segfault due to a backward incompatible change in a C type on FreeBSD\n  12.0.\n- :gh:`1656`, [Windows]: :meth:`Process.memory_full_info` raises\n  :exc:`AccessDenied` even for the current user and os.getpid().\n- :gh:`1660`, [Windows]: :meth:`Process.open_files` complete rewrite + check\n  of errors.\n- :gh:`1662`, [Windows], **[critical]**: :meth:`Process.exe` may raise\n  \"[WinError 0] The operation completed successfully\".\n- :gh:`1665`, [Linux]: :func:`disk_io_counters` does not take into account\n  extra fields added to recent kernels.  (patch by Mike Hommey)\n- :gh:`1672`: use the right C type when dealing with PIDs (int or long). Thus\n  far (long) was almost always assumed, which is wrong on most platforms.\n- :gh:`1673`, [OpenBSD]: :meth:`Process.connections`,\n  :meth:`Process.num_fds` and\n  :meth:`Process.threads` returned improper exception if process is gone.\n- :gh:`1674`, [SunOS]: :func:`disk_partitions` may raise ``OSError``.\n- :gh:`1684`, [Linux]: :func:`disk_io_counters` may raise ``ValueError`` on\n  systems not having ``/proc/diskstats``.\n- :gh:`1695`, [Linux]: could not compile on kernels <= 2.6.13 due to\n  ``PSUTIL_HAS_IOPRIO`` not being defined.  (patch by Anselm Kruis)\n\n5.6.7 — 2019-11-26\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1630`, [Windows], **[critical]**: can't compile source distribution due\n  to C syntax error.\n\n5.6.6 — 2019-11-25\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1179`, [Linux]: :meth:`Process.cmdline` now takes into account\n  misbehaving processes renaming the command line and using inappropriate chars\n  to separate args.\n- :gh:`1616`, **[critical]**: use of ``Py_DECREF`` instead of ``Py_CLEAR`` will\n  result in double ``free()`` and segfault (`CVE-2019-18874\n  <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18874>`__). (patch\n  by Riccardo Schirone)\n- :gh:`1619`, [OpenBSD], **[critical]**: compilation fails due to C syntax\n  error. (patch by Nathan Houghton)\n\n5.6.5 — 2019-11-06\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1615`: remove ``pyproject.toml`` as it was causing installation issues.\n\n5.6.4 — 2019-11-04\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1527`, [Linux]: added :meth:`Process.cpu_times` ``iowait`` counter,\n  which is the time spent waiting for blocking I/O to complete.\n- :gh:`1565`: add PEP 517/8 build backend and requirements specification for\n  better pip integration.  (patch by Bernát Gábor)\n\n**Bug fixes**\n\n- :gh:`875`, [Windows], **[critical]**: :meth:`Process.cmdline`,\n  :meth:`Process.environ` or\n  :meth:`Process.cwd` may occasionally fail with ``ERROR_PARTIAL_COPY`` which\n  now gets translated to :exc:`AccessDenied`.\n- :gh:`1126`, [Linux], **[critical]**: :meth:`Process.cpu_affinity` segfaults\n  on CentOS 5 / manylinux. :meth:`Process.cpu_affinity` support for CentOS 5\n  was removed.\n- :gh:`1528`, [AIX], **[critical]**: compilation error on AIX 7.2 due to 32 vs\n  64 bit differences. (patch by Arnon Yaari)\n- :gh:`1535`: ``type`` and ``family`` fields returned by\n  :func:`net_connections` are not always turned into enums.\n- :gh:`1536`, [NetBSD]: :meth:`Process.cmdline` erroneously raise\n  :exc:`ZombieProcess` error if cmdline has non encodable chars.\n- :gh:`1546`: usage percent may be rounded to 0 on Python 2.\n- :gh:`1552`, [Windows]: :func:`getloadavg` math for calculating 5 and 15\n  mins values is incorrect.\n- :gh:`1568`, [Linux]: use CC compiler env var if defined.\n- :gh:`1570`, [Windows]: ``NtWow64*`` syscalls fail to raise the proper error\n  code\n- :gh:`1585`, [OSX]: avoid calling ``close()`` (in C) on possible negative\n  integers. (patch by Athos Ribeiro)\n- :gh:`1606`, [SunOS], **[critical]**: compilation fails on SunOS 5.10. (patch\n  by vser1)\n\n5.6.3 — 2019-06-11\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1494`, [AIX]: added support for :meth:`Process.environ`.  (patch by\n  Arnon Yaari)\n\n**Bug fixes**\n\n- :gh:`1276`, [AIX]: can't get whole :meth:`Process.cmdline`.  (patch by\n  Arnon Yaari)\n- :gh:`1501`, [Windows]: :meth:`Process.cmdline` and :meth:`Process.exe`\n  raise unhandled \"WinError 1168 element not found\" exceptions for \"Registry\"\n  and \"Memory Compression\" pseudo processes on Windows 10.\n- :gh:`1526`, [NetBSD], **[critical]**: :meth:`Process.cmdline` could raise\n  ``MemoryError``.  (patch by Kamil Rytarowski)\n\n5.6.2 — 2019-04-26\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`604`, [Windows]: add new :func:`getloadavg`, returning system load\n  average calculation, including on Windows (emulated).  (patch by Ammar Askar)\n- :gh:`1404`, [Linux]: :func:`cpu_count` with ``logical=False`` uses a second\n  method (read from ``/sys/devices/system/cpu/cpu[0-9]/topology/core_id``) in\n  order to determine the number of CPU cores in case ``/proc/cpuinfo`` does not\n  provide this info.\n- :gh:`1458`: provide coloured test output. Also show failures on\n  ``KeyboardInterrupt``.\n- :gh:`1464`: various docfixes (always point to Python 3 doc, fix links, etc.).\n- :gh:`1476`, [Windows]: it is now possible to set process high I/O priority\n  (:meth:`Process.ionice`). Also, I/O priority values are now exposed as 4\n  new constants: ``IOPRIO_VERYLOW``, ``IOPRIO_LOW``, ``IOPRIO_NORMAL``,\n  ``IOPRIO_HIGH``.\n- :gh:`1478`: add make command to re-run tests failed on last run.\n\n**Bug fixes**\n\n- :gh:`1223`, [Windows]: :func:`boot_time` may return incorrect value on\n  Windows XP.\n- :gh:`1456`, [Linux]: :func:`cpu_freq` returns ``None`` instead of 0.0 when\n  ``min`` and ``max`` fields can't be determined. (patch by Alex Manuskin)\n- :gh:`1462`, [Linux]: (tests) make tests invariant to ``LANG`` setting (patch\n  by Benjamin Drung)\n- :gh:`1463`: `cpu_distribution.py`_ script was broken.\n- :gh:`1470`, [Linux]: :func:`disk_partitions`: fix corner case when\n  ``/etc/mtab`` doesn't exist.  (patch by Cedric Lamoriniere)\n- :gh:`1471`, [SunOS]: :meth:`Process.name` and :meth:`Process.cmdline` can\n  return ``SystemError``.  (patch by Daniel Beer)\n- :gh:`1472`, [Linux]: :func:`cpu_freq` does not return all CPUs on\n  Raspberry-pi 3.\n- :gh:`1474`: fix formatting of ``psutil.tests()`` which mimics ``ps aux``\n  output.\n- :gh:`1475`, [Windows], **[critical]**: ``OSError.winerror`` attribute wasn't\n  properly checked resulting in ``WindowsError(ERROR_ACCESS_DENIED)`` being\n  raised instead of :exc:`AccessDenied`.\n- :gh:`1477`, [Windows]: wrong or absent error handling for private\n  ``NTSTATUS`` Windows APIs. Different process methods were affected by this.\n- :gh:`1480`, [Windows], **[critical]**: :func:`cpu_count` with\n  ``logical=False`` could cause a crash due to fixed read violation.  (patch by\n  Samer Masterson)\n- :gh:`1486`, [AIX], [SunOS]: ``AttributeError`` when interacting with\n  :class:`Process` methods involved into :meth:`Process.oneshot` context.\n- :gh:`1491`, [SunOS]: :func:`net_if_addrs`: use ``free()`` against ``ifap``\n  struct on error.  (patch by Agnewee)\n- :gh:`1493`, [Linux]: :func:`cpu_freq`: handle the case where\n  ``/sys/devices/system/cpu/cpufreq/`` exists but it's empty.\n\n5.6.1 — 2019-03-11\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1329`, [AIX]: psutil doesn't compile on AIX 6.1.  (patch by Arnon Yaari)\n- :gh:`1448`, [Windows], **[critical]**: crash on import due to\n  ``rtlIpv6AddressToStringA`` not available on Wine.\n- :gh:`1451`, [Windows], **[critical]**: :meth:`Process.memory_full_info`\n  segfaults. ``NtQueryVirtualMemory`` is now used instead of\n  ``QueryWorkingSet`` to calculate USS memory.\n\n5.6.0 — 2019-03-05\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1379`, [Windows]: :meth:`Process.suspend` and :meth:`Process.resume`\n  now use ``NtSuspendProcess`` and ``NtResumeProcess`` instead of\n  stopping/resuming all threads of a process. This is faster and more reliable\n  (aka this is what ProcessHacker does).\n- :gh:`1420`, [Windows]: in case of exception :func:`disk_usage` now also\n  shows the path name.\n- :gh:`1422`, [Windows]: Windows APIs requiring to be dynamically loaded from\n  DLL libraries are now loaded only once on startup (instead of on per function\n  call) significantly speeding up different functions and methods.\n- :gh:`1426`, [Windows]: ``PAGESIZE`` and number of processors is now\n  calculated on startup.\n- :gh:`1428`: in case of error, the traceback message now shows the underlying\n  C function called which failed.\n- :gh:`1433`: new :meth:`Process.parents` method.  (idea by Ghislain Le Meur)\n- :gh:`1437`: :func:`pids` are returned in sorted order.\n- :gh:`1442`: Python 3 is now the default interpreter used by Makefile.\n\n**Bug fixes**\n\n- :gh:`1353`: :func:`process_iter` is now thread safe (it rarely raised\n  ``TypeError``).\n- :gh:`1394`, [Windows], **[critical]**: :meth:`Process.name` and\n  :meth:`Process.exe` may erroneously return \"Registry\" or fail with \"[Error\n  0] The operation completed successfully\". ``QueryFullProcessImageNameW``\n  is now used instead of ``GetProcessImageFileNameW`` in order to prevent\n  that.\n- :gh:`1411`, [BSD]: lack of ``Py_DECREF`` could cause segmentation fault on\n  process instantiation.\n- :gh:`1419`, [Windows]: :meth:`Process.environ` raises\n  ``NotImplementedError`` when querying a 64-bit process in 32-bit-WoW mode.\n  Now it raises\n  :exc:`AccessDenied`.\n- :gh:`1427`, [OSX]: :meth:`Process.cmdline` and :meth:`Process.environ`\n  may erroneously raise ``OSError`` on failed ``malloc()``.\n- :gh:`1429`, [Windows]: ``SE DEBUG`` was not properly set for current process.\n  It is now, and it should result in less :exc:`AccessDenied` exceptions for\n  low PID processes.\n- :gh:`1432`, [Windows]: :meth:`Process.memory_info_ex`'s USS memory is\n  miscalculated because we're not using the actual system ``PAGESIZE``.\n- :gh:`1439`, [NetBSD]: :meth:`Process.connections` may return incomplete\n  results if using\n  :meth:`Process.oneshot`.\n- :gh:`1447`: original exception wasn't turned into :exc:`NoSuchProcess` /\n  :exc:`AccessDenied` exceptions when using :meth:`Process.oneshot` context\n  manager.\n\n**Incompatible API changes**\n\n- :gh:`1291`, [OSX], **[critical]**: :meth:`Process.memory_maps` was removed\n  because inherently broken (segfault) for years.\n\n5.5.1 — 2019-02-15\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1348`, [Windows]: on Windows >= 8.1 if :meth:`Process.cmdline` fails\n  due to ``ERROR_ACCESS_DENIED`` attempt using ``NtQueryInformationProcess`` +\n  ``ProcessCommandLineInformation``. (patch by EccoTheFlintstone)\n\n**Bug fixes**\n\n- :gh:`1394`, [Windows]: :meth:`Process.exe` returns \"[Error 0] The operation\n  completed successfully\" when Python process runs in \"Virtual Secure Mode\".\n- :gh:`1402`: psutil exceptions' ``repr()`` show the internal private module\n  path.\n- :gh:`1408`, [AIX], **[critical]**: psutil won't compile on AIX 7.1 due to\n  missing header.  (patch by Arnon Yaari)\n\n5.5.0 — 2019-01-23\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1350`, [FreeBSD]: added support for :func:`sensors_temperatures`.\n  (patch by Alex Manuskin)\n- :gh:`1352`, [FreeBSD]: added support for :func:`cpu_freq`.  (patch by Alex\n  Manuskin)\n\n**Bug fixes**\n\n- :gh:`1111`: :meth:`Process.oneshot` is now thread safe.\n- :gh:`1354`, [Linux]: :func:`disk_io_counters` fails on Linux kernel 4.18+.\n- :gh:`1357`, [Linux]: :meth:`Process.memory_maps` and\n  :meth:`Process.io_counters` methods are no longer exposed if not supported\n  by the kernel.\n- :gh:`1368`, [Windows]: fix :meth:`Process.ionice` mismatch.  (patch by\n  EccoTheFlintstone)\n- :gh:`1370`, [Windows]: improper usage of ``CloseHandle()`` may lead to\n  override the original error code when raising an exception.\n- :gh:`1373`, **[critical]**: incorrect handling of cache in\n  :meth:`Process.oneshot` context causes :class:`Process` instances to return\n  incorrect results.\n- :gh:`1376`, [Windows]: ``OpenProcess`` now uses\n  ``PROCESS_QUERY_LIMITED_INFORMATION`` access rights wherever possible,\n  resulting in less :exc:`AccessDenied` exceptions being thrown for system\n  processes.\n- :gh:`1376`, [Windows]: check if variable is ``NULL`` before ``free()`` ing\n  it. (patch by EccoTheFlintstone)\n\n5.4.8 — 2018-10-30\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1197`, [Linux]: :func:`cpu_freq` is now implemented by parsing\n  ``/proc/cpuinfo`` in case ``/sys/devices/system/cpu/*`` filesystem is not\n  available.\n- :gh:`1310`, [Linux]: :func:`sensors_temperatures` now parses\n  ``/sys/class/thermal`` in case ``/sys/class/hwmon`` fs is not available (e.g.\n  Raspberry Pi).  (patch by Alex Manuskin)\n- :gh:`1320`, [POSIX]: better compilation support when using g++ instead of\n  GCC. (patch by Jaime Fullaondo)\n\n**Bug fixes**\n\n- :gh:`715`: do not print exception on import time in case :func:`cpu_times`\n  fails.\n- :gh:`1004`, [Linux]: :meth:`Process.io_counters` may raise ``ValueError``.\n- :gh:`1277`, [OSX]: available and used memory (:func:`virtual_memory`)\n  metrics are not accurate.\n- :gh:`1294`, [Windows]: :meth:`Process.connections` may sometimes fail with\n  intermittent ``0xC0000001``.  (patch by Sylvain Duchesne)\n- :gh:`1307`, [Linux]: :func:`disk_partitions` does not honour\n  :data:`PROCFS_PATH`.\n- :gh:`1320`, [AIX]: system CPU times (:func:`cpu_times`) were being reported\n  with ticks unit as opposed to seconds.  (patch by Jaime Fullaondo)\n- :gh:`1332`, [OSX]: psutil debug messages are erroneously printed all the\n  time. (patch by Ilya Yanok)\n- :gh:`1346`, [SunOS]: :func:`net_connections` returns an empty list.  (patch\n  by Oleksii Shevchuk)\n\n5.4.7 — 2018-08-14\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1286`, [macOS]: ``psutil.OSX`` constant is now deprecated in favor of\n  new ``psutil.MACOS``.\n- :gh:`1309`, [Linux]: added ``psutil.STATUS_PARKED`` constant for\n  :meth:`Process.status`.\n- :gh:`1321`, [Linux]: add :func:`disk_io_counters` dual implementation\n  relying on ``/sys/block`` filesystem in case ``/proc/diskstats`` is not\n  available. (patch by Lawrence Ye)\n\n**Bug fixes**\n\n- :gh:`1209`, [macOS]: :meth:`Process.memory_maps` may fail with ``EINVAL``\n  due to poor ``task_for_pid()`` syscall. :exc:`AccessDenied` is now raised\n  instead.\n- :gh:`1278`, [macOS]: :meth:`Process.threads` incorrectly return\n  microseconds instead of seconds. (patch by Nikhil Marathe)\n- :gh:`1279`, [Linux], [macOS], [BSD]: :func:`net_if_stats` may return\n  ``ENODEV``.\n- :gh:`1294`, [Windows]: :meth:`Process.connections` may sometime fail with\n  ``MemoryError``.  (patch by sylvainduchesne)\n- :gh:`1305`, [Linux]: :func:`disk_io_counters` may report inflated r/w bytes\n  values.\n- :gh:`1309`, [Linux]: :meth:`Process.status` is unable to recognize\n  ``\"idle\"`` and ``\"parked\"`` statuses (returns ``\"?\"``).\n- :gh:`1313`, [Linux]: :func:`disk_io_counters` can report inflated values\n  due to counting base disk device and its partition(s) twice.\n- :gh:`1323`, [Linux]: :func:`sensors_temperatures` may fail with\n  ``ValueError``.\n\n5.4.6 — 2018-06-07\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1258`, [Windows], **[critical]**: :meth:`Process.username` may cause a\n  segfault (Python interpreter crash).  (patch by Jean-Luc Migot)\n- :gh:`1273`: :func:`net_if_addrs` named tuple's name has been renamed from\n  ``snic`` to ``snicaddr``.\n- :gh:`1274`, [Linux]: there was a small chance :meth:`Process.children` may\n  swallow\n  :exc:`AccessDenied` exceptions.\n\n5.4.5 — 2018-04-14\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1268`: setup.py's ``extra_require`` parameter requires latest setuptools\n  version, breaking quite a lot of installations.\n\n5.4.4 — 2018-04-13\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1239`, [Linux]: expose kernel ``slab`` memory field for\n  :func:`virtual_memory`. (patch by Maxime Mouial)\n\n**Bug fixes**\n\n- :gh:`694`, [SunOS]: :meth:`Process.cmdline` could be truncated at the 15th\n  character when reading it from ``/proc``. An extra effort is made by reading\n  it from process address space first.  (patch by Georg Sauthoff)\n- :gh:`771`, [Windows]: :func:`cpu_count` (both logical and cores) return a\n  wrong (smaller) number on systems using process groups (> 64 cores).\n- :gh:`771`, [Windows]: :func:`cpu_times` with ``percpu=True`` return fewer\n  CPUs on systems using process groups (> 64 cores).\n- :gh:`771`, [Windows]: :func:`cpu_stats` and :func:`cpu_freq` may return\n  incorrect results on systems using process groups (> 64 cores).\n- :gh:`1193`, [SunOS]: return uid/gid from ``/proc/pid/psinfo`` if there aren't\n  enough permissions for ``/proc/pid/cred``.  (patch by Georg Sauthoff)\n- :gh:`1194`, [SunOS]: return nice value from ``psinfo`` as ``getpriority()``\n  doesn't support real-time processes.  (patch by Georg Sauthoff)\n- :gh:`1194`, [SunOS]: fix double ``free()`` in :meth:`Process.cpu_num`.\n  (patch by Georg Sauthoff)\n- :gh:`1194`, [SunOS]: fix undefined behavior related to strict-aliasing rules\n  and warnings.  (patch by Georg Sauthoff)\n- :gh:`1210`, [Linux]: :func:`cpu_percent` steal time may remain stuck at\n  100% due to Linux erroneously reporting a decreased steal time between calls.\n  (patch by Arnon Yaari)\n- :gh:`1216`: fix compatibility with Python 2.6 on Windows (patch by Dan\n  Vinakovsky)\n- :gh:`1222`, [Linux]: :meth:`Process.memory_full_info` was erroneously\n  summing \"Swap:\" and \"SwapPss:\". Same for \"Pss:\" and \"SwapPss\". Not anymore.\n- :gh:`1224`, [Windows]: :meth:`Process.wait` may erroneously raise\n  :exc:`TimeoutExpired`.\n- :gh:`1238`, [Linux]: :func:`sensors_battery` may return ``None`` in case\n  battery is not listed as \"BAT0\" under ``/sys/class/power_supply``.\n- :gh:`1240`, [Windows]: :func:`cpu_times` float loses accuracy in a long\n  running system. (patch by stswandering)\n- :gh:`1245`, [Linux]: :func:`sensors_temperatures` may fail with ``IOError``\n  \"no such file\".\n- :gh:`1255`, [FreeBSD]: :func:`swap_memory` stats were erroneously\n  represented in KB. (patch by Denis Krienbühl)\n\n**Backward compatibility**\n\n- :gh:`771`, [Windows]: :func:`cpu_count` with ``logical=False`` on Windows\n  XP and Vista is no longer supported and returns ``None``.\n\n5.4.3 — 2018-01-01\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`775`: :func:`disk_partitions` on Windows return mount points.\n\n**Bug fixes**\n\n- :gh:`1193`: :func:`pids` may return ``False`` on macOS.\n\n5.4.2 — 2017-12-07\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1173`: introduced ``PSUTIL_DEBUG`` environment variable which can be set\n  in order to print useful debug messages on stderr (useful in case of nasty\n  errors).\n- :gh:`1177`, [macOS]: added support for :func:`sensors_battery`.  (patch by\n  Arnon Yaari)\n- :gh:`1183`: :meth:`Process.children` is 2x faster on POSIX and 2.4x faster\n  on Linux.\n- :gh:`1188`: deprecated method :meth:`Process.memory_info_ex` now warns by\n  using ``FutureWarning`` instead of ``DeprecationWarning``.\n\n**Bug fixes**\n\n- :gh:`1152`, [Windows]: :func:`disk_io_counters` may return an empty dict.\n- :gh:`1169`, [Linux]: :func:`users` ``hostname`` returns username instead.\n  (patch by janderbrain)\n- :gh:`1172`, [Windows]: ``make test`` does not work.\n- :gh:`1179`, [Linux]: :meth:`Process.cmdline` is now able to split cmdline\n  args for misbehaving processes which overwrite ``/proc/pid/cmdline`` and use\n  spaces instead of null bytes as args separator.\n- :gh:`1181`, [macOS]: :meth:`Process.memory_maps` may raise ``ENOENT``.\n- :gh:`1187`, [macOS]: :func:`pids` does not return PID 0 on recent macOS\n  versions.\n\n5.4.1 — 2017-11-08\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1164`, [AIX]: add support for :meth:`Process.num_ctx_switches`.\n  (patch by Arnon Yaari)\n- :gh:`1053`: drop Python 3.3 support (psutil still works but it's no longer\n  tested).\n\n**Bug fixes**\n\n- :gh:`1150`, [Windows]: when a process is terminated now the exit code is set\n  to ``SIGTERM`` instead of ``0``.  (patch by Akos Kiss)\n- :gh:`1151`: ``python -m psutil.tests`` fail.\n- :gh:`1154`, [AIX], **[critical]**: psutil won't compile on AIX 6.1.0. (patch\n  by Arnon Yaari)\n- :gh:`1167`, [Windows]: :func:`net_io_counters` packets count now include\n  also non-unicast packets.  (patch by Matthew Long)\n\n5.4.0 — 2017-10-12\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1123`, [AIX]: added support for AIX platform.  (patch by Arnon Yaari)\n\n**Bug fixes**\n\n- :gh:`1009`, [Linux]: :func:`sensors_temperatures` may crash with\n  ``IOError``.\n- :gh:`1012`, [Windows]: :func:`disk_io_counters` ``read_time`` and\n  ``write_time`` were expressed in tens of micro seconds instead of\n  milliseconds.\n- :gh:`1127`, [macOS], **[critical]**: invalid reference counting in\n  :meth:`Process.open_files` may lead to segfault.  (patch by Jakub Bacic)\n- :gh:`1129`, [Linux]: :func:`sensors_fans` may crash with ``IOError``.\n  (patch by Sebastian Saip)\n- :gh:`1131`, [SunOS]: fix compilation warnings.  (patch by Arnon Yaari)\n- :gh:`1133`, [Windows]: can't compile on newer versions of Visual Studio 2017\n  15.4. (patch by Max Bélanger)\n- :gh:`1138`, [Linux]: can't compile on CentOS 5.0 and RedHat 5.0. (patch by\n  Prodesire)\n\n5.3.1 — 2017-09-10\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`1124`: documentation moved to http://psutil.readthedocs.io\n\n**Bug fixes**\n\n- :gh:`1105`, [FreeBSD]: psutil does not compile on FreeBSD 12.\n- :gh:`1125`, [BSD]: :func:`net_connections` raises ``TypeError``.\n\n**Compatibility notes**\n\n- :gh:`1120`: ``.exe`` files for Windows are no longer uploaded on PyPI as per\n  PEP-527. Only wheels are provided.\n\n5.3.0 — 2017-09-01\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`802`: :func:`disk_io_counters` and :func:`net_io_counters` numbers\n  no longer wrap (restart from 0). Introduced a new ``nowrap`` argument.\n- :gh:`928`: :func:`net_connections` and :meth:`Process.connections`\n  ``laddr`` and ``raddr`` are now named tuples.\n- :gh:`1015`: :func:`swap_memory` now relies on ``/proc/meminfo`` instead of\n  ``sysinfo()`` syscall so that it can be used in conjunction with\n  :data:`PROCFS_PATH` in order to retrieve memory info about Linux containers\n  such as Docker and Heroku.\n- :gh:`1022`: :func:`users` provides a new ``pid`` field.\n- :gh:`1025`: :func:`process_iter` accepts two new parameters in order to\n  invoke\n  :meth:`Process.as_dict`: ``attrs`` and ``ad_value``. With these you can\n  iterate over all processes in one shot without needing to catch\n  :exc:`NoSuchProcess` and do list/dict comprehensions.\n- :gh:`1040`: implemented full unicode support.\n- :gh:`1051`: :func:`disk_usage` on Python 3 is now able to accept bytes.\n- :gh:`1058`: test suite now enables all warnings by default.\n- :gh:`1060`: source distribution is dynamically generated so that it only\n  includes relevant files.\n- :gh:`1079`, [FreeBSD]: :func:`net_connections` ``fd`` number is now being\n  set for real (instead of ``-1``).  (patch by Gleb Smirnoff)\n- :gh:`1091`, [SunOS]: implemented :meth:`Process.environ`.  (patch by\n  Oleksii Shevchuk)\n\n**Bug fixes**\n\n- :gh:`989`, [Windows]: :func:`boot_time` may return a negative value.\n- :gh:`1007`, [Windows]: :func:`boot_time` can have a 1 sec fluctuation\n  between calls. The value of the first call is now cached so that\n  :func:`boot_time` always returns the same value if fluctuation is <= 1\n  second.\n- :gh:`1013`, [FreeBSD]: :func:`net_connections` may return incorrect PID.\n  (patch by Gleb Smirnoff)\n- :gh:`1014`, [Linux]: :class:`Process` class can mask legitimate ``ENOENT``\n  exceptions as\n  :exc:`NoSuchProcess`.\n- :gh:`1016`: :func:`disk_io_counters` raises ``RuntimeError`` on a system\n  with no disks.\n- :gh:`1017`: :func:`net_io_counters` raises ``RuntimeError`` on a system\n  with no network cards installed.\n- :gh:`1021`, [Linux]: :meth:`Process.open_files` may erroneously raise\n  :exc:`NoSuchProcess` instead of skipping a file which gets deleted while open\n  files are retrieved.\n- :gh:`1029`, [macOS], [FreeBSD]: :meth:`Process.connections` with\n  ``family=unix`` on Python 3 doesn't properly handle unicode paths and may\n  raise ``UnicodeDecodeError``.\n- :gh:`1033`, [macOS], [FreeBSD]: memory leak for :func:`net_connections` and\n  :meth:`Process.connections` when retrieving UNIX sockets (``kind='unix'``).\n- :gh:`1040`: fixed many unicode related issues such as ``UnicodeDecodeError``\n  on Python 3 + POSIX and invalid encoded data on Windows.\n- :gh:`1042`, [FreeBSD], **[critical]**: psutil won't compile on FreeBSD 12.\n- :gh:`1044`, [macOS]: different :class:`Process` methods incorrectly raise\n  :exc:`AccessDenied` for zombie processes.\n- :gh:`1046`, [Windows]: :func:`disk_partitions` on Windows overrides user's\n  ``SetErrorMode``.\n- :gh:`1047`, [Windows]: :meth:`Process.username`: memory leak in case\n  exception is thrown.\n- :gh:`1048`, [Windows]: :func:`users` ``host`` field report an invalid IP\n  address.\n- :gh:`1050`, [Windows]: :meth:`Process.memory_maps` leaks memory.\n- :gh:`1055`: :func:`cpu_count` is no longer cached. This is useful on\n  systems such as Linux where CPUs can be disabled at runtime. This also\n  reflects on\n  :meth:`Process.cpu_percent` which no longer uses the cache.\n- :gh:`1058`: fixed Python warnings.\n- :gh:`1062`: :func:`disk_io_counters` and :func:`net_io_counters` raise\n  ``TypeError`` if no disks or NICs are installed on the system.\n- :gh:`1063`, [NetBSD]: :func:`net_connections` may list incorrect sockets.\n- :gh:`1064`, [NetBSD], **[critical]**: :func:`swap_memory` may segfault in\n  case of error.\n- :gh:`1065`, [OpenBSD], **[critical]**: :meth:`Process.cmdline` may raise\n  ``SystemError``.\n- :gh:`1067`, [NetBSD]: :meth:`Process.cmdline` leaks memory if process has\n  terminated.\n- :gh:`1069`, [FreeBSD]: :meth:`Process.cpu_num` may return 255 for certain\n  kernel processes.\n- :gh:`1071`, [Linux]: :func:`cpu_freq` may raise ``IOError`` on old RedHat\n  distros.\n- :gh:`1074`, [FreeBSD]: :func:`sensors_battery` raises ``OSError`` in case\n  of no battery.\n- :gh:`1075`, [Windows]: :func:`net_if_addrs`: ``inet_ntop()`` return value\n  is not checked.\n- :gh:`1077`, [SunOS]: :func:`net_if_addrs` shows garbage addresses on SunOS\n  5.10. (patch by Oleksii Shevchuk)\n- :gh:`1077`, [SunOS]: :func:`net_connections` does not work on SunOS 5.10.\n  (patch by Oleksii Shevchuk)\n- :gh:`1079`, [FreeBSD]: :func:`net_connections` didn't list locally\n  connected sockets. (patch by Gleb Smirnoff)\n- :gh:`1085`: :func:`cpu_count` return value is now checked and forced to\n  ``None`` if <= 1.\n- :gh:`1087`: :meth:`Process.cpu_percent` guard against :func:`cpu_count`\n  returning ``None`` and assumes 1 instead.\n- :gh:`1093`, [SunOS]: :meth:`Process.memory_maps` shows wrong 64 bit\n  addresses.\n- :gh:`1094`, [Windows]: :func:`pid_exists` may lie. Also, all process APIs\n  relying on ``OpenProcess`` Windows API now check whether the PID is actually\n  running.\n- :gh:`1098`, [Windows]: :meth:`Process.wait` may erroneously return sooner,\n  when the PID is still alive.\n- :gh:`1099`, [Windows]: :meth:`Process.terminate` may raise\n  :exc:`AccessDenied` even if the process already died.\n- :gh:`1101`, [Linux]: :func:`sensors_temperatures` may raise ``ENODEV``.\n\n**Porting notes**\n\n- :gh:`1039`: returned types consolidation. 1) Windows /\n  :meth:`Process.cpu_times`: fields #3 and #4 were int instead of float. 2)\n  Linux / FreeBSD / OpenBSD:\n  :meth:`Process.connections` ``raddr`` is now set to  ``\"\"`` instead of\n  ``None`` when retrieving UNIX sockets.\n- :gh:`1040`: all strings are encoded by using OS fs encoding.\n- :gh:`1040`: the following Windows APIs on Python 2 now return a string\n  instead of unicode: ``Process.memory_maps().path``,\n  ``WindowsService.bin_path()``, ``WindowsService.description()``,\n  ``WindowsService.display_name()``, ``WindowsService.username()``.\n\n5.2.2 — 2017-04-10\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`1000`: fixed some setup.py warnings.\n- :gh:`1002`, [SunOS]: remove C macro which will not be available on new\n  Solaris versions. (patch by Danek Duvall)\n- :gh:`1004`, [Linux]: :meth:`Process.io_counters` may raise ``ValueError``.\n- :gh:`1006`, [Linux]: :func:`cpu_freq` may return ``None`` on some Linux\n  versions does not support the function. Let's not make the function available\n  instead.\n- :gh:`1009`, [Linux]: :func:`sensors_temperatures` may raise ``OSError``.\n- :gh:`1010`, [Linux]: :func:`virtual_memory` may raise ``ValueError`` on\n  Ubuntu 14.04.\n\n5.2.1 — 2017-03-24\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`981`, [Linux]: :func:`cpu_freq` may return an empty list.\n- :gh:`993`, [Windows]: :meth:`Process.memory_maps` on Python 3 may raise\n  ``UnicodeDecodeError``.\n- :gh:`996`, [Linux]: :func:`sensors_temperatures` may not show all\n  temperatures.\n- :gh:`997`, [FreeBSD]: :func:`virtual_memory` may fail due to missing\n  ``sysctl`` parameter on FreeBSD 12.\n\n5.2.0 — 2017-03-05\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`971`, [Linux]: Add :func:`sensors_fans` function.  (patch by Nicolas\n  Hennion)\n- :gh:`976`, [Windows]: :meth:`Process.io_counters` has 2 new fields:\n  ``other_count`` and ``other_bytes``.\n- :gh:`976`, [Linux]: :meth:`Process.io_counters` has 2 new fields:\n  ``read_chars`` and ``write_chars``.\n\n**Bug fixes**\n\n- :gh:`872`, [Linux]: can now compile on Linux by using MUSL C library.\n- :gh:`985`, [Windows]: Fix a crash in :meth:`Process.open_files` when the\n  worker thread for ``NtQueryObject`` times out.\n- :gh:`986`, [Linux]: :meth:`Process.cwd` may raise :exc:`NoSuchProcess`\n  instead of :exc:`ZombieProcess`.\n\n5.1.3\n^^^^^\n\n**Bug fixes**\n\n- :gh:`971`, [Linux]: :func:`sensors_temperatures` didn't work on CentOS 7.\n- :gh:`973`, **[critical]**: :func:`cpu_percent` may raise\n  ``ZeroDivisionError``.\n\n5.1.2 — 2017-02-03\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`966`, [Linux]: :func:`sensors_battery` ``power_plugged`` may\n  erroneously return ``None`` on Python 3.\n- :gh:`968`, [Linux]: :func:`disk_io_counters` raises ``TypeError`` on Python\n  3.\n- :gh:`970`, [Linux]: :func:`sensors_battery` ``name`` and ``label`` fields\n  on Python 3 are bytes instead of str.\n\n5.1.1 — 2017-02-03\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`966`, [Linux]: :func:`sensors_battery` ``percent`` is a float and is\n  more precise.\n\n**Bug fixes**\n\n- :gh:`964`, [Windows]: :meth:`Process.username` and :func:`users` may\n  return badly decoded character on Python 3.\n- :gh:`965`, [Linux]: :func:`disk_io_counters` may miscalculate sector size\n  and report the wrong number of bytes read and written.\n- :gh:`966`, [Linux]: :func:`sensors_battery` may fail with\n  ``FileNotFoundError``.\n- :gh:`966`, [Linux]: :func:`sensors_battery` ``power_plugged`` may lie.\n\n5.1.0 — 2017-02-01\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`357`: added :meth:`Process.cpu_num` (what CPU a process is on).\n- :gh:`371`: added :func:`sensors_temperatures` (Linux only).\n- :gh:`941`: added :func:`cpu_freq` (CPU frequency).\n- :gh:`955`: added :func:`sensors_battery` (Linux, Windows, only).\n- :gh:`956`: :meth:`Process.cpu_affinity` can now be passed ``[]`` argument\n  as an alias to set affinity against all eligible CPUs.\n\n**Bug fixes**\n\n- :gh:`687`, [Linux]: :func:`pid_exists` no longer returns ``True`` if passed\n  a process thread ID.\n- :gh:`948`: cannot install psutil with ``PYTHONOPTIMIZE=2``.\n- :gh:`950`, [Windows]: :meth:`Process.cpu_percent` was calculated\n  incorrectly and showed higher number than real usage.\n- :gh:`951`, [Windows]: the uploaded wheels for Python 3.6 64 bit didn't work.\n- :gh:`959`: psutil exception objects could not be pickled.\n- :gh:`960`: :class:`Popen` ``wait()`` did not return the correct negative exit\n  status if process is killed by a signal.\n- :gh:`961`, [Windows]: ``WindowsService.description()`` method may fail with\n  ``ERROR_MUI_FILE_NOT_FOUND``.\n\n5.0.1 — 2016-12-21\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`939`: tar.gz distribution went from 1.8M to 258K.\n- :gh:`811`, [Windows]: provide a more meaningful error message if trying to\n  use psutil on unsupported Windows XP.\n\n**Bug fixes**\n\n- :gh:`609`, [SunOS], **[critical]**: psutil does not compile on Solaris 10.\n- :gh:`936`, [Windows]: fix compilation error on VS 2013 (patch by Max\n  Bélanger).\n- :gh:`940`, [Linux]: :func:`cpu_percent` and :func:`cpu_times_percent` was\n  calculated incorrectly as ``iowait``, ``guest`` and ``guest_nice`` times were\n  not properly taken into account.\n- :gh:`944`, [OpenBSD]: :func:`pids` was omitting PID 0.\n\n5.0.0 — 2016-11-06\n^^^^^^^^^^^^^^^^^^\n\n**Enhncements**\n\n- :gh:`799`: new :meth:`Process.oneshot` context manager making\n  :class:`Process` methods around +2x faster in general and from +2x to +6x\n  faster on Windows.\n- :gh:`943`: better error message in case of version conflict on import.\n\n**Bug fixes**\n\n- :gh:`932`, [NetBSD]: :func:`net_connections` and\n  :meth:`Process.connections` may fail without raising an exception.\n- :gh:`933`, [Windows]: memory leak in :func:`cpu_stats` and\n  ``WindowsService.description()`` method.\n\n4.4.2 — 2016-10-26\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`931`, **[critical]**: psutil no longer compiles on Solaris.\n\n4.4.1 — 2016-10-25\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`927`, **[critical]**: :class:`Popen` ``__del__`` may cause maximum\n  recursion depth error.\n\n4.4.0 — 2016-10-23\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`874`, [Windows]: make :func:`net_if_addrs` also return the\n  ``netmask``.\n- :gh:`887`, [Linux]: :func:`virtual_memory` ``available`` and ``used``\n  values are more precise and match ``free`` cmdline utility.  ``available``\n  also takes into account LCX containers preventing ``available`` to overflow\n  ``total``.\n- :gh:`891`: `procinfo.py`_ script has been updated and provides a lot more\n  info.\n\n**Bug fixes**\n\n- :gh:`514`, [macOS], **[critical]**: :meth:`Process.memory_maps` can\n  segfault.\n- :gh:`783`, [macOS]: :meth:`Process.status` may erroneously return\n  ``\"running\"`` for zombie processes.\n- :gh:`798`, [Windows]: :meth:`Process.open_files` returns and empty list on\n  Windows 10.\n- :gh:`825`, [Linux]: :meth:`Process.cpu_affinity`: fix possible double close\n  and use of unopened socket.\n- :gh:`880`, [Windows]: fix race condition inside :func:`net_connections`.\n- :gh:`885`: ``ValueError`` is raised if a negative integer is passed to\n  :func:`cpu_percent` functions.\n- :gh:`892`, [Linux], **[critical]**: :meth:`Process.cpu_affinity` with\n  ``[-1]`` as arg raises ``SystemError`` with no error set; now ``ValueError``\n  is raised.\n- :gh:`906`, [BSD]: :func:`disk_partitions` with ``all=False`` returned an\n  empty list. Now the argument is ignored and all partitions are always\n  returned.\n- :gh:`907`, [FreeBSD]: :meth:`Process.exe` may fail with\n  ``OSError(ENOENT)``.\n- :gh:`908`, [macOS], [BSD]: different process methods could errounesuly mask\n  the real error for high-privileged PIDs and raise :exc:`NoSuchProcess` and\n  :exc:`AccessDenied` instead of ``OSError`` and ``RuntimeError``.\n- :gh:`909`, [macOS]: :meth:`Process.open_files` and\n  :meth:`Process.connections` methods may raise ``OSError`` with no exception\n  set if process is gone.\n- :gh:`916`, [macOS]: fix many compilation warnings.\n\n4.3.1 — 2016-09-01\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`881`: ``make install`` now works also when using a virtual env.\n\n**Bug fixes**\n\n- :gh:`854`: :meth:`Process.as_dict` raises ``ValueError`` if passed an\n  erroneous attrs name.\n- :gh:`857`, [SunOS]: :meth:`Process.cpu_times`,\n  :meth:`Process.cpu_percent`,\n  :meth:`Process.threads` and :meth:`Process.memory_maps` may raise\n  ``RuntimeError`` if attempting to query a 64bit process with a 32bit Python.\n  \"Null\" values are returned as a fallback.\n- :gh:`858`: :meth:`Process.as_dict` should not call\n  :meth:`Process.memory_info_ex` because it's deprecated.\n- :gh:`863`, [Windows]: :meth:`Process.memory_maps` truncates addresses above\n  32 bits.\n- :gh:`866`, [Windows]: :func:`win_service_iter` and services in general are\n  not able to handle unicode service names / descriptions.\n- :gh:`869`, [Windows]: :meth:`Process.wait` may raise :exc:`TimeoutExpired`\n  with wrong timeout unit (ms instead of sec).\n- :gh:`870`, [Windows]: handle leak inside ``psutil_get_process_data``.\n\n4.3.0 — 2016-06-18\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`819`, [Linux]: different speedup improvements:\n  :meth:`Process.ppid` +20% faster.\n  :meth:`Process.status` +28% faster.\n  :meth:`Process.name` +25% faster.\n  :meth:`Process.num_threads` +20% faster on Python 3.\n\n**Bug fixes**\n\n- :gh:`810`, [Windows]: Windows wheels are incompatible with pip 7.1.2.\n- :gh:`812`, [NetBSD], **[critical]**: fix compilation on NetBSD-5.x.\n- :gh:`823`, [NetBSD]: :func:`virtual_memory` raises ``TypeError`` on Python\n  3.\n- :gh:`829`, [POSIX]: :func:`disk_usage` ``percent`` field takes root\n  reserved space into account.\n- :gh:`816`, [Windows]: fixed :func:`net_io_counters` values wrapping after\n  4.3GB in Windows Vista (NT 6.0) and above using 64bit values from newer win\n  APIs.\n\n4.2.0 — 2016-05-14\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`795`, [Windows]: new APIs to deal with Windows services:\n  :func:`win_service_iter` and :func:`win_service_get`.\n- :gh:`800`, [Linux]: :func:`virtual_memory` returns a new ``shared`` memory\n  field.\n- :gh:`819`, [Linux]: speedup ``/proc`` parsing:\n  :meth:`Process.ppid` +20% faster.\n  :meth:`Process.status` +28% faster.\n  :meth:`Process.name` +25% faster.\n  :meth:`Process.num_threads` +20% faster on Python 3.\n\n**Bug fixes**\n\n- :gh:`797`, [Linux]: :func:`net_if_stats` may raise ``OSError`` for certain\n  NIC cards.\n- :gh:`813`: :meth:`Process.as_dict` should ignore extraneous attribute names\n  which gets attached to the :class:`Process` instance.\n\n4.1.0 — 2016-03-12\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`777`, [Linux]: :meth:`Process.open_files` on Linux return 3 new\n  fields: ``position``, ``mode`` and ``flags``.\n- :gh:`779`: :meth:`Process.cpu_times` returns two new fields,\n  ``children_user`` and ``children_system`` (always set to 0 on macOS and\n  Windows).\n- :gh:`789`, [Windows]: :func:`cpu_times` return two new fields:\n  ``interrupt`` and ``dpc``. Same for :func:`cpu_times_percent`.\n- :gh:`792`: new :func:`cpu_stats` function returning number of CPU\n  ``ctx_switches``, ``interrupts``, ``soft_interrupts`` and ``syscalls``.\n\n**Bug fixes**\n\n- :gh:`774`, [FreeBSD]: :func:`net_io_counters` dropout is no longer set to 0\n  if the kernel provides it.\n- :gh:`776`, [Linux]: :meth:`Process.cpu_affinity` may erroneously raise\n  :exc:`NoSuchProcess`. (patch by wxwright)\n- :gh:`780`, [macOS]: psutil does not compile with some GCC versions.\n- :gh:`786`: :func:`net_if_addrs` may report incomplete MAC addresses.\n- :gh:`788`, [NetBSD]: :func:`virtual_memory` ``buffers`` and ``shared``\n  values were set to 0.\n- :gh:`790`, [macOS], **[critical]**: psutil won't compile on macOS 10.4.\n\n4.0.0 — 2016-02-17\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`523`, [Linux], [FreeBSD]: :func:`disk_io_counters` return a new\n  ``busy_time`` field.\n- :gh:`660`, [Windows]: make.bat is smarter in finding alternative VS install\n  locations.  (patch by mpderbec)\n- :gh:`732`: :meth:`Process.environ`.  (patch by Frank Benkstein)\n- :gh:`753`, [Linux], [macOS], [Windows]: process USS and PSS (Linux) \"real\"\n  memory stats. (patch by Eric Rahm)\n- :gh:`755`: :meth:`Process.memory_percent` ``memtype`` parameter.\n- :gh:`758`: tests now live in psutil namespace.\n- :gh:`760`: expose OS constants (``psutil.LINUX``, ``psutil.OSX``, etc.)\n- :gh:`756`, [Linux]: :func:`disk_io_counters` return 2 new fields:\n  ``read_merged_count`` and ``write_merged_count``.\n- :gh:`762`: new `procsmem.py`_ script.\n\n**Bug fixes**\n\n- :gh:`685`, [Linux]: :func:`virtual_memory` provides wrong results on\n  systems with a lot of physical memory.\n- :gh:`704`, [SunOS]: psutil does not compile on Solaris sparc.\n- :gh:`734`: on Python 3 invalid UTF-8 data is not correctly handled for\n  :meth:`Process.name`, :meth:`Process.cwd`, :meth:`Process.exe`,\n  :meth:`Process.cmdline` and :meth:`Process.open_files` methods resulting\n  in ``UnicodeDecodeError`` exceptions. ``'surrogateescape'`` error handler\n  is now used as a workaround for replacing the corrupted data.\n- :gh:`737`, [Windows]: when the bitness of psutil and the target process was\n  different, :meth:`Process.cmdline` and :meth:`Process.cwd` could return a\n  wrong result or incorrectly report an :exc:`AccessDenied` error.\n- :gh:`741`, [OpenBSD]: psutil does not compile on mips64.\n- :gh:`751`, [Linux]: fixed call to ``Py_DECREF`` on possible ``NULL`` object.\n- :gh:`754`, [Linux]: :meth:`Process.cmdline` can be wrong in case of zombie\n  process.\n- :gh:`759`, [Linux]: :meth:`Process.memory_maps` may return paths ending\n  with ``\" (deleted)\"``.\n- :gh:`761`, [Windows]: :func:`boot_time` wraps to 0 after 49 days.\n- :gh:`764`, [NetBSD]: fix compilation on NetBSD-6.x.\n- :gh:`766`, [Linux]: :func:`net_connections` can't handle malformed\n  ``/proc/net/unix`` file.\n- :gh:`767`, [Linux]: :func:`disk_io_counters` may raise ``ValueError`` on\n  2.6 kernels and it's broken on 2.4 kernels.\n- :gh:`770`, [NetBSD]: :func:`disk_io_counters` metrics didn't update.\n\n3.4.2 — 2016-01-20\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`728`, [SunOS]: exposed :data:`PROCFS_PATH` constant to change the\n  default location of ``/proc`` filesystem.\n\n**Bug fixes**\n\n- :gh:`724`, [FreeBSD]: :func:`virtual_memory` ``total`` is incorrect.\n- :gh:`730`, [FreeBSD], **[critical]**: :func:`virtual_memory` crashes with\n  \"OSError: [Errno 12] Cannot allocate memory\".\n\n3.4.1 — 2016-01-15\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`557`, [NetBSD]: added NetBSD support.  (contributed by Ryo Onodera and\n  Thomas Klausner)\n- :gh:`708`, [Linux]: :func:`net_connections` and\n  :meth:`Process.connections` on Python 2 can be up to 3x faster in case of\n  many connections. Also\n  :meth:`Process.memory_maps` is slightly faster.\n- :gh:`718`: :func:`process_iter` is now thread safe.\n\n**Bug fixes**\n\n- :gh:`714`, [OpenBSD]: :func:`virtual_memory` ``cached`` value was always\n  set to 0.\n- :gh:`715`, **[critical]**: don't crash at import time if :func:`cpu_times`\n  fail for some reason.\n- :gh:`717`, [Linux]: :meth:`Process.open_files` fails if deleted files still\n  visible.\n- :gh:`722`, [Linux]: :func:`swap_memory` no longer crashes if ``sin`` /\n  ``sout`` can't be determined due to missing ``/proc/vmstat``.\n- :gh:`724`, [FreeBSD]: :func:`virtual_memory` ``total`` is slightly\n  incorrect.\n\n3.3.0 — 2015-11-25\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`558`, [Linux]: exposed :data:`PROCFS_PATH` constant to change the\n  default location of ``/proc`` filesystem.\n- :gh:`615`, [OpenBSD]: added OpenBSD support.  (contributed by Landry Breuil)\n\n**Bug fixes**\n\n- :gh:`692`, [POSIX]: :meth:`Process.name` is no longer cached as it may\n  change.\n\n3.2.2 — 2015-10-04\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`517`, [SunOS]: :func:`net_io_counters` failed to detect network\n  interfaces correctly on Solaris 10\n- :gh:`541`, [FreeBSD]: :func:`disk_io_counters` r/w times were expressed in\n  seconds instead of milliseconds.  (patch by dasumin)\n- :gh:`610`, [SunOS]: fix build and tests on Solaris 10\n- :gh:`623`, [Linux]: process or system connections raises ``ValueError`` if\n  IPv6 is not supported by the system.\n- :gh:`678`, [Linux], **[critical]**: can't install psutil due to bug in\n  setup.py.\n- :gh:`688`, [Windows]: compilation fails with MSVC 2015, Python 3.5. (patch by\n  Mike Sarahan)\n\n3.2.1 — 2015-09-03\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`677`, [Linux], **[critical]**: can't install psutil due to bug in\n  setup.py.\n\n3.2.0 — 2015-09-02\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`644`, [Windows]: added support for ``CTRL_C_EVENT`` and\n  ``CTRL_BREAK_EVENT`` signals to use with :meth:`Process.send_signal`.\n- :gh:`648`: CI test integration for macOS. (patch by Jeff Tang)\n- :gh:`663`, [POSIX]: :func:`net_if_addrs` now returns point-to-point (VPNs)\n  addresses.\n- :gh:`655`, [Windows]: different issues regarding unicode handling were fixed.\n  On Python 2 all APIs returning a string will now return an encoded version of\n  it by using sys.getfilesystemencoding() codec. The APIs involved are:\n  :func:`net_if_addrs`, :func:`net_if_stats`, :func:`net_io_counters`,\n  :meth:`Process.cmdline`, :meth:`Process.name`,\n  :meth:`Process.username`, :func:`users`.\n\n**Bug fixes**\n\n- :gh:`513`, [Linux]: fixed integer overflow for ``RLIM_INFINITY``.\n- :gh:`641`, [Windows]: fixed many compilation warnings.  (patch by Jeff Tang)\n- :gh:`652`, [Windows]: :func:`net_if_addrs` ``UnicodeDecodeError`` in case\n  of non-ASCII NIC names.\n- :gh:`655`, [Windows]: :func:`net_if_stats` ``UnicodeDecodeError`` in case\n  of non-ASCII NIC names.\n- :gh:`659`, [Linux]: compilation error on Suse 10. (patch by maozguttman)\n- :gh:`664`, [Linux]: compilation error on Alpine Linux. (patch by Bart van\n  Kleef)\n- :gh:`670`, [Windows]: segfgault of :func:`net_if_addrs` in case of\n  non-ASCII NIC names. (patch by sk6249)\n- :gh:`672`, [Windows]: compilation fails if using Windows SDK v8.0. (patch by\n  Steven Winfield)\n- :gh:`675`, [Linux]: :func:`net_connections`: ``UnicodeDecodeError`` may\n  occur when listing UNIX sockets.\n\n3.1.1 — 2015-07-15\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`603`, [Linux]: :meth:`Process.ionice` set value range is incorrect.\n  (patch by spacewander)\n- :gh:`645`, [Linux]: :func:`cpu_times_percent` may produce negative results.\n- :gh:`656`: ``from psutil import *`` does not work.\n\n3.1.0 — 2015-07-15\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`534`, [Linux]: :func:`disk_partitions` added support for ZFS\n  filesystems.\n- :gh:`646`, [Windows]: continuous tests integration for Windows with\n  https://ci.appveyor.com/project/giampaolo/psutil.\n- :gh:`647`: new dev guide:\n  https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst\n- :gh:`651`: continuous code quality test integration with scrutinizer-ci.com\n\n**Bug fixes**\n\n- :gh:`340`, [Windows], **[critical]**: :meth:`Process.open_files` no longer\n  hangs. Instead it uses a thread which times out and skips the file handle in\n  case it's taking too long to be retrieved.  (patch by Jeff Tang)\n- :gh:`627`, [Windows]: :meth:`Process.name` no longer raises\n  :exc:`AccessDenied` for pids owned by another user.\n- :gh:`636`, [Windows]: :meth:`Process.memory_info` raise\n  :exc:`AccessDenied`.\n- :gh:`637`, [POSIX]: raise exception if trying to send signal to PID 0 as it\n  will affect ``os.getpid()`` 's process group and not PID 0.\n- :gh:`639`, [Linux]: :meth:`Process.cmdline` can be truncated.\n- :gh:`640`, [Linux]: ``*connections`` functions may swallow errors and return\n  an incomplete list of connections.\n- :gh:`642`: ``repr()`` of exceptions is incorrect.\n- :gh:`653`, [Windows]: add ``inet_ntop()`` function for Windows XP to support\n  IPv6.\n- :gh:`641`, [Windows]: replace deprecated string functions with safe\n  equivalents.\n\n3.0.1 — 2015-06-18\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`632`, [Linux]: better error message if cannot parse process UNIX\n  connections.\n- :gh:`634`, [Linux]: :meth:`Process.cmdline` does not include empty string\n  arguments.\n- :gh:`635`, [POSIX], **[critical]**: crash on module import if ``enum``\n  package is installed on Python < 3.4.\n\n3.0.0 — 2015-06-13\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`250`: new :func:`net_if_stats` returning NIC statistics (``isup``,\n  ``duplex``, ``speed``, ``mtu``).\n- :gh:`376`: new :func:`net_if_addrs` returning all NIC addresses a-la\n  ``ifconfig``.\n- :gh:`469`: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS``\n  constants returned by :meth:`Process.ionice` and :meth:`Process.nice` are\n  enums instead of plain integers.\n- :gh:`581`: add ``.gitignore``. (patch by Gabi Davar)\n- :gh:`582`: connection constants returned by :func:`net_connections` and\n  :meth:`Process.connections` were turned from int to enums on Python > 3.4.\n- :gh:`587`: move native extension into the package.\n- :gh:`589`: :meth:`Process.cpu_affinity` accepts any kind of iterable (set,\n  tuple, ...), not only lists.\n- :gh:`594`: all deprecated APIs were removed.\n- :gh:`599`, [Windows]: :meth:`Process.name` can now be determined for all\n  processes even when running as a limited user.\n- :gh:`602`: pre-commit GIT hook.\n- :gh:`629`: enhanced support for ``pytest`` and ``nose`` test runners.\n- :gh:`616`, [Windows]: add ``inet_ntop()`` function for Windows XP.\n\n**Bug fixes**\n\n- :gh:`428`, [POSIX], **[critical]**: correct handling of zombie processes on\n  POSIX. Introduced new :exc:`ZombieProcess` exception class.\n- :gh:`512`, [BSD], **[critical]**: fix segfault in :func:`net_connections`.\n- :gh:`555`, [Linux]: :func:`users` correctly handles ``\":0\"`` as an alias\n  for ``\"localhost\"``.\n- :gh:`579`, [Windows]: fixed :meth:`Process.open_files` for PID > 64K.\n- :gh:`579`, [Windows]: fixed many compiler warnings.\n- :gh:`585`, [FreeBSD]: :func:`net_connections` may raise ``KeyError``.\n- :gh:`586`, [FreeBSD], **[critical]**: :meth:`Process.cpu_affinity`\n  segfaults on set in case an invalid CPU number is provided.\n- :gh:`593`, [FreeBSD], **[critical]**: :meth:`Process.memory_maps`\n  segfaults.\n- :gh:`606`: :meth:`Process.parent` may swallow :exc:`NoSuchProcess`\n  exceptions.\n- :gh:`611`, [SunOS]: :func:`net_io_counters` has send and received swapped\n- :gh:`614`, [Linux]:: :func:`cpu_count` with ``logical=False`` return the\n  number of sockets instead of cores.\n- :gh:`618`, [SunOS]: swap tests fail on Solaris when run as normal user.\n- :gh:`628`, [Linux]: :meth:`Process.name` truncates string in case it\n  contains spaces or parentheses.\n\n2.2.1 — 2015-02-02\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`572`, [Linux]: fix \"ValueError: ambiguous inode with multiple PIDs\n  references\" for :meth:`Process.connections`. (patch by Bruno Binet)\n\n2.2.0 — 2015-01-06\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`521`: drop support for Python 2.4 and 2.5.\n- :gh:`553`: new `pstree.py`_ script.\n- :gh:`564`: C extension version mismatch in case the user messed up with\n  psutil installation or with sys.path is now detected at import time.\n- :gh:`568`: new `pidof.py`_ script.\n- :gh:`569`, [FreeBSD]: add support for :meth:`Process.cpu_affinity` on\n  FreeBSD.\n\n**Bug fixes**\n\n- :gh:`496`, [SunOS], **[critical]**: can't import psutil.\n- :gh:`547`, [POSIX]: :meth:`Process.username` may raise ``KeyError`` if UID\n  can't be resolved.\n- :gh:`551`, [Windows]: get rid of the unicode hack for\n  :func:`net_io_counters` NIC names.\n- :gh:`556`, [Linux]: lots of file handles were left open.\n- :gh:`561`, [Linux]: :func:`net_connections` might skip some legitimate UNIX\n  sockets. (patch by spacewander)\n- :gh:`565`, [Windows]: use proper encoding for :meth:`Process.username` and\n  :func:`users`. (patch by Sylvain Mouquet)\n- :gh:`567`, [Linux]: in the alternative implementation of\n  :meth:`Process.cpu_affinity` ``PyList_Append`` and ``Py_BuildValue`` return\n  values are not checked.\n- :gh:`569`, [FreeBSD]: fix memory leak in :func:`cpu_count` with\n  ``logical=False``.\n- :gh:`571`, [Linux]: :meth:`Process.open_files` might swallow\n  :exc:`AccessDenied` exceptions and return an incomplete list of open files.\n\n2.1.3 — 2014-09-26\n^^^^^^^^^^^^^^^^^^\n\n- :gh:`536`, [Linux], **[critical]**: fix \"undefined symbol: CPU_ALLOC\"\n  compilation error.\n\n2.1.2 — 2014-09-21\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`407`: project moved from Google Code to Github; code moved from\n  Mercurial to Git.\n- :gh:`492`: use ``tox`` to run tests on multiple Python versions.  (patch by\n  msabramo)\n- :gh:`505`, [Windows]: distribution as wheel packages.\n- :gh:`511`: add `ps.py`_ script.\n\n**Bug fixes**\n\n- :gh:`340`, [Windows]: :meth:`Process.open_files` no longer hangs.  (patch\n  by Jeff Tang)\n- :gh:`501`, [Windows]: :func:`disk_io_counters` may return negative values.\n- :gh:`503`, [Linux]: in rare conditions :meth:`Process.exe`,\n  :meth:`Process.open_files` and\n  :meth:`Process.connections` can raise ``OSError(ESRCH)`` instead of\n  :exc:`NoSuchProcess`.\n- :gh:`504`, [Linux]: can't build RPM packages via setup.py\n- :gh:`506`, [Linux], **[critical]**: Python 2.4 support was broken.\n- :gh:`522`, [Linux]: :meth:`Process.cpu_affinity` might return ``EINVAL``.\n  (patch by David Daeschler)\n- :gh:`529`, [Windows]: :meth:`Process.exe` may raise unhandled\n  ``WindowsError`` exception for PIDs 0 and 4.  (patch by Jeff Tang)\n- :gh:`530`, [Linux]: :func:`disk_io_counters` may crash on old Linux distros\n  (< 2.6.5)  (patch by Yaolong Huang)\n- :gh:`533`, [Linux]: :meth:`Process.memory_maps` may raise ``TypeError`` on\n  old Linux distros.\n\n2.1.1 — 2014-04-30\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`446`, [Windows]: fix encoding error when using :func:`net_io_counters`\n  on Python 3. (patch by Szigeti Gabor Niif)\n- :gh:`460`, [Windows]: :func:`net_io_counters` wraps after 4G.\n- :gh:`491`, [Linux]: :func:`net_connections` exceptions. (patch by Alexander\n  Grothe)\n\n2.1.0 — 2014-04-08\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`387`: system-wide open connections a-la ``netstat`` (add\n  :func:`net_connections`).\n\n**Bug fixes**\n\n- :gh:`421`, [SunOS], **[critical]**: psutil does not compile on SunOS 5.10.\n  (patch by Naveed Roudsari)\n- :gh:`489`, [Linux]: :func:`disk_partitions` return an empty list.\n\n2.0.0 — 2014-03-10\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`424`, [Windows]: installer for Python 3.X 64 bit.\n- :gh:`427`: number of logical CPUs and physical cores (:func:`cpu_count`).\n- :gh:`447`: :func:`wait_procs` ``timeout`` parameter is now optional.\n- :gh:`452`: make :class:`Process` instances hashable and usable with ``set()``\n  s.\n- :gh:`453`: tests on Python < 2.7 require ``unittest2`` module.\n- :gh:`459`: add a Makefile for running tests and other repetitive tasks (also\n  on Windows).\n- :gh:`463`: make timeout parameter of ``cpu_percent*`` functions default to\n  ``0.0`` 'cause it's a common trap to introduce slowdowns.\n- :gh:`468`: move documentation to readthedocs.com.\n- :gh:`477`: :meth:`Process.cpu_percent` is about 30% faster.  (suggested by\n  crusaderky)\n- :gh:`478`, [Linux]: almost all APIs are about 30% faster on Python 3.X.\n- :gh:`479`: long deprecated ``psutil.error`` module is gone; exception classes\n  now live in psutil namespace only.\n\n**Bug fixes**\n\n- :gh:`193`: :class:`Popen` constructor can throw an exception if the spawned\n  process terminates quickly.\n- :gh:`340`, [Windows]: :meth:`Process.open_files` no longer hangs.  (patch\n  by jtang@vahna.net)\n- :gh:`443`, [Linux]: fix a potential overflow issue for\n  :meth:`Process.cpu_affinity` (set) on systems with more than 64 CPUs.\n- :gh:`448`, [Windows]: :meth:`Process.children` and :meth:`Process.ppid`\n  memory leak (patch by Ulrich Klank).\n- :gh:`457`, [POSIX]: :func:`pid_exists` always returns ``True`` for PID 0.\n- :gh:`461`: named tuples are not pickle-able.\n- :gh:`466`, [Linux]: :meth:`Process.exe` improper null bytes handling.\n  (patch by Gautam Singh)\n- :gh:`470`: :func:`wait_procs` might not wait.  (patch by crusaderky)\n- :gh:`471`, [Windows]: :meth:`Process.exe` improper unicode handling. (patch\n  by alex@mroja.net)\n- :gh:`473`: :class:`Popen` ``wait()`` method does not set returncode\n  attribute.\n- :gh:`474`, [Windows]: :meth:`Process.cpu_percent` is no longer capped at\n  100%.\n- :gh:`476`, [Linux]: encoding error for :meth:`Process.name` and\n  :meth:`Process.cmdline`.\n\n**API changes**\n\nFor the sake of consistency a lot of psutil APIs have been renamed. In most\ncases accessing the old names will work but it will cause a\n``DeprecationWarning``.\n\n- ``psutil.*`` module level constants have being replaced by functions:\n\n  +-----------------------+----------------------------------+\n  | Old name              | Replacement                      |\n  +=======================+==================================+\n  | psutil.NUM_CPUS       | psutil.cpu_count()               |\n  +-----------------------+----------------------------------+\n  | psutil.BOOT_TIME      | psutil.boot_time()               |\n  +-----------------------+----------------------------------+\n  | psutil.TOTAL_PHYMEM   | virtual_memory.total             |\n  +-----------------------+----------------------------------+\n\n- Renamed ``psutil.*`` functions:\n\n  +------------------------+-------------------------------+\n  | Old name               | Replacement                   |\n  +========================+===============================+\n  | psutil.get_pid_list()  | psutil.pids()                 |\n  +------------------------+-------------------------------+\n  | psutil.get_users()     | psutil.users()                |\n  +------------------------+-------------------------------+\n  | psutil.get_boot_time() | psutil.boot_time()            |\n  +------------------------+-------------------------------+\n\n- All :class:`Process` ``get_*`` methods lost the ``get_`` prefix. E.g.\n  ``get_ext_memory_info()`` was renamed to ``memory_info_ex()``. Assuming ``p =\n  psutil.Process()``:\n\n  +--------------------------+----------------------+\n  | Old name                 | Replacement          |\n  +==========================+======================+\n  | p.get_children()         | p.children()         |\n  +--------------------------+----------------------+\n  | p.get_connections()      | p.connections()      |\n  +--------------------------+----------------------+\n  | p.get_cpu_affinity()     | p.cpu_affinity()     |\n  +--------------------------+----------------------+\n  | p.get_cpu_percent()      | p.cpu_percent()      |\n  +--------------------------+----------------------+\n  | p.get_cpu_times()        | p.cpu_times()        |\n  +--------------------------+----------------------+\n  | p.get_ext_memory_info()  | p.memory_info_ex()   |\n  +--------------------------+----------------------+\n  | p.get_io_counters()      | p.io_counters()      |\n  +--------------------------+----------------------+\n  | p.get_ionice()           | p.ionice()           |\n  +--------------------------+----------------------+\n  | p.get_memory_info()      | p.memory_info()      |\n  +--------------------------+----------------------+\n  | p.get_memory_maps()      | p.memory_maps()      |\n  +--------------------------+----------------------+\n  | p.get_memory_percent()   | p.memory_percent()   |\n  +--------------------------+----------------------+\n  | p.get_nice()             | p.nice()             |\n  +--------------------------+----------------------+\n  | p.get_num_ctx_switches() | p.num_ctx_switches() |\n  +--------------------------+----------------------+\n  | p.get_num_fds()          | p.num_fds()          |\n  +--------------------------+----------------------+\n  | p.get_num_threads()      | p.num_threads()      |\n  +--------------------------+----------------------+\n  | p.get_open_files()       | p.open_files()       |\n  +--------------------------+----------------------+\n  | p.get_rlimit()           | p.rlimit()           |\n  +--------------------------+----------------------+\n  | p.get_threads()          | p.threads()          |\n  +--------------------------+----------------------+\n  | p.getcwd()               | p.cwd()              |\n  +--------------------------+----------------------+\n\n- All :class:`Process` ``set_*`` methods lost the ``set_`` prefix. Assuming ``p\n  = psutil.Process()``:\n\n  +----------------------+---------------------------------+\n  | Old name             | Replacement                     |\n  +======================+=================================+\n  | p.set_nice()         | p.nice(value)                   |\n  +----------------------+---------------------------------+\n  | p.set_ionice()       | p.ionice(ioclass, value=None)   |\n  +----------------------+---------------------------------+\n  | p.set_cpu_affinity() | p.cpu_affinity(cpus)            |\n  +----------------------+---------------------------------+\n  | p.set_rlimit()       | p.rlimit(resource, limits=None) |\n  +----------------------+---------------------------------+\n\n- Except for ``pid``, all :class:`Process` class properties have been turned\n  into methods. This is the only case which there are no aliases. Assuming ``p\n  = psutil.Process()``:\n\n  +---------------+-----------------+\n  | Old name      | Replacement     |\n  +===============+=================+\n  | p.name        | p.name()        |\n  +---------------+-----------------+\n  | p.parent      | p.parent()      |\n  +---------------+-----------------+\n  | p.ppid        | p.ppid()        |\n  +---------------+-----------------+\n  | p.exe         | p.exe()         |\n  +---------------+-----------------+\n  | p.cmdline     | p.cmdline()     |\n  +---------------+-----------------+\n  | p.status      | p.status()      |\n  +---------------+-----------------+\n  | p.uids        | p.uids()        |\n  +---------------+-----------------+\n  | p.gids        | p.gids()        |\n  +---------------+-----------------+\n  | p.username    | p.username()    |\n  +---------------+-----------------+\n  | p.create_time | p.create_time() |\n  +---------------+-----------------+\n\n- timeout parameter of ``cpu_percent*`` functions defaults to 0.0 instead of\n  0.1.\n- long deprecated ``psutil.error`` module is gone; exception classes now live\n  in \"psutil\" namespace only.\n- :class:`Process` instances' ``retcode`` attribute returned by\n  :func:`wait_procs` has been renamed to ``returncode`` for consistency with\n  ``subprocess.Popen``.\n\n1.2.1 — 2013-11-25\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`348`, [Windows], **[critical]**: fixed \"ImportError: DLL load failed\"\n  occurring on module import on Windows XP.\n- :gh:`425`, [SunOS], **[critical]**: crash on import due to failure at\n  determining ``BOOT_TIME``.\n- :gh:`443`, [Linux]: :meth:`Process.cpu_affinity` can't set affinity on\n  systems with more than 64 cores.\n\n1.2.0 — 2013-11-20\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`439`: assume ``os.getpid()`` if no argument is passed to\n  :class:`Process` class constructor.\n- :gh:`440`: new :func:`wait_procs` utility function which waits for multiple\n  processes to terminate.\n\n**Bug fixes**\n\n- :gh:`348`, [Windows]: fix \"ImportError: DLL load failed\" occurring on module\n  import on Windows XP / Vista.\n\n1.1.3 — 2013-11-07\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`442`, [Linux], **[critical]**: psutil won't compile on certain version\n  of Linux because of missing ``prlimit(2)`` syscall.\n\n1.1.2 — 2013-10-22\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`442`, [Linux], **[critical]**: psutil won't compile on Debian 6.0\n  because of missing ``prlimit(2)`` syscall.\n\n1.1.1 — 2013-10-08\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`442`, [Linux], **[critical]**: psutil won't compile on kernels < 2.6.36\n  due to missing ``prlimit(2)`` syscall.\n\n1.1.0 — 2013-09-28\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`410`: host tar.gz and Windows binary files are on PyPI.\n- :gh:`412`, [Linux]: get/set process resource limits\n  (:meth:`Process.rlimit`).\n- :gh:`415`, [Windows]: :meth:`Process.children` is an order of magnitude\n  faster.\n- :gh:`426`, [Windows]: :meth:`Process.name` is an order of magnitude faster.\n- :gh:`431`, [POSIX]: :meth:`Process.name` is slightly faster because it\n  unnecessarily retrieved also :meth:`Process.cmdline`.\n\n**Bug fixes**\n\n- :gh:`391`, [Windows]: :func:`cpu_times_percent` returns negative\n  percentages.\n- :gh:`408`: ``STATUS_*`` and ``CONN_*`` constants don't properly serialize on\n  JSON.\n- :gh:`411`, [Windows]: `disk_usage.py`_ may pop-up a GUI error.\n- :gh:`413`, [Windows]: :meth:`Process.memory_info` leaks memory.\n- :gh:`414`, [Windows]: :meth:`Process.exe` on Windows XP may raise\n  ``ERROR_INVALID_PARAMETER``.\n- :gh:`416`: :func:`disk_usage` doesn't work well with unicode path names.\n- :gh:`430`, [Linux]: :meth:`Process.io_counters` report wrong number of r/w\n  syscalls.\n- :gh:`435`, [Linux]: :func:`net_io_counters` might report erreneous NIC\n  names.\n- :gh:`436`, [Linux]: :func:`net_io_counters` reports a wrong ``dropin``\n  value.\n\n**API changes**\n\n- :gh:`408`: turn ``STATUS_*`` and ``CONN_*`` constants into plain Python\n  strings.\n\n1.0.1 — 2013-07-12\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`405`: :func:`net_io_counters` ``pernic=True`` no longer works as\n  intended in 1.0.0.\n\n1.0.0 — 2013-07-10\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`18`, [SunOS]: add Solaris support (yay!)  (thanks Justin Venus)\n- :gh:`367`: :meth:`Process.connections` ``status`` strings are now\n  constants.\n- :gh:`380`: test suite exits with non-zero on failure.  (patch by\n  floppymaster)\n- :gh:`391`: introduce unittest2 facilities and provide workarounds if\n  unittest2 is not installed (Python < 2.7).\n\n**Bug fixes**\n\n- :gh:`374`, [Windows]: negative memory usage reported if process uses a lot of\n  memory.\n- :gh:`379`, [Linux]: :meth:`Process.memory_maps` may raise ``ValueError``.\n- :gh:`394`, [macOS]: mapped memory regions of :meth:`Process.memory_maps`\n  report incorrect file name.\n- :gh:`404`, [Linux]: ``sched_*affinity()`` are implicitly declared. (patch by\n  Arfrever)\n\n**API changes**\n\n- :meth:`Process.connections` ``status`` field is no longer a string but a\n  constant object (``psutil.CONN_*``).\n- :meth:`Process.connections` ``local_address`` and ``remote_address`` fields\n  renamed to ``laddr`` and ``raddr``.\n- psutil.network_io_counters() renamed to :func:`net_io_counters`.\n\n0.7.1 — 2013-05-03\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`325`, [BSD], **[critical]**: :func:`virtual_memory` can raise\n  ``SystemError``. (patch by Jan Beich)\n- :gh:`370`, [BSD]: :meth:`Process.connections` requires root.  (patch by\n  John Baldwin)\n- :gh:`372`, [BSD]: different process methods raise :exc:`NoSuchProcess`\n  instead of\n  :exc:`AccessDenied`.\n\n0.7.0 — 2013-04-12\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`233`: code migrated to Mercurial (yay!)\n- :gh:`246`: psutil.error module is deprecated and scheduled for removal.\n- :gh:`328`, [Windows]: :meth:`Process.ionice` support.\n- :gh:`359`: add :func:`boot_time` as a substitute of ``psutil.BOOT_TIME``\n  since the latter cannot reflect system clock updates.\n- :gh:`361`, [Linux]: :func:`cpu_times` now includes new ``steal``, ``guest``\n  and ``guest_nice`` fields available on recent Linux kernels. Also,\n  :func:`cpu_percent` is more accurate.\n- :gh:`362`: add :func:`cpu_times_percent` (per-CPU-time utilization as a\n  percentage).\n\n**Bug fixes**\n\n- :gh:`234`, [Windows]: :func:`disk_io_counters` fails to list certain disks.\n- :gh:`264`, [Windows]: use of :func:`disk_partitions` may cause a message\n  box to appear.\n- :gh:`313`, [Linux], **[critical]**: :func:`virtual_memory` and\n  :func:`swap_memory` can crash on certain exotic Linux flavors having an\n  incomplete ``/proc`` interface. If that's the case we now set the\n  unretrievable stats to ``0`` and raise ``RuntimeWarning`` instead.\n- :gh:`315`, [macOS]: fix some compilation warnings.\n- :gh:`317`, [Windows]: cannot set process CPU affinity above 31 cores.\n- :gh:`319`, [Linux]: :meth:`Process.memory_maps` raises ``KeyError``\n  'Anonymous' on Debian squeeze.\n- :gh:`321`, [POSIX]: :meth:`Process.ppid` property is no longer cached as\n  the kernel may set the PPID to 1 in case of a zombie process.\n- :gh:`323`, [macOS]: :func:`disk_io_counters` ``read_time`` and\n  ``write_time`` parameters were reporting microseconds not milliseconds.\n  (patch by Gregory Szorc)\n- :gh:`331`: :meth:`Process.cmdline` is no longer cached after first access\n  as it may change.\n- :gh:`333`, [macOS]: leak of Mach ports (patch by rsesek@google.com)\n- :gh:`337`, [Linux], **[critical]**: :class:`Process` methods not working\n  because of a poor ``/proc`` implementation will raise ``NotImplementedError``\n  rather than ``RuntimeError`` and :meth:`Process.as_dict` will not blow up.\n  (patch by Curtin1060)\n- :gh:`338`, [Linux]: :func:`disk_io_counters` fails to find some disks.\n- :gh:`339`, [FreeBSD]: ``get_pid_list()`` can allocate all the memory on\n  system.\n- :gh:`341`, [Linux], **[critical]**: psutil might crash on import due to error\n  in retrieving system terminals map.\n- :gh:`344`, [FreeBSD]: :func:`swap_memory` might return incorrect results\n  due to ``kvm_open(3)`` not being called. (patch by Jean Sebastien)\n- :gh:`338`, [Linux]: :func:`disk_io_counters` fails to find some disks.\n- :gh:`351`, [Windows]: if psutil is compiled with MinGW32 (provided installers\n  for py2.4 and py2.5 are) :func:`disk_io_counters` will fail. (Patch by\n  m.malycha)\n- :gh:`353`, [macOS]: :func:`users` returns an empty list on macOS 10.8.\n- :gh:`356`: :meth:`Process.parent` now checks whether parent PID has been\n  reused in which case returns ``None``.\n- :gh:`365`: :meth:`Process.nice` (set) should check PID has not been reused\n  by another process.\n- :gh:`366`, [FreeBSD], **[critical]**: :meth:`Process.memory_maps`,\n  :meth:`Process.num_fds`,\n  :meth:`Process.open_files` and :meth:`Process.cwd` methods raise\n  ``RuntimeError`` instead of :exc:`AccessDenied`.\n\n**API changes**\n\n- :meth:`Process.cmdline` property is no longer cached after first access.\n- :meth:`Process.ppid` property is no longer cached after first access.\n- [Linux] :class:`Process` methods not working because of a poor ``/proc``\n  implementation will raise ``NotImplementedError`` instead of\n  ``RuntimeError``.\n- ``psutil.error`` module is deprecated and scheduled for removal.\n\n0.6.1 — 2012-08-16\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`316`: :meth:`Process.cmdline` property now makes a better job at\n  guessing the process executable from the cmdline.\n\n**Bug fixes**\n\n- :gh:`316`: :meth:`Process.exe` was resolved in case it was a symlink.\n- :gh:`318`, **[critical]**: Python 2.4 compatibility was broken.\n\n**API changes**\n\n- :meth:`Process.exe` can now return an empty string instead of raising\n  :exc:`AccessDenied`.\n- :meth:`Process.exe` is no longer resolved in case it's a symlink.\n\n0.6.0 — 2012-08-13\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`216`, [POSIX]: :meth:`Process.connections` UNIX sockets support.\n- :gh:`220`, [FreeBSD]: ``get_connections()`` has been rewritten in C and no\n  longer requires ``lsof``.\n- :gh:`222`, [macOS]: add support for :meth:`Process.cwd`.\n- :gh:`261`: per-process extended memory info\n  (:meth:`Process.memory_info_ex`).\n- :gh:`295`, [macOS]: :meth:`Process.exe` path is now determined by asking\n  the OS instead of being guessed from :meth:`Process.cmdline`.\n- :gh:`297`, [macOS]: the :class:`Process` methods below were always raising\n  :exc:`AccessDenied` for any process except the current one. Now this is no\n  longer true. Also they are 2.5x faster. :meth:`Process.name`,\n  :meth:`Process.memory_info`,\n  :meth:`Process.memory_percent`, :meth:`Process.cpu_times`,\n  :meth:`Process.cpu_percent`,\n  :meth:`Process.num_threads`.\n- :gh:`300`: add `pmap.py`_ script.\n- :gh:`301`: :func:`process_iter` now yields processes sorted by their PIDs.\n- :gh:`302`: per-process number of voluntary and involuntary context switches\n  (:meth:`Process.num_ctx_switches`).\n- :gh:`303`, [Windows]: the :class:`Process` methods below were always raising\n  :exc:`AccessDenied` for any process not owned by current user. Now this is no\n  longer true:\n  :meth:`Process.create_time`, :meth:`Process.cpu_times`,\n  :meth:`Process.cpu_percent`,\n  :meth:`Process.memory_info`, :meth:`Process.memory_percent`,\n  :meth:`Process.num_handles`,\n  :meth:`Process.io_counters`.\n- :gh:`305`: add `netstat.py`_ script.\n- :gh:`311`: system memory functions has been refactorized and rewritten and\n  now provide a more detailed and consistent representation of the system\n  memory. Added new :func:`virtual_memory` and :func:`swap_memory`\n  functions. All old memory-related functions are deprecated. Also two new\n  example scripts were added:  `free.py`_ and `meminfo.py`_.\n- :gh:`312`: ``net_io_counters()`` named tuple includes 4 new fields: ``errin``,\n  ``errout``, ``dropin`` and ``dropout``, reflecting the number of packets\n  dropped and with errors.\n\n**Bug fixes**\n\n- :gh:`298`, [macOS], [BSD]: memory leak in :meth:`Process.num_fds`.\n- :gh:`299`: potential memory leak every time ``PyList_New(0)`` is used.\n- :gh:`303`, [Windows], **[critical]**: potential heap corruption in\n  :meth:`Process.num_threads` and :meth:`Process.status` methods.\n- :gh:`305`, [FreeBSD], **[critical]**: can't compile on FreeBSD 9 due to\n  removal of ``utmp.h``.\n- :gh:`306`, **[critical]**: at C level, errors are not checked when invoking\n  ``Py*`` functions which create or manipulate Python objects leading to\n  potential memory related errors and/or segmentation faults.\n- :gh:`307`, [FreeBSD]: values returned by :func:`net_io_counters` are wrong.\n- :gh:`308`, [BSD], [Windows]: ``psutil.virtmem_usage()`` wasn't actually\n  returning information about swap memory usage as it was supposed to do. It\n  does now.\n- :gh:`309`: :meth:`Process.open_files` might not return files which can not\n  be accessed due to limited permissions. :exc:`AccessDenied` is now raised\n  instead.\n\n**API changes**\n\n- ``psutil.phymem_usage()`` is deprecated (use :func:`virtual_memory`)\n- ``psutil.virtmem_usage()`` is deprecated (use :func:`swap_memory`)\n- [Linux]: ``psutil.phymem_buffers()`` is deprecated (use\n  :func:`virtual_memory`)\n- [Linux]: ``psutil.cached_phymem()`` is deprecated (use\n  :func:`virtual_memory`)\n- [Windows], [BSD]: ``psutil.virtmem_usage()`` now returns information about\n  swap memory instead of virtual memory.\n\n0.5.1 — 2012-06-29\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`293`, [Windows]: :meth:`Process.exe` path is now determined by asking\n  the OS instead of being guessed from :meth:`Process.cmdline`.\n\n**Bug fixes**\n\n- :gh:`292`, [Linux]: race condition in process :meth:`Process.open_files`,\n  :meth:`Process.connections`, :meth:`Process.threads`.\n- :gh:`294`, [Windows]: :meth:`Process.cpu_affinity` is only able to set CPU\n  #0.\n\n0.5.0 — 2012-06-27\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`195`, [Windows]: number of handles opened by process\n  (:meth:`Process.num_handles`).\n- :gh:`209`: :func:`disk_partitions` now provides also mount options.\n- :gh:`229`: list users currently connected on the system (:func:`users`).\n- :gh:`238`, [Linux], [Windows]: process CPU affinity (get and set,\n  :meth:`Process.cpu_affinity`).\n- :gh:`242`: add ``recursive=True`` to :meth:`Process.children`: return all\n  process descendants.\n- :gh:`245`, [POSIX]: :meth:`Process.wait` incrementally consumes less CPU\n  cycles.\n- :gh:`257`, [Windows]: removed Windows 2000 support.\n- :gh:`258`, [Linux]: :meth:`Process.memory_info` is now 0.5x faster.\n- :gh:`260`: process's mapped memory regions. (Windows patch by wj32.64, macOS\n  patch by Jeremy Whitlock)\n- :gh:`262`, [Windows]: :func:`disk_partitions` was slow due to inspecting\n  the floppy disk drive also when parameter is ``all=False``.\n- :gh:`273`: ``psutil.get_process_list()`` is deprecated.\n- :gh:`274`: psutil no longer requires ``2to3`` at installation time in order\n  to work with Python 3.\n- :gh:`278`: new :meth:`Process.as_dict` method.\n- :gh:`281`: :meth:`Process.ppid`, :meth:`Process.name`,\n  :meth:`Process.exe`,\n  :meth:`Process.cmdline` and :meth:`Process.create_time` properties of\n  :class:`Process` class are now cached after being accessed.\n- :gh:`282`: ``psutil.STATUS_*`` constants can now be compared by using their\n  string representation.\n- :gh:`283`: speedup :meth:`Process.is_running` by caching its return value\n  in case the process is terminated.\n- :gh:`284`, [POSIX]: per-process number of opened file descriptors\n  (:meth:`Process.num_fds`).\n- :gh:`287`: :func:`process_iter` now caches :class:`Process` instances\n  between calls.\n- :gh:`290`: :meth:`Process.nice` property is deprecated in favor of new\n  ``get_nice()`` and ``set_nice()`` methods.\n\n**Bug fixes**\n\n- :gh:`193`: :class:`Popen` constructor can throw an exception if the spawned\n  process terminates quickly.\n- :gh:`240`, [macOS]: incorrect use of ``free()`` for\n  :meth:`Process.connections`.\n- :gh:`244`, [POSIX]: :meth:`Process.wait` can hog CPU resources if called\n  against a process which is not our children.\n- :gh:`248`, [Linux]: :func:`net_io_counters` might return erroneous NIC\n  names.\n- :gh:`252`, [Windows]: :meth:`Process.cwd` erroneously raise\n  :exc:`NoSuchProcess` for processes owned by another user.  It now raises\n  :exc:`AccessDenied` instead.\n- :gh:`266`, [Windows]: ``psutil.get_pid_list()`` only shows 1024 processes.\n  (patch by Amoser)\n- :gh:`267`, [macOS]: :meth:`Process.connections` returns wrong remote\n  address. (Patch by Amoser)\n- :gh:`272`, [Linux]: :meth:`Process.open_files` potential race condition can\n  lead to unexpected :exc:`NoSuchProcess` exception. Also, we can get incorrect\n  reports of not absolutized path names.\n- :gh:`275`, [Linux]: ``Process.io_counters()`` erroneously raise\n  :exc:`NoSuchProcess` on old Linux versions. Where not available it now raises\n  ``NotImplementedError``.\n- :gh:`286`: :meth:`Process.is_running` doesn't actually check whether PID\n  has been reused.\n- :gh:`314`: :meth:`Process.children` can sometimes return non-children.\n\n**API changes**\n\n- ``Process.nice`` property is deprecated in favor of new ``get_nice()`` and\n  ``set_nice()`` methods.\n- ``psutil.get_process_list()`` is deprecated.\n- :meth:`Process.ppid`, :meth:`Process.name`, :meth:`Process.exe`,\n  :meth:`Process.cmdline` and :meth:`Process.create_time` properties of\n  :class:`Process` class are now cached after being accessed, meaning\n  :exc:`NoSuchProcess` will no longer be raised in case the process is gone in\n  the meantime.\n- ``psutil.STATUS_*`` constants can now be compared by using their string\n  representation.\n\n0.4.1 — 2011-12-14\n^^^^^^^^^^^^^^^^^^\n\n**Bug fixes**\n\n- :gh:`228`: some example scripts were not working with Python 3.\n- :gh:`230`, [Windows], [macOS]: fix memory leak in\n  :meth:`Process.connections`.\n- :gh:`232`, [Linux]: ``psutil.phymem_usage()`` can report erroneous values\n  which are different than ``free`` command.\n- :gh:`236`, [Windows]: fix memory/handle leak in\n  :meth:`Process.memory_info`,\n  :meth:`Process.suspend` and :meth:`Process.resume` methods.\n\n0.4.0 — 2011-10-29\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`150`: network I/O counters (:func:`net_io_counters`). (macOS and\n  Windows patch by Jeremy Whitlock)\n- :gh:`154`, [FreeBSD]: add support for :meth:`Process.cwd`.\n- :gh:`157`, [Windows]: provide installer for Python 3.2 64-bit.\n- :gh:`198`: :meth:`Process.wait` with ``timeout=0`` can now be used to make\n  the function return immediately.\n- :gh:`206`: disk I/O counters (:func:`disk_io_counters`). (macOS and Windows\n  patch by Jeremy Whitlock)\n- :gh:`213`: add `iotop.py`_ script.\n- :gh:`217`: :meth:`Process.connections` now has a ``kind`` argument to\n  filter for connections with different criteria.\n- :gh:`221`, [FreeBSD]: :meth:`Process.open_files` has been rewritten in C\n  and no longer relies on ``lsof``.\n- :gh:`223`: add `top.py`_ script.\n- :gh:`227`: add `nettop.py`_ script.\n\n**Bug fixes**\n\n- :gh:`135`, [macOS]: psutil cannot create :class:`Process` object.\n- :gh:`144`, [Linux]: no longer support 0 special PID.\n- :gh:`188`, [Linux]: psutil import error on Linux ARM architectures.\n- :gh:`194`, [POSIX]: :meth:`Process.cpu_percent` now reports a percentage\n  over 100 on multicore processors.\n- :gh:`197`, [Linux]: :meth:`Process.connections` is broken on platforms not\n  supporting IPv6.\n- :gh:`200`, [Linux], **[critical]**: ``psutil.NUM_CPUS`` not working on armel\n  and sparc architectures and causing crash on module import.\n- :gh:`201`, [Linux]: :meth:`Process.connections` is broken on big-endian\n  architectures.\n- :gh:`211`: :class:`Process` instance can unexpectedly raise\n  :exc:`NoSuchProcess` if tested for equality with another object.\n- :gh:`218`, [Linux], **[critical]**: crash at import time on Debian 64-bit\n  because of a missing line in ``/proc/meminfo``.\n- :gh:`226`, [FreeBSD], **[critical]**: crash at import time on FreeBSD 7 and\n  minor.\n\n0.3.0 — 2011-07-08\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`125`: system per-cpu percentage utilization and times\n  (:meth:`Process.cpu_times`,\n  :meth:`Process.cpu_percent`).\n- :gh:`163`: per-process associated terminal / TTY\n  (:meth:`Process.terminal`).\n- :gh:`171`: added ``get_phymem()`` and ``get_virtmem()`` functions returning\n  system memory information (``total``, ``used``, ``free``) and memory percent\n  usage. ``total_*``, ``avail_*`` and ``used_*`` memory functions are\n  deprecated.\n- :gh:`172`: disk usage statistics (:func:`disk_usage`).\n- :gh:`174`: mounted disk partitions (:func:`disk_partitions`).\n- :gh:`179`: setuptools is now used in setup.py\n\n**Bug fixes**\n\n- :gh:`159`, [Windows]: ``SetSeDebug()`` does not close handles or unset\n  impersonation on return.\n- :gh:`164`, [Windows]: wait function raises a ``TimeoutException`` when a\n  process returns ``-1``.\n- :gh:`165`: :meth:`Process.status` raises an unhandled exception.\n- :gh:`166`: :meth:`Process.memory_info` leaks handles hogging system\n  resources.\n- :gh:`168`: :func:`cpu_percent` returns erroneous results when used in\n  non-blocking mode.  (patch by Philip Roberts)\n- :gh:`178`, [macOS]: :meth:`Process.threads` leaks memory.\n- :gh:`180`, [Windows]: :meth:`Process.num_threads` and\n  :meth:`Process.threads` methods can raise :exc:`NoSuchProcess` exception\n  while process still exists.\n\n0.2.1 — 2011-03-20\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`64`: per-process I/O counters (:meth:`Process.io_counters`).\n- :gh:`116`: per-process :meth:`Process.wait` (wait for process to terminate\n  and return its exit code).\n- :gh:`134`: per-process threads (:meth:`Process.threads`).\n- :gh:`136`: :meth:`Process.exe` path on FreeBSD is now determined by asking\n  the kernel instead of guessing it from cmdline[0].\n- :gh:`137`: per-process real, effective and saved user and group ids\n  (:meth:`Process.gids`).\n- :gh:`140`: system boot time (:func:`boot_time`).\n- :gh:`142`: per-process get and set niceness (priority)\n  (:meth:`Process.nice`).\n- :gh:`143`: per-process status (:meth:`Process.status`).\n- :gh:`147` [Linux]: per-process I/O niceness / priority\n  (:meth:`Process.ionice`).\n- :gh:`148`: :class:`Popen` class which tidies up ``subprocess.Popen`` and\n  :class:`Process` class in a single interface.\n- :gh:`152`, [macOS]: :meth:`Process.open_files` implementation has been\n  rewritten in C and no longer relies on ``lsof`` resulting in a 3x speedup.\n- :gh:`153`, [macOS]: :meth:`Process.connections` implementation has been\n  rewritten in C and no longer relies on ``lsof`` resulting in a 3x speedup.\n\n**Bug fixes**\n\n- :gh:`83`, [macOS]:  :meth:`Process.cmdline` is empty on macOS 64-bit.\n- :gh:`130`, [Linux]: a race condition can cause ``IOError`` exception be\n  raised on if process disappears between ``open()`` and the subsequent\n  ``read()`` call.\n- :gh:`145`, [Windows], **[critical]**: ``WindowsError`` was raised instead of\n  :exc:`AccessDenied` when using :meth:`Process.resume` or\n  :meth:`Process.suspend`.\n- :gh:`146`, [Linux]: :meth:`Process.exe` property can raise ``TypeError`` if\n  path contains NULL bytes.\n- :gh:`151`, [Linux]: :meth:`Process.exe` and :meth:`Process.cwd` for PID 0\n  return inconsistent data.\n\n**API changes**\n\n- :class:`Process` ``uid`` and ``gid`` properties are deprecated in favor of\n  ``uids`` and ``gids`` properties.\n\n0.2.0 — 2010-11-13\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`79`: per-process open files (:meth:`Process.open_files`).\n- :gh:`88`: total system physical cached memory.\n- :gh:`88`: total system physical memory buffers used by the kernel.\n- :gh:`91`: add :meth:`Process.send_signal` and :meth:`Process.terminate`\n  methods.\n- :gh:`95`: :exc:`NoSuchProcess` and :exc:`AccessDenied` exception classes now\n  provide ``pid``, ``name`` and ``msg`` attributes.\n- :gh:`97`: per-process children (:meth:`Process.children`).\n- :gh:`98`: :meth:`Process.cpu_times` and :meth:`Process.memory_info` now\n  return a named tuple instead of a tuple.\n- :gh:`103`: per-process opened TCP and UDP connections\n  (:meth:`Process.connections`).\n- :gh:`107`, [Windows]: add support for Windows 64 bit. (patch by cjgohlke)\n- :gh:`111`: per-process executable name (:meth:`Process.exe`).\n- :gh:`113`: exception messages now include :meth:`Process.name` and\n  :attr:`Process.pid`.\n- :gh:`114`, [Windows]: :meth:`Process.username` has been rewritten in pure C\n  and no longer uses WMI resulting in a big speedup. Also, pywin32 is no longer\n  required as a third-party dependency. (patch by wj32)\n- :gh:`117`, [Windows]: added support for Windows 2000.\n- :gh:`123`: :func:`cpu_percent` and :meth:`Process.cpu_percent` accept a\n  new ``interval`` parameter.\n- :gh:`129`: per-process threads (:meth:`Process.threads`).\n\n**Bug fixes**\n\n- :gh:`80`: fixed warnings when installing psutil with easy_install.\n- :gh:`81`, [Windows]: psutil fails to compile with Visual Studio.\n- :gh:`94`: :meth:`Process.suspend` raises ``OSError`` instead of\n  :exc:`AccessDenied`.\n- :gh:`86`, [FreeBSD]: psutil didn't compile against FreeBSD 6.x.\n- :gh:`102`, [Windows]: orphaned process handles obtained by using\n  ``OpenProcess`` in C were left behind every time :class:`Process` class was\n  instantiated.\n- :gh:`111`, [POSIX]: ``path`` and ``name`` :class:`Process` properties report\n  truncated or erroneous values on POSIX.\n- :gh:`120`, [macOS]: :func:`cpu_percent` always returning 100%.\n- :gh:`112`: ``uid`` and ``gid`` properties don't change if process changes\n  effective user/group id at some point.\n- :gh:`126`: :meth:`Process.ppid`, :meth:`Process.uids`,\n  :meth:`Process.gids`,\n  :meth:`Process.name`,\n  :meth:`Process.exe`, :meth:`Process.cmdline` and\n  :meth:`Process.create_time` properties are no longer cached and correctly\n  raise :exc:`NoSuchProcess` exception if the process disappears.\n\n**API changes**\n\n- ``psutil.Process.path`` property is deprecated and works as an alias for\n  ``psutil.Process.exe`` property.\n- :meth:`Process.kill`: signal argument was removed - to send a signal to the\n  process use :meth:`Process.send_signal` method instead.\n- :meth:`Process.memory_info` returns a nametuple instead of a tuple.\n- :func:`cpu_times` returns a nametuple instead of a tuple.\n- New :class:`Process` methods: :meth:`Process.open_files`,\n  :meth:`Process.connections`,\n  :meth:`Process.send_signal` and :meth:`Process.terminate`.\n- :meth:`Process.ppid`, :meth:`Process.uids`, :meth:`Process.gids`,\n  :meth:`Process.name`,\n  :meth:`Process.exe`, :meth:`Process.cmdline` and\n  :meth:`Process.create_time` properties are no longer cached and raise\n  :exc:`NoSuchProcess` exception if process disappears.\n- :func:`cpu_percent` no longer returns immediately (see issue 123).\n- :meth:`Process.cpu_percent` and :func:`cpu_percent` no longer returns\n  immediately by default (see issue :gh:`123`).\n\n0.1.3 — 2010-03-02\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`14`: :meth:`Process.username`.\n- :gh:`51`, [Linux], [Windows]: per-process current working directory\n  (:meth:`Process.cwd`).\n- :gh:`59`: :meth:`Process.is_running` is now 10 times faster.\n- :gh:`61`, [FreeBSD]: added supoprt for FreeBSD 64 bit.\n- :gh:`71`: per-process suspend and resume (:meth:`Process.suspend` and\n  :meth:`Process.resume`).\n- :gh:`75`: Python 3 support.\n\n**Bug fixes**\n\n- :gh:`36`: :meth:`Process.cpu_times` and :meth:`Process.memory_info`\n  functions succeeded. also for dead processes while a :exc:`NoSuchProcess`\n  exception is supposed to be raised.\n- :gh:`48`, [FreeBSD]: incorrect size for MIB array defined in ``getcmdargs``.\n- :gh:`49`, [FreeBSD]: possible memory leak due to missing ``free()`` on error\n  condition in ``getcmdpath()``.\n- :gh:`50`, [BSD]: fixed ``getcmdargs()`` memory fragmentation.\n- :gh:`55`, [Windows]: ``test_pid_4`` was failing on Windows Vista.\n- :gh:`57`: some unit tests were failing on systems where no swap memory is\n  available.\n- :gh:`58`: :meth:`Process.is_running` is now called before\n  :meth:`Process.kill` to make sure we are going to kill the correct process.\n- :gh:`73`, [macOS]: virtual memory size reported on includes shared library\n  size.\n- :gh:`77`: :exc:`NoSuchProcess` wasn't raised on :meth:`Process.create_time`\n  if\n  :meth:`Process.kill` was used first.\n\n0.1.2 — 2009-05-06\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`32`: Per-process CPU user/kernel times (:meth:`Process.cpu_times`).\n- :gh:`33`: Per-process create time (:meth:`Process.create_time`).\n- :gh:`34`: Per-process CPU utilization percentage\n  (:meth:`Process.cpu_percent`).\n- :gh:`38`: Per-process memory usage (bytes) (:meth:`Process.memory_info`).\n- :gh:`41`: Per-process memory percent (:meth:`Process.memory_percent`).\n- :gh:`39`: System uptime (:func:`boot_time`).\n- :gh:`43`: Total system virtual memory.\n- :gh:`46`: Total system physical memory.\n- :gh:`44`: Total system used/free virtual and physical memory.\n\n**Bug fixes**\n\n- :gh:`36`, [Windows]: :exc:`NoSuchProcess` not raised when accessing timing\n  methods.\n- :gh:`40`, [FreeBSD], [macOS]: fix ``test_get_cpu_times`` failures.\n- :gh:`42`, [Windows]: :meth:`Process.memory_percent` raises\n  :exc:`AccessDenied`.\n\n0.1.1 — 2009-03-06\n^^^^^^^^^^^^^^^^^^\n\n**Enhancements**\n\n- :gh:`4`, [FreeBSD]: support for all functions of psutil.\n- :gh:`9`, [macOS], [Windows]: add ``Process.uid`` and ``Process.gid``,\n  returning process UID and GID.\n- :gh:`11`: per-process parent object: :meth:`Process.parent` property\n  returns a\n  :class:`Process` object representing the parent process, and\n  :meth:`Process.ppid` returns the parent PID.\n- :gh:`12`, :gh:`15`:\n  :exc:`NoSuchProcess` exception now raised when creating an object for a\n  nonexistent process, or when retrieving information about a process that\n  has gone away.\n- :gh:`21`, [Windows]: :exc:`AccessDenied` exception created for raising access\n  denied errors from ``OSError`` or ``WindowsError`` on individual platforms.\n- :gh:`26`: :func:`process_iter` function to iterate over processes as\n  :class:`Process` objects with a generator.\n- :class:`Process` objects can now also be compared with == operator for\n  equality (PID, name, command line are compared).\n\n**Bug fixes**\n\n- :gh:`16`, [Windows]: Special case for \"System Idle Process\" (PID 0) which\n  otherwise would return an \"invalid parameter\" exception.\n- :gh:`17`: get_process_list() ignores :exc:`NoSuchProcess` and\n  :exc:`AccessDenied` exceptions during building of the list.\n- :gh:`22`, [Windows]: :meth:`Process.kill` for PID 0 was failing with an\n  unset exception.\n- :gh:`23`, [Linux], [macOS]: create special case for :func:`pid_exists` with\n  PID 0.\n- :gh:`24`, [Windows], **[critical]**: :meth:`Process.kill` for PID 0 now\n  raises\n  :exc:`AccessDenied` exception instead of ``WindowsError``.\n- :gh:`30`: psutil.get_pid_list() was returning two 0 PIDs.\n\n\n.. _`cpu_distribution.py`: https://github.com/giampaolo/psutil/blob/master/scripts/cpu_distribution.py\n.. _`disk_usage.py`: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py\n.. _`free.py`: https://github.com/giampaolo/psutil/blob/master/scripts/free.py\n.. _`iotop.py`: https://github.com/giampaolo/psutil/blob/master/scripts/iotop.py\n.. _`meminfo.py`: https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py\n.. _`netstat.py`: https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py\n.. _`nettop.py`: https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py\n.. _`pidof.py`: https://github.com/giampaolo/psutil/blob/master/scripts/pidof.py\n.. _`pmap.py`: https://github.com/giampaolo/psutil/blob/master/scripts/pmap.py\n.. _`procinfo.py`: https://github.com/giampaolo/psutil/blob/master/scripts/procinfo.py\n.. _`procsmem.py`: https://github.com/giampaolo/psutil/blob/master/scripts/procsmem.py\n.. _`ps.py`: https://github.com/giampaolo/psutil/blob/master/scripts/ps.py\n.. _`pstree.py`: https://github.com/giampaolo/psutil/blob/master/scripts/pstree.py\n.. _`top.py`: https://github.com/giampaolo/psutil/blob/master/scripts/top.py\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Sphinx config file.\"\"\"\n\n# See doc at:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output\n\nimport datetime\nimport pathlib\nimport sys\n\nPROJECT_NAME = \"psutil\"\nAUTHOR = \"Giampaolo Rodola\"\nTHIS_YEAR = str(datetime.datetime.now().year)\nHERE = pathlib.Path(__file__).resolve().parent\nROOT_DIR = HERE.parent\n\nsys.path.insert(0, str(ROOT_DIR))\nfrom _bootstrap import get_version  # noqa: E402\n\nVERSION = get_version()\n\n\nsys.path.insert(0, str(HERE / '_ext'))\n\nextensions = [\n    \"sphinx.ext.extlinks\",\n    \"sphinx.ext.intersphinx\",\n    \"sphinx.ext.viewcode\",\n    \"sphinx_copybutton\",\n    # custom extensions in _ext/ dir\n    \"availability\",\n    \"add_home_link\",\n    \"changelog_anchors\",\n    \"check_python_syntax\",\n]\n\nproject = PROJECT_NAME\ncopyright = f\"2009-{THIS_YEAR}, {AUTHOR}\"\nauthor = AUTHOR\nversion = VERSION\nrelease = VERSION\nintersphinx_mapping = {\n    'python': ('https://docs.python.org/3', None),\n}\nextlinks = {\n    'gh': ('https://github.com/giampaolo/psutil/issues/%s', '#%s'),\n}\ntemplates_path = ['_templates']\nhtml_static_path = ['_static']\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\nhtml_theme = 'sphinx_rtd_theme'\nhtmlhelp_basename = f\"{PROJECT_NAME}-doc\"\ncopybutton_exclude = '.linenos, .gp'\nhtml_css_files = [\n    'https://media.readthedocs.org/css/sphinx_rtd_theme.css',\n    'https://media.readthedocs.org/css/readthedocs-doc-embed.css',\n    'css/custom.css',\n]\n"
  },
  {
    "path": "docs/credits.rst",
    "content": ".. currentmodule:: psutil\n\nCredits\n=======\n\nI would like to recognize some of the people who have been instrumental in the\ndevelopment of psutil. I'm sure I'm forgetting someone (feel free to email me)\nbut here is a short list.\n\nA big thanks to all of you.\n\n— Giampaolo\n\nTop contributors\n----------------\n\n* `Giampaolo Rodola`_: creator, primary author and long-time maintainer\n* `Jay Loden`_: original co-author, initial design and bootstrap, original\n  macOS / Windows / FreeBSD implementations\n* `Arnon Yaari`_: AIX implementation\n* `Landry Breuil`_: original OpenBSD implementation\n* `Ryo Onodera`_ and `Thomas Klausner`_: original NetBSD implementation\n\nDonations\n---------\n\nThe following individuals and organizations have supported\npsutil development through donations.\n\nCompanies:\n\n* `Apivoid`_ *(sponsor)*\n* `Canonical Juju`_\n* `Canonical Launchpad`_\n* `Canonical`_\n* `Codecov`_\n* `Indeed Engineering`_\n* `Kubernetes`_\n* `Robusta`_\n* `sansec.io`_ *(sponsor)*\n* `Sentry`_\n* `Sourcegraph`_\n* `Tidelift`_ *(sponsor)*\n\nPeople:\n\n* `Alex Laird`_\n* Alexander Kaftan\n* `Alexey Vazhnov`_\n* Amit Kulkarni\n* Andrew Bays\n* `Artyom Vancyan`_\n* Brett Harris\n* `c0m4r`_\n* Carver Koella\n* `Chenyoo Hao`_\n* `Coşkun Deniz`_\n* `cybersecgeek`_\n* `Daniel Widdis`_\n* `Eugenio E Breijo`_\n* `Evan Allrich`_\n* Florian Bruhin\n* `great-work-told-is`_\n* Gyula Áfra\n* HTB Industries\n* `inarikami`_\n* `JeremyGrosser`_\n* `Johannes Maron`_\n* `Jakob P. Liljenberg`_\n* `Karthik Kumar`_\n* Kahntent\n* Kristjan Võrk\n* Mahmut Dumlupinar\n* Marco Schrank\n* Matthew Callow\n* Mindview LLC\n* `Maximilian Wu`_\n* Mehver\n* mirko\n* Morgan Heijdemann\n* Oche Ejembi\n* `Ofek Lev`_\n* Olivier Grisel\n* Pavan Maddamsetti\n* `PySimpleGUI`_\n* Peter Friedland\n* Praveen Bhamidipati\n* Remi Chateauneu\n* `roboflow.com`_\n* Rodion Stratov\n* Russell Robinson\n* `Sašo Živanović`_\n* `scoutapm-sponsorships`_\n* Sigmund Vik\n* `trashnothing.com`_\n* Thomas Guettler\n* Willem de Groot\n* Wompasoft\n* `Valeriy Abramov`_\n* Григорьев Андрей\n\nCode contributors by year\n-------------------------\n\n.. image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg?label=Total%20contributors&style=flat\n    :target: https://github.com/giampaolo/psutil/graphs/contributors\n    :alt: contributors\n\n2026\n~~~~\n\n* `Amaan Qureshi`_ - :gh:`2770`\n* `Felix Yan`_ - :gh:`2732`\n* `Sergey Fedorov`_ - :gh:`2701`\n\n2025\n~~~~\n\n* `Ben Peddell`_ - :gh:`2495`, :gh:`2568`\n* `Ben Raz`_ - :gh:`2643`\n* `Eli Wenig`_ - :gh:`2638`\n* `Fabien Bousquet`_ - :gh:`2529`\n* `Irene Sheen`_ - :gh:`2606`\n* `Isaac K. Ko`_ - :gh:`2612`\n* `Jonathan Kohler`_ - :gh:`2527`\n* `Lysandros Nikolaou`_ - :gh:`2565`, :gh:`2588`, :gh:`2589`, :gh:`2590`, :gh:`2591`, :gh:`2609`, :gh:`2615`, :gh:`2627`, :gh:`2659` (wheels for free-threaded Python)\n* `Marcel Telka`_ - :gh:`2469`, :gh:`2545`, :gh:`2546`, :gh:`2592`, :gh:`2594`\n* `Matthieu Darbois`_ - :gh:`2503`, :gh:`2581` (Windows ARM64 wheels)\n* `Sergey Fedorov`_ - :gh:`2694`\n* `Will Hawes`_ - :gh:`2496`\n* `Xianpeng Shen`_ - :gh:`2640`\n\n2024\n~~~~\n\n* `Aleksey Lobanov`_ - :gh:`2457`\n* `Cristian Vîjdea`_ - :gh:`2442`\n* `Matthieu Darbois`_ - :gh:`2370`, :gh:`2375`, :gh:`2417`, :gh:`2425`, :gh:`2429`, :gh:`2450`, :gh:`2479`, :gh:`2486` (macOS and Linux ARM64 wheels)\n* `Mayank Jha`_ - :gh:`2379`\n* `Oliver Tomé`_ - :gh:`2222`\n* `Ryan Carsten Schmidt`_ - :gh:`2361`, :gh:`2364`, :gh:`2365`\n* `Sam Gross`_ - :gh:`2401`, :gh:`2402`, :gh:`2427`, :gh:`2428` (free-threading Python)\n* `Shade Gladden`_ - :gh:`2376`\n\n2023\n~~~~\n\n* `Amir Rossert`_ - :gh:`2346`\n* `Matthieu Darbois`_ - :gh:`2211`, :gh:`2216`, :gh:`2246`, :gh:`2247`, :gh:`2252`, :gh:`2269`, :gh:`2270`, :gh:`2315`\n* `Po-Chuan Hsieh`_ - :gh:`2186`, :gh:`1646`\n* `Thomas Klausner`_ - :gh:`2241`\n* `Xuehai Pan`_ - :gh:`2266`\n\n2022\n~~~~\n\n* `Amir Rossert`_ - :gh:`2156`, :gh:`2345`\n* `Bernhard Urban-Forster`_ - :gh:`2135`\n* `Chris Lalancette`_ - :gh:`2037` (:func:`net_if_stats` flags arg on POSIX)\n* `Daniel Li`_ - :gh:`2150`\n* `Daniel Widdis`_ - :gh:`2077`, :gh:`2160`\n* `Garrison Carter`_ - :gh:`2096`\n* `Hiroyuki Tanaka`_ - :gh:`2086`\n* `Hugo van Kemenade`_ - :gh:`2099` (Drop Python 2.6 support)\n* `Lawrence D'Anna`_ - :gh:`2010`\n* `Matthieu Darbois`_ - :gh:`1954`, :gh:`2021`, :gh:`2039`, :gh:`2040`, :gh:`2102`, :gh:`2111`, :gh:`2142`, :gh:`2145`, :gh:`2146`, :gh:`2147`, :gh:`2153`, :gh:`2155`, :gh:`2168`\n* `Steve Dower`_ - :gh:`2080`\n* `Thomas Klausner`_ - :gh:`2088`, :gh:`2128`\n* Torsten Blum - :gh:`2114`\n\n2021\n~~~~\n\n* `David Knaack`_ - :gh:`1921`\n* `Guillermo`_ - :gh:`1913`\n* `Martin Liška`_ - :gh:`1851`\n* `MaWe2019`_ - :gh:`1953`\n* `Nikita Radchenko`_ - :gh:`1940`\n* `Oleksii Shevchuk`_ - :gh:`1904`\n* `Olivier Dormond`_ - :gh:`1956`\n* `Pablo Baeyens`_ - :gh:`1598`\n* `PetrPospisil`_ - :gh:`1980`\n* `Saeed Rasooli`_ - :gh:`1996`\n* `Wilfried Goesgens`_ - :gh:`1990`\n* `Xuehai Pan`_ - :gh:`1949`\n\n2020\n~~~~\n\n* `Anselm Kruis`_ - :gh:`1695`\n* `Armin Gruner`_ - :gh:`1800` (:meth:`Process.environ` on BSD)\n* `Chris Burger`_ - :gh:`1830`\n* `vser1`_ - :gh:`1637`\n* `Grzegorz Bokota`_ - :gh:`1758`, :gh:`1762`\n* `Jake Omann`_ - :gh:`1876`\n* `Jakob P. Liljenberg`_ - :gh:`1837`, :gh:`1838`\n* `Javad Karabi`_ - :gh:`1648`\n* `Julien Lebot`_ - :gh:`1768` (Windows Nano server support)\n* `Michał Górny`_ - :gh:`1726`\n* `Mike Hommey`_ - :gh:`1665`\n* `Po-Chuan Hsieh`_ - :gh:`1646`\n* `Riccardo Schirone`_ - :gh:`1616`\n* `Tim Schlueter`_ - :gh:`1708`\n* `Vincent A. Arcila`_ - :gh:`1620`, :gh:`1727`\n\n2019\n~~~~\n\n* `qcha0`_ - :gh:`1491`\n* `Alex Manuskin`_ - :gh:`1487`\n* `Ammar Askar`_ - :gh:`1485` (:func:`getloadavg` on Windows)\n* `Arnon Yaari`_ - :gh:`607`, :gh:`1349`, :gh:`1409`, :gh:`1500`, :gh:`1505`, :gh:`1507`, :gh:`1533`\n* `Athos Ribeiro`_ - :gh:`1585`\n* `Benjamin Drung`_ - :gh:`1462`\n* `Bernát Gábor`_ - :gh:`1565`\n* `Cedric Lamoriniere`_ - :gh:`1470`\n* `Daniel Beer`_ - :gh:`1471`\n* `David Brochart`_ - :gh:`1493`, :gh:`1496`\n* `EccoTheFlintstone`_ - :gh:`1368`, :gh:`1348`\n* `Erwan Le Pape`_ - :gh:`1570`\n* `Ghislain Le Meur`_ - :gh:`1379`\n* `Kamil Rytarowski`_ - :gh:`1526`, :gh:`1530` (:meth:`Process.cwd` for NetBSD)\n* `Nathan Houghton`_ - :gh:`1619`\n* `Samer Masterson`_ - :gh:`1480`\n* `Xiaoling Bao`_ - :gh:`1223`\n* Mozilla Foundation - Sample code for process USS memory\n\n2018\n~~~~\n\n* `Alex Manuskin`_ - :gh:`1284`, :gh:`1345`, :gh:`1350`, :gh:`1369` (:func:`sensors_temperatures` for macOS, FreeBSD, Linux)\n* `Arnon Yaari`_ - :gh:`1214`\n* `Dan Vinakovsky`_ - :gh:`1216`\n* `Denis Krienbühl`_ - :gh:`1260`\n* `Ilya Yanok`_ - :gh:`1332`\n* `janderbrain`_ - :gh:`1169`\n* `Jaime Fullaondo`_ - :gh:`1320`\n* `Jean-Luc Migot`_ - :gh:`1258`, :gh:`1289`\n* `Koen Kooi`_ - :gh:`1360`\n* `Lawrence Ye`_ - :gh:`1321`\n* `Maxime Mouial`_ - :gh:`1239`\n* `Nikhil Marathe`_ - :gh:`1278`\n* `stswandering`_ - :gh:`1243`\n* `Sylvain Duchesne`_ - :gh:`1294`\n\n2017\n~~~~\n\n* `Adrian Page`_ - :gh:`1160`\n* `Akos Kiss`_ - :gh:`1150`\n* `Alexander Hasselhuhn`_ - :gh:`1022`\n* `Antoine Pitrou`_ - :gh:`1186`\n* `Arnon Yaari`_ - :gh:`1130`, :gh:`1137`, :gh:`1145`, :gh:`1156`, :gh:`1164`, :gh:`1174`, :gh:`1177`, :gh:`1123` (AIX implementation)\n* `Baruch Siach`_ - :gh:`872`\n* `Danek Duvall`_ - :gh:`1002`\n* `Gleb Smirnoff`_ - :gh:`1070`, :gh:`1076`, :gh:`1079`\n* `Himanshu Shekhar`_ - :gh:`1036`\n* `Jakub Bacic`_ - :gh:`1127`\n* `Matthew Long`_ - :gh:`1167`\n* `Nicolas Hennion`_ - :gh:`974`\n* `Oleksii Shevchuk`_ - :gh:`1091`, :gh:`1093`, :gh:`1220`, :gh:`1346`\n* `Pierre Fersing`_ - :gh:`950`\n* `Sebastian Saip`_ - :gh:`1141`\n* `Thiago Borges Abdnur`_ - :gh:`959`\n* `Yannick Gingras`_ - :gh:`1057`\n\n2016\n~~~~\n\n* `Andre Caron`_ - :gh:`880`\n* `Arcadiy Ivanov`_ - :gh:`919`\n* `ewedlund`_ - :gh:`874`\n* `Farhan Khan`_ - :gh:`823`\n* `Frank Benkstein`_ - :gh:`732`, :gh:`733`, :gh:`736`, :gh:`738`, :gh:`739`, :gh:`740`\n* `Ilya Georgievsky`_ - :gh:`870`\n* `Jake Omann`_ - :gh:`816`, :gh:`775`, :gh:`1874`\n* `Jeremy Humble`_ - :gh:`863`\n* `Landry Breuil`_ - :gh:`741`\n* `Mark Derbecker`_ - :gh:`660`\n* `Max Bélanger`_ - :gh:`936`, :gh:`1133`\n* `Patrick Welche`_ - :gh:`812`\n* `Syohei YOSHIDA`_ - :gh:`730`\n* `Timmy Konick`_ - :gh:`751`\n* `Yago Jesus`_ - :gh:`798`\n\n2015\n~~~~\n\n* `Arnon Yaari`_ - :gh:`680`, :gh:`679`, :gh:`610`\n* `Bruno Binet`_ - :gh:`572`\n* `Denis`_ - :gh:`541`\n* `Fabian Groffen`_ - :gh:`611`, :gh:`618`\n* `Gabi Davar`_ - :gh:`578`, :gh:`581`, :gh:`587`\n* `Jeff Tang`_ - :gh:`616`, :gh:`648`, :gh:`653`, :gh:`654`\n* `John Burnett`_ - :gh:`614`\n* `karthik`_ - :gh:`568`\n* `Landry Breuil`_ - :gh:`713`, :gh:`709` (OpenBSD implementation)\n* `Mike Sarahan`_ - :gh:`690`\n* `Sebastian-Gabriel Brestin`_ - :gh:`704`\n* `sk6249`_ - :gh:`670`\n* `spacewander`_ - :gh:`561`, :gh:`603`, :gh:`555`\n* `Steven Winfield`_ - :gh:`672`\n* `Sylvain Mouquet`_ - :gh:`565`\n* `Árni Már Jónsson`_ - :gh:`634`\n* `Ryo Onodera`_: `e124acba <https://github.com/giampaolo/psutil/commit/e124acba>`_ (NetBSD implementation)\n\n2014\n~~~~\n\n* `Alexander Grothe`_ - :gh:`497`\n* `Anders Chrigström`_ - :gh:`548`\n* Francois Charron - :gh:`474`\n* `Guido Imperiale`_ - :gh:`470`, :gh:`477`\n* `Jeff Tang`_ - :gh:`340`, :gh:`519`, :gh:`529`, :gh:`654`\n* `Marc Abramowitz`_ - :gh:`492`\n* Naveed Roudsari - :gh:`421`\n* `Yaolong Huang`_ - :gh:`530`\n\n2013\n~~~~\n\n* Arfrever.FTA - :gh:`404`\n* danudey - :gh:`386`\n* Jason Kirtland - backward compatible implementation of collections.defaultdict\n* John Baldwin - :gh:`370`\n* John Pankov - :gh:`435`\n* `Josiah Carlson`_ - :gh:`451`, :gh:`452`\n* m.malycha - :gh:`351`\n* `Matt Good`_ - :gh:`438`\n* `Thomas Klausner`_ - :gh:`557` (NetBSD implementation)\n* Ulrich Klank - :gh:`448`\n\n2012\n~~~~\n\n* Amoser - :gh:`266`, :gh:`267`, :gh:`340`\n* `Florent Xicluna`_ - :gh:`319`\n* `Gregory Szorc`_ - :gh:`323`\n* Jan Beich - :gh:`344`\n* Youngsik Kim - :gh:`317`\n\n2011\n~~~~\n\n* Jeremy Whitlock - :gh:`125`, :gh:`150`, :gh:`206`, :gh:`217`, :gh:`260` (:func:`net_io_counters` and :func:`disk_io_counters` on macOS)\n\n2010\n~~~~\n\n* cjgohlke - :gh:`107`\n* `Wen Jia Liu (wj32)`_ - :gh:`114`, :gh:`115`\n\n2009\n~~~~\n\n* Yan Raber: `c861c08b <https://github.com/giampaolo/psutil/commit/c861c08b>`_ (Windows :func:`cpu_times`), `15159111 <https://github.com/giampaolo/psutil/commit/15159111>`_ (Windows :meth:`Process.username`)\n* `Jay Loden`_ - `79128baa <https://github.com/giampaolo/psutil/commit/79128baa>`_ (first commit of FreeBSD implementation)\n\n2008\n~~~~\n\n* `Jay Loden`_ - `efe9236a <https://github.com/giampaolo/psutil/commit/efe9236a>`_ (first commit of macOS implementation)\n* Dave Daeschler - `71875761 <https://github.com/giampaolo/psutil/commit/71875761>`_ (first commit of Windows implementation)\n* `Giampaolo Rodola`_ - `6296c2ab <https://github.com/giampaolo/psutil/commit/6296c2ab>`_ (first commit of Linux implementation)\n* `Giampaolo Rodola`_ - `8472a17f <https://github.com/giampaolo/psutil/commit/8472a17f>`_ (inception / initial directory structure)\n\n.. People Donors\n.. ============================================================================\n\n.. _`Alex Laird`: https://github.com/alexdlaird\n.. _`Alexey Vazhnov`: https://opencollective.com/alexey-vazhnov\n.. _`Artyom Vancyan`: https://github.com/ArtyomVancyan\n.. _`c0m4r`: https://github.com/c0m4r\n.. _`Chenyoo Hao`: https://opencollective.com/chenyoo-hao\n.. _`Coşkun Deniz`: https://github.com/coskundeniz\n.. _`cybersecgeek`: https://github.com/cybersecgeek\n.. _`Eugenio E Breijo`: https://github.com/u93\n.. _`Evan Allrich`: https://github.com/eallrich\n.. _`great-work-told-is`: https://github.com/great-work-told-is\n.. _`inarikami`: https://github.com/inarikami\n.. _`JeremyGrosser`: https://github.com/JeremyGrosser\n.. _`Johannes Maron`: https://github.com/codingjoe\n.. _`Karthik Kumar`: https://github.com/guilt\n.. _`Maximilian Wu`: https://github.com/maxesisn\n.. _`PySimpleGUI`: https://github.com/PySimpleGUI\n.. _`roboflow.com`: https://github.com/roboflow\n.. _`sansec.io`: https://github.com/sansecio\n.. _`Sašo Živanović`: https://github.com/sasozivanovic\n.. _`scoutapm-sponsorships`: https://github.com/scoutapm-sponsorships\n.. _`trashnothing.com`: https://github.com/Trash-Nothing\n.. _`Valeriy Abramov`: https://github.com/abramov-v\n\n.. Company donors\n.. ============================================================================\n\n.. _`Apivoid`: https://www.apivoid.com\n.. _`Canonical Juju`: https://github.com/juju\n.. _`Canonical Launchpad`: https://launchpad.net/\n.. _`Canonical`: https://github.com/canonical\n.. _`Codecov`: https://github.com/codecov\n.. _`Kubernetes`: https://github.com/kubernetes/kubernetes\n.. _`Indeed Engineering`: https://github.com/indeedeng\n.. _`Robusta`: https://github.com/robusta-dev\n.. _`Sentry`: https://sentry.io/\n.. _`Sourcegraph`: https://sourcegraph.com/\n.. _`Tidelift`: https://tidelift.com\n\n.. Code contributors\n.. ============================================================================\n\n.. _`Adrian Page`: https://github.com/adpag\n.. _`Amaan Qureshi`: https://github.com/amaanq\n.. _`qcha0`: https://github.com/qcha0\n.. _`Akos Kiss`: https://github.com/akosthekiss\n.. _`Aleksey Lobanov`: https://github.com/AlekseyLobanov\n.. _`Alex Manuskin`: https://github.com/amanusk\n.. _`Alexander Grothe`: https://github.com/agrethe\n.. _`Alexander Hasselhuhn`: https://github.com/alexanha\n.. _`Amir Rossert`: https://github.com/arossert\n.. _`Ammar Askar`: https://github.com/ammaraskar\n.. _`Anders Chrigström`: https://github.com/anders-chrigstrom\n.. _`Andre Caron`: https://github.com/AndreLouisCaron\n.. _`Anselm Kruis`: https://github.com/akruis\n.. _`Antoine Pitrou`: https://github.com/pitrou\n.. _`Arcadiy Ivanov`: https://github.com/arcivanov\n.. _`Armin Gruner`: https://github.com/ArminGruner\n.. _`Arnon Yaari`: https://github.com/wiggin15\n.. _`Athos Ribeiro`: https://github.com/athos-ribeiro\n.. _`Baruch Siach`: https://github.com/baruchsiach\n.. _`Ben Peddell`: https://github.com/klightspeed\n.. _`Ben Raz`: https://github.com/ben9923\n.. _`Benjamin Drung`: https://github.com/bdrung\n.. _`Bernhard Urban-Forster`: https://github.com/lewurm\n.. _`Bernát Gábor`: https://github.com/gaborbernat\n.. _`Bruno Binet`: https://github.com/bbinet\n.. _`Cedric Lamoriniere`: https://github.com/clamoriniere\n.. _`Chris Burger`: https://github.com/phobozad\n.. _`Chris Lalancette`: https://github.com/clalancette\n.. _`Cristian Vîjdea`: https://github.com/cvijdea-bd\n.. _`Dan Vinakovsky`: https://github.com/hexaclock\n.. _`Danek Duvall`: https://github.com/dhduvall\n.. _`Daniel Beer`: https://github.com/dbeer1\n.. _`Daniel Li`: https://github.com/li-dan\n.. _`Daniel Widdis`: https://github.com/dbwiddis\n.. _`Denis`: https://github.com/denis-sumin\n.. _`David Brochart`: https://github.com/davidbrochart\n.. _`David Knaack`: https://github.com/davidkna\n.. _`Denis Krienbühl`: https://github.com/href\n.. _`EccoTheFlintstone`: https://github.com/EccoTheFlintstone\n.. _`Eli Wenig`: https://github.com/elisw93\n.. _`Erwan Le Pape`: https://github.com/erwan-le-pape\n.. _`ewedlund`: https://github.com/ewedlund\n.. _`Fabian Groffen`: https://github.com/fabian\n.. _`Fabien Bousquet`: https://github.com/fafanoulele\n.. _`Farhan Khan`: https://github.com/khanzf\n.. _`Felix Yan`: https://github.com/felixonmars\n.. _`Florent Xicluna`: https://github.com/florentx\n.. _`Frank Benkstein`: https://github.com/fbenkstein\n.. _`Gabi Davar`: https://github.com/mindw\n.. _`Garrison Carter`: https://github.com/garrisoncarter\n.. _`Ghislain Le Meur`: https://github.com/gigi206\n.. _`Giampaolo Rodola`: https://github.com/giampaolo\n.. _`Gleb Smirnoff`: https://github.com/glebius\n.. _`Gregory Szorc`: https://github.com/indygreg\n.. _`Grzegorz Bokota`: https://github.com/Czaki\n.. _`Guido Imperiale`: https://github.com/crusaderky\n.. _`Guillermo`: https://github.com/guille\n.. _`Himanshu Shekhar`: https://github.com/himanshub16\n.. _`Hiroyuki Tanaka`: https://github.com/myheroyuki\n.. _`Hugo van Kemenade`: https://github.com/hugovk\n.. _`Ilya Georgievsky`: https://github.com/xBeAsTx\n.. _`Ilya Yanok`: https://github.com/yanok\n.. _`Irene Sheen`: https://github.com/ceda-ei\n.. _`Isaac K. Ko`: https://github.com/1saac-k\n.. _`Jaime Fullaondo`: https://github.com/truthbk\n.. _`Jake Omann`: https://github.com/jomann09\n.. _`Jakob P. Liljenberg`: https://github.com/aristocratos\n.. _`Jakub Bacic`: https://github.com/jakub-bacic\n.. _`janderbrain`: https://github.com/janderbrain\n.. _`Javad Karabi`: https://github.com/karabijavad\n.. _`Jay Loden`: https://github.com/jloden\n.. _`Jean-Luc Migot`: https://github.com/jmigot-tehtris\n.. _`Jeff Tang`: https://github.com/mrjefftang\n.. _`Jeremy Humble`: https://github.com/jhumble\n.. _`John Burnett`: https://github.com/johnburnett\n.. _`Jonathan Kohler`: https://github.com/kohlerjl\n.. _`Josiah Carlson`: https://github.com/josiahcarlson\n.. _`Julien Lebot`: https://github.com/julien-lebot\n.. _`Kamil Rytarowski`: https://github.com/krytarowski\n.. _`karthik`: https://github.com/karthikrev\n.. _`Koen Kooi`: https://github.com/koenkooi\n.. _`Landry Breuil`: https://github.com/landryb\n.. _`Lawrence D'Anna`: https://github.com/smoofra\n.. _`Lawrence Ye`: https://github.com/LEAFERx\n.. _`Lysandros Nikolaou`: https://github.com/lysnikolaou\n.. _`Marc Abramowitz`: https://github.com/msabramo\n.. _`Marcel Telka`: https://github.com/mtelka\n.. _`Mark Derbecker`: https://github.com/mpderbec\n.. _`Martin Liška`: https://github.com/marxin\n.. _`Matt Good`: https://github.com/mgood\n.. _`Matthew Long`: https://github.com/matray\n.. _`Matthieu Darbois`: https://github.com/mayeut\n.. _`MaWe2019`: https://github.com/MaWe2019\n.. _`Max Bélanger`: https://github.com/maxbelanger\n.. _`Maxime Mouial`: https://github.com/hush-hush\n.. _`Mayank Jha`: https://github.com/maynk27\n.. _`Michał Górny`: https://github.com/mgorny\n.. _`Mike Hommey`: https://github.com/glandium\n.. _`Mike Sarahan`: https://github.com/msarahan\n.. _`Nathan Houghton`: https://github.com/n1000\n.. _`Nicolas Hennion`: https://github.com/nicolargo\n.. _`Nikhil Marathe`: https://github.com/nikhilm\n.. _`Nikita Radchenko`: https://github.com/nradchenko\n.. _`Ofek Lev`: https://github.com/ofek\n.. _`Oleksii Shevchuk`: https://github.com/alxchk\n.. _`Oliver Tomé`: https://github.com/snom3ad\n.. _`Olivier Dormond`: https://github.com/odormond\n.. _`Pablo Baeyens`: https://github.com/mx-psi\n.. _`Patrick Welche`: https://github.com/prlw1\n.. _`PetrPospisil`: https://github.com/PetrPospisil\n.. _`Pierre Fersing`: https://github.com/PierreF\n.. _`Po-Chuan Hsieh`: https://github.com/sunpoet\n.. _`Riccardo Schirone`: https://github.com/ret2libc\n.. _`Ryan Carsten Schmidt`: https://github.com/ryandesign\n.. _`Ryo Onodera`: https://github.com/ryoon\n.. _`Saeed Rasooli`: https://github.com/ilius\n.. _`Sam Gross`: https://github.com/colesbury\n.. _`Samer Masterson`: https://github.com/samertm\n.. _`Sebastian Saip`: https://github.com/ssaip\n.. _`Sebastian-Gabriel Brestin`: https://github.com/bsebi\n.. _`Sergey Fedorov`: https://github.com/barracuda156\n.. _`Shade Gladden`: https://github.com/shadeyg56\n.. _`sk6249`: https://github.com/sk6249\n.. _`spacewander`: https://github.com/spacewander\n.. _`Steve Dower`: https://github.com/zooba\n.. _`Steven Winfield`: https://github.com/stevenwinfield\n.. _`stswandering`: https://github.com/stswandering\n.. _`Sylvain Duchesne`: https://github.com/sylvainduchesne\n.. _`Sylvain Mouquet`: https://github.com/sylvainmouquet\n.. _`Syohei YOSHIDA`: https://github.com/syohex\n.. _`Thiago Borges Abdnur`: https://github.com/bolaum\n.. _`Thomas Klausner`: https://github.com/tklauser\n.. _`Tim Schlueter`: https://github.com/modelrockettier\n.. _`Timmy Konick`: https://github.com/tijko\n.. _`Vincent A. Arcila`: https://github.com/jandrovins\n.. _`Wen Jia Liu (wj32)`: https://github.com/wj32\n.. _`Wilfried Goesgens`: https://github.com/dothebart\n.. _`Will Hawes`: https://github.com/wdh\n.. _`Xianpeng Shen`: https://github.com/shenxianpeng\n.. _`Xiaoling Bao`: https://github.com/xiaolingbao\n.. _`Xuehai Pan`: https://github.com/XuehaiPan\n.. _`Yago Jesus`: https://github.com/YJesus\n.. _`Yannick Gingras`: https://github.com/ygingras\n.. _`Yaolong Huang`: http://airekans.github.io/\n.. _`Árni Már Jónsson`: https://github.com/arnimarj\n.. _`vser1`: https://github.com/vser1\n"
  },
  {
    "path": "docs/devguide.rst",
    "content": "Development guide\n=================\n\nBuild, setup and running tests\n------------------------------\n\n- psutil makes extensive use of C extension modules, meaning a C compiler is\n  required, see :doc:`install instructions <install>`. Once you have a compiler\n  installed run:\n\n  .. code-block:: bash\n\n      git clone git@github.com:giampaolo/psutil.git\n      make install-sysdeps      # install gcc and python headers\n      make install-pydeps-test  # install python deps necessary to run unit tests\n      make build\n      make install\n      make test\n\n- ``make`` (and the accompanying `Makefile`_) is the designated tool to build,\n  install, run tests and do pretty much anything that involves development,\n  including on Windows. Some useful commands:\n\n  .. code-block:: bash\n\n      make clean                # remove build files\n      make install-pydeps-dev   # install all development deps (ruff, black, coverage, ...)\n      make test                 # run tests\n      make test-parallel        # run tests in parallel (faster)\n      make test-memleaks        # run memory leak tests\n      make test-coverage        # run test coverage\n      make lint-all             # run linters\n      make fix-all              # fix linters errors\n      make uninstall\n      make help\n\n- To run a specific unit test:\n\n  .. code-block::\n\n      make test ARGS=tests/test_system.py\n\n- Do not use ``sudo``. ``make install`` installs psutil as a limited user in\n  \"edit\" / development mode, meaning you can edit psutil code on the fly while\n  you develop.\n\n- If you want to target a specific Python version:\n\n  .. code-block::\n\n      make test PYTHON=python3.8\n\nWindows\n-------\n\n- The recommended way to develop on Windows is using ``make``, just like on\n  UNIX systems.\n- First, install `Git for Windows`_ and launch a **Git Bash shell**. This\n  provides a Unix-like environment where ``make`` works.\n- Once inside Git Bash, you can run the usual ``make`` commands:\n\n  .. code-block:: bash\n\n      make build\n      make test-parallel\n\nDebug mode\n----------\n\nIf you want to debug unusual situations or want to report a bug, it may be\nuseful to enable debug mode via ``PSUTIL_DEBUG`` environment variable. In this\nmode, psutil may print additional information to stderr. Usually these are\nnon-severe error conditions that are ignored instead of causing a crash.\nUnit tests automatically run with debug mode enabled. On UNIX:\n\n::\n\n  $ PSUTIL_DEBUG=1 python3 script.py\n  psutil-debug [psutil/_psutil_linux.c:150]> setmntent() failed (ignored)\n\nOn Windows:\n\n::\n\n  set PSUTIL_DEBUG=1 && python.exe script.py\n  psutil-debug [psutil/arch/windows/proc.c:90]> NtWow64ReadVirtualMemory64(pbi64.PebBaseAddress) -> 998 (Unknown error) (ignored)\n\n\nCoding style\n------------\n\nAll style and formatting checks are automatically enforced both **locally on\neach `git commit`** and **remotely via a GitHub Actions pipeline**.\n\n- **Python** code follows the `PEP-8`_ style guide. We use `black` and `ruff`\n  for formatting and linting.\n- **C** code generally follows the `PEP-7`_ style guide, with formatting\n  enforced by `clang-format`.\n- **Other files** (`.rst`, `.toml`, `.md`, `.yml`) are also validated by\n  dedicated command-line linters.\n- The **GitHub Actions pipeline** re-runs all these checks to ensure\n  consistency (via ``make lint-all``).\n\nCode organization\n-----------------\n\n.. code-block:: bash\n\n    psutil/__init__.py                   # Main API namespace (\"import psutil\")\n    psutil/_common.py                    # Generic utilities\n    psutil/_ntuples.py                   # Named tuples returned by psutil APIs\n    psutil/_enums.py                     # Enum containers backing psutil constants\n    psutil/_ps{platform}.py              # Platform-specific python wrappers\n    psutil/_psutil_{platform}.c          # Platform-specific C extensions (entry point)\n    psutil/arch/all/*.c                  # C code common to all platforms\n    psutil/arch/{platform}/*.c           # Platform-specific C extension\n    tests/test_process|system.py         # Main system/process API tests\n    tests/test_{platform}.py             # Platform-specific tests\n\nAdding a new API\n----------------\n\nTypically, this is what you do:\n\n- Define the new API in `psutil/__init__.py`_.\n- Write the platform specific implementation in ``psutil/_ps{platform}.py``\n  (e.g. `psutil/_pslinux.py`_).\n- If the change requires C code, write the C implementation in\n  ``psutil/arch/{platform}/file.c`` (e.g. `psutil/arch/linux/disk.c`_).\n- Write a generic test in `tests/test_system.py`_ or\n  `tests/test_process.py`_.\n- If possible, write a platform-specific test in\n  ``tests/test_{platform}.py`` (e.g. `tests/test_linux.py`_).\n  This usually means testing the return value of the new API against\n  a system CLI tool.\n- Update the doc in ``docs/api.rst``.\n- Update `changelog.rst`_ and `credits.rst`_ files.\n- Make a pull request.\n\nMake a pull request\n-------------------\n\n- Fork psutil (go to https://github.com/giampaolo/psutil and click on \"fork\")\n- Git clone the fork locally: ``git clone git@github.com:YOUR-USERNAME/psutil.git``\n- Create a branch: ``git checkout -b new-feature``\n- Commit your changes: ``git commit -am 'add some feature'``\n- Push the branch: ``git push origin new-feature``\n- Create a new PR via the GitHub web interface and sign-off your work (see\n  `CONTRIBUTING.md`_ guidelines)\n\nContinuous integration\n----------------------\n\nUnit tests are automatically run on every ``git push`` on all platforms except\nAIX. See config files in the `.github/workflows <https://github.com/giampaolo/psutil/tree/master/.github/workflows>`_\ndirectory.\n\nDocumentation\n-------------\n\n- doc is under ``docs/``.\n- doc can be built with ``make install-pydeps-dev; cd docs; make html``.\n- public doc is hosted at https://psutil.readthedocs.io.\n\n.. _`changelog.rst`: https://github.com/giampaolo/psutil/blob/master/docs/changelog.rst\n.. _`CONTRIBUTING.md`: https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md\n.. _`credits.rst`: https://github.com/giampaolo/psutil/blob/master/docs/credits.rst\n.. _`Git for Windows`: https://git-scm.com/install/windows\n.. _`Makefile`: https://github.com/giampaolo/psutil/blob/master/Makefile\n.. _`PEP-7`: https://www.python.org/dev/peps/pep-0007/\n.. _`PEP-8`: https://www.python.org/dev/peps/pep-0008/\n.. _`psutil/__init__.py`: https://github.com/giampaolo/psutil/blob/master/psutil/__init__.py\n.. _`psutil/_pslinux.py`: https://github.com/giampaolo/psutil/blob/master/psutil/_pslinux.py\n.. _`psutil/arch/linux/disk.c`: https://github.com/giampaolo/psutil/blob/master/psutil/arch/linux/disk.c\n.. _`tests/test_linux.py`: https://github.com/giampaolo/psutil/blob/master/tests/test_linux.py\n.. _`tests/test_process.py`: https://github.com/giampaolo/psutil/blob/master/tests/test_process.py\n.. _`tests/test_system.py`: https://github.com/giampaolo/psutil/blob/master/tests/test_system.py\n"
  },
  {
    "path": "docs/faq.rst",
    "content": ".. currentmodule:: psutil\n\nFAQ\n===\n\nThis section answers common questions and pitfalls when using psutil.\n\n.. contents::\n   :local:\n   :depth: 3\n\nExceptions\n----------\n\n.. _faq_access_denied:\n\nWhy do I get AccessDenied?\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:exc:`AccessDenied` is raised when the OS refuses to return information about\na process because the calling user does not have sufficient privileges.\nThis is expected behavior and is not a bug. It typically happens when:\n\n- querying processes owned by other users (e.g. *root*)\n- calling certain methods like :meth:`Process.memory_maps`,\n  :meth:`Process.open_files` or :meth:`Process.net_connections` for privileged\n  processes\n\nYou have two options to deal with it.\n\n- Option 1: call the method directly and catch the exception:\n\n  .. code-block:: python\n\n    import psutil\n\n    p = psutil.Process(pid)\n    try:\n        print(p.memory_maps())\n    except (psutil.AccessDenied, psutil.NoSuchProcess):\n        pass\n\n- Option 2: use :func:`process_iter` with a list of attribute names to pre-fetch. If\n  fetching an attribute raises :exc:`AccessDenied` internally, its value in\n  ``p.info`` is set to ``None`` (or to the ``ad_value`` argument, if specified):\n\n  .. code-block:: python\n\n    import psutil\n\n    for p in psutil.process_iter([\"name\", \"username\"], ad_value=\"N/A\"):\n        print(p.info[\"username\"])  # may print \"N/A\"\n\n.. _faq_no_such_process:\n\nWhy do I get NoSuchProcess?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:exc:`NoSuchProcess` is raised when a process no longer exists.\nThe most common cause is a TOCTOU (time-of-check / time-of-use) race\ncondition: a process can die between the moment its PID is obtained and\nthe moment it is queried. The following 2 naive patterns are racy:\n\n.. code-block:: python\n\n  import psutil\n\n  for pid in psutil.pids():\n      p = psutil.Process(pid)  # may raise NoSuchProcess\n      print(p.name())  # may raise NoSuchProcess\n\n.. code-block:: python\n\n  import psutil\n\n  if psutil.pid_exists(pid):\n      p = psutil.Process(pid)  # may raise NoSuchProcess\n      print(p.name())  # may raise NoSuchProcess\n\nThe correct approach is to use :func:`process_iter`, which handles\n:exc:`NoSuchProcess` internally and skips processes that disappear\nduring iteration:\n\n.. code-block:: python\n\n  import psutil\n\n  for p in psutil.process_iter([\"name\"]):\n      print(p.info[\"name\"])\n\nIf you have a specific PID (e.g. a known child process), wrap the\ncall in a try/except:\n\n.. code-block:: python\n\n  import psutil\n\n  try:\n      p = psutil.Process(pid)\n      print(p.name(), p.status())\n  except (psutil.NoSuchProcess, psutil.AccessDenied):\n      pass\n\nAn even simpler pattern is to catch :exc:`Error`, which implies both\n:exc:`AccessDenied` and :exc:`NoSuchProcess`:\n\n.. code-block:: python\n\n  import psutil\n\n  try:\n      p = psutil.Process(pid)\n      print(p.name(), p.status())\n  except psutil.Error:\n      pass\n\n.. _faq_zombie_process:\n\nWhat is ZombieProcess?\n^^^^^^^^^^^^^^^^^^^^^^^\n\n:exc:`ZombieProcess` is a subclass of :exc:`NoSuchProcess` raised on UNIX\nfor a :term:`zombie process`.\n\n**What you can and cannot do with a zombie:**\n\n- A zombie can be instantiated via :class:`Process` (pid) without error.\n- :meth:`Process.status` always returns :data:`STATUS_ZOMBIE`.\n- :meth:`Process.is_running` and :func:`pid_exists` return ``True``.\n- The zombie appears in :func:`process_iter` and :func:`pids`.\n- Sending signals (:meth:`Process.terminate`, :meth:`Process.kill`,\n  etc.) has no effect.\n- Most other methods (:meth:`Process.cmdline`, :meth:`Process.exe`,\n  :meth:`Process.memory_maps`, etc.) may raise :exc:`ZombieProcess`,\n  return a meaningful value, or return a null/empty value depending on\n  the platform.\n- :meth:`Process.as_dict` will not crash.\n\n**How to detect zombies:**\n\n.. code-block:: python\n\n  import psutil\n\n  for p in psutil.process_iter([\"status\"]):\n      if p.info[\"status\"] == psutil.STATUS_ZOMBIE:\n          print(f\"zombie: pid={p.pid}\")\n\n**How to get rid of a zombie:** the only way is to have its parent\nprocess call ``wait()`` (or ``waitpid()``). If the parent never does\nthis, killing the parent will cause the zombie to be re-parented to\n``init`` / ``systemd``, which will reap it automatically.\n\n----\n\nProcesses\n---------\n\n.. _faq_pid_reuse:\n\nPID reuse\n^^^^^^^^^\n\nOperating systems recycle PIDs. A :class:`Process` object obtained now may\nlater refer to a different process if the original one terminated and a new one\nwas assigned the same PID.\n\n**How psutil handles this:**\n\n- *Most read-only methods* (e.g. :meth:`Process.name`,\n  :meth:`Process.cpu_percent`) do **not** check for PID reuse and instead\n  query whatever process currently holds that PID.\n\n- *Signal methods* (e.g. :meth:`Process.send_signal`,\n  :meth:`Process.suspend`, :meth:`Process.resume`,\n  :meth:`Process.terminate`, :meth:`Process.kill`) **do** check for PID\n  reuse (via PID + creation time) before acting, raising\n  :exc:`NoSuchProcess` if the PID was recycled. This prevents accidentally\n  killing the wrong process (`BPO-6973\n  <https://bugs.python.org/issue6973>`_).\n\n- *Set methods* :meth:`Process.nice` (set), :meth:`Process.ionice` (set),\n  :meth:`Process.cpu_affinity` (set), and\n  :meth:`Process.rlimit` (set) also perform this check before applying\n  changes.\n\n:meth:`Process.is_running` is the recommended way to verify whether a\n:class:`Process` instance still refers to the same process. It compares\nPID and creation time, and returns ``False`` if the PID was reused.\nPrefer it over :func:`pid_exists`.\n\n.. _faq_pid_exists_vs_isrunning:\n\nWhat is the difference between pid_exists() and Process.is_running()?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:func:`pid_exists` checks whether a PID is present in the process list.\n:meth:`Process.is_running` does the same, but also detects :ref:`PID\nreuse <faq_pid_reuse>` by comparing the process creation time. Use\n:func:`pid_exists` when you have a bare PID and don't need to guard\nagainst reuse (it's faster). Use :meth:`Process.is_running` when you\nhold a :class:`Process` object and want to confirm it still refers to\nthe same process.\n\n----\n\nCPU\n---\n\n.. _faq_cpu_percent:\n\nWhy does cpu_percent() return 0.0 on first call?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:func:`cpu_percent` (and :meth:`Process.cpu_percent`) measures CPU usage\n*between two calls*. The very first call has no prior sample to compare\nagainst, so it always returns ``0.0``. The fix is to call it once to\ninitialize the baseline, discard the result, then call it again after a\nshort sleep:\n\n.. code-block:: python\n\n  import time\n  import psutil\n\n  psutil.cpu_percent()          # discard first call\n  time.sleep(0.5)\n  print(psutil.cpu_percent())   # meaningful value\n\nAlternatively, pass ``interval`` to make it block internally:\n\n.. code-block:: python\n\n  print(psutil.cpu_percent(interval=0.5))\n\nThe same applies to :meth:`Process.cpu_percent`:\n\n.. code-block:: python\n\n  p = psutil.Process()\n  p.cpu_percent()               # discard\n  time.sleep(0.5)\n  print(p.cpu_percent())        # meaningful value\n\n.. _faq_cpu_percent_gt_100:\n\nCan Process.cpu_percent() return a value higher than 100%?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYes. On a multi-core system a process can run threads on several CPUs at\nthe same time. The maximum value is ``psutil.cpu_count() * 100``. For\nexample, on a 4-core machine a fully-loaded process can reach 400%.\nThe system-wide :func:`cpu_percent` (without a :class:`Process`) always\nstays in the 0–100% range because it averages across all cores.\n\n----\n\nMemory\n------\n\n.. _faq_virtual_memory_available:\n\nWhat is the difference between virtual_memory() available and free?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:func:`virtual_memory` returns both ``free`` and ``available``, but they\nmeasure different things:\n\n- ``free``: memory that is not being used at all.\n- ``available``: how much memory can be given to processes without swapping.\n  This includes reclaimable caches and buffers that the OS can reclaim under\n  pressure.\n\nIn practice, ``available`` is almost always the metric you want when monitoring\nmemory. ``free`` can be misleadingly low on systems where the OS aggressively\nuses RAM for caches (which is normal and healthy). On Windows, ``free`` and\n``available`` are the same value.\n\n.. _faq_memory_rss_vs_vms:\n\nWhat is the difference between RSS and VMS?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n- ``rss`` (Resident Set Size): the amount of physical memory (RAM)\n  currently mapped into the process.\n- ``vms`` (Virtual Memory Size): the total virtual address space of the\n  process, including memory that has been swapped out, shared libraries,\n  and memory-mapped files.\n\n``rss`` is the go-to metric for answering \"how much RAM is this process\nusing?\". Note that it includes shared memory, so it may overestimate\nactual usage when compared across processes. ``vms`` is generally larger\nand can be misleadingly high, as it includes memory that is not resident\nin physical RAM.\n\nBoth values are portable across platforms and are returned by\n:meth:`Process.memory_info`.\n\n.. _faq_memory_footprint:\n\nWhen should I use memory_footprint() vs memory_info()?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`Process.memory_info` returns ``rss``, which includes shared\nlibraries counted in every process that uses them. For example, if\n``libc`` uses 2 MB and 100 processes map it, each process includes those\n2 MB in its ``rss``.\n\n:meth:`Process.memory_footprint` returns USS (Unique Set Size), i.e.\nmemory private to the process. It represents the amount of memory that\nwould be freed if the process were terminated right now.\nIt is more accurate than RSS, but substantially slower and requires higher\nprivileges. On Linux it also returns PSS (Proportional Set Size) and swap.\n\n.. _faq_used_plus_free:\n\nWhy does virtual_memory().used + free != total?\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBecause some memory (like cache and buffers) is reclaimable and accounted\nseparately:\n\n.. code-block:: pycon\n\n  >>> import psutil\n  >>> m = psutil.virtual_memory()\n  >>> m.used + m.free == m.total\n  False\n\nThe ``available`` field already includes this reclaimable memory and is the\nbest indicator of memory pressure. See :ref:`faq_virtual_memory_available`.\n"
  },
  {
    "path": "docs/glossary.rst",
    "content": ".. currentmodule:: psutil\n\nGlossary\n========\n\n.. glossary::\n   :sorted:\n\n   anonymous memory\n\n      RAM used by the program that is not associated with any file (unlike the\n      :term:`page cache`), such as the heap, the stack, and other memory\n      allocated directly by the program (e.g. via ``malloc()``).\n      Anonymous pages have no on-disk counterpart and must be written to swap\n      if evicted. Visible in the ``path`` column of :meth:`Process.memory_maps`\n      as ``\"[heap]\"``, ``\"[stack]\"``, or an empty string.\n\n   available memory\n\n      The amount of RAM that can be given to processes without the system\n      going into swap. This is the right field to watch for memory\n      pressure, not ``free``. ``free`` is often deceptively low because\n      the OS keeps recently freed pages as reclaimable cache; those pages\n      are counted in ``available`` but not in ``free``. A monitoring\n      alert should fire on ``available`` (or ``percent``) falling below a\n      threshold, not on ``free``. See :func:`virtual_memory`.\n\n   busy_time\n\n      A :term:`cumulative counter` (milliseconds) tracking the time a\n      disk device spent actually performing I/O, as reported in the\n      ``busy_time`` field of :func:`disk_io_counters` (Linux and FreeBSD\n      only). To use it, sample twice and divide the delta by elapsed\n      time to get a utilisation percentage (analogous to CPU percent but\n      for disks). When it approaches 100% the disk queue is growing and\n      I/O latency will spike. Unlike ``read_bytes``/``write_bytes``,\n      ``busy_time`` reveals saturation even when throughput looks modest\n      (e.g. many small random I/Os).\n\n   CPU affinity\n\n      A property of a process (or thread) that restricts which logical CPUs\n      it is allowed to run on. For example, pinning a process to CPU 0 and\n      CPU 1 prevents the OS scheduler from moving it to other cores. See\n      :meth:`Process.cpu_affinity`.\n\n   CPU percent\n\n      The fraction of CPU time consumed by a process or the whole system\n      over a measurement interval, expressed as a percentage. A value of\n      100 % means one full logical CPU core was busy for the entire\n      interval. Values above 100 % are possible on multi-core systems when\n      multiple threads run in parallel. See :func:`cpu_percent` and\n      :meth:`Process.cpu_percent`.\n\n   CPU times\n\n      :term:`Cumulative counters <cumulative counter>` (in seconds) recording\n      how much time a CPU or process spent in different modes:\n      **user** (normal code), **system** (kernel code on behalf of the process),\n      **idle**, **iowait**, etc.\n      These always increase monotonically. See :func:`cpu_times` and\n      :meth:`Process.cpu_times`.\n\n   context switch\n\n      Occurs whenever the CPU stops executing one process or thread and starts\n      executing another. Frequent context switching can indicate high system\n      load or excessive thread contention. See :func:`cpu_stats` and\n      :meth:`Process.num_ctx_switches`.\n\n      The ``voluntary`` and ``involuntary`` fields of\n      :meth:`Process.num_ctx_switches` tell you *why* the process was switched\n      out. A **voluntary** switch means the process gave up the CPU itself\n      (waiting for I/O, a lock, or a timer); a high rate is normal for\n      I/O-bound processes. An **involuntary** switch means the OS forcibly took\n      the CPU away (time slice expired, higher-priority process woke up); a\n      high rate means the process wants to run but keeps getting interrupted —\n      a sign it is competing for CPU. If involuntary switches dominate, adding\n      CPU capacity or reducing other load will directly speed up the process;\n      if voluntary switches dominate, the bottleneck is I/O or locking, not\n      CPU.\n\n   cumulative counter\n\n      A field whose value only increases over time (since boot or process\n      creation) and never resets. Examples include :func:`cpu_times`,\n      :func:`disk_io_counters`, :func:`net_io_counters`,\n      :meth:`Process.io_counters`, and :meth:`Process.num_ctx_switches`.\n      The raw value is rarely useful on its own; divide the delta between\n      two samples by the elapsed time to get a meaningful rate (e.g.\n      bytes per second, context switches per second).\n\n   dropin / dropout\n\n      Fields in :func:`net_io_counters` counting packets dropped at the\n      NIC level before they could be processed (``dropin``) or sent\n      (``dropout``). Unlike transmission errors, drops indicate the\n      interface or kernel buffer was overwhelmed. A non-zero and growing\n      count is a sign of network saturation or misconfiguration.\n\n   file descriptor\n\n      An integer handle used by UNIX processes to reference open files,\n      sockets, pipes, and other I/O resources. On Windows the equivalent\n      are *handles*. Leaking file descriptors (opening without closing)\n      eventually causes ``EMFILE`` / ``Too many open files`` errors. See\n      :meth:`Process.num_fds` and :meth:`Process.open_files`.\n\n   handle\n\n      On Windows, an opaque reference to a kernel object such as a file,\n      thread, process, event, mutex, or registry key. Handles are the\n      Windows equivalent of UNIX :term:`file descriptors <file descriptor>`. Each open\n      handle consumes a small amount of kernel memory. Leaking / unclosed\n      handles eventually causes ``ERROR_NO_MORE_FILES`` or similar errors. See\n      :meth:`Process.num_handles`.\n\n   hardware interrupt\n\n      A signal sent by a hardware device (disk controller, NIC, keyboard)\n      to the CPU to request attention. Each interrupt briefly preempts\n      whatever the CPU was doing. Reported as the ``interrupts`` field of\n      :func:`cpu_stats` and ``irq`` field of :func:`cpu_times`.\n      A very high rate may indicate a misbehaving device driver or a heavily\n      loaded NIC. Also see :term:`soft interrupt`.\n\n   involuntary context switch\n\n      See :term:`context switch`.\n\n   iowait\n\n      A CPU time field (Linux, SunOS, AIX) measuring time spent by the CPU\n      waiting for I/O operations to complete. High iowait indicates a\n      disk or network bottleneck. It is reported as part of\n      :func:`cpu_times` but is *not* included in the idle counter.\n\n   ionice\n\n      An I/O scheduling priority that controls how much disk bandwidth a\n      process receives. On Linux three scheduling classes are supported:\n      ``IOPRIO_CLASS_RT`` (real-time), ``IOPRIO_CLASS_BE`` (best-effort,\n      the default), and ``IOPRIO_CLASS_IDLE``. See\n      :meth:`Process.ionice`.\n\n   logical CPU\n\n      A CPU as seen by the operating system scheduler. On systems with\n      *hyper-threading* each physical core exposes two logical CPUs, so a\n      4-core hyper-threaded chip has 8 logical CPUs. This is the count\n      returned by :func:`cpu_count` (the default) and the number of\n      entries returned by ``cpu_percent(percpu=True)``. See also\n      :term:`physical CPU`.\n\n   load average\n\n      Three floating-point values representing the average number of\n      processes in a *runnable* or *uninterruptible* state over the last\n      1, 5, and 15 minutes. A load average equal to the number of logical\n      CPUs means the system is fully saturated. See :func:`getloadavg`.\n\n   nice\n\n      A process priority value that influences how much CPU time the OS\n      scheduler gives to a process. Lower nice values mean higher priority.\n      The range is −20 (highest priority) to 19 (lowest) on UNIX; on\n      Windows the concept maps to priority classes. See\n      :meth:`Process.nice`.\n\n   page cache\n\n      RAM used by the kernel to cache file data read from or written to disk.\n      When a process reads a file, the data stays in the page cache. Subsequent\n      reads are served from RAM without any disk I/O. The OS reclaims page\n      cache memory automatically under pressure, so a large cache is healthy.\n      Shown as the ``cached`` field of :func:`virtual_memory` on Linux/BSD.\n      See also :term:`available memory`.\n\n   peak_rss\n\n      The highest :term:`RSS` value a process has ever reached since it\n      started (memory high-water mark). Available via\n      :meth:`Process.memory_info` (BSD, Windows) and\n      :meth:`Process.memory_info_ex` (Linux, macOS). Useful for capacity\n      planning and leak detection: if ``peak_rss`` keeps growing across\n      successive runs or over time, the process is likely leaking memory.\n\n   page fault\n\n      An event that occurs when a process accesses a virtual memory page\n      that is not currently mapped in physical RAM. A **minor** fault is\n      resolved without disk I/O (e.g. the page is already in RAM but not\n      yet mapped, or it is copy-on-write). A **major** fault requires\n      reading the page from disk (e.g. from a memory-mapped file or the\n      swap area) and is significantly more expensive. Many major faults\n      may indicate memory pressure or excessive swapping. See\n      :meth:`Process.page_faults`.\n\n   physical CPU\n\n      An actual hardware CPU core on the motherboard, as opposed to a\n      :term:`logical CPU`. A single physical core may appear as multiple\n      logical CPUs when hyper-threading is enabled. The physical count is\n      returned by ``cpu_count(logical=False)``.\n\n   PSS\n\n      *Proportional Set Size*, the amount of RAM used by a process,\n      where shared pages are divided proportionally among all processes\n      that map them. PSS gives a fairer per-process memory estimate than\n      :term:`RSS` when shared libraries are involved. Available on Linux\n      via :meth:`Process.memory_footprint`.\n\n   RSS\n\n      *Resident Set Size*, the amount of physical RAM currently occupied\n      by a process, including shared library pages. It is the most\n      commonly reported memory metric (shown as ``RES`` in ``top``), but\n      it can be misleading because shared pages are counted in full for\n      every process that maps them. See :meth:`Process.memory_info`.\n\n   status (process)\n\n      The scheduling state of a process at a given instant. Common\n      values are:\n\n      - ``running``: actively executing on a CPU.\n      - ``sleeping``: waiting for an event (interruptible sleep).\n      - ``disk-sleep``: waiting for I/O (uninterruptible sleep).\n      - ``stopped``: suspended via ``SIGSTOP`` or a debugger.\n      - ``zombie``: exited but not yet reaped by its parent.\n      - ``idle``: doing nothing.\n\n      See :meth:`Process.status` and the ``STATUS_*`` constants.\n\n   soft interrupt\n\n      Deferred work scheduled by a :term:`hardware interrupt` handler to\n      run later in a less time-critical context (e.g. network packet\n      processing, block I/O completion). Using soft interrupts lets the\n      hardware interrupt return quickly while the heavier processing\n      happens shortly after. Reported as the ``soft_interrupts`` field of\n      :func:`cpu_stats`. A high rate usually points to heavy network or\n      disk I/O throughput rather than a hardware problem.\n\n   swap-in\n\n      A page moved from swap space on disk back into RAM. Reported as the\n      ``sin`` :term:`cumulative counter` of :func:`swap_memory`. On its\n      own a non-zero ``sin`` rate is not alarming — it may just mean the\n      system is reloading pages that were quietly evicted during idle\n      time. It becomes a concern when it coincides with a high\n      :term:`swap-out` rate, meaning the system is continuously trading\n      pages in and out. See also :term:`swap-out`.\n\n   swap-out\n\n      A page evicted from RAM to swap space on disk to free memory.\n      Reported as the ``sout`` :term:`cumulative counter` of\n      :func:`swap_memory`. A sustained non-zero rate is the clearest\n      sign of memory pressure: the system is running out of RAM and\n      actively offloading pages to disk. Compute the rate of change\n      over an interval rather than reading the absolute value.\n      See also :term:`swap-in`.\n\n   swap memory\n\n      Disk space used as an overflow extension of physical RAM. When the\n      OS runs low on RAM it *swaps out* memory pages to disk and restores\n      them on demand. Heavy swapping significantly degrades performance.\n      See :func:`swap_memory`.\n\n   NIC\n\n      *Network Interface Card*, a hardware or virtual network interface.\n      psutil uses this term when referring to per-interface network\n      statistics. See :func:`net_if_addrs` and :func:`net_if_stats`.\n\n   resource limit\n\n      A per-process cap on a system resource enforced by the kernel (POSIX\n      :data:`RLIMIT_* <psutil.RLIM_INFINITY>` constants).\n      Each limit has a *soft* value (the current enforcement threshold, which\n      the process may raise up to the hard limit) and a *hard* value\n      (the ceiling, settable only by root).\n      Common limits include :data:`RLIM_INFINITY` (open file descriptors),\n      :data:`RLIMIT_AS` (virtual address space), and :data:`RLIMIT_CPU`\n      (CPU time in seconds). See :meth:`Process.rlimit`.\n\n   USS\n\n      *Unique Set Size*, the amount of RAM that belongs exclusively to a\n      process and would be freed if it exited. It excludes shared pages\n      entirely, making it the most accurate single-process memory metric.\n      Available on Linux, macOS, and Windows via\n      :meth:`Process.memory_footprint`.\n\n   voluntary context switch\n\n      See :term:`context switch`.\n\n   VMS\n\n      *Virtual Memory Size*, the total virtual address space reserved by a\n      process, including mapped files, shared libraries, stack, heap, and\n      swap. VMS is almost always much larger than :term:`RSS` because most\n      virtual pages are never actually loaded into RAM. See\n      :meth:`Process.memory_info`.\n\n   zombie process\n\n      A process that has exited but whose entry remains in the process\n      table until its parent calls ``wait()``. Zombies hold a PID but consume\n      no CPU or memory.\n      See :ref:`faq_zombie_process`.\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. module:: psutil\n   :synopsis: psutil module\n.. moduleauthor:: Giampaolo Rodola <grodola@gmail.com>\n\npsutil documentation\n====================\n\n.. image:: https://img.shields.io/badge/GitHub-repo-blue\n   :target: https://github.com/giampaolo/psutil\n   :alt: GitHub repo\n\n.. image:: https://readthedocs.org/projects/psutil/badge/?version=latest\n   :target: https://app.readthedocs.org/projects/psutil/builds/\n   :alt: ReadTheDocs\n\n.. image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi&color=red\n    :target: https://pypi.org/project/psutil\n    :alt: Latest version\n\n.. image:: https://img.shields.io/pypi/dm/psutil.svg\n    :target: https://clickpy.clickhouse.com/dashboard/psutil\n    :alt: Downloads\n\npsutil (Python system and process utilities) is a cross-platform library for\nretrieving information about running\n**processes** and **system utilization** (CPU, memory, disks, network, sensors)\nin **Python**.\nIt is useful mainly for **system monitoring**, **profiling**, **limiting\nprocess resources**, and **managing running processes**.\nIt implements many functionalities offered by UNIX command line tools\nsuch as *ps, top, free, iotop, netstat, ifconfig, lsof* and others\n(see :doc:`shell equivalents <shell_equivalents>`).\npsutil currently supports the following platforms, from **Python 3.7** onwards:\n\n- **Linux**\n- **Windows**\n- **macOS**\n- **FreeBSD, OpenBSD**, **NetBSD**\n- **Sun Solaris**\n- **AIX**\n\npsutil is used by `many notable projects <adoption.html>`__ including\nTensorFlow, PyTorch, Home Assistant, Ansible, and Celery.\n\nSponsors\n--------\n\n.. raw:: html\n\n    <table border=\"0\" cellpadding=\"10\" cellspacing=\"0\">\n      <tr>\n        <td align=\"center\">\n          <a href=\"https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme\">\n            <img width=\"200\" src=\"https://github.com/giampaolo/psutil/raw/master/docs/_static/tidelift-logo.svg\">\n          </a>\n        </td>\n        <td align=\"center\">\n          <a href=\"https://sansec.io/\">\n            <img src=\"https://sansec.io/assets/images/logo.svg\">\n          </a>\n        </td>\n        <td align=\"center\">\n          <a href=\"https://www.apivoid.com/\">\n            <img width=\"180\" src=\"https://gmpy.dev/images/apivoid-logo.svg\">\n          </a>\n        </td>\n      </tr>\n    </table>\n\n    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<sup><a href=\"https://github.com/sponsors/giampaolo\">add your logo</a></sup><br/><br/>\n\nFunding\n-------\n\nWhile psutil is free software and will always be, the project would benefit\nimmensely from some funding.\npsutil is among the `top 100 <https://clickpy.clickhouse.com/dashboard/psutil>`_\nmost-downloaded Python packages, and keeping up with bug reports, user support,\nand ongoing maintenance has become increasingly difficult to sustain as a\none-person effort.\nIf you're a company that's making significant use of psutil you can consider\nbecoming a sponsor via `GitHub <https://github.com/sponsors/giampaolo>`_,\n`Open Collective <https://opencollective.com/psutil>`_ or\n`PayPal <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8>`_.\nSponsors can have their logo displayed here and in the psutil `documentation <https://psutil.readthedocs.io>`_.\n\nSecurity\n--------\n\nTo report a security vulnerability, please use the `Tidelift security contact`_.\nTidelift will coordinate the fix and disclosure.\n\nTable of contents\n-----------------\n\n.. toctree::\n   :maxdepth: 2\n\n   Install <install>\n   API Reference <api>\n   FAQ <faq>\n   Recipes <recipes>\n   Shell equivalents <shell_equivalents>\n   Glossary <glossary>\n   Platform support <platform>\n   Who uses psutil <adoption>\n   Alternatives <alternatives>\n   Migration <migration>\n   Development guide <devguide>\n   Credits <credits>\n   Timeline <timeline>\n\n.. toctree::\n   :titlesonly:\n\n   Changelog <changelog>\n\n.. _`Tidelift security contact`: https://tidelift.com/security\n..\n"
  },
  {
    "path": "docs/install.rst",
    "content": "Install psutil\n==============\n\nLinux, Windows, macOS (wheels)\n------------------------------\n\nPre-compiled wheels are distributed for these platforms, so you usually won't\nneed a C compiler. All you have to do is::\n\n    pip install psutil\n\nIf wheels are not available for your platform or architecture, or you wish to\nbuild & install psutil from sources, keep reading.\n\nCompile psutil from source\n--------------------------\n\nUNIX\n^^^^\n\nOn all UNIX systems you can use the `install-sysdeps.sh\n<https://github.com/giampaolo/psutil/blob/master/scripts/internal/install-sysdeps.sh>`_\nscript. This will install the system dependencies necessary to compile psutil\nfrom sources. You can invoke this script from the Makefile as::\n\n    make install-sysdeps\n\nAfter system deps are installed, you can compile and install psutil with::\n\n    make build\n    make install\n\n...or this, which will fetch the latest source distribution from `PyPI <https://pypi.org/project/psutil/>`_::\n\n    pip install --no-binary :all: psutil\n\nLinux\n^^^^^\n\nDebian / Ubuntu::\n\n    sudo apt install gcc python3-dev\n    pip install --no-binary :all: psutil\n\nRedHat / CentOS::\n\n    sudo yum install gcc python3-devel\n    pip install --no-binary :all: psutil\n\nArch::\n\n    sudo pacman -S gcc python\n    pip install --no-binary :all: psutil\n\nAlpine::\n\n    sudo apk add gcc python3-dev musl-dev linux-headers\n    pip install --no-binary :all: psutil\n\nWindows\n^^^^^^^\n\n- To build or install psutil from source on Windows, you need to have\n  `Visual Studio 2017 <https://visualstudio.microsoft.com/vs/older-downloads/>`_\n  or later installed. For detailed instructions, see the\n  `CPython Developer Guide <https://devguide.python.org/getting-started/setup-building/#windows>`_.\n- MinGW is not supported for building psutil on Windows.\n- To build directly from the source tarball (.tar.gz) on PYPI, run::\n\n    pip install --no-binary :all: psutil\n\n- If you want to clone psutil's Git repository and build / develop locally,\n  first install: `Git for Windows <https://git-scm.com/install/windows>`_\n  and launch a Git Bash shell. This provides a Unix-like environment where\n  ``make`` works.\n- Once inside Git Bash, you can run the usual ``make`` commands::\n\n    make build\n    make install\n\nmacOS\n^^^^^\n\nInstall Xcode first:\n\n::\n\n    xcode-select --install\n    pip install --no-binary :all: psutil\n\nFreeBSD\n^^^^^^^\n\n::\n\n    pkg install python3 gcc\n    python3 -m pip install psutil\n\nOpenBSD\n^^^^^^^\n\n::\n\n    export PKG_PATH=https://cdn.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/\n    pkg_add -v python3 gcc\n    pip install psutil\n\nNetBSD\n^^^^^^\n\nAssuming Python 3.11:\n\n::\n\n    export PKG_PATH=\"https://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All\"\n    pkg_add -v pkgin\n    pkgin install python311-* gcc12-* py311-setuptools-* py311-pip-*\n    python3.11 -m pip install psutil\n\nSun Solaris\n^^^^^^^^^^^\n\nIf ``cc`` compiler is not installed create a symbolic link to ``gcc``::\n\n    sudo ln -s /usr/bin/gcc /usr/local/bin/cc\n\nInstall::\n\n    pkg install gcc\n    pip install psutil\n\nTroubleshooting\n---------------\n\nInstall pip\n^^^^^^^^^^^\n\nIf you don't have pip you can install it with wget::\n\n    wget https://bootstrap.pypa.io/get-pip.py -O - | python3\n\n...or with curl::\n\n    python3 < <(curl -s https://bootstrap.pypa.io/get-pip.py)\n\nOn Windows, `download pip <https://pip.pypa.io/en/latest/installing/>`_, open\ncmd.exe and install it with::\n\n    py get-pip.py\n\n\"pip not found\"\n^^^^^^^^^^^^^^^\n\nSometimes pip is installed but it's not available in your ``PATH``\n(\"pip command not found\" or similar). Try this::\n\n    python3 -m pip install psutil\n\nPermission errors (UNIX)\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nIf you want to install psutil system-wide and you bump into permission errors\neither run as root user or prepend ``sudo``::\n\n    sudo pip install psutil\n"
  },
  {
    "path": "docs/migration.rst",
    "content": ".. currentmodule:: psutil\n.. include:: _links.rst\n\nMigration guide\n===============\n\nThis page summarises the breaking changes introduced in each major\nrelease and shows the code changes required to upgrade.\n\n.. note::\n  Minor and patch releases (e.g. 6.1.x, 7.1.x) never contain\n  breaking changes. Only major releases are listed here.\n\n.. contents::\n   :local:\n   :depth: 2\n\n.. _migration-8.0:\n\nMigrating to 8.0\n-----------------\n\nNamed tuple field order changed\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n- :func:`cpu_times`: ``user, system, idle`` fields changed order on Linux,\n  macOS and BSD. They are now always the first 3 fields on all platforms, with\n  platform-specific fields (e.g. ``nice``) following. Positional access (e.g.\n  ``cpu_times()[3]``) will silently return the wrong field. Always use\n  attribute access instead (e.g. ``cpu_times().idle``).\n\n  .. code-block:: python\n\n    # before\n    user, nice, system, idle = psutil.cpu_times()\n\n    # after\n    t = psutil.cpu_times()\n    user, system, idle = t.user, t.system, t.idle\n\n- :meth:`Process.memory_info`:\n\n  - The returned named tuple changed size and field\n    order. Positional access (e.g. ``p.memory_info()[3]`` or ``a, b, c =\n    p.memory_info()``) may break or silently return the wrong field. Always use\n    attribute access instead (e.g. ``p.memory_info().rss``). Also, ``lib`` and ``dirty`` on Linux were removed and turned into aliases emitting `DeprecationWarning`.\n\n    .. code-block:: python\n\n      # Linux before\n      rss, vms, shared, text, lib, data, dirty = p.memory_info()\n\n      # Linux after\n      t = p.memory_info()\n      rss, vms, shared, text, data = t.rss, t.vms, t.shared, t.text, t.data\n\n  - macOS: ``pfaults`` and ``pageins`` fields were removed with **no\n    backward-compatible aliases**. Use :meth:`page_faults` instead.\n\n    .. code-block:: python\n\n      # before\n      rss, vms, pfaults, pageins = p.memory_info()\n\n      # after\n      rss, vms = p.memory_info()\n      minor, major = p.page_faults()\n\n  - Windows: eliminated old aliases: ``wset`` → ``rss``, ``peak_wset`` →\n    ``peak_rss``, ``pagefile`` / ``private`` → ``vms``, ``peak_pagefile`` →\n    ``peak_vms``, ``num_page_faults`` → :meth:`page_faults` method. At the same\n    time ``paged_pool``, ``nonpaged_pool``, ``peak_paged_pool``,\n    ``peak_nonpaged_pool`` were moved to :meth:`memory_info_ex`. All these old\n    names still work but raise `DeprecationWarning`.\n\n  - BSD: a new ``peak_rss`` field was added.\n\n- :func:`virtual_memory`: on Windows, new ``cached`` and ``wired`` fields were\n  added. Code using positional unpacking will break:\n\n  .. code-block:: python\n\n    # before\n    total, avail, percent, used, free = psutil.virtual_memory()\n\n    # after\n    m = psutil.virtual_memory()\n    total, avail, percent, used, free = m.total, m.available, m.percent, m.used, m.free\n\ncpu_times() interrupt renamed to irq on Windows\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``interrupt`` field of :func:`cpu_times` on Windows was renamed to ``irq``\nto match the name used on Linux and BSD. The old name still works but raises\n:exc:`DeprecationWarning`:\n\n.. code-block:: python\n\n  # before\n  t = psutil.cpu_times()\n  print(t.interrupt)\n\n  # after\n  t = psutil.cpu_times()\n  print(t.irq)\n\nStatus and connection fields are now enums\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n- :meth:`Process.status` now returns a :class:`psutil.ProcessStatus` member\n  instead of a plain ``str``.\n- :meth:`Process.net_connections` and :func:`net_connections` ``status`` field\n  now returns a :class:`psutil.ConnectionStatus` member instead of a plain\n  ``str``.\n\nBecause both are :class:`enum.StrEnum` subclasses they compare equal to their\nstring values, so existing comparisons continue to work unchanged:\n\n.. code-block:: python\n\n  # these still work\n  p.status() == \"running\"\n  p.status() == psutil.STATUS_RUNNING\n\n  # repr() and type() differ, so code inspecting these may need updating\n\n\nThe individual constants (e.g. :data:`psutil.STATUS_RUNNING`) are kept as\naliases for the enum members, and should be preferred over accessing them via\nthe enum class:\n\n.. code-block:: python\n\n  # prefer this\n  p.status() == psutil.STATUS_RUNNING\n\n  # not this\n  p.status() == psutil.ProcessStatus.STATUS_RUNNING\n\nmemory_full_info() is deprecated\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`Process.memory_full_info` is deprecated. Use the new\n:meth:`Process.memory_footprint` instead:\n\n.. code-block:: python\n\n  # before\n  mem = p.memory_full_info()\n  uss = mem.uss\n\n  # after\n  mem = p.memory_footprint()\n  uss = mem.uss\n\nPython 3.6 dropped\n^^^^^^^^^^^^^^^^^^^^\n\nPython 3.6 is no longer supported. Minimum version is Python 3.7.\n\n----\n\n.. _migration-7.0:\n\nMigrating to 7.0\n-----------------\n\nProcess.memory_info_ex() removed\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe long-deprecated :meth:`Process.memory_info_ex` was removed (it was\ndeprecated since 4.0.0 in 2016). Use :meth:`Process.memory_full_info`\ninstead:\n\n.. code-block:: python\n\n  # before\n  p.memory_info_ex()\n\n  # after\n  p.memory_full_info()\n\nPython 2.7 dropped\n^^^^^^^^^^^^^^^^^^^^\n\nPython 2.7 is no longer supported. The last release to support Python\n2.7 is psutil 6.1.x:\n\n.. code-block:: bash\n\n  pip2 install \"psutil==6.1.*\"\n\n----\n\n.. _migration-6.0:\n\nMigrating to 6.0\n-----------------\n\nProcess.connections() renamed\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`Process.connections` was renamed to\n:meth:`Process.net_connections` for consistency with the system-level\n:func:`net_connections`. The old name triggers a ``DeprecationWarning``\nand will be removed in a future release:\n\n.. code-block:: python\n\n  # before\n  p.connections()\n  p.connections(kind=\"tcp\")\n\n  # after\n  p.net_connections()\n  p.net_connections(kind=\"tcp\")\n\ndisk_partitions() lost two fields\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``maxfile`` and ``maxpath`` fields were removed from the named tuple\nreturned by :func:`disk_partitions`. Code unpacking the tuple\npositionally will break:\n\n.. code-block:: python\n\n  # before (broken)\n  device, mountpoint, fstype, opts, maxfile, maxpath = part\n\n  # after\n  device, mountpoint, fstype, opts = (\n      part.device, part.mountpoint, part.fstype, part.opts\n  )\n\nprocess_iter() no longer checks for PID reuse\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:func:`process_iter` no longer pre-emptively checks whether yielded\nPIDs have been reused (this made it ~20× faster). If you need to verify\nthat a process object is still alive and refers to the same process, use\n:meth:`Process.is_running` explicitly:\n\n.. code-block:: python\n\n  for p in psutil.process_iter([\"name\"]):\n      if p.is_running():\n          print(p.pid, p.info[\"name\"])\n\n----\n\n.. _migration-5.0:\n\nMigrating to 5.0\n-----------------\n\n5.0.0 was the largest renaming in psutil history. All ``get_*`` and\n``set_*`` :class:`Process` methods lost their prefix, and several\nmodule-level names were changed.\n\nOld :class:`Process` method names still worked but raised\n``DeprecationWarning``. They were fully removed in 6.0.\n\nProcess methods\n^^^^^^^^^^^^^^^^\n\n.. list-table::\n   :header-rows: 1\n   :widths: 40 40\n\n   * - Old (< 5.0)\n     - New (>= 5.0)\n   * - ``p.get_children()``\n     - ``p.children()``\n   * - ``p.get_connections()``\n     - ``p.connections()`` → ``p.net_connections()`` in 6.0\n   * - ``p.get_cpu_affinity()``\n     - ``p.cpu_affinity()``\n   * - ``p.get_cpu_percent()``\n     - ``p.cpu_percent()``\n   * - ``p.get_cpu_times()``\n     - ``p.cpu_times()``\n   * - ``p.get_ext_memory_info()``\n     - ``p.memory_info_ex()`` → ``p.memory_full_info()`` in 4.0\n   * - ``p.get_io_counters()``\n     - ``p.io_counters()``\n   * - ``p.get_ionice()``\n     - ``p.ionice()``\n   * - ``p.get_memory_info()``\n     - ``p.memory_info()``\n   * - ``p.get_memory_maps()``\n     - ``p.memory_maps()``\n   * - ``p.get_memory_percent()``\n     - ``p.memory_percent()``\n   * - ``p.get_nice()``\n     - ``p.nice()``\n   * - ``p.get_num_ctx_switches()``\n     - ``p.num_ctx_switches()``\n   * - ``p.get_num_fds()``\n     - ``p.num_fds()``\n   * - ``p.get_num_threads()``\n     - ``p.num_threads()``\n   * - ``p.get_open_files()``\n     - ``p.open_files()``\n   * - ``p.get_rlimit()``\n     - ``p.rlimit()``\n   * - ``p.get_threads()``\n     - ``p.threads()``\n   * - ``p.getcwd()``\n     - ``p.cwd()``\n   * - ``p.set_nice(v)``\n     - ``p.nice(v)``\n   * - ``p.set_ionice(cls)``\n     - ``p.ionice(cls)``\n   * - ``p.set_cpu_affinity(cpus)``\n     - ``p.cpu_affinity(cpus)``\n\nModule-level renames\n^^^^^^^^^^^^^^^^^^^^^\n\n.. list-table::\n   :header-rows: 1\n   :widths: 40 40\n\n   * - Old (< 5.0)\n     - New (>= 5.0)\n   * - ``psutil.NUM_CPUS``\n     - ``psutil.cpu_count()``\n   * - ``psutil.BOOT_TIME``\n     - ``psutil.boot_time()``\n   * - ``psutil.TOTAL_PHYMEM``\n     - ``psutil.virtual_memory().total``\n   * - ``psutil.get_pid_list()``\n     - ``psutil.pids()``\n   * - ``psutil.get_users()``\n     - ``psutil.users()``\n   * - ``psutil.get_boot_time()``\n     - ``psutil.boot_time()``\n   * - ``psutil.network_io_counters()``\n     - ``psutil.net_io_counters()``\n   * - ``psutil.phymem_usage()``\n     - ``psutil.virtual_memory()``\n   * - ``psutil.virtmem_usage()``\n     - ``psutil.swap_memory()``\n"
  },
  {
    "path": "docs/platform.rst",
    "content": "Platform support\n================\n\nPython\n^^^^^^\n\n**Current Python:** 3.7 and PyPy3.\n\n**Python 2.7**: latest psutil version supporting it is\n`psutil 6.1.1 <https://pypi.org/project/psutil/6.1.1/>`_ (Dec 2024).\nThe 6.1.X series may still receive critical bug-fixes but no new features.\nTo install psutil on Python 2.7 run:\n\n::\n\n    python2 -m pip install psutil==6.1.*\n\nOperating systems\n^^^^^^^^^^^^^^^^^\n\n================  ================  ====  =================================================  =========\nPlatform          Minimum version   Year  How enforced                                       CI tested\n================  ================  ====  =================================================  =========\nLinux             2.6.13 (soft)     2005  graceful fallbacks; no hard check                  yes\nWindows           Vista             2007  hard check at import + build time                  yes\nmacOS             10.7 (Lion)       2011  ``MAC_OS_X_VERSION_MIN_REQUIRED`` in C             yes\nFreeBSD           12.0              2018  graceful fallbacks via ``#if __FreeBSD_version``   yes\nNetBSD            5.0               2009  graceful fallbacks via ``#if __NetBSD_Version__``  yes\nOpenBSD           unknown                                                                    yes\nSunOS / Solaris   unknown                                                                    memleak tests only\nAIX               unknown                                                                    no\n================  ================  ====  =================================================  =========\n\nNote: psutil may work on older versions of the above platforms but it is not\nguaranteed.\n\nArchitectures\n^^^^^^^^^^^^^\n\nSupported CPU architectures and platforms tested in CI or with prebuilt wheels:\n\n================  ===========================  ===========================\nArchitecture      CI-tested platforms          Wheel builds\n================  ===========================  ===========================\nx86_64            Linux, macOS, Windows        Linux, macOS, Windows\naarch64 / ARM64   Linux, macOS, Windows        Linux, macOS, Windows\n================  ===========================  ===========================\n\nNotes:\n\n- Linux wheels are built for both glibc (manylinux) and musl.\n- macOS wheels are universal2 (include both x86_64 and arm64 slices).\n- Windows wheels are labeled AMD64 or ARM64 according to architecture.\n- Other architectures (i686, ppc64le, s390x, riscv64, ...) are supported\n  but not CI-tested. They can be compiled from the source tarball\n  (``pip install psutil --no-binary psutil``).\n\nSupport history\n^^^^^^^^^^^^^^^\n\n* psutil 8.0.0 (2026-XX): drop Python 3.6\n* psutil 7.2.0 (2025-12): publish wheels for **Linux musl**\n* psutil 7.1.2 (2025-10): publish wheels for **free-threaded Python**\n* psutil 7.1.2 (2025-10): no longer publish wheels for 32-bit Python (Linux and Windows)\n* psutil 7.1.1 (2025-10): drop **SunOS 10**\n* psutil 7.1.0 (2025-09): drop **FreeBSD 8**\n* psutil 7.0.0 (2025-02): drop Python 2.7\n* psutil 5.9.6 (2023-10): drop Python 3.4 and 3.5\n* psutil 5.9.1 (2022-05): drop Python 2.6\n* psutil 5.9.0 (2021-12): add **MidnightBSD**\n* psutil 5.8.0 (2020-12): add **PyPy 2** on Windows\n* psutil 5.7.1 (2020-07): add **Windows Nano**\n* psutil 5.7.0 (2020-02): drop Windows XP & Windows Server 2003\n* psutil 5.7.0 (2020-02): add **PyPy 3** on Windows\n* psutil 5.4.0 (2017-11): add **AIX**\n* psutil 3.4.1 (2016-01): add **NetBSD**\n* psutil 3.3.0 (2015-11): add **OpenBSD**\n* psutil 1.0.0 (2013-07): add **Solaris**\n* psutil 0.1.1 (2009-03): add **FreeBSD**\n* psutil 0.1.0 (2009-01): add **Linux, Windows, macOS**\n"
  },
  {
    "path": "docs/recipes.rst",
    "content": "Recipes\n=======\n\nA collection of standalone, copy-paste solutions to specific problems. Each\nrecipe focuses on a single problem and provides a minimal solution which can be\nadapted to real-world code. The examples are intentionally short and avoid\nunnecessary abstractions so that the underlying psutil APIs are easy to\nunderstand. Most of them are not meant to be used in production.\n\n.. contents::\n   :local:\n   :depth: 3\n\nProcesses\n---------\n\nFinding processes\n^^^^^^^^^^^^^^^^^\n\nFind process by name:\n\n.. code-block:: python\n\n  import psutil\n\n  def find_procs_by_name(name):\n      ls = []\n      for p in psutil.process_iter([\"name\"]):\n          if p.info[\"name\"] == name:\n              ls.append(p)\n      return ls\n\n----\n\nA bit more advanced, check string against :meth:`Process.name`,\n:meth:`Process.exe` and :meth:`Process.cmdline`:\n\n.. code-block:: python\n\n  import os\n  import psutil\n\n  def find_procs_by_name_ex(name):\n      ls = []\n      for p in psutil.process_iter([\"name\", \"exe\", \"cmdline\"]):\n          if (\n              name == p.info[\"name\"]\n              or (p.info[\"exe\"] and os.path.basename(p.info[\"exe\"]) == name)\n              or (p.info[\"cmdline\"] and p.info[\"cmdline\"][0] == name)\n          ):\n              ls.append(p)\n      return ls\n\n----\n\nFind the process listening on a given TCP port:\n\n.. code-block:: python\n\n  import psutil\n\n  def find_proc_by_port(port):\n      for proc in psutil.process_iter():\n          try:\n              cons = proc.net_connections(kind=\"tcp\")\n          except psutil.Error:\n              pass\n          else:\n              for conn in cons:\n                  if conn.laddr.port == port and conn.status == psutil.CONN_LISTEN:\n                      return proc\n      return None\n\n----\n\nFind all processes that have an active connection to a given remote IP:\n\n.. code-block:: python\n\n  import psutil\n\n  def find_procs_by_remote_host(host):\n      ls = []\n      for proc in psutil.process_iter():\n          try:\n              cons = proc.net_connections(kind=\"inet\")\n          except psutil.Error:\n              pass\n          else:\n              for conn in cons:\n                  if conn.raddr and conn.raddr.ip == host:\n                      ls.append(proc)\n      return ls\n\n----\n\nFind all processes that have a given file open (useful on Windows):\n\n.. code-block:: python\n\n  import psutil\n\n  def find_procs_using_file(path):\n      ls = []\n      for p in psutil.process_iter([\"open_files\"]):\n          for f in p.info[\"open_files\"] or []:\n              if f.path == path:\n                  ls.append(p)\n                  break\n      return ls\n\nFiltering and sorting processes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProcesses owned by user:\n\n.. code-block:: python\n\n  >>> import getpass\n  >>> import psutil\n  >>> from pprint import pprint as pp\n  >>> pp([(p.pid, p.info[\"name\"]) for p in psutil.process_iter([\"name\", \"username\"]) if p.info[\"username\"] == getpass.getuser()])\n  (16832, 'bash'),\n  (19772, 'ssh'),\n  (20492, 'python')]\n\n----\n\nProcesses actively running:\n\n.. code-block:: python\n\n  >>> pp([(p.pid, p.info) for p in psutil.process_iter([\"name\", \"status\"]) if p.info[\"status\"] == psutil.STATUS_RUNNING])\n  [(1150, {'name': 'Xorg', 'status': <ProcessStatus.STATUS_RUNNING: 'running'>}),\n   (1776, {'name': 'unity-panel-service', 'status': <ProcessStatus.STATUS_RUNNING: 'running'>}),\n   (20492, {'name': 'python', 'status': <ProcessStatus.STATUS_RUNNING: 'running'>})]\n\n----\n\nProcesses using log files:\n\n.. code-block:: python\n\n  >>> for p in psutil.process_iter([\"name\", \"open_files\"]):\n  ...      for file in p.info[\"open_files\"] or []:\n  ...          if file.path.endswith(\".log\"):\n  ...               print(\"{:<5} {:<10} {}\".format(p.pid, p.info[\"name\"][:10], file.path))\n  ...\n  1510  upstart    /home/giampaolo/.cache/upstart/unity-settings-daemon.log\n  2174  nautilus   /home/giampaolo/.local/share/gvfs-metadata/home-ce08efac.log\n  2650  chrome     /home/giampaolo/.config/google-chrome/Default/data_reduction_proxy_leveldb/000003.log\n\n----\n\nProcesses consuming more than 500M of memory:\n\n.. code-block:: python\n\n  >>> pp([(p.pid, p.info[\"name\"], p.info[\"memory_info\"].rss) for p in psutil.process_iter([\"name\", \"memory_info\"]) if p.info[\"memory_info\"].rss > 500 * 1024 * 1024])\n  [(2650, 'chrome', 532324352),\n   (3038, 'chrome', 1120088064),\n   (21915, 'sublime_text', 615407616)]\n\n----\n\nTop 3 processes which consumed the most CPU time:\n\n.. code-block:: python\n\n  >>> pp([(p.pid, p.info[\"name\"], sum(p.info[\"cpu_times\"])) for p in sorted(psutil.process_iter([\"name\", \"cpu_times\"]), key=lambda p: sum(p.info[\"cpu_times\"][:2]))][-3:])\n  [(2721, 'chrome', 10219.73),\n   (1150, 'Xorg', 11116.989999999998),\n   (2650, 'chrome', 18451.97)]\n\n----\n\nTop N processes by cumulative disk read + write bytes (similar to ``iotop``):\n\n.. code-block:: python\n\n  import psutil\n\n\n  def top_io_procs(n=5):\n      procs = []\n      for p in psutil.process_iter([\"io_counters\"]):\n          try:\n              io = p.io_counters()\n          except psutil.Error:\n              pass\n          else:\n              procs.append((io.read_bytes + io.write_bytes, p))\n      procs.sort(key=lambda x: x[0], reverse=True)\n      return procs[:n]\n\n----\n\nTop N processes by open file descriptors (useful for diagnosing fd leaks):\n\n.. code-block:: python\n\n  import psutil\n\n  def top_open_files(n=5):\n      procs = []\n      for p in psutil.process_iter():\n          try:\n              procs.append((p.num_fds(), p))\n          except (psutil.NoSuchProcess, psutil.AccessDenied):\n              pass\n      procs.sort(key=lambda x: x[0], reverse=True)\n      return procs[:n]\n\nMonitoring processes\n^^^^^^^^^^^^^^^^^^^^\n\nPeriodically monitor CPU and memory usage of a process using\n:meth:`Process.oneshot` for efficiency:\n\n.. code-block:: python\n\n  import time\n  import psutil\n\n  def monitor(pid, interval=1):\n      p = psutil.Process(pid)\n      while p.is_running():\n          with p.oneshot():\n              cpu = p.cpu_percent()\n              mem = p.memory_info().rss\n              print(\"cpu={:<6} mem={}\".format(str(cpu) + \"%\", mem / 1024 / 1024))\n          time.sleep(interval)\n\n.. code-block:: none\n\n  cpu=4.2%   mem=23.4M\n  cpu=3.1%   mem=23.5M\n\nControlling processes\n^^^^^^^^^^^^^^^^^^^^^\n\n.. _recipe_kill_proc_tree:\n\nKill a process tree (including grandchildren):\n\n.. code-block:: python\n\n  import os\n  import signal\n\n  import psutil\n\n\n  def kill_proc_tree(\n      pid,\n      sig=signal.SIGTERM,\n      include_parent=True,\n      timeout=None,\n      on_terminate=None,\n  ):\n      \"\"\"Kill a process tree (including grandchildren) with signal\n      \"sig\" and return a (gone, still_alive) tuple.\n      \"on_terminate\", if specified, is a callback function which is\n      called as soon as a child terminates.\n      \"\"\"\n      assert pid != os.getpid(), \"I won't kill myself!\"\n      parent = psutil.Process(pid)\n      children = parent.children(recursive=True)\n      if include_parent:\n          children.append(parent)\n      for p in children:\n          try:\n              p.send_signal(sig)\n          except psutil.NoSuchProcess:\n              pass\n      gone, alive = psutil.wait_procs(\n          children, timeout=timeout, callback=on_terminate\n      )\n      return (gone, alive)\n\n----\n\nFind zombie (defunct) processes:\n\n\n.. code-block:: python\n\n  import psutil\n\n  for p in psutil.process_iter([\"status\"]):\n      if p.info[\"status\"] == psutil.STATUS_ZOMBIE:\n          print(f\"zombie: pid={p.pid}\")\n\n----\n\nTerminate all processes matching a given name:\n\n.. code-block:: python\n\n  import psutil\n\n  def terminate_procs_by_name(name):\n      for p in psutil.process_iter([\"name\"]):\n          if p.info[\"name\"] == name:\n              p.terminate()\n\n----\n\nTerminate a process gracefully, falling back to ``SIGKILL`` if it does not\nexit within the timeout:\n\n.. code-block:: python\n\n  import psutil\n\n  def graceful_kill(pid, timeout=3):\n      p = psutil.Process(pid)\n      p.terminate()\n      try:\n          p.wait(timeout=timeout)\n      except psutil.TimeoutExpired:\n          p.kill()\n\n----\n\nRestart a process:\n\n.. code-block:: python\n\n  import subprocess\n  import psutil\n\n  def restart_process(pid):\n      p = psutil.Process(pid)\n      cmd = p.cmdline()\n      p.terminate()\n      p.wait()\n      return subprocess.Popen(cmd)\n\n----\n\nTemporarily pause and resume a process using a context manager:\n\n.. code-block:: python\n\n  import contextlib\n  import psutil\n\n  @contextlib.contextmanager\n  def suspended(pid):\n      p = psutil.Process(pid)\n      p.suspend()\n      try:\n          yield p\n      finally:\n          p.resume()\n\n  # usage\n  with suspended(pid):\n      pass  # process is paused here\n\n----\n\nCPU throttle: limit a process's CPU usage to a target percentage by\nalternating :meth:`Process.suspend` and :meth:`Process.resume`:\n\n.. code-block:: python\n\n  import time\n  import psutil\n\n  def throttle(pid, max_cpu_percent=50, interval=0.1):\n      \"\"\"Slow down a process so it uses at most max_cpu_percent% CPU.\"\"\"\n      p = psutil.Process(pid)\n      while p.is_running():\n          cpu = p.cpu_percent(interval=interval)\n          if cpu > max_cpu_percent:\n              p.suspend()\n              time.sleep(interval * cpu / max_cpu_percent)\n              p.resume()\n\n----\n\nRestart a process automatically if it dies:\n\n.. code-block:: python\n\n  import subprocess\n  import time\n\n  import psutil\n\n\n  def watchdog(cmd, max_restarts=None, interval=1):\n      \"\"\"Run cmd as a persistent process. Restart on failure, optionally\n      with a max restarts. Logs start, exit, and restart events.\n      \"\"\"\n      restarts = 0\n      while True:\n          proc = subprocess.Popen(cmd)\n          p = psutil.Process(proc.pid)\n          print(f\"started PID {p.pid}\")\n          proc.wait()\n\n          if proc.returncode == 0:\n              # success\n              print(f\"PID {p.pid} exited cleanly\")\n              break\n\n          # failure\n          restarts += 1\n          print(f\"PID {p.pid} died, restarting ({restarts})\")\n\n          if max_restarts is not None and restarts > max_restarts:\n              print(\"max restarts reached, giving up\")\n              break\n\n          time.sleep(interval)\n\n\n  if __name__ == \"__main__\":\n      watchdog([\"python3\", \"script.py\"])\n\n\nSystem\n------\n\nAll APIs returning amounts (memory, disk, network I/O) express them in bytes.\nThis ``bytes2human()`` utility function used in the examples below converts\nthem to a human-readable string:\n\n.. code-block:: python\n\n  def bytes2human(n):\n      \"\"\"\n      >>> bytes2human(10000)\n      '9.8K'\n      >>> bytes2human(100001221)\n      '95.4M'\n      \"\"\"\n      symbols = (\"K\", \"M\", \"G\", \"T\", \"P\", \"E\", \"Z\", \"Y\")\n      prefix = {}\n      for i, s in enumerate(symbols):\n          prefix[s] = 1 << (i + 1) * 10\n      for s in reversed(symbols):\n          if abs(n) >= prefix[s]:\n              value = float(n) / prefix[s]\n              return \"{:.1f}{}\".format(value, s)\n      return \"{}B\".format(n)\n\nMemory\n^^^^^^\n\nShow both RAM and swap usage in human-readable form:\n\n.. code-block:: python\n\n  import psutil\n\n  def print_memory():\n      ram = psutil.virtual_memory()\n      swap = psutil.swap_memory()\n      print(\n          \"RAM:  total={}, used={}, free={}, percent={}%\".format(\n              bytes2human(ram.total),\n              bytes2human(ram.used),\n              bytes2human(ram.available),\n              ram.percent,\n          )\n      )\n      print(\n          \"Swap: total={}, used={}, free={}, percent={}%\".format(\n              bytes2human(swap.total),\n              bytes2human(swap.used),\n              bytes2human(swap.free),\n              swap.percent,\n          )\n      )\n\n\n.. code-block:: none\n\n  RAM:  total=8.0G, used=4.5G, free=3.0G, percent=56.2%\n  Swap: total=2.0G, used=0.1G, free=1.9G, percent=4.1%\n\nCPU\n^^^\n\nPrint real-time CPU usage percentage:\n\n.. code-block:: python\n\n  import psutil\n\n  while True:\n      print(\"CPU: {}%\".format(psutil.cpu_percent(interval=1)))\n\n.. code-block:: none\n\n  CPU: 2.1%\n  CPU: 1.4%\n  CPU: 0.9%\n\n----\n\nFor each CPU core:\n\n.. code-block:: python\n\n  import psutil\n\n  while True:\n      for i, pct in enumerate(psutil.cpu_percent(percpu=True, interval=1)):\n          print(\"CPU-{}: {}%\".format(i, pct))\n      print()\n\n.. code-block:: none\n\n  CPU-0: 1.0%\n  CPU-1: 2.1%\n  CPU-2: 3.0%\n\nDisks\n^^^^^\n\nShow disk usage for all mounted partitions:\n\n.. code-block:: python\n\n  import psutil\n\n  def print_disk_usage():\n      for part in psutil.disk_partitions():\n          usage = psutil.disk_usage(part.mountpoint)\n          print(\"{:<20} total={:<8} used={:<8} free={:<8} percent={}%\".format(\n              part.mountpoint,\n              bytes2human(usage.total), bytes2human(usage.used),\n              bytes2human(usage.free), usage.percent))\n\n.. code-block:: none\n\n  /               total=47.8G    used=17.4G    free=27.9G    percent=38.4%\n  /boot/efi       total=256.0M   used=73.6M    free=182.4M   percent=28.8%\n  /home           total=878.7G   used=497.5G   free=336.5G   percent=59.7%\n\n\n----\n\nShow real-time disk I/O:\n\n.. code-block:: python\n\n  import psutil, time\n\n  def disk_io():\n      while True:\n          before = psutil.disk_io_counters()\n          time.sleep(1)\n          after = psutil.disk_io_counters()\n          r = after.read_bytes - before.read_bytes\n          w = after.write_bytes - before.write_bytes\n          print(\"Read: {}/s, Write: {}/s\".format(bytes2human(r), bytes2human(w)))\n\n.. code-block:: none\n\n  Read: 1.2M/s, Write: 256.0K/s\n  Read: 0.0B/s, Write: 128.0K/s\n\nNetwork\n^^^^^^^\n\nList IP addresses for each network interface:\n\n.. code-block:: python\n\n  import psutil, socket\n\n  def print_net_addrs():\n      for iface, addrs in psutil.net_if_addrs().items():\n          for addr in addrs:\n              if addr.family == socket.AF_INET:\n                  print(\n                      \"{:<15} address={:<15} netmask={}\".format(\n                          iface, addr.address, addr.netmask\n                      )\n                  )\n\n.. code-block:: none\n\n  lo              address=127.0.0.1       netmask=255.0.0.0\n  eth0            address=10.0.0.4        netmask=255.255.255.0\n\n----\n\nShow real-time network I/O per interface:\n\n.. code-block:: python\n\n  import psutil, time\n\n  def net_io():\n      while True:\n          before = psutil.net_io_counters(pernic=True)\n          time.sleep(1)\n          after = psutil.net_io_counters(pernic=True)\n          for iface in after:\n              s = after[iface].bytes_sent - before[iface].bytes_sent\n              r = after[iface].bytes_recv - before[iface].bytes_recv\n              print(\n                  \"{:<10} sent={:<10} recv={}\".format(\n                      iface, bytes2human(s) + \"/s\", bytes2human(r) + \"/s\"\n                  )\n              )\n          print()\n\n.. code-block:: none\n\n  lo         sent=0.0B/s    recv=0.0B/s\n  eth0       sent=12.3K/s   recv=45.6K/s\n\n----\n\nList all active TCP connections with their status:\n\n.. code-block:: python\n\n  import psutil\n\n  def netstat():\n      templ = \"{:<20} {:<20} {:<13} {:<6}\"\n      print(templ.format(\"Local\", \"Remote\", \"Status\", \"PID\"))\n      for conn in psutil.net_connections(kind=\"tcp\"):\n          laddr = \"{}:{}\".format(conn.laddr.ip, conn.laddr.port)\n          raddr = (\n              \"{}:{}\".format(conn.raddr.ip, conn.raddr.port)\n              if conn.raddr\n              else \"-\"\n          )\n          print(templ.format(laddr, raddr, conn.status, conn.pid or \"\"))\n\n.. code-block:: none\n\n  Local             Remote              Status        PID\n  :::1716           -                   LISTEN        223441\n  127.0.0.1:631     -                   LISTEN\n  10.0.0.4:45278    20.222.111.74:443   ESTABLISHED   437213\n  10.0.0.4:40130    172.14.148.135:443  ESTABLISHED\n  0.0.0.0:22        -                   LISTEN        723345\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "sphinx\nsphinx_rtd_theme\nsphinx_copybutton\nsphinxcontrib-googleanalytics\n"
  },
  {
    "path": "docs/shell_equivalents.rst",
    "content": "\n.. currentmodule:: psutil\n\nShell equivalents\n=================\n\nThis page maps psutil's Python API to the equivalent native terminal commands\non each platform. This is useful for understanding what psutil replaces and for\ncross-checking results.\n\nSystem-wide functions\n---------------------\n\nCPU\n~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil function\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :func:`cpu_percent`\n     - ``top``\n     - same\n     - same\n     -\n   * - :func:`cpu_count(logical=True) <cpu_count>`\n     - ``nproc``\n     - ``sysctl hw.logicalcpu``\n     - ``sysctl hw.ncpu``\n     -\n   * - :func:`cpu_count(logical=False) <cpu_count>`\n     - ``lscpu | grep '^Core(s)'``\n     - ``sysctl hw.physicalcpu``\n     -\n     -\n   * - :func:`cpu_times(percpu=False) <cpu_times>`\n     - ``cat /proc/stat | grep '^cpu\\s'``\n     -\n     - ``systat -vmstat``\n     -\n   * - :func:`cpu_times(percpu=True) <cpu_times>`\n     - ``cat /proc/stat | grep '^cpu'``\n     -\n     - ``systat -vmstat``\n     -\n   * - :func:`cpu_times_percent(percpu=False) <cpu_times_percent>`\n     - ``mpstat``\n     -\n     -\n     -\n   * - :func:`cpu_times_percent(percpu=True) <cpu_times_percent>`\n     - ``mpstat -P ALL``\n     -\n     -\n     -\n   * - :func:`cpu_freq`\n     - ``cpufreq-info``, ``lscpu | grep \"MHz\"``\n     - ``sysctl hw.cpufrequency``\n     - ``sysctl dev.cpu.0.freq``\n     - ``systeminfo``\n   * - :func:`cpu_stats`\n     -\n     -\n     - ``sysctl vm.stats.sys``\n     -\n   * - :func:`getloadavg`\n     - ``uptime``\n     - same\n     - same\n     -\n\nMemory\n~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil function\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :func:`virtual_memory`\n     - ``free``, ``vmstat``, ``cat /proc/meminfo``\n     - ``vm_stat``\n     - ``sysctl vm.stats``\n     - ``systeminfo``\n   * - :func:`swap_memory`\n     - ``free``, ``vmstat``, ``swapon``\n     - ``sysctl vm.swapusage``\n     - ``swapinfo``\n     -\n   * - :func:`heap_info`\n     -\n     -\n     -\n     -\n   * - :func:`heap_trim`\n     -\n     -\n     -\n     -\n\nDisks\n~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil function\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :func:`disk_usage`\n     - ``df``\n     - same\n     - same\n     - ``fsutil volume diskfree C:\\``\n   * - :func:`disk_partitions`\n     - ``findmnt``, ``mount``\n     - ``mount``\n     - ``mount``\n     -\n   * - :func:`disk_io_counters`\n     - ``iostat -dx``\n     - ``iostat``\n     - ``iostat -x``\n     -\n\nNetwork\n~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil function\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :func:`net_connections`\n     - ``netstat -anp``, ``ss``, ``lsof -nP -i -U``\n     - ``netstat -anp``\n     - ``netstat -an``\n     - ``netstat -an``\n   * - :func:`net_if_addrs`\n     - ``ifconfig``, ``ip addr``\n     - ``ifconfig``\n     - ``ifconfig``\n     - ``ipconfig``, ``systeminfo``\n   * - :func:`net_io_counters`\n     - ``netstat -i``, ``ifconfig``, ``ip -s link``\n     - ``netstat -i``\n     - ``netstat -i``\n     - ``netstat -e``\n   * - :func:`net_if_stats`\n     - ``ifconfig``, ``ip -br link``, ``ip link``\n     - ``ifconfig``\n     - ``ifconfig``\n     - ``netsh interface show interface``\n\nSensors\n~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil function\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :func:`sensors_temperatures`\n     - ``sensors``\n     -\n     - ``sysctl dev.cpu.*.temperature``\n     -\n   * - :func:`sensors_fans`\n     - ``sensors``\n     -\n     - ``sysctl dev.cpu.*.fan``\n     -\n   * - :func:`sensors_battery`\n     - ``acpi -b``\n     - ``pmset -g batt``\n     - ``apm -b``\n     -\n\nOther\n~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil function\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :func:`boot_time`\n     - ``uptime``, ``who -b``\n     - ``sysctl kern.boottime``\n     - ``sysctl kern.boottime``\n     - ``systeminfo``\n   * - :func:`users`\n     - ``who -a``, ``w``\n     - same\n     - same\n     -\n   * - :func:`pids`\n     - ``ps -eo pid``\n     - same\n     - same\n     - ``tasklist``\n   * - :func:`pid_exists`\n     - ``kill -0 PID``\n     - same\n     - same\n     -\n\nProcess methods\n---------------\n\nIdentity\n~~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.name`\n     - ``ps -o comm -p PID``\n     - same\n     - ``procstat -b PID``\n     -\n   * - :meth:`Process.exe`\n     - ``readlink /proc/PID/exe``\n     - ``lsof -p PID``\n     - ``procstat -b PID``\n     -\n   * - :meth:`Process.cmdline`\n     - ``ps -o args -p PID``\n     - same\n     - ``procstat -c PID``\n     -\n   * - :meth:`Process.status`\n     - ``ps -o stat -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.create_time`\n     - ``ps -o lstart -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.is_running`\n     - ``kill -0 PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.environ`\n     - ``xargs -0 -a /proc/PID/environ``\n     -\n     - ``procstat -e PID``\n     -\n   * - :meth:`Process.cwd`\n     - ``pwdx PID``\n     - ``lsof -p PID -a -d cwd``\n     -\n     -\n\nProcess tree\n~~~~~~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.ppid`\n     - ``ps -o ppid= -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.parent`\n     - ``ps -p $(ps -o ppid= -p PID)``\n     - same\n     - same\n     -\n   * - :meth:`Process.parents`\n     - ``pstree -s PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.children(recursive=False) <Process.children>`\n     - ``pgrep -P PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.children(recursive=True) <Process.children>`\n     - ``pstree -p PID``\n     - same\n     - same\n     -\n\nCredentials\n~~~~~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.uids`\n     - ``ps -o uid,ruid,suid -p PID``\n     - same\n     - ``procstat -s PID``\n     -\n   * - :meth:`Process.gids`\n     - ``ps -o gid,rgid,sgid -p PID``\n     - same\n     - ``procstat -s PID``\n     -\n   * - :meth:`Process.username`\n     - ``ps -o user -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.terminal`\n     - ``ps -o tty -p PID``\n     - same\n     - same\n     -\n\nCPU / scheduling\n~~~~~~~~~~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.cpu_percent`\n     - ``ps -o %cpu -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.cpu_times`\n     - ``ps -o cputime -p PID``\n     - same\n     - ``procstat -r PID``\n     -\n   * - :meth:`Process.cpu_num`\n     - ``ps -o psr -p PID``\n     -\n     -\n     -\n   * - :meth:`Process.num_ctx_switches`\n     - ``pidstat -w -p PID``\n     -\n     - ``procstat -r PID``\n     -\n   * - :meth:`Process.cpu_affinity() <Process.cpu_affinity>`\n     - ``taskset -p PID``\n     -\n     - ``cpuset -g -p PID``\n     -\n   * - :meth:`Process.cpu_affinity(CPUS) <Process.cpu_affinity>`\n     - ``taskset -p MASK PID``\n     -\n     - ``cpuset -s -p PID -l CPUS``\n     -\n   * - :meth:`Process.ionice() <Process.ionice>`\n     - ``ionice -p PID``\n     -\n     -\n     -\n   * - :meth:`Process.ionice(CLASS) <Process.ionice>`\n     - ``ionice -c CLASS -p PID``\n     -\n     -\n     -\n   * - :meth:`Process.nice() <Process.nice>`\n     - ``ps -o nice -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.nice(VALUE) <Process.nice>`\n     - ``renice -n VALUE -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.rlimit(RES) <Process.rlimit>`\n     - ``prlimit --pid PID``\n     -\n     - ``procstat rlimit PID``\n     -\n   * - :meth:`Process.rlimit(RES, LIMITS) <Process.rlimit>`\n     - ``prlimit --pid PID --RES=SOFT:HARD``\n     -\n     -\n     -\n\nMemory\n~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.memory_info`\n     - ``ps -o rss,vsz -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.memory_info_ex`\n     - ``cat /proc/PID/status``\n     -\n     -\n     -\n   * - :meth:`Process.memory_percent`\n     - ``ps -o %mem -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.memory_maps`\n     - ``pmap PID``\n     - ``vmmap PID``\n     - ``procstat -v PID``\n     -\n   * - :meth:`Process.memory_footprint`\n     - ``smem``, ``smemstat``\n     -\n     -\n     -\n   * - :meth:`Process.page_faults`\n     - ``ps -o maj_flt,min_flt -p PID``\n     - ``ps -o faults -p PID``\n     - ``procstat -r PID``\n     -\n\nThreads\n~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.num_threads`\n     - ``ps -o nlwp -p PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.threads`\n     - ``ps -T -p PID``\n     -\n     -\n     -\n\nFiles and connections\n~~~~~~~~~~~~~~~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.net_connections`\n     - ``ss -p``, ``lsof -p PID -i``\n     - ``lsof -p PID -i``\n     -\n     - ``netstat -ano | findstr PID``\n   * - :meth:`Process.open_files`\n     - ``lsof -p PID``\n     - same\n     - ``procstat -f PID``\n     - ``handle.exe -p PID``\n   * - :meth:`Process.io_counters`\n     - ``cat /proc/PID/io``\n     -\n     -\n     -\n   * - :meth:`Process.num_fds`\n     - ``ls /proc/PID/fd | wc -l``\n     -\n     -\n     -\n   * - :meth:`Process.num_handles`\n     -\n     -\n     -\n     -\n\nSignals\n~~~~~~~\n\n.. list-table::\n   :header-rows: 1\n\n   * - psutil method\n     - Linux\n     - macOS\n     - BSD\n     - Windows\n   * - :meth:`Process.send_signal`\n     - ``kill -SIG PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.suspend`\n     - ``kill -STOP PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.resume`\n     - ``kill -CONT PID``\n     - same\n     - same\n     -\n   * - :meth:`Process.terminate`\n     - ``kill -TERM PID``\n     - same\n     - same\n     - ``taskkill /PID PID``\n   * - :meth:`Process.kill`\n     - ``kill -KILL PID``\n     - same\n     - same\n     - ``taskkill /F /PID PID``\n   * - :meth:`Process.wait`\n     - ``tail --pid=PID -f /dev/null``\n     - ``lsof -p PID +r 1``\n     - ``pwait PID``\n     -\n"
  },
  {
    "path": "docs/timeline.rst",
    "content": "Timeline\n========\n\n- 2026-02-08:\n  `7.2.3 <https://pypi.org/project/psutil/7.2.3/#files>`_ -\n  `what's new <changelog.html#723>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.2.2...release-7.2.3#files_bucket>`_\n- 2026-01-28:\n  `7.2.2 <https://pypi.org/project/psutil/7.2.2/#files>`_ -\n  `what's new <changelog.html#722>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.2.1...release-7.2.2#files_bucket>`_\n- 2025-12-29:\n  `7.2.1 <https://pypi.org/project/psutil/7.2.1/#files>`_ -\n  `what's new <changelog.html#721>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.2.0...release-7.2.1#files_bucket>`_\n- 2025-12-23:\n  `7.2.0 <https://pypi.org/project/psutil/7.2.0/#files>`_ -\n  `what's new <changelog.html#720>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.1.3...release-7.2.0#files_bucket>`_\n- 2025-11-02:\n  `7.1.3 <https://pypi.org/project/psutil/7.1.3/#files>`_ -\n  `what's new <changelog.html#713>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.1.2...release-7.1.3#files_bucket>`_\n- 2025-10-25:\n  `7.1.2 <https://pypi.org/project/psutil/7.1.2/#files>`_ -\n  `what's new <changelog.html#712>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.1.1...release-7.1.2#files_bucket>`_\n- 2025-10-19:\n  `7.1.1 <https://pypi.org/project/psutil/7.1.1/#files>`_ -\n  `what's new <changelog.html#711>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.1.0...release-7.1.1#files_bucket>`_\n- 2025-09-17:\n  `7.1.0 <https://pypi.org/project/psutil/7.1.0/#files>`_ -\n  `what's new <changelog.html#710>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-7.0.0...release-7.1.0#files_bucket>`_\n- 2025-02-13:\n  `7.0.0 <https://pypi.org/project/psutil/7.0.0/#files>`_ -\n  `what's new <changelog.html#700>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-6.1.1...release-7.0.0#files_bucket>`_\n- 2024-12-19:\n  `6.1.1 <https://pypi.org/project/psutil/6.1.1/#files>`_ -\n  `what's new <changelog.html#611>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-6.1.0...release-6.1.1#files_bucket>`_\n- 2024-10-17:\n  `6.1.0 <https://pypi.org/project/psutil/6.1.0/#files>`_ -\n  `what's new <changelog.html#610>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-6.0.0...release-6.1.0#files_bucket>`_\n- 2024-06-18:\n  `6.0.0 <https://pypi.org/project/psutil/6.0.0/#files>`_ -\n  `what's new <changelog.html#600>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.8...release-6.0.0#files_bucket>`_\n- 2024-01-19:\n  `5.9.8 <https://pypi.org/project/psutil/5.9.8/#files>`_ -\n  `what's new <changelog.html#598>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.7...release-5.9.8#files_bucket>`_\n- 2023-12-17:\n  `5.9.7 <https://pypi.org/project/psutil/5.9.7/#files>`_ -\n  `what's new <changelog.html#597>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.6...release-5.9.7#files_bucket>`_\n- 2023-10-15:\n  `5.9.6 <https://pypi.org/project/psutil/5.9.6/#files>`_ -\n  `what's new <changelog.html#596>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.5...release-5.9.6#files_bucket>`_\n- 2023-04-17:\n  `5.9.5 <https://pypi.org/project/psutil/5.9.5/#files>`_ -\n  `what's new <changelog.html#595>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.4...release-5.9.5#files_bucket>`_\n- 2022-11-07:\n  `5.9.4 <https://pypi.org/project/psutil/5.9.4/#files>`_ -\n  `what's new <changelog.html#594>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.3...release-5.9.4#files_bucket>`_\n- 2022-10-18:\n  `5.9.3 <https://pypi.org/project/psutil/5.9.3/#files>`_ -\n  `what's new <changelog.html#593>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.2...release-5.9.3#files_bucket>`_\n- 2022-09-04:\n  `5.9.2 <https://pypi.org/project/psutil/5.9.2/#files>`_ -\n  `what's new <changelog.html#592>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.1...release-5.9.2#files_bucket>`_\n- 2022-05-20:\n  `5.9.1 <https://pypi.org/project/psutil/5.9.1/#files>`_ -\n  `what's new <changelog.html#591>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.9.0...release-5.9.1#files_bucket>`_\n- 2021-12-29:\n  `5.9.0 <https://pypi.org/project/psutil/5.9.0/#files>`_ -\n  `what's new <changelog.html#590>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.8.0...release-5.9.0#files_bucket>`_\n- 2020-12-19:\n  `5.8.0 <https://pypi.org/project/psutil/5.8.0/#files>`_ -\n  `what's new <changelog.html#580>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.7.3...release-5.8.0#files_bucket>`_\n- 2020-10-24:\n  `5.7.3 <https://pypi.org/project/psutil/5.7.3/#files>`_ -\n  `what's new <changelog.html#573>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.7.2...release-5.7.3#files_bucket>`_\n- 2020-07-15:\n  `5.7.2 <https://pypi.org/project/psutil/5.7.2/#files>`_ -\n  `what's new <changelog.html#572>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.7.1...release-5.7.2#files_bucket>`_\n- 2020-07-15:\n  `5.7.1 <https://pypi.org/project/psutil/5.7.1/#files>`_ -\n  `what's new <changelog.html#571>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.7.0...release-5.7.1#files_bucket>`_\n- 2020-02-18:\n  `5.7.0 <https://pypi.org/project/psutil/5.7.0/#files>`_ -\n  `what's new <changelog.html#570>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.7...release-5.7.0#files_bucket>`_\n- 2019-11-26:\n  `5.6.7 <https://pypi.org/project/psutil/5.6.7/#files>`_ -\n  `what's new <changelog.html#567>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.6...release-5.6.7#files_bucket>`_\n- 2019-11-25:\n  `5.6.6 <https://pypi.org/project/psutil/5.6.6/#files>`_ -\n  `what's new <changelog.html#566>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.5...release-5.6.6#files_bucket>`_\n- 2019-11-06:\n  `5.6.5 <https://pypi.org/project/psutil/5.6.5/#files>`_ -\n  `what's new <changelog.html#565>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.4...release-5.6.5#files_bucket>`_\n- 2019-11-04:\n  `5.6.4 <https://pypi.org/project/psutil/5.6.4/#files>`_ -\n  `what's new <changelog.html#564>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.3...release-5.6.4#files_bucket>`_\n- 2019-06-11:\n  `5.6.3 <https://pypi.org/project/psutil/5.6.3/#files>`_ -\n  `what's new <changelog.html#563>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.2...release-5.6.3#files_bucket>`_\n- 2019-04-26:\n  `5.6.2 <https://pypi.org/project/psutil/5.6.2/#files>`_ -\n  `what's new <changelog.html#562>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.1...release-5.6.2#files_bucket>`_\n- 2019-03-11:\n  `5.6.1 <https://pypi.org/project/psutil/5.6.1/#files>`_ -\n  `what's new <changelog.html#561>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.0...release-5.6.1#files_bucket>`_\n- 2019-03-05:\n  `5.6.0 <https://pypi.org/project/psutil/5.6.0/#files>`_ -\n  `what's new <changelog.html#560>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.5.1...release-5.6.0#files_bucket>`_\n- 2019-02-15:\n  `5.5.1 <https://pypi.org/project/psutil/5.5.1/#files>`_ -\n  `what's new <changelog.html#551>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.5.0...release-5.5.1#files_bucket>`_\n- 2019-01-23:\n  `5.5.0 <https://pypi.org/project/psutil/5.5.0/#files>`_ -\n  `what's new <changelog.html#550>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.8...release-5.5.0#files_bucket>`_\n- 2018-10-30:\n  `5.4.8 <https://pypi.org/project/psutil/5.4.8/#files>`_ -\n  `what's new <changelog.html#548>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.7...release-5.4.8#files_bucket>`_\n- 2018-08-14:\n  `5.4.7 <https://pypi.org/project/psutil/5.4.7/#files>`_ -\n  `what's new <changelog.html#547>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.6...release-5.4.7#files_bucket>`_\n- 2018-06-07:\n  `5.4.6 <https://pypi.org/project/psutil/5.4.6/#files>`_ -\n  `what's new <changelog.html#546>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.5...release-5.4.6#files_bucket>`_\n- 2018-04-13:\n  `5.4.5 <https://pypi.org/project/psutil/5.4.5/#files>`_ -\n  `what's new <changelog.html#545>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.4...release-5.4.5#files_bucket>`_\n- 2018-04-13:\n  `5.4.4 <https://pypi.org/project/psutil/5.4.4/#files>`_ -\n  `what's new <changelog.html#544>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.3...release-5.4.4#files_bucket>`_\n- 2018-01-01:\n  `5.4.3 <https://pypi.org/project/psutil/5.4.3/#files>`_ -\n  `what's new <changelog.html#543>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.2...release-5.4.3#files_bucket>`_\n- 2017-12-07:\n  `5.4.2 <https://pypi.org/project/psutil/5.4.2/#files>`_ -\n  `what's new <changelog.html#542>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.1...release-5.4.2#files_bucket>`_\n- 2017-11-08:\n  `5.4.1 <https://pypi.org/project/psutil/5.4.1/#files>`_ -\n  `what's new <changelog.html#541>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.0...release-5.4.1#files_bucket>`_\n- 2017-10-12:\n  `5.4.0 <https://pypi.org/project/psutil/5.4.0/#files>`_ -\n  `what's new <changelog.html#540>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.3.1...release-5.4.0#files_bucket>`_\n- 2017-09-10:\n  `5.3.1 <https://pypi.org/project/psutil/5.3.1/#files>`_ -\n  `what's new <changelog.html#531>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.3.0...release-5.3.1#files_bucket>`_\n- 2017-09-01:\n  `5.3.0 <https://pypi.org/project/psutil/5.3.0/#files>`_ -\n  `what's new <changelog.html#530>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.2.2...release-5.3.0#files_bucket>`_\n- 2017-04-10:\n  `5.2.2 <https://pypi.org/project/psutil/5.2.2/#files>`_ -\n  `what's new <changelog.html#522>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.2.1...release-5.2.2#files_bucket>`_\n- 2017-03-24:\n  `5.2.1 <https://pypi.org/project/psutil/5.2.1/#files>`_ -\n  `what's new <changelog.html#521>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.2.0...release-5.2.1#files_bucket>`_\n- 2017-03-05:\n  `5.2.0 <https://pypi.org/project/psutil/5.2.0/#files>`_ -\n  `what's new <changelog.html#520>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.3...release-5.2.0#files_bucket>`_\n- 2017-02-07:\n  `5.1.3 <https://pypi.org/project/psutil/5.1.3/#files>`_ -\n  `what's new <changelog.html#513>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.2...release-5.1.3#files_bucket>`_\n- 2017-02-03:\n  `5.1.2 <https://pypi.org/project/psutil/5.1.2/#files>`_ -\n  `what's new <changelog.html#512>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.1...release-5.1.2#files_bucket>`_\n- 2017-02-03:\n  `5.1.1 <https://pypi.org/project/psutil/5.1.1/#files>`_ -\n  `what's new <changelog.html#511>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.0...release-5.1.1#files_bucket>`_\n- 2017-02-01:\n  `5.1.0 <https://pypi.org/project/psutil/5.1.0/#files>`_ -\n  `what's new <changelog.html#510>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.0.1...release-5.1.0#files_bucket>`_\n- 2016-12-21:\n  `5.0.1 <https://pypi.org/project/psutil/5.0.1/#files>`_ -\n  `what's new <changelog.html#501>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-5.0.0...release-5.0.1#files_bucket>`_\n- 2016-11-06:\n  `5.0.0 <https://pypi.org/project/psutil/5.0.0/#files>`_ -\n  `what's new <changelog.html#500>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.4.2...release-5.0.0#files_bucket>`_\n- 2016-10-25:\n  `4.4.2 <https://pypi.org/project/psutil/4.4.2/#files>`_ -\n  `what's new <changelog.html#442>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.4.1...release-4.4.2#files_bucket>`_\n- 2016-10-23:\n  `4.4.1 <https://pypi.org/project/psutil/4.4.1/#files>`_ -\n  `what's new <changelog.html#441>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.4.0...release-4.4.1#files_bucket>`_\n- 2016-10-05:\n  `4.4.0 <https://pypi.org/project/psutil/4.4.0/#files>`_ -\n  `what's new <changelog.html#440>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.3.1...release-4.4.0#files_bucket>`_\n- 2016-09-01:\n  `4.3.1 <https://pypi.org/project/psutil/4.3.1/#files>`_ -\n  `what's new <changelog.html#431>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.3.0...release-4.3.1#files_bucket>`_\n- 2016-06-18:\n  `4.3.0 <https://pypi.org/project/psutil/4.3.0/#files>`_ -\n  `what's new <changelog.html#430>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.2.0...release-4.3.0#files_bucket>`_\n- 2016-05-14:\n  `4.2.0 <https://pypi.org/project/psutil/4.2.0/#files>`_ -\n  `what's new <changelog.html#420>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.1.0...release-4.2.0#files_bucket>`_\n- 2016-03-12:\n  `4.1.0 <https://pypi.org/project/psutil/4.1.0/#files>`_ -\n  `what's new <changelog.html#410>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-4.0.0...release-4.1.0#files_bucket>`_\n- 2016-02-17:\n  `4.0.0 <https://pypi.org/project/psutil/4.0.0/#files>`_ -\n  `what's new <changelog.html#400>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.4.2...release-4.0.0#files_bucket>`_\n- 2016-01-20:\n  `3.4.2 <https://pypi.org/project/psutil/3.4.2/#files>`_ -\n  `what's new <changelog.html#342>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.4.1...release-3.4.2#files_bucket>`_\n- 2016-01-15:\n  `3.4.1 <https://pypi.org/project/psutil/3.4.1/#files>`_ -\n  `what's new <changelog.html#341>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.3.0...release-3.4.1#files_bucket>`_\n- 2015-11-25:\n  `3.3.0 <https://pypi.org/project/psutil/3.3.0/#files>`_ -\n  `what's new <changelog.html#330>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.2.2...release-3.3.0#files_bucket>`_\n- 2015-10-04:\n  `3.2.2 <https://pypi.org/project/psutil/3.2.2/#files>`_ -\n  `what's new <changelog.html#322>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.2.1...release-3.2.2#files_bucket>`_\n- 2015-09-03:\n  `3.2.1 <https://pypi.org/project/psutil/3.2.1/#files>`_ -\n  `what's new <changelog.html#321>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.2.0...release-3.2.1#files_bucket>`_\n- 2015-09-02:\n  `3.2.0 <https://pypi.org/project/psutil/3.2.0/#files>`_ -\n  `what's new <changelog.html#320>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.1.1...release-3.2.0#files_bucket>`_\n- 2015-07-15:\n  `3.1.1 <https://pypi.org/project/psutil/3.1.1/#files>`_ -\n  `what's new <changelog.html#311>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.1.0...release-3.1.1#files_bucket>`_\n- 2015-07-15:\n  `3.1.0 <https://pypi.org/project/psutil/3.1.0/#files>`_ -\n  `what's new <changelog.html#310>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.0.1...release-3.1.0#files_bucket>`_\n- 2015-06-18:\n  `3.0.1 <https://pypi.org/project/psutil/3.0.1/#files>`_ -\n  `what's new <changelog.html#301>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-3.0.0...release-3.0.1#files_bucket>`_\n- 2015-06-13:\n  `3.0.0 <https://pypi.org/project/psutil/3.0.0/#files>`_ -\n  `what's new <changelog.html#300>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.2.1...release-3.0.0#files_bucket>`_\n- 2015-02-02:\n  `2.2.1 <https://pypi.org/project/psutil/2.2.1/#files>`_ -\n  `what's new <changelog.html#221>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.2.0...release-2.2.1#files_bucket>`_\n- 2015-01-06:\n  `2.2.0 <https://pypi.org/project/psutil/2.2.0/#files>`_ -\n  `what's new <changelog.html#220>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.3...release-2.2.0#files_bucket>`_\n- 2014-09-26:\n  `2.1.3 <https://pypi.org/project/psutil/2.1.3/#files>`_ -\n  `what's new <changelog.html#213>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.2...release-2.1.3#files_bucket>`_\n- 2014-09-21:\n  `2.1.2 <https://pypi.org/project/psutil/2.1.2/#files>`_ -\n  `what's new <changelog.html#212>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.1...release-2.1.2#files_bucket>`_\n- 2014-04-30:\n  `2.1.1 <https://pypi.org/project/psutil/2.1.1/#files>`_ -\n  `what's new <changelog.html#211>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.0...release-2.1.1#files_bucket>`_\n- 2014-04-08:\n  `2.1.0 <https://pypi.org/project/psutil/2.1.0/#files>`_ -\n  `what's new <changelog.html#210>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-2.0.0...release-2.1.0#files_bucket>`_\n- 2014-03-10:\n  `2.0.0 <https://pypi.org/project/psutil/2.0.0/#files>`_ -\n  `what's new <changelog.html#200>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.2.1...release-2.0.0#files_bucket>`_\n- 2013-11-25:\n  `1.2.1 <https://pypi.org/project/psutil/1.2.1/#files>`_ -\n  `what's new <changelog.html#121>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.2.0...release-1.2.1#files_bucket>`_\n- 2013-11-20:\n  `1.2.0 <https://pypi.org/project/psutil/1.2.0/#files>`_ -\n  `what's new <changelog.html#120>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.1.2...release-1.2.0#files_bucket>`_\n- 2013-10-22:\n  `1.1.2 <https://pypi.org/project/psutil/1.1.2/#files>`_ -\n  `what's new <changelog.html#112>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.1.1...release-1.1.2#files_bucket>`_\n- 2013-10-08:\n  `1.1.1 <https://pypi.org/project/psutil/1.1.1/#files>`_ -\n  `what's new <changelog.html#111>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.1.0...release-1.1.1#files_bucket>`_\n- 2013-09-28:\n  `1.1.0 <https://pypi.org/project/psutil/1.1.0/#files>`_ -\n  `what's new <changelog.html#110>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.0.1...release-1.1.0#files_bucket>`_\n- 2013-07-12:\n  `1.0.1 <https://pypi.org/project/psutil/1.0.1/#files>`_ -\n  `what's new <changelog.html#101>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-1.0.0...release-1.0.1#files_bucket>`_\n- 2013-07-10:\n  `1.0.0 <https://pypi.org/project/psutil/1.0.0/#files>`_ -\n  `what's new <changelog.html#100>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.7.1...release-1.0.0#files_bucket>`_\n- 2013-05-03:\n  `0.7.1 <https://pypi.org/project/psutil/0.7.1/#files>`_ -\n  `what's new <changelog.html#071>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.7.0...release-0.7.1#files_bucket>`_\n- 2013-04-12:\n  `0.7.0 <https://pypi.org/project/psutil/0.7.0/#files>`_ -\n  `what's new <changelog.html#070>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.6.1...release-0.7.0#files_bucket>`_\n- 2012-08-16:\n  `0.6.1 <https://pypi.org/project/psutil/0.6.1/#files>`_ -\n  `what's new <changelog.html#061>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.6.0...release-0.6.1#files_bucket>`_\n- 2012-08-13:\n  `0.6.0 <https://pypi.org/project/psutil/0.6.0/#files>`_ -\n  `what's new <changelog.html#060>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.5.1...release-0.6.0#files_bucket>`_\n- 2012-06-29:\n  `0.5.1 <https://pypi.org/project/psutil/0.5.1/#files>`_ -\n  `what's new <changelog.html#051>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.5.0...release-0.5.1#files_bucket>`_\n- 2012-06-27:\n  `0.5.0 <https://pypi.org/project/psutil/0.5.0/#files>`_ -\n  `what's new <changelog.html#050>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.4.1...release-0.5.0#files_bucket>`_\n- 2011-12-14:\n  `0.4.1 <https://pypi.org/project/psutil/0.4.1/#files>`_ -\n  `what's new <changelog.html#041>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.4.0...release-0.4.1#files_bucket>`_\n- 2011-10-29:\n  `0.4.0 <https://pypi.org/project/psutil/0.4.0/#files>`_ -\n  `what's new <changelog.html#040>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.3.0...release-0.4.0#files_bucket>`_\n- 2011-07-08:\n  `0.3.0 <https://pypi.org/project/psutil/0.3.0/#files>`_ -\n  `what's new <changelog.html#030>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.2.1...release-0.3.0#files_bucket>`_\n- 2011-03-20:\n  `0.2.1 <https://pypi.org/project/psutil/0.2.1/#files>`_ -\n  `what's new <changelog.html#021>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.2.0...release-0.2.1#files_bucket>`_\n- 2010-11-13:\n  `0.2.0 <https://pypi.org/project/psutil/0.2.0/#files>`_ -\n  `what's new <changelog.html#020>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.3...release-0.2.0#files_bucket>`_\n- 2010-03-02:\n  `0.1.3 <https://pypi.org/project/psutil/0.1.3/#files>`_ -\n  `what's new <changelog.html#013>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.2...release-0.1.3#files_bucket>`_\n- 2009-05-06:\n  `0.1.2 <https://pypi.org/project/psutil/0.1.2/#files>`_ -\n  `what's new <changelog.html#012>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.1...release-0.1.2#files_bucket>`_\n- 2009-03-06:\n  `0.1.1 <https://pypi.org/project/psutil/0.1.1/#files>`_ -\n  `what's new <changelog.html#011>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.0...release-0.1.1#files_bucket>`_\n- 2009-01-27:\n  `0.1.0 <https://pypi.org/project/psutil/0.1.0/#files>`_ -\n  `diff <https://github.com/giampaolo/psutil/compare/d84cc9a783d977368a64016cdb3568d2c9bceacc...release-0.1.0#files_bucket>`_\n"
  },
  {
    "path": "psutil/__init__.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"psutil is a cross-platform library for retrieving information on\nrunning processes and system utilization (CPU, memory, disks, network,\nsensors) in Python. Supported platforms:\n\n - Linux\n - Windows\n - macOS\n - FreeBSD\n - OpenBSD\n - NetBSD\n - Sun Solaris\n - AIX\n\nSupported Python versions are cPython 3.7+ and PyPy.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\nimport contextlib\nimport datetime\nimport functools\nimport os\nimport signal\nimport socket\nimport subprocess\nimport sys\nimport threading\nimport time\nimport warnings\nfrom typing import TYPE_CHECKING as _TYPE_CHECKING\n\ntry:\n    import pwd\nexcept ImportError:\n    pwd = None\n\nfrom . import _common\nfrom . import _ntuples as _ntp\nfrom ._common import AIX\nfrom ._common import BSD\nfrom ._common import FREEBSD\nfrom ._common import LINUX\nfrom ._common import MACOS\nfrom ._common import NETBSD\nfrom ._common import OPENBSD\nfrom ._common import OSX  # deprecated alias\nfrom ._common import POSIX\nfrom ._common import SUNOS\nfrom ._common import WINDOWS\nfrom ._common import AccessDenied\nfrom ._common import Error\nfrom ._common import NoSuchProcess\nfrom ._common import TimeoutExpired\nfrom ._common import ZombieProcess\nfrom ._common import debug\nfrom ._common import memoize_when_activated\nfrom ._common import wrap_numbers as _wrap_numbers\nfrom ._enums import BatteryTime\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessStatus\n\nif _TYPE_CHECKING:\n    from typing import Any\n    from typing import Callable\n    from typing import Generator\n    from typing import Iterator\n\n    from ._ntuples import pconn\n    from ._ntuples import pcputimes\n    from ._ntuples import pctxsw\n    from ._ntuples import pfootprint\n    from ._ntuples import pfullmem\n    from ._ntuples import pgids\n    from ._ntuples import pheap\n    from ._ntuples import pio\n    from ._ntuples import pionice\n    from ._ntuples import pmem\n    from ._ntuples import pmem_ex\n    from ._ntuples import pmmap_ext\n    from ._ntuples import pmmap_grouped\n    from ._ntuples import popenfile\n    from ._ntuples import ppagefaults\n    from ._ntuples import pthread\n    from ._ntuples import puids\n    from ._ntuples import sbattery\n    from ._ntuples import sconn\n    from ._ntuples import scpufreq\n    from ._ntuples import scpustats\n    from ._ntuples import scputimes\n    from ._ntuples import sdiskio\n    from ._ntuples import sdiskpart\n    from ._ntuples import sdiskusage\n    from ._ntuples import sfan\n    from ._ntuples import shwtemp\n    from ._ntuples import snetio\n    from ._ntuples import snicaddr\n    from ._ntuples import snicstats\n    from ._ntuples import sswap\n    from ._ntuples import suser\n    from ._ntuples import svmem\n    from ._pswindows import WindowsService\n\n\nif LINUX:\n    # This is public API and it will be retrieved from _pslinux.py\n    # via sys.modules.\n    PROCFS_PATH = \"/proc\"\n\n    from . import _pslinux as _psplatform\n    from ._enums import ProcessIOPriority\n    from ._enums import ProcessRlimit\n\nelif WINDOWS:\n    from . import _pswindows as _psplatform\n    from ._enums import ProcessIOPriority\n    from ._enums import ProcessPriority\n\nelif MACOS:\n    from . import _psosx as _psplatform\n\nelif BSD:\n    from . import _psbsd as _psplatform\n\n    if FREEBSD:\n        from ._enums import ProcessRlimit\n\nelif SUNOS:\n    from . import _pssunos as _psplatform\n\n    # This is public writable API which is read from _pslinux.py and\n    # _pssunos.py via sys.modules.\n    PROCFS_PATH = \"/proc\"\n\nelif AIX:\n    from . import _psaix as _psplatform\n\n    # This is public API and it will be retrieved from _pslinux.py\n    # via sys.modules.\n    PROCFS_PATH = \"/proc\"\n\nelse:  # pragma: no cover\n    msg = f\"platform {sys.platform} is not supported\"\n    raise NotImplementedError(msg)\n\n\n# fmt: off\n__all__ = [\n    # exceptions\n    \"Error\", \"NoSuchProcess\", \"ZombieProcess\", \"AccessDenied\",\n    \"TimeoutExpired\",\n\n    # constants\n    \"version_info\", \"__version__\",\n\n    \"AF_LINK\",\n\n    \"BSD\", \"FREEBSD\", \"LINUX\", \"NETBSD\", \"OPENBSD\", \"MACOS\", \"OSX\", \"POSIX\",\n    \"SUNOS\", \"WINDOWS\", \"AIX\",\n\n    # classes\n    \"Process\", \"Popen\",\n\n    # functions\n    \"pid_exists\", \"pids\", \"process_iter\", \"wait_procs\",             # proc\n    \"virtual_memory\", \"swap_memory\",                                # memory\n    \"cpu_times\", \"cpu_percent\", \"cpu_times_percent\", \"cpu_count\",   # cpu\n    \"cpu_stats\", \"getloadavg\",  # \"cpu_freq\",\n    \"net_io_counters\", \"net_connections\", \"net_if_addrs\",           # network\n    \"net_if_stats\",\n    \"disk_io_counters\", \"disk_partitions\", \"disk_usage\",            # disk\n    # \"sensors_temperatures\", \"sensors_battery\", \"sensors_fans\"     # sensors\n    \"users\", \"boot_time\",                                           # others\n]\n# fmt: on\n\n__all__.extend(_psplatform.__extra__all__)\n_globals = globals()\n\n\ndef _export_enum(cls):\n    __all__.append(cls.__name__)\n    for name, member in cls.__members__.items():\n        if name not in _globals:  # noqa: F821\n            _globals[name] = member  # noqa: F821\n            __all__.append(name)\n\n\n# Populate global namespace with enums and CONSTANTs.\n_export_enum(ProcessStatus)\n_export_enum(ConnectionStatus)\n_export_enum(NicDuplex)\n_export_enum(BatteryTime)\nif LINUX or WINDOWS:\n    _export_enum(ProcessIOPriority)\nif WINDOWS:\n    _export_enum(ProcessPriority)\nif LINUX or FREEBSD:\n    _export_enum(ProcessRlimit)\nif LINUX or SUNOS or AIX:\n    __all__.append(\"PROCFS_PATH\")\n\ndel _globals, _export_enum\n\nAF_LINK = _psplatform.AF_LINK\n\n__author__ = \"Giampaolo Rodola'\"\n__version__ = \"8.0.0\"\nversion_info = tuple(int(num) for num in __version__.split('.'))\n\n_timer = getattr(time, 'monotonic', time.time)\n_TOTAL_PHYMEM = None\n_LOWEST_PID = None\n_SENTINEL = object()\n\n# Sanity check in case the user messed up with psutil installation\n# or did something weird with sys.path. In this case we might end\n# up importing a python module using a C extension module which\n# was compiled for a different version of psutil.\n# We want to prevent that by failing sooner rather than later.\n# See: https://github.com/giampaolo/psutil/issues/564\nif int(__version__.replace('.', '')) != getattr(\n    _psplatform.cext, 'version', None\n):\n    msg = f\"version conflict: {_psplatform.cext.__file__!r} C extension \"\n    msg += \"module was built for another version of psutil\"\n    if hasattr(_psplatform.cext, 'version'):\n        v = \".\".join(list(str(_psplatform.cext.version)))\n        msg += f\" ({v} instead of {__version__})\"\n    else:\n        msg += f\" (different than {__version__})\"\n    what = getattr(\n        _psplatform.cext,\n        \"__file__\",\n        \"the existing psutil install directory\",\n    )\n    msg += f\"; you may try to 'pip uninstall psutil', manually remove {what}\"\n    msg += \" or clean the virtual env somehow, then reinstall\"\n    raise ImportError(msg)\n\n\n# =====================================================================\n# --- Utils\n# =====================================================================\n\n\nif hasattr(_psplatform, 'ppid_map'):\n    # Faster version (Windows and Linux).\n    _ppid_map = _psplatform.ppid_map\nelse:  # pragma: no cover\n\n    def _ppid_map():\n        \"\"\"Return a {pid: ppid, ...} dict for all running processes in\n        one shot. Used to speed up Process.children().\n        \"\"\"\n        ret = {}\n        for pid in pids():\n            try:\n                ret[pid] = _psplatform.Process(pid).ppid()\n            except (NoSuchProcess, ZombieProcess):\n                pass\n        return ret\n\n\ndef _pprint_secs(secs):\n    \"\"\"Format seconds in a human readable form.\"\"\"\n    now = time.time()\n    secs_ago = int(now - secs)\n    fmt = \"%H:%M:%S\" if secs_ago < 60 * 60 * 24 else \"%Y-%m-%d %H:%M:%S\"\n    return datetime.datetime.fromtimestamp(secs).strftime(fmt)\n\n\ndef _check_conn_kind(kind):\n    \"\"\"Check net_connections()'s `kind` parameter.\"\"\"\n    kinds = tuple(_common.conn_tmap)\n    if kind not in kinds:\n        msg = f\"invalid kind argument {kind!r}; valid ones are: {kinds}\"\n        raise ValueError(msg)\n\n\n# =====================================================================\n# --- Process class\n# =====================================================================\n\n\nclass Process:\n    \"\"\"Represents an OS process with the given PID.\n    If PID is omitted current process PID (os.getpid()) is used.\n    Raise NoSuchProcess if PID does not exist.\n\n    Note that most of the methods of this class do not make sure that\n    the PID of the process being queried has been reused. That means\n    that you may end up retrieving information for another process.\n\n    The only exceptions for which process identity is pre-emptively\n    checked and guaranteed are:\n\n     - parent()\n     - children()\n     - nice() (set)\n     - ionice() (set)\n     - rlimit() (set)\n     - cpu_affinity (set)\n     - suspend()\n     - resume()\n     - send_signal()\n     - terminate()\n     - kill()\n\n    To prevent this problem for all other methods you can use\n    is_running() before querying the process.\n    \"\"\"\n\n    def __init__(self, pid: int | None = None) -> None:\n        self._init(pid)\n\n    def _init(self, pid, _ignore_nsp=False):\n        if pid is None:\n            pid = os.getpid()\n        else:\n            if pid < 0:\n                msg = f\"pid must be a positive integer (got {pid})\"\n                raise ValueError(msg)\n            try:\n                _psplatform.cext.check_pid_range(pid)\n            except OverflowError as err:\n                msg = \"process PID out of range\"\n                raise NoSuchProcess(pid, msg=msg) from err\n\n        self._pid = pid\n        self._name = None\n        self._exe = None\n        self._create_time = None\n        self._gone = False\n        self._pid_reused = False\n        self._hash = None\n        self._lock = threading.RLock()\n        # used for caching on Windows only (on POSIX ppid may change)\n        self._ppid = None\n        # platform-specific modules define an _psplatform.Process\n        # implementation class\n        self._proc = _psplatform.Process(pid)\n        self._last_sys_cpu_times = None\n        self._last_proc_cpu_times = None\n        self._exitcode = _SENTINEL\n        self._ident = (self.pid, None)\n        try:\n            self._ident = self._get_ident()\n        except AccessDenied:\n            # This should happen on Windows only, since we use the fast\n            # create time method. AFAIK, on all other platforms we are\n            # able to get create time for all PIDs.\n            pass\n        except ZombieProcess:\n            # Zombies can still be queried by this class (although\n            # not always) and pids() return them so just go on.\n            pass\n        except NoSuchProcess:\n            if not _ignore_nsp:\n                msg = \"process PID not found\"\n                raise NoSuchProcess(pid, msg=msg) from None\n            self._gone = True\n\n    def _get_ident(self):\n        \"\"\"Return a (pid, uid) tuple which is supposed to identify a\n        Process instance univocally over time. The PID alone is not\n        enough, as it can be assigned to a new process after this one\n        terminates, so we add process creation time to the mix. We need\n        this in order to prevent killing the wrong process later on.\n        This is also known as PID reuse or PID recycling problem.\n\n        The reliability of this strategy mostly depends on\n        create_time() precision, which is 0.01 secs on Linux. The\n        assumption is that, after a process terminates, the kernel\n        won't reuse the same PID after such a short period of time\n        (0.01 secs). Technically this is inherently racy, but\n        practically it should be good enough.\n\n        NOTE: unreliable on FreeBSD and OpenBSD as ctime is subject to\n        system clock updates.\n        \"\"\"\n\n        if WINDOWS:\n            # Use create_time() fast method in order to speedup\n            # `process_iter()`. This means we'll get AccessDenied for\n            # most ADMIN processes, but that's fine since it means\n            # we'll also get AccessDenied on kill().\n            # https://github.com/giampaolo/psutil/issues/2366#issuecomment-2381646555\n            self._create_time = self._proc.create_time(fast_only=True)\n            return (self.pid, self._create_time)\n        elif LINUX or NETBSD or OSX:\n            # Use 'monotonic' process starttime since boot to form unique\n            # process identity, since it is stable over changes to system\n            # time.\n            return (self.pid, self._proc.create_time(monotonic=True))\n        else:\n            return (self.pid, self.create_time())\n\n    def __str__(self):\n        info = {}\n        info[\"pid\"] = self.pid\n        if self._name:\n            info['name'] = self._name\n        with self.oneshot():\n            if self._pid_reused:\n                info[\"status\"] = \"terminated + PID reused\"\n            else:\n                try:\n                    info[\"name\"] = self.name()\n                    info[\"status\"] = self.status()\n                except ZombieProcess:\n                    info[\"status\"] = \"zombie\"\n                except NoSuchProcess:\n                    info[\"status\"] = \"terminated\"\n                except AccessDenied:\n                    pass\n\n            if self._exitcode not in {_SENTINEL, None}:\n                info[\"exitcode\"] = self._exitcode\n            if self._create_time is not None:\n                info['started'] = _pprint_secs(self._create_time)\n\n            return \"{}.{}({})\".format(\n                self.__class__.__module__,\n                self.__class__.__name__,\n                \", \".join([f\"{k}={v!r}\" for k, v in info.items()]),\n            )\n\n    __repr__ = __str__\n\n    def __eq__(self, other):\n        # Test for equality with another Process object based\n        # on PID and creation time.\n        if not isinstance(other, Process):\n            return NotImplemented\n        if OPENBSD or NETBSD or SUNOS:  # pragma: no cover\n            # Zombie processes on Open/NetBSD/illumos/Solaris have a\n            # creation time of 0.0.  This covers the case when a process\n            # started normally (so it has a ctime), then it turned into a\n            # zombie. It's important to do this because is_running()\n            # depends on __eq__.\n            pid1, ident1 = self._ident\n            pid2, ident2 = other._ident\n            if pid1 == pid2:\n                if ident1 and not ident2:\n                    try:\n                        return self.status() == ProcessStatus.STATUS_ZOMBIE\n                    except Error:\n                        pass\n        return self._ident == other._ident\n\n    def __ne__(self, other):\n        return not self == other\n\n    def __hash__(self):\n        if self._hash is None:\n            self._hash = hash(self._ident)\n        return self._hash\n\n    def _raise_if_pid_reused(self):\n        \"\"\"Raises NoSuchProcess in case process PID has been reused.\"\"\"\n        if self._pid_reused or (not self.is_running() and self._pid_reused):\n            # We may directly raise NSP in here already if PID is just\n            # not running, but I prefer NSP to be raised naturally by\n            # the actual Process API call. This way unit tests will tell\n            # us if the API is broken (aka don't raise NSP when it\n            # should). We also remain consistent with all other \"get\"\n            # APIs which don't use _raise_if_pid_reused().\n            msg = \"process no longer exists and its PID has been reused\"\n            raise NoSuchProcess(self.pid, self._name, msg=msg)\n\n    @property\n    def pid(self) -> int:\n        \"\"\"The process PID.\"\"\"\n        return self._pid\n\n    # --- utility methods\n\n    @contextlib.contextmanager\n    def oneshot(self) -> Generator[None, None, None]:\n        \"\"\"Utility context manager which considerably speeds up the\n        retrieval of multiple process information at the same time.\n\n        Internally different process info (e.g. name, ppid, uids,\n        gids, ...) may be fetched by using the same routine, but\n        only one information is returned and the others are discarded.\n        When using this context manager the internal routine is\n        executed once (in the example below on name()) and the\n        other info are cached.\n\n        The cache is cleared when exiting the context manager block.\n        The advice is to use this every time you retrieve more than\n        one information about the process. If you're lucky, you'll\n        get a hell of a speedup.\n\n        >>> import psutil\n        >>> p = psutil.Process()\n        >>> with p.oneshot():\n        ...     p.name()  # collect multiple info\n        ...     p.cpu_times()  # return cached value\n        ...     p.cpu_percent()  # return cached value\n        ...     p.create_time()  # return cached value\n        ...\n        >>>\n        \"\"\"\n        with self._lock:\n            if hasattr(self, \"_cache\"):\n                # NOOP: this covers the use case where the user enters the\n                # context twice:\n                #\n                # >>> with p.oneshot():\n                # ...    with p.oneshot():\n                # ...\n                #\n                # Also, since as_dict() internally uses oneshot()\n                # I expect that the code below will be a pretty common\n                # \"mistake\" that the user will make, so let's guard\n                # against that:\n                #\n                # >>> with p.oneshot():\n                # ...    p.as_dict()\n                # ...\n                yield\n            else:\n                try:\n                    # cached in case cpu_percent() is used\n                    self.cpu_times.cache_activate(self)\n                    # cached in case memory_percent() is used\n                    self.memory_info.cache_activate(self)\n                    # cached in case parent() is used\n                    self.ppid.cache_activate(self)\n                    # cached in case username() is used\n                    if POSIX:\n                        self.uids.cache_activate(self)\n                    # specific implementation cache\n                    self._proc.oneshot_enter()\n                    yield\n                finally:\n                    self.cpu_times.cache_deactivate(self)\n                    self.memory_info.cache_deactivate(self)\n                    self.ppid.cache_deactivate(self)\n                    if POSIX:\n                        self.uids.cache_deactivate(self)\n                    self._proc.oneshot_exit()\n\n    def as_dict(\n        self, attrs: list[str] | None = None, ad_value: Any = None\n    ) -> dict[str, Any]:\n        \"\"\"Utility method returning process information as a\n        hashable dictionary.\n        If *attrs* is specified it must be a list of strings\n        reflecting available Process class' attribute names\n        (e.g. ['cpu_times', 'name']) else all public (read\n        only) attributes are assumed.\n        *ad_value* is the value which gets assigned in case\n        AccessDenied or ZombieProcess exception is raised when\n        retrieving that particular process information.\n        \"\"\"\n        valid_names = _as_dict_attrnames\n        if attrs is not None:\n            if not isinstance(attrs, (list, tuple, set, frozenset)):\n                msg = f\"invalid attrs type {type(attrs)}\"\n                raise TypeError(msg)\n            attrs = set(attrs)\n            invalid_names = attrs - valid_names - _as_dict_attrnames_deprecated\n            if invalid_names:\n                msg = \"invalid attr name{} {}\".format(\n                    \"s\" if len(invalid_names) > 1 else \"\",\n                    \", \".join(map(repr, invalid_names)),\n                )\n                raise ValueError(msg)\n\n        retdict = {}\n        ls = attrs or valid_names\n        with self.oneshot():\n            for name in ls:\n                try:\n                    if name == 'pid':\n                        ret = self.pid\n                    else:\n                        meth = getattr(self, name)\n                        ret = meth()\n                except (AccessDenied, ZombieProcess):\n                    ret = ad_value\n                except NotImplementedError:\n                    # in case of not implemented functionality (may happen\n                    # on old or exotic systems) we want to crash only if\n                    # the user explicitly asked for that particular attr\n                    if attrs:\n                        raise\n                    continue\n                retdict[name] = ret\n        return retdict\n\n    def parent(self) -> Process | None:\n        \"\"\"Return the parent process as a Process object pre-emptively\n        checking whether PID has been reused.\n        If no parent is known return None.\n        \"\"\"\n        lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0]\n        if self.pid == lowest_pid:\n            return None\n        ppid = self.ppid()\n        if ppid is not None:\n            # Get a fresh (non-cached) ctime in case the system clock\n            # was updated. TODO: use a monotonic ctime on platforms\n            # where it's supported.\n            proc_ctime = Process(self.pid).create_time()\n            try:\n                parent = Process(ppid)\n                if parent.create_time() <= proc_ctime:\n                    return parent\n                # ...else ppid has been reused by another process\n            except NoSuchProcess:\n                pass\n\n    def parents(self) -> list[Process]:\n        \"\"\"Return the parents of this process as a list of Process\n        instances. If no parents are known return an empty list.\n        \"\"\"\n        parents = []\n        proc = self.parent()\n        while proc is not None:\n            parents.append(proc)\n            proc = proc.parent()\n        return parents\n\n    def is_running(self) -> bool:\n        \"\"\"Return whether this process is running.\n\n        It also checks if PID has been reused by another process, in\n        which case it will remove the process from `process_iter()`\n        internal cache and return False.\n        \"\"\"\n        if self._gone or self._pid_reused:\n            return False\n        try:\n            # Checking if PID is alive is not enough as the PID might\n            # have been reused by another process. Process identity /\n            # uniqueness over time is guaranteed by (PID + creation\n            # time) and that is verified in __eq__.\n            self._pid_reused = self != Process(self.pid)\n            if self._pid_reused:\n                _pids_reused.add(self.pid)\n                raise NoSuchProcess(self.pid)\n            return True\n        except ZombieProcess:\n            # We should never get here as it's already handled in\n            # Process.__init__; here just for extra safety.\n            return True\n        except NoSuchProcess:\n            self._gone = True\n            return False\n\n    # --- actual API\n\n    @memoize_when_activated\n    def ppid(self) -> int:\n        \"\"\"The process parent PID.\n        On Windows the return value is cached after first call.\n        \"\"\"\n        # On POSIX we don't want to cache the ppid as it may unexpectedly\n        # change to 1 (init) in case this process turns into a zombie:\n        # https://github.com/giampaolo/psutil/issues/321\n        # http://stackoverflow.com/questions/356722/\n\n        # XXX should we check creation time here rather than in\n        # Process.parent()?\n        self._raise_if_pid_reused()\n        if POSIX:\n            return self._proc.ppid()\n        else:  # pragma: no cover\n            self._ppid = self._ppid or self._proc.ppid()\n            return self._ppid\n\n    def name(self) -> str:\n        \"\"\"The process name. The return value is cached after first call.\"\"\"\n        # Process name is only cached on Windows as on POSIX it may\n        # change, see:\n        # https://github.com/giampaolo/psutil/issues/692\n        if WINDOWS and self._name is not None:\n            return self._name\n        name = self._proc.name()\n        if POSIX and len(name) >= 15:\n            # On UNIX the name gets truncated to the first 15 characters.\n            # If it matches the first part of the cmdline we return that\n            # one instead because it's usually more explicative.\n            # Examples are \"gnome-keyring-d\" vs. \"gnome-keyring-daemon\".\n            try:\n                cmdline = self.cmdline()\n            except (AccessDenied, ZombieProcess):\n                # Just pass and return the truncated name: it's better\n                # than nothing. Note: there are actual cases where a\n                # zombie process can return a name() but not a\n                # cmdline(), see:\n                # https://github.com/giampaolo/psutil/issues/2239\n                pass\n            else:\n                if cmdline:\n                    extended_name = os.path.basename(cmdline[0])\n                    if extended_name.startswith(name):\n                        name = extended_name\n        self._name = name\n        self._proc._name = name\n        return name\n\n    def exe(self) -> str:\n        \"\"\"The process executable as an absolute path.\n        May also be an empty string.\n        The return value is cached after first call.\n        \"\"\"\n\n        def guess_it(fallback):\n            # try to guess exe from cmdline[0] in absence of a native\n            # exe representation\n            cmdline = self.cmdline()\n            if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'):\n                exe = cmdline[0]  # the possible exe\n                # Attempt to guess only in case of an absolute path.\n                # It is not safe otherwise as the process might have\n                # changed cwd.\n                if (\n                    os.path.isabs(exe)\n                    and os.path.isfile(exe)\n                    and os.access(exe, os.X_OK)\n                ):\n                    return exe\n            if isinstance(fallback, AccessDenied):\n                raise fallback\n            return fallback\n\n        if self._exe is None:\n            try:\n                exe = self._proc.exe()\n            except AccessDenied as err:\n                return guess_it(fallback=err)\n            else:\n                if not exe:\n                    # underlying implementation can legitimately return an\n                    # empty string; if that's the case we don't want to\n                    # raise AD while guessing from the cmdline\n                    try:\n                        exe = guess_it(fallback=exe)\n                    except AccessDenied:\n                        pass\n                self._exe = exe\n        return self._exe\n\n    def cmdline(self) -> list[str]:\n        \"\"\"The command line this process has been called with.\"\"\"\n        return self._proc.cmdline()\n\n    def status(self) -> ProcessStatus | str:\n        \"\"\"The process current status as a STATUS_* constant.\"\"\"\n        try:\n            return self._proc.status()\n        except ZombieProcess:\n            return ProcessStatus.STATUS_ZOMBIE\n\n    def username(self) -> str:\n        \"\"\"The name of the user that owns the process.\n        On UNIX this is calculated by using *real* process uid.\n        \"\"\"\n        if POSIX:\n            if pwd is None:\n                # might happen if python was installed from sources\n                msg = \"requires pwd module shipped with standard python\"\n                raise ImportError(msg)\n            real_uid = self.uids().real\n            try:\n                return pwd.getpwuid(real_uid).pw_name\n            except KeyError:\n                # the uid can't be resolved by the system\n                return str(real_uid)\n        else:\n            return self._proc.username()\n\n    def create_time(self) -> float:\n        \"\"\"The process creation time as a floating point number\n        expressed in seconds since the epoch (seconds since January 1,\n        1970, at midnight UTC). The return value, which is cached after\n        first call, is based on the system clock, which means it may be\n        affected by changes such as manual adjustments or time\n        synchronization (e.g. NTP).\n        \"\"\"\n        if self._create_time is None:\n            self._create_time = self._proc.create_time()\n        return self._create_time\n\n    def cwd(self) -> str:\n        \"\"\"Process current working directory as an absolute path.\"\"\"\n        return self._proc.cwd()\n\n    def nice(self, value: int | None = None) -> int | None:\n        \"\"\"Get or set process niceness (priority).\"\"\"\n        if value is None:\n            return self._proc.nice_get()\n        else:\n            self._raise_if_pid_reused()\n            self._proc.nice_set(value)\n\n    if POSIX:\n\n        @memoize_when_activated\n        def uids(self) -> puids:\n            \"\"\"Return process UIDs as a (real, effective, saved)\n            named tuple.\n            \"\"\"\n            return self._proc.uids()\n\n        def gids(self) -> pgids:\n            \"\"\"Return process GIDs as a (real, effective, saved)\n            named tuple.\n            \"\"\"\n            return self._proc.gids()\n\n        def terminal(self) -> str | None:\n            \"\"\"The terminal associated with this process, if any,\n            else None.\n            \"\"\"\n            return self._proc.terminal()\n\n        def num_fds(self) -> int:\n            \"\"\"Return the number of file descriptors opened by this\n            process (POSIX only).\n            \"\"\"\n            return self._proc.num_fds()\n\n    # Linux, BSD, AIX and Windows only\n    if hasattr(_psplatform.Process, \"io_counters\"):\n\n        def io_counters(self) -> pio:\n            \"\"\"Return process I/O statistics as a\n            (read_count, write_count, read_bytes, write_bytes)\n            named tuple.\n            Those are the number of read/write calls performed and the\n            amount of bytes read and written by the process.\n            \"\"\"\n            return self._proc.io_counters()\n\n    # Linux and Windows\n    if hasattr(_psplatform.Process, \"ionice_get\"):\n\n        def ionice(\n            self, ioclass: int | None = None, value: int | None = None\n        ) -> pionice | ProcessIOPriority | None:\n            \"\"\"Get or set process I/O niceness (priority).\n\n            On Linux *ioclass* is one of the IOPRIO_CLASS_* constants.\n            *value* is a number which goes from 0 to 7. The higher the\n            value, the lower the I/O priority of the process.\n\n            On Windows only *ioclass* is used and it can be set to 2\n            (normal), 1 (low) or 0 (very low).\n\n            Available on Linux and Windows > Vista only.\n            \"\"\"\n            if ioclass is None:\n                if value is not None:\n                    msg = \"'ioclass' argument must be specified\"\n                    raise ValueError(msg)\n                return self._proc.ionice_get()\n            else:\n                self._raise_if_pid_reused()\n                return self._proc.ionice_set(ioclass, value)\n\n    # Linux / FreeBSD only\n    if hasattr(_psplatform.Process, \"rlimit\"):\n\n        def rlimit(\n            self,\n            resource: int,\n            limits: tuple[int, int] | None = None,\n        ) -> tuple[int, int] | None:\n            \"\"\"Get or set process resource limits as a (soft, hard)\n            tuple.\n\n            *resource* is one of the RLIMIT_* constants.\n            *limits* is supposed to be a (soft, hard) tuple.\n\n            See \"man prlimit\" for further info.\n            Available on Linux and FreeBSD only.\n            \"\"\"\n            if limits is not None:\n                self._raise_if_pid_reused()\n            return self._proc.rlimit(resource, limits)\n\n    # Windows, Linux and FreeBSD only\n    if hasattr(_psplatform.Process, \"cpu_affinity_get\"):\n\n        def cpu_affinity(\n            self, cpus: list[int] | None = None\n        ) -> list[int] | None:\n            \"\"\"Get or set process CPU affinity.\n            If specified, *cpus* must be a list of CPUs for which you\n            want to set the affinity (e.g. [0, 1]).\n            If an empty list is passed, all egible CPUs are assumed\n            (and set).\n            (Windows, Linux and BSD only).\n            \"\"\"\n            if cpus is None:\n                return sorted(set(self._proc.cpu_affinity_get()))\n            else:\n                self._raise_if_pid_reused()\n                if not cpus:\n                    if hasattr(self._proc, \"_get_eligible_cpus\"):\n                        cpus = self._proc._get_eligible_cpus()\n                    else:\n                        cpus = tuple(range(len(cpu_times(percpu=True))))\n                self._proc.cpu_affinity_set(list(set(cpus)))\n\n    # Linux, FreeBSD, SunOS\n    if hasattr(_psplatform.Process, \"cpu_num\"):\n\n        def cpu_num(self) -> int:\n            \"\"\"Return what CPU this process is currently running on.\n            The returned number should be <= psutil.cpu_count()\n            and <= len(psutil.cpu_percent(percpu=True)).\n            It may be used in conjunction with\n            psutil.cpu_percent(percpu=True) to observe the system\n            workload distributed across CPUs.\n            \"\"\"\n            return self._proc.cpu_num()\n\n    # All platforms has it, but maybe not in the future.\n    if hasattr(_psplatform.Process, \"environ\"):\n\n        def environ(self) -> dict[str, str]:\n            \"\"\"The environment variables of the process as a dict.  Note: this\n            might not reflect changes made after the process started.\n            \"\"\"\n            return self._proc.environ()\n\n    if WINDOWS:\n\n        def num_handles(self) -> int:\n            \"\"\"Return the number of handles opened by this process\n            (Windows only).\n            \"\"\"\n            return self._proc.num_handles()\n\n    def num_ctx_switches(self) -> pctxsw:\n        \"\"\"Return the number of voluntary and involuntary context\n        switches performed by this process.\n        \"\"\"\n        return self._proc.num_ctx_switches()\n\n    def num_threads(self) -> int:\n        \"\"\"Return the number of threads used by this process.\"\"\"\n        return self._proc.num_threads()\n\n    if hasattr(_psplatform.Process, \"threads\"):\n\n        def threads(self) -> list[pthread]:\n            \"\"\"Return threads opened by process as a list of\n            (id, user_time, system_time) named tuples representing\n            thread id and thread CPU times (user/system).\n            On OpenBSD this method requires root access.\n            \"\"\"\n            return self._proc.threads()\n\n    def children(self, recursive: bool = False) -> list[Process]:\n        \"\"\"Return the children of this process as a list of Process\n        instances, pre-emptively checking whether PID has been reused.\n        If *recursive* is True return all the parent descendants.\n\n        Example (A == this process):\n\n         A ─┐\n            │\n            ├─ B (child) ─┐\n            │             └─ X (grandchild) ─┐\n            │                                └─ Y (great grandchild)\n            ├─ C (child)\n            └─ D (child)\n\n        >>> import psutil\n        >>> p = psutil.Process()\n        >>> p.children()\n        B, C, D\n        >>> p.children(recursive=True)\n        B, X, Y, C, D\n\n        Note that in the example above if process X disappears\n        process Y won't be listed as the reference to process A\n        is lost.\n        \"\"\"\n        self._raise_if_pid_reused()\n        ppid_map = _ppid_map()\n        # Get a fresh (non-cached) ctime in case the system clock was\n        # updated. TODO: use a monotonic ctime on platforms where it's\n        # supported.\n        proc_ctime = Process(self.pid).create_time()\n        ret = []\n        if not recursive:\n            for pid, ppid in ppid_map.items():\n                if ppid == self.pid:\n                    try:\n                        child = Process(pid)\n                        # if child happens to be older than its parent\n                        # (self) it means child's PID has been reused\n                        if proc_ctime <= child.create_time():\n                            ret.append(child)\n                    except (NoSuchProcess, ZombieProcess):\n                        pass\n        else:\n            # Construct a {pid: [child pids]} dict\n            reverse_ppid_map = collections.defaultdict(list)\n            for pid, ppid in ppid_map.items():\n                reverse_ppid_map[ppid].append(pid)\n            # Recursively traverse that dict, starting from self.pid,\n            # such that we only call Process() on actual children\n            seen = set()\n            stack = [self.pid]\n            while stack:\n                pid = stack.pop()\n                if pid in seen:\n                    # Since pids can be reused while the ppid_map is\n                    # constructed, there may be rare instances where\n                    # there's a cycle in the recorded process \"tree\".\n                    continue\n                seen.add(pid)\n                for child_pid in reverse_ppid_map[pid]:\n                    try:\n                        child = Process(child_pid)\n                        # if child happens to be older than its parent\n                        # (self) it means child's PID has been reused\n                        intime = proc_ctime <= child.create_time()\n                        if intime:\n                            ret.append(child)\n                            stack.append(child_pid)\n                    except (NoSuchProcess, ZombieProcess):\n                        pass\n        return ret\n\n    def cpu_percent(self, interval: float | None = None) -> float:\n        \"\"\"Return a float representing the current process CPU\n        utilization as a percentage.\n\n        When *interval* is 0.0 or None (default) compares process times\n        to system CPU times elapsed since last call, returning\n        immediately (non-blocking). That means that the first time\n        this is called it will return a meaningful 0.0 value.\n\n        When *interval* is > 0.0 compares process times to system CPU\n        times elapsed before and after the interval (blocking).\n\n        In this case is recommended for accuracy that this function\n        be called with at least 0.1 seconds between calls.\n\n        A value > 100.0 can be returned in case of processes running\n        multiple threads on different CPU cores.\n\n        The returned value is explicitly NOT split evenly between\n        all available logical CPUs. This means that a busy loop process\n        running on a system with 2 logical CPUs will be reported as\n        having 100% CPU utilization instead of 50%.\n\n        Examples:\n\n          >>> import psutil\n          >>> p = psutil.Process(os.getpid())\n          >>> # blocking\n          >>> p.cpu_percent(interval=1)\n          2.0\n          >>> # non-blocking (percentage since last call)\n          >>> p.cpu_percent(interval=None)\n          2.9\n          >>>\n        \"\"\"\n        blocking = interval is not None and interval > 0.0\n        if interval is not None and interval < 0:\n            msg = f\"interval is not positive (got {interval!r})\"\n            raise ValueError(msg)\n        num_cpus = cpu_count() or 1\n\n        def timer():\n            return _timer() * num_cpus\n\n        if blocking:\n            st1 = timer()\n            pt1 = self._proc.cpu_times()\n            time.sleep(interval)\n            st2 = timer()\n            pt2 = self._proc.cpu_times()\n        else:\n            st1 = self._last_sys_cpu_times\n            pt1 = self._last_proc_cpu_times\n            st2 = timer()\n            pt2 = self._proc.cpu_times()\n            if st1 is None or pt1 is None:\n                self._last_sys_cpu_times = st2\n                self._last_proc_cpu_times = pt2\n                return 0.0\n\n        delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system)\n        delta_time = st2 - st1\n        # reset values for next call in case of interval == None\n        self._last_sys_cpu_times = st2\n        self._last_proc_cpu_times = pt2\n\n        try:\n            # This is the utilization split evenly between all CPUs.\n            # E.g. a busy loop process on a 2-CPU-cores system at this\n            # point is reported as 50% instead of 100%.\n            overall_cpus_percent = (delta_proc / delta_time) * 100\n        except ZeroDivisionError:\n            # interval was too low\n            return 0.0\n        else:\n            # Note 1:\n            # in order to emulate \"top\" we multiply the value for the num\n            # of CPU cores. This way the busy process will be reported as\n            # having 100% (or more) usage.\n            #\n            # Note 2:\n            # taskmgr.exe on Windows differs in that it will show 50%\n            # instead.\n            #\n            # Note 3:\n            # a percentage > 100 is legitimate as it can result from a\n            # process with multiple threads running on different CPU\n            # cores (top does the same), see:\n            # http://stackoverflow.com/questions/1032357\n            # https://github.com/giampaolo/psutil/issues/474\n            single_cpu_percent = overall_cpus_percent * num_cpus\n            return round(single_cpu_percent, 1)\n\n    @memoize_when_activated\n    def cpu_times(self) -> pcputimes:\n        \"\"\"Return a (user, system, children_user, children_system)\n        named tuple representing the accumulated process time, in\n        seconds.\n        This is similar to os.times() but per-process.\n        On macOS and Windows children_user and children_system are\n        always set to 0.\n        \"\"\"\n        return self._proc.cpu_times()\n\n    @memoize_when_activated\n    def memory_info(self) -> pmem:\n        \"\"\"Return a named tuple with variable fields depending on the\n        platform, representing memory information about the process.\n\n        The \"portable\" fields available on all platforms are `rss` and `vms`.\n\n        All numbers are expressed in bytes.\n        \"\"\"\n        return self._proc.memory_info()\n\n    @memoize_when_activated\n    def memory_info_ex(self) -> pmem_ex:\n        \"\"\"Return a named tuple extending memory_info() with extra\n        metrics.\n\n        All numbers are expressed in bytes.\n        \"\"\"\n        base = self.memory_info()\n        if hasattr(self._proc, \"memory_info_ex\"):\n            extras = self._proc.memory_info_ex()\n            return _ntp.pmem_ex(**base._asdict(), **extras)\n        return base\n\n    # Linux, macOS, Windows\n    if hasattr(_psplatform.Process, \"memory_footprint\"):\n\n        def memory_footprint(self) -> pfootprint:\n            \"\"\"Return a named tuple with USS, PSS and swap memory\n            metrics. These provide a better representation of\n            actual process memory usage.\n\n            USS is the memory unique to a process and which would\n            be freed if the process was terminated right now.\n\n            It does so by passing through the whole process address. As\n            such it usually requires higher user privileges than\n            memory_info() or memory_info_ex() and is considerably\n            slower.\n            \"\"\"\n            return self._proc.memory_footprint()\n\n    # DEPRECATED\n    def memory_full_info(self) -> pfullmem:\n        \"\"\"Return the same information as memory_info() plus\n        memory_footprint() in a single named tuple.\n\n        DEPRECATED in 8.0.0. Use memory_footprint() instead.\n        \"\"\"\n        msg = (\n            \"memory_full_info() is deprecated; use memory_footprint() instead\"\n        )\n        warnings.warn(msg, DeprecationWarning, stacklevel=2)\n        basic_mem = self.memory_info()\n        if hasattr(self, \"memory_footprint\"):\n            fp = self.memory_footprint()\n            return _ntp.pfullmem(*basic_mem + fp)\n        return _ntp.pfullmem(*basic_mem)\n\n    def memory_percent(self, memtype: str = \"rss\") -> float:\n        \"\"\"Compare process memory to total physical system memory and\n        calculate process memory utilization as a percentage.\n        *memtype* argument is a string that dictates what type of\n        process memory you want to compare against (defaults to \"rss\").\n        The list of available strings can be obtained like this:\n\n        >>> psutil.Process().memory_info()._fields\n        ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')\n        \"\"\"\n        valid_types = list(_ntp.pmem._fields)\n        if hasattr(_ntp, \"pmem_ex\"):\n            valid_types += [\n                f for f in _ntp.pmem_ex._fields if f not in valid_types\n            ]\n        if hasattr(_ntp, \"pfootprint\"):\n            valid_types += [\n                f for f in _ntp.pfootprint._fields if f not in valid_types\n            ]\n        if memtype not in valid_types:\n            msg = (\n                f\"invalid memtype {memtype!r}; valid types are\"\n                f\" {tuple(valid_types)!r}\"\n            )\n            raise ValueError(msg)\n        if memtype in _ntp.pmem._fields:\n            fun = self.memory_info\n        elif (\n            hasattr(_ntp, \"pfootprint\") and memtype in _ntp.pfootprint._fields\n        ):\n            fun = self.memory_footprint\n        else:\n            fun = self.memory_info_ex\n        metrics = fun()\n        value = getattr(metrics, memtype)\n\n        # use cached value if available\n        total_phymem = _TOTAL_PHYMEM or virtual_memory().total\n        if not total_phymem > 0:\n            # we should never get here\n            msg = (\n                \"can't calculate process memory percent because total physical\"\n                f\" system memory is not positive ({total_phymem!r})\"\n            )\n            raise ValueError(msg)\n        return (value / float(total_phymem)) * 100\n\n    if hasattr(_psplatform.Process, \"memory_maps\"):\n\n        def memory_maps(\n            self, grouped: bool = True\n        ) -> list[pmmap_grouped] | list[pmmap_ext]:\n            \"\"\"Return process' mapped memory regions as a list of named tuples\n            whose fields are variable depending on the platform.\n\n            If *grouped* is True the mapped regions with the same 'path'\n            are grouped together and the different memory fields are summed.\n\n            If *grouped* is False every mapped region is shown as a single\n            entity and the named tuple will also include the mapped region's\n            address space ('addr') and permission set ('perms').\n            \"\"\"\n            it = self._proc.memory_maps()\n            if grouped:\n                d = {}\n                for tupl in it:\n                    path = tupl[2]\n                    nums = tupl[3:]\n                    try:\n                        d[path] = list(map(lambda x, y: x + y, d[path], nums))\n                    except KeyError:\n                        d[path] = nums\n                return [_ntp.pmmap_grouped(path, *d[path]) for path in d]\n            else:\n                return [_ntp.pmmap_ext(*x) for x in it]\n\n    def page_faults(self) -> ppagefaults:\n        \"\"\"Return the number of page faults for this process as a\n        (minor, major) named tuple.\n\n        - *minor* (a.k.a. *soft* faults): occur when a memory page is\n          not currently mapped into the process address space, but is\n          already present in physical RAM (e.g. a shared library page\n          loaded by another process). The kernel resolves these without\n          disk I/O.\n\n        - *major* (a.k.a. *hard* faults): occur when the page must be\n          fetched from disk. These are expensive because they stall the\n          process until I/O completes.\n\n        Both counters are cumulative since process creation.\n        \"\"\"\n        return self._proc.page_faults()\n\n    def open_files(self) -> list[popenfile]:\n        \"\"\"Return files opened by process as a list of\n        (path, fd) named tuples including the absolute file name\n        and file descriptor number.\n        \"\"\"\n        return self._proc.open_files()\n\n    def net_connections(self, kind: str = \"inet\") -> list[pconn]:\n        \"\"\"Return socket connections opened by process as a list of\n        (fd, family, type, laddr, raddr, status) named tuples.\n        The *kind* parameter filters for connections that match the\n        following criteria:\n\n        +------------+----------------------------------------------------+\n        | Kind Value | Connections using                                  |\n        +------------+----------------------------------------------------+\n        | inet       | IPv4 and IPv6                                      |\n        | inet4      | IPv4                                               |\n        | inet6      | IPv6                                               |\n        | tcp        | TCP                                                |\n        | tcp4       | TCP over IPv4                                      |\n        | tcp6       | TCP over IPv6                                      |\n        | udp        | UDP                                                |\n        | udp4       | UDP over IPv4                                      |\n        | udp6       | UDP over IPv6                                      |\n        | unix       | UNIX socket (both UDP and TCP protocols)           |\n        | all        | the sum of all the possible families and protocols |\n        +------------+----------------------------------------------------+\n        \"\"\"\n        _check_conn_kind(kind)\n        return self._proc.net_connections(kind)\n\n    @_common.deprecated_method(replacement=\"net_connections\")\n    def connections(self, kind=\"inet\") -> list[pconn]:\n        return self.net_connections(kind=kind)\n\n    # --- signals\n\n    if POSIX:\n\n        def _send_signal(self, sig):\n            assert not self.pid < 0, self.pid\n            self._raise_if_pid_reused()\n\n            pid, ppid, name = self.pid, self._ppid, self._name\n            if pid == 0:\n                # see \"man 2 kill\"\n                msg = (\n                    \"preventing sending signal to process with PID 0 as it \"\n                    \"would affect every process in the process group of the \"\n                    \"calling process (os.getpid()) instead of PID 0\"\n                )\n                raise ValueError(msg)\n            try:\n                os.kill(pid, sig)\n            except ProcessLookupError as err:\n                if OPENBSD and pid_exists(pid):\n                    # We do this because os.kill() lies in case of\n                    # zombie processes.\n                    raise ZombieProcess(pid, name, ppid) from err\n                self._gone = True\n                raise NoSuchProcess(pid, name) from err\n            except PermissionError as err:\n                raise AccessDenied(pid, name) from err\n\n    def send_signal(self, sig: int) -> None:\n        \"\"\"Send a signal *sig* to process pre-emptively checking\n        whether PID has been reused (see signal module constants) .\n        On Windows only SIGTERM is valid and is treated as an alias\n        for kill().\n        \"\"\"\n        if POSIX:\n            self._send_signal(sig)\n        else:  # pragma: no cover\n            self._raise_if_pid_reused()\n            if sig != signal.SIGTERM and not self.is_running():\n                msg = \"process no longer exists\"\n                raise NoSuchProcess(self.pid, self._name, msg=msg)\n            self._proc.send_signal(sig)\n\n    def suspend(self) -> None:\n        \"\"\"Suspend process execution with SIGSTOP pre-emptively checking\n        whether PID has been reused.\n        On Windows this has the effect of suspending all process threads.\n        \"\"\"\n        if POSIX:\n            self._send_signal(signal.SIGSTOP)\n        else:  # pragma: no cover\n            self._raise_if_pid_reused()\n            self._proc.suspend()\n\n    def resume(self) -> None:\n        \"\"\"Resume process execution with SIGCONT pre-emptively checking\n        whether PID has been reused.\n        On Windows this has the effect of resuming all process threads.\n        \"\"\"\n        if POSIX:\n            self._send_signal(signal.SIGCONT)\n        else:  # pragma: no cover\n            self._raise_if_pid_reused()\n            self._proc.resume()\n\n    def terminate(self) -> None:\n        \"\"\"Terminate the process with SIGTERM pre-emptively checking\n        whether PID has been reused.\n        On Windows this is an alias for kill().\n        \"\"\"\n        if POSIX:\n            self._send_signal(signal.SIGTERM)\n        else:  # pragma: no cover\n            self._raise_if_pid_reused()\n            self._proc.kill()\n\n    def kill(self) -> None:\n        \"\"\"Kill the current process with SIGKILL pre-emptively checking\n        whether PID has been reused.\n        \"\"\"\n        if POSIX:\n            self._send_signal(signal.SIGKILL)\n        else:  # pragma: no cover\n            self._raise_if_pid_reused()\n            self._proc.kill()\n\n    def wait(self, timeout: float | None = None) -> int | None:\n        \"\"\"Wait for process to terminate, and if process is a children\n        of os.getpid(), also return its exit code, else None.\n        On Windows there's no such limitation (exit code is always\n        returned).\n\n        If the process is already terminated, immediately return None\n        instead of raising NoSuchProcess.\n\n        If *timeout* (in seconds) is specified and process is still\n        alive, raise TimeoutExpired.\n\n        If *timeout=0* either return immediately or raise\n        TimeoutExpired (non-blocking).\n\n        To wait for multiple Process objects use psutil.wait_procs().\n        \"\"\"\n        if self.pid == 0:\n            msg = \"can't wait for PID 0\"\n            raise ValueError(msg)\n        if timeout is not None:\n            if not isinstance(timeout, (int, float)):\n                msg = f\"timeout must be an int or float (got {type(timeout)})\"\n                raise TypeError(msg)\n            if timeout < 0:\n                msg = f\"timeout must be positive or zero (got {timeout})\"\n                raise ValueError(msg)\n\n        if self._exitcode is not _SENTINEL:\n            return self._exitcode\n\n        try:\n            self._exitcode = self._proc.wait(timeout)\n        except TimeoutExpired as err:\n            exc = TimeoutExpired(timeout, pid=self.pid, name=self._name)\n            raise exc from err\n\n        return self._exitcode\n\n\n# The valid attr names which can be processed by Process.as_dict().\n# fmt: off\n_as_dict_attrnames = {\n    x for x in dir(Process) if not x.startswith(\"_\") and x not in\n     {'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',\n      'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit',\n      'connections', 'memory_full_info', 'oneshot'}\n}\n# fmt: on\n\n# Deprecated attrs: not returned by default but still accepted if\n# explicitly requested via as_dict(attrs=[...]).\n_as_dict_attrnames_deprecated = {'memory_full_info'}\n\n\n# =====================================================================\n# --- Popen class\n# =====================================================================\n\n\nclass Popen(Process):\n    \"\"\"Same as subprocess.Popen, but in addition it provides all\n    psutil.Process methods in a single class.\n    For the following methods which are common to both classes, psutil\n    implementation takes precedence:\n\n    * send_signal()\n    * terminate()\n    * kill()\n\n    This is done in order to avoid killing another process in case its\n    PID has been reused, fixing BPO-6973.\n\n      >>> import psutil\n      >>> from subprocess import PIPE\n      >>> p = psutil.Popen([\"python\", \"-c\", \"print 'hi'\"], stdout=PIPE)\n      >>> p.name()\n      'python'\n      >>> p.uids()\n      user(real=1000, effective=1000, saved=1000)\n      >>> p.username()\n      'giampaolo'\n      >>> p.communicate()\n      ('hi', None)\n      >>> p.terminate()\n      >>> p.wait(timeout=2)\n      0\n      >>>\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        # Explicitly avoid to raise NoSuchProcess in case the process\n        # spawned by subprocess.Popen terminates too quickly, see:\n        # https://github.com/giampaolo/psutil/issues/193\n        self.__subproc = subprocess.Popen(*args, **kwargs)\n        self._init(self.__subproc.pid, _ignore_nsp=True)\n\n    def __dir__(self):\n        return sorted(set(dir(Popen) + dir(subprocess.Popen)))\n\n    def __enter__(self) -> Popen:\n        if hasattr(self.__subproc, '__enter__'):\n            self.__subproc.__enter__()\n        return self\n\n    def __exit__(self, *args, **kwargs):\n        if hasattr(self.__subproc, '__exit__'):\n            return self.__subproc.__exit__(*args, **kwargs)\n        else:\n            if self.stdout:\n                self.stdout.close()\n            if self.stderr:\n                self.stderr.close()\n            try:\n                # Flushing a BufferedWriter may raise an error.\n                if self.stdin:\n                    self.stdin.close()\n            finally:\n                # Wait for the process to terminate, to avoid zombies.\n                self.wait()\n\n    def __getattribute__(self, name):\n        try:\n            return object.__getattribute__(self, name)\n        except AttributeError:\n            try:\n                return object.__getattribute__(self.__subproc, name)\n            except AttributeError:\n                msg = f\"{self.__class__!r} has no attribute {name!r}\"\n                raise AttributeError(msg) from None\n\n    def wait(self, timeout: float | None = None) -> int | None:\n        if self.__subproc.returncode is not None:\n            return self.__subproc.returncode\n        ret = super().wait(timeout)\n        self.__subproc.returncode = ret\n        return ret\n\n\n# =====================================================================\n# --- system processes related functions\n# =====================================================================\n\n\ndef pids() -> list[int]:\n    \"\"\"Return a list of current running PIDs.\"\"\"\n    global _LOWEST_PID\n    ret = sorted(_psplatform.pids())\n    _LOWEST_PID = ret[0]\n    return ret\n\n\ndef pid_exists(pid: int) -> bool:\n    \"\"\"Return True if given PID exists in the current process list.\n    This is faster than doing \"pid in psutil.pids()\" and\n    should be preferred.\n    \"\"\"\n    if pid < 0:\n        return False\n    elif pid == 0 and POSIX:\n        # On POSIX we use os.kill() to determine PID existence.\n        # According to \"man 2 kill\" PID 0 has a special meaning\n        # though: it refers to <<every process in the process\n        # group of the calling process>> and that is not we want\n        # to do here.\n        return pid in pids()\n    else:\n        return _psplatform.pid_exists(pid)\n\n\n_pmap = {}\n_pids_reused = set()\n\n\ndef process_iter(\n    attrs: list[str] | None = None, ad_value: Any = None\n) -> Iterator[Process]:\n    \"\"\"Return a generator yielding a Process instance for all\n    running processes.\n\n    Every new Process instance is only created once and then cached\n    into an internal table which is updated every time this is used.\n    Cache can optionally be cleared via `process_iter.cache_clear()`.\n\n    The sorting order in which processes are yielded is based on\n    their PIDs.\n\n    *attrs* and *ad_value* have the same meaning as in\n    Process.as_dict(). If *attrs* is specified as_dict() is called\n    and the resulting dict is stored as a 'info' attribute attached\n    to returned Process instance.\n    If *attrs* is an empty list it will retrieve all process info\n    (slow).\n    \"\"\"\n    global _pmap\n\n    def add(pid):\n        proc = Process(pid)\n        pmap[proc.pid] = proc\n        return proc\n\n    def remove(pid):\n        pmap.pop(pid, None)\n\n    pmap = _pmap.copy()\n    a = set(pids())\n    b = set(pmap)\n    new_pids = a - b\n    gone_pids = b - a\n    for pid in gone_pids:\n        remove(pid)\n    while _pids_reused:\n        pid = _pids_reused.pop()\n        debug(f\"refreshing Process instance for reused PID {pid}\")\n        remove(pid)\n    try:\n        ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items()))\n        for pid, proc in ls:\n            try:\n                if proc is None:  # new process\n                    proc = add(pid)\n                if attrs is not None:\n                    proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value)\n                yield proc\n            except NoSuchProcess:\n                remove(pid)\n    finally:\n        _pmap = pmap\n\n\nprocess_iter.cache_clear = lambda: _pmap.clear()  # noqa: PLW0108\nprocess_iter.cache_clear.__doc__ = \"Clear process_iter() internal cache.\"\n\n\ndef wait_procs(\n    procs: list[Process],\n    timeout: float | None = None,\n    callback: Callable[[Process], None] | None = None,\n) -> tuple[list[Process], list[Process]]:\n    \"\"\"Convenience function which waits for a list of processes to\n    terminate.\n\n    Return a (gone, alive) tuple indicating which processes\n    are gone and which ones are still alive.\n\n    The gone ones will have a new *returncode* attribute indicating\n    process exit status (may be None).\n\n    *callback* is a function which gets called every time a process\n    terminates (a Process instance is passed as callback argument).\n\n    Function will return as soon as all processes terminate or when\n    *timeout* occurs.\n    Differently from Process.wait() it will not raise TimeoutExpired if\n    *timeout* occurs.\n\n    Typical use case is:\n\n     - send SIGTERM to a list of processes\n     - give them some time to terminate\n     - send SIGKILL to those ones which are still alive\n\n    Example:\n\n    >>> def on_terminate(proc):\n    ...     print(\"process {} terminated\".format(proc))\n    ...\n    >>> for p in procs:\n    ...    p.terminate()\n    ...\n    >>> gone, alive = wait_procs(procs, timeout=3, callback=on_terminate)\n    >>> for p in alive:\n    ...     p.kill()\n    \"\"\"\n\n    def check_gone(proc, timeout):\n        try:\n            returncode = proc.wait(timeout=timeout)\n        except (TimeoutExpired, subprocess.TimeoutExpired):\n            pass\n        else:\n            if returncode is not None or not proc.is_running():\n                # Set new Process instance attribute.\n                proc.returncode = returncode\n                gone.add(proc)\n                if callback is not None:\n                    callback(proc)\n\n    if timeout is not None and not timeout >= 0:\n        msg = f\"timeout must be a positive integer, got {timeout}\"\n        raise ValueError(msg)\n    if callback is not None and not callable(callback):\n        msg = f\"callback {callback!r} is not a callable\"\n        raise TypeError(msg)\n\n    gone = set()\n    alive = set(procs)\n    if timeout is not None:\n        deadline = _timer() + timeout\n\n    while alive:\n        if timeout is not None and timeout <= 0:\n            break\n        for proc in alive:\n            # Make sure that every complete iteration (all processes)\n            # will last max 1 sec.\n            # We do this because we don't want to wait too long on a\n            # single process: in case it terminates too late other\n            # processes may disappear in the meantime and their PID\n            # reused.\n            max_timeout = 1.0 / len(alive)\n            if timeout is not None:\n                timeout = min((deadline - _timer()), max_timeout)\n                if timeout <= 0:\n                    break\n                check_gone(proc, timeout)\n            else:\n                check_gone(proc, max_timeout)\n        alive = alive - gone  # noqa: PLR6104\n\n    if alive:\n        # Last attempt over processes survived so far.\n        # timeout == 0 won't make this function wait any further.\n        for proc in alive:\n            check_gone(proc, 0)\n        alive = alive - gone  # noqa: PLR6104\n\n    return (list(gone), list(alive))\n\n\n# =====================================================================\n# --- CPU related functions\n# =====================================================================\n\n\ndef cpu_count(logical: bool = True) -> int | None:\n    \"\"\"Return the number of logical CPUs in the system (same as\n    os.cpu_count()).\n\n    If *logical* is False return the number of physical cores only\n    (e.g. hyper thread CPUs are excluded).\n\n    Return None if undetermined.\n\n    The return value is cached after first call.\n    If desired cache can be cleared like this:\n\n    >>> psutil.cpu_count.cache_clear()\n    \"\"\"\n    if logical:\n        ret = _psplatform.cpu_count_logical()\n    else:\n        ret = _psplatform.cpu_count_cores()\n    if ret is not None and ret < 1:\n        ret = None\n    return ret\n\n\ndef cpu_times(percpu: bool = False) -> scputimes | list[scputimes]:\n    \"\"\"Return system-wide CPU times as a named tuple.\n    Every CPU time represents the seconds the CPU has spent in the\n    given mode. The named tuple's fields availability varies depending on the\n    platform:\n\n     - user\n     - system\n     - idle\n     - nice (UNIX)\n     - iowait (Linux)\n     - irq (Linux, FreeBSD)\n     - softirq (Linux)\n     - steal (Linux)\n     - guest (Linux)\n     - guest_nice (Linux)\n\n    When *percpu* is True return a list of named tuples for each CPU.\n    First element of the list refers to first CPU, second element\n    to second CPU and so on.\n    The order of the list is consistent across calls.\n    \"\"\"\n    if not percpu:\n        return _psplatform.cpu_times()\n    else:\n        return _psplatform.per_cpu_times()\n\n\ntry:\n    _last_cpu_times = {threading.current_thread().ident: cpu_times()}\nexcept Exception:  # noqa: BLE001\n    # Don't want to crash at import time.\n    _last_cpu_times = {}\n\ntry:\n    _last_per_cpu_times = {\n        threading.current_thread().ident: cpu_times(percpu=True)\n    }\nexcept Exception:  # noqa: BLE001\n    # Don't want to crash at import time.\n    _last_per_cpu_times = {}\n\n\ndef _cpu_tot_time(times):\n    \"\"\"Given a cpu_time() ntuple calculates the total CPU time\n    (including idle time).\n    \"\"\"\n    tot = sum(times)\n    if LINUX:\n        # On Linux guest times are already accounted in \"user\" or\n        # \"nice\" times, so we subtract them from total.\n        # Htop does the same. References:\n        # https://github.com/giampaolo/psutil/pull/940\n        # http://unix.stackexchange.com/questions/178045\n        # https://github.com/torvalds/linux/blob/447976ef4/kernel/sched/cputime.c#L158\n        tot -= times.guest\n        tot -= times.guest_nice\n    return tot\n\n\ndef _cpu_busy_time(times):\n    \"\"\"Given a cpu_time() ntuple calculates the busy CPU time.\n    We do so by subtracting all idle CPU times.\n    \"\"\"\n    busy = _cpu_tot_time(times)\n    busy -= times.idle\n    # Linux: \"iowait\" is time during which the CPU does not do anything\n    # (waits for IO to complete). On Linux IO wait is *not* accounted\n    # in \"idle\" time so we subtract it. Htop does the same.\n    # References:\n    # https://github.com/torvalds/linux/blob/447976ef4/kernel/sched/cputime.c#L244\n    busy -= getattr(times, \"iowait\", 0)\n    return busy\n\n\ndef _cpu_times_deltas(t1, t2):\n    assert t1._fields == t2._fields, (t1, t2)\n    field_deltas = []\n    for field in _ntp.scputimes._fields:\n        field_delta = getattr(t2, field) - getattr(t1, field)\n        # CPU times are always supposed to increase over time\n        # or at least remain the same and that's because time\n        # cannot go backwards.\n        # Surprisingly sometimes this might not be the case (at\n        # least on Windows and Linux), see:\n        # https://github.com/giampaolo/psutil/issues/392\n        # https://github.com/giampaolo/psutil/issues/645\n        # https://github.com/giampaolo/psutil/issues/1210\n        # Trim negative deltas to zero to ignore decreasing fields.\n        # top does the same. Reference:\n        # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063\n        field_delta = max(0, field_delta)\n        field_deltas.append(field_delta)\n    return _ntp.scputimes(*field_deltas)\n\n\ndef cpu_percent(\n    interval: float | None = None, percpu: bool = False\n) -> float | list[float]:\n    \"\"\"Return a float representing the current system-wide CPU\n    utilization as a percentage.\n\n    When *interval* is > 0.0 compares system CPU times elapsed before\n    and after the interval (blocking).\n\n    When *interval* is 0.0 or None compares system CPU times elapsed\n    since last call or module import, returning immediately (non\n    blocking). That means the first time this is called it will\n    return a meaningless 0.0 value which you should ignore.\n    In this case is recommended for accuracy that this function be\n    called with at least 0.1 seconds between calls.\n\n    When *percpu* is True returns a list of floats representing the\n    utilization as a percentage for each CPU.\n    First element of the list refers to first CPU, second element\n    to second CPU and so on.\n    The order of the list is consistent across calls.\n\n    Examples:\n\n      >>> # blocking, system-wide\n      >>> psutil.cpu_percent(interval=1)\n      2.0\n      >>>\n      >>> # blocking, per-cpu\n      >>> psutil.cpu_percent(interval=1, percpu=True)\n      [2.0, 1.0]\n      >>>\n      >>> # non-blocking (percentage since last call)\n      >>> psutil.cpu_percent(interval=None)\n      2.9\n      >>>\n    \"\"\"\n    tid = threading.current_thread().ident\n    blocking = interval is not None and interval > 0.0\n    if interval is not None and interval < 0:\n        msg = f\"interval is not positive (got {interval})\"\n        raise ValueError(msg)\n\n    def calculate(t1, t2):\n        times_delta = _cpu_times_deltas(t1, t2)\n        all_delta = _cpu_tot_time(times_delta)\n        busy_delta = _cpu_busy_time(times_delta)\n\n        try:\n            busy_perc = (busy_delta / all_delta) * 100\n        except ZeroDivisionError:\n            return 0.0\n        else:\n            return round(busy_perc, 1)\n\n    # system-wide usage\n    if not percpu:\n        if blocking:\n            t1 = cpu_times()\n            time.sleep(interval)\n        else:\n            t1 = _last_cpu_times.get(tid) or cpu_times()\n        _last_cpu_times[tid] = cpu_times()\n        return calculate(t1, _last_cpu_times[tid])\n    # per-cpu usage\n    else:\n        ret = []\n        if blocking:\n            tot1 = cpu_times(percpu=True)\n            time.sleep(interval)\n        else:\n            tot1 = _last_per_cpu_times.get(tid) or cpu_times(percpu=True)\n        _last_per_cpu_times[tid] = cpu_times(percpu=True)\n        for t1, t2 in zip(tot1, _last_per_cpu_times[tid]):\n            ret.append(calculate(t1, t2))\n        return ret\n\n\n# Use a separate dict for cpu_times_percent(), so it's independent from\n# cpu_percent() and they can both be used within the same program.\n_last_cpu_times_2 = _last_cpu_times.copy()\n_last_per_cpu_times_2 = _last_per_cpu_times.copy()\n\n\ndef cpu_times_percent(\n    interval: float | None = None, percpu: bool = False\n) -> scputimes | list[scputimes]:\n    \"\"\"Same as cpu_percent() but provides utilization percentages\n    for each specific CPU time as is returned by cpu_times().\n    For instance, on Linux we'll get:\n\n      >>> cpu_times_percent()\n      cpupercent(user=4.8, nice=0.0, system=4.8, idle=90.5, iowait=0.0,\n                 irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)\n      >>>\n\n    *interval* and *percpu* arguments have the same meaning as in\n    cpu_percent().\n    \"\"\"\n    tid = threading.current_thread().ident\n    blocking = interval is not None and interval > 0.0\n    if interval is not None and interval < 0:\n        msg = f\"interval is not positive (got {interval!r})\"\n        raise ValueError(msg)\n\n    def calculate(t1, t2):\n        nums = []\n        times_delta = _cpu_times_deltas(t1, t2)\n        all_delta = _cpu_tot_time(times_delta)\n        # \"scale\" is the value to multiply each delta with to get percentages.\n        # We use \"max\" to avoid division by zero (if all_delta is 0, then all\n        # fields are 0 so percentages will be 0 too. all_delta cannot be a\n        # fraction because cpu times are integers)\n        scale = 100.0 / max(1, all_delta)\n        for field_delta in times_delta:\n            field_perc = field_delta * scale\n            field_perc = round(field_perc, 1)\n            # make sure we don't return negative values or values over 100%\n            field_perc = min(max(0.0, field_perc), 100.0)\n            nums.append(field_perc)\n        return _ntp.scputimes(*nums)\n\n    # system-wide usage\n    if not percpu:\n        if blocking:\n            t1 = cpu_times()\n            time.sleep(interval)\n        else:\n            t1 = _last_cpu_times_2.get(tid) or cpu_times()\n        _last_cpu_times_2[tid] = cpu_times()\n        return calculate(t1, _last_cpu_times_2[tid])\n    # per-cpu usage\n    else:\n        ret = []\n        if blocking:\n            tot1 = cpu_times(percpu=True)\n            time.sleep(interval)\n        else:\n            tot1 = _last_per_cpu_times_2.get(tid) or cpu_times(percpu=True)\n        _last_per_cpu_times_2[tid] = cpu_times(percpu=True)\n        for t1, t2 in zip(tot1, _last_per_cpu_times_2[tid]):\n            ret.append(calculate(t1, t2))\n        return ret\n\n\ndef cpu_stats() -> scpustats:\n    \"\"\"Return CPU statistics.\"\"\"\n    return _psplatform.cpu_stats()\n\n\nif hasattr(_psplatform, \"cpu_freq\"):\n\n    def cpu_freq(percpu: bool = False) -> scpufreq | list[scpufreq] | None:\n        \"\"\"Return CPU frequency as a named tuple including current,\n        min and max frequency expressed in Mhz.\n\n        If *percpu* is True and the system supports per-cpu frequency\n        retrieval (Linux only) a list of frequencies is returned for\n        each CPU. If not a list with one element is returned.\n        \"\"\"\n        ret = _psplatform.cpu_freq()\n        if percpu:\n            return ret\n        else:\n            num_cpus = float(len(ret))\n            if num_cpus == 0:\n                return None\n            elif num_cpus == 1:\n                return ret[0]\n            else:\n                currs, mins, maxs = 0.0, 0.0, 0.0\n                set_none = False\n                for cpu in ret:\n                    currs += cpu.current\n                    # On Linux if /proc/cpuinfo is used min/max are set\n                    # to None.\n                    if LINUX and cpu.min is None:\n                        set_none = True\n                        continue\n                    mins += cpu.min\n                    maxs += cpu.max\n\n                current = currs / num_cpus\n\n                if set_none:\n                    min_ = max_ = None\n                else:\n                    min_ = mins / num_cpus\n                    max_ = maxs / num_cpus\n\n                return _ntp.scpufreq(current, min_, max_)\n\n    __all__.append(\"cpu_freq\")\n\n\ndef getloadavg() -> tuple[float, float, float]:\n    \"\"\"Return the average system load over the last 1, 5 and 15 minutes\n    as a tuple. On Windows this is emulated by using a Windows API that\n    spawns a thread which keeps running in background and updates\n    results every 5 seconds, mimicking the UNIX behavior.\n    \"\"\"\n    if hasattr(os, \"getloadavg\"):\n        return os.getloadavg()\n    else:\n        return _psplatform.getloadavg()\n\n\n# =====================================================================\n# --- system memory related functions\n# =====================================================================\n\n\ndef virtual_memory() -> svmem:\n    \"\"\"Return statistics about system memory usage as a named tuple\n    including the following fields, expressed in bytes:\n\n     - total:\n       total physical memory available.\n\n     - available:\n       the memory that can be given instantly to processes without the\n       system going into swap.\n       This is calculated by summing different memory values depending\n       on the platform and it is supposed to be used to monitor actual\n       memory usage in a cross platform fashion.\n\n     - percent:\n       the percentage usage calculated as (total - available) / total * 100\n\n     - used:\n        memory used, calculated differently depending on the platform and\n        designed for informational purposes only:\n        macOS: active + wired\n        BSD: active + wired + cached\n        Linux: total - free\n\n     - free:\n       memory not being used at all (zeroed) that is readily available;\n       note that this doesn't reflect the actual memory available\n       (use 'available' instead)\n\n    Platform-specific fields:\n\n     - active (UNIX):\n       memory currently in use or very recently used, and so it is in RAM.\n\n     - inactive (UNIX):\n       memory that is marked as not used.\n\n     - buffers (BSD, Linux):\n       cache for things like file system metadata.\n\n     - cached (BSD, macOS):\n       cache for various things.\n\n     - wired (macOS, BSD):\n       memory that is marked to always stay in RAM. It is never moved to disk.\n\n     - shared (BSD):\n       memory that may be simultaneously accessed by multiple processes.\n\n    The sum of 'used' and 'available' does not necessarily equal total.\n    On Windows 'available' and 'free' are the same.\n    \"\"\"\n    global _TOTAL_PHYMEM\n    ret = _psplatform.virtual_memory()\n    # cached for later use in Process.memory_percent()\n    _TOTAL_PHYMEM = ret.total\n    return ret\n\n\ndef swap_memory() -> sswap:\n    \"\"\"Return system swap memory statistics as a named tuple including\n    the following fields:\n\n     - total:   total swap memory in bytes\n     - used:    used swap memory in bytes\n     - free:    free swap memory in bytes\n     - percent: the percentage usage\n     - sin:     no. of bytes the system has swapped in from disk (cumulative)\n     - sout:    no. of bytes the system has swapped out from disk (cumulative)\n\n    'sin' and 'sout' on Windows are meaningless and always set to 0.\n    \"\"\"\n    return _psplatform.swap_memory()\n\n\n# =====================================================================\n# --- disks/partitions related functions\n# =====================================================================\n\n\ndef disk_usage(path: str) -> sdiskusage:\n    \"\"\"Return disk usage statistics about the given *path* as a\n    named tuple including total, used and free space expressed in bytes\n    plus the percentage usage.\n    \"\"\"\n    return _psplatform.disk_usage(path)\n\n\ndef disk_partitions(all: bool = False) -> list[sdiskpart]:\n    \"\"\"Return mounted partitions as a list of\n    (device, mountpoint, fstype, opts) named tuple.\n    'opts' field is a raw string separated by commas indicating mount\n    options which may vary depending on the platform.\n\n    If *all* parameter is False return physical devices only and ignore\n    all others.\n    \"\"\"\n    return _psplatform.disk_partitions(all)\n\n\ndef disk_io_counters(\n    perdisk: bool = False, nowrap: bool = True\n) -> sdiskio | dict[str, sdiskio]:\n    \"\"\"Return system disk I/O statistics as a named tuple including\n    the following fields:\n\n     - read_count:  number of reads\n     - write_count: number of writes\n     - read_bytes:  number of bytes read\n     - write_bytes: number of bytes written\n     - read_time:   time spent reading from disk (in ms)\n     - write_time:  time spent writing to disk (in ms)\n\n    Platform specific:\n\n     - busy_time: (Linux, FreeBSD) time spent doing actual I/Os (in ms)\n     - read_merged_count (Linux): number of merged reads\n     - write_merged_count (Linux): number of merged writes\n\n    If *perdisk* is True return the same information for every\n    physical disk installed on the system as a dictionary\n    with partition names as the keys and the named tuple\n    described above as the values.\n\n    If *nowrap* is True it detects and adjust the numbers which overflow\n    and wrap (restart from 0) and add \"old value\" to \"new value\" so that\n    the returned numbers will always be increasing or remain the same,\n    but never decrease.\n    \"disk_io_counters.cache_clear()\" can be used to invalidate the\n    cache.\n\n    On recent Windows versions 'diskperf -y' command may need to be\n    executed first otherwise this function won't find any disk.\n    \"\"\"\n    kwargs = dict(perdisk=perdisk) if LINUX else {}\n    rawdict = _psplatform.disk_io_counters(**kwargs)\n    if not rawdict:\n        return {} if perdisk else None\n    if nowrap:\n        rawdict = _wrap_numbers(rawdict, 'psutil.disk_io_counters')\n    if perdisk:\n        for disk, fields in rawdict.items():\n            rawdict[disk] = _ntp.sdiskio(*fields)\n        return rawdict\n    else:\n        return _ntp.sdiskio(*(sum(x) for x in zip(*rawdict.values())))\n\n\ndisk_io_counters.cache_clear = functools.partial(\n    _wrap_numbers.cache_clear, 'psutil.disk_io_counters'\n)\ndisk_io_counters.cache_clear.__doc__ = \"Clears nowrap argument cache\"\n\n\n# =====================================================================\n# --- network related functions\n# =====================================================================\n\n\ndef net_io_counters(\n    pernic: bool = False, nowrap: bool = True\n) -> snetio | dict[str, snetio] | None:\n    \"\"\"Return network I/O statistics as a named tuple including\n    the following fields:\n\n     - bytes_sent:   number of bytes sent\n     - bytes_recv:   number of bytes received\n     - packets_sent: number of packets sent\n     - packets_recv: number of packets received\n     - errin:        total number of errors while receiving\n     - errout:       total number of errors while sending\n     - dropin:       total number of incoming packets which were dropped\n     - dropout:      total number of outgoing packets which were dropped\n                     (always 0 on macOS and BSD)\n\n    If *pernic* is True return the same information for every\n    network interface installed on the system as a dictionary\n    with network interface names as the keys and the named tuple\n    described above as the values.\n\n    If *nowrap* is True it detects and adjust the numbers which overflow\n    and wrap (restart from 0) and add \"old value\" to \"new value\" so that\n    the returned numbers will always be increasing or remain the same,\n    but never decrease.\n    \"net_io_counters.cache_clear()\" can be used to invalidate the\n    cache.\n    \"\"\"\n    rawdict = _psplatform.net_io_counters()\n    if not rawdict:\n        return {} if pernic else None\n    if nowrap:\n        rawdict = _wrap_numbers(rawdict, 'psutil.net_io_counters')\n    if pernic:\n        for nic, fields in rawdict.items():\n            rawdict[nic] = _ntp.snetio(*fields)\n        return rawdict\n    else:\n        return _ntp.snetio(*[sum(x) for x in zip(*rawdict.values())])\n\n\nnet_io_counters.cache_clear = functools.partial(\n    _wrap_numbers.cache_clear, 'psutil.net_io_counters'\n)\nnet_io_counters.cache_clear.__doc__ = \"Clears nowrap argument cache\"\n\n\ndef net_connections(kind: str = 'inet') -> list[sconn]:\n    \"\"\"Return system-wide socket connections as a list of\n    (fd, family, type, laddr, raddr, status, pid) named tuples.\n    In case of limited privileges 'fd' and 'pid' may be set to -1\n    and None respectively.\n    The *kind* parameter filters for connections that fit the\n    following criteria:\n\n    +------------+----------------------------------------------------+\n    | Kind Value | Connections using                                  |\n    +------------+----------------------------------------------------+\n    | inet       | IPv4 and IPv6                                      |\n    | inet4      | IPv4                                               |\n    | inet6      | IPv6                                               |\n    | tcp        | TCP                                                |\n    | tcp4       | TCP over IPv4                                      |\n    | tcp6       | TCP over IPv6                                      |\n    | udp        | UDP                                                |\n    | udp4       | UDP over IPv4                                      |\n    | udp6       | UDP over IPv6                                      |\n    | unix       | UNIX socket (both UDP and TCP protocols)           |\n    | all        | the sum of all the possible families and protocols |\n    +------------+----------------------------------------------------+\n\n    On macOS this function requires root privileges.\n    \"\"\"\n    _check_conn_kind(kind)\n    return _psplatform.net_connections(kind)\n\n\ndef net_if_addrs() -> dict[str, list[snicaddr]]:\n    \"\"\"Return the addresses associated to each NIC (network interface\n    card) installed on the system as a dictionary whose keys are the\n    NIC names and value is a list of named tuples for each address\n    assigned to the NIC. Each named tuple includes 5 fields:\n\n     - family: can be either socket.AF_INET, socket.AF_INET6 or\n               psutil.AF_LINK, which refers to a MAC address.\n     - address: is the primary address and it is always set.\n     - netmask: and 'broadcast' and 'ptp' may be None.\n     - ptp: stands for \"point to point\" and references the\n            destination address on a point to point interface\n            (typically a VPN).\n     - broadcast: and *ptp* are mutually exclusive.\n\n    Note: you can have more than one address of the same family\n    associated with each interface.\n    \"\"\"\n    rawlist = _psplatform.net_if_addrs()\n    rawlist.sort(key=lambda x: x[1])  # sort by family\n    ret = collections.defaultdict(list)\n    for name, fam, addr, mask, broadcast, ptp in rawlist:\n        try:\n            fam = socket.AddressFamily(fam)\n        except ValueError:\n            if WINDOWS and fam == -1:\n                fam = _psplatform.AF_LINK\n            elif (\n                hasattr(_psplatform, \"AF_LINK\") and fam == _psplatform.AF_LINK\n            ):\n                # Linux defines AF_LINK as an alias for AF_PACKET.\n                # We re-set the family here so that repr(family)\n                # will show AF_LINK rather than AF_PACKET\n                fam = _psplatform.AF_LINK\n\n        if fam == _psplatform.AF_LINK:\n            # The underlying C function may return an incomplete MAC\n            # address in which case we fill it with null bytes, see:\n            # https://github.com/giampaolo/psutil/issues/786\n            separator = \":\" if POSIX else \"-\"\n            while addr.count(separator) < 5:\n                addr += f\"{separator}00\"\n\n        nt = _ntp.snicaddr(fam, addr, mask, broadcast, ptp)\n\n        # On Windows broadcast is None, so we determine it via\n        # ipaddress module.\n        if WINDOWS and fam in {socket.AF_INET, socket.AF_INET6}:\n            try:\n                broadcast = _common.broadcast_addr(nt)\n            except Exception as err:  # noqa: BLE001\n                debug(err)\n            else:\n                if broadcast is not None:\n                    nt._replace(broadcast=broadcast)\n\n        ret[name].append(nt)\n\n    return dict(ret)\n\n\ndef net_if_stats() -> dict[str, snicstats]:\n    \"\"\"Return information about each NIC (network interface card)\n    installed on the system as a dictionary whose keys are the\n    NIC names and value is a named tuple with the following fields:\n\n     - isup: whether the interface is up (bool)\n     - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or\n               NIC_DUPLEX_UNKNOWN\n     - speed: the NIC speed expressed in mega bits (MB); if it can't\n              be determined (e.g. 'localhost') it will be set to 0.\n     - mtu: the maximum transmission unit expressed in bytes.\n    \"\"\"\n    return _psplatform.net_if_stats()\n\n\n# =====================================================================\n# --- sensors\n# =====================================================================\n\n\n# Linux, macOS\nif hasattr(_psplatform, \"sensors_temperatures\"):\n\n    def sensors_temperatures(\n        fahrenheit: bool = False,\n    ) -> dict[str, list[shwtemp]]:\n        \"\"\"Return hardware temperatures. Each entry is a named tuple\n        representing a certain hardware sensor (it may be a CPU, an\n        hard disk or something else, depending on the OS and its\n        configuration).\n        All temperatures are expressed in celsius unless *fahrenheit*\n        is set to True.\n        \"\"\"\n\n        def convert(n):\n            if n is not None:\n                return (float(n) * 9 / 5) + 32 if fahrenheit else n\n\n        ret = collections.defaultdict(list)\n        rawdict = _psplatform.sensors_temperatures()\n\n        for name, values in rawdict.items():\n            while values:\n                label, current, high, critical = values.pop(0)\n                current = convert(current)\n                high = convert(high)\n                critical = convert(critical)\n\n                if high and not critical:\n                    critical = high\n                elif critical and not high:\n                    high = critical\n\n                ret[name].append(_ntp.shwtemp(label, current, high, critical))\n\n        return dict(ret)\n\n    __all__.append(\"sensors_temperatures\")\n\n\n# Linux\nif hasattr(_psplatform, \"sensors_fans\"):\n\n    def sensors_fans() -> dict[str, list[sfan]]:\n        \"\"\"Return fans speed. Each entry is a named tuple\n        representing a certain hardware sensor.\n        All speed are expressed in RPM (rounds per minute).\n        \"\"\"\n        return _psplatform.sensors_fans()\n\n    __all__.append(\"sensors_fans\")\n\n\n# Linux, Windows, FreeBSD, macOS\nif hasattr(_psplatform, \"sensors_battery\"):\n\n    def sensors_battery() -> sbattery | None:\n        \"\"\"Return battery information. If no battery is installed\n        returns None.\n\n         - percent: battery power left as a percentage.\n         - secsleft: a rough approximation of how many seconds are left\n                     before the battery runs out of power. May be\n                     POWER_TIME_UNLIMITED or POWER_TIME_UNLIMITED.\n         - power_plugged: True if the AC power cable is connected.\n        \"\"\"\n        return _psplatform.sensors_battery()\n\n    __all__.append(\"sensors_battery\")\n\n\n# =====================================================================\n# --- other system related functions\n# =====================================================================\n\n\ndef boot_time() -> float:\n    \"\"\"Return the system boot time expressed in seconds since the epoch\n    (seconds since January 1, 1970, at midnight UTC). The returned\n    value is based on the system clock, which means it may be affected\n    by changes such as manual adjustments or time synchronization (e.g.\n    NTP).\n    \"\"\"\n    return _psplatform.boot_time()\n\n\ndef users() -> list[suser]:\n    \"\"\"Return users currently connected on the system as a list of\n    named tuples including the following fields.\n\n     - user: the name of the user\n     - terminal: the tty or pseudo-tty associated with the user, if any.\n     - host: the host name associated with the entry, if any.\n     - started: the creation time as a floating point number expressed in\n       seconds since the epoch.\n    \"\"\"\n    return _psplatform.users()\n\n\n# =====================================================================\n# --- Windows services\n# =====================================================================\n\n\nif WINDOWS:\n\n    def win_service_iter() -> Iterator[WindowsService]:\n        \"\"\"Return a generator yielding a WindowsService instance for all\n        Windows services installed.\n        \"\"\"\n        return _psplatform.win_service_iter()\n\n    def win_service_get(name) -> WindowsService:\n        \"\"\"Get a Windows service by *name*.\n        Raise NoSuchProcess if no service with such name exists.\n        \"\"\"\n        return _psplatform.win_service_get(name)\n\n\n# =====================================================================\n# --- malloc / heap\n# =====================================================================\n\n\n# Linux + glibc, Windows, macOS, FreeBSD, NetBSD\nif hasattr(_psplatform, \"heap_info\"):\n\n    def heap_info() -> pheap:\n        \"\"\"Return low-level heap statistics from the C heap allocator\n        (glibc).\n\n        - `heap_used`: the total number of bytes allocated via\n          malloc/free. These are typically allocations smaller than\n          MMAP_THRESHOLD.\n\n        - `mmap_used`: the total number of bytes allocated via `mmap()`\n          or via large ``malloc()`` allocations.\n\n        - `heap_count` (Windows only): number of private heaps created\n          via `HeapCreate()`.\n        \"\"\"\n        return _ntp.pheap(*_psplatform.heap_info())\n\n    def heap_trim() -> None:\n        \"\"\"Request that the underlying allocator free any unused memory\n        it's holding in the heap (typically small `malloc()`\n        allocations).\n\n        In practice, modern allocators rarely comply, so this is not a\n        general-purpose memory-reduction tool and won't meaningfully\n        shrink RSS in real programs. Its primary value is in **leak\n        detection tools**.\n\n        Calling `heap_trim()` before taking measurements helps reduce\n        allocator noise, giving you a cleaner baseline so that changes\n        in `heap_used` come from the code you're testing, not from\n        internal allocator caching or fragmentation. Its effectiveness\n        depends on allocator behavior and fragmentation patterns.\n        \"\"\"\n        _psplatform.heap_trim()\n\n    __all__.append(\"heap_info\")\n    __all__.append(\"heap_trim\")\n\n\n# =====================================================================\n\n\ndef _set_debug(value):\n    \"\"\"Enable or disable PSUTIL_DEBUG option, which prints debugging\n    messages to stderr.\n    \"\"\"\n    import psutil._common\n\n    psutil._common.PSUTIL_DEBUG = bool(value)\n    _psplatform.cext.set_debug(bool(value))\n\n\ndel memoize_when_activated\n"
  },
  {
    "path": "psutil/_common.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Common objects shared by __init__.py and _ps*.py modules.\n\nNote: this module is imported by setup.py, so it should not import\npsutil or third-party modules.\n\"\"\"\n\nimport collections\nimport functools\nimport os\nimport socket\nimport stat\nimport sys\nimport threading\nimport warnings\nfrom socket import AF_INET\nfrom socket import SOCK_DGRAM\nfrom socket import SOCK_STREAM\n\ntry:\n    from socket import AF_INET6\nexcept ImportError:\n    AF_INET6 = None\ntry:\n    from socket import AF_UNIX\nexcept ImportError:\n    AF_UNIX = None\n\n\nPSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG'))\n_DEFAULT = object()\n\n# fmt: off\n__all__ = [\n    # OS constants\n    'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',\n    'SUNOS', 'WINDOWS',\n    # other constants\n    'ENCODING', 'ENCODING_ERRS', 'AF_INET6',\n    # utility functions\n    'conn_tmap', 'deprecated_method', 'isfile_strict',\n    'parse_environ_block', 'path_exists_strict', 'usage_percent',\n    'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', \"wrap_numbers\",\n    'open_text', 'open_binary', 'cat', 'bcat',\n    'bytes2human', 'conn_to_ntuple', 'debug',\n    # shell utils\n    'hilite', 'term_supports_colors', 'print_color',\n]\n# fmt: on\n\n\n# ===================================================================\n# --- OS constants\n# ===================================================================\n\n\nPOSIX = os.name == \"posix\"\nWINDOWS = os.name == \"nt\"\nLINUX = sys.platform.startswith(\"linux\")\nMACOS = sys.platform.startswith(\"darwin\")\nOSX = MACOS  # deprecated alias\nFREEBSD = sys.platform.startswith((\"freebsd\", \"midnightbsd\"))\nOPENBSD = sys.platform.startswith(\"openbsd\")\nNETBSD = sys.platform.startswith(\"netbsd\")\nBSD = FREEBSD or OPENBSD or NETBSD\nSUNOS = sys.platform.startswith((\"sunos\", \"solaris\"))\nAIX = sys.platform.startswith(\"aix\")\n\nENCODING = sys.getfilesystemencoding()\nENCODING_ERRS = sys.getfilesystemencodeerrors()\n\n\n# ===================================================================\n# --- Process.net_connections() 'kind' parameter mapping\n# ===================================================================\n\n\nconn_tmap = {\n    \"all\": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),\n    \"tcp\": ([AF_INET, AF_INET6], [SOCK_STREAM]),\n    \"tcp4\": ([AF_INET], [SOCK_STREAM]),\n    \"udp\": ([AF_INET, AF_INET6], [SOCK_DGRAM]),\n    \"udp4\": ([AF_INET], [SOCK_DGRAM]),\n    \"inet\": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),\n    \"inet4\": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]),\n    \"inet6\": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),\n}\n\nif AF_INET6 is not None:\n    conn_tmap.update({\n        \"tcp6\": ([AF_INET6], [SOCK_STREAM]),\n        \"udp6\": ([AF_INET6], [SOCK_DGRAM]),\n    })\n\nif AF_UNIX is not None and not SUNOS:\n    conn_tmap.update({\"unix\": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM])})\n\n\n# =====================================================================\n# --- Exceptions\n# =====================================================================\n\n\nclass Error(Exception):\n    \"\"\"Base exception class. All other psutil exceptions inherit\n    from this one.\n    \"\"\"\n\n    __module__ = 'psutil'\n\n    def _infodict(self, attrs):\n        info = {}\n        for name in attrs:\n            value = getattr(self, name, None)\n            if value or (name == \"pid\" and value == 0):\n                info[name] = value\n        return info\n\n    def __str__(self):\n        # invoked on `raise Error`\n        info = self._infodict((\"pid\", \"ppid\", \"name\"))\n        if info:\n            details = \"({})\".format(\n                \", \".join([f\"{k}={v!r}\" for k, v in info.items()])\n            )\n        else:\n            details = None\n        return \" \".join([x for x in (getattr(self, \"msg\", \"\"), details) if x])\n\n    def __repr__(self):\n        # invoked on `repr(Error)`\n        info = self._infodict((\"pid\", \"ppid\", \"name\", \"seconds\", \"msg\"))\n        details = \", \".join([f\"{k}={v!r}\" for k, v in info.items()])\n        return f\"psutil.{self.__class__.__name__}({details})\"\n\n\nclass NoSuchProcess(Error):\n    \"\"\"Exception raised when a process with a certain PID doesn't\n    or no longer exists.\n    \"\"\"\n\n    __module__ = 'psutil'\n\n    def __init__(self, pid, name=None, msg=None):\n        Error.__init__(self)\n        self.pid = pid\n        self.name = name\n        self.msg = msg or \"process no longer exists\"\n\n    def __reduce__(self):\n        return (self.__class__, (self.pid, self.name, self.msg))\n\n\nclass ZombieProcess(NoSuchProcess):\n    \"\"\"Exception raised when querying a zombie process. This is\n    raised on macOS, BSD and Solaris only, and not always: depending\n    on the query the OS may be able to succeed anyway.\n    On Linux all zombie processes are querable (hence this is never\n    raised). Windows doesn't have zombie processes.\n    \"\"\"\n\n    __module__ = 'psutil'\n\n    def __init__(self, pid, name=None, ppid=None, msg=None):\n        NoSuchProcess.__init__(self, pid, name, msg)\n        self.ppid = ppid\n        self.msg = msg or \"PID still exists but it's a zombie\"\n\n    def __reduce__(self):\n        return (self.__class__, (self.pid, self.name, self.ppid, self.msg))\n\n\nclass AccessDenied(Error):\n    \"\"\"Exception raised when permission to perform an action is denied.\"\"\"\n\n    __module__ = 'psutil'\n\n    def __init__(self, pid=None, name=None, msg=None):\n        Error.__init__(self)\n        self.pid = pid\n        self.name = name\n        self.msg = msg or \"\"\n\n    def __reduce__(self):\n        return (self.__class__, (self.pid, self.name, self.msg))\n\n\nclass TimeoutExpired(Error):\n    \"\"\"Raised on Process.wait(timeout) if timeout expires and process\n    is still alive.\n    \"\"\"\n\n    __module__ = 'psutil'\n\n    def __init__(self, seconds, pid=None, name=None):\n        Error.__init__(self)\n        self.seconds = seconds\n        self.pid = pid\n        self.name = name\n        self.msg = f\"timeout after {seconds} seconds\"\n\n    def __reduce__(self):\n        return (self.__class__, (self.seconds, self.pid, self.name))\n\n\n# ===================================================================\n# --- utils\n# ===================================================================\n\n\ndef usage_percent(used, total, round_=None):\n    \"\"\"Calculate percentage usage of 'used' against 'total'.\"\"\"\n    try:\n        ret = (float(used) / total) * 100\n    except ZeroDivisionError:\n        return 0.0\n    else:\n        if round_ is not None:\n            ret = round(ret, round_)\n        return ret\n\n\ndef memoize_when_activated(fun):\n    \"\"\"A memoize decorator which is disabled by default. It can be\n    activated and deactivated on request.\n    For efficiency reasons it can be used only against class methods\n    accepting no arguments.\n\n    >>> class Foo:\n    ...     @memoize_when_activated\n    ...     def foo()\n    ...         print(1)\n    ...\n    >>> f = Foo()\n    >>> # deactivated (default)\n    >>> foo()\n    1\n    >>> foo()\n    1\n    >>>\n    >>> # activated\n    >>> foo.cache_activate(self)\n    >>> foo()\n    1\n    >>> foo()\n    >>> foo()\n    >>>\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self):\n        try:\n            # case 1: we previously entered oneshot() ctx\n            ret = self._cache[fun]\n        except AttributeError:\n            # case 2: we never entered oneshot() ctx\n            try:\n                return fun(self)\n            except Exception as err:\n                raise err from None\n        except KeyError:\n            # case 3: we entered oneshot() ctx but there's no cache\n            # for this entry yet\n            try:\n                ret = fun(self)\n            except Exception as err:\n                raise err from None\n            try:\n                self._cache[fun] = ret\n            except AttributeError:\n                # multi-threading race condition, see:\n                # https://github.com/giampaolo/psutil/issues/1948\n                pass\n        return ret\n\n    def cache_activate(proc):\n        \"\"\"Activate cache. Expects a Process instance. Cache will be\n        stored as a \"_cache\" instance attribute.\n        \"\"\"\n        proc._cache = {}\n\n    def cache_deactivate(proc):\n        \"\"\"Deactivate and clear cache.\"\"\"\n        try:\n            del proc._cache\n        except AttributeError:\n            pass\n\n    wrapper.cache_activate = cache_activate\n    wrapper.cache_deactivate = cache_deactivate\n    return wrapper\n\n\ndef isfile_strict(path):\n    \"\"\"Same as os.path.isfile() but does not swallow EACCES / EPERM\n    exceptions, see:\n    http://mail.python.org/pipermail/python-dev/2012-June/120787.html.\n    \"\"\"\n    try:\n        st = os.stat(path)\n    except PermissionError:\n        raise\n    except OSError:\n        return False\n    else:\n        return stat.S_ISREG(st.st_mode)\n\n\ndef path_exists_strict(path):\n    \"\"\"Same as os.path.exists() but does not swallow EACCES / EPERM\n    exceptions. See:\n    http://mail.python.org/pipermail/python-dev/2012-June/120787.html.\n    \"\"\"\n    try:\n        os.stat(path)\n    except PermissionError:\n        raise\n    except OSError:\n        return False\n    else:\n        return True\n\n\ndef supports_ipv6():\n    \"\"\"Return True if IPv6 is supported on this platform.\"\"\"\n    if not socket.has_ipv6 or AF_INET6 is None:\n        return False\n    try:\n        with socket.socket(AF_INET6, socket.SOCK_STREAM) as sock:\n            sock.bind((\"::1\", 0))\n        return True\n    except OSError:\n        return False\n\n\ndef parse_environ_block(data):\n    \"\"\"Parse a C environ block of environment variables into a dictionary.\"\"\"\n    # The block is usually raw data from the target process.  It might contain\n    # trailing garbage and lines that do not look like assignments.\n    ret = {}\n    pos = 0\n\n    # localize global variable to speed up access.\n    WINDOWS_ = WINDOWS\n    while True:\n        next_pos = data.find(\"\\0\", pos)\n        # nul byte at the beginning or double nul byte means finish\n        if next_pos <= pos:\n            break\n        # there might not be an equals sign\n        equal_pos = data.find(\"=\", pos, next_pos)\n        if equal_pos > pos:\n            key = data[pos:equal_pos]\n            value = data[equal_pos + 1 : next_pos]\n            # Windows expects environment variables to be uppercase only\n            if WINDOWS_:\n                key = key.upper()\n            ret[key] = value\n        pos = next_pos + 1\n\n    return ret\n\n\ndef sockfam_to_enum(num):\n    \"\"\"Convert a numeric socket family value to an IntEnum member.\n    If it's not a known member, return the numeric value itself.\n    \"\"\"\n    try:\n        return socket.AddressFamily(num)\n    except ValueError:\n        return num\n\n\ndef socktype_to_enum(num):\n    \"\"\"Convert a numeric socket type value to an IntEnum member.\n    If it's not a known member, return the numeric value itself.\n    \"\"\"\n    try:\n        return socket.SocketKind(num)\n    except ValueError:\n        return num\n\n\ndef conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):\n    \"\"\"Convert a raw connection tuple to a proper ntuple.\"\"\"\n    from . import _ntuples as ntp\n    from ._enums import ConnectionStatus\n\n    if fam in {socket.AF_INET, AF_INET6}:\n        if laddr:\n            laddr = ntp.addr(*laddr)\n        if raddr:\n            raddr = ntp.addr(*raddr)\n    if type_ == socket.SOCK_STREAM and fam in {AF_INET, AF_INET6}:\n        status = status_map.get(status, ConnectionStatus.CONN_NONE)\n    else:\n        status = ConnectionStatus.CONN_NONE  # ignore whatever C returned to us\n    fam = sockfam_to_enum(fam)\n    type_ = socktype_to_enum(type_)\n    if pid is None:\n        return ntp.pconn(fd, fam, type_, laddr, raddr, status)\n    else:\n        return ntp.sconn(fd, fam, type_, laddr, raddr, status, pid)\n\n\ndef broadcast_addr(addr):\n    \"\"\"Given the address ntuple returned by ``net_if_addrs()``\n    calculates the broadcast address.\n    \"\"\"\n    import ipaddress\n\n    if not addr.address or not addr.netmask:\n        return None\n    if addr.family == socket.AF_INET:\n        return str(\n            ipaddress.IPv4Network(\n                f\"{addr.address}/{addr.netmask}\", strict=False\n            ).broadcast_address\n        )\n    if addr.family == socket.AF_INET6:\n        return str(\n            ipaddress.IPv6Network(\n                f\"{addr.address}/{addr.netmask}\", strict=False\n            ).broadcast_address\n        )\n\n\ndef deprecated_method(replacement):\n    \"\"\"A decorator which can be used to mark a method as deprecated\n    'replcement' is the method name which will be called instead.\n    \"\"\"\n\n    def outer(fun):\n        msg = (\n            f\"{fun.__name__}() is deprecated and will be removed; use\"\n            f\" {replacement}() instead\"\n        )\n        if fun.__doc__ is None:\n            fun.__doc__ = msg\n\n        @functools.wraps(fun)\n        def inner(self, *args, **kwargs):\n            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)\n            return getattr(self, replacement)(*args, **kwargs)\n\n        return inner\n\n    return outer\n\n\nclass deprecated_property:\n    \"\"\"A descriptor which can be used to mark a property as deprecated.\n    'replacement' is the attribute name to use instead. Usage::\n\n        class Foo:\n            bar = deprecated_property(\"baz\")\n    \"\"\"\n\n    def __init__(self, replacement):\n        self.replacement = replacement\n        self._msg = None\n\n    def __set_name__(self, owner, name):\n        self._msg = (\n            f\"{name} is deprecated and will be removed; use\"\n            f\" {self.replacement} instead\"\n        )\n\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self\n        warnings.warn(self._msg, category=DeprecationWarning, stacklevel=2)\n        return getattr(obj, self.replacement)\n\n\nclass _WrapNumbers:\n    \"\"\"Watches numbers so that they don't overflow and wrap\n    (reset to zero).\n    \"\"\"\n\n    def __init__(self):\n        self.lock = threading.Lock()\n        self.cache = {}\n        self.reminders = {}\n        self.reminder_keys = {}\n\n    def _add_dict(self, input_dict, name):\n        assert name not in self.cache\n        assert name not in self.reminders\n        assert name not in self.reminder_keys\n        self.cache[name] = input_dict\n        self.reminders[name] = collections.defaultdict(int)\n        self.reminder_keys[name] = collections.defaultdict(set)\n\n    def _remove_dead_reminders(self, input_dict, name):\n        \"\"\"In case the number of keys changed between calls (e.g. a\n        disk disappears) this removes the entry from self.reminders.\n        \"\"\"\n        old_dict = self.cache[name]\n        gone_keys = set(old_dict) - set(input_dict)\n        for gone_key in gone_keys:\n            for remkey in self.reminder_keys[name][gone_key]:\n                del self.reminders[name][remkey]\n            del self.reminder_keys[name][gone_key]\n\n    def run(self, input_dict, name):\n        \"\"\"Cache dict and sum numbers which overflow and wrap.\n        Return an updated copy of `input_dict`.\n        \"\"\"\n        if name not in self.cache:\n            # This was the first call.\n            self._add_dict(input_dict, name)\n            return input_dict\n\n        self._remove_dead_reminders(input_dict, name)\n\n        old_dict = self.cache[name]\n        new_dict = {}\n        for key in input_dict:\n            input_tuple = input_dict[key]\n            try:\n                old_tuple = old_dict[key]\n            except KeyError:\n                # The input dict has a new key (e.g. a new disk or NIC)\n                # which didn't exist in the previous call.\n                new_dict[key] = input_tuple\n                continue\n\n            bits = []\n            for i in range(len(input_tuple)):\n                input_value = input_tuple[i]\n                old_value = old_tuple[i]\n                remkey = (key, i)\n                if input_value < old_value:\n                    # it wrapped!\n                    self.reminders[name][remkey] += old_value\n                    self.reminder_keys[name][key].add(remkey)\n                bits.append(input_value + self.reminders[name][remkey])\n\n            new_dict[key] = tuple(bits)\n\n        self.cache[name] = input_dict\n        return new_dict\n\n    def cache_clear(self, name=None):\n        \"\"\"Clear the internal cache, optionally only for function 'name'.\"\"\"\n        with self.lock:\n            if name is None:\n                self.cache.clear()\n                self.reminders.clear()\n                self.reminder_keys.clear()\n            else:\n                self.cache.pop(name, None)\n                self.reminders.pop(name, None)\n                self.reminder_keys.pop(name, None)\n\n    def cache_info(self):\n        \"\"\"Return internal cache dicts as a tuple of 3 elements.\"\"\"\n        with self.lock:\n            return (self.cache, self.reminders, self.reminder_keys)\n\n\ndef wrap_numbers(input_dict, name):\n    \"\"\"Given an `input_dict` and a function `name`, adjust the numbers\n    which \"wrap\" (restart from zero) across different calls by adding\n    \"old value\" to \"new value\" and return an updated dict.\n    \"\"\"\n    with _wn.lock:\n        return _wn.run(input_dict, name)\n\n\n_wn = _WrapNumbers()\nwrap_numbers.cache_clear = _wn.cache_clear\nwrap_numbers.cache_info = _wn.cache_info\n\n\n# The read buffer size for open() builtin. This (also) dictates how\n# much data we read(2) when iterating over file lines as in:\n#   >>> with open(file) as f:\n#   ...    for line in f:\n#   ...        ...\n# Default per-line buffer size for binary files is 1K. For text files\n# is 8K. We use a bigger buffer (32K) in order to have more consistent\n# results when reading /proc pseudo files on Linux, see:\n# https://github.com/giampaolo/psutil/issues/2050\n# https://github.com/giampaolo/psutil/issues/708\nFILE_READ_BUFFER_SIZE = 32 * 1024\n\n\ndef open_binary(fname):\n    return open(fname, \"rb\", buffering=FILE_READ_BUFFER_SIZE)\n\n\ndef open_text(fname):\n    \"\"\"Open a file in text mode by using the proper FS encoding and\n    en/decoding error handlers.\n    \"\"\"\n    # See:\n    # https://github.com/giampaolo/psutil/issues/675\n    # https://github.com/giampaolo/psutil/pull/733\n    fobj = open(  # noqa: SIM115\n        fname,\n        buffering=FILE_READ_BUFFER_SIZE,\n        encoding=ENCODING,\n        errors=ENCODING_ERRS,\n    )\n    try:\n        # Dictates per-line read(2) buffer size. Defaults is 8k. See:\n        # https://github.com/giampaolo/psutil/issues/2050#issuecomment-1013387546\n        fobj._CHUNK_SIZE = FILE_READ_BUFFER_SIZE\n    except AttributeError:\n        pass\n    except Exception:\n        fobj.close()\n        raise\n\n    return fobj\n\n\ndef cat(fname, fallback=_DEFAULT, _open=open_text):\n    \"\"\"Read entire file content and return it as a string. File is\n    opened in text mode. If specified, `fallback` is the value\n    returned in case of error, either if the file does not exist or\n    it can't be read().\n    \"\"\"\n    if fallback is _DEFAULT:\n        with _open(fname) as f:\n            return f.read()\n    else:\n        try:\n            with _open(fname) as f:\n                return f.read()\n        except OSError:\n            return fallback\n\n\ndef bcat(fname, fallback=_DEFAULT):\n    \"\"\"Same as above but opens file in binary mode.\"\"\"\n    return cat(fname, fallback=fallback, _open=open_binary)\n\n\ndef bytes2human(n, format=\"%(value).1f%(symbol)s\"):\n    \"\"\"Used by various scripts. See: https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/?in=user-4178764.\n\n    >>> bytes2human(10000)\n    '9.8K'\n    >>> bytes2human(100001221)\n    '95.4M'\n    \"\"\"\n    symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')\n    prefix = {}\n    for i, s in enumerate(symbols[1:]):\n        prefix[s] = 1 << (i + 1) * 10\n    for symbol in reversed(symbols[1:]):\n        if abs(n) >= prefix[symbol]:\n            value = float(n) / prefix[symbol]\n            return format % locals()\n    return format % dict(symbol=symbols[0], value=n)\n\n\ndef get_procfs_path():\n    \"\"\"Return updated psutil.PROCFS_PATH constant.\"\"\"\n    return sys.modules['psutil'].PROCFS_PATH\n\n\ndef decode(s):\n    return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)\n\n\n# =====================================================================\n# --- shell utils\n# =====================================================================\n\n\n@functools.lru_cache\ndef term_supports_colors(force_color=False):\n    if WINDOWS:\n        return False\n    if force_color:\n        return True\n    if not hasattr(sys.stdout, \"isatty\") or not sys.stdout.isatty():\n        return False\n    try:\n        sys.stdout.fileno()\n    except Exception:  # noqa: BLE001\n        return False\n    return True\n\n\ndef hilite(s, color=None, bold=False, force_color=False):\n    \"\"\"Return an highlighted version of 'string'.\"\"\"\n    if not term_supports_colors(force_color=force_color):\n        return s\n    attr = []\n    colors = dict(\n        blue='34',\n        brown='33',\n        darkgrey='30',\n        green='32',\n        grey='37',\n        lightblue='36',\n        red='31',\n        violet='35',\n        yellow='93',\n    )\n    colors[None] = '29'\n    try:\n        color = colors[color]\n    except KeyError:\n        msg = f\"invalid color {color!r}; choose amongst {list(colors)}\"\n        raise ValueError(msg) from None\n    attr.append(color)\n    if bold:\n        attr.append('1')\n    return f\"\\x1b[{';'.join(attr)}m{s}\\x1b[0m\"\n\n\ndef print_color(\n    s, color=None, bold=False, file=sys.stdout\n):  # pragma: no cover\n    \"\"\"Print a colorized version of string.\"\"\"\n    if term_supports_colors():\n        s = hilite(s, color=color, bold=bold)\n    print(s, file=file, flush=True)\n\n\ndef debug(msg):\n    \"\"\"If PSUTIL_DEBUG env var is set, print a debug message to stderr.\"\"\"\n    if PSUTIL_DEBUG:\n        import inspect\n\n        fname, lineno, _, _lines, _index = inspect.getframeinfo(\n            inspect.currentframe().f_back\n        )\n        if isinstance(msg, Exception):\n            if isinstance(msg, OSError):\n                # ...because str(exc) may contain info about the file name\n                msg = f\"ignoring {msg}\"\n            else:\n                msg = f\"ignoring {msg!r}\"\n        print(  # noqa: T201\n            f\"psutil-debug [{fname}:{lineno}]> {msg}\", file=sys.stderr\n        )\n"
  },
  {
    "path": "psutil/_enums.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Enum containers backing psutil constants.\n\nThis module groups constants used by psutil APIs into Enum classes.\nThese enums mainly act as containers for related values and are useful\nfor type annotations and introspection.\n\nIn normal usage constants should be accessed directly from the psutil\nnamespace instead of importing these enums, e.g.:\n\n    import psutil\n    if proc.status() == psutil.STATUS_RUNNING:\n        ...\n\nThe top-level constants (e.g. ``psutil.STATUS_RUNNING``) are aliases of\nthe enum members defined here and represent the primary public API.\n\"\"\"\n\nimport enum\n\nfrom ._common import FREEBSD\nfrom ._common import LINUX\nfrom ._common import SUNOS\nfrom ._common import WINDOWS\n\nif WINDOWS:\n    from . import _psutil_windows as cext\nelif LINUX:\n    from . import _psutil_linux as cext\nelif FREEBSD:\n    from . import _psutil_bsd as cext\nelse:\n    cext = None\n\nif hasattr(enum, \"StrEnum\"):  # Python >= 3.11\n    StrEnum = enum.StrEnum\nelse:\n\n    # A backport of Python 3.11 StrEnum class for >= Python 3.8\n    class StrEnum(str, enum.Enum):\n        def __new__(cls, *values):\n            value = str(*values)\n            member = str.__new__(cls, value)\n            member._value_ = value\n            return member\n\n        __str__ = str.__str__\n\n        @staticmethod\n        def _generate_next_value_(name, _start, _count, _last_values):\n            return name.lower()\n\n\n# psutil.Process.status()\nclass ProcessStatus(StrEnum):\n    STATUS_DEAD = \"dead\"\n    STATUS_DISK_SLEEP = \"disk-sleep\"\n    STATUS_IDLE = \"idle\"  # Linux, macOS, FreeBSD\n    STATUS_LOCKED = \"locked\"  # FreeBSD\n    STATUS_PARKED = \"parked\"  # Linux\n    STATUS_RUNNING = \"running\"\n    STATUS_SLEEPING = \"sleeping\"\n    STATUS_STOPPED = \"stopped\"\n    STATUS_SUSPENDED = \"suspended\"  # NetBSD\n    STATUS_TRACING_STOP = \"tracing-stop\"\n    STATUS_WAITING = \"waiting\"  # FreeBSD\n    STATUS_WAKE_KILL = \"wake-kill\"\n    STATUS_WAKING = \"waking\"\n    STATUS_ZOMBIE = \"zombie\"\n\n\n# psutil.Process.net_connections() and psutil.net_connections()\nclass ConnectionStatus(StrEnum):\n    CONN_CLOSE = \"CLOSE\"\n    CONN_CLOSE_WAIT = \"CLOSE_WAIT\"\n    CONN_CLOSING = \"CLOSING\"\n    CONN_ESTABLISHED = \"ESTABLISHED\"\n    CONN_FIN_WAIT1 = \"FIN_WAIT1\"\n    CONN_FIN_WAIT2 = \"FIN_WAIT2\"\n    CONN_LAST_ACK = \"LAST_ACK\"\n    CONN_LISTEN = \"LISTEN\"\n    CONN_NONE = \"NONE\"\n    CONN_SYN_RECV = \"SYN_RECV\"\n    CONN_SYN_SENT = \"SYN_SENT\"\n    CONN_TIME_WAIT = \"TIME_WAIT\"\n    if WINDOWS:\n        CONN_DELETE_TCB = \"DELETE_TCB\"\n    if SUNOS:\n        CONN_BOUND = \"CONN_BOUND\"\n        CONN_IDLE = \"CONN_IDLE\"\n\n\n# psutil.net_if_stats()\nclass NicDuplex(enum.IntEnum):\n    NIC_DUPLEX_FULL = 2\n    NIC_DUPLEX_HALF = 1\n    NIC_DUPLEX_UNKNOWN = 0\n\n\n# psutil.sensors_battery()\nclass BatteryTime(enum.IntEnum):\n    POWER_TIME_UNKNOWN = -1\n    POWER_TIME_UNLIMITED = -2\n\n\nif LINUX:\n\n    # psutil.Process.ionice(ioclass=…)\n    class ProcessIOPriority(enum.IntEnum):\n        # ioprio_* constants http://linux.die.net/man/2/ioprio_get\n        IOPRIO_CLASS_NONE = 0\n        IOPRIO_CLASS_RT = 1\n        IOPRIO_CLASS_BE = 2\n        IOPRIO_CLASS_IDLE = 3\n\n\nif WINDOWS:\n\n    # psutil.Process.ionice(ioclass=…)\n    class ProcessIOPriority(enum.IntEnum):\n        IOPRIO_VERYLOW = 0\n        IOPRIO_LOW = 1\n        IOPRIO_NORMAL = 2\n        IOPRIO_HIGH = 3\n\n    # psutil.Process.nice()\n    class ProcessPriority(enum.IntEnum):\n        ABOVE_NORMAL_PRIORITY_CLASS = cext.ABOVE_NORMAL_PRIORITY_CLASS\n        BELOW_NORMAL_PRIORITY_CLASS = cext.BELOW_NORMAL_PRIORITY_CLASS\n        HIGH_PRIORITY_CLASS = cext.HIGH_PRIORITY_CLASS\n        IDLE_PRIORITY_CLASS = cext.IDLE_PRIORITY_CLASS\n        NORMAL_PRIORITY_CLASS = cext.NORMAL_PRIORITY_CLASS\n        REALTIME_PRIORITY_CLASS = cext.REALTIME_PRIORITY_CLASS\n\n\nif LINUX or FREEBSD:\n\n    # psutil.Process.rlimit()\n    ProcessRlimit = enum.IntEnum(\n        \"ProcessRlimit\",\n        (\n            (name, getattr(cext, name))\n            for name in dir(cext)\n            if name.startswith(\"RLIM\") and name.isupper()\n        ),\n    )\n"
  },
  {
    "path": "psutil/_ntuples.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola\". All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\nfrom __future__ import annotations\n\nimport warnings\nfrom collections import namedtuple\nfrom typing import TYPE_CHECKING\nfrom typing import NamedTuple\n\nif TYPE_CHECKING:\n    import socket\n\n    from ._enums import BatteryTime\n    from ._enums import ConnectionStatus\n    from ._enums import NicDuplex\n    from ._enums import ProcessIOPriority\n\nfrom ._common import AIX\nfrom ._common import BSD\nfrom ._common import FREEBSD\nfrom ._common import LINUX\nfrom ._common import MACOS\nfrom ._common import NETBSD\nfrom ._common import OPENBSD\nfrom ._common import SUNOS\nfrom ._common import WINDOWS\n\n# ===================================================================\n# --- system functions\n# ===================================================================\n\n\n# psutil.swap_memory()\nclass sswap(NamedTuple):\n    total: int\n    used: int\n    free: int\n    percent: float\n    sin: int\n    sout: int\n\n\n# psutil.disk_usage()\nclass sdiskusage(NamedTuple):\n    total: int\n    used: int\n    free: int\n    percent: float\n\n\n# psutil.disk_io_counters()\nclass sdiskio(NamedTuple):\n    read_count: int\n    write_count: int\n    read_bytes: int\n    write_bytes: int\n    if not (NETBSD or OPENBSD):\n        read_time: int\n        write_time: int\n    if LINUX:\n        read_merged_count: int\n        write_merged_count: int\n        busy_time: int\n    if FREEBSD:\n        busy_time: int\n\n\n# psutil.disk_partitions()\nclass sdiskpart(NamedTuple):\n    device: str\n    mountpoint: str\n    fstype: str\n    opts: str\n\n\n# psutil.net_io_counters()\nclass snetio(NamedTuple):\n    bytes_sent: int\n    bytes_recv: int\n    packets_sent: int\n    packets_recv: int\n    errin: int\n    errout: int\n    dropin: int\n    dropout: int\n\n\n# psutil.users()\nclass suser(NamedTuple):\n    name: str\n    terminal: str | None\n    host: str | None\n    started: float\n    pid: int | None\n\n\n# psutil.net_connections() and psutil.Process.net_connections()\nclass addr(NamedTuple):\n    ip: str\n    port: int\n\n\n# psutil.net_connections()\nclass sconn(NamedTuple):\n    fd: int\n    family: socket.AddressFamily\n    type: socket.SocketKind\n    laddr: addr | tuple | str\n    raddr: addr | tuple | str\n    status: ConnectionStatus\n    pid: int | None\n\n\n# psutil.net_if_addrs()\nclass snicaddr(NamedTuple):\n    family: socket.AddressFamily\n    address: str\n    netmask: str | None\n    broadcast: str | None\n    ptp: str | None\n\n\n# psutil.net_if_stats()\nclass snicstats(NamedTuple):\n    isup: bool\n    duplex: NicDuplex\n    speed: int\n    mtu: int\n    flags: str\n\n\n# psutil.cpu_times()\nclass scputimes(NamedTuple):\n    user: float\n    system: float\n    idle: float\n    if LINUX or MACOS or BSD:\n        nice: float\n    if LINUX:\n        iowait: float\n        irq: float\n        softirq: float\n        steal: float\n        guest: float\n        guest_nice: float\n    if BSD:\n        irq: float\n    if SUNOS or AIX:\n        iowait: float\n    if WINDOWS:\n        irq: float\n        dpc: float\n\n        @property\n        def interrupt(self):\n            msg = \"'interrupt' field is deprecated, use 'irq' instead\"\n            warnings.warn(msg, DeprecationWarning, stacklevel=2)\n            return self.irq\n\n\n# psutil.cpu_stats()\nclass scpustats(NamedTuple):\n    ctx_switches: int\n    interrupts: int\n    soft_interrupts: int\n    syscalls: int\n\n\n# psutil.cpu_freq()\nclass scpufreq(NamedTuple):\n    current: float\n    min: float | None\n    max: float | None\n\n\n# psutil.sensors_temperatures()\nclass shwtemp(NamedTuple):\n    label: str\n    current: float | None\n    high: float | None\n    critical: float | None\n\n\n# psutil.sensors_battery()\nclass sbattery(NamedTuple):\n    percent: float\n    secsleft: int | BatteryTime\n    power_plugged: bool | None\n\n\n# psutil.sensors_fans()\nclass sfan(NamedTuple):\n    label: str\n    current: int\n\n\nif LINUX or WINDOWS or MACOS or BSD:\n\n    # psutil.heap_info()\n    class pheap(NamedTuple):\n        heap_used: int\n        mmap_used: int\n        if WINDOWS:\n            heap_count: int\n\n\n# psutil.virtual_memory()\nclass svmem(NamedTuple):\n    total: int\n    available: int\n    percent: float\n    used: int\n    free: int\n    if LINUX:\n        active: int\n        inactive: int\n        buffers: int\n        cached: int\n        shared: int\n        slab: int\n    elif BSD:\n        active: int\n        inactive: int\n        buffers: int\n        cached: int\n        shared: int\n        wired: int\n    elif WINDOWS:\n        cached: int\n        wired: int\n    elif MACOS:\n        active: int\n        inactive: int\n        wired: int\n\n\n# ===================================================================\n# --- Process class\n# ===================================================================\n\n\n# psutil.Process.cpu_times()\nclass pcputimes(NamedTuple):\n    user: float\n    system: float\n    children_user: float\n    children_system: float\n    if LINUX:\n        iowait: float\n\n\n# psutil.Process.open_files()\nclass popenfile(NamedTuple):\n    path: str\n    fd: int\n    if LINUX:\n        position: int\n        mode: str\n        flags: int\n\n\n# psutil.Process.threads()\nclass pthread(NamedTuple):\n    id: int\n    user_time: float\n    system_time: float\n\n\n# psutil.Process.uids()\nclass puids(NamedTuple):\n    real: int\n    effective: int\n    saved: int\n\n\n# psutil.Process.gids()\nclass pgids(NamedTuple):\n    real: int\n    effective: int\n    saved: int\n\n\n# psutil.Process.io_counters()\nclass pio(NamedTuple):\n    read_count: int\n    write_count: int\n    read_bytes: int\n    write_bytes: int\n    if LINUX:\n        read_chars: int\n        write_chars: int\n    elif WINDOWS:\n        other_count: int\n        other_bytes: int\n\n\n# psutil.Process.ionice()\nclass pionice(NamedTuple):\n    ioclass: ProcessIOPriority\n    value: int\n\n\n# psutil.Process.ctx_switches()\nclass pctxsw(NamedTuple):\n    voluntary: int\n    involuntary: int\n\n\n# psutil.Process.page_faults()\nclass ppagefaults(NamedTuple):\n    minor: int\n    major: int\n\n\n# psutil.Process().memory_footprint()\nif LINUX or MACOS or WINDOWS:\n\n    class pfootprint(NamedTuple):\n        uss: int\n        if LINUX:\n            pss: int\n            swap: int\n\n\n# psutil.Process.net_connections()\nclass pconn(NamedTuple):\n    fd: int\n    family: socket.AddressFamily\n    type: socket.SocketKind\n    laddr: addr | tuple | str\n    raddr: addr | tuple | str\n    status: ConnectionStatus\n\n\n# psutil.Process.memory_maps(grouped=True)\nclass pmmap_grouped(NamedTuple):\n    path: str\n    rss: int\n    if LINUX:\n        size: int\n        pss: int\n        shared_clean: int\n        shared_dirty: int\n        private_clean: int\n        private_dirty: int\n        referenced: int\n        anonymous: int\n        swap: int\n    elif BSD:\n        private: int\n        ref_count: int\n        shadow_count: int\n    elif SUNOS:\n        anonymous: int\n        locked: int\n\n\n# psutil.Process.memory_maps(grouped=False)\nclass pmmap_ext(NamedTuple):\n    addr: str\n    perms: str\n    path: str\n    rss: int\n    if LINUX:\n        size: int\n        pss: int\n        shared_clean: int\n        shared_dirty: int\n        private_clean: int\n        private_dirty: int\n        referenced: int\n        anonymous: int\n        swap: int\n    elif BSD:\n        private: int\n        ref_count: int\n        shadow_count: int\n    elif SUNOS:\n        anonymous: int\n        locked: int\n\n\n# ===================================================================\n# --- Process memory_info() / memory_info_ex() / memory_full_info()\n# ===================================================================\n\nif LINUX:\n\n    # psutil.Process().memory_info()\n    class pmem(NamedTuple):\n        rss: int\n        vms: int\n        shared: int\n        text: int\n        data: int\n\n        @property\n        def lib(self):\n            # It has always been 0 since Linux 2.6.\n            msg = \"'lib' field is deprecated and will be removed\"\n            warnings.warn(msg, DeprecationWarning, stacklevel=2)\n            return 0\n\n        @property\n        def dirty(self):\n            # It has always been 0 since Linux 2.6.\n            msg = \"'dirty' field is deprecated and will be removed\"\n            warnings.warn(msg, DeprecationWarning, stacklevel=2)\n            return 0\n\n    # psutil.Process().memory_info_ex()\n    pmem_ex = namedtuple(\n        \"pmem_ex\",\n        pmem._fields\n        + (\n            \"peak_rss\",\n            \"peak_vms\",\n            \"rss_anon\",\n            \"rss_file\",\n            \"rss_shmem\",\n            \"swap\",\n            \"hugetlb\",\n        ),\n    )\n\n    # psutil.Process().memory_full_info()\n    pfullmem = namedtuple(\"pfullmem\", pmem._fields + (\"uss\", \"pss\", \"swap\"))\n\nelif WINDOWS:\n\n    # psutil.Process.memory_info()\n    class pmem(  # noqa: SLOT002\n        namedtuple(\"pmem\", (\"rss\", \"vms\", \"peak_rss\", \"peak_vms\"))\n    ):\n        def __new__(cls, rss, vms, peak_rss, peak_vms, _deprecated=None):\n            inst = super().__new__(cls, rss, vms, peak_rss, peak_vms)\n            inst.__dict__[\"_deprecated\"] = _deprecated or {}\n            return inst\n\n        def __getattr__(self, name):\n            depr = self.__dict__[\"_deprecated\"]\n            if name in depr:\n                msg = f\"pmem.{name} is deprecated\"\n                if name in {\n                    \"paged_pool\",\n                    \"nonpaged_pool\",\n                    \"peak_paged_pool\",\n                    \"peak_nonpaged_pool\",\n                }:\n                    msg += \"; use memory_info_ex() instead\"\n                elif name == \"num_page_faults\":\n                    msg += \"; use page_faults() instead\"\n                warnings.warn(msg, DeprecationWarning, stacklevel=2)\n                return depr[name]\n\n            msg = f\"{self.__class__.__name__} object has no attribute {name!r}\"\n            raise AttributeError(msg)\n\n    # psutil.Process.memory_info_ex()\n    pmem_ex = namedtuple(\n        \"pmem_ex\",\n        pmem._fields\n        + (\n            \"virtual\",\n            \"peak_virtual\",\n            \"paged_pool\",\n            \"nonpaged_pool\",\n            \"peak_paged_pool\",\n            \"peak_nonpaged_pool\",\n        ),\n    )\n\n    # psutil.Process.memory_full_info()\n    pfullmem = namedtuple(\"pfullmem\", pmem._fields + (\"uss\",))\n\nelif MACOS:\n\n    # psutil.Process.memory_info()\n    class pmem(NamedTuple):\n        rss: int\n        vms: int\n\n    # psutil.Process.memory_info_ex()\n    class pmem_ex(NamedTuple):\n        rss: int\n        vms: int\n        peak_rss: int\n        rss_anon: int\n        rss_file: int\n        wired: int\n        compressed: int\n        phys_footprint: int\n\n    # psutil.Process.memory_full_info()\n    pfullmem = namedtuple(\"pfullmem\", pmem._fields + (\"uss\",))\n\nelif BSD:\n\n    # psutil.Process.memory_info()\n    class pmem(NamedTuple):\n        rss: int\n        vms: int\n        text: int\n        data: int\n        stack: int\n        peak_rss: int\n\n    # psutil.Process.memory_info_ex()\n    pmem_ex = pmem\n\n    # psutil.Process.memory_full_info()\n    pfullmem = pmem\n\nelif SUNOS or AIX:\n\n    # psutil.Process.memory_info()\n    class pmem(NamedTuple):\n        rss: int\n        vms: int\n\n    # psutil.Process.memory_info_ex()\n    pmem_ex = pmem\n\n    # psutil.Process.memory_full_info()\n    pfullmem = pmem\n"
  },
  {
    "path": "psutil/_psaix.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'\n# Copyright (c) 2017, Arnon Yaari\n# All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"AIX platform implementation.\"\"\"\n\nimport functools\nimport glob\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom . import _ntuples as ntp\nfrom . import _psposix\nfrom . import _psutil_aix as cext\nfrom ._common import AccessDenied\nfrom ._common import NoSuchProcess\nfrom ._common import ZombieProcess\nfrom ._common import conn_tmap\nfrom ._common import conn_to_ntuple\nfrom ._common import get_procfs_path\nfrom ._common import memoize_when_activated\nfrom ._common import usage_percent\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessStatus\n\n__extra__all__ = [\"PROCFS_PATH\"]\n\n\n# =====================================================================\n# --- globals\n# =====================================================================\n\n\nHAS_THREADS = hasattr(cext, \"proc_threads\")\nHAS_NET_IO_COUNTERS = hasattr(cext, \"net_io_counters\")\nHAS_PROC_IO_COUNTERS = hasattr(cext, \"proc_io_counters\")\n\nPAGE_SIZE = cext.getpagesize()\nAF_LINK = cext.AF_LINK\n\nPROC_STATUSES = {\n    cext.SIDL: ProcessStatus.STATUS_IDLE,\n    cext.SZOMB: ProcessStatus.STATUS_ZOMBIE,\n    cext.SACTIVE: ProcessStatus.STATUS_RUNNING,\n    cext.SSWAP: ProcessStatus.STATUS_RUNNING,  # TODO what status is this?\n    cext.SSTOP: ProcessStatus.STATUS_STOPPED,\n}\n\nTCP_STATUSES = {\n    cext.TCPS_ESTABLISHED: ConnectionStatus.CONN_ESTABLISHED,\n    cext.TCPS_SYN_SENT: ConnectionStatus.CONN_SYN_SENT,\n    cext.TCPS_SYN_RCVD: ConnectionStatus.CONN_SYN_RECV,\n    cext.TCPS_FIN_WAIT_1: ConnectionStatus.CONN_FIN_WAIT1,\n    cext.TCPS_FIN_WAIT_2: ConnectionStatus.CONN_FIN_WAIT2,\n    cext.TCPS_TIME_WAIT: ConnectionStatus.CONN_TIME_WAIT,\n    cext.TCPS_CLOSED: ConnectionStatus.CONN_CLOSE,\n    cext.TCPS_CLOSE_WAIT: ConnectionStatus.CONN_CLOSE_WAIT,\n    cext.TCPS_LAST_ACK: ConnectionStatus.CONN_LAST_ACK,\n    cext.TCPS_LISTEN: ConnectionStatus.CONN_LISTEN,\n    cext.TCPS_CLOSING: ConnectionStatus.CONN_CLOSING,\n    cext.PSUTIL_CONN_NONE: ConnectionStatus.CONN_NONE,\n}\n\nproc_info_map = dict(\n    ppid=0,\n    rss=1,\n    vms=2,\n    create_time=3,\n    nice=4,\n    num_threads=5,\n    status=6,\n    ttynr=7,\n)\n\n\n# =====================================================================\n# --- memory\n# =====================================================================\n\n\ndef virtual_memory():\n    total, avail, free, _pinned, inuse = cext.virtual_mem()\n    percent = usage_percent((total - avail), total, round_=1)\n    return ntp.svmem(total, avail, percent, inuse, free)\n\n\ndef swap_memory():\n    \"\"\"Swap system memory as a (total, used, free, sin, sout) tuple.\"\"\"\n    total, free, sin, sout = cext.swap_mem()\n    used = total - free\n    percent = usage_percent(used, total, round_=1)\n    return ntp.sswap(total, used, free, percent, sin, sout)\n\n\n# =====================================================================\n# --- CPU\n# =====================================================================\n\n\ndef cpu_times():\n    \"\"\"Return system-wide CPU times as a named tuple.\"\"\"\n    ret = cext.per_cpu_times()\n    return ntp.scputimes(*[sum(x) for x in zip(*ret)])\n\n\ndef per_cpu_times():\n    \"\"\"Return system per-CPU times as a list of named tuples.\"\"\"\n    ret = cext.per_cpu_times()\n    return [ntp.scputimes(*x) for x in ret]\n\n\ndef cpu_count_logical():\n    \"\"\"Return the number of logical CPUs in the system.\"\"\"\n    try:\n        return os.sysconf(\"SC_NPROCESSORS_ONLN\")\n    except ValueError:\n        # mimic os.cpu_count() behavior\n        return None\n\n\ndef cpu_count_cores():\n    cmd = [\"lsdev\", \"-Cc\", \"processor\"]\n    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    stdout, stderr = p.communicate()\n    stdout, stderr = (x.decode(sys.stdout.encoding) for x in (stdout, stderr))\n    if p.returncode != 0:\n        msg = f\"{cmd!r} command error\\n{stderr}\"\n        raise RuntimeError(msg)\n    processors = stdout.strip().splitlines()\n    return len(processors) or None\n\n\ndef cpu_stats():\n    \"\"\"Return various CPU stats as a named tuple.\"\"\"\n    ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats()\n    return ntp.scpustats(ctx_switches, interrupts, soft_interrupts, syscalls)\n\n\n# =====================================================================\n# --- disks\n# =====================================================================\n\n\ndisk_io_counters = cext.disk_io_counters\ndisk_usage = _psposix.disk_usage\n\n\ndef disk_partitions(all=False):\n    \"\"\"Return system disk partitions.\"\"\"\n    # TODO - the filtering logic should be better checked so that\n    # it tries to reflect 'df' as much as possible\n    retlist = []\n    partitions = cext.disk_partitions()\n    for partition in partitions:\n        device, mountpoint, fstype, opts = partition\n        if device == 'none':\n            device = ''\n        if not all:\n            # Differently from, say, Linux, we don't have a list of\n            # common fs types so the best we can do, AFAIK, is to\n            # filter by filesystem having a total size > 0.\n            if not disk_usage(mountpoint).total:\n                continue\n        ntuple = ntp.sdiskpart(device, mountpoint, fstype, opts)\n        retlist.append(ntuple)\n    return retlist\n\n\n# =====================================================================\n# --- network\n# =====================================================================\n\n\nnet_if_addrs = cext.net_if_addrs\n\nif HAS_NET_IO_COUNTERS:\n    net_io_counters = cext.net_io_counters\n\n\ndef net_connections(kind, _pid=-1):\n    \"\"\"Return socket connections.  If pid == -1 return system-wide\n    connections (as opposed to connections opened by one process only).\n    \"\"\"\n    families, types = conn_tmap[kind]\n    rawlist = cext.net_connections(_pid)\n    ret = []\n    for item in rawlist:\n        fd, fam, type_, laddr, raddr, status, pid = item\n        if fam not in families:\n            continue\n        if type_ not in types:\n            continue\n        nt = conn_to_ntuple(\n            fd,\n            fam,\n            type_,\n            laddr,\n            raddr,\n            status,\n            TCP_STATUSES,\n            pid=pid if _pid == -1 else None,\n        )\n        ret.append(nt)\n    return ret\n\n\ndef net_if_stats():\n    \"\"\"Get NIC stats (isup, duplex, speed, mtu).\"\"\"\n    duplex_map = {\n        \"Full\": NicDuplex.NIC_DUPLEX_FULL,\n        \"Half\": NicDuplex.NIC_DUPLEX_HALF,\n    }\n    names = {x[0] for x in net_if_addrs()}\n    ret = {}\n    for name in names:\n        mtu = cext.net_if_mtu(name)\n        flags = cext.net_if_flags(name)\n\n        # try to get speed and duplex\n        # TODO: rewrite this in C (entstat forks, so use truss -f to follow.\n        # looks like it is using an undocumented ioctl?)\n        duplex = \"\"\n        speed = 0\n        p = subprocess.Popen(\n            [\"/usr/bin/entstat\", \"-d\", name],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n        )\n        stdout, stderr = p.communicate()\n        stdout, stderr = (\n            x.decode(sys.stdout.encoding) for x in (stdout, stderr)\n        )\n        if p.returncode == 0:\n            re_result = re.search(\n                r\"Running: (\\d+) Mbps.*?(\\w+) Duplex\", stdout\n            )\n            if re_result is not None:\n                speed = int(re_result.group(1))\n                duplex = re_result.group(2)\n\n        output_flags = ','.join(flags)\n        isup = 'running' in flags\n        duplex = duplex_map.get(duplex, NicDuplex.NIC_DUPLEX_UNKNOWN)\n        ret[name] = ntp.snicstats(isup, duplex, speed, mtu, output_flags)\n    return ret\n\n\n# =====================================================================\n# --- other system functions\n# =====================================================================\n\n\ndef boot_time():\n    \"\"\"The system boot time expressed in seconds since the epoch.\"\"\"\n    return cext.boot_time()\n\n\ndef users():\n    \"\"\"Return currently connected users as a list of named tuples.\"\"\"\n    retlist = []\n    rawlist = cext.users()\n    localhost = (':0.0', ':0')\n    for item in rawlist:\n        user, tty, hostname, tstamp, user_process, pid = item\n        # note: the underlying C function includes entries about\n        # system boot, run level and others.  We might want\n        # to use them in the future.\n        if not user_process:\n            continue\n        if hostname in localhost:\n            hostname = 'localhost'\n        nt = ntp.suser(user, tty, hostname, tstamp, pid)\n        retlist.append(nt)\n    return retlist\n\n\n# =====================================================================\n# --- processes\n# =====================================================================\n\n\ndef pids():\n    \"\"\"Returns a list of PIDs currently running on the system.\"\"\"\n    return [int(x) for x in os.listdir(get_procfs_path()) if x.isdigit()]\n\n\ndef pid_exists(pid):\n    \"\"\"Check for the existence of a unix pid.\"\"\"\n    return os.path.exists(os.path.join(get_procfs_path(), str(pid), \"psinfo\"))\n\n\ndef wrap_exceptions(fun):\n    \"\"\"Call callable into a try/except clause and translate ENOENT,\n    EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        pid, ppid, name = self.pid, self._ppid, self._name\n        try:\n            return fun(self, *args, **kwargs)\n        except (FileNotFoundError, ProcessLookupError) as err:\n            # ENOENT (no such file or directory) gets raised on open().\n            # ESRCH (no such process) can get raised on read() if\n            # process is gone in meantime.\n            if not pid_exists(pid):\n                raise NoSuchProcess(pid, name) from err\n            raise ZombieProcess(pid, name, ppid) from err\n        except PermissionError as err:\n            raise AccessDenied(pid, name) from err\n\n    return wrapper\n\n\nclass Process:\n    \"\"\"Wrapper class around underlying C implementation.\"\"\"\n\n    __slots__ = [\"_cache\", \"_name\", \"_ppid\", \"_procfs_path\", \"pid\"]\n\n    def __init__(self, pid):\n        self.pid = pid\n        self._name = None\n        self._ppid = None\n        self._procfs_path = get_procfs_path()\n\n    def oneshot_enter(self):\n        self._proc_oneshot.cache_activate(self)\n        self._proc_cred.cache_activate(self)\n\n    def oneshot_exit(self):\n        self._proc_oneshot.cache_deactivate(self)\n        self._proc_cred.cache_deactivate(self)\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _proc_oneshot(self):\n        return cext.proc_oneshot(self.pid, self._procfs_path)\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _proc_cred(self):\n        return cext.proc_cred(self.pid, self._procfs_path)\n\n    @wrap_exceptions\n    def name(self):\n        if self.pid == 0:\n            return \"swapper\"\n        # note: max 16 characters\n        return cext.proc_name(self.pid, self._procfs_path).rstrip(\"\\x00\")\n\n    @wrap_exceptions\n    def exe(self):\n        # there is no way to get executable path in AIX other than to guess,\n        # and guessing is more complex than what's in the wrapping class\n        cmdline = self.cmdline()\n        if not cmdline:\n            return ''\n        exe = cmdline[0]\n        if os.path.sep in exe:\n            # relative or absolute path\n            if not os.path.isabs(exe):\n                # if cwd has changed, we're out of luck - this may be wrong!\n                exe = os.path.abspath(os.path.join(self.cwd(), exe))\n            if (\n                os.path.isabs(exe)\n                and os.path.isfile(exe)\n                and os.access(exe, os.X_OK)\n            ):\n                return exe\n            # not found, move to search in PATH using basename only\n            exe = os.path.basename(exe)\n        # search for exe name PATH\n        for path in os.environ[\"PATH\"].split(\":\"):\n            possible_exe = os.path.abspath(os.path.join(path, exe))\n            if os.path.isfile(possible_exe) and os.access(\n                possible_exe, os.X_OK\n            ):\n                return possible_exe\n        return ''\n\n    @wrap_exceptions\n    def cmdline(self):\n        return cext.proc_args(self.pid)\n\n    @wrap_exceptions\n    def environ(self):\n        return cext.proc_environ(self.pid)\n\n    @wrap_exceptions\n    def create_time(self):\n        return self._proc_oneshot()[proc_info_map['create_time']]\n\n    @wrap_exceptions\n    def num_threads(self):\n        return self._proc_oneshot()[proc_info_map['num_threads']]\n\n    if HAS_THREADS:\n\n        @wrap_exceptions\n        def threads(self):\n            rawlist = cext.proc_threads(self.pid)\n            retlist = []\n            for thread_id, utime, stime in rawlist:\n                ntuple = ntp.pthread(thread_id, utime, stime)\n                retlist.append(ntuple)\n            # The underlying C implementation retrieves all OS threads\n            # and filters them by PID.  At this point we can't tell whether\n            # an empty list means there were no connections for process or\n            # process is no longer active so we force NSP in case the PID\n            # is no longer there.\n            if not retlist:\n                # will raise NSP if process is gone\n                os.stat(f\"{self._procfs_path}/{self.pid}\")\n            return retlist\n\n    @wrap_exceptions\n    def net_connections(self, kind='inet'):\n        ret = net_connections(kind, _pid=self.pid)\n        # The underlying C implementation retrieves all OS connections\n        # and filters them by PID.  At this point we can't tell whether\n        # an empty list means there were no connections for process or\n        # process is no longer active so we force NSP in case the PID\n        # is no longer there.\n        if not ret:\n            # will raise NSP if process is gone\n            os.stat(f\"{self._procfs_path}/{self.pid}\")\n        return ret\n\n    @wrap_exceptions\n    def nice_get(self):\n        return cext.proc_priority_get(self.pid)\n\n    @wrap_exceptions\n    def nice_set(self, value):\n        return cext.proc_priority_set(self.pid, value)\n\n    @wrap_exceptions\n    def ppid(self):\n        self._ppid = self._proc_oneshot()[proc_info_map['ppid']]\n        return self._ppid\n\n    @wrap_exceptions\n    def uids(self):\n        real, effective, saved, _, _, _ = self._proc_cred()\n        return ntp.puids(real, effective, saved)\n\n    @wrap_exceptions\n    def gids(self):\n        _, _, _, real, effective, saved = self._proc_cred()\n        return ntp.puids(real, effective, saved)\n\n    @wrap_exceptions\n    def cpu_times(self):\n        t = cext.proc_cpu_times(self.pid, self._procfs_path)\n        return ntp.pcputimes(*t)\n\n    @wrap_exceptions\n    def terminal(self):\n        ttydev = self._proc_oneshot()[proc_info_map['ttynr']]\n        # convert from 64-bit dev_t to 32-bit dev_t and then map the device\n        ttydev = ((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF)\n        # try to match rdev of /dev/pts/* files ttydev\n        for dev in glob.glob(\"/dev/**/*\"):\n            if os.stat(dev).st_rdev == ttydev:\n                return dev\n        return None\n\n    @wrap_exceptions\n    def cwd(self):\n        procfs_path = self._procfs_path\n        try:\n            result = os.readlink(f\"{procfs_path}/{self.pid}/cwd\")\n            return result.rstrip('/')\n        except FileNotFoundError:\n            os.stat(f\"{procfs_path}/{self.pid}\")  # raise NSP or AD\n            return \"\"\n\n    @wrap_exceptions\n    def memory_info(self):\n        ret = self._proc_oneshot()\n        rss = ret[proc_info_map['rss']] * 1024\n        vms = ret[proc_info_map['vms']] * 1024\n        return ntp.pmem(rss, vms)\n\n    @wrap_exceptions\n    def status(self):\n        code = self._proc_oneshot()[proc_info_map['status']]\n        # XXX is '?' legit? (we're not supposed to return it anyway)\n        return PROC_STATUSES.get(code, '?')\n\n    def open_files(self):\n        # TODO rewrite without using procfiles (stat /proc/pid/fd/* and then\n        # find matching name of the inode)\n        p = subprocess.Popen(\n            [\"/usr/bin/procfiles\", \"-n\", str(self.pid)],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n        )\n        stdout, stderr = p.communicate()\n        stdout, stderr = (\n            x.decode(sys.stdout.encoding) for x in (stdout, stderr)\n        )\n        if \"no such process\" in stderr.lower():\n            raise NoSuchProcess(self.pid, self._name)\n        procfiles = re.findall(r\"(\\d+): S_IFREG.*name:(.*)\\n\", stdout)\n        retlist = []\n        for fd, path in procfiles:\n            path = path.strip()\n            if path.startswith(\"//\"):\n                path = path[1:]\n            if path.lower() == \"cannot be retrieved\":\n                continue\n            retlist.append(ntp.popenfile(path, int(fd)))\n        return retlist\n\n    @wrap_exceptions\n    def num_fds(self):\n        if self.pid == 0:  # no /proc/0/fd\n            return 0\n        return len(os.listdir(f\"{self._procfs_path}/{self.pid}/fd\"))\n\n    @wrap_exceptions\n    def num_ctx_switches(self):\n        return ntp.pctxsw(*cext.proc_num_ctx_switches(self.pid))\n\n    @wrap_exceptions\n    def wait(self, timeout=None):\n        return _psposix.wait_pid(self.pid, timeout)\n\n    if HAS_PROC_IO_COUNTERS:\n\n        @wrap_exceptions\n        def io_counters(self):\n            try:\n                rc, wc, rb, wb = cext.proc_io_counters(self.pid)\n            except OSError as err:\n                # if process is terminated, proc_io_counters returns OSError\n                # instead of NSP\n                if not pid_exists(self.pid):\n                    raise NoSuchProcess(self.pid, self._name) from err\n                raise\n            return ntp.pio(rc, wc, rb, wb)\n"
  },
  {
    "path": "psutil/_psbsd.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"FreeBSD, OpenBSD and NetBSD platforms implementation.\"\"\"\n\nimport contextlib\nimport errno\nimport functools\nimport os\nfrom collections import defaultdict\nfrom collections import namedtuple\nfrom xml.etree import ElementTree  # noqa: ICN001\n\nfrom . import _ntuples as ntp\nfrom . import _psposix\nfrom . import _psutil_bsd as cext\nfrom ._common import FREEBSD\nfrom ._common import NETBSD\nfrom ._common import OPENBSD\nfrom ._common import AccessDenied\nfrom ._common import NoSuchProcess\nfrom ._common import ZombieProcess\nfrom ._common import conn_tmap\nfrom ._common import conn_to_ntuple\nfrom ._common import debug\nfrom ._common import memoize_when_activated\nfrom ._enums import BatteryTime\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessStatus\n\n__extra__all__ = []\n\n\n# =====================================================================\n# --- globals\n# =====================================================================\n\n\nif FREEBSD:\n    PROC_STATUSES = {\n        cext.SIDL: ProcessStatus.STATUS_IDLE,\n        cext.SRUN: ProcessStatus.STATUS_RUNNING,\n        cext.SSLEEP: ProcessStatus.STATUS_SLEEPING,\n        cext.SSTOP: ProcessStatus.STATUS_STOPPED,\n        cext.SZOMB: ProcessStatus.STATUS_ZOMBIE,\n        cext.SWAIT: ProcessStatus.STATUS_WAITING,\n        cext.SLOCK: ProcessStatus.STATUS_LOCKED,\n    }\nelif OPENBSD:\n    PROC_STATUSES = {\n        cext.SIDL: ProcessStatus.STATUS_IDLE,\n        cext.SSLEEP: ProcessStatus.STATUS_SLEEPING,\n        cext.SSTOP: ProcessStatus.STATUS_STOPPED,\n        # According to /usr/include/sys/proc.h SZOMB is unused.\n        # test_zombie_process() shows that SDEAD is the right\n        # equivalent. Also it appears there's no equivalent of\n        # psutil.STATUS_DEAD. SDEAD really means STATUS_ZOMBIE.\n        # cext.SZOMB: ProcStatus.STATUS_ZOMBIE,\n        cext.SDEAD: ProcessStatus.STATUS_ZOMBIE,\n        cext.SZOMB: ProcessStatus.STATUS_ZOMBIE,\n        # From http://www.eecs.harvard.edu/~margo/cs161/videos/proc.h.txt\n        # OpenBSD has SRUN and SONPROC: SRUN indicates that a process\n        # is runnable but *not* yet running, i.e. is on a run queue.\n        # SONPROC indicates that the process is actually executing on\n        # a CPU, i.e. it is no longer on a run queue.\n        # As such we'll map SRUN to STATUS_WAKING and SONPROC to\n        # STATUS_RUNNING\n        cext.SRUN: ProcessStatus.STATUS_WAKING,\n        cext.SONPROC: ProcessStatus.STATUS_RUNNING,\n    }\nelif NETBSD:\n    PROC_STATUSES = {\n        cext.SIDL: ProcessStatus.STATUS_IDLE,\n        cext.SSLEEP: ProcessStatus.STATUS_SLEEPING,\n        cext.SSTOP: ProcessStatus.STATUS_STOPPED,\n        cext.SZOMB: ProcessStatus.STATUS_ZOMBIE,\n        cext.SRUN: ProcessStatus.STATUS_WAKING,\n        cext.SONPROC: ProcessStatus.STATUS_RUNNING,\n    }\n\nTCP_STATUSES = {\n    cext.TCPS_ESTABLISHED: ConnectionStatus.CONN_ESTABLISHED,\n    cext.TCPS_SYN_SENT: ConnectionStatus.CONN_SYN_SENT,\n    cext.TCPS_SYN_RECEIVED: ConnectionStatus.CONN_SYN_RECV,\n    cext.TCPS_FIN_WAIT_1: ConnectionStatus.CONN_FIN_WAIT1,\n    cext.TCPS_FIN_WAIT_2: ConnectionStatus.CONN_FIN_WAIT2,\n    cext.TCPS_TIME_WAIT: ConnectionStatus.CONN_TIME_WAIT,\n    cext.TCPS_CLOSED: ConnectionStatus.CONN_CLOSE,\n    cext.TCPS_CLOSE_WAIT: ConnectionStatus.CONN_CLOSE_WAIT,\n    cext.TCPS_LAST_ACK: ConnectionStatus.CONN_LAST_ACK,\n    cext.TCPS_LISTEN: ConnectionStatus.CONN_LISTEN,\n    cext.TCPS_CLOSING: ConnectionStatus.CONN_CLOSING,\n    cext.PSUTIL_CONN_NONE: ConnectionStatus.CONN_NONE,\n}\n\nPAGESIZE = cext.getpagesize()\nAF_LINK = cext.AF_LINK\n\nHAS_PROC_NUM_THREADS = hasattr(cext, \"proc_num_threads\")\n\n\n# =====================================================================\n# --- memory\n# =====================================================================\n\n\ndef virtual_memory():\n    d = cext.virtual_mem()\n    return ntp.svmem(**d)\n\n\ndef swap_memory():\n    \"\"\"System swap memory as a (total, used, free, percent, sin, sout)\n    named tuple. sin and sout are always 0 on OpenBSD\n    \"\"\"\n    d = cext.swap_mem()\n    return ntp.sswap(**d)\n\n\n# malloc / heap functions (FreeBSD / NetBSD)\nif hasattr(cext, \"heap_info\"):\n    heap_info = cext.heap_info\n    heap_trim = cext.heap_trim\n\n\n# =====================================================================\n# --- CPU\n# =====================================================================\n\n\ndef cpu_times():\n    \"\"\"Return system per-CPU times as a named tuple.\"\"\"\n    user, nice, system, idle, irq = cext.cpu_times()\n    return ntp.scputimes(user, system, idle, nice, irq)\n\n\ndef per_cpu_times():\n    \"\"\"Return system CPU times as a named tuple.\"\"\"\n    ret = []\n    for cpu_t in cext.per_cpu_times():\n        user, nice, system, idle, irq = cpu_t\n        item = ntp.scputimes(user, system, idle, nice, irq)\n        ret.append(item)\n    return ret\n\n\ndef cpu_count_logical():\n    \"\"\"Return the number of logical CPUs in the system.\"\"\"\n    return cext.cpu_count_logical()\n\n\nif OPENBSD or NETBSD:\n\n    def cpu_count_cores():\n        # OpenBSD and NetBSD do not implement this.\n        return 1 if cpu_count_logical() == 1 else None\n\nelse:\n\n    def cpu_count_cores():\n        \"\"\"Return the number of CPU cores in the system.\"\"\"\n        # From the C module we'll get an XML string similar to this:\n        # http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html\n        # We may get None in case \"sysctl kern.sched.topology_spec\"\n        # is not supported on this BSD version, in which case we'll mimic\n        # os.cpu_count() and return None.\n        ret = None\n        s = cext.cpu_topology()\n        if s is not None:\n            # get rid of padding chars appended at the end of the string\n            index = s.rfind(\"</groups>\")\n            if index != -1:\n                s = s[: index + 9]\n                root = ElementTree.fromstring(s)\n                try:\n                    ret = len(root.findall('group/children/group/cpu')) or None\n                finally:\n                    # needed otherwise it will memleak\n                    root.clear()\n        if not ret:\n            # If logical CPUs == 1 it's obvious we' have only 1 core.\n            if cpu_count_logical() == 1:\n                return 1\n        return ret\n\n\ndef cpu_stats():\n    \"\"\"Return various CPU stats as a named tuple.\"\"\"\n    if FREEBSD:\n        # Note: the C ext is returning some metrics we are not exposing:\n        # traps.\n        ctxsw, intrs, soft_intrs, syscalls, _traps = cext.cpu_stats()\n    elif NETBSD:\n        # XXX\n        # Note about intrs: the C extension returns 0. intrs\n        # can be determined via /proc/stat; it has the same value as\n        # soft_intrs thought so the kernel is faking it (?).\n        #\n        # Note about syscalls: the C extension always sets it to 0 (?).\n        #\n        # Note: the C ext is returning some metrics we are not exposing:\n        # traps, faults and forks.\n        ctxsw, intrs, soft_intrs, syscalls, _traps, _faults, _forks = (\n            cext.cpu_stats()\n        )\n        with open('/proc/stat', 'rb') as f:\n            for line in f:\n                if line.startswith(b'intr'):\n                    intrs = int(line.split()[1])\n    elif OPENBSD:\n        # Note: the C ext is returning some metrics we are not exposing:\n        # traps, faults and forks.\n        ctxsw, intrs, soft_intrs, syscalls, _traps, _faults, _forks = (\n            cext.cpu_stats()\n        )\n    return ntp.scpustats(ctxsw, intrs, soft_intrs, syscalls)\n\n\nif FREEBSD:\n\n    def cpu_freq():\n        \"\"\"Return frequency metrics for CPUs. As of Dec 2018 only\n        CPU 0 appears to be supported by FreeBSD and all other cores\n        match the frequency of CPU 0.\n        \"\"\"\n        ret = []\n        num_cpus = cpu_count_logical()\n        for cpu in range(num_cpus):\n            try:\n                current, available_freq = cext.cpu_freq(cpu)\n            except NotImplementedError:\n                continue\n            if available_freq:\n                try:\n                    min_freq = int(available_freq.split(\" \")[-1].split(\"/\")[0])\n                except (IndexError, ValueError):\n                    min_freq = None\n                try:\n                    max_freq = int(available_freq.split(\" \")[0].split(\"/\")[0])\n                except (IndexError, ValueError):\n                    max_freq = None\n            ret.append(ntp.scpufreq(current, min_freq, max_freq))\n        return ret\n\nelif OPENBSD:\n\n    def cpu_freq():\n        curr = float(cext.cpu_freq())\n        return [ntp.scpufreq(curr, 0.0, 0.0)]\n\n\n# =====================================================================\n# --- disks\n# =====================================================================\n\n\ndef disk_partitions(all=False):\n    \"\"\"Return mounted disk partitions as a list of named tuples.\n    'all' argument is ignored, see:\n    https://github.com/giampaolo/psutil/issues/906.\n    \"\"\"\n    retlist = []\n    partitions = cext.disk_partitions()\n    for partition in partitions:\n        device, mountpoint, fstype, opts = partition\n        ntuple = ntp.sdiskpart(device, mountpoint, fstype, opts)\n        retlist.append(ntuple)\n    return retlist\n\n\ndisk_usage = _psposix.disk_usage\ndisk_io_counters = cext.disk_io_counters\n\n\n# =====================================================================\n# --- network\n# =====================================================================\n\n\nnet_io_counters = cext.net_io_counters\nnet_if_addrs = cext.net_if_addrs\n\n\ndef net_if_stats():\n    \"\"\"Get NIC stats (isup, duplex, speed, mtu).\"\"\"\n    names = net_io_counters().keys()\n    ret = {}\n    for name in names:\n        try:\n            mtu = cext.net_if_mtu(name)\n            flags = cext.net_if_flags(name)\n            duplex, speed = cext.net_if_duplex_speed(name)\n        except OSError as err:\n            # https://github.com/giampaolo/psutil/issues/1279\n            if err.errno != errno.ENODEV:\n                raise\n        else:\n            duplex = NicDuplex(duplex)\n            output_flags = ','.join(flags)\n            isup = 'running' in flags\n            ret[name] = ntp.snicstats(isup, duplex, speed, mtu, output_flags)\n    return ret\n\n\ndef net_connections(kind):\n    \"\"\"System-wide network connections.\"\"\"\n    families, types = conn_tmap[kind]\n    ret = set()\n    if OPENBSD:\n        rawlist = cext.net_connections(-1, families, types)\n    elif NETBSD:\n        rawlist = cext.net_connections(-1, kind)\n    else:  # FreeBSD\n        rawlist = cext.net_connections(families, types)\n\n    for item in rawlist:\n        fd, fam, type, laddr, raddr, status, pid = item\n        nt = conn_to_ntuple(\n            fd, fam, type, laddr, raddr, status, TCP_STATUSES, pid\n        )\n        ret.add(nt)\n    return list(ret)\n\n\n# =====================================================================\n#  --- sensors\n# =====================================================================\n\n\nif FREEBSD:\n\n    def sensors_battery():\n        \"\"\"Return battery info.\"\"\"\n        try:\n            percent, minsleft, power_plugged = cext.sensors_battery()\n        except NotImplementedError:\n            # See: https://github.com/giampaolo/psutil/issues/1074\n            return None\n        power_plugged = power_plugged == 1\n        if power_plugged:\n            secsleft = BatteryTime.POWER_TIME_UNLIMITED\n        elif minsleft == -1:\n            secsleft = BatteryTime.POWER_TIME_UNKNOWN\n        else:\n            secsleft = minsleft * 60\n        return ntp.sbattery(percent, secsleft, power_plugged)\n\n    def sensors_temperatures():\n        \"\"\"Return CPU cores temperatures if available, else an empty dict.\"\"\"\n        ret = defaultdict(list)\n        num_cpus = cpu_count_logical()\n        for cpu in range(num_cpus):\n            try:\n                current, high = cext.sensors_cpu_temperature(cpu)\n                if high <= 0:\n                    high = None\n                name = f\"Core {cpu}\"\n                ret[\"coretemp\"].append(ntp.shwtemp(name, current, high, high))\n            except NotImplementedError:\n                pass\n\n        return ret\n\n\n# =====================================================================\n#  --- other system functions\n# =====================================================================\n\n\ndef boot_time():\n    \"\"\"The system boot time expressed in seconds since the epoch.\"\"\"\n    return cext.boot_time()\n\n\nif NETBSD:\n\n    try:\n        INIT_BOOT_TIME = boot_time()\n    except Exception as err:  # noqa: BLE001\n        # Don't want to crash at import time.\n        debug(f\"ignoring exception on import: {err!r}\")\n        INIT_BOOT_TIME = 0\n\n    def adjust_proc_create_time(ctime):\n        \"\"\"Account for system clock updates.\"\"\"\n        if INIT_BOOT_TIME == 0:\n            return ctime\n\n        diff = INIT_BOOT_TIME - boot_time()\n        if diff == 0 or abs(diff) < 1:\n            return ctime\n\n        debug(\"system clock was updated; adjusting process create_time()\")\n        if diff < 0:\n            return ctime - diff\n        return ctime + diff\n\n\ndef users():\n    \"\"\"Return currently connected users as a list of named tuples.\"\"\"\n    retlist = []\n    rawlist = cext.users()\n    for item in rawlist:\n        user, tty, hostname, tstamp, pid = item\n        if tty == '~':\n            continue  # reboot or shutdown\n        nt = ntp.suser(user, tty or None, hostname, tstamp, pid)\n        retlist.append(nt)\n    return retlist\n\n\n# =====================================================================\n# --- processes\n# =====================================================================\n\n\n@functools.lru_cache\ndef _pid_0_exists():\n    try:\n        Process(0).name()\n    except NoSuchProcess:\n        return False\n    except AccessDenied:\n        return True\n    else:\n        return True\n\n\ndef pids():\n    \"\"\"Returns a list of PIDs currently running on the system.\"\"\"\n    ret = cext.pids()\n    if OPENBSD and (0 not in ret) and _pid_0_exists():\n        # On OpenBSD the kernel does not return PID 0 (neither does\n        # ps) but it's actually querable (Process(0) will succeed).\n        ret.insert(0, 0)\n    return ret\n\n\nif NETBSD:\n\n    def pid_exists(pid):\n        exists = _psposix.pid_exists(pid)\n        if not exists:\n            # We do this because _psposix.pid_exists() lies in case of\n            # zombie processes.\n            return pid in pids()\n        else:\n            return True\n\nelif OPENBSD:\n\n    def pid_exists(pid):\n        exists = _psposix.pid_exists(pid)\n        if not exists:\n            return False\n        else:\n            # OpenBSD seems to be the only BSD platform where\n            # _psposix.pid_exists() returns True for thread IDs (tids),\n            # so we can't use it.\n            return pid in pids()\n\nelse:  # FreeBSD\n    pid_exists = _psposix.pid_exists\n\n\ndef wrap_exceptions(fun):\n    \"\"\"Decorator which translates bare OSError exceptions into\n    NoSuchProcess and AccessDenied.\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        pid, ppid, name = self.pid, self._ppid, self._name\n        try:\n            return fun(self, *args, **kwargs)\n        except ProcessLookupError as err:\n            if cext.proc_is_zombie(pid):\n                raise ZombieProcess(pid, name, ppid) from err\n            raise NoSuchProcess(pid, name) from err\n        except PermissionError as err:\n            raise AccessDenied(pid, name) from err\n        except cext.ZombieProcessError as err:\n            raise ZombieProcess(pid, name, ppid) from err\n        except OSError as err:\n            if pid == 0 and 0 in pids():\n                raise AccessDenied(pid, name) from err\n            raise err from None\n\n    return wrapper\n\n\n@contextlib.contextmanager\ndef wrap_exceptions_procfs(inst):\n    \"\"\"Same as above, for routines relying on reading /proc fs.\"\"\"\n    pid, name, ppid = inst.pid, inst._name, inst._ppid\n    try:\n        yield\n    except (ProcessLookupError, FileNotFoundError) as err:\n        # ENOENT (no such file or directory) gets raised on open().\n        # ESRCH (no such process) can get raised on read() if\n        # process is gone in meantime.\n        if cext.proc_is_zombie(inst.pid):\n            raise ZombieProcess(pid, name, ppid) from err\n        else:\n            raise NoSuchProcess(pid, name) from err\n    except PermissionError as err:\n        raise AccessDenied(pid, name) from err\n\n\nclass Process:\n    \"\"\"Wrapper class around underlying C implementation.\"\"\"\n\n    __slots__ = [\"_cache\", \"_name\", \"_ppid\", \"pid\"]\n\n    def __init__(self, pid):\n        self.pid = pid\n        self._name = None\n        self._ppid = None\n\n    def _assert_alive(self):\n        \"\"\"Raise NSP if the process disappeared on us.\"\"\"\n        # For those C function who do not raise NSP, possibly returning\n        # incorrect or incomplete result.\n        cext.proc_name(self.pid)\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def oneshot(self):\n        \"\"\"Retrieves multiple process info in one shot as a raw dict.\"\"\"\n        return cext.proc_oneshot_kinfo(self.pid)\n\n    def oneshot_enter(self):\n        self.oneshot.cache_activate(self)\n\n    def oneshot_exit(self):\n        self.oneshot.cache_deactivate(self)\n\n    @wrap_exceptions\n    def name(self):\n        name = self.oneshot()[\"name\"]\n        return name if name is not None else cext.proc_name(self.pid)\n\n    @wrap_exceptions\n    def exe(self):\n        if FREEBSD:\n            if self.pid == 0:\n                return ''  # else NSP\n            return cext.proc_exe(self.pid)\n        elif NETBSD:\n            if self.pid == 0:\n                # /proc/0 dir exists but /proc/0/exe doesn't\n                return \"\"\n            with wrap_exceptions_procfs(self):\n                return os.readlink(f\"/proc/{self.pid}/exe\")\n        else:\n            # OpenBSD: exe cannot be determined; references:\n            # https://chromium.googlesource.com/chromium/src/base/+/master/base_paths_posix.cc\n            # We try our best guess by using which against the first\n            # cmdline arg (may return None).\n            import shutil\n\n            cmdline = self.cmdline()\n            if cmdline:\n                return shutil.which(cmdline[0]) or \"\"\n            else:\n                return \"\"\n\n    @wrap_exceptions\n    def cmdline(self):\n        if OPENBSD and self.pid == 0:\n            return []  # ...else it crashes\n        elif NETBSD:\n            # XXX - most of the times the underlying sysctl() call on\n            # NetBSD and OpenBSD returns a truncated string. Also\n            # /proc/pid/cmdline behaves the same so it looks like this\n            # is a kernel bug.\n            try:\n                return cext.proc_cmdline(self.pid)\n            except OSError as err:\n                if err.errno == errno.EINVAL:\n                    pid, name, ppid = self.pid, self._name, self._ppid\n                    if cext.proc_is_zombie(self.pid):\n                        raise ZombieProcess(pid, name, ppid) from err\n                    if not pid_exists(self.pid):\n                        raise NoSuchProcess(pid, name, ppid) from err\n                    # XXX: this happens with unicode tests. It means the C\n                    # routine is unable to decode invalid unicode chars.\n                    debug(f\"ignoring {err!r} and returning an empty list\")\n                    return []\n                else:\n                    raise\n        else:\n            return cext.proc_cmdline(self.pid)\n\n    @wrap_exceptions\n    def environ(self):\n        return cext.proc_environ(self.pid)\n\n    @wrap_exceptions\n    def terminal(self):\n        tty_nr = self.oneshot()[\"ttynr\"]\n        tmap = _psposix.get_terminal_map()\n        try:\n            return tmap[tty_nr]\n        except KeyError:\n            return None\n\n    @wrap_exceptions\n    def ppid(self):\n        self._ppid = self.oneshot()[\"ppid\"]\n        return self._ppid\n\n    @wrap_exceptions\n    def uids(self):\n        d = self.oneshot()\n        return ntp.puids(d[\"real_uid\"], d[\"effective_uid\"], d[\"saved_uid\"])\n\n    @wrap_exceptions\n    def gids(self):\n        d = self.oneshot()\n        return ntp.pgids(d[\"real_gid\"], d[\"effective_gid\"], d[\"saved_gid\"])\n\n    @wrap_exceptions\n    def cpu_times(self):\n        d = self.oneshot()\n        return ntp.pcputimes(\n            d[\"user_time\"], d[\"sys_time\"], d[\"ch_user_time\"], d[\"ch_sys_time\"]\n        )\n\n    if FREEBSD:\n\n        @wrap_exceptions\n        def cpu_num(self):\n            return self.oneshot()[\"cpunum\"]\n\n    @wrap_exceptions\n    def memory_info(self):\n        d = self.oneshot()\n        return ntp.pmem(\n            rss=d[\"rss\"],\n            vms=d[\"vms\"],\n            text=d[\"memtext\"],\n            data=d[\"memdata\"],\n            stack=d[\"memstack\"],\n            peak_rss=d[\"peak_rss\"],\n        )\n\n    @wrap_exceptions\n    def create_time(self, monotonic=False):\n        ctime = self.oneshot()[\"create_time\"]\n        if NETBSD and not monotonic:\n            # NetBSD: ctime subject to system clock updates.\n            ctime = adjust_proc_create_time(ctime)\n        return ctime\n\n    @wrap_exceptions\n    def num_threads(self):\n        if HAS_PROC_NUM_THREADS:\n            # FreeBSD / NetBSD\n            return cext.proc_num_threads(self.pid)\n        else:\n            return len(self.threads())\n\n    @wrap_exceptions\n    def num_ctx_switches(self):\n        d = self.oneshot()\n        return ntp.pctxsw(d[\"ctx_switches_vol\"], d[\"ctx_switches_unvol\"])\n\n    @wrap_exceptions\n    def page_faults(self):\n        d = self.oneshot()\n        return ntp.ppagefaults(d[\"min_faults\"], d[\"maj_faults\"])\n\n    @wrap_exceptions\n    def threads(self):\n        # Note: on OpenSBD this (/dev/mem) requires root access.\n        rawlist = cext.proc_threads(self.pid)\n        retlist = []\n        for thread_id, utime, stime in rawlist:\n            ntuple = ntp.pthread(thread_id, utime, stime)\n            retlist.append(ntuple)\n        if OPENBSD:\n            self._assert_alive()\n        return retlist\n\n    @wrap_exceptions\n    def net_connections(self, kind='inet'):\n        families, types = conn_tmap[kind]\n        ret = []\n\n        if NETBSD:\n            rawlist = cext.net_connections(self.pid, kind)\n        elif OPENBSD:\n            rawlist = cext.net_connections(self.pid, families, types)\n        else:\n            rawlist = cext.proc_net_connections(self.pid, families, types)\n\n        for item in rawlist:\n            fd, fam, type, laddr, raddr, status = item[:6]\n            if FREEBSD:\n                if (fam not in families) or (type not in types):\n                    continue\n            nt = conn_to_ntuple(\n                fd, fam, type, laddr, raddr, status, TCP_STATUSES\n            )\n            ret.append(nt)\n\n        self._assert_alive()\n        return ret\n\n    @wrap_exceptions\n    def wait(self, timeout=None):\n        return _psposix.wait_pid(self.pid, timeout)\n\n    @wrap_exceptions\n    def nice_get(self):\n        return cext.proc_priority_get(self.pid)\n\n    @wrap_exceptions\n    def nice_set(self, value):\n        return cext.proc_priority_set(self.pid, value)\n\n    @wrap_exceptions\n    def status(self):\n        code = self.oneshot()[\"status\"]\n        # XXX is '?' legit? (we're not supposed to return it anyway)\n        return PROC_STATUSES.get(code, '?')\n\n    @wrap_exceptions\n    def io_counters(self):\n        d = self.oneshot()\n        return ntp.pio(d[\"read_io_count\"], d[\"write_io_count\"], -1, -1)\n\n    @wrap_exceptions\n    def cwd(self):\n        \"\"\"Return process current working directory.\"\"\"\n        # sometimes we get an empty string, in which case we turn\n        # it into None\n        if OPENBSD and self.pid == 0:\n            return \"\"  # ...else it would raise EINVAL\n        return cext.proc_cwd(self.pid)\n\n    nt_mmap_grouped = namedtuple(\n        'mmap', 'path rss, private, ref_count, shadow_count'\n    )\n    nt_mmap_ext = namedtuple(\n        'mmap', 'addr, perms path rss, private, ref_count, shadow_count'\n    )\n\n    @wrap_exceptions\n    def open_files(self):\n        \"\"\"Return files opened by process as a list of named tuples.\"\"\"\n        rawlist = cext.proc_open_files(self.pid)\n        return [ntp.popenfile(path, fd) for path, fd in rawlist]\n\n    @wrap_exceptions\n    def num_fds(self):\n        \"\"\"Return the number of file descriptors opened by this process.\"\"\"\n        ret = cext.proc_num_fds(self.pid)\n        if NETBSD:\n            self._assert_alive()\n        return ret\n\n    # --- FreeBSD only APIs\n\n    if FREEBSD:\n\n        @wrap_exceptions\n        def cpu_affinity_get(self):\n            return cext.proc_cpu_affinity_get(self.pid)\n\n        @wrap_exceptions\n        def cpu_affinity_set(self, cpus):\n            # Pre-emptively check if CPUs are valid because the C\n            # function has a weird behavior in case of invalid CPUs,\n            # see: https://github.com/giampaolo/psutil/issues/586\n            allcpus = set(range(len(per_cpu_times())))\n            for cpu in cpus:\n                if cpu not in allcpus:\n                    msg = f\"invalid CPU {cpu!r} (choose between {allcpus})\"\n                    raise ValueError(msg)\n            try:\n                cext.proc_cpu_affinity_set(self.pid, cpus)\n            except OSError as err:\n                # 'man cpuset_setaffinity' about EDEADLK:\n                # <<the call would leave a thread without a valid CPU to run\n                # on because the set does not overlap with the thread's\n                # anonymous mask>>\n                if err.errno in {errno.EINVAL, errno.EDEADLK}:\n                    for cpu in cpus:\n                        if cpu not in allcpus:\n                            msg = (\n                                f\"invalid CPU {cpu!r} (choose between\"\n                                f\" {allcpus})\"\n                            )\n                            raise ValueError(msg) from err\n                raise\n\n        @wrap_exceptions\n        def memory_maps(self):\n            return cext.proc_memory_maps(self.pid)\n\n        @wrap_exceptions\n        def rlimit(self, resource, limits=None):\n            if limits is None:\n                return cext.proc_getrlimit(self.pid, resource)\n            else:\n                if len(limits) != 2:\n                    msg = (\n                        \"second argument must be a (soft, hard) tuple, got\"\n                        f\" {limits!r}\"\n                    )\n                    raise ValueError(msg)\n                soft, hard = limits\n                return cext.proc_setrlimit(self.pid, resource, soft, hard)\n"
  },
  {
    "path": "psutil/_pslinux.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Linux platform implementation.\"\"\"\n\nimport base64\nimport collections\nimport enum\nimport errno\nimport functools\nimport glob\nimport os\nimport re\nimport resource\nimport socket\nimport struct\nimport sys\nimport warnings\nfrom collections import defaultdict\n\nfrom . import _ntuples as ntp\nfrom . import _psposix\nfrom . import _psutil_linux as cext\nfrom ._common import ENCODING\nfrom ._common import AccessDenied\nfrom ._common import NoSuchProcess\nfrom ._common import ZombieProcess\nfrom ._common import bcat\nfrom ._common import cat\nfrom ._common import debug\nfrom ._common import decode\nfrom ._common import get_procfs_path\nfrom ._common import isfile_strict\nfrom ._common import memoize_when_activated\nfrom ._common import open_binary\nfrom ._common import open_text\nfrom ._common import parse_environ_block\nfrom ._common import path_exists_strict\nfrom ._common import socktype_to_enum\nfrom ._common import supports_ipv6\nfrom ._common import usage_percent\nfrom ._enums import BatteryTime\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessIOPriority\nfrom ._enums import ProcessStatus\n\n__extra__all__ = ['PROCFS_PATH']\n\n\n# =====================================================================\n# --- globals\n# =====================================================================\n\n\nPOWER_SUPPLY_PATH = \"/sys/class/power_supply\"\nHAS_PROC_SMAPS = os.path.exists(f\"/proc/{os.getpid()}/smaps\")\nHAS_PROC_SMAPS_ROLLUP = os.path.exists(f\"/proc/{os.getpid()}/smaps_rollup\")\nHAS_PROC_IO_PRIORITY = hasattr(cext, \"proc_ioprio_get\")\nHAS_CPU_AFFINITY = hasattr(cext, \"proc_cpu_affinity_get\")\n\n# Number of clock ticks per second\nCLOCK_TICKS = os.sysconf(\"SC_CLK_TCK\")\nPAGESIZE = cext.getpagesize()\nLITTLE_ENDIAN = sys.byteorder == 'little'\nUNSET = object()\n\n# \"man iostat\" states that sectors are equivalent with blocks and have\n# a size of 512 bytes. Despite this value can be queried at runtime\n# via /sys/block/{DISK}/queue/hw_sector_size and results may vary\n# between 1k, 2k, or 4k... 512 appears to be a magic constant used\n# throughout Linux source code:\n# * https://stackoverflow.com/a/38136179/376587\n# * https://lists.gt.net/linux/kernel/2241060\n# * https://github.com/giampaolo/psutil/issues/1305\n# * https://github.com/torvalds/linux/blob/\n#     4f671fe2f9523a1ea206f63fe60a7c7b3a56d5c7/include/linux/bio.h#L99\n# * https://lkml.org/lkml/2015/8/17/234\nDISK_SECTOR_SIZE = 512\n\nAddressFamily = enum.IntEnum(\n    'AddressFamily', {'AF_LINK': int(socket.AF_PACKET)}\n)\nAF_LINK = AddressFamily.AF_LINK\n\n\n# See:\n# https://github.com/torvalds/linux/blame/master/fs/proc/array.c\n# ...and (TASK_* constants):\n# https://github.com/torvalds/linux/blob/master/include/linux/sched.h\nPROC_STATUSES = {\n    \"R\": ProcessStatus.STATUS_RUNNING,\n    \"S\": ProcessStatus.STATUS_SLEEPING,\n    \"D\": ProcessStatus.STATUS_DISK_SLEEP,\n    \"T\": ProcessStatus.STATUS_STOPPED,\n    \"t\": ProcessStatus.STATUS_TRACING_STOP,\n    \"Z\": ProcessStatus.STATUS_ZOMBIE,\n    \"X\": ProcessStatus.STATUS_DEAD,\n    \"x\": ProcessStatus.STATUS_DEAD,\n    \"K\": ProcessStatus.STATUS_WAKE_KILL,\n    \"W\": ProcessStatus.STATUS_WAKING,\n    \"I\": ProcessStatus.STATUS_IDLE,\n    \"P\": ProcessStatus.STATUS_PARKED,\n}\n\n# https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h\nTCP_STATUSES = {\n    \"01\": ConnectionStatus.CONN_ESTABLISHED,\n    \"02\": ConnectionStatus.CONN_SYN_SENT,\n    \"03\": ConnectionStatus.CONN_SYN_RECV,\n    \"04\": ConnectionStatus.CONN_FIN_WAIT1,\n    \"05\": ConnectionStatus.CONN_FIN_WAIT2,\n    \"06\": ConnectionStatus.CONN_TIME_WAIT,\n    \"07\": ConnectionStatus.CONN_CLOSE,\n    \"08\": ConnectionStatus.CONN_CLOSE_WAIT,\n    \"09\": ConnectionStatus.CONN_LAST_ACK,\n    \"0A\": ConnectionStatus.CONN_LISTEN,\n    \"0B\": ConnectionStatus.CONN_CLOSING,\n}\n\n\n# =====================================================================\n# --- utils\n# =====================================================================\n\n\ndef readlink(path):\n    \"\"\"Wrapper around os.readlink().\"\"\"\n    assert isinstance(path, str), path\n    path = os.readlink(path)\n    # readlink() might return paths containing null bytes ('\\x00')\n    # resulting in \"TypeError: must be encoded string without NULL\n    # bytes, not str\" errors when the string is passed to other\n    # fs-related functions (os.*, open(), ...).\n    # Apparently everything after '\\x00' is garbage (we can have\n    # ' (deleted)', 'new' and possibly others), see:\n    # https://github.com/giampaolo/psutil/issues/717\n    path = path.split('\\x00')[0]\n    # Certain paths have ' (deleted)' appended. Usually this is\n    # bogus as the file actually exists. Even if it doesn't we\n    # don't care.\n    if path.endswith(' (deleted)') and not path_exists_strict(path):\n        path = path[:-10]\n    return path\n\n\ndef file_flags_to_mode(flags):\n    \"\"\"Convert file's open() flags into a readable string.\n    Used by Process.open_files().\n    \"\"\"\n    modes_map = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}\n    mode = modes_map[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]\n    if flags & os.O_APPEND:\n        mode = mode.replace('w', 'a', 1)\n    mode = mode.replace('w+', 'r+')\n    # possible values: r, w, a, r+, a+\n    return mode\n\n\ndef is_storage_device(name):\n    \"\"\"Return True if the given name refers to a root device (e.g.\n    \"sda\", \"nvme0n1\") as opposed to a logical partition (e.g.  \"sda1\",\n    \"nvme0n1p1\"). If name is a virtual device (e.g. \"loop1\", \"ram\")\n    return True.\n    \"\"\"\n    # Re-adapted from iostat source code, see:\n    # https://github.com/sysstat/sysstat/blob/97912938cd476/common.c#L208\n    # Some devices may have a slash in their name (e.g. cciss/c0d0...).\n    name = name.replace('/', '!')\n    including_virtual = True\n    if including_virtual:\n        path = f\"/sys/block/{name}\"\n    else:\n        path = f\"/sys/block/{name}/device\"\n    return os.access(path, os.F_OK)\n\n\n# =====================================================================\n# --- system memory\n# =====================================================================\n\n\ndef calculate_avail_vmem(mems):\n    \"\"\"Fallback for kernels < 3.14 where /proc/meminfo does not provide\n    \"MemAvailable\", see:\n    https://blog.famzah.net/2014/09/24/.\n\n    This code reimplements the algorithm outlined here:\n    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/\n        commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773\n\n    We use this function also when \"MemAvailable\" returns 0 (possibly a\n    kernel bug, see: https://github.com/giampaolo/psutil/issues/1915).\n    In that case this routine matches \"free\" CLI tool result (\"available\"\n    column).\n\n    XXX: on recent kernels this calculation may differ by ~1.5% compared\n    to \"MemAvailable:\", as it's calculated slightly differently.\n    It is still way more realistic than doing (free + cached) though.\n    See:\n    * https://gitlab.com/procps-ng/procps/issues/42\n    * https://github.com/famzah/linux-memavailable-procfs/issues/2\n    \"\"\"\n    # Note about \"fallback\" value. According to:\n    # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398\n    # ...long ago \"available\" memory was calculated as (free + cached),\n    # We use fallback when one of these is missing from /proc/meminfo:\n    # \"Active(file)\": introduced in 2.6.28 / Dec 2008\n    # \"Inactive(file)\": introduced in 2.6.28 / Dec 2008\n    # \"SReclaimable\": introduced in 2.6.19 / Nov 2006\n    # /proc/zoneinfo: introduced in 2.6.13 / Aug 2005\n    free = mems[b'MemFree:']\n    fallback = free + mems.get(b\"Cached:\", 0)\n    try:\n        lru_active_file = mems[b'Active(file):']\n        lru_inactive_file = mems[b'Inactive(file):']\n        slab_reclaimable = mems[b'SReclaimable:']\n    except KeyError as err:\n        debug(\n            f\"{err.args[0]} is missing from /proc/meminfo; using an\"\n            \" approximation for calculating available memory\"\n        )\n        return fallback\n    try:\n        f = open_binary(f\"{get_procfs_path()}/zoneinfo\")\n    except OSError:\n        return fallback  # kernel 2.6.13\n\n    watermark_low = 0\n    with f:\n        for line in f:\n            line = line.strip()\n            if line.startswith(b'low'):\n                watermark_low += int(line.split()[1])\n    watermark_low *= PAGESIZE\n\n    avail = free - watermark_low\n    pagecache = lru_active_file + lru_inactive_file\n    pagecache -= min(pagecache / 2, watermark_low)\n    avail += pagecache\n    avail += slab_reclaimable - min(slab_reclaimable / 2.0, watermark_low)\n    return int(avail)\n\n\ndef virtual_memory():\n    \"\"\"Report virtual memory stats.\n    This implementation mimics procps-ng-3.3.12, aka \"free\" CLI tool:\n    https://gitlab.com/procps-ng/procps/blob/\n        24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L778-791\n    The returned values are supposed to match both \"free\" and \"vmstat -s\"\n    CLI tools.\n    \"\"\"\n    missing_fields = []\n    mems = {}\n    with open_binary(f\"{get_procfs_path()}/meminfo\") as f:\n        for line in f:\n            fields = line.split()\n            mems[fields[0]] = int(fields[1]) * 1024\n\n    # /proc doc states that the available fields in /proc/meminfo vary\n    # by architecture and compile options, but these 3 values are also\n    # returned by sysinfo(2); as such we assume they are always there.\n    total = mems[b'MemTotal:']\n    free = mems[b'MemFree:']\n    try:\n        buffers = mems[b'Buffers:']\n    except KeyError:\n        # https://github.com/giampaolo/psutil/issues/1010\n        buffers = 0\n        missing_fields.append('buffers')\n    try:\n        cached = mems[b\"Cached:\"]\n    except KeyError:\n        cached = 0\n        missing_fields.append('cached')\n    else:\n        # \"free\" cmdline utility sums reclaimable to cached.\n        # Older versions of procps used to add slab memory instead.\n        # This got changed in:\n        # https://gitlab.com/procps-ng/procps/-/commit/05d751c4f\n        cached += mems.get(b\"SReclaimable:\", 0)  # since kernel 2.6.19\n\n    try:\n        shared = mems[b'Shmem:']  # since kernel 2.6.32\n    except KeyError:\n        try:\n            shared = mems[b'MemShared:']  # kernels 2.4\n        except KeyError:\n            shared = 0\n            missing_fields.append('shared')\n\n    try:\n        active = mems[b\"Active:\"]\n    except KeyError:\n        active = 0\n        missing_fields.append('active')\n\n    try:\n        inactive = mems[b\"Inactive:\"]\n    except KeyError:\n        try:\n            inactive = (\n                mems[b\"Inact_dirty:\"]\n                + mems[b\"Inact_clean:\"]\n                + mems[b\"Inact_laundry:\"]\n            )\n        except KeyError:\n            inactive = 0\n            missing_fields.append('inactive')\n\n    try:\n        slab = mems[b\"Slab:\"]\n    except KeyError:\n        slab = 0\n\n    # - starting from 4.4.0 we match free's \"available\" column.\n    #   Before 4.4.0 we calculated it as (free + buffers + cached)\n    #   which matched htop.\n    # - free and htop available memory differs as per:\n    #   http://askubuntu.com/a/369589\n    #   http://unix.stackexchange.com/a/65852/168884\n    # - MemAvailable has been introduced in kernel 3.14\n    try:\n        avail = mems[b'MemAvailable:']\n    except KeyError:\n        avail = calculate_avail_vmem(mems)\n    else:\n        if avail == 0:\n            # Yes, it can happen (probably a kernel bug):\n            # https://github.com/giampaolo/psutil/issues/1915\n            # In this case \"free\" CLI tool makes an estimate. We do the same,\n            # and it matches \"free\" CLI tool.\n            avail = calculate_avail_vmem(mems)\n\n    if avail < 0:\n        avail = 0\n        missing_fields.append('available')\n    elif avail > total:\n        # If avail is greater than total or our calculation overflows,\n        # that's symptomatic of running within a LCX container where such\n        # values will be dramatically distorted over those of the host.\n        # https://gitlab.com/procps-ng/procps/blob/24fd2605c51fcc/proc/sysinfo.c#L764\n        avail = free\n\n    used = total - avail\n\n    percent = usage_percent((total - avail), total, round_=1)\n\n    # Warn about missing metrics which are set to 0.\n    if missing_fields:\n        msg = \"{} memory stats couldn't be determined and {} set to 0\".format(\n            \", \".join(missing_fields),\n            \"was\" if len(missing_fields) == 1 else \"were\",\n        )\n        warnings.warn(msg, RuntimeWarning, stacklevel=2)\n\n    return ntp.svmem(\n        total,\n        avail,\n        percent,\n        used,\n        free,\n        active,\n        inactive,\n        buffers,\n        cached,\n        shared,\n        slab,\n    )\n\n\ndef swap_memory():\n    \"\"\"Return swap memory metrics.\"\"\"\n    mems = {}\n    with open_binary(f\"{get_procfs_path()}/meminfo\") as f:\n        for line in f:\n            fields = line.split()\n            mems[fields[0]] = int(fields[1]) * 1024\n    # We prefer /proc/meminfo over sysinfo() syscall so that\n    # psutil.PROCFS_PATH can be used in order to allow retrieval\n    # for linux containers, see:\n    # https://github.com/giampaolo/psutil/issues/1015\n    try:\n        total = mems[b'SwapTotal:']\n        free = mems[b'SwapFree:']\n    except KeyError:\n        _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()\n        total *= unit_multiplier\n        free *= unit_multiplier\n\n    used = total - free\n    percent = usage_percent(used, total, round_=1)\n    # get pgin/pgouts\n    try:\n        f = open_binary(f\"{get_procfs_path()}/vmstat\")\n    except OSError as err:\n        # see https://github.com/giampaolo/psutil/issues/722\n        msg = (\n            \"'sin' and 'sout' swap memory stats couldn't \"\n            f\"be determined and were set to 0 ({err})\"\n        )\n        warnings.warn(msg, RuntimeWarning, stacklevel=2)\n        sin = sout = 0\n    else:\n        with f:\n            sin = sout = None\n            for line in f:\n                # values are expressed in 4 kilo bytes, we want\n                # bytes instead\n                if line.startswith(b'pswpin'):\n                    sin = int(line.split(b' ')[1]) * 4 * 1024\n                elif line.startswith(b'pswpout'):\n                    sout = int(line.split(b' ')[1]) * 4 * 1024\n                if sin is not None and sout is not None:\n                    break\n            else:\n                # we might get here when dealing with exotic Linux\n                # flavors, see:\n                # https://github.com/giampaolo/psutil/issues/313\n                msg = \"'sin' and 'sout' swap memory stats couldn't \"\n                msg += \"be determined and were set to 0\"\n                warnings.warn(msg, RuntimeWarning, stacklevel=2)\n                sin = sout = 0\n    return ntp.sswap(total, used, free, percent, sin, sout)\n\n\n# malloc / heap functions; require glibc\nif hasattr(cext, \"heap_info\"):\n    heap_info = cext.heap_info\n    heap_trim = cext.heap_trim\n\n\n# =====================================================================\n# --- CPU\n# =====================================================================\n\n\ndef cpu_times():\n    \"\"\"Return a named tuple representing system-wide CPU times.\"\"\"\n\n    def lsget(lst, idx, field_name):\n        try:\n            return lst[idx]\n        except IndexError:\n            debug(f\"can't get {field_name} CPU time; set it to 0\")\n            return 0\n\n    procfs_path = get_procfs_path()\n    with open_binary(f\"{procfs_path}/stat\") as f:\n        values = f.readline().split()\n    nfields = len(ntp.scputimes._fields)\n    raw = [float(x) / CLOCK_TICKS for x in values[1 : nfields + 1]]\n    user, nice, system, idle = raw[:4]\n    return ntp.scputimes(\n        user,\n        system,\n        idle,\n        nice,\n        lsget(raw, 4, \"iowait\"),  # Linux >= 2.5.41\n        lsget(raw, 5, \"irq\"),  # Linux >= 2.6.0\n        lsget(raw, 6, \"softirq\"),  # Linux >= 2.6.0\n        lsget(raw, 7, \"steal\"),  # Linux >= 2.6.11\n        lsget(raw, 8, \"guest\"),  # Linux >= 2.6.24\n        lsget(raw, 9, \"guest_nice\"),  # Linux >= 2.6.33\n    )\n\n\ndef per_cpu_times():\n    \"\"\"Return a list of named tuples representing the CPU times\n    for every CPU available on the system.\n    \"\"\"\n    procfs_path = get_procfs_path()\n    cpus = []\n    nfields = len(ntp.scputimes._fields)\n    with open_binary(f\"{procfs_path}/stat\") as f:\n        # get rid of the first line which refers to system wide CPU stats\n        f.readline()\n        for line in f:\n            if line.startswith(b'cpu'):\n                values = line.split()\n                raw = [float(x) / CLOCK_TICKS for x in values[1 : nfields + 1]]\n                user, nice, system, idle = raw[0], raw[1], raw[2], raw[3]\n                entry = ntp.scputimes(user, system, idle, nice, *raw[4:])\n                cpus.append(entry)\n        return cpus\n\n\ndef cpu_count_logical():\n    \"\"\"Return the number of logical CPUs in the system.\"\"\"\n    try:\n        return os.sysconf(\"SC_NPROCESSORS_ONLN\")\n    except ValueError:\n        # as a second fallback we try to parse /proc/cpuinfo\n        num = 0\n        with open_binary(f\"{get_procfs_path()}/cpuinfo\") as f:\n            for line in f:\n                if line.lower().startswith(b'processor'):\n                    num += 1\n\n        # unknown format (e.g. amrel/sparc architectures), see:\n        # https://github.com/giampaolo/psutil/issues/200\n        # try to parse /proc/stat as a last resort\n        if num == 0:\n            search = re.compile(r'cpu\\d')\n            with open_text(f\"{get_procfs_path()}/stat\") as f:\n                for line in f:\n                    line = line.split(' ')[0]\n                    if search.match(line):\n                        num += 1\n\n        if num == 0:\n            # mimic os.cpu_count()\n            return None\n        return num\n\n\ndef cpu_count_cores():\n    \"\"\"Return the number of CPU cores in the system.\"\"\"\n    # Method #1\n    ls = set()\n    # These 2 files are the same but */core_cpus_list is newer while\n    # */thread_siblings_list is deprecated and may disappear in the future.\n    # https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst\n    # https://github.com/giampaolo/psutil/pull/1727#issuecomment-707624964\n    # https://lkml.org/lkml/2019/2/26/41\n    p1 = \"/sys/devices/system/cpu/cpu[0-9]*/topology/core_cpus_list\"\n    p2 = \"/sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list\"\n    for path in glob.glob(p1) or glob.glob(p2):\n        with open_binary(path) as f:\n            ls.add(f.read().strip())\n    result = len(ls)\n    if result != 0:\n        return result\n\n    # Method #2\n    mapping = {}\n    current_info = {}\n    with open_binary(f\"{get_procfs_path()}/cpuinfo\") as f:\n        for line in f:\n            line = line.strip().lower()\n            if not line:\n                # new section\n                try:\n                    mapping[current_info[b'physical id']] = current_info[\n                        b'cpu cores'\n                    ]\n                except KeyError:\n                    pass\n                current_info = {}\n            elif line.startswith((b'physical id', b'cpu cores')):\n                # ongoing section\n                key, value = line.split(b':', 1)\n                current_info[key.strip()] = int(value)\n\n    result = sum(mapping.values())\n    return result or None  # mimic os.cpu_count()\n\n\ndef cpu_stats():\n    \"\"\"Return various CPU stats as a named tuple.\"\"\"\n    with open_binary(f\"{get_procfs_path()}/stat\") as f:\n        ctx_switches = None\n        interrupts = None\n        soft_interrupts = None\n        for line in f:\n            if line.startswith(b'ctxt'):\n                ctx_switches = int(line.split()[1])\n            elif line.startswith(b'intr'):\n                interrupts = int(line.split()[1])\n            elif line.startswith(b'softirq'):\n                soft_interrupts = int(line.split()[1])\n            if (\n                ctx_switches is not None\n                and soft_interrupts is not None\n                and interrupts is not None\n            ):\n                break\n    syscalls = 0\n    return ntp.scpustats(ctx_switches, interrupts, soft_interrupts, syscalls)\n\n\ndef _cpu_get_cpuinfo_freq():\n    \"\"\"Return current CPU frequency from cpuinfo if available.\"\"\"\n    with open_binary(f\"{get_procfs_path()}/cpuinfo\") as f:\n        return [\n            float(line.split(b':', 1)[1])\n            for line in f\n            if line.lower().startswith(b'cpu mhz')\n        ]\n\n\nif os.path.exists(\"/sys/devices/system/cpu/cpufreq/policy0\") or os.path.exists(\n    \"/sys/devices/system/cpu/cpu0/cpufreq\"\n):\n\n    def cpu_freq():\n        \"\"\"Return frequency metrics for all CPUs.\n        Contrarily to other OSes, Linux updates these values in\n        real-time.\n        \"\"\"\n        cpuinfo_freqs = _cpu_get_cpuinfo_freq()\n        paths = glob.glob(\n            \"/sys/devices/system/cpu/cpufreq/policy[0-9]*\"\n        ) or glob.glob(\"/sys/devices/system/cpu/cpu[0-9]*/cpufreq\")\n        paths.sort(key=lambda x: int(re.search(r\"[0-9]+\", x).group()))\n        ret = []\n        pjoin = os.path.join\n        for i, path in enumerate(paths):\n            if len(paths) == len(cpuinfo_freqs):\n                # take cached value from cpuinfo if available, see:\n                # https://github.com/giampaolo/psutil/issues/1851\n                curr = cpuinfo_freqs[i] * 1000\n            else:\n                curr = bcat(pjoin(path, \"scaling_cur_freq\"), fallback=None)\n            if curr is None:\n                # Likely an old RedHat, see:\n                # https://github.com/giampaolo/psutil/issues/1071\n                curr = bcat(pjoin(path, \"cpuinfo_cur_freq\"), fallback=None)\n                if curr is None:\n                    online_path = f\"/sys/devices/system/cpu/cpu{i}/online\"\n                    # if cpu core is offline, set to all zeroes\n                    if cat(online_path, fallback=None) == \"0\\n\":\n                        ret.append(ntp.scpufreq(0.0, 0.0, 0.0))\n                        continue\n                    msg = \"can't find current frequency file\"\n                    raise NotImplementedError(msg)\n            curr = int(curr) / 1000\n            max_ = int(bcat(pjoin(path, \"scaling_max_freq\"))) / 1000\n            min_ = int(bcat(pjoin(path, \"scaling_min_freq\"))) / 1000\n            ret.append(ntp.scpufreq(curr, min_, max_))\n        return ret\n\nelse:\n\n    def cpu_freq():\n        \"\"\"Alternate implementation using /proc/cpuinfo.\n        min and max frequencies are not available and are set to None.\n        \"\"\"\n        return [ntp.scpufreq(x, 0.0, 0.0) for x in _cpu_get_cpuinfo_freq()]\n\n\n# =====================================================================\n# --- network\n# =====================================================================\n\n\nnet_if_addrs = cext.net_if_addrs\n\n\nclass _Ipv6UnsupportedError(Exception):\n    pass\n\n\nclass NetConnections:\n    \"\"\"A wrapper on top of /proc/net/* files, retrieving per-process\n    and system-wide open connections (TCP, UDP, UNIX) similarly to\n    \"netstat -an\".\n\n    Note: in case of UNIX sockets we're only able to determine the\n    local endpoint/path, not the one it's connected to.\n    According to [1] it would be possible but not easily.\n\n    [1] http://serverfault.com/a/417946\n    \"\"\"\n\n    def __init__(self):\n        # The string represents the basename of the corresponding\n        # /proc/net/{proto_name} file.\n        tcp4 = (\"tcp\", socket.AF_INET, socket.SOCK_STREAM)\n        tcp6 = (\"tcp6\", socket.AF_INET6, socket.SOCK_STREAM)\n        udp4 = (\"udp\", socket.AF_INET, socket.SOCK_DGRAM)\n        udp6 = (\"udp6\", socket.AF_INET6, socket.SOCK_DGRAM)\n        unix = (\"unix\", socket.AF_UNIX, None)\n        self.tmap = {\n            \"all\": (tcp4, tcp6, udp4, udp6, unix),\n            \"tcp\": (tcp4, tcp6),\n            \"tcp4\": (tcp4,),\n            \"tcp6\": (tcp6,),\n            \"udp\": (udp4, udp6),\n            \"udp4\": (udp4,),\n            \"udp6\": (udp6,),\n            \"unix\": (unix,),\n            \"inet\": (tcp4, tcp6, udp4, udp6),\n            \"inet4\": (tcp4, udp4),\n            \"inet6\": (tcp6, udp6),\n        }\n        self._procfs_path = None\n\n    def get_proc_inodes(self, pid):\n        inodes = defaultdict(list)\n        for fd in os.listdir(f\"{self._procfs_path}/{pid}/fd\"):\n            try:\n                inode = readlink(f\"{self._procfs_path}/{pid}/fd/{fd}\")\n            except (FileNotFoundError, ProcessLookupError):\n                # ENOENT == file which is gone in the meantime;\n                # os.stat(f\"/proc/{self.pid}\") will be done later\n                # to force NSP (if it's the case)\n                continue\n            except OSError as err:\n                if err.errno == errno.EINVAL:\n                    # not a link\n                    continue\n                if err.errno == errno.ENAMETOOLONG:\n                    # file name too long\n                    debug(err)\n                    continue\n                raise\n            else:\n                if inode.startswith('socket:['):\n                    # the process is using a socket\n                    inode = inode[8:][:-1]\n                    inodes[inode].append((pid, int(fd)))\n        return inodes\n\n    def get_all_inodes(self):\n        inodes = {}\n        for pid in pids():\n            try:\n                inodes.update(self.get_proc_inodes(pid))\n            except (FileNotFoundError, ProcessLookupError, PermissionError):\n                # os.listdir() is gonna raise a lot of access denied\n                # exceptions in case of unprivileged user; that's fine\n                # as we'll just end up returning a connection with PID\n                # and fd set to None anyway.\n                # Both netstat -an and lsof does the same so it's\n                # unlikely we can do any better.\n                # ENOENT just means a PID disappeared on us.\n                continue\n        return inodes\n\n    @staticmethod\n    def decode_address(addr, family):\n        \"\"\"Accept an \"ip:port\" address as displayed in /proc/net/*\n        and convert it into a human readable form, like:\n\n        \"0500000A:0016\" -> (\"10.0.0.5\", 22)\n        \"0000000000000000FFFF00000100007F:9E49\" -> (\"::ffff:127.0.0.1\", 40521)\n\n        The IP address portion is a little or big endian four-byte\n        hexadecimal number; that is, the least significant byte is listed\n        first, so we need to reverse the order of the bytes to convert it\n        to an IP address.\n        The port is represented as a two-byte hexadecimal number.\n\n        Reference:\n        http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html\n        \"\"\"\n        ip, port = addr.split(':')\n        port = int(port, 16)\n        # this usually refers to a local socket in listen mode with\n        # no end-points connected\n        if not port:\n            return ()\n        ip = ip.encode('ascii')\n        if family == socket.AF_INET:\n            # see: https://github.com/giampaolo/psutil/issues/201\n            if LITTLE_ENDIAN:\n                ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1])\n            else:\n                ip = socket.inet_ntop(family, base64.b16decode(ip))\n        else:  # IPv6\n            ip = base64.b16decode(ip)\n            try:\n                # see: https://github.com/giampaolo/psutil/issues/201\n                if LITTLE_ENDIAN:\n                    ip = socket.inet_ntop(\n                        socket.AF_INET6,\n                        struct.pack('>4I', *struct.unpack('<4I', ip)),\n                    )\n                else:\n                    ip = socket.inet_ntop(\n                        socket.AF_INET6,\n                        struct.pack('<4I', *struct.unpack('<4I', ip)),\n                    )\n            except ValueError:\n                # see: https://github.com/giampaolo/psutil/issues/623\n                if not supports_ipv6():\n                    raise _Ipv6UnsupportedError from None\n                raise\n        return ntp.addr(ip, port)\n\n    @staticmethod\n    def process_inet(file, family, type_, inodes, filter_pid=None):\n        \"\"\"Parse /proc/net/tcp* and /proc/net/udp* files.\"\"\"\n        if file.endswith('6') and not os.path.exists(file):\n            # IPv6 not supported\n            return\n        with open_text(file) as f:\n            f.readline()  # skip the first line\n            for lineno, line in enumerate(f, 1):\n                try:\n                    _, laddr, raddr, status, _, _, _, _, _, inode = (\n                        line.split()[:10]\n                    )\n                except ValueError:\n                    msg = (\n                        f\"error while parsing {file}; malformed line\"\n                        f\" {lineno} {line!r}\"\n                    )\n                    raise RuntimeError(msg) from None\n                if inode in inodes:\n                    # # We assume inet sockets are unique, so we error\n                    # # out if there are multiple references to the\n                    # # same inode. We won't do this for UNIX sockets.\n                    # if len(inodes[inode]) > 1 and family != socket.AF_UNIX:\n                    #     raise ValueError(\"ambiguous inode with multiple \"\n                    #                      \"PIDs references\")\n                    pid, fd = inodes[inode][0]\n                else:\n                    pid, fd = None, -1\n                if filter_pid is not None and filter_pid != pid:\n                    continue\n                else:\n                    if type_ == socket.SOCK_STREAM:\n                        status = TCP_STATUSES[status]\n                    else:\n                        status = ConnectionStatus.CONN_NONE\n                    try:\n                        laddr = NetConnections.decode_address(laddr, family)\n                        raddr = NetConnections.decode_address(raddr, family)\n                    except _Ipv6UnsupportedError:\n                        continue\n                    yield (fd, family, type_, laddr, raddr, status, pid)\n\n    @staticmethod\n    def process_unix(file, family, inodes, filter_pid=None):\n        \"\"\"Parse /proc/net/unix files.\"\"\"\n        with open_text(file) as f:\n            f.readline()  # skip the first line\n            for line in f:\n                tokens = line.split()\n                try:\n                    _, _, _, _, type_, _, inode = tokens[0:7]\n                except ValueError:\n                    if ' ' not in line:\n                        # see: https://github.com/giampaolo/psutil/issues/766\n                        continue\n                    msg = (\n                        f\"error while parsing {file}; malformed line {line!r}\"\n                    )\n                    raise RuntimeError(msg)  # noqa: B904\n                if inode in inodes:  # noqa: SIM108\n                    # With UNIX sockets we can have a single inode\n                    # referencing many file descriptors.\n                    pairs = inodes[inode]\n                else:\n                    pairs = [(None, -1)]\n                for pid, fd in pairs:\n                    if filter_pid is not None and filter_pid != pid:\n                        continue\n                    else:\n                        path = tokens[-1] if len(tokens) == 8 else ''\n                        type_ = socktype_to_enum(int(type_))\n                        # XXX: determining the remote endpoint of a\n                        # UNIX socket on Linux is not possible, see:\n                        # https://serverfault.com/questions/252723/\n                        raddr = \"\"\n                        status = ConnectionStatus.CONN_NONE\n                        yield (fd, family, type_, path, raddr, status, pid)\n\n    def retrieve(self, kind, pid=None):\n        self._procfs_path = get_procfs_path()\n        if pid is not None:\n            inodes = self.get_proc_inodes(pid)\n            if not inodes:\n                # no connections for this process\n                return []\n        else:\n            inodes = self.get_all_inodes()\n        ret = set()\n        for proto_name, family, type_ in self.tmap[kind]:\n            path = f\"{self._procfs_path}/net/{proto_name}\"\n            if family in {socket.AF_INET, socket.AF_INET6}:\n                ls = self.process_inet(\n                    path, family, type_, inodes, filter_pid=pid\n                )\n            else:\n                ls = self.process_unix(path, family, inodes, filter_pid=pid)\n            for fd, family, type_, laddr, raddr, status, bound_pid in ls:\n                if pid:\n                    conn = ntp.pconn(fd, family, type_, laddr, raddr, status)\n                else:\n                    conn = ntp.sconn(\n                        fd, family, type_, laddr, raddr, status, bound_pid\n                    )\n                ret.add(conn)\n        return list(ret)\n\n\n_net_connections = NetConnections()\n\n\ndef net_connections(kind='inet'):\n    \"\"\"Return system-wide open connections.\"\"\"\n    return _net_connections.retrieve(kind)\n\n\ndef net_io_counters():\n    \"\"\"Return network I/O statistics for every network interface\n    installed on the system as a dict of raw tuples.\n    \"\"\"\n    with open_text(f\"{get_procfs_path()}/net/dev\") as f:\n        lines = f.readlines()\n    retdict = {}\n    for line in lines[2:]:\n        colon = line.rfind(':')\n        assert colon > 0, repr(line)\n        name = line[:colon].strip()\n        fields = line[colon + 1 :].strip().split()\n\n        (\n            # in\n            bytes_recv,\n            packets_recv,\n            errin,\n            dropin,\n            _fifoin,  # unused\n            _framein,  # unused\n            _compressedin,  # unused\n            _multicastin,  # unused\n            # out\n            bytes_sent,\n            packets_sent,\n            errout,\n            dropout,\n            _fifoout,  # unused\n            _collisionsout,  # unused\n            _carrierout,  # unused\n            _compressedout,  # unused\n        ) = map(int, fields)\n\n        retdict[name] = (\n            bytes_sent,\n            bytes_recv,\n            packets_sent,\n            packets_recv,\n            errin,\n            errout,\n            dropin,\n            dropout,\n        )\n    return retdict\n\n\ndef net_if_stats():\n    \"\"\"Get NIC stats (isup, duplex, speed, mtu).\"\"\"\n    duplex_map = {\n        cext.DUPLEX_FULL: NicDuplex.NIC_DUPLEX_FULL,\n        cext.DUPLEX_HALF: NicDuplex.NIC_DUPLEX_HALF,\n        cext.DUPLEX_UNKNOWN: NicDuplex.NIC_DUPLEX_UNKNOWN,\n    }\n    names = net_io_counters().keys()\n    ret = {}\n    for name in names:\n        try:\n            mtu = cext.net_if_mtu(name)\n            flags = cext.net_if_flags(name)\n            duplex, speed = cext.net_if_duplex_speed(name)\n        except OSError as err:\n            # https://github.com/giampaolo/psutil/issues/1279\n            if err.errno != errno.ENODEV:\n                raise\n            debug(err)\n        else:\n            output_flags = ','.join(flags)\n            isup = 'running' in flags\n            ret[name] = ntp.snicstats(\n                isup, duplex_map[duplex], speed, mtu, output_flags\n            )\n    return ret\n\n\n# =====================================================================\n# --- disks\n# =====================================================================\n\n\ndisk_usage = _psposix.disk_usage\n\n\ndef disk_io_counters(perdisk=False):\n    \"\"\"Return disk I/O statistics for every disk installed on the\n    system as a dict of raw tuples.\n    \"\"\"\n\n    def read_procfs():\n        # OK, this is a bit confusing. The format of /proc/diskstats can\n        # have 3 variations.\n        # On Linux 2.4 each line has always 15 fields, e.g.:\n        # \"3     0   8 hda 8 8 8 8 8 8 8 8 8 8 8\"\n        # On Linux 2.6+ each line *usually* has 14 fields, and the disk\n        # name is in another position, like this:\n        # \"3    0   hda 8 8 8 8 8 8 8 8 8 8 8\"\n        # ...unless (Linux 2.6) the line refers to a partition instead\n        # of a disk, in which case the line has less fields (7):\n        # \"3    1   hda1 8 8 8 8\"\n        # 4.18+ has 4 fields added:\n        # \"3    0   hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0\"\n        # 5.5 has 2 more fields.\n        # See:\n        # https://www.kernel.org/doc/Documentation/iostats.txt\n        # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats\n        with open_text(f\"{get_procfs_path()}/diskstats\") as f:\n            lines = f.readlines()\n        for line in lines:\n            fields = line.split()\n            flen = len(fields)\n            # fmt: off\n            if flen == 15:\n                # Linux 2.4\n                name = fields[3]\n                reads = int(fields[2])\n                (reads_merged, rbytes, rtime, writes, writes_merged,\n                    wbytes, wtime, _, busy_time, _) = map(int, fields[4:14])\n            elif flen == 14 or flen >= 18:\n                # Linux 2.6+, line referring to a disk\n                name = fields[2]\n                (reads, reads_merged, rbytes, rtime, writes, writes_merged,\n                    wbytes, wtime, _, busy_time, _) = map(int, fields[3:14])\n            elif flen == 7:\n                # Linux 2.6+, line referring to a partition\n                name = fields[2]\n                reads, rbytes, writes, wbytes = map(int, fields[3:])\n                rtime = wtime = reads_merged = writes_merged = busy_time = 0\n            else:\n                msg = f\"not sure how to interpret line {line!r}\"\n                raise ValueError(msg)\n            yield (name, reads, writes, rbytes, wbytes, rtime, wtime,\n                   reads_merged, writes_merged, busy_time)\n            # fmt: on\n\n    def read_sysfs():\n        for block in os.listdir('/sys/block'):\n            for root, _, files in os.walk(os.path.join('/sys/block', block)):\n                if 'stat' not in files:\n                    continue\n                with open_text(os.path.join(root, 'stat')) as f:\n                    fields = f.read().strip().split()\n                name = os.path.basename(root)\n                # fmt: off\n                (reads, reads_merged, rbytes, rtime, writes, writes_merged,\n                    wbytes, wtime, _, busy_time) = map(int, fields[:10])\n                yield (name, reads, writes, rbytes, wbytes, rtime,\n                       wtime, reads_merged, writes_merged, busy_time)\n                # fmt: on\n\n    if os.path.exists(f\"{get_procfs_path()}/diskstats\"):\n        gen = read_procfs()\n    elif os.path.exists('/sys/block'):\n        gen = read_sysfs()\n    else:\n        msg = (\n            f\"{get_procfs_path()}/diskstats nor /sys/block are available on\"\n            \" this system\"\n        )\n        raise NotImplementedError(msg)\n\n    retdict = {}\n    for entry in gen:\n        # fmt: off\n        (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged,\n            writes_merged, busy_time) = entry\n        if not perdisk and not is_storage_device(name):\n            # perdisk=False means we want to calculate totals so we skip\n            # partitions (e.g. 'sda1', 'nvme0n1p1') and only include\n            # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks\n            # include a total of all their partitions + some extra size\n            # of their own:\n            #     $ cat /proc/diskstats\n            #     259       0 sda 10485760 ...\n            #     259       1 sda1 5186039 ...\n            #     259       1 sda2 5082039 ...\n            # See:\n            # https://github.com/giampaolo/psutil/pull/1313\n            continue\n\n        rbytes *= DISK_SECTOR_SIZE\n        wbytes *= DISK_SECTOR_SIZE\n        retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime,\n                         reads_merged, writes_merged, busy_time)\n        # fmt: on\n\n    return retdict\n\n\nclass RootFsDeviceFinder:\n    \"\"\"disk_partitions() may return partitions with device == \"/dev/root\"\n    or \"rootfs\". This container class uses different strategies to try to\n    obtain the real device path. Resources:\n    https://bootlin.com/blog/find-root-device/\n    https://www.systutorials.com/how-to-find-the-disk-where-root-is-on-in-bash-on-linux/.\n    \"\"\"\n\n    __slots__ = ['major', 'minor']\n\n    def __init__(self):\n        dev = os.stat(\"/\").st_dev\n        self.major = os.major(dev)\n        self.minor = os.minor(dev)\n\n    def ask_proc_partitions(self):\n        with open_text(f\"{get_procfs_path()}/partitions\") as f:\n            for line in f.readlines()[2:]:\n                fields = line.split()\n                if len(fields) < 4:  # just for extra safety\n                    continue\n                major = int(fields[0]) if fields[0].isdigit() else None\n                minor = int(fields[1]) if fields[1].isdigit() else None\n                name = fields[3]\n                if major == self.major and minor == self.minor:\n                    if name:  # just for extra safety\n                        return f\"/dev/{name}\"\n\n    def ask_sys_dev_block(self):\n        path = f\"/sys/dev/block/{self.major}:{self.minor}/uevent\"\n        with open_text(path) as f:\n            for line in f:\n                if line.startswith(\"DEVNAME=\"):\n                    name = line.strip().rpartition(\"DEVNAME=\")[2]\n                    if name:  # just for extra safety\n                        return f\"/dev/{name}\"\n\n    def ask_sys_class_block(self):\n        needle = f\"{self.major}:{self.minor}\"\n        files = glob.iglob(\"/sys/class/block/*/dev\")\n        for file in files:\n            try:\n                f = open_text(file)\n            except FileNotFoundError:  # race condition\n                continue\n            else:\n                with f:\n                    data = f.read().strip()\n                    if data == needle:\n                        name = os.path.basename(os.path.dirname(file))\n                        return f\"/dev/{name}\"\n\n    def find(self):\n        path = None\n        if path is None:\n            try:\n                path = self.ask_proc_partitions()\n            except OSError as err:\n                debug(err)\n        if path is None:\n            try:\n                path = self.ask_sys_dev_block()\n            except OSError as err:\n                debug(err)\n        if path is None:\n            try:\n                path = self.ask_sys_class_block()\n            except OSError as err:\n                debug(err)\n        # We use exists() because the \"/dev/*\" part of the path is hard\n        # coded, so we want to be sure.\n        if path is not None and os.path.exists(path):\n            return path\n\n\ndef disk_partitions(all=False):\n    \"\"\"Return mounted disk partitions as a list of named tuples.\"\"\"\n    fstypes = set()\n    procfs_path = get_procfs_path()\n    if not all:\n        with open_text(f\"{procfs_path}/filesystems\") as f:\n            for line in f:\n                line = line.strip()\n                if not line.startswith(\"nodev\"):\n                    fstypes.add(line.strip())\n                else:\n                    # ignore all lines starting with \"nodev\" except \"nodev zfs\"\n                    fstype = line.split(\"\\t\")[1]\n                    if fstype == \"zfs\":\n                        fstypes.add(\"zfs\")\n\n    # See: https://github.com/giampaolo/psutil/issues/1307\n    if procfs_path == \"/proc\" and os.path.isfile('/etc/mtab'):\n        mounts_path = os.path.realpath(\"/etc/mtab\")\n    else:\n        mounts_path = os.path.realpath(f\"{procfs_path}/self/mounts\")\n\n    retlist = []\n    partitions = cext.disk_partitions(mounts_path)\n    for partition in partitions:\n        device, mountpoint, fstype, opts = partition\n        if device == 'none':\n            device = ''\n        if device in {\"/dev/root\", \"rootfs\"}:\n            device = RootFsDeviceFinder().find() or device\n        if not all:\n            if not device or fstype not in fstypes:\n                continue\n        ntuple = ntp.sdiskpart(device, mountpoint, fstype, opts)\n        retlist.append(ntuple)\n\n    return retlist\n\n\n# =====================================================================\n# --- sensors\n# =====================================================================\n\n\ndef sensors_temperatures():\n    \"\"\"Return hardware (CPU and others) temperatures as a dict\n    including hardware name, label, current, max and critical\n    temperatures.\n\n    Implementation notes:\n    - /sys/class/hwmon looks like the most recent interface to\n      retrieve this info, and this implementation relies on it\n      only (old distros will probably use something else)\n    - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon\n    - /sys/class/thermal/thermal_zone* is another one but it's more\n      difficult to parse\n    \"\"\"\n    ret = collections.defaultdict(list)\n    basenames = glob.glob('/sys/class/hwmon/hwmon*/temp*_*')\n    # CentOS has an intermediate /device directory:\n    # https://github.com/giampaolo/psutil/issues/971\n    # https://github.com/nicolargo/glances/issues/1060\n    basenames.extend(glob.glob('/sys/class/hwmon/hwmon*/device/temp*_*'))\n    basenames = sorted({x.split('_')[0] for x in basenames})\n\n    # Only add the coretemp hwmon entries if they're not already in\n    # /sys/class/hwmon/\n    # https://github.com/giampaolo/psutil/issues/1708\n    # https://github.com/giampaolo/psutil/pull/1648\n    basenames2 = glob.glob(\n        '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*'\n    )\n    repl = re.compile(r\"/sys/devices/platform/coretemp.*/hwmon/\")\n    for name in basenames2:\n        altname = repl.sub('/sys/class/hwmon/', name)\n        if altname not in basenames:\n            basenames.append(name)\n\n    for base in basenames:\n        try:\n            path = base + '_input'\n            current = float(bcat(path)) / 1000.0\n            path = os.path.join(os.path.dirname(base), 'name')\n            unit_name = cat(path).strip()\n        except (OSError, ValueError):\n            # A lot of things can go wrong here, so let's just skip the\n            # whole entry. Sure thing is Linux's /sys/class/hwmon really\n            # is a stinky broken mess.\n            # https://github.com/giampaolo/psutil/issues/1009\n            # https://github.com/giampaolo/psutil/issues/1101\n            # https://github.com/giampaolo/psutil/issues/1129\n            # https://github.com/giampaolo/psutil/issues/1245\n            # https://github.com/giampaolo/psutil/issues/1323\n            continue\n\n        high = bcat(base + '_max', fallback=None)\n        critical = bcat(base + '_crit', fallback=None)\n        label = cat(base + '_label', fallback='').strip()\n\n        if high is not None:\n            try:\n                high = float(high) / 1000.0\n            except ValueError:\n                high = None\n        if critical is not None:\n            try:\n                critical = float(critical) / 1000.0\n            except ValueError:\n                critical = None\n\n        ret[unit_name].append((label, current, high, critical))\n\n    # Indication that no sensors were detected in /sys/class/hwmon/\n    if not basenames:\n        basenames = glob.glob('/sys/class/thermal/thermal_zone*')\n        basenames = sorted(set(basenames))\n\n        for base in basenames:\n            try:\n                path = os.path.join(base, 'temp')\n                current = float(bcat(path)) / 1000.0\n                path = os.path.join(base, 'type')\n                unit_name = cat(path).strip()\n            except (OSError, ValueError) as err:\n                debug(err)\n                continue\n\n            trip_paths = glob.glob(base + '/trip_point*')\n            trip_points = {\n                '_'.join(os.path.basename(p).split('_')[0:3])\n                for p in trip_paths\n            }\n            critical = None\n            high = None\n            for trip_point in trip_points:\n                path = os.path.join(base, trip_point + \"_type\")\n                trip_type = cat(path, fallback='').strip()\n                if trip_type == 'critical':\n                    critical = bcat(\n                        os.path.join(base, trip_point + \"_temp\"), fallback=None\n                    )\n                elif trip_type == 'high':\n                    high = bcat(\n                        os.path.join(base, trip_point + \"_temp\"), fallback=None\n                    )\n\n                if high is not None:\n                    try:\n                        high = float(high) / 1000.0\n                    except ValueError:\n                        high = None\n                if critical is not None:\n                    try:\n                        critical = float(critical) / 1000.0\n                    except ValueError:\n                        critical = None\n\n            ret[unit_name].append(('', current, high, critical))\n\n    return dict(ret)\n\n\ndef sensors_fans():\n    \"\"\"Return hardware fans info (for CPU and other peripherals) as a\n    dict including hardware label and current speed.\n\n    Implementation notes:\n    - /sys/class/hwmon looks like the most recent interface to\n      retrieve this info, and this implementation relies on it\n      only (old distros will probably use something else)\n    - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon\n    \"\"\"\n    ret = collections.defaultdict(list)\n    basenames = glob.glob('/sys/class/hwmon/hwmon*/fan*_*')\n    if not basenames:\n        # CentOS has an intermediate /device directory:\n        # https://github.com/giampaolo/psutil/issues/971\n        basenames = glob.glob('/sys/class/hwmon/hwmon*/device/fan*_*')\n\n    basenames = sorted({x.split(\"_\")[0] for x in basenames})\n    for base in basenames:\n        try:\n            current = int(bcat(base + '_input'))\n        except OSError as err:\n            debug(err)\n            continue\n        unit_name = cat(os.path.join(os.path.dirname(base), 'name')).strip()\n        label = cat(base + '_label', fallback='').strip()\n        ret[unit_name].append(ntp.sfan(label, current))\n\n    return dict(ret)\n\n\ndef sensors_battery():\n    \"\"\"Return battery information.\n    Implementation note: it appears /sys/class/power_supply/BAT0/\n    directory structure may vary and provide files with the same\n    meaning but under different names, see:\n    https://github.com/giampaolo/psutil/issues/966.\n    \"\"\"\n    null = object()\n\n    def multi_bcat(*paths):\n        \"\"\"Attempt to read the content of multiple files which may\n        not exist. If none of them exist return None.\n        \"\"\"\n        for path in paths:\n            ret = bcat(path, fallback=null)\n            if ret != null:\n                try:\n                    return int(ret)\n                except ValueError:\n                    return ret.strip()\n        return None\n\n    bats = [\n        x\n        for x in os.listdir(POWER_SUPPLY_PATH)\n        if x.startswith('BAT') or 'battery' in x.lower()\n    ]\n    if not bats:\n        return None\n    # Get the first available battery. Usually this is \"BAT0\", except\n    # some rare exceptions:\n    # https://github.com/giampaolo/psutil/issues/1238\n    root = os.path.join(POWER_SUPPLY_PATH, min(bats))\n\n    # Base metrics.\n    energy_now = multi_bcat(root + \"/energy_now\", root + \"/charge_now\")\n    power_now = multi_bcat(root + \"/power_now\", root + \"/current_now\")\n    energy_full = multi_bcat(root + \"/energy_full\", root + \"/charge_full\")\n    time_to_empty = multi_bcat(root + \"/time_to_empty_now\")\n\n    # Percent. If we have energy_full the percentage will be more\n    # accurate compared to reading /capacity file (float vs. int).\n    if energy_full is not None and energy_now is not None:\n        try:\n            percent = 100.0 * energy_now / energy_full\n        except ZeroDivisionError:\n            percent = 0.0\n    else:\n        percent = float(cat(root + \"/capacity\", fallback=-1))\n        if percent == -1:\n            return None\n\n    # Is AC power cable plugged in?\n    # Note: AC0 is not always available and sometimes (e.g. CentOS7)\n    # it's called \"AC\".\n    power_plugged = None\n    online = multi_bcat(\n        os.path.join(POWER_SUPPLY_PATH, \"AC0/online\"),\n        os.path.join(POWER_SUPPLY_PATH, \"AC/online\"),\n    )\n    if online is not None:\n        power_plugged = online == 1\n    else:\n        status = cat(root + \"/status\", fallback=\"\").strip().lower()\n        if status == \"discharging\":\n            power_plugged = False\n        elif status in {\"charging\", \"full\"}:\n            power_plugged = True\n\n    # Seconds left.\n    if power_plugged:\n        secsleft = BatteryTime.POWER_TIME_UNLIMITED\n    elif energy_now is not None and power_now is not None:\n        try:\n            secsleft = int(energy_now / abs(power_now) * 3600)\n        except ZeroDivisionError:\n            secsleft = BatteryTime.POWER_TIME_UNKNOWN\n    elif time_to_empty is not None:\n        secsleft = int(time_to_empty * 60)\n        if secsleft < 0:\n            secsleft = BatteryTime.POWER_TIME_UNKNOWN\n    else:\n        secsleft = BatteryTime.POWER_TIME_UNKNOWN\n\n    return ntp.sbattery(percent, secsleft, power_plugged)\n\n\n# =====================================================================\n# --- other system functions\n# =====================================================================\n\n\ndef users():\n    \"\"\"Return currently connected users as a list of named tuples.\"\"\"\n    retlist = []\n    rawlist = cext.users()\n    for item in rawlist:\n        user, tty, hostname, tstamp, pid = item\n        nt = ntp.suser(user, tty or None, hostname, tstamp, pid)\n        retlist.append(nt)\n    return retlist\n\n\ndef boot_time():\n    \"\"\"Return the system boot time expressed in seconds since the epoch.\"\"\"\n    path = f\"{get_procfs_path()}/stat\"\n    with open_binary(path) as f:\n        for line in f:\n            if line.startswith(b'btime'):\n                return float(line.strip().split()[1])\n        msg = f\"line 'btime' not found in {path}\"\n        raise RuntimeError(msg)\n\n\n# =====================================================================\n# --- processes\n# =====================================================================\n\n\ndef pids():\n    \"\"\"Returns a list of PIDs currently running on the system.\"\"\"\n    path = get_procfs_path().encode(ENCODING)\n    return [int(x) for x in os.listdir(path) if x.isdigit()]\n\n\ndef pid_exists(pid):\n    \"\"\"Check for the existence of a unix PID. Linux TIDs are not\n    supported (always return False).\n    \"\"\"\n    if not _psposix.pid_exists(pid):\n        return False\n    else:\n        # Linux's apparently does not distinguish between PIDs and TIDs\n        # (thread IDs).\n        # listdir(\"/proc\") won't show any TID (only PIDs) but\n        # os.stat(\"/proc/{tid}\") will succeed if {tid} exists.\n        # os.kill() can also be passed a TID. This is quite confusing.\n        # In here we want to enforce this distinction and support PIDs\n        # only, see:\n        # https://github.com/giampaolo/psutil/issues/687\n        try:\n            # Note: already checked that this is faster than using a\n            # regular expr. Also (a lot) faster than doing\n            # 'return pid in pids()'\n            path = f\"{get_procfs_path()}/{pid}/status\"\n            with open_binary(path) as f:\n                for line in f:\n                    if line.startswith(b\"Tgid:\"):\n                        tgid = int(line.split()[1])\n                        # If tgid and pid are the same then we're\n                        # dealing with a process PID.\n                        return tgid == pid\n                msg = f\"'Tgid' line not found in {path}\"\n                raise ValueError(msg)\n        except (OSError, ValueError):\n            return pid in pids()\n\n\ndef ppid_map():\n    \"\"\"Obtain a {pid: ppid, ...} dict for all running processes in\n    one shot. Used to speed up Process.children().\n    \"\"\"\n    ret = {}\n    procfs_path = get_procfs_path()\n    for pid in pids():\n        try:\n            with open_binary(f\"{procfs_path}/{pid}/stat\") as f:\n                data = f.read()\n        except (FileNotFoundError, ProcessLookupError):\n            pass\n        except PermissionError as err:\n            raise AccessDenied(pid) from err\n        else:\n            rpar = data.rfind(b')')\n            dset = data[rpar + 2 :].split()\n            ppid = int(dset[1])\n            ret[pid] = ppid\n    return ret\n\n\ndef wrap_exceptions(fun):\n    \"\"\"Decorator which translates bare OSError exceptions into\n    NoSuchProcess and AccessDenied.\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        pid, name = self.pid, self._name\n        try:\n            return fun(self, *args, **kwargs)\n        except PermissionError as err:\n            raise AccessDenied(pid, name) from err\n        except ProcessLookupError as err:\n            self._raise_if_zombie()\n            raise NoSuchProcess(pid, name) from err\n        except FileNotFoundError as err:\n            self._raise_if_zombie()\n            # /proc/PID directory may still exist, but the files within\n            # it may not, indicating the process is gone, see:\n            # https://github.com/giampaolo/psutil/issues/2418\n            if not os.path.exists(f\"{self._procfs_path}/{pid}/stat\"):\n                raise NoSuchProcess(pid, name) from err\n            raise\n\n    return wrapper\n\n\nclass Process:\n    \"\"\"Linux process implementation.\"\"\"\n\n    __slots__ = [\n        \"_cache\",\n        \"_ctime\",\n        \"_name\",\n        \"_ppid\",\n        \"_procfs_path\",\n        \"pid\",\n    ]\n\n    def __init__(self, pid):\n        self.pid = pid\n        self._name = None\n        self._ppid = None\n        self._ctime = None\n        self._procfs_path = get_procfs_path()\n\n    def _is_zombie(self):\n        # Note: most of the times Linux is able to return info about the\n        # process even if it's a zombie, and /proc/{pid} will exist.\n        # There are some exceptions though, like exe(), cmdline() and\n        # memory_maps(). In these cases /proc/{pid}/{file} exists but\n        # it's empty. Instead of returning a \"null\" value we'll raise an\n        # exception.\n        try:\n            data = bcat(f\"{self._procfs_path}/{self.pid}/stat\")\n        except OSError:\n            return False\n        else:\n            rpar = data.rfind(b')')\n            status = data[rpar + 2 : rpar + 3]\n            return status == b\"Z\"\n\n    def _raise_if_zombie(self):\n        if self._is_zombie():\n            raise ZombieProcess(self.pid, self._name, self._ppid)\n\n    def _raise_if_not_alive(self):\n        \"\"\"Raise NSP if the process disappeared on us.\"\"\"\n        # For those C function who do not raise NSP, possibly returning\n        # incorrect or incomplete result.\n        os.stat(f\"{self._procfs_path}/{self.pid}\")\n\n    def _readlink(self, path, fallback=UNSET):\n        # * https://github.com/giampaolo/psutil/issues/503\n        #   os.readlink('/proc/pid/exe') may raise ESRCH (ProcessLookupError)\n        #   instead of ENOENT (FileNotFoundError) when it races.\n        # * ENOENT may occur also if the path actually exists if PID is\n        #   a low PID (~0-20 range).\n        # * https://github.com/giampaolo/psutil/issues/2514\n        try:\n            return readlink(path)\n        except (FileNotFoundError, ProcessLookupError):\n            if os.path.lexists(f\"{self._procfs_path}/{self.pid}\"):\n                self._raise_if_zombie()\n                if fallback is not UNSET:\n                    return fallback\n            raise\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _parse_stat_file(self):\n        \"\"\"Parse /proc/{pid}/stat file and return a dict with various\n        process info.\n        Using \"man proc\" as a reference: where \"man proc\" refers to\n        position N always subtract 3 (e.g ppid position 4 in\n        'man proc' == position 1 in here).\n        The return value is cached in case oneshot() ctx manager is\n        in use.\n        \"\"\"\n        data = bcat(f\"{self._procfs_path}/{self.pid}/stat\")\n        # Process name is between parentheses. It can contain spaces and\n        # other parentheses. This is taken into account by looking for\n        # the first occurrence of \"(\" and the last occurrence of \")\".\n        rpar = data.rfind(b')')\n        name = data[data.find(b'(') + 1 : rpar]\n        fields = data[rpar + 2 :].split()\n\n        ret = {}\n        ret['name'] = name\n        ret['status'] = fields[0]\n        ret['ppid'] = fields[1]\n        ret['ttynr'] = fields[4]\n        ret['minflt'] = fields[7]\n        ret['majflt'] = fields[9]\n        ret['utime'] = fields[11]\n        ret['stime'] = fields[12]\n        ret['children_utime'] = fields[13]\n        ret['children_stime'] = fields[14]\n        ret['create_time'] = fields[19]\n        ret['cpu_num'] = fields[36]\n        try:\n            ret['blkio_ticks'] = fields[39]  # aka 'delayacct_blkio_ticks'\n        except IndexError:\n            # https://github.com/giampaolo/psutil/issues/2455\n            debug(\"can't get blkio_ticks, set iowait to 0\")\n            ret['blkio_ticks'] = 0\n\n        return ret\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _read_status_file(self):\n        \"\"\"Read /proc/{pid}/stat file and return its content.\n        The return value is cached in case oneshot() ctx manager is\n        in use.\n        \"\"\"\n        with open_binary(f\"{self._procfs_path}/{self.pid}/status\") as f:\n            return f.read()\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _read_smaps_file(self):\n        with open_binary(f\"{self._procfs_path}/{self.pid}/smaps\") as f:\n            return f.read().strip()\n\n    def oneshot_enter(self):\n        self._parse_stat_file.cache_activate(self)\n        self._read_status_file.cache_activate(self)\n        self._read_smaps_file.cache_activate(self)\n\n    def oneshot_exit(self):\n        self._parse_stat_file.cache_deactivate(self)\n        self._read_status_file.cache_deactivate(self)\n        self._read_smaps_file.cache_deactivate(self)\n\n    @wrap_exceptions\n    def name(self):\n        # XXX - gets changed later and probably needs refactoring\n        return decode(self._parse_stat_file()['name'])\n\n    @wrap_exceptions\n    def exe(self):\n        return self._readlink(\n            f\"{self._procfs_path}/{self.pid}/exe\", fallback=\"\"\n        )\n\n    @wrap_exceptions\n    def cmdline(self):\n        with open_text(f\"{self._procfs_path}/{self.pid}/cmdline\") as f:\n            data = f.read()\n        if not data:\n            # may happen in case of zombie process\n            self._raise_if_zombie()\n            return []\n        # 'man proc' states that args are separated by null bytes '\\0'\n        # and last char is supposed to be a null byte. Nevertheless\n        # some processes may change their cmdline after being started\n        # (via setproctitle() or similar), they are usually not\n        # compliant with this rule and use spaces instead. Google\n        # Chrome process is an example. See:\n        # https://github.com/giampaolo/psutil/issues/1179\n        sep = '\\x00' if data.endswith('\\x00') else ' '\n        if data.endswith(sep):\n            data = data[:-1]\n        cmdline = data.split(sep)\n        # Sometimes last char is a null byte '\\0' but the args are\n        # separated by spaces, see: https://github.com/giampaolo/psutil/\n        # issues/1179#issuecomment-552984549\n        if sep == '\\x00' and len(cmdline) == 1 and ' ' in data:\n            cmdline = data.split(' ')\n        return cmdline\n\n    @wrap_exceptions\n    def environ(self):\n        with open_text(f\"{self._procfs_path}/{self.pid}/environ\") as f:\n            data = f.read()\n        return parse_environ_block(data)\n\n    @wrap_exceptions\n    def terminal(self):\n        tty_nr = int(self._parse_stat_file()['ttynr'])\n        tmap = _psposix.get_terminal_map()\n        try:\n            return tmap[tty_nr]\n        except KeyError:\n            return None\n\n    # May not be available on old kernels.\n    if os.path.exists(f\"/proc/{os.getpid()}/io\"):\n\n        @wrap_exceptions\n        def io_counters(self):\n            fname = f\"{self._procfs_path}/{self.pid}/io\"\n            fields = {}\n            with open_binary(fname) as f:\n                for line in f:\n                    # https://github.com/giampaolo/psutil/issues/1004\n                    line = line.strip()\n                    if line:\n                        try:\n                            name, value = line.split(b': ')\n                        except ValueError:\n                            # https://github.com/giampaolo/psutil/issues/1004\n                            continue\n                        else:\n                            fields[name] = int(value)\n            if not fields:\n                msg = f\"{fname} file was empty\"\n                raise RuntimeError(msg)\n            try:\n                return ntp.pio(\n                    fields[b'syscr'],  # read syscalls\n                    fields[b'syscw'],  # write syscalls\n                    fields[b'read_bytes'],  # read bytes\n                    fields[b'write_bytes'],  # write bytes\n                    fields[b'rchar'],  # read chars\n                    fields[b'wchar'],  # write chars\n                )\n            except KeyError as err:\n                msg = (\n                    f\"{err.args[0]!r} field was not found in {fname}; found\"\n                    f\" fields are {fields!r}\"\n                )\n                raise ValueError(msg) from None\n\n    @wrap_exceptions\n    def cpu_times(self):\n        values = self._parse_stat_file()\n        utime = float(values['utime']) / CLOCK_TICKS\n        stime = float(values['stime']) / CLOCK_TICKS\n        children_utime = float(values['children_utime']) / CLOCK_TICKS\n        children_stime = float(values['children_stime']) / CLOCK_TICKS\n        iowait = float(values['blkio_ticks']) / CLOCK_TICKS\n        return ntp.pcputimes(\n            utime, stime, children_utime, children_stime, iowait\n        )\n\n    @wrap_exceptions\n    def cpu_num(self):\n        \"\"\"What CPU the process is on.\"\"\"\n        return int(self._parse_stat_file()['cpu_num'])\n\n    @wrap_exceptions\n    def wait(self, timeout=None):\n        return _psposix.wait_pid(self.pid, timeout)\n\n    @wrap_exceptions\n    def create_time(self, monotonic=False):\n        # The 'starttime' field in /proc/[pid]/stat is expressed in\n        # jiffies (clock ticks per second), a relative value which\n        # represents the number of clock ticks that have passed since\n        # the system booted until the process was created. It never\n        # changes and is unaffected by system clock updates.\n        if self._ctime is None:\n            self._ctime = (\n                float(self._parse_stat_file()['create_time']) / CLOCK_TICKS\n            )\n        if monotonic:\n            return self._ctime\n        # Add the boot time, returning time expressed in seconds since\n        # the epoch. This is subject to system clock updates.\n        return self._ctime + boot_time()\n\n    @wrap_exceptions\n    def memory_info(self):\n        #  ============================================================\n        # | FIELD  | DESCRIPTION                         | AKA  | TOP  |\n        #  ============================================================\n        # | rss    | resident set size                   |      | RES  |\n        # | vms    | total program size                  | size | VIRT |\n        # | shared | shared pages (from shared mappings) |      | SHR  |\n        # | text   | text ('code')                       | trs  | CODE |\n        # | lib    | library (unused in Linux 2.6)       | lrs  |      |\n        # | data   | data + stack                        | drs  | DATA |\n        # | dirty  | dirty pages (unused in Linux 2.6)   | dt   |      |\n        #  ============================================================\n        with open_binary(f\"{self._procfs_path}/{self.pid}/statm\") as f:\n            vms, rss, shared, text, _lib, data, _dirty = (\n                int(x) * PAGESIZE for x in f.readline().split()[:7]\n            )\n        return ntp.pmem(rss, vms, shared, text, data)\n\n    @wrap_exceptions\n    def memory_info_ex(\n        self,\n        _vmpeak_re=re.compile(br\"VmPeak:\\s+(\\d+)\"),\n        _vmhwm_re=re.compile(br\"VmHWM:\\s+(\\d+)\"),\n        _rssanon_re=re.compile(br\"RssAnon:\\s+(\\d+)\"),\n        _rssfile_re=re.compile(br\"RssFile:\\s+(\\d+)\"),\n        _rssshmem_re=re.compile(br\"RssShmem:\\s+(\\d+)\"),\n        _vmswap_re=re.compile(br\"VmSwap:\\s+(\\d+)\"),\n        _hugetlb_re=re.compile(br\"HugetlbPages:\\s+(\\d+)\"),\n    ):\n        # Read /proc/{pid}/status which provides peak RSS/VMS and a\n        # cheaper way to get swap (no smaps parsing needed).\n        # RssAnon/RssFile/RssShmem were added in Linux 4.5;\n        # VmSwap in 2.6.34; HugetlbPages in 4.4.\n        data = self._read_status_file()\n\n        def parse(regex):\n            m = regex.search(data)\n            return int(m.group(1)) * 1024 if m else 0\n\n        return {\n            \"peak_rss\": parse(_vmhwm_re),\n            \"peak_vms\": parse(_vmpeak_re),\n            \"rss_anon\": parse(_rssanon_re),\n            \"rss_file\": parse(_rssfile_re),\n            \"rss_shmem\": parse(_rssshmem_re),\n            \"swap\": parse(_vmswap_re),\n            \"hugetlb\": parse(_hugetlb_re),\n        }\n\n    if HAS_PROC_SMAPS_ROLLUP or HAS_PROC_SMAPS:\n\n        def _parse_smaps_rollup(self):\n            # /proc/pid/smaps_rollup was added to Linux in 2017. Faster\n            # than /proc/pid/smaps. It reports higher PSS than */smaps\n            # (from 1k up to 200k higher; tested against all processes).\n            # IMPORTANT: /proc/pid/smaps_rollup is weird, because it\n            # raises ESRCH / ENOENT for many PIDs, even if they're alive\n            # (also as root). In that case we'll use /proc/pid/smaps as\n            # fallback, which is slower but has a +50% success rate\n            # compared to /proc/pid/smaps_rollup.\n            uss = pss = swap = 0\n            with open_binary(\n                f\"{self._procfs_path}/{self.pid}/smaps_rollup\"\n            ) as f:\n                for line in f:\n                    if line.startswith(b\"Private_\"):\n                        # Private_Clean, Private_Dirty, Private_Hugetlb\n                        uss += int(line.split()[1]) * 1024\n                    elif line.startswith(b\"Pss:\"):\n                        pss = int(line.split()[1]) * 1024\n                    elif line.startswith(b\"Swap:\"):\n                        swap = int(line.split()[1]) * 1024\n            return (uss, pss, swap)\n\n        @wrap_exceptions\n        def _parse_smaps(\n            self,\n            # Gets Private_Clean, Private_Dirty, Private_Hugetlb.\n            _private_re=re.compile(br\"\\nPrivate.*:\\s+(\\d+)\"),\n            _pss_re=re.compile(br\"\\nPss\\:\\s+(\\d+)\"),\n            _swap_re=re.compile(br\"\\nSwap\\:\\s+(\\d+)\"),\n        ):\n            # /proc/pid/smaps does not exist on kernels < 2.6.14 or if\n            # CONFIG_MMU kernel configuration option is not enabled.\n\n            # Note: using 3 regexes is faster than reading the file\n            # line by line.\n            #\n            # You might be tempted to calculate USS by subtracting\n            # the \"shared\" value from the \"resident\" value in\n            # /proc/<pid>/statm. But at least on Linux, statm's \"shared\"\n            # value actually counts pages backed by files, which has\n            # little to do with whether the pages are actually shared.\n            # /proc/self/smaps on the other hand appears to give us the\n            # correct information.\n            smaps_data = self._read_smaps_file()\n            # Note: smaps file can be empty for certain processes.\n            # The code below will not crash though and will result to 0.\n            uss = sum(map(int, _private_re.findall(smaps_data))) * 1024\n            pss = sum(map(int, _pss_re.findall(smaps_data))) * 1024\n            swap = sum(map(int, _swap_re.findall(smaps_data))) * 1024\n            return (uss, pss, swap)\n\n        @wrap_exceptions\n        def memory_footprint(self):\n            def fetch():\n                if HAS_PROC_SMAPS_ROLLUP:  # faster\n                    try:\n                        return self._parse_smaps_rollup()\n                    except (ProcessLookupError, FileNotFoundError):\n                        pass\n                return self._parse_smaps()\n\n            uss, pss, swap = fetch()\n            return ntp.pfootprint(uss, pss, swap)\n\n    if HAS_PROC_SMAPS:\n\n        @wrap_exceptions\n        def memory_maps(self):\n            \"\"\"Return process's mapped memory regions as a list of named\n            tuples. Fields are explained in 'man proc'; here is an updated\n            (Apr 2012) version: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/proc.txt?id=b76437579d1344b612cf1851ae610c636cec7db0.\n\n            /proc/{PID}/smaps does not exist on kernels < 2.6.14 or if\n            CONFIG_MMU kernel configuration option is not enabled.\n            \"\"\"\n\n            def get_blocks(lines, current_block):\n                data = {}\n                for line in lines:\n                    fields = line.split(None, 5)\n                    if not fields[0].endswith(b':'):\n                        # new block section\n                        yield (current_block.pop(), data)\n                        current_block.append(line)\n                    else:\n                        try:\n                            data[fields[0]] = int(fields[1]) * 1024\n                        except (ValueError, IndexError):\n                            if fields[0].startswith(b'VmFlags:'):\n                                # see issue #369\n                                continue\n                            msg = f\"don't know how to interpret line {line!r}\"\n                            raise ValueError(msg) from None\n                yield (current_block.pop(), data)\n\n            data = self._read_smaps_file()\n            # Note: smaps file can be empty for certain processes or for\n            # zombies.\n            if not data:\n                self._raise_if_zombie()\n                return []\n            lines = data.split(b'\\n')\n            ls = []\n            first_line = lines.pop(0)\n            current_block = [first_line]\n            for header, data in get_blocks(lines, current_block):\n                hfields = header.split(None, 5)\n                try:\n                    addr, perms, _offset, _dev, _inode, path = hfields\n                except ValueError:\n                    addr, perms, _offset, _dev, _inode, path = hfields + ['']\n                if not path:\n                    path = '[anon]'\n                else:\n                    path = decode(path)\n                    path = path.strip()\n                    if path.endswith(' (deleted)') and not path_exists_strict(\n                        path\n                    ):\n                        path = path[:-10]\n                item = (\n                    decode(addr),\n                    decode(perms),\n                    path,\n                    data.get(b'Rss:', 0),\n                    data.get(b'Size:', 0),\n                    data.get(b'Pss:', 0),\n                    data.get(b'Shared_Clean:', 0),\n                    data.get(b'Shared_Dirty:', 0),\n                    data.get(b'Private_Clean:', 0),\n                    data.get(b'Private_Dirty:', 0),\n                    data.get(b'Referenced:', 0),\n                    data.get(b'Anonymous:', 0),\n                    data.get(b'Swap:', 0),\n                )\n                ls.append(item)\n            return ls\n\n    @wrap_exceptions\n    def page_faults(self):\n        values = self._parse_stat_file()\n        return ntp.ppagefaults(int(values['minflt']), int(values['majflt']))\n\n    @wrap_exceptions\n    def cwd(self):\n        return self._readlink(\n            f\"{self._procfs_path}/{self.pid}/cwd\", fallback=\"\"\n        )\n\n    @wrap_exceptions\n    def num_ctx_switches(\n        self, _ctxsw_re=re.compile(br'ctxt_switches:\\t(\\d+)')\n    ):\n        data = self._read_status_file()\n        ctxsw = _ctxsw_re.findall(data)\n        if not ctxsw:\n            msg = (\n                \"'voluntary_ctxt_switches' and\"\n                \" 'nonvoluntary_ctxt_switches'lines were not found in\"\n                f\" {self._procfs_path}/{self.pid}/status; the kernel is\"\n                \" probably older than 2.6.23\"\n            )\n            raise NotImplementedError(msg)\n        return ntp.pctxsw(int(ctxsw[0]), int(ctxsw[1]))\n\n    @wrap_exceptions\n    def num_threads(self, _num_threads_re=re.compile(br'Threads:\\t(\\d+)')):\n        # Using a re is faster than iterating over file line by line.\n        data = self._read_status_file()\n        return int(_num_threads_re.findall(data)[0])\n\n    @wrap_exceptions\n    def threads(self):\n        thread_ids = os.listdir(f\"{self._procfs_path}/{self.pid}/task\")\n        thread_ids.sort()\n        retlist = []\n        hit_enoent = False\n        for thread_id in thread_ids:\n            fname = f\"{self._procfs_path}/{self.pid}/task/{thread_id}/stat\"\n            try:\n                with open_binary(fname) as f:\n                    st = f.read().strip()\n            except (FileNotFoundError, ProcessLookupError):\n                # no such file or directory or no such process;\n                # it means thread disappeared on us\n                hit_enoent = True\n                continue\n            # ignore the first two values (\"pid (exe)\")\n            st = st[st.find(b')') + 2 :]\n            values = st.split(b' ')\n            utime = float(values[11]) / CLOCK_TICKS\n            stime = float(values[12]) / CLOCK_TICKS\n            ntuple = ntp.pthread(int(thread_id), utime, stime)\n            retlist.append(ntuple)\n        if hit_enoent:\n            self._raise_if_not_alive()\n        return retlist\n\n    @wrap_exceptions\n    def nice_get(self):\n        # with open_text(f\"{self._procfs_path}/{self.pid}/stat\") as f:\n        #   data = f.read()\n        #   return int(data.split()[18])\n\n        # Use C implementation\n        return cext.proc_priority_get(self.pid)\n\n    @wrap_exceptions\n    def nice_set(self, value):\n        return cext.proc_priority_set(self.pid, value)\n\n    # starting from CentOS 6.\n    if HAS_CPU_AFFINITY:\n\n        @wrap_exceptions\n        def cpu_affinity_get(self):\n            return cext.proc_cpu_affinity_get(self.pid)\n\n        def _get_eligible_cpus(\n            self, _re=re.compile(br\"Cpus_allowed_list:\\t(\\d+)-(\\d+)\")\n        ):\n            # See: https://github.com/giampaolo/psutil/issues/956\n            data = self._read_status_file()\n            match = _re.findall(data)\n            if match:\n                return list(range(int(match[0][0]), int(match[0][1]) + 1))\n            else:\n                return list(range(len(per_cpu_times())))\n\n        @wrap_exceptions\n        def cpu_affinity_set(self, cpus):\n            try:\n                cext.proc_cpu_affinity_set(self.pid, cpus)\n            except (OSError, ValueError) as err:\n                if isinstance(err, ValueError) or err.errno == errno.EINVAL:\n                    eligible_cpus = self._get_eligible_cpus()\n                    all_cpus = tuple(range(len(per_cpu_times())))\n                    for cpu in cpus:\n                        if cpu not in all_cpus:\n                            msg = (\n                                f\"invalid CPU {cpu!r}; choose between\"\n                                f\" {eligible_cpus!r}\"\n                            )\n                            raise ValueError(msg) from None\n                        if cpu not in eligible_cpus:\n                            msg = (\n                                f\"CPU number {cpu} is not eligible; choose\"\n                                f\" between {eligible_cpus}\"\n                            )\n                            raise ValueError(msg) from err\n                raise\n\n    # only starting from kernel 2.6.13\n    if HAS_PROC_IO_PRIORITY:\n\n        @wrap_exceptions\n        def ionice_get(self):\n            ioclass, value = cext.proc_ioprio_get(self.pid)\n            ioclass = ProcessIOPriority(ioclass)\n            return ntp.pionice(ioclass, value)\n\n        @wrap_exceptions\n        def ionice_set(self, ioclass, value):\n            if value is None:\n                value = 0\n            if value and ioclass in {\n                ProcessIOPriority.IOPRIO_CLASS_IDLE,\n                ProcessIOPriority.IOPRIO_CLASS_NONE,\n            }:\n                msg = f\"{ioclass!r} ioclass accepts no value\"\n                raise ValueError(msg)\n            if value < 0 or value > 7:\n                msg = \"value not in 0-7 range\"\n                raise ValueError(msg)\n            return cext.proc_ioprio_set(self.pid, ioclass, value)\n\n    if hasattr(resource, \"prlimit\"):\n\n        @wrap_exceptions\n        def rlimit(self, resource_, limits=None):\n            # If pid is 0 prlimit() applies to the calling process and\n            # we don't want that. We should never get here though as\n            # PID 0 is not supported on Linux.\n            if self.pid == 0:\n                msg = \"can't use prlimit() against PID 0 process\"\n                raise ValueError(msg)\n            try:\n                if limits is None:\n                    # get\n                    return resource.prlimit(self.pid, resource_)\n                else:\n                    # set\n                    if len(limits) != 2:\n                        msg = (\n                            \"second argument must be a (soft, hard) \"\n                            f\"tuple, got {limits!r}\"\n                        )\n                        raise ValueError(msg)\n                    resource.prlimit(self.pid, resource_, limits)\n            except OSError as err:\n                if err.errno == errno.ENOSYS:\n                    # I saw this happening on Travis:\n                    # https://travis-ci.org/giampaolo/psutil/jobs/51368273\n                    self._raise_if_zombie()\n                raise\n\n    @wrap_exceptions\n    def status(self):\n        letter = self._parse_stat_file()['status']\n        letter = letter.decode()\n        # XXX is '?' legit? (we're not supposed to return it anyway)\n        return PROC_STATUSES.get(letter, '?')\n\n    @wrap_exceptions\n    def open_files(self):\n        retlist = []\n        files = os.listdir(f\"{self._procfs_path}/{self.pid}/fd\")\n        hit_enoent = False\n        for fd in files:\n            file = f\"{self._procfs_path}/{self.pid}/fd/{fd}\"\n            try:\n                path = readlink(file)\n            except (FileNotFoundError, ProcessLookupError):\n                # ENOENT == file which is gone in the meantime\n                hit_enoent = True\n                continue\n            except OSError as err:\n                if err.errno == errno.EINVAL:\n                    # not a link\n                    continue\n                if err.errno == errno.ENAMETOOLONG:\n                    # file name too long\n                    debug(err)\n                    continue\n                raise\n            else:\n                # If path is not an absolute there's no way to tell\n                # whether it's a regular file or not, so we skip it.\n                # A regular file is always supposed to be have an\n                # absolute path though.\n                if path.startswith('/') and isfile_strict(path):\n                    # Get file position and flags.\n                    file = f\"{self._procfs_path}/{self.pid}/fdinfo/{fd}\"\n                    try:\n                        with open_binary(file) as f:\n                            pos = int(f.readline().split()[1])\n                            flags = int(f.readline().split()[1], 8)\n                    except (FileNotFoundError, ProcessLookupError):\n                        # fd gone in the meantime; process may\n                        # still be alive\n                        hit_enoent = True\n                    else:\n                        mode = file_flags_to_mode(flags)\n                        ntuple = ntp.popenfile(\n                            path, int(fd), int(pos), mode, flags\n                        )\n                        retlist.append(ntuple)\n        if hit_enoent:\n            self._raise_if_not_alive()\n        return retlist\n\n    @wrap_exceptions\n    def net_connections(self, kind='inet'):\n        ret = _net_connections.retrieve(kind, self.pid)\n        self._raise_if_not_alive()\n        return ret\n\n    @wrap_exceptions\n    def num_fds(self):\n        return len(os.listdir(f\"{self._procfs_path}/{self.pid}/fd\"))\n\n    @wrap_exceptions\n    def ppid(self):\n        return int(self._parse_stat_file()['ppid'])\n\n    @wrap_exceptions\n    def uids(self, _uids_re=re.compile(br'Uid:\\t(\\d+)\\t(\\d+)\\t(\\d+)')):\n        data = self._read_status_file()\n        real, effective, saved = _uids_re.findall(data)[0]\n        return ntp.puids(int(real), int(effective), int(saved))\n\n    @wrap_exceptions\n    def gids(self, _gids_re=re.compile(br'Gid:\\t(\\d+)\\t(\\d+)\\t(\\d+)')):\n        data = self._read_status_file()\n        real, effective, saved = _gids_re.findall(data)[0]\n        return ntp.pgids(int(real), int(effective), int(saved))\n"
  },
  {
    "path": "psutil/_psosx.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"macOS platform implementation.\"\"\"\n\nimport errno\nimport functools\nimport os\n\nfrom . import _ntuples as ntp\nfrom . import _psposix\nfrom . import _psutil_osx as cext\nfrom ._common import AccessDenied\nfrom ._common import NoSuchProcess\nfrom ._common import ZombieProcess\nfrom ._common import conn_tmap\nfrom ._common import conn_to_ntuple\nfrom ._common import debug\nfrom ._common import isfile_strict\nfrom ._common import memoize_when_activated\nfrom ._common import parse_environ_block\nfrom ._common import usage_percent\nfrom ._enums import BatteryTime\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessStatus\n\n__extra__all__ = []\n\n\n# =====================================================================\n# --- globals\n# =====================================================================\n\n\nPAGESIZE = cext.getpagesize()\nAF_LINK = cext.AF_LINK\n\nTCP_STATUSES = {\n    cext.TCPS_ESTABLISHED: ConnectionStatus.CONN_ESTABLISHED,\n    cext.TCPS_SYN_SENT: ConnectionStatus.CONN_SYN_SENT,\n    cext.TCPS_SYN_RECEIVED: ConnectionStatus.CONN_SYN_RECV,\n    cext.TCPS_FIN_WAIT_1: ConnectionStatus.CONN_FIN_WAIT1,\n    cext.TCPS_FIN_WAIT_2: ConnectionStatus.CONN_FIN_WAIT2,\n    cext.TCPS_TIME_WAIT: ConnectionStatus.CONN_TIME_WAIT,\n    cext.TCPS_CLOSED: ConnectionStatus.CONN_CLOSE,\n    cext.TCPS_CLOSE_WAIT: ConnectionStatus.CONN_CLOSE_WAIT,\n    cext.TCPS_LAST_ACK: ConnectionStatus.CONN_LAST_ACK,\n    cext.TCPS_LISTEN: ConnectionStatus.CONN_LISTEN,\n    cext.TCPS_CLOSING: ConnectionStatus.CONN_CLOSING,\n    cext.PSUTIL_CONN_NONE: ConnectionStatus.CONN_NONE,\n}\n\nPROC_STATUSES = {\n    cext.SIDL: ProcessStatus.STATUS_IDLE,\n    cext.SRUN: ProcessStatus.STATUS_RUNNING,\n    cext.SSLEEP: ProcessStatus.STATUS_SLEEPING,\n    cext.SSTOP: ProcessStatus.STATUS_STOPPED,\n    cext.SZOMB: ProcessStatus.STATUS_ZOMBIE,\n}\n\n\n# =====================================================================\n# --- memory\n# =====================================================================\n\n\ndef virtual_memory():\n    \"\"\"System virtual memory as a named tuple.\"\"\"\n    d = cext.virtual_mem()\n    d[\"percent\"] = usage_percent(\n        (d[\"total\"] - d[\"available\"]), d[\"total\"], round_=1\n    )\n    return ntp.svmem(**d)\n\n\ndef swap_memory():\n    \"\"\"Swap system memory as a (total, used, free, sin, sout) tuple.\"\"\"\n    d = cext.swap_mem()\n    d[\"percent\"] = usage_percent(d[\"used\"], d[\"total\"], round_=1)\n    return ntp.sswap(**d)\n\n\n# malloc / heap functions\nheap_info = cext.heap_info\nheap_trim = cext.heap_trim\n\n\n# =====================================================================\n# --- CPU\n# =====================================================================\n\n\ndef cpu_times():\n    \"\"\"Return system CPU times as a named tuple.\"\"\"\n    user, nice, system, idle = cext.cpu_times()\n    return ntp.scputimes(user, system, idle, nice)\n\n\ndef per_cpu_times():\n    \"\"\"Return system CPU times as a named tuple.\"\"\"\n    ret = []\n    for cpu_t in cext.per_cpu_times():\n        user, nice, system, idle = cpu_t\n        item = ntp.scputimes(user, system, idle, nice)\n        ret.append(item)\n    return ret\n\n\ndef cpu_count_logical():\n    \"\"\"Return the number of logical CPUs in the system.\"\"\"\n    return cext.cpu_count_logical()\n\n\ndef cpu_count_cores():\n    \"\"\"Return the number of CPU cores in the system.\"\"\"\n    return cext.cpu_count_cores()\n\n\ndef cpu_stats():\n    ctx_switches, interrupts, soft_interrupts, syscalls, _traps = (\n        cext.cpu_stats()\n    )\n    return ntp.scpustats(ctx_switches, interrupts, soft_interrupts, syscalls)\n\n\nif cext.has_cpu_freq():  # not always available on ARM64\n\n    def cpu_freq():\n        \"\"\"Return CPU frequency.\n        On macOS per-cpu frequency is not supported.\n        Also, the returned frequency never changes, see:\n        https://arstechnica.com/civis/viewtopic.php?f=19&t=465002.\n        \"\"\"\n        curr, min_, max_ = cext.cpu_freq()\n        return [ntp.scpufreq(curr, min_, max_)]\n\n\n# =====================================================================\n# --- disks\n# =====================================================================\n\n\ndisk_usage = _psposix.disk_usage\ndisk_io_counters = cext.disk_io_counters\n\n\ndef disk_partitions(all=False):\n    \"\"\"Return mounted disk partitions as a list of named tuples.\"\"\"\n    retlist = []\n    partitions = cext.disk_partitions()\n    for partition in partitions:\n        device, mountpoint, fstype, opts = partition\n        if device == 'none':\n            device = ''\n        if not all:\n            if not os.path.isabs(device) or not os.path.exists(device):\n                continue\n        ntuple = ntp.sdiskpart(device, mountpoint, fstype, opts)\n        retlist.append(ntuple)\n    return retlist\n\n\n# =====================================================================\n# --- sensors\n# =====================================================================\n\n\ndef sensors_battery():\n    \"\"\"Return battery information.\"\"\"\n    try:\n        percent, minsleft, power_plugged = cext.sensors_battery()\n    except NotImplementedError:\n        # no power source - return None according to interface\n        return None\n    power_plugged = power_plugged == 1\n    if power_plugged:\n        secsleft = BatteryTime.POWER_TIME_UNLIMITED\n    elif minsleft == -1:\n        secsleft = BatteryTime.POWER_TIME_UNKNOWN\n    else:\n        secsleft = minsleft * 60\n    return ntp.sbattery(percent, secsleft, power_plugged)\n\n\n# =====================================================================\n# --- network\n# =====================================================================\n\n\nnet_io_counters = cext.net_io_counters\nnet_if_addrs = cext.net_if_addrs\n\n\ndef net_connections(kind='inet'):\n    \"\"\"System-wide network connections.\"\"\"\n    # Note: on macOS this will fail with AccessDenied unless\n    # the process is owned by root.\n    ret = []\n    for pid in pids():\n        try:\n            cons = Process(pid).net_connections(kind)\n        except NoSuchProcess:\n            continue\n        else:\n            if cons:\n                for c in cons:\n                    c = list(c) + [pid]\n                    ret.append(ntp.sconn(*c))\n    return ret\n\n\ndef net_if_stats():\n    \"\"\"Get NIC stats (isup, duplex, speed, mtu).\"\"\"\n    names = net_io_counters().keys()\n    ret = {}\n    for name in names:\n        try:\n            mtu = cext.net_if_mtu(name)\n            flags = cext.net_if_flags(name)\n            duplex, speed = cext.net_if_duplex_speed(name)\n        except OSError as err:\n            # https://github.com/giampaolo/psutil/issues/1279\n            if err.errno != errno.ENODEV:\n                raise\n        else:\n            duplex = NicDuplex(duplex)\n            output_flags = ','.join(flags)\n            isup = 'running' in flags\n            ret[name] = ntp.snicstats(isup, duplex, speed, mtu, output_flags)\n    return ret\n\n\n# =====================================================================\n# --- other system functions\n# =====================================================================\n\n\ndef boot_time():\n    \"\"\"The system boot time expressed in seconds since the epoch.\"\"\"\n    return cext.boot_time()\n\n\ntry:\n    INIT_BOOT_TIME = boot_time()\nexcept Exception as err:  # noqa: BLE001\n    # Don't want to crash at import time.\n    debug(f\"ignoring exception on import: {err!r}\")\n    INIT_BOOT_TIME = 0\n\n\ndef adjust_proc_create_time(ctime):\n    \"\"\"Account for system clock updates.\"\"\"\n    if INIT_BOOT_TIME == 0:\n        return ctime\n\n    diff = INIT_BOOT_TIME - boot_time()\n    if diff == 0 or abs(diff) < 1:\n        return ctime\n\n    debug(\"system clock was updated; adjusting process create_time()\")\n    if diff < 0:\n        return ctime - diff\n    return ctime + diff\n\n\ndef users():\n    \"\"\"Return currently connected users as a list of named tuples.\"\"\"\n    retlist = []\n    rawlist = cext.users()\n    for item in rawlist:\n        user, tty, hostname, tstamp, pid = item\n        if tty == '~':\n            continue  # reboot or shutdown\n        if not tstamp:\n            continue\n        nt = ntp.suser(user, tty or None, hostname or None, tstamp, pid)\n        retlist.append(nt)\n    return retlist\n\n\n# =====================================================================\n# --- processes\n# =====================================================================\n\n\ndef pids():\n    ls = cext.pids()\n    if 0 not in ls:\n        # On certain macOS versions pids() C doesn't return PID 0 but\n        # \"ps\" does and the process is querable via sysctl():\n        # https://travis-ci.org/giampaolo/psutil/jobs/309619941\n        try:\n            Process(0).create_time()\n            ls.insert(0, 0)\n        except NoSuchProcess:\n            pass\n        except AccessDenied:\n            ls.insert(0, 0)\n    return ls\n\n\npid_exists = _psposix.pid_exists\n\n\ndef wrap_exceptions(fun):\n    \"\"\"Decorator which translates bare OSError exceptions into\n    NoSuchProcess and AccessDenied.\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        pid, ppid, name = self.pid, self._ppid, self._name\n        try:\n            return fun(self, *args, **kwargs)\n        except ProcessLookupError as err:\n            if cext.proc_is_zombie(pid):\n                raise ZombieProcess(pid, name, ppid) from err\n            raise NoSuchProcess(pid, name) from err\n        except PermissionError as err:\n            raise AccessDenied(pid, name) from err\n        except cext.ZombieProcessError as err:\n            raise ZombieProcess(pid, name, ppid) from err\n\n    return wrapper\n\n\nclass Process:\n    \"\"\"Wrapper class around underlying C implementation.\"\"\"\n\n    __slots__ = [\"_cache\", \"_name\", \"_ppid\", \"pid\"]\n\n    def __init__(self, pid):\n        self.pid = pid\n        self._name = None\n        self._ppid = None\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _oneshot_kinfo(self):\n        # Note: should work with all PIDs without permission issues.\n        return cext.proc_oneshot_kinfo(self.pid)\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _oneshot_pidtaskinfo(self):\n        # Note: should work for PIDs owned by user only.\n        return cext.proc_oneshot_pidtaskinfo(self.pid)\n\n    def oneshot_enter(self):\n        self._oneshot_kinfo.cache_activate(self)\n        self._oneshot_pidtaskinfo.cache_activate(self)\n\n    def oneshot_exit(self):\n        self._oneshot_kinfo.cache_deactivate(self)\n        self._oneshot_pidtaskinfo.cache_deactivate(self)\n\n    @wrap_exceptions\n    def name(self):\n        name = self._oneshot_kinfo()[\"name\"]\n        return name if name is not None else cext.proc_name(self.pid)\n\n    @wrap_exceptions\n    def exe(self):\n        return cext.proc_exe(self.pid)\n\n    @wrap_exceptions\n    def cmdline(self):\n        return cext.proc_cmdline(self.pid)\n\n    @wrap_exceptions\n    def environ(self):\n        return parse_environ_block(cext.proc_environ(self.pid))\n\n    @wrap_exceptions\n    def ppid(self):\n        self._ppid = self._oneshot_kinfo()[\"ppid\"]\n        return self._ppid\n\n    @wrap_exceptions\n    def cwd(self):\n        return cext.proc_cwd(self.pid)\n\n    @wrap_exceptions\n    def uids(self):\n        d = self._oneshot_kinfo()\n        return ntp.puids(d[\"ruid\"], d[\"euid\"], d[\"suid\"])\n\n    @wrap_exceptions\n    def gids(self):\n        d = self._oneshot_kinfo()\n        return ntp.pgids(d[\"rgid\"], d[\"egid\"], d[\"sgid\"])\n\n    @wrap_exceptions\n    def terminal(self):\n        tty_nr = self._oneshot_kinfo()[\"ttynr\"]\n        tmap = _psposix.get_terminal_map()\n        try:\n            return tmap[tty_nr]\n        except KeyError:\n            return None\n\n    @wrap_exceptions\n    def memory_info(self):\n        d = self._oneshot_pidtaskinfo()\n        return ntp.pmem(d[\"rss\"], d[\"vms\"])\n\n    @wrap_exceptions\n    def memory_info_ex(self):\n        return cext.proc_memory_info_ex(self.pid)\n\n    @wrap_exceptions\n    def memory_footprint(self):\n        uss = cext.proc_memory_uss(self.pid)\n        return ntp.pfootprint(uss)\n\n    @wrap_exceptions\n    def page_faults(self):\n        d = self._oneshot_pidtaskinfo()\n        return ntp.ppagefaults(d[\"minor_faults\"], d[\"major_faults\"])\n\n    @wrap_exceptions\n    def cpu_times(self):\n        d = self._oneshot_pidtaskinfo()\n        # children user / system times are not retrievable (set to 0)\n        return ntp.pcputimes(d[\"cpu_utime\"], d[\"cpu_stime\"], 0.0, 0.0)\n\n    @wrap_exceptions\n    def create_time(self, monotonic=False):\n        ctime = self._oneshot_kinfo()[\"ctime\"]\n        if not monotonic:\n            ctime = adjust_proc_create_time(ctime)\n        return ctime\n\n    @wrap_exceptions\n    def num_ctx_switches(self):\n        # Unvoluntary value seems not to be available;\n        # getrusage() numbers seems to confirm this theory.\n        # We set it to 0.\n        vol = self._oneshot_pidtaskinfo()[\"volctxsw\"]\n        return ntp.pctxsw(vol, 0)\n\n    @wrap_exceptions\n    def num_threads(self):\n        return self._oneshot_pidtaskinfo()[\"num_threads\"]\n\n    @wrap_exceptions\n    def open_files(self):\n        if self.pid == 0:\n            return []\n        files = []\n        rawlist = cext.proc_open_files(self.pid)\n        for path, fd in rawlist:\n            if isfile_strict(path):\n                ntuple = ntp.popenfile(path, fd)\n                files.append(ntuple)\n        return files\n\n    @wrap_exceptions\n    def net_connections(self, kind='inet'):\n        families, types = conn_tmap[kind]\n        rawlist = cext.proc_net_connections(self.pid, families, types)\n        ret = []\n        for item in rawlist:\n            fd, fam, type, laddr, raddr, status = item\n            nt = conn_to_ntuple(\n                fd, fam, type, laddr, raddr, status, TCP_STATUSES\n            )\n            ret.append(nt)\n        return ret\n\n    @wrap_exceptions\n    def num_fds(self):\n        if self.pid == 0:\n            return 0\n        return cext.proc_num_fds(self.pid)\n\n    @wrap_exceptions\n    def wait(self, timeout=None):\n        return _psposix.wait_pid(self.pid, timeout)\n\n    @wrap_exceptions\n    def nice_get(self):\n        return cext.proc_priority_get(self.pid)\n\n    @wrap_exceptions\n    def nice_set(self, value):\n        return cext.proc_priority_set(self.pid, value)\n\n    @wrap_exceptions\n    def status(self):\n        code = self._oneshot_kinfo()[\"status\"]\n        # XXX is '?' legit? (we're not supposed to return it anyway)\n        return PROC_STATUSES.get(code, '?')\n\n    @wrap_exceptions\n    def threads(self):\n        rawlist = cext.proc_threads(self.pid)\n        retlist = []\n        for thread_id, utime, stime in rawlist:\n            ntuple = ntp.pthread(thread_id, utime, stime)\n            retlist.append(ntuple)\n        return retlist\n"
  },
  {
    "path": "psutil/_psposix.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Routines common to all posix systems.\"\"\"\n\nimport enum\nimport errno\nimport functools\nimport glob\nimport os\nimport select\nimport signal\nimport time\n\nfrom . import _ntuples as ntp\nfrom ._common import MACOS\nfrom ._common import TimeoutExpired\nfrom ._common import debug\nfrom ._common import usage_percent\n\nif MACOS:\n    from . import _psutil_osx\n\n\n__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']\n\n\ndef pid_exists(pid):\n    \"\"\"Check whether pid exists in the current process table.\"\"\"\n    if pid == 0:\n        # According to \"man 2 kill\" PID 0 has a special meaning:\n        # it refers to <<every process in the process group of the\n        # calling process>> so we don't want to go any further.\n        # If we get here it means this UNIX platform *does* have\n        # a process with id 0.\n        return True\n    try:\n        os.kill(pid, 0)\n    except ProcessLookupError:\n        return False\n    except PermissionError:\n        # EPERM clearly means there's a process to deny access to\n        return True\n    # According to \"man 2 kill\" possible error values are\n    # (EINVAL, EPERM, ESRCH)\n    else:\n        return True\n\n\nNegsignal = enum.IntEnum(\n    'Negsignal', {x.name: -x.value for x in signal.Signals}\n)\n\n\ndef negsig_to_enum(num):\n    \"\"\"Convert a negative signal value to an enum.\"\"\"\n    try:\n        return Negsignal(num)\n    except ValueError:\n        return num\n\n\ndef convert_exit_code(status):\n    \"\"\"Convert a os.waitpid() status to an exit code.\"\"\"\n    if os.WIFEXITED(status):\n        # Process terminated normally by calling exit(3) or _exit(2),\n        # or by returning from main(). The return value is the\n        # positive integer passed to *exit().\n        return os.WEXITSTATUS(status)\n    if os.WIFSIGNALED(status):\n        # Process exited due to a signal. Return the negative value\n        # of that signal.\n        return negsig_to_enum(-os.WTERMSIG(status))\n    # if os.WIFSTOPPED(status):\n    #     # Process was stopped via SIGSTOP or is being traced, and\n    #     # waitpid() was called with WUNTRACED flag. PID is still\n    #     # alive. From now on waitpid() will keep returning (0, 0)\n    #     # until the process state doesn't change.\n    #     # It may make sense to catch/enable this since stopped PIDs\n    #     # ignore SIGTERM.\n    #     interval = sleep(interval)\n    #     continue\n    # if os.WIFCONTINUED(status):\n    #     # Process was resumed via SIGCONT and waitpid() was called\n    #     # with WCONTINUED flag.\n    #     interval = sleep(interval)\n    #     continue\n\n    # Should never happen.\n    msg = f\"unknown process exit status {status!r}\"\n    raise ValueError(msg)\n\n\ndef wait_pid_posix(\n    pid,\n    timeout=None,\n    _waitpid=os.waitpid,\n    _timer=getattr(time, 'monotonic', time.time),  # noqa: B008\n    _min=min,\n    _sleep=time.sleep,\n    _pid_exists=pid_exists,\n):\n    \"\"\"Wait for a process PID to terminate.\n\n    If the process terminated normally by calling exit(3) or _exit(2),\n    or by returning from main(), the return value is the positive integer\n    passed to *exit().\n\n    If it was terminated by a signal it returns the negated value of the\n    signal which caused the termination (e.g. -SIGTERM).\n\n    If PID is not a children of os.getpid() (current process) just\n    wait until the process disappears and return None.\n\n    If PID does not exist at all return None immediately.\n\n    If timeout is specified and process is still alive raise\n    TimeoutExpired.\n\n    If timeout=0 either return immediately or raise TimeoutExpired\n    (non-blocking).\n    \"\"\"\n    interval = 0.0001\n    max_interval = 0.04\n    flags = 0\n    stop_at = None\n\n    if timeout is not None:\n        flags |= os.WNOHANG\n        if timeout != 0:\n            stop_at = _timer() + timeout\n\n    def sleep_or_timeout(interval):\n        # Sleep for some time and return a new increased interval.\n        if timeout == 0 or (stop_at is not None and _timer() >= stop_at):\n            raise TimeoutExpired(timeout)\n        _sleep(interval)\n        return _min(interval * 2, max_interval)\n\n    # See: https://linux.die.net/man/2/waitpid\n    while True:\n        try:\n            retpid, status = os.waitpid(pid, flags)\n        except ChildProcessError:\n            # This has two meanings:\n            # - PID is not a child of os.getpid() in which case\n            #   we keep polling until it's gone\n            # - PID never existed in the first place\n            # In both cases we'll eventually return None as we\n            # can't determine its exit status code.\n            while _pid_exists(pid):\n                interval = sleep_or_timeout(interval)\n            return None\n        else:\n            if retpid == 0:\n                # WNOHANG flag was used and PID is still running.\n                interval = sleep_or_timeout(interval)\n            else:\n                return convert_exit_code(status)\n\n\ndef _waitpid(pid, timeout):\n    \"\"\"Wrapper around os.waitpid(). PID is supposed to be gone already,\n    it just returns the exit code.\n    \"\"\"\n    try:\n        retpid, status = os.waitpid(pid, 0)\n    except ChildProcessError:\n        # PID is not a child of os.getpid().\n        return wait_pid_posix(pid, timeout)\n    else:\n        assert retpid != 0\n        return convert_exit_code(status)\n\n\ndef wait_pid_pidfd_open(pid, timeout=None):\n    \"\"\"Wait for PID to terminate using pidfd_open() + poll(). Linux >=\n    5.3 + Python >= 3.9 only.\n    \"\"\"\n    try:\n        pidfd = os.pidfd_open(pid, 0)\n    except OSError as err:\n        # ESRCH = no such process, EMFILE / ENFILE = too many open files\n        if err.errno not in {errno.ESRCH, errno.EMFILE, errno.ENFILE}:\n            debug(f\"pidfd_open() failed unexpectedly ({err!r}); use fallback\")\n        return wait_pid_posix(pid, timeout)\n\n    try:\n        # poll() / select() have the advantage of not requiring any\n        # extra file descriptor, contrary to epoll() / kqueue().\n        # select() crashes if process opens > 1024 FDs, so we use\n        # poll().\n        poller = select.poll()\n        poller.register(pidfd, select.POLLIN)\n        timeout_ms = None if timeout is None else int(timeout * 1000)\n        events = poller.poll(timeout_ms)  # wait\n\n        if not events:\n            raise TimeoutExpired(timeout)\n        return _waitpid(pid, timeout)\n    finally:\n        os.close(pidfd)\n\n\ndef wait_pid_kqueue(pid, timeout=None):\n    \"\"\"Wait for PID to terminate using kqueue(). macOS and BSD only.\"\"\"\n    try:\n        kq = select.kqueue()\n    except OSError as err:\n        if err.errno not in {errno.EMFILE, errno.ENFILE}:\n            debug(f\"kqueue() failed unexpectedly ({err!r}); use fallback\")\n        return wait_pid_posix(pid, timeout)\n\n    try:\n        kev = select.kevent(\n            pid,\n            filter=select.KQ_FILTER_PROC,\n            flags=select.KQ_EV_ADD | select.KQ_EV_ONESHOT,\n            fflags=select.KQ_NOTE_EXIT,\n        )\n        try:\n            events = kq.control([kev], 1, timeout)  # wait\n        except OSError as err:\n            if err.errno in {errno.EACCES, errno.EPERM, errno.ESRCH}:\n                debug(f\"kqueue.control() failed ({err!r}); use fallback\")\n                return wait_pid_posix(pid, timeout)\n            raise\n        else:\n            if not events:\n                raise TimeoutExpired(timeout)\n            return _waitpid(pid, timeout)\n    finally:\n        kq.close()\n\n\n@functools.lru_cache\ndef can_use_pidfd_open():\n    # Availability: Linux >= 5.3, Python >= 3.9\n    if not hasattr(os, \"pidfd_open\"):\n        return False\n    try:\n        pidfd = os.pidfd_open(os.getpid(), 0)\n    except OSError as err:\n        if err.errno in {errno.EMFILE, errno.ENFILE}:  # noqa: SIM103\n            # transitory 'too many open files'\n            return True\n        # likely blocked by security policy like SECCOMP (EPERM,\n        # EACCES, ENOSYS)\n        return False\n    else:\n        os.close(pidfd)\n        return True\n\n\n@functools.lru_cache\ndef can_use_kqueue():\n    # Availability: macOS, BSD\n    names = (\n        \"kqueue\",\n        \"KQ_EV_ADD\",\n        \"KQ_EV_ONESHOT\",\n        \"KQ_FILTER_PROC\",\n        \"KQ_NOTE_EXIT\",\n    )\n    if not all(hasattr(select, x) for x in names):\n        return False\n    kq = None\n    try:\n        kq = select.kqueue()\n        kev = select.kevent(\n            os.getpid(),\n            filter=select.KQ_FILTER_PROC,\n            flags=select.KQ_EV_ADD | select.KQ_EV_ONESHOT,\n            fflags=select.KQ_NOTE_EXIT,\n        )\n        kq.control([kev], 1, 0)\n        return True\n    except OSError as err:\n        if err.errno in {errno.EMFILE, errno.ENFILE}:  # noqa: SIM103\n            # transitory 'too many open files'\n            return True\n        return False\n    finally:\n        if kq is not None:\n            kq.close()\n\n\ndef wait_pid(pid, timeout=None):\n    # PID 0 passed to waitpid() waits for any child of the current\n    # process to change state.\n    assert pid > 0\n    if timeout is not None:\n        assert timeout >= 0\n\n    if can_use_pidfd_open():\n        return wait_pid_pidfd_open(pid, timeout)\n    elif can_use_kqueue():\n        return wait_pid_kqueue(pid, timeout)\n    else:\n        return wait_pid_posix(pid, timeout)\n\n\nwait_pid.__doc__ = wait_pid_posix.__doc__\n\n\ndef disk_usage(path):\n    \"\"\"Return disk usage associated with path.\n    Note: UNIX usually reserves 5% disk space which is not accessible\n    by user. In this function \"total\" and \"used\" values reflect the\n    total and used disk space whereas \"free\" and \"percent\" represent\n    the \"free\" and \"used percent\" user disk space.\n    \"\"\"\n    st = os.statvfs(path)\n    # Total space which is only available to root (unless changed\n    # at system level).\n    total = st.f_blocks * st.f_frsize\n    # Remaining free space usable by root.\n    avail_to_root = st.f_bfree * st.f_frsize\n    # Remaining free space usable by user.\n    avail_to_user = st.f_bavail * st.f_frsize\n    # Total space being used in general.\n    used = total - avail_to_root\n    if MACOS:\n        # see: https://github.com/giampaolo/psutil/pull/2152\n        used = _psutil_osx.disk_usage_used(path, used)\n    # Total space which is available to user (same as 'total' but\n    # for the user).\n    total_user = used + avail_to_user\n    # User usage percent compared to the total amount of space\n    # the user can use. This number would be higher if compared\n    # to root's because the user has less space (usually -5%).\n    usage_percent_user = usage_percent(used, total_user, round_=1)\n\n    # NB: the percentage is -5% than what shown by df due to\n    # reserved blocks that we are currently not considering:\n    # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462\n    return ntp.sdiskusage(\n        total=total, used=used, free=avail_to_user, percent=usage_percent_user\n    )\n\n\n@functools.lru_cache\ndef get_terminal_map():\n    \"\"\"Get a map of device-id -> path as a dict.\n    Used by Process.terminal().\n    \"\"\"\n    ret = {}\n    ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')\n    for name in ls:\n        assert name not in ret, name\n        try:\n            ret[os.stat(name).st_rdev] = name\n        except FileNotFoundError:\n            pass\n    return ret\n"
  },
  {
    "path": "psutil/_pssunos.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Sun OS Solaris platform implementation.\"\"\"\n\nimport errno\nimport functools\nimport os\nimport socket\nimport subprocess\nimport sys\nfrom collections import namedtuple\nfrom socket import AF_INET\n\nfrom . import _ntuples as ntp\nfrom . import _psposix\nfrom . import _psutil_sunos as cext\nfrom ._common import AF_INET6\nfrom ._common import ENCODING\nfrom ._common import AccessDenied\nfrom ._common import NoSuchProcess\nfrom ._common import ZombieProcess\nfrom ._common import conn_tmap\nfrom ._common import debug\nfrom ._common import get_procfs_path\nfrom ._common import isfile_strict\nfrom ._common import memoize_when_activated\nfrom ._common import sockfam_to_enum\nfrom ._common import socktype_to_enum\nfrom ._common import usage_percent\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessStatus\n\n__extra__all__ = [\"PROCFS_PATH\"]\n\n\n# =====================================================================\n# --- globals\n# =====================================================================\n\n\nPAGE_SIZE = cext.getpagesize()\nAF_LINK = cext.AF_LINK\nIS_64_BIT = sys.maxsize > 2**32\n\n\nPROC_STATUSES = {\n    cext.SSLEEP: ProcessStatus.STATUS_SLEEPING,\n    cext.SRUN: ProcessStatus.STATUS_RUNNING,\n    cext.SZOMB: ProcessStatus.STATUS_ZOMBIE,\n    cext.SSTOP: ProcessStatus.STATUS_STOPPED,\n    cext.SIDL: ProcessStatus.STATUS_IDLE,\n    cext.SONPROC: ProcessStatus.STATUS_RUNNING,  # same as run\n    cext.SWAIT: ProcessStatus.STATUS_WAITING,\n}\n\nTCP_STATUSES = {\n    cext.TCPS_ESTABLISHED: ConnectionStatus.CONN_ESTABLISHED,\n    cext.TCPS_SYN_SENT: ConnectionStatus.CONN_SYN_SENT,\n    cext.TCPS_SYN_RCVD: ConnectionStatus.CONN_SYN_RECV,\n    cext.TCPS_FIN_WAIT_1: ConnectionStatus.CONN_FIN_WAIT1,\n    cext.TCPS_FIN_WAIT_2: ConnectionStatus.CONN_FIN_WAIT2,\n    cext.TCPS_TIME_WAIT: ConnectionStatus.CONN_TIME_WAIT,\n    cext.TCPS_CLOSED: ConnectionStatus.CONN_CLOSE,\n    cext.TCPS_CLOSE_WAIT: ConnectionStatus.CONN_CLOSE_WAIT,\n    cext.TCPS_LAST_ACK: ConnectionStatus.CONN_LAST_ACK,\n    cext.TCPS_LISTEN: ConnectionStatus.CONN_LISTEN,\n    cext.TCPS_CLOSING: ConnectionStatus.CONN_CLOSING,\n    cext.PSUTIL_CONN_NONE: ConnectionStatus.CONN_NONE,\n    cext.TCPS_IDLE: ConnectionStatus.CONN_IDLE,  # sunos specific\n    cext.TCPS_BOUND: ConnectionStatus.CONN_BOUND,  # sunos specific\n}\n\nproc_info_map = dict(\n    ppid=0,\n    rss=1,\n    vms=2,\n    create_time=3,\n    nice=4,\n    num_threads=5,\n    status=6,\n    ttynr=7,\n    uid=8,\n    euid=9,\n    gid=10,\n    egid=11,\n)\n\n\n# =====================================================================\n# --- memory\n# =====================================================================\n\n\ndef virtual_memory():\n    \"\"\"Report virtual memory metrics.\"\"\"\n    # we could have done this with kstat, but IMHO this is good enough\n    total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE\n    # note: there's no difference on Solaris\n    free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE\n    used = total - free\n    percent = usage_percent(used, total, round_=1)\n    return ntp.svmem(total, avail, percent, used, free)\n\n\ndef swap_memory():\n    \"\"\"Report swap memory metrics.\"\"\"\n    sin, sout = cext.swap_mem()\n    # XXX\n    # we are supposed to get total/free by doing so:\n    # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/swap/swap.c\n    # ...nevertheless I can't manage to obtain the same numbers as 'swap'\n    # cmdline utility, so let's parse its output (sigh!)\n    p = subprocess.Popen(\n        [\n            '/usr/bin/env',\n            f\"PATH=/usr/sbin:/sbin:{os.environ['PATH']}\",\n            'swap',\n            '-l',\n        ],\n        stdout=subprocess.PIPE,\n    )\n    stdout, _ = p.communicate()\n    stdout = stdout.decode(sys.stdout.encoding)\n    if p.returncode != 0:\n        msg = f\"'swap -l' failed (retcode={p.returncode})\"\n        raise RuntimeError(msg)\n\n    lines = stdout.strip().split('\\n')[1:]\n    if not lines:\n        msg = 'no swap device(s) configured'\n        raise RuntimeError(msg)\n    total = free = 0\n    for line in lines:\n        line = line.split()\n        t, f = line[3:5]\n        total += int(int(t) * 512)\n        free += int(int(f) * 512)\n    used = total - free\n    percent = usage_percent(used, total, round_=1)\n    return ntp.sswap(\n        total, used, free, percent, sin * PAGE_SIZE, sout * PAGE_SIZE\n    )\n\n\n# =====================================================================\n# --- CPU\n# =====================================================================\n\n\ndef cpu_times():\n    \"\"\"Return system-wide CPU times as a named tuple.\"\"\"\n    ret = cext.per_cpu_times()\n    return ntp.scputimes(*[sum(x) for x in zip(*ret)])\n\n\ndef per_cpu_times():\n    \"\"\"Return system per-CPU times as a list of named tuples.\"\"\"\n    ret = cext.per_cpu_times()\n    return [ntp.scputimes(*x) for x in ret]\n\n\ndef cpu_count_logical():\n    \"\"\"Return the number of logical CPUs in the system.\"\"\"\n    try:\n        return os.sysconf(\"SC_NPROCESSORS_ONLN\")\n    except ValueError:\n        # mimic os.cpu_count() behavior\n        return None\n\n\ndef cpu_count_cores():\n    \"\"\"Return the number of CPU cores in the system.\"\"\"\n    return cext.cpu_count_cores()\n\n\ndef cpu_stats():\n    \"\"\"Return various CPU stats as a named tuple.\"\"\"\n    ctx_switches, interrupts, syscalls, _traps = cext.cpu_stats()\n    soft_interrupts = 0\n    return ntp.scpustats(ctx_switches, interrupts, soft_interrupts, syscalls)\n\n\n# =====================================================================\n# --- disks\n# =====================================================================\n\n\ndisk_io_counters = cext.disk_io_counters\ndisk_usage = _psposix.disk_usage\n\n\ndef disk_partitions(all=False):\n    \"\"\"Return system disk partitions.\"\"\"\n    # TODO - the filtering logic should be better checked so that\n    # it tries to reflect 'df' as much as possible\n    retlist = []\n    partitions = cext.disk_partitions()\n    for partition in partitions:\n        device, mountpoint, fstype, opts = partition\n        if device == 'none':\n            device = ''\n        if not all:\n            # Differently from, say, Linux, we don't have a list of\n            # common fs types so the best we can do, AFAIK, is to\n            # filter by filesystem having a total size > 0.\n            try:\n                if not disk_usage(mountpoint).total:\n                    continue\n            except OSError as err:\n                # https://github.com/giampaolo/psutil/issues/1674\n                debug(f\"skipping {mountpoint!r}: {err}\")\n                continue\n        ntuple = ntp.sdiskpart(device, mountpoint, fstype, opts)\n        retlist.append(ntuple)\n    return retlist\n\n\n# =====================================================================\n# --- network\n# =====================================================================\n\n\nnet_io_counters = cext.net_io_counters\nnet_if_addrs = cext.net_if_addrs\n\n\ndef net_connections(kind, _pid=-1):\n    \"\"\"Return socket connections.  If pid == -1 return system-wide\n    connections (as opposed to connections opened by one process only).\n    Only INET sockets are returned (UNIX are not).\n    \"\"\"\n    families, types = conn_tmap[kind]\n    rawlist = cext.net_connections(_pid)\n    ret = set()\n    for item in rawlist:\n        fd, fam, type_, laddr, raddr, status, pid = item\n        if fam not in families:\n            continue\n        if type_ not in types:\n            continue\n        # TODO: refactor and use _common.conn_to_ntuple.\n        if fam in {AF_INET, AF_INET6}:\n            if laddr:\n                laddr = ntp.addr(*laddr)\n            if raddr:\n                raddr = ntp.addr(*raddr)\n        status = TCP_STATUSES[status]\n        fam = sockfam_to_enum(fam)\n        type_ = socktype_to_enum(type_)\n        if _pid == -1:\n            nt = ntp.sconn(fd, fam, type_, laddr, raddr, status, pid)\n        else:\n            nt = ntp.pconn(fd, fam, type_, laddr, raddr, status)\n        ret.add(nt)\n    return list(ret)\n\n\ndef net_if_stats():\n    \"\"\"Get NIC stats (isup, duplex, speed, mtu).\"\"\"\n    ret = cext.net_if_stats()\n    for name, items in ret.items():\n        isup, duplex, speed, mtu = items\n        duplex = NicDuplex(duplex)\n        ret[name] = ntp.snicstats(isup, duplex, speed, mtu, '')\n    return ret\n\n\n# =====================================================================\n# --- other system functions\n# =====================================================================\n\n\ndef boot_time():\n    \"\"\"The system boot time expressed in seconds since the epoch.\"\"\"\n    return cext.boot_time()\n\n\ndef users():\n    \"\"\"Return currently connected users as a list of named tuples.\"\"\"\n    retlist = []\n    rawlist = cext.users()\n    localhost = (':0.0', ':0')\n    for item in rawlist:\n        user, tty, hostname, tstamp, user_process, pid = item\n        # note: the underlying C function includes entries about\n        # system boot, run level and others.  We might want\n        # to use them in the future.\n        if not user_process:\n            continue\n        if hostname in localhost:\n            hostname = 'localhost'\n        nt = ntp.suser(user, tty, hostname, tstamp, pid)\n        retlist.append(nt)\n    return retlist\n\n\n# =====================================================================\n# --- processes\n# =====================================================================\n\n\ndef pids():\n    \"\"\"Returns a list of PIDs currently running on the system.\"\"\"\n    path = get_procfs_path().encode(ENCODING)\n    return [int(x) for x in os.listdir(path) if x.isdigit()]\n\n\ndef pid_exists(pid):\n    \"\"\"Check for the existence of a unix pid.\"\"\"\n    return _psposix.pid_exists(pid)\n\n\ndef wrap_exceptions(fun):\n    \"\"\"Call callable into a try/except clause and translate ENOENT,\n    EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        pid, ppid, name = self.pid, self._ppid, self._name\n        try:\n            return fun(self, *args, **kwargs)\n        except (FileNotFoundError, ProcessLookupError) as err:\n            # ENOENT (no such file or directory) gets raised on open().\n            # ESRCH (no such process) can get raised on read() if\n            # process is gone in meantime.\n            if not pid_exists(pid):\n                raise NoSuchProcess(pid, name) from err\n            raise ZombieProcess(pid, name, ppid) from err\n        except PermissionError as err:\n            raise AccessDenied(pid, name) from err\n        except OSError as err:\n            if pid == 0:\n                if 0 in pids():\n                    raise AccessDenied(pid, name) from err\n                raise\n            raise\n\n    return wrapper\n\n\nclass Process:\n    \"\"\"Wrapper class around underlying C implementation.\"\"\"\n\n    __slots__ = [\"_cache\", \"_name\", \"_ppid\", \"_procfs_path\", \"pid\"]\n\n    def __init__(self, pid):\n        self.pid = pid\n        self._name = None\n        self._ppid = None\n        self._procfs_path = get_procfs_path()\n\n    def _assert_alive(self):\n        \"\"\"Raise NSP if the process disappeared on us.\"\"\"\n        # For those C function who do not raise NSP, possibly returning\n        # incorrect or incomplete result.\n        os.stat(f\"{self._procfs_path}/{self.pid}\")\n\n    def oneshot_enter(self):\n        self._oneshot.cache_activate(self)\n        self._proc_name_and_args.cache_activate(self)\n        self._proc_cred.cache_activate(self)\n\n    def oneshot_exit(self):\n        self._oneshot.cache_deactivate(self)\n        self._proc_name_and_args.cache_deactivate(self)\n        self._proc_cred.cache_deactivate(self)\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _proc_name_and_args(self):\n        return cext.proc_name_and_args(self.pid, self._procfs_path)\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _oneshot(self):\n        if self.pid == 0 and not os.path.exists(\n            f\"{self._procfs_path}/{self.pid}/psinfo\"\n        ):\n            raise AccessDenied(self.pid)\n        ret = cext.proc_oneshot(self.pid, self._procfs_path)\n        assert len(ret) == len(proc_info_map)\n        return ret\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def _proc_cred(self):\n        return cext.proc_cred(self.pid, self._procfs_path)\n\n    @wrap_exceptions\n    def name(self):\n        # note: max len == 15\n        return self._proc_name_and_args()[0]\n\n    @wrap_exceptions\n    def exe(self):\n        try:\n            return os.readlink(f\"{self._procfs_path}/{self.pid}/path/a.out\")\n        except OSError:\n            pass  # continue and guess the exe name from the cmdline\n        # Will be guessed later from cmdline but we want to explicitly\n        # invoke cmdline here in order to get an AccessDenied\n        # exception if the user has not enough privileges.\n        self.cmdline()\n        return \"\"\n\n    @wrap_exceptions\n    def cmdline(self):\n        return self._proc_name_and_args()[1]\n\n    @wrap_exceptions\n    def environ(self):\n        return cext.proc_environ(self.pid, self._procfs_path)\n\n    @wrap_exceptions\n    def create_time(self):\n        return self._oneshot()[proc_info_map['create_time']]\n\n    @wrap_exceptions\n    def num_threads(self):\n        return self._oneshot()[proc_info_map['num_threads']]\n\n    @wrap_exceptions\n    def nice_get(self):\n        # Note #1: getpriority(3) doesn't work for realtime processes.\n        # Psinfo is what ps uses, see:\n        # https://github.com/giampaolo/psutil/issues/1194\n        return self._oneshot()[proc_info_map['nice']]\n\n    @wrap_exceptions\n    def nice_set(self, value):\n        if self.pid in {2, 3}:\n            # Special case PIDs: internally setpriority(3) return ESRCH\n            # (no such process), no matter what.\n            # The process actually exists though, as it has a name,\n            # creation time, etc.\n            raise AccessDenied(self.pid, self._name)\n        return cext.proc_priority_set(self.pid, value)\n\n    @wrap_exceptions\n    def ppid(self):\n        self._ppid = self._oneshot()[proc_info_map['ppid']]\n        return self._ppid\n\n    @wrap_exceptions\n    def uids(self):\n        try:\n            real, effective, saved, _, _, _ = self._proc_cred()\n        except AccessDenied:\n            real = self._oneshot()[proc_info_map['uid']]\n            effective = self._oneshot()[proc_info_map['euid']]\n            saved = None\n        return ntp.puids(real, effective, saved)\n\n    @wrap_exceptions\n    def gids(self):\n        try:\n            _, _, _, real, effective, saved = self._proc_cred()\n        except AccessDenied:\n            real = self._oneshot()[proc_info_map['gid']]\n            effective = self._oneshot()[proc_info_map['egid']]\n            saved = None\n        return ntp.puids(real, effective, saved)\n\n    @wrap_exceptions\n    def cpu_times(self):\n        try:\n            times = cext.proc_cpu_times(self.pid, self._procfs_path)\n        except OSError as err:\n            if err.errno == errno.EOVERFLOW and not IS_64_BIT:\n                # We may get here if we attempt to query a 64bit process\n                # with a 32bit python.\n                # Error originates from read() and also tools like \"cat\"\n                # fail in the same way (!).\n                # Since there simply is no way to determine CPU times we\n                # return 0.0 as a fallback. See:\n                # https://github.com/giampaolo/psutil/issues/857\n                times = (0.0, 0.0, 0.0, 0.0)\n            else:\n                raise\n        return ntp.pcputimes(*times)\n\n    @wrap_exceptions\n    def cpu_num(self):\n        return cext.proc_cpu_num(self.pid, self._procfs_path)\n\n    @wrap_exceptions\n    def terminal(self):\n        procfs_path = self._procfs_path\n        hit_enoent = False\n        tty = wrap_exceptions(self._oneshot()[proc_info_map['ttynr']])\n        if tty != cext.PRNODEV:\n            for x in (0, 1, 2, 255):\n                try:\n                    return os.readlink(f\"{procfs_path}/{self.pid}/path/{x}\")\n                except FileNotFoundError:\n                    hit_enoent = True\n                    continue\n        if hit_enoent:\n            self._assert_alive()\n\n    @wrap_exceptions\n    def cwd(self):\n        # /proc/PID/path/cwd may not be resolved by readlink() even if\n        # it exists (ls shows it). If that's the case and the process\n        # is still alive return None (we can return None also on BSD).\n        # Reference: https://groups.google.com/g/comp.unix.solaris/c/tcqvhTNFCAs\n        procfs_path = self._procfs_path\n        try:\n            return os.readlink(f\"{procfs_path}/{self.pid}/path/cwd\")\n        except FileNotFoundError:\n            os.stat(f\"{procfs_path}/{self.pid}\")  # raise NSP or AD\n            return \"\"\n\n    @wrap_exceptions\n    def memory_info(self):\n        ret = self._oneshot()\n        rss = ret[proc_info_map['rss']] * 1024\n        vms = ret[proc_info_map['vms']] * 1024\n        return ntp.pmem(rss, vms)\n\n    @wrap_exceptions\n    def status(self):\n        code = self._oneshot()[proc_info_map['status']]\n        # XXX is '?' legit? (we're not supposed to return it anyway)\n        return PROC_STATUSES.get(code, '?')\n\n    @wrap_exceptions\n    def threads(self):\n        procfs_path = self._procfs_path\n        ret = []\n        tids = os.listdir(f\"{procfs_path}/{self.pid}/lwp\")\n        hit_enoent = False\n        for tid in tids:\n            tid = int(tid)\n            try:\n                utime, stime = cext.query_process_thread(\n                    self.pid, tid, procfs_path\n                )\n            except OSError as err:\n                if err.errno == errno.EOVERFLOW and not IS_64_BIT:\n                    # We may get here if we attempt to query a 64bit process\n                    # with a 32bit python.\n                    # Error originates from read() and also tools like \"cat\"\n                    # fail in the same way (!).\n                    # Since there simply is no way to determine CPU times we\n                    # return 0.0 as a fallback. See:\n                    # https://github.com/giampaolo/psutil/issues/857\n                    continue\n                # ENOENT == thread gone in meantime\n                if err.errno == errno.ENOENT:\n                    hit_enoent = True\n                    continue\n                raise\n            else:\n                nt = ntp.pthread(tid, utime, stime)\n                ret.append(nt)\n        if hit_enoent:\n            self._assert_alive()\n        return ret\n\n    @wrap_exceptions\n    def open_files(self):\n        retlist = []\n        hit_enoent = False\n        procfs_path = self._procfs_path\n        pathdir = f\"{procfs_path}/{self.pid}/path\"\n        for fd in os.listdir(f\"{procfs_path}/{self.pid}/fd\"):\n            path = os.path.join(pathdir, fd)\n            if os.path.islink(path):\n                try:\n                    file = os.readlink(path)\n                except FileNotFoundError:\n                    hit_enoent = True\n                    continue\n                else:\n                    if isfile_strict(file):\n                        retlist.append(ntp.popenfile(file, int(fd)))\n        if hit_enoent:\n            self._assert_alive()\n        return retlist\n\n    def _get_unix_sockets(self, pid):\n        \"\"\"Get UNIX sockets used by process by parsing 'pfiles' output.\"\"\"\n        # TODO: rewrite this in C (...but the damn netstat source code\n        # does not include this part! Argh!!)\n        cmd = [\"pfiles\", str(pid)]\n        p = subprocess.Popen(\n            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE\n        )\n        stdout, stderr = p.communicate()\n        stdout, stderr = (\n            x.decode(sys.stdout.encoding) for x in (stdout, stderr)\n        )\n        if p.returncode != 0:\n            if 'permission denied' in stderr.lower():\n                raise AccessDenied(self.pid, self._name)\n            if 'no such process' in stderr.lower():\n                raise NoSuchProcess(self.pid, self._name)\n            msg = f\"{cmd!r} command error\\n{stderr}\"\n            raise RuntimeError(msg)\n\n        lines = stdout.split('\\n')[2:]\n        for i, line in enumerate(lines):\n            line = line.lstrip()\n            if line.startswith('sockname: AF_UNIX'):\n                path = line.split(' ', 2)[2]\n                type = lines[i - 2].strip()\n                if type == 'SOCK_STREAM':\n                    type = socket.SOCK_STREAM\n                elif type == 'SOCK_DGRAM':\n                    type = socket.SOCK_DGRAM\n                else:\n                    type = -1\n                yield (\n                    -1,\n                    socket.AF_UNIX,\n                    type,\n                    path,\n                    \"\",\n                    ConnectionStatus.CONN_NONE,\n                )\n\n    @wrap_exceptions\n    def net_connections(self, kind='inet'):\n        ret = net_connections(kind, _pid=self.pid)\n        # The underlying C implementation retrieves all OS connections\n        # and filters them by PID.  At this point we can't tell whether\n        # an empty list means there were no connections for process or\n        # process is no longer active so we force NSP in case the PID\n        # is no longer there.\n        if not ret:\n            # will raise NSP if process is gone\n            os.stat(f\"{self._procfs_path}/{self.pid}\")\n\n        # UNIX sockets\n        if kind in {'all', 'unix'}:\n            ret.extend(\n                [ntp.pconn(*conn) for conn in self._get_unix_sockets(self.pid)]\n            )\n        return ret\n\n    nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')\n    nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked')\n\n    @wrap_exceptions\n    def memory_maps(self):\n        def toaddr(start, end):\n            return f\"{hex(start)[2:].strip('L')}-{hex(end)[2:].strip('L')}\"\n\n        procfs_path = self._procfs_path\n        retlist = []\n        try:\n            rawlist = cext.proc_memory_maps(self.pid, procfs_path)\n        except OSError as err:\n            if err.errno == errno.EOVERFLOW and not IS_64_BIT:\n                # We may get here if we attempt to query a 64bit process\n                # with a 32bit python.\n                # Error originates from read() and also tools like \"cat\"\n                # fail in the same way (!).\n                # Since there simply is no way to determine CPU times we\n                # return 0.0 as a fallback. See:\n                # https://github.com/giampaolo/psutil/issues/857\n                return []\n            else:\n                raise\n        hit_enoent = False\n        for item in rawlist:\n            addr, addrsize, perm, name, rss, anon, locked = item\n            addr = toaddr(addr, addrsize)\n            if not name.startswith('['):\n                try:\n                    name = os.readlink(f\"{procfs_path}/{self.pid}/path/{name}\")\n                except OSError as err:\n                    if err.errno == errno.ENOENT:\n                        # sometimes the link may not be resolved by\n                        # readlink() even if it exists (ls shows it).\n                        # If that's the case we just return the\n                        # unresolved link path.\n                        # This seems an inconsistency with /proc similar\n                        # to: http://goo.gl/55XgO\n                        name = f\"{procfs_path}/{self.pid}/path/{name}\"\n                        hit_enoent = True\n                    else:\n                        raise\n            retlist.append((addr, perm, name, rss, anon, locked))\n        if hit_enoent:\n            self._assert_alive()\n        return retlist\n\n    @wrap_exceptions\n    def num_fds(self):\n        return len(os.listdir(f\"{self._procfs_path}/{self.pid}/fd\"))\n\n    @wrap_exceptions\n    def num_ctx_switches(self):\n        return ntp.pctxsw(\n            *cext.proc_num_ctx_switches(self.pid, self._procfs_path)\n        )\n\n    @wrap_exceptions\n    def page_faults(self):\n        ret = cext.proc_page_faults(self.pid, self._procfs_path)\n        return ntp.ppagefaults(*ret)\n\n    @wrap_exceptions\n    def wait(self, timeout=None):\n        return _psposix.wait_pid(self.pid, timeout)\n"
  },
  {
    "path": "psutil/_psutil_aix.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\n * AIX support is experimental at this time.\n * The following functions and methods are unsupported on the AIX platform:\n * - psutil.Process.memory_maps\n *\n * Known limitations:\n * - psutil.Process.io_counters read count is always 0\n * - psutil.Process.io_counters may not be available on older AIX versions\n * - psutil.Process.threads may not be available on older AIX versions\n * - psutil.net_io_counters may not be available on older AIX versions\n * - reading basic process info may fail or return incorrect values when\n *   process is starting (see IBM APAR IV58499 - fixed in newer AIX versions)\n * - sockets and pipes may not be counted in num_fds (fixed in newer AIX\n *    versions)\n *\n * Useful resources:\n * - proc filesystem: http://www-01.ibm.com/support/knowledgecenter/\n *       ssw_aix_72/com.ibm.aix.files/proc.htm\n * - libperfstat: http://www-01.ibm.com/support/knowledgecenter/\n *       ssw_aix_72/com.ibm.aix.files/libperfstat.h.htm\n */\n\n#include <Python.h>\n#include <sys/limits.h>\n#include <sys/proc.h>\n#include <sys/procfs.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/sysinfo.h>\n#include <sys/thread.h>\n#include <sys/types.h>\n#include <fcntl.h>\n#include <utmp.h>\n#include <utmpx.h>\n#include <mntent.h>\n#include <sys/ioctl.h>\n#include <sys/tihdr.h>\n#include <stropts.h>\n#include <netinet/tcp_fsm.h>\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <libperfstat.h>\n#include <unistd.h>\n\n#include \"arch/all/init.h\"\n#include \"arch/aix/ifaddrs.h\"\n#include \"arch/aix/net_connections.h\"\n#include \"arch/aix/common.h\"\n\n\n#define TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec)\n\n/*\n * Read a file content and fills a C structure with it.\n */\nint\npsutil_file_to_struct(char *path, void *fstruct, size_t size) {\n    int fd;\n    size_t nbytes;\n    fd = open(path, O_RDONLY);\n    if (fd == -1) {\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);\n        return 0;\n    }\n    nbytes = read(fd, fstruct, size);\n    if (nbytes <= 0) {\n        close(fd);\n        psutil_oserror();\n        return 0;\n    }\n    if (nbytes != size) {\n        close(fd);\n        psutil_runtime_error(\"psutil_file_to_struct() size mismatch\");\n        return 0;\n    }\n    close(fd);\n    return nbytes;\n}\n\n\n/*\n * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty\n * as a Python tuple.\n */\nstatic PyObject *\npsutil_proc_oneshot(PyObject *self, PyObject *args) {\n    int pid;\n    char path[100];\n    psinfo_t info;\n    pstatus_t status;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n\n    str_format(path, sizeof(path), \"%s/%i/psinfo\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n\n    if (info.pr_nlwp == 0 && info.pr_lwp.pr_lwpid == 0) {\n        // From the /proc docs: \"If the process is a zombie, the pr_nlwp\n        // and pr_lwp.pr_lwpid flags are zero.\"\n        status.pr_stat = SZOMB;\n    }\n    else if (info.pr_flag & SEXIT) {\n        // \"exiting\" processes don't have /proc/<pid>/status\n        // There are other \"exiting\" processes that 'ps' shows as \"active\"\n        status.pr_stat = SACTIVE;\n    }\n    else {\n        str_format(path, sizeof(path), \"%s/%i/status\", procfs_path, pid);\n        if (!psutil_file_to_struct(path, (void *)&status, sizeof(status)))\n            return NULL;\n    }\n\n    return Py_BuildValue(\n        \"KKKdiiiK\",\n        (unsigned long long)info.pr_ppid,  // parent pid\n        (unsigned long long)info.pr_rssize,  // rss\n        (unsigned long long)info.pr_size,  // vms\n        TV2DOUBLE(info.pr_start),  // create time\n        (int)info.pr_lwp.pr_nice,  // nice\n        (int)info.pr_nlwp,  // no. of threads\n        (int)status.pr_stat,  // status code\n        (unsigned long long)info.pr_ttydev  // tty nr\n    );\n}\n\n\n/*\n * Return process name as a Python string.\n */\nstatic PyObject *\npsutil_proc_name(PyObject *self, PyObject *args) {\n    int pid;\n    char path[100];\n    psinfo_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/psinfo\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n\n    return PyUnicode_DecodeFSDefaultAndSize(info.pr_fname, PRFNSZ);\n}\n\n\n/*\n * Return process command line arguments as a Python list\n */\nstatic PyObject *\npsutil_proc_args(PyObject *self, PyObject *args) {\n    int pid;\n    PyObject *py_retlist = PyList_New(0);\n    struct procsinfo procbuf;\n    long arg_max;\n    char *argbuf = NULL;\n    char *curarg = NULL;\n    int ret;\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, \"i\", &pid))\n        goto error;\n    arg_max = sysconf(_SC_ARG_MAX);\n    argbuf = malloc(arg_max);\n    if (argbuf == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    procbuf.pi_pid = pid;\n    ret = getargs(&procbuf, sizeof(procbuf), argbuf, ARG_MAX);\n    if (ret == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    curarg = argbuf;\n    /* getargs will always append an extra NULL to end the arg list,\n     * even if the buffer is not big enough (even though it is supposed\n     * to be) so the following 'while' is safe */\n    while (*curarg != '\\0') {\n        if (!pylist_append_obj(py_retlist, PyUnicode_DecodeFSDefault(curarg)))\n            goto error;\n        curarg = strchr(curarg, '\\0') + 1;\n    }\n\n    free(argbuf);\n\n    return py_retlist;\n\nerror:\n    if (argbuf != NULL)\n        free(argbuf);\n    Py_XDECREF(py_retlist);\n    return NULL;\n}\n\n\n/*\n * Return process environment variables as a Python dict\n */\nstatic PyObject *\npsutil_proc_environ(PyObject *self, PyObject *args) {\n    int pid;\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_key = NULL;\n    PyObject *py_val = NULL;\n    struct procsinfo procbuf;\n    long env_max;\n    char *envbuf = NULL;\n    char *curvar = NULL;\n    char *separator = NULL;\n    int ret;\n\n    if (py_retdict == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, \"i\", &pid))\n        goto error;\n    env_max = sysconf(_SC_ARG_MAX);\n    envbuf = malloc(env_max);\n    if (envbuf == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    procbuf.pi_pid = pid;\n    ret = getevars(&procbuf, sizeof(procbuf), envbuf, ARG_MAX);\n    if (ret == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    curvar = envbuf;\n    /* getevars will always append an extra NULL to end the arg list,\n     * even if the buffer is not big enough (even though it is supposed\n     * to be) so the following 'while' is safe */\n    while (*curvar != '\\0') {\n        separator = strchr(curvar, '=');\n        if (separator != NULL) {\n            py_key = PyUnicode_DecodeFSDefaultAndSize(\n                curvar, (Py_ssize_t)(separator - curvar)\n            );\n            if (!py_key)\n                goto error;\n            py_val = PyUnicode_DecodeFSDefault(separator + 1);\n            if (!py_val)\n                goto error;\n            if (PyDict_SetItem(py_retdict, py_key, py_val))\n                goto error;\n            Py_CLEAR(py_key);\n            Py_CLEAR(py_val);\n        }\n        curvar = strchr(curvar, '\\0') + 1;\n    }\n\n    free(envbuf);\n\n    return py_retdict;\n\nerror:\n    if (envbuf != NULL)\n        free(envbuf);\n    Py_XDECREF(py_retdict);\n    Py_XDECREF(py_key);\n    Py_XDECREF(py_val);\n    return NULL;\n}\n\n\n#ifdef CURR_VERSION_THREAD\n\n/*\n * Retrieves all threads used by process returning a list of tuples\n * including thread id, user time and system time.\n */\nstatic PyObject *\npsutil_proc_threads(PyObject *self, PyObject *args) {\n    long pid;\n    PyObject *py_retlist = PyList_New(0);\n    perfstat_thread_t *threadt = NULL;\n    perfstat_id_t id;\n    int i, rc, thread_count;\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, \"l\", &pid))\n        goto error;\n\n    /* Get the count of threads */\n    thread_count = perfstat_thread(NULL, NULL, sizeof(perfstat_thread_t), 0);\n    if (thread_count <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    /* Allocate enough memory */\n    threadt = (perfstat_thread_t *)calloc(\n        thread_count, sizeof(perfstat_thread_t)\n    );\n    if (threadt == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    strcpy(id.name, \"\");\n    rc = perfstat_thread(\n        &id, threadt, sizeof(perfstat_thread_t), thread_count\n    );\n    if (rc <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < thread_count; i++) {\n        if (threadt[i].pid != pid)\n            continue;\n\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"Idd\",\n                threadt[i].tid,\n                threadt[i].ucpu_time,\n                threadt[i].scpu_time\n            ))\n        {\n            goto error;\n        }\n    }\n    free(threadt);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (threadt != NULL)\n        free(threadt);\n    return NULL;\n}\n\n#endif\n\n\n#ifdef CURR_VERSION_PROCESS\n\nstatic PyObject *\npsutil_proc_io_counters(PyObject *self, PyObject *args) {\n    long pid;\n    int rc;\n    perfstat_process_t procinfo;\n    perfstat_id_t id;\n    if (!PyArg_ParseTuple(args, \"l\", &pid))\n        return NULL;\n\n    snprintf(id.name, sizeof(id.name), \"%ld\", pid);\n    rc = perfstat_process(&id, &procinfo, sizeof(perfstat_process_t), 1);\n    if (rc <= 0) {\n        psutil_oserror();\n        return NULL;\n    }\n\n    return Py_BuildValue(\n        \"(KKKK)\",\n        procinfo.inOps,  // XXX always 0\n        procinfo.outOps,\n        procinfo.inBytes,  // XXX always 0\n        procinfo.outBytes\n    );\n}\n\n#endif\n\n\n/*\n * Return process user and system CPU times as a Python tuple.\n */\nstatic PyObject *\npsutil_proc_cpu_times(PyObject *self, PyObject *args) {\n    int pid;\n    char path[100];\n    pstatus_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/status\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    // results are more precise than os.times()\n    return Py_BuildValue(\n        \"dddd\",\n        TV2DOUBLE(info.pr_utime),\n        TV2DOUBLE(info.pr_stime),\n        TV2DOUBLE(info.pr_cutime),\n        TV2DOUBLE(info.pr_cstime)\n    );\n}\n\n\n/*\n * Return process uids/gids as a Python tuple.\n */\nstatic PyObject *\npsutil_proc_cred(PyObject *self, PyObject *args) {\n    int pid;\n    char path[100];\n    prcred_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/cred\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    return Py_BuildValue(\n        \"iiiiii\",\n        info.pr_ruid,\n        info.pr_euid,\n        info.pr_suid,\n        info.pr_rgid,\n        info.pr_egid,\n        info.pr_sgid\n    );\n}\n\n\n/*\n * Return process voluntary and involuntary context switches as a Python tuple.\n */\nstatic PyObject *\npsutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {\n    PyObject *py_tuple = NULL;\n    pid32_t requested_pid;\n    pid32_t pid = 0;\n    int np = 0;\n    struct procentry64 *processes = (struct procentry64 *)NULL;\n    struct procentry64 *p;\n\n    if (!PyArg_ParseTuple(args, \"i\", &requested_pid))\n        return NULL;\n\n    processes = psutil_read_process_table(&np);\n    if (!processes)\n        return NULL;\n\n    /* Loop through processes */\n    for (p = processes; np > 0; np--, p++) {\n        pid = p->pi_pid;\n        if (requested_pid != pid)\n            continue;\n        py_tuple = Py_BuildValue(\n            \"LL\",\n            (long long)p->pi_ru.ru_nvcsw, /* voluntary context switches */\n            (long long)p->pi_ru.ru_nivcsw\n        ); /* involuntary */\n        free(processes);\n        return py_tuple;\n    }\n\n    /* finished iteration without finding requested pid */\n    free(processes);\n    return psutil_oserror_nsp(\"psutil_read_process_table (no PID found)\");\n}\n\n\n/*\n * Return disk mounted partitions as a list of tuples including device,\n * mount point and filesystem type.\n */\nstatic PyObject *\npsutil_disk_partitions(PyObject *self, PyObject *args) {\n    FILE *file = NULL;\n    struct mntent *mt = NULL;\n    PyObject *py_dev = NULL;\n    PyObject *py_mountp = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    file = setmntent(MNTTAB, \"rb\");\n    if (file == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n    mt = getmntent(file);\n    while (mt != NULL) {\n        py_dev = PyUnicode_DecodeFSDefault(mt->mnt_fsname);\n        if (!py_dev)\n            goto error;\n        py_mountp = PyUnicode_DecodeFSDefault(mt->mnt_dir);\n        if (!py_mountp)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(OOss)\",\n                py_dev,  // device\n                py_mountp,  // mount point\n                mt->mnt_type,  // fs type\n                mt->mnt_opts  // options\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_dev);\n        Py_CLEAR(py_mountp);\n        mt = getmntent(file);\n    }\n    endmntent(file);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_dev);\n    Py_XDECREF(py_mountp);\n    Py_DECREF(py_retlist);\n    if (file != NULL)\n        endmntent(file);\n    return NULL;\n}\n\n\n#if defined(CURR_VERSION_NETINTERFACE) && CURR_VERSION_NETINTERFACE >= 3\n\n/*\n * Return a list of tuples for network I/O statistics.\n */\nstatic PyObject *\npsutil_net_io_counters(PyObject *self, PyObject *args) {\n    perfstat_netinterface_t *statp = NULL;\n    int tot, i;\n    perfstat_id_t first;\n\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_ifc_info = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    /* check how many perfstat_netinterface_t structures are available */\n    tot = perfstat_netinterface(\n        NULL, NULL, sizeof(perfstat_netinterface_t), 0\n    );\n    if (tot == 0) {\n        // no network interfaces - return empty dict\n        return py_retdict;\n    }\n    if (tot < 0) {\n        psutil_oserror();\n        goto error;\n    }\n    statp = (perfstat_netinterface_t *)malloc(\n        tot * sizeof(perfstat_netinterface_t)\n    );\n    if (statp == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n    strcpy(first.name, FIRST_NETINTERFACE);\n    tot = perfstat_netinterface(\n        &first, statp, sizeof(perfstat_netinterface_t), tot\n    );\n    if (tot < 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < tot; i++) {\n        py_ifc_info = Py_BuildValue(\n            \"(KKKKKKKK)\",\n            statp[i].obytes,  // bytes sent\n            statp[i].ibytes,  // bytes received\n            statp[i].opackets,  // packets sent\n            statp[i].ipackets,  // packets received\n            statp[i].ierrors,  // input errors\n            statp[i].oerrors,  // output errors\n            statp[i].if_iqdrop s,  // dropped on input\n            statp[i].xmitdrops  // not transmitted\n        );\n        if (!py_ifc_info)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, statp[i].name, py_ifc_info))\n            goto error;\n        Py_DECREF(py_ifc_info);\n    }\n\n    free(statp);\n    return py_retdict;\n\nerror:\n    if (statp != NULL)\n        free(statp);\n    Py_XDECREF(py_ifc_info);\n    Py_DECREF(py_retdict);\n    return NULL;\n}\n\n#endif\n\n\nstatic PyObject *\npsutil_net_if_stats(PyObject *self, PyObject *args) {\n    char *nic_name;\n    int sock = 0;\n    int ret;\n    int mtu;\n    struct ifreq ifr;\n    PyObject *py_is_up = NULL;\n    PyObject *py_retlist = NULL;\n\n    if (!PyArg_ParseTuple(args, \"s\", &nic_name))\n        return NULL;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1)\n        goto error;\n\n    str_copy(ifr.ifr_name, sizeof(ifr.ifr_name), nic_name);\n\n    // is up?\n    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);\n    if (ret == -1)\n        goto error;\n    if ((ifr.ifr_flags & IFF_UP) != 0)\n        py_is_up = Py_True;\n    else\n        py_is_up = Py_False;\n    Py_INCREF(py_is_up);\n\n    // MTU\n    ret = ioctl(sock, SIOCGIFMTU, &ifr);\n    if (ret == -1)\n        goto error;\n    mtu = ifr.ifr_mtu;\n\n    close(sock);\n    py_retlist = Py_BuildValue(\"[Oi]\", py_is_up, mtu);\n    if (!py_retlist)\n        goto error;\n    Py_DECREF(py_is_up);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_is_up);\n    if (sock != 0)\n        close(sock);\n    psutil_oserror();\n    return NULL;\n}\n\n\nstatic PyObject *\npsutil_boot_time(PyObject *self, PyObject *args) {\n    float boot_time = 0.0;\n    struct utmpx *ut;\n\n    UTXENT_MUTEX_LOCK();\n    setutxent();\n    while (NULL != (ut = getutxent())) {\n        if (ut->ut_type == BOOT_TIME) {\n            boot_time = (float)ut->ut_tv.tv_sec;\n            break;\n        }\n    }\n    endutxent();\n    UTXENT_MUTEX_UNLOCK();\n    if (boot_time == 0.0) {\n        // could not find BOOT_TIME in getutxent loop\n        psutil_runtime_error(\"can't determine boot time\");\n        return NULL;\n    }\n    return Py_BuildValue(\"f\", boot_time);\n}\n\n\n/*\n * Return a Python list of tuple representing per-cpu times\n */\nstatic PyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    int ncpu, rc, i;\n    long ticks;\n    perfstat_cpu_t *cpu = NULL;\n    perfstat_id_t id;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    /* get the number of ticks per second */\n    ticks = sysconf(_SC_CLK_TCK);\n    if (ticks < 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    /* get the number of cpus in ncpu */\n    ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);\n    if (ncpu <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    /* allocate enough memory to hold the ncpu structures */\n    cpu = (perfstat_cpu_t *)malloc(ncpu * sizeof(perfstat_cpu_t));\n    if (cpu == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    strcpy(id.name, \"\");\n    rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);\n\n    if (rc <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < ncpu; i++) {\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(dddd)\",\n                (double)cpu[i].user / ticks,\n                (double)cpu[i].sys / ticks,\n                (double)cpu[i].idle / ticks,\n                (double)cpu[i].wait / ticks\n            ))\n        {\n            goto error;\n        }\n    }\n    free(cpu);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (cpu != NULL)\n        free(cpu);\n    return NULL;\n}\n\n\n/*\n * Return disk IO statistics.\n */\nstatic PyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_disk_info = NULL;\n    perfstat_disk_t *diskt = NULL;\n    perfstat_id_t id;\n    int i, rc, disk_count;\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    /* Get the count of disks */\n    disk_count = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);\n    if (disk_count <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    /* Allocate enough memory */\n    diskt = (perfstat_disk_t *)calloc(disk_count, sizeof(perfstat_disk_t));\n    if (diskt == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    strcpy(id.name, FIRST_DISK);\n    rc = perfstat_disk(&id, diskt, sizeof(perfstat_disk_t), disk_count);\n    if (rc <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < disk_count; i++) {\n        py_disk_info = Py_BuildValue(\n            \"KKKKKK\",\n            diskt[i].__rxfers,\n            diskt[i].xfers - diskt[i].__rxfers,\n            diskt[i].rblks * diskt[i].bsize,\n            diskt[i].wblks * diskt[i].bsize,\n            diskt[i].rserv / 1000 / 1000,  // from nano to milli secs\n            diskt[i].wserv / 1000 / 1000  // from nano to milli secs\n        );\n        if (py_disk_info == NULL)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, diskt[i].name, py_disk_info))\n            goto error;\n        Py_DECREF(py_disk_info);\n    }\n    free(diskt);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_disk_info);\n    Py_DECREF(py_retdict);\n    if (diskt != NULL)\n        free(diskt);\n    return NULL;\n}\n\n\n/*\n * Return virtual memory usage statistics.\n */\nstatic PyObject *\npsutil_virtual_mem(PyObject *self, PyObject *args) {\n    int rc;\n    long pagesize = psutil_getpagesize();\n    perfstat_memory_total_t memory;\n\n    rc = perfstat_memory_total(\n        NULL, &memory, sizeof(perfstat_memory_total_t), 1\n    );\n    if (rc <= 0) {\n        psutil_oserror();\n        return NULL;\n    }\n\n    return Py_BuildValue(\n        \"KKKKK\",\n        (unsigned long long)memory.real_total * pagesize,\n        (unsigned long long)memory.real_avail * pagesize,\n        (unsigned long long)memory.real_free * pagesize,\n        (unsigned long long)memory.real_pinned * pagesize,\n        (unsigned long long)memory.real_inuse * pagesize\n    );\n}\n\n\n/*\n * Return stats about swap memory.\n */\nstatic PyObject *\npsutil_swap_mem(PyObject *self, PyObject *args) {\n    int rc;\n    long pagesize = psutil_getpagesize();\n    perfstat_memory_total_t memory;\n\n    rc = perfstat_memory_total(\n        NULL, &memory, sizeof(perfstat_memory_total_t), 1\n    );\n    if (rc <= 0) {\n        psutil_oserror();\n        return NULL;\n    }\n\n    return Py_BuildValue(\n        \"KKKK\",\n        (unsigned long long)memory.pgsp_total * pagesize,\n        (unsigned long long)memory.pgsp_free * pagesize,\n        (unsigned long long)memory.pgins * pagesize,\n        (unsigned long long)memory.pgouts * pagesize\n    );\n}\n\n\n/*\n * Return CPU statistics.\n */\nstatic PyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    int ncpu, rc, i;\n    // perfstat_cpu_total_t doesn't have invol/vol cswitch, only pswitch\n    // which is apparently something else. We have to sum over all cpus\n    perfstat_cpu_t *cpu = NULL;\n    perfstat_id_t id;\n    u_longlong_t cswitches = 0;\n    u_longlong_t devintrs = 0;\n    u_longlong_t softintrs = 0;\n    u_longlong_t syscall = 0;\n\n    /* get the number of cpus in ncpu */\n    ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);\n    if (ncpu <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    /* allocate enough memory to hold the ncpu structures */\n    cpu = (perfstat_cpu_t *)malloc(ncpu * sizeof(perfstat_cpu_t));\n    if (cpu == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    strcpy(id.name, \"\");\n    rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);\n\n    if (rc <= 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < ncpu; i++) {\n        cswitches += cpu[i].invol_cswitch + cpu[i].vol_cswitch;\n        devintrs += cpu[i].devintrs;\n        softintrs += cpu[i].softintrs;\n        syscall += cpu[i].syscall;\n    }\n\n    free(cpu);\n\n    return Py_BuildValue(\"KKKK\", cswitches, devintrs, softintrs, syscall);\n\nerror:\n    if (cpu != NULL)\n        free(cpu);\n    return NULL;\n}\n\n\n/*\n * define the psutil C module methods and initialize the module.\n */\nstatic PyMethodDef PsutilMethods[] = {\n    // --- process-related functions\n    {\"proc_args\", psutil_proc_args, METH_VARARGS},\n    {\"proc_cpu_times\", psutil_proc_cpu_times, METH_VARARGS},\n    {\"proc_cred\", psutil_proc_cred, METH_VARARGS},\n    {\"proc_environ\", psutil_proc_environ, METH_VARARGS},\n    {\"proc_name\", psutil_proc_name, METH_VARARGS},\n    {\"proc_oneshot\", psutil_proc_oneshot, METH_VARARGS},\n#ifdef CURR_VERSION_THREAD\n    {\"proc_threads\", psutil_proc_threads, METH_VARARGS},\n#endif\n#ifdef CURR_VERSION_PROCESS\n    {\"proc_io_counters\", psutil_proc_io_counters, METH_VARARGS},\n#endif\n    {\"proc_num_ctx_switches\", psutil_proc_num_ctx_switches, METH_VARARGS},\n\n    // --- system-related functions\n    {\"boot_time\", psutil_boot_time, METH_VARARGS},\n    {\"disk_io_counters\", psutil_disk_io_counters, METH_VARARGS},\n    {\"disk_partitions\", psutil_disk_partitions, METH_VARARGS},\n    {\"per_cpu_times\", psutil_per_cpu_times, METH_VARARGS},\n    {\"swap_mem\", psutil_swap_mem, METH_VARARGS},\n    {\"virtual_mem\", psutil_virtual_mem, METH_VARARGS},\n#if defined(CURR_VERSION_NETINTERFACE) && CURR_VERSION_NETINTERFACE >= 3\n    {\"net_io_counters\", psutil_net_io_counters, METH_VARARGS},\n#endif\n    {\"cpu_stats\", psutil_cpu_stats, METH_VARARGS},\n    {\"net_connections\", psutil_net_connections, METH_VARARGS},\n    {\"net_if_stats\", psutil_net_if_stats, METH_VARARGS},\n\n    // --- others\n    {\"check_pid_range\", psutil_check_pid_range, METH_VARARGS},\n    {\"set_debug\", psutil_set_debug, METH_VARARGS},\n\n    {NULL, NULL, 0, NULL}\n};\n\n\nstruct module_state {\n    PyObject *error;\n};\n\n#define GETSTATE(m) ((struct module_state *)PyModule_GetState(m))\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstatic int\npsutil_aix_traverse(PyObject *m, visitproc visit, void *arg) {\n    Py_VISIT(GETSTATE(m)->error);\n    return 0;\n}\n\nstatic int\npsutil_aix_clear(PyObject *m) {\n    Py_CLEAR(GETSTATE(m)->error);\n    return 0;\n}\n\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"psutil_aix\",\n    NULL,\n    sizeof(struct module_state),\n    PsutilMethods,\n    NULL,\n    psutil_aix_traverse,\n    psutil_aix_clear,\n    NULL\n};\n\n\nPyMODINIT_FUNC\nPyInit__psutil_aix(void) {\n    PyObject *mod = PyModule_Create(&moduledef);\n    if (mod == NULL)\n        return NULL;\n\n#ifdef Py_GIL_DISABLED\n    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED))\n        return NULL;\n#endif\n\n    if (psutil_setup() != 0)\n        return NULL;\n    if (psutil_posix_add_constants(mod) != 0)\n        return NULL;\n    if (psutil_posix_add_methods(mod) != 0)\n        return NULL;\n\n    if (PyModule_AddIntConstant(mod, \"version\", PSUTIL_VERSION))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SIDL\", SIDL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SZOMB\", SZOMB))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SACTIVE\", SACTIVE))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSWAP\", SSWAP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSTOP\", SSTOP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSED\", TCPS_CLOSED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSING\", TCPS_CLOSING))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSE_WAIT\", TCPS_CLOSE_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LISTEN\", TCPS_LISTEN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_ESTABLISHED\", TCPS_ESTABLISHED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_SENT\", TCPS_SYN_SENT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_RCVD\", TCPS_SYN_RECEIVED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_1\", TCPS_FIN_WAIT_1))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_2\", TCPS_FIN_WAIT_2))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LAST_ACK\", TCPS_LAST_ACK))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_TIME_WAIT\", TCPS_TIME_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"PSUTIL_CONN_NONE\", PSUTIL_CONN_NONE))\n        return NULL;\n\n    return mod;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "psutil/_psutil_bsd.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola', Landry Breuil\n * (OpenBSD implementation), Ryo Onodera (NetBSD implementation).\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\n * Platform-specific module methods for FreeBSD and OpenBSD.\n\n * OpenBSD references:\n * - OpenBSD source code: https://github.com/openbsd/src\n *\n * OpenBSD / NetBSD: missing APIs compared to FreeBSD implementation:\n * - psutil.net_connections()\n * - psutil.Process.get/set_cpu_affinity()  (not supported natively)\n * - psutil.Process.memory_maps()\n */\n\n#include <Python.h>\n#include <sys/proc.h>\n#include <sys/param.h>  // BSD version\n#include <netinet/tcp_fsm.h>  // for TCP connection states\n\n#include \"arch/all/init.h\"\n#include \"arch/bsd/init.h\"\n\n#ifdef PSUTIL_FREEBSD\n#include \"arch/freebsd/init.h\"\n#elif PSUTIL_OPENBSD\n#include \"arch/openbsd/init.h\"\n#elif PSUTIL_NETBSD\n#include \"arch/netbsd/init.h\"\n#endif\n\n\n/*\n * define the psutil C module methods and initialize the module.\n */\nstatic PyMethodDef mod_methods[] = {\n    // --- per-process functions\n\n    {\"proc_cmdline\", psutil_proc_cmdline, METH_VARARGS},\n    {\"proc_cwd\", psutil_proc_cwd, METH_VARARGS},\n    {\"proc_environ\", psutil_proc_environ, METH_VARARGS},\n    {\"proc_name\", psutil_proc_name, METH_VARARGS},\n    {\"proc_num_fds\", psutil_proc_num_fds, METH_VARARGS},\n    {\"proc_oneshot_kinfo\", psutil_proc_oneshot_kinfo, METH_VARARGS},\n    {\"proc_open_files\", psutil_proc_open_files, METH_VARARGS},\n    {\"proc_threads\", psutil_proc_threads, METH_VARARGS},\n#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)\n    {\"proc_num_threads\", psutil_proc_num_threads, METH_VARARGS},\n#endif\n#if defined(PSUTIL_FREEBSD)\n    {\"proc_cpu_affinity_get\", psutil_proc_cpu_affinity_get, METH_VARARGS},\n    {\"proc_cpu_affinity_set\", psutil_proc_cpu_affinity_set, METH_VARARGS},\n    {\"proc_exe\", psutil_proc_exe, METH_VARARGS},\n    {\"proc_getrlimit\", psutil_proc_getrlimit, METH_VARARGS},\n    {\"proc_memory_maps\", psutil_proc_memory_maps, METH_VARARGS},\n    {\"proc_net_connections\", psutil_proc_net_connections, METH_VARARGS},\n    {\"proc_setrlimit\", psutil_proc_setrlimit, METH_VARARGS},\n#endif\n\n    // --- system-related functions\n    {\"boot_time\", psutil_boot_time, METH_VARARGS},\n    {\"cpu_count_logical\", psutil_cpu_count_logical, METH_VARARGS},\n    {\"cpu_stats\", psutil_cpu_stats, METH_VARARGS},\n    {\"cpu_times\", psutil_cpu_times, METH_VARARGS},\n    {\"disk_io_counters\", psutil_disk_io_counters, METH_VARARGS},\n    {\"disk_partitions\", psutil_disk_partitions, METH_VARARGS},\n    {\"net_connections\", psutil_net_connections, METH_VARARGS},\n    {\"net_io_counters\", psutil_net_io_counters, METH_VARARGS},\n    {\"per_cpu_times\", psutil_per_cpu_times, METH_VARARGS},\n    {\"pids\", psutil_pids, METH_VARARGS},\n    {\"swap_mem\", psutil_swap_mem, METH_VARARGS},\n#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)\n    {\"heap_info\", psutil_heap_info, METH_VARARGS},\n    {\"heap_trim\", psutil_heap_trim, METH_VARARGS},\n#endif\n#if defined(PSUTIL_OPENBSD)\n    {\"users\", psutil_users, METH_VARARGS},\n#endif\n    {\"virtual_mem\", psutil_virtual_mem, METH_VARARGS},\n#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD)\n    {\"cpu_freq\", psutil_cpu_freq, METH_VARARGS},\n#endif\n#if defined(PSUTIL_FREEBSD)\n    {\"cpu_topology\", psutil_cpu_topology, METH_VARARGS},\n    {\"sensors_battery\", psutil_sensors_battery, METH_VARARGS},\n    {\"sensors_cpu_temperature\", psutil_sensors_cpu_temperature, METH_VARARGS},\n#endif\n    // --- others\n    {\"check_pid_range\", psutil_check_pid_range, METH_VARARGS},\n    {\"set_debug\", psutil_set_debug, METH_VARARGS},\n\n    {NULL, NULL, 0, NULL}\n};\n\n\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"_psutil_bsd\",\n    NULL,\n    -1,\n    mod_methods,\n    NULL,\n    NULL,\n    NULL,\n    NULL\n};\n\nPyObject *\nPyInit__psutil_bsd(void) {\n    PyObject *v;\n    PyObject *mod = PyModule_Create(&moduledef);\n    if (mod == NULL)\n        return NULL;\n\n#ifdef Py_GIL_DISABLED\n    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED))\n        return NULL;\n#endif\n\n    if (psutil_setup() != 0)\n        return NULL;\n    if (psutil_posix_add_constants(mod) != 0)\n        return NULL;\n    if (psutil_posix_add_methods(mod) != 0)\n        return NULL;\n\n    if (PyModule_AddIntConstant(mod, \"version\", PSUTIL_VERSION))\n        return NULL;\n\n        // process status constants\n#ifdef PSUTIL_FREEBSD\n    if (PyModule_AddIntConstant(mod, \"SIDL\", SIDL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SRUN\", SRUN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSLEEP\", SSLEEP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSTOP\", SSTOP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SZOMB\", SZOMB))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SWAIT\", SWAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SLOCK\", SLOCK))\n        return NULL;\n#elif PSUTIL_OPENBSD\n    if (PyModule_AddIntConstant(mod, \"SIDL\", SIDL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SRUN\", SRUN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSLEEP\", SSLEEP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSTOP\", SSTOP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SZOMB\", SZOMB))\n        return NULL;  // unused\n    if (PyModule_AddIntConstant(mod, \"SDEAD\", SDEAD))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SONPROC\", SONPROC))\n        return NULL;\n#elif defined(PSUTIL_NETBSD)\n    if (PyModule_AddIntConstant(mod, \"SIDL\", LSIDL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SRUN\", LSRUN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSLEEP\", LSSLEEP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSTOP\", LSSTOP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SZOMB\", LSZOMB))\n        return NULL;\n#if __NetBSD_Version__ < 500000000\n    if (PyModule_AddIntConstant(mod, \"SDEAD\", LSDEAD))\n        return NULL;\n#endif\n    if (PyModule_AddIntConstant(mod, \"SONPROC\", LSONPROC))\n        return NULL;\n    // unique to NetBSD\n    if (PyModule_AddIntConstant(mod, \"SSUSPENDED\", LSSUSPENDED))\n        return NULL;\n#endif\n\n    // connection status constants\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSED\", TCPS_CLOSED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSING\", TCPS_CLOSING))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSE_WAIT\", TCPS_CLOSE_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LISTEN\", TCPS_LISTEN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_ESTABLISHED\", TCPS_ESTABLISHED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_SENT\", TCPS_SYN_SENT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_RECEIVED\", TCPS_SYN_RECEIVED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_1\", TCPS_FIN_WAIT_1))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_2\", TCPS_FIN_WAIT_2))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LAST_ACK\", TCPS_LAST_ACK))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_TIME_WAIT\", TCPS_TIME_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"PSUTIL_CONN_NONE\", 128))\n        return NULL;\n\n    return mod;\n}\n"
  },
  {
    "path": "psutil/_psutil_linux.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Linux-specific functions.\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE 1\n#endif\n#include <Python.h>\n#include <linux/ethtool.h>  // DUPLEX_*\n\n#include \"arch/all/init.h\"\n\n// May happen on old RedHat versions, see:\n// https://github.com/giampaolo/psutil/issues/607\n#ifndef DUPLEX_UNKNOWN\n#define DUPLEX_UNKNOWN 0xff\n#endif\n\nstatic PyMethodDef mod_methods[] = {\n    // --- per-process functions\n    {\"proc_ioprio_get\", psutil_proc_ioprio_get, METH_VARARGS},\n    {\"proc_ioprio_set\", psutil_proc_ioprio_set, METH_VARARGS},\n#ifdef PSUTIL_HAS_CPU_AFFINITY\n    {\"proc_cpu_affinity_get\", psutil_proc_cpu_affinity_get, METH_VARARGS},\n    {\"proc_cpu_affinity_set\", psutil_proc_cpu_affinity_set, METH_VARARGS},\n#endif\n    // --- system related functions\n    {\"disk_partitions\", psutil_disk_partitions, METH_VARARGS},\n    {\"net_if_duplex_speed\", psutil_net_if_duplex_speed, METH_VARARGS},\n#ifdef PSUTIL_HAS_HEAP_INFO\n    {\"heap_info\", psutil_heap_info, METH_VARARGS},\n#endif\n#ifdef PSUTIL_HAS_HEAP_TRIM\n    {\"heap_trim\", psutil_heap_trim, METH_VARARGS},\n#endif\n\n    // --- linux specific\n    {\"linux_sysinfo\", psutil_linux_sysinfo, METH_VARARGS},\n    // --- others\n    {\"check_pid_range\", psutil_check_pid_range, METH_VARARGS},\n    {\"set_debug\", psutil_set_debug, METH_VARARGS},\n    {NULL, NULL, 0, NULL}\n};\n\n\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"_psutil_linux\",\n    NULL,\n    -1,\n    mod_methods,\n    NULL,\n    NULL,\n    NULL,\n    NULL\n};\n\n\nPyObject *\nPyInit__psutil_linux(void) {\n    PyObject *mod = PyModule_Create(&moduledef);\n    if (mod == NULL)\n        return NULL;\n\n#ifdef Py_GIL_DISABLED\n    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED))\n        return NULL;\n#endif\n\n    if (psutil_setup() != 0)\n        return NULL;\n    if (psutil_posix_add_constants(mod) != 0)\n        return NULL;\n    if (psutil_posix_add_methods(mod) != 0)\n        return NULL;\n\n    if (PyModule_AddIntConstant(mod, \"version\", PSUTIL_VERSION))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"DUPLEX_HALF\", DUPLEX_HALF))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"DUPLEX_FULL\", DUPLEX_FULL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"DUPLEX_UNKNOWN\", DUPLEX_UNKNOWN))\n        return NULL;\n\n    return mod;\n}\n"
  },
  {
    "path": "psutil/_psutil_osx.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// macOS platform-specific module methods.\n\n#include <Python.h>\n#include <sys/time.h>  // needed for old macOS versions\n#include <sys/proc.h>\n#include <netinet/tcp_fsm.h>\n\n#include \"arch/all/init.h\"\n#include \"arch/osx/init.h\"\n\n\nstatic PyMethodDef mod_methods[] = {\n    // --- per-process functions\n    // clang-format off\n    {\"proc_cmdline\", psutil_proc_cmdline, METH_VARARGS},\n    {\"proc_cwd\", psutil_proc_cwd, METH_VARARGS},\n    {\"proc_environ\", psutil_proc_environ, METH_VARARGS},\n    {\"proc_exe\", psutil_proc_exe, METH_VARARGS},\n    {\"proc_is_zombie\", psutil_proc_is_zombie, METH_VARARGS},\n    {\"proc_memory_info_ex\", psutil_proc_memory_info_ex, METH_VARARGS},\n    {\"proc_memory_uss\", psutil_proc_memory_uss, METH_VARARGS},\n    {\"proc_name\", psutil_proc_name, METH_VARARGS},\n    {\"proc_net_connections\", psutil_proc_net_connections, METH_VARARGS},\n    {\"proc_num_fds\", psutil_proc_num_fds, METH_VARARGS},\n    {\"proc_oneshot_kinfo\", psutil_proc_oneshot_kinfo, METH_VARARGS},\n    {\"proc_oneshot_pidtaskinfo\", psutil_proc_oneshot_pidtaskinfo, METH_VARARGS},\n    {\"proc_open_files\", psutil_proc_open_files, METH_VARARGS},\n    {\"proc_threads\", psutil_proc_threads, METH_VARARGS},\n    // clang-format on\n\n    // --- system-related functions\n    {\"boot_time\", psutil_boot_time, METH_VARARGS},\n    {\"cpu_count_cores\", psutil_cpu_count_cores, METH_VARARGS},\n    {\"cpu_count_logical\", psutil_cpu_count_logical, METH_VARARGS},\n    {\"cpu_freq\", psutil_cpu_freq, METH_VARARGS},\n    {\"cpu_stats\", psutil_cpu_stats, METH_VARARGS},\n    {\"cpu_times\", psutil_cpu_times, METH_VARARGS},\n    {\"disk_io_counters\", psutil_disk_io_counters, METH_VARARGS},\n    {\"disk_partitions\", psutil_disk_partitions, METH_VARARGS},\n    {\"disk_usage_used\", psutil_disk_usage_used, METH_VARARGS},\n    {\"has_cpu_freq\", psutil_has_cpu_freq, METH_VARARGS},\n    {\"heap_info\", psutil_heap_info, METH_VARARGS},\n    {\"heap_trim\", psutil_heap_trim, METH_VARARGS},\n    {\"net_io_counters\", psutil_net_io_counters, METH_VARARGS},\n    {\"per_cpu_times\", psutil_per_cpu_times, METH_VARARGS},\n    {\"pids\", psutil_pids, METH_VARARGS},\n    {\"sensors_battery\", psutil_sensors_battery, METH_VARARGS},\n    {\"swap_mem\", psutil_swap_mem, METH_VARARGS},\n    {\"virtual_mem\", psutil_virtual_mem, METH_VARARGS},\n\n    // --- others\n    {\"check_pid_range\", psutil_check_pid_range, METH_VARARGS},\n    {\"set_debug\", psutil_set_debug, METH_VARARGS},\n\n    {NULL, NULL, 0, NULL}\n};\n\n\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"_psutil_osx\",\n    NULL,\n    -1,\n    mod_methods,\n    NULL,\n    NULL,\n    NULL,\n    NULL\n};\n\n\nPyObject *\nPyInit__psutil_osx(void) {\n    PyObject *mod = PyModule_Create(&moduledef);\n    if (mod == NULL)\n        return NULL;\n\n#ifdef Py_GIL_DISABLED\n    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED))\n        return NULL;\n#endif\n\n    if (psutil_setup() != 0)\n        return NULL;\n    if (psutil_setup_osx() != 0)\n        return NULL;\n    if (psutil_posix_add_constants(mod) != 0)\n        return NULL;\n    if (psutil_posix_add_methods(mod) != 0)\n        return NULL;\n\n    if (PyModule_AddIntConstant(mod, \"version\", PSUTIL_VERSION))\n        return NULL;\n    // process status constants, defined in:\n    // http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149\n    if (PyModule_AddIntConstant(mod, \"SIDL\", SIDL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SRUN\", SRUN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSLEEP\", SSLEEP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSTOP\", SSTOP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SZOMB\", SZOMB))\n        return NULL;\n    // connection status constants\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSED\", TCPS_CLOSED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSING\", TCPS_CLOSING))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSE_WAIT\", TCPS_CLOSE_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LISTEN\", TCPS_LISTEN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_ESTABLISHED\", TCPS_ESTABLISHED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_SENT\", TCPS_SYN_SENT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_RECEIVED\", TCPS_SYN_RECEIVED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_1\", TCPS_FIN_WAIT_1))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_2\", TCPS_FIN_WAIT_2))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LAST_ACK\", TCPS_LAST_ACK))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_TIME_WAIT\", TCPS_TIME_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"PSUTIL_CONN_NONE\", PSUTIL_CONN_NONE))\n        return NULL;\n\n    return mod;\n}\n"
  },
  {
    "path": "psutil/_psutil_sunos.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\n * Functions specific to Sun OS Solaris platforms.\n *\n * Thanks to Justin Venus who originally wrote a consistent part of\n * this in Cython which I later on translated in C.\n *\n * Fix compilation issue on SunOS 5.10, see:\n * https://github.com/giampaolo/psutil/issues/421\n * https://github.com/giampaolo/psutil/issues/1077\n */\n\n#define _STRUCTURED_PROC 1\n#define NEW_MIB_COMPLIANT 1\n\n#include <Python.h>\n\n#if !defined(_LP64) && _FILE_OFFSET_BITS == 64\n#undef _FILE_OFFSET_BITS\n#undef _LARGEFILE64_SOURCE\n#endif\n\n#include <inet/tcp.h>\n#include <sys/proc.h>\n\n#include \"arch/all/init.h\"\n\n\nstatic PyMethodDef mod_methods[] = {\n    // --- process-related functions\n    {\"proc_cpu_num\", psutil_proc_cpu_num, METH_VARARGS},\n    {\"proc_cpu_times\", psutil_proc_cpu_times, METH_VARARGS},\n    {\"proc_cred\", psutil_proc_cred, METH_VARARGS},\n    {\"proc_environ\", psutil_proc_environ, METH_VARARGS},\n    {\"proc_memory_maps\", psutil_proc_memory_maps, METH_VARARGS},\n    {\"proc_name_and_args\", psutil_proc_name_and_args, METH_VARARGS},\n    {\"proc_num_ctx_switches\", psutil_proc_num_ctx_switches, METH_VARARGS},\n    {\"proc_oneshot\", psutil_proc_oneshot, METH_VARARGS},\n    {\"proc_page_faults\", psutil_proc_page_faults, METH_VARARGS},\n    {\"query_process_thread\", psutil_proc_query_thread, METH_VARARGS},\n\n    // --- system-related functions\n    {\"boot_time\", psutil_boot_time, METH_VARARGS},\n    {\"cpu_count_cores\", psutil_cpu_count_cores, METH_VARARGS},\n    {\"cpu_stats\", psutil_cpu_stats, METH_VARARGS},\n    {\"disk_io_counters\", psutil_disk_io_counters, METH_VARARGS},\n    {\"disk_partitions\", psutil_disk_partitions, METH_VARARGS},\n    {\"net_connections\", psutil_net_connections, METH_VARARGS},\n    {\"net_if_stats\", psutil_net_if_stats, METH_VARARGS},\n    {\"net_io_counters\", psutil_net_io_counters, METH_VARARGS},\n    {\"per_cpu_times\", psutil_per_cpu_times, METH_VARARGS},\n    {\"swap_mem\", psutil_swap_mem, METH_VARARGS},\n\n    // --- others\n    {\"check_pid_range\", psutil_check_pid_range, METH_VARARGS},\n    {\"set_debug\", psutil_set_debug, METH_VARARGS},\n\n    {NULL, NULL, 0, NULL}\n};\n\n\nstruct module_state {\n    PyObject *error;\n};\n\n\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"psutil_sunos\",\n    NULL,\n    -1,\n    mod_methods,\n    NULL,\n    NULL,\n    NULL,\n    NULL\n};\n\n\nPyObject *\nPyInit__psutil_sunos(void) {\n    PyObject *mod = PyModule_Create(&moduledef);\n    if (mod == NULL)\n        return NULL;\n\n#ifdef Py_GIL_DISABLED\n    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED))\n        return NULL;\n#endif\n\n    if (psutil_setup() != 0)\n        return NULL;\n    if (psutil_posix_add_constants(mod) != 0)\n        return NULL;\n    if (psutil_posix_add_methods(mod) != 0)\n        return NULL;\n\n    if (PyModule_AddIntConstant(mod, \"version\", PSUTIL_VERSION))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSLEEP\", SSLEEP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SRUN\", SRUN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SZOMB\", SZOMB))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SSTOP\", SSTOP))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SIDL\", SIDL))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"SONPROC\", SONPROC))\n        return NULL;\n#ifdef SWAIT\n    if (PyModule_AddIntConstant(mod, \"SWAIT\", SWAIT))\n        return NULL;\n#else\n    /* sys/proc.h started defining SWAIT somewhere\n     * after Update 3 and prior to Update 5 included.\n     */\n    if (PyModule_AddIntConstant(mod, \"SWAIT\", 0))\n        return NULL;\n#endif\n    // for process tty\n    if (PyModule_AddIntConstant(mod, \"PRNODEV\", PRNODEV))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSED\", TCPS_CLOSED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSING\", TCPS_CLOSING))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_CLOSE_WAIT\", TCPS_CLOSE_WAIT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LISTEN\", TCPS_LISTEN))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_ESTABLISHED\", TCPS_ESTABLISHED))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_SENT\", TCPS_SYN_SENT))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_SYN_RCVD\", TCPS_SYN_RCVD))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_1\", TCPS_FIN_WAIT_1))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_FIN_WAIT_2\", TCPS_FIN_WAIT_2))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_LAST_ACK\", TCPS_LAST_ACK))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"TCPS_TIME_WAIT\", TCPS_TIME_WAIT))\n        return NULL;\n    // sunos specific\n    if (PyModule_AddIntConstant(mod, \"TCPS_IDLE\", TCPS_IDLE))\n        return NULL;\n    // sunos specific\n    if (PyModule_AddIntConstant(mod, \"TCPS_BOUND\", TCPS_BOUND))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"PSUTIL_CONN_NONE\", PSUTIL_CONN_NONE))\n        return NULL;\n\n    return mod;\n}\n"
  },
  {
    "path": "psutil/_psutil_windows.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\n * Windows platform-specific module methods for _psutil_windows.\n *\n * List of undocumented Windows NT APIs which are used in here and in\n * other modules:\n * - NtQuerySystemInformation\n * - NtQueryInformationProcess\n * - NtQueryObject\n * - NtSuspendProcess\n * - NtResumeProcess\n */\n\n#include <Python.h>\n#include <windows.h>\n\n#include \"arch/all/init.h\"\n#include \"arch/windows/init.h\"\n\n\n#define GETSTATE(m) ((struct module_state *)PyModule_GetState(m))\n\n\n// ------------------------ Python init ---------------------------\n\nstatic PyMethodDef PsutilMethods[] = {\n    // --- per-process functions\n    {\"proc_cmdline\",\n     (PyCFunction)(void (*)(void))psutil_proc_cmdline,\n     METH_VARARGS | METH_KEYWORDS},\n    {\"proc_cpu_affinity_get\", psutil_proc_cpu_affinity_get, METH_VARARGS},\n    {\"proc_cpu_affinity_set\", psutil_proc_cpu_affinity_set, METH_VARARGS},\n    {\"proc_cwd\", psutil_proc_cwd, METH_VARARGS},\n    {\"proc_environ\", psutil_proc_environ, METH_VARARGS},\n    {\"proc_exe\", psutil_proc_exe, METH_VARARGS},\n    {\"proc_io_counters\", psutil_proc_io_counters, METH_VARARGS},\n    {\"proc_io_priority_get\", psutil_proc_io_priority_get, METH_VARARGS},\n    {\"proc_io_priority_set\", psutil_proc_io_priority_set, METH_VARARGS},\n    {\"proc_is_suspended\", psutil_proc_is_suspended, METH_VARARGS},\n    {\"proc_kill\", psutil_proc_kill, METH_VARARGS},\n    {\"proc_memory_info\", psutil_proc_memory_info, METH_VARARGS},\n    {\"proc_memory_maps\", psutil_proc_memory_maps, METH_VARARGS},\n    {\"proc_memory_uss\", psutil_proc_memory_uss, METH_VARARGS},\n    {\"proc_num_handles\", psutil_proc_num_handles, METH_VARARGS},\n    {\"proc_open_files\", psutil_proc_open_files, METH_VARARGS},\n    {\"proc_page_faults\", psutil_proc_page_faults, METH_VARARGS},\n    {\"proc_priority_get\", psutil_proc_priority_get, METH_VARARGS},\n    {\"proc_priority_set\", psutil_proc_priority_set, METH_VARARGS},\n    {\"proc_suspend_or_resume\", psutil_proc_suspend_or_resume, METH_VARARGS},\n    {\"proc_threads\", psutil_proc_threads, METH_VARARGS},\n    {\"proc_times\", psutil_proc_times, METH_VARARGS},\n    {\"proc_username\", psutil_proc_username, METH_VARARGS},\n    {\"proc_wait\", psutil_proc_wait, METH_VARARGS},\n\n    // --- alternative pinfo interface\n    {\"proc_oneshot\", psutil_proc_oneshot, METH_VARARGS},\n\n    // --- system-related functions\n    {\"uptime\", psutil_uptime, METH_VARARGS},\n    {\"cpu_count_cores\", psutil_cpu_count_cores, METH_VARARGS},\n    {\"cpu_count_logical\", psutil_cpu_count_logical, METH_VARARGS},\n    {\"cpu_freq\", psutil_cpu_freq, METH_VARARGS},\n    {\"cpu_stats\", psutil_cpu_stats, METH_VARARGS},\n    {\"cpu_times\", psutil_cpu_times, METH_VARARGS},\n    {\"disk_io_counters\", psutil_disk_io_counters, METH_VARARGS},\n    {\"disk_partitions\", psutil_disk_partitions, METH_VARARGS},\n    {\"disk_usage\", psutil_disk_usage, METH_VARARGS},\n    {\"getloadavg\", (PyCFunction)psutil_get_loadavg, METH_VARARGS},\n    {\"getpagesize\", psutil_getpagesize, METH_VARARGS},\n    {\"swap_percent\", psutil_swap_percent, METH_VARARGS},\n    {\"init_loadavg_counter\",\n     (PyCFunction)psutil_init_loadavg_counter,\n     METH_VARARGS},\n    {\"heap_info\", psutil_heap_info, METH_VARARGS},\n    {\"heap_trim\", psutil_heap_trim, METH_VARARGS},\n    {\"net_connections\", psutil_net_connections, METH_VARARGS},\n    {\"net_if_addrs\", psutil_net_if_addrs, METH_VARARGS},\n    {\"net_if_stats\", psutil_net_if_stats, METH_VARARGS},\n    {\"net_io_counters\", psutil_net_io_counters, METH_VARARGS},\n    {\"per_cpu_times\", psutil_per_cpu_times, METH_VARARGS},\n    {\"pid_exists\", psutil_pid_exists, METH_VARARGS},\n    {\"pids\", psutil_pids, METH_VARARGS},\n    {\"ppid_map\", psutil_ppid_map, METH_VARARGS},\n    {\"sensors_battery\", psutil_sensors_battery, METH_VARARGS},\n    {\"users\", psutil_users, METH_VARARGS},\n\n    // --- windows services\n    {\"winservice_enumerate\", psutil_winservice_enumerate, METH_VARARGS},\n    {\"winservice_query_config\", psutil_winservice_query_config, METH_VARARGS},\n    {\"winservice_query_descr\", psutil_winservice_query_descr, METH_VARARGS},\n    {\"winservice_query_status\", psutil_winservice_query_status, METH_VARARGS},\n    {\"winservice_start\", psutil_winservice_start, METH_VARARGS},\n    {\"winservice_stop\", psutil_winservice_stop, METH_VARARGS},\n\n    // --- direct Windows APIs\n    {\"GetPerformanceInfo\", psutil_GetPerformanceInfo, METH_VARARGS},\n    {\"QueryDosDevice\", psutil_QueryDosDevice, METH_VARARGS},\n\n    // --- others\n    {\"check_pid_range\", psutil_check_pid_range, METH_VARARGS},\n    {\"set_debug\", psutil_set_debug, METH_VARARGS},\n\n    {NULL, NULL, 0, NULL}\n};\n\n\nstruct module_state {\n    PyObject *error;\n};\n\n\nstatic int\npsutil_windows_traverse(PyObject *m, visitproc visit, void *arg) {\n    Py_VISIT(GETSTATE(m)->error);\n    return 0;\n}\n\nstatic int\npsutil_windows_clear(PyObject *m) {\n    Py_CLEAR(GETSTATE(m)->error);\n    return 0;\n}\n\nstatic struct PyModuleDef moduledef = {\n    PyModuleDef_HEAD_INIT,\n    \"psutil_windows\",\n    NULL,\n    sizeof(struct module_state),\n    PsutilMethods,\n    NULL,\n    psutil_windows_traverse,\n    psutil_windows_clear,\n    NULL\n};\n\n\nPyMODINIT_FUNC\nPyInit__psutil_windows(void) {\n    PyObject *mod = PyModule_Create(&moduledef);\n    if (mod == NULL)\n        return NULL;\n\n#ifdef Py_GIL_DISABLED\n    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED))\n        return NULL;\n#endif\n\n    if (psutil_setup() != 0)\n        return NULL;\n    if (psutil_setup_windows() != 0)\n        return NULL;\n    if (psutil_set_se_debug() != 0)\n        return NULL;\n\n    // Exceptions\n    TimeoutExpired = PyErr_NewException(\n        \"_psutil_windows.TimeoutExpired\", NULL, NULL\n    );\n    if (TimeoutExpired == NULL)\n        return NULL;\n    if (PyModule_AddObject(mod, \"TimeoutExpired\", TimeoutExpired))\n        return NULL;\n\n    TimeoutAbandoned = PyErr_NewException(\n        \"_psutil_windows.TimeoutAbandoned\", NULL, NULL\n    );\n    if (TimeoutAbandoned == NULL)\n        return NULL;\n    if (PyModule_AddObject(mod, \"TimeoutAbandoned\", TimeoutAbandoned))\n        return NULL;\n\n    // version constant\n    if (PyModule_AddIntConstant(mod, \"version\", PSUTIL_VERSION))\n        return NULL;\n\n    // process status constants\n    // http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx\n    if (PyModule_AddIntConstant(\n            mod, \"ABOVE_NORMAL_PRIORITY_CLASS\", ABOVE_NORMAL_PRIORITY_CLASS\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"BELOW_NORMAL_PRIORITY_CLASS\", BELOW_NORMAL_PRIORITY_CLASS\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"HIGH_PRIORITY_CLASS\", HIGH_PRIORITY_CLASS\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"IDLE_PRIORITY_CLASS\", IDLE_PRIORITY_CLASS\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"NORMAL_PRIORITY_CLASS\", NORMAL_PRIORITY_CLASS\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"REALTIME_PRIORITY_CLASS\", REALTIME_PRIORITY_CLASS\n        ))\n        return NULL;\n\n    // connection status constants\n    // http://msdn.microsoft.com/en-us/library/cc669305.aspx\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_CLOSED\", MIB_TCP_STATE_CLOSED\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_CLOSING\", MIB_TCP_STATE_CLOSING\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_CLOSE_WAIT\", MIB_TCP_STATE_CLOSE_WAIT\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_LISTEN\", MIB_TCP_STATE_LISTEN\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_ESTAB\", MIB_TCP_STATE_ESTAB\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_SYN_SENT\", MIB_TCP_STATE_SYN_SENT\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_SYN_RCVD\", MIB_TCP_STATE_SYN_RCVD\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_FIN_WAIT1\", MIB_TCP_STATE_FIN_WAIT1\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_FIN_WAIT2\", MIB_TCP_STATE_FIN_WAIT2\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_LAST_ACK\", MIB_TCP_STATE_LAST_ACK\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_TIME_WAIT\", MIB_TCP_STATE_TIME_WAIT\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_TIME_WAIT\", MIB_TCP_STATE_TIME_WAIT\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"MIB_TCP_STATE_DELETE_TCB\", MIB_TCP_STATE_DELETE_TCB\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"PSUTIL_CONN_NONE\", PSUTIL_CONN_NONE))\n        return NULL;\n\n    // ...for internal use in _psutil_windows.py\n    if (PyModule_AddIntConstant(mod, \"INFINITE\", INFINITE))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"ERROR_ACCESS_DENIED\", ERROR_ACCESS_DENIED\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"ERROR_INVALID_NAME\", ERROR_INVALID_NAME))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"ERROR_SERVICE_DOES_NOT_EXIST\", ERROR_SERVICE_DOES_NOT_EXIST\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(\n            mod, \"ERROR_PRIVILEGE_NOT_HELD\", ERROR_PRIVILEGE_NOT_HELD\n        ))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"WINVER\", PSUTIL_WINVER))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"WINDOWS_VISTA\", PSUTIL_WINDOWS_VISTA))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"WINDOWS_7\", PSUTIL_WINDOWS_7))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"WINDOWS_8\", PSUTIL_WINDOWS_8))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"WINDOWS_8_1\", PSUTIL_WINDOWS_8_1))\n        return NULL;\n    if (PyModule_AddIntConstant(mod, \"WINDOWS_10\", PSUTIL_WINDOWS_10))\n        return NULL;\n\n    return mod;\n}\n"
  },
  {
    "path": "psutil/_pswindows.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Windows platform implementation.\"\"\"\n\nfrom __future__ import annotations\n\nimport contextlib\nimport enum\nimport functools\nimport os\nimport signal\nimport sys\nimport threading\nimport time\n\nfrom . import _ntuples as ntp\nfrom ._common import ENCODING\nfrom ._common import AccessDenied\nfrom ._common import NoSuchProcess\nfrom ._common import TimeoutExpired\nfrom ._common import conn_tmap\nfrom ._common import conn_to_ntuple\nfrom ._common import debug\nfrom ._common import isfile_strict\nfrom ._common import memoize_when_activated\nfrom ._common import parse_environ_block\nfrom ._common import usage_percent\nfrom ._enums import BatteryTime\nfrom ._enums import ConnectionStatus\nfrom ._enums import NicDuplex\nfrom ._enums import ProcessIOPriority\nfrom ._enums import ProcessPriority\nfrom ._enums import ProcessStatus\n\ntry:\n    from . import _psutil_windows as cext\nexcept ImportError as err:\n    if (\n        str(err).lower().startswith(\"dll load failed\")\n        and sys.getwindowsversion()[0] < 6\n    ):\n        # We may get here if:\n        # 1) we are on an old Windows version\n        # 2) psutil was installed via pip + wheel\n        # See: https://github.com/giampaolo/psutil/issues/811\n        msg = \"this Windows version is too old (< Windows Vista); \"\n        msg += \"psutil 3.4.2 is the latest version which supports Windows \"\n        msg += \"2000, XP and 2003 server\"\n        raise RuntimeError(msg) from err\n    else:\n        raise\n\n\n# process priority constants, import from __init__.py:\n# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx\n__extra__all__ = [\"win_service_iter\", \"win_service_get\", \"AF_LINK\"]\n\n\n# =====================================================================\n# --- globals\n# =====================================================================\n\nERROR_PARTIAL_COPY = 299\nPYPY = '__pypy__' in sys.builtin_module_names\n\nAddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1})\nAF_LINK = AddressFamily.AF_LINK\n\nTCP_STATUSES = {\n    cext.MIB_TCP_STATE_ESTAB: ConnectionStatus.CONN_ESTABLISHED,\n    cext.MIB_TCP_STATE_SYN_SENT: ConnectionStatus.CONN_SYN_SENT,\n    cext.MIB_TCP_STATE_SYN_RCVD: ConnectionStatus.CONN_SYN_RECV,\n    cext.MIB_TCP_STATE_FIN_WAIT1: ConnectionStatus.CONN_FIN_WAIT1,\n    cext.MIB_TCP_STATE_FIN_WAIT2: ConnectionStatus.CONN_FIN_WAIT2,\n    cext.MIB_TCP_STATE_TIME_WAIT: ConnectionStatus.CONN_TIME_WAIT,\n    cext.MIB_TCP_STATE_CLOSED: ConnectionStatus.CONN_CLOSE,\n    cext.MIB_TCP_STATE_CLOSE_WAIT: ConnectionStatus.CONN_CLOSE_WAIT,\n    cext.MIB_TCP_STATE_LAST_ACK: ConnectionStatus.CONN_LAST_ACK,\n    cext.MIB_TCP_STATE_LISTEN: ConnectionStatus.CONN_LISTEN,\n    cext.MIB_TCP_STATE_CLOSING: ConnectionStatus.CONN_CLOSING,\n    cext.MIB_TCP_STATE_DELETE_TCB: ConnectionStatus.CONN_DELETE_TCB,\n    cext.PSUTIL_CONN_NONE: ConnectionStatus.CONN_NONE,\n}\n\n\n# =====================================================================\n# --- utils\n# =====================================================================\n\n\n@functools.lru_cache(maxsize=512)\ndef convert_dos_path(s):\n    r\"\"\"Convert paths using native DOS format like:\n        \"\\Device\\HarddiskVolume1\\Windows\\systemew\\file.txt\" or\n        \"\\??\\C:\\Windows\\systemew\\file.txt\"\n    into:\n        \"C:\\Windows\\systemew\\file.txt\".\n    \"\"\"\n    if s.startswith('\\\\\\\\'):\n        return s\n    rawdrive = '\\\\'.join(s.split('\\\\')[:3])\n    if rawdrive in {\"\\\\??\\\\UNC\", \"\\\\Device\\\\Mup\"}:\n        rawdrive = '\\\\'.join(s.split('\\\\')[:5])\n        driveletter = '\\\\\\\\' + '\\\\'.join(s.split('\\\\')[3:5])\n    elif rawdrive.startswith('\\\\??\\\\'):\n        driveletter = s.split('\\\\')[2]\n    else:\n        driveletter = cext.QueryDosDevice(rawdrive)\n    remainder = s[len(rawdrive) :]\n    return os.path.join(driveletter, remainder)\n\n\n@functools.lru_cache\ndef getpagesize():\n    return cext.getpagesize()\n\n\n# =====================================================================\n# --- memory\n# =====================================================================\n\n\ndef virtual_memory():\n    \"\"\"System virtual memory as a named tuple.\"\"\"\n    info = cext.GetPerformanceInfo()\n    pagesize = info[\"PageSize\"]\n    total = info[\"PhysicalTotal\"] * pagesize\n    avail = info[\"PhysicalAvailable\"] * pagesize\n    cached = info[\"SystemCache\"] * pagesize\n    wired = info[\"KernelNonpaged\"] * pagesize\n    free = avail\n    used = total - avail\n    percent = usage_percent((total - avail), total, round_=1)\n    return ntp.svmem(total, avail, percent, used, free, cached, wired)\n\n\ndef swap_memory():\n    \"\"\"Swap system memory as a (total, used, free, sin, sout) tuple.\"\"\"\n    info = cext.GetPerformanceInfo()\n    pagesize = info[\"PageSize\"]\n    total_phys = info[\"PhysicalTotal\"] * pagesize\n    # CommitLimit == Maximum pages that can be committed into RAM +\n    # page file (swap). In the context of swap, it's the total \"system\n    # memory\" (physical + virtual), thus substract the physical part\n    # from it to get the \"total swap\".\n    total_system = info[\"CommitLimit\"] * pagesize  # physical + swap\n    total = total_system - total_phys\n\n    # commit total is incremented immediately (decrementing free_system)\n    # while the corresponding free physical value is not decremented until\n    # pages are accessed, so we can't use free system memory for swap.\n    # instead, we calculate page file usage based on performance counter\n    if total > 0:\n        percentswap = cext.swap_percent()\n        used = int(0.01 * percentswap * total)\n    else:\n        percentswap = 0.0\n        used = 0\n\n    free = total - used\n    percent = round(percentswap, 1)\n    return ntp.sswap(total, used, free, percent, 0, 0)\n\n\n# malloc / heap functions\nheap_info = cext.heap_info\nheap_trim = cext.heap_trim\n\n\n# =====================================================================\n# --- disk\n# =====================================================================\n\n\ndisk_io_counters = cext.disk_io_counters\n\n\ndef disk_usage(path):\n    \"\"\"Return disk usage associated with path.\"\"\"\n    if isinstance(path, bytes):\n        # XXX: do we want to use \"strict\"? Probably yes, in order\n        # to fail immediately. After all we are accepting input here...\n        path = path.decode(ENCODING, errors=\"strict\")\n    total, used, free = cext.disk_usage(path)\n    percent = usage_percent(used, total, round_=1)\n    return ntp.sdiskusage(total, used, free, percent)\n\n\ndef disk_partitions(all):\n    \"\"\"Return disk partitions.\"\"\"\n    rawlist = cext.disk_partitions(all)\n    return [ntp.sdiskpart(*x) for x in rawlist]\n\n\n# =====================================================================\n# --- CPU\n# =====================================================================\n\n\ndef cpu_times():\n    \"\"\"Return system CPU times as a named tuple.\"\"\"\n    user, system, idle = cext.cpu_times()\n    # Internally, GetSystemTimes() is used, and it doesn't return\n    # interrupt and dpc times. cext.per_cpu_times() does, so we\n    # rely on it to get those only.\n    percpu_summed = ntp.scputimes(\n        *[sum(n) for n in zip(*cext.per_cpu_times())]\n    )\n    return ntp.scputimes(\n        user, system, idle, percpu_summed.irq, percpu_summed.dpc\n    )\n\n\ndef per_cpu_times():\n    \"\"\"Return system per-CPU times as a list of named tuples.\"\"\"\n    ret = []\n    for user, system, idle, irq, dpc in cext.per_cpu_times():\n        item = ntp.scputimes(user, system, idle, irq, dpc)\n        ret.append(item)\n    return ret\n\n\ndef cpu_count_logical():\n    \"\"\"Return the number of logical CPUs in the system.\"\"\"\n    return cext.cpu_count_logical()\n\n\ndef cpu_count_cores():\n    \"\"\"Return the number of CPU cores in the system.\"\"\"\n    return cext.cpu_count_cores()\n\n\ndef cpu_stats():\n    \"\"\"Return CPU statistics.\"\"\"\n    ctx_switches, interrupts, _dpcs, syscalls = cext.cpu_stats()\n    soft_interrupts = 0\n    return ntp.scpustats(ctx_switches, interrupts, soft_interrupts, syscalls)\n\n\ndef cpu_freq():\n    \"\"\"Return CPU frequency.\n    On Windows per-cpu frequency is not supported.\n    \"\"\"\n    curr, max_ = cext.cpu_freq()\n    min_ = 0.0\n    return [ntp.scpufreq(float(curr), min_, float(max_))]\n\n\n_loadavg_initialized = False\n_lock = threading.Lock()\n\n\ndef _getloadavg_impl():\n    # Drop to 2 decimal points which is what Linux does\n    raw_loads = cext.getloadavg()\n    return tuple(round(load, 2) for load in raw_loads)\n\n\ndef getloadavg():\n    \"\"\"Return the number of processes in the system run queue averaged\n    over the last 1, 5, and 15 minutes respectively as a tuple.\n    \"\"\"\n    global _loadavg_initialized\n\n    if _loadavg_initialized:\n        return _getloadavg_impl()\n\n    with _lock:\n        if not _loadavg_initialized:\n            cext.init_loadavg_counter()\n            _loadavg_initialized = True\n\n    return _getloadavg_impl()\n\n\n# =====================================================================\n# --- network\n# =====================================================================\n\n\ndef net_connections(kind, _pid=-1):\n    \"\"\"Return socket connections.  If pid == -1 return system-wide\n    connections (as opposed to connections opened by one process only).\n    \"\"\"\n    families, types = conn_tmap[kind]\n    rawlist = cext.net_connections(_pid, families, types)\n    ret = set()\n    for item in rawlist:\n        fd, fam, type, laddr, raddr, status, pid = item\n        nt = conn_to_ntuple(\n            fd,\n            fam,\n            type,\n            laddr,\n            raddr,\n            status,\n            TCP_STATUSES,\n            pid=pid if _pid == -1 else None,\n        )\n        ret.add(nt)\n    return list(ret)\n\n\ndef net_if_stats():\n    \"\"\"Get NIC stats (isup, duplex, speed, mtu).\"\"\"\n    ret = {}\n    rawdict = cext.net_if_stats()\n    for name, items in rawdict.items():\n        isup, duplex, speed, mtu = items\n        duplex = NicDuplex(duplex)\n        ret[name] = ntp.snicstats(isup, duplex, speed, mtu, '')\n    return ret\n\n\ndef net_io_counters():\n    \"\"\"Return network I/O statistics for every network interface\n    installed on the system as a dict of raw tuples.\n    \"\"\"\n    return cext.net_io_counters()\n\n\ndef net_if_addrs():\n    \"\"\"Return the addresses associated to each NIC.\"\"\"\n    return cext.net_if_addrs()\n\n\n# =====================================================================\n# --- sensors\n# =====================================================================\n\n\ndef sensors_battery():\n    \"\"\"Return battery information.\"\"\"\n    # For constants meaning see:\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa373232(v=vs.85).aspx\n    acline_status, flags, percent, secsleft = cext.sensors_battery()\n    power_plugged = acline_status == 1\n    no_battery = bool(flags & 128)\n    charging = bool(flags & 8)\n\n    if no_battery:\n        return None\n    if power_plugged or charging:\n        secsleft = BatteryTime.POWER_TIME_UNLIMITED\n    elif secsleft == -1:\n        secsleft = BatteryTime.POWER_TIME_UNKNOWN\n\n    return ntp.sbattery(percent, secsleft, power_plugged)\n\n\n# =====================================================================\n# --- other system functions\n# =====================================================================\n\n\n_last_btime = 0\n\n\ndef boot_time():\n    \"\"\"The system boot time expressed in seconds since the epoch. This\n    also includes the time spent during hybernate / suspend.\n    \"\"\"\n    # This dirty hack is to adjust the precision of the returned\n    # value which may have a 1 second fluctuation, see:\n    # https://github.com/giampaolo/psutil/issues/1007\n    global _last_btime\n    ret = time.time() - cext.uptime()\n    if abs(ret - _last_btime) <= 1:\n        return _last_btime\n    else:\n        _last_btime = ret\n        return ret\n\n\ndef users():\n    \"\"\"Return currently connected users as a list of named tuples.\"\"\"\n    retlist = []\n    rawlist = cext.users()\n    for item in rawlist:\n        user, hostname, tstamp = item\n        nt = ntp.suser(user, None, hostname, tstamp, None)\n        retlist.append(nt)\n    return retlist\n\n\n# =====================================================================\n# --- Windows services\n# =====================================================================\n\n\ndef win_service_iter():\n    \"\"\"Yields a list of WindowsService instances.\"\"\"\n    for name, display_name in cext.winservice_enumerate():\n        yield WindowsService(name, display_name)\n\n\ndef win_service_get(name):\n    \"\"\"Open a Windows service and return it as a WindowsService instance.\"\"\"\n    service = WindowsService(name, None)\n    service._display_name = service._query_config()['display_name']\n    return service\n\n\nclass WindowsService:  # noqa: PLW1641\n    \"\"\"Represents an installed Windows service.\"\"\"\n\n    def __init__(self, name: str, display_name: str):\n        self._name = name\n        self._display_name = display_name\n\n    def __str__(self):\n        details = f\"(name={self._name!r}, display_name={self._display_name!r})\"\n        return f\"{self.__class__.__name__}{details}\"\n\n    def __repr__(self):\n        return f\"<{self.__str__()} at {id(self)}>\"\n\n    def __eq__(self, other: object):\n        # Test for equality with another WindosService object based\n        # on name.\n        if not isinstance(other, WindowsService):\n            return NotImplemented\n        return self._name == other._name\n\n    def __ne__(self, other: object):\n        return not self == other\n\n    def _query_config(self):\n        with self._wrap_exceptions():\n            display_name, binpath, username, start_type = (\n                cext.winservice_query_config(self._name)\n            )\n        # XXX - update _self.display_name?\n        return dict(\n            display_name=display_name,\n            binpath=binpath,\n            username=username,\n            start_type=start_type,\n        )\n\n    def _query_status(self):\n        with self._wrap_exceptions():\n            status, pid = cext.winservice_query_status(self._name)\n        if pid == 0:\n            pid = None\n        return dict(status=status, pid=pid)\n\n    @contextlib.contextmanager\n    def _wrap_exceptions(self):\n        \"\"\"Ctx manager which translates bare OSError and WindowsError\n        exceptions into NoSuchProcess and AccessDenied.\n        \"\"\"\n        try:\n            yield\n        except OSError as err:\n            name = self._name\n            if is_permission_err(err):\n                msg = (\n                    f\"service {name!r} is not querable (not enough privileges)\"\n                )\n                raise AccessDenied(pid=None, name=name, msg=msg) from err\n            elif err.winerror in {\n                cext.ERROR_INVALID_NAME,\n                cext.ERROR_SERVICE_DOES_NOT_EXIST,\n            }:\n                msg = f\"service {name!r} does not exist\"\n                raise NoSuchProcess(pid=None, name=name, msg=msg) from err\n            else:\n                raise\n\n    # config query\n\n    def name(self) -> str:\n        \"\"\"The service name. This string is how a service is referenced\n        and can be passed to win_service_get() to get a new\n        WindowsService instance.\n        \"\"\"\n        return self._name\n\n    def display_name(self) -> str:\n        \"\"\"The service display name. The value is cached when this class\n        is instantiated.\n        \"\"\"\n        return self._display_name\n\n    def binpath(self) -> str:\n        \"\"\"The fully qualified path to the service binary/exe file as\n        a string, including command line arguments.\n        \"\"\"\n        return self._query_config()['binpath']\n\n    def username(self) -> str:\n        \"\"\"The name of the user that owns this service.\"\"\"\n        return self._query_config()['username']\n\n    def start_type(self) -> str:\n        \"\"\"A string which can either be \"automatic\", \"manual\" or\n        \"disabled\".\n        \"\"\"\n        return self._query_config()['start_type']\n\n    # status query\n\n    def pid(self) -> int | None:\n        \"\"\"The process PID, if any, else None. This can be passed\n        to Process class to control the service's process.\n        \"\"\"\n        return self._query_status()['pid']\n\n    def status(self) -> str:\n        \"\"\"Service status as a string.\"\"\"\n        return self._query_status()['status']\n\n    def description(self) -> str:\n        \"\"\"Service long description.\"\"\"\n        return cext.winservice_query_descr(self.name())\n\n    # utils\n\n    def as_dict(self) -> dict[str, str | int | None]:\n        \"\"\"Utility method retrieving all the information above as a\n        dictionary.\n        \"\"\"\n        d = self._query_config()\n        d.update(self._query_status())\n        d['name'] = self.name()\n        d['display_name'] = self.display_name()\n        d['description'] = self.description()\n        return d\n\n    # actions\n    # XXX: the necessary C bindings for start() and stop() are\n    # implemented but for now I prefer not to expose them.\n    # I may change my mind in the future. Reasons:\n    # - they require Administrator privileges\n    # - can't implement a timeout for stop() (unless by using a thread,\n    #   which sucks)\n    # - would require adding ServiceAlreadyStarted and\n    #   ServiceAlreadyStopped exceptions, adding two new APIs.\n    # - we might also want to have modify(), which would basically mean\n    #   rewriting win32serviceutil.ChangeServiceConfig, which involves a\n    #   lot of stuff (and API constants which would pollute the API), see:\n    #   http://pyxr.sourceforge.net/PyXR/c/python24/lib/site-packages/\n    #       win32/lib/win32serviceutil.py.html#0175\n    # - psutil is typically about \"read only\" monitoring stuff;\n    #   win_service_* APIs should only be used to retrieve a service and\n    #   check whether it's running\n\n    # def start(self, timeout=None):\n    #     with self._wrap_exceptions():\n    #         cext.winservice_start(self.name())\n    #         if timeout:\n    #             giveup_at = time.time() + timeout\n    #             while True:\n    #                 if self.status() == \"running\":\n    #                     return\n    #                 else:\n    #                     if time.time() > giveup_at:\n    #                         raise TimeoutExpired(timeout)\n    #                     else:\n    #                         time.sleep(.1)\n\n    # def stop(self):\n    #     # Note: timeout is not implemented because it's just not\n    #     # possible, see:\n    #     # http://stackoverflow.com/questions/11973228/\n    #     with self._wrap_exceptions():\n    #         return cext.winservice_stop(self.name())\n\n\n# =====================================================================\n# --- processes\n# =====================================================================\n\n\npids = cext.pids\npid_exists = cext.pid_exists\nppid_map = cext.ppid_map  # used internally by Process.children()\n\n\ndef is_permission_err(exc):\n    \"\"\"Return True if this is a permission error.\"\"\"\n    assert isinstance(exc, OSError), exc\n    return isinstance(exc, PermissionError) or exc.winerror in {\n        cext.ERROR_ACCESS_DENIED,\n        cext.ERROR_PRIVILEGE_NOT_HELD,\n    }\n\n\ndef convert_oserror(exc, pid=None, name=None):\n    \"\"\"Convert OSError into NoSuchProcess or AccessDenied.\"\"\"\n    assert isinstance(exc, OSError), exc\n    if is_permission_err(exc):\n        return AccessDenied(pid=pid, name=name)\n    if isinstance(exc, ProcessLookupError):\n        return NoSuchProcess(pid=pid, name=name)\n    raise exc\n\n\ndef wrap_exceptions(fun):\n    \"\"\"Decorator which converts OSError into NoSuchProcess or AccessDenied.\"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        try:\n            return fun(self, *args, **kwargs)\n        except OSError as err:\n            raise convert_oserror(err, pid=self.pid, name=self._name) from err\n\n    return wrapper\n\n\ndef retry_error_partial_copy(fun):\n    \"\"\"Workaround for https://github.com/giampaolo/psutil/issues/875.\n    See: https://stackoverflow.com/questions/4457745#4457745.\n    \"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(self, *args, **kwargs):\n        delay = 0.0001\n        times = 33\n        for _ in range(times):  # retries for roughly 1 second\n            try:\n                return fun(self, *args, **kwargs)\n            except OSError as _:\n                err = _\n                if err.winerror == ERROR_PARTIAL_COPY:\n                    time.sleep(delay)\n                    delay = min(delay * 2, 0.04)\n                    continue\n                raise\n        msg = (\n            f\"{fun} retried {times} times, converted to AccessDenied as it's \"\n            f\"still returning {err}\"\n        )\n        raise AccessDenied(pid=self.pid, name=self._name, msg=msg)\n\n    return wrapper\n\n\nclass Process:\n    \"\"\"Wrapper class around underlying C implementation.\"\"\"\n\n    __slots__ = [\"_cache\", \"_name\", \"_ppid\", \"pid\"]\n\n    def __init__(self, pid):\n        self.pid = pid\n        self._name = None\n        self._ppid = None\n\n    # --- oneshot() stuff\n\n    def oneshot_enter(self):\n        self._oneshot.cache_activate(self)\n        self.exe.cache_activate(self)\n\n    def oneshot_exit(self):\n        self._oneshot.cache_deactivate(self)\n        self.exe.cache_deactivate(self)\n\n    @memoize_when_activated\n    def _oneshot(self):\n        \"\"\"Return multiple information about this process as a\n        raw dict.\n        \"\"\"\n        return cext.proc_oneshot(self.pid)\n\n    def name(self):\n        \"\"\"Return process name, which on Windows is always the final\n        part of the executable.\n        \"\"\"\n        # This is how PIDs 0 and 4 are always represented in taskmgr\n        # and process-hacker.\n        if self.pid == 0:\n            return \"System Idle Process\"\n        if self.pid == 4:\n            return \"System\"\n        return os.path.basename(self.exe())\n\n    @wrap_exceptions\n    @memoize_when_activated\n    def exe(self):\n        if PYPY:\n            try:\n                exe = cext.proc_exe(self.pid)\n            except OSError as err:\n                # 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens\n                # (perhaps PyPy's JIT delaying garbage collection of files?).\n                if err.errno == 24:\n                    debug(f\"{err!r} translated into AccessDenied\")\n                    raise AccessDenied(self.pid, self._name) from err\n                raise\n        else:\n            exe = cext.proc_exe(self.pid)\n        if exe.startswith('\\\\'):\n            return convert_dos_path(exe)\n        return exe  # May be \"Registry\", \"MemCompression\", ...\n\n    @wrap_exceptions\n    @retry_error_partial_copy\n    def cmdline(self):\n        if cext.WINVER >= cext.WINDOWS_8_1:\n            # PEB method detects cmdline changes but requires more\n            # privileges: https://github.com/giampaolo/psutil/pull/1398\n            try:\n                return cext.proc_cmdline(self.pid, use_peb=True)\n            except OSError as err:\n                if is_permission_err(err):\n                    return cext.proc_cmdline(self.pid, use_peb=False)\n                else:\n                    raise\n        else:\n            return cext.proc_cmdline(self.pid, use_peb=True)\n\n    @wrap_exceptions\n    @retry_error_partial_copy\n    def environ(self):\n        s = cext.proc_environ(self.pid)\n        return parse_environ_block(s)\n\n    def ppid(self):\n        try:\n            return ppid_map()[self.pid]\n        except KeyError:\n            raise NoSuchProcess(self.pid, self._name) from None\n\n    def _get_raw_meminfo(self):\n        try:\n            return cext.proc_memory_info(self.pid)\n        except OSError as err:\n            if is_permission_err(err):\n                # TODO: the C ext can probably be refactored in order\n                # to get this from cext.proc_oneshot()\n                debug(\"attempting memory_info() fallback (slower)\")\n                info = self._oneshot()\n                return {\n                    \"PageFaultCount\": info[\"PageFaultCount\"],\n                    \"PeakWorkingSetSize\": info[\"PeakWorkingSetSize\"],\n                    \"WorkingSetSize\": info[\"WorkingSetSize\"],\n                    \"QuotaPeakPagedPoolUsage\": info[\"QuotaPeakPagedPoolUsage\"],\n                    \"QuotaPagedPoolUsage\": info[\"QuotaPagedPoolUsage\"],\n                    \"QuotaPeakNonPagedPoolUsage\": info[\n                        \"QuotaPeakNonPagedPoolUsage\"\n                    ],\n                    \"QuotaNonPagedPoolUsage\": info[\"QuotaNonPagedPoolUsage\"],\n                    \"PagefileUsage\": info[\"PagefileUsage\"],\n                    \"PeakPagefileUsage\": info[\"PeakPagefileUsage\"],\n                    \"PrivateUsage\": info[\"PrivatePageCount\"],  # adjust name\n                }\n            raise\n\n    @wrap_exceptions\n    def memory_info(self):\n        # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS\n        # struct.\n        d = self._get_raw_meminfo()\n        return ntp.pmem(\n            rss=d[\"WorkingSetSize\"],\n            vms=d[\"PrivateUsage\"],\n            peak_rss=d[\"PeakWorkingSetSize\"],\n            peak_vms=d[\"PeakPagefileUsage\"],\n            _deprecated={\n                # old aliases\n                \"wset\": d[\"WorkingSetSize\"],  # 'rss'\n                \"peak_wset\": d[\"PeakWorkingSetSize\"],  # 'peak_rss'\n                \"pagefile\": d[\"PrivateUsage\"],  # 'vms'\n                \"private\": d[\"PrivateUsage\"],  # 'vms'\n                \"peak_pagefile\": d[\"PeakPagefileUsage\"],  # 'vms'\n                # fields which were moved to memory_info_ex()\n                \"paged_pool\": d[\"QuotaPagedPoolUsage\"],\n                \"nonpaged_pool\": d[\"QuotaNonPagedPoolUsage\"],\n                \"peak_paged_pool\": d[\"QuotaPeakPagedPoolUsage\"],\n                \"peak_nonpaged_pool\": d[\"QuotaPeakNonPagedPoolUsage\"],\n                # moved to page_faults()\n                \"num_page_faults\": d[\"PageFaultCount\"],\n            },\n        )\n\n    @wrap_exceptions\n    def memory_info_ex(self):\n        d = self._oneshot()\n        raw = self._get_raw_meminfo()\n        return {\n            \"virtual\": d[\"VirtualSize\"],\n            \"peak_virtual\": d[\"PeakVirtualSize\"],\n            \"paged_pool\": raw[\"QuotaPagedPoolUsage\"],\n            \"nonpaged_pool\": raw[\"QuotaNonPagedPoolUsage\"],\n            \"peak_paged_pool\": raw[\"QuotaPeakPagedPoolUsage\"],\n            \"peak_nonpaged_pool\": raw[\"QuotaPeakNonPagedPoolUsage\"],\n        }\n\n    @wrap_exceptions\n    def memory_footprint(self):\n        uss = cext.proc_memory_uss(self.pid)\n        uss *= getpagesize()\n        return ntp.pfootprint(uss)\n\n    @wrap_exceptions\n    def page_faults(self):\n        t = cext.proc_page_faults(self.pid)\n        return ntp.ppagefaults(*t)\n\n    def memory_maps(self):\n        try:\n            raw = cext.proc_memory_maps(self.pid)\n        except OSError as err:\n            # XXX - can't use wrap_exceptions decorator as we're\n            # returning a generator; probably needs refactoring.\n            raise convert_oserror(err, self.pid, self._name) from err\n        else:\n            for addr, perm, path, rss in raw:\n                path = convert_dos_path(path)\n                addr = hex(addr)\n                yield (addr, perm, path, rss)\n\n    @wrap_exceptions\n    def kill(self):\n        return cext.proc_kill(self.pid)\n\n    @wrap_exceptions\n    def send_signal(self, sig):\n        if sig == signal.SIGTERM:\n            cext.proc_kill(self.pid)\n        elif sig in {signal.CTRL_C_EVENT, signal.CTRL_BREAK_EVENT}:\n            os.kill(self.pid, sig)\n        else:\n            msg = (\n                \"only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals \"\n                \"are supported on Windows\"\n            )\n            raise ValueError(msg)\n\n    @wrap_exceptions\n    def wait(self, timeout=None):\n        if timeout is None:\n            cext_timeout = cext.INFINITE\n        else:\n            # WaitForSingleObject() expects time in milliseconds.\n            cext_timeout = int(timeout * 1000)\n\n        timer = getattr(time, 'monotonic', time.time)\n        stop_at = timer() + timeout if timeout is not None else None\n\n        try:\n            # Exit code is supposed to come from GetExitCodeProcess().\n            # May also be None if OpenProcess() failed with\n            # ERROR_INVALID_PARAMETER, meaning PID is already gone.\n            exit_code = cext.proc_wait(self.pid, cext_timeout)\n        except cext.TimeoutExpired as err:\n            # WaitForSingleObject() returned WAIT_TIMEOUT. Just raise.\n            raise TimeoutExpired(timeout, self.pid, self._name) from err\n        except cext.TimeoutAbandoned:\n            # WaitForSingleObject() returned WAIT_ABANDONED, see:\n            # https://github.com/giampaolo/psutil/issues/1224\n            # We'll just rely on the internal polling and return None\n            # when the PID disappears. Subprocess module does the same\n            # (return None):\n            # https://github.com/python/cpython/blob/be50a7b627d0/Lib/subprocess.py#L1193-L1194\n            exit_code = None\n\n        # At this point WaitForSingleObject() returned WAIT_OBJECT_0,\n        # meaning the process is gone. Stupidly there are cases where\n        # its PID may still stick around so we do a further internal\n        # polling.\n        delay = 0.0001\n        while True:\n            if not pid_exists(self.pid):\n                return exit_code\n            if stop_at and timer() >= stop_at:\n                raise TimeoutExpired(timeout, pid=self.pid, name=self._name)\n            time.sleep(delay)\n            delay = min(delay * 2, 0.04)  # incremental delay\n\n    @wrap_exceptions\n    def username(self):\n        if self.pid in {0, 4}:\n            return 'NT AUTHORITY\\\\SYSTEM'\n        domain, user = cext.proc_username(self.pid)\n        return f\"{domain}\\\\{user}\"\n\n    @wrap_exceptions\n    def create_time(self, fast_only=False):\n        # Note: proc_times() not put under oneshot() 'cause create_time()\n        # is already cached by the main Process class.\n        try:\n            _user, _system, created = cext.proc_times(self.pid)\n            return created\n        except OSError as err:\n            if is_permission_err(err):\n                if fast_only:\n                    raise\n                debug(\"attempting create_time() fallback (slower)\")\n                return self._oneshot()[\"create_time\"]\n            raise\n\n    @wrap_exceptions\n    def num_threads(self):\n        return self._oneshot()[\"num_threads\"]\n\n    @wrap_exceptions\n    def threads(self):\n        rawlist = cext.proc_threads(self.pid)\n        retlist = []\n        for thread_id, utime, stime in rawlist:\n            ntuple = ntp.pthread(thread_id, utime, stime)\n            retlist.append(ntuple)\n        return retlist\n\n    @wrap_exceptions\n    def cpu_times(self):\n        try:\n            user, system, _created = cext.proc_times(self.pid)\n        except OSError as err:\n            if not is_permission_err(err):\n                raise\n            debug(\"attempting cpu_times() fallback (slower)\")\n            info = self._oneshot()\n            user = info[\"user_time\"]\n            system = info[\"kernel_time\"]\n        # Children user/system times are not retrievable (set to 0).\n        return ntp.pcputimes(user, system, 0.0, 0.0)\n\n    @wrap_exceptions\n    def suspend(self):\n        cext.proc_suspend_or_resume(self.pid, True)\n\n    @wrap_exceptions\n    def resume(self):\n        cext.proc_suspend_or_resume(self.pid, False)\n\n    @wrap_exceptions\n    @retry_error_partial_copy\n    def cwd(self):\n        if self.pid in {0, 4}:\n            raise AccessDenied(self.pid, self._name)\n        # return a normalized pathname since the native C function appends\n        # \"\\\\\" at the and of the path\n        path = cext.proc_cwd(self.pid)\n        return os.path.normpath(path)\n\n    @wrap_exceptions\n    def open_files(self):\n        if self.pid in {0, 4}:\n            return []\n        ret = set()\n        # Filenames come in in native format like:\n        # \"\\Device\\HarddiskVolume1\\Windows\\systemew\\file.txt\"\n        # Convert the first part in the corresponding drive letter\n        # (e.g. \"C:\\\") by using Windows's QueryDosDevice()\n        raw_file_names = cext.proc_open_files(self.pid)\n        for file in raw_file_names:\n            file = convert_dos_path(file)\n            if isfile_strict(file):\n                ntuple = ntp.popenfile(file, -1)\n                ret.add(ntuple)\n        return list(ret)\n\n    @wrap_exceptions\n    def net_connections(self, kind='inet'):\n        return net_connections(kind, _pid=self.pid)\n\n    @wrap_exceptions\n    def nice_get(self):\n        value = cext.proc_priority_get(self.pid)\n        value = ProcessPriority(value)\n        return value\n\n    @wrap_exceptions\n    def nice_set(self, value):\n        return cext.proc_priority_set(self.pid, value)\n\n    @wrap_exceptions\n    def ionice_get(self):\n        ret = cext.proc_io_priority_get(self.pid)\n        ret = ProcessIOPriority(ret)\n        return ret\n\n    @wrap_exceptions\n    def ionice_set(self, ioclass, value):\n        if value:\n            msg = \"value argument not accepted on Windows\"\n            raise TypeError(msg)\n        if ioclass not in ProcessIOPriority:\n            msg = f\"{ioclass} is not a valid priority\"\n            raise ValueError(msg)\n        cext.proc_io_priority_set(self.pid, ioclass)\n\n    @wrap_exceptions\n    def io_counters(self):\n        try:\n            ret = cext.proc_io_counters(self.pid)\n        except OSError as err:\n            if not is_permission_err(err):\n                raise\n            debug(\"attempting io_counters() fallback (slower)\")\n            info = self._oneshot()\n            ret = (\n                info[\"io_rcount\"],\n                info[\"io_wcount\"],\n                info[\"io_rbytes\"],\n                info[\"io_wbytes\"],\n                info[\"io_count_others\"],\n                info[\"io_bytes_others\"],\n            )\n        return ntp.pio(*ret)\n\n    @wrap_exceptions\n    def status(self):\n        suspended = cext.proc_is_suspended(self.pid)\n        if suspended:\n            return ProcessStatus.STATUS_STOPPED\n        else:\n            return ProcessStatus.STATUS_RUNNING\n\n    @wrap_exceptions\n    def cpu_affinity_get(self):\n        def from_bitmask(x):\n            return [i for i in range(64) if (1 << i) & x]\n\n        bitmask = cext.proc_cpu_affinity_get(self.pid)\n        return from_bitmask(bitmask)\n\n    @wrap_exceptions\n    def cpu_affinity_set(self, value):\n        def to_bitmask(ls):\n            if not ls:\n                msg = f\"invalid argument {ls!r}\"\n                raise ValueError(msg)\n            out = 0\n            for b in ls:\n                out |= 2**b\n            return out\n\n        # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER\n        # is returned for an invalid CPU but this seems not to be true,\n        # therefore we check CPUs validy beforehand.\n        allcpus = list(range(len(per_cpu_times())))\n        for cpu in value:\n            if cpu not in allcpus:\n                if not isinstance(cpu, int):\n                    msg = f\"invalid CPU {cpu!r}; an integer is required\"\n                    raise TypeError(msg)\n                msg = f\"invalid CPU {cpu!r}\"\n                raise ValueError(msg)\n\n        bitmask = to_bitmask(value)\n        cext.proc_cpu_affinity_set(self.pid, bitmask)\n\n    @wrap_exceptions\n    def num_handles(self):\n        try:\n            return cext.proc_num_handles(self.pid)\n        except OSError as err:\n            if is_permission_err(err):\n                debug(\"attempting num_handles() fallback (slower)\")\n                return self._oneshot()[\"num_handles\"]\n            raise\n\n    @wrap_exceptions\n    def num_ctx_switches(self):\n        ctx_switches = self._oneshot()[\"ctx_switches\"]\n        # only voluntary ctx switches are supported\n        return ntp.pctxsw(ctx_switches, 0)\n"
  },
  {
    "path": "psutil/arch/aix/common.c",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/core.h>\n#include <stdlib.h>\n\n#include \"../../arch/all/init.h\"\n#include \"common.h\"\n\n\n/* psutil_kread() - read from kernel memory */\nint\npsutil_kread(\n    int Kd,  // kernel memory file descriptor\n    KA_T addr,  // kernel memory address\n    char *buf,  // buffer to receive data\n    size_t len  // length to read\n) {\n    int br;\n\n    if (lseek64(Kd, (off64_t)addr, L_SET) == (off64_t)-1) {\n        psutil_oserror();\n        return 1;\n    }\n    br = read(Kd, buf, len);\n    if (br == -1) {\n        psutil_oserror();\n        return 1;\n    }\n    if (br != len) {\n        psutil_runtime_error(\"size mismatch when reading kernel memory fd\");\n        return 1;\n    }\n    return 0;\n}\n\nstruct procentry64 *\npsutil_read_process_table(int *num) {\n    size_t msz;\n    pid32_t pid = 0;\n    struct procentry64 *processes = (struct procentry64 *)NULL;\n    struct procentry64 *p;\n    int Np = 0;  // number of processes allocated in 'processes'\n    int np = 0;  // number of processes read into 'processes'\n    int i;  // number of processes read in current iteration\n\n    msz = (size_t)(PROCSIZE * PROCINFO_INCR);\n    processes = (struct procentry64 *)malloc(msz);\n    if (!processes) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n    Np = PROCINFO_INCR;\n    p = processes;\n    while ((i = getprocs64(\n                p, PROCSIZE, (struct fdsinfo64 *)NULL, 0, &pid, PROCINFO_INCR\n            ))\n           == PROCINFO_INCR)\n    {\n        np += PROCINFO_INCR;\n        if (np >= Np) {\n            msz = (size_t)(PROCSIZE * (Np + PROCINFO_INCR));\n            processes = (struct procentry64 *)realloc((char *)processes, msz);\n            if (!processes) {\n                PyErr_NoMemory();\n                return NULL;\n            }\n            Np += PROCINFO_INCR;\n        }\n        p = (struct procentry64 *)((char *)processes + (np * PROCSIZE));\n    }\n\n    /* add the number of processes read in the last iteration */\n    if (i > 0)\n        np += i;\n\n    *num = np;\n    return processes;\n}\n"
  },
  {
    "path": "psutil/arch/aix/common.h",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#ifndef __PSUTIL_AIX_COMMON_H__\n#define __PSUTIL_AIX_COMMON_H__\n\n#include <sys/core.h>\n\n#define PROCINFO_INCR (256)\n#define PROCSIZE (sizeof(struct procentry64))\n#define FDSINFOSIZE (sizeof(struct fdsinfo64))\n#define KMEM \"/dev/kmem\"\n\ntypedef u_longlong_t KA_T;\n\n// psutil_kread() - read from kernel memory\nint psutil_kread(\n    int Kd,  // kernel memory file descriptor\n    KA_T addr,  // kernel memory address\n    char *buf,  // buffer to receive data\n    size_t len  // length to read\n);\n\nstruct procentry64 *psutil_read_process_table(\n    int *num  // out - number of processes read\n);\n\n#endif  // __PSUTIL_AIX_COMMON_H__\n"
  },
  {
    "path": "psutil/arch/aix/ifaddrs.c",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*! Based on code from\n    https://lists.samba.org/archive/samba-technical/2009-February/063079.html\n!*/\n\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <net/if.h>\n#include <netinet/in.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n\n#include \"ifaddrs.h\"\n#include \"../../arch/all/init.h\"\n\n#define MAX(x, y) ((x) > (y) ? (x) : (y))\n#define SIZE(p) MAX((p).sa_len, sizeof(p))\n\n\nstatic struct sockaddr *\nsa_dup(struct sockaddr *sa1) {\n    struct sockaddr *sa2;\n    size_t sz = sa1->sa_len;\n    sa2 = (struct sockaddr *)calloc(1, sz);\n    if (sa2 == NULL)\n        return NULL;\n    memcpy(sa2, sa1, sz);\n    return sa2;\n}\n\n\nvoid\nfreeifaddrs(struct ifaddrs *ifp) {\n    if (NULL == ifp)\n        return;\n    free(ifp->ifa_name);\n    free(ifp->ifa_addr);\n    free(ifp->ifa_netmask);\n    free(ifp->ifa_dstaddr);\n    freeifaddrs(ifp->ifa_next);\n    free(ifp);\n}\n\n\nint\ngetifaddrs(struct ifaddrs **ifap) {\n    int sd, ifsize;\n    char *ccp, *ecp;\n    struct ifconf ifc;\n    struct ifreq *ifr;\n    struct ifaddrs *cifa = NULL; /* current */\n    struct ifaddrs *pifa = NULL; /* previous */\n    const size_t IFREQSZ = sizeof(struct ifreq);\n    int fam;\n\n    *ifap = NULL;\n\n    sd = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sd == -1)\n        goto error;\n\n    /* find how much memory to allocate for the SIOCGIFCONF call */\n    if (ioctl(sd, SIOCGSIZIFCONF, (caddr_t)&ifsize) < 0)\n        goto error;\n\n    ifc.ifc_req = (struct ifreq *)calloc(1, ifsize);\n    if (ifc.ifc_req == NULL)\n        goto error;\n    ifc.ifc_len = ifsize;\n\n    if (ioctl(sd, SIOCGIFCONF, &ifc) < 0)\n        goto error;\n\n    ccp = (char *)ifc.ifc_req;\n    ecp = ccp + ifsize;\n\n    while (ccp < ecp) {\n        ifr = (struct ifreq *)ccp;\n        ifsize = sizeof(ifr->ifr_name) + SIZE(ifr->ifr_addr);\n        fam = ifr->ifr_addr.sa_family;\n\n        if (fam == AF_INET || fam == AF_INET6) {\n            cifa = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs));\n            if (cifa == NULL)\n                goto error;\n            cifa->ifa_next = NULL;\n\n            if (pifa == NULL)\n                *ifap = cifa;  // first one\n            else\n                pifa->ifa_next = cifa;\n\n            cifa->ifa_name = strdup(ifr->ifr_name);\n            if (cifa->ifa_name == NULL)\n                goto error;\n            cifa->ifa_flags = 0;\n            cifa->ifa_dstaddr = NULL;\n\n            cifa->ifa_addr = sa_dup(&ifr->ifr_addr);\n            if (cifa->ifa_addr == NULL)\n                goto error;\n\n            if (fam == AF_INET) {\n                if (ioctl(sd, SIOCGIFNETMASK, ifr, IFREQSZ) < 0)\n                    goto error;\n                cifa->ifa_netmask = sa_dup(&ifr->ifr_addr);\n                if (cifa->ifa_netmask == NULL)\n                    goto error;\n            }\n\n            if (0 == ioctl(sd, SIOCGIFFLAGS, ifr)) /* optional */\n                cifa->ifa_flags = ifr->ifr_flags;\n\n            if (fam == AF_INET) {\n                if (ioctl(sd, SIOCGIFDSTADDR, ifr, IFREQSZ) < 0) {\n                    if (0 == ioctl(sd, SIOCGIFBRDADDR, ifr, IFREQSZ)) {\n                        cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);\n                        if (cifa->ifa_dstaddr == NULL)\n                            goto error;\n                    }\n                }\n                else {\n                    cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);\n                    if (cifa->ifa_dstaddr == NULL)\n                        goto error;\n                }\n            }\n            pifa = cifa;\n        }\n\n        ccp += ifsize;\n    }\n    free(ifc.ifc_req);\n    close(sd);\n    return 0;\nerror:\n    if (ifc.ifc_req != NULL)\n        free(ifc.ifc_req);\n    if (sd != -1)\n        close(sd);\n    freeifaddrs(*ifap);\n    return (-1);\n}\n"
  },
  {
    "path": "psutil/arch/aix/ifaddrs.h",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*! Based on code from\n    https://lists.samba.org/archive/samba-technical/2009-February/063079.html\n!*/\n\n\n#ifndef GENERIC_AIX_IFADDRS_H\n#define GENERIC_AIX_IFADDRS_H\n\n#include <sys/socket.h>\n#include <net/if.h>\n\n#undef ifa_dstaddr\n#undef ifa_broadaddr\n#define ifa_broadaddr ifa_dstaddr\n\nstruct ifaddrs {\n    struct ifaddrs *ifa_next;\n    char *ifa_name;\n    unsigned int ifa_flags;\n    struct sockaddr *ifa_addr;\n    struct sockaddr *ifa_netmask;\n    struct sockaddr *ifa_dstaddr;\n};\n\nextern int getifaddrs(struct ifaddrs **);\nextern void freeifaddrs(struct ifaddrs *);\n#endif\n"
  },
  {
    "path": "psutil/arch/aix/net_connections.c",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/* Baded on code from lsof:\n * http://www.ibm.com/developerworks/aix/library/au-lsof.html\n * - dialects/aix/dproc.c:gather_proc_info\n * - lib/prfp.c:process_file\n * - dialects/aix/dsock.c:process_socket\n * - dialects/aix/dproc.c:get_kernel_access\n */\n\n#include <Python.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#define _KERNEL\n#include <sys/file.h>\n#undef _KERNEL\n#include <sys/types.h>\n#include <sys/core.h>\n#include <sys/domain.h>\n#include <sys/un.h>\n#include <netinet/in_pcb.h>\n#include <arpa/inet.h>\n\n#include \"../../arch/all/init.h\"\n#include \"net_kernel_structs.h\"\n#include \"net_connections.h\"\n#include \"common.h\"\n\n\n#define NO_SOCKET (PyObject *)(-1)\n\nstatic int\nread_unp_addr(int Kd, KA_T unp_addr, char *buf, size_t buflen) {\n    struct sockaddr_un *ua = (struct sockaddr_un *)NULL;\n    struct sockaddr_un un;\n    struct mbuf64 mb;\n    int uo;\n\n    if (psutil_kread(Kd, unp_addr, (char *)&mb, sizeof(mb))) {\n        return 1;\n    }\n\n    uo = (int)(mb.m_hdr.mh_data - unp_addr);\n    if ((uo + sizeof(struct sockaddr)) <= sizeof(mb))\n        ua = (struct sockaddr_un *)((char *)&mb + uo);\n    else {\n        if (psutil_kread(Kd, (KA_T)mb.m_hdr.mh_data, (char *)&un, sizeof(un)))\n        {\n            return 1;\n        }\n        ua = &un;\n    }\n    if (ua && ua->sun_path[0]) {\n        if (mb.m_len > sizeof(struct sockaddr_un))\n            mb.m_len = sizeof(struct sockaddr_un);\n        *((char *)ua + mb.m_len - 1) = '\\0';\n        snprintf(buf, buflen, \"%s\", ua->sun_path);\n    }\n    return 0;\n}\n\nstatic PyObject *\nprocess_file(int Kd, pid32_t pid, int fd, KA_T fp) {\n    struct file64 f;\n    struct socket64 s;\n    struct protosw64 p;\n    struct domain d;\n    struct inpcb64 inp;\n    int fam;\n    struct tcpcb64 t;\n    int state = PSUTIL_CONN_NONE;\n    unsigned char *laddr = (unsigned char *)NULL;\n    unsigned char *raddr = (unsigned char *)NULL;\n    int rport, lport;\n    char laddr_str[INET6_ADDRSTRLEN];\n    char raddr_str[INET6_ADDRSTRLEN];\n    struct unpcb64 unp;\n    char unix_laddr_str[PATH_MAX] = {0};\n    char unix_raddr_str[PATH_MAX] = {0};\n\n    /* Read file structure */\n    if (psutil_kread(Kd, fp, (char *)&f, sizeof(f))) {\n        return NULL;\n    }\n    if (!f.f_count || f.f_type != DTYPE_SOCKET) {\n        return NO_SOCKET;\n    }\n\n    if (psutil_kread(Kd, (KA_T)f.f_data, (char *)&s, sizeof(s))) {\n        return NULL;\n    }\n\n    if (!s.so_type) {\n        return NO_SOCKET;\n    }\n\n    if (!s.so_proto) {\n        psutil_runtime_error(\"invalid socket protocol handle\");\n        return NULL;\n    }\n    if (psutil_kread(Kd, (KA_T)s.so_proto, (char *)&p, sizeof(p))) {\n        return NULL;\n    }\n\n    if (!p.pr_domain) {\n        psutil_runtime_error(\"invalid socket protocol domain\");\n        return NULL;\n    }\n    if (psutil_kread(Kd, (KA_T)p.pr_domain, (char *)&d, sizeof(d))) {\n        return NULL;\n    }\n\n    fam = d.dom_family;\n    if (fam == AF_INET || fam == AF_INET6) {\n        /* Read protocol control block */\n        if (!s.so_pcb) {\n            psutil_runtime_error(\"invalid socket PCB\");\n            return NULL;\n        }\n        if (psutil_kread(Kd, (KA_T)s.so_pcb, (char *)&inp, sizeof(inp))) {\n            return NULL;\n        }\n\n        if (p.pr_protocol == IPPROTO_TCP) {\n            /* If this is a TCP socket, read its control block */\n            if (inp.inp_ppcb\n                && !psutil_kread(\n                    Kd, (KA_T)inp.inp_ppcb, (char *)&t, sizeof(t)\n                ))\n                state = t.t_state;\n        }\n\n        if (fam == AF_INET6) {\n            laddr = (unsigned char *)&inp.inp_laddr6;\n            if (!IN6_IS_ADDR_UNSPECIFIED(&inp.inp_faddr6)) {\n                raddr = (unsigned char *)&inp.inp_faddr6;\n                rport = (int)ntohs(inp.inp_fport);\n            }\n        }\n        if (fam == AF_INET) {\n            laddr = (unsigned char *)&inp.inp_laddr;\n            if (inp.inp_faddr.s_addr != INADDR_ANY || inp.inp_fport != 0) {\n                raddr = (unsigned char *)&inp.inp_faddr;\n                rport = (int)ntohs(inp.inp_fport);\n            }\n        }\n        lport = (int)ntohs(inp.inp_lport);\n\n        inet_ntop(fam, laddr, laddr_str, sizeof(laddr_str));\n\n        if (raddr != NULL) {\n            inet_ntop(fam, raddr, raddr_str, sizeof(raddr_str));\n            return Py_BuildValue(\n                \"(iii(si)(si)ii)\",\n                fd,\n                fam,\n                s.so_type,\n                laddr_str,\n                lport,\n                raddr_str,\n                rport,\n                state,\n                pid\n            );\n        }\n        else {\n            return Py_BuildValue(\n                \"(iii(si)()ii)\",\n                fd,\n                fam,\n                s.so_type,\n                laddr_str,\n                lport,\n                state,\n                pid\n            );\n        }\n    }\n\n\n    if (fam == AF_UNIX) {\n        if (psutil_kread(Kd, (KA_T)s.so_pcb, (char *)&unp, sizeof(unp))) {\n            return NULL;\n        }\n        if ((KA_T)f.f_data != (KA_T)unp.unp_socket) {\n            psutil_runtime_error(\"unp_socket mismatch\");\n            return NULL;\n        }\n\n        if (unp.unp_addr) {\n            if (read_unp_addr(\n                    Kd, unp.unp_addr, unix_laddr_str, sizeof(unix_laddr_str)\n                ))\n            {\n                return NULL;\n            }\n        }\n\n        if (unp.unp_conn) {\n            if (psutil_kread(\n                    Kd, (KA_T)unp.unp_conn, (char *)&unp, sizeof(unp)\n                ))\n            {\n                return NULL;\n            }\n            if (read_unp_addr(\n                    Kd, unp.unp_addr, unix_raddr_str, sizeof(unix_raddr_str)\n                ))\n            {\n                return NULL;\n            }\n        }\n\n        return Py_BuildValue(\n            \"(iiissii)\",\n            fd,\n            d.dom_family,\n            s.so_type,\n            unix_laddr_str,\n            unix_raddr_str,\n            PSUTIL_CONN_NONE,\n            pid\n        );\n    }\n    return NO_SOCKET;\n}\n\nPyObject *\npsutil_net_connections(PyObject *self, PyObject *args) {\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_tuple = NULL;\n    KA_T fp;\n    int Kd = -1;\n    int i, np;\n    struct procentry64 *p;\n    struct fdsinfo64 *fds = (struct fdsinfo64 *)NULL;\n    pid32_t requested_pid;\n    pid32_t pid;\n    struct procentry64 *processes = (struct procentry64 *)NULL;\n    // the process table\n\n    if (py_retlist == NULL)\n        goto error;\n    if (!PyArg_ParseTuple(args, \"i\", &requested_pid))\n        goto error;\n\n    Kd = open(KMEM, O_RDONLY, 0);\n    if (Kd < 0) {\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, KMEM);\n        goto error;\n    }\n\n    processes = psutil_read_process_table(&np);\n    if (!processes)\n        goto error;\n\n    /* Loop through processes */\n    for (p = processes; np > 0; np--, p++) {\n        pid = p->pi_pid;\n        if (requested_pid != -1 && requested_pid != pid)\n            continue;\n        if (p->pi_state == 0 || p->pi_state == SZOMB)\n            continue;\n\n        if (!fds) {\n            fds = (struct fdsinfo64 *)malloc((size_t)FDSINFOSIZE);\n            if (!fds) {\n                PyErr_NoMemory();\n                goto error;\n            }\n        }\n        if (getprocs64(\n                (struct procentry64 *)NULL, PROCSIZE, fds, FDSINFOSIZE, &pid, 1\n            )\n            != 1)\n            continue;\n\n        /* loop over file descriptors */\n        for (i = 0; i < p->pi_maxofile; i++) {\n            fp = (KA_T)fds->pi_ufd[i].fp;\n            if (fp) {\n                py_tuple = process_file(Kd, p->pi_pid, i, fp);\n                if (py_tuple == NULL)\n                    goto error;\n                if (py_tuple != NO_SOCKET) {\n                    if (PyList_Append(py_retlist, py_tuple))\n                        goto error;\n                    Py_DECREF(py_tuple);\n                }\n            }\n        }\n    }\n    close(Kd);\n    free(processes);\n    if (fds != NULL)\n        free(fds);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_tuple);\n    Py_DECREF(py_retlist);\n    if (Kd > 0)\n        close(Kd);\n    if (processes != NULL)\n        free(processes);\n    if (fds != NULL)\n        free(fds);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/aix/net_connections.h",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#ifndef __NET_CONNECTIONS_H__\n#define __NET_CONNECTIONS_H__\n\n#include <Python.h>\n\nPyObject *psutil_net_connections(PyObject *self, PyObject *args);\n\n#endif  // __NET_CONNECTIONS_H__\n"
  },
  {
    "path": "psutil/arch/aix/net_kernel_structs.h",
    "content": "/*\n * Copyright (c) 2017, Arnon Yaari\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/* The kernel is always 64 bit but Python is usually compiled as a 32 bit\n * process. We're reading the kernel memory to get the network connections,\n * so we need the structs we read to be defined with 64 bit \"pointers\".\n * Here are the partial definitions of the structs we use, taken from the\n * header files, with data type sizes converted to their 64 bit counterparts,\n * and unused data truncated. */\n\n#ifdef __64BIT__\n// In case we're in a 64 bit process after all\n#include <sys/socketvar.h>\n#include <sys/protosw.h>\n#include <sys/unpcb.h>\n#include <sys/mbuf_base.h>\n#include <sys/mbuf_macro.h>\n#include <netinet/ip_var.h>\n#include <netinet/tcp.h>\n#include <netinet/tcpip.h>\n#include <netinet/tcp_timer.h>\n#include <netinet/tcp_var.h>\n#define file64 file\n#define socket64 socket\n#define protosw64 protosw\n#define inpcb64 inpcb\n#define tcpcb64 tcpcb\n#define unpcb64 unpcb\n#define mbuf64 mbuf\n#else  // __64BIT__\nstruct file64 {\n    int f_flag;\n    int f_count;\n    int f_options;\n    int f_type;\n    u_longlong_t f_data;\n};\n\nstruct socket64 {\n    short so_type;  // generic type, see socket.h\n    short so_options;  // from socket call, see socket.h\n    ushort so_linger;  // time to linger while closing\n    short so_state;  // internal state flags SS_*, below\n    u_longlong_t so_pcb;  // protocol control block\n    u_longlong_t so_proto;  // protocol handle\n};\n\nstruct protosw64 {\n    short pr_type;  // socket type used for\n    u_longlong_t pr_domain;  // domain protocol a member of\n    short pr_protocol;  // protocol number\n    short pr_flags;  // see below\n};\n\nstruct inpcb64 {\n    u_longlong_t inp_next, inp_prev;\n    // pointers to other pcb's\n    u_longlong_t\n        inp_head;  // pointer back to chain of inpcb's for this protocol\n    u_int32_t inp_iflowinfo;  // input flow label\n    u_short inp_fport;  // foreign port\n    u_int16_t inp_fatype;  // foreign address type\n    union in_addr_6 inp_faddr_6;  // foreign host table entry\n    u_int32_t inp_oflowinfo;  // output flow label\n    u_short inp_lport;  // local port\n    u_int16_t inp_latype;  // local address type\n    union in_addr_6 inp_laddr_6;  // local host table entry\n    u_longlong_t inp_socket;  // back pointer to socket\n    u_longlong_t inp_ppcb;  // pointer to per-protocol pcb\n    u_longlong_t space_rt;\n    struct sockaddr_in6 spare_dst;\n    u_longlong_t inp_ifa;  // interface address to use\n    int inp_flags;  // generic IP/datagram flags\n};\n\nstruct tcpcb64 {\n    u_longlong_t seg__next;\n    u_longlong_t seg__prev;\n    short t_state;  // state of this connection\n};\n\nstruct unpcb64 {\n    u_longlong_t unp_socket;  // pointer back to socket\n    u_longlong_t unp_vnode;  // if associated with file\n    ino_t unp_vno;  // fake vnode number\n    u_longlong_t unp_conn;  // control block of connected socket\n    u_longlong_t unp_refs;  // referencing socket linked list\n    u_longlong_t unp_nextref;  // link in unp_refs list\n    u_longlong_t unp_addr;  // bound address of socket\n};\n\nstruct m_hdr64 {\n    u_longlong_t mh_next;  // next buffer in chain\n    u_longlong_t mh_nextpkt;  // next chain in queue/record\n    long mh_len;  // amount of data in this mbuf\n    u_longlong_t mh_data;  // location of data\n};\n\nstruct mbuf64 {\n    struct m_hdr64 m_hdr;\n};\n\n#define m_len m_hdr.mh_len\n\n#endif  // __64BIT__\n"
  },
  {
    "path": "psutil/arch/all/errors.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <errno.h>\n#include <string.h>\n#if defined(PSUTIL_WINDOWS)\n#include <windows.h>\n#endif\n\n#include \"init.h\"\n\n#define MSG_SIZE 512\n\n\n// Set OSError() based on errno (UNIX) or GetLastError() (Windows).\nPyObject *\npsutil_oserror(void) {\n#ifdef PSUTIL_WINDOWS\n    PyErr_SetFromWindowsErr(GetLastError());\n#else\n    PyErr_SetFromErrno(PyExc_OSError);\n#endif\n    return NULL;\n}\n\n\n// Same as above, but adds the syscall to the exception message. On\n// Windows this is achieved by setting the `filename` attribute of the\n// OSError object.\nPyObject *\npsutil_oserror_wsyscall(const char *syscall) {\n    char msg[MSG_SIZE];\n\n#ifdef PSUTIL_WINDOWS\n    DWORD err = GetLastError();\n    str_format(msg, sizeof(msg), \"(originated from %s)\", syscall);\n    PyErr_SetFromWindowsErrWithFilename(err, msg);\n#else\n    PyObject *exc;\n    str_format(\n        msg, sizeof(msg), \"%s (originated from %s)\", strerror(errno), syscall\n    );\n    exc = PyObject_CallFunction(PyExc_OSError, \"(is)\", errno, msg);\n    PyErr_SetObject(PyExc_OSError, exc);\n    Py_XDECREF(exc);\n#endif\n    return NULL;\n}\n\n\n// Set OSError(errno=ESRCH) (\"No such process\").\nPyObject *\npsutil_oserror_nsp(const char *syscall) {\n    PyObject *exc;\n    char msg[MSG_SIZE];\n\n    str_format(\n        msg, sizeof(msg), \"force no such process (originated from %s)\", syscall\n    );\n    exc = PyObject_CallFunction(PyExc_OSError, \"(is)\", ESRCH, msg);\n    PyErr_SetObject(PyExc_OSError, exc);\n    Py_XDECREF(exc);\n    return NULL;\n}\n\n\n// Set OSError(errno=EACCES) (\"Permission denied\").\nPyObject *\npsutil_oserror_ad(const char *syscall) {\n    PyObject *exc;\n    char msg[MSG_SIZE];\n\n    str_format(\n        msg,\n        sizeof(msg),\n        \"force permission denied (originated from %s)\",\n        syscall\n    );\n    exc = PyObject_CallFunction(PyExc_OSError, \"(is)\", EACCES, msg);\n    PyErr_SetObject(PyExc_OSError, exc);\n    Py_XDECREF(exc);\n    return NULL;\n}\n\n\n// Set RuntimeError with formatted `msg` and optional arguments.\nPyObject *\npsutil_runtime_error(const char *msg, ...) {\n    va_list args;\n\n    va_start(args, msg);\n    PyErr_FormatV(PyExc_RuntimeError, msg, args);\n    va_end(args);\n    return NULL;\n}\n\n\n// Use it when invalid args are passed to a C function.\nint\npsutil_badargs(const char *funcname) {\n    PyErr_Format(\n        PyExc_RuntimeError, \"%s() invalid args passed to function\", funcname\n    );\n    return -1;\n}\n"
  },
  {
    "path": "psutil/arch/all/init.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Global names shared by all platforms.\n\n#include <Python.h>\n\n#include \"init.h\"\n\nint PSUTIL_DEBUG = 0;\nint PSUTIL_TESTING = 0;\nint PSUTIL_CONN_NONE = 128;\n\n#ifdef Py_GIL_DISABLED\nPyMutex utxent_lock = {0};\n#endif\n\n\n// Enable or disable PSUTIL_DEBUG messages.\nPyObject *\npsutil_set_debug(PyObject *self, PyObject *args) {\n    PyObject *value;\n    int x;\n\n    if (!PyArg_ParseTuple(args, \"O\", &value))\n        return NULL;\n    x = PyObject_IsTrue(value);\n    if (x < 0) {\n        return NULL;\n    }\n    else if (x == 0) {\n        PSUTIL_DEBUG = 0;\n    }\n    else {\n        PSUTIL_DEBUG = 1;\n    }\n    Py_RETURN_NONE;\n}\n\n\n// Called on module import on all platforms.\nint\npsutil_setup(void) {\n    if (getenv(\"PSUTIL_DEBUG\") != NULL)\n        PSUTIL_DEBUG = 1;\n    if (getenv(\"PSUTIL_TESTING\") != NULL)\n        PSUTIL_TESTING = 1;\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/all/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Global names shared by all platforms.\n\n#include <Python.h>\n\n// We do this so that all .c files have to include only one header\n// (ourselves, init.h).\n\n// clang-format off\n#if defined(PSUTIL_POSIX)\n    #include \"../../arch/posix/init.h\"\n#endif\n#if defined(PSUTIL_BSD)\n    #include \"../../arch/bsd/init.h\"\n#endif\n\n#if defined(PSUTIL_LINUX)\n    #include \"../../arch/linux/init.h\"\n#elif defined(PSUTIL_WINDOWS)\n    #include \"../../arch/windows/init.h\"\n#elif defined(PSUTIL_OSX)\n    #include \"../../arch/osx/init.h\"\n#elif defined(PSUTIL_FREEBSD)\n    #include \"../../arch/freebsd/init.h\"\n#elif defined(PSUTIL_OPENBSD)\n    #include \"../../arch/openbsd/init.h\"\n#elif defined(PSUTIL_NETBSD)\n    #include \"../../arch/netbsd/init.h\"\n#elif defined(PSUTIL_SUNOS)\n    #include \"../../arch/sunos/init.h\"\n#endif\n\n// print debug messages when set to 1\nextern int PSUTIL_DEBUG;\n// a signaler for connections without an actual status\nextern int PSUTIL_CONN_NONE;\nextern int PSUTIL_TESTING;\n\n#ifdef Py_GIL_DISABLED\n    extern PyMutex utxent_lock;\n    #define UTXENT_MUTEX_LOCK() PyMutex_Lock(&utxent_lock)\n    #define UTXENT_MUTEX_UNLOCK() PyMutex_Unlock(&utxent_lock)\n#else\n    #define UTXENT_MUTEX_LOCK()\n    #define UTXENT_MUTEX_UNLOCK()\n#endif\n// clang-format on\n\n\n// ====================================================================\n// --- Backward compatibility with missing Python.h APIs\n// ====================================================================\n\n// --- _Py_PARSE_PID\n\n// clang-format off\n// SIZEOF_INT|LONG is missing on Linux + PyPy (only?).\n// In this case we guess it from setup.py. It's not 100% bullet proof,\n// If wrong we'll probably get compiler warnings.\n// FWIW on all UNIX platforms I've seen pid_t is defined as an int.\n// _getpid() on Windows also returns an int.\n#if !defined(SIZEOF_INT)\n    #define SIZEOF_INT 4\n#endif\n#if !defined(SIZEOF_LONG)\n    #define SIZEOF_LONG 8\n#endif\n#if !defined(SIZEOF_PID_T)\n    #define SIZEOF_PID_T PSUTIL_SIZEOF_PID_T  // set as a macro in setup.py\n#endif\n\n// _Py_PARSE_PID was added in Python 3, but since it's private we make\n// sure it's always present.\n#ifndef _Py_PARSE_PID\n    #if SIZEOF_PID_T == SIZEOF_INT\n        #define _Py_PARSE_PID \"i\"\n    #elif SIZEOF_PID_T == SIZEOF_LONG\n        #define _Py_PARSE_PID \"l\"\n    #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG\n        #define _Py_PARSE_PID \"L\"\n    #else\n        #error \"_Py_PARSE_PID: sizeof(pid_t) is neither sizeof(int), \"\n               \"sizeof(long) or sizeof(long long)\"\n    #endif\n#endif\n\n// PyPy on Windows\n#ifndef PyLong_FromPid\n    #if ((SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_LONG))\n        #define PyLong_FromPid PyLong_FromLong\n    #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG\n        #define PyLong_FromPid PyLong_FromLongLong\n    #else\n        #error \"PyLong_FromPid: sizeof(pid_t) is neither sizeof(int), \"\n               \"sizeof(long) or sizeof(long long)\"\n    #endif\n#endif\n// clang-format on\n\n// ====================================================================\n// --- Internal utils\n// ====================================================================\n\n// Print a debug message to stderr, including where it originated from\n// within the C code (file path + lineno).\n#define psutil_debug(...)                                              \\\n    do {                                                               \\\n        if (!PSUTIL_DEBUG)                                             \\\n            break;                                                     \\\n        fprintf(stderr, \"psutil-debug [%s:%d]> \", __FILE__, __LINE__); \\\n        fprintf(stderr, __VA_ARGS__);                                  \\\n        fprintf(stderr, \"\\n\");                                         \\\n    } while (0)\n\n\nPyObject *psutil_oserror(void);\nPyObject *psutil_oserror_ad(const char *msg);\nPyObject *psutil_oserror_nsp(const char *msg);\nPyObject *psutil_oserror_wsyscall(const char *syscall);\nPyObject *psutil_runtime_error(const char *msg, ...);\n\nint str_append(char *dst, size_t dst_size, const char *src);\nint str_copy(char *dst, size_t dst_size, const char *src);\nint str_format(char *buf, size_t size, const char *fmt, ...);\n\nint pydict_add(PyObject *dict, const char *key, const char *fmt, ...);\nint pylist_append_fmt(PyObject *list, const char *fmt, ...);\nint pylist_append_obj(PyObject *list, PyObject *obj);\n\nint psutil_badargs(const char *funcname);\nint psutil_setup(void);\ndouble psutil_usage_percent(double used, double total, int round_);\n\n// ====================================================================\n// --- Exposed to Python\n// ====================================================================\n\n#if defined(PSUTIL_WINDOWS) || defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\nPyObject *psutil_pids(PyObject *self, PyObject *args);\n#endif\nPyObject *psutil_set_debug(PyObject *self, PyObject *args);\nPyObject *psutil_check_pid_range(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/all/pids.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\n#include \"init.h\"\n\n\n// Raise OverflowError if Python int value overflowed when converting\n// to pid_t. Raise ValueError if Python int value is negative.\n// Otherwise, return None.\nPyObject *\npsutil_check_pid_range(PyObject *self, PyObject *args) {\n#ifdef PSUTIL_WINDOWS\n    DWORD pid;\n#else\n    pid_t pid;\n#endif\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (pid < 0) {\n        PyErr_SetString(PyExc_ValueError, \"pid must be a positive integer\");\n        return NULL;\n    }\n    Py_RETURN_NONE;\n}\n\n\n#if defined(PSUTIL_WINDOWS) || defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\nPyObject *\npsutil_pids(PyObject *self, PyObject *args) {\n#ifdef PSUTIL_WINDOWS\n    DWORD *pids_array = NULL;\n#else\n    pid_t *pids_array = NULL;\n#endif\n    int pids_count = 0;\n    int i;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (!py_retlist)\n        return NULL;\n\n    if (_psutil_pids(&pids_array, &pids_count) != 0)\n        goto error;\n\n    if (pids_count == 0) {\n        psutil_runtime_error(\"no PIDs found\");\n        goto error;\n    }\n\n    for (i = 0; i < pids_count; i++) {\n        if (!pylist_append_obj(py_retlist, PyLong_FromPid(pids_array[i])))\n            goto error;\n    }\n\n    free(pids_array);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    free(pids_array);\n    return NULL;\n}\n#endif\n"
  },
  {
    "path": "psutil/arch/all/str.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// String utilities.\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"init.h\"\n\n\nstatic int\n_error(const char *msg) {\n    if (PSUTIL_TESTING) {\n        fprintf(stderr, \"CRITICAL: %s\\n\", msg);\n        fflush(stderr);\n        exit(EXIT_FAILURE);  // terminate execution\n    }\n    else {\n        // Print debug msg because we never check str_*() return value.\n        psutil_debug(\"%s\", msg);\n    }\n    return -1;\n}\n\n\n// Safely formats a string into a buffer. Writes a printf-style\n// formatted string into `buf` of size `size`, always null-terminating\n// if size > 0. Returns the number of characters written (excluding the\n// null terminator) on success, or -1 if the buffer is too small or an\n// error occurs.\nint\nstr_format(char *buf, size_t size, const char *fmt, ...) {\n    va_list args;\n    int ret;\n\n    if (size == 0)\n        return _error(\"str_format: invalid arg 'size' = 0\");\n\n    va_start(args, fmt);\n#if defined(PSUTIL_WINDOWS)\n    ret = _vsnprintf_s(buf, size, _TRUNCATE, fmt, args);\n#else\n    ret = vsnprintf(buf, size, fmt, args);\n#endif\n    va_end(args);\n\n    if (ret < 0 || (size_t)ret >= size) {\n        psutil_debug(\"str_format: error in format '%s'\", fmt);\n        buf[size - 1] = '\\0';\n        return -1;\n    }\n    return ret;\n}\n\n\n// Safely copy `src` to `dst`, always null-terminating. Replaces unsafe\n// strcpy/strncpy.\nint\nstr_copy(char *dst, size_t dst_size, const char *src) {\n    if (dst_size == 0)\n        return _error(\"str_copy: invalid arg 'dst_size' = 0\");\n\n#if defined(PSUTIL_WINDOWS)\n    if (strcpy_s(dst, dst_size, src) != 0)\n        return _error(\"str_copy: strcpy_s failed\");\n#else\n    strncpy(dst, src, dst_size - 1);\n    dst[dst_size - 1] = '\\0';\n#endif\n    return 0;\n}\n\n\n// Safely append `src` to `dst`, always null-terminating. Returns 0 on\n// success, -1 on truncation.\nint\nstr_append(char *dst, size_t dst_size, const char *src) {\n    size_t dst_len;\n\n    if (!dst || !src || dst_size == 0)\n        return _error(\"str_append: invalid arg\");\n\n#if defined(PSUTIL_WINDOWS)\n    dst_len = strnlen_s(dst, dst_size);\n    if (dst_len >= dst_size - 1)\n        return _error(\"str_append: destination full or truncated\");\n    if (strcat_s(dst, dst_size, src) != 0)\n        return _error(\"str_append: strcat_s failed\");\n#elif defined(PSUTIL_MACOS) || defined(PSUTIL_BSD)\n    dst_len = strlcat(dst, src, dst_size);\n    if (dst_len >= dst_size)\n        return _error(\"str_append: truncated\");\n#else\n    dst_len = strnlen(dst, dst_size);\n    if (dst_len >= dst_size - 1)\n        return _error(\"str_append: destination full or truncated\");\n    strncat(dst, src, dst_size - dst_len - 1);\n    dst[dst_size - 1] = '\\0';\n#endif\n\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/all/utils.c",
    "content": "#include <Python.h>\n#include <stdarg.h>\n#include <math.h>\n\n\n// Build a Python object from a format string, append it to a list,\n// then decref it. Eliminates the need for a temporary variable, a NULL\n// check, and a Py_DECREF / Py_XDECREF at the error label. Returns 1 on\n// success, 0 on failure with a Python exception set.\nint\npylist_append_fmt(PyObject *list, const char *fmt, ...) {\n    int ret = 0;  // 0 = failure\n    PyObject *obj = NULL;\n    va_list ap;\n\n    va_start(ap, fmt);\n    obj = Py_VaBuildValue(fmt, ap);\n    va_end(ap);\n\n    if (!obj)\n        return 0;\n    if (PyList_Append(list, obj) < 0)\n        goto done;\n    ret = 1;  // success\n\ndone:\n    Py_DECREF(obj);\n    return ret;\n}\n\n\n// Append a pre-built Python object to a list, then decref it. Same as\n// pylist_append_fmt() but takes an already-built object instead of a\n// format string. Returns 1 on success, 0 on failure with a Python\n// exception set.\nint\npylist_append_obj(PyObject *list, PyObject *obj) {\n    if (!obj)\n        return 0;\n    if (PyList_Append(list, obj) < 0) {\n        Py_DECREF(obj);\n        return 0;\n    }\n    Py_DECREF(obj);\n    return 1;\n}\n\n\n// Build a Python object from a format string, set it as a key in a\n// dict, then decref it. Same idea as pylist_append_fmt() but for\n// dicts. Returns 1 on success, 0 on failure with a Python exception\n// set.\nint\npydict_add(PyObject *dict, const char *key, const char *fmt, ...) {\n    int ret = 0;  // 0 = failure\n    PyObject *obj = NULL;\n    va_list ap;\n\n    va_start(ap, fmt);\n    obj = Py_VaBuildValue(fmt, ap);\n    va_end(ap);\n\n    if (!obj)\n        return 0;\n    if (PyDict_SetItemString(dict, key, obj) < 0)\n        goto done;\n    ret = 1;  // success\n\ndone:\n    Py_DECREF(obj);\n    return ret;\n}\n\n\ndouble\npsutil_usage_percent(double used, double total, int round_) {\n    double ret;\n\n    if (total == 0.0)\n        return 0.0;\n    ret = (used / total) * 100.0;\n    if (round_ >= 0)\n        ret = round(ret * pow(10, round_)) / pow(10, round_);\n    return ret;\n}\n"
  },
  {
    "path": "psutil/arch/bsd/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/resource.h>\n#include <sys/types.h>\n#include <sys/sched.h>  // CP_* on OpenBSD\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_cpu_count_logical(PyObject *self, PyObject *args) {\n    int mib[2];\n    int ncpu;\n    size_t len;\n\n    mib[0] = CTL_HW;\n    mib[1] = HW_NCPU;\n    len = sizeof(ncpu);\n\n    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)\n        Py_RETURN_NONE;  // mimic os.cpu_count()\n    else\n        return Py_BuildValue(\"i\", ncpu);\n}\n\n\nPyObject *\npsutil_cpu_times(PyObject *self, PyObject *args) {\n#ifdef PSUTIL_NETBSD\n    u_int64_t cpu_time[CPUSTATES];\n#else\n    long cpu_time[CPUSTATES];\n#endif\n    size_t size = sizeof(cpu_time);\n    int ret;\n\n#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)\n    ret = psutil_sysctlbyname(\"kern.cp_time\", &cpu_time, size);\n#elif PSUTIL_OPENBSD\n    int mib[] = {CTL_KERN, KERN_CPTIME};\n    ret = psutil_sysctl(mib, 2, &cpu_time, size);\n#endif\n    if (ret != 0)\n        return NULL;\n    return Py_BuildValue(\n        \"(ddddd)\",\n        (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,\n        (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,\n        (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,\n        (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,\n        (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC\n    );\n}\n"
  },
  {
    "path": "psutil/arch/bsd/disk.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#if PSUTIL_NETBSD  // getvfsstat()\n#include <sys/types.h>\n#include <sys/statvfs.h>\n#else  // getfsstat()\n#include <sys/param.h>\n#include <sys/ucred.h>\n#include <sys/mount.h>\n#endif\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_disk_partitions(PyObject *self, PyObject *args) {\n    int num;\n    int i;\n    long len;\n    uint64_t flags;\n    char opts[200];\n#ifdef PSUTIL_NETBSD\n    struct statvfs *fs = NULL;\n#else\n    struct statfs *fs = NULL;\n#endif\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_dev = NULL;\n    PyObject *py_mountp = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    // get the number of mount points\n    Py_BEGIN_ALLOW_THREADS\n#ifdef PSUTIL_NETBSD\n    num = getvfsstat(NULL, 0, MNT_NOWAIT);\n#else\n    num = getfsstat(NULL, 0, MNT_NOWAIT);\n#endif\n    Py_END_ALLOW_THREADS\n    if (num == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    len = sizeof(*fs) * num;\n    fs = malloc(len);\n    if (fs == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    Py_BEGIN_ALLOW_THREADS\n#ifdef PSUTIL_NETBSD\n    num = getvfsstat(fs, len, MNT_NOWAIT);\n#else\n    num = getfsstat(fs, len, MNT_NOWAIT);\n#endif\n    Py_END_ALLOW_THREADS\n    if (num == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < num; i++) {\n        opts[0] = 0;\n#ifdef PSUTIL_NETBSD\n        flags = fs[i].f_flag;\n#else\n        flags = fs[i].f_flags;\n#endif\n\n        // see sys/mount.h\n        if (flags & MNT_RDONLY)\n            str_append(opts, sizeof(opts), \"ro\");\n        else\n            str_append(opts, sizeof(opts), \"rw\");\n        if (flags & MNT_SYNCHRONOUS)\n            str_append(opts, sizeof(opts), \",sync\");\n        if (flags & MNT_NOEXEC)\n            str_append(opts, sizeof(opts), \",noexec\");\n        if (flags & MNT_NOSUID)\n            str_append(opts, sizeof(opts), \",nosuid\");\n        if (flags & MNT_ASYNC)\n            str_append(opts, sizeof(opts), \",async\");\n        if (flags & MNT_NOATIME)\n            str_append(opts, sizeof(opts), \",noatime\");\n        if (flags & MNT_SOFTDEP)\n            str_append(opts, sizeof(opts), \",softdep\");\n#ifdef PSUTIL_FREEBSD\n        if (flags & MNT_UNION)\n            str_append(opts, sizeof(opts), \",union\");\n        if (flags & MNT_SUIDDIR)\n            str_append(opts, sizeof(opts), \",suiddir\");\n        if (flags & MNT_NOSYMFOLLOW)\n            str_append(opts, sizeof(opts), \",nosymfollow\");\n#ifdef MNT_GJOURNAL\n        if (flags & MNT_GJOURNAL)\n            str_append(opts, sizeof(opts), \",gjournal\");\n#endif\n        if (flags & MNT_MULTILABEL)\n            str_append(opts, sizeof(opts), \",multilabel\");\n        if (flags & MNT_ACLS)\n            str_append(opts, sizeof(opts), \",acls\");\n        if (flags & MNT_NOCLUSTERR)\n            str_append(opts, sizeof(opts), \",noclusterr\");\n        if (flags & MNT_NOCLUSTERW)\n            str_append(opts, sizeof(opts), \",noclusterw\");\n#ifdef MNT_NFS4ACLS\n        if (flags & MNT_NFS4ACLS)\n            str_append(opts, sizeof(opts), \",nfs4acls\");\n#endif\n#elif PSUTIL_NETBSD\n        if (flags & MNT_NODEV)\n            str_append(opts, sizeof(opts), \",nodev\");\n        if (flags & MNT_UNION)\n            str_append(opts, sizeof(opts), \",union\");\n        if (flags & MNT_NOCOREDUMP)\n            str_append(opts, sizeof(opts), \",nocoredump\");\n#ifdef MNT_RELATIME\n        if (flags & MNT_RELATIME)\n            str_append(opts, sizeof(opts), \",relatime\");\n#endif\n        if (flags & MNT_IGNORE)\n            str_append(opts, sizeof(opts), \",ignore\");\n#ifdef MNT_DISCARD\n        if (flags & MNT_DISCARD)\n            str_append(opts, sizeof(opts), \",discard\");\n#endif\n#ifdef MNT_EXTATTR\n        if (flags & MNT_EXTATTR)\n            str_append(opts, sizeof(opts), \",extattr\");\n#endif\n        if (flags & MNT_LOG)\n            str_append(opts, sizeof(opts), \",log\");\n        if (flags & MNT_SYMPERM)\n            str_append(opts, sizeof(opts), \",symperm\");\n        if (flags & MNT_NODEVMTIME)\n            str_append(opts, sizeof(opts), \",nodevmtime\");\n#endif\n        py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);\n        if (!py_dev)\n            goto error;\n        py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);\n        if (!py_mountp)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(OOss)\",\n                py_dev,  // device\n                py_mountp,  // mount point\n                fs[i].f_fstypename,  // fs type\n                opts  // options\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_dev);\n        Py_CLEAR(py_mountp);\n    }\n\n    free(fs);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_dev);\n    Py_XDECREF(py_mountp);\n    Py_DECREF(py_retlist);\n    if (fs != NULL)\n        free(fs);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/bsd/heap.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)\n#include <Python.h>\n#include <stdlib.h>\n#if defined(PSUTIL_FREEBSD)\n#include <malloc_np.h>\n#else\n#include <malloc.h>\n#endif\n\n#include \"../../arch/all/init.h\"\n\n\n// Return low-level heap statistics from the C allocator. Return\n// jemalloc heap stats via `mallctl()`. Mimics Linux `mallinfo2()`:\n//   - heap_used  ~ stats.allocated  (like `uordblks`)\n//   - mmap_used  ~ stats.mapped     (like `hblkhd`)\nPyObject *\npsutil_heap_info(PyObject *self, PyObject *args) {\n    uint64_t epoch = 0;\n    uint64_t allocated = 0, active = 0, mapped = 0;\n    size_t sz_epoch = sizeof(epoch);\n    size_t sz_val;\n    int ret;\n\n    // Flush per-thread tcache so small leaks become visible.\n    // Originates from https://github.com/giampaolo/psleak/issues/6. In\n    // there we had failures for small allocations, which disappeared\n    // after we added this.\n    ret = mallctl(\"thread.tcache.flush\", NULL, NULL, NULL, 0);\n    if (ret != 0)\n        return psutil_oserror_wsyscall(\"mallctl('thread.tcache.flush')\");\n\n    // Read current epoch\n    ret = mallctl(\"epoch\", &epoch, &sz_epoch, NULL, 0);\n    if (ret != 0)\n        return psutil_oserror_wsyscall(\"mallctl('epoch')\");\n\n    // Refresh stats\n    ret = mallctl(\"epoch\", NULL, NULL, &epoch, sz_epoch);\n    if (ret != 0)\n        return psutil_oserror_wsyscall(\"mallctl('epoch') update\");\n\n    // Read stats\n    sz_val = sizeof(allocated);\n    ret = mallctl(\"stats.allocated\", &allocated, &sz_val, NULL, 0);\n    if (ret != 0)\n        return psutil_oserror_wsyscall(\"mallctl('stats.allocated')\");\n\n    sz_val = sizeof(mapped);\n    ret = mallctl(\"stats.mapped\", &mapped, &sz_val, NULL, 0);\n    if (ret != 0)\n        return psutil_oserror_wsyscall(\"mallctl('stats.mapped')\");\n\n    return Py_BuildValue(\"KK\", allocated, mapped);\n}\n\n\n// Release unused heap memory from all jemalloc arenas back to the OS.\n// Aggressively purges free pages from all arenas (main + per-thread).\n// More effective than Linux `heap_trim(0)`.\nPyObject *\npsutil_heap_trim(PyObject *self, PyObject *args) {\n    char cmd[32];\n    int ret;\n\n#ifdef MALLCTL_ARENAS_ALL\n    // FreeBSD. MALLCTL_ARENAS_ALL is a magic number (4096) which means \"all\n    // arenas\".\n    str_format(cmd, sizeof(cmd), \"arena.%u.purge\", MALLCTL_ARENAS_ALL);\n    ret = mallctl(cmd, NULL, NULL, NULL, 0);\n    if (ret != 0)\n        return psutil_oserror();\n#else\n    // NetBSD. Iterate over all arenas.\n    unsigned narenas;\n    size_t sz = sizeof(narenas);\n\n    ret = mallctl(\"arenas.narenas\", &narenas, &sz, NULL, 0);\n    if (ret != 0)\n        return psutil_oserror_wsyscall(\"mallctl('arenas.narenas')\");\n\n    for (unsigned i = 0; i < narenas; i++) {\n        str_format(cmd, sizeof(cmd), \"arena.%u.purge\", i);\n        ret = mallctl(cmd, NULL, NULL, NULL, 0);\n        if (ret != 0)\n            return psutil_oserror_wsyscall(\"mallctl('arena.{n}.purge')\");\n    }\n#endif\n\n    Py_RETURN_NONE;\n}\n#endif  // PSUTIL_FREEBSD || PSUTIL_NETBSD\n"
  },
  {
    "path": "psutil/arch/bsd/init.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"../../arch/all/init.h\"\n\n\nvoid\nconvert_kvm_err(const char *syscall, char *errbuf) {\n    char fullmsg[512];\n\n    str_format(\n        fullmsg, sizeof(fullmsg), \"(originated from %s: %s)\", syscall, errbuf\n    );\n    if (strstr(errbuf, \"Permission denied\") != NULL)\n        psutil_oserror_ad(fullmsg);\n    else if (strstr(errbuf, \"Operation not permitted\") != NULL)\n        psutil_oserror_ad(fullmsg);\n    else\n        psutil_runtime_error(fullmsg);\n}\n"
  },
  {
    "path": "psutil/arch/bsd/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/types.h>\n\n\n#define PSUTIL_KPT2DOUBLE(t) (t##_sec + t##_usec / 1000000.0)\n\n#if defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)\n#define PSUTIL_HASNT_KINFO_GETFILE\nstruct kinfo_file *kinfo_getfile(pid_t pid, int *cnt);\n#endif\n\nint psutil_kinfo_proc(pid_t pid, void *proc);\nvoid convert_kvm_err(const char *syscall, char *errbuf);\nint is_zombie(size_t pid);\n\nPyObject *psutil_boot_time(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_disk_partitions(PyObject *self, PyObject *args);\nPyObject *psutil_heap_info(PyObject *self, PyObject *args);\nPyObject *psutil_heap_trim(PyObject *self, PyObject *args);\nPyObject *psutil_net_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_proc_environ(PyObject *self, PyObject *args);\nPyObject *psutil_proc_name(PyObject *self, PyObject *args);\nPyObject *psutil_proc_oneshot_kinfo(PyObject *self, PyObject *args);\nPyObject *psutil_proc_open_files(PyObject *self, PyObject *args);\n#if defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)\nPyObject *psutil_swap_mem(PyObject *self, PyObject *args);\n#endif\n"
  },
  {
    "path": "psutil/arch/bsd/mem.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include \"../../arch/all/init.h\"\n\n#if defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)\n#include <Python.h>\n#include <sys/swap.h>\n#include <uvm/uvm_extern.h>\n#include <sys/sysctl.h>\n#include <sys/param.h>\n\n\nPyObject *\npsutil_swap_mem(PyObject *self, PyObject *args) {\n    uint64_t swap_total = 0;\n    uint64_t swap_free = 0;\n    uint64_t swap_used = 0;\n    uint64_t sin = 0;\n    uint64_t sout = 0;\n    double percent = 0.0;\n    struct swapent *swdev = NULL;\n    int nswap, i;\n    long pagesize = psutil_getpagesize();\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    nswap = swapctl(SWAP_NSWAP, 0, 0);\n    if (nswap == 0)  // this means there's no swap partition\n        goto done;\n\n    swdev = calloc(nswap, sizeof(*swdev));\n    if (swdev == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n\n    if (swapctl(SWAP_STATS, swdev, nswap) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    // Total things up.\n    for (i = 0; i < nswap; i++) {\n        if (swdev[i].se_flags & SWF_ENABLE) {\n            swap_total += (uint64_t)swdev[i].se_nblks * DEV_BSIZE;\n            swap_free += (uint64_t)(swdev[i].se_nblks - swdev[i].se_inuse)\n                         * DEV_BSIZE;\n        }\n    }\n\n#if defined(PSUTIL_NETBSD)\n    // Get swap in/out\n    struct uvmexp_sysctl uv;\n    int mib[] = {CTL_VM, VM_UVMEXP2};\n    if (psutil_sysctl(mib, 2, &uv, sizeof(uv)) != 0)\n        goto error;\n    sin = (uint64_t)uv.pgswapin * pagesize;\n    sout = (uint64_t)uv.pgswapout * pagesize;\n#endif\n\n    free(swdev);\n\n    swap_used = swap_total - swap_free;\n    percent = psutil_usage_percent((double)swap_used, (double)swap_total, 1);\n\ndone:\n    if (!(pydict_add(dict, \"total\", \"K\", swap_total)\n          | pydict_add(dict, \"used\", \"K\", swap_used)\n          | pydict_add(dict, \"free\", \"K\", swap_free)\n          | pydict_add(dict, \"percent\", \"d\", percent)\n          | pydict_add(dict, \"sin\", \"K\", sin)\n          | pydict_add(dict, \"sout\", \"K\", sout)))\n        goto error;\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    free(swdev);\n    return NULL;\n}\n#endif  // PSUTIL_OPENBSD || PSUTIL_NETBSD\n"
  },
  {
    "path": "psutil/arch/bsd/net.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <string.h>\n#include <net/if.h>\n#include <net/if_dl.h>\n#include <net/route.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_net_io_counters(PyObject *self, PyObject *args) {\n    char *buf = NULL, *lim, *next;\n    struct if_msghdr *ifm;\n    int mib[6];\n    size_t len;\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_ifc_info = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    mib[0] = CTL_NET;  // networking subsystem\n    mib[1] = PF_ROUTE;  // type of information\n    mib[2] = 0;  // protocol (IPPROTO_xxx)\n    mib[3] = 0;  // address family\n    mib[4] = NET_RT_IFLIST;  // operation\n    mib[5] = 0;\n\n    if (psutil_sysctl_malloc(mib, 6, &buf, &len) != 0)\n        goto error;\n\n    lim = buf + len;\n\n    for (next = buf; next < lim;) {\n        py_ifc_info = NULL;\n        ifm = (struct if_msghdr *)next;\n        next += ifm->ifm_msglen;\n\n        if (ifm->ifm_type == RTM_IFINFO) {\n            struct if_msghdr *if2m = (struct if_msghdr *)ifm;\n            struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);\n            char ifc_name[32];\n\n            strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);\n            ifc_name[sdl->sdl_nlen] = '\\0';\n\n            // XXX: ignore usbus interfaces:\n            // http://lists.freebsd.org/pipermail/freebsd-current/\n            //     2011-October/028752.html\n            // 'ifconfig -a' doesn't show them, nor do we.\n            if (strncmp(ifc_name, \"usbus\", 5) == 0)\n                continue;\n\n            py_ifc_info = Py_BuildValue(\n                \"(kkkkkkki)\",\n                if2m->ifm_data.ifi_obytes,\n                if2m->ifm_data.ifi_ibytes,\n                if2m->ifm_data.ifi_opackets,\n                if2m->ifm_data.ifi_ipackets,\n                if2m->ifm_data.ifi_ierrors,\n                if2m->ifm_data.ifi_oerrors,\n                if2m->ifm_data.ifi_iqdrops,\n#ifdef _IFI_OQDROPS\n                if2m->ifm_data.ifi_oqdrops\n#else\n                0\n#endif\n            );\n            if (!py_ifc_info)\n                goto error;\n            if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info) != 0)\n                goto error;\n            Py_CLEAR(py_ifc_info);\n        }\n    }\n\n    free(buf);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_ifc_info);\n    Py_DECREF(py_retdict);\n    free(buf);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/bsd/proc.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <kvm.h>\n#include <sys/proc.h>\n#include <sys/sysctl.h>\n#include <sys/types.h>\n#include <sys/file.h>\n#include <sys/vnode.h>\n#ifdef PSUTIL_FREEBSD\n#include <sys/user.h>\n#include <libutil.h>\n#endif\n\n#include \"../../arch/all/init.h\"\n\n\n/*\n * Collect different info about a process in one shot and return\n * them as a Python dict.\n */\nPyObject *\npsutil_proc_oneshot_kinfo(PyObject *self, PyObject *args) {\n    pid_t pid;\n    long rss;\n    long vms;\n    long memtext;\n    long memdata;\n    long memstack;\n    long peak_rss;\n    int oncpu;\n#ifdef PSUTIL_NETBSD\n    struct kinfo_proc2 kp;\n#else\n    struct kinfo_proc kp;\n#endif\n    long pagesize = psutil_getpagesize();\n    char name_buf[256];  // buffer for process name\n    PyObject *py_name = NULL;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_kinfo_proc(pid, &kp) == -1)\n        goto error;\n\n        // Process name\n#ifdef PSUTIL_FREEBSD\n    str_format(name_buf, sizeof(name_buf), \"%s\", kp.ki_comm);\n#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)\n    str_format(name_buf, sizeof(name_buf), \"%s\", kp.p_comm);\n#endif\n    py_name = PyUnicode_DecodeFSDefault(name_buf);\n    if (!py_name) {\n        // If decoding fails, fall back to None safely\n        PyErr_Clear();\n        py_name = Py_None;\n        Py_INCREF(py_name);\n    }\n\n    // Calculate memory.\n#ifdef PSUTIL_FREEBSD\n    rss = (long)kp.ki_rssize * pagesize;\n    vms = (long)kp.ki_size;\n    memtext = (long)kp.ki_tsize * pagesize;\n    memdata = (long)kp.ki_dsize * pagesize;\n    memstack = (long)kp.ki_ssize * pagesize;\n    peak_rss = kp.ki_rusage.ru_maxrss * 1024;  // expressed in KB\n#else\n    rss = (long)kp.p_vm_rssize * pagesize;\n    peak_rss = kp.p_uru_maxrss * 1024;  // expressed in KB\n#ifdef PSUTIL_OPENBSD\n    // VMS, this is how ps determines it on OpenBSD:\n    // https://github.com/openbsd/src/blob/\n    //     588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L505\n    vms = (long)(kp.p_vm_dsize + kp.p_vm_ssize + kp.p_vm_tsize) * pagesize;\n#elif PSUTIL_NETBSD\n    // VMS, this is how top determines it on NetBSD:\n    // https://github.com/IIJ-NetBSD/netbsd-src/blob/master/external/\n    //     bsd/top/dist/machine/m_netbsd.c\n    vms = (long)kp.p_vm_msize * pagesize;\n#endif\n    memtext = (long)kp.p_vm_tsize * pagesize;\n    memdata = (long)kp.p_vm_dsize * pagesize;\n    memstack = (long)kp.p_vm_ssize * pagesize;\n#endif\n\n    // kernel doesn't always update peak_rss atomically, so rss can\n    // briefly exceed it. Difference is almost always 16KB. peak_rss is\n    // 0 for kernel/root PIDs: we leave it as-is so the caller knows it\n    // can't be relied upon.\n    if ((peak_rss < rss && peak_rss != 0)) {\n        psutil_debug(\n            \"pid: %ld, ru_maxrss (%ld KB) < rss (%ld KB); using rss as \"\n            \"peak_rss\",\n            (long)pid,\n            peak_rss / 1024,\n            rss / 1024\n        );\n        peak_rss = rss;  // use rss as peak_rss\n    }\n\n#ifdef PSUTIL_FREEBSD\n    // what CPU we're on; top was used as an example:\n    // https://svnweb.freebsd.org/base/head/usr.bin/top/machine.c?\n    //     view=markup&pathrev=273835\n    // XXX - note: for \"intr\" PID this is -1.\n    if (kp.ki_stat == SRUN && kp.ki_oncpu != NOCPU)\n        oncpu = kp.ki_oncpu;\n    else\n        oncpu = kp.ki_lastcpu;\n#else\n    // On Net/OpenBSD we have kp.p_cpuid but it appears it's always\n    // set to KI_NOCPU. Even if it's not, ki_lastcpu does not exist\n    // so there's no way to determine where \"sleeping\" processes\n    // were. Not supported.\n    oncpu = -1;\n#endif\n\n        // clang-format off\n\n#ifdef PSUTIL_FREEBSD\n    if (!pydict_add(dict, \"ppid\", _Py_PARSE_PID, kp.ki_ppid)) goto error;\n    if (!pydict_add(dict, \"status\", \"i\", (int)kp.ki_stat)) goto error;\n    if (!pydict_add(dict, \"real_uid\", \"l\", (long)kp.ki_ruid)) goto error;\n    if (!pydict_add(dict, \"effective_uid\", \"l\", (long)kp.ki_uid)) goto error;\n    if (!pydict_add(dict, \"saved_uid\", \"l\", (long)kp.ki_svuid)) goto error;\n    if (!pydict_add(dict, \"real_gid\", \"l\", (long)kp.ki_rgid)) goto error;\n    if (!pydict_add(dict, \"effective_gid\", \"l\", (long)kp.ki_groups[0])) goto error;\n    if (!pydict_add(dict, \"saved_gid\", \"l\", (long)kp.ki_svuid)) goto error;\n    if (!pydict_add(dict, \"ttynr\", \"L\", (unsigned long long)kp.ki_tdev)) goto error;\n    if (!pydict_add(dict, \"create_time\", \"d\", PSUTIL_TV2DOUBLE(kp.ki_start))) goto error;\n    if (!pydict_add(dict, \"ctx_switches_vol\", \"l\", kp.ki_rusage.ru_nvcsw)) goto error;\n    if (!pydict_add(dict, \"ctx_switches_unvol\", \"l\", kp.ki_rusage.ru_nivcsw)) goto error;\n    if (!pydict_add(dict, \"read_io_count\", \"l\", kp.ki_rusage.ru_inblock)) goto error;\n    if (!pydict_add(dict, \"write_io_count\", \"l\", kp.ki_rusage.ru_oublock)) goto error;\n    if (!pydict_add(dict, \"user_time\", \"d\", PSUTIL_TV2DOUBLE(kp.ki_rusage.ru_utime))) goto error;\n    if (!pydict_add(dict, \"sys_time\", \"d\", PSUTIL_TV2DOUBLE(kp.ki_rusage.ru_stime))) goto error;\n    if (!pydict_add(dict, \"ch_user_time\", \"d\", PSUTIL_TV2DOUBLE(kp.ki_rusage_ch.ru_utime))) goto error;\n    if (!pydict_add(dict, \"ch_sys_time\", \"d\", PSUTIL_TV2DOUBLE(kp.ki_rusage_ch.ru_stime))) goto error;\n    if (!pydict_add(dict, \"min_faults\", \"l\", (long)kp.ki_rusage.ru_minflt)) goto error;\n    if (!pydict_add(dict, \"maj_faults\", \"l\", (long)kp.ki_rusage.ru_majflt)) goto error;\n#else\n    // OpenBSD / NetBSD\n    if (!pydict_add(dict, \"ppid\", _Py_PARSE_PID, kp.p_ppid)) goto error;\n    if (!pydict_add(dict, \"status\", \"i\", (int)kp.p_stat)) goto error;\n    if (!pydict_add(dict, \"real_uid\", \"l\", (long)kp.p_ruid)) goto error;\n    if (!pydict_add(dict, \"effective_uid\", \"l\", (long)kp.p_uid)) goto error;\n    if (!pydict_add(dict, \"saved_uid\", \"l\", (long)kp.p_svuid)) goto error;\n    if (!pydict_add(dict, \"real_gid\", \"l\", (long)kp.p_rgid)) goto error;\n    if (!pydict_add(dict, \"effective_gid\", \"l\", (long)kp.p_groups[0])) goto error;\n    if (!pydict_add(dict, \"saved_gid\", \"l\", (long)kp.p_svuid)) goto error;\n    if (!pydict_add(dict, \"ttynr\", \"i\", (int)kp.p_tdev)) goto error;\n    if (!pydict_add(dict, \"create_time\", \"d\", PSUTIL_KPT2DOUBLE(kp.p_ustart))) goto error;\n    if (!pydict_add(dict, \"ctx_switches_vol\", \"l\", kp.p_uru_nvcsw)) goto error;\n    if (!pydict_add(dict, \"ctx_switches_unvol\", \"l\", kp.p_uru_nivcsw)) goto error;\n    if (!pydict_add(dict, \"read_io_count\", \"l\", kp.p_uru_inblock)) goto error;\n    if (!pydict_add(dict, \"write_io_count\", \"l\", kp.p_uru_oublock)) goto error;\n    if (!pydict_add(dict, \"user_time\", \"d\", PSUTIL_KPT2DOUBLE(kp.p_uutime))) goto error;\n    if (!pydict_add(dict, \"sys_time\", \"d\", PSUTIL_KPT2DOUBLE(kp.p_ustime))) goto error;\n    // OpenBSD and NetBSD provide children user + system times summed\n    // together (no distinction).\n    if (!pydict_add(dict, \"ch_user_time\", \"d\", kp.p_uctime_sec + kp.p_uctime_usec / 1000000.0)) goto error;\n    if (!pydict_add(dict, \"ch_sys_time\", \"d\", kp.p_uctime_sec + kp.p_uctime_usec / 1000000.0)) goto error;\n    if (!pydict_add(dict, \"min_faults\", \"l\", (long)kp.p_uru_minflt)) goto error;\n    if (!pydict_add(dict, \"maj_faults\", \"l\", (long)kp.p_uru_majflt)) goto error;\n#endif\n    // all BSDs\n    if (!pydict_add(dict, \"rss\", \"l\", rss)) goto error;\n    if (!pydict_add(dict, \"vms\", \"l\", vms)) goto error;\n    if (!pydict_add(dict, \"memtext\", \"l\", memtext)) goto error;\n    if (!pydict_add(dict, \"memdata\", \"l\", memdata)) goto error;\n    if (!pydict_add(dict, \"memstack\", \"l\", memstack)) goto error;\n    if (!pydict_add(dict, \"peak_rss\", \"l\", peak_rss)) goto error;\n    if (!pydict_add(dict, \"cpunum\", \"i\", oncpu)) goto error;\n    if (!pydict_add(dict, \"name\", \"O\", py_name)) goto error;\n\n    // clang-format on\n    Py_DECREF(py_name);\n    return dict;\n\nerror:\n    Py_XDECREF(py_name);\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_name(PyObject *self, PyObject *args) {\n    pid_t pid;\n#ifdef PSUTIL_NETBSD\n    struct kinfo_proc2 kp;\n#else\n    struct kinfo_proc kp;\n#endif\n    char str[1000];\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_kinfo_proc(pid, &kp) == -1)\n        return NULL;\n\n#ifdef PSUTIL_FREEBSD\n    str_format(str, sizeof(str), \"%s\", kp.ki_comm);\n#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)\n    str_format(str, sizeof(str), \"%s\", kp.p_comm);\n#endif\n    return PyUnicode_DecodeFSDefault(str);\n}\n\n\nPyObject *\npsutil_proc_environ(PyObject *self, PyObject *args) {\n    int i, cnt = -1;\n    long pid;\n    char *s, **envs, errbuf[_POSIX2_LINE_MAX];\n    PyObject *py_value = NULL, *py_retdict = NULL;\n    kvm_t *kd;\n#ifdef PSUTIL_NETBSD\n    struct kinfo_proc2 *p;\n#else\n    struct kinfo_proc *p;\n#endif\n\n    if (!PyArg_ParseTuple(args, \"l\", &pid))\n        return NULL;\n\n#if defined(PSUTIL_FREEBSD)\n    kd = kvm_openfiles(NULL, \"/dev/null\", NULL, 0, errbuf);\n#else\n    kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);\n#endif\n    if (!kd) {\n        convert_kvm_err(\"kvm_openfiles\", errbuf);\n        return NULL;\n    }\n\n    py_retdict = PyDict_New();\n    if (!py_retdict)\n        goto error;\n\n#if defined(PSUTIL_FREEBSD)\n    p = kvm_getprocs(kd, KERN_PROC_PID, pid, &cnt);\n#elif defined(PSUTIL_OPENBSD)\n    p = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(*p), &cnt);\n#elif defined(PSUTIL_NETBSD)\n    p = kvm_getproc2(kd, KERN_PROC_PID, pid, sizeof(*p), &cnt);\n#endif\n    if (!p) {\n        psutil_oserror_nsp(\"kvm_getprocs\");\n        goto error;\n    }\n    if (cnt <= 0) {\n        psutil_oserror_nsp(cnt < 0 ? kvm_geterr(kd) : \"kvm_getprocs: cnt==0\");\n        goto error;\n    }\n\n    // On *BSD kernels there are a few kernel-only system processes without an\n    // environment (See e.g. \"procstat -e 0 | 1 | 2 ...\" on FreeBSD.)\n    // Some system process have no stats attached at all\n    // (they are marked with P_SYSTEM.)\n    // On FreeBSD, it's possible that the process is swapped or paged out,\n    // then there no access to the environ stored in the process' user area.\n    // On NetBSD, we cannot call kvm_getenvv2() for a zombie process.\n    // To make unittest suite happy, return an empty environment.\n#if defined(PSUTIL_FREEBSD)\n    if (!((p)->ki_flag & P_INMEM) || ((p)->ki_flag & P_SYSTEM)) {\n#elif defined(PSUTIL_NETBSD)\n    if ((p)->p_stat == SZOMB) {\n#elif defined(PSUTIL_OPENBSD)\n    if ((p)->p_flag & P_SYSTEM) {\n#endif\n        kvm_close(kd);\n        return py_retdict;\n    }\n\n#if defined(PSUTIL_NETBSD)\n    envs = kvm_getenvv2(kd, p, 0);\n#else\n    envs = kvm_getenvv(kd, p, 0);\n#endif\n    if (!envs) {\n        // Map to \"psutil\" general high-level exceptions\n        switch (errno) {\n            case 0:\n                // Process has cleared it's environment, return empty one\n                kvm_close(kd);\n                return py_retdict;\n            case EPERM:\n                psutil_oserror_ad(\"kvm_getenvv -> EPERM\");\n                break;\n            case ESRCH:\n                psutil_oserror_nsp(\"kvm_getenvv -> ESRCH\");\n                break;\n#if defined(PSUTIL_FREEBSD)\n            case ENOMEM:\n                // Unfortunately, under FreeBSD kvm_getenvv() returns\n                // failure for certain processes ( e.g. try\n                // \"sudo procstat -e <pid of your XOrg server>\".)\n                // Map the error condition to 'AccessDenied'.\n                str_format(\n                    errbuf,\n                    sizeof(errbuf),\n                    \"kvm_getenvv(pid=%ld, ki_uid=%d) -> ENOMEM\",\n                    pid,\n                    p->ki_uid\n                );\n                psutil_oserror_ad(errbuf);\n                break;\n#endif\n            default:\n                str_format(\n                    errbuf, sizeof(errbuf), \"kvm_getenvv(pid=%ld)\", pid\n                );\n                psutil_oserror_wsyscall(errbuf);\n                break;\n        }\n        goto error;\n    }\n\n    for (i = 0; envs[i] != NULL; i++) {\n        s = strchr(envs[i], '=');\n        if (!s)\n            continue;\n        *s++ = 0;\n        py_value = PyUnicode_DecodeFSDefault(s);\n        if (!py_value)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, envs[i], py_value)) {\n            goto error;\n        }\n        Py_DECREF(py_value);\n    }\n\n    kvm_close(kd);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_value);\n    Py_XDECREF(py_retdict);\n    kvm_close(kd);\n    return NULL;\n}\n\n\n/*\n * Return files opened by process as a list of (path, fd) tuples.\n * TODO: this is broken as it may report empty paths. 'procstat'\n * utility has the same problem see:\n * https://github.com/giampaolo/psutil/issues/595\n */\nPyObject *\npsutil_proc_open_files(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int i;\n    int cnt;\n    int regular;\n    int fd;\n    char *path;\n    struct kinfo_file *freep = NULL;\n    struct kinfo_file *kif;\n#ifdef PSUTIL_NETBSD\n    struct kinfo_proc2 kipp;\n#else\n    struct kinfo_proc kipp;\n#endif\n    PyObject *py_path = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_kinfo_proc(pid, &kipp) == -1)\n        goto error;\n\n    errno = 0;\n    freep = kinfo_getfile(pid, &cnt);\n\n    if (freep == NULL) {\n#if defined(PSUTIL_OPENBSD)\n        if ((pid == 0) && (errno == ESRCH)) {\n            psutil_debug(\n                \"open_files() returned ESRCH for PID 0; forcing `return []`\"\n            );\n            PyErr_Clear();\n            return py_retlist;\n        }\n#else\n        psutil_raise_for_pid(pid, \"kinfo_getfile()\");\n#endif\n        goto error;\n    }\n\n    for (i = 0; i < cnt; i++) {\n        kif = &freep[i];\n\n#ifdef PSUTIL_FREEBSD\n        regular = (kif->kf_type == KF_TYPE_VNODE)\n                  && (kif->kf_vnode_type == KF_VTYPE_VREG);\n        fd = kif->kf_fd;\n        path = kif->kf_path;\n#elif PSUTIL_OPENBSD\n        regular = (kif->f_type == DTYPE_VNODE) && (kif->v_type == VREG);\n        fd = kif->fd_fd;\n        // XXX - it appears path is not exposed in the kinfo_file struct.\n        path = \"\";\n#elif PSUTIL_NETBSD\n        regular = (kif->ki_ftype == DTYPE_VNODE) && (kif->ki_vtype == VREG);\n        fd = kif->ki_fd;\n        // XXX - it appears path is not exposed in the kinfo_file struct.\n        path = \"\";\n#endif\n        if (regular == 1) {\n            py_path = PyUnicode_DecodeFSDefault(path);\n            if (!py_path)\n                goto error;\n            if (!pylist_append_fmt(py_retlist, \"(Oi)\", py_path, fd))\n                goto error;\n            Py_CLEAR(py_path);\n        }\n    }\n    free(freep);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_path);\n    Py_DECREF(py_retlist);\n    if (freep != NULL)\n        free(freep);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/bsd/proc_utils.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <errno.h>\n#include <limits.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <sys/proc.h>\n#include <limits.h>\n#include <unistd.h>\n#ifdef PSUTIL_FREEBSD\n#include <sys/user.h>\n#endif\n\n#include \"../../arch/all/init.h\"\n\n\n// Fills a kinfo_proc or kinfo_proc2 struct based on process PID.\nint\npsutil_kinfo_proc(pid_t pid, void *proc) {\n#if defined(PSUTIL_FREEBSD)\n    size_t size = sizeof(struct kinfo_proc);\n    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};\n    int len = 4;\n#elif defined(PSUTIL_OPENBSD)\n    size_t size = sizeof(struct kinfo_proc);\n    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, (int)size, 1};\n    int len = 6;\n#elif defined(PSUTIL_NETBSD)\n    size_t size = sizeof(struct kinfo_proc2);\n    int mib[] = {CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid, (int)size, 1};\n    int len = 6;\n#else\n#error \"unsupported BSD variant\"\n#endif\n\n    if (pid < 0 || proc == NULL)\n        return psutil_badargs(\"psutil_kinfo_proc\");\n\n    if (sysctl(mib, len, proc, &size, NULL, 0) == -1) {\n        psutil_oserror_wsyscall(\"sysctl(kinfo_proc)\");\n        return -1;\n    }\n\n    // sysctl stores 0 in size if the process doesn't exist.\n    if (size == 0) {\n        psutil_oserror_nsp(\"sysctl(kinfo_proc), size = 0\");\n        return -1;\n    }\n\n    return 0;\n}\n\n\n// Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an\n// int as arg and returns an array with cnt struct kinfo_file.\n// Caller is responsible for freeing the returned pointer with free().\n#ifdef PSUTIL_HASNT_KINFO_GETFILE\nstruct kinfo_file *\nkinfo_getfile(pid_t pid, int *cnt) {\n    if (pid < 0 || !cnt) {\n        psutil_badargs(\"kinfo_getfile\");\n        return NULL;\n    }\n\n    int mib[6];\n    size_t len;\n    struct kinfo_file *kf = NULL;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_FILE;\n    mib[2] = KERN_FILE_BYPID;\n    mib[3] = pid;\n    mib[4] = sizeof(struct kinfo_file);\n    mib[5] = 0;\n\n    if (psutil_sysctl_malloc(mib, 6, (char **)&kf, &len) != 0) {\n        return NULL;\n    }\n\n    // Calculate number of entries and check for overflow\n    if (len / sizeof(struct kinfo_file) > INT_MAX) {\n        psutil_debug(\"exceeded INT_MAX\");\n        free(kf);\n        errno = EOVERFLOW;\n        return NULL;\n    }\n\n    *cnt = (int)(len / sizeof(struct kinfo_file));\n    return kf;\n}\n#endif  // PSUTIL_HASNT_KINFO_GETFILE\n\n\nint\nis_zombie(size_t pid) {\n#ifdef PSUTIL_NETBSD\n    struct kinfo_proc2 kp;\n#else\n    struct kinfo_proc kp;\n#endif\n    if (psutil_kinfo_proc(pid, &kp) == -1) {\n        errno = 0;\n        PyErr_Clear();\n        return 0;\n    }\n\n#if defined(PSUTIL_FREEBSD)\n    return kp.ki_stat == SZOMB;\n#elif defined(PSUTIL_OPENBSD)\n    // According to /usr/include/sys/proc.h SZOMB is unused.\n    // test_zombie_process() shows that SDEAD is the right\n    // equivalent.\n    return ((kp.p_stat == SZOMB) || (kp.p_stat == SDEAD));\n#else\n    return kp.p_stat == SZOMB;\n#endif\n}\n"
  },
  {
    "path": "psutil/arch/bsd/sys.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// Return a Python float indicating the system boot time expressed in\n// seconds since the epoch.\nPyObject *\npsutil_boot_time(PyObject *self, PyObject *args) {\n    // fetch sysctl \"kern.boottime\"\n    static int request[2] = {CTL_KERN, KERN_BOOTTIME};\n    struct timeval boottime;\n\n    if (psutil_sysctl(request, 2, &boottime, sizeof(boottime)) != 0)\n        return NULL;\n    return Py_BuildValue(\"d\", (double)boottime.tv_sec);\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\nSystem-wide CPU related functions.\nOriginal code was refactored and moved from psutil/arch/freebsd/specific.c\nin 2020 (and was moved in there previously already) from cset.\na4c0a0eb0d2a872ab7a45e47fcf37ef1fde5b012\nFor reference, here's the git history with original(ish) implementations:\n- CPU stats: fb0154ef164d0e5942ac85102ab660b8d2938fbb\n- CPU freq: 459556dd1e2979cdee22177339ced0761caf4c83\n- CPU cores: e0d6d7865df84dc9a1d123ae452fd311f79b1dde\n*/\n\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <devstat.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    int maxcpus;\n    int mib[2];\n    int ncpu;\n    size_t size;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    // retrieve the number of CPUs currently online\n    mib[0] = CTL_HW;\n    mib[1] = HW_NCPU;\n    if (psutil_sysctl(mib, 2, &ncpu, sizeof(ncpu)) != 0) {\n        goto error;\n    }\n\n    // allocate buffer dynamically based on actual CPU count\n    long(*cpu_time)[CPUSTATES] = malloc(ncpu * sizeof(*cpu_time));\n    if (!cpu_time) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    // get per-cpu times using ncpu count\n    size = ncpu * sizeof(*cpu_time);\n    if (psutil_sysctlbyname(\"kern.cp_times\", cpu_time, size) == -1) {\n        free(cpu_time);\n        goto error;\n    }\n\n    for (int i = 0; i < ncpu; i++) {\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(ddddd)\",\n                (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,\n                (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,\n                (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,\n                (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,\n                (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC\n            ))\n        {\n            free(cpu_time);\n            goto error;\n        }\n    }\n\n    free(cpu_time);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n\n\nPyObject *\npsutil_cpu_topology(PyObject *self, PyObject *args) {\n    char *topology = NULL;\n    size_t size = 0;\n    PyObject *py_str;\n\n    if (psutil_sysctlbyname_malloc(\n            \"kern.sched.topology_spec\", &topology, &size\n        )\n        != 0)\n    {\n        psutil_debug(\"ignore sysctlbyname('kern.sched.topology_spec') error\");\n        Py_RETURN_NONE;\n    }\n\n    py_str = PyUnicode_FromString(topology);\n    free(topology);\n    return py_str;\n}\n\n\nPyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    unsigned int v_soft;\n    unsigned int v_intr;\n    unsigned int v_syscall;\n    unsigned int v_trap;\n    unsigned int v_swtch;\n    size_t size = sizeof(v_soft);\n\n    if (psutil_sysctlbyname(\"vm.stats.sys.v_soft\", &v_soft, size) != 0)\n        return NULL;\n    if (psutil_sysctlbyname(\"vm.stats.sys.v_intr\", &v_intr, size) != 0)\n        return NULL;\n    if (psutil_sysctlbyname(\"vm.stats.sys.v_syscall\", &v_syscall, size) != 0)\n        return NULL;\n    if (psutil_sysctlbyname(\"vm.stats.sys.v_trap\", &v_trap, size) != 0)\n        return NULL;\n    if (psutil_sysctlbyname(\"vm.stats.sys.v_swtch\", &v_swtch, size) != 0)\n        return NULL;\n\n    return Py_BuildValue(\n        \"IIIII\",\n        v_swtch,  // ctx switches\n        v_intr,  // interrupts\n        v_soft,  // software interrupts\n        v_syscall,  // syscalls\n        v_trap  // traps\n    );\n}\n\n\n/*\n * Return frequency information of a given CPU.\n * As of Dec 2018 only CPU 0 appears to be supported and all other\n * cores match the frequency of CPU 0.\n */\nPyObject *\npsutil_cpu_freq(PyObject *self, PyObject *args) {\n    int current;\n    int core;\n    char sensor[26];\n    char available_freq_levels[1000];\n    size_t size;\n\n    if (!PyArg_ParseTuple(args, \"i\", &core))\n        return NULL;\n\n    // https://www.unix.com/man-page/FreeBSD/4/cpufreq/\n    size = sizeof(current);\n    str_format(sensor, sizeof(sensor), \"dev.cpu.%d.freq\", core);\n    if (psutil_sysctlbyname(sensor, &current, size) != 0)\n        goto error;\n\n    // In case of failure, an empty string is returned.\n    size = sizeof(available_freq_levels);\n    str_format(sensor, sizeof(sensor), \"dev.cpu.%d.freq_levels\", core);\n    if (psutil_sysctlbyname(sensor, &available_freq_levels, size) != 0)\n        psutil_debug(\"cpu freq levels failed (ignored)\");\n\n    return Py_BuildValue(\"is\", current, available_freq_levels);\n\nerror:\n    if (errno == ENOENT)\n        PyErr_SetString(PyExc_NotImplementedError, \"unable to read frequency\");\n    else\n        psutil_oserror();\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/disk.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <devstat.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// convert a bintime struct to milliseconds\n#define PSUTIL_BT2MSEC(bt) \\\n    (bt.sec * 1000         \\\n     + (((uint64_t)1000000000 * (uint32_t)(bt.frac >> 32)) >> 32) / 1000000)\n\n\nPyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    int i;\n    struct statinfo stats;\n\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_disk_info = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n    if (devstat_checkversion(NULL) < 0) {\n        psutil_runtime_error(\"devstat_checkversion() syscall failed\");\n        goto error;\n    }\n\n    stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));\n    if (stats.dinfo == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n    bzero(stats.dinfo, sizeof(struct devinfo));\n\n    if (devstat_getdevs(NULL, &stats) == -1) {\n        psutil_runtime_error(\"devstat_getdevs() syscall failed\");\n        goto error;\n    }\n\n    for (i = 0; i < stats.dinfo->numdevs; i++) {\n        py_disk_info = NULL;\n        struct devstat current;\n        char disk_name[128];\n        current = stats.dinfo->devices[i];\n        str_format(\n            disk_name,\n            sizeof(disk_name),\n            \"%s%d\",\n            current.device_name,\n            current.unit_number\n        );\n\n        py_disk_info = Py_BuildValue(\n            \"(KKKKLLL)\",\n            current.operations[DEVSTAT_READ],  // no reads\n            current.operations[DEVSTAT_WRITE],  // no writes\n            current.bytes[DEVSTAT_READ],  // bytes read\n            current.bytes[DEVSTAT_WRITE],  // bytes written\n            (long long)PSUTIL_BT2MSEC(current.duration[DEVSTAT_READ]\n            ),  // r time\n            (long long)PSUTIL_BT2MSEC(current.duration[DEVSTAT_WRITE]\n            ),  // w time\n            (long long)PSUTIL_BT2MSEC(current.busy_time)  // busy time\n        );  // finished transactions\n        if (!py_disk_info)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))\n            goto error;\n        Py_DECREF(py_disk_info);\n    }\n\n    if (stats.dinfo->mem_ptr)\n        free(stats.dinfo->mem_ptr);\n    free(stats.dinfo);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_disk_info);\n    Py_DECREF(py_retdict);\n    if (stats.dinfo != NULL)\n        free(stats.dinfo);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Jay Loden.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/types.h>\n\nint _psutil_pids(pid_t **pids_array, int *pids_count);\n\nPyObject *psutil_cpu_freq(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_stats(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_topology(PyObject *self, PyObject *args);\nPyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cmdline(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cwd(PyObject *self, PyObject *args);\nPyObject *psutil_proc_exe(PyObject *self, PyObject *args);\nPyObject *psutil_proc_getrlimit(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_maps(PyObject *self, PyObject *args);\nPyObject *psutil_proc_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_threads(PyObject *self, PyObject *args);\nPyObject *psutil_proc_setrlimit(PyObject *self, PyObject *args);\nPyObject *psutil_proc_threads(PyObject *self, PyObject *args);\nPyObject *psutil_sensors_battery(PyObject *self, PyObject *args);\nPyObject *psutil_sensors_cpu_temperature(PyObject *self, PyObject *args);\nPyObject *psutil_swap_mem(PyObject *self, PyObject *args);\nPyObject *psutil_virtual_mem(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/freebsd/mem.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <kvm.h>\n#include <sys/sysctl.h>\n#include <sys/vmmeter.h>\n#include <vm/vm_param.h>\n#include <paths.h>\n#include <fcntl.h>\n\n#include \"../../arch/all/init.h\"\n\n#ifndef _PATH_DEVNULL\n#define _PATH_DEVNULL \"/dev/null\"\n#endif\n\n\n// shorter alias for psutil_sysctlbyname()\nstatic inline int\nsbn(const char *name, void *oldp, size_t oldlen) {\n    return psutil_sysctlbyname(name, oldp, oldlen);\n}\n\n\nPyObject *\npsutil_virtual_mem(PyObject *self, PyObject *args) {\n    unsigned long _total;\n    long _buffers;\n    unsigned int _active, _inactive, _wired, _cached, _free;\n    unsigned long long total, buffers, active, inactive, wired, cached, free;\n    unsigned long long avail, used, shared;\n    double percent;\n    struct vmtotal vm;\n    int mib[] = {CTL_VM, VM_METER};\n    long pagesize = psutil_getpagesize();\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    if (sbn(\"hw.physmem\", &_total, sizeof(_total)) != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_active_count\", &_active, sizeof(_active)) != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_inactive_count\", &_inactive, sizeof(_inactive))\n        != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_wire_count\", &_wired, sizeof(_wired)) != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_free_count\", &_free, sizeof(_free)) != 0)\n        goto error;\n    if (sbn(\"vfs.bufspace\", &_buffers, sizeof(_buffers)) != 0)\n        goto error;\n\n    // Optional; ignore error if not avail\n    if (sbn(\"vm.stats.vm.v_cache_count\", &_cached, sizeof(_cached)) != 0) {\n        PyErr_Clear();\n        _cached = 0;\n    }\n\n    if (psutil_sysctl(mib, 2, &vm, sizeof(vm)) != 0)\n        goto error;\n\n    total = (unsigned long long)_total;\n    buffers = (unsigned long long)_buffers;\n    free = (unsigned long long)_free * pagesize;\n    active = (unsigned long long)_active * pagesize;\n    inactive = (unsigned long long)_inactive * pagesize;\n    wired = (unsigned long long)_wired * pagesize;\n    cached = (unsigned long long)_cached * pagesize;\n    shared = (unsigned long long)(vm.t_vmshr + vm.t_rmshr) * pagesize;\n\n    // matches freebsd-memory CLI:\n    // * https://people.freebsd.org/~rse/dist/freebsd-memory\n    // * https://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt\n    // matches zabbix:\n    // * https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/freebsd/memory.c#L143\n    avail = (inactive + cached + free);\n    used = (active + wired + cached);\n    percent = psutil_usage_percent((double)(total - avail), (double)total, 1);\n\n    if (!(pydict_add(dict, \"total\", \"K\", total)\n          | pydict_add(dict, \"available\", \"K\", avail)\n          | pydict_add(dict, \"percent\", \"d\", percent)\n          | pydict_add(dict, \"used\", \"K\", used)\n          | pydict_add(dict, \"free\", \"K\", free)\n          | pydict_add(dict, \"active\", \"K\", active)\n          | pydict_add(dict, \"inactive\", \"K\", inactive)\n          | pydict_add(dict, \"buffers\", \"K\", buffers)\n          | pydict_add(dict, \"cached\", \"K\", cached)\n          | pydict_add(dict, \"shared\", \"K\", shared)\n          | pydict_add(dict, \"wired\", \"K\", wired)))\n        goto error;\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\nPyObject *\npsutil_swap_mem(PyObject *self, PyObject *args) {\n    // Return swap memory stats (see 'swapinfo' cmdline tool)\n    kvm_t *kd;\n    struct kvm_swap kvmsw[1];\n    unsigned long long total, used, free;\n    unsigned int swapin, swapout, nodein, nodeout;\n    long pagesize = psutil_getpagesize();\n    double percent;\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, \"kvm_open failed\");\n    if (kd == NULL) {\n        psutil_runtime_error(\"kvm_open() syscall failed\");\n        goto error;\n    }\n\n    if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {\n        kvm_close(kd);\n        psutil_runtime_error(\"kvm_getswapinfo() syscall failed\");\n        goto error;\n    }\n\n    kvm_close(kd);\n\n    if (sbn(\"vm.stats.vm.v_swapin\", &swapin, sizeof(swapin)) != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_swapout\", &swapout, sizeof(swapout)) != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_vnodein\", &nodein, sizeof(nodein)) != 0)\n        goto error;\n    if (sbn(\"vm.stats.vm.v_vnodeout\", &nodeout, sizeof(nodeout)) != 0)\n        goto error;\n\n    total = (unsigned long long)kvmsw[0].ksw_total * pagesize;\n    used = (unsigned long long)kvmsw[0].ksw_used * pagesize;\n    free = (unsigned long long)(kvmsw[0].ksw_total - kvmsw[0].ksw_used)\n           * pagesize;\n    percent = psutil_usage_percent((double)used, (double)total, 1);\n\n    if (!(pydict_add(dict, \"total\", \"K\", total)\n          | pydict_add(dict, \"used\", \"K\", used)\n          | pydict_add(dict, \"free\", \"K\", free)\n          | pydict_add(dict, \"percent\", \"d\", percent)\n          | pydict_add(dict, \"sin\", \"I\", swapin + swapout)\n          | pydict_add(dict, \"sout\", \"I\", nodein + nodeout)))\n        goto error;\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/pids.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <sys/user.h>\n\n#include \"../../arch/all/init.h\"\n\n\nint\n_psutil_pids(pid_t **pids_array, int *pids_count) {\n    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0};\n    size_t len = 0;\n    char *buf = NULL;\n    struct kinfo_proc *proc_list = NULL;\n    size_t num_procs = 0;\n\n    *pids_array = NULL;\n    *pids_count = 0;\n\n    if (psutil_sysctl_malloc(mib, 4, &buf, &len) != 0)\n        return -1;\n\n    if (len == 0) {\n        psutil_runtime_error(\"no PIDs found\");\n        goto error;\n    }\n\n    proc_list = (struct kinfo_proc *)buf;\n    num_procs = len / sizeof(struct kinfo_proc);\n\n    *pids_array = malloc(num_procs * sizeof(pid_t));\n    if (!*pids_array) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    for (size_t i = 0; i < num_procs; i++) {\n        (*pids_array)[i] = proc_list[i].ki_pid;  // FreeBSD PID field\n    }\n\n    *pids_count = (int)num_procs;\n    free(buf);\n    return 0;\n\nerror:\n    if (buf != NULL)\n        free(buf);\n    return -1;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/proc.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/cpuset.h>\n#include <sys/sysctl.h>\n#include <sys/user.h>\n#include <libutil.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// ============================================================================\n// Utility functions\n// ============================================================================\n\n\n// remove spaces from string\nstatic void\npsutil_remove_spaces(char *str) {\n    char *p1 = str;\n    char *p2 = str;\n    do\n        while (*p2 == ' ')\n            p2++;\n    while ((*p1++ = *p2++));\n}\n\n\n// ============================================================================\n// APIS\n// ============================================================================\n\n/*\n * Borrowed from psi Python System Information project\n * Based on code from ps.\n */\nPyObject *\npsutil_proc_cmdline(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int mib[4];\n    char *procargs = NULL;\n    size_t size = 0;\n    size_t pos = 0;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_ARGS;\n    mib[3] = pid;\n\n    if (psutil_sysctl_malloc(mib, 4, &procargs, &size) != 0)\n        goto error;\n\n    // args are returned as a flattened string with \\0 separators between\n    // arguments add each string to the list then step forward to the next\n    // separator\n    if (size > 0) {\n        while (pos < size) {\n            if (!pylist_append_obj(\n                    py_retlist, PyUnicode_DecodeFSDefault(&procargs[pos])\n                ))\n                goto error;\n            pos += strlen(&procargs[pos]) + 1;\n        }\n    }\n\n    free(procargs);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_retlist);\n    if (procargs != NULL)\n        free(procargs);\n    return NULL;\n}\n\n/*\n * Return process pathname executable.\n * Thanks to Robert N. M. Watson:\n * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT\n */\nPyObject *\npsutil_proc_exe(PyObject *self, PyObject *args) {\n    pid_t pid;\n    char pathname[PATH_MAX];\n    int error;\n    int mib[4];\n    int ret;\n    size_t size;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PATHNAME;\n    mib[3] = pid;\n\n    size = sizeof(pathname);\n    error = sysctl(mib, 4, pathname, &size, NULL, 0);\n    if (error == -1) {\n        // see: https://github.com/giampaolo/psutil/issues/907\n        if (errno == ENOENT) {\n            return PyUnicode_DecodeFSDefault(\"\");\n        }\n        else {\n            return psutil_oserror_wsyscall(\"sysctl(KERN_PROC_PATHNAME)\");\n        }\n    }\n    if (size == 0 || strlen(pathname) == 0) {\n        ret = psutil_pid_exists(pid);\n        if (ret == -1) {\n            psutil_oserror();\n            return NULL;\n        }\n        else if (ret == 0)\n            return psutil_oserror_nsp(\"psutil_pid_exists -> 0\");\n        else\n            str_copy(pathname, sizeof(pathname), \"\");\n    }\n\n    return PyUnicode_DecodeFSDefault(pathname);\n}\n\n\nPyObject *\npsutil_proc_num_threads(PyObject *self, PyObject *args) {\n    // Return number of threads used by process as a Python integer.\n    pid_t pid;\n    struct kinfo_proc kp;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_kinfo_proc(pid, &kp) == -1)\n        return NULL;\n    return Py_BuildValue(\"l\", (long)kp.ki_numthreads);\n}\n\n\nPyObject *\npsutil_proc_threads(PyObject *self, PyObject *args) {\n    // Retrieves all threads used by process returning a list of tuples\n    // including thread id, user time and system time.\n    // Thanks to Robert N. M. Watson:\n    // http://code.metager.de/source/xref/freebsd/usr.bin/procstat/\n    //     procstat_threads.c\n    pid_t pid;\n    int mib[4];\n    struct kinfo_proc *kip = NULL;\n    struct kinfo_proc *kipp = NULL;\n    unsigned int i;\n    size_t size = 0;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    // we need to re-query for thread information, so don't use *kipp\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD;\n    mib[3] = pid;\n\n    if (psutil_sysctl_malloc(mib, 4, (char **)&kip, &size) != 0)\n        goto error;\n\n    // subtle check: size == 0 means no such process\n    if (size == 0) {\n        psutil_oserror_nsp(\"sysctl (size = 0)\");\n        goto error;\n    }\n\n    for (i = 0; i < size / sizeof(*kip); i++) {\n        kipp = &kip[i];\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"Idd\",\n                kipp->ki_tid,\n                PSUTIL_TV2DOUBLE(kipp->ki_rusage.ru_utime),\n                PSUTIL_TV2DOUBLE(kipp->ki_rusage.ru_stime)\n            ))\n        {\n            goto error;\n        }\n    }\n\n    free(kip);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    free(kip);\n    return NULL;\n}\n\nPyObject *\npsutil_proc_cwd(PyObject *self, PyObject *args) {\n    pid_t pid;\n    struct kinfo_file *freep = NULL;\n    struct kinfo_file *kif;\n    struct kinfo_proc kipp;\n    PyObject *py_path = NULL;\n\n    int i, cnt;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_kinfo_proc(pid, &kipp) == -1)\n        goto error;\n\n    errno = 0;\n    freep = kinfo_getfile(pid, &cnt);\n    if (freep == NULL) {\n        psutil_raise_for_pid(pid, \"kinfo_getfile()\");\n        goto error;\n    }\n\n    for (i = 0; i < cnt; i++) {\n        kif = &freep[i];\n        if (kif->kf_fd == KF_FD_TYPE_CWD) {\n            py_path = PyUnicode_DecodeFSDefault(kif->kf_path);\n            if (!py_path)\n                goto error;\n            break;\n        }\n    }\n    /*\n     * For lower pids it seems we can't retrieve any information\n     * (lsof can't do that it either).  Since this happens even\n     * as root we return an empty string instead of AccessDenied.\n     */\n    if (py_path == NULL)\n        py_path = PyUnicode_DecodeFSDefault(\"\");\n    free(freep);\n    return py_path;\n\nerror:\n    Py_XDECREF(py_path);\n    if (freep != NULL)\n        free(freep);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_num_fds(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int cnt;\n\n    struct kinfo_file *freep;\n    struct kinfo_proc kipp;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_kinfo_proc(pid, &kipp) == -1)\n        return NULL;\n\n    errno = 0;\n    freep = kinfo_getfile(pid, &cnt);\n    if (freep == NULL) {\n        psutil_raise_for_pid(pid, \"kinfo_getfile()\");\n        return NULL;\n    }\n    free(freep);\n\n    return Py_BuildValue(\"i\", cnt);\n}\n\n\nPyObject *\npsutil_proc_memory_maps(PyObject *self, PyObject *args) {\n    // Return a list of tuples for every process memory maps.\n    // 'procstat' cmdline utility has been used as an example.\n    pid_t pid;\n    int ptrwidth;\n    int i, cnt;\n    char addr[1000];\n    char perms[4];\n    char *path;\n    struct kinfo_proc kp;\n    struct kinfo_vmentry *freep = NULL;\n    struct kinfo_vmentry *kve;\n    ptrwidth = 2 * sizeof(void *);\n    long pagesize = psutil_getpagesize();\n    PyObject *py_path = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_kinfo_proc(pid, &kp) == -1)\n        goto error;\n\n    errno = 0;\n    freep = kinfo_getvmmap(pid, &cnt);\n    if (freep == NULL) {\n        psutil_raise_for_pid(pid, \"kinfo_getvmmap()\");\n        goto error;\n    }\n    for (i = 0; i < cnt; i++) {\n        kve = &freep[i];\n        addr[0] = '\\0';\n        perms[0] = '\\0';\n        str_format(\n            addr,\n            sizeof(addr),\n            \"%#*jx-%#*jx\",\n            ptrwidth,\n            (uintmax_t)kve->kve_start,\n            ptrwidth,\n            (uintmax_t)kve->kve_end\n        );\n        psutil_remove_spaces(addr);\n        str_append(\n            perms,\n            sizeof(perms),\n            kve->kve_protection & KVME_PROT_READ ? \"r\" : \"-\"\n        );\n        str_append(\n            perms,\n            sizeof(perms),\n            kve->kve_protection & KVME_PROT_WRITE ? \"w\" : \"-\"\n        );\n        str_append(\n            perms,\n            sizeof(perms),\n            kve->kve_protection & KVME_PROT_EXEC ? \"x\" : \"-\"\n        );\n\n        if (strlen(kve->kve_path) == 0) {\n            switch (kve->kve_type) {\n                case KVME_TYPE_NONE:\n                    path = \"[none]\";\n                    break;\n                case KVME_TYPE_DEFAULT:\n                    path = \"[default]\";\n                    break;\n                case KVME_TYPE_VNODE:\n                    path = \"[vnode]\";\n                    break;\n                case KVME_TYPE_SWAP:\n                    path = \"[swap]\";\n                    break;\n                case KVME_TYPE_DEVICE:\n                    path = \"[device]\";\n                    break;\n                case KVME_TYPE_PHYS:\n                    path = \"[phys]\";\n                    break;\n                case KVME_TYPE_DEAD:\n                    path = \"[dead]\";\n                    break;\n#ifdef KVME_TYPE_SG\n                case KVME_TYPE_SG:\n                    path = \"[sg]\";\n                    break;\n#endif\n                case KVME_TYPE_UNKNOWN:\n                    path = \"[unknown]\";\n                    break;\n                default:\n                    path = \"[?]\";\n                    break;\n            }\n        }\n        else {\n            path = kve->kve_path;\n        }\n\n        py_path = PyUnicode_DecodeFSDefault(path);\n        if (!py_path)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"ssOKKii\",\n                addr,  // \"start-end\" address\n                perms,  // \"rwx\" permissions\n                py_path,  // path\n                (unsigned long long)kve->kve_resident * pagesize,  // rss\n                (unsigned long long)kve->kve_private_resident  // private\n                    * pagesize,\n                kve->kve_ref_count,  // ref count\n                kve->kve_shadow_count  // shadow count\n            ))\n        {\n            goto error;\n        }\n        Py_DECREF(py_path);\n        py_path = NULL;\n    }\n    free(freep);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_path);\n    Py_DECREF(py_retlist);\n    if (freep != NULL)\n        free(freep);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {\n    // Get process CPU affinity.\n    // Reference:\n    // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c\n    pid_t pid;\n    int ret;\n    int i;\n    cpuset_t mask;\n    PyObject *py_retlist;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    ret = cpuset_getaffinity(\n        CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, sizeof(mask), &mask\n    );\n    if (ret != 0)\n        return psutil_oserror();\n\n    py_retlist = PyList_New(0);\n    if (py_retlist == NULL)\n        return NULL;\n\n    for (i = 0; i < CPU_SETSIZE; i++) {\n        if (CPU_ISSET(i, &mask)) {\n            if (!pylist_append_fmt(py_retlist, \"i\", i))\n                goto error;\n        }\n    }\n\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {\n    // Set process CPU affinity.\n    // Reference:\n    // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c\n    pid_t pid;\n    int i;\n    int seq_len;\n    int ret;\n    cpuset_t cpu_set;\n    PyObject *py_cpu_set;\n    PyObject *py_cpu_seq = NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"O\", &pid, &py_cpu_set))\n        return NULL;\n\n    py_cpu_seq = PySequence_Fast(py_cpu_set, \"expected a sequence or integer\");\n    if (!py_cpu_seq)\n        return NULL;\n    seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);\n\n    // calculate the mask\n    CPU_ZERO(&cpu_set);\n    for (i = 0; i < seq_len; i++) {\n        PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);\n        long value = PyLong_AsLong(item);\n        if (value == -1 || PyErr_Occurred())\n            goto error;\n        CPU_SET(value, &cpu_set);\n    }\n\n    // set affinity\n    ret = cpuset_setaffinity(\n        CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, sizeof(cpu_set), &cpu_set\n    );\n    if (ret != 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    Py_DECREF(py_cpu_seq);\n    Py_RETURN_NONE;\n\nerror:\n    if (py_cpu_seq != NULL)\n        Py_DECREF(py_cpu_seq);\n    return NULL;\n}\n\n\n/*\n * An emulation of Linux prlimit(). Returns a (soft, hard) tuple.\n */\nPyObject *\npsutil_proc_getrlimit(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int resource;\n    int name[5];\n    struct rlimit rlp;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"i\", &pid, &resource))\n        return NULL;\n\n    name[0] = CTL_KERN;\n    name[1] = KERN_PROC;\n    name[2] = KERN_PROC_RLIMIT;\n    name[3] = pid;\n    name[4] = resource;\n\n    if (psutil_sysctl(name, 5, &rlp, sizeof(rlp)) != 0)\n        return NULL;\n\n#if defined(HAVE_LONG_LONG)\n    return Py_BuildValue(\n        \"LL\", (PY_LONG_LONG)rlp.rlim_cur, (PY_LONG_LONG)rlp.rlim_max\n    );\n#else\n    return Py_BuildValue(\"ll\", (long)rlp.rlim_cur, (long)rlp.rlim_max);\n#endif\n}\n\n\n/*\n * An emulation of Linux prlimit() (set).\n */\nPyObject *\npsutil_proc_setrlimit(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int ret;\n    int resource;\n    int name[5];\n    struct rlimit new;\n    struct rlimit *newp = NULL;\n    PyObject *py_soft = NULL;\n    PyObject *py_hard = NULL;\n\n    if (!PyArg_ParseTuple(\n            args, _Py_PARSE_PID \"iOO\", &pid, &resource, &py_soft, &py_hard\n        ))\n        return NULL;\n\n    name[0] = CTL_KERN;\n    name[1] = KERN_PROC;\n    name[2] = KERN_PROC_RLIMIT;\n    name[3] = pid;\n    name[4] = resource;\n\n#if defined(HAVE_LONG_LONG)\n    new.rlim_cur = PyLong_AsLongLong(py_soft);\n    if (new.rlim_cur == (rlim_t)-1 && PyErr_Occurred())\n        return NULL;\n    new.rlim_max = PyLong_AsLongLong(py_hard);\n    if (new.rlim_max == (rlim_t)-1 && PyErr_Occurred())\n        return NULL;\n#else\n    new.rlim_cur = PyLong_AsLong(py_soft);\n    if (new.rlim_cur == (rlim_t)-1 && PyErr_Occurred())\n        return NULL;\n    new.rlim_max = PyLong_AsLong(py_hard);\n    if (new.rlim_max == (rlim_t)-1 && PyErr_Occurred())\n        return NULL;\n#endif\n    newp = &new;\n    ret = sysctl(name, 5, NULL, 0, newp, sizeof(*newp));\n    if (ret == -1)\n        return psutil_oserror();\n    Py_RETURN_NONE;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/proc_socks.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Retrieves per-process open socket connections.\n */\n\n#include <Python.h>\n#include <sys/param.h>\n#include <sys/user.h>\n#include <sys/socketvar.h>  // for struct xsocket\n#include <sys/un.h>\n#include <sys/sysctl.h>\n#include <netinet/in.h>  // for xinpcb struct\n#include <netinet/in_pcb.h>\n#include <netinet/tcp_var.h>  // for struct xtcpcb\n#include <arpa/inet.h>  // for inet_ntop()\n#include <libutil.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// The tcplist fetching and walking is borrowed from netstat/inet.c.\nstatic char *\npsutil_fetch_tcplist(void) {\n    char *buf;\n    size_t len;\n\n    for (;;) {\n        if (sysctlbyname(\"net.inet.tcp.pcblist\", NULL, &len, NULL, 0) < 0) {\n            psutil_oserror();\n            return NULL;\n        }\n        buf = malloc(len);\n        if (buf == NULL) {\n            PyErr_NoMemory();\n            return NULL;\n        }\n        if (sysctlbyname(\"net.inet.tcp.pcblist\", buf, &len, NULL, 0) < 0) {\n            free(buf);\n            psutil_oserror();\n            return NULL;\n        }\n        return buf;\n    }\n}\n\n\nstatic int\npsutil_sockaddr_port(int family, struct sockaddr_storage *ss) {\n    struct sockaddr_in6 *sin6;\n    struct sockaddr_in *sin;\n\n    if (family == AF_INET) {\n        sin = (struct sockaddr_in *)ss;\n        return (sin->sin_port);\n    }\n    else {\n        sin6 = (struct sockaddr_in6 *)ss;\n        return (sin6->sin6_port);\n    }\n}\n\n\nstatic void *\npsutil_sockaddr_addr(int family, struct sockaddr_storage *ss) {\n    struct sockaddr_in6 *sin6;\n    struct sockaddr_in *sin;\n\n    if (family == AF_INET) {\n        sin = (struct sockaddr_in *)ss;\n        return (&sin->sin_addr);\n    }\n    else {\n        sin6 = (struct sockaddr_in6 *)ss;\n        return (&sin6->sin6_addr);\n    }\n}\n\n\nstatic socklen_t\npsutil_sockaddr_addrlen(int family) {\n    if (family == AF_INET)\n        return (sizeof(struct in_addr));\n    else\n        return (sizeof(struct in6_addr));\n}\n\n\nstatic int\npsutil_sockaddr_matches(\n    int family, int port, void *pcb_addr, struct sockaddr_storage *ss\n) {\n    if (psutil_sockaddr_port(family, ss) != port)\n        return (0);\n    return (\n        memcmp(\n            psutil_sockaddr_addr(family, ss),\n            pcb_addr,\n            psutil_sockaddr_addrlen(family)\n        )\n        == 0\n    );\n}\n\n\n#if __FreeBSD_version >= 1200026\nstatic struct xtcpcb *\npsutil_search_tcplist(char *buf, struct kinfo_file *kif) {\n    struct xtcpcb *tp;\n    struct xinpcb *inp;\n#else\nstatic struct tcpcb *\npsutil_search_tcplist(char *buf, struct kinfo_file *kif) {\n    struct tcpcb *tp;\n    struct inpcb *inp;\n#endif\n    struct xinpgen *xig, *oxig;\n    struct xsocket *so;\n\n    oxig = xig = (struct xinpgen *)buf;\n    for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);\n         xig->xig_len > sizeof(struct xinpgen);\n         xig = (struct xinpgen *)((char *)xig + xig->xig_len))\n    {\n#if __FreeBSD_version >= 1200026\n        tp = (struct xtcpcb *)xig;\n        inp = &tp->xt_inp;\n        so = &inp->xi_socket;\n#else\n        tp = &((struct xtcpcb *)xig)->xt_tp;\n        inp = &((struct xtcpcb *)xig)->xt_inp;\n        so = &((struct xtcpcb *)xig)->xt_socket;\n#endif\n\n        if (so->so_type != kif->kf_sock_type\n            || so->xso_family != kif->kf_sock_domain\n            || so->xso_protocol != kif->kf_sock_protocol)\n            continue;\n\n        if (kif->kf_sock_domain == AF_INET) {\n            if (!psutil_sockaddr_matches(\n                    AF_INET,\n                    inp->inp_lport,\n                    &inp->inp_laddr,\n#if __FreeBSD_version < 1200031\n                    &kif->kf_sa_local\n                ))\n#else\n                    &kif->kf_un.kf_sock.kf_sa_local\n                ))\n#endif\n                continue;\n            if (!psutil_sockaddr_matches(\n                    AF_INET,\n                    inp->inp_fport,\n                    &inp->inp_faddr,\n#if __FreeBSD_version < 1200031\n                    &kif->kf_sa_peer\n                ))\n#else\n                    &kif->kf_un.kf_sock.kf_sa_peer\n                ))\n#endif\n                continue;\n        }\n        else {\n            if (!psutil_sockaddr_matches(\n                    AF_INET6,\n                    inp->inp_lport,\n                    &inp->in6p_laddr,\n#if __FreeBSD_version < 1200031\n                    &kif->kf_sa_local\n                ))\n#else\n                    &kif->kf_un.kf_sock.kf_sa_local\n                ))\n#endif\n                continue;\n            if (!psutil_sockaddr_matches(\n                    AF_INET6,\n                    inp->inp_fport,\n                    &inp->in6p_faddr,\n#if __FreeBSD_version < 1200031\n                    &kif->kf_sa_peer\n                ))\n#else\n                    &kif->kf_un.kf_sock.kf_sa_peer\n                ))\n#endif\n                continue;\n        }\n\n        return (tp);\n    }\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_net_connections(PyObject *self, PyObject *args) {\n    // Return connections opened by process.\n    pid_t pid;\n    int i;\n    int cnt;\n    struct kinfo_file *freep = NULL;\n    struct kinfo_file *kif;\n    char *tcplist = NULL;\n#if __FreeBSD_version >= 1200026\n    struct xtcpcb *tcp;\n#else\n    struct tcpcb *tcp;\n#endif\n\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_laddr = NULL;\n    PyObject *py_raddr = NULL;\n    PyObject *py_af_filter = NULL;\n    PyObject *py_type_filter = NULL;\n    PyObject *py_family = NULL;\n    PyObject *py_type = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(\n            args, _Py_PARSE_PID \"OO\", &pid, &py_af_filter, &py_type_filter\n        ))\n    {\n        goto error;\n    }\n    if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {\n        PyErr_SetString(PyExc_TypeError, \"arg 2 or 3 is not a sequence\");\n        goto error;\n    }\n\n    errno = 0;\n    freep = kinfo_getfile(pid, &cnt);\n    if (freep == NULL) {\n        psutil_raise_for_pid(pid, \"kinfo_getfile()\");\n        goto error;\n    }\n\n    tcplist = psutil_fetch_tcplist();\n    if (tcplist == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < cnt; i++) {\n        int lport, rport, state;\n        char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];\n        char path[PATH_MAX];\n        int inseq;\n        py_laddr = NULL;\n        py_raddr = NULL;\n\n        kif = &freep[i];\n        if (kif->kf_type == KF_TYPE_SOCKET) {\n            // apply filters\n            py_family = PyLong_FromLong((long)kif->kf_sock_domain);\n            inseq = PySequence_Contains(py_af_filter, py_family);\n            Py_DECREF(py_family);\n            if (inseq == 0)\n                continue;\n            py_type = PyLong_FromLong((long)kif->kf_sock_type);\n            inseq = PySequence_Contains(py_type_filter, py_type);\n            Py_DECREF(py_type);\n            if (inseq == 0)\n                continue;\n            // IPv4 / IPv6 socket\n            if ((kif->kf_sock_domain == AF_INET)\n                || (kif->kf_sock_domain == AF_INET6))\n            {\n                // fill status\n                state = PSUTIL_CONN_NONE;\n                if (kif->kf_sock_type == SOCK_STREAM) {\n                    tcp = psutil_search_tcplist(tcplist, kif);\n                    if (tcp != NULL)\n                        state = (int)tcp->t_state;\n                }\n\n                // build addr and port\n                inet_ntop(\n                    kif->kf_sock_domain,\n                    psutil_sockaddr_addr(\n                        kif->kf_sock_domain,\n#if __FreeBSD_version < 1200031\n                        &kif->kf_sa_local\n                    ),\n#else\n                        &kif->kf_un.kf_sock.kf_sa_local\n                    ),\n#endif\n                    lip,\n                    sizeof(lip)\n                );\n                inet_ntop(\n                    kif->kf_sock_domain,\n                    psutil_sockaddr_addr(\n                        kif->kf_sock_domain,\n#if __FreeBSD_version < 1200031\n                        &kif->kf_sa_peer\n                    ),\n#else\n                        &kif->kf_un.kf_sock.kf_sa_peer\n                    ),\n#endif\n                    rip,\n                    sizeof(rip)\n                );\n                lport = htons(psutil_sockaddr_port(\n                    kif->kf_sock_domain,\n#if __FreeBSD_version < 1200031\n                    &kif->kf_sa_local\n                ));\n#else\n                    &kif->kf_un.kf_sock.kf_sa_local\n                ));\n#endif\n                rport = htons(psutil_sockaddr_port(\n                    kif->kf_sock_domain,\n#if __FreeBSD_version < 1200031\n                    &kif->kf_sa_peer\n                ));\n#else\n                    &kif->kf_un.kf_sock.kf_sa_peer\n                ));\n#endif\n\n                // construct python tuple/list\n                py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n                if (!py_laddr)\n                    goto error;\n                if (rport != 0)\n                    py_raddr = Py_BuildValue(\"(si)\", rip, rport);\n                else\n                    py_raddr = Py_BuildValue(\"()\");\n                if (!py_raddr)\n                    goto error;\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiNNi)\",\n                        kif->kf_fd,\n                        kif->kf_sock_domain,\n                        kif->kf_sock_type,\n                        py_laddr,\n                        py_raddr,\n                        state\n                    ))\n                {\n                    goto error;\n                }\n                py_laddr = NULL;\n                py_raddr = NULL;\n            }\n            // UNIX socket.\n            // Note: remote path cannot be determined.\n            else if (kif->kf_sock_domain == AF_UNIX) {\n                struct sockaddr_un *sun;\n\n#if __FreeBSD_version < 1200031\n                sun = (struct sockaddr_un *)&kif->kf_sa_local;\n#else\n                sun = (struct sockaddr_un *)&kif->kf_un.kf_sock.kf_sa_local;\n#endif\n                str_format(\n                    path,\n                    sizeof(path),\n                    \"%.*s\",\n                    (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))\n                    ),\n                    sun->sun_path\n                );\n\n                py_laddr = PyUnicode_DecodeFSDefault(path);\n                if (!py_laddr)\n                    goto error;\n\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiOsi)\",\n                        kif->kf_fd,\n                        kif->kf_sock_domain,\n                        kif->kf_sock_type,\n                        py_laddr,\n                        \"\",  // raddr can't be determined\n                        PSUTIL_CONN_NONE\n                    ))\n                {\n                    goto error;\n                }\n                Py_DECREF(py_laddr);\n                py_laddr = NULL;\n            }\n        }\n    }\n    free(freep);\n    free(tcplist);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_laddr);\n    Py_XDECREF(py_raddr);\n    Py_DECREF(py_retlist);\n    if (freep != NULL)\n        free(freep);\n    if (tcplist != NULL)\n        free(tcplist);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/sensors.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\nOriginal code was refactored and moved from psutil/arch/freebsd/specific.c\nFor reference, here's the git history with original(ish) implementations:\n- sensors_battery(): 022cf0a05d34f4274269d4f8002ee95b9f3e32d2\n- sensors_cpu_temperature(): bb5d032be76980a9e110f03f1203bd35fa85a793\n  (patch by Alex Manuskin)\n*/\n\n\n#include <Python.h>\n#include <sys/sysctl.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#define DECIKELVIN_2_CELSIUS(t) (t - 2731) / 10\n\n\nPyObject *\npsutil_sensors_battery(PyObject *self, PyObject *args) {\n    int percent;\n    int minsleft;\n    int power_plugged;\n    size_t size = sizeof(percent);\n\n    if (psutil_sysctlbyname(\"hw.acpi.battery.life\", &percent, size) != 0)\n        goto error;\n    if (psutil_sysctlbyname(\"hw.acpi.battery.time\", &minsleft, size) != 0)\n        goto error;\n    if (psutil_sysctlbyname(\"hw.acpi.acline\", &power_plugged, size) != 0)\n        goto error;\n    return Py_BuildValue(\"dii\", (double)percent, minsleft, power_plugged);\n\nerror:\n    // see: https://github.com/giampaolo/psutil/issues/1074\n    if (errno == ENOENT)\n        PyErr_SetString(PyExc_NotImplementedError, \"no battery\");\n    else\n        psutil_oserror();\n    return NULL;\n}\n\n\n// Return temperature information for a given CPU core number.\nPyObject *\npsutil_sensors_cpu_temperature(PyObject *self, PyObject *args) {\n    int current;\n    int tjmax;\n    int core;\n    char sensor[26];\n    size_t size = sizeof(current);\n\n    if (!PyArg_ParseTuple(args, \"i\", &core))\n        return NULL;\n    str_format(sensor, sizeof(sensor), \"dev.cpu.%d.temperature\", core);\n    if (psutil_sysctlbyname(sensor, &current, size) != 0)\n        goto error;\n    current = DECIKELVIN_2_CELSIUS(current);\n\n    // Return -273 in case of failure.\n    str_format(sensor, sizeof(sensor), \"dev.cpu.%d.coretemp.tjmax\", core);\n    if (psutil_sysctlbyname(sensor, &tjmax, size) != 0)\n        tjmax = 0;\n    tjmax = DECIKELVIN_2_CELSIUS(tjmax);\n\n    return Py_BuildValue(\"ii\", current, tjmax);\n\nerror:\n    if (errno == ENOENT)\n        PyErr_SetString(PyExc_NotImplementedError, \"no temperature sensors\");\n    else\n        psutil_oserror();\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/freebsd/sys_socks.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Retrieves system-wide open socket connections. This is based off of\n * sockstat utility source code:\n * https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c\n */\n\n#include <Python.h>\n#include <sys/param.h>\n#include <sys/user.h>\n#include <sys/file.h>\n#include <sys/socketvar.h>  // for struct xsocket\n#include <sys/un.h>\n#include <sys/unpcb.h>\n#include <sys/sysctl.h>\n#include <netinet/in.h>  // for xinpcb struct\n#include <netinet/ip.h>\n#include <netinet/in_pcb.h>\n#include <netinet/tcp_var.h>  // for struct xtcpcb\n#include <arpa/inet.h>  // for inet_ntop()\n\n#include \"../../arch/all/init.h\"\n\n\nstatic int\npsutil_populate_xfiles(struct xfile **psutil_xfiles, int *psutil_nxfiles) {\n    struct xfile *new_psutil_xfiles;\n    size_t len = sizeof(struct xfile);\n\n    while (sysctlbyname(\"kern.file\", *psutil_xfiles, &len, 0, 0) == -1) {\n        if (errno != ENOMEM) {\n            psutil_oserror();\n            return -1;\n        }\n        len *= 2;\n        new_psutil_xfiles = realloc(*psutil_xfiles, len);\n        if (new_psutil_xfiles == NULL) {\n            PyErr_NoMemory();\n            return -1;\n        }\n        *psutil_xfiles = new_psutil_xfiles;\n    }\n    if (len > 0 && (*psutil_xfiles)->xf_size != sizeof(struct xfile)) {\n        psutil_runtime_error(\"struct xfile size mismatch\");\n        return -1;\n    }\n    *psutil_nxfiles = len / sizeof(struct xfile);\n    return 0;\n}\n\n\nstatic struct xfile *\npsutil_get_file_from_sock(\n    kvaddr_t sock, struct xfile *psutil_xfiles, int psutil_nxfiles\n) {\n    struct xfile *xf;\n    int n;\n\n    for (xf = psutil_xfiles, n = 0; n < psutil_nxfiles; ++n, ++xf) {\n        if (xf->xf_data == sock)\n            return xf;\n    }\n    return NULL;\n}\n\n\n// Reference:\n// https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c\nstatic int\npsutil_gather_inet(\n    int proto,\n    int include_v4,\n    int include_v6,\n    PyObject *py_retlist,\n    struct xfile *psutil_xfiles,\n    int psutil_nxfiles\n) {\n    struct xinpgen *xig, *exig;\n    struct xinpcb *xip;\n    struct xtcpcb *xtp;\n#if __FreeBSD_version >= 1200026\n    struct xinpcb *inp;\n#else\n    struct inpcb *inp;\n#endif\n    struct xsocket *so;\n    const char *varname = NULL;\n    size_t len, bufsize;\n    void *buf;\n    int retry;\n    int type;\n\n    PyObject *py_laddr = NULL;\n    PyObject *py_raddr = NULL;\n\n    switch (proto) {\n        case IPPROTO_TCP:\n            varname = \"net.inet.tcp.pcblist\";\n            type = SOCK_STREAM;\n            break;\n        case IPPROTO_UDP:\n            varname = \"net.inet.udp.pcblist\";\n            type = SOCK_DGRAM;\n            break;\n    }\n\n    buf = NULL;\n    bufsize = 8192;\n    retry = 5;\n    do {\n        for (;;) {\n            buf = realloc(buf, bufsize);\n            if (buf == NULL)\n                continue;  // XXX\n            len = bufsize;\n            if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)\n                break;\n            if (errno != ENOMEM) {\n                psutil_oserror();\n                goto error;\n            }\n            bufsize *= 2;\n        }\n        xig = (struct xinpgen *)buf;\n        exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig);\n        if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) {\n            psutil_runtime_error(\"struct xinpgen size mismatch\");\n            goto error;\n        }\n    } while (xig->xig_gen != exig->xig_gen && retry--);\n\n    for (;;) {\n        struct xfile *xf;\n        int lport, rport, status, family;\n\n        xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);\n        if (xig >= exig)\n            break;\n\n        switch (proto) {\n            case IPPROTO_TCP:\n                xtp = (struct xtcpcb *)xig;\n                if (xtp->xt_len != sizeof *xtp) {\n                    psutil_runtime_error(\"struct xtcpcb size mismatch\");\n                    goto error;\n                }\n                inp = &xtp->xt_inp;\n#if __FreeBSD_version >= 1200026\n                so = &inp->xi_socket;\n                status = xtp->t_state;\n#else\n                so = &xtp->xt_socket;\n                status = xtp->xt_tp.t_state;\n#endif\n                break;\n            case IPPROTO_UDP:\n                xip = (struct xinpcb *)xig;\n                if (xip->xi_len != sizeof *xip) {\n                    psutil_runtime_error(\"struct xinpcb size mismatch\");\n                    goto error;\n                }\n#if __FreeBSD_version >= 1200026\n                inp = xip;\n#else\n                inp = &xip->xi_inp;\n#endif\n                so = &xip->xi_socket;\n                status = PSUTIL_CONN_NONE;\n                break;\n            default:\n                psutil_runtime_error(\"invalid proto\");\n                goto error;\n        }\n\n        // filter\n        if ((inp->inp_vflag & INP_IPV4) && (include_v4 == 0))\n            continue;\n        if ((inp->inp_vflag & INP_IPV6) && (include_v6 == 0))\n            continue;\n\n        char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];\n\n        xf = psutil_get_file_from_sock(\n            so->xso_so, psutil_xfiles, psutil_nxfiles\n        );\n        if (xf == NULL)\n            continue;\n        lport = ntohs(inp->inp_lport);\n        rport = ntohs(inp->inp_fport);\n\n        if (inp->inp_vflag & INP_IPV4) {\n            family = AF_INET;\n            inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip));\n            inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip));\n        }\n        else if (inp->inp_vflag & INP_IPV6) {\n            family = AF_INET6;\n            inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip));\n            inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip));\n        }\n\n        // construct python tuple/list\n        py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n        if (!py_laddr)\n            goto error;\n        if (rport != 0)\n            py_raddr = Py_BuildValue(\"(si)\", rip, rport);\n        else\n            py_raddr = Py_BuildValue(\"()\");\n        if (!py_raddr)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"iiiNNi\" _Py_PARSE_PID,\n                xf->xf_fd,  // fd\n                family,  // family\n                type,  // type\n                py_laddr,  // laddr\n                py_raddr,  // raddr\n                status,  // status\n                xf->xf_pid  // pid\n            ))\n        {\n            goto error;\n        }\n        py_laddr = NULL;\n        py_raddr = NULL;\n    }\n\n    free(buf);\n    return 0;\n\nerror:\n    Py_XDECREF(py_laddr);\n    Py_XDECREF(py_raddr);\n    free(buf);\n    return -1;\n}\n\n\nstatic int\npsutil_gather_unix(\n    int proto,\n    PyObject *py_retlist,\n    struct xfile *psutil_xfiles,\n    int psutil_nxfiles\n) {\n    struct xunpgen *xug, *exug;\n    struct xunpcb *xup;\n    const char *varname = NULL;\n    const char *protoname = NULL;\n    size_t len;\n    size_t bufsize;\n    void *buf;\n    int retry;\n    struct sockaddr_un *sun;\n    char path[PATH_MAX];\n\n    PyObject *py_lpath = NULL;\n\n    switch (proto) {\n        case SOCK_STREAM:\n            varname = \"net.local.stream.pcblist\";\n            protoname = \"stream\";\n            break;\n        case SOCK_DGRAM:\n            varname = \"net.local.dgram.pcblist\";\n            protoname = \"dgram\";\n            break;\n    }\n\n    buf = NULL;\n    bufsize = 8192;\n    retry = 5;\n\n    do {\n        for (;;) {\n            buf = realloc(buf, bufsize);\n            if (buf == NULL) {\n                PyErr_NoMemory();\n                goto error;\n            }\n            len = bufsize;\n            if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)\n                break;\n            if (errno != ENOMEM) {\n                psutil_oserror();\n                goto error;\n            }\n            bufsize *= 2;\n        }\n        xug = (struct xunpgen *)buf;\n        exug = (struct xunpgen *)(void *)((char *)buf + len - sizeof *exug);\n        if (xug->xug_len != sizeof *xug || exug->xug_len != sizeof *exug) {\n            psutil_runtime_error(\"struct xinpgen size mismatch\");\n            goto error;\n        }\n    } while (xug->xug_gen != exug->xug_gen && retry--);\n\n    for (;;) {\n        struct xfile *xf;\n\n        xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);\n        if (xug >= exug)\n            break;\n        xup = (struct xunpcb *)xug;\n        if (xup->xu_len != sizeof *xup)\n            goto error;\n\n        xf = psutil_get_file_from_sock(\n            xup->xu_socket.xso_so, psutil_xfiles, psutil_nxfiles\n        );\n        if (xf == NULL)\n            continue;\n\n        sun = (struct sockaddr_un *)&xup->xu_addr;\n        str_format(\n            path,\n            sizeof(path),\n            \"%.*s\",\n            (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),\n            sun->sun_path\n        );\n        py_lpath = PyUnicode_DecodeFSDefault(path);\n        if (!py_lpath)\n            goto error;\n\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(iiiOsii)\",\n                xf->xf_fd,  // fd\n                AF_UNIX,  // family\n                proto,  // type\n                py_lpath,  // lpath\n                \"\",  // rath\n                PSUTIL_CONN_NONE,  // status\n                xf->xf_pid  // pid\n            ))\n        {\n            goto error;\n        }\n        Py_DECREF(py_lpath);\n        py_lpath = NULL;\n    }\n\n    free(buf);\n    return 0;\n\nerror:\n    Py_XDECREF(py_lpath);\n    free(buf);\n    return -1;\n}\n\n\nstatic int\npsutil_int_in_seq(int value, PyObject *py_seq) {\n    int inseq;\n    PyObject *py_value;\n\n    py_value = PyLong_FromLong((long)value);\n    if (py_value == NULL)\n        return -1;\n    inseq = PySequence_Contains(py_seq, py_value);  // return -1 on failure\n    Py_DECREF(py_value);\n    return inseq;\n}\n\n\nPyObject *\npsutil_net_connections(PyObject *self, PyObject *args) {\n    int include_v4, include_v6, include_unix, include_tcp, include_udp,\n        psutil_nxfiles;\n    struct xfile *psutil_xfiles;\n    PyObject *py_af_filter = NULL;\n    PyObject *py_type_filter = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, \"OO\", &py_af_filter, &py_type_filter)) {\n        goto error;\n    }\n    if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {\n        PyErr_SetString(PyExc_TypeError, \"arg 2 or 3 is not a sequence\");\n        goto error;\n    }\n\n    if ((include_v4 = psutil_int_in_seq(AF_INET, py_af_filter)) == -1)\n        goto error;\n    if ((include_v6 = psutil_int_in_seq(AF_INET6, py_af_filter)) == -1)\n        goto error;\n    if ((include_unix = psutil_int_in_seq(AF_UNIX, py_af_filter)) == -1)\n        goto error;\n    if ((include_tcp = psutil_int_in_seq(SOCK_STREAM, py_type_filter)) == -1)\n        goto error;\n    if ((include_udp = psutil_int_in_seq(SOCK_DGRAM, py_type_filter)) == -1)\n        goto error;\n\n    psutil_xfiles = malloc(sizeof(struct xfile));\n    if (psutil_xfiles == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    if (psutil_populate_xfiles(&psutil_xfiles, &psutil_nxfiles) != 0)\n        goto error_free_psutil_xfiles;\n\n    // TCP\n    if (include_tcp == 1) {\n        if (psutil_gather_inet(\n                IPPROTO_TCP,\n                include_v4,\n                include_v6,\n                py_retlist,\n                psutil_xfiles,\n                psutil_nxfiles\n            )\n            != 0)\n        {\n            goto error_free_psutil_xfiles;\n        }\n    }\n    // UDP\n    if (include_udp == 1) {\n        if (psutil_gather_inet(\n                IPPROTO_UDP,\n                include_v4,\n                include_v6,\n                py_retlist,\n                psutil_xfiles,\n                psutil_nxfiles\n            )\n            != 0)\n        {\n            goto error_free_psutil_xfiles;\n        }\n    }\n    // UNIX\n    if (include_unix == 1) {\n        if (psutil_gather_unix(\n                SOCK_STREAM, py_retlist, psutil_xfiles, psutil_nxfiles\n            )\n            != 0)\n            goto error_free_psutil_xfiles;\n        if (psutil_gather_unix(\n                SOCK_DGRAM, py_retlist, psutil_xfiles, psutil_nxfiles\n            )\n            != 0)\n            goto error_free_psutil_xfiles;\n    }\n\n    free(psutil_xfiles);\n    return py_retlist;\n\nerror_free_psutil_xfiles:\n    free(psutil_xfiles);\nerror:\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/linux/disk.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include \"../../arch/all/init.h\"\n\n#include <Python.h>\n#include <mntent.h>\n\n\n// Return disk mounted partitions as a list of tuples including device,\n// mount point and filesystem type.\nPyObject *\npsutil_disk_partitions(PyObject *self, PyObject *args) {\n    FILE *file = NULL;\n    struct mntent *entry;\n    char *mtab_path;\n    PyObject *py_dev = NULL;\n    PyObject *py_mountp = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (!PyArg_ParseTuple(args, \"s\", &mtab_path))\n        return NULL;\n\n    Py_BEGIN_ALLOW_THREADS\n    file = setmntent(mtab_path, \"r\");\n    Py_END_ALLOW_THREADS\n    if ((file == 0) || (file == NULL)) {\n        psutil_debug(\"setmntent() failed\");\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path);\n        goto error;\n    }\n\n    while ((entry = getmntent(file))) {\n        if (entry == NULL) {\n            psutil_runtime_error(\"getmntent() syscall failed\");\n            goto error;\n        }\n        py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname);\n        if (!py_dev)\n            goto error;\n        py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir);\n        if (!py_mountp)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(OOss)\",\n                py_dev,  // device\n                py_mountp,  // mount point\n                entry->mnt_type,  // fs type\n                entry->mnt_opts  // options\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_dev);\n        Py_CLEAR(py_mountp);\n    }\n    endmntent(file);\n    return py_retlist;\n\nerror:\n    if (file != NULL)\n        endmntent(file);\n    Py_XDECREF(py_dev);\n    Py_XDECREF(py_mountp);\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/linux/heap.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include \"../../arch/all/init.h\"\n\n#if defined(PSUTIL_HAS_HEAP_INFO)  // Not available on MUSL / Alpine Linux\n#include <Python.h>\n#include <malloc.h>\n#include <dlfcn.h>\n\n\n// A copy of glibc's mallinfo2 layout. Allows compilation even if\n// <malloc.h> doesn't define mallinfo2.\nstruct my_mallinfo2 {\n    size_t arena;\n    size_t ordblks;\n    size_t smblks;\n    size_t hblks;\n    size_t hblkhd;\n    size_t usmblks;\n    size_t fsmblks;\n    size_t uordblks;\n    size_t fordblks;\n    size_t keepcost;\n};\n\n\n// psutil_heap_info() -> (heap_used, mmap_used)\n// Return low-level heap statistics from the C allocator (glibc).\nPyObject *\npsutil_heap_info(PyObject *self, PyObject *args) {\n    static int warned = 0;\n    void *handle = NULL;\n    void *fun = NULL;\n    unsigned long long uord, mmap;\n\n    handle = dlopen(\"libc.so.6\", RTLD_LAZY);\n    if (handle != NULL) {\n        fun = dlsym(handle, \"mallinfo2\");\n    }\n\n    // mallinfo2() appeared in glibc 2.33, February 2021.\n    if (fun != NULL) {\n        struct my_mallinfo2 m2;\n\n        m2 = ((struct my_mallinfo2(*)(void))fun)();\n\n        uord = (unsigned long long)m2.uordblks;\n        mmap = (unsigned long long)m2.hblkhd;\n    }\n\n    // mallinfo() is broken due to its fields that are 32-bit\n    // integers, meaning they overflow if process allocates more than\n    // 2GB in the heap.\n    else {\n        struct mallinfo m1;\n\n        if (!warned) {\n            psutil_debug(\"WARNING: using deprecated mallinfo()\");\n            warned = 1;\n        }\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n        m1 = mallinfo();\n#pragma GCC diagnostic pop\n\n        uord = (unsigned long long)m1.uordblks;\n        mmap = (unsigned long long)m1.hblkhd;\n    }\n\n    if (handle)\n        dlclose(handle);\n\n    return Py_BuildValue(\"KK\", uord, mmap);\n}\n\n\n// Release unused memory held by the allocator back to the OS.\nPyObject *\npsutil_heap_trim(PyObject *self, PyObject *args) {\n    // heap_trim returns 1 if some memory was released, else 0.\n    int ret = malloc_trim(0);\n    return PyBool_FromLong(ret);\n}\n#endif  // PSUTIL_HAS_HEAP_INFO\n"
  },
  {
    "path": "psutil/arch/linux/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/syscall.h>  // __NR_*\n#include <sched.h>  // CPU_ALLOC\n\nPyObject *psutil_disk_partitions(PyObject *self, PyObject *args);\nPyObject *psutil_linux_sysinfo(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_duplex_speed(PyObject *self, PyObject *args);\nPyObject *psutil_proc_ioprio_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_ioprio_set(PyObject *self, PyObject *args);\n\n// Should exist starting from CentOS 6 (year 2011).\n#ifdef CPU_ALLOC\n#define PSUTIL_HAS_CPU_AFFINITY\nPyObject *psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args);\n#endif\n\n// Does not exist on MUSL / Alpine Linux.\n#if defined(__GLIBC__)\n#define PSUTIL_HAS_HEAP_INFO\n#define PSUTIL_HAS_HEAP_TRIM\nPyObject *psutil_heap_trim(PyObject *self, PyObject *args);\nPyObject *psutil_heap_info(PyObject *self, PyObject *args);\n#endif\n"
  },
  {
    "path": "psutil/arch/linux/mem.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysinfo.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_linux_sysinfo(PyObject *self, PyObject *args) {\n    struct sysinfo info;\n\n    if (sysinfo(&info) != 0)\n        return psutil_oserror();\n    // note: boot time might also be determined from here\n    return Py_BuildValue(\n        \"(kkkkkkI)\",\n        info.totalram,  // total\n        info.freeram,  // free\n        info.bufferram,  // buffer\n        info.sharedram,  // shared\n        info.totalswap,  // swap tot\n        info.freeswap,  // swap free\n        info.mem_unit  // multiplier\n    );\n}\n"
  },
  {
    "path": "psutil/arch/linux/net.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <linux/sockios.h>\n#include <sys/socket.h>\n#include <sys/ioctl.h>\n#include <linux/if.h>\n#include <linux/version.h>\n#include <unistd.h>\n\n#include \"../../arch/all/init.h\"\n\n// see: https://github.com/giampaolo/psutil/issues/659\n#ifdef PSUTIL_ETHTOOL_MISSING_TYPES\n#include <linux/types.h>\ntypedef __u64 u64;\ntypedef __u32 u32;\ntypedef __u16 u16;\ntypedef __u8 u8;\n#endif\n\n// Avoid redefinition of struct sysinfo with musl libc.\n#define _LINUX_SYSINFO_H\n#include <linux/ethtool.h>\n\n// * defined in linux/ethtool.h but not always available (e.g. Android)\n// * #ifdef check needed for old kernels, see:\n//   https://github.com/giampaolo/psutil/issues/2164\nstatic inline uint32_t\npsutil_ethtool_cmd_speed(const struct ethtool_cmd *ecmd) {\n#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)\n    return ecmd->speed;\n#else\n    return (ecmd->speed_hi << 16) | ecmd->speed;\n#endif\n}\n\n// May happen on old RedHat versions, see:\n// https://github.com/giampaolo/psutil/issues/607\n#ifndef DUPLEX_UNKNOWN\n#define DUPLEX_UNKNOWN 0xff\n#endif\n\n// https://github.com/giampaolo/psutil/pull/2156\n#ifndef SPEED_UNKNOWN\n#define SPEED_UNKNOWN -1\n#endif\n\n// References:\n// * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py\n// * http://www.i-scream.org/libstatgrab/\nPyObject *\npsutil_net_if_duplex_speed(PyObject *self, PyObject *args) {\n    char *nic_name;\n    int sock = 0;\n    int ret;\n    int duplex;\n    __u32 uint_speed;\n    int speed;\n    struct ifreq ifr;\n    struct ethtool_cmd ethcmd;\n    PyObject *py_retlist = NULL;\n\n    if (!PyArg_ParseTuple(args, \"s\", &nic_name))\n        return NULL;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1)\n        return psutil_oserror_wsyscall(\"socket()\");\n    str_copy(ifr.ifr_name, sizeof(ifr.ifr_name), nic_name);\n\n\n    // duplex and speed\n    memset(&ethcmd, 0, sizeof ethcmd);\n    ethcmd.cmd = ETHTOOL_GSET;\n    ifr.ifr_data = (void *)&ethcmd;\n    ret = ioctl(sock, SIOCETHTOOL, &ifr);\n\n    if (ret != -1) {\n        duplex = ethcmd.duplex;\n        // speed is returned from ethtool as a __u32 ranging from 0 to INT_MAX\n        // or SPEED_UNKNOWN (-1)\n        uint_speed = psutil_ethtool_cmd_speed(&ethcmd);\n        if (uint_speed == (__u32)SPEED_UNKNOWN || uint_speed > INT_MAX) {\n            speed = 0;\n        }\n        else {\n            speed = (int)uint_speed;\n        }\n    }\n    else {\n        if ((errno == EOPNOTSUPP) || (errno == EINVAL) || (errno == EBUSY)) {\n            // EOPNOTSUPP may occur in case of wi-fi cards.\n            // For EINVAL see:\n            // https://github.com/giampaolo/psutil/issues/797\n            //     #issuecomment-202999532\n            // EBUSY may occur with broken drivers or busy devices.\n            duplex = DUPLEX_UNKNOWN;\n            speed = 0;\n        }\n        else {\n            psutil_oserror_wsyscall(\"ioctl(SIOCETHTOOL)\");\n            goto error;\n        }\n    }\n\n    py_retlist = Py_BuildValue(\"[ii]\", duplex, speed);\n    if (!py_retlist)\n        goto error;\n    close(sock);\n    return py_retlist;\n\nerror:\n    if (sock != -1)\n        close(sock);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/linux/proc.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include \"../../arch/all/init.h\"\n\n#include <Python.h>\n#include <sys/syscall.h>\n#include <sched.h>\n#include <unistd.h>\n\n\n// ====================================================================\n// --- process priority (niceness)\n// ====================================================================\n\n\nenum {\n    IOPRIO_WHO_PROCESS = 1,\n};\n\nstatic inline int\nioprio_get(int which, int who) {\n    return syscall(__NR_ioprio_get, which, who);\n}\n\nstatic inline int\nioprio_set(int which, int who, int ioprio) {\n    return syscall(__NR_ioprio_set, which, who, ioprio);\n}\n\n#define IOPRIO_CLASS_SHIFT 13\n#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)\n\n#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)\n#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)\n#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)\n\n// Return a (ioclass, iodata) Python tuple representing process I/O\n// priority.\nPyObject *\npsutil_proc_ioprio_get(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int ioprio, ioclass, iodata;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);\n    if (ioprio == -1)\n        return psutil_oserror();\n    ioclass = IOPRIO_PRIO_CLASS(ioprio);\n    iodata = IOPRIO_PRIO_DATA(ioprio);\n    return Py_BuildValue(\"ii\", ioclass, iodata);\n}\n\n\n// A wrapper around ioprio_set(); sets process I/O priority. ioclass\n// can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE or\n// 0. iodata goes from 0 to 7 depending on ioclass specified.\nPyObject *\npsutil_proc_ioprio_set(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int ioprio, ioclass, iodata;\n    int retval;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"ii\", &pid, &ioclass, &iodata)) {\n        return NULL;\n    }\n    ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);\n    retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);\n    if (retval == -1)\n        return psutil_oserror();\n    Py_RETURN_NONE;\n}\n\n\n// ====================================================================\n// --- process CPU affinity\n// ====================================================================\n\n\n#ifdef PSUTIL_HAS_CPU_AFFINITY\n// Return process CPU affinity as a list of integers.\nPyObject *\npsutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {\n    int cpu, ncpus, count, cpucount_s;\n    pid_t pid;\n    size_t setsize;\n    cpu_set_t *mask = NULL;\n    PyObject *py_list = NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    // The minimum number of CPUs allocated in a cpu_set_t.\n    ncpus = sizeof(unsigned long) * CHAR_BIT;\n    while (1) {\n        setsize = CPU_ALLOC_SIZE(ncpus);\n        mask = CPU_ALLOC(ncpus);\n        if (mask == NULL) {\n            psutil_debug(\"CPU_ALLOC() failed\");\n            return PyErr_NoMemory();\n        }\n        if (sched_getaffinity(pid, setsize, mask) == 0)\n            break;\n        CPU_FREE(mask);\n        if (errno != EINVAL)\n            return psutil_oserror();\n        if (ncpus > INT_MAX / 2) {\n            PyErr_SetString(\n                PyExc_OverflowError,\n                \"could not allocate \"\n                \"a large enough CPU set\"\n            );\n            return NULL;\n        }\n        ncpus = ncpus * 2;\n    }\n\n    py_list = PyList_New(0);\n    if (py_list == NULL)\n        goto error;\n\n    cpucount_s = CPU_COUNT_S(setsize, mask);\n    for (cpu = 0, count = cpucount_s; count; cpu++) {\n        if (CPU_ISSET_S(cpu, setsize, mask)) {\n            if (!pylist_append_obj(py_list, PyLong_FromLong(cpu)))\n                goto error;\n            --count;\n        }\n    }\n    CPU_FREE(mask);\n    return py_list;\n\nerror:\n    if (mask)\n        CPU_FREE(mask);\n    Py_XDECREF(py_list);\n    return NULL;\n}\n\n\n// Set process CPU affinity; expects a bitmask.\nPyObject *\npsutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {\n    cpu_set_t cpu_set;\n    size_t len;\n    pid_t pid;\n    Py_ssize_t i, seq_len;\n    PyObject *py_cpu_set;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"O\", &pid, &py_cpu_set))\n        return NULL;\n\n    if (!PySequence_Check(py_cpu_set)) {\n        return PyErr_Format(\n            PyExc_TypeError,\n            \"sequence argument expected, got %R\",\n            Py_TYPE(py_cpu_set)\n        );\n    }\n\n    seq_len = PySequence_Size(py_cpu_set);\n    if (seq_len < 0) {\n        return NULL;\n    }\n    CPU_ZERO(&cpu_set);\n    for (i = 0; i < seq_len; i++) {\n        PyObject *item = PySequence_GetItem(py_cpu_set, i);\n        if (!item) {\n            return NULL;\n        }\n        long value = PyLong_AsLong(item);\n        Py_XDECREF(item);\n        if ((value == -1) || PyErr_Occurred()) {\n            if (!PyErr_Occurred())\n                PyErr_SetString(PyExc_ValueError, \"invalid CPU value\");\n            return NULL;\n        }\n        CPU_SET(value, &cpu_set);\n    }\n\n    len = sizeof(cpu_set);\n    if (sched_setaffinity(pid, len, &cpu_set)) {\n        return psutil_oserror();\n    }\n\n    Py_RETURN_NONE;\n}\n#endif  // PSUTIL_HAS_CPU_AFFINITY\n"
  },
  {
    "path": "psutil/arch/netbsd/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sched.h>\n#include <sys/sysctl.h>\n#include <uvm/uvm_extern.h>\n\n#include \"../../arch/all/init.h\"\n\n\n/*\nCPU related functions. Original code was refactored and moved from\npsutil/arch/netbsd/specific.c in 2023 (and was moved in there previously\nalready) from cset 84219ad. For reference, here's the git history with\noriginal(ish) implementations:\n- per CPU times: 312442ad2a5b5d0c608476c5ab3e267735c3bc59 (Jan 2016)\n- CPU stats: a991494e4502e1235ebc62b5ba450287d0dedec0 (Jan 2016)\n*/\n\n\nPyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    struct uvmexp_sysctl uv;\n    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2};\n\n    if (psutil_sysctl(uvmexp_mib, 2, &uv, sizeof(uv)) != 0)\n        return NULL;\n    return Py_BuildValue(\n        \"IIIIIII\",\n        uv.swtch,  // ctx switches\n        uv.intrs,  // interrupts - XXX always 0, will be determined via /proc\n        uv.softs,  // soft interrupts\n        uv.syscalls,  // syscalls - XXX always 0\n        uv.traps,  // traps\n        uv.faults,  // faults\n        uv.forks  // forks\n    );\n}\n\n\nPyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    int mib[3];\n    int ncpu;\n    size_t len;\n    size_t size;\n    int i;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    // retrieve the number of cpus\n    mib[0] = CTL_HW;\n    mib[1] = HW_NCPU;\n    if (psutil_sysctl(mib, 2, &ncpu, sizeof(ncpu)) != 0)\n        goto error;\n\n    uint64_t cpu_time[CPUSTATES];\n\n    for (i = 0; i < ncpu; i++) {\n        // per-cpu info\n        mib[0] = CTL_KERN;\n        mib[1] = KERN_CP_TIME;\n        mib[2] = i;\n        if (psutil_sysctl(mib, 3, &cpu_time, sizeof(cpu_time)) != 0)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(ddddd)\",\n                (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC\n            ))\n        {\n            goto error;\n        }\n    }\n\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/netbsd/disk.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\nDisk related functions. Original code was refactored and moved from\npsutil/arch/netbsd/specific.c in 2023 (and was moved in there previously\nalready) from cset 84219ad. For reference, here's the git history with\noriginal(ish) implementations:\n- disk IO counters: 312442ad2a5b5d0c608476c5ab3e267735c3bc59 (Jan 2016)\n*/\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/disk.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    int i, dk_ndrive;\n    int mib[3];\n    struct io_sysctl *stats = NULL;\n    size_t stats_len;\n    PyObject *py_disk_info = NULL;\n    PyObject *py_retdict = PyDict_New();\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    mib[0] = CTL_HW;\n    mib[1] = HW_IOSTATS;\n    mib[2] = sizeof(struct io_sysctl);\n\n    // Use helper to allocate and fill buffer\n    if (psutil_sysctl_malloc(mib, 3, (char **)&stats, &stats_len) == -1)\n        goto error;\n\n    dk_ndrive = (int)(stats_len / sizeof(struct io_sysctl));\n\n    for (i = 0; i < dk_ndrive; i++) {\n        py_disk_info = Py_BuildValue(\n            \"(KKKK)\",\n            stats[i].rxfer,\n            stats[i].wxfer,\n            stats[i].rbytes,\n            stats[i].wbytes\n        );\n        if (!py_disk_info)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, stats[i].name, py_disk_info))\n            goto error;\n        Py_DECREF(py_disk_info);\n    }\n\n    free(stats);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_disk_info);\n    Py_DECREF(py_retdict);\n    if (stats != NULL)\n        free(stats);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/netbsd/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'.\n * Copyright (c) 2015, Ryo ONODERA.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/types.h>\n\nint _psutil_pids(pid_t **pids_array, int *pids_count);\n\nPyObject *psutil_cpu_stats(PyObject *self, PyObject *args);\nPyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_net_connections(PyObject *, PyObject *);\nPyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cmdline(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cwd(PyObject *self, PyObject *args);\nPyObject *psutil_proc_exe(PyObject *self, PyObject *args);\nPyObject *psutil_proc_net_connections(PyObject *, PyObject *);\nPyObject *psutil_proc_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_threads(PyObject *self, PyObject *args);\nPyObject *psutil_proc_threads(PyObject *self, PyObject *args);\nPyObject *psutil_virtual_mem(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/netbsd/mem.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\nMemory related functions. Original code was refactored and moved from\npsutil/arch/netbsd/specific.c in 2023 (and was moved in there previously\nalready) from cset 84219ad. For reference, here's the git history with\noriginal(ish) implementations:\n- virtual memory: 0749a69c01b374ca3e2180aaafc3c95e3b2d91b9 (Oct 2016)\n- swap memory: 312442ad2a5b5d0c608476c5ab3e267735c3bc59 (Jan 2016)\n*/\n\n#include <Python.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/sysctl.h>\n#include <sys/vmmeter.h>\n#include <uvm/uvm_extern.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// Virtual memory stats, taken from:\n// https://github.com/zabbix/zabbix/blob/master/src/libs/zbxsysinfo/netbsd/memory.c\nPyObject *\npsutil_virtual_mem(PyObject *self, PyObject *args) {\n    struct uvmexp_sysctl uv;\n    struct vmtotal vmdata;\n    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2};\n    int vmmeter_mib[] = {CTL_VM, VM_METER};\n    unsigned long long total, free, active, inactive, wired, cached;\n    unsigned long long buffers, shared, used, avail;\n    double percent;\n    long pagesize = psutil_getpagesize();\n    FILE *f;\n    char line[256];\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    if (psutil_sysctl(uvmexp_mib, 2, &uv, sizeof(uv)) != 0)\n        goto error;\n    if (psutil_sysctl(vmmeter_mib, 2, &vmdata, sizeof(vmdata)) != 0)\n        goto error;\n\n    // get buffers from /proc/meminfo\n    buffers = 0;\n    f = fopen(\"/proc/meminfo\", \"r\");\n    if (f != NULL) {\n        while (fgets(line, sizeof(line), f) != NULL) {\n            if (strncmp(line, \"Buffers:\", 8) == 0) {\n                sscanf(line + 8, \"%llu\", &buffers);\n                break;\n            }\n        }\n        fclose(f);\n        buffers *= 1024;\n    }\n\n    total = (unsigned long long)uv.npages << uv.pageshift;\n    free = (unsigned long long)uv.free << uv.pageshift;\n    active = (unsigned long long)uv.active << uv.pageshift;\n    inactive = (unsigned long long)uv.inactive << uv.pageshift;\n    wired = (unsigned long long)uv.wired << uv.pageshift;\n    // Also in /proc/meminfo as MemShared\n    shared = (unsigned long long)(vmdata.t_vmshr + vmdata.t_rmshr) * pagesize;\n\n    // Note: zabbix does not include anonpages, but that doesn't match\n    // the \"Cached\" value in /proc/meminfo.\n    // https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/\n    //   zbxsysinfo/netbsd/memory.c#L182\n    cached = (unsigned long long)(uv.filepages + uv.execpages + uv.anonpages)\n             << uv.pageshift;\n\n    // Before avail was calculated as (inactive + cached + free), same\n    // as zabbix, but it turned out it could exceed total (see #2233),\n    // so zabbix seems to be wrong. Htop calculates it differently, and\n    // the used value seem more realistic, so let's match htop.\n    // https://github.com/htop-dev/htop/blob/e7f447b/netbsd/NetBSDProcessList.c#L162\n    // https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/netbsd/memory.c#L135\n    used = active + wired;\n    avail = total - used;\n    percent = psutil_usage_percent((double)(total - avail), (double)total, 1);\n\n    if (!(pydict_add(dict, \"total\", \"K\", total)\n          | pydict_add(dict, \"available\", \"K\", avail)\n          | pydict_add(dict, \"percent\", \"d\", percent)\n          | pydict_add(dict, \"used\", \"K\", used)\n          | pydict_add(dict, \"free\", \"K\", free)\n          | pydict_add(dict, \"active\", \"K\", active)\n          | pydict_add(dict, \"inactive\", \"K\", inactive)\n          | pydict_add(dict, \"buffers\", \"K\", buffers)\n          | pydict_add(dict, \"wired\", \"K\", wired)\n          | pydict_add(dict, \"cached\", \"K\", cached)\n          | pydict_add(dict, \"shared\", \"K\", shared)))\n        goto error;\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/netbsd/pids.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <stdlib.h>\n#include <kvm.h>\n#include <sys/sysctl.h>\n\n#include \"../../arch/all/init.h\"\n\n\nint\n_psutil_pids(pid_t **pids_array, int *pids_count) {\n    char errbuf[_POSIX2_LINE_MAX];\n    kvm_t *kd;\n    struct kinfo_proc2 *proc_list = NULL;\n    struct kinfo_proc2 *result;\n    int cnt;\n    size_t i;\n\n    *pids_array = NULL;\n    *pids_count = 0;\n\n    kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);\n    if (kd == NULL) {\n        psutil_runtime_error(\"kvm_openfiles() failed: %s\", errbuf);\n        return -1;\n    }\n\n    result = kvm_getproc2(\n        kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &cnt\n    );\n    if (result == NULL) {\n        psutil_runtime_error(\"kvm_getproc2() failed\");\n        kvm_close(kd);\n        return -1;\n    }\n\n    if (cnt == 0) {\n        psutil_runtime_error(\"no PIDs found\");\n        kvm_close(kd);\n        return -1;\n    }\n\n    *pids_array = malloc(cnt * sizeof(pid_t));\n    if (!*pids_array) {\n        PyErr_NoMemory();\n        kvm_close(kd);\n        return -1;\n    }\n\n    for (i = 0; i < (size_t)cnt; i++) {\n        (*pids_array)[i] = result[i].p_pid;\n    }\n\n    *pids_count = cnt;\n    kvm_close(kd);\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/netbsd/proc.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Platform-specific module methods for NetBSD.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <kvm.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_proc_cwd(PyObject *self, PyObject *args) {\n    long pid;\n    char path[MAXPATHLEN];\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n#ifdef KERN_PROC_CWD  // available since NetBSD 99.43\n    int name[] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD};\n    size_t pathlen = sizeof(path);\n\n    if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0)\n        goto error;\n#else\n    char buf[32];\n    ssize_t len;\n\n    str_format(buf, sizeof(buf), \"/proc/%d/cwd\", (int)pid);\n    len = readlink(buf, path, sizeof(path) - 1);\n    if (len == -1)\n        goto error;\n    path[len] = '\\0';\n#endif\n\n    return PyUnicode_DecodeFSDefault(path);\n\nerror:\n    if (errno == ENOENT)\n        psutil_oserror_nsp(\"sysctl -> ENOENT\");\n    else\n        psutil_oserror();\n    return NULL;\n}\n\n\n// XXX: This is no longer used as per\n// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820\n// Current implementation uses /proc instead.\n// Left here just in case.\n/*\nPyObject *\npsutil_proc_exe(PyObject *self, PyObject *args) {\n#if __NetBSD_Version__ >= 799000000\n    pid_t pid;\n    char pathname[MAXPATHLEN];\n    int error;\n    int mib[4];\n    int ret;\n    size_t size;\n\n    if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (pid == 0) {\n        // else returns ENOENT\n        return PyUnicode_FromString(\"\");\n    }\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC_ARGS;\n    mib[2] = pid;\n    mib[3] = KERN_PROC_PATHNAME;\n\n    size = sizeof(pathname);\n    error = sysctl(mib, 4, NULL, &size, NULL, 0);\n    if (error == -1) {\n        psutil_oserror();\n        return NULL;\n    }\n\n    error = sysctl(mib, 4, pathname, &size, NULL, 0);\n    if (error == -1) {\n        psutil_oserror();\n        return NULL;\n    }\n    if (size == 0 || strlen(pathname) == 0) {\n        ret = psutil_pid_exists(pid);\n        if (ret == -1)\n            return NULL;\n        else if (ret == 0)\n            return psutil_oserror_nsp(\"psutil_pid_exists -> 0\");\n        else\n            str_copy(pathname, sizeof(pathname), \"\");\n    }\n\n    return PyUnicode_DecodeFSDefault(pathname);\n#else\n    return Py_BuildValue(\"s\", \"\");\n#endif\n}\n*/\n\n\nPyObject *\npsutil_proc_num_threads(PyObject *self, PyObject *args) {\n    long pid;\n    struct kinfo_proc2 kp;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_kinfo_proc(pid, &kp) == -1)\n        return NULL;\n    return Py_BuildValue(\"l\", (long)kp.p_nlwps);\n}\n\n\nPyObject *\npsutil_proc_threads(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int mib[5];\n    int i, nlwps;\n    ssize_t st;\n    size_t size;\n    struct kinfo_lwp *kl = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_LWP;\n    mib[2] = pid;\n    mib[3] = sizeof(struct kinfo_lwp);\n    mib[4] = 0;\n\n    // first query size\n    st = sysctl(mib, 5, NULL, &size, NULL, 0);\n    if (st == -1) {\n        psutil_oserror();\n        goto error;\n    }\n    if (size == 0) {\n        psutil_oserror_nsp(\"sysctl (size = 0)\");\n        goto error;\n    }\n\n    // set slot count for NetBSD KERN_LWP\n    mib[4] = size / sizeof(size_t);\n\n    if (psutil_sysctl_malloc(mib, __arraycount(mib), (char **)&kl, &size) != 0)\n    {\n        goto error;\n    }\n    if (size == 0) {\n        psutil_oserror_nsp(\"sysctl (size = 0)\");\n        goto error;\n    }\n\n    nlwps = (int)(size / sizeof(struct kinfo_lwp));\n    for (i = 0; i < nlwps; i++) {\n        if (kl[i].l_stat == LSIDL || kl[i].l_stat == LSZOMB)\n            continue;\n        // XXX: return 2 \"user\" times, no \"system\" time available\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"idd\",\n                kl[i].l_lid,\n                PSUTIL_KPT2DOUBLE(kl[i].l_rtime),\n                PSUTIL_KPT2DOUBLE(kl[i].l_rtime)\n            ))\n        {\n            goto error;\n        }\n    }\n\n    free(kl);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (kl != NULL)\n        free(kl);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_cmdline(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int mib[4];\n    int st;\n    int attempt = 0;\n    int max_attempts = 50;\n    size_t len = 0;\n    size_t pos = 0;\n    char *procargs = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC_ARGS;\n    mib[2] = pid;\n    mib[3] = KERN_PROC_ARGV;\n\n    while (1) {\n        if (psutil_sysctl_malloc(\n                mib, __arraycount(mib), (char **)&procargs, &len\n            )\n            != 0)\n        {\n            if (errno == EBUSY) {\n                // Usually happens with TestProcess.test_long_cmdline. See:\n                // https://github.com/giampaolo/psutil/issues/2250\n                attempt += 1;\n                if (attempt < max_attempts) {\n                    psutil_debug(\"proc %zu cmdline(): retry on EBUSY\", pid);\n                    continue;\n                }\n                else {\n                    psutil_debug(\n                        \"proc %zu cmdline(): return [] due to EBUSY\", pid\n                    );\n                    return py_retlist;\n                }\n            }\n            goto error;\n        }\n        break;\n    }\n\n    if (len > 0) {\n        while (pos < len) {\n            if (!pylist_append_obj(\n                    py_retlist, PyUnicode_DecodeFSDefault(&procargs[pos])\n                ))\n                goto error;\n            pos = pos + strlen(&procargs[pos]) + 1;\n        }\n    }\n\n    free(procargs);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (procargs != NULL)\n        free(procargs);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_num_fds(PyObject *self, PyObject *args) {\n    long pid;\n    int cnt;\n\n    struct kinfo_file *freep;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    errno = 0;\n    freep = kinfo_getfile(pid, &cnt);\n    if (freep == NULL) {\n        psutil_raise_for_pid(pid, \"kinfo_getfile()\");\n        return NULL;\n    }\n    free(freep);\n\n    return Py_BuildValue(\"i\", cnt);\n}\n"
  },
  {
    "path": "psutil/arch/netbsd/socks.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'.\n * Copyright (c) 2015, Ryo ONODERA.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <sys/queue.h>\n#include <sys/un.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// kinfo_file results\nstruct kif {\n    SLIST_ENTRY(kif) kifs;\n    struct kinfo_file *kif;\n    char *buf;\n    int has_buf;\n};\n\n// kinfo_file results list\nSLIST_HEAD(kifhead, kif) kihead = SLIST_HEAD_INITIALIZER(kihead);\n\n\n// kinfo_pcb results\nstruct kpcb {\n    SLIST_ENTRY(kpcb) kpcbs;\n    struct kinfo_pcb *kpcb;\n    struct kinfo_pcb *buf;\n    int has_buf;\n};\n\n// kinfo_pcb results list\nSLIST_HEAD(kpcbhead, kpcb) kpcbhead = SLIST_HEAD_INITIALIZER(kpcbhead);\n\n\n// Initialize kinfo_file results list.\nstatic void\npsutil_kiflist_init(void) {\n    SLIST_INIT(&kihead);\n}\n\n\n// Clear kinfo_file results list.\nstatic void\npsutil_kiflist_clear(void) {\n    while (!SLIST_EMPTY(&kihead)) {\n        struct kif *kif = SLIST_FIRST(&kihead);\n        if (kif->has_buf == 1)\n            free(kif->buf);\n        free(kif);\n        SLIST_REMOVE_HEAD(&kihead, kifs);\n    }\n}\n\n\n// Initialize kinof_pcb result list.\nstatic void\npsutil_kpcblist_init(void) {\n    SLIST_INIT(&kpcbhead);\n}\n\n\n// Clear kinof_pcb result list.\nstatic void\npsutil_kpcblist_clear(void) {\n    while (!SLIST_EMPTY(&kpcbhead)) {\n        struct kpcb *kpcb = SLIST_FIRST(&kpcbhead);\n        if (kpcb->has_buf == 1)\n            free(kpcb->buf);\n        free(kpcb);\n        SLIST_REMOVE_HEAD(&kpcbhead, kpcbs);\n    }\n}\n\n\n// Get all open files including socket.\nstatic int\npsutil_get_files(void) {\n    size_t len;\n    size_t j;\n    int mib[6];\n    char *buf = NULL;\n    off_t offset;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_FILE2;\n    mib[2] = KERN_FILE_BYFILE;\n    mib[3] = 0;\n    mib[4] = sizeof(struct kinfo_file);\n    mib[5] = 0;\n\n    if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    offset = len % sizeof(off_t);\n    mib[5] = len / sizeof(struct kinfo_file);\n\n    if ((buf = malloc(len + offset)) == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    if (sysctl(mib, 6, buf + offset, &len, NULL, 0) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    len /= sizeof(struct kinfo_file);\n    struct kinfo_file *ki = (struct kinfo_file *)(buf + offset);\n\n    if (len > 0) {\n        for (j = 0; j < len; j++) {\n            struct kif *kif = malloc(sizeof(struct kif));\n            if (kif == NULL) {\n                PyErr_NoMemory();\n                goto error;\n            }\n            kif->kif = &ki[j];\n            if (j == 0) {\n                kif->has_buf = 1;\n                kif->buf = buf;\n            }\n            else {\n                kif->has_buf = 0;\n                kif->buf = NULL;\n            }\n            SLIST_INSERT_HEAD(&kihead, kif, kifs);\n        }\n    }\n    else {\n        free(buf);\n    }\n\n    return 0;\n\nerror:\n    if (buf != NULL)\n        free(buf);\n    return -1;\n}\n\n\n// Get open sockets.\nstatic int\npsutil_get_sockets(const char *name) {\n    size_t namelen;\n    int mib[8];\n    struct kinfo_pcb *pcb = NULL;\n    size_t len;\n    size_t j;\n\n    memset(mib, 0, sizeof(mib));\n\n    if (sysctlnametomib(name, mib, &namelen) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    if ((pcb = malloc(len)) == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n    memset(pcb, 0, len);\n\n    mib[6] = sizeof(*pcb);\n    mib[7] = len / sizeof(*pcb);\n\n    if (sysctl(mib, __arraycount(mib), pcb, &len, NULL, 0) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    len /= sizeof(struct kinfo_pcb);\n    struct kinfo_pcb *kp = (struct kinfo_pcb *)pcb;\n\n    if (len > 0) {\n        for (j = 0; j < len; j++) {\n            struct kpcb *kpcb = malloc(sizeof(struct kpcb));\n            if (kpcb == NULL) {\n                PyErr_NoMemory();\n                goto error;\n            }\n            kpcb->kpcb = &kp[j];\n            if (j == 0) {\n                kpcb->has_buf = 1;\n                kpcb->buf = pcb;\n            }\n            else {\n                kpcb->has_buf = 0;\n                kpcb->buf = NULL;\n            }\n            SLIST_INSERT_HEAD(&kpcbhead, kpcb, kpcbs);\n        }\n    }\n    else {\n        free(pcb);\n    }\n\n    return 0;\n\nerror:\n    if (pcb != NULL)\n        free(pcb);\n    return -1;\n}\n\n\n// Collect open file and connections.\nstatic int\npsutil_get_info(char *kind) {\n    if (strcmp(kind, \"inet\") == 0) {\n        if (psutil_get_sockets(\"net.inet.tcp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet.udp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.tcp6.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.udp6.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"inet4\") == 0) {\n        if (psutil_get_sockets(\"net.inet.tcp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet.udp.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"inet6\") == 0) {\n        if (psutil_get_sockets(\"net.inet6.tcp6.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.udp6.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"tcp\") == 0) {\n        if (psutil_get_sockets(\"net.inet.tcp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.tcp6.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"tcp4\") == 0) {\n        if (psutil_get_sockets(\"net.inet.tcp.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"tcp6\") == 0) {\n        if (psutil_get_sockets(\"net.inet6.tcp6.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"udp\") == 0) {\n        if (psutil_get_sockets(\"net.inet.udp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.udp6.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"udp4\") == 0) {\n        if (psutil_get_sockets(\"net.inet.udp.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"udp6\") == 0) {\n        if (psutil_get_sockets(\"net.inet6.udp6.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"unix\") == 0) {\n        if (psutil_get_sockets(\"net.local.stream.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.local.seqpacket.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.local.dgram.pcblist\") != 0)\n            return -1;\n    }\n    else if (strcmp(kind, \"all\") == 0) {\n        if (psutil_get_sockets(\"net.inet.tcp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet.udp.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.tcp6.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.inet6.udp6.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.local.stream.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.local.seqpacket.pcblist\") != 0)\n            return -1;\n        if (psutil_get_sockets(\"net.local.dgram.pcblist\") != 0)\n            return -1;\n    }\n    else {\n        PyErr_SetString(PyExc_ValueError, \"invalid kind value\");\n        return -1;\n    }\n    return 0;\n}\n\n\n/*\n * Return system-wide connections (unless a pid != -1 is passed).\n */\nPyObject *\npsutil_net_connections(PyObject *self, PyObject *args) {\n    char laddr[PATH_MAX];\n    char raddr[PATH_MAX];\n    char *kind;\n    int32_t lport;\n    int32_t rport;\n    int32_t status;\n    pid_t pid;\n    PyObject *py_laddr = NULL;\n    PyObject *py_raddr = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"s\", &pid, &kind)) {\n        goto error;\n    }\n\n    psutil_kiflist_init();\n    psutil_kpcblist_init();\n\n    if (psutil_get_files() != 0)\n        goto error;\n    if (psutil_get_info(kind) != 0)\n        goto error;\n\n    struct kif *k;\n    SLIST_FOREACH(k, &kihead, kifs) {\n        struct kpcb *kp;\n        if ((pid != -1) && (k->kif->ki_pid != (unsigned int)pid))\n            continue;\n        SLIST_FOREACH(kp, &kpcbhead, kpcbs) {\n            if (k->kif->ki_fdata != kp->kpcb->ki_sockaddr)\n                continue;\n\n            // IPv4 or IPv6\n            if ((kp->kpcb->ki_family == AF_INET)\n                || (kp->kpcb->ki_family == AF_INET6))\n            {\n                if (kp->kpcb->ki_family == AF_INET) {\n                    // IPv4\n                    struct sockaddr_in *sin_src = (struct sockaddr_in *)&kp\n                                                      ->kpcb->ki_src;\n                    struct sockaddr_in *sin_dst = (struct sockaddr_in *)&kp\n                                                      ->kpcb->ki_dst;\n                    // source addr and port\n                    inet_ntop(\n                        AF_INET, &sin_src->sin_addr, laddr, sizeof(laddr)\n                    );\n                    lport = ntohs(sin_src->sin_port);\n                    // remote addr and port\n                    inet_ntop(\n                        AF_INET, &sin_dst->sin_addr, raddr, sizeof(raddr)\n                    );\n                    rport = ntohs(sin_dst->sin_port);\n                }\n                else {\n                    // IPv6\n                    struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)&kp\n                                                        ->kpcb->ki_src;\n                    struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)&kp\n                                                        ->kpcb->ki_dst;\n                    // local addr and port\n                    inet_ntop(\n                        AF_INET6, &sin6_src->sin6_addr, laddr, sizeof(laddr)\n                    );\n                    lport = ntohs(sin6_src->sin6_port);\n                    // remote addr and port\n                    inet_ntop(\n                        AF_INET6, &sin6_dst->sin6_addr, raddr, sizeof(raddr)\n                    );\n                    rport = ntohs(sin6_dst->sin6_port);\n                }\n\n                // status\n                if (kp->kpcb->ki_type == SOCK_STREAM)\n                    status = kp->kpcb->ki_tstate;\n                else\n                    status = PSUTIL_CONN_NONE;\n\n                // build addr tuple\n                py_laddr = Py_BuildValue(\"(si)\", laddr, lport);\n                if (!py_laddr)\n                    goto error;\n                if (rport != 0)\n                    py_raddr = Py_BuildValue(\"(si)\", raddr, rport);\n                else\n                    py_raddr = Py_BuildValue(\"()\");\n                if (!py_raddr)\n                    goto error;\n            }\n            else if (kp->kpcb->ki_family == AF_UNIX) {\n                // UNIX sockets\n                struct sockaddr_un *sun_src = (struct sockaddr_un *)&kp->kpcb\n                                                  ->ki_src;\n                struct sockaddr_un *sun_dst = (struct sockaddr_un *)&kp->kpcb\n                                                  ->ki_dst;\n                str_copy(laddr, sizeof(sun_src->sun_path), sun_src->sun_path);\n                str_copy(raddr, sizeof(sun_dst->sun_path), sun_dst->sun_path);\n                status = PSUTIL_CONN_NONE;\n                py_laddr = PyUnicode_DecodeFSDefault(laddr);\n                if (!py_laddr)\n                    goto error;\n                py_raddr = PyUnicode_DecodeFSDefault(raddr);\n                if (!py_raddr)\n                    goto error;\n            }\n            else {\n                continue;\n            }\n\n            // append tuple to list\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiOOii)\",\n                    k->kif->ki_fd,\n                    kp->kpcb->ki_family,\n                    kp->kpcb->ki_type,\n                    py_laddr,\n                    py_raddr,\n                    status,\n                    k->kif->ki_pid\n                ))\n            {\n                goto error;\n            }\n            Py_DECREF(py_laddr);\n            py_laddr = NULL;\n            Py_DECREF(py_raddr);\n            py_raddr = NULL;\n        }\n    }\n\n    psutil_kiflist_clear();\n    psutil_kpcblist_clear();\n    return py_retlist;\n\nerror:\n    psutil_kiflist_clear();\n    psutil_kpcblist_clear();\n    Py_DECREF(py_retlist);\n    Py_XDECREF(py_laddr);\n    Py_XDECREF(py_raddr);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/sched.h>  // for CPUSTATES & CP_*\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    int mib[3];\n    int ncpu;\n    size_t len;\n    int i;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    // retrieve the number of cpus\n    mib[0] = CTL_HW;\n    mib[1] = HW_NCPU;\n    if (psutil_sysctl(mib, 2, &ncpu, sizeof(ncpu)) != 0)\n        goto error;\n\n    uint64_t cpu_time[CPUSTATES];\n\n    for (i = 0; i < ncpu; i++) {\n        mib[0] = CTL_KERN;\n        mib[1] = KERN_CPTIME2;\n        mib[2] = i;\n\n        if (psutil_sysctl(mib, 3, &cpu_time, sizeof(cpu_time)) != 0)\n            goto error;\n\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(ddddd)\",\n                (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,\n                (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC\n            ))\n        {\n            goto error;\n        }\n    }\n\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n\n\nPyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    struct uvmexp uv;\n    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};\n\n    if (psutil_sysctl(uvmexp_mib, 2, &uv, sizeof(uv)) != 0)\n        return NULL;\n\n    return Py_BuildValue(\n        \"IIIIIII\",\n        uv.swtch,  // ctx switches\n        uv.intrs,  // interrupts - XXX always 0, will be determined via /proc\n        uv.softs,  // soft interrupts\n        uv.syscalls,  // syscalls - XXX always 0\n        uv.traps,  // traps\n        uv.faults,  // faults\n        uv.forks  // forks\n    );\n}\n\n\nPyObject *\npsutil_cpu_freq(PyObject *self, PyObject *args) {\n    int freq;\n    int mib[2] = {CTL_HW, HW_CPUSPEED};\n\n    // On VirtualBox I get \"sysctl hw.cpuspeed=2593\" (never changing),\n    // which appears to be expressed in Mhz.\n    if (psutil_sysctl(mib, 2, &freq, sizeof(freq)) != 0)\n        return NULL;\n\n    return Py_BuildValue(\"i\", freq);\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/disk.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/disk.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    int i, dk_ndrive, mib[3];\n    size_t len;\n    struct diskstats *stats = NULL;\n\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_disk_info = NULL;\n    if (py_retdict == NULL)\n        return NULL;\n\n    mib[0] = CTL_HW;\n    mib[1] = HW_DISKSTATS;\n\n    if (psutil_sysctl_malloc(mib, 2, (char **)&stats, &len) != 0)\n        goto error;\n\n    dk_ndrive = (int)(len / sizeof(struct diskstats));\n\n    for (i = 0; i < dk_ndrive; i++) {\n        py_disk_info = Py_BuildValue(\n            \"(KKKK)\",\n            stats[i].ds_rxfer,  // num reads\n            stats[i].ds_wxfer,  // num writes\n            stats[i].ds_rbytes,  // read bytes\n            stats[i].ds_wbytes  // write bytes\n        );\n        if (!py_disk_info)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, stats[i].ds_name, py_disk_info))\n            goto error;\n        Py_DECREF(py_disk_info);\n    }\n\n    free(stats);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_disk_info);\n    Py_DECREF(py_retdict);\n    if (stats != NULL)\n        free(stats);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\nint _psutil_pids(pid_t **pids_array, int *pids_count);\n\nPyObject *psutil_cpu_freq(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_stats(PyObject *self, PyObject *args);\nPyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cmdline(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cwd(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);\nPyObject *psutil_proc_threads(PyObject *self, PyObject *args);\nPyObject *psutil_users(PyObject *self, PyObject *args);\nPyObject *psutil_virtual_mem(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/openbsd/mem.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/vmmeter.h>\n#include <sys/mount.h>\n#include <sys/param.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_virtual_mem(PyObject *self, PyObject *args) {\n    int64_t _total;\n    unsigned long long free, active, inactive, wired, cached, shared;\n    unsigned long long total, avail, used;\n    double percent;\n    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};\n    int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};\n    int physmem_mib[] = {CTL_HW, HW_PHYSMEM64};\n    int vmmeter_mib[] = {CTL_VM, VM_METER};\n    struct uvmexp uvmexp;\n    struct bcachestats bcstats;\n    struct vmtotal vmdata;\n    long pagesize = psutil_getpagesize();\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    // Note: many programs calculate total memory as \"uvmexp.npages *\n    // pagesize\" but this is incorrect and does not match \"sysctl |\n    // grep hw.physmem\".\n    if (psutil_sysctl(physmem_mib, 2, &_total, sizeof(_total)) != 0)\n        goto error;\n    if (psutil_sysctl(uvmexp_mib, 2, &uvmexp, sizeof(uvmexp)) != 0)\n        goto error;\n    if (psutil_sysctl(bcstats_mib, 3, &bcstats, sizeof(bcstats)) != 0)\n        goto error;\n    if (psutil_sysctl(vmmeter_mib, 2, &vmdata, sizeof(vmdata)) != 0)\n        goto error;\n\n    total = (unsigned long long)_total;\n    free = (unsigned long long)uvmexp.free * pagesize;\n    active = (unsigned long long)uvmexp.active * pagesize;\n    inactive = (unsigned long long)uvmexp.inactive * pagesize;\n    wired = (unsigned long long)uvmexp.wired * pagesize;\n    shared = (unsigned long long)vmdata.t_vmshr + vmdata.t_rmshr;\n\n    // this is how \"top\" determines cached mem\n    cached = (unsigned long long)bcstats.numbufpages * pagesize;\n\n    // matches freebsd-memory CLI:\n    // * https://people.freebsd.org/~rse/dist/freebsd-memory\n    // * https://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt\n    // matches zabbix:\n    // * https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/\n    //   zbxsysinfo/freebsd/memory.c#L143\n    avail = inactive + cached + free;\n    used = active + wired + cached;\n    percent = psutil_usage_percent((double)(total - avail), (double)total, 1);\n\n    if (!(pydict_add(dict, \"total\", \"K\", total)\n          | pydict_add(dict, \"available\", \"K\", avail)\n          | pydict_add(dict, \"percent\", \"d\", percent)\n          | pydict_add(dict, \"used\", \"K\", used)\n          | pydict_add(dict, \"free\", \"K\", free)\n          | pydict_add(dict, \"active\", \"K\", active)\n          | pydict_add(dict, \"inactive\", \"K\", inactive)\n          | pydict_add(dict, \"buffers\", \"K\", 0ULL)\n          | pydict_add(dict, \"cached\", \"K\", cached)\n          | pydict_add(dict, \"shared\", \"K\", shared)\n          | pydict_add(dict, \"wired\", \"K\", wired)))\n        goto error;\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/pids.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <stdlib.h>\n#include <kvm.h>\n#include <sys/sysctl.h>\n\n#include \"../../arch/all/init.h\"\n\n\nint\n_psutil_pids(pid_t **pids_array, int *pids_count) {\n    char errbuf[_POSIX2_LINE_MAX];\n    kvm_t *kd;\n    struct kinfo_proc *proc_list = NULL;\n    struct kinfo_proc *result;\n    int cnt;\n    size_t i;\n\n    *pids_array = NULL;\n    *pids_count = 0;\n\n    kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);\n    if (kd == NULL) {\n        psutil_runtime_error(\"kvm_openfiles() failed: %s\", errbuf);\n        return -1;\n    }\n\n    result = kvm_getprocs(\n        kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cnt\n    );\n    if (result == NULL) {\n        psutil_runtime_error(\"kvm_getproc2() failed\");\n        kvm_close(kd);\n        return -1;\n    }\n\n    if (cnt == 0) {\n        psutil_runtime_error(\"no PIDs found\");\n        kvm_close(kd);\n        return -1;\n    }\n\n    *pids_array = malloc(cnt * sizeof(pid_t));\n    if (!*pids_array) {\n        PyErr_NoMemory();\n        kvm_close(kd);\n        return -1;\n    }\n\n    for (i = 0; i < (size_t)cnt; i++) {\n        (*pids_array)[i] = result[i].p_pid;\n    }\n\n    *pids_count = cnt;\n    kvm_close(kd);\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/proc.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/sysctl.h>\n#include <sys/proc.h>\n#include <kvm.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// TODO: refactor this (it's clunky)\nPyObject *\npsutil_proc_cmdline(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int mib[4];\n    char *argv_buf = NULL;\n    size_t argv_len = 0;\n    char **argv = NULL;\n    char **p;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC_ARGS;\n    mib[2] = pid;\n    mib[3] = KERN_PROC_ARGV;\n\n    if (psutil_sysctl_malloc(mib, 4, &argv_buf, &argv_len) == -1)\n        goto error;\n\n    argv = (char **)argv_buf;\n\n    for (p = argv; *p != NULL; p++) {\n        if (!pylist_append_obj(py_retlist, PyUnicode_DecodeFSDefault(*p)))\n            goto error;\n    }\n\n    free(argv_buf);\n    return py_retlist;\n\nerror:\n    if (argv_buf != NULL)\n        free(argv_buf);\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_threads(PyObject *self, PyObject *args) {\n    // OpenBSD reference:\n    // https://github.com/janmojzis/pstree/blob/master/proc_kvm.c\n    // Note: this requires root access, else it will fail trying\n    // to access /dev/kmem.\n    pid_t pid;\n    kvm_t *kd = NULL;\n    int nentries, i;\n    char errbuf[4096];\n    struct kinfo_proc *kp;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);\n    if (!kd) {\n        // Usually fails due to EPERM against /dev/mem. We retry with\n        // KVM_NO_FILES which apparently has the same effect.\n        // https://stackoverflow.com/questions/22369736/\n        psutil_debug(\"kvm_openfiles(O_RDONLY) failed\");\n        kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);\n        if (!kd) {\n            convert_kvm_err(\"kvm_openfiles()\", errbuf);\n            goto error;\n        }\n    }\n\n    kp = kvm_getprocs(\n        kd,\n        KERN_PROC_PID | KERN_PROC_SHOW_THREADS | KERN_PROC_KTHREAD,\n        pid,\n        sizeof(*kp),\n        &nentries\n    );\n    if (!kp) {\n        if (strstr(errbuf, \"Permission denied\") != NULL)\n            psutil_oserror_ad(\"kvm_getprocs\");\n        else\n            psutil_runtime_error(\"kvm_getprocs() syscall failed\");\n        goto error;\n    }\n\n    for (i = 0; i < nentries; i++) {\n        if (kp[i].p_tid < 0)\n            continue;\n        if (kp[i].p_pid == pid) {\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    _Py_PARSE_PID \"dd\",\n                    kp[i].p_tid,\n                    PSUTIL_KPT2DOUBLE(kp[i].p_uutime),\n                    PSUTIL_KPT2DOUBLE(kp[i].p_ustime)\n                ))\n            {\n                goto error;\n            }\n        }\n    }\n\n    kvm_close(kd);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (kd != NULL)\n        kvm_close(kd);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_num_fds(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int cnt;\n\n    struct kinfo_file *freep;\n    struct kinfo_proc kipp;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    if (psutil_kinfo_proc(pid, &kipp) == -1)\n        return NULL;\n\n    freep = kinfo_getfile(pid, &cnt);\n\n    if (freep == NULL) {\n#if defined(PSUTIL_OPENBSD)\n        if ((pid == 0) && (errno == ESRCH)) {\n            psutil_debug(\n                \"num_fds() returned ESRCH for PID 0; forcing `return 0`\"\n            );\n            PyErr_Clear();\n            return Py_BuildValue(\"i\", 0);\n        }\n#endif\n        return NULL;\n    }\n\n    free(freep);\n    return Py_BuildValue(\"i\", cnt);\n}\n\n\nPyObject *\npsutil_proc_cwd(PyObject *self, PyObject *args) {\n    // Reference:\n    // https://github.com/openbsd/src/blob/\n    //     588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L191\n    pid_t pid;\n    struct kinfo_proc kp;\n    char path[MAXPATHLEN];\n    size_t pathlen = sizeof path;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_kinfo_proc(pid, &kp) == -1)\n        return NULL;\n\n    int name[] = {CTL_KERN, KERN_PROC_CWD, pid};\n    if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) {\n        if (errno == ENOENT) {\n            psutil_debug(\"sysctl(KERN_PROC_CWD) -> ENOENT converted to ''\");\n            return PyUnicode_FromString(\"\");\n        }\n        else {\n            psutil_oserror();\n            return NULL;\n        }\n    }\n    return PyUnicode_DecodeFSDefault(path);\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/socks.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n#include <sys/socket.h>\n#include <kvm.h>\n#define _KERNEL  // silence compiler warning\n#include <sys/file.h>  // DTYPE_SOCKET\n#include <netdb.h>  // INET6_ADDRSTRLEN, in6_addr\n#undef _KERNEL\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_net_connections(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int i;\n    int cnt;\n    int state;\n    int lport;\n    int rport;\n    char lip[INET6_ADDRSTRLEN];\n    char rip[INET6_ADDRSTRLEN];\n    int inseq;\n\n    char errbuf[_POSIX2_LINE_MAX];\n    kvm_t *kd = NULL;\n\n    struct kinfo_file *kif;\n    struct kinfo_file *ikf;\n    struct in6_addr laddr6;\n\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_laddr = NULL;\n    PyObject *py_raddr = NULL;\n    PyObject *py_lpath = NULL;\n    PyObject *py_af_filter = NULL;\n    PyObject *py_type_filter = NULL;\n    PyObject *py_family = NULL;\n    PyObject *_type = NULL;\n\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(\n            args, _Py_PARSE_PID \"OO\", &pid, &py_af_filter, &py_type_filter\n        ))\n    {\n        goto error;\n    }\n    if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {\n        PyErr_SetString(PyExc_TypeError, \"arg 2 or 3 is not a sequence\");\n        goto error;\n    }\n\n    kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);\n    if (!kd) {\n        convert_kvm_err(\"kvm_openfiles\", errbuf);\n        goto error;\n    }\n\n    ikf = kvm_getfiles(kd, KERN_FILE_BYPID, -1, sizeof(*ikf), &cnt);\n    if (!ikf) {\n        psutil_oserror_wsyscall(\"kvm_getfiles\");\n        goto error;\n    }\n\n    for (int i = 0; i < cnt; i++) {\n        const struct kinfo_file *kif = ikf + i;\n        py_laddr = NULL;\n        py_raddr = NULL;\n        py_lpath = NULL;\n\n        // apply filters\n        if (kif->f_type != DTYPE_SOCKET)\n            continue;\n        if (pid != -1 && kif->p_pid != (uint32_t)pid)\n            continue;\n        py_family = PyLong_FromLong((long)kif->so_family);\n        inseq = PySequence_Contains(py_af_filter, py_family);\n        Py_DECREF(py_family);\n        if (inseq == 0)\n            continue;\n        _type = PyLong_FromLong((long)kif->so_type);\n        inseq = PySequence_Contains(py_type_filter, _type);\n        Py_DECREF(_type);\n        if (inseq == 0)\n            continue;\n\n        // IPv4 / IPv6 socket\n        if ((kif->so_family == AF_INET) || (kif->so_family == AF_INET6)) {\n            // status\n            if (kif->so_type == SOCK_STREAM)\n                state = kif->t_state;\n            else\n                state = PSUTIL_CONN_NONE;\n\n            // local & remote port\n            lport = ntohs(kif->inp_lport);\n            rport = ntohs(kif->inp_fport);\n\n            // local addr\n            inet_ntop(kif->so_family, &kif->inp_laddru, lip, sizeof(lip));\n            py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n            if (!py_laddr)\n                goto error;\n\n            // remote addr\n            if (rport != 0) {\n                inet_ntop(kif->so_family, &kif->inp_faddru, rip, sizeof(rip));\n                py_raddr = Py_BuildValue(\"(si)\", rip, rport);\n            }\n            else {\n                py_raddr = Py_BuildValue(\"()\");\n            }\n            if (!py_raddr)\n                goto error;\n\n            // populate tuple and list\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiNNil)\",\n                    kif->fd_fd,\n                    kif->so_family,\n                    kif->so_type,\n                    py_laddr,\n                    py_raddr,\n                    state,\n                    kif->p_pid\n                ))\n            {\n                goto error;\n            }\n            py_laddr = NULL;\n            py_raddr = NULL;\n        }\n        // UNIX socket\n        else if (kif->so_family == AF_UNIX) {\n            py_lpath = PyUnicode_DecodeFSDefault(kif->unp_path);\n            if (!py_lpath)\n                goto error;\n\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiOsil)\",\n                    kif->fd_fd,\n                    kif->so_family,\n                    kif->so_type,\n                    py_lpath,\n                    \"\",  // raddr\n                    PSUTIL_CONN_NONE,\n                    kif->p_pid\n                ))\n            {\n                goto error;\n            }\n            Py_DECREF(py_lpath);\n            py_lpath = NULL;\n        }\n    }\n\n    kvm_close(kd);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_laddr);\n    Py_XDECREF(py_raddr);\n    Py_XDECREF(py_lpath);\n    Py_DECREF(py_retlist);\n    if (kd != NULL)\n        kvm_close(kd);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/openbsd/users.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'.\n * All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <utmp.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_users(PyObject *self, PyObject *args) {\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_username = NULL;\n    PyObject *py_tty = NULL;\n    PyObject *py_hostname = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    struct utmp ut;\n    FILE *fp;\n\n    Py_BEGIN_ALLOW_THREADS\n    fp = fopen(_PATH_UTMP, \"r\");\n    Py_END_ALLOW_THREADS\n    if (fp == NULL) {\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, _PATH_UTMP);\n        goto error;\n    }\n\n    while (fread(&ut, sizeof(ut), 1, fp) == 1) {\n        if (*ut.ut_name == '\\0')\n            continue;\n        py_username = PyUnicode_DecodeFSDefault(ut.ut_name);\n        if (!py_username)\n            goto error;\n        py_tty = PyUnicode_DecodeFSDefault(ut.ut_line);\n        if (!py_tty)\n            goto error;\n        py_hostname = PyUnicode_DecodeFSDefault(ut.ut_host);\n        if (!py_hostname)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(OOOdO)\",\n                py_username,  // username\n                py_tty,  // tty\n                py_hostname,  // hostname\n                (double)ut.ut_time,  // start time\n                Py_None  // pid\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_username);\n        Py_CLEAR(py_tty);\n        Py_CLEAR(py_hostname);\n    }\n\n    fclose(fp);\n    return py_retlist;\n\nerror:\n    fclose(fp);\n    Py_XDECREF(py_username);\n    Py_XDECREF(py_tty);\n    Py_XDECREF(py_hostname);\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\nSystem-wide CPU related functions.\n\nOriginal code was refactored and moved from psutil/_psutil_osx.c in 2020\nright before a4c0a0eb0d2a872ab7a45e47fcf37ef1fde5b012.\nFor reference, here's the git history with original implementations:\n\n- CPU count logical: 3d291d425b856077e65163e43244050fb188def1\n- CPU count physical: 4263e354bb4984334bc44adf5dd2f32013d69fba\n- CPU times: 32488bdf54aed0f8cef90d639c1667ffaa3c31c7\n- CPU stat: fa00dfb961ef63426c7818899340866ced8d2418\n- CPU frequency: 6ba1ac4ebfcd8c95fca324b15606ab0ec1412d39\n*/\n\n#include <Python.h>\n#include <mach/mach_error.h>\n#include <mach/mach_host.h>\n#include <mach/mach_port.h>\n#include <mach/mach_vm.h>\n#include <sys/sysctl.h>\n#include <sys/vmmeter.h>\n#include <mach/mach.h>\n#if defined(__arm64__) || defined(__aarch64__)\n#include <CoreFoundation/CoreFoundation.h>\n#include <IOKit/IOKitLib.h>\n#endif\n\n#include \"../../arch/all/init.h\"\n\n// added in macOS 12\n#ifndef kIOMainPortDefault\n#define kIOMainPortDefault 0\n#endif\n\nPyObject *\npsutil_cpu_count_logical(PyObject *self, PyObject *args) {\n    int num;\n\n    if (psutil_sysctlbyname(\"hw.logicalcpu\", &num, sizeof(num)) != 0)\n        Py_RETURN_NONE;\n    return Py_BuildValue(\"i\", num);\n}\n\n\nPyObject *\npsutil_cpu_count_cores(PyObject *self, PyObject *args) {\n    int num;\n\n    if (psutil_sysctlbyname(\"hw.physicalcpu\", &num, sizeof(num)) != 0)\n        Py_RETURN_NONE;\n    return Py_BuildValue(\"i\", num);\n}\n\n\nPyObject *\npsutil_cpu_times(PyObject *self, PyObject *args) {\n    mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;\n    kern_return_t error;\n    host_cpu_load_info_data_t r_load;\n    mach_port_t mport = mach_host_self();\n\n    if (mport == MACH_PORT_NULL) {\n        psutil_runtime_error(\"mach_host_self() returned MACH_PORT_NULL\");\n        return NULL;\n    }\n\n    error = host_statistics(\n        mport, HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count\n    );\n    mach_port_deallocate(mach_task_self(), mport);\n\n    if (error != KERN_SUCCESS) {\n        return psutil_runtime_error(\n            \"host_statistics(HOST_CPU_LOAD_INFO) syscall failed: %s\",\n            mach_error_string(error)\n        );\n    }\n\n    return Py_BuildValue(\n        \"(dddd)\",\n        (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK,\n        (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK,\n        (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,\n        (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK\n    );\n}\n\n\nPyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    kern_return_t ret;\n    mach_msg_type_number_t count = HOST_VM_INFO_COUNT;\n    mach_port_t mport = mach_host_self();\n    struct vmmeter vmstat;\n\n    if (mport == MACH_PORT_NULL) {\n        psutil_runtime_error(\"mach_host_self() returned MACH_PORT_NULL\");\n        return NULL;\n    }\n\n    ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vmstat, &count);\n    mach_port_deallocate(mach_task_self(), mport);\n\n    if (ret != KERN_SUCCESS) {\n        psutil_runtime_error(\n            \"host_statistics(HOST_VM_INFO) failed: %s\", mach_error_string(ret)\n        );\n        return NULL;\n    }\n\n    return Py_BuildValue(\n        \"IIIII\",\n        vmstat.v_swtch,\n        vmstat.v_intr,\n        vmstat.v_soft,\n#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \\\n    && __MAC_OS_X_VERSION_MIN_REQUIRED__ >= 120000\n        0,\n#else\n        vmstat.v_syscall,\n#endif\n        vmstat.v_trap\n    );\n}\n\n\n#if defined(__arm64__) || defined(__aarch64__)\n\n// Helper to locate the 'pmgr' entry in AppleARMIODevice. Returns 0 on\n// failure, nonzero on success, and stores the found entry in\n// *out_entry. Caller is responsible for IOObjectRelease(*out_entry).\n// Needed because on GitHub CI sometimes (but not all the times)\n// \"AppleARMIODevice\" is not available.\nstatic int\npsutil_find_pmgr_entry(io_registry_entry_t *out_entry) {\n    kern_return_t status;\n    io_iterator_t iter = IO_OBJECT_NULL;\n    io_registry_entry_t entry = IO_OBJECT_NULL;\n    CFDictionaryRef matching = IOServiceMatching(\"AppleARMIODevice\");\n    int found = 0;\n\n    if (!out_entry || !matching)\n        return 0;\n\n    status = IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter);\n    if (status != KERN_SUCCESS || iter == IO_OBJECT_NULL)\n        return 0;\n\n    while ((entry = IOIteratorNext(iter)) != IO_OBJECT_NULL) {\n        io_name_t name;\n        if (IORegistryEntryGetName(entry, name) == KERN_SUCCESS\n            && strcmp(name, \"pmgr\") == 0)\n        {\n            found = 1;\n            break;\n        }\n        IOObjectRelease(entry);\n    }\n\n    IOObjectRelease(iter);\n\n    if (found) {\n        *out_entry = entry;\n        return 1;\n    }\n    return 0;\n}\n\n// Python wrapper: return True/False.\nPyObject *\npsutil_has_cpu_freq(PyObject *self, PyObject *args) {\n    io_registry_entry_t entry = IO_OBJECT_NULL;\n    int ok = psutil_find_pmgr_entry(&entry);\n    if (entry != IO_OBJECT_NULL)\n        IOObjectRelease(entry);\n    if (ok)\n        Py_RETURN_TRUE;\n    Py_RETURN_FALSE;\n}\n\n\nPyObject *\npsutil_cpu_freq(PyObject *self, PyObject *args) {\n    io_registry_entry_t entry = IO_OBJECT_NULL;\n    CFTypeRef pCoreRef = NULL;\n    CFTypeRef eCoreRef = NULL;\n    size_t pCoreLength = 0;\n    uint32_t pMin = 0, eMin = 0, min = 0, max = 0, curr = 0;\n\n    if (!psutil_find_pmgr_entry(&entry)) {\n        psutil_runtime_error(\"'pmgr' entry not found in AppleARMIODevice\");\n        return NULL;\n    }\n\n    pCoreRef = IORegistryEntryCreateCFProperty(\n        entry, CFSTR(\"voltage-states5-sram\"), kCFAllocatorDefault, 0\n    );\n    eCoreRef = IORegistryEntryCreateCFProperty(\n        entry, CFSTR(\"voltage-states1-sram\"), kCFAllocatorDefault, 0\n    );\n\n    if (!pCoreRef || !eCoreRef || CFGetTypeID(pCoreRef) != CFDataGetTypeID()\n        || CFGetTypeID(eCoreRef) != CFDataGetTypeID()\n        || CFDataGetLength(pCoreRef) < 8 || CFDataGetLength(eCoreRef) < 4)\n    {\n        psutil_runtime_error(\"invalid CPU frequency data\");\n        goto cleanup;\n    }\n\n    pCoreLength = CFDataGetLength(pCoreRef);\n    CFDataGetBytes(pCoreRef, CFRangeMake(0, 4), (UInt8 *)&pMin);\n    CFDataGetBytes(eCoreRef, CFRangeMake(0, 4), (UInt8 *)&eMin);\n    CFDataGetBytes(pCoreRef, CFRangeMake(pCoreLength - 8, 4), (UInt8 *)&max);\n\n    min = (pMin < eMin) ? pMin : eMin;\n    curr = max;\n\ncleanup:\n    if (pCoreRef)\n        CFRelease(pCoreRef);\n    if (eCoreRef)\n        CFRelease(eCoreRef);\n    if (entry != IO_OBJECT_NULL)\n        IOObjectRelease(entry);\n\n    return Py_BuildValue(\n        \"KKK\",\n        (unsigned long long)(curr / 1000 / 1000),\n        (unsigned long long)(min / 1000 / 1000),\n        (unsigned long long)(max / 1000 / 1000)\n    );\n}\n\n#else  // not ARM64 / ARCH64\n\nPyObject *\npsutil_has_cpu_freq(PyObject *self, PyObject *args) {\n    Py_RETURN_TRUE;\n}\n\nPyObject *\npsutil_cpu_freq(PyObject *self, PyObject *args) {\n    unsigned int curr;\n    int64_t min = 0, max = 0;\n    int mib[2] = {CTL_HW, HW_CPU_FREQ};\n\n    if (psutil_sysctl(mib, 2, &curr, sizeof(curr)) < 0)\n        return psutil_oserror_wsyscall(\"sysctl(HW_CPU_FREQ)\");\n\n    if (psutil_sysctlbyname(\"hw.cpufrequency_min\", &min, sizeof(min)) != 0) {\n        min = 0;\n        psutil_debug(\"sysctlbyname('hw.cpufrequency_min') failed (set to 0)\");\n    }\n\n    if (psutil_sysctlbyname(\"hw.cpufrequency_max\", &max, sizeof(max)) != 0) {\n        max = 0;\n        psutil_debug(\"sysctlbyname('hw.cpufrequency_max') failed (set to 0)\");\n    }\n\n    return Py_BuildValue(\n        \"KKK\",\n        (unsigned long long)(curr / 1000 / 1000),\n        (unsigned long long)(min / 1000 / 1000),\n        (unsigned long long)(max / 1000 / 1000)\n    );\n}\n\n#endif  // ARM64\n\nPyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    natural_t cpu_count = 0;\n    mach_msg_type_number_t info_count = 0;\n    processor_cpu_load_info_data_t *cpu_load_info = NULL;\n    processor_info_array_t info_array = NULL;\n    kern_return_t error;\n    mach_port_t mport = mach_host_self();\n    PyObject *py_retlist = PyList_New(0);\n\n    if (!py_retlist)\n        return NULL;\n\n    if (mport == MACH_PORT_NULL) {\n        psutil_runtime_error(\"mach_host_self() returned NULL\");\n        goto error;\n    }\n\n    error = host_processor_info(\n        mport, PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info_array, &info_count\n    );\n    mach_port_deallocate(mach_task_self(), mport);\n\n    if (error != KERN_SUCCESS || !info_array) {\n        psutil_runtime_error(\n            \"host_processor_info failed: %s\", mach_error_string(error)\n        );\n        goto error;\n    }\n\n    cpu_load_info = (processor_cpu_load_info_data_t *)info_array;\n\n    for (natural_t i = 0; i < cpu_count; i++) {\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(dddd)\",\n                (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK,\n                (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK,\n                (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,\n                (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK\n            ))\n        {\n            goto error;\n        }\n    }\n\n    vm_deallocate(\n        mach_task_self(),\n        (vm_address_t)info_array,\n        info_count * sizeof(integer_t)\n    );\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_retlist);\n    if (info_array)\n        vm_deallocate(\n            mach_task_self(),\n            (vm_address_t)info_array,\n            info_count * sizeof(integer_t)\n        );\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/disk.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Disk related functions. Original code was refactored and moved\n// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:\n// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c\n\n#include <Python.h>\n#include <sys/param.h>\n#include <sys/ucred.h>\n#include <sys/mount.h>\n#include <CoreFoundation/CoreFoundation.h>\n#include <IOKit/IOKitLib.h>\n#include <IOKit/storage/IOBlockStorageDriver.h>\n#include <IOKit/storage/IOMedia.h>\n#include <IOKit/IOBSD.h>\n\n#include \"../../arch/all/init.h\"\n\n\n/*\n * Return a list of tuples including device, mount point and fs type\n * for all partitions mounted on the system.\n */\nPyObject *\npsutil_disk_partitions(PyObject *self, PyObject *args) {\n    int num;\n    int i;\n    int len;\n    uint64_t flags;\n    char opts[400];\n    struct statfs *fs = NULL;\n    PyObject *py_dev = NULL;\n    PyObject *py_mountp = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    // get the number of mount points\n    Py_BEGIN_ALLOW_THREADS\n    num = getfsstat(NULL, 0, MNT_NOWAIT);\n    Py_END_ALLOW_THREADS\n    if (num == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    len = sizeof(*fs) * num;\n    fs = malloc(len);\n    if (fs == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    Py_BEGIN_ALLOW_THREADS\n    num = getfsstat(fs, len, MNT_NOWAIT);\n    Py_END_ALLOW_THREADS\n    if (num == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (i = 0; i < num; i++) {\n        opts[0] = 0;\n        flags = fs[i].f_flags;\n\n        // see sys/mount.h\n        if (flags & MNT_RDONLY)\n            str_append(opts, sizeof(opts), \"ro\");\n        else\n            str_append(opts, sizeof(opts), \"rw\");\n        if (flags & MNT_SYNCHRONOUS)\n            str_append(opts, sizeof(opts), \",sync\");\n        if (flags & MNT_NOEXEC)\n            str_append(opts, sizeof(opts), \",noexec\");\n        if (flags & MNT_NOSUID)\n            str_append(opts, sizeof(opts), \",nosuid\");\n        if (flags & MNT_UNION)\n            str_append(opts, sizeof(opts), \",union\");\n        if (flags & MNT_ASYNC)\n            str_append(opts, sizeof(opts), \",async\");\n        if (flags & MNT_EXPORTED)\n            str_append(opts, sizeof(opts), \",exported\");\n        if (flags & MNT_LOCAL)\n            str_append(opts, sizeof(opts), \",local\");\n        if (flags & MNT_QUOTA)\n            str_append(opts, sizeof(opts), \",quota\");\n        if (flags & MNT_ROOTFS)\n            str_append(opts, sizeof(opts), \",rootfs\");\n        if (flags & MNT_DOVOLFS)\n            str_append(opts, sizeof(opts), \",dovolfs\");\n        if (flags & MNT_DONTBROWSE)\n            str_append(opts, sizeof(opts), \",dontbrowse\");\n        if (flags & MNT_IGNORE_OWNERSHIP)\n            str_append(opts, sizeof(opts), \",ignore-ownership\");\n        if (flags & MNT_AUTOMOUNTED)\n            str_append(opts, sizeof(opts), \",automounted\");\n        if (flags & MNT_JOURNALED)\n            str_append(opts, sizeof(opts), \",journaled\");\n        if (flags & MNT_NOUSERXATTR)\n            str_append(opts, sizeof(opts), \",nouserxattr\");\n        if (flags & MNT_DEFWRITE)\n            str_append(opts, sizeof(opts), \",defwrite\");\n        if (flags & MNT_UPDATE)\n            str_append(opts, sizeof(opts), \",update\");\n        if (flags & MNT_RELOAD)\n            str_append(opts, sizeof(opts), \",reload\");\n        if (flags & MNT_FORCE)\n            str_append(opts, sizeof(opts), \",force\");\n        if (flags & MNT_CMDFLAGS)\n            str_append(opts, sizeof(opts), \",cmdflags\");\n            // requires macOS >= 10.5\n#ifdef MNT_QUARANTINE\n        if (flags & MNT_QUARANTINE)\n            str_append(opts, sizeof(opts), \",quarantine\");\n#endif\n#ifdef MNT_MULTILABEL\n        if (flags & MNT_MULTILABEL)\n            str_append(opts, sizeof(opts), \",multilabel\");\n#endif\n#ifdef MNT_NOATIME\n        if (flags & MNT_NOATIME)\n            str_append(opts, sizeof(opts), \",noatime\");\n#endif\n        py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);\n        if (!py_dev)\n            goto error;\n        py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);\n        if (!py_mountp)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(OOss)\",\n                py_dev,  // device\n                py_mountp,  // mount point\n                fs[i].f_fstypename,  // fs type\n                opts  // options\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_dev);\n        Py_CLEAR(py_mountp);\n    }\n\n    free(fs);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_dev);\n    Py_XDECREF(py_mountp);\n    Py_DECREF(py_retlist);\n    if (fs != NULL)\n        free(fs);\n    return NULL;\n}\n\n\nPyObject *\npsutil_disk_usage_used(PyObject *self, PyObject *args) {\n    PyObject *py_default_value;\n    PyObject *py_mount_point_bytes = NULL;\n    char *mount_point;\n\n    if (!PyArg_ParseTuple(\n            args,\n            \"O&O\",\n            PyUnicode_FSConverter,\n            &py_mount_point_bytes,\n            &py_default_value\n        ))\n    {\n        return NULL;\n    }\n    mount_point = PyBytes_AsString(py_mount_point_bytes);\n    if (NULL == mount_point) {\n        Py_XDECREF(py_mount_point_bytes);\n        return NULL;\n    }\n\n#ifdef ATTR_VOL_SPACEUSED\n    /* Call getattrlist(ATTR_VOL_SPACEUSED) to get used space info. */\n    int ret;\n    struct {\n        uint32_t size;\n        uint64_t spaceused;\n    } __attribute__((aligned(4), packed)) attrbuf = {0};\n    struct attrlist attrs = {0};\n\n    attrs.bitmapcount = ATTR_BIT_MAP_COUNT;\n    attrs.volattr = ATTR_VOL_INFO | ATTR_VOL_SPACEUSED;\n    attrbuf.size = sizeof(attrbuf);\n\n    Py_BEGIN_ALLOW_THREADS\n    ret = getattrlist(mount_point, &attrs, &attrbuf, sizeof(attrbuf), 0);\n    Py_END_ALLOW_THREADS\n    if (ret == 0) {\n        Py_XDECREF(py_mount_point_bytes);\n        return PyLong_FromUnsignedLongLong(attrbuf.spaceused);\n    }\n    psutil_debug(\n        \"getattrlist(ATTR_VOL_SPACEUSED) failed, fall-back to default value\"\n    );\n#endif\n    Py_XDECREF(py_mount_point_bytes);\n    Py_INCREF(py_default_value);\n    return py_default_value;\n}\n\n\n/*\n * Return a Python dict of tuples for disk I/O information\n */\nPyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    CFDictionaryRef parent_dict = NULL;\n    CFDictionaryRef props_dict = NULL;\n    CFDictionaryRef stats_dict = NULL;\n    io_registry_entry_t parent = IO_OBJECT_NULL;\n    io_registry_entry_t disk = IO_OBJECT_NULL;\n    io_iterator_t disk_list = IO_OBJECT_NULL;\n    PyObject *py_disk_info = NULL;\n    PyObject *py_retdict = PyDict_New();\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    if (IOServiceGetMatchingServices(\n            kIOMasterPortDefault, IOServiceMatching(kIOMediaClass), &disk_list\n        )\n        != kIOReturnSuccess)\n    {\n        psutil_runtime_error(\"unable to get the list of disks\");\n        goto error;\n    }\n\n    while ((disk = IOIteratorNext(disk_list)) != 0) {\n        py_disk_info = NULL;\n        parent_dict = NULL;\n        props_dict = NULL;\n        stats_dict = NULL;\n        parent = IO_OBJECT_NULL;\n\n        if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent)\n            != kIOReturnSuccess)\n        {\n            psutil_runtime_error(\"unable to get the disk's parent\");\n            goto error;\n        }\n\n        if (!IOObjectConformsTo(parent, \"IOBlockStorageDriver\")) {\n            IOObjectRelease(parent);\n            IOObjectRelease(disk);\n            continue;\n        }\n\n        if (IORegistryEntryCreateCFProperties(\n                disk,\n                (CFMutableDictionaryRef *)&parent_dict,\n                kCFAllocatorDefault,\n                kNilOptions\n            )\n            != kIOReturnSuccess)\n        {\n            psutil_runtime_error(\"unable to get the parent's properties\");\n            goto error;\n        }\n\n        if (IORegistryEntryCreateCFProperties(\n                parent,\n                (CFMutableDictionaryRef *)&props_dict,\n                kCFAllocatorDefault,\n                kNilOptions\n            )\n            != kIOReturnSuccess)\n        {\n            psutil_runtime_error(\"unable to get the disk properties\");\n            goto error;\n        }\n\n        CFStringRef disk_name_ref = (CFStringRef\n        )CFDictionaryGetValue(parent_dict, CFSTR(kIOBSDNameKey));\n        if (disk_name_ref == NULL) {\n            psutil_runtime_error(\"unable to get disk name\");\n            goto error;\n        }\n\n        const int kMaxDiskNameSize = 64;\n        char disk_name[kMaxDiskNameSize];\n        if (!CFStringGetCString(\n                disk_name_ref,\n                disk_name,\n                kMaxDiskNameSize,\n                CFStringGetSystemEncoding()\n            ))\n        {\n            psutil_runtime_error(\"unable to convert disk name to C string\");\n            goto error;\n        }\n\n        stats_dict = (CFDictionaryRef)CFDictionaryGetValue(\n            props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey)\n        );\n        if (stats_dict == NULL) {\n            psutil_runtime_error(\"unable to get disk stats\");\n            goto error;\n        }\n\n        CFNumberRef number;\n        int64_t reads = 0, writes = 0, read_bytes = 0, write_bytes = 0;\n        int64_t read_time = 0, write_time = 0;\n\n        if ((number = (CFNumberRef)CFDictionaryGetValue(\n                 stats_dict, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)\n             )))\n            CFNumberGetValue(number, kCFNumberSInt64Type, &reads);\n\n        if ((number = (CFNumberRef)CFDictionaryGetValue(\n                 stats_dict, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)\n             )))\n            CFNumberGetValue(number, kCFNumberSInt64Type, &writes);\n\n        if ((number = (CFNumberRef)CFDictionaryGetValue(\n                 stats_dict, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)\n             )))\n            CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes);\n\n        if ((number = (CFNumberRef)CFDictionaryGetValue(\n                 stats_dict,\n                 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)\n             )))\n            CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes);\n\n        if ((number = (CFNumberRef)CFDictionaryGetValue(\n                 stats_dict,\n                 CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)\n             )))\n            CFNumberGetValue(number, kCFNumberSInt64Type, &read_time);\n\n        if ((number = (CFNumberRef)CFDictionaryGetValue(\n                 stats_dict,\n                 CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)\n             )))\n            CFNumberGetValue(number, kCFNumberSInt64Type, &write_time);\n\n        py_disk_info = Py_BuildValue(\n            \"(KKKKKK)\",\n            (unsigned long long)reads,\n            (unsigned long long)writes,\n            (unsigned long long)read_bytes,\n            (unsigned long long)write_bytes,\n            (unsigned long long)(read_time / 1000 / 1000),\n            (unsigned long long)(write_time / 1000 / 1000)\n        );\n\n        if (!py_disk_info)\n            goto error;\n\n        if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info)) {\n            Py_CLEAR(py_disk_info);\n            goto error;\n        }\n\n        Py_CLEAR(py_disk_info);\n\n        if (parent_dict)\n            CFRelease(parent_dict);\n        if (props_dict)\n            CFRelease(props_dict);\n        IOObjectRelease(parent);\n        IOObjectRelease(disk);\n    }\n\n    IOObjectRelease(disk_list);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_disk_info);\n    Py_DECREF(py_retdict);\n    if (parent_dict)\n        CFRelease(parent_dict);\n    if (props_dict)\n        CFRelease(props_dict);\n    if (parent != IO_OBJECT_NULL)\n        IOObjectRelease(parent);\n    if (disk != IO_OBJECT_NULL)\n        IOObjectRelease(disk);\n    if (disk_list != IO_OBJECT_NULL)\n        IOObjectRelease(disk_list);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/heap.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <malloc/malloc.h>\n#include <mach/mach.h>\n#include <mach/vm_map.h>\n#include <AvailabilityMacros.h>\n\n#include \"../../arch/all/init.h\"\n\n\nstatic int\nget_zones(malloc_zone_t ***out_zones, unsigned int *out_count) {\n    vm_address_t *raw = NULL;\n    unsigned int count = 0;\n    kern_return_t kr;\n    malloc_zone_t **zones;\n    malloc_zone_t *zone;\n\n    *out_zones = NULL;\n    *out_count = 0;\n\n    kr = malloc_get_all_zones(mach_task_self(), NULL, &raw, &count);\n    if (kr == KERN_SUCCESS && raw != NULL && count > 0) {\n        *out_zones = (malloc_zone_t **)raw;\n        *out_count = count;\n        return 1;  // success\n    }\n\n    psutil_debug(\"malloc_get_all_zones() failed; using malloc_default_zone()\");\n\n    zones = (malloc_zone_t **)malloc(sizeof(malloc_zone_t *));\n    if (!zones) {\n        PyErr_NoMemory();\n        return -1;\n    }\n\n    zone = malloc_default_zone();\n    if (!zone) {\n        free(zones);\n        psutil_runtime_error(\"malloc_default_zone() failed\");\n        return -1;\n    }\n\n    zones[0] = zone;\n    *out_zones = zones;\n    *out_count = 1;\n    return 0;  // fallback, caller must free()\n}\n\n\n// psutil_heap_info() -> (heap_used, mmap_used)\n//\n// Return libmalloc heap stats via `malloc_zone_statistics()`.\n// Compatible with macOS 10.6+ (Sierra and earlier).\n//\n// Mapping:\n//   - heap_used  ~ size_in_use          (live allocated bytes)\n//   - mmap_used  ~ 0                    (no direct stat)\nPyObject *\npsutil_heap_info(PyObject *self, PyObject *args) {\n    malloc_zone_t **zones = NULL;\n    unsigned int count = 0;\n    uint64_t heap_used = 0;\n    uint64_t mmap_used = 0;\n    int ok;\n\n    ok = get_zones(&zones, &count);\n    if (ok == -1)\n        return NULL;\n\n    for (unsigned int i = 0; i < count; i++) {\n        malloc_statistics_t stats = {0};\n        malloc_zone_statistics(zones[i], &stats);\n        heap_used += (uint64_t)stats.size_in_use;\n    }\n\n    if (!ok)\n        free(zones);\n\n    return Py_BuildValue(\"KK\", heap_used, mmap_used);\n}\n\n\n// Return unused heap memory back to the OS.\nPyObject *\npsutil_heap_trim(PyObject *self, PyObject *args) {\n    malloc_zone_t **zones = NULL;\n    unsigned int count = 0;\n    int ok;\n\n    ok = get_zones(&zones, &count);\n    if (ok == -1)\n        return NULL;\n\n// malloc_zone_pressure_relief added in macOS 10.7.\n#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070\n    for (unsigned int i = 0; i < count; i++)\n        malloc_zone_pressure_relief(zones[i], 0);\n#endif\n\n    if (!ok)\n        free(zones);\n\n    Py_RETURN_NONE;\n}\n"
  },
  {
    "path": "psutil/arch/osx/init.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/sysctl.h>\n\n#include \"../../arch/all/init.h\"\n#include \"init.h\"\n\n\nuint64_t PSUTIL_HW_TBFREQUENCY;\n\n// Called on module import.\nint\npsutil_setup_osx(void) {\n    size_t size = sizeof(PSUTIL_HW_TBFREQUENCY);\n\n    // hw.tbfrequency gives the real hardware timer frequency regardless of\n    // whether we are running under Rosetta 2 (x86_64 on Apple Silicon).\n    // mach_timebase_info() is intercepted by Rosetta and returns numer=1,\n    // denom=1 for x86_64 processes, but proc_pidinfo() returns raw ARM Mach\n    // ticks, so mach_timebase_info gives a wrong conversion factor there.\n    if (sysctlbyname(\"hw.tbfrequency\", &PSUTIL_HW_TBFREQUENCY, &size, NULL, 0)\n        != 0)\n    {\n        psutil_oserror_wsyscall(\"sysctlbyname('hw.tbfrequency')\");\n        return -1;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/osx/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/user.h>\n#include <sys/types.h>\n#include <libproc.h>\n#include <mach/mach_time.h>\n\nextern uint64_t PSUTIL_HW_TBFREQUENCY;\n\nint psutil_setup_osx(void);\nint _psutil_pids(pid_t **pids_array, int *pids_count);\nint is_zombie(size_t pid);\n\nint psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp);\nint psutil_sysctl_procargs(pid_t pid, char *procargs, size_t *argmax);\nint psutil_proc_pidinfo(\n    pid_t pid, int flavor, uint64_t arg, void *pti, int size\n);\nint psutil_task_for_pid(pid_t pid, mach_port_t *task);\nstruct proc_fdinfo *psutil_proc_list_fds(pid_t pid, int *num_fds);\n\nPyObject *psutil_boot_time(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_freq(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_stats(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_disk_partitions(PyObject *self, PyObject *args);\nPyObject *psutil_disk_usage_used(PyObject *self, PyObject *args);\nPyObject *psutil_has_cpu_freq(PyObject *self, PyObject *args);\nPyObject *psutil_heap_info(PyObject *self, PyObject *args);\nPyObject *psutil_heap_trim(PyObject *self, PyObject *args);\nPyObject *psutil_net_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cmdline(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cwd(PyObject *self, PyObject *args);\nPyObject *psutil_proc_environ(PyObject *self, PyObject *args);\nPyObject *psutil_proc_exe(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_info_ex(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_uss(PyObject *self, PyObject *args);\nPyObject *psutil_proc_name(PyObject *self, PyObject *args);\nPyObject *psutil_proc_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);\nPyObject *psutil_proc_oneshot_kinfo(PyObject *self, PyObject *args);\nPyObject *psutil_proc_oneshot_pidtaskinfo(PyObject *self, PyObject *args);\nPyObject *psutil_proc_open_files(PyObject *self, PyObject *args);\nPyObject *psutil_proc_threads(PyObject *self, PyObject *args);\nPyObject *psutil_sensors_battery(PyObject *self, PyObject *args);\nPyObject *psutil_swap_mem(PyObject *self, PyObject *args);\nPyObject *psutil_virtual_mem(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/osx/mem.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// System memory related functions. Original code was refactored and moved\n// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:\n// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c\n\n// See:\n// https://github.com/apple-open-source/macos/blob/master/system_cmds/vm_stat/vm_stat.c\n\n#include <Python.h>\n#include <mach/host_info.h>\n#include <sys/sysctl.h>\n#include <mach/mach.h>\n\n#include \"../../arch/all/init.h\"\n\n\nstatic int\npsutil_sys_vminfo(vm_statistics64_t vmstat) {\n    kern_return_t ret;\n    mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;\n    mach_port_t mport;\n\n    mport = mach_host_self();\n    if (mport == MACH_PORT_NULL) {\n        psutil_runtime_error(\"mach_host_self() returned MACH_PORT_NULL\");\n        return -1;\n    }\n\n    ret = host_statistics64(\n        mport, HOST_VM_INFO64, (host_info64_t)vmstat, &count\n    );\n    mach_port_deallocate(mach_task_self(), mport);\n    if (ret != KERN_SUCCESS) {\n        psutil_runtime_error(\n            \"host_statistics64(HOST_VM_INFO64) syscall failed: %s\",\n            mach_error_string(ret)\n        );\n        return -1;\n    }\n    return 0;\n}\n\n\n/*\n * Return system virtual memory stats.\n * See:\n * https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/vm_stat.c.auto.html\n */\nPyObject *\npsutil_virtual_mem(PyObject *self, PyObject *args) {\n    uint64_t total;\n    unsigned long long active, inactive, wired, free, _speculative;\n    unsigned long long available, used;\n    int mib[2] = {CTL_HW, HW_MEMSIZE};\n    vm_statistics64_data_t vm;\n    long pagesize = psutil_getpagesize();\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    // This is also available as sysctlbyname(\"hw.memsize\").\n    if (psutil_sysctl(mib, 2, &total, sizeof(total)) != 0)\n        goto error;\n    if (psutil_sys_vminfo(&vm) != 0)\n        goto error;\n\n    active = (unsigned long long)vm.active_count * pagesize;\n    inactive = (unsigned long long)vm.inactive_count * pagesize;\n    wired = (unsigned long long)vm.wire_count * pagesize;\n    free = (unsigned long long)vm.free_count * pagesize;\n    _speculative = (unsigned long long)vm.speculative_count * pagesize;\n\n    // This is how Zabbix calculates avail and used mem:\n    // https://github.com/zabbix/zabbix/blob/master/src/libs/zbxsysinfo/osx/memory.c\n    // Also see: https://github.com/giampaolo/psutil/issues/1277\n    available = inactive + free;\n    used = active + wired;\n\n    // This is NOT how Zabbix calculates free mem but it matches \"free\"\n    // CLI utility.\n    free -= _speculative;\n\n    if (!(pydict_add(dict, \"total\", \"K\", (unsigned long long)total)\n          | pydict_add(dict, \"available\", \"K\", available)\n          | pydict_add(dict, \"used\", \"K\", used)\n          | pydict_add(dict, \"free\", \"K\", free)\n          | pydict_add(dict, \"active\", \"K\", active)\n          | pydict_add(dict, \"inactive\", \"K\", inactive)\n          | pydict_add(dict, \"wired\", \"K\", wired)))\n        goto error;\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\n/*\n * Return stats about swap memory.\n */\nPyObject *\npsutil_swap_mem(PyObject *self, PyObject *args) {\n    struct xsw_usage totals;\n    vm_statistics64_data_t vmstat;\n    long pagesize = psutil_getpagesize();\n    int mib[2] = {CTL_VM, VM_SWAPUSAGE};\n    PyObject *dict = PyDict_New();\n\n    if (dict == NULL)\n        return NULL;\n\n    if (psutil_sysctl(mib, 2, &totals, sizeof(totals)) != 0)\n        goto error;\n    if (psutil_sys_vminfo(&vmstat) != 0)\n        goto error;\n\n    // clang-format off\n    if (!pydict_add(dict, \"total\", \"K\", (unsigned long long)totals.xsu_total)) goto error;\n    if (!pydict_add(dict, \"used\", \"K\", (unsigned long long)totals.xsu_used)) goto error;\n    if (!pydict_add(dict, \"free\", \"K\", (unsigned long long)totals.xsu_avail)) goto error;\n    if (!pydict_add(dict, \"sin\", \"K\", (unsigned long long)vmstat.pageins * pagesize)) goto error;\n    if (!pydict_add(dict, \"sout\", \"K\", (unsigned long long)vmstat.pageouts * pagesize)) goto error;\n    // clang-format on\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/net.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Networks related functions. Original code was refactored and moved\n// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:\n// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c\n\n#include <Python.h>\n#include <sys/socket.h>\n#include <sys/sysctl.h>\n#include <net/if.h>\n#include <net/if_dl.h>\n#include <net/route.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_net_io_counters(PyObject *self, PyObject *args) {\n    char *buf = NULL, *lim, *next;\n    struct if_msghdr *ifm;\n    int mib[6];\n    size_t len = 0;\n    PyObject *py_ifc_info = NULL;\n    PyObject *py_retdict = PyDict_New();\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    mib[0] = CTL_NET;  // networking subsystem\n    mib[1] = PF_ROUTE;  // type of information\n    mib[2] = 0;  // protocol (IPPROTO_xxx)\n    mib[3] = 0;  // address family\n    mib[4] = NET_RT_IFLIST2;  // operation\n    mib[5] = 0;\n\n    if (psutil_sysctl_malloc(mib, 6, &buf, &len) != 0)\n        goto error;\n\n    lim = buf + len;\n\n    for (next = buf; next < lim;) {\n        if ((size_t)(lim - next) < sizeof(struct if_msghdr)) {\n            psutil_debug(\"struct if_msghdr size mismatch (skip entry)\");\n            break;\n        }\n\n        ifm = (struct if_msghdr *)next;\n\n        if (ifm->ifm_msglen == 0 || next + ifm->ifm_msglen > lim) {\n            psutil_debug(\"ifm_msglen size mismatch (skip entry)\");\n            break;\n        }\n\n        next += ifm->ifm_msglen;\n\n        if (ifm->ifm_type == RTM_IFINFO2) {\n            py_ifc_info = NULL;\n            struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;\n\n            if ((char *)if2m + sizeof(struct if_msghdr2) > lim) {\n                psutil_debug(\"if_msghdr2 + sockaddr_dl mismatch (skip entry)\");\n                continue;\n            }\n\n            struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);\n\n            if ((char *)sdl + sizeof(struct sockaddr_dl) > lim) {\n                psutil_debug(\"not enough buffer for sockaddr_dl (skip entry)\");\n                continue;\n            }\n\n            char ifc_name[IFNAMSIZ];\n            size_t namelen = sdl->sdl_nlen;\n            if (namelen >= IFNAMSIZ)\n                namelen = IFNAMSIZ - 1;\n\n            memcpy(ifc_name, sdl->sdl_data, namelen);\n            ifc_name[namelen] = '\\0';\n\n            py_ifc_info = Py_BuildValue(\n                \"(KKKKKKKi)\",\n                (unsigned long long)if2m->ifm_data.ifi_obytes,\n                (unsigned long long)if2m->ifm_data.ifi_ibytes,\n                (unsigned long long)if2m->ifm_data.ifi_opackets,\n                (unsigned long long)if2m->ifm_data.ifi_ipackets,\n                (unsigned long long)if2m->ifm_data.ifi_ierrors,\n                (unsigned long long)if2m->ifm_data.ifi_oerrors,\n                (unsigned long long)if2m->ifm_data.ifi_iqdrops,\n                0\n            );  // dropout not supported\n\n            if (!py_ifc_info)\n                goto error;\n\n            if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info)) {\n                Py_CLEAR(py_ifc_info);\n                goto error;\n            }\n\n            Py_CLEAR(py_ifc_info);\n        }\n    }\n\n    free(buf);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_ifc_info);\n    Py_DECREF(py_retdict);\n    if (buf != NULL)\n        free(buf);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/pids.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <sys/user.h>\n\n#include \"../../arch/all/init.h\"\n\n\nint\n_psutil_pids(pid_t **pids_array, int *pids_count) {\n    int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};\n    size_t len = 0;\n    char *buf = NULL;\n    struct kinfo_proc *proc_list = NULL;\n    size_t num_procs = 0;\n\n    *pids_array = NULL;\n    *pids_count = 0;\n\n    if (psutil_sysctl_malloc(mib, 3, &buf, &len) != 0)\n        return -1;\n\n    if (len == 0) {\n        psutil_runtime_error(\"no PIDs found\");\n        goto error;\n    }\n\n    proc_list = (struct kinfo_proc *)buf;\n    num_procs = len / sizeof(struct kinfo_proc);\n\n    *pids_array = malloc(num_procs * sizeof(pid_t));\n    if (!*pids_array) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    for (size_t i = 0; i < num_procs; i++) {\n        (*pids_array)[i] = proc_list[i].kp_proc.p_pid;\n    }\n\n    *pids_count = (int)num_procs;\n    free(buf);\n    return 0;\n\nerror:\n    if (buf != NULL)\n        free(buf);\n    return -1;\n}\n"
  },
  {
    "path": "psutil/arch/osx/proc.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Process related functions. Original code was moved in here from\n// psutil/_psutil_osx.c and psutil/arc/osx/process_info.c in 2023.\n// For reference, here's the GIT blame history before the move:\n// https://github.com/giampaolo/psutil/blame/59504a5/psutil/_psutil_osx.c\n// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/arch/osx/process_info.c\n\n#include <Python.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/sysctl.h>\n#include <libproc.h>\n#include <sys/proc_info.h>\n#include <sys/resource.h>\n#include <sys/sysctl.h>\n#include <netinet/tcp_fsm.h>\n#include <arpa/inet.h>\n#include <pwd.h>\n#include <unistd.h>\n#include <mach/mach.h>\n#include <mach/mach_vm.h>\n#include <mach/shared_region.h>\n#include <mach-o/loader.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// macOS is apparently the only UNIX where the process \"base\" status\n// (running, idle, etc.) is unreliable and must be guessed from flags:\n// https://github.com/giampaolo/psutil/issues/2675\nstatic int\nconvert_status(struct extern_proc *p, struct eproc *e) {\n    int flag = p->p_flag;\n    int eflag = e->e_flag;\n\n    // zombies and stopped\n    if (p->p_stat == SZOMB)\n        return SZOMB;\n    if (p->p_stat == SSTOP)\n        return SSTOP;\n\n    if (flag & P_SYSTEM)\n        return SIDL;  // system idle\n    if (flag & P_WEXIT)\n        return SIDL;  // waiting to exit\n    if (flag & P_PPWAIT)\n        return SIDL;  // parent waiting\n    if (eflag & EPROC_SLEADER)\n        return SSLEEP;  // session leader treated as sleeping\n\n    // Default: 99% is SRUN (running)\n    return p->p_stat;\n}\n\n\n/*\n * Return multiple process info as a Python dict in one shot by\n * using sysctl() and filling up a kinfo_proc struct.\n * It should be possible to do this for all processes without\n * incurring into permission (EPERM) errors.\n * This will also succeed for zombie processes returning correct\n * information.\n */\nPyObject *\npsutil_proc_oneshot_kinfo(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int status;\n    struct kinfo_proc kp;\n    PyObject *py_name = NULL;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_get_kinfo_proc(pid, &kp) == -1)\n        goto error;\n\n    py_name = PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);\n    if (!py_name) {\n        // Likely a decoding error. We don't want to fail the whole\n        // operation. The python module may retry with proc_name().\n        PyErr_Clear();\n        Py_INCREF(Py_None);\n        py_name = Py_None;\n    }\n\n    status = convert_status(&kp.kp_proc, &kp.kp_eproc);\n\n    // clang-format off\n    if (!pydict_add(dict, \"ppid\", _Py_PARSE_PID, kp.kp_eproc.e_ppid)) goto error;\n    if (!pydict_add(dict, \"ruid\", \"l\", (long)kp.kp_eproc.e_pcred.p_ruid)) goto error;\n    if (!pydict_add(dict, \"euid\", \"l\", (long)kp.kp_eproc.e_ucred.cr_uid)) goto error;\n    if (!pydict_add(dict, \"suid\", \"l\", (long)kp.kp_eproc.e_pcred.p_svuid)) goto error;\n    if (!pydict_add(dict, \"rgid\", \"l\", (long)kp.kp_eproc.e_pcred.p_rgid)) goto error;\n    if (!pydict_add(dict, \"egid\", \"l\", (long)kp.kp_eproc.e_ucred.cr_groups[0])) goto error;\n    if (!pydict_add(dict, \"sgid\", \"l\", (long)kp.kp_eproc.e_pcred.p_svgid)) goto error;\n    if (!pydict_add(dict, \"ttynr\", \"l\", (long)kp.kp_eproc.e_tdev)) goto error;\n    if (!pydict_add(dict, \"ctime\", \"d\", PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime))) goto error;\n    if (!pydict_add(dict, \"status\", \"i\", status)) goto error;\n    if (!pydict_add(dict, \"name\", \"O\", py_name)) goto error;\n    // clang-format on\n\n    Py_DECREF(py_name);\n    return dict;\n\nerror:\n    Py_XDECREF(py_name);\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\n/*\n * Return multiple process info as a Python dict in one shot by\n * using proc_pidinfo(PROC_PIDTASKINFO) and filling a proc_taskinfo\n * struct.\n * Contrarily from proc_kinfo above this function will fail with\n * EACCES for PIDs owned by another user and with ESRCH for zombie\n * processes.\n */\nPyObject *\npsutil_proc_oneshot_pidtaskinfo(PyObject *self, PyObject *args) {\n    pid_t pid;\n    struct proc_taskinfo pti;\n    unsigned long maj_faults;\n    unsigned long min_faults;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)) != 0)\n        goto error;\n\n    // Match getrusage() ru_majflt and ru_minflt. getrusage() source:\n    // https://github.com/apple/darwin-xnu/blob/2ff845c2e033/bsd/kern/kern_resource.c#L1263-L1265\n    maj_faults = (unsigned long)pti.pti_pageins;\n    min_faults = (unsigned long)pti.pti_faults - maj_faults;\n\n    // clang-format off\n    // pti_total_user/system are in Mach ticks; hw.tbfrequency gives\n    // ticks/second and is not intercepted by Rosetta 2, unlike\n    // mach_timebase_info() which returns numer=1/denom=1 for x86_64\n    // processes on Apple Silicon, causing a 41.67x undercount there.\n    if (!pydict_add(dict, \"cpu_utime\", \"d\", (double)pti.pti_total_user / PSUTIL_HW_TBFREQUENCY)) goto error;\n    if (!pydict_add(dict, \"cpu_stime\", \"d\", (double)pti.pti_total_system / PSUTIL_HW_TBFREQUENCY)) goto error;\n    // Note about memory: determining other mem stats on macOS is a mess:\n    // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt\n    // I just give up.\n    if (!pydict_add(dict, \"rss\", \"K\", pti.pti_resident_size)) goto error;\n    if (!pydict_add(dict, \"vms\", \"K\", pti.pti_virtual_size)) goto error;\n    if (!pydict_add(dict, \"minor_faults\", \"k\", min_faults)) goto error;\n    if (!pydict_add(dict, \"major_faults\", \"k\", maj_faults)) goto error;\n    if (!pydict_add(dict, \"num_threads\", \"k\", (unsigned long)pti.pti_threadnum)) goto error;\n    // Unvoluntary not available on macOS. `pti_csw` refers to the\n    // sum of voluntary + involuntary. getrusage() numbers confirm\n    // this theory.\n    if (!pydict_add(dict, \"volctxsw\", \"k\", (unsigned long)pti.pti_csw)) goto error;\n    // clang-format on\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\n/*\n * Return process name from kinfo_proc as a Python string.\n */\nPyObject *\npsutil_proc_name(PyObject *self, PyObject *args) {\n    pid_t pid;\n    struct kinfo_proc kp;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_get_kinfo_proc(pid, &kp) == -1)\n        return NULL;\n\n    return PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);\n}\n\n/*\n * Return process current working directory.\n * Raises NSP in case of zombie process.\n */\nPyObject *\npsutil_proc_cwd(PyObject *self, PyObject *args) {\n    pid_t pid;\n    struct proc_vnodepathinfo pathinfo;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    if (psutil_proc_pidinfo(\n            pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof(pathinfo)\n        )\n        != 0)\n    {\n        return NULL;\n    }\n\n    return PyUnicode_DecodeFSDefault(pathinfo.pvi_cdir.vip_path);\n}\n\n\n/*\n * Return path of the process executable.\n */\nPyObject *\npsutil_proc_exe(PyObject *self, PyObject *args) {\n    pid_t pid;\n    char buf[PATH_MAX];\n    int ret;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    errno = 0;\n    ret = proc_pidpath(pid, &buf, sizeof(buf));\n    if (ret == 0) {\n        if (pid == 0) {\n            psutil_oserror_ad(\"automatically set for PID 0\");\n            return NULL;\n        }\n        else if (errno == ENOENT) {\n            // It may happen (file not found error) if the process is\n            // still alive but the executable which launched it got\n            // deleted, see:\n            // https://github.com/giampaolo/psutil/issues/1738\n            return PyUnicode_FromString(\"\");\n        }\n        else {\n            psutil_raise_for_pid(pid, \"proc_pidpath()\");\n            return NULL;\n        }\n    }\n    return PyUnicode_DecodeFSDefault(buf);\n}\n\n\n/*\n * Indicates if the given virtual address on the given architecture is in the\n * shared VM region.\n */\nstatic bool\npsutil_in_shared_region(mach_vm_address_t addr, cpu_type_t type) {\n    mach_vm_address_t base;\n    mach_vm_address_t size;\n\n    switch (type) {\n        case CPU_TYPE_ARM:\n            base = SHARED_REGION_BASE_ARM;\n            size = SHARED_REGION_SIZE_ARM;\n            break;\n        case CPU_TYPE_I386:\n            base = SHARED_REGION_BASE_I386;\n            size = SHARED_REGION_SIZE_I386;\n            break;\n        case CPU_TYPE_X86_64:\n            base = SHARED_REGION_BASE_X86_64;\n            size = SHARED_REGION_SIZE_X86_64;\n            break;\n        default:\n            return false;\n    }\n\n    return base <= addr && addr < (base + size);\n}\n\n\n/*\n * Return extended memory info via task_info(TASK_VM_INFO).\n */\nPyObject *\npsutil_proc_memory_info_ex(PyObject *self, PyObject *args) {\n    pid_t pid;\n    mach_port_t task = MACH_PORT_NULL;\n    kern_return_t kr;\n    task_vm_info_data_t info;\n    mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_task_for_pid(pid, &task) != 0)\n        goto error;\n\n    // Fetch multiple metrics. Fails with access denied for any PID not\n    // owned by us.\n    kr = task_info(task, TASK_VM_INFO, (task_info_t)&info, &info_count);\n    mach_port_deallocate(mach_task_self(), task);\n    task = MACH_PORT_NULL;\n    if (kr != KERN_SUCCESS) {\n        psutil_runtime_error(\"task_info(TASK_VM_INFO) syscall failed\");\n        goto error;\n    }\n\n    // Fetch wired memory.\n    uint64_t wired_size = 0;\n    struct rusage_info_v0 ri;\n    if (proc_pid_rusage(pid, RUSAGE_INFO_V0, (rusage_info_t *)&ri) == 0)\n        wired_size = ri.ri_wired_size;\n    else\n        psutil_debug(\"proc_pid_rusage() failed (pid=%i)\", pid);\n\n    // clang-format off\n    if (!pydict_add(dict, \"peak_rss\", \"K\", (unsigned long long)info.resident_size_peak)) goto error;\n    if (!pydict_add(dict, \"rss_anon\", \"K\", (unsigned long long)info.internal)) goto error;\n    if (!pydict_add(dict, \"rss_file\", \"K\", (unsigned long long)info.external)) goto error;\n    if (!pydict_add(dict, \"wired\", \"K\", (unsigned long long)wired_size)) goto error;\n    if (!pydict_add(dict, \"compressed\", \"K\", (unsigned long long)info.compressed)) goto error;\n    if (!pydict_add(dict, \"phys_footprint\", \"K\", (unsigned long long)info.phys_footprint)) goto error;\n    // clang-format on\n\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\n/*\n * Returns the USS (unique set size) of the process. Reference:\n * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/\n *     nsMemoryReporterManager.cpp\n */\nPyObject *\npsutil_proc_memory_uss(PyObject *self, PyObject *args) {\n    pid_t pid;\n    cpu_type_t cpu_type;\n    size_t private_pages = 0;\n    mach_vm_size_t size = 0;\n    mach_msg_type_number_t info_count;\n    kern_return_t kr;\n    long pagesize = psutil_getpagesize();\n    mach_vm_address_t addr;\n    mach_port_t task = MACH_PORT_NULL;\n    vm_region_top_info_data_t info;\n    mach_port_t object_name;\n    mach_vm_address_t prev_addr;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    if (psutil_task_for_pid(pid, &task) != 0)\n        return NULL;\n\n    if (psutil_sysctlbyname(\"sysctl.proc_cputype\", &cpu_type, sizeof(cpu_type))\n        != 0)\n    {\n        mach_port_deallocate(mach_task_self(), task);\n        return NULL;\n    }\n\n    // Roughly based on libtop_update_vm_regions in\n    // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c\n    for (addr = MACH_VM_MIN_ADDRESS;; addr += size) {\n        prev_addr = addr;\n        info_count = VM_REGION_TOP_INFO_COUNT;  // reset before each call\n        object_name = MACH_PORT_NULL;\n\n        kr = mach_vm_region(\n            task,\n            &addr,\n            &size,\n            VM_REGION_TOP_INFO,\n            (vm_region_info_t)&info,\n            &info_count,\n            &object_name\n        );\n\n        if (object_name != MACH_PORT_NULL) {\n            mach_port_deallocate(mach_task_self(), object_name);\n            object_name = MACH_PORT_NULL;\n        }\n\n        if (kr == KERN_INVALID_ADDRESS) {\n            // Done iterating VM regions.\n            break;\n        }\n        else if (kr != KERN_SUCCESS) {\n            psutil_runtime_error(\n                \"mach_vm_region(VM_REGION_TOP_INFO) syscall failed\"\n            );\n            mach_port_deallocate(mach_task_self(), task);\n            return NULL;\n        }\n\n        if (size == 0 || addr < prev_addr) {\n            psutil_debug(\"prevent infinite loop\");\n            break;\n        }\n\n        if (psutil_in_shared_region(addr, cpu_type)\n            && info.share_mode != SM_PRIVATE)\n        {\n            continue;\n        }\n\n        switch (info.share_mode) {\n#ifdef SM_LARGE_PAGE\n            case SM_LARGE_PAGE:\n                // NB: Large pages are not shareable and always resident.\n#endif\n            case SM_PRIVATE:\n                private_pages += info.private_pages_resident;\n                private_pages += info.shared_pages_resident;\n                break;\n            case SM_COW:\n                private_pages += info.private_pages_resident;\n                if (info.ref_count == 1) {\n                    // Treat copy-on-write pages as private if they only\n                    // have one reference.\n                    private_pages += info.shared_pages_resident;\n                }\n                break;\n            case SM_SHARED:\n            default:\n                break;\n        }\n    }\n\n    mach_port_deallocate(mach_task_self(), task);\n    return Py_BuildValue(\"K\", private_pages * pagesize);\n}\n\n\n/*\n * Return process threads\n */\nPyObject *\npsutil_proc_threads(PyObject *self, PyObject *args) {\n    pid_t pid;\n    kern_return_t kr;\n    mach_port_t task = MACH_PORT_NULL;\n    struct task_basic_info tasks_info;\n    thread_act_port_array_t thread_list = NULL;\n    thread_info_data_t thinfo_basic;\n    thread_basic_info_t basic_info_th;\n    mach_msg_type_number_t thread_count, thread_info_count, j;\n\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    if (psutil_task_for_pid(pid, &task) != 0)\n        goto error;\n\n    // Get basic task info (optional, ignored if access denied)\n    mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT;\n    kr = task_info(\n        task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count\n    );\n    if (kr != KERN_SUCCESS) {\n        if (kr == KERN_INVALID_ARGUMENT) {\n            psutil_oserror_ad(\"task_info(TASK_BASIC_INFO)\");\n        }\n        else {\n            // otherwise throw a runtime error with appropriate error code\n            psutil_runtime_error(\"task_info(TASK_BASIC_INFO) syscall failed\");\n        }\n        goto error;\n    }\n\n    kr = task_threads(task, &thread_list, &thread_count);\n    if (kr != KERN_SUCCESS) {\n        psutil_runtime_error(\"task_threads() syscall failed\");\n        goto error;\n    }\n\n    for (j = 0; j < thread_count; j++) {\n        thread_info_count = THREAD_INFO_MAX;\n        kr = thread_info(\n            thread_list[j],\n            THREAD_BASIC_INFO,\n            (thread_info_t)thinfo_basic,\n            &thread_info_count\n        );\n        if (kr != KERN_SUCCESS) {\n            psutil_runtime_error(\n                \"thread_info(THREAD_BASIC_INFO) syscall failed\"\n            );\n            goto error;\n        }\n\n        basic_info_th = (thread_basic_info_t)thinfo_basic;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"Iff\",\n                j + 1,\n                basic_info_th->user_time.seconds\n                    + (float)basic_info_th->user_time.microseconds / 1000000.0,\n                basic_info_th->system_time.seconds\n                    + (float)basic_info_th->system_time.microseconds\n                          / 1000000.0\n            ))\n        {\n            goto error;\n        }\n    }\n\n    // deallocate thread_list if it was allocated\n    if (thread_list != NULL) {\n        vm_deallocate(\n            mach_task_self(),\n            (vm_address_t)thread_list,\n            thread_count * sizeof(thread_act_t)\n        );\n        thread_list = NULL;\n    }\n\n    // deallocate the task port\n    if (task != MACH_PORT_NULL) {\n        mach_port_deallocate(mach_task_self(), task);\n        task = MACH_PORT_NULL;\n    }\n\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_retlist);\n\n    if (thread_list != NULL) {\n        vm_deallocate(\n            mach_task_self(),\n            (vm_address_t)thread_list,\n            thread_count * sizeof(thread_act_t)\n        );\n        thread_list = NULL;\n    }\n\n    if (task != MACH_PORT_NULL) {\n        mach_port_deallocate(mach_task_self(), task);\n        task = MACH_PORT_NULL;\n    }\n\n    return NULL;\n}\n\n\n/*\n * Return process open files as a Python tuple.\n * See lsof source code:\n * https://github.com/apple-opensource/lsof/blob/28/lsof/dialects/darwin/libproc/dproc.c#L342\n * ...and /usr/include/sys/proc_info.h\n */\nPyObject *\npsutil_proc_open_files(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int num_fds;\n    int i;\n    unsigned long nb;\n    struct proc_fdinfo *fds_pointer = NULL;\n    struct proc_fdinfo *fdp_pointer;\n    struct vnode_fdinfowithpath vi;\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_path = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    // see: https://github.com/giampaolo/psutil/issues/2116\n    if (pid == 0)\n        return py_retlist;\n\n    fds_pointer = psutil_proc_list_fds(pid, &num_fds);\n    if (fds_pointer == NULL)\n        goto error;\n\n    for (i = 0; i < num_fds; i++) {\n        fdp_pointer = &fds_pointer[i];\n\n        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {\n            errno = 0;\n            nb = proc_pidfdinfo(\n                (pid_t)pid,\n                fdp_pointer->proc_fd,\n                PROC_PIDFDVNODEPATHINFO,\n                &vi,\n                sizeof(vi)\n            );\n\n            // --- errors checking\n            if ((nb <= 0) || nb < sizeof(vi)) {\n                if ((errno == ENOENT) || (errno == EBADF)) {\n                    // no such file or directory or bad file descriptor;\n                    // let's assume the file has been closed or removed\n                    continue;\n                }\n                else {\n                    psutil_raise_for_pid(\n                        pid, \"proc_pidinfo(PROC_PIDFDVNODEPATHINFO)\"\n                    );\n                    goto error;\n                }\n            }\n            // --- /errors checking\n\n            // --- construct python list\n            py_path = PyUnicode_DecodeFSDefault(vi.pvip.vip_path);\n            if (!py_path)\n                goto error;\n            if (!pylist_append_fmt(\n                    py_retlist, \"(Oi)\", py_path, (int)fdp_pointer->proc_fd\n                ))\n            {\n                goto error;\n            }\n            Py_CLEAR(py_path);\n            // --- /construct python list\n        }\n    }\n\n    free(fds_pointer);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_path);\n    Py_DECREF(py_retlist);\n    if (fds_pointer != NULL)\n        free(fds_pointer);\n    return NULL;  // exception has already been set earlier\n}\n\n\n/*\n * Return process TCP and UDP connections as a list of tuples.\n * Raises NSP in case of zombie process.\n * See lsof source code:\n * https://github.com/apple-opensource/lsof/blob/28/lsof/dialects/darwin/libproc/dproc.c#L342\n * ...and /usr/include/sys/proc_info.h\n */\nPyObject *\npsutil_proc_net_connections(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int num_fds;\n    int i;\n    unsigned long nb;\n    struct proc_fdinfo *fds_pointer = NULL;\n    struct proc_fdinfo *fdp_pointer;\n    struct socket_fdinfo si;\n    const char *ntopret;\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_laddr = NULL;\n    PyObject *py_raddr = NULL;\n    PyObject *py_af_filter = NULL;\n    PyObject *py_type_filter = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (!PyArg_ParseTuple(\n            args, _Py_PARSE_PID \"OO\", &pid, &py_af_filter, &py_type_filter\n        ))\n    {\n        goto error;\n    }\n\n    // see: https://github.com/giampaolo/psutil/issues/2116\n    if (pid == 0)\n        return py_retlist;\n\n    if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {\n        PyErr_SetString(PyExc_TypeError, \"arg 2 or 3 is not a sequence\");\n        goto error;\n    }\n\n    fds_pointer = psutil_proc_list_fds(pid, &num_fds);\n    if (fds_pointer == NULL)\n        goto error;\n\n    for (i = 0; i < num_fds; i++) {\n        py_laddr = NULL;\n        py_raddr = NULL;\n        fdp_pointer = &fds_pointer[i];\n\n        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) {\n            nb = proc_pidfdinfo(\n                pid,\n                fdp_pointer->proc_fd,\n                PROC_PIDFDSOCKETINFO,\n                &si,\n                sizeof(si)\n            );\n\n            // --- errors checking\n            if ((nb <= 0) || (nb < sizeof(si))) {\n                if (errno == EBADF) {\n                    // let's assume socket has been closed\n                    psutil_debug(\n                        \"proc_pidfdinfo(PROC_PIDFDSOCKETINFO) -> \"\n                        \"EBADF (ignored)\"\n                    );\n                    continue;\n                }\n                else if (errno == EOPNOTSUPP) {\n                    // may happen sometimes, see:\n                    // https://github.com/giampaolo/psutil/issues/1512\n                    psutil_debug(\n                        \"proc_pidfdinfo(PROC_PIDFDSOCKETINFO) -> \"\n                        \"EOPNOTSUPP (ignored)\"\n                    );\n                    continue;\n                }\n                else {\n                    psutil_raise_for_pid(\n                        pid, \"proc_pidinfo(PROC_PIDFDSOCKETINFO)\"\n                    );\n                    goto error;\n                }\n            }\n            // --- /errors checking\n\n            //\n            int fd, family, type, lport, rport, state;\n            char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];\n            int inseq;\n            PyObject *py_family;\n            PyObject *py_type;\n\n            fd = (int)fdp_pointer->proc_fd;\n            family = si.psi.soi_family;\n            type = si.psi.soi_type;\n\n            // apply filters\n            py_family = PyLong_FromLong((long)family);\n            inseq = PySequence_Contains(py_af_filter, py_family);\n            Py_DECREF(py_family);\n            if (inseq == -1)\n                goto error;\n            if (inseq == 0)\n                continue;\n\n            py_type = PyLong_FromLong((long)type);\n            inseq = PySequence_Contains(py_type_filter, py_type);\n            Py_DECREF(py_type);\n            if (inseq == -1)\n                goto error;\n            if (inseq == 0)\n                continue;\n\n            if ((family == AF_INET) || (family == AF_INET6)) {\n                if (family == AF_INET) {\n                    ntopret = inet_ntop(\n                        AF_INET,\n                        &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46\n                             .i46a_addr4,\n                        lip,\n                        sizeof(lip)\n                    );\n                    if (!ntopret) {\n                        psutil_oserror_wsyscall(\"inet_ntop()\");\n                        goto error;\n                    }\n                    ntopret = inet_ntop(\n                        AF_INET,\n                        &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46\n                             .i46a_addr4,\n                        rip,\n                        sizeof(rip)\n                    );\n                    if (!ntopret) {\n                        psutil_oserror_wsyscall(\"inet_ntop()\");\n                        goto error;\n                    }\n                }\n                else {\n                    ntopret = inet_ntop(\n                        AF_INET6,\n                        &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6,\n                        lip,\n                        sizeof(lip)\n                    );\n                    if (!ntopret) {\n                        psutil_oserror_wsyscall(\"inet_ntop()\");\n                        goto error;\n                    }\n                    ntopret = inet_ntop(\n                        AF_INET6,\n                        &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6,\n                        rip,\n                        sizeof(rip)\n                    );\n                    if (!ntopret) {\n                        psutil_oserror_wsyscall(\"inet_ntop()\");\n                        goto error;\n                    }\n                }\n\n                lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport);\n                rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);\n                if (type == SOCK_STREAM)\n                    state = (int)si.psi.soi_proto.pri_tcp.tcpsi_state;\n                else\n                    state = PSUTIL_CONN_NONE;\n\n                py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n                if (!py_laddr)\n                    goto error;\n                if (rport != 0)\n                    py_raddr = Py_BuildValue(\"(si)\", rip, rport);\n                else\n                    py_raddr = Py_BuildValue(\"()\");\n                if (!py_raddr)\n                    goto error;\n\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiNNi)\",\n                        fd,\n                        family,\n                        type,\n                        py_laddr,\n                        py_raddr,\n                        state\n                    ))\n                {\n                    goto error;\n                }\n                py_laddr = NULL;\n                py_raddr = NULL;\n            }\n            else if (family == AF_UNIX) {\n                py_laddr = PyUnicode_DecodeFSDefault(\n                    si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path\n                );\n                if (!py_laddr)\n                    goto error;\n                py_raddr = PyUnicode_DecodeFSDefault(\n                    si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path\n                );\n                if (!py_raddr)\n                    goto error;\n\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiOOi)\",\n                        fd,\n                        family,\n                        type,\n                        py_laddr,\n                        py_raddr,\n                        PSUTIL_CONN_NONE\n                    ))\n                {\n                    goto error;\n                }\n                Py_CLEAR(py_laddr);\n                Py_CLEAR(py_raddr);\n            }\n        }\n    }\n\n    free(fds_pointer);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_laddr);\n    Py_XDECREF(py_raddr);\n    Py_DECREF(py_retlist);\n    if (fds_pointer != NULL)\n        free(fds_pointer);\n    return NULL;\n}\n\n\n/*\n * Return number of file descriptors opened by process.\n * Raises NSP in case of zombie process.\n */\nPyObject *\npsutil_proc_num_fds(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int num_fds;\n    struct proc_fdinfo *fds_pointer;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    fds_pointer = psutil_proc_list_fds(pid, &num_fds);\n    if (fds_pointer == NULL)\n        return NULL;\n\n    free(fds_pointer);\n    return Py_BuildValue(\"i\", num_fds);\n}\n\n\n// return process args as a python list\nPyObject *\npsutil_proc_cmdline(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int nargs;\n    size_t len;\n    char *procargs = NULL;\n    char *arg_ptr;\n    char *arg_end;\n    char *curr_arg;\n    size_t argmax;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    // special case for PID 0 (kernel_task) where cmdline cannot be fetched\n    if (pid == 0)\n        return py_retlist;\n\n    // read argmax and allocate memory for argument space.\n    argmax = psutil_sysctl_argmax();\n    if (argmax == 0)\n        goto error;\n\n    procargs = (char *)malloc(argmax);\n    if (NULL == procargs) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    if (psutil_sysctl_procargs(pid, procargs, &argmax) != 0)\n        goto error;\n\n    arg_end = &procargs[argmax];\n    // copy the number of arguments to nargs\n    memcpy(&nargs, procargs, sizeof(nargs));\n\n    arg_ptr = procargs + sizeof(nargs);\n    len = strlen(arg_ptr);\n    arg_ptr += len + 1;\n\n    if (arg_ptr == arg_end) {\n        free(procargs);\n        return py_retlist;\n    }\n\n    // skip ahead to the first argument\n    for (; arg_ptr < arg_end; arg_ptr++) {\n        if (*arg_ptr != '\\0')\n            break;\n    }\n\n    // iterate through arguments\n    curr_arg = arg_ptr;\n    while (arg_ptr < arg_end && nargs > 0) {\n        if (*arg_ptr++ == '\\0') {\n            if (!pylist_append_obj(\n                    py_retlist, PyUnicode_DecodeFSDefault(curr_arg)\n                ))\n                goto error;\n            // iterate to next arg and decrement # of args\n            curr_arg = arg_ptr;\n            nargs--;\n        }\n    }\n\n    free(procargs);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_retlist);\n    if (procargs != NULL)\n        free(procargs);\n    return NULL;\n}\n\n\n// Return process environment as a python string.\n// On Big Sur this function returns an empty string unless:\n// * kernel is DEVELOPMENT || DEBUG\n// * target process is same as current_proc()\n// * target process is not cs_restricted\n// * SIP is off\n// * caller has an entitlement\n// See:\n// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/kern_sysctl.c#L1315-L1321\nPyObject *\npsutil_proc_environ(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int nargs;\n    char *procargs = NULL;\n    char *procenv = NULL;\n    char *arg_ptr;\n    char *arg_end;\n    char *env_start;\n    size_t argmax;\n    size_t env_len;\n    PyObject *py_ret = NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    // PID 0 (kernel_task) has no cmdline.\n    if (pid == 0)\n        goto empty;\n\n    // Allocate buffer for process args.\n    argmax = psutil_sysctl_argmax();\n    if (argmax == 0)\n        goto error;\n\n    procargs = (char *)malloc(argmax);\n    if (procargs == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    if (psutil_sysctl_procargs(pid, procargs, &argmax) != 0)\n        goto error;\n\n    arg_end = procargs + argmax;\n\n    // Copy nargs.\n    memcpy(&nargs, procargs, sizeof(nargs));\n\n    // skip executable path\n    arg_ptr = procargs + sizeof(nargs);\n    arg_ptr = memchr(arg_ptr, '\\0', arg_end - arg_ptr);\n    if (arg_ptr == NULL || arg_ptr >= arg_end)\n        goto empty;\n\n    // Skip null bytes until first argument.\n    while (arg_ptr < arg_end && *arg_ptr == '\\0')\n        arg_ptr++;\n\n    // Skip arguments.\n    while (arg_ptr < arg_end && nargs > 0) {\n        if (*arg_ptr++ == '\\0')\n            nargs--;\n    }\n\n    if (arg_ptr >= arg_end)\n        goto empty;\n\n    env_start = arg_ptr;\n\n    // Compute maximum possible environment length.\n    env_len = (size_t)(arg_end - env_start);\n    if (env_len == 0)\n        goto empty;\n\n    procenv = (char *)calloc(1, env_len);\n    if (procenv == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    while (arg_ptr < arg_end && *arg_ptr != '\\0') {\n        // Find the next NUL terminator.\n        size_t rem = (size_t)(arg_end - arg_ptr);\n        char *s = memchr(arg_ptr, '\\0', rem);\n        if (s == NULL)\n            break;\n\n        size_t copy_len = (size_t)(s - arg_ptr);\n        size_t offset = (size_t)(arg_ptr - env_start);\n        if (offset + copy_len >= env_len)\n            break;  // prevent overflow.\n\n        memcpy(procenv + offset, arg_ptr, copy_len);\n        arg_ptr = s + 1;\n    }\n\n    size_t used = (size_t)(arg_ptr - env_start);\n    if (used >= env_len)\n        used = env_len - 1;\n\n    py_ret = PyUnicode_DecodeFSDefaultAndSize(procenv, (Py_ssize_t)used);\n    if (py_ret == NULL) {\n        procargs = NULL;  // don't double free; see psutil issue #926.\n        goto error;\n    }\n\n    free(procargs);\n    free(procenv);\n    return py_ret;\n\nempty:\n    psutil_debug(\"set environ to empty\");\n    if (procargs != NULL)\n        free(procargs);\n    return PyUnicode_FromString(\"\");\n\nerror:\n    Py_XDECREF(py_ret);\n    if (procargs != NULL)\n        free(procargs);\n    if (procenv != NULL)\n        free(procenv);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/proc_utils.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <sys/user.h>\n#include <unistd.h>\n#include <mach/mach.h>\n#include <libproc.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"../../arch/all/init.h\"\n\n\nint\npsutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) {\n    int mib[4];\n    size_t len;\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PID;\n    mib[3] = pid;\n\n    if (pid < 0 || !kp)\n        return psutil_badargs(\"psutil_get_kinfo_proc\");\n\n    len = sizeof(struct kinfo_proc);\n\n    if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) {\n        // raise an exception and throw errno as the error\n        psutil_oserror_wsyscall(\"sysctl\");\n        return -1;\n    }\n\n    // sysctl succeeds but len is zero, happens when process has gone away\n    if (len == 0) {\n        psutil_oserror_nsp(\"sysctl(kinfo_proc), len == 0\");\n        return -1;\n    }\n    return 0;\n}\n\n\n// Return 1 if PID a zombie, else 0 (including on error).\nint\nis_zombie(size_t pid) {\n    struct kinfo_proc kp;\n\n    if (psutil_get_kinfo_proc(pid, &kp) == -1) {\n        errno = 0;\n        PyErr_Clear();\n        return 0;\n    }\n    return kp.kp_proc.p_stat == SZOMB;\n}\n\n\n// Read process argument space.\nint\npsutil_sysctl_procargs(pid_t pid, char *procargs, size_t *argmax) {\n    int mib[3];\n\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROCARGS2;\n    mib[2] = pid;\n\n    if (pid < 0 || !procargs || !argmax || *argmax == 0)\n        return psutil_badargs(\"psutil_sysctl_procargs\");\n\n    if (sysctl(mib, 3, procargs, argmax, NULL, 0) < 0) {\n        if (psutil_pid_exists(pid) == 0) {\n            psutil_oserror_nsp(\"psutil_pid_exists -> 0\");\n            return -1;\n        }\n\n        if (is_zombie(pid) == 1) {\n            PyErr_SetString(ZombieProcessError, \"\");\n            return -1;\n        }\n\n        if (errno == EINVAL) {\n            psutil_debug(\"sysctl(KERN_PROCARGS2) -> EINVAL translated to AD\");\n            psutil_oserror_ad(\"sysctl(KERN_PROCARGS2) -> EINVAL\");\n            return -1;\n        }\n\n        if (errno == EIO) {\n            psutil_debug(\"sysctl(KERN_PROCARGS2) -> EIO translated to AD\");\n            psutil_oserror_ad(\"sysctl(KERN_PROCARGS2) -> EIO\");\n            return -1;\n        }\n        if (errno == 0) {\n            // see: https://github.com/giampaolo/psutil/issues/2708\n            psutil_debug(\"sysctl(KERN_PROCARGS2) -> errno 0\");\n            psutil_oserror_ad(\"sysctl(KERN_PROCARGS2) -> errno 0\");\n            return 0;\n        }\n        psutil_oserror_wsyscall(\"sysctl(KERN_PROCARGS2)\");\n        return -1;\n    }\n    return 0;\n}\n\n\n/*\n * A wrapper around proc_pidinfo().\n * https://opensource.apple.com/source/xnu/xnu-2050.7.9/bsd/kern/proc_info.c\n * Returns 0 on failure.\n */\nint\npsutil_proc_pidinfo(pid_t pid, int flavor, uint64_t arg, void *pti, int size) {\n    int ret;\n\n    if (pid < 0 || !pti || size <= 0)\n        return psutil_badargs(\"psutil_proc_pidinfo\");\n\n    errno = 0;\n    ret = proc_pidinfo(pid, flavor, arg, pti, size);\n    if (ret <= 0) {\n        psutil_raise_for_pid(pid, \"proc_pidinfo()\");\n        return -1;\n    }\n\n    // check for truncated return size\n    if (ret < size) {\n        psutil_raise_for_pid(\n            pid, \"proc_pidinfo() returned less data than requested buffer size\"\n        );\n        return -1;\n    }\n\n    return 0;\n}\n\n\n/*\n * A wrapper around task_for_pid() which sucks big time:\n * - it's not documented\n * - errno is set only sometimes\n * - sometimes errno is ENOENT (?!?)\n * - for PIDs != getpid() or PIDs which are not members of the procmod\n *   it requires root\n * As such we can only guess what the heck went wrong and fail either\n * with NoSuchProcess or give up with AccessDenied.\n * References:\n * https://github.com/giampaolo/psutil/issues/1181\n * https://github.com/giampaolo/psutil/issues/1209\n * https://github.com/giampaolo/psutil/issues/1291#issuecomment-396062519\n */\nint\npsutil_task_for_pid(pid_t pid, mach_port_t *task) {\n    kern_return_t err;\n\n    if (pid < 0 || !task)\n        return psutil_badargs(\"psutil_task_for_pid\");\n\n    err = task_for_pid(mach_task_self(), pid, task);\n    if (err != KERN_SUCCESS) {\n        if (psutil_pid_exists(pid) == 0) {\n            psutil_oserror_nsp(\"task_for_pid\");\n        }\n        else if (is_zombie(pid) == 1) {\n            PyErr_SetString(\n                ZombieProcessError, \"task_for_pid -> psutil_is_zombie -> 1\"\n            );\n        }\n        else {\n            psutil_debug(\n                \"task_for_pid() failed (pid=%ld, err=%i, errno=%i, msg='%s'); \"\n                \"setting EACCES\",\n                (long)pid,\n                err,\n                errno,\n                mach_error_string(err)\n            );\n            psutil_oserror_ad(\"task_for_pid\");\n        }\n        return -1;\n    }\n\n    return 0;\n}\n\n\n/*\n * A wrapper around proc_pidinfo(PROC_PIDLISTFDS), which dynamically sets\n * the buffer size.\n */\nstruct proc_fdinfo *\npsutil_proc_list_fds(pid_t pid, int *num_fds) {\n    int ret;\n    int fds_size = 0;\n    int max_size = 24 * 1024 * 1024;  // 24M\n    struct proc_fdinfo *fds_pointer = NULL;\n\n    if (pid < 0 || num_fds == NULL) {\n        psutil_badargs(\"psutil_proc_list_fds\");\n        return NULL;\n    }\n\n    errno = 0;\n    ret = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);\n    if (ret <= 0) {\n        psutil_raise_for_pid(pid, \"proc_pidinfo(PROC_PIDLISTFDS) 1/2\");\n        goto error;\n    }\n\n    while (1) {\n        if (ret > fds_size) {\n            while (ret > fds_size) {\n                fds_size += PROC_PIDLISTFD_SIZE * 32;\n                if (fds_size > max_size) {\n                    psutil_runtime_error(\"prevent malloc() to allocate > 24M\");\n                    goto error;\n                }\n            }\n\n            if (fds_pointer != NULL) {\n                free(fds_pointer);\n            }\n            fds_pointer = malloc(fds_size);\n\n            if (fds_pointer == NULL) {\n                PyErr_NoMemory();\n                goto error;\n            }\n        }\n\n        errno = 0;\n        ret = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, fds_size);\n        if (ret <= 0) {\n            psutil_raise_for_pid(pid, \"proc_pidinfo(PROC_PIDLISTFDS) 2/2\");\n            goto error;\n        }\n\n        if (ret + (int)PROC_PIDLISTFD_SIZE >= fds_size) {\n            psutil_debug(\"PROC_PIDLISTFDS: make room for 1 extra fd\");\n            ret = fds_size + (int)PROC_PIDLISTFD_SIZE;\n            continue;\n        }\n\n        break;\n    }\n\n    *num_fds = (ret / (int)PROC_PIDLISTFD_SIZE);\n    return fds_pointer;\n\nerror:\n    if (fds_pointer != NULL)\n        free(fds_pointer);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/sensors.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Sensors related functions. Original code was refactored and moved\n// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:\n// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c\n// Original battery code:\n// https://github.com/giampaolo/psutil/commit/e0df5da\n\n\n#include <Python.h>\n#include <CoreFoundation/CoreFoundation.h>\n#include <IOKit/ps/IOPowerSources.h>\n#include <IOKit/ps/IOPSKeys.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_sensors_battery(PyObject *self, PyObject *args) {\n    PyObject *py_tuple = NULL;\n    CFTypeRef power_info = NULL;\n    CFArrayRef power_sources_list = NULL;\n    CFDictionaryRef power_sources_information = NULL;\n    CFNumberRef capacity_ref = NULL;\n    CFNumberRef time_to_empty_ref = NULL;\n    CFStringRef ps_state_ref = NULL;\n    uint32_t capacity;  // units are percent\n    int time_to_empty;  // units are minutes\n    int is_power_plugged;\n\n    power_info = IOPSCopyPowerSourcesInfo();\n    if (!power_info) {\n        psutil_runtime_error(\"IOPSCopyPowerSourcesInfo() syscall failed\");\n        goto error;\n    }\n\n    power_sources_list = IOPSCopyPowerSourcesList(power_info);\n    if (!power_sources_list) {\n        psutil_runtime_error(\"IOPSCopyPowerSourcesList() syscall failed\");\n        goto error;\n    }\n\n    if (CFArrayGetCount(power_sources_list) == 0) {\n        PyErr_SetString(PyExc_NotImplementedError, \"no battery\");\n        goto error;\n    }\n\n    power_sources_information = IOPSGetPowerSourceDescription(\n        power_info, CFArrayGetValueAtIndex(power_sources_list, 0)\n    );\n    if (!power_sources_information) {\n        psutil_runtime_error(\"Failed to get power source description\");\n        goto error;\n    }\n\n    capacity_ref = (CFNumberRef)CFDictionaryGetValue(\n        power_sources_information, CFSTR(kIOPSCurrentCapacityKey)\n    );\n    if (!capacity_ref\n        || !CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity))\n    {\n        psutil_runtime_error(\n            \"No battery capacity information in power sources info\"\n        );\n        goto error;\n    }\n\n    ps_state_ref = (CFStringRef)CFDictionaryGetValue(\n        power_sources_information, CFSTR(kIOPSPowerSourceStateKey)\n    );\n    if (!ps_state_ref) {\n        psutil_runtime_error(\"power source state info missing\");\n        goto error;\n    }\n    is_power_plugged = CFStringCompare(\n                           ps_state_ref, CFSTR(kIOPSACPowerValue), 0\n                       )\n                       == kCFCompareEqualTo;\n\n    time_to_empty_ref = (CFNumberRef)CFDictionaryGetValue(\n        power_sources_information, CFSTR(kIOPSTimeToEmptyKey)\n    );\n    if (!time_to_empty_ref\n        || !CFNumberGetValue(\n            time_to_empty_ref, kCFNumberIntType, &time_to_empty\n        ))\n    {\n        /* This value is recommended for non-Apple power sources, so it's not\n         * an error if it doesn't exist. We'll return -1 for \"unknown\" */\n        /* A value of -1 indicates \"Still Calculating the Time\" also for\n         * apple power source */\n        time_to_empty = -1;\n    }\n\n    py_tuple = Py_BuildValue(\n        \"dii\", (double)capacity, time_to_empty, is_power_plugged\n    );\n    if (!py_tuple)\n        goto error;\n\n    CFRelease(power_info);\n    CFRelease(power_sources_list);\n    /* Caller should NOT release power_sources_information */\n    return py_tuple;\n\nerror:\n    if (power_info)\n        CFRelease(power_info);\n    if (power_sources_list)\n        CFRelease(power_sources_list);\n    Py_XDECREF(py_tuple);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/osx/sys.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// System related functions. Original code was refactored and moved\n// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:\n// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c\n\n#include <Python.h>\n#include <sys/sysctl.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_boot_time(PyObject *self, PyObject *args) {\n    // fetch sysctl \"kern.boottime\"\n    int mib[2] = {CTL_KERN, KERN_BOOTTIME};\n    struct timeval result;\n    time_t boot_time = 0;\n\n    if (psutil_sysctl(mib, 2, &result, sizeof(result)) == -1)\n        return NULL;\n    boot_time = result.tv_sec;\n    return Py_BuildValue(\"d\", (double)boot_time);\n}\n"
  },
  {
    "path": "psutil/arch/posix/init.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <sys/resource.h>\n#include <sys/socket.h>\n\n#include \"init.h\"\n\nPyObject *ZombieProcessError = NULL;\n\n/*\n * From \"man getpagesize\" on Linux, https://linux.die.net/man/2/getpagesize:\n *\n * > In SUSv2 the getpagesize() call is labeled LEGACY, and in POSIX.1-2001\n * > it has been dropped.\n * > Portable applications should employ sysconf(_SC_PAGESIZE) instead\n * > of getpagesize().\n * > Most systems allow the synonym _SC_PAGE_SIZE for _SC_PAGESIZE.\n * > Whether getpagesize() is present as a Linux system call depends on the\n * > architecture.\n */\nlong\npsutil_getpagesize(void) {\n#ifdef _SC_PAGESIZE\n    // recommended POSIX\n    return sysconf(_SC_PAGESIZE);\n#elif _SC_PAGE_SIZE\n    // alias\n    return sysconf(_SC_PAGE_SIZE);\n#else\n    // legacy\n    return (long)getpagesize();\n#endif\n}\n\n\n// Exposed so we can test it against Python's stdlib.\nPyObject *\npsutil_getpagesize_pywrapper(PyObject *self, PyObject *args) {\n    return Py_BuildValue(\"l\", psutil_getpagesize());\n}\n\n\n// POSIX-only methods.\nstatic PyMethodDef posix_methods[] = {\n    {\"getpagesize\", psutil_getpagesize_pywrapper, METH_VARARGS},\n    {\"net_if_addrs\", psutil_net_if_addrs, METH_VARARGS},\n    {\"net_if_flags\", psutil_net_if_flags, METH_VARARGS},\n    {\"net_if_is_running\", psutil_net_if_is_running, METH_VARARGS},\n    {\"net_if_mtu\", psutil_net_if_mtu, METH_VARARGS},\n    {\"proc_priority_get\", psutil_proc_priority_get, METH_VARARGS},\n    {\"proc_priority_set\", psutil_proc_priority_set, METH_VARARGS},\n#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\n    {\"net_if_duplex_speed\", psutil_net_if_duplex_speed, METH_VARARGS},\n#endif\n#if !defined(PSUTIL_OPENBSD) && !defined(PSUTIL_AIX)\n    {\"users\", psutil_users, METH_VARARGS},\n#endif\n#if defined(PSUTIL_OSX) || defined(PSUTIL_BSD)\n    {\"proc_is_zombie\", psutil_proc_is_zombie, METH_VARARGS},\n#endif\n    {NULL, NULL, 0, NULL}\n};\n\n\n// Add POSIX methods to main OS module.\nint\npsutil_posix_add_methods(PyObject *mod) {\n    for (int i = 0; posix_methods[i].ml_name != NULL; i++) {\n        PyObject *f = PyCFunction_NewEx(&posix_methods[i], NULL, mod);\n        if (!f) {\n            return -1;\n        }\n        if (PyModule_AddObject(mod, posix_methods[i].ml_name, f)) {\n            Py_DECREF(f);\n            return -1;\n        }\n    }\n\n    // custom exception\n    ZombieProcessError = PyErr_NewException(\n        \"_psutil_posix.ZombieProcessError\", NULL, NULL\n    );\n    if (ZombieProcessError == NULL)\n        return -1;\n    if (PyModule_AddObject(mod, \"ZombieProcessError\", ZombieProcessError))\n        return -1;\n\n    return 0;\n}\n\n\n// Add POSIX constants to main OS module.\nint\npsutil_posix_add_constants(PyObject *mod) {\n    if (!mod)\n        return -1;\n\n#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) \\\n    || defined(PSUTIL_AIX)\n    if (PyModule_AddIntConstant(mod, \"AF_LINK\", AF_LINK))\n        return -1;\n#endif\n\n#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)\n    PyObject *v;\n\n#ifdef RLIMIT_AS\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_AS\", RLIMIT_AS))\n        return -1;\n#endif\n\n#ifdef RLIMIT_CORE\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_CORE\", RLIMIT_CORE))\n        return -1;\n#endif\n\n#ifdef RLIMIT_CPU\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_CPU\", RLIMIT_CPU))\n        return -1;\n#endif\n\n#ifdef RLIMIT_DATA\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_DATA\", RLIMIT_DATA))\n        return -1;\n#endif\n\n#ifdef RLIMIT_FSIZE\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_FSIZE\", RLIMIT_FSIZE))\n        return -1;\n#endif\n\n#ifdef RLIMIT_MEMLOCK\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_MEMLOCK\", RLIMIT_MEMLOCK))\n        return -1;\n#endif\n\n#ifdef RLIMIT_NOFILE\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_NOFILE\", RLIMIT_NOFILE))\n        return -1;\n#endif\n\n#ifdef RLIMIT_NPROC\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_NPROC\", RLIMIT_NPROC))\n        return -1;\n#endif\n\n#ifdef RLIMIT_RSS\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_RSS\", RLIMIT_RSS))\n        return -1;\n#endif\n\n#ifdef RLIMIT_STACK\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_STACK\", RLIMIT_STACK))\n        return -1;\n#endif\n\n        // Linux specific\n\n#ifdef RLIMIT_LOCKS\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_LOCKS\", RLIMIT_LOCKS))\n        return -1;\n#endif\n\n#ifdef RLIMIT_MSGQUEUE\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_MSGQUEUE\", RLIMIT_MSGQUEUE))\n        return -1;\n#endif\n\n#ifdef RLIMIT_NICE\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_NICE\", RLIMIT_NICE))\n        return -1;\n#endif\n\n#ifdef RLIMIT_RTPRIO\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_RTPRIO\", RLIMIT_RTPRIO))\n        return -1;\n#endif\n\n#ifdef RLIMIT_RTTIME\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_RTTIME\", RLIMIT_RTTIME))\n        return -1;\n#endif\n\n#ifdef RLIMIT_SIGPENDING\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_SIGPENDING\", RLIMIT_SIGPENDING))\n        return -1;\n#endif\n\n        // Free specific\n\n#ifdef RLIMIT_SWAP\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_SWAP\", RLIMIT_SWAP))\n        return -1;\n#endif\n\n#ifdef RLIMIT_SBSIZE\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_SBSIZE\", RLIMIT_SBSIZE))\n        return -1;\n#endif\n\n#ifdef RLIMIT_NPTS\n    if (PyModule_AddIntConstant(mod, \"RLIMIT_NPTS\", RLIMIT_NPTS))\n        return -1;\n#endif\n\n#if defined(HAVE_LONG_LONG)\n    if (sizeof(RLIM_INFINITY) > sizeof(long)) {\n        v = PyLong_FromLongLong((PY_LONG_LONG)RLIM_INFINITY);\n    }\n    else\n#endif\n    {\n        v = PyLong_FromLong((long)RLIM_INFINITY);\n    }\n    if (v) {\n        if (PyModule_AddObject(mod, \"RLIM_INFINITY\", v))\n            return -1;\n    }\n#endif  // defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)\n\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/posix/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\nextern PyObject *ZombieProcessError;\n\n// convert a timeval struct to a double\n#ifdef PSUTIL_SUNOS\n#define PSUTIL_TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec)\n#else\n#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)\n#endif\n\n// clang-format off\n#if !defined(PSUTIL_OPENBSD) && !defined(PSUTIL_AIX)\n    #define PSUTIL_HAS_POSIX_USERS\n    PyObject *psutil_users(PyObject *self, PyObject *args);\n#endif\n\n#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\n    #include <sys/types.h>\n    #define PSUTIL_HAS_SYSCTL\n    int psutil_sysctl(int *mib, u_int miblen, void *buf, size_t buflen);\n    int psutil_sysctl_malloc(int *mib, u_int miblen, char **buf, size_t *buflen);\n    size_t psutil_sysctl_argmax();\n\n    #if !defined(PSUTIL_OPENBSD)\n        #define PSUTIL_HAS_SYSCTLBYNAME\n        int psutil_sysctlbyname(const char *name, void *buf, size_t buflen);\n        int psutil_sysctlbyname_malloc(const char *name, char **buf, size_t *buflen);\n    #endif\n#endif\n\n#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\n    #define PSUTIL_HAS_NET_IF_DUPLEX_SPEED\n    PyObject *psutil_net_if_duplex_speed(PyObject *self, PyObject *args);\n#endif\n// clang-format on\n\n// --- internal utils\n\nint psutil_pid_exists(pid_t pid);\nlong psutil_getpagesize(void);\nint psutil_posix_add_constants(PyObject *mod);\nint psutil_posix_add_methods(PyObject *mod);\nPyObject *psutil_raise_for_pid(pid_t pid, char *msg);\n\n// --- Python wrappers\n\nPyObject *psutil_getpagesize_pywrapper(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_addrs(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_flags(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_is_running(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_mtu(PyObject *self, PyObject *args);\nPyObject *psutil_proc_priority_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_priority_set(PyObject *self, PyObject *args);\n#if defined(PSUTIL_OSX) || defined(PSUTIL_BSD)\nPyObject *psutil_proc_is_zombie(PyObject *self, PyObject *args);\n#endif\n"
  },
  {
    "path": "psutil/arch/posix/net.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/ioctl.h>\n#include <net/if.h>\n#include <unistd.h>\n\n#ifdef PSUTIL_AIX\n#include \"arch/aix/ifaddrs.h\"\n#else\n#include <ifaddrs.h>\n#endif\n\n#if defined(PSUTIL_LINUX)\n#include <netdb.h>\n#include <linux/types.h>\n#include <linux/if_packet.h>\n#endif\n\n#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\n#include <netdb.h>\n#include <netinet/in.h>\n#include <net/if_dl.h>\n#include <sys/sockio.h>\n#include <net/if_media.h>\n#include <net/if.h>\n#endif\n\n#if defined(PSUTIL_SUNOS)\n#include <netdb.h>\n#include <sys/sockio.h>\n#endif\n\n#if defined(PSUTIL_AIX)\n#include <netdb.h>\n#endif\n\n#include \"../../arch/all/init.h\"\n\n\n/*\n * Translate a sockaddr struct into a Python string.\n * Return None if address family is not AF_INET* or AF_PACKET.\n */\nPyObject *\npsutil_convert_ipaddr(struct sockaddr *addr, int family) {\n    char buf[NI_MAXHOST];\n    int err;\n    int addrlen;\n    size_t n;\n    size_t len;\n    const char *data;\n    char *ptr;\n\n    if (addr == NULL) {\n        Py_RETURN_NONE;\n    }\n    else if (family == AF_INET || family == AF_INET6) {\n        if (family == AF_INET)\n            addrlen = sizeof(struct sockaddr_in);\n        else\n            addrlen = sizeof(struct sockaddr_in6);\n        err = getnameinfo(\n            addr, addrlen, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST\n        );\n        if (err != 0) {\n            // XXX we get here on FreeBSD when processing 'lo' / AF_INET6\n            // broadcast. Not sure what to do other than returning None.\n            // ifconfig does not show anything BTW.\n            // psutil_runtime_error(gai_strerror(err));\n            // return NULL;\n            Py_RETURN_NONE;\n        }\n        else {\n            return PyUnicode_FromString(buf);\n        }\n    }\n#ifdef PSUTIL_LINUX\n    else if (family == AF_PACKET) {\n        struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr;\n        len = lladdr->sll_halen;\n        data = (const char *)lladdr->sll_addr;\n    }\n#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX)\n    else if (addr->sa_family == AF_LINK) {\n        // Note: prior to Python 3.4 socket module does not expose\n        // AF_LINK so we'll do.\n        struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr;\n        len = dladdr->sdl_alen;\n        data = LLADDR(dladdr);\n    }\n#endif\n    else {\n        // unknown family\n        Py_RETURN_NONE;\n    }\n\n    // AF_PACKET or AF_LINK\n    if (len > 0) {\n        ptr = buf;\n        for (n = 0; n < len; ++n) {\n            str_format(ptr, sizeof(ptr), \"%02x:\", data[n] & 0xff);\n            ptr += 3;\n        }\n        *--ptr = '\\0';\n        return PyUnicode_FromString(buf);\n    }\n    else {\n        Py_RETURN_NONE;\n    }\n}\n\n\n/*\n * Return NICs information a-la ifconfig as a list of tuples.\n * TODO: on Solaris we won't get any MAC address.\n */\nPyObject *\npsutil_net_if_addrs(PyObject *self, PyObject *args) {\n    struct ifaddrs *ifaddr, *ifa;\n    int family;\n\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_address = NULL;\n    PyObject *py_netmask = NULL;\n    PyObject *py_broadcast = NULL;\n    PyObject *py_ptp = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (getifaddrs(&ifaddr) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {\n        if (!ifa->ifa_addr)\n            continue;\n        family = ifa->ifa_addr->sa_family;\n        py_address = psutil_convert_ipaddr(ifa->ifa_addr, family);\n        // If the primary address can't be determined just skip it.\n        // I've never seen this happen on Linux but I did on FreeBSD.\n        if (py_address == Py_None)\n            continue;\n        if (py_address == NULL)\n            goto error;\n        py_netmask = psutil_convert_ipaddr(ifa->ifa_netmask, family);\n        if (py_netmask == NULL)\n            goto error;\n\n        if (ifa->ifa_flags & IFF_BROADCAST) {\n            py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family);\n            Py_INCREF(Py_None);\n            py_ptp = Py_None;\n        }\n        else if (ifa->ifa_flags & IFF_POINTOPOINT) {\n            py_ptp = psutil_convert_ipaddr(ifa->ifa_dstaddr, family);\n            Py_INCREF(Py_None);\n            py_broadcast = Py_None;\n        }\n        else {\n            Py_INCREF(Py_None);\n            Py_INCREF(Py_None);\n            py_broadcast = Py_None;\n            py_ptp = Py_None;\n        }\n\n        if ((py_broadcast == NULL) || (py_ptp == NULL))\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(siOOOO)\",\n                ifa->ifa_name,\n                family,\n                py_address,\n                py_netmask,\n                py_broadcast,\n                py_ptp\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_address);\n        Py_CLEAR(py_netmask);\n        Py_CLEAR(py_broadcast);\n        Py_CLEAR(py_ptp);\n    }\n\n    freeifaddrs(ifaddr);\n    return py_retlist;\n\nerror:\n    if (ifaddr != NULL)\n        freeifaddrs(ifaddr);\n    Py_DECREF(py_retlist);\n    Py_XDECREF(py_address);\n    Py_XDECREF(py_netmask);\n    Py_XDECREF(py_broadcast);\n    Py_XDECREF(py_ptp);\n    return NULL;\n}\n\n\n/*\n * Return NIC MTU. References:\n * http://www.i-scream.org/libstatgrab/\n */\nPyObject *\npsutil_net_if_mtu(PyObject *self, PyObject *args) {\n    char *nic_name;\n    int sock = -1;\n    int ret;\n    struct ifreq ifr;\n\n    if (!PyArg_ParseTuple(args, \"s\", &nic_name))\n        return NULL;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1)\n        goto error;\n\n    str_copy(ifr.ifr_name, sizeof(ifr.ifr_name), nic_name);\n    ret = ioctl(sock, SIOCGIFMTU, &ifr);\n    if (ret == -1)\n        goto error;\n    close(sock);\n\n    return Py_BuildValue(\"i\", ifr.ifr_mtu);\n\nerror:\n    if (sock != -1)\n        close(sock);\n    return psutil_oserror();\n}\n\nstatic int\nappend_flag(PyObject *py_retlist, const char *flag_name) {\n    return pylist_append_obj(py_retlist, PyUnicode_FromString(flag_name));\n}\n\n/*\n * Get all of the NIC flags and return them.\n */\nPyObject *\npsutil_net_if_flags(PyObject *self, PyObject *args) {\n    char *nic_name;\n    int sock = -1;\n    int ret;\n    struct ifreq ifr;\n    PyObject *py_retlist = PyList_New(0);\n    short int flags;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (!PyArg_ParseTuple(args, \"s\", &nic_name))\n        goto error;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1) {\n        psutil_oserror_wsyscall(\"socket(SOCK_DGRAM)\");\n        goto error;\n    }\n\n    str_copy(ifr.ifr_name, sizeof(ifr.ifr_name), nic_name);\n    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);\n    if (ret == -1) {\n        psutil_oserror_wsyscall(\"ioctl(SIOCGIFFLAGS)\");\n        goto error;\n    }\n\n    close(sock);\n    sock = -1;\n\n    flags = ifr.ifr_flags & 0xFFFF;\n\n    // Linux/glibc IFF flags:\n    // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/gnu/net/if.h;h=251418f82331c0426e58707fe4473d454893b132;hb=HEAD\n    // macOS IFF flags:\n    // https://opensource.apple.com/source/xnu/xnu-792/bsd/net/if.h.auto.html\n    // AIX IFF flags:\n    // https://www.ibm.com/support/pages/how-hexadecimal-flags-displayed-ifconfig-are-calculated\n    // FreeBSD IFF flags:\n    // https://www.freebsd.org/cgi/man.cgi?query=if_allmulti&apropos=0&sektion=0&manpath=FreeBSD+10-current&format=html\n\n#ifdef IFF_UP\n    // Available in (at least) Linux, macOS, AIX, BSD\n    if (flags & IFF_UP)\n        if (!append_flag(py_retlist, \"up\"))\n            goto error;\n#endif\n#ifdef IFF_BROADCAST\n    // Available in (at least) Linux, macOS, AIX, BSD\n    if (flags & IFF_BROADCAST)\n        if (!append_flag(py_retlist, \"broadcast\"))\n            goto error;\n#endif\n#ifdef IFF_DEBUG\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_DEBUG)\n        if (!append_flag(py_retlist, \"debug\"))\n            goto error;\n#endif\n#ifdef IFF_LOOPBACK\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_LOOPBACK)\n        if (!append_flag(py_retlist, \"loopback\"))\n            goto error;\n#endif\n#ifdef IFF_POINTOPOINT\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_POINTOPOINT)\n        if (!append_flag(py_retlist, \"pointopoint\"))\n            goto error;\n#endif\n#ifdef IFF_NOTRAILERS\n    // Available in (at least) Linux, macOS, AIX\n    if (flags & IFF_NOTRAILERS)\n        if (!append_flag(py_retlist, \"notrailers\"))\n            goto error;\n#endif\n#ifdef IFF_RUNNING\n    // Available in (at least) Linux, macOS, AIX, BSD\n    if (flags & IFF_RUNNING)\n        if (!append_flag(py_retlist, \"running\"))\n            goto error;\n#endif\n#ifdef IFF_NOARP\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_NOARP)\n        if (!append_flag(py_retlist, \"noarp\"))\n            goto error;\n#endif\n#ifdef IFF_PROMISC\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_PROMISC)\n        if (!append_flag(py_retlist, \"promisc\"))\n            goto error;\n#endif\n#ifdef IFF_ALLMULTI\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_ALLMULTI)\n        if (!append_flag(py_retlist, \"allmulti\"))\n            goto error;\n#endif\n#ifdef IFF_MASTER\n    // Available in (at least) Linux\n    if (flags & IFF_MASTER)\n        if (!append_flag(py_retlist, \"master\"))\n            goto error;\n#endif\n#ifdef IFF_SLAVE\n    // Available in (at least) Linux\n    if (flags & IFF_SLAVE)\n        if (!append_flag(py_retlist, \"slave\"))\n            goto error;\n#endif\n#ifdef IFF_MULTICAST\n    // Available in (at least) Linux, macOS, BSD\n    if (flags & IFF_MULTICAST)\n        if (!append_flag(py_retlist, \"multicast\"))\n            goto error;\n#endif\n#ifdef IFF_PORTSEL\n    // Available in (at least) Linux\n    if (flags & IFF_PORTSEL)\n        if (!append_flag(py_retlist, \"portsel\"))\n            goto error;\n#endif\n#ifdef IFF_AUTOMEDIA\n    // Available in (at least) Linux\n    if (flags & IFF_AUTOMEDIA)\n        if (!append_flag(py_retlist, \"automedia\"))\n            goto error;\n#endif\n#ifdef IFF_DYNAMIC\n    // Available in (at least) Linux\n    if (flags & IFF_DYNAMIC)\n        if (!append_flag(py_retlist, \"dynamic\"))\n            goto error;\n#endif\n#ifdef IFF_OACTIVE\n    // Available in (at least) macOS, BSD\n    if (flags & IFF_OACTIVE)\n        if (!append_flag(py_retlist, \"oactive\"))\n            goto error;\n#endif\n#ifdef IFF_SIMPLEX\n    // Available in (at least) macOS, AIX, BSD\n    if (flags & IFF_SIMPLEX)\n        if (!append_flag(py_retlist, \"simplex\"))\n            goto error;\n#endif\n#ifdef IFF_LINK0\n    // Available in (at least) macOS, BSD\n    if (flags & IFF_LINK0)\n        if (!append_flag(py_retlist, \"link0\"))\n            goto error;\n#endif\n#ifdef IFF_LINK1\n    // Available in (at least) macOS, BSD\n    if (flags & IFF_LINK1)\n        if (!append_flag(py_retlist, \"link1\"))\n            goto error;\n#endif\n#ifdef IFF_LINK2\n    // Available in (at least) macOS, BSD\n    if (flags & IFF_LINK2)\n        if (!append_flag(py_retlist, \"link2\"))\n            goto error;\n#endif\n#ifdef IFF_D2\n    // Available in (at least) AIX\n    if (flags & IFF_D2)\n        if (!append_flag(py_retlist, \"d2\"))\n            goto error;\n#endif\n\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (sock != -1)\n        close(sock);\n    return NULL;\n}\n\n\n/*\n * Inspect NIC flags, returns a bool indicating whether the NIC is\n * running. References:\n * http://www.i-scream.org/libstatgrab/\n */\nPyObject *\npsutil_net_if_is_running(PyObject *self, PyObject *args) {\n    char *nic_name;\n    int sock = -1;\n    int ret;\n    struct ifreq ifr;\n\n    if (!PyArg_ParseTuple(args, \"s\", &nic_name))\n        return NULL;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1)\n        goto error;\n\n    str_copy(ifr.ifr_name, sizeof(ifr.ifr_name), nic_name);\n    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);\n    if (ret == -1)\n        goto error;\n\n    close(sock);\n    if ((ifr.ifr_flags & IFF_RUNNING) != 0)\n        return Py_BuildValue(\"O\", Py_True);\n    else\n        return Py_BuildValue(\"O\", Py_False);\n\nerror:\n    if (sock != -1)\n        close(sock);\n    return psutil_oserror();\n}\n\n\n// net_if_stats() macOS/BSD implementation.\n#ifdef PSUTIL_HAS_NET_IF_DUPLEX_SPEED\n\nint\npsutil_get_nic_speed(int ifm_active) {\n    // Determine NIC speed. Taken from:\n    // http://www.i-scream.org/libstatgrab/\n    // Assuming only ETHER devices\n    switch (IFM_TYPE(ifm_active)) {\n        case IFM_ETHER:\n            switch (IFM_SUBTYPE(ifm_active)) {\n#if defined(IFM_HPNA_1) \\\n    && ((!defined(IFM_10G_LR)) || (IFM_10G_LR != IFM_HPNA_1))\n                // HomePNA 1.0 (1Mb/s)\n                case (IFM_HPNA_1):\n                    return 1;\n#endif\n                // 10 Mbit\n                case (IFM_10_T):  // 10BaseT - RJ45\n                case (IFM_10_2):  // 10Base2 - Thinnet\n                case (IFM_10_5):  // 10Base5 - AUI\n                case (IFM_10_STP):  // 10BaseT over shielded TP\n                case (IFM_10_FL):  // 10baseFL - Fiber\n                    return 10;\n                // 100 Mbit\n                case (IFM_100_TX):  // 100BaseTX - RJ45\n                case (IFM_100_FX):  // 100BaseFX - Fiber\n                case (IFM_100_T4):  // 100BaseT4 - 4 pair cat 3\n                case (IFM_100_VG):  // 100VG-AnyLAN\n                case (IFM_100_T2):  // 100BaseT2\n                    return 100;\n                // 1000 Mbit\n                case (IFM_1000_SX):  // 1000BaseSX - multi-mode fiber\n                case (IFM_1000_LX):  // 1000baseLX - single-mode fiber\n                case (IFM_1000_CX):  // 1000baseCX - 150ohm STP\n#if defined(IFM_1000_TX) && !defined(PSUTIL_OPENBSD)\n#define HAS_CASE_IFM_1000_TX 1\n                // FreeBSD 4 and others (but NOT OpenBSD) -> #define IFM_1000_T\n                // in net/if_media.h\n                case (IFM_1000_TX):\n#endif\n#ifdef IFM_1000_FX\n                case (IFM_1000_FX):\n#endif\n#if defined(IFM_1000_T) && (!HAS_CASE_IFM_1000_TX || IFM_1000_T != IFM_1000_TX)\n                case (IFM_1000_T):\n#endif\n                    return 1000;\n#if defined(IFM_10G_SR) || defined(IFM_10G_LR) || defined(IFM_10G_CX4) \\\n    || defined(IFM_10G_T)\n#ifdef IFM_10G_SR\n                case (IFM_10G_SR):\n#endif\n#ifdef IFM_10G_LR\n                case (IFM_10G_LR):\n#endif\n#ifdef IFM_10G_CX4\n                case (IFM_10G_CX4):\n#endif\n#ifdef IFM_10G_TWINAX\n                case (IFM_10G_TWINAX):\n#endif\n#ifdef IFM_10G_TWINAX_LONG\n                case (IFM_10G_TWINAX_LONG):\n#endif\n#ifdef IFM_10G_T\n                case (IFM_10G_T):\n#endif\n                    return 10000;\n#endif\n#if defined(IFM_2500_SX)\n#ifdef IFM_2500_SX\n                case (IFM_2500_SX):\n#endif\n                    return 2500;\n#endif  // any 2.5GBit stuff...\n        // We don't know what it is\n                default:\n                    return 0;\n            }\n            break;\n\n#ifdef IFM_TOKEN\n        case IFM_TOKEN:\n            switch (IFM_SUBTYPE(ifm_active)) {\n                case IFM_TOK_STP4:  // Shielded twisted pair 4m - DB9\n                case IFM_TOK_UTP4:  // Unshielded twisted pair 4m - RJ45\n                    return 4;\n                case IFM_TOK_STP16:  // Shielded twisted pair 16m - DB9\n                case IFM_TOK_UTP16:  // Unshielded twisted pair 16m - RJ45\n                    return 16;\n#if defined(IFM_TOK_STP100) || defined(IFM_TOK_UTP100)\n#ifdef IFM_TOK_STP100\n                case IFM_TOK_STP100:  // Shielded twisted pair 100m - DB9\n#endif\n#ifdef IFM_TOK_UTP100\n                case IFM_TOK_UTP100:  // Unshielded twisted pair 100m - RJ45\n#endif\n                    return 100;\n#endif\n                // We don't know what it is\n                default:\n                    return 0;\n            }\n            break;\n#endif\n\n#ifdef IFM_FDDI\n        case IFM_FDDI:\n            switch (IFM_SUBTYPE(ifm_active)) {\n                // We don't know what it is\n                default:\n                    return 0;\n            }\n            break;\n#endif\n        case IFM_IEEE80211:\n            switch (IFM_SUBTYPE(ifm_active)) {\n                case IFM_IEEE80211_FH1:  // Frequency Hopping 1Mbps\n                case IFM_IEEE80211_DS1:  // Direct Sequence 1Mbps\n                    return 1;\n                case IFM_IEEE80211_FH2:  // Frequency Hopping 2Mbps\n                case IFM_IEEE80211_DS2:  // Direct Sequence 2Mbps\n                    return 2;\n                case IFM_IEEE80211_DS5:  // Direct Sequence 5Mbps\n                    return 5;\n                case IFM_IEEE80211_DS11:  // Direct Sequence 11Mbps\n                    return 11;\n                case IFM_IEEE80211_DS22:  // Direct Sequence 22Mbps\n                    return 22;\n                // We don't know what it is\n                default:\n                    return 0;\n            }\n            break;\n\n        default:\n            return 0;\n    }\n}\n\n\n/*\n * Return stats about a particular network interface.\n * References:\n * http://www.i-scream.org/libstatgrab/\n */\nPyObject *\npsutil_net_if_duplex_speed(PyObject *self, PyObject *args) {\n    char *nic_name;\n    int sock = -1;\n    int ret;\n    int duplex;\n    int speed;\n    struct ifreq ifr;\n    struct ifmediareq ifmed;\n\n    if (!PyArg_ParseTuple(args, \"s\", &nic_name))\n        return NULL;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1)\n        return psutil_oserror();\n    str_copy(ifr.ifr_name, sizeof(ifr.ifr_name), nic_name);\n\n    // speed / duplex\n    memset(&ifmed, 0, sizeof(struct ifmediareq));\n    str_copy(ifmed.ifm_name, sizeof(ifmed.ifm_name), nic_name);\n    ret = ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed);\n    if (ret == -1) {\n        speed = 0;\n        duplex = 0;\n    }\n    else {\n        speed = psutil_get_nic_speed(ifmed.ifm_active);\n        if ((ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active)\n            duplex = 2;\n        else if ((ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active)\n            duplex = 1;\n        else\n            duplex = 0;\n    }\n\n    close(sock);\n    return Py_BuildValue(\"[ii]\", duplex, speed);\n}\n#endif  // PSUTIL_HAS_NET_IF_DUPLEX_SPEED\n"
  },
  {
    "path": "psutil/arch/posix/pids.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <sys/types.h>\n#include <signal.h>\n#include <errno.h>\n\n#include \"../../arch/all/init.h\"\n\n\nstatic int\nhas_pid_zero(void) {\n#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)\n    return 0;\n#else\n    return 1;\n#endif\n}\n\n\n// Check if PID exists. Return values:\n// 1: exists\n// 0: does not exist\n// -1: error (Python exception is set)\nint\npsutil_pid_exists(pid_t pid) {\n    int ret;\n\n    // No negative PID exists, plus -1 is an alias for sending signal\n    // too all processes except system ones. Not what we want.\n    if (pid < 0)\n        return 0;\n\n    // As per \"man 2 kill\" PID 0 is an alias for sending the signal to\n    // every process in the process group of the calling process. Not\n    // what we want. Some platforms have PID 0, some do not. We decide\n    // that at runtime.\n    if (pid == 0)\n        return has_pid_zero();\n\n    ret = kill(pid, 0);\n    if (ret == 0)\n        return 1;\n\n    // ESRCH == No such process\n    if (errno == ESRCH)\n        return 0;\n\n    // EPERM clearly indicates there's a process to deny access to.\n    if (errno == EPERM)\n        return 1;\n\n    // According to \"man 2 kill\" possible error values are (EINVAL,\n    // EPERM, ESRCH) therefore we should never get here.\n    return -1;\n}\n"
  },
  {
    "path": "psutil/arch/posix/proc.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <signal.h>\n#include <errno.h>\n#include <sys/resource.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#if defined(PSUTIL_OSX) || defined(PSUTIL_BSD)\n// Return True if PID is a zombie else False, including if PID does not\n// exist or the underlying function fails (never raise exception).\nPyObject *\npsutil_proc_is_zombie(PyObject *self, PyObject *args) {\n    pid_t pid;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (is_zombie(pid) == 1)\n        Py_RETURN_TRUE;\n    else\n        Py_RETURN_FALSE;\n}\n#endif\n\n\n// Utility used for those syscalls which do not return a meaningful\n// error that we can directly translate into an exception which makes\n// sense. As such we'll have to guess, e.g. if errno is set or if PID\n// does not exist. If reason can't be determined raise RuntimeError.\nPyObject *\npsutil_raise_for_pid(pid_t pid, char *syscall) {\n    if (errno != 0)\n        psutil_oserror_wsyscall(syscall);\n    else if (psutil_pid_exists(pid) == 0)\n        psutil_oserror_nsp(syscall);\n#if defined(PSUTIL_OSX) || defined(PSUTIL_BSD)\n    else if (is_zombie(pid))\n        PyErr_SetString(ZombieProcessError, \"\");\n#endif\n    else\n        psutil_runtime_error(\"%s syscall failed\", syscall);\n    return NULL;\n}\n\n\n// Get PID priority.\nPyObject *\npsutil_proc_priority_get(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int priority;\n    errno = 0;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n#ifdef PSUTIL_OSX\n    priority = getpriority(PRIO_PROCESS, (id_t)pid);\n#else\n    priority = getpriority(PRIO_PROCESS, pid);\n#endif\n    if (errno != 0)\n        return psutil_oserror();\n    return Py_BuildValue(\"i\", priority);\n}\n\n\n// Set PID priority.\nPyObject *\npsutil_proc_priority_set(PyObject *self, PyObject *args) {\n    pid_t pid;\n    int priority;\n    int retval;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"i\", &pid, &priority))\n        return NULL;\n\n#ifdef PSUTIL_OSX\n    retval = setpriority(PRIO_PROCESS, (id_t)pid, priority);\n#else\n    retval = setpriority(PRIO_PROCESS, pid, priority);\n#endif\n    if (retval == -1)\n        return psutil_oserror();\n    Py_RETURN_NONE;\n}\n"
  },
  {
    "path": "psutil/arch/posix/sysctl.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include \"../../arch/all/init.h\"\n\n#ifdef PSUTIL_HAS_SYSCTL\n#include <Python.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n\n\nstatic const int MAX_RETRIES = 10;\n\n\n// A thin wrapper on top of sysctl().\nint\npsutil_sysctl(int *mib, u_int miblen, void *buf, size_t buflen) {\n    size_t len = buflen;\n\n    if (!mib || miblen == 0 || !buf || buflen == 0)\n        return psutil_badargs(\"psutil_sysctl\");\n\n    if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) {\n        psutil_oserror_wsyscall(\"sysctl()\");\n        return -1;\n    }\n\n    if (len != buflen) {\n        psutil_runtime_error(\"sysctl() size mismatch\");\n        return -1;\n    }\n\n    return 0;\n}\n\n\n// Allocate buffer for sysctl with retry on ENOMEM or buffer size mismatch.\n// The caller is responsible for freeing the memory.\nint\npsutil_sysctl_malloc(int *mib, u_int miblen, char **buf, size_t *buflen) {\n    size_t needed = 0;\n    char *buffer = NULL;\n    int ret;\n    int max_retries = MAX_RETRIES;\n\n    if (!mib || miblen == 0 || !buf || !buflen)\n        return psutil_badargs(\"psutil_sysctl_malloc\");\n\n    // First query to determine required size\n    ret = sysctl(mib, miblen, NULL, &needed, NULL, 0);\n    if (ret == -1) {\n        psutil_oserror_wsyscall(\"sysctl() malloc 1/3\");\n        return -1;\n    }\n\n    if (needed == 0) {\n        psutil_debug(\"psutil_sysctl_malloc() size = 0\");\n    }\n\n    while (max_retries-- > 0) {\n        // zero-initialize buffer to prevent uninitialized bytes\n        buffer = calloc(1, needed);\n        if (buffer == NULL) {\n            PyErr_NoMemory();\n            return -1;\n        }\n\n        size_t len = needed;\n        ret = sysctl(mib, miblen, buffer, &len, NULL, 0);\n\n        if (ret == 0) {\n            // Success: return buffer and length\n            *buf = buffer;\n            *buflen = len;\n            return 0;\n        }\n\n        // Handle buffer too small\n        if (errno == ENOMEM) {\n            free(buffer);\n            buffer = NULL;\n\n            // Re-query needed size for next attempt\n            if (sysctl(mib, miblen, NULL, &needed, NULL, 0) == -1) {\n                psutil_oserror_wsyscall(\"sysctl() malloc 2/3\");\n                return -1;\n            }\n\n            psutil_debug(\"psutil_sysctl_malloc() retry\");\n            continue;\n        }\n\n        // Other errors: clean up and give up\n        free(buffer);\n        psutil_oserror_wsyscall(\"sysctl() malloc 3/3\");\n        return -1;\n    }\n\n    psutil_runtime_error(\"sysctl() buffer allocation retry limit exceeded\");\n    return -1;\n}\n\n\n// Get the maximum process arguments size. Return 0 on error.\nsize_t\npsutil_sysctl_argmax() {\n    int argmax;\n    int mib[2] = {CTL_KERN, KERN_ARGMAX};\n\n    if (psutil_sysctl(mib, 2, &argmax, sizeof(argmax)) != 0) {\n        return 0;\n    }\n\n    if (argmax <= 0) {\n        psutil_runtime_error(\"sysctl(KERN_ARGMAX) return <= 0\");\n        return 0;\n    }\n\n    return (size_t)argmax;\n}\n\n\n#ifdef PSUTIL_HAS_SYSCTLBYNAME\n// A thin wrapper on top of sysctlbyname().\nint\npsutil_sysctlbyname(const char *name, void *buf, size_t buflen) {\n    size_t len = buflen;\n    char errbuf[256];\n\n    if (!name || !buf || buflen == 0)\n        return psutil_badargs(\"psutil_sysctlbyname\");\n\n    if (sysctlbyname(name, buf, &len, NULL, 0) == -1) {\n        str_format(errbuf, sizeof(errbuf), \"sysctlbyname('%s')\", name);\n        psutil_oserror_wsyscall(errbuf);\n        return -1;\n    }\n\n    if (len != buflen) {\n        str_format(\n            errbuf,\n            sizeof(errbuf),\n            \"sysctlbyname('%s') size mismatch: returned %zu, expected %zu\",\n            name,\n            len,\n            buflen\n        );\n        psutil_runtime_error(errbuf);\n        return -1;\n    }\n\n    return 0;\n}\n\n\n// Allocate buffer for sysctlbyname with retry on ENOMEM or size mismatch.\n// The caller is responsible for freeing the memory.\nint\npsutil_sysctlbyname_malloc(const char *name, char **buf, size_t *buflen) {\n    int ret;\n    int max_retries = MAX_RETRIES;\n    size_t needed = 0;\n    size_t len = 0;\n    char *buffer = NULL;\n    char errbuf[256];\n\n    if (!name || !buf || !buflen)\n        return psutil_badargs(\"psutil_sysctlbyname_malloc\");\n\n    // First query to determine required size.\n    ret = sysctlbyname(name, NULL, &needed, NULL, 0);\n    if (ret == -1) {\n        str_format(\n            errbuf, sizeof(errbuf), \"sysctlbyname('%s') malloc 1/3\", name\n        );\n        psutil_oserror_wsyscall(errbuf);\n        return -1;\n    }\n\n    if (needed == 0) {\n        psutil_debug(\"psutil_sysctlbyname_malloc() size = 0\");\n    }\n\n    while (max_retries-- > 0) {\n        // Zero-initialize buffer to prevent uninitialized bytes.\n        buffer = calloc(1, needed);\n        if (buffer == NULL) {\n            PyErr_NoMemory();\n            return -1;\n        }\n\n        len = needed;\n        ret = sysctlbyname(name, buffer, &len, NULL, 0);\n        if (ret == 0) {\n            // Success: return buffer and actual length.\n            *buf = buffer;\n            *buflen = len;\n            return 0;\n        }\n\n        // Handle buffer too small. Re-query and retry.\n        if (errno == ENOMEM) {\n            free(buffer);\n            buffer = NULL;\n\n            if (sysctlbyname(name, NULL, &needed, NULL, 0) == -1) {\n                str_format(\n                    errbuf,\n                    sizeof(errbuf),\n                    \"sysctlbyname('%s') malloc 2/3\",\n                    name\n                );\n                psutil_oserror_wsyscall(errbuf);\n                return -1;\n            }\n\n            psutil_debug(\"psutil_sysctlbyname_malloc() retry\");\n            continue;\n        }\n\n        // Other errors: clean up and give up.\n        free(buffer);\n        str_format(\n            errbuf, sizeof(errbuf), \"sysctlbyname('%s') malloc 3/3\", name\n        );\n        psutil_oserror_wsyscall(errbuf);\n        return -1;\n    }\n\n    str_format(\n        errbuf,\n        sizeof(errbuf),\n        \"sysctlbyname('%s') buffer allocation retry limit exceeded\",\n        name\n    );\n    psutil_runtime_error(errbuf);\n    return -1;\n}\n#endif  // PSUTIL_HAS_SYSCTLBYNAME\n#endif  // PSUTIL_HAS_SYSCTL\n"
  },
  {
    "path": "psutil/arch/posix/users.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include \"../../arch/all/init.h\"\n\n#ifdef PSUTIL_HAS_POSIX_USERS\n#include <Python.h>\n#include <string.h>\n\n#include <utmpx.h>\n\n\nstatic void\nsetup() {\n    UTXENT_MUTEX_LOCK();\n    setutxent();\n}\n\n\nstatic void\nteardown() {\n    endutxent();\n    UTXENT_MUTEX_UNLOCK();\n}\n\n\nPyObject *\npsutil_users(PyObject *self, PyObject *args) {\n    struct utmpx *ut;\n    size_t host_len;\n    PyObject *py_username = NULL;\n    PyObject *py_tty = NULL;\n    PyObject *py_hostname = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    setup();\n\n    while ((ut = getutxent()) != NULL) {\n        if (ut->ut_type != USER_PROCESS)\n            continue;\n\n        py_username = PyUnicode_DecodeFSDefault(ut->ut_user);\n        if (!py_username)\n            goto error;\n\n        py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);\n        if (!py_tty)\n            goto error;\n\n        host_len = strnlen(ut->ut_host, sizeof(ut->ut_host));\n        if (host_len == 0 || (strcmp(ut->ut_host, \":0\") == 0)\n            || (strcmp(ut->ut_host, \":0.0\") == 0))\n        {\n            py_hostname = PyUnicode_DecodeFSDefault(\"localhost\");\n        }\n        else {\n            // ut_host might not be null-terminated if the hostname is\n            // very long, so we do it.\n            char hostbuf[sizeof(ut->ut_host)];\n            memcpy(hostbuf, ut->ut_host, host_len);\n            hostbuf[host_len] = '\\0';\n            py_hostname = PyUnicode_DecodeFSDefault(hostbuf);\n        }\n        if (!py_hostname)\n            goto error;\n\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"OOOd\" _Py_PARSE_PID,\n                py_username,  // username\n                py_tty,  // tty\n                py_hostname,  // hostname\n                (double)ut->ut_tv.tv_sec,  // tstamp\n                ut->ut_pid  // process id\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_username);\n        Py_CLEAR(py_tty);\n        Py_CLEAR(py_hostname);\n    }\n\n    teardown();\n    return py_retlist;\n\nerror:\n    teardown();\n    Py_XDECREF(py_username);\n    Py_XDECREF(py_tty);\n    Py_XDECREF(py_hostname);\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n#endif  // PSUTIL_HAS_POSIX_USERS\n"
  },
  {
    "path": "psutil/arch/sunos/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\n#include <sys/sysinfo.h>\n#include <kstat.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// System-wide CPU times.\nPyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    kstat_ctl_t *kc;\n    kstat_t *ksp;\n    cpu_stat_t cs;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    kc = kstat_open();\n    if (kc == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {\n        if (strcmp(ksp->ks_module, \"cpu_stat\") == 0) {\n            if (kstat_read(kc, ksp, &cs) == -1) {\n                psutil_oserror();\n                goto error;\n            }\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"ffff\",\n                    (float)cs.cpu_sysinfo.cpu[CPU_USER],\n                    (float)cs.cpu_sysinfo.cpu[CPU_KERNEL],\n                    (float)cs.cpu_sysinfo.cpu[CPU_IDLE],\n                    (float)cs.cpu_sysinfo.cpu[CPU_WAIT]\n                ))\n            {\n                goto error;\n            }\n        }\n    }\n\n    kstat_close(kc);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (kc != NULL)\n        kstat_close(kc);\n    return NULL;\n}\n\n\n// Return the number of CPU cores on the system.\nPyObject *\npsutil_cpu_count_cores(PyObject *self, PyObject *args) {\n    kstat_ctl_t *kc;\n    kstat_t *ksp;\n    int ncpus = 0;\n\n    kc = kstat_open();\n    if (kc == NULL)\n        goto error;\n    ksp = kstat_lookup(kc, \"cpu_info\", -1, NULL);\n    if (ksp == NULL)\n        goto error;\n\n    for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {\n        if (strcmp(ksp->ks_module, \"cpu_info\") != 0)\n            continue;\n        if (kstat_read(kc, ksp, NULL) == -1)\n            goto error;\n        ncpus += 1;\n    }\n\n    kstat_close(kc);\n    if (ncpus > 0)\n        return Py_BuildValue(\"i\", ncpus);\n    else\n        goto error;\n\nerror:\n    // mimic os.cpu_count()\n    if (kc != NULL)\n        kstat_close(kc);\n    Py_RETURN_NONE;\n}\n\n\n// Return CPU statistics.\nPyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    kstat_ctl_t *kc;\n    kstat_t *ksp;\n    cpu_stat_t cs;\n    unsigned int ctx_switches = 0;\n    unsigned int interrupts = 0;\n    unsigned int traps = 0;\n    unsigned int syscalls = 0;\n\n    kc = kstat_open();\n    if (kc == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {\n        if (strcmp(ksp->ks_module, \"cpu_stat\") == 0) {\n            if (kstat_read(kc, ksp, &cs) == -1) {\n                psutil_oserror();\n                goto error;\n            }\n            // voluntary + involuntary\n            ctx_switches += cs.cpu_sysinfo.pswitch + cs.cpu_sysinfo.inv_swtch;\n            interrupts += cs.cpu_sysinfo.intr;\n            traps += cs.cpu_sysinfo.trap;\n            syscalls += cs.cpu_sysinfo.syscall;\n        }\n    }\n\n    kstat_close(kc);\n    return Py_BuildValue(\"IIII\", ctx_switches, interrupts, syscalls, traps);\n\nerror:\n    if (kc != NULL)\n        kstat_close(kc);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/sunos/disk.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\n#include <sys/mntent.h>\n#include <sys/mnttab.h>\n#include <kstat.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    kstat_ctl_t *kc;\n    kstat_t *ksp;\n    kstat_io_t kio;\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_disk_info = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n    kc = kstat_open();\n    if (kc == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n    ksp = kc->kc_chain;\n    while (ksp != NULL) {\n        if (ksp->ks_type == KSTAT_TYPE_IO) {\n            if (strcmp(ksp->ks_class, \"disk\") == 0) {\n                if (kstat_read(kc, ksp, &kio) == -1) {\n                    kstat_close(kc);\n                    return psutil_oserror();\n                    ;\n                }\n                py_disk_info = Py_BuildValue(\n                    \"(IIKKLL)\",\n                    kio.reads,\n                    kio.writes,\n                    kio.nread,\n                    kio.nwritten,\n                    kio.rtime / 1000 / 1000,  // from nano to milli secs\n                    kio.wtime / 1000 / 1000  // from nano to milli secs\n                );\n                if (!py_disk_info)\n                    goto error;\n                if (PyDict_SetItemString(\n                        py_retdict, ksp->ks_name, py_disk_info\n                    ))\n                    goto error;\n                Py_CLEAR(py_disk_info);\n            }\n        }\n        ksp = ksp->ks_next;\n    }\n    kstat_close(kc);\n\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_disk_info);\n    Py_DECREF(py_retdict);\n    if (kc != NULL)\n        kstat_close(kc);\n    return NULL;\n}\n\n\nPyObject *\npsutil_disk_partitions(PyObject *self, PyObject *args) {\n    FILE *file;\n    struct mnttab mt;\n    PyObject *py_dev = NULL;\n    PyObject *py_mountp = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    file = fopen(MNTTAB, \"rb\");\n    if (file == NULL) {\n        psutil_oserror();\n        goto error;\n    }\n\n    while (getmntent(file, &mt) == 0) {\n        py_dev = PyUnicode_DecodeFSDefault(mt.mnt_special);\n        if (!py_dev)\n            goto error;\n        py_mountp = PyUnicode_DecodeFSDefault(mt.mnt_mountp);\n        if (!py_mountp)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(OOss)\",\n                py_dev,  // device\n                py_mountp,  // mount point\n                mt.mnt_fstype,  // fs type\n                mt.mnt_mntopts  // options\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_dev);\n        Py_CLEAR(py_mountp);\n    }\n    fclose(file);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_dev);\n    Py_XDECREF(py_mountp);\n    Py_DECREF(py_retlist);\n    if (file != NULL)\n        fclose(file);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/sunos/environ.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola', Oleksii Shevchuk.\n * All rights reserved. Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n *\n * Functions specific for Process.environ().\n */\n\n#define _STRUCTURED_PROC 1\n\n#include <Python.h>\n\n#include <sys/types.h>\n#include <sys/procfs.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#define STRING_SEARCH_BUF_SIZE 512\n\n\n/*\n * Open address space of specified process and return file descriptor.\n *  @param pid a pid of process.\n *  @param procfs_path a path to mounted procfs filesystem.\n *  @return file descriptor or -1 in case of error.\n */\nstatic int\nopen_address_space(pid_t pid, const char *procfs_path) {\n    int fd;\n    char proc_path[PATH_MAX];\n\n    str_format(proc_path, PATH_MAX, \"%s/%i/as\", procfs_path, pid);\n    fd = open(proc_path, O_RDONLY);\n    if (fd < 0)\n        psutil_oserror();\n\n    return fd;\n}\n\n\n/*\n * Read chunk of data by offset to specified buffer of the same size.\n * @param fd a file descriptor.\n * @param offset an required offset in file.\n * @param buf a buffer where to store result.\n * @param buf_size a size of buffer where data will be stored.\n * @return amount of bytes stored to the buffer or -1 in case of\n *         error.\n */\nstatic size_t\nread_offt(int fd, off_t offset, char *buf, size_t buf_size) {\n    size_t to_read = buf_size;\n    size_t stored = 0;\n    int r;\n\n    while (to_read) {\n        r = pread(fd, buf + stored, to_read, offset + stored);\n        if (r < 0)\n            goto error;\n        else if (r == 0)\n            break;\n\n        to_read -= r;\n        stored += r;\n    }\n\n    return stored;\n\nerror:\n    psutil_oserror();\n    return -1;\n}\n\n\n/*\n * Read null-terminated string from file descriptor starting from\n * specified offset.\n * @param fd a file descriptor of opened address space.\n * @param offset an offset in specified file descriptor.\n * @return allocated null-terminated string or NULL in case of error.\n */\nstatic char *\nread_cstring_offt(int fd, off_t offset) {\n    int r;\n    int i = 0;\n    off_t end = offset;\n    size_t len;\n    char buf[STRING_SEARCH_BUF_SIZE];\n    char *result = NULL;\n\n    if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    // Search end of string\n    for (;;) {\n        r = read(fd, buf, sizeof(buf));\n        if (r == -1) {\n            psutil_oserror();\n            goto error;\n        }\n        else if (r == 0) {\n            break;\n        }\n        else {\n            for (i = 0; i < r; i++)\n                if (!buf[i])\n                    goto found;\n        }\n\n        end += r;\n    }\n\nfound:\n    len = end + i - offset;\n\n    result = malloc(len + 1);\n    if (!result) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    if (len) {\n        if (read_offt(fd, offset, result, len) < 0) {\n            goto error;\n        }\n    }\n\n    result[len] = '\\0';\n    return result;\n\nerror:\n    if (result)\n        free(result);\n    return NULL;\n}\n\n\n/*\n * Read block of addresses by offset, dereference them one by one\n * and create an array of null terminated C strings from them.\n * @param fd a file descriptor of address space of interesting process.\n * @param offset an offset of address block in address space.\n * @param ptr_size a size of pointer. Only 4 or 8 are valid values.\n * @param count amount of pointers in block.\n * @return allocated array of strings dereferenced and read by offset.\n * Number of elements in array are count. In case of error function\n * returns NULL.\n */\nstatic char **\nread_cstrings_block(int fd, off_t offset, size_t ptr_size, size_t count) {\n    char **result = NULL;\n    char *pblock = NULL;\n    size_t pblock_size;\n    size_t i;\n\n    assert(ptr_size == 4 || ptr_size == 8);\n\n    if (!count)\n        goto error;\n\n    pblock_size = ptr_size * count;\n\n    pblock = malloc(pblock_size);\n    if (!pblock) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    if (read_offt(fd, offset, pblock, pblock_size) != pblock_size)\n        goto error;\n\n    result = (char **)calloc(count, sizeof(char *));\n    if (!result) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    for (i = 0; i < count; i++) {\n        result[i] = read_cstring_offt(\n            fd,\n            (ptr_size == 4 ? ((uint32_t *)pblock)[i] : ((uint64_t *)pblock)[i])\n        );\n\n        if (!result[i])\n            goto error;\n    }\n\n    free(pblock);\n    return result;\n\nerror:\n    if (result)\n        psutil_free_cstrings_array(result, i);\n    if (pblock)\n        free(pblock);\n    return NULL;\n}\n\n\n/*\n * Check that caller process can extract proper values from psinfo_t\n * structure.\n * @param info a pointer to process info (psinfo_t) structure of the\n *             interesting process.\n *  @return 1 in case if caller process can extract proper values from\n *          psinfo_t structure, or 0 otherwise.\n */\nstatic inline int\nis_ptr_dereference_possible(psinfo_t info) {\n#if !defined(_LP64)\n    return info.pr_dmodel == PR_MODEL_ILP32;\n#else\n    return 1;\n#endif\n}\n\n\n/*\n * Return pointer size according to psinfo_t structure\n * @param info a pointer to process info (psinfo_t) structure of the\n *             interesting process.\n * @return pointer size (4 or 8).\n */\nstatic inline int\nptr_size_by_psinfo(psinfo_t info) {\n    return info.pr_dmodel == PR_MODEL_ILP32 ? 4 : 8;\n}\n\n\n/*\n * Count amount of pointers in a block which ends with NULL.\n * @param fd a descriptor of /proc/PID/as special file.\n * @param offt an offset of block of pointers at the file.\n * @param ptr_size a pointer size (allowed values: {4, 8}).\n * @return amount of non-NULL pointers or -1 in case of error.\n */\nstatic int\nsearch_pointers_vector_size_offt(int fd, off_t offt, size_t ptr_size) {\n    int count = 0;\n    size_t r;\n    char buf[8];\n    static const char zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};\n\n    assert(ptr_size == 4 || ptr_size == 8);\n\n    if (lseek(fd, offt, SEEK_SET) == (off_t)-1)\n        goto error;\n\n    for (;; count++) {\n        r = read(fd, buf, ptr_size);\n\n        if (r < 0)\n            goto error;\n\n        if (r == 0)\n            break;\n\n        if (r != ptr_size) {\n            psutil_runtime_error(\"pointer block is truncated\");\n            return -1;\n        }\n\n        if (!memcmp(buf, zeros, ptr_size))\n            break;\n    }\n\n    return count;\n\nerror:\n    psutil_oserror();\n    return -1;\n}\n\n\n/*\n * Dereference and read array of strings by psinfo_t.pr_argv pointer from\n * remote process.\n * @param info a pointer to process info (psinfo_t) structure of the\n *             interesting process\n * @param procfs_path a cstring with path to mounted procfs filesystem.\n * @param count a pointer to variable where to store amount of elements in\n *        returned array. In case of error value of variable will not be\n          changed.\n * @return allocated array of cstrings or NULL in case of error.\n */\nchar **\npsutil_read_raw_args(psinfo_t info, const char *procfs_path, size_t *count) {\n    int as;\n    char **result;\n\n    if (!is_ptr_dereference_possible(info)) {\n        PyErr_SetString(\n            PyExc_NotImplementedError,\n            \"can't get env of a 64 bit process from a 32 bit process\"\n        );\n        return NULL;\n    }\n\n    if (!(info.pr_argv && info.pr_argc)) {\n        psutil_runtime_error(\"process doesn't have arguments block\");\n\n        return NULL;\n    }\n\n    as = open_address_space(info.pr_pid, procfs_path);\n    if (as < 0)\n        return NULL;\n\n    result = read_cstrings_block(\n        as, info.pr_argv, ptr_size_by_psinfo(info), info.pr_argc\n    );\n\n    if (result && count)\n        *count = info.pr_argc;\n\n    close(as);\n\n    return result;\n}\n\n\n/*\n * Dereference and read array of strings by psinfo_t.pr_envp pointer\n * from remote process.\n * @param info a pointer to process info (psinfo_t) structure of the\n *             interesting process.\n * @param procfs_path a cstring with path to mounted procfs filesystem.\n * @param count a pointer to variable where to store amount of elements in\n *        returned array. In case of error value of variable will not be\n *        changed. To detect special case (described later) variable should be\n *        initialized by -1 or other negative value.\n * @return allocated array of cstrings or NULL in case of error.\n *         Special case: count set to 0, return NULL.\n *         Special case means there is no error acquired, but no data\n *         retrieved.\n *         Special case exists because the nature of the process. From the\n *         beginning it's not clean how many pointers in envp array. Also\n *         situation when environment is empty is common for kernel processes.\n */\nchar **\npsutil_read_raw_env(psinfo_t info, const char *procfs_path, ssize_t *count) {\n    int as;\n    int env_count;\n    int ptr_size;\n    char **result = NULL;\n\n    if (!is_ptr_dereference_possible(info)) {\n        PyErr_SetString(\n            PyExc_NotImplementedError,\n            \"can't get env of a 64 bit process from a 32 bit process\"\n        );\n        return NULL;\n    }\n\n    as = open_address_space(info.pr_pid, procfs_path);\n    if (as < 0)\n        return NULL;\n\n    ptr_size = ptr_size_by_psinfo(info);\n\n    env_count = search_pointers_vector_size_offt(as, info.pr_envp, ptr_size);\n\n    if (env_count >= 0 && count)\n        *count = env_count;\n\n    if (env_count > 0)\n        result = read_cstrings_block(as, info.pr_envp, ptr_size, env_count);\n\n    close(as);\n    return result;\n}\n\n\n/*\n * Free array of cstrings.\n * @param array an array of cstrings returned by psutil_read_raw_env,\n *              psutil_read_raw_args or any other function.\n * @param count a count of strings in the passed array\n */\nvoid\npsutil_free_cstrings_array(char **array, size_t count) {\n    size_t i;\n\n    if (!array)\n        return;\n    for (i = 0; i < count; i++) {\n        if (array[i]) {\n            free(array[i]);\n        }\n    }\n    free(array);\n}\n"
  },
  {
    "path": "psutil/arch/sunos/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <libproc.h>\n\n#ifndef PROCESS_AS_UTILS_H\n#define PROCESS_AS_UTILS_H\n\nchar **psutil_read_raw_args(\n    psinfo_t info, const char *procfs_path, size_t *count\n);\n\nchar **psutil_read_raw_env(\n    psinfo_t info, const char *procfs_path, ssize_t *count\n);\n\nvoid psutil_free_cstrings_array(char **array, size_t count);\n#endif  // PROCESS_AS_UTILS_H\n\nPyObject *psutil_boot_time(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_stats(PyObject *self, PyObject *args);\nPyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_disk_partitions(PyObject *self, PyObject *args);\nPyObject *psutil_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_stats(PyObject *self, PyObject *args);\nPyObject *psutil_net_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cpu_num(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cred(PyObject *self, PyObject *args);\nPyObject *psutil_proc_environ(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_maps(PyObject *self, PyObject *args);\nPyObject *psutil_proc_name_and_args(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_ctx_switches(PyObject *self, PyObject *args);\nPyObject *psutil_proc_oneshot(PyObject *self, PyObject *args);\nPyObject *psutil_proc_page_faults(PyObject *self, PyObject *args);\nPyObject *psutil_proc_query_thread(PyObject *self, PyObject *args);\nPyObject *psutil_swap_mem(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/sunos/mem.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\n#include <kstat.h>\n#include <sys/sysinfo.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_swap_mem(PyObject *self, PyObject *args) {\n    // XXX (arghhh!)\n    // total/free swap mem: commented out as for some reason I can't\n    // manage to get the same results shown by \"swap -l\", despite the\n    // code below is exactly the same as:\n    // http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/\n    //    cmd/swap/swap.c\n    // We're going to parse \"swap -l\" output from Python (sigh!)\n\n    /*\n        struct swaptable     *st;\n        struct swapent    *swapent;\n        int    i;\n        struct stat64 statbuf;\n        char *path;\n        char fullpath[MAXPATHLEN+1];\n        int    num;\n\n        if ((num = swapctl(SC_GETNSWP, NULL)) == -1) {\n            psutil_oserror();\n            return NULL;\n        }\n        if (num == 0) {\n            psutil_runtime_error(\"no swap devices configured\");\n            return NULL;\n        }\n        if ((st = malloc(num * sizeof(swapent_t) + sizeof (int))) == NULL) {\n            psutil_runtime_error(\"malloc failed\");\n            return NULL;\n        }\n        if ((path = malloc(num * MAXPATHLEN)) == NULL) {\n            psutil_runtime_error(\"malloc failed\");\n            return NULL;\n        }\n        swapent = st->swt_ent;\n        for (i = 0; i < num; i++, swapent++) {\n            swapent->ste_path = path;\n            path += MAXPATHLEN;\n        }\n        st->swt_n = num;\n        if ((num = swapctl(SC_LIST, st)) == -1) {\n            psutil_oserror();\n            return NULL;\n        }\n\n        swapent = st->swt_ent;\n        long t = 0, f = 0;\n        for (i = 0; i < num; i++, swapent++) {\n            int diskblks_per_page =(int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT);\n            t += (long)swapent->ste_pages;\n            f += (long)swapent->ste_free;\n        }\n\n        free(st);\n        return Py_BuildValue(\"(kk)\", t, f);\n    */\n\n    kstat_ctl_t *kc;\n    kstat_t *k;\n    cpu_stat_t *cpu;\n    int cpu_count = 0;\n    int flag = 0;\n    uint_t sin = 0;\n    uint_t sout = 0;\n\n    kc = kstat_open();\n    if (kc == NULL)\n        return psutil_oserror();\n    ;\n\n    k = kc->kc_chain;\n    while (k != NULL) {\n        if ((strncmp(k->ks_name, \"cpu_stat\", 8) == 0)\n            && (kstat_read(kc, k, NULL) != -1))\n        {\n            flag = 1;\n            cpu = (cpu_stat_t *)k->ks_data;\n            sin += cpu->cpu_vminfo.pgswapin;  // num pages swapped in\n            sout += cpu->cpu_vminfo.pgswapout;  // num pages swapped out\n        }\n        cpu_count += 1;\n        k = k->ks_next;\n    }\n    kstat_close(kc);\n    if (!flag) {\n        psutil_runtime_error(\"no swap device was found\");\n        return NULL;\n    }\n    return Py_BuildValue(\"(II)\", sin, sout);\n}\n"
  },
  {
    "path": "psutil/arch/sunos/net.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <inet/mib2.h>\n#include <kstat.h>\n#include <netinet/in.h>\n#include <stdio.h>\n#include <stropts.h>\n#include <sys/fcntl.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/sockio.h>\n#include <netinet/in.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stropts.h>\n#include <sys/tihdr.h>\n#include <net/if.h>\n#include <fcntl.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_net_io_counters(PyObject *self, PyObject *args) {\n    kstat_ctl_t *kc = NULL;\n    kstat_t *ksp;\n    kstat_named_t *rbytes, *wbytes, *rpkts, *wpkts, *ierrs, *oerrs;\n    int ret;\n    int sock = -1;\n    struct lifreq ifr;\n\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_ifc_info = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n    kc = kstat_open();\n    if (kc == NULL)\n        goto error;\n\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    ksp = kc->kc_chain;\n    while (ksp != NULL) {\n        if (ksp->ks_type != KSTAT_TYPE_NAMED)\n            goto next;\n        if (strcmp(ksp->ks_class, \"net\") != 0)\n            goto next;\n        // skip 'lo' (localhost) because it doesn't have the statistics we need\n        // and it makes kstat_data_lookup() fail\n        if (strcmp(ksp->ks_module, \"lo\") == 0)\n            goto next;\n\n        // check if this is a network interface by sending a ioctl\n        str_copy(ifr.lifr_name, sizeof(ifr.lifr_name), ksp->ks_name);\n        ret = ioctl(sock, SIOCGLIFFLAGS, &ifr);\n        if (ret == -1)\n            goto next;\n\n        if (kstat_read(kc, ksp, NULL) == -1) {\n            errno = 0;\n            goto next;\n        }\n\n        rbytes = (kstat_named_t *)kstat_data_lookup(ksp, \"rbytes\");\n        wbytes = (kstat_named_t *)kstat_data_lookup(ksp, \"obytes\");\n        rpkts = (kstat_named_t *)kstat_data_lookup(ksp, \"ipackets\");\n        wpkts = (kstat_named_t *)kstat_data_lookup(ksp, \"opackets\");\n        ierrs = (kstat_named_t *)kstat_data_lookup(ksp, \"ierrors\");\n        oerrs = (kstat_named_t *)kstat_data_lookup(ksp, \"oerrors\");\n\n        if ((rbytes == NULL) || (wbytes == NULL) || (rpkts == NULL)\n            || (wpkts == NULL) || (ierrs == NULL) || (oerrs == NULL))\n        {\n            psutil_runtime_error(\"kstat_data_lookup() failed\");\n            goto error;\n        }\n\n        if (rbytes->data_type == KSTAT_DATA_UINT64) {\n            py_ifc_info = Py_BuildValue(\n                \"(KKKKIIii)\",\n                wbytes->value.ui64,\n                rbytes->value.ui64,\n                wpkts->value.ui64,\n                rpkts->value.ui64,\n                ierrs->value.ui32,\n                oerrs->value.ui32,\n                0,  // dropin not supported\n                0  // dropout not supported\n            );\n        }\n        else {\n            py_ifc_info = Py_BuildValue(\n                \"(IIIIIIii)\",\n                wbytes->value.ui32,\n                rbytes->value.ui32,\n                wpkts->value.ui32,\n                rpkts->value.ui32,\n                ierrs->value.ui32,\n                oerrs->value.ui32,\n                0,  // dropin not supported\n                0  // dropout not supported\n            );\n        }\n        if (!py_ifc_info)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))\n            goto error;\n        Py_CLEAR(py_ifc_info);\n        goto next;\n\n    next:\n        ksp = ksp->ks_next;\n    }\n\n    kstat_close(kc);\n    close(sock);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_ifc_info);\n    Py_DECREF(py_retdict);\n    if (kc != NULL)\n        kstat_close(kc);\n    if (sock != -1) {\n        close(sock);\n    }\n    return NULL;\n}\n\n\n// Return stats about a particular network interface. Refs:\n// * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py\n// * http://www.i-scream.org/libstatgrab/\nPyObject *\npsutil_net_if_stats(PyObject *self, PyObject *args) {\n    kstat_ctl_t *kc = NULL;\n    kstat_t *ksp;\n    kstat_named_t *knp;\n    int ret;\n    int sock = -1;\n    int duplex;\n    int speed;\n\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_ifc_info = NULL;\n    PyObject *py_is_up = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n    kc = kstat_open();\n    if (kc == NULL)\n        goto error;\n    sock = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sock == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {\n        if (strcmp(ksp->ks_class, \"net\") == 0) {\n            struct lifreq ifr;\n\n            kstat_read(kc, ksp, NULL);\n            if (ksp->ks_type != KSTAT_TYPE_NAMED)\n                continue;\n            if (strcmp(ksp->ks_class, \"net\") != 0)\n                continue;\n\n            str_copy(ifr.lifr_name, sizeof(ifr.lifr_name), ksp->ks_name);\n            ret = ioctl(sock, SIOCGLIFFLAGS, &ifr);\n            if (ret == -1)\n                continue;  // not a network interface\n\n            // is up?\n            if ((ifr.lifr_flags & IFF_UP) != 0) {\n                if ((knp = kstat_data_lookup(ksp, \"link_up\")) != NULL) {\n                    if (knp->value.ui32 != 0u)\n                        py_is_up = Py_True;\n                    else\n                        py_is_up = Py_False;\n                }\n                else {\n                    py_is_up = Py_True;\n                }\n            }\n            else {\n                py_is_up = Py_False;\n            }\n            Py_INCREF(py_is_up);\n\n            // duplex\n            duplex = 0;  // unknown\n            if ((knp = kstat_data_lookup(ksp, \"link_duplex\")) != NULL) {\n                if (knp->value.ui32 == 1)\n                    duplex = 1;  // half\n                else if (knp->value.ui32 == 2)\n                    duplex = 2;  // full\n            }\n\n            // speed\n            if ((knp = kstat_data_lookup(ksp, \"ifspeed\")) != NULL)\n                // expressed in bits per sec, we want mega bits per sec\n                speed = (int)knp->value.ui64 / 1000000;\n            else\n                speed = 0;\n\n            // mtu\n            ret = ioctl(sock, SIOCGLIFMTU, &ifr);\n            if (ret == -1)\n                goto error;\n\n            py_ifc_info = Py_BuildValue(\n                \"(Oiii)\", py_is_up, duplex, speed, ifr.lifr_mtu\n            );\n            if (!py_ifc_info)\n                goto error;\n            if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))\n                goto error;\n            Py_CLEAR(py_ifc_info);\n        }\n    }\n\n    close(sock);\n    kstat_close(kc);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_is_up);\n    Py_XDECREF(py_ifc_info);\n    Py_DECREF(py_retdict);\n    if (sock != -1)\n        close(sock);\n    if (kc != NULL)\n        kstat_close(kc);\n    psutil_oserror();\n    return NULL;\n}\n\n\n/*\n * Return TCP and UDP connections opened by process.\n * UNIX sockets are excluded.\n *\n * Thanks to:\n * https://github.com/DavidGriffith/finx/blob/master/\n *     nxsensor-3.5.0-1/src/sysdeps/solaris.c\n * ...and:\n * https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/cmd/\n *     cmd-inet/usr.bin/netstat/netstat.c\n */\nPyObject *\npsutil_net_connections(PyObject *self, PyObject *args) {\n    long pid;\n    int sd = 0;\n    mib2_tcpConnEntry_t tp;\n    mib2_udpEntry_t ude;\n#if defined(AF_INET6)\n    mib2_tcp6ConnEntry_t tp6;\n    mib2_udp6Entry_t ude6;\n#endif\n    char buf[512];\n    int i, flags, getcode, num_ent, state, ret;\n    char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];\n    int lport, rport;\n    int processed_pid;\n    int databuf_init = 0;\n    struct strbuf ctlbuf, databuf;\n    struct T_optmgmt_req tor = {0};\n    struct T_optmgmt_ack toa = {0};\n    struct T_error_ack tea = {0};\n    struct opthdr mibhdr = {0};\n\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_laddr = NULL;\n    PyObject *py_raddr = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, \"l\", &pid))\n        goto error;\n\n    sd = open(\"/dev/arp\", O_RDWR);\n    if (sd == -1) {\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, \"/dev/arp\");\n        goto error;\n    }\n\n    ret = ioctl(sd, I_PUSH, \"tcp\");\n    if (ret == -1) {\n        psutil_oserror();\n        goto error;\n    }\n    ret = ioctl(sd, I_PUSH, \"udp\");\n    if (ret == -1) {\n        psutil_oserror();\n        goto error;\n    }\n    //\n    // OK, this mess is basically copied and pasted from nxsensor project\n    // which copied and pasted it from netstat source code, mibget()\n    // function.  Also see:\n    // http://stackoverflow.com/questions/8723598/\n    tor.PRIM_type = T_SVR4_OPTMGMT_REQ;\n    tor.OPT_offset = sizeof(struct T_optmgmt_req);\n    tor.OPT_length = sizeof(struct opthdr);\n    tor.MGMT_flags = T_CURRENT;\n    mibhdr.level = MIB2_IP;\n    mibhdr.name = 0;\n\n    mibhdr.len = 1;\n    memcpy(buf, &tor, sizeof tor);\n    memcpy(buf + tor.OPT_offset, &mibhdr, sizeof mibhdr);\n\n    ctlbuf.buf = buf;\n    ctlbuf.len = tor.OPT_offset + tor.OPT_length;\n    flags = 0;  // request to be sent in non-priority\n\n    if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    ctlbuf.maxlen = sizeof(buf);\n    for (;;) {\n        flags = 0;\n        getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags);\n        memcpy(&toa, buf, sizeof toa);\n        memcpy(&tea, buf, sizeof tea);\n\n        if (getcode != MOREDATA\n            || ctlbuf.len < (int)sizeof(struct T_optmgmt_ack)\n            || toa.PRIM_type != T_OPTMGMT_ACK || toa.MGMT_flags != T_SUCCESS)\n        {\n            break;\n        }\n        if (ctlbuf.len >= (int)sizeof(struct T_error_ack)\n            && tea.PRIM_type == T_ERROR_ACK)\n        {\n            psutil_runtime_error(\"ERROR_ACK\");\n            goto error;\n        }\n        if (getcode == 0 && ctlbuf.len >= (int)sizeof(struct T_optmgmt_ack)\n            && toa.PRIM_type == T_OPTMGMT_ACK && toa.MGMT_flags == T_SUCCESS)\n        {\n            psutil_runtime_error(\"ERROR_T_OPTMGMT_ACK\");\n            goto error;\n        }\n\n        memset(&mibhdr, 0x0, sizeof(mibhdr));\n        memcpy(&mibhdr, buf + toa.OPT_offset, toa.OPT_length);\n\n        databuf.maxlen = mibhdr.len;\n        databuf.len = 0;\n        databuf.buf = (char *)malloc((int)mibhdr.len);\n        if (!databuf.buf) {\n            PyErr_NoMemory();\n            goto error;\n        }\n        databuf_init = 1;\n\n        flags = 0;\n        getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags);\n        if (getcode < 0) {\n            psutil_oserror();\n            goto error;\n        }\n\n        // TCPv4\n        if (mibhdr.level == MIB2_TCP && mibhdr.name == MIB2_TCP_13) {\n            num_ent = mibhdr.len / sizeof(mib2_tcpConnEntry_t);\n            for (i = 0; i < num_ent; i++) {\n                memcpy(&tp, databuf.buf + i * sizeof tp, sizeof tp);\n                processed_pid = tp.tcpConnCreationProcess;\n                if (pid != -1 && processed_pid != pid)\n                    continue;\n                // construct local/remote addresses\n                inet_ntop(AF_INET, &tp.tcpConnLocalAddress, lip, sizeof(lip));\n                inet_ntop(AF_INET, &tp.tcpConnRemAddress, rip, sizeof(rip));\n                lport = tp.tcpConnLocalPort;\n                rport = tp.tcpConnRemPort;\n\n                // construct python tuple/list\n                py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n                if (!py_laddr)\n                    goto error;\n                if (rport != 0)\n                    py_raddr = Py_BuildValue(\"(si)\", rip, rport);\n                else {\n                    py_raddr = Py_BuildValue(\"()\");\n                }\n                if (!py_raddr)\n                    goto error;\n                state = tp.tcpConnEntryInfo.ce_state;\n\n                // add item\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiNNiI)\",\n                        -1,\n                        AF_INET,\n                        SOCK_STREAM,\n                        py_laddr,\n                        py_raddr,\n                        state,\n                        processed_pid\n                    ))\n                {\n                    goto error;\n                }\n                py_laddr = NULL;\n                py_raddr = NULL;\n            }\n        }\n#if defined(AF_INET6)\n        // TCPv6\n        else if (mibhdr.level == MIB2_TCP6 && mibhdr.name == MIB2_TCP6_CONN) {\n            num_ent = mibhdr.len / sizeof(mib2_tcp6ConnEntry_t);\n\n            for (i = 0; i < num_ent; i++) {\n                memcpy(&tp6, databuf.buf + i * sizeof tp6, sizeof tp6);\n                processed_pid = tp6.tcp6ConnCreationProcess;\n                if (pid != -1 && processed_pid != pid)\n                    continue;\n                // construct local/remote addresses\n                inet_ntop(\n                    AF_INET6, &tp6.tcp6ConnLocalAddress, lip, sizeof(lip)\n                );\n                inet_ntop(AF_INET6, &tp6.tcp6ConnRemAddress, rip, sizeof(rip));\n                lport = tp6.tcp6ConnLocalPort;\n                rport = tp6.tcp6ConnRemPort;\n\n                // construct python tuple/list\n                py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n                if (!py_laddr)\n                    goto error;\n                if (rport != 0)\n                    py_raddr = Py_BuildValue(\"(si)\", rip, rport);\n                else\n                    py_raddr = Py_BuildValue(\"()\");\n                if (!py_raddr)\n                    goto error;\n                state = tp6.tcp6ConnEntryInfo.ce_state;\n\n                // add item\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiNNiI)\",\n                        -1,\n                        AF_INET6,\n                        SOCK_STREAM,\n                        py_laddr,\n                        py_raddr,\n                        state,\n                        processed_pid\n                    ))\n                {\n                    goto error;\n                }\n                py_laddr = NULL;\n                py_raddr = NULL;\n            }\n        }\n#endif\n        // UDPv4\n        else if (mibhdr.level == MIB2_UDP && mibhdr.name == MIB2_UDP_ENTRY) {\n            num_ent = mibhdr.len / sizeof(mib2_udpEntry_t);\n            assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr.len);\n            for (i = 0; i < num_ent; i++) {\n                memcpy(&ude, databuf.buf + i * sizeof ude, sizeof ude);\n                processed_pid = ude.udpCreationProcess;\n                if (pid != -1 && processed_pid != pid)\n                    continue;\n                // XXX Very ugly hack! It seems we get here only the first\n                // time we bump into a UDPv4 socket.  PID is a very high\n                // number (clearly impossible) and the address does not\n                // belong to any valid interface.  Not sure what else\n                // to do other than skipping.\n                if (processed_pid > 131072)\n                    continue;\n                inet_ntop(AF_INET, &ude.udpLocalAddress, lip, sizeof(lip));\n                lport = ude.udpLocalPort;\n                py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n                if (!py_laddr)\n                    goto error;\n                py_raddr = Py_BuildValue(\"()\");\n                if (!py_raddr)\n                    goto error;\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiNNiI)\",\n                        -1,\n                        AF_INET,\n                        SOCK_DGRAM,\n                        py_laddr,\n                        py_raddr,\n                        PSUTIL_CONN_NONE,\n                        processed_pid\n                    ))\n                {\n                    goto error;\n                }\n                py_laddr = NULL;\n                py_raddr = NULL;\n            }\n        }\n#if defined(AF_INET6)\n        // UDPv6\n        else if (mibhdr.level == MIB2_UDP6 || mibhdr.level == MIB2_UDP6_ENTRY)\n        {\n            num_ent = mibhdr.len / sizeof(mib2_udp6Entry_t);\n            for (i = 0; i < num_ent; i++) {\n                memcpy(&ude6, databuf.buf + i * sizeof ude6, sizeof ude6);\n                processed_pid = ude6.udp6CreationProcess;\n                if (pid != -1 && processed_pid != pid)\n                    continue;\n                inet_ntop(AF_INET6, &ude6.udp6LocalAddress, lip, sizeof(lip));\n                lport = ude6.udp6LocalPort;\n                py_laddr = Py_BuildValue(\"(si)\", lip, lport);\n                if (!py_laddr)\n                    goto error;\n                py_raddr = Py_BuildValue(\"()\");\n                if (!py_raddr)\n                    goto error;\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(iiiNNiI)\",\n                        -1,\n                        AF_INET6,\n                        SOCK_DGRAM,\n                        py_laddr,\n                        py_raddr,\n                        PSUTIL_CONN_NONE,\n                        processed_pid\n                    ))\n                {\n                    goto error;\n                }\n                py_laddr = NULL;\n                py_raddr = NULL;\n            }\n        }\n#endif\n        free(databuf.buf);\n    }\n\n    close(sd);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_laddr);\n    Py_XDECREF(py_raddr);\n    Py_DECREF(py_retlist);\n    if (databuf_init == 1)\n        free(databuf.buf);\n    if (sd != 0)\n        close(sd);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/sunos/proc.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\n#include <fcntl.h>\n#include <libproc.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// Read a file content and fills a C structure with it.\nstatic int\npsutil_file_to_struct(char *path, void *fstruct, size_t size) {\n    int fd;\n    ssize_t nbytes;\n    fd = open(path, O_RDONLY);\n    if (fd == -1) {\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);\n        return 0;\n    }\n    nbytes = read(fd, fstruct, size);\n    if (nbytes == -1) {\n        close(fd);\n        psutil_oserror();\n        return 0;\n    }\n    if (nbytes != (ssize_t)size) {\n        close(fd);\n        psutil_runtime_error(\"read() file structure size mismatch\");\n        return 0;\n    }\n    close(fd);\n    return nbytes;\n}\n\n\n/*\n * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty\n * as a Python tuple.\n */\nPyObject *\npsutil_proc_oneshot(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    psinfo_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n\n    str_format(path, sizeof(path), \"%s/%i/psinfo\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    return Py_BuildValue(\n        \"ikkdiiikiiii\",\n        info.pr_ppid,  // parent pid\n        info.pr_rssize,  // rss\n        info.pr_size,  // vms\n        PSUTIL_TV2DOUBLE(info.pr_start),  // create time\n        info.pr_lwp.pr_nice,  // nice\n        info.pr_nlwp,  // no. of threads\n        info.pr_lwp.pr_state,  // status code\n        info.pr_ttydev,  // tty nr\n        (int)info.pr_uid,  // real user id\n        (int)info.pr_euid,  // effective user id\n        (int)info.pr_gid,  // real group id\n        (int)info.pr_egid  // effective group id\n    );\n}\n\n\n/*\n * Return process name and args as a Python tuple.\n */\nPyObject *\npsutil_proc_name_and_args(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    psinfo_t info;\n    const char *procfs_path;\n    size_t i;\n    size_t argc;\n    char **argv = NULL;\n    PyObject *py_name = NULL;\n    PyObject *py_sep = NULL;\n    PyObject *py_arg = NULL;\n    PyObject *py_args_str = NULL;\n    PyObject *py_args_list = NULL;\n    PyObject *py_rettuple = NULL;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/psinfo\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n\n    py_name = PyUnicode_DecodeFSDefault(info.pr_fname);\n    if (!py_name)\n        goto error;\n\n    // SunOS truncates arguments to length PRARGSZ and has them\n    // space-separated. The only way to retrieve full properly-split\n    // command line is to parse process memory.\n    argv = psutil_read_raw_args(info, procfs_path, &argc);\n    if (argv) {\n        py_args_list = PyList_New(argc);\n        if (!py_args_list)\n            goto error;\n\n        // iterate through arguments\n        for (i = 0; i < argc; i++) {\n            py_arg = PyUnicode_DecodeFSDefault(argv[i]);\n            if (!py_arg) {\n                Py_DECREF(py_args_list);\n                py_args_list = NULL;\n                break;\n            }\n\n            if (PyList_SetItem(py_args_list, i, py_arg))\n                goto error;\n\n            py_arg = NULL;\n        }\n\n        psutil_free_cstrings_array(argv, argc);\n    }\n\n    /* If we can't read process memory or can't decode the result\n     * then return args from /proc. */\n    if (!py_args_list) {\n        PyErr_Clear();\n        py_args_str = PyUnicode_DecodeFSDefault(info.pr_psargs);\n        if (!py_args_str)\n            goto error;\n\n        py_sep = PyUnicode_FromString(\" \");\n        if (!py_sep)\n            goto error;\n\n        py_args_list = PyUnicode_Split(py_args_str, py_sep, -1);\n        if (!py_args_list)\n            goto error;\n\n        Py_XDECREF(py_sep);\n        Py_XDECREF(py_args_str);\n    }\n\n    py_rettuple = Py_BuildValue(\"OO\", py_name, py_args_list);\n    if (!py_rettuple)\n        goto error;\n\n    Py_DECREF(py_name);\n    Py_DECREF(py_args_list);\n\n    return py_rettuple;\n\nerror:\n    psutil_free_cstrings_array(argv, argc);\n    Py_XDECREF(py_name);\n    Py_XDECREF(py_args_list);\n    Py_XDECREF(py_sep);\n    Py_XDECREF(py_arg);\n    Py_XDECREF(py_args_str);\n    Py_XDECREF(py_rettuple);\n    return NULL;\n}\n\n\n/*\n * Return process environ block.\n */\nPyObject *\npsutil_proc_environ(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    psinfo_t info;\n    const char *procfs_path;\n    char **env = NULL;\n    ssize_t env_count = -1;\n    char *dm;\n    int i = 0;\n    PyObject *py_envname = NULL;\n    PyObject *py_envval = NULL;\n    PyObject *py_retdict = PyDict_New();\n\n    if (!py_retdict)\n        return PyErr_NoMemory();\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n\n    str_format(path, sizeof(path), \"%s/%i/psinfo\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        goto error;\n\n    if (!info.pr_envp) {\n        psutil_oserror_ad(\"/proc/pid/psinfo struct not set\");\n        goto error;\n    }\n\n    env = psutil_read_raw_env(info, procfs_path, &env_count);\n    if (!env && env_count != 0)\n        goto error;\n\n    for (i = 0; i < env_count; i++) {\n        if (!env[i])\n            break;\n\n        dm = strchr(env[i], '=');\n        if (!dm)\n            continue;\n\n        *dm = '\\0';\n\n        py_envname = PyUnicode_DecodeFSDefault(env[i]);\n        if (!py_envname)\n            goto error;\n\n        py_envval = PyUnicode_DecodeFSDefault(dm + 1);\n        if (!py_envname)\n            goto error;\n\n        if (PyDict_SetItem(py_retdict, py_envname, py_envval) < 0)\n            goto error;\n\n        Py_CLEAR(py_envname);\n        Py_CLEAR(py_envval);\n    }\n\n    psutil_free_cstrings_array(env, env_count);\n    return py_retdict;\n\nerror:\n    if (env && env_count >= 0)\n        psutil_free_cstrings_array(env, env_count);\n\n    Py_XDECREF(py_envname);\n    Py_XDECREF(py_envval);\n    Py_XDECREF(py_retdict);\n    return NULL;\n}\n\n\n/*\n * Return process user and system CPU times as a Python tuple.\n */\nPyObject *\npsutil_proc_cpu_times(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    pstatus_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/status\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    // results are more precise than os.times()\n    return Py_BuildValue(\n        \"(dddd)\",\n        PSUTIL_TV2DOUBLE(info.pr_utime),\n        PSUTIL_TV2DOUBLE(info.pr_stime),\n        PSUTIL_TV2DOUBLE(info.pr_cutime),\n        PSUTIL_TV2DOUBLE(info.pr_cstime)\n    );\n}\n\n\n/*\n * Return what CPU the process is running on.\n */\nPyObject *\npsutil_proc_cpu_num(PyObject *self, PyObject *args) {\n    int fd = -1;\n    int pid;\n    char path[1000];\n    struct prheader header;\n    struct lwpsinfo *lwp = NULL;\n    int nent;\n    int size;\n    int proc_num;\n    ssize_t nbytes;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n\n    str_format(path, sizeof(path), \"%s/%i/lpsinfo\", procfs_path, pid);\n    fd = open(path, O_RDONLY);\n    if (fd == -1) {\n        PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);\n        return NULL;\n    }\n\n    // read header\n    nbytes = pread(fd, &header, sizeof(header), 0);\n    if (nbytes == -1) {\n        psutil_oserror();\n        goto error;\n    }\n    if (nbytes != sizeof(header)) {\n        psutil_runtime_error(\"read() file structure size mismatch\");\n        goto error;\n    }\n\n    // malloc\n    nent = header.pr_nent;\n    size = header.pr_entsize * nent;\n    lwp = malloc(size);\n    if (lwp == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    // read the rest\n    nbytes = pread(fd, lwp, size, sizeof(header));\n    if (nbytes == -1) {\n        psutil_oserror();\n        goto error;\n    }\n    if (nbytes != size) {\n        psutil_runtime_error(\"read() file structure size mismatch\");\n        goto error;\n    }\n\n    // done\n    proc_num = lwp->pr_onpro;\n    close(fd);\n    free(lwp);\n    return Py_BuildValue(\"i\", proc_num);\n\nerror:\n    if (fd != -1)\n        close(fd);\n    free(lwp);\n    return NULL;\n}\n\n\n/*\n * Return process uids/gids as a Python tuple.\n */\nPyObject *\npsutil_proc_cred(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    prcred_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/cred\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    return Py_BuildValue(\n        \"iiiiii\",\n        info.pr_ruid,\n        info.pr_euid,\n        info.pr_suid,\n        info.pr_rgid,\n        info.pr_egid,\n        info.pr_sgid\n    );\n}\n\n\n/*\n * Return process voluntary and involuntary context switches as a Python tuple.\n */\nPyObject *\npsutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    prusage_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/usage\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    return Py_BuildValue(\"kk\", info.pr_vctx, info.pr_ictx);\n}\n\n\n/*\n * Return process page faults as a (minor, major) tuple.\n */\nPyObject *\npsutil_proc_page_faults(PyObject *self, PyObject *args) {\n    int pid;\n    char path[1000];\n    prusage_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/usage\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    return Py_BuildValue(\"(kk)\", info.pr_minf, info.pr_majf);\n}\n\n\n/*\n * Process IO counters.\n *\n * Commented out and left here as a reminder.  Apparently we cannot\n * retrieve process IO stats because:\n * - 'pr_ioch' is a sum of chars read and written, with no distinction\n * - 'pr_inblk' and 'pr_oublk', which should be the number of bytes\n *    read and written, hardly increase and according to:\n *    http://www.brendangregg.com/Solaris/paper_diskubyp1.pdf\n *    ...they should be meaningless anyway.\n *\nPyObject*\nproc_io_counters(PyObject* self, PyObject* args) {\n    int pid;\n    char path[1000];\n    prusage_t info;\n    const char *procfs_path;\n\n    if (! PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        return NULL;\n    str_format(path, sizeof(path), \"%s/%i/usage\", procfs_path, pid);\n    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n\n    // On Solaris we only have 'pr_ioch' which accounts for bytes read\n    // *and* written.\n    // 'pr_inblk' and 'pr_oublk' should be expressed in blocks of\n    // 8KB according to:\n    // http://www.brendangregg.com/Solaris/paper_diskubyp1.pdf (pag. 8)\n    return Py_BuildValue(\"kkkk\",\n                         info.pr_ioch,\n                         info.pr_ioch,\n                         info.pr_inblk,\n                         info.pr_oublk);\n}\n*/\n\n\n/*\n * Return information about a given process thread.\n */\nPyObject *\npsutil_proc_query_thread(PyObject *self, PyObject *args) {\n    int pid, tid;\n    char path[1000];\n    lwpstatus_t info;\n    const char *procfs_path;\n\n    if (!PyArg_ParseTuple(args, \"iis\", &pid, &tid, &procfs_path))\n        return NULL;\n    str_format(\n        path, sizeof(path), \"%s/%i/lwp/%i/lwpstatus\", procfs_path, pid, tid\n    );\n    if (!psutil_file_to_struct(path, (void *)&info, sizeof(info)))\n        return NULL;\n    return Py_BuildValue(\n        \"dd\", PSUTIL_TV2DOUBLE(info.pr_utime), PSUTIL_TV2DOUBLE(info.pr_stime)\n    );\n}\n\n\n/*\n * Return process memory mappings.\n */\nPyObject *\npsutil_proc_memory_maps(PyObject *self, PyObject *args) {\n    int pid;\n    int fd = -1;\n    char path[1000];\n    char perms[10];\n    const char *name;\n    struct stat st;\n    pstatus_t status;\n\n    prxmap_t *xmap = NULL, *p;\n    off_t size;\n    size_t nread;\n    int nmap;\n    uintptr_t pr_addr_sz;\n    uintptr_t stk_base_sz, brk_base_sz;\n    const char *procfs_path;\n\n    PyObject *py_path = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, \"is\", &pid, &procfs_path))\n        goto error;\n\n    str_format(path, sizeof(path), \"%s/%i/status\", procfs_path, pid);\n    if (!psutil_file_to_struct(path, (void *)&status, sizeof(status)))\n        goto error;\n\n    str_format(path, sizeof(path), \"%s/%i/xmap\", procfs_path, pid);\n    if (stat(path, &st) == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    size = st.st_size;\n\n    fd = open(path, O_RDONLY);\n    if (fd == -1) {\n        psutil_oserror();\n        goto error;\n    }\n\n    xmap = (prxmap_t *)malloc(size);\n    if (xmap == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    nread = pread(fd, xmap, size, 0);\n    nmap = nread / sizeof(prxmap_t);\n    p = xmap;\n\n    while (nmap) {\n        nmap -= 1;\n        if (p == NULL) {\n            p += 1;\n            continue;\n        }\n\n        perms[0] = '\\0';\n        pr_addr_sz = p->pr_vaddr + p->pr_size;\n\n        // perms\n        str_format(\n            perms,\n            sizeof(perms),\n            \"%c%c%c%c\",\n            p->pr_mflags & MA_READ ? 'r' : '-',\n            p->pr_mflags & MA_WRITE ? 'w' : '-',\n            p->pr_mflags & MA_EXEC ? 'x' : '-',\n            p->pr_mflags & MA_SHARED ? 's' : '-'\n        );\n\n        // name\n        if (strlen(p->pr_mapname) > 0) {\n            name = p->pr_mapname;\n        }\n        else {\n            if ((p->pr_mflags & MA_ISM) || (p->pr_mflags & MA_SHM)) {\n                name = \"[shmid]\";\n            }\n            else {\n                stk_base_sz = status.pr_stkbase + status.pr_stksize;\n                brk_base_sz = status.pr_brkbase + status.pr_brksize;\n\n                if ((pr_addr_sz > status.pr_stkbase)\n                    && (p->pr_vaddr < stk_base_sz))\n                {\n                    name = \"[stack]\";\n                }\n                else if ((p->pr_mflags & MA_ANON)\n                         && (pr_addr_sz > status.pr_brkbase)\n                         && (p->pr_vaddr < brk_base_sz))\n                {\n                    name = \"[heap]\";\n                }\n                else {\n                    name = \"[anon]\";\n                }\n            }\n        }\n\n        py_path = PyUnicode_DecodeFSDefault(name);\n        if (!py_path)\n            goto error;\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"kksOkkk\",\n                (unsigned long)p->pr_vaddr,\n                (unsigned long)pr_addr_sz,\n                perms,\n                py_path,\n                (unsigned long)p->pr_rss * p->pr_pagesize,\n                (unsigned long)p->pr_anon * p->pr_pagesize,\n                (unsigned long)p->pr_locked * p->pr_pagesize\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_path);\n\n        // increment pointer\n        p += 1;\n    }\n\n    close(fd);\n    free(xmap);\n    return py_retlist;\n\nerror:\n    if (fd != -1)\n        close(fd);\n    Py_XDECREF(py_path);\n    Py_DECREF(py_retlist);\n    if (xmap != NULL)\n        free(xmap);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/sunos/sys.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n\n#include <utmpx.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_boot_time(PyObject *self, PyObject *args) {\n    float boot_time = 0.0;\n    struct utmpx *ut;\n\n    UTXENT_MUTEX_LOCK();\n    setutxent();\n    while (NULL != (ut = getutxent())) {\n        if (ut->ut_type == BOOT_TIME) {\n            boot_time = (float)ut->ut_tv.tv_sec;\n            break;\n        }\n    }\n    endutxent();\n    UTXENT_MUTEX_UNLOCK();\n    if (fabs(boot_time) < 0.000001) {\n        /* could not find BOOT_TIME in getutxent loop */\n        psutil_runtime_error(\"can't determine boot time\");\n        return NULL;\n    }\n    return Py_BuildValue(\"f\", boot_time);\n}\n"
  },
  {
    "path": "psutil/arch/windows/cpu.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <PowrProf.h>\n\n#include \"../../arch/all/init.h\"\n\n\n/*\n * Return the number of logical, active CPUs. Return 0 if undetermined.\n * See discussion at: https://bugs.python.org/issue33166#msg314631\n */\nstatic unsigned int\npsutil_get_num_cpus(int fail_on_err) {\n    unsigned int ncpus = 0;\n\n    // Minimum requirement: Windows 7\n    if (GetActiveProcessorCount != NULL) {\n        ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);\n        if ((ncpus == 0) && (fail_on_err == 1)) {\n            psutil_oserror();\n        }\n    }\n    else {\n        psutil_debug(\n            \"GetActiveProcessorCount() not available; \"\n            \"using GetSystemInfo()\"\n        );\n        ncpus = (unsigned int)PSUTIL_SYSTEM_INFO.dwNumberOfProcessors;\n        if ((ncpus <= 0) && (fail_on_err == 1)) {\n            psutil_runtime_error(\"GetSystemInfo failed to retrieve CPU count\");\n        }\n    }\n    return ncpus;\n}\n\n\n/*\n * Retrieves system CPU timing information as a (user, system, idle)\n * tuple. On a multiprocessor system, the values returned are the\n * sum of the designated times across all processors.\n */\nPyObject *\npsutil_cpu_times(PyObject *self, PyObject *args) {\n    double idle, kernel, user, system;\n    FILETIME idle_time, kernel_time, user_time;\n\n    if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) {\n        psutil_oserror();\n        return NULL;\n    }\n\n    idle = (double)((HI_T * idle_time.dwHighDateTime)\n                    + (LO_T * idle_time.dwLowDateTime));\n    user = (double)((HI_T * user_time.dwHighDateTime)\n                    + (LO_T * user_time.dwLowDateTime));\n    kernel = (double)((HI_T * kernel_time.dwHighDateTime)\n                      + (LO_T * kernel_time.dwLowDateTime));\n\n    // Kernel time includes idle time.\n    // We return only busy kernel time subtracting idle time from\n    // kernel time.\n    system = (kernel - idle);\n    return Py_BuildValue(\"(ddd)\", user, system, idle);\n}\n\n\n/*\n * Same as above but for all system CPUs.\n */\nPyObject *\npsutil_per_cpu_times(PyObject *self, PyObject *args) {\n    double idle, kernel, systemt, user, interrupt, dpc;\n    NTSTATUS status;\n    _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;\n    UINT i;\n    unsigned int ncpus;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    // retrieves number of processors\n    ncpus = psutil_get_num_cpus(1);\n    if (ncpus == 0)\n        goto error;\n\n    // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION\n    // structures, one per processor\n    sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)malloc(\n        ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)\n    );\n    if (sppi == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    // gets cpu time information\n    status = NtQuerySystemInformation(\n        SystemProcessorPerformanceInformation,\n        sppi,\n        ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),\n        NULL\n    );\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status,\n            \"NtQuerySystemInformation(SystemProcessorPerformanceInformation)\"\n        );\n        goto error;\n    }\n\n    // computes system global times summing each\n    // processor value\n    idle = user = kernel = interrupt = dpc = 0;\n    for (i = 0; i < ncpus; i++) {\n        user = (double)((HI_T * sppi[i].UserTime.HighPart)\n                        + (LO_T * sppi[i].UserTime.LowPart));\n        idle = (double)((HI_T * sppi[i].IdleTime.HighPart)\n                        + (LO_T * sppi[i].IdleTime.LowPart));\n        kernel = (double)((HI_T * sppi[i].KernelTime.HighPart)\n                          + (LO_T * sppi[i].KernelTime.LowPart));\n        interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart)\n                             + (LO_T * sppi[i].InterruptTime.LowPart));\n        dpc = (double)((HI_T * sppi[i].DpcTime.HighPart)\n                       + (LO_T * sppi[i].DpcTime.LowPart));\n\n        // kernel time includes idle time on windows\n        // we return only busy kernel time subtracting\n        // idle time from kernel time\n        systemt = kernel - idle;\n        if (!pylist_append_fmt(\n                py_retlist, \"(ddddd)\", user, systemt, idle, interrupt, dpc\n            ))\n            goto error;\n    }\n\n    free(sppi);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (sppi)\n        free(sppi);\n    return NULL;\n}\n\n\n/*\n * Return the number of active, logical CPUs.\n */\nPyObject *\npsutil_cpu_count_logical(PyObject *self, PyObject *args) {\n    unsigned int ncpus;\n\n    ncpus = psutil_get_num_cpus(0);\n    if (ncpus != 0)\n        return Py_BuildValue(\"I\", ncpus);\n    else\n        Py_RETURN_NONE;  // mimic os.cpu_count()\n}\n\n\n/*\n * Return the number of CPU cores (non hyper-threading).\n */\nPyObject *\npsutil_cpu_count_cores(PyObject *self, PyObject *args) {\n    DWORD rc;\n    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;\n    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;\n    DWORD length = 0;\n    DWORD offset = 0;\n    DWORD ncpus = 0;\n    DWORD prev_processor_info_size = 0;\n\n    // GetLogicalProcessorInformationEx() is available from Windows 7\n    // onward. Differently from GetLogicalProcessorInformation()\n    // it supports process groups, meaning this is able to report more\n    // than 64 CPUs. See:\n    // https://bugs.python.org/issue33166\n    if (GetLogicalProcessorInformationEx == NULL) {\n        psutil_debug(\"Win < 7; cpu_count_cores() forced to None\");\n        Py_RETURN_NONE;\n    }\n\n    while (1) {\n        rc = GetLogicalProcessorInformationEx(RelationAll, buffer, &length);\n        if (rc == FALSE) {\n            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n                if (buffer) {\n                    free(buffer);\n                }\n                buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX\n                )malloc(length);\n                if (NULL == buffer) {\n                    PyErr_NoMemory();\n                    return NULL;\n                }\n            }\n            else {\n                psutil_debug(\n                    \"GetLogicalProcessorInformationEx() returned %u\",\n                    GetLastError()\n                );\n                goto return_none;\n            }\n        }\n        else {\n            break;\n        }\n    }\n\n    ptr = buffer;\n    while (offset < length) {\n        // Advance ptr by the size of the previous\n        // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct.\n        ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX\n                   *)(((char *)ptr) + prev_processor_info_size);\n\n        if (ptr->Relationship == RelationProcessorCore) {\n            ncpus += 1;\n        }\n\n        // When offset == length, we've reached the last processor\n        // info struct in the buffer.\n        offset += ptr->Size;\n        prev_processor_info_size = ptr->Size;\n    }\n\n    free(buffer);\n    if (ncpus != 0) {\n        return Py_BuildValue(\"I\", ncpus);\n    }\n    else {\n        psutil_debug(\"GetLogicalProcessorInformationEx() count was 0\");\n        Py_RETURN_NONE;  // mimic os.cpu_count()\n    }\n\nreturn_none:\n    if (buffer != NULL)\n        free(buffer);\n    Py_RETURN_NONE;\n}\n\n\n/*\n * Return CPU statistics.\n */\nPyObject *\npsutil_cpu_stats(PyObject *self, PyObject *args) {\n    NTSTATUS status;\n    _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL;\n    _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;\n    _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL;\n    unsigned int ncpus;\n    UINT i;\n    ULONG64 dpcs = 0;\n    ULONG interrupts = 0;\n\n    // retrieves number of processors\n    ncpus = psutil_get_num_cpus(1);\n    if (ncpus == 0)\n        goto error;\n\n    // get syscalls / ctx switches\n    spi = (_SYSTEM_PERFORMANCE_INFORMATION *)malloc(\n        ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION)\n    );\n    if (spi == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n    status = NtQuerySystemInformation(\n        SystemPerformanceInformation,\n        spi,\n        ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION),\n        NULL\n    );\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status, \"NtQuerySystemInformation(SystemPerformanceInformation)\"\n        );\n        goto error;\n    }\n\n    // get DPCs\n    InterruptInformation = malloc(\n        sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus\n    );\n    if (InterruptInformation == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    status = NtQuerySystemInformation(\n        SystemInterruptInformation,\n        InterruptInformation,\n        ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION),\n        NULL\n    );\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status, \"NtQuerySystemInformation(SystemInterruptInformation)\"\n        );\n        goto error;\n    }\n    for (i = 0; i < ncpus; i++) {\n        dpcs += InterruptInformation[i].DpcCount;\n    }\n\n    // get interrupts\n    sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)malloc(\n        ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)\n    );\n    if (sppi == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    status = NtQuerySystemInformation(\n        SystemProcessorPerformanceInformation,\n        sppi,\n        ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),\n        NULL\n    );\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status,\n            \"NtQuerySystemInformation(SystemProcessorPerformanceInformation)\"\n        );\n        goto error;\n    }\n\n    for (i = 0; i < ncpus; i++) {\n        interrupts += sppi[i].InterruptCount;\n    }\n\n    // done\n    free(spi);\n    free(InterruptInformation);\n    free(sppi);\n    return Py_BuildValue(\n        \"kkkk\",\n        spi->ContextSwitches,\n        interrupts,\n        (unsigned long)dpcs,\n        spi->SystemCalls\n    );\n\nerror:\n    if (spi)\n        free(spi);\n    if (InterruptInformation)\n        free(InterruptInformation);\n    if (sppi)\n        free(sppi);\n    return NULL;\n}\n\n\n/*\n * Return CPU frequency.\n */\nPyObject *\npsutil_cpu_freq(PyObject *self, PyObject *args) {\n    PROCESSOR_POWER_INFORMATION *ppi;\n    NTSTATUS ret;\n    ULONG size;\n    LPBYTE pBuffer = NULL;\n    ULONG current;\n    ULONG max;\n    unsigned int ncpus;\n\n    // Get the number of CPUs.\n    ncpus = psutil_get_num_cpus(1);\n    if (ncpus == 0)\n        return NULL;\n\n    // Allocate size.\n    size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION);\n    pBuffer = (BYTE *)LocalAlloc(LPTR, size);\n    if (!pBuffer) {\n        psutil_oserror();\n        return NULL;\n    }\n\n    // Syscall.\n    ret = CallNtPowerInformation(ProcessorInformation, NULL, 0, pBuffer, size);\n    if (ret != 0) {\n        psutil_runtime_error(\"CallNtPowerInformation syscall failed\");\n        goto error;\n    }\n\n    // Results.\n    ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer;\n    max = ppi->MaxMhz;\n    current = ppi->CurrentMhz;\n    LocalFree(pBuffer);\n\n    return Py_BuildValue(\"kk\", current, max);\n\nerror:\n    if (pBuffer != NULL)\n        LocalFree(pBuffer);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/disk.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <tchar.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#ifndef _ARRAYSIZE\n#define _ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))\n#endif\n\nstatic char *\npsutil_get_drive_type(int type) {\n    switch (type) {\n        case DRIVE_FIXED:\n            return \"fixed\";\n        case DRIVE_CDROM:\n            return \"cdrom\";\n        case DRIVE_REMOVABLE:\n            return \"removable\";\n        case DRIVE_UNKNOWN:\n            return \"unknown\";\n        case DRIVE_NO_ROOT_DIR:\n            return \"unmounted\";\n        case DRIVE_REMOTE:\n            return \"remote\";\n        case DRIVE_RAMDISK:\n            return \"ramdisk\";\n        default:\n            return \"?\";\n    }\n}\n\n\n// Return path's disk total, used, and free space.\nPyObject *\npsutil_disk_usage(PyObject *self, PyObject *args) {\n    PyObject *py_path;\n    wchar_t *path = NULL;\n    ULARGE_INTEGER total, free, avail;\n    BOOL retval;\n    ULONGLONG used;\n\n    if (!PyArg_ParseTuple(args, \"U\", &py_path)) {\n        return NULL;\n    }\n\n    path = PyUnicode_AsWideCharString(py_path, NULL);\n    if (path == NULL) {\n        return NULL;\n    }\n\n    Py_BEGIN_ALLOW_THREADS\n    retval = GetDiskFreeSpaceExW(path, &avail, &total, &free);\n    Py_END_ALLOW_THREADS\n\n    PyMem_Free(path);\n\n    if (retval == 0) {\n        return PyErr_SetExcFromWindowsErrWithFilenameObject(\n            PyExc_OSError, 0, py_path\n        );\n    }\n\n    used = total.QuadPart - free.QuadPart;\n    return Py_BuildValue(\"(KKK)\", total.QuadPart, used, free.QuadPart);\n}\n\n\n/*\n * Return a Python dict of tuples for disk I/O information. This may\n * require running \"diskperf -y\" command first.\n */\nPyObject *\npsutil_disk_io_counters(PyObject *self, PyObject *args) {\n    DISK_PERFORMANCE diskPerformance;\n    DWORD dwSize;\n    HANDLE hDevice = NULL;\n    char szDevice[MAX_PATH];\n    char szDeviceDisplay[MAX_PATH];\n    int devNum;\n    int i;\n    DWORD ioctrlSize;\n    BOOL ret;\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_tuple = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n    // Apparently there's no way to figure out how many times we have\n    // to iterate in order to find valid drives.\n    // Let's assume 32, which is higher than 26, the number of letters\n    // in the alphabet (from A:\\ to Z:\\).\n    for (devNum = 0; devNum <= 32; ++devNum) {\n        py_tuple = NULL;\n        str_format(szDevice, MAX_PATH, \"\\\\\\\\.\\\\PhysicalDrive%d\", devNum);\n        hDevice = CreateFile(\n            szDevice,\n            0,\n            FILE_SHARE_READ | FILE_SHARE_WRITE,\n            NULL,\n            OPEN_EXISTING,\n            0,\n            NULL\n        );\n        if (hDevice == INVALID_HANDLE_VALUE)\n            continue;\n\n        // DeviceIoControl() sucks!\n        i = 0;\n        ioctrlSize = sizeof(diskPerformance);\n        while (1) {\n            i += 1;\n            ret = DeviceIoControl(\n                hDevice,\n                IOCTL_DISK_PERFORMANCE,\n                NULL,\n                0,\n                &diskPerformance,\n                ioctrlSize,\n                &dwSize,\n                NULL\n            );\n            if (ret != 0)\n                break;  // OK!\n            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n                // Retry with a bigger buffer (+ limit for retries).\n                if (i <= 1024) {\n                    ioctrlSize *= 2;\n                    continue;\n                }\n            }\n            else if (GetLastError() == ERROR_INVALID_FUNCTION) {\n                // This happens on AppVeyor:\n                // https://ci.appveyor.com/project/giampaolo/psutil/build/\n                //      1364/job/ascpdi271b06jle3\n                // Assume it means we're dealing with some exotic disk\n                // and go on.\n                psutil_debug(\n                    \"DeviceIoControl -> ERROR_INVALID_FUNCTION; \"\n                    \"ignore PhysicalDrive%i\",\n                    devNum\n                );\n                goto next;\n            }\n            else if (GetLastError() == ERROR_NOT_SUPPORTED) {\n                // Again, let's assume we're dealing with some exotic disk.\n                psutil_debug(\n                    \"DeviceIoControl -> ERROR_NOT_SUPPORTED; \"\n                    \"ignore PhysicalDrive%i\",\n                    devNum\n                );\n                goto next;\n            }\n            // XXX: it seems we should also catch ERROR_INVALID_PARAMETER:\n            // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/\n            //     ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/\n            //     openafs-1.4.14/src/usd/usd_nt.c\n\n            // XXX: we can also bump into ERROR_MORE_DATA in which case\n            // (quoting doc) we're supposed to retry with a bigger buffer\n            // and specify  a new \"starting point\", whatever it means.\n            psutil_oserror();\n            goto error;\n        }\n\n        str_format(szDeviceDisplay, MAX_PATH, \"PhysicalDrive%i\", devNum);\n        py_tuple = Py_BuildValue(\n            \"(IILLKK)\",\n            diskPerformance.ReadCount,\n            diskPerformance.WriteCount,\n            diskPerformance.BytesRead,\n            diskPerformance.BytesWritten,\n            // convert to ms:\n            // https://github.com/giampaolo/psutil/issues/1012\n            (unsigned long long)(diskPerformance.ReadTime.QuadPart) / 10000000,\n            (unsigned long long)(diskPerformance.WriteTime.QuadPart) / 10000000\n        );\n        if (!py_tuple)\n            goto error;\n        if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple))\n            goto error;\n        Py_CLEAR(py_tuple);\n\n    next:\n        CloseHandle(hDevice);\n    }\n\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_tuple);\n    Py_DECREF(py_retdict);\n    if (hDevice != NULL)\n        CloseHandle(hDevice);\n    return NULL;\n}\n\n\n/*\n * Return disk partitions as a list of tuples such as\n * (drive_letter, drive_letter, type, \"\")\n */\nPyObject *\npsutil_disk_partitions(PyObject *self, PyObject *args) {\n    DWORD num_bytes;\n    char drive_strings[255];\n    char *drive_letter = drive_strings;\n    char mp_buf[MAX_PATH];\n    char mp_path[MAX_PATH];\n    int all;\n    int type;\n    int ret;\n    unsigned int old_mode = 0;\n    char opts[50];\n    HANDLE mp_h;\n    BOOL mp_flag = TRUE;\n    LPTSTR fs_type[MAX_PATH + 1] = {0};\n    DWORD pflags = 0;\n    DWORD lpMaximumComponentLength = 0;  // max file name\n    PyObject *py_all;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL) {\n        return NULL;\n    }\n\n    // avoid to visualize a message box in case something goes wrong\n    // see https://github.com/giampaolo/psutil/issues/264\n    old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);\n\n    if (!PyArg_ParseTuple(args, \"O\", &py_all))\n        goto error;\n    all = PyObject_IsTrue(py_all);\n\n    Py_BEGIN_ALLOW_THREADS\n    num_bytes = GetLogicalDriveStrings(254, drive_letter);\n    Py_END_ALLOW_THREADS\n\n    if (num_bytes == 0) {\n        psutil_oserror();\n        goto error;\n    }\n\n    while (*drive_letter != 0) {\n        opts[0] = 0;\n        fs_type[0] = 0;\n\n        Py_BEGIN_ALLOW_THREADS\n        type = GetDriveType(drive_letter);\n        Py_END_ALLOW_THREADS\n\n        // by default we only show hard drives and cd-roms\n        if (all == 0) {\n            if ((type == DRIVE_UNKNOWN) || (type == DRIVE_NO_ROOT_DIR)\n                || (type == DRIVE_REMOTE) || (type == DRIVE_RAMDISK))\n            {\n                goto next;\n            }\n            // floppy disk: skip it by default as it introduces a\n            // considerable slowdown.\n            if ((type == DRIVE_REMOVABLE)\n                && (strcmp(drive_letter, \"A:\\\\\") == 0))\n            {\n                goto next;\n            }\n        }\n\n        ret = GetVolumeInformation(\n            (LPCTSTR)drive_letter,\n            NULL,\n            _ARRAYSIZE(drive_letter),\n            NULL,\n            &lpMaximumComponentLength,\n            &pflags,\n            (LPTSTR)fs_type,\n            _ARRAYSIZE(fs_type)\n        );\n        if (ret == 0) {\n            // We might get here in case of a floppy hard drive, in\n            // which case the error is (21, \"device not ready\").\n            // Let's pretend it didn't happen as we already have\n            // the drive name and type ('removable').\n            str_append(opts, sizeof(opts), \"\");\n            SetLastError(0);\n        }\n        else {\n            if (pflags & FILE_READ_ONLY_VOLUME)\n                str_append(opts, sizeof(opts), \"ro\");\n            else\n                str_append(opts, sizeof(opts), \"rw\");\n            if (pflags & FILE_VOLUME_IS_COMPRESSED)\n                str_append(opts, sizeof(opts), \",compressed\");\n            if (pflags & FILE_READ_ONLY_VOLUME)\n                str_append(opts, sizeof(opts), \",readonly\");\n\n            // Check for mount points on this volume and add/get info\n            // (checks first to know if we can even have mount points)\n            if (pflags & FILE_SUPPORTS_REPARSE_POINTS) {\n                mp_h = FindFirstVolumeMountPoint(\n                    drive_letter, mp_buf, MAX_PATH\n                );\n                if (mp_h != INVALID_HANDLE_VALUE) {\n                    mp_flag = TRUE;\n                    while (mp_flag) {\n                        // Append full mount path with drive letter\n                        str_copy(\n                            mp_path, sizeof(mp_path), drive_letter\n                        );  // initialize\n                        str_append(\n                            mp_path, sizeof(mp_path), mp_buf\n                        );  // append mount point\n\n                        if (!pylist_append_fmt(\n                                py_retlist,\n                                \"(ssss)\",\n                                drive_letter,\n                                mp_path,\n                                fs_type,  // typically \"NTFS\"\n                                opts\n                            ))\n                        {\n                            FindVolumeMountPointClose(mp_h);\n                            goto error;\n                        }\n\n                        // Continue looking for more mount points\n                        mp_flag = FindNextVolumeMountPoint(\n                            mp_h, mp_buf, MAX_PATH\n                        );\n                    }\n                    FindVolumeMountPointClose(mp_h);\n                }\n            }\n        }\n\n        if (strlen(opts) > 0)\n            str_append(opts, sizeof(opts), \",\");\n        str_append(opts, sizeof(opts), psutil_get_drive_type(type));\n\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"(ssss)\",\n                drive_letter,\n                drive_letter,\n                fs_type,  // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS\n                opts\n            ))\n        {\n            goto error;\n        }\n        goto next;\n\n    next:\n        drive_letter = strchr(drive_letter, 0) + 1;\n    }\n\n    SetErrorMode(old_mode);\n    return py_retlist;\n\nerror:\n    SetErrorMode(old_mode);\n    Py_DECREF(py_retlist);\n    return NULL;\n}\n\n\n/*\n Accept a filename's drive in native  format like \"\\Device\\HarddiskVolume1\\\"\n and return the corresponding drive letter (e.g. \"C:\\\\\").\n If no match is found return an empty string.\n*/\nPyObject *\npsutil_QueryDosDevice(PyObject *self, PyObject *args) {\n    LPCTSTR lpDevicePath;\n    TCHAR d = TEXT('A');\n    TCHAR szBuff[5];\n\n    if (!PyArg_ParseTuple(args, \"s\", &lpDevicePath))\n        return NULL;\n\n    while (d <= TEXT('Z')) {\n        TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\\0')};\n        TCHAR szTarget[512] = {0};\n        if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) {\n            if (_tcscmp(lpDevicePath, szTarget) == 0) {\n                _stprintf_s(szBuff, _countof(szBuff), TEXT(\"%c:\"), d);\n                return PyUnicode_FromString(szBuff);\n            }\n        }\n        d++;\n    }\n    return PyUnicode_FromString(\"\");\n}\n"
  },
  {
    "path": "psutil/arch/windows/heap.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n\n#include <Python.h>\n#include <windows.h>\n#include <malloc.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// Returns a tuple with:\n//\n// - heap_used: sum of used blocks, like `uordblks` on Linux. Catches\n//   small `malloc()` without `free()` and small `HeapAlloc()` without\n//   `HeapFree()`. If bigger than some KB they go into `mmap_used`.\n//\n// - mmap_used: VirtualAlloc'd regions, like `hblkhd` on Linux. Catches\n//   `VirtualAlloc()` without `VirtualFree()`.\n//\n// - heap_count (Windows only): number of private heaps. Catches\n//   `HeapCreate()` without `HeapDestroy()`.\nPyObject *\npsutil_heap_info(PyObject *self, PyObject *args) {\n    MEMORY_BASIC_INFORMATION mbi;\n    LPVOID addr = NULL;\n    SIZE_T heap_used = 0;\n    SIZE_T mmap_used = 0;\n    DWORD heap_count;\n    DWORD written;\n    _HEAPINFO hinfo = {0};\n    hinfo._pentry = NULL;\n    int status;\n    HANDLE *heaps = NULL;\n\n    // Walk CRT heaps to measure heap used.\n    while ((status = _heapwalk(&hinfo)) == _HEAPOK) {\n        if (hinfo._useflag == _USEDENTRY) {\n            heap_used += hinfo._size;\n        }\n    }\n    if ((status != _HEAPEND) && (status != _HEAPOK))\n        return psutil_oserror_wsyscall(\"_heapwalk\");\n\n    // Get number of heaps (+ heap handles).\n    heap_count = GetProcessHeaps(0, NULL);  // 1st: get count\n    if (heap_count == 0)\n        return psutil_oserror_wsyscall(\"GetProcessHeaps (1/2)\");\n    heaps = (HANDLE *)malloc(heap_count * sizeof(HANDLE));\n    if (!heaps) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n    written = GetProcessHeaps(heap_count, heaps);  // 2nd: get heaps handles\n    if (written == 0) {\n        free(heaps);\n        return psutil_oserror_wsyscall(\"GetProcessHeaps (2/2)\");\n    }\n\n    // VirtualAlloc'd regions (large allocations / mmap|hblkhd equivalent).\n    while (VirtualQuery(addr, &mbi, sizeof(mbi)) == sizeof(mbi)) {\n        if (mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE\n            && (mbi.AllocationProtect & PAGE_READWRITE))\n        {\n            int is_heap_region = 0;\n            for (DWORD i = 0; i < heap_count; i++) {\n                if (mbi.AllocationBase == heaps[i]) {\n                    is_heap_region = 1;\n                    break;\n                }\n            }\n\n            if (!is_heap_region) {\n                mmap_used += mbi.RegionSize;\n            }\n        }\n        addr = (LPBYTE)mbi.BaseAddress + mbi.RegionSize;\n    }\n\n    free(heaps);\n\n    return Py_BuildValue(\n        \"nnn\",\n        (Py_ssize_t)heap_used,\n        (Py_ssize_t)mmap_used,\n        (Py_ssize_t)heap_count\n    );\n}\n\n\n// Return unused heap memory back to the OS. Return the size of the\n// largest committed free block in the heap, in bytes. Equivalent to\n// Linux `heap_trim(0)`.\nPyObject *\npsutil_heap_trim(PyObject *self, PyObject *args) {\n    HANDLE hHeap = GetProcessHeap();\n    SIZE_T largest_free;\n\n    if (hHeap == NULL)\n        return psutil_oserror_wsyscall(\"GetProcessHeap\");\n\n    largest_free = HeapCompact(hHeap, 0);\n    if (largest_free == 0) {\n        if (GetLastError() != NO_ERROR) {\n            return psutil_oserror_wsyscall(\"HeapCompact\");\n        }\n    }\n    return Py_BuildValue(\"K\", (unsigned long long)largest_free);\n}\n"
  },
  {
    "path": "psutil/arch/windows/init.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n\n#include \"../../arch/all/init.h\"\n#include \"ntextapi.h\"\n\n\n// Needed to make these globally visible.\nint PSUTIL_WINVER;\nSYSTEM_INFO PSUTIL_SYSTEM_INFO;\nCRITICAL_SECTION PSUTIL_CRITICAL_SECTION;\n\n\n// ====================================================================\n// --- Backward compatibility with missing Python.h APIs\n// ====================================================================\n\n// PyPy on Windows. Missing APIs added in PyPy 7.3.14.\n#if defined(PYPY_VERSION)\n#if !defined(PyErr_SetFromWindowsErrWithFilename)\nPyObject *\nPyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) {\n    PyObject *py_exc = NULL;\n    PyObject *py_winerr = NULL;\n\n    if (winerr == 0)\n        winerr = GetLastError();\n    if (filename == NULL) {\n        py_exc = PyObject_CallFunction(\n            PyExc_OSError, \"(is)\", winerr, strerror(winerr)\n        );\n    }\n    else {\n        py_exc = PyObject_CallFunction(\n            PyExc_OSError, \"(iss)\", winerr, strerror(winerr), filename\n        );\n    }\n    if (py_exc == NULL)\n        return NULL;\n\n    py_winerr = Py_BuildValue(\"i\", winerr);\n    if (py_winerr == NULL)\n        goto error;\n    if (PyObject_SetAttrString(py_exc, \"winerror\", py_winerr) != 0)\n        goto error;\n    PyErr_SetObject(PyExc_OSError, py_exc);\n    Py_XDECREF(py_exc);\n    return NULL;\n\nerror:\n    Py_XDECREF(py_exc);\n    Py_XDECREF(py_winerr);\n    return NULL;\n}\n#endif  // !defined(PyErr_SetFromWindowsErrWithFilename)\n\n\n#if !defined(PyErr_SetExcFromWindowsErrWithFilenameObject)\nPyObject *\nPyErr_SetExcFromWindowsErrWithFilenameObject(\n    PyObject *type, int ierr, PyObject *filename\n) {\n    // Original function is too complex. Just raise OSError without\n    // filename.\n    return PyErr_SetFromWindowsErrWithFilename(ierr, NULL);\n}\n#endif  // !defined(PyErr_SetExcFromWindowsErrWithFilenameObject)\n#endif  // defined(PYPY_VERSION)\n\n\n// ====================================================================\n// --- Utils\n// ====================================================================\n\n// Convert a NTSTATUS value to a Win32 error code and set the proper\n// Python exception.\nPVOID\npsutil_SetFromNTStatusErr(NTSTATUS status, const char *syscall) {\n    ULONG err;\n    char fullmsg[1024];\n\n    if (NT_NTWIN32(status))\n        err = WIN32_FROM_NTSTATUS(status);\n    else\n        err = RtlNtStatusToDosErrorNoTeb(status);\n    // if (GetLastError() != 0)\n    //     err = GetLastError();\n    str_format(fullmsg, sizeof(fullmsg), \"(originated from %s)\", syscall);\n    return PyErr_SetFromWindowsErrWithFilename(err, fullmsg);\n}\n\n\n// A wrapper around GetModuleHandle and GetProcAddress.\nPVOID\npsutil_GetProcAddress(LPCSTR libname, LPCSTR apiname) {\n    HMODULE mod;\n    FARPROC addr;\n\n    if ((mod = GetModuleHandleA(libname)) == NULL) {\n        psutil_debug(\n            \"%s module not supported (needed for %s)\", libname, apiname\n        );\n        PyErr_SetFromWindowsErrWithFilename(0, libname);\n        return NULL;\n    }\n    if ((addr = GetProcAddress(mod, apiname)) == NULL) {\n        psutil_debug(\"%s -> %s API not supported\", libname, apiname);\n        PyErr_SetFromWindowsErrWithFilename(0, apiname);\n        return NULL;\n    }\n    return addr;\n}\n\n\n// A wrapper around LoadLibrary and GetProcAddress.\nPVOID\npsutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR apiname) {\n    HMODULE mod;\n    FARPROC addr;\n\n    Py_BEGIN_ALLOW_THREADS\n    mod = LoadLibraryA(libname);\n    Py_END_ALLOW_THREADS\n    if (mod == NULL) {\n        psutil_debug(\"%s lib not supported (needed for %s)\", libname, apiname);\n        PyErr_SetFromWindowsErrWithFilename(0, libname);\n        return NULL;\n    }\n    if ((addr = GetProcAddress(mod, apiname)) == NULL) {\n        psutil_debug(\"%s -> %s not supported\", libname, apiname);\n        PyErr_SetFromWindowsErrWithFilename(0, apiname);\n        FreeLibrary(mod);\n        return NULL;\n    }\n    // Causes crash.\n    // FreeLibrary(mod);\n    return addr;\n}\n\n\n// Convert the hi and lo parts of a FILETIME structure or a\n// LARGE_INTEGER to a UNIX time. A FILETIME contains a 64-bit value\n// representing the number of 100-nanosecond intervals since January 1,\n// 1601 (UTC). A UNIX time is the number of seconds that have elapsed\n// since the UNIX epoch, that is the time 00:00:00 UTC on 1 January\n// 1970.\nstatic double\n_to_unix_time(ULONGLONG hiPart, ULONGLONG loPart) {\n    ULONGLONG ret;\n\n    // 100 nanosecond intervals since January 1, 1601.\n    ret = hiPart << 32;\n    ret += loPart;\n    // Change starting time to the Epoch (00:00:00 UTC, January 1, 1970).\n    ret -= 116444736000000000ull;\n    // Convert nano secs to secs.\n    return (double)ret / 10000000ull;\n}\n\n\ndouble\npsutil_FiletimeToUnixTime(FILETIME ft) {\n    return _to_unix_time(\n        (ULONGLONG)ft.dwHighDateTime, (ULONGLONG)ft.dwLowDateTime\n    );\n}\n\n\ndouble\npsutil_LargeIntegerToUnixTime(LARGE_INTEGER li) {\n    return _to_unix_time((ULONGLONG)li.HighPart, (ULONGLONG)li.LowPart);\n}\n\n\n// ====================================================================\n// --- Init / load libs\n// ====================================================================\n\n\nstatic int\npsutil_loadlibs() {\n    // --- Mandatory\n    NtQuerySystemInformation = psutil_GetProcAddressFromLib(\n        \"ntdll.dll\", \"NtQuerySystemInformation\"\n    );\n    if (!NtQuerySystemInformation)\n        return -1;\n    NtQueryInformationProcess = psutil_GetProcAddress(\n        \"ntdll.dll\", \"NtQueryInformationProcess\"\n    );\n    if (!NtQueryInformationProcess)\n        return -1;\n    NtSetInformationProcess = psutil_GetProcAddress(\n        \"ntdll.dll\", \"NtSetInformationProcess\"\n    );\n    if (!NtSetInformationProcess)\n        return -1;\n    NtQueryObject = psutil_GetProcAddressFromLib(\"ntdll.dll\", \"NtQueryObject\");\n    if (!NtQueryObject)\n        return -1;\n    RtlIpv4AddressToStringA = psutil_GetProcAddressFromLib(\n        \"ntdll.dll\", \"RtlIpv4AddressToStringA\"\n    );\n    if (!RtlIpv4AddressToStringA)\n        return -1;\n    GetExtendedTcpTable = psutil_GetProcAddressFromLib(\n        \"iphlpapi.dll\", \"GetExtendedTcpTable\"\n    );\n    if (!GetExtendedTcpTable)\n        return -1;\n    GetExtendedUdpTable = psutil_GetProcAddressFromLib(\n        \"iphlpapi.dll\", \"GetExtendedUdpTable\"\n    );\n    if (!GetExtendedUdpTable)\n        return -1;\n    RtlGetVersion = psutil_GetProcAddressFromLib(\"ntdll.dll\", \"RtlGetVersion\");\n    if (!RtlGetVersion)\n        return -1;\n    NtSuspendProcess = psutil_GetProcAddressFromLib(\n        \"ntdll\", \"NtSuspendProcess\"\n    );\n    if (!NtSuspendProcess)\n        return -1;\n    NtResumeProcess = psutil_GetProcAddressFromLib(\"ntdll\", \"NtResumeProcess\");\n    if (!NtResumeProcess)\n        return -1;\n    NtQueryVirtualMemory = psutil_GetProcAddressFromLib(\n        \"ntdll\", \"NtQueryVirtualMemory\"\n    );\n    if (!NtQueryVirtualMemory)\n        return -1;\n    RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib(\n        \"ntdll\", \"RtlNtStatusToDosErrorNoTeb\"\n    );\n    if (!RtlNtStatusToDosErrorNoTeb)\n        return -1;\n    GetTickCount64 = psutil_GetProcAddress(\"kernel32\", \"GetTickCount64\");\n    if (!GetTickCount64)\n        return -1;\n    RtlIpv6AddressToStringA = psutil_GetProcAddressFromLib(\n        \"ntdll.dll\", \"RtlIpv6AddressToStringA\"\n    );\n    if (!RtlIpv6AddressToStringA)\n        return -1;\n\n    // --- Optional\n\n    // minimum requirement: Win 7\n    QueryInterruptTime = psutil_GetProcAddressFromLib(\n        \"kernelbase.dll\", \"QueryInterruptTime\"\n    );\n    // minimum requirement: Win 7\n    GetActiveProcessorCount = psutil_GetProcAddress(\n        \"kernel32\", \"GetActiveProcessorCount\"\n    );\n    // minimum requirement: Win 7\n    GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib(\n        \"kernel32\", \"GetLogicalProcessorInformationEx\"\n    );\n    // minimum requirements: Windows Server Core\n    WTSEnumerateSessionsW = psutil_GetProcAddressFromLib(\n        \"wtsapi32.dll\", \"WTSEnumerateSessionsW\"\n    );\n    WTSQuerySessionInformationW = psutil_GetProcAddressFromLib(\n        \"wtsapi32.dll\", \"WTSQuerySessionInformationW\"\n    );\n    WTSFreeMemory = psutil_GetProcAddressFromLib(\n        \"wtsapi32.dll\", \"WTSFreeMemory\"\n    );\n\n    PyErr_Clear();\n    return 0;\n}\n\n\nstatic int\npsutil_set_winver() {\n    RTL_OSVERSIONINFOEXW versionInfo;\n    ULONG maj;\n    ULONG min;\n\n    versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);\n    memset(&versionInfo, 0, sizeof(RTL_OSVERSIONINFOEXW));\n    RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo);\n    maj = versionInfo.dwMajorVersion;\n    min = versionInfo.dwMinorVersion;\n    if (maj == 6 && min == 0)\n        PSUTIL_WINVER = PSUTIL_WINDOWS_VISTA;  // or Server 2008\n    else if (maj == 6 && min == 1)\n        PSUTIL_WINVER = PSUTIL_WINDOWS_7;\n    else if (maj == 6 && min == 2)\n        PSUTIL_WINVER = PSUTIL_WINDOWS_8;\n    else if (maj == 6 && min == 3)\n        PSUTIL_WINVER = PSUTIL_WINDOWS_8_1;\n    else if (maj == 10 && min == 0)\n        PSUTIL_WINVER = PSUTIL_WINDOWS_10;\n    else\n        PSUTIL_WINVER = PSUTIL_WINDOWS_NEW;\n    return 0;\n}\n\n\n// Called on module import.\nint\npsutil_setup_windows(void) {\n    if (psutil_loadlibs() != 0)\n        return -1;\n    if (psutil_set_winver() != 0)\n        return -1;\n    GetSystemInfo(&PSUTIL_SYSTEM_INFO);\n    InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION);\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/windows/init.h",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <Winsvc.h>\n\n#include \"ntextapi.h\"\n\n\nextern int PSUTIL_WINVER;\nextern SYSTEM_INFO PSUTIL_SYSTEM_INFO;\nextern CRITICAL_SECTION PSUTIL_CRITICAL_SECTION;\n\n#define PSUTIL_WINDOWS_VISTA 60\n#define PSUTIL_WINDOWS_7 61\n#define PSUTIL_WINDOWS_8 62\n#define PSUTIL_WINDOWS_8_1 63\n#define PSUTIL_WINDOWS_10 100\n#define PSUTIL_WINDOWS_NEW MAXLONG\n\n#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))\n#define MALLOC_ZERO(x) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x))\n#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))\n\n#define _NT_FACILITY_MASK 0xfff\n#define _NT_FACILITY_SHIFT 16\n#define _NT_FACILITY(status) \\\n    ((((ULONG)(status)) >> _NT_FACILITY_SHIFT) & _NT_FACILITY_MASK)\n\n#define NT_NTWIN32(status) (_NT_FACILITY(status) == FACILITY_WIN32)\n#define WIN32_FROM_NTSTATUS(status) (((ULONG)(status)) & 0xffff)\n\n#define LO_T 1e-7\n#define HI_T 429.4967296\n\n#ifndef AF_INET6\n#define AF_INET6 23\n#endif\n\n#if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)\n#if !defined(PyErr_SetFromWindowsErrWithFilename)\nPyObject *PyErr_SetFromWindowsErrWithFilename(int ierr, const char *filename);\n#endif\n#if !defined(PyErr_SetExcFromWindowsErrWithFilenameObject)\nPyObject *PyErr_SetExcFromWindowsErrWithFilenameObject(\n    PyObject *type, int ierr, PyObject *filename\n);\n#endif\n#endif\n\ndouble psutil_FiletimeToUnixTime(FILETIME ft);\ndouble psutil_LargeIntegerToUnixTime(LARGE_INTEGER li);\nint psutil_setup_windows(void);\nPVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname);\nPVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname);\nPVOID psutil_SetFromNTStatusErr(NTSTATUS status, const char *syscall);\n\nPyObject *TimeoutExpired;\nPyObject *TimeoutAbandoned;\n\n\nint _psutil_pids(DWORD **pids_array, int *pids_count);\nHANDLE psutil_check_phandle(HANDLE hProcess, DWORD pid, int check_exit_code);\nHANDLE psutil_handle_from_pid(DWORD pid, DWORD dwDesiredAccess);\nint psutil_assert_pid_exists(DWORD pid, char *err);\nint psutil_assert_pid_not_exists(DWORD pid, char *err);\nint psutil_pid_is_running(DWORD pid);\nint psutil_set_se_debug();\nSC_HANDLE psutil_get_service_handle(\n    char service_name, DWORD scm_access, DWORD access\n);\n\nint psutil_get_proc_info(\n    DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID *retBuffer\n);\n\nPyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_freq(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_stats(PyObject *self, PyObject *args);\nPyObject *psutil_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_disk_partitions(PyObject *self, PyObject *args);\nPyObject *psutil_disk_usage(PyObject *self, PyObject *args);\nPyObject *psutil_get_loadavg();\nPyObject *psutil_get_open_files(DWORD pid, HANDLE hProcess);\nPyObject *psutil_getpagesize(PyObject *self, PyObject *args);\nPyObject *psutil_heap_info(PyObject *self, PyObject *args);\nPyObject *psutil_heap_trim(PyObject *self, PyObject *args);\nPyObject *psutil_init_loadavg_counter();\nPyObject *psutil_net_connections(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_addrs(PyObject *self, PyObject *args);\nPyObject *psutil_net_if_stats(PyObject *self, PyObject *args);\nPyObject *psutil_net_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);\nPyObject *psutil_pid_exists(PyObject *self, PyObject *args);\nPyObject *psutil_pids(PyObject *self, PyObject *args);\nPyObject *psutil_ppid_map(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kw);\nPyObject *psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args);\nPyObject *psutil_proc_cwd(PyObject *self, PyObject *args);\nPyObject *psutil_proc_environ(PyObject *self, PyObject *args);\nPyObject *psutil_proc_exe(PyObject *self, PyObject *args);\nPyObject *psutil_proc_io_counters(PyObject *self, PyObject *args);\nPyObject *psutil_proc_io_priority_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_io_priority_set(PyObject *self, PyObject *args);\nPyObject *psutil_proc_is_suspended(PyObject *self, PyObject *args);\nPyObject *psutil_proc_kill(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_info(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_maps(PyObject *self, PyObject *args);\nPyObject *psutil_proc_memory_uss(PyObject *self, PyObject *args);\nPyObject *psutil_proc_num_handles(PyObject *self, PyObject *args);\nPyObject *psutil_proc_oneshot(PyObject *self, PyObject *args);\nPyObject *psutil_proc_open_files(PyObject *self, PyObject *args);\nPyObject *psutil_proc_page_faults(PyObject *self, PyObject *args);\nPyObject *psutil_proc_priority_get(PyObject *self, PyObject *args);\nPyObject *psutil_proc_priority_set(PyObject *self, PyObject *args);\nPyObject *psutil_proc_suspend_or_resume(PyObject *self, PyObject *args);\nPyObject *psutil_proc_threads(PyObject *self, PyObject *args);\nPyObject *psutil_proc_times(PyObject *self, PyObject *args);\nPyObject *psutil_proc_username(PyObject *self, PyObject *args);\nPyObject *psutil_proc_wait(PyObject *self, PyObject *args);\nPyObject *psutil_QueryDosDevice(PyObject *self, PyObject *args);\nPyObject *psutil_sensors_battery(PyObject *self, PyObject *args);\nPyObject *psutil_swap_percent(PyObject *self, PyObject *args);\nPyObject *psutil_uptime(PyObject *self, PyObject *args);\nPyObject *psutil_users(PyObject *self, PyObject *args);\nPyObject *psutil_GetPerformanceInfo(PyObject *self, PyObject *args);\nPyObject *psutil_winservice_enumerate(PyObject *self, PyObject *args);\nPyObject *psutil_winservice_query_config(PyObject *self, PyObject *args);\nPyObject *psutil_winservice_query_descr(PyObject *self, PyObject *args);\nPyObject *psutil_winservice_query_status(PyObject *self, PyObject *args);\nPyObject *psutil_winservice_start(PyObject *self, PyObject *args);\nPyObject *psutil_winservice_stop(PyObject *self, PyObject *args);\n"
  },
  {
    "path": "psutil/arch/windows/mem.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <Psapi.h>\n#include <pdh.h>\n\n#include \"../../arch/all/init.h\"\n\n\nPyObject *\npsutil_getpagesize(PyObject *self, PyObject *args) {\n    // XXX: we may want to use GetNativeSystemInfo to differentiate\n    // page size for WoW64 processes (but am not sure).\n    return Py_BuildValue(\"I\", PSUTIL_SYSTEM_INFO.dwPageSize);\n}\n\n\nPyObject *\npsutil_GetPerformanceInfo(PyObject *self, PyObject *args) {\n    PERFORMANCE_INFORMATION info;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!GetPerformanceInfo(&info, sizeof(PERFORMANCE_INFORMATION))) {\n        psutil_oserror();\n        goto error;\n    }\n\n    // clang-format off\n    if (!pydict_add(dict, \"CommitTotal\", \"K\", (ULONGLONG)info.CommitTotal)) goto error;\n    if (!pydict_add(dict, \"CommitLimit\", \"K\", (ULONGLONG)info.CommitLimit)) goto error;\n    if (!pydict_add(dict, \"CommitPeak\", \"K\", (ULONGLONG)info.CommitPeak)) goto error;\n    if (!pydict_add(dict, \"PhysicalTotal\", \"K\", (ULONGLONG)info.PhysicalTotal)) goto error;\n    if (!pydict_add(dict, \"PhysicalAvailable\", \"K\", (ULONGLONG)info.PhysicalAvailable)) goto error;\n    if (!pydict_add(dict, \"KernelTotal\", \"K\", (ULONGLONG)info.KernelTotal)) goto error;\n    if (!pydict_add(dict, \"KernelPaged\", \"K\", (ULONGLONG)info.KernelPaged)) goto error;\n    if (!pydict_add(dict, \"KernelNonpaged\", \"K\", (ULONGLONG)info.KernelNonpaged)) goto error;\n    if (!pydict_add(dict, \"SystemCache\", \"K\", (ULONGLONG)info.SystemCache)) goto error;\n    if (!pydict_add(dict, \"PageSize\", \"K\", (ULONGLONG)info.PageSize)) goto error;\n    // if (!pydict_add(dict, \"HandleCount\", \"I\", (unsigned int)info.HandleCount)) goto error;\n    // if (!pydict_add(dict, \"ProcessCount\", \"I\", (unsigned int)info.ProcessCount)) goto error;\n    // if (!pydict_add(dict, \"ThreadCount\", \"I\", (unsigned int)info.ThreadCount)) goto error;\n    // clang-format on\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\n// Return a float representing the percent usage of all paging files on\n// the system.\nPyObject *\npsutil_swap_percent(PyObject *self, PyObject *args) {\n    WCHAR *szCounterPath = L\"\\\\Paging File(_Total)\\\\% Usage\";\n    PDH_STATUS s;\n    HQUERY hQuery;\n    HCOUNTER hCounter;\n    PDH_FMT_COUNTERVALUE counterValue;\n    double percentUsage;\n\n    if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS) {\n        psutil_runtime_error(\"PdhOpenQueryW failed\");\n        return NULL;\n    }\n\n    s = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);\n    if (s != ERROR_SUCCESS) {\n        PdhCloseQuery(hQuery);\n        psutil_runtime_error(\n            \"PdhAddEnglishCounterW failed. Performance counters may be \"\n            \"disabled.\"\n        );\n        return NULL;\n    }\n\n    s = PdhCollectQueryData(hQuery);\n    if (s != ERROR_SUCCESS) {\n        // If swap disabled this will fail.\n        psutil_debug(\"PdhCollectQueryData failed; assume swap percent is 0\");\n        percentUsage = 0;\n    }\n    else {\n        s = PdhGetFormattedCounterValue(\n            (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &counterValue\n        );\n        if (s != ERROR_SUCCESS) {\n            PdhCloseQuery(hQuery);\n            psutil_runtime_error(\"PdhGetFormattedCounterValue failed\");\n            return NULL;\n        }\n        percentUsage = counterValue.doubleValue;\n    }\n\n    PdhRemoveCounter(hCounter);\n    PdhCloseQuery(hQuery);\n    return Py_BuildValue(\"d\", percentUsage);\n}\n"
  },
  {
    "path": "psutil/arch/windows/net.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Fixes clash between winsock2.h and windows.h\n#define WIN32_LEAN_AND_MEAN\n\n#include <Python.h>\n#include <windows.h>\n#include <wchar.h>\n#include <ws2tcpip.h>\n\n#include \"../../arch/all/init.h\"\n\n\nstatic PIP_ADAPTER_ADDRESSES\npsutil_get_nic_addresses(void) {\n    ULONG bufferLength = 0;\n    PIP_ADAPTER_ADDRESSES buffer;\n\n    if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &bufferLength)\n        != ERROR_BUFFER_OVERFLOW)\n    {\n        psutil_runtime_error(\"GetAdaptersAddresses() syscall failed.\");\n        return NULL;\n    }\n\n    buffer = malloc(bufferLength);\n    if (buffer == NULL) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n    memset(buffer, 0, bufferLength);\n\n    if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buffer, &bufferLength)\n        != ERROR_SUCCESS)\n    {\n        free(buffer);\n        psutil_runtime_error(\"GetAdaptersAddresses() syscall failed.\");\n        return NULL;\n    }\n\n    return buffer;\n}\n\n\n/*\n * Return a Python list of named tuples with overall network I/O information\n */\nPyObject *\npsutil_net_io_counters(PyObject *self, PyObject *args) {\n    DWORD dwRetVal = 0;\n    MIB_IF_ROW2 ifRow;\n    PIP_ADAPTER_ADDRESSES pAddresses = NULL;\n    PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_nic_info = NULL;\n    PyObject *py_nic_name = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n    pAddresses = psutil_get_nic_addresses();\n    if (pAddresses == NULL)\n        goto error;\n\n    pCurrAddresses = pAddresses;\n\n    while (pCurrAddresses) {\n        py_nic_info = NULL;\n        py_nic_name = NULL;\n\n        SecureZeroMemory(&ifRow, sizeof(ifRow));\n        ifRow.InterfaceIndex = pCurrAddresses->IfIndex;\n\n        dwRetVal = GetIfEntry2(&ifRow);\n        if (dwRetVal != NO_ERROR) {\n            psutil_runtime_error(\n                \"GetIfEntry2() syscall failed for interface %lu\",\n                (unsigned long)ifRow.InterfaceIndex\n            );\n            goto error;\n        }\n\n        py_nic_info = Py_BuildValue(\n            \"(KKKKKKKK)\",\n            ifRow.OutOctets,\n            ifRow.InOctets,\n            ifRow.OutUcastPkts + ifRow.OutNUcastPkts,\n            ifRow.InUcastPkts + ifRow.InNUcastPkts,\n            ifRow.InErrors,\n            ifRow.OutErrors,\n            ifRow.InDiscards,\n            ifRow.OutDiscards\n        );\n        if (!py_nic_info)\n            goto error;\n\n        py_nic_name = PyUnicode_FromWideChar(\n            pCurrAddresses->FriendlyName,\n            wcsnlen(pCurrAddresses->FriendlyName, IF_MAX_STRING_SIZE)\n        );\n        if (!py_nic_name)\n            goto error;\n\n        if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info))\n            goto error;\n\n        Py_CLEAR(py_nic_info);\n        Py_CLEAR(py_nic_name);\n\n        pCurrAddresses = pCurrAddresses->Next;\n    }\n\n    free(pAddresses);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_nic_info);\n    Py_XDECREF(py_nic_name);\n    Py_DECREF(py_retdict);\n    if (pAddresses)\n        free(pAddresses);\n    return NULL;\n}\n\n\n/*\n * Return NICs addresses.\n */\nPyObject *\npsutil_net_if_addrs(PyObject *self, PyObject *args) {\n    unsigned int i = 0;\n    ULONG family;\n    PCTSTR intRet;\n    PCTSTR netmaskIntRet;\n    char *ptr;\n    char buff_addr[1024];\n    char buff_macaddr[1024];\n    char buff_netmask[1024];\n    DWORD dwRetVal = 0;\n    ULONG converted_netmask;\n    UINT netmask_bits;\n    int n;\n    size_t remaining;\n    struct in_addr in_netmask;\n    PIP_ADAPTER_ADDRESSES pAddresses = NULL;\n    PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;\n    PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;\n\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_address = NULL;\n    PyObject *py_mac_address = NULL;\n    PyObject *py_nic_name = NULL;\n    PyObject *py_netmask = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    pAddresses = psutil_get_nic_addresses();\n    if (pAddresses == NULL)\n        goto error;\n    pCurrAddresses = pAddresses;\n\n    while (pCurrAddresses) {\n        Py_CLEAR(py_nic_name);\n        Py_CLEAR(py_address);\n        Py_CLEAR(py_mac_address);\n        Py_CLEAR(py_netmask);\n\n        pUnicast = pCurrAddresses->FirstUnicastAddress;\n\n        netmaskIntRet = NULL;\n\n        py_nic_name = PyUnicode_FromWideChar(\n            pCurrAddresses->FriendlyName, wcslen(pCurrAddresses->FriendlyName)\n        );\n        if (py_nic_name == NULL)\n            goto error;\n\n        // MAC address\n        if (pCurrAddresses->PhysicalAddressLength != 0) {\n            ptr = buff_macaddr;\n            remaining = sizeof(buff_macaddr);\n            for (i = 0; i < pCurrAddresses->PhysicalAddressLength; i++) {\n                if (i == pCurrAddresses->PhysicalAddressLength - 1) {\n                    n = str_format(\n                        ptr,\n                        remaining,\n                        \"%.2X\",\n                        (int)pCurrAddresses->PhysicalAddress[i]\n                    );\n                }\n                else {\n                    n = str_format(\n                        ptr,\n                        remaining,\n                        \"%.2X-\",\n                        (int)pCurrAddresses->PhysicalAddress[i]\n                    );\n                }\n                if (n < 0) {  // error or truncated\n                    psutil_runtime_error(\"str_format() error\");\n                    break;\n                }\n                ptr += n;\n                remaining -= n;\n            }\n\n            py_mac_address = PyUnicode_FromString(buff_macaddr);\n            if (py_mac_address == NULL)\n                goto error;\n\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(OiOOOO)\",\n                    py_nic_name,\n                    -1,  // this will be converted later to AF_LINK\n                    py_mac_address,\n                    Py_None,  // netmask (not supported)\n                    Py_None,  // broadcast (not supported)\n                    Py_None  // ptp (not supported on Windows)\n                ))\n            {\n                goto error;\n            }\n\n            Py_CLEAR(py_mac_address);\n        }\n\n        // find out the IP address associated with the NIC\n        if (pUnicast != NULL) {\n            for (i = 0; pUnicast != NULL; i++) {\n                family = pUnicast->Address.lpSockaddr->sa_family;\n                if (family == AF_INET) {\n                    struct sockaddr_in *sa_in = (struct sockaddr_in *)pUnicast\n                                                    ->Address.lpSockaddr;\n                    intRet = inet_ntop(\n                        AF_INET,\n                        &(sa_in->sin_addr),\n                        buff_addr,\n                        sizeof(buff_addr)\n                    );\n                    if (!intRet)\n                        goto error;\n                    netmask_bits = pUnicast->OnLinkPrefixLength;\n                    dwRetVal = ConvertLengthToIpv4Mask(\n                        netmask_bits, &converted_netmask\n                    );\n                    if (dwRetVal == NO_ERROR) {\n                        in_netmask.s_addr = converted_netmask;\n                        netmaskIntRet = inet_ntop(\n                            AF_INET,\n                            &in_netmask,\n                            buff_netmask,\n                            sizeof(buff_netmask)\n                        );\n                        if (!netmaskIntRet)\n                            goto error;\n                    }\n                }\n                else if (family == AF_INET6) {\n                    struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)\n                                                      pUnicast->Address\n                                                          .lpSockaddr;\n                    intRet = inet_ntop(\n                        AF_INET6,\n                        &(sa_in6->sin6_addr),\n                        buff_addr,\n                        sizeof(buff_addr)\n                    );\n                    if (!intRet)\n                        goto error;\n                }\n                else {\n                    // we should never get here\n                    pUnicast = pUnicast->Next;\n                    continue;\n                }\n\n                Py_CLEAR(py_address);\n                Py_CLEAR(py_netmask);\n\n                py_address = PyUnicode_FromString(buff_addr);\n                if (py_address == NULL)\n                    goto error;\n\n                if (netmaskIntRet != NULL) {\n                    py_netmask = PyUnicode_FromString(buff_netmask);\n                }\n                else {\n                    Py_INCREF(Py_None);\n                    py_netmask = Py_None;\n                }\n\n                if (!pylist_append_fmt(\n                        py_retlist,\n                        \"(OiOOOO)\",\n                        py_nic_name,\n                        family,\n                        py_address,\n                        py_netmask,\n                        Py_None,  // broadcast (not supported)\n                        Py_None  // ptp (not supported on Windows)\n                    ))\n                {\n                    goto error;\n                }\n\n                Py_CLEAR(py_address);\n                Py_CLEAR(py_netmask);\n\n                pUnicast = pUnicast->Next;\n            }\n        }\n\n        Py_CLEAR(py_nic_name);\n        pCurrAddresses = pCurrAddresses->Next;\n    }\n\n    free(pAddresses);\n    return py_retlist;\n\nerror:\n    if (pAddresses)\n        free(pAddresses);\n    Py_XDECREF(py_retlist);\n    Py_XDECREF(py_address);\n    Py_XDECREF(py_mac_address);\n    Py_XDECREF(py_nic_name);\n    Py_XDECREF(py_netmask);\n    return NULL;\n}\n\n\n/*\n * Provides stats about NIC interfaces installed on the system.\n * TODO: get 'duplex' (currently it's hard coded to '2', aka 'full duplex')\n */\nPyObject *\npsutil_net_if_stats(PyObject *self, PyObject *args) {\n    int i;\n    DWORD dwSize = 0;\n    DWORD dwRetVal = 0;\n    MIB_IFTABLE *pIfTable = NULL;\n    MIB_IFROW *pIfRow;\n    PIP_ADAPTER_ADDRESSES pAddresses = NULL;\n    PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;\n    char descr[MAX_PATH];\n    int ifname_found;\n\n    PyObject *py_nic_name = NULL;\n    PyObject *py_retdict = PyDict_New();\n    PyObject *py_ifc_info = NULL;\n    PyObject *py_is_up = NULL;\n\n    if (py_retdict == NULL)\n        return NULL;\n\n    pAddresses = psutil_get_nic_addresses();\n    if (pAddresses == NULL)\n        goto error;\n\n    pIfTable = (MIB_IFTABLE *)malloc(sizeof(MIB_IFTABLE));\n    if (pIfTable == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n    dwSize = sizeof(MIB_IFTABLE);\n    if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) {\n        free(pIfTable);\n        pIfTable = (MIB_IFTABLE *)malloc(dwSize);\n        if (pIfTable == NULL) {\n            PyErr_NoMemory();\n            goto error;\n        }\n    }\n    // Make a second call to GetIfTable to get the actual\n    // data we want.\n    if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) {\n        psutil_runtime_error(\"GetIfTable() syscall failed\");\n        goto error;\n    }\n\n    for (i = 0; i < (int)pIfTable->dwNumEntries; i++) {\n        pIfRow = (MIB_IFROW *)&pIfTable->table[i];\n\n        // GetIfTable is not able to give us NIC with \"friendly names\"\n        // so we determine them via GetAdapterAddresses() which\n        // provides friendly names *and* descriptions and find the\n        // ones that match.\n        ifname_found = 0;\n        Py_CLEAR(py_nic_name);\n        Py_CLEAR(py_ifc_info);\n\n        pCurrAddresses = pAddresses;\n        while (pCurrAddresses) {\n            str_format(descr, MAX_PATH, \"%wS\", pCurrAddresses->Description);\n            if (lstrcmp(descr, pIfRow->bDescr) == 0) {\n                py_nic_name = PyUnicode_FromWideChar(\n                    pCurrAddresses->FriendlyName,\n                    wcslen(pCurrAddresses->FriendlyName)\n                );\n                if (py_nic_name == NULL)\n                    goto error;\n                ifname_found = 1;\n                break;\n            }\n            pCurrAddresses = pCurrAddresses->Next;\n        }\n        if (ifname_found == 0) {\n            // Name not found means GetAdapterAddresses() doesn't list\n            // this NIC, only GetIfTable, meaning it's not really a NIC\n            // interface so we skip it.\n            continue;\n        }\n\n        Py_CLEAR(py_is_up);\n        if ((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED\n             || pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL)\n            && pIfRow->dwAdminStatus == 1)\n        {\n            py_is_up = Py_True;\n        }\n        else {\n            py_is_up = Py_False;\n        }\n        Py_INCREF(py_is_up);\n\n        py_ifc_info = Py_BuildValue(\n            \"(Oikk)\",\n            py_is_up,\n            2,  // there's no way to know duplex so let's assume 'full'\n            pIfRow->dwSpeed / 1000000,  // expressed in bytes, we want Mb\n            pIfRow->dwMtu\n        );\n        if (!py_ifc_info)\n            goto error;\n\n        if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info))\n            goto error;\n\n        Py_CLEAR(py_ifc_info);\n        Py_CLEAR(py_nic_name);\n    }\n\n    free(pIfTable);\n    free(pAddresses);\n    Py_CLEAR(py_nic_name);\n    Py_CLEAR(py_ifc_info);\n    Py_CLEAR(py_is_up);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_is_up);\n    Py_XDECREF(py_ifc_info);\n    Py_XDECREF(py_nic_name);\n    Py_XDECREF(py_retdict);\n    if (pIfTable)\n        free(pIfTable);\n    if (pAddresses)\n        free(pAddresses);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/ntextapi.h",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n * Define Windows structs and constants which are considered private.\n */\n\n// clang-format off\n#if !defined(__NTEXTAPI_H__)\n#define __NTEXTAPI_H__\n#include <winternl.h>\n#include <iphlpapi.h>\n\ntypedef LONG NTSTATUS;\n\n// https://github.com/ajkhoury/TestDll/blob/master/nt_ddk.h\n#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)\n#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)\n#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)\n#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)\n#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)\n\n// WtsApi32.h\n#define WTS_CURRENT_SERVER_HANDLE  ((HANDLE)NULL)\n#define WINSTATIONNAME_LENGTH    32\n#define DOMAIN_LENGTH            17\n#define USERNAME_LENGTH          20\n\n// ================================================================\n// Enums\n// ================================================================\n\n#undef  SystemExtendedHandleInformation\n#define SystemExtendedHandleInformation 64\n#undef  MemoryWorkingSetInformation\n#define MemoryWorkingSetInformation 0x1\n#undef  ObjectNameInformation\n#define ObjectNameInformation 1\n#undef  ProcessIoPriority\n#define ProcessIoPriority 33\n#undef  ProcessWow64Information\n#define ProcessWow64Information 26\n#undef  SystemProcessIdInformation\n#define SystemProcessIdInformation 88\n\n// process suspend() / resume()\ntypedef enum _KTHREAD_STATE {\n    Initialized,\n    Ready,\n    Running,\n    Standby,\n    Terminated,\n    Waiting,\n    Transition,\n    DeferredReady,\n    GateWait,\n    MaximumThreadState\n} KTHREAD_STATE, *PKTHREAD_STATE;\n\ntypedef enum _KWAIT_REASON {\n    Executive,\n    FreePage,\n    PageIn,\n    PoolAllocation,\n    DelayExecution,\n    Suspended,\n    UserRequest,\n    WrExecutive,\n    WrFreePage,\n    WrPageIn,\n    WrPoolAllocation,\n    WrDelayExecution,\n    WrSuspended,\n    WrUserRequest,\n    WrEventPair,\n    WrQueue,\n    WrLpcReceive,\n    WrLpcReply,\n    WrVirtualMemory,\n    WrPageOut,\n    WrRendezvous,\n    WrKeyedEvent,\n    WrTerminated,\n    WrProcessInSwap,\n    WrCpuRateControl,\n    WrCalloutStack,\n    WrKernel,\n    WrResource,\n    WrPushLock,\n    WrMutex,\n    WrQuantumEnd,\n    WrDispatchInt,\n    WrPreempted,\n    WrYieldExecution,\n    WrFastMutex,\n    WrGuardedMutex,\n    WrRundown,\n    WrAlertByThreadId,\n    WrDeferredPreempt,\n    MaximumWaitReason\n} KWAIT_REASON, *PKWAIT_REASON;\n\n// users()\ntypedef enum _WTS_INFO_CLASS {\n    WTSInitialProgram,\n    WTSApplicationName,\n    WTSWorkingDirectory,\n    WTSOEMId,\n    WTSSessionId,\n    WTSUserName,\n    WTSWinStationName,\n    WTSDomainName,\n    WTSConnectState,\n    WTSClientBuildNumber,\n    WTSClientName,\n    WTSClientDirectory,\n    WTSClientProductId,\n    WTSClientHardwareId,\n    WTSClientAddress,\n    WTSClientDisplay,\n    WTSClientProtocolType,\n    WTSIdleTime,\n    WTSLogonTime,\n    WTSIncomingBytes,\n    WTSOutgoingBytes,\n    WTSIncomingFrames,\n    WTSOutgoingFrames,\n    WTSClientInfo,\n    WTSSessionInfo,\n    WTSSessionInfoEx,\n    WTSConfigInfo,\n    WTSValidationInfo,   // Info Class value used to fetch Validation Information through the WTSQuerySessionInformation\n    WTSSessionAddressV4,\n    WTSIsRemoteSession\n} WTS_INFO_CLASS;\n\ntypedef enum _WTS_CONNECTSTATE_CLASS {\n    WTSActive,              // User logged on to WinStation\n    WTSConnected,           // WinStation connected to client\n    WTSConnectQuery,        // In the process of connecting to client\n    WTSShadow,              // Shadowing another WinStation\n    WTSDisconnected,        // WinStation logged on without client\n    WTSIdle,                // Waiting for client to connect\n    WTSListen,              // WinStation is listening for connection\n    WTSReset,               // WinStation is being reset\n    WTSDown,                // WinStation is down due to error\n    WTSInit,                // WinStation in initialization\n} WTS_CONNECTSTATE_CLASS;\n\n// ================================================================\n// Structs.\n// ================================================================\n\n// cpu_stats(), per_cpu_times()\ntypedef struct {\n    LARGE_INTEGER IdleTime;\n    LARGE_INTEGER KernelTime;\n    LARGE_INTEGER UserTime;\n    LARGE_INTEGER DpcTime;\n    LARGE_INTEGER InterruptTime;\n    ULONG InterruptCount;\n} _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;\n\n// cpu_stats()\ntypedef struct {\n    LARGE_INTEGER IdleProcessTime;\n    LARGE_INTEGER IoReadTransferCount;\n    LARGE_INTEGER IoWriteTransferCount;\n    LARGE_INTEGER IoOtherTransferCount;\n    ULONG IoReadOperationCount;\n    ULONG IoWriteOperationCount;\n    ULONG IoOtherOperationCount;\n    ULONG AvailablePages;\n    ULONG CommittedPages;\n    ULONG CommitLimit;\n    ULONG PeakCommitment;\n    ULONG PageFaultCount;\n    ULONG CopyOnWriteCount;\n    ULONG TransitionCount;\n    ULONG CacheTransitionCount;\n    ULONG DemandZeroCount;\n    ULONG PageReadCount;\n    ULONG PageReadIoCount;\n    ULONG CacheReadCount;\n    ULONG CacheIoCount;\n    ULONG DirtyPagesWriteCount;\n    ULONG DirtyWriteIoCount;\n    ULONG MappedPagesWriteCount;\n    ULONG MappedWriteIoCount;\n    ULONG PagedPoolPages;\n    ULONG NonPagedPoolPages;\n    ULONG PagedPoolAllocs;\n    ULONG PagedPoolFrees;\n    ULONG NonPagedPoolAllocs;\n    ULONG NonPagedPoolFrees;\n    ULONG FreeSystemPtes;\n    ULONG ResidentSystemCodePage;\n    ULONG TotalSystemDriverPages;\n    ULONG TotalSystemCodePages;\n    ULONG NonPagedPoolLookasideHits;\n    ULONG PagedPoolLookasideHits;\n    ULONG AvailablePagedPoolPages;\n    ULONG ResidentSystemCachePage;\n    ULONG ResidentPagedPoolPage;\n    ULONG ResidentSystemDriverPage;\n    ULONG CcFastReadNoWait;\n    ULONG CcFastReadWait;\n    ULONG CcFastReadResourceMiss;\n    ULONG CcFastReadNotPossible;\n    ULONG CcFastMdlReadNoWait;\n    ULONG CcFastMdlReadWait;\n    ULONG CcFastMdlReadResourceMiss;\n    ULONG CcFastMdlReadNotPossible;\n    ULONG CcMapDataNoWait;\n    ULONG CcMapDataWait;\n    ULONG CcMapDataNoWaitMiss;\n    ULONG CcMapDataWaitMiss;\n    ULONG CcPinMappedDataCount;\n    ULONG CcPinReadNoWait;\n    ULONG CcPinReadWait;\n    ULONG CcPinReadNoWaitMiss;\n    ULONG CcPinReadWaitMiss;\n    ULONG CcCopyReadNoWait;\n    ULONG CcCopyReadWait;\n    ULONG CcCopyReadNoWaitMiss;\n    ULONG CcCopyReadWaitMiss;\n    ULONG CcMdlReadNoWait;\n    ULONG CcMdlReadWait;\n    ULONG CcMdlReadNoWaitMiss;\n    ULONG CcMdlReadWaitMiss;\n    ULONG CcReadAheadIos;\n    ULONG CcLazyWriteIos;\n    ULONG CcLazyWritePages;\n    ULONG CcDataFlushes;\n    ULONG CcDataPages;\n    ULONG ContextSwitches;\n    ULONG FirstLevelTbFills;\n    ULONG SecondLevelTbFills;\n    ULONG SystemCalls;\n} _SYSTEM_PERFORMANCE_INFORMATION;\n\n// cpu_stats()\ntypedef struct {\n    ULONG ContextSwitches;\n    ULONG DpcCount;\n    ULONG DpcRate;\n    ULONG TimeIncrement;\n    ULONG DpcBypassCount;\n    ULONG ApcBypassCount;\n} _SYSTEM_INTERRUPT_INFORMATION;\n\ntypedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {\n    PVOID Object;\n    HANDLE UniqueProcessId;\n    HANDLE HandleValue;\n    ULONG GrantedAccess;\n    USHORT CreatorBackTraceIndex;\n    USHORT ObjectTypeIndex;\n    ULONG HandleAttributes;\n    ULONG Reserved;\n} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;\n\ntypedef struct _SYSTEM_HANDLE_INFORMATION_EX {\n    ULONG_PTR NumberOfHandles;\n    ULONG_PTR Reserved;\n    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];\n} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;\n\ntypedef struct _CLIENT_ID2 {\n    HANDLE UniqueProcess;\n    HANDLE UniqueThread;\n} CLIENT_ID2, *PCLIENT_ID2;\n\n#define CLIENT_ID CLIENT_ID2\n#define PCLIENT_ID PCLIENT_ID2\n\ntypedef struct _SYSTEM_THREAD_INFORMATION2 {\n    LARGE_INTEGER KernelTime;\n    LARGE_INTEGER UserTime;\n    LARGE_INTEGER CreateTime;\n    ULONG WaitTime;\n    PVOID StartAddress;\n    CLIENT_ID ClientId;\n    LONG Priority;\n    LONG BasePriority;\n    ULONG ContextSwitches;\n    ULONG ThreadState;\n    KWAIT_REASON WaitReason;\n} SYSTEM_THREAD_INFORMATION2, *PSYSTEM_THREAD_INFORMATION2;\n\n#define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2\n#define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2\n\ntypedef struct _SYSTEM_PROCESS_INFORMATION2 {\n    ULONG NextEntryOffset;                      // The address of the previous item plus the value in the NextEntryOffset member. For the last item in the array, NextEntryOffset is 0.\n    ULONG NumberOfThreads;                      // The NumberOfThreads member contains the number of threads in the process.\n    ULONGLONG WorkingSetPrivateSize;            // The total private memory that a process currently has allocated and is physically resident in memory. // since VISTA\n    ULONG HardFaultCount;                       // The total number of hard faults for data from disk rather than from in-memory pages. // since WIN7\n    ULONG NumberOfThreadsHighWatermark;         // The peak number of threads that were running at any given point in time, indicative of potential performance bottlenecks related to thread management.\n    ULONGLONG CycleTime;                        // The sum of the cycle time of all threads in the process.\n    LARGE_INTEGER CreateTime;                   // Number of 100-nanosecond intervals since the creation time of the process. Not updated during system timezone changes.\n    LARGE_INTEGER UserTime;                     // Number of 100-nanosecond intervals the process has executed in user mode.\n    LARGE_INTEGER KernelTime;                   // Number of 100-nanosecond intervals the process has executed in kernel mode.\n    UNICODE_STRING ImageName;                   // The file name of the executable image.\n    KPRIORITY BasePriority;                     // The starting priority of the process.\n    HANDLE UniqueProcessId;                     // The identifier of the process.\n    HANDLE InheritedFromUniqueProcessId;        // The identifier of the process that created this process. Not updated and incorrectly refers to processes with recycled identifiers.\n    ULONG HandleCount;                          // The current number of open handles used by the process.\n    ULONG SessionId;                            // The identifier of the Remote Desktop Services session under which the specified process is running.\n    ULONG_PTR UniqueProcessKey;                 // since VISTA (requires SystemExtendedProcessInformation)\n    SIZE_T PeakVirtualSize;                     // The peak size, in bytes, of the virtual memory used by the process.\n    SIZE_T VirtualSize;                         // The current size, in bytes, of virtual memory used by the process.\n    ULONG PageFaultCount;                       // The total number of page faults for data that is not currently in memory. The value wraps around to zero on average 24 hours.\n    SIZE_T PeakWorkingSetSize;                  // The peak size, in kilobytes, of the working set of the process.\n    SIZE_T WorkingSetSize;                      // The number of pages visible to the process in physical memory. These pages are resident and available for use without triggering a page fault.\n    SIZE_T QuotaPeakPagedPoolUsage;             // The peak quota charged to the process for pool usage, in bytes.\n    SIZE_T QuotaPagedPoolUsage;                 // The quota charged to the process for paged pool usage, in bytes.\n    SIZE_T QuotaPeakNonPagedPoolUsage;          // The peak quota charged to the process for nonpaged pool usage, in bytes.\n    SIZE_T QuotaNonPagedPoolUsage;              // The current quota charged to the process for nonpaged pool usage.\n    SIZE_T PagefileUsage;                       // The total number of bytes of page file storage in use by the process.\n    SIZE_T PeakPagefileUsage;                   // The maximum number of bytes of page-file storage used by the process.\n    SIZE_T PrivatePageCount;                    // The number of memory pages allocated for the use by the process.\n    LARGE_INTEGER ReadOperationCount;           // The total number of read operations performed.\n    LARGE_INTEGER WriteOperationCount;          // The total number of write operations performed.\n    LARGE_INTEGER OtherOperationCount;          // The total number of I/O operations performed other than read and write operations.\n    LARGE_INTEGER ReadTransferCount;            // The total number of bytes read during a read operation.\n    LARGE_INTEGER WriteTransferCount;           // The total number of bytes written during a write operation.\n    LARGE_INTEGER OtherTransferCount;           // The total number of bytes transferred during operations other than read and write operations.\n    SYSTEM_THREAD_INFORMATION Threads[1];       // This type is not defined in the structure but was added for convenience.\n} SYSTEM_PROCESS_INFORMATION2, *PSYSTEM_PROCESS_INFORMATION2;\n\n#define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2\n#define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2\n\n// cpu_freq()\ntypedef struct _PROCESSOR_POWER_INFORMATION {\n   ULONG Number;\n   ULONG MaxMhz;\n   ULONG CurrentMhz;\n   ULONG MhzLimit;\n   ULONG MaxIdleState;\n   ULONG CurrentIdleState;\n} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;\n\n#ifndef __IPHLPAPI_H__\ntypedef struct in6_addr {\n    union {\n        UCHAR Byte[16];\n        USHORT Word[8];\n    } u;\n} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR;\n#endif\n\n// PEB / cmdline(), cwd(), environ()\ntypedef struct {\n    BYTE Reserved1[16];\n    PVOID Reserved2[5];\n    UNICODE_STRING CurrentDirectoryPath;\n    PVOID CurrentDirectoryHandle;\n    UNICODE_STRING DllPath;\n    UNICODE_STRING ImagePathName;\n    UNICODE_STRING CommandLine;\n    LPCWSTR env;\n} RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_;\n\n// users()\ntypedef struct _WTS_SESSION_INFOW {\n    DWORD SessionId;             // session id\n    LPWSTR pWinStationName;      // name of WinStation this session is\n                                 // connected to\n    WTS_CONNECTSTATE_CLASS State; // connection state (see enum)\n} WTS_SESSION_INFOW, * PWTS_SESSION_INFOW;\n\n#define PWTS_SESSION_INFO PWTS_SESSION_INFOW\n\ntypedef struct _WTS_CLIENT_ADDRESS {\n    DWORD AddressFamily;  // AF_INET, AF_INET6, AF_IPX, AF_NETBIOS, AF_UNSPEC\n    BYTE  Address[20];    // client network address\n} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS;\n\ntypedef struct _WTSINFOW {\n    WTS_CONNECTSTATE_CLASS State; // connection state (see enum)\n    DWORD SessionId;             // session id\n    DWORD IncomingBytes;\n    DWORD OutgoingBytes;\n    DWORD IncomingFrames;\n    DWORD OutgoingFrames;\n    DWORD IncomingCompressedBytes;\n    DWORD OutgoingCompressedBytes;\n    WCHAR WinStationName[WINSTATIONNAME_LENGTH];\n    WCHAR Domain[DOMAIN_LENGTH];\n    WCHAR UserName[USERNAME_LENGTH + 1];// name of WinStation this session is\n                                 // connected to\n    LARGE_INTEGER ConnectTime;\n    LARGE_INTEGER DisconnectTime;\n    LARGE_INTEGER LastInputTime;\n    LARGE_INTEGER LogonTime;\n    LARGE_INTEGER CurrentTime;\n\n} WTSINFOW, * PWTSINFOW;\n\n#define PWTSINFO PWTSINFOW\n\n// cpu_count_cores()\n#if (_WIN32_WINNT < 0x0601)  // Windows < 7 (Vista and XP)\ntypedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {\n    LOGICAL_PROCESSOR_RELATIONSHIP Relationship;\n    DWORD Size;\n    _ANONYMOUS_UNION\n    union {\n        PROCESSOR_RELATIONSHIP Processor;\n        NUMA_NODE_RELATIONSHIP NumaNode;\n        CACHE_RELATIONSHIP Cache;\n        GROUP_RELATIONSHIP Group;\n    } DUMMYUNIONNAME;\n} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, \\\n    *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;\n#endif\n\n// memory_uss()\ntypedef struct _MEMORY_WORKING_SET_BLOCK {\n    ULONG_PTR Protection : 5;\n    ULONG_PTR ShareCount : 3;\n    ULONG_PTR Shared : 1;\n    ULONG_PTR Node : 3;\n#ifdef _WIN64\n    ULONG_PTR VirtualPage : 52;\n#else\n    ULONG VirtualPage : 20;\n#endif\n} MEMORY_WORKING_SET_BLOCK, *PMEMORY_WORKING_SET_BLOCK;\n\n// memory_uss()\ntypedef struct _MEMORY_WORKING_SET_INFORMATION {\n    ULONG_PTR NumberOfEntries;\n    MEMORY_WORKING_SET_BLOCK WorkingSetInfo[1];\n} MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION;\n\n// memory_uss()\ntypedef struct _PSUTIL_PROCESS_WS_COUNTERS {\n    SIZE_T NumberOfPages;\n    SIZE_T NumberOfPrivatePages;\n    SIZE_T NumberOfSharedPages;\n    SIZE_T NumberOfShareablePages;\n} PSUTIL_PROCESS_WS_COUNTERS, *PPSUTIL_PROCESS_WS_COUNTERS;\n\n// exe()\ntypedef struct _SYSTEM_PROCESS_ID_INFORMATION {\n    HANDLE ProcessId;\n    UNICODE_STRING ImageName;\n} SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION;\n\n// ====================================================================\n// PEB structs for cmdline(), cwd(), environ()\n// ====================================================================\n\n#ifdef _WIN64\ntypedef struct {\n    BYTE Reserved1[2];\n    BYTE BeingDebugged;\n    BYTE Reserved2[21];\n    PVOID LoaderData;\n    PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;\n    // more fields...\n} PEB_;\n\n// When we are a 64 bit process accessing a 32 bit (WoW64)\n// process we need to use the 32 bit structure layout.\ntypedef struct {\n    USHORT Length;\n    USHORT MaxLength;\n    DWORD Buffer;\n} UNICODE_STRING32;\n\ntypedef struct {\n    BYTE Reserved1[16];\n    DWORD Reserved2[5];\n    UNICODE_STRING32 CurrentDirectoryPath;\n    DWORD CurrentDirectoryHandle;\n    UNICODE_STRING32 DllPath;\n    UNICODE_STRING32 ImagePathName;\n    UNICODE_STRING32 CommandLine;\n    DWORD env;\n} RTL_USER_PROCESS_PARAMETERS32;\n\ntypedef struct {\n    BYTE Reserved1[2];\n    BYTE BeingDebugged;\n    BYTE Reserved2[1];\n    DWORD Reserved3[2];\n    DWORD Ldr;\n    DWORD ProcessParameters;\n    // more fields...\n} PEB32;\n#else  // ! _WIN64\ntypedef struct {\n    BYTE Reserved1[2];\n    BYTE BeingDebugged;\n    BYTE Reserved2[1];\n    PVOID Reserved3[2];\n    PVOID Ldr;\n    PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;\n    // more fields...\n} PEB_;\n\n// When we are a 32 bit (WoW64) process accessing a 64 bit process\n// we need to use the 64 bit structure layout and a special function\n// to read its memory.\ntypedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(\n    HANDLE ProcessHandle,\n    PVOID64 BaseAddress,\n    PVOID Buffer,\n    ULONG64 Size,\n    PULONG64 NumberOfBytesRead);\n\ntypedef struct {\n    PVOID Reserved1[2];\n    PVOID64 PebBaseAddress;\n    PVOID Reserved2[4];\n    PVOID UniqueProcessId[2];\n    PVOID Reserved3[2];\n} PROCESS_BASIC_INFORMATION64;\n\ntypedef struct {\n    USHORT Length;\n    USHORT MaxLength;\n    PVOID64 Buffer;\n} UNICODE_STRING64;\n\ntypedef struct {\n    BYTE Reserved1[16];\n    PVOID64 Reserved2[5];\n    UNICODE_STRING64 CurrentDirectoryPath;\n    PVOID64 CurrentDirectoryHandle;\n    UNICODE_STRING64 DllPath;\n    UNICODE_STRING64 ImagePathName;\n    UNICODE_STRING64 CommandLine;\n    PVOID64 env;\n} RTL_USER_PROCESS_PARAMETERS64;\n\ntypedef struct {\n    BYTE Reserved1[2];\n    BYTE BeingDebugged;\n    BYTE Reserved2[21];\n    PVOID64 LoaderData;\n    PVOID64 ProcessParameters;\n    // more fields...\n} PEB64;\n#endif  // _WIN64\n\n// ================================================================\n// Type defs for modules loaded at runtime.\n// ================================================================\n\nBOOL (WINAPI *_GetLogicalProcessorInformationEx) (\n    LOGICAL_PROCESSOR_RELATIONSHIP relationship,\n    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,\n    PDWORD ReturnLength);\n\n#define GetLogicalProcessorInformationEx _GetLogicalProcessorInformationEx\n\nBOOLEAN (WINAPI * _WinStationQueryInformationW) (\n    HANDLE ServerHandle,\n    ULONG SessionId,\n    WINSTATIONINFOCLASS WinStationInformationClass,\n    PVOID pWinStationInformation,\n    ULONG WinStationInformationLength,\n    PULONG pReturnLength);\n\n#define WinStationQueryInformationW _WinStationQueryInformationW\n\nNTSTATUS (NTAPI *_NtQueryInformationProcess) (\n    HANDLE ProcessHandle,\n    DWORD ProcessInformationClass,\n    PVOID ProcessInformation,\n    DWORD ProcessInformationLength,\n    PDWORD ReturnLength);\n\n#define NtQueryInformationProcess _NtQueryInformationProcess\n\nNTSTATUS (NTAPI *_NtQuerySystemInformation) (\n    ULONG SystemInformationClass,\n    PVOID SystemInformation,\n    ULONG SystemInformationLength,\n    PULONG ReturnLength);\n\n#define NtQuerySystemInformation _NtQuerySystemInformation\n\nNTSTATUS (NTAPI *_NtSetInformationProcess) (\n    HANDLE ProcessHandle,\n    DWORD ProcessInformationClass,\n    PVOID ProcessInformation,\n    DWORD ProcessInformationLength);\n\n#define NtSetInformationProcess _NtSetInformationProcess\n\nPSTR (NTAPI * _RtlIpv4AddressToStringA) (\n    struct in_addr *Addr,\n    PSTR S);\n\n#define RtlIpv4AddressToStringA _RtlIpv4AddressToStringA\n\nPSTR (NTAPI * _RtlIpv6AddressToStringA) (\n    struct in6_addr *Addr,\n    PSTR P);\n\n#define RtlIpv6AddressToStringA _RtlIpv6AddressToStringA\n\nDWORD (WINAPI * _GetExtendedTcpTable) (\n    PVOID pTcpTable,\n    PDWORD pdwSize,\n    BOOL bOrder,\n    ULONG ulAf,\n    TCP_TABLE_CLASS TableClass,\n    ULONG Reserved);\n\n#define GetExtendedTcpTable _GetExtendedTcpTable\n\nDWORD (WINAPI * _GetExtendedUdpTable) (\n    PVOID pUdpTable,\n    PDWORD pdwSize,\n    BOOL bOrder,\n    ULONG ulAf,\n    UDP_TABLE_CLASS TableClass,\n    ULONG Reserved);\n\n#define GetExtendedUdpTable _GetExtendedUdpTable\n\nDWORD (CALLBACK *_GetActiveProcessorCount) (\n    WORD GroupNumber);\n\n#define GetActiveProcessorCount _GetActiveProcessorCount\n\nBOOL(CALLBACK *_WTSQuerySessionInformationW) (\n    HANDLE hServer,\n    DWORD SessionId,\n    WTS_INFO_CLASS WTSInfoClass,\n    LPWSTR* ppBuffer,\n    DWORD* pBytesReturned\n    );\n\n#define WTSQuerySessionInformationW _WTSQuerySessionInformationW\n\nBOOL(CALLBACK *_WTSEnumerateSessionsW)(\n    HANDLE hServer,\n    DWORD Reserved,\n    DWORD Version,\n    PWTS_SESSION_INFO* ppSessionInfo,\n    DWORD* pCount\n    );\n\n#define WTSEnumerateSessionsW _WTSEnumerateSessionsW\n\nVOID(CALLBACK *_WTSFreeMemory)(\n    IN PVOID pMemory\n    );\n\n#define WTSFreeMemory _WTSFreeMemory\n\nULONGLONG (CALLBACK *_GetTickCount64) (\n    void);\n\n#define GetTickCount64 _GetTickCount64\n\nVOID(CALLBACK *_QueryInterruptTime) (\n    PULONGLONG lpInterruptTime\n);\n\n#define QueryInterruptTime _QueryInterruptTime\n\nNTSTATUS (NTAPI *_NtQueryObject) (\n    HANDLE Handle,\n    OBJECT_INFORMATION_CLASS ObjectInformationClass,\n    PVOID ObjectInformation,\n    ULONG ObjectInformationLength,\n    PULONG ReturnLength);\n\n#define NtQueryObject _NtQueryObject\n\nNTSTATUS (WINAPI *_RtlGetVersion) (\n    PRTL_OSVERSIONINFOW lpVersionInformation\n);\n\n#define RtlGetVersion _RtlGetVersion\n\nNTSTATUS (WINAPI *_NtResumeProcess) (\n    HANDLE hProcess\n);\n\n#define NtResumeProcess _NtResumeProcess\n\nNTSTATUS (WINAPI *_NtSuspendProcess) (\n    HANDLE hProcess\n);\n\n#define NtSuspendProcess _NtSuspendProcess\n\nNTSTATUS (NTAPI *_NtQueryVirtualMemory) (\n    HANDLE ProcessHandle,\n    PVOID BaseAddress,\n    int MemoryInformationClass,\n    PVOID MemoryInformation,\n    SIZE_T MemoryInformationLength,\n    PSIZE_T ReturnLength\n);\n\n#define NtQueryVirtualMemory _NtQueryVirtualMemory\n\nULONG (WINAPI *_RtlNtStatusToDosErrorNoTeb) (\n    NTSTATUS status\n);\n\n#define RtlNtStatusToDosErrorNoTeb _RtlNtStatusToDosErrorNoTeb\n\n#endif // __NTEXTAPI_H__\n// clang-format on\n"
  },
  {
    "path": "psutil/arch/windows/pids.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <psapi.h>\n\n#include \"../../arch/all/init.h\"\n\n\nint\n_psutil_pids(DWORD **pids_array, int *pids_count) {\n    DWORD *proc_array = NULL;\n    DWORD proc_array_bytes;\n    int proc_array_sz = 0;\n    DWORD enum_return_bytes = 0;\n\n    *pids_array = NULL;\n    *pids_count = 0;\n\n    do {\n        proc_array_sz += 1024;\n        if (proc_array != NULL)\n            free(proc_array);\n\n        proc_array_bytes = proc_array_sz * sizeof(DWORD);\n        proc_array = malloc(proc_array_bytes);\n        if (proc_array == NULL) {\n            PyErr_NoMemory();\n            return -1;\n        }\n\n        if (!EnumProcesses(proc_array, proc_array_bytes, &enum_return_bytes)) {\n            free(proc_array);\n            psutil_oserror();\n            return -1;\n        }\n\n        // Retry if our buffer was too small.\n    } while (enum_return_bytes == proc_array_bytes);\n\n    *pids_count = (int)(enum_return_bytes / sizeof(DWORD));\n    *pids_array = proc_array;\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/windows/proc.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\n * Process related functions. Original code was moved in here from\n * psutil/_psutil_windows.c in 2023. For reference, here's the GIT blame\n * history before the move:\n * https://github.com/giampaolo/psutil/blame/59504a5/psutil/_psutil_windows.c\n */\n\n// Fixes clash between winsock2.h and windows.h\n#define WIN32_LEAN_AND_MEAN\n\n#include <Python.h>\n#include <windows.h>\n#include <Psapi.h>  // memory_info(), memory_maps()\n#include <signal.h>\n#include <tlhelp32.h>  // threads(), PROCESSENTRY32\n\n// Link with Iphlpapi.lib\n#pragma comment(lib, \"IPHLPAPI.lib\")\n\n#include \"../../arch/all/init.h\"\n\n\n// Raised by Process.wait().\nPyObject *TimeoutExpired;\nPyObject *TimeoutAbandoned;\n\n\n/*\n * Return 1 if PID exists in the current process list, else 0.\n */\nPyObject *\npsutil_pid_exists(PyObject *self, PyObject *args) {\n    DWORD pid;\n    int status;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    status = psutil_pid_is_running(pid);\n    if (-1 == status)\n        return NULL;  // exception raised in psutil_pid_is_running()\n    return PyBool_FromLong(status);\n}\n\n\n/*\n * Kill a process given its PID.\n */\nPyObject *\npsutil_proc_kill(PyObject *self, PyObject *args) {\n    HANDLE hProcess;\n    DWORD pid;\n    DWORD access = PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (pid == 0)\n        return psutil_oserror_ad(\"automatically set for PID 0\");\n\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (hProcess == NULL) {\n        return NULL;\n    }\n\n    if (!TerminateProcess(hProcess, SIGTERM)) {\n        // ERROR_ACCESS_DENIED may happen if the process already died. See:\n        // https://github.com/giampaolo/psutil/issues/1099\n        // http://bugs.python.org/issue14252\n        if (GetLastError() != ERROR_ACCESS_DENIED) {\n            psutil_oserror_wsyscall(\"TerminateProcess\");\n            return NULL;\n        }\n    }\n\n    CloseHandle(hProcess);\n    Py_RETURN_NONE;\n}\n\n\n/*\n * Wait for process to terminate and return its exit code.\n */\nPyObject *\npsutil_proc_wait(PyObject *self, PyObject *args) {\n    HANDLE hProcess;\n    DWORD ExitCode;\n    DWORD retVal;\n    DWORD pid;\n    long timeout;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"l\", &pid, &timeout))\n        return NULL;\n    if (pid == 0)\n        return psutil_oserror_ad(\"automatically set for PID 0\");\n\n    hProcess = OpenProcess(\n        SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid\n    );\n    if (hProcess == NULL) {\n        if (GetLastError() == ERROR_INVALID_PARAMETER) {\n            // no such process; we do not want to raise NSP but\n            // return None instead.\n            Py_RETURN_NONE;\n        }\n        else {\n            psutil_oserror_wsyscall(\"OpenProcess\");\n            return NULL;\n        }\n    }\n\n    // wait until the process has terminated\n    Py_BEGIN_ALLOW_THREADS\n    retVal = WaitForSingleObject(hProcess, timeout);\n    Py_END_ALLOW_THREADS\n\n    // handle return code\n    if (retVal == WAIT_FAILED) {\n        psutil_oserror_wsyscall(\"WaitForSingleObject\");\n        CloseHandle(hProcess);\n        return NULL;\n    }\n    if (retVal == WAIT_TIMEOUT) {\n        PyErr_SetString(\n            TimeoutExpired, \"WaitForSingleObject() returned WAIT_TIMEOUT\"\n        );\n        CloseHandle(hProcess);\n        return NULL;\n    }\n    if (retVal == WAIT_ABANDONED) {\n        psutil_debug(\"WaitForSingleObject() -> WAIT_ABANDONED\");\n        PyErr_SetString(\n            TimeoutAbandoned, \"WaitForSingleObject() returned WAIT_ABANDONED\"\n        );\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    // WaitForSingleObject() returned WAIT_OBJECT_0. It means the\n    // process is gone so we can get its process exit code. The PID\n    // may still stick around though but we'll handle that from Python.\n    if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {\n        psutil_oserror_wsyscall(\"GetExitCodeProcess\");\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    CloseHandle(hProcess);\n\n    return PyLong_FromLong((long)ExitCode);\n}\n\n\n/*\n * Return a Python tuple (user_time, kernel_time)\n */\nPyObject *\npsutil_proc_times(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    FILETIME ftCreate, ftExit, ftKernel, ftUser;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n\n    if (hProcess == NULL)\n        return NULL;\n    if (!GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {\n        if (GetLastError() == ERROR_ACCESS_DENIED) {\n            // usually means the process has died so we throw a NoSuchProcess\n            // here\n            psutil_oserror_nsp(\"GetProcessTimes -> ERROR_ACCESS_DENIED\");\n        }\n        else {\n            psutil_oserror();\n        }\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    CloseHandle(hProcess);\n\n    /*\n     * User and kernel times are represented as a FILETIME structure\n     * which contains a 64-bit value representing the number of\n     * 100-nanosecond intervals since January 1, 1601 (UTC):\n     * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx\n     * To convert it into a float representing the seconds that the\n     * process has executed in user/kernel mode I borrowed the code\n     * below from Python's Modules/posixmodule.c\n     */\n    return Py_BuildValue(\n        \"(ddd)\",\n        (double)(ftUser.dwHighDateTime * HI_T + ftUser.dwLowDateTime * LO_T),\n        (double)(ftKernel.dwHighDateTime * HI_T + ftKernel.dwLowDateTime * LO_T\n        ),\n        psutil_FiletimeToUnixTime(ftCreate)\n    );\n}\n\n\n/*\n * Return process executable path. Works for all processes regardless of\n * privilege. NtQuerySystemInformation has some sort of internal cache,\n * since it succeeds even when a process is gone (but not if a PID never\n * existed).\n */\nPyObject *\npsutil_proc_exe(PyObject *self, PyObject *args) {\n    DWORD pid;\n    NTSTATUS status;\n    PVOID buffer = NULL;\n    ULONG bufferSize = 0x104 * 2;  // WIN_MAX_PATH * sizeof(wchar_t)\n    SYSTEM_PROCESS_ID_INFORMATION processIdInfo;\n    PyObject *py_exe;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    if (pid == 0)\n        return psutil_oserror_ad(\"automatically set for PID 0\");\n\n    // ...because NtQuerySystemInformation can succeed for terminated\n    // processes.\n    if (psutil_pid_is_running(pid) == 0)\n        return psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n\n    buffer = MALLOC_ZERO(bufferSize);\n    if (!buffer) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n\n    processIdInfo.ProcessId = (HANDLE)(ULONG_PTR)pid;\n    processIdInfo.ImageName.Length = 0;\n    processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize;\n    processIdInfo.ImageName.Buffer = buffer;\n\n    status = NtQuerySystemInformation(\n        SystemProcessIdInformation,\n        &processIdInfo,\n        sizeof(SYSTEM_PROCESS_ID_INFORMATION),\n        NULL\n    );\n\n    if ((status == STATUS_INFO_LENGTH_MISMATCH)\n        && (processIdInfo.ImageName.MaximumLength <= bufferSize))\n    {\n        // Required length was NOT stored in MaximumLength (WOW64 issue).\n        ULONG maxBufferSize = 0x7FFF * 2;  // NTFS_MAX_PATH * sizeof(wchar_t)\n        do {\n            // Iteratively double the size of the buffer up to maxBufferSize\n            bufferSize *= 2;\n            FREE(buffer);\n            buffer = MALLOC_ZERO(bufferSize);\n            if (!buffer) {\n                PyErr_NoMemory();\n                return NULL;\n            }\n\n            processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize;\n            processIdInfo.ImageName.Buffer = buffer;\n\n            status = NtQuerySystemInformation(\n                SystemProcessIdInformation,\n                &processIdInfo,\n                sizeof(SYSTEM_PROCESS_ID_INFORMATION),\n                NULL\n            );\n        } while ((status == STATUS_INFO_LENGTH_MISMATCH)\n                 && (bufferSize <= maxBufferSize));\n    }\n    else if (status == STATUS_INFO_LENGTH_MISMATCH) {\n        // Required length is stored in MaximumLength.\n        FREE(buffer);\n        buffer = MALLOC_ZERO(processIdInfo.ImageName.MaximumLength);\n        if (!buffer) {\n            PyErr_NoMemory();\n            return NULL;\n        }\n\n        processIdInfo.ImageName.Buffer = buffer;\n\n        status = NtQuerySystemInformation(\n            SystemProcessIdInformation,\n            &processIdInfo,\n            sizeof(SYSTEM_PROCESS_ID_INFORMATION),\n            NULL\n        );\n    }\n\n    if (!NT_SUCCESS(status)) {\n        FREE(buffer);\n        if (psutil_pid_is_running(pid) == 0)\n            psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n        else\n            psutil_SetFromNTStatusErr(status, \"NtQuerySystemInformation\");\n        return NULL;\n    }\n\n    if (processIdInfo.ImageName.Buffer == NULL) {\n        // Happens for PID 4.\n        py_exe = PyUnicode_FromString(\"\");\n    }\n    else {\n        py_exe = PyUnicode_FromWideChar(\n            processIdInfo.ImageName.Buffer, processIdInfo.ImageName.Length / 2\n        );\n    }\n    FREE(buffer);\n    return py_exe;\n}\n\n\n/*\n * Return process memory information as a Python dict.\n */\nPyObject *\npsutil_proc_memory_info(PyObject *self, PyObject *args) {\n    HANDLE hProcess;\n    DWORD pid;\n    PROCESS_MEMORY_COUNTERS_EX cnt;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (NULL == hProcess)\n        goto error;\n\n    if (!GetProcessMemoryInfo(\n            hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt, sizeof(cnt)\n        ))\n    {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        goto error;\n    }\n    CloseHandle(hProcess);\n\n    // clang-format off\n    if (!pydict_add(dict, \"PageFaultCount\", \"K\", (ULONGLONG)cnt.PageFaultCount)) goto error;\n    if (!pydict_add(dict, \"PeakWorkingSetSize\", \"K\", (ULONGLONG)cnt.PeakWorkingSetSize)) goto error;\n    if (!pydict_add(dict, \"WorkingSetSize\", \"K\", (ULONGLONG)cnt.WorkingSetSize)) goto error;\n    if (!pydict_add(dict, \"QuotaPeakPagedPoolUsage\", \"K\", (ULONGLONG)cnt.QuotaPeakPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"QuotaPagedPoolUsage\", \"K\", (ULONGLONG)cnt.QuotaPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"QuotaPeakNonPagedPoolUsage\", \"K\", (ULONGLONG)cnt.QuotaPeakNonPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"QuotaNonPagedPoolUsage\", \"K\", (ULONGLONG)cnt.QuotaNonPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"PagefileUsage\", \"K\", (ULONGLONG)cnt.PagefileUsage)) goto error;\n    if (!pydict_add(dict, \"PeakPagefileUsage\", \"K\", (ULONGLONG)cnt.PeakPagefileUsage)) goto error;\n    if (!pydict_add(dict, \"PrivateUsage\", \"K\", (ULONGLONG)cnt.PrivateUsage)) goto error;\n    // clang-format on\n    return dict;\n\nerror:\n    Py_DECREF(dict);\n    return NULL;\n}\n\n\nstatic int\npsutil_GetProcWsetInformation(\n    DWORD pid, HANDLE hProcess, PMEMORY_WORKING_SET_INFORMATION *wSetInfo\n) {\n    NTSTATUS status;\n    PVOID buffer;\n    SIZE_T bufferSize;\n\n    bufferSize = 0x8000;\n    buffer = MALLOC_ZERO(bufferSize);\n    if (!buffer) {\n        PyErr_NoMemory();\n        return -1;\n    }\n\n    while ((status = NtQueryVirtualMemory(\n                hProcess,\n                NULL,\n                MemoryWorkingSetInformation,\n                buffer,\n                bufferSize,\n                NULL\n            ))\n           == STATUS_INFO_LENGTH_MISMATCH)\n    {\n        FREE(buffer);\n        bufferSize *= 2;\n        // Fail if we're resizing the buffer to something very large.\n        if (bufferSize > 256 * 1024 * 1024) {\n            psutil_runtime_error(\"NtQueryVirtualMemory bufsize is too large\");\n            return -1;\n        }\n        buffer = MALLOC_ZERO(bufferSize);\n        if (!buffer) {\n            PyErr_NoMemory();\n            return -1;\n        }\n    }\n\n    if (!NT_SUCCESS(status)) {\n        if (status == STATUS_ACCESS_DENIED) {\n            psutil_oserror_ad(\"NtQueryVirtualMemory -> STATUS_ACCESS_DENIED\");\n        }\n        else if (psutil_pid_is_running(pid) == 0) {\n            psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n        }\n        else {\n            PyErr_Clear();\n            psutil_SetFromNTStatusErr(\n                status, \"NtQueryVirtualMemory(MemoryWorkingSetInformation)\"\n            );\n        }\n        HeapFree(GetProcessHeap(), 0, buffer);\n        return -1;\n    }\n\n    *wSetInfo = (PMEMORY_WORKING_SET_INFORMATION)buffer;\n    return 0;\n}\n\n\n/*\n * Returns the USS of the process.\n * Reference:\n * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/\n *     nsMemoryReporterManager.cpp\n */\nPyObject *\npsutil_proc_memory_uss(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    PSUTIL_PROCESS_WS_COUNTERS wsCounters;\n    PMEMORY_WORKING_SET_INFORMATION wsInfo;\n    ULONG_PTR i;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_INFORMATION);\n    if (hProcess == NULL)\n        return NULL;\n\n    if (psutil_GetProcWsetInformation(pid, hProcess, &wsInfo) != 0) {\n        CloseHandle(hProcess);\n        return NULL;\n    }\n    memset(&wsCounters, 0, sizeof(PSUTIL_PROCESS_WS_COUNTERS));\n\n    for (i = 0; i < wsInfo->NumberOfEntries; i++) {\n        // This is what ProcessHacker does.\n        /*\n        wsCounters.NumberOfPages++;\n        if (wsInfo->WorkingSetInfo[i].ShareCount > 1)\n            wsCounters.NumberOfSharedPages++;\n        if (wsInfo->WorkingSetInfo[i].ShareCount == 0)\n            wsCounters.NumberOfPrivatePages++;\n        if (wsInfo->WorkingSetInfo[i].Shared)\n            wsCounters.NumberOfShareablePages++;\n        */\n\n        // This is what we do: count shared pages that only one process\n        // is using as private (USS).\n        if (!wsInfo->WorkingSetInfo[i].Shared\n            || wsInfo->WorkingSetInfo[i].ShareCount <= 1)\n        {\n            wsCounters.NumberOfPrivatePages++;\n        }\n    }\n\n    HeapFree(GetProcessHeap(), 0, wsInfo);\n    CloseHandle(hProcess);\n\n    return Py_BuildValue(\"I\", wsCounters.NumberOfPrivatePages);\n}\n\n\n/*\n * Resume or suspends a process\n */\nPyObject *\npsutil_proc_suspend_or_resume(PyObject *self, PyObject *args) {\n    DWORD pid;\n    NTSTATUS status;\n    HANDLE hProcess;\n    DWORD access = PROCESS_SUSPEND_RESUME | PROCESS_QUERY_LIMITED_INFORMATION;\n    PyObject *suspend;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"O\", &pid, &suspend))\n        return NULL;\n\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (hProcess == NULL)\n        return NULL;\n\n    if (PyObject_IsTrue(suspend))\n        status = NtSuspendProcess(hProcess);\n    else\n        status = NtResumeProcess(hProcess);\n\n    if (!NT_SUCCESS(status)) {\n        CloseHandle(hProcess);\n        return psutil_SetFromNTStatusErr(status, \"NtSuspend|ResumeProcess\");\n    }\n\n    CloseHandle(hProcess);\n    Py_RETURN_NONE;\n}\n\n\nPyObject *\npsutil_proc_threads(PyObject *self, PyObject *args) {\n    HANDLE hThread = NULL;\n    THREADENTRY32 te32 = {0};\n    DWORD pid;\n    int pid_return;\n    int rc;\n    FILETIME ftDummy, ftKernel, ftUser;\n    HANDLE hThreadSnap = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (pid == 0) {\n        // raise AD instead of returning 0 as procexp is able to\n        // retrieve useful information somehow\n        psutil_oserror_ad(\"forced for PID 0\");\n        goto error;\n    }\n\n    pid_return = psutil_pid_is_running(pid);\n    if (pid_return == 0) {\n        psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n        goto error;\n    }\n    if (pid_return == -1)\n        goto error;\n\n    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);\n    if (hThreadSnap == INVALID_HANDLE_VALUE) {\n        psutil_oserror_wsyscall(\"CreateToolhelp32Snapshot\");\n        goto error;\n    }\n\n    // Fill in the size of the structure before using it\n    te32.dwSize = sizeof(THREADENTRY32);\n\n    if (!Thread32First(hThreadSnap, &te32)) {\n        psutil_oserror_wsyscall(\"Thread32First\");\n        goto error;\n    }\n\n    // Walk the thread snapshot to find all threads of the process.\n    // If the thread belongs to the process, increase the counter.\n    do {\n        if (te32.th32OwnerProcessID == pid) {\n            hThread = NULL;\n            hThread = OpenThread(\n                THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID\n            );\n            if (hThread == NULL) {\n                // thread has disappeared on us\n                continue;\n            }\n\n            rc = GetThreadTimes(\n                hThread, &ftDummy, &ftDummy, &ftKernel, &ftUser\n            );\n            if (rc == 0) {\n                psutil_oserror_wsyscall(\"GetThreadTimes\");\n                goto error;\n            }\n\n            /*\n             * User and kernel times are represented as a FILETIME structure\n             * which contains a 64-bit value representing the number of\n             * 100-nanosecond intervals since January 1, 1601 (UTC):\n             * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx\n             * To convert it into a float representing the seconds that the\n             * process has executed in user/kernel mode I borrowed the code\n             * below from Python's Modules/posixmodule.c\n             */\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"kdd\",\n                    te32.th32ThreadID,\n                    (double)(ftUser.dwHighDateTime * HI_T\n                             + ftUser.dwLowDateTime * LO_T),\n                    (double)(ftKernel.dwHighDateTime * HI_T\n                             + ftKernel.dwLowDateTime * LO_T)\n                ))\n            {\n                goto error;\n            }\n\n            CloseHandle(hThread);\n        }\n    } while (Thread32Next(hThreadSnap, &te32));\n\n    CloseHandle(hThreadSnap);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_retlist);\n    if (hThread != NULL)\n        CloseHandle(hThread);\n    if (hThreadSnap != NULL)\n        CloseHandle(hThreadSnap);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_open_files(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE processHandle;\n    DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;\n    PyObject *py_retlist;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    processHandle = psutil_handle_from_pid(pid, access);\n    if (processHandle == NULL)\n        return NULL;\n\n    py_retlist = psutil_get_open_files(pid, processHandle);\n    CloseHandle(processHandle);\n    return py_retlist;\n}\n\n\nstatic PTOKEN_USER\n_psutil_user_token_from_pid(DWORD pid) {\n    HANDLE hProcess = NULL;\n    HANDLE hToken = NULL;\n    PTOKEN_USER userToken = NULL;\n    ULONG bufferSize = 0x100;\n\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (hProcess == NULL)\n        return NULL;\n\n    if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {\n        psutil_oserror_wsyscall(\"OpenProcessToken\");\n        goto error;\n    }\n\n    // Get the user SID.\n    while (1) {\n        userToken = malloc(bufferSize);\n        if (userToken == NULL) {\n            PyErr_NoMemory();\n            goto error;\n        }\n        if (!GetTokenInformation(\n                hToken, TokenUser, userToken, bufferSize, &bufferSize\n            ))\n        {\n            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n                free(userToken);\n                userToken = NULL;\n                continue;\n            }\n            else {\n                psutil_oserror_wsyscall(\"GetTokenInformation\");\n                goto error;\n            }\n        }\n        break;\n    }\n\n    CloseHandle(hProcess);\n    CloseHandle(hToken);\n    return userToken;\n\nerror:\n    if (userToken != NULL)\n        free(userToken);\n    if (hProcess != NULL)\n        CloseHandle(hProcess);\n    if (hToken != NULL)\n        CloseHandle(hToken);\n    return NULL;\n}\n\n\n/*\n * Return process username as a \"DOMAIN//USERNAME\" string.\n */\nPyObject *\npsutil_proc_username(PyObject *self, PyObject *args) {\n    DWORD pid;\n    PTOKEN_USER userToken = NULL;\n    WCHAR *userName = NULL;\n    WCHAR *domainName = NULL;\n    ULONG nameSize = 0x100;\n    ULONG domainNameSize = 0x100;\n    SID_NAME_USE nameUse;\n    PyObject *py_username = NULL;\n    PyObject *py_domain = NULL;\n    PyObject *py_tuple = NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    userToken = _psutil_user_token_from_pid(pid);\n    if (userToken == NULL)\n        return NULL;\n\n    // resolve the SID to a name\n    while (1) {\n        userName = malloc(nameSize * sizeof(WCHAR));\n        if (userName == NULL) {\n            PyErr_NoMemory();\n            goto error;\n        }\n        domainName = malloc(domainNameSize * sizeof(WCHAR));\n        if (domainName == NULL) {\n            PyErr_NoMemory();\n            goto error;\n        }\n        if (!LookupAccountSidW(\n                NULL,\n                userToken->User.Sid,\n                userName,\n                &nameSize,\n                domainName,\n                &domainNameSize,\n                &nameUse\n            ))\n        {\n            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n                free(userName);\n                free(domainName);\n                continue;\n            }\n            else if (GetLastError() == ERROR_NONE_MAPPED) {\n                // From MS doc:\n                // https://docs.microsoft.com/en-us/windows/win32/api/winbase/\n                //     nf-winbase-lookupaccountsida\n                // If the function cannot find an account name for the SID,\n                // GetLastError returns ERROR_NONE_MAPPED. This can occur if\n                // a network time-out prevents the function from finding the\n                // name. It also occurs for SIDs that have no corresponding\n                // account name, such as a logon SID that identifies a logon\n                // session.\n                psutil_oserror_ad(\"LookupAccountSidW -> ERROR_NONE_MAPPED\");\n                goto error;\n            }\n            else {\n                psutil_oserror_wsyscall(\"LookupAccountSidW\");\n                goto error;\n            }\n        }\n        break;\n    }\n\n    py_domain = PyUnicode_FromWideChar(domainName, wcslen(domainName));\n    if (!py_domain)\n        goto error;\n    py_username = PyUnicode_FromWideChar(userName, wcslen(userName));\n    if (!py_username)\n        goto error;\n    py_tuple = Py_BuildValue(\"OO\", py_domain, py_username);\n    if (!py_tuple)\n        goto error;\n    Py_DECREF(py_domain);\n    Py_DECREF(py_username);\n\n    free(userName);\n    free(domainName);\n    free(userToken);\n    return py_tuple;\n\nerror:\n    if (userName != NULL)\n        free(userName);\n    if (domainName != NULL)\n        free(domainName);\n    if (userToken != NULL)\n        free(userToken);\n    Py_XDECREF(py_domain);\n    Py_XDECREF(py_username);\n    Py_XDECREF(py_tuple);\n    return NULL;\n}\n\n\n/*\n * Get process priority as a Python integer.\n */\nPyObject *\npsutil_proc_priority_get(PyObject *self, PyObject *args) {\n    DWORD pid;\n    DWORD priority;\n    HANDLE hProcess;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (hProcess == NULL)\n        return NULL;\n\n    priority = GetPriorityClass(hProcess);\n    if (priority == 0) {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        return NULL;\n    }\n    CloseHandle(hProcess);\n    return Py_BuildValue(\"i\", priority);\n}\n\n\n/*\n * Set process priority.\n */\nPyObject *\npsutil_proc_priority_set(PyObject *self, PyObject *args) {\n    DWORD pid;\n    int priority;\n    int retval;\n    HANDLE hProcess;\n    DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"i\", &pid, &priority))\n        return NULL;\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (hProcess == NULL)\n        return NULL;\n\n    retval = SetPriorityClass(hProcess, priority);\n    if (retval == 0) {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    CloseHandle(hProcess);\n    Py_RETURN_NONE;\n}\n\n\n/*\n * Get process IO priority as a Python integer.\n */\nPyObject *\npsutil_proc_io_priority_get(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    DWORD IoPriority;\n    NTSTATUS status;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (hProcess == NULL)\n        return NULL;\n\n    status = NtQueryInformationProcess(\n        hProcess, ProcessIoPriority, &IoPriority, sizeof(DWORD), NULL\n    );\n\n    CloseHandle(hProcess);\n    if (!NT_SUCCESS(status))\n        return psutil_SetFromNTStatusErr(status, \"NtQueryInformationProcess\");\n    return Py_BuildValue(\"i\", IoPriority);\n}\n\n\n/*\n * Set process IO priority.\n */\nPyObject *\npsutil_proc_io_priority_set(PyObject *self, PyObject *args) {\n    DWORD pid;\n    DWORD prio;\n    HANDLE hProcess;\n    NTSTATUS status;\n    DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"i\", &pid, &prio))\n        return NULL;\n\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (hProcess == NULL)\n        return NULL;\n\n    status = NtSetInformationProcess(\n        hProcess, ProcessIoPriority, (PVOID)&prio, sizeof(DWORD)\n    );\n\n    CloseHandle(hProcess);\n    if (!NT_SUCCESS(status))\n        return psutil_SetFromNTStatusErr(status, \"NtSetInformationProcess\");\n    Py_RETURN_NONE;\n}\n\n\n/*\n * Return a Python tuple referencing process I/O counters.\n */\nPyObject *\npsutil_proc_io_counters(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    IO_COUNTERS IoCounters;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (NULL == hProcess)\n        return NULL;\n\n    if (!GetProcessIoCounters(hProcess, &IoCounters)) {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    CloseHandle(hProcess);\n    return Py_BuildValue(\n        \"(KKKKKK)\",\n        IoCounters.ReadOperationCount,\n        IoCounters.WriteOperationCount,\n        IoCounters.ReadTransferCount,\n        IoCounters.WriteTransferCount,\n        IoCounters.OtherOperationCount,\n        IoCounters.OtherTransferCount\n    );\n}\n\n\n/*\n * Return process CPU affinity as a bitmask\n */\nPyObject *\npsutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    DWORD_PTR proc_mask;\n    DWORD_PTR system_mask;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (hProcess == NULL) {\n        return NULL;\n    }\n    if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    CloseHandle(hProcess);\n    return Py_BuildValue(\"K\", (unsigned long long)proc_mask);\n}\n\n\n/*\n * Set process CPU affinity\n */\nPyObject *\npsutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;\n    DWORD_PTR mask;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID \"K\", &pid, &mask))\n        return NULL;\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (hProcess == NULL)\n        return NULL;\n\n    if (SetProcessAffinityMask(hProcess, mask) == 0) {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        return NULL;\n    }\n\n    CloseHandle(hProcess);\n    Py_RETURN_NONE;\n}\n\n\n// Return process page faults as a (minor, major) tuple. Uses\n// NtQuerySystemInformation(SystemProcessInformation) which returns\n// SYSTEM_PROCESS_INFORMATION. PageFaultCount is the total (soft +\n// hard), while HardFaultCount (available since Win7) tracks hard\n// (major) faults only. Minor faults are derived by subtracting the\n// two.\nPyObject *\npsutil_proc_page_faults(PyObject *self, PyObject *args) {\n    DWORD pid;\n    PSYSTEM_PROCESS_INFORMATION process;\n    PVOID buffer;\n    ULONG minor;\n    ULONG major;\n    PyObject *ret;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_get_proc_info(pid, &process, &buffer) != 0)\n        return NULL;\n    major = process->HardFaultCount;\n    minor = process->PageFaultCount - major;\n    ret = Py_BuildValue(\"(kk)\", (unsigned long)minor, (unsigned long)major);\n    free(buffer);\n    return ret;\n}\n\n\n/*\n * Return True if all process threads are in waiting/suspended state.\n */\nPyObject *\npsutil_proc_is_suspended(PyObject *self, PyObject *args) {\n    DWORD pid;\n    ULONG i;\n    PSYSTEM_PROCESS_INFORMATION process;\n    PVOID buffer;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if (psutil_get_proc_info(pid, &process, &buffer) != 0)\n        return NULL;\n    for (i = 0; i < process->NumberOfThreads; i++) {\n        if (process->Threads[i].ThreadState != Waiting\n            || process->Threads[i].WaitReason != Suspended)\n        {\n            free(buffer);\n            Py_RETURN_FALSE;\n        }\n    }\n    free(buffer);\n    Py_RETURN_TRUE;\n}\n\n\n/*\n * Return the number of handles opened by process.\n */\nPyObject *\npsutil_proc_num_handles(PyObject *self, PyObject *args) {\n    DWORD pid;\n    HANDLE hProcess;\n    DWORD handleCount;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (NULL == hProcess)\n        return NULL;\n    if (!GetProcessHandleCount(hProcess, &handleCount)) {\n        psutil_oserror();\n        CloseHandle(hProcess);\n        return NULL;\n    }\n    CloseHandle(hProcess);\n    return Py_BuildValue(\"k\", handleCount);\n}\n\n\nstatic char *\nget_region_protection_string(ULONG protection) {\n    switch (protection & 0xff) {\n        case PAGE_NOACCESS:\n            return \"\";\n        case PAGE_READONLY:\n            return \"r\";\n        case PAGE_READWRITE:\n            return \"rw\";\n        case PAGE_WRITECOPY:\n            return \"wc\";\n        case PAGE_EXECUTE:\n            return \"x\";\n        case PAGE_EXECUTE_READ:\n            return \"xr\";\n        case PAGE_EXECUTE_READWRITE:\n            return \"xrw\";\n        case PAGE_EXECUTE_WRITECOPY:\n            return \"xwc\";\n        default:\n            return \"?\";\n    }\n}\n\n\n/*\n * Return a list of process's memory mappings.\n */\nPyObject *\npsutil_proc_memory_maps(PyObject *self, PyObject *args) {\n    MEMORY_BASIC_INFORMATION basicInfo;\n    DWORD pid;\n    HANDLE hProcess = NULL;\n    PVOID baseAddress;\n    WCHAR mappedFileName[MAX_PATH];\n    LPVOID maxAddr;\n    // required by GetMappedFileNameW\n    DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_str = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (NULL == hProcess)\n        goto error;\n\n    maxAddr = PSUTIL_SYSTEM_INFO.lpMaximumApplicationAddress;\n    baseAddress = NULL;\n\n    while (VirtualQueryEx(\n        hProcess, baseAddress, &basicInfo, sizeof(MEMORY_BASIC_INFORMATION)\n    ))\n    {\n        if (baseAddress > maxAddr)\n            break;\n        if (GetMappedFileNameW(\n                hProcess, baseAddress, mappedFileName, sizeof(mappedFileName)\n            ))\n        {\n            py_str = PyUnicode_FromWideChar(\n                mappedFileName, wcslen(mappedFileName)\n            );\n            if (py_str == NULL)\n                goto error;\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(KsOI)\",\n                    (unsigned long long)baseAddress,\n                    get_region_protection_string(basicInfo.Protect),\n                    py_str,\n                    basicInfo.RegionSize\n                ))\n            {\n                goto error;\n            }\n            Py_CLEAR(py_str);\n        }\n        baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize;\n    }\n\n    CloseHandle(hProcess);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_str);\n    Py_DECREF(py_retlist);\n    if (hProcess != NULL)\n        CloseHandle(hProcess);\n    return NULL;\n}\n\n\n/*\n * Return a {pid:ppid, ...} dict for all running processes.\n */\nPyObject *\npsutil_ppid_map(PyObject *self, PyObject *args) {\n    PyObject *py_pid = NULL;\n    PyObject *py_ppid = NULL;\n    PyObject *py_retdict = PyDict_New();\n    HANDLE handle = NULL;\n    PROCESSENTRY32 pe = {0};\n    pe.dwSize = sizeof(PROCESSENTRY32);\n\n    if (py_retdict == NULL)\n        return NULL;\n    handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);\n    if (handle == INVALID_HANDLE_VALUE) {\n        psutil_oserror();\n        Py_DECREF(py_retdict);\n        return NULL;\n    }\n\n    if (Process32First(handle, &pe)) {\n        do {\n            py_pid = PyLong_FromPid(pe.th32ProcessID);\n            if (py_pid == NULL)\n                goto error;\n            py_ppid = PyLong_FromPid(pe.th32ParentProcessID);\n            if (py_ppid == NULL)\n                goto error;\n            if (PyDict_SetItem(py_retdict, py_pid, py_ppid))\n                goto error;\n            Py_CLEAR(py_pid);\n            Py_CLEAR(py_ppid);\n        } while (Process32Next(handle, &pe));\n    }\n\n    CloseHandle(handle);\n    return py_retdict;\n\nerror:\n    Py_XDECREF(py_pid);\n    Py_XDECREF(py_ppid);\n    Py_DECREF(py_retdict);\n    CloseHandle(handle);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/proc_handles.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\n * This module retrieves handles opened by a process.\n * We use NtQuerySystemInformation to enumerate them and NtQueryObject\n * to obtain the corresponding file name.\n * Since NtQueryObject hangs for certain handle types we call it in a\n * separate thread which gets killed if it doesn't complete within 100ms.\n * This is a limitation of the Windows API and ProcessHacker uses the\n * same trick: https://github.com/giampaolo/psutil/pull/597\n *\n * CREDITS: original implementation was written by Jeff Tang.\n * It was then rewritten by Giampaolo Rodola many years later.\n * Utility functions for getting the file handles and names were re-adapted\n * from the excellent ProcessHacker.\n */\n\n#include <windows.h>\n#include <Python.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#define THREAD_TIMEOUT 100  // ms\n// Global object shared between the 2 threads.\nPUNICODE_STRING globalFileName = NULL;\n\n\nstatic int\npsutil_enum_handles(PSYSTEM_HANDLE_INFORMATION_EX *handles) {\n    static ULONG initialBufferSize = 0x10000;\n    NTSTATUS status;\n    PVOID buffer;\n    ULONG bufferSize;\n\n    bufferSize = initialBufferSize;\n    buffer = MALLOC_ZERO(bufferSize);\n    if (buffer == NULL) {\n        PyErr_NoMemory();\n        return -1;\n    }\n\n    while ((status = NtQuerySystemInformation(\n                SystemExtendedHandleInformation, buffer, bufferSize, NULL\n            ))\n           == STATUS_INFO_LENGTH_MISMATCH)\n    {\n        FREE(buffer);\n        bufferSize *= 2;\n\n        // Fail if we're resizing the buffer to something very large.\n        if (bufferSize > 256 * 1024 * 1024) {\n            psutil_runtime_error(\n                \"SystemExtendedHandleInformation buffer too big\"\n            );\n            return -1;\n        }\n\n        buffer = MALLOC_ZERO(bufferSize);\n        if (buffer == NULL) {\n            PyErr_NoMemory();\n            return -1;\n        }\n    }\n\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(status, \"NtQuerySystemInformation\");\n        FREE(buffer);\n        return -1;\n    }\n\n    *handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer;\n    return 0;\n}\n\n\nstatic int\npsutil_get_filename(LPVOID lpvParam) {\n    HANDLE hFile = *((HANDLE *)lpvParam);\n    NTSTATUS status;\n    ULONG bufferSize;\n    ULONG attempts = 8;\n\n    bufferSize = 0x200;\n    globalFileName = MALLOC_ZERO(bufferSize);\n    if (globalFileName == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n\n    // Note: also this is supposed to hang, hence why we do it in here.\n    if (GetFileType(hFile) != FILE_TYPE_DISK) {\n        SetLastError(0);\n        globalFileName->Length = 0;\n        return 0;\n    }\n\n    // A loop is needed because the I/O subsystem likes to give us the\n    // wrong return lengths...\n    do {\n        status = NtQueryObject(\n            hFile,\n            ObjectNameInformation,\n            globalFileName,\n            bufferSize,\n            &bufferSize\n        );\n        if (status == STATUS_BUFFER_OVERFLOW\n            || status == STATUS_INFO_LENGTH_MISMATCH\n            || status == STATUS_BUFFER_TOO_SMALL)\n        {\n            FREE(globalFileName);\n            globalFileName = MALLOC_ZERO(bufferSize);\n            if (globalFileName == NULL) {\n                PyErr_NoMemory();\n                goto error;\n            }\n        }\n        else {\n            break;\n        }\n    } while (--attempts);\n\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(status, \"NtQuerySystemInformation\");\n        FREE(globalFileName);\n        globalFileName = NULL;\n        return 1;\n    }\n\n    return 0;\n\nerror:\n    if (globalFileName != NULL) {\n        FREE(globalFileName);\n        globalFileName = NULL;\n    }\n    return 1;\n}\n\n\nstatic DWORD\npsutil_threaded_get_filename(HANDLE hFile) {\n    DWORD dwWait;\n    HANDLE hThread;\n    DWORD threadRetValue;\n\n    hThread = CreateThread(\n        NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename, &hFile, 0, NULL\n    );\n    if (hThread == NULL) {\n        psutil_oserror_wsyscall(\"CreateThread\");\n        return -1;\n    }\n\n    // Wait for the worker thread to finish.\n    dwWait = WaitForSingleObject(hThread, THREAD_TIMEOUT);\n\n    // If the thread hangs, kill it and cleanup.\n    if (dwWait == WAIT_TIMEOUT) {\n        psutil_debug(\n            \"get handle name thread timed out after %i ms\", THREAD_TIMEOUT\n        );\n        if (TerminateThread(hThread, 0) == 0) {\n            psutil_oserror_wsyscall(\"TerminateThread\");\n            CloseHandle(hThread);\n            return -1;\n        }\n        CloseHandle(hThread);\n        return 0;\n    }\n\n    if (dwWait == WAIT_FAILED) {\n        psutil_debug(\"WaitForSingleObject -> WAIT_FAILED\");\n        if (TerminateThread(hThread, 0) == 0) {\n            psutil_oserror_wsyscall(\n                \"WaitForSingleObject -> WAIT_FAILED -> TerminateThread\"\n            );\n            CloseHandle(hThread);\n            return -1;\n        }\n        psutil_oserror_wsyscall(\"WaitForSingleObject\");\n        CloseHandle(hThread);\n        return -1;\n    }\n\n    if (GetExitCodeThread(hThread, &threadRetValue) == 0) {\n        if (TerminateThread(hThread, 0) == 0) {\n            psutil_oserror_wsyscall(\n                \"GetExitCodeThread (failed) -> TerminateThread\"\n            );\n            CloseHandle(hThread);\n            return -1;\n        }\n\n        CloseHandle(hThread);\n        psutil_oserror_wsyscall(\"GetExitCodeThread\");\n        return -1;\n    }\n    CloseHandle(hThread);\n    return threadRetValue;\n}\n\n\nPyObject *\npsutil_get_open_files(DWORD dwPid, HANDLE hProcess) {\n    PSYSTEM_HANDLE_INFORMATION_EX handlesList = NULL;\n    PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL;\n    HANDLE hFile = NULL;\n    ULONG i = 0;\n    BOOLEAN errorOccurred = FALSE;\n    PyObject *py_retlist = PyList_New(0);\n    ;\n\n    if (!py_retlist)\n        return NULL;\n\n    // Due to the use of global variables, ensure only 1 call\n    // to psutil_get_open_files() is running.\n    EnterCriticalSection(&PSUTIL_CRITICAL_SECTION);\n\n    if (psutil_enum_handles(&handlesList) != 0)\n        goto error;\n\n    for (i = 0; i < handlesList->NumberOfHandles; i++) {\n        hHandle = &handlesList->Handles[i];\n        if ((ULONG_PTR)hHandle->UniqueProcessId != dwPid)\n            continue;\n        if (!DuplicateHandle(\n                hProcess,\n                hHandle->HandleValue,\n                GetCurrentProcess(),\n                &hFile,\n                0,\n                TRUE,\n                DUPLICATE_SAME_ACCESS\n            ))\n        {\n            // Will fail if not a regular file; just skip it.\n            continue;\n        }\n\n        // This will set *globalFileName* global variable.\n        if (psutil_threaded_get_filename(hFile) != 0)\n            goto error;\n\n        if ((globalFileName != NULL) && (globalFileName->Length > 0)) {\n            if (!pylist_append_obj(\n                    py_retlist,\n                    PyUnicode_FromWideChar(\n                        globalFileName->Buffer, wcslen(globalFileName->Buffer)\n                    )\n                ))\n                goto error;\n        }\n\n        // Loop cleanup section.\n        if (globalFileName != NULL) {\n            FREE(globalFileName);\n            globalFileName = NULL;\n        }\n        CloseHandle(hFile);\n        hFile = NULL;\n    }\n\n    goto exit;\n\nerror:\n    Py_XDECREF(py_retlist);\n    errorOccurred = TRUE;\n    goto exit;\n\nexit:\n    if (hFile != NULL)\n        CloseHandle(hFile);\n    if (globalFileName != NULL) {\n        FREE(globalFileName);\n        globalFileName = NULL;\n    }\n    if (handlesList != NULL)\n        FREE(handlesList);\n\n    LeaveCriticalSection(&PSUTIL_CRITICAL_SECTION);\n    if (errorOccurred == TRUE)\n        return NULL;\n    return py_retlist;\n}\n"
  },
  {
    "path": "psutil/arch/windows/proc_info.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Helper functions related to fetching process information. Used by\n * _psutil_windows module methods.\n */\n\n#include <Python.h>\n#include <windows.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#ifndef _WIN64\ntypedef NTSTATUS(NTAPI *__NtQueryInformationProcess)(\n    HANDLE ProcessHandle,\n    DWORD ProcessInformationClass,\n    PVOID ProcessInformation,\n    DWORD ProcessInformationLength,\n    PDWORD ReturnLength\n);\n#endif\n\n#define PSUTIL_FIRST_PROCESS(Processes) \\\n    ((PSYSTEM_PROCESS_INFORMATION)(Processes))\n\n#define PSUTIL_NEXT_PROCESS(Process)                                              \\\n    (((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset                    \\\n         ? (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process)                         \\\n                                         + ((PSYSTEM_PROCESS_INFORMATION)(Process \\\n                                            ))                                    \\\n                                               ->NextEntryOffset)                 \\\n         : NULL)\n\n\n/*\n * Given a pointer into a process's memory, figure out how much\n * data can be read from it.\n */\nstatic int\npsutil_get_process_region_size(HANDLE hProcess, LPCVOID src, SIZE_T *psize) {\n    MEMORY_BASIC_INFORMATION info;\n\n    if (!VirtualQueryEx(hProcess, src, &info, sizeof(info))) {\n        psutil_oserror_wsyscall(\"VirtualQueryEx\");\n        return -1;\n    }\n\n    *psize = info.RegionSize - ((char *)src - (char *)info.BaseAddress);\n    return 0;\n}\n\n\nenum psutil_process_data_kind {\n    KIND_CMDLINE,\n    KIND_CWD,\n    KIND_ENVIRON,\n};\n\n\nstatic void\npsutil_convert_winerr(ULONG err, char *syscall) {\n    char fullmsg[8192];\n\n    if (err == ERROR_NOACCESS) {\n        str_format(fullmsg, sizeof(fullmsg), \"%s -> ERROR_NOACCESS\", syscall);\n        psutil_debug(fullmsg);\n        psutil_oserror_ad(fullmsg);\n    }\n    else {\n        psutil_oserror_wsyscall(syscall);\n    }\n}\n\n\nstatic void\npsutil_convert_ntstatus_err(NTSTATUS status, char *syscall) {\n    ULONG err;\n\n    if (NT_NTWIN32(status))\n        err = WIN32_FROM_NTSTATUS(status);\n    else\n        err = RtlNtStatusToDosErrorNoTeb(status);\n    psutil_convert_winerr(err, syscall);\n}\n\n\nstatic void\npsutil_giveup_with_ad(NTSTATUS status, char *syscall) {\n    ULONG err;\n    char fullmsg[2048];\n\n    if (NT_NTWIN32(status))\n        err = WIN32_FROM_NTSTATUS(status);\n    else\n        err = RtlNtStatusToDosErrorNoTeb(status);\n    str_format(\n        fullmsg, sizeof(fullmsg), \"%s -> %lu (%s)\", syscall, err, strerror(err)\n    );\n    psutil_debug(fullmsg);\n    psutil_oserror_ad(fullmsg);\n}\n\n\n/*\n * Get data from the process with the given pid.  The data is returned\n * in the pdata output member as a nul terminated string which must be\n * freed on success.\n * On success 0 is returned.  On error the output parameter is not touched,\n * -1 is returned, and an appropriate Python exception is set.\n */\nstatic int\npsutil_get_process_data(\n    DWORD pid, enum psutil_process_data_kind kind, WCHAR **pdata, SIZE_T *psize\n) {\n    /* This function is quite complex because there are several cases to be\n       considered:\n\n       Two cases are really simple:  we (i.e. the python interpreter) and the\n       target process are both 32 bit or both 64 bit.  In that case the memory\n       layout of the structures matches up and all is well.\n\n       When we are 64 bit and the target process is 32 bit we need to use\n       custom 32 bit versions of the structures.\n\n       When we are 32 bit and the target process is 64 bit we need to use\n       custom 64 bit version of the structures.  Also we need to use separate\n       Wow64 functions to get the information.\n\n       A few helper structs are defined above so that the compiler can handle\n       calculating the correct offsets.\n\n       Additional help also came from the following sources:\n\n         https://github.com/kohsuke/winp and\n         http://wj32.org/wp/2009/01/24/howto-get-the-command-line-of-processes/\n         http://stackoverflow.com/a/14012919\n         http://www.drdobbs.com/embracing-64-bit-windows/184401966\n     */\n    SIZE_T size = 0;\n    HANDLE hProcess = NULL;\n    LPCVOID src;\n    WCHAR *buffer = NULL;\n#ifdef _WIN64\n    LPVOID ppeb32 = NULL;\n#else\n    static __NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL;\n    static _NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = NULL;\n    PVOID64 src64;\n    BOOL weAreWow64;\n    BOOL theyAreWow64;\n#endif\n    DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;\n    NTSTATUS status;\n\n    hProcess = psutil_handle_from_pid(pid, access);\n    if (hProcess == NULL)\n        return -1;\n\n#ifdef _WIN64\n    /* 64 bit case.  Check if the target is a 32 bit process running in WoW64\n     * mode. */\n    status = NtQueryInformationProcess(\n        hProcess, ProcessWow64Information, &ppeb32, sizeof(LPVOID), NULL\n    );\n\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status, \"NtQueryInformationProcess(ProcessWow64Information)\"\n        );\n        goto error;\n    }\n\n    if (ppeb32 != NULL) {\n        /* We are 64 bit.  Target process is 32 bit running in WoW64 mode. */\n        PEB32 peb32;\n        RTL_USER_PROCESS_PARAMETERS32 procParameters32;\n\n        // read PEB\n        if (!ReadProcessMemory(hProcess, ppeb32, &peb32, sizeof(peb32), NULL))\n        {\n            // May fail with ERROR_PARTIAL_COPY, see:\n            // https://github.com/giampaolo/psutil/issues/875\n            psutil_convert_winerr(GetLastError(), \"ReadProcessMemory\");\n            goto error;\n        }\n\n        // read process parameters\n        if (!ReadProcessMemory(\n                hProcess,\n                UlongToPtr(peb32.ProcessParameters),\n                &procParameters32,\n                sizeof(procParameters32),\n                NULL\n            ))\n        {\n            // May fail with ERROR_PARTIAL_COPY, see:\n            // https://github.com/giampaolo/psutil/issues/875\n            psutil_convert_winerr(GetLastError(), \"ReadProcessMemory\");\n            goto error;\n        }\n\n        switch (kind) {\n            case KIND_CMDLINE:\n                src = UlongToPtr(procParameters32.CommandLine.Buffer),\n                size = procParameters32.CommandLine.Length;\n                break;\n            case KIND_CWD:\n                src = UlongToPtr(procParameters32.CurrentDirectoryPath.Buffer);\n                size = procParameters32.CurrentDirectoryPath.Length;\n                break;\n            case KIND_ENVIRON:\n                src = UlongToPtr(procParameters32.env);\n                break;\n        }\n    }\n    else\n#else  // #ifdef _WIN64\n    // 32 bit process. In here we may run into a lot of errors, e.g.:\n    // * [Error 0] The operation completed successfully\n    //   (originated from NtWow64ReadVirtualMemory64)\n    // * [Error 998] Invalid access to memory location:\n    //   (originated from NtWow64ReadVirtualMemory64)\n    // Refs:\n    // * https://github.com/giampaolo/psutil/issues/1839\n    // * https://github.com/giampaolo/psutil/pull/1866\n    // Since the following code is quite hackish and fails unpredictably,\n    // in case of any error from NtWow64* APIs we raise AccessDenied.\n\n    // 32 bit case.  Check if the target is also 32 bit.\n    if (!IsWow64Process(GetCurrentProcess(), &weAreWow64)\n        || !IsWow64Process(hProcess, &theyAreWow64))\n    {\n        psutil_oserror_wsyscall(\"IsWow64Process\");\n        goto error;\n    }\n\n    if (weAreWow64 && !theyAreWow64) {\n        /* We are 32 bit running in WoW64 mode.  Target process is 64 bit. */\n        PROCESS_BASIC_INFORMATION64 pbi64;\n        PEB64 peb64;\n        RTL_USER_PROCESS_PARAMETERS64 procParameters64;\n\n        if (NtWow64QueryInformationProcess64 == NULL) {\n            NtWow64QueryInformationProcess64 = psutil_GetProcAddressFromLib(\n                \"ntdll.dll\", \"NtWow64QueryInformationProcess64\"\n            );\n            if (NtWow64QueryInformationProcess64 == NULL) {\n                PyErr_Clear();\n                psutil_oserror_ad(\n                    \"can't query 64-bit process in 32-bit-WoW mode\"\n                );\n                goto error;\n            }\n        }\n        if (NtWow64ReadVirtualMemory64 == NULL) {\n            NtWow64ReadVirtualMemory64 = psutil_GetProcAddressFromLib(\n                \"ntdll.dll\", \"NtWow64ReadVirtualMemory64\"\n            );\n            if (NtWow64ReadVirtualMemory64 == NULL) {\n                PyErr_Clear();\n                psutil_oserror_ad(\n                    \"can't query 64-bit process in 32-bit-WoW mode\"\n                );\n                goto error;\n            }\n        }\n\n        status = NtWow64QueryInformationProcess64(\n            hProcess, ProcessBasicInformation, &pbi64, sizeof(pbi64), NULL\n        );\n        if (!NT_SUCCESS(status)) {\n            /*\n            psutil_convert_ntstatus_err(\n                status,\n                \"NtWow64QueryInformationProcess64(ProcessBasicInformation)\");\n            */\n            psutil_giveup_with_ad(\n                status,\n                \"NtWow64QueryInformationProcess64(ProcessBasicInformation)\"\n            );\n            goto error;\n        }\n\n        // read peb\n        status = NtWow64ReadVirtualMemory64(\n            hProcess, pbi64.PebBaseAddress, &peb64, sizeof(peb64), NULL\n        );\n        if (!NT_SUCCESS(status)) {\n            /*\n            psutil_convert_ntstatus_err(\n                status, \"NtWow64ReadVirtualMemory64(pbi64.PebBaseAddress)\");\n            */\n            psutil_giveup_with_ad(\n                status, \"NtWow64ReadVirtualMemory64(pbi64.PebBaseAddress)\"\n            );\n            goto error;\n        }\n\n        // read process parameters\n        status = NtWow64ReadVirtualMemory64(\n            hProcess,\n            peb64.ProcessParameters,\n            &procParameters64,\n            sizeof(procParameters64),\n            NULL\n        );\n        if (!NT_SUCCESS(status)) {\n            /*\n            psutil_convert_ntstatus_err(\n                status, \"NtWow64ReadVirtualMemory64(peb64.ProcessParameters)\");\n            */\n            psutil_giveup_with_ad(\n                status, \"NtWow64ReadVirtualMemory64(peb64.ProcessParameters)\"\n            );\n            goto error;\n        }\n\n        switch (kind) {\n            case KIND_CMDLINE:\n                src64 = procParameters64.CommandLine.Buffer;\n                size = procParameters64.CommandLine.Length;\n                break;\n            case KIND_CWD:\n                src64 = procParameters64.CurrentDirectoryPath.Buffer,\n                size = procParameters64.CurrentDirectoryPath.Length;\n                break;\n            case KIND_ENVIRON:\n                src64 = procParameters64.env;\n                break;\n        }\n    }\n    else\n#endif\n    /* Target process is of the same bitness as us. */\n    {\n        PROCESS_BASIC_INFORMATION pbi;\n        PEB_ peb;\n        RTL_USER_PROCESS_PARAMETERS_ procParameters;\n\n        status = NtQueryInformationProcess(\n            hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL\n        );\n\n        if (!NT_SUCCESS(status)) {\n            psutil_SetFromNTStatusErr(\n                status, \"NtQueryInformationProcess(ProcessBasicInformation)\"\n            );\n            goto error;\n        }\n\n\n        // read peb\n        if (!ReadProcessMemory(\n                hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL\n            ))\n        {\n            // May fail with ERROR_PARTIAL_COPY, see:\n            // https://github.com/giampaolo/psutil/issues/875\n            psutil_convert_winerr(GetLastError(), \"ReadProcessMemory\");\n            goto error;\n        }\n\n        // read process parameters\n        if (!ReadProcessMemory(\n                hProcess,\n                peb.ProcessParameters,\n                &procParameters,\n                sizeof(procParameters),\n                NULL\n            ))\n        {\n            // May fail with ERROR_PARTIAL_COPY, see:\n            // https://github.com/giampaolo/psutil/issues/875\n            psutil_convert_winerr(GetLastError(), \"ReadProcessMemory\");\n            goto error;\n        }\n\n        switch (kind) {\n            case KIND_CMDLINE:\n                src = procParameters.CommandLine.Buffer;\n                size = procParameters.CommandLine.Length;\n                break;\n            case KIND_CWD:\n                src = procParameters.CurrentDirectoryPath.Buffer;\n                size = procParameters.CurrentDirectoryPath.Length;\n                break;\n            case KIND_ENVIRON:\n                src = procParameters.env;\n                break;\n        }\n    }\n\n    if (kind == KIND_ENVIRON) {\n#ifndef _WIN64\n        if (weAreWow64 && !theyAreWow64) {\n            psutil_oserror_ad(\"can't query 64-bit process in 32-bit-WoW mode\");\n            goto error;\n        }\n        else\n#endif\n            if (psutil_get_process_region_size(hProcess, src, &size) != 0)\n            goto error;\n    }\n\n    buffer = calloc(size + 2, 1);\n    if (buffer == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n#ifndef _WIN64\n    if (weAreWow64 && !theyAreWow64) {\n        status = NtWow64ReadVirtualMemory64(\n            hProcess, src64, buffer, size, NULL\n        );\n        if (!NT_SUCCESS(status)) {\n            // psutil_convert_ntstatus_err(status,\n            // \"NtWow64ReadVirtualMemory64\");\n            psutil_giveup_with_ad(status, \"NtWow64ReadVirtualMemory64\");\n            goto error;\n        }\n    }\n    else\n#endif\n        if (!ReadProcessMemory(hProcess, src, buffer, size, NULL))\n    {\n        // May fail with ERROR_PARTIAL_COPY, see:\n        // https://github.com/giampaolo/psutil/issues/875\n        psutil_convert_winerr(GetLastError(), \"ReadProcessMemory\");\n        goto error;\n    }\n\n    CloseHandle(hProcess);\n\n    *pdata = buffer;\n    *psize = size;\n\n    return 0;\n\nerror:\n    if (hProcess != NULL)\n        CloseHandle(hProcess);\n    if (buffer != NULL)\n        free(buffer);\n    return -1;\n}\n\n\n/*\n * Get process cmdline by using NtQueryInformationProcess. This is a\n * method alternative to PEB which is less likely to result in\n * AccessDenied. Requires Windows 8.1+.\n */\nstatic int\npsutil_cmdline_query_proc(DWORD pid, WCHAR **pdata, SIZE_T *psize) {\n    HANDLE hProcess = NULL;\n    ULONG bufLen = 0;\n    NTSTATUS status;\n    char *buffer = NULL;\n    WCHAR *bufWchar = NULL;\n    PUNICODE_STRING tmp = NULL;\n    size_t size;\n    int ProcessCommandLineInformation = 60;\n\n    if (PSUTIL_WINVER < PSUTIL_WINDOWS_8_1) {\n        psutil_runtime_error(\"requires Windows 8.1+\");\n        goto error;\n    }\n\n    hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);\n    if (hProcess == NULL)\n        goto error;\n\n    // get the right buf size\n    status = NtQueryInformationProcess(\n        hProcess, ProcessCommandLineInformation, NULL, 0, &bufLen\n    );\n\n    // https://github.com/giampaolo/psutil/issues/1501\n    if (status == STATUS_NOT_FOUND) {\n        psutil_oserror_ad(\n            \"NtQueryInformationProcess(ProcessBasicInformation) -> \"\n            \"STATUS_NOT_FOUND\"\n        );\n        goto error;\n    }\n\n    if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL\n        && status != STATUS_INFO_LENGTH_MISMATCH)\n    {\n        psutil_SetFromNTStatusErr(\n            status, \"NtQueryInformationProcess(ProcessBasicInformation)\"\n        );\n        goto error;\n    }\n\n    // allocate memory\n    buffer = calloc(bufLen, 1);\n    if (buffer == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    // get the cmdline\n    status = NtQueryInformationProcess(\n        hProcess, ProcessCommandLineInformation, buffer, bufLen, &bufLen\n    );\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status, \"NtQueryInformationProcess(ProcessCommandLineInformation)\"\n        );\n        goto error;\n    }\n\n    // build the string\n    tmp = (PUNICODE_STRING)buffer;\n    size = wcslen(tmp->Buffer) + 1;\n    bufWchar = (WCHAR *)calloc(size, sizeof(WCHAR));\n    if (bufWchar == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n    wcscpy_s(bufWchar, size, tmp->Buffer);\n    *pdata = bufWchar;\n    *psize = size * sizeof(WCHAR);\n    free(buffer);\n    CloseHandle(hProcess);\n    return 0;\n\nerror:\n    if (buffer != NULL)\n        free(buffer);\n    if (bufWchar != NULL)\n        free(bufWchar);\n    if (hProcess != NULL)\n        CloseHandle(hProcess);\n    return -1;\n}\n\n\n/*\n * Return a Python list representing the arguments for the process\n * with given pid or NULL on error.\n */\nPyObject *\npsutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) {\n    WCHAR *data = NULL;\n    LPWSTR *szArglist = NULL;\n    SIZE_T size;\n    int nArgs;\n    int i;\n    int func_ret;\n    DWORD pid;\n    int pid_return;\n    int use_peb;\n    // TODO: shouldn't this be decref-ed in case of error on\n    // PyArg_ParseTuple?\n    PyObject *py_usepeb = Py_True;\n    PyObject *py_retlist = NULL;\n    PyObject *py_unicode = NULL;\n    static char *keywords[] = {\"pid\", \"use_peb\", NULL};\n\n    if (!PyArg_ParseTupleAndKeywords(\n            args, kwdict, _Py_PARSE_PID \"|O\", keywords, &pid, &py_usepeb\n        ))\n    {\n        return NULL;\n    }\n    if ((pid == 0) || (pid == 4))\n        return Py_BuildValue(\"[]\");\n\n    pid_return = psutil_pid_is_running(pid);\n    if (pid_return == 0)\n        return psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n    if (pid_return == -1)\n        return NULL;\n\n    use_peb = (py_usepeb == Py_True) ? 1 : 0;\n\n    /*\n    Reading the PEB to get the cmdline seem to be the best method if\n    somebody has tampered with the parameters after creating the process.\n    For instance, create a process as suspended, patch the command line\n    in its PEB and unfreeze it. It requires more privileges than\n    NtQueryInformationProcess though (the fallback):\n    - https://github.com/giampaolo/psutil/pull/1398\n    - https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/\n    */\n    if (use_peb == 1)\n        func_ret = psutil_get_process_data(pid, KIND_CMDLINE, &data, &size);\n    else\n        func_ret = psutil_cmdline_query_proc(pid, &data, &size);\n    if (func_ret != 0)\n        goto error;\n\n    // attempt to parse the command line using Win32 API\n    szArglist = CommandLineToArgvW(data, &nArgs);\n    if (szArglist == NULL) {\n        psutil_oserror_wsyscall(\"CommandLineToArgvW\");\n        goto error;\n    }\n\n    // arglist parsed as array of UNICODE_STRING, so convert each to\n    // Python string object and add to arg list\n    py_retlist = PyList_New(nArgs);\n    if (py_retlist == NULL)\n        goto error;\n    for (i = 0; i < nArgs; i++) {\n        py_unicode = PyUnicode_FromWideChar(\n            szArglist[i], wcslen(szArglist[i])\n        );\n        if (py_unicode == NULL)\n            goto error;\n        PyList_SetItem(py_retlist, i, py_unicode);\n        py_unicode = NULL;\n    }\n\n    LocalFree(szArglist);\n    free(data);\n    return py_retlist;\n\nerror:\n    if (szArglist != NULL)\n        LocalFree(szArglist);\n    if (data != NULL)\n        free(data);\n    Py_XDECREF(py_unicode);\n    Py_XDECREF(py_retlist);\n    return NULL;\n}\n\n\nPyObject *\npsutil_proc_cwd(PyObject *self, PyObject *args) {\n    DWORD pid;\n    PyObject *ret = NULL;\n    WCHAR *data = NULL;\n    SIZE_T size;\n    int pid_return;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n\n    pid_return = psutil_pid_is_running(pid);\n    if (pid_return == 0)\n        return psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n    if (pid_return == -1)\n        return NULL;\n\n    if (psutil_get_process_data(pid, KIND_CWD, &data, &size) != 0)\n        goto out;\n\n    // convert wchar array to a Python unicode string\n    ret = PyUnicode_FromWideChar(data, wcslen(data));\n\nout:\n    if (data != NULL)\n        free(data);\n\n    return ret;\n}\n\n\n/*\n * returns a Python string containing the environment variable data for the\n * process with given pid or NULL on error.\n */\nPyObject *\npsutil_proc_environ(PyObject *self, PyObject *args) {\n    DWORD pid;\n    WCHAR *data = NULL;\n    SIZE_T size;\n    int pid_return;\n    PyObject *ret = NULL;\n\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        return NULL;\n    if ((pid == 0) || (pid == 4))\n        return PyUnicode_FromString(\"\");\n\n    pid_return = psutil_pid_is_running(pid);\n    if (pid_return == 0)\n        return psutil_oserror_nsp(\"psutil_pid_is_running -> 0\");\n    if (pid_return == -1)\n        return NULL;\n\n    if (psutil_get_process_data(pid, KIND_ENVIRON, &data, &size) != 0)\n        goto out;\n\n    // convert wchar array to a Python unicode string\n    ret = PyUnicode_FromWideChar(data, size / 2);\n\nout:\n    if (data != NULL)\n        free(data);\n    return ret;\n}\n\n\n/*\n * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure\n * fills the structure with various process information in one shot\n * by using NtQuerySystemInformation.\n * We use this as a fallback when faster functions fail with access\n * denied. This is slower because it iterates over all processes\n * but it doesn't require any privilege (also work for PID 0).\n * On success return 0, else -1 with Python exception already set.\n */\nint\npsutil_get_proc_info(\n    DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID *retBuffer\n) {\n    static ULONG initialBufferSize = 0x4000;\n    NTSTATUS status;\n    PVOID buffer;\n    ULONG bufferSize;\n    PSYSTEM_PROCESS_INFORMATION process;\n\n    bufferSize = initialBufferSize;\n    buffer = malloc(bufferSize);\n    if (buffer == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    while (TRUE) {\n        status = NtQuerySystemInformation(\n            SystemProcessInformation, buffer, bufferSize, &bufferSize\n        );\n        if (status == STATUS_BUFFER_TOO_SMALL\n            || status == STATUS_INFO_LENGTH_MISMATCH)\n        {\n            free(buffer);\n            buffer = malloc(bufferSize);\n            if (buffer == NULL) {\n                PyErr_NoMemory();\n                goto error;\n            }\n        }\n        else {\n            break;\n        }\n    }\n\n    if (!NT_SUCCESS(status)) {\n        psutil_SetFromNTStatusErr(\n            status, \"NtQuerySystemInformation(SystemProcessInformation)\"\n        );\n        goto error;\n    }\n\n    if (bufferSize <= 0x20000)\n        initialBufferSize = bufferSize;\n\n    process = PSUTIL_FIRST_PROCESS(buffer);\n    do {\n        if ((ULONG_PTR)process->UniqueProcessId == pid) {\n            *retProcess = process;\n            *retBuffer = buffer;\n            return 0;\n        }\n    } while ((process = PSUTIL_NEXT_PROCESS(process)));\n\n    psutil_oserror_nsp(\"NtQuerySystemInformation (no PID found)\");\n    goto error;\n\nerror:\n    if (buffer != NULL)\n        free(buffer);\n    return -1;\n}\n\n\n/*\n * Get various process information by using NtQuerySystemInformation.\n * We use this as a fallback when faster functions fail with access\n * denied. This is slower because it iterates over all processes.\n * Returned dict includes the following process info:\n *\n * - num_threads()\n * - ctx_switches()\n * - num_handles() (fallback)\n * - cpu_times() (fallback)\n * - create_time() (fallback)\n * - io_counters() (fallback)\n * - memory_info() (fallback)\n */\nPyObject *\npsutil_proc_oneshot(PyObject *self, PyObject *args) {\n    DWORD pid;\n    PSYSTEM_PROCESS_INFORMATION proc;\n    PVOID buffer = NULL;\n    ULONG i;\n    ULONG ctx_switches = 0;\n    double user_time;\n    double kernel_time;\n    double create_time;\n    PyObject *dict = PyDict_New();\n\n    if (!dict)\n        return NULL;\n    if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))\n        goto error;\n    if (psutil_get_proc_info(pid, &proc, &buffer) != 0)\n        goto error;\n\n    for (i = 0; i < proc->NumberOfThreads; i++)\n        ctx_switches += proc->Threads[i].ContextSwitches;\n\n    user_time = (double)proc->UserTime.HighPart * HI_T\n                + (double)proc->UserTime.LowPart * LO_T;\n    kernel_time = (double)proc->KernelTime.HighPart * HI_T\n                  + (double)proc->KernelTime.LowPart * LO_T;\n\n    // Convert the LARGE_INTEGER union to a Unix time.\n    // It's the best I could find by googling and borrowing code here\n    // and there. The time returned has a precision of 1 second.\n    if (0 == pid || 4 == pid) {\n        // the python module will translate this into BOOT_TIME later\n        create_time = 0;\n    }\n    else {\n        create_time = psutil_LargeIntegerToUnixTime(proc->CreateTime);\n    }\n\n    // clang-format off\n    if (!pydict_add(dict, \"num_handles\", \"k\", proc->HandleCount)) goto error;\n    if (!pydict_add(dict, \"ctx_switches\", \"k\", ctx_switches)) goto error;\n    if (!pydict_add(dict, \"user_time\", \"d\", user_time)) goto error;\n    if (!pydict_add(dict, \"kernel_time\", \"d\", kernel_time)) goto error;\n    if (!pydict_add(dict, \"create_time\", \"d\", create_time)) goto error;\n    if (!pydict_add(dict, \"num_threads\", \"k\", proc->NumberOfThreads)) goto error;\n    // I/O\n    if (!pydict_add(dict, \"io_rcount\", \"K\", proc->ReadOperationCount.QuadPart)) goto error;\n    if (!pydict_add(dict, \"io_wcount\", \"K\", proc->WriteOperationCount.QuadPart)) goto error;\n    if (!pydict_add(dict, \"io_rbytes\", \"K\", proc->ReadTransferCount.QuadPart)) goto error;\n    if (!pydict_add(dict, \"io_wbytes\", \"K\", proc->WriteTransferCount.QuadPart)) goto error;\n    if (!pydict_add(dict, \"io_count_others\", \"K\", proc->OtherOperationCount.QuadPart)) goto error;\n    if (!pydict_add(dict, \"io_bytes_others\", \"K\", proc->OtherTransferCount.QuadPart)) goto error;\n    // proc memory\n    if (!pydict_add(dict, \"PageFaultCount\", \"K\", (ULONGLONG)proc->PageFaultCount)) goto error;\n    if (!pydict_add(dict, \"PeakWorkingSetSize\", \"K\", (ULONGLONG)proc->PeakWorkingSetSize)) goto error;\n    if (!pydict_add(dict, \"WorkingSetSize\", \"K\", (ULONGLONG)proc->WorkingSetSize)) goto error;\n    if (!pydict_add(dict, \"QuotaPeakPagedPoolUsage\", \"K\", (ULONGLONG)proc->QuotaPeakPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"QuotaPagedPoolUsage\", \"K\", (ULONGLONG)proc->QuotaPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"QuotaPeakNonPagedPoolUsage\", \"K\", (ULONGLONG)proc->QuotaPeakNonPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"QuotaNonPagedPoolUsage\", \"K\", (ULONGLONG)proc->QuotaNonPagedPoolUsage)) goto error;\n    if (!pydict_add(dict, \"PagefileUsage\", \"K\", (ULONGLONG)proc->PagefileUsage)) goto error;\n    if (!pydict_add(dict, \"PeakPagefileUsage\", \"K\", (ULONGLONG)proc->PeakPagefileUsage)) goto error;\n    if (!pydict_add(dict, \"PrivatePageCount\", \"K\", (ULONGLONG)proc->PrivatePageCount)) goto error;\n    if (!pydict_add(dict, \"VirtualSize\", \"K\", (ULONGLONG)proc->VirtualSize)) goto error;\n    if (!pydict_add(dict, \"PeakVirtualSize\", \"K\", (ULONGLONG)proc->PeakVirtualSize)) goto error;\n    // clang-format on\n\n    free(buffer);\n    return dict;\n\nerror:\n    if (buffer != NULL)\n        free(buffer);\n    Py_DECREF(dict);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/proc_utils.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Helper process functions.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <Psapi.h>  // EnumProcesses\n\n#include \"../../arch/all/init.h\"\n\n\n// Return 1 if PID exists, 0 if not, -1 on error.\nint\npsutil_pid_in_pids(DWORD pid) {\n    DWORD *pids_array = NULL;\n    int pids_count = 0;\n    int i;\n\n    if (_psutil_pids(&pids_array, &pids_count) != 0)\n        return -1;\n\n    for (i = 0; i < pids_count; i++) {\n        if (pids_array[i] == pid) {\n            free(pids_array);\n            return 1;\n        }\n    }\n\n    free(pids_array);\n    return 0;\n}\n\n\n// Given a process handle checks whether it's actually running. If it\n// does return the handle, else return NULL with Python exception set.\n// This is needed because OpenProcess API sucks.\nHANDLE\npsutil_check_phandle(HANDLE hProcess, DWORD pid, int check_exit_code) {\n    DWORD exitCode;\n\n    if (hProcess == NULL) {\n        if (GetLastError() == ERROR_INVALID_PARAMETER) {\n            // Yeah, this is the actual error code in case of\n            // \"no such process\".\n            psutil_oserror_nsp(\"OpenProcess -> ERROR_INVALID_PARAMETER\");\n            return NULL;\n        }\n        if (GetLastError() == ERROR_SUCCESS) {\n            // Yeah, it's this bad.\n            // https://github.com/giampaolo/psutil/issues/1877\n            if (psutil_pid_in_pids(pid) == 1) {\n                psutil_debug(\"OpenProcess -> ERROR_SUCCESS turned into AD\");\n                psutil_oserror_ad(\"OpenProcess -> ERROR_SUCCESS\");\n            }\n            else {\n                psutil_debug(\"OpenProcess -> ERROR_SUCCESS turned into NSP\");\n                psutil_oserror_nsp(\"OpenProcess -> ERROR_SUCCESS\");\n            }\n            return NULL;\n        }\n        psutil_oserror_wsyscall(\"OpenProcess\");\n        return NULL;\n    }\n\n    if (check_exit_code == 0)\n        return hProcess;\n\n    if (GetExitCodeProcess(hProcess, &exitCode)) {\n        // XXX - maybe STILL_ACTIVE is not fully reliable as per:\n        // http://stackoverflow.com/questions/1591342/#comment47830782_1591379\n        if (exitCode == STILL_ACTIVE) {\n            return hProcess;\n        }\n        if (psutil_pid_in_pids(pid) == 1) {\n            return hProcess;\n        }\n        CloseHandle(hProcess);\n        psutil_oserror_nsp(\"GetExitCodeProcess != STILL_ACTIVE\");\n        return NULL;\n    }\n\n    if (GetLastError() == ERROR_ACCESS_DENIED) {\n        psutil_debug(\"GetExitCodeProcess -> ERROR_ACCESS_DENIED (ignored)\");\n        SetLastError(0);\n        return hProcess;\n    }\n    psutil_oserror_wsyscall(\"GetExitCodeProcess\");\n    CloseHandle(hProcess);\n    return NULL;\n}\n\n\n// A wrapper around OpenProcess setting NSP exception if process no\n// longer exists. *pid* is the process PID, *access* is the first\n// argument to OpenProcess.\n// Return a process handle or NULL with exception set.\nHANDLE\npsutil_handle_from_pid(DWORD pid, DWORD access) {\n    HANDLE hProcess;\n\n    if (pid == 0) {\n        // otherwise we'd get NoSuchProcess\n        return psutil_oserror_ad(\"automatically set for PID 0\");\n    }\n\n    hProcess = OpenProcess(access, FALSE, pid);\n\n    if ((hProcess == NULL) && (GetLastError() == ERROR_ACCESS_DENIED)) {\n        psutil_oserror_wsyscall(\"OpenProcess\");\n        return NULL;\n    }\n\n    hProcess = psutil_check_phandle(hProcess, pid, 1);\n    return hProcess;\n}\n\n\n// Check for PID existence. Return 1 if pid exists, 0 if not, -1 on error.\nint\npsutil_pid_is_running(DWORD pid) {\n    HANDLE hProcess;\n\n    // Special case for PID 0 System Idle Process\n    if (pid == 0)\n        return 1;\n    if (pid < 0)\n        return 0;\n\n    hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);\n\n    if (hProcess != NULL) {\n        hProcess = psutil_check_phandle(hProcess, pid, 1);\n        if (hProcess != NULL) {\n            CloseHandle(hProcess);\n            return 1;\n        }\n        CloseHandle(hProcess);\n    }\n\n    PyErr_Clear();\n    return psutil_pid_in_pids(pid);\n}\n"
  },
  {
    "path": "psutil/arch/windows/security.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Security related functions for Windows platform (Set privileges such as\n * SE DEBUG).\n */\n\n#include <windows.h>\n#include <Python.h>\n\n#include \"../../arch/all/init.h\"\n\n\nstatic BOOL\npsutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) {\n    TOKEN_PRIVILEGES tp;\n    LUID luid;\n    TOKEN_PRIVILEGES tpPrevious;\n    DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);\n\n    if (!LookupPrivilegeValue(NULL, Privilege, &luid)) {\n        psutil_oserror_wsyscall(\"LookupPrivilegeValue\");\n        return -1;\n    }\n\n    // first pass.  get current privilege setting\n    tp.PrivilegeCount = 1;\n    tp.Privileges[0].Luid = luid;\n    tp.Privileges[0].Attributes = 0;\n\n    if (!AdjustTokenPrivileges(\n            hToken,\n            FALSE,\n            &tp,\n            sizeof(TOKEN_PRIVILEGES),\n            &tpPrevious,\n            &cbPrevious\n        ))\n    {\n        psutil_oserror_wsyscall(\"AdjustTokenPrivileges\");\n        return -1;\n    }\n\n    // Second pass. Set privilege based on previous setting.\n    tpPrevious.PrivilegeCount = 1;\n    tpPrevious.Privileges[0].Luid = luid;\n\n    if (bEnablePrivilege)\n        tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);\n    else\n        tpPrevious.Privileges[0].Attributes ^=\n            (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes);\n\n    if (!AdjustTokenPrivileges(\n            hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL\n        ))\n    {\n        psutil_oserror_wsyscall(\"AdjustTokenPrivileges\");\n        return -1;\n    }\n\n    return 0;\n}\n\n\nstatic HANDLE\npsutil_get_thisproc_token() {\n    HANDLE hToken = NULL;\n    HANDLE me = GetCurrentProcess();\n\n    if (!OpenProcessToken(me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))\n    {\n        if (GetLastError() == ERROR_NO_TOKEN) {\n            if (!ImpersonateSelf(SecurityImpersonation)) {\n                psutil_oserror_wsyscall(\"ImpersonateSelf\");\n                return NULL;\n            }\n            if (!OpenProcessToken(\n                    me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken\n                ))\n            {\n                psutil_oserror_wsyscall(\"OpenProcessToken\");\n                return NULL;\n            }\n        }\n        else {\n            psutil_oserror_wsyscall(\"OpenProcessToken\");\n            return NULL;\n        }\n    }\n\n    return hToken;\n}\n\n\nstatic void\npsutil_print_err() {\n    char *msg =\n        \"psutil module couldn't set SE DEBUG mode for this process; \"\n        \"please file an issue against psutil bug tracker\";\n    psutil_debug(msg);\n    if (GetLastError() != ERROR_ACCESS_DENIED)\n        PyErr_WarnEx(PyExc_RuntimeWarning, msg, 1);\n    PyErr_Clear();\n}\n\n\n/*\n * Set this process in SE DEBUG mode so that we have more chances of\n * querying processes owned by other users, including many owned by\n * Administrator and Local System.\n * https://docs.microsoft.com/windows-hardware/drivers/debugger/debug-privilege\n * This is executed on module import and we don't crash on error.\n */\nint\npsutil_set_se_debug() {\n    HANDLE hToken;\n\n    if ((hToken = psutil_get_thisproc_token()) == NULL) {\n        // \"return -1;\" to get an exception\n        psutil_print_err();\n        return 0;\n    }\n\n    if (psutil_set_privilege(hToken, SE_DEBUG_NAME, TRUE) != 0) {\n        // \"return -1;\" to get an exception\n        psutil_print_err();\n    }\n\n    RevertToSelf();\n    CloseHandle(hToken);\n    return 0;\n}\n"
  },
  {
    "path": "psutil/arch/windows/sensors.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// Added in https://github.com/giampaolo/psutil/commit/109f873 in 2017.\n// Moved in here in 2023.\nPyObject *\npsutil_sensors_battery(PyObject *self, PyObject *args) {\n    SYSTEM_POWER_STATUS sps;\n\n    if (GetSystemPowerStatus(&sps) == 0) {\n        psutil_oserror();\n        return NULL;\n    }\n    return Py_BuildValue(\n        \"iidI\",\n        sps.ACLineStatus,  // whether AC is connected: 0=no, 1=yes, 255=unknown\n        // status flag:\n        // 1, 2, 4 = high, low, critical\n        // 8 = charging\n        // 128 = no battery\n        sps.BatteryFlag,\n        (double)sps.BatteryLifePercent,  // percent\n        sps.BatteryLifeTime  // remaining secs\n    );\n}\n"
  },
  {
    "path": "psutil/arch/windows/services.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <Winsvc.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// ==================================================================\n// utils\n// ==================================================================\n\n\nSC_HANDLE\npsutil_get_service_handler(\n    const wchar_t *service_name, DWORD scm_access, DWORD access\n) {\n    SC_HANDLE sc = NULL;\n    SC_HANDLE hService = NULL;\n\n    sc = OpenSCManagerW(NULL, NULL, scm_access);\n    if (sc == NULL) {\n        psutil_oserror_wsyscall(\"OpenSCManagerW\");\n        return NULL;\n    }\n\n    hService = OpenServiceW(sc, service_name, access);\n    if (hService == NULL) {\n        psutil_oserror_wsyscall(\"OpenServiceW\");\n        CloseServiceHandle(sc);\n        return NULL;\n    }\n\n    CloseServiceHandle(sc);\n    return hService;\n}\n\n\n// helper: parse args, convert to wchar, and open service\n// returns NULL on error. On success, fills *service_name_out.\nstatic SC_HANDLE\npsutil_get_service_from_args(\n    PyObject *args, DWORD scm_access, DWORD access, wchar_t **service_name_out\n) {\n    PyObject *py_service_name = NULL;\n    wchar_t *service_name = NULL;\n    Py_ssize_t wlen;\n    SC_HANDLE hService = NULL;\n\n    if (!PyArg_ParseTuple(args, \"U\", &py_service_name)) {\n        return NULL;\n    }\n\n    service_name = PyUnicode_AsWideCharString(py_service_name, &wlen);\n    if (service_name == NULL) {\n        return NULL;\n    }\n\n    hService = psutil_get_service_handler(service_name, scm_access, access);\n    if (hService == NULL) {\n        PyMem_Free(service_name);\n        return NULL;\n    }\n\n    *service_name_out = service_name;\n    return hService;\n}\n\n\n// XXX - expose these as constants?\nstatic const char *\nget_startup_string(DWORD startup) {\n    switch (startup) {\n        case SERVICE_AUTO_START:\n            return \"automatic\";\n        case SERVICE_DEMAND_START:\n            return \"manual\";\n        case SERVICE_DISABLED:\n            return \"disabled\";\n        // drivers only (since we use EnumServicesStatusEx() with\n        // SERVICE_WIN32)\n        // case SERVICE_BOOT_START:\n        //     return \"boot-start\";\n        // case SERVICE_SYSTEM_START:\n        //     return \"system-start\";\n        default:\n            return \"unknown\";\n    }\n}\n\n\n// XXX - expose these as constants?\nstatic const char *\nget_state_string(DWORD state) {\n    switch (state) {\n        case SERVICE_RUNNING:\n            return \"running\";\n        case SERVICE_PAUSED:\n            return \"paused\";\n        case SERVICE_START_PENDING:\n            return \"start_pending\";\n        case SERVICE_PAUSE_PENDING:\n            return \"pause_pending\";\n        case SERVICE_CONTINUE_PENDING:\n            return \"continue_pending\";\n        case SERVICE_STOP_PENDING:\n            return \"stop_pending\";\n        case SERVICE_STOPPED:\n            return \"stopped\";\n        default:\n            return \"unknown\";\n    }\n}\n\n\n// ==================================================================\n// APIs\n// ==================================================================\n\n/*\n * Enumerate all services.\n */\nPyObject *\npsutil_winservice_enumerate(PyObject *self, PyObject *args) {\n    ENUM_SERVICE_STATUS_PROCESSW *lpService = NULL;\n    BOOL ok;\n    SC_HANDLE sc = NULL;\n    DWORD bytesNeeded = 0;\n    DWORD srvCount;\n    DWORD resumeHandle = 0;\n    DWORD dwBytes = 0;\n    DWORD i;\n    PyObject *py_retlist = PyList_New(0);\n    PyObject *py_name = NULL;\n    PyObject *py_display_name = NULL;\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    sc = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);\n    if (sc == NULL) {\n        psutil_oserror_wsyscall(\"OpenSCManager\");\n        return NULL;\n    }\n\n    for (;;) {\n        ok = EnumServicesStatusExW(\n            sc,\n            SC_ENUM_PROCESS_INFO,\n            SERVICE_WIN32,  // XXX - extend this to include drivers etc.?\n            SERVICE_STATE_ALL,\n            (LPBYTE)lpService,\n            dwBytes,\n            &bytesNeeded,\n            &srvCount,\n            &resumeHandle,\n            NULL\n        );\n        if (ok || (GetLastError() != ERROR_MORE_DATA))\n            break;\n        if (lpService)\n            free(lpService);\n        dwBytes = bytesNeeded;\n        lpService = (ENUM_SERVICE_STATUS_PROCESSW *)malloc(dwBytes);\n    }\n\n    for (i = 0; i < srvCount; i++) {\n        // Get unicode name / display name.\n        py_name = NULL;\n        py_name = PyUnicode_FromWideChar(\n            lpService[i].lpServiceName, wcslen(lpService[i].lpServiceName)\n        );\n        if (py_name == NULL)\n            goto error;\n\n        py_display_name = NULL;\n        py_display_name = PyUnicode_FromWideChar(\n            lpService[i].lpDisplayName, wcslen(lpService[i].lpDisplayName)\n        );\n        if (py_display_name == NULL)\n            goto error;\n\n        // Construct the result.\n        if (!pylist_append_fmt(py_retlist, \"(OO)\", py_name, py_display_name))\n            goto error;\n        Py_DECREF(py_display_name);\n        Py_DECREF(py_name);\n    }\n\n    // Free resources.\n    CloseServiceHandle(sc);\n    free(lpService);\n    return py_retlist;\n\nerror:\n    Py_DECREF(py_name);\n    Py_XDECREF(py_display_name);\n    Py_DECREF(py_retlist);\n    if (sc != NULL)\n        CloseServiceHandle(sc);\n    if (lpService != NULL)\n        free(lpService);\n    return NULL;\n}\n\n\n/*\n * Get service config information. Returns:\n * - display_name\n * - binpath\n * - username\n * - startup_type\n */\nPyObject *\npsutil_winservice_query_config(PyObject *self, PyObject *args) {\n    wchar_t *service_name = NULL;\n    SC_HANDLE hService = NULL;\n    BOOL ok;\n    DWORD bytesNeeded = 0;\n    QUERY_SERVICE_CONFIGW *qsc = NULL;\n    PyObject *py_tuple = NULL;\n    PyObject *py_unicode_display_name = NULL;\n    PyObject *py_unicode_binpath = NULL;\n    PyObject *py_unicode_username = NULL;\n\n    hService = psutil_get_service_from_args(\n        args, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_CONFIG, &service_name\n    );\n    if (hService == NULL)\n        return NULL;\n\n    // First call to QueryServiceConfigW() is necessary to get the\n    // right size.\n    bytesNeeded = 0;\n    QueryServiceConfigW(hService, NULL, 0, &bytesNeeded);\n    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\n        psutil_oserror_wsyscall(\"QueryServiceConfigW\");\n        goto error;\n    }\n\n    qsc = (QUERY_SERVICE_CONFIGW *)malloc(bytesNeeded);\n    if (qsc == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    ok = QueryServiceConfigW(hService, qsc, bytesNeeded, &bytesNeeded);\n    if (!ok) {\n        psutil_oserror_wsyscall(\"QueryServiceConfigW\");\n        goto error;\n    }\n\n    // Get unicode display name.\n    py_unicode_display_name = PyUnicode_FromWideChar(\n        qsc->lpDisplayName, wcslen(qsc->lpDisplayName)\n    );\n    if (py_unicode_display_name == NULL)\n        goto error;\n\n    // Get unicode bin path.\n    py_unicode_binpath = PyUnicode_FromWideChar(\n        qsc->lpBinaryPathName, wcslen(qsc->lpBinaryPathName)\n    );\n    if (py_unicode_binpath == NULL)\n        goto error;\n\n    // Get unicode username.\n    py_unicode_username = PyUnicode_FromWideChar(\n        qsc->lpServiceStartName, wcslen(qsc->lpServiceStartName)\n    );\n    if (py_unicode_username == NULL)\n        goto error;\n\n    // Construct result tuple.\n    py_tuple = Py_BuildValue(\n        \"(OOOs)\",\n        py_unicode_display_name,\n        py_unicode_binpath,\n        py_unicode_username,\n        get_startup_string(qsc->dwStartType)  // startup\n    );\n    if (py_tuple == NULL)\n        goto error;\n\n    // Free resources.\n    Py_DECREF(py_unicode_display_name);\n    Py_DECREF(py_unicode_binpath);\n    Py_DECREF(py_unicode_username);\n    free(qsc);\n    CloseServiceHandle(hService);\n    PyMem_Free(service_name);\n    return py_tuple;\n\nerror:\n    Py_XDECREF(py_unicode_display_name);\n    Py_XDECREF(py_unicode_binpath);\n    Py_XDECREF(py_unicode_username);\n    Py_XDECREF(py_tuple);\n    if (hService)\n        CloseServiceHandle(hService);\n    if (qsc)\n        free(qsc);\n    if (service_name)\n        PyMem_Free(service_name);\n    return NULL;\n}\n\n\n/*\n * Get service status information. Returns:\n * - status\n * - pid\n */\nPyObject *\npsutil_winservice_query_status(PyObject *self, PyObject *args) {\n    wchar_t *service_name = NULL;\n    SC_HANDLE hService = NULL;\n    BOOL ok;\n    DWORD bytesNeeded = 0;\n    SERVICE_STATUS_PROCESS *ssp = NULL;\n    PyObject *py_tuple = NULL;\n\n    hService = psutil_get_service_from_args(\n        args, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_STATUS, &service_name\n    );\n    if (hService == NULL)\n        return NULL;\n\n    // First call to QueryServiceStatusEx() is necessary to get the\n    // right size.\n    QueryServiceStatusEx(\n        hService, SC_STATUS_PROCESS_INFO, NULL, 0, &bytesNeeded\n    );\n    if (GetLastError() == ERROR_MUI_FILE_NOT_FOUND) {\n        // Also services.msc fails in the same manner, so we return an\n        // empty string.\n        CloseServiceHandle(hService);\n        PyMem_Free(service_name);\n        return PyUnicode_FromString(\"\");\n    }\n    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\n        psutil_oserror_wsyscall(\"QueryServiceStatusEx\");\n        goto error;\n    }\n\n    ssp = (SERVICE_STATUS_PROCESS *)HeapAlloc(\n        GetProcessHeap(), 0, bytesNeeded\n    );\n    if (ssp == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    // Actual call.\n    ok = QueryServiceStatusEx(\n        hService,\n        SC_STATUS_PROCESS_INFO,\n        (LPBYTE)ssp,\n        bytesNeeded,\n        &bytesNeeded\n    );\n    if (!ok) {\n        psutil_oserror_wsyscall(\"QueryServiceStatusEx\");\n        goto error;\n    }\n\n    py_tuple = Py_BuildValue(\n        \"(sk)\", get_state_string(ssp->dwCurrentState), ssp->dwProcessId\n    );\n    if (py_tuple == NULL)\n        goto error;\n\n    CloseServiceHandle(hService);\n    HeapFree(GetProcessHeap(), 0, ssp);\n    PyMem_Free(service_name);\n    return py_tuple;\n\nerror:\n    Py_XDECREF(py_tuple);\n    if (hService)\n        CloseServiceHandle(hService);\n    if (ssp)\n        HeapFree(GetProcessHeap(), 0, ssp);\n    if (service_name)\n        PyMem_Free(service_name);\n    return NULL;\n}\n\nPyObject *\npsutil_winservice_query_descr(PyObject *self, PyObject *args) {\n    BOOL ok;\n    DWORD bytesNeeded = 0;\n    SC_HANDLE hService = NULL;\n    SERVICE_DESCRIPTIONW *scd = NULL;\n    wchar_t *service_name = NULL;\n    PyObject *py_retstr = NULL;\n\n    hService = psutil_get_service_from_args(\n        args, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_CONFIG, &service_name\n    );\n    if (hService == NULL)\n        return NULL;\n\n    QueryServiceConfig2W(\n        hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &bytesNeeded\n    );\n\n    if ((GetLastError() == ERROR_NOT_FOUND)\n        || (GetLastError() == ERROR_MUI_FILE_NOT_FOUND))\n    {\n        // E.g. services.msc fails in this manner, so we return an\n        // empty string.\n        psutil_debug(\"set empty string for NOT_FOUND service description\");\n        CloseServiceHandle(hService);\n        PyMem_Free(service_name);\n        return PyUnicode_FromString(\"\");\n    }\n\n    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\n        psutil_oserror_wsyscall(\"QueryServiceConfig2W\");\n        goto error;\n    }\n\n    scd = (SERVICE_DESCRIPTIONW *)malloc(bytesNeeded);\n    if (scd == NULL) {\n        PyErr_NoMemory();\n        goto error;\n    }\n\n    ok = QueryServiceConfig2W(\n        hService,\n        SERVICE_CONFIG_DESCRIPTION,\n        (LPBYTE)scd,\n        bytesNeeded,\n        &bytesNeeded\n    );\n    if (!ok) {\n        psutil_oserror_wsyscall(\"QueryServiceConfig2W\");\n        goto error;\n    }\n\n    if (scd->lpDescription == NULL) {\n        py_retstr = PyUnicode_FromString(\"\");\n    }\n    else {\n        py_retstr = PyUnicode_FromWideChar(\n            scd->lpDescription, wcslen(scd->lpDescription)\n        );\n    }\n\n    if (!py_retstr)\n        goto error;\n\n    free(scd);\n    CloseServiceHandle(hService);\n    PyMem_Free(service_name);\n    return py_retstr;\n\nerror:\n    if (hService)\n        CloseServiceHandle(hService);\n    if (scd)\n        free(scd);\n    if (service_name)\n        PyMem_Free(service_name);\n    return NULL;\n}\n\n\n/*\n * Start service.\n * XXX - note: this is exposed but not used.\n */\nPyObject *\npsutil_winservice_start(PyObject *self, PyObject *args) {\n    BOOL ok;\n    SC_HANDLE hService = NULL;\n    wchar_t *service_name = NULL;\n\n    hService = psutil_get_service_from_args(\n        args, SC_MANAGER_ALL_ACCESS, SERVICE_START, &service_name\n    );\n    if (hService == NULL)\n        return NULL;\n\n    ok = StartService(hService, 0, NULL);\n    if (!ok) {\n        psutil_oserror_wsyscall(\"StartService\");\n        goto error;\n    }\n\n    CloseServiceHandle(hService);\n    PyMem_Free(service_name);\n    Py_RETURN_NONE;\n\nerror:\n    if (hService)\n        CloseServiceHandle(hService);\n    if (service_name)\n        PyMem_Free(service_name);\n    return NULL;\n}\n\n\n/*\n * Stop service.\n * XXX - note: this is exposed but not used.\n */\nPyObject *\npsutil_winservice_stop(PyObject *self, PyObject *args) {\n    wchar_t *service_name = NULL;\n    BOOL ok;\n    SC_HANDLE hService = NULL;\n    SERVICE_STATUS ssp;\n\n    hService = psutil_get_service_from_args(\n        args, SC_MANAGER_ALL_ACCESS, SERVICE_STOP, &service_name\n    );\n    if (hService == NULL)\n        return NULL;\n\n    // Note: this can hang for 30 secs.\n    Py_BEGIN_ALLOW_THREADS\n    ok = ControlService(hService, SERVICE_CONTROL_STOP, &ssp);\n    Py_END_ALLOW_THREADS\n    if (!ok) {\n        psutil_oserror_wsyscall(\"ControlService\");\n        goto error;\n    }\n\n    CloseServiceHandle(hService);\n    PyMem_Free(service_name);\n    Py_RETURN_NONE;\n\nerror:\n    if (hService)\n        CloseServiceHandle(hService);\n    if (service_name)\n        PyMem_Free(service_name);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/socks.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n// Fixes clash between winsock2.h and windows.h\n#define WIN32_LEAN_AND_MEAN\n\n#include <Python.h>\n#include <windows.h>\n#include <ws2tcpip.h>\n\n#include \"../../arch/all/init.h\"\n\n\n#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff)\n#define STATUS_UNSUCCESSFUL 0xC0000001\n\n// Note about GetExtended[Tcp|Udp]Table syscalls: due to other processes\n// being active on the machine, it's possible that the size of the table\n// increases between the moment we query the size and the moment we query\n// the data. Therefore we retry if that happens. See:\n// https://github.com/giampaolo/psutil/pull/1335\n// https://github.com/giampaolo/psutil/issues/1294\n\n\nstatic PVOID\n__GetExtendedTcpTable(ULONG family) {\n    DWORD err;\n    PVOID table;\n    ULONG size = 0;\n    TCP_TABLE_CLASS class = TCP_TABLE_OWNER_PID_ALL;\n\n    GetExtendedTcpTable(NULL, &size, FALSE, family, class, 0);\n    // reserve 25% more space to be sure\n    size = size + (size / 2 / 2);\n\n    table = malloc(size);\n    if (table == NULL) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n\n    err = GetExtendedTcpTable(table, &size, FALSE, family, class, 0);\n    if (err == NO_ERROR)\n        return table;\n\n    free(table);\n    if (err == ERROR_INSUFFICIENT_BUFFER || err == STATUS_UNSUCCESSFUL) {\n        psutil_debug(\"GetExtendedTcpTable: retry with different bufsize\");\n        return __GetExtendedTcpTable(family);\n    }\n\n    psutil_runtime_error(\"GetExtendedTcpTable failed\");\n    return NULL;\n}\n\n\nstatic PVOID\n__GetExtendedUdpTable(ULONG family) {\n    DWORD err;\n    PVOID table;\n    ULONG size = 0;\n    UDP_TABLE_CLASS class = UDP_TABLE_OWNER_PID;\n\n    GetExtendedUdpTable(NULL, &size, FALSE, family, class, 0);\n    // reserve 25% more space\n    size = size + (size / 2 / 2);\n\n    table = malloc(size);\n    if (table == NULL) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n\n    err = GetExtendedUdpTable(table, &size, FALSE, family, class, 0);\n    if (err == NO_ERROR)\n        return table;\n\n    free(table);\n    if (err == ERROR_INSUFFICIENT_BUFFER || err == STATUS_UNSUCCESSFUL) {\n        psutil_debug(\"GetExtendedUdpTable: retry with different bufsize\");\n        return __GetExtendedUdpTable(family);\n    }\n\n    psutil_runtime_error(\"GetExtendedUdpTable failed\");\n    return NULL;\n}\n\n\n#define psutil_conn_decref_objs() \\\n    Py_DECREF(_AF_INET);          \\\n    Py_DECREF(_AF_INET6);         \\\n    Py_DECREF(_SOCK_STREAM);      \\\n    Py_DECREF(_SOCK_DGRAM);\n\n\n/*\n * Return a list of network connections opened by a process\n */\nPyObject *\npsutil_net_connections(PyObject *self, PyObject *args) {\n    static long null_address[4] = {0, 0, 0, 0};\n    DWORD pid;\n    int pid_return;\n    PVOID table = NULL;\n    PMIB_TCPTABLE_OWNER_PID tcp4Table;\n    PMIB_UDPTABLE_OWNER_PID udp4Table;\n    PMIB_TCP6TABLE_OWNER_PID tcp6Table;\n    PMIB_UDP6TABLE_OWNER_PID udp6Table;\n    ULONG i;\n    CHAR addressBufferLocal[65];\n    CHAR addressBufferRemote[65];\n\n    PyObject *py_retlist = NULL;\n    PyObject *py_af_filter = NULL;\n    PyObject *py_type_filter = NULL;\n    PyObject *py_addr_tuple_local = NULL;\n    PyObject *py_addr_tuple_remote = NULL;\n    PyObject *_AF_INET = PyLong_FromLong((long)AF_INET);\n    PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6);\n    PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM);\n    PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM);\n\n    if (!PyArg_ParseTuple(\n            args, _Py_PARSE_PID \"OO\", &pid, &py_af_filter, &py_type_filter\n        ))\n    {\n        goto error;\n    }\n\n    if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {\n        psutil_conn_decref_objs();\n        PyErr_SetString(PyExc_TypeError, \"arg 2 or 3 is not a sequence\");\n        return NULL;\n    }\n\n    if (pid != -1) {\n        pid_return = psutil_pid_is_running(pid);\n        if (pid_return == 0) {\n            psutil_conn_decref_objs();\n            return psutil_oserror_nsp(\"psutil_pid_is_running\");\n        }\n        else if (pid_return == -1) {\n            psutil_conn_decref_objs();\n            return NULL;\n        }\n    }\n\n    py_retlist = PyList_New(0);\n    if (py_retlist == NULL) {\n        psutil_conn_decref_objs();\n        return NULL;\n    }\n\n    // TCP IPv4\n\n    if ((PySequence_Contains(py_af_filter, _AF_INET) == 1)\n        && (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1))\n    {\n        table = NULL;\n        py_addr_tuple_local = NULL;\n        py_addr_tuple_remote = NULL;\n\n        table = __GetExtendedTcpTable(AF_INET);\n        if (table == NULL)\n            goto error;\n        tcp4Table = table;\n        for (i = 0; i < tcp4Table->dwNumEntries; i++) {\n            if (pid != -1) {\n                if (tcp4Table->table[i].dwOwningPid != pid) {\n                    continue;\n                }\n            }\n\n            if (tcp4Table->table[i].dwLocalAddr != 0\n                || tcp4Table->table[i].dwLocalPort != 0)\n            {\n                struct in_addr addr;\n\n                addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr;\n                RtlIpv4AddressToStringA(&addr, addressBufferLocal);\n                py_addr_tuple_local = Py_BuildValue(\n                    \"(si)\",\n                    addressBufferLocal,\n                    BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)\n                );\n            }\n            else {\n                py_addr_tuple_local = PyTuple_New(0);\n            }\n\n            if (py_addr_tuple_local == NULL)\n                goto error;\n\n            // On Windows <= XP, remote addr is filled even if socket\n            // is in LISTEN mode in which case we just ignore it.\n            if ((tcp4Table->table[i].dwRemoteAddr != 0\n                 || tcp4Table->table[i].dwRemotePort != 0)\n                && (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN))\n            {\n                struct in_addr addr;\n\n                addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr;\n                RtlIpv4AddressToStringA(&addr, addressBufferRemote);\n                py_addr_tuple_remote = Py_BuildValue(\n                    \"(si)\",\n                    addressBufferRemote,\n                    BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)\n                );\n            }\n            else {\n                py_addr_tuple_remote = PyTuple_New(0);\n            }\n\n            if (py_addr_tuple_remote == NULL)\n                goto error;\n\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiNNiI)\",\n                    -1,\n                    AF_INET,\n                    SOCK_STREAM,\n                    py_addr_tuple_local,\n                    py_addr_tuple_remote,\n                    tcp4Table->table[i].dwState,\n                    tcp4Table->table[i].dwOwningPid\n                ))\n            {\n                goto error;\n            }\n            py_addr_tuple_local = NULL;\n            py_addr_tuple_remote = NULL;\n        }\n\n        free(table);\n        table = NULL;\n    }\n\n    // TCP IPv6\n    if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1)\n        && (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)\n        && (RtlIpv6AddressToStringA != NULL))\n    {\n        table = NULL;\n        py_addr_tuple_local = NULL;\n        py_addr_tuple_remote = NULL;\n\n        table = __GetExtendedTcpTable(AF_INET6);\n        if (table == NULL)\n            goto error;\n        tcp6Table = table;\n        for (i = 0; i < tcp6Table->dwNumEntries; i++) {\n            if (pid != -1) {\n                if (tcp6Table->table[i].dwOwningPid != pid) {\n                    continue;\n                }\n            }\n\n            if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) != 0\n                || tcp6Table->table[i].dwLocalPort != 0)\n            {\n                struct in6_addr addr;\n\n                memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16);\n                RtlIpv6AddressToStringA(&addr, addressBufferLocal);\n                py_addr_tuple_local = Py_BuildValue(\n                    \"(si)\",\n                    addressBufferLocal,\n                    BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)\n                );\n            }\n            else {\n                py_addr_tuple_local = PyTuple_New(0);\n            }\n\n            if (py_addr_tuple_local == NULL)\n                goto error;\n\n            // On Windows <= XP, remote addr is filled even if socket\n            // is in LISTEN mode in which case we just ignore it.\n            if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16)\n                     != 0\n                 || tcp6Table->table[i].dwRemotePort != 0)\n                && (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN))\n            {\n                struct in6_addr addr;\n\n                memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16);\n                RtlIpv6AddressToStringA(&addr, addressBufferRemote);\n                py_addr_tuple_remote = Py_BuildValue(\n                    \"(si)\",\n                    addressBufferRemote,\n                    BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)\n                );\n            }\n            else {\n                py_addr_tuple_remote = PyTuple_New(0);\n            }\n\n            if (py_addr_tuple_remote == NULL)\n                goto error;\n\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiNNiI)\",\n                    -1,\n                    AF_INET6,\n                    SOCK_STREAM,\n                    py_addr_tuple_local,\n                    py_addr_tuple_remote,\n                    tcp6Table->table[i].dwState,\n                    tcp6Table->table[i].dwOwningPid\n                ))\n            {\n                goto error;\n            }\n            py_addr_tuple_local = NULL;\n            py_addr_tuple_remote = NULL;\n        }\n\n        free(table);\n        table = NULL;\n    }\n\n    // UDP IPv4\n\n    if ((PySequence_Contains(py_af_filter, _AF_INET) == 1)\n        && (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1))\n    {\n        table = NULL;\n        py_addr_tuple_local = NULL;\n        table = __GetExtendedUdpTable(AF_INET);\n        if (table == NULL)\n            goto error;\n        udp4Table = table;\n        for (i = 0; i < udp4Table->dwNumEntries; i++) {\n            if (pid != -1) {\n                if (udp4Table->table[i].dwOwningPid != pid) {\n                    continue;\n                }\n            }\n\n            if (udp4Table->table[i].dwLocalAddr != 0\n                || udp4Table->table[i].dwLocalPort != 0)\n            {\n                struct in_addr addr;\n\n                addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr;\n                RtlIpv4AddressToStringA(&addr, addressBufferLocal);\n                py_addr_tuple_local = Py_BuildValue(\n                    \"(si)\",\n                    addressBufferLocal,\n                    BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)\n                );\n            }\n            else {\n                py_addr_tuple_local = PyTuple_New(0);\n            }\n\n            if (py_addr_tuple_local == NULL)\n                goto error;\n\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiNNiI)\",\n                    -1,\n                    AF_INET,\n                    SOCK_DGRAM,\n                    py_addr_tuple_local,\n                    PyTuple_New(0),\n                    PSUTIL_CONN_NONE,\n                    udp4Table->table[i].dwOwningPid\n                ))\n            {\n                goto error;\n            }\n            py_addr_tuple_local = NULL;\n        }\n\n        free(table);\n        table = NULL;\n    }\n\n    // UDP IPv6\n\n    if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1)\n        && (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)\n        && (RtlIpv6AddressToStringA != NULL))\n    {\n        table = NULL;\n        py_addr_tuple_local = NULL;\n        table = __GetExtendedUdpTable(AF_INET6);\n        if (table == NULL)\n            goto error;\n        udp6Table = table;\n        for (i = 0; i < udp6Table->dwNumEntries; i++) {\n            if (pid != -1) {\n                if (udp6Table->table[i].dwOwningPid != pid) {\n                    continue;\n                }\n            }\n\n            if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) != 0\n                || udp6Table->table[i].dwLocalPort != 0)\n            {\n                struct in6_addr addr;\n\n                memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16);\n                RtlIpv6AddressToStringA(&addr, addressBufferLocal);\n                py_addr_tuple_local = Py_BuildValue(\n                    \"(si)\",\n                    addressBufferLocal,\n                    BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)\n                );\n            }\n            else {\n                py_addr_tuple_local = PyTuple_New(0);\n            }\n\n            if (py_addr_tuple_local == NULL)\n                goto error;\n\n            if (!pylist_append_fmt(\n                    py_retlist,\n                    \"(iiiNNiI)\",\n                    -1,\n                    AF_INET6,\n                    SOCK_DGRAM,\n                    py_addr_tuple_local,\n                    PyTuple_New(0),\n                    PSUTIL_CONN_NONE,\n                    udp6Table->table[i].dwOwningPid\n                ))\n            {\n                goto error;\n            }\n            py_addr_tuple_local = NULL;\n        }\n\n        free(table);\n        table = NULL;\n    }\n\n    psutil_conn_decref_objs();\n    return py_retlist;\n\nerror:\n    psutil_conn_decref_objs();\n    Py_XDECREF(py_addr_tuple_local);\n    Py_XDECREF(py_addr_tuple_remote);\n    Py_DECREF(py_retlist);\n    if (table != NULL)\n        free(table);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/sys.c",
    "content": "/*\n * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n */\n\n/*\nSystem related functions. Original code moved in here from\npsutil/_psutil_windows.c in 2023. For reference, here's the GIT blame\nhistory before the move:\n\n- boot_time():\n  https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_windows.c#L51-L60\n- users():\n  https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_windows.c#L1103-L1244\n*/\n\n#include <Python.h>\n#include <windows.h>\n\n#include \"ntextapi.h\"\n#include \"../../arch/all/init.h\"\n\n\n// The number of seconds passed since boot. This is a monotonic timer,\n// not affected by system clock updates. On Windows 7+ it also includes\n// the time spent during suspend / hybernate.\nPyObject *\npsutil_uptime(PyObject *self, PyObject *args) {\n    double uptimeSeconds;\n    ULONGLONG interruptTime100ns = 0;\n\n    if (QueryInterruptTime) {  // Windows 7+\n        QueryInterruptTime(&interruptTime100ns);\n        // Convert from 100-nanosecond to seconds.\n        uptimeSeconds = interruptTime100ns / 10000000.0;\n    }\n    else {\n        // Convert from milliseconds to seconds.\n        uptimeSeconds = (double)GetTickCount64() / 1000.0;\n    }\n    return Py_BuildValue(\"d\", uptimeSeconds);\n}\n\n\nPyObject *\npsutil_users(PyObject *self, PyObject *args) {\n    HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;\n    LPWSTR buffer_user = NULL;\n    LPWSTR buffer_addr = NULL;\n    LPWSTR buffer_info = NULL;\n    PWTS_SESSION_INFOW sessions = NULL;\n    DWORD count;\n    DWORD i;\n    DWORD sessionId;\n    DWORD bytes;\n    PWTS_CLIENT_ADDRESS address;\n    char address_str[50];\n    PWTSINFOW wts_info;\n    PyObject *py_address = NULL;\n    PyObject *py_username = NULL;\n    PyObject *py_retlist = PyList_New(0);\n\n    if (py_retlist == NULL)\n        return NULL;\n\n    if (WTSEnumerateSessionsW == NULL || WTSQuerySessionInformationW == NULL\n        || WTSFreeMemory == NULL)\n    {\n        // If we don't run in an environment that is a Remote Desktop Services\n        // environment the Wtsapi32 proc might not be present.\n        // https://docs.microsoft.com/en-us/windows/win32/termserv/run-time-linking-to-wtsapi32-dll\n        return py_retlist;\n    }\n\n    if (WTSEnumerateSessionsW(hServer, 0, 1, &sessions, &count) == 0) {\n        if (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) {\n            // On Windows Nano server, the Wtsapi32 API can be present, but\n            // return WinError 120.\n            return py_retlist;\n        }\n        psutil_oserror_wsyscall(\"WTSEnumerateSessionsW\");\n        goto error;\n    }\n\n    for (i = 0; i < count; i++) {\n        py_address = NULL;\n        sessionId = sessions[i].SessionId;\n        if (buffer_user != NULL)\n            WTSFreeMemory(buffer_user);\n        if (buffer_addr != NULL)\n            WTSFreeMemory(buffer_addr);\n        if (buffer_info != NULL)\n            WTSFreeMemory(buffer_info);\n\n        buffer_user = NULL;\n        buffer_addr = NULL;\n        buffer_info = NULL;\n\n        // username\n        bytes = 0;\n        if (WTSQuerySessionInformationW(\n                hServer, sessionId, WTSUserName, &buffer_user, &bytes\n            )\n            == 0)\n        {\n            psutil_oserror_wsyscall(\"WTSQuerySessionInformationW\");\n            goto error;\n        }\n        if (bytes <= 2)\n            continue;\n\n        // address\n        bytes = 0;\n        if (WTSQuerySessionInformationW(\n                hServer, sessionId, WTSClientAddress, &buffer_addr, &bytes\n            )\n            == 0)\n        {\n            psutil_oserror_wsyscall(\"WTSQuerySessionInformationW\");\n            goto error;\n        }\n\n        address = (PWTS_CLIENT_ADDRESS)buffer_addr;\n        if (address->AddressFamily == 2) {  // AF_INET == 2\n            str_format(\n                address_str,\n                sizeof(address_str),\n                \"%u.%u.%u.%u\",\n                // The IP address is offset by two bytes from the start of the\n                // Address member of the WTS_CLIENT_ADDRESS structure.\n                address->Address[2],\n                address->Address[3],\n                address->Address[4],\n                address->Address[5]\n            );\n            py_address = PyUnicode_FromString(address_str);\n            if (!py_address)\n                goto error;\n        }\n        else {\n            Py_INCREF(Py_None);\n            py_address = Py_None;\n        }\n\n        // login time\n        bytes = 0;\n        if (WTSQuerySessionInformationW(\n                hServer, sessionId, WTSSessionInfo, &buffer_info, &bytes\n            )\n            == 0)\n        {\n            psutil_oserror_wsyscall(\"WTSQuerySessionInformationW\");\n            goto error;\n        }\n        wts_info = (PWTSINFOW)buffer_info;\n\n        py_username = PyUnicode_FromWideChar(buffer_user, wcslen(buffer_user));\n        if (py_username == NULL)\n            goto error;\n\n        if (!pylist_append_fmt(\n                py_retlist,\n                \"OOd\",\n                py_username,\n                py_address,\n                psutil_LargeIntegerToUnixTime(wts_info->ConnectTime)\n            ))\n        {\n            goto error;\n        }\n        Py_CLEAR(py_username);\n        Py_CLEAR(py_address);\n    }\n\n    WTSFreeMemory(sessions);\n    WTSFreeMemory(buffer_user);\n    WTSFreeMemory(buffer_addr);\n    WTSFreeMemory(buffer_info);\n    return py_retlist;\n\nerror:\n    Py_XDECREF(py_username);\n    Py_XDECREF(py_address);\n    Py_DECREF(py_retlist);\n\n    if (sessions != NULL)\n        WTSFreeMemory(sessions);\n    if (buffer_user != NULL)\n        WTSFreeMemory(buffer_user);\n    if (buffer_addr != NULL)\n        WTSFreeMemory(buffer_addr);\n    if (buffer_info != NULL)\n        WTSFreeMemory(buffer_info);\n    return NULL;\n}\n"
  },
  {
    "path": "psutil/arch/windows/wmi.c",
    "content": "/*\n * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n * Use of this source code is governed by a BSD-style license that can be\n * found in the LICENSE file.\n *\n * Functions related to the Windows Management Instrumentation API.\n */\n\n#include <Python.h>\n#include <windows.h>\n#include <pdh.h>\n\n#include \"../../arch/all/init.h\"\n\n\n// We use an exponentially weighted moving average, just like Unix systems do\n// https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation\n//\n// These constants serve as the damping factor and are calculated with\n// 1 / exp(sampling interval in seconds / window size in seconds)\n//\n// This formula comes from linux's include/linux/sched/loadavg.h\n// https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23\n#define LOADAVG_FACTOR_1F 0.9200444146293232478931553241\n#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501\n#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394\n// The time interval in seconds between taking load counts, same as Linux\n#define SAMPLING_INTERVAL 5\n\ndouble load_avg_1m = 0;\ndouble load_avg_5m = 0;\ndouble load_avg_15m = 0;\n\n// clang-format off\n#ifdef Py_GIL_DISABLED\n    static PyMutex mutex;\n    #define MUTEX_LOCK(m) PyMutex_Lock(m)\n    #define MUTEX_UNLOCK(m) PyMutex_Unlock(m)\n#else\n    #define MUTEX_LOCK(m)\n    #define MUTEX_UNLOCK(m)\n#endif\n// clang-format on\n\n\nVOID CALLBACK\nLoadAvgCallback(PVOID hCounter, BOOLEAN timedOut) {\n    PDH_FMT_COUNTERVALUE displayValue;\n    double currentLoad;\n    PDH_STATUS err;\n\n    err = PdhGetFormattedCounterValue(\n        (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &displayValue\n    );\n    // Skip updating the load if we can't get the value successfully\n    if (err != ERROR_SUCCESS) {\n        return;\n    }\n    currentLoad = displayValue.doubleValue;\n\n    MUTEX_LOCK(&mutex);\n    load_avg_1m = load_avg_1m * LOADAVG_FACTOR_1F\n                  + currentLoad * (1.0 - LOADAVG_FACTOR_1F);\n    load_avg_5m = load_avg_5m * LOADAVG_FACTOR_5F\n                  + currentLoad * (1.0 - LOADAVG_FACTOR_5F);\n    load_avg_15m = load_avg_15m * LOADAVG_FACTOR_15F\n                   + currentLoad * (1.0 - LOADAVG_FACTOR_15F);\n    MUTEX_UNLOCK(&mutex);\n}\n\n\nPyObject *\npsutil_init_loadavg_counter(PyObject *self, PyObject *args) {\n    WCHAR *szCounterPath = L\"\\\\System\\\\Processor Queue Length\";\n    PDH_STATUS s;\n    BOOL ret;\n    HQUERY hQuery;\n    HCOUNTER hCounter;\n    HANDLE event;\n    HANDLE waitHandle;\n\n    if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS) {\n        psutil_runtime_error(\"PdhOpenQueryW failed\");\n        return NULL;\n    }\n\n    s = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);\n    if (s != ERROR_SUCCESS) {\n        psutil_runtime_error(\n            \"PdhAddEnglishCounterW failed. Performance counters may be \"\n            \"disabled.\"\n        );\n        return NULL;\n    }\n\n    event = CreateEventW(NULL, FALSE, FALSE, L\"LoadUpdateEvent\");\n    if (event == NULL) {\n        psutil_oserror_wsyscall(\"CreateEventW\");\n        return NULL;\n    }\n\n    s = PdhCollectQueryDataEx(hQuery, SAMPLING_INTERVAL, event);\n    if (s != ERROR_SUCCESS) {\n        psutil_runtime_error(\"PdhCollectQueryDataEx failed\");\n        return NULL;\n    }\n\n    ret = RegisterWaitForSingleObject(\n        &waitHandle,\n        event,\n        (WAITORTIMERCALLBACK)LoadAvgCallback,\n        (PVOID)hCounter,\n        INFINITE,\n        WT_EXECUTEDEFAULT\n    );\n\n    if (ret == 0) {\n        psutil_oserror_wsyscall(\"RegisterWaitForSingleObject\");\n        return NULL;\n    }\n\n    Py_RETURN_NONE;\n}\n\n\n/*\n * Gets the emulated 1 minute, 5 minute and 15 minute load averages\n * (processor queue length) for the system.\n * `init_loadavg_counter` must be called before this function to engage the\n * mechanism that records load values.\n */\nPyObject *\npsutil_get_loadavg(PyObject *self, PyObject *args) {\n    MUTEX_LOCK(&mutex);\n    double load_avg_1m_l = load_avg_1m;\n    double load_avg_5m_l = load_avg_5m;\n    double load_avg_15m_l = load_avg_15m;\n    MUTEX_UNLOCK(&mutex);\n    return Py_BuildValue(\n        \"(ddd)\", load_avg_1m_l, load_avg_5m_l, load_avg_15m_l\n    );\n}\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[tool.black]\ntarget-version = [\"py37\"]\nline-length = 79\nskip-string-normalization = true\n# https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html\npreview = true\nenable-unstable-feature = [\"hug_parens_with_braces_and_square_brackets\", \"string_processing\", \"wrap_long_dict_values_in_parens\"]\n\n[tool.ruff]\n# https://beta.ruff.rs/docs/settings/\ntarget-version = \"py37\"\nline-length = 79\n\n[tool.ruff.lint]\npreview = true\nextend-safe-fixes = [\n    \"PLR6201\",  # turn `1 in (1, 2)` into `1 in {1, 2}`\n]\nselect = [\n    # To get a list of all values: `python3 -m ruff linter`.\n    \"ALL\",\n    \"D200\",  # [*] One-line docstring should fit on one line\n    \"D204\",  # [*] 1 blank line required after class docstring\n    \"D209\",  # [*] Multi-line docstring closing quotes should be on a separate line\n    \"D212\",  # [*] Multi-line docstring summary should start at the first line\n    \"D301\",  # Use `r\"\"\"` if any backslashes in a docstring\n    \"D403\",  # [*] First word of the first line should be capitalized\n    \"PERF102\",  # [*] When using only the keys of a dict use the `keys()` method\n    \"PERF401\",  # Use a list comprehension to create a transformed list\n    \"S113\",  # Probable use of requests call without timeout\n    \"S602\",  # `subprocess` call with `shell=True` identified, security issue\n]\nignore = [\n    \"A\",  # flake8-builtins (shadowing of builtins like all, any, ...)\n    \"ANN\",  # flake8-annotations\n    \"ARG001\",  # unused-function-argument\n    \"ARG002\",  # unused-method-argument\n    \"B007\",  # Loop control variable `x` not used within loop body\n    \"C408\",  # Unnecessary dict() call\n    \"C90\",  # mccabe (function `X` is too complex)\n    \"COM812\",  # Trailing comma missing\n    \"D\",  # pydocstyle\n    \"DOC\",  # various docstring warnings\n    \"DTZ\",  # flake8-datetimez\n    \"ERA001\",  # Found commented-out code\n    \"FBT\",  # flake8-boolean-trap (makes zero sense)\n    \"FIX\",  # Line contains TODO / XXX / ..., consider resolving the issue\n    \"FLY002\",  # static-join-to-f-string / Consider {expression} instead of string join\n    \"FURB101\",  # `open` and `read` should be replaced by `Path(src).read_text()`\n    \"FURB103\",  # `open` and `write` should be replaced by `Path(src).write_text()`\n    \"FURB116\",  # [*] Replace `hex` call with `f\"{start:x}\"`\n    \"FURB118\",  # [*] Use `operator.add` instead of defining a lambda\n    \"FURB140\",  # [*] Use `itertools.starmap` instead of the generator\n    \"INP\",  # flake8-no-pep420\n    \"N801\",  # Class name `async_chat` should use CapWords convention (ASYNCORE COMPAT)\n    \"N802\",  # Function name X should be lowercase.\n    \"N806\",  # Variable X in function should be lowercase.\n    \"N818\",  # Exception name `FooBar` should be named with an Error suffix\n    \"PERF\",  # Perflint\n    \"PLC0415\",  # `import` should be at the top-level of a file\n    \"PLC2701\",  # Private name import `x` from external module `y`\n    \"PLR0904\",  # Too many public methods (x > y)\n    \"PLR0911\",  # Too many return statements (8 > 6)\n    \"PLR0912\",  # Too many branches (x > y)\n    \"PLR0913\",  # Too many arguments in function definition (x > y)\n    \"PLR0914\",  # Too many local variables (x/y)\n    \"PLR0915\",  # Too many statements (x > y)\n    \"PLR0917\",  # Too many positional arguments (x/y)\n    \"PLR1702\",  # Too many nested blocks (x > y)\n    \"PLR1704\",  # Redefining argument with the local name `type_`\n    \"PLR2004\",  # Magic value used in comparison, consider replacing X with a constant variable\n    \"PLR6301\",  # Method `x` could be a function, class method, or static method\n    \"PLW0603\",  # Using the global statement to update `lineno` is discouraged\n    \"PLW1514\",  # `open` in text mode without explicit `encoding` argument\n    \"PLW2901\",  # `for` loop variable `x` overwritten by assignment target\n    \"PT028\",  # pytest-parameter-with-default-argument\n    \"PTH\",  # flake8-use-pathlib\n    \"PYI\",  # flake8-pyi (python types stuff)\n    \"Q000\",  # Single quotes found but double quotes preferred\n    \"RET502\",  # Do not implicitly `return None` in function able to return non-`None` value\n    \"RET503\",  # Missing explicit `return` at the end of function able to return non-`None` value\n    \"RET504\",  # Unnecessary assignment to `result` before `return` statement\n    \"RET505\",  # [*] Unnecessary `else` after `return` statement\n    \"RUF005\",  # Consider iterable unpacking instead of concatenation\n    \"RUF012\",  # Mutable class attributes should be annotated with `typing.ClassVar`\n    \"RUF022\",  # `__all__` is not sorted\n    \"RUF028\",  # This suppression comment is invalid\n    \"RUF031\",  # [*] Avoid parentheses for tuples in subscripts\n    \"RUF067\",  # `__init__` module should only contain docstrings and re-exports\n    \"S\",  # flake8-bandit\n    \"SIM102\",  # Use a single `if` statement instead of nested `if` statements\n    \"SIM105\",  # Use `contextlib.suppress(OSError)` instead of `try`-`except`-`pass`\n    \"SIM117\",  # Use a single `with` statement with multiple contexts instead of nested `with` statements\n    \"SLF\",  # flake8-self\n    \"TD\",  # all TODOs, XXXs, etc.\n    \"TRY300\",  # Consider moving this statement to an `else` block\n    \"TRY301\",  # Abstract `raise` to an inner function\n    \"UP032\",  # [*] Use f-string instead of `format` call\n]\n\n[tool.ruff.lint.per-file-ignores]\n# B904 == Use `raise from` to specify exception cause\n# EM101 == raw-string-in-exception\n# EM102 == f-string-in-exception\n# EM103 == dot-format-in-exception\n# PLC1901 == `x == \"\"` can be simplified to `not x` as an empty string is falsey\n# PT009 == Use a regular `assert` instead of unittest-style `self.assert*`\n# T201 == print()\n# T203 == pprint()\n# TRY003 == raise-vanilla-args\n\".github/workflows/*\" = [\"B904\", \"EM101\", \"EM102\", \"EM103\", \"T201\", \"T203\"]\n\"tests/*\" = [\"B904\", \"EM101\", \"EM102\", \"EM103\", \"PLC1901\", \"RUF069\", \"TRY003\"]\n\"tests/test_sudo.py\" = [\"PT009\"]\n\"scripts/*\" = [\"B904\", \"EM101\", \"EM102\", \"EM103\", \"T201\", \"T203\"]\n\"scripts/internal/*\" = [\"B904\", \"EM101\", \"EM102\", \"EM103\", \"T201\", \"T203\", \"TRY003\"]\n\"doc/internal/*\" = [\"B904\", \"EM101\", \"EM102\", \"EM103\", \"T201\", \"T203\", \"TRY003\"]\n\"setup.py\" = [\n    \"T201\",\n    \"T203\",\n]\n\n[tool.ruff.lint.isort]\n# https://beta.ruff.rs/docs/settings/#isort\nforce-single-line = true  # one import per line\n\n[tool.coverage.report]\nexclude_lines = [\n    \"except ImportError:\",\n    \"globals().update\",\n    \"if BSD\",\n    \"if FREEBSD\",\n    \"if LINUX\",\n    \"if LITTLE_ENDIAN:\",\n    \"if MACOS\",\n    \"if NETBSD\",\n    \"if OPENBSD\",\n    \"if SUNOS\",\n    \"if WINDOWS\",\n    \"if _WINDOWS:\",\n    \"if __name__ == .__main__.:\",\n    \"if ppid_map is None:\",\n    \"if sys.platform.startswith\",\n    \"pragma: no cover\",\n    \"raise NotImplementedError\",\n]\nomit = [\n    \"setup.py\",\n    \"tests/*\",\n]\n\n[tool.pylint.messages_control]\n# Important ones:\n# undefined-all-variable, invalid-envvar-default, reimported, raising-format-tuple, simplifiable-if-expression, useless-object-inheritance\ndisable = [\n    \"broad-except\",  # except Exception:\n    \"consider-using-dict-comprehension\",\n    \"consider-using-f-string\",\n    \"consider-using-set-comprehension\",\n    \"consider-using-with\",\n    \"disallowed-name\",\n    \"fixme\",\n    \"global-statement\",\n    \"import-error\",\n    \"import-outside-toplevel\",\n    \"inconsistent-return-statements\",\n    \"invalid-name\",\n    \"missing-class-docstring\",\n    \"missing-function-docstring\",\n    \"no-else-raise\",\n    \"no-else-return\",\n    \"protected-access\",\n    \"raise-missing-from\",\n    \"redefined-builtin\",\n    \"super-with-arguments\",\n    \"too-few-public-methods\",\n    \"too-many-arguments\",\n    \"too-many-branches\",\n    \"too-many-instance-attributes\",\n    \"too-many-lines\",\n    \"too-many-locals\",\n    \"too-many-public-methods\",\n    \"too-many-return-statements\",\n    \"too-many-statements\",\n    \"ungrouped-imports\",\n    \"unspecified-encoding\",\n    \"wrong-import-position\",\n]\n\n[tool.vulture]\nexclude = [\n    \"docs/conf.py\",\n    \"tests/\",\n]\nignore_decorators = [\n    \"@_common.deprecated_method\",\n    \"@atexit.register\",\n    \"@pytest.fixture\",\n]\n\n[tool.tomlsort]\nin_place = true\nno_sort_tables = true\nsort_inline_arrays = true\nspaces_before_inline_comment = 2\nspaces_indent_inline_array = 4\ntrailing_comma_inline_array = true\n\n[tool.cibuildwheel]\nskip = [\n    \"cp3{8,9,10,11,12}-*linux_{ppc64le,s390x} cp3*t-musllinux*\",\n]\ntest-extras = [\"test\"]  # same as doing `pip install .[test]`\ntest-command = \"make -C {project} PYTHON=python PSUTIL_ROOT_DIR=\\\"{project}\\\" ci-test-cibuildwheel\"\n\n# Run tests on Python 3.13. On all other Python versions do a lightweight\n# import test.\n[[tool.cibuildwheel.overrides]]\nselect = \"cp3{8,9,10,11,12,13t,14,14t}-* cp313-macosx* *-musllinux*\"\ntest-extras = []\ntest-command = \"python -c \\\"import psutil; print(psutil.__version__)\\\"\"\n\n[[tool.cibuildwheel.overrides]]\nselect = \"cp38-macosx*\"  # In macOS use Python 3.8: higher Python version hang for some reason.\ntest-extras = [\"test\"]\ntest-command = \"make -C {project} PYTHON=python PSUTIL_ROOT_DIR=\\\"{project}\\\" ci-test-cibuildwheel\"\n\n[tool.pytest]\naddopts = [\n    \"--capture=no\",\n    \"--instafail\",\n    \"--no-header\",\n    \"--strict-config\",\n    \"--strict-markers\",\n    \"--tb=short\",\n    \"--verbose\",\n    \"-p instafail\",\n    \"-p no:doctest\",\n    \"-p no:junitxml\",\n    \"-p no:nose\",\n    \"-p no:pastebin\",\n    \"-p xdist\",\n]\nverbosity_subtests = \"0\"\ntestpaths = [\"tests/\"]\npython_files = [\"test_*.py\"]\n\n[build-system]\nbuild-backend = \"setuptools.build_meta\"\nrequires = [\"setuptools>=43\"]\n"
  },
  {
    "path": "scripts/battery.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Show battery information.\n\n$ python3 scripts/battery.py\ncharge:     74%\nleft:       2:11:31\nstatus:     discharging\nplugged in: no\n\"\"\"\n\nimport sys\n\nimport psutil\n\n\ndef secs2hours(secs):\n    mm, ss = divmod(secs, 60)\n    hh, mm = divmod(mm, 60)\n    return f\"{int(hh)}:{int(mm):02}:{int(ss):02}\"\n\n\ndef main():\n    if not hasattr(psutil, \"sensors_battery\"):\n        return sys.exit(\"platform not supported\")\n    batt = psutil.sensors_battery()\n    if batt is None:\n        return sys.exit(\"no battery is installed\")\n\n    print(f\"charge:     {round(batt.percent, 2)}%\")\n    if batt.power_plugged:\n        print(\n            \"status:    \"\n            f\" {'charging' if batt.percent < 100 else 'fully charged'}\"\n        )\n        print(\"plugged in: yes\")\n    else:\n        print(f\"left:      {secs2hours(batt.secsleft)}\")\n        print(\"status:     discharging\")\n        print(\"plugged in: no\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/cpu_distribution.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Shows CPU workload split across different CPUs.\n\n$ python3 scripts/cpu_workload.py\nCPU 0     CPU 1     CPU 2     CPU 3     CPU 4     CPU 5     CPU 6     CPU 7\n19.8      20.6      18.2      15.8      6.9       17.3      5.0       20.4\ngvfsd     pytho     kwork     chrom     unity     kwork     kwork     kwork\nchrom     chrom     indic     ibus-     whoop     nfsd      (sd-p     gvfsd\nibus-     cat       at-sp     chrom     Modem     nfsd4     light     upsta\nibus-     iprt-     ibus-     nacl_     cfg80     kwork     nfsd      bluet\nchrom     irqba     gpg-a     chrom     ext4-     biose     nfsd      dio/n\nchrom     acpid     bamfd     nvidi     kwork     scsi_     sshd      rpc.m\nupsta     rsysl     dbus-     nfsd      biose     scsi_     ext4-     polki\nrtkit     avahi     upowe     Netwo     scsi_     biose     UVM T     irq/9\nlight     rpcbi     snapd     cron      ipv6_     biose     kwork     dbus-\nagett     kvm-i     avahi     kwork     biose     biose     scsi_     syste\nnfsd      syste     rpc.i     biose     biose     kbloc     kthro     UVM g\nnfsd      kwork     kwork     biose     vmsta     kwork     crypt     kaudi\nnfsd      scsi_     charg     biose     md        ksoft     kwork     kwork\nmemca     biose     ksmd      ecryp     ksoft     watch     migra     nvme\ntherm     biose     kcomp     kswap     migra     cpuhp     watch     biose\nsyste     biose     kdevt     khuge     watch               cpuhp     biose\nled_w     devfr     kwork     write     cpuhp                         biose\nrpcio     oom_r     ksoft     kwork     syste                         biose\nkwork     kwork     watch     migra                                   acpi_\nbiose     ksoft     cpuhp     watch                                   watch\nbiose     migra               cpuhp                                   kinte\nbiose     watch               rcu_s                                   netns\nbiose     cpuhp               kthre                                   kwork\ncpuhp                                                                 ksoft\nwatch                                                                 migra\nrcu_b                                                                 cpuhp\nkwork\n\"\"\"\n\nimport collections\nimport os\nimport shutil\nimport sys\nimport time\n\nimport psutil\n\nif not hasattr(psutil.Process, \"cpu_num\"):\n    sys.exit(\"platform not supported\")\n\n\ndef clean_screen():\n    if psutil.POSIX:\n        os.system('clear')\n    else:\n        os.system('cls')\n\n\ndef main():\n    num_cpus = psutil.cpu_count()\n    if num_cpus > 8:\n        num_cpus = 8  # try to fit into screen\n        cpus_hidden = True\n    else:\n        cpus_hidden = False\n\n    while True:\n        # header\n        clean_screen()\n        cpus_percent = psutil.cpu_percent(percpu=True)\n        for i in range(num_cpus):\n            print(f\"CPU {i:<6}\", end=\"\")\n        if cpus_hidden:\n            print(\" (+ hidden)\", end=\"\")\n\n        print()\n        for _ in range(num_cpus):\n            print(f\"{cpus_percent.pop(0):<10}\", end=\"\")\n        print()\n\n        # processes\n        procs = collections.defaultdict(list)\n        for p in psutil.process_iter(['name', 'cpu_num']):\n            procs[p.info['cpu_num']].append(p.info['name'][:5])\n\n        curr_line = 3\n        while True:\n            for num in range(num_cpus):\n                try:\n                    pname = procs[num].pop()\n                except IndexError:\n                    pname = \"\"\n                print(f\"{pname[:10]:<10}\", end=\"\")\n            print()\n            curr_line += 1\n            if curr_line >= shutil.get_terminal_size()[1]:\n                break\n\n        time.sleep(1)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/disk_usage.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"List all mounted disk partitions a-la \"df -h\" command.\n\n$ python3 scripts/disk_usage.py\nDevice               Total     Used     Free  Use %      Type  Mount\n/dev/sdb3            18.9G    14.7G     3.3G    77%      ext4  /\n/dev/sda6           345.9G    83.8G   244.5G    24%      ext4  /home\n/dev/sda1           296.0M    43.1M   252.9M    14%      vfat  /boot/efi\n/dev/sda2           600.0M   312.4M   287.6M    52%   fuseblk  /media/Recovery\n\n\"\"\"\n\nimport os\nimport sys\n\nimport psutil\nfrom psutil._common import bytes2human\n\n\ndef main():\n    templ = \"{:<17} {:>8} {:>8} {:>8} {:>5}% {:>9}  {}\"\n    print(\n        templ.format(\n            \"Device\", \"Total\", \"Used\", \"Free\", \"Use \", \"Type\", \"Mount\"\n        )\n    )\n    for part in psutil.disk_partitions(all=False):\n        if os.name == 'nt':\n            if 'cdrom' in part.opts or not part.fstype:\n                # skip cd-rom drives with no disk in it; they may raise\n                # ENOENT, pop-up a Windows GUI error for a non-ready\n                # partition or just hang.\n                continue\n        usage = psutil.disk_usage(part.mountpoint)\n        line = templ.format(\n            part.device,\n            bytes2human(usage.total),\n            bytes2human(usage.used),\n            bytes2human(usage.free),\n            int(usage.percent),\n            part.fstype,\n            part.mountpoint,\n        )\n        print(line)\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "scripts/fans.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Show fans information.\n\n$ python fans.py\nasus\n    cpu_fan              3200 RPM\n\"\"\"\n\nimport sys\n\nimport psutil\n\n\ndef main():\n    if not hasattr(psutil, \"sensors_fans\"):\n        return sys.exit(\"platform not supported\")\n    fans = psutil.sensors_fans()\n    if not fans:\n        print(\"no fans detected\")\n        return None\n    for name, entries in fans.items():\n        print(name)\n        for entry in entries:\n            print(f\"    {entry.label or name:<20} {entry.current} RPM\")\n        print()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/free.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'free' cmdline utility.\n\n$ python3 scripts/free.py\n             total       used       free     shared    buffers      cache\nMem:      10125520    8625996    1499524          0     349500    3307836\nSwap:            0          0          0\n\"\"\"\n\nimport psutil\n\n\ndef main():\n    virt = psutil.virtual_memory()\n    swap = psutil.swap_memory()\n    templ = \"{:<7} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}\"\n    print(\n        templ.format(\"\", \"total\", \"used\", \"free\", \"shared\", \"buffers\", \"cache\")\n    )\n    sect = templ.format(\n        'Mem:',\n        int(virt.total / 1024),\n        int(virt.used / 1024),\n        int(virt.free / 1024),\n        int(getattr(virt, 'shared', 0) / 1024),\n        int(getattr(virt, 'buffers', 0) / 1024),\n        int(getattr(virt, 'cached', 0) / 1024),\n    )\n    print(sect)\n    sect = templ.format(\n        'Swap:',\n        int(swap.total / 1024),\n        int(swap.used / 1024),\n        int(swap.free / 1024),\n        '',\n        '',\n        '',\n    )\n    print(sect)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/ifconfig.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'ifconfig' on UNIX.\n\n$ python3 scripts/ifconfig.py\nlo:\n    stats          : speed=0MB, duplex=?, mtu=65536, up=yes\n    incoming       : bytes=1.95M, pkts=22158, errs=0, drops=0\n    outgoing       : bytes=1.95M, pkts=22158, errs=0, drops=0\n    IPv4 address   : 127.0.0.1\n         netmask   : 255.0.0.0\n    IPv6 address   : ::1\n         netmask   : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n    MAC  address   : 00:00:00:00:00:00\n\ndocker0:\n    stats          : speed=0MB, duplex=?, mtu=1500, up=yes\n    incoming       : bytes=3.48M, pkts=65470, errs=0, drops=0\n    outgoing       : bytes=164.06M, pkts=112993, errs=0, drops=0\n    IPv4 address   : 172.17.0.1\n         broadcast : 172.17.0.1\n         netmask   : 255.255.0.0\n    IPv6 address   : fe80::42:27ff:fe5e:799e%docker0\n         netmask   : ffff:ffff:ffff:ffff::\n    MAC  address   : 02:42:27:5e:79:9e\n         broadcast : ff:ff:ff:ff:ff:ff\n\nwlp3s0:\n    stats          : speed=0MB, duplex=?, mtu=1500, up=yes\n    incoming       : bytes=7.04G, pkts=5637208, errs=0, drops=0\n    outgoing       : bytes=372.01M, pkts=3200026, errs=0, drops=0\n    IPv4 address   : 10.0.0.2\n         broadcast : 10.255.255.255\n         netmask   : 255.0.0.0\n    IPv6 address   : fe80::ecb3:1584:5d17:937%wlp3s0\n         netmask   : ffff:ffff:ffff:ffff::\n    MAC  address   : 48:45:20:59:a4:0c\n         broadcast : ff:ff:ff:ff:ff:ff\n\"\"\"\n\nimport socket\n\nimport psutil\nfrom psutil._common import bytes2human\n\naf_map = {\n    socket.AF_INET: 'IPv4',\n    socket.AF_INET6: 'IPv6',\n    psutil.AF_LINK: 'MAC',\n}\n\nduplex_map = {\n    psutil.NIC_DUPLEX_FULL: \"full\",\n    psutil.NIC_DUPLEX_HALF: \"half\",\n    psutil.NIC_DUPLEX_UNKNOWN: \"?\",\n}\n\n\ndef main():\n    stats = psutil.net_if_stats()\n    io_counters = psutil.net_io_counters(pernic=True)\n    for nic, addrs in psutil.net_if_addrs().items():\n        print(f\"{nic}:\")\n        if nic in stats:\n            st = stats[nic]\n            print(\"    stats          : \", end='')\n            print(\n                \"speed={}MB, duplex={}, mtu={}, up={}\".format(\n                    st.speed,\n                    duplex_map[st.duplex],\n                    st.mtu,\n                    \"yes\" if st.isup else \"no\",\n                )\n            )\n        if nic in io_counters:\n            io = io_counters[nic]\n            print(\"    incoming       : \", end='')\n            print(\n                \"bytes={}, pkts={}, errs={}, drops={}\".format(\n                    bytes2human(io.bytes_recv),\n                    io.packets_recv,\n                    io.errin,\n                    io.dropin,\n                )\n            )\n            print(\"    outgoing       : \", end='')\n            print(\n                \"bytes={}, pkts={}, errs={}, drops={}\".format(\n                    bytes2human(io.bytes_sent),\n                    io.packets_sent,\n                    io.errout,\n                    io.dropout,\n                )\n            )\n        for addr in addrs:\n            fam = \"    {:<4}\".format(af_map.get(addr.family, addr.family))\n            print(fam, end=\"\")\n            print(f\" address   : {addr.address}\")\n            if addr.broadcast:\n                print(f\"         broadcast : {addr.broadcast}\")\n            if addr.netmask:\n                print(f\"         netmask   : {addr.netmask}\")\n            if addr.ptp:\n                print(f\"      p2p       : {addr.ptp}\")\n        print()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/README",
    "content": "This directory contains scripts which are meant to be used internally\n(benchmarks, CI automation, etc.).\n"
  },
  {
    "path": "scripts/internal/bench_oneshot.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A simple micro benchmark script which prints the speedup when using\nProcess.oneshot() ctx manager.\nSee: https://github.com/giampaolo/psutil/issues/799.\n\"\"\"\n\nimport sys\nimport textwrap\nimport timeit\n\nimport psutil\n\nITERATIONS = 1000\n\n# The list of Process methods which gets collected in one shot and\n# as such get advantage of the speedup.\nnames = [\n    'cpu_times',\n    'cpu_percent',\n    'memory_info',\n    'memory_percent',\n    'ppid',\n    'parent',\n]\n\nif psutil.POSIX:\n    names.extend(('uids', 'username'))\n\nif psutil.LINUX:\n    names += [\n        # 'memory_footprint',\n        # 'memory_maps',\n        'cpu_num',\n        'cpu_times',\n        'gids',\n        'name',\n        'num_ctx_switches',\n        'num_threads',\n        'ppid',\n        'status',\n        'terminal',\n        'uids',\n    ]\nelif psutil.BSD:\n    names = [\n        'cpu_times',\n        'gids',\n        'io_counters',\n        'memory_footprint',\n        'memory_info',\n        'name',\n        'num_ctx_switches',\n        'ppid',\n        'status',\n        'terminal',\n        'uids',\n    ]\n    if psutil.FREEBSD:\n        names.append('cpu_num')\nelif psutil.SUNOS:\n    names += [\n        'cmdline',\n        'gids',\n        'memory_footprint',\n        'memory_info',\n        'name',\n        'num_threads',\n        'ppid',\n        'status',\n        'terminal',\n        'uids',\n    ]\nelif psutil.MACOS:\n    names += [\n        'cpu_times',\n        'create_time',\n        'gids',\n        'memory_info',\n        'name',\n        'num_ctx_switches',\n        'num_threads',\n        'ppid',\n        'terminal',\n        'uids',\n    ]\nelif psutil.WINDOWS:\n    names += [\n        'num_ctx_switches',\n        'num_threads',\n        # dual implementation, called in case of AccessDenied\n        'num_handles',\n        'cpu_times',\n        'create_time',\n        'num_threads',\n        'io_counters',\n        'memory_info',\n    ]\n\nnames = sorted(set(names))\n\nsetup = textwrap.dedent(\"\"\"\n    from __main__ import names\n    import psutil\n\n    def call_normal(funs):\n        for fun in funs:\n            fun()\n\n    def call_oneshot(funs):\n        with p.oneshot():\n            for fun in funs:\n                fun()\n\n    p = psutil.Process()\n    funs = [getattr(p, n) for n in names]\n    \"\"\")\n\n\ndef main():\n    print(\n        f\"{len(names)} methods involved on platform\"\n        f\" {sys.platform!r} ({ITERATIONS} iterations, psutil\"\n        f\" {psutil.__version__}):\"\n    )\n    for name in sorted(names):\n        print(\"    \" + name)\n\n    # \"normal\" run\n    elapsed1 = timeit.timeit(\n        \"call_normal(funs)\", setup=setup, number=ITERATIONS\n    )\n    print(f\"normal:  {elapsed1:.3f} secs\")\n\n    # \"one shot\" run\n    elapsed2 = timeit.timeit(\n        \"call_oneshot(funs)\", setup=setup, number=ITERATIONS\n    )\n    print(f\"onshot:  {elapsed2:.3f} secs\")\n\n    # done\n    if elapsed2 < elapsed1:\n        print(f\"speedup: +{elapsed1 / elapsed2:.2f}x\")\n    elif elapsed2 > elapsed1:\n        print(f\"slowdown: -{elapsed2 / elapsed1:.2f}x\")\n    else:\n        print(\"same speed\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/bench_oneshot_2.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Same as bench_oneshot.py but uses perf module instead, which is\nsupposed to be more precise.\n\"\"\"\n\nimport sys\n\nimport pyperf  # requires \"pip install pyperf\"\n\nimport psutil\n\np = psutil.Process()\n\n\ndef call_normal(funs):\n    for fun in funs:\n        fun()\n\n\ndef call_oneshot(funs):\n    with p.oneshot():\n        for fun in funs:\n            fun()\n\n\ndef main():\n    from bench_oneshot import names\n\n    runner = pyperf.Runner()\n\n    args = runner.parse_args()\n    if not args.worker:\n        print(\n            f\"{len(names)} methods involved on platform\"\n            f\" {sys.platform!r} (psutil {psutil.__version__}):\"\n        )\n        for name in sorted(names):\n            print(\"    \" + name)\n\n    funs = [getattr(p, n) for n in names]\n    runner.bench_func(\"normal\", call_normal, funs)\n    runner.bench_func(\"oneshot\", call_oneshot, funs)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/convert_readme.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Strip raw HTML and other unsupported parts from README.rst\nso it renders correctly on PyPI when uploading a new release.\n\"\"\"\n\nimport argparse\nimport re\n\nquick_links = \"\"\"\\\nQuick links\n===========\n\n- `Home page <https://github.com/giampaolo/psutil>`_\n- `Documentation <https://psutil.readthedocs.io>`_\n- `Who uses psutil <https://psutil.readthedocs.io/adoption>`_\n- `Download <https://pypi.org/project/psutil/#files>`_\n- `Blog <https://gmpy.dev/tags/psutil>`_\n- `What's new <https://psutil.readthedocs.io/changelog>`_\n\"\"\"\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument('file', type=str)\n    args = parser.parse_args()\n\n    lines = []\n\n    with open(args.file) as f:\n        excluding = False\n        for line in f:\n            # Exclude sections which are not meant to be rendered on PYPI\n            if line.startswith(\".. <PYPI-EXCLUDE>\"):\n                excluding = True\n                continue\n            if line.startswith(\".. </PYPI-EXCLUDE>\"):\n                excluding = False\n                continue\n            if not excluding:\n                lines.append(line)\n\n    text = \"\".join(lines)\n\n    # Rewrite summary\n    text = re.sub(\n        r\".. raw:: html\\n+\\s+<div align[\\s\\S]*?/div>\", quick_links, text\n    )\n\n    print(text)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/download_wheels.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Script which downloads wheel files hosted on GitHub:\nhttps://github.com/giampaolo/psutil/actions\nIt needs an access token string generated from personal GitHub profile:\nhttps://github.com/settings/tokens\nThe token must be created with at least \"public_repo\" scope/rights.\nIf you lose it, just generate a new token.\nREST API doc:\nhttps://developer.github.com/v3/actions/artifacts/.\n\"\"\"\n\nimport argparse\nimport json\nimport os\nimport pathlib\nimport shutil\nimport sys\nimport zipfile\n\nimport requests\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent\nsys.path.insert(0, str(ROOT_DIR))\nfrom _bootstrap import load_module  # noqa: E402\n\n_common = load_module(ROOT_DIR / \"psutil\" / \"_common.py\")\nbytes2human = _common.bytes2human\n\nUSER = \"giampaolo\"\nPROJECT = \"psutil\"\nOUTFILE = \"wheels-github.zip\"\nTOKEN = \"\"\nTIMEOUT = 30\n\n\ndef safe_rmpath(path):\n    \"\"\"Convenience function for removing temporary test files or dirs.\"\"\"\n    if os.path.isdir(path):\n        shutil.rmtree(path)\n    else:\n        try:\n            os.remove(path)\n        except FileNotFoundError:\n            pass\n\n\ndef get_artifacts():\n    base_url = f\"https://api.github.com/repos/{USER}/{PROJECT}\"\n    url = base_url + \"/actions/artifacts\"\n    res = requests.get(\n        url=url, headers={\"Authorization\": f\"token {TOKEN}\"}, timeout=TIMEOUT\n    )\n    res.raise_for_status()\n    data = json.loads(res.content)\n    return data\n\n\ndef download_zip(url):\n    print(\"downloading: \" + url)\n    res = requests.get(\n        url=url, headers={\"Authorization\": f\"token {TOKEN}\"}, timeout=TIMEOUT\n    )\n    res.raise_for_status()\n    totbytes = 0\n    with open(OUTFILE, 'wb') as f:\n        for chunk in res.iter_content(chunk_size=16384):\n            f.write(chunk)\n            totbytes += len(chunk)\n    print(f\"got {OUTFILE}, size {bytes2human(totbytes)})\")\n\n\ndef run():\n    data = get_artifacts()\n    download_zip(data['artifacts'][0]['archive_download_url'])\n    os.makedirs('dist', exist_ok=True)\n    with zipfile.ZipFile(OUTFILE, 'r') as zf:\n        zf.extractall('dist')\n\n\ndef main():\n    global TOKEN\n    parser = argparse.ArgumentParser(description='GitHub wheels downloader')\n    parser.add_argument('--token')\n    parser.add_argument('--tokenfile')\n    args = parser.parse_args()\n\n    if args.tokenfile:\n        with open(os.path.expanduser(args.tokenfile)) as f:\n            TOKEN = f.read().strip()\n    elif args.token:\n        TOKEN = args.token\n    else:\n        return sys.exit('specify --token or --tokenfile args')\n\n    try:\n        run()\n    finally:\n        safe_rmpath(OUTFILE)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/find_adopters.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\nr\"\"\"Search GitHub for notable projects that use a given project\nas a dependency.\n\nHow it works:\n\n1. Enumerate all popular Python repos on GitHub via GraphQL\n   (paginated, >=MIN_STARS).\n2. Batch-fetch data needed for the requested filters\n   (README content, dependency files).\n3. Apply filters. Only repos passing ALL specified filters\n   are confirmed. Available filters:\n   - --inreadme : PROJECT mentioned in the README\n   - --indeps   : PROJECT mentioned in dep files\n     (pyproject.toml, setup.py, setup.cfg, requirements*.txt)\n\nAt least one filter must be specified.\n\nOutput is RsT formatted, ready to paste into docs/adoption.rst.\n\nUsage:\n    python3 scripts/internal/find_adopters.py \\\n        --project=psutil \\\n        --token=~/.github.api.key \\\n        --skip-file-urls=docs/adoption.rst \\\n        --min-stars=10000 --indeps\n\"\"\"\n\nimport argparse\nimport os\nimport pickle\nimport re\nimport sys\nimport time\n\nimport requests\n\nfrom psutil._common import hilite\nfrom psutil._common import print_color\n\nGITHUB_GRAPHQL = \"https://api.github.com/graphql\"\n_CACHE_FILE = \".find_adopters.cache\"\n\n# Set by parse_cli().\nPROJECT = \"\"\nMIN_STARS = 0\nMAX_STARS = 0\nTOKEN = \"\"\nSKIP = set()\nINREADME = False\nINDEPS = False\nNO_CACHE = False\n\n# Fixed files to check for dependency declarations.\n_FIXED_DEP_FILES = {\n    \"pyproject.toml\": \"pyprojectToml\",\n    \"setup.py\": \"setupPy\",\n    \"setup.cfg\": \"setupCfg\",\n    \"requirements.txt\": \"requirementsTxt\",\n}\n\n# Max repos to batch in a single GraphQL query.\n_BATCH_SIZE = 5\n\n\ngreen = lambda msg: hilite(msg, color=\"green\")  # noqa: E731\nyellow = lambda msg: hilite(msg, color=\"yellow\")  # noqa: E731\n\n\ndef stderr(msg=\"\", color=None):\n    if color:\n        print_color(msg, color=color, file=sys.stderr)\n    else:\n        print(msg, file=sys.stderr)\n\n\ndef graphql(session, query, variables=None):\n    \"\"\"Execute a GraphQL query. Returns the 'data' dict.\"\"\"\n    payload = {\"query\": query}\n    if variables:\n        payload[\"variables\"] = variables\n    try:\n        resp = session.post(GITHUB_GRAPHQL, json=payload)\n    except requests.exceptions.RequestException as err:\n        stderr(f\"  GraphQL request error: {err}\")\n        return None\n    if resp.status_code != 200:\n        stderr(f\"  GraphQL HTTP error: {resp.status_code} {resp.text}\")\n        return None\n    body = resp.json()\n    if \"errors\" in body:\n        for err in body[\"errors\"]:\n            stderr(f\"  GraphQL error: {err.get('message', err)}\")\n        return None\n    return body.get(\"data\")\n\n\ndef get_session(token):\n    s = requests.Session()\n    s.headers[\"Authorization\"] = f\"Bearer {token}\"\n    s.headers[\"Content-Type\"] = \"application/json\"\n    return s\n\n\n# --- Enumerate repos ---\n\n\ndef enumerate_repos(session):\n    \"\"\"Enumerate all Python repos with >=MIN_STARS on GitHub.\"\"\"\n    stars_q = f\"stars:>={MIN_STARS}\"\n    if MAX_STARS:\n        stars_q = f\"stars:{MIN_STARS}..{MAX_STARS}\"\n    search_q = f\"language:Python {stars_q}\"\n    query = \"\"\"\n    query($q: String!, $first: Int!, $after: String) {\n      search(query: $q, type: REPOSITORY,\n             first: $first, after: $after) {\n        repositoryCount\n        edges {\n          node {\n            ... on Repository {\n              nameWithOwner\n              owner { login }\n              name\n              url\n              description\n              stargazerCount\n              isArchived\n            }\n          }\n        }\n        pageInfo {\n          hasNextPage\n          endCursor\n        }\n      }\n    }\n    \"\"\"\n    results = []\n    cursor = None\n    page = 0\n    while True:\n        page += 1\n        variables = {\n            \"q\": search_q,\n            \"first\": 100,\n            \"after\": cursor,\n        }\n        data = graphql(session, query, variables)\n        if data is None:\n            break\n        search_data = data[\"search\"]\n        edges = search_data[\"edges\"]\n        if not edges:\n            break\n        for edge in edges:\n            node = edge[\"node\"]\n            if not node:\n                continue\n            results.append({\n                \"full_name\": node[\"nameWithOwner\"],\n                \"owner\": node[\"owner\"][\"login\"],\n                \"repo\": node[\"name\"],\n                \"stars\": node[\"stargazerCount\"],\n                \"description\": node.get(\"description\") or \"\",\n                \"html_url\": node[\"url\"],\n                \"archived\": node[\"isArchived\"],\n            })\n        stderr(\n            f\"  page {page}: got {len(edges)} repos \"\n            f\"(total so far: {len(results)})\"\n        )\n        page_info = search_data[\"pageInfo\"]\n        if not page_info[\"hasNextPage\"]:\n            break\n        cursor = page_info[\"endCursor\"]\n        time.sleep(1)\n    return results\n\n\n# --- Fetch README ---\n\n\ndef fetch_readmes(session, repos):\n    \"\"\"Batch-fetch README content for a list of repos.\n\n    Returns a dict mapping full_name to README text\n    (or empty string if not found).\n    \"\"\"\n    result = {}\n    total = len(repos)\n    stderr(f\"  fetching READMEs ({total} repos)...\")\n    for start in range(0, total, _BATCH_SIZE):\n        batch = repos[start : start + _BATCH_SIZE]\n        stderr(f\"    batch {start + 1}-{start + len(batch)}/{total}...\")\n        repo_parts = []\n        for i, c in enumerate(batch):\n            # Try both README.md and README.rst.\n            repo_parts.append(\n                f\"  repo{i}: repository(\"\n                f'owner: \"{c[\"owner\"]}\", '\n                f'name: \"{c[\"repo\"]}\") {{\\n'\n                f\"    readmeMd: object(\"\n                f'expression: \"HEAD:README.md\") {{\\n'\n                f\"      ... on Blob {{ text }}\\n\"\n                f\"    }}\\n\"\n                f\"    readmeRst: object(\"\n                f'expression: \"HEAD:README.rst\") {{\\n'\n                f\"      ... on Blob {{ text }}\\n\"\n                f\"    }}\\n\"\n                f\"  }}\"\n            )\n        query = \"query {\\n\" + \"\\n\".join(repo_parts) + \"\\n}\"\n        data = graphql(session, query)\n        if data is None:\n            for c in batch:\n                result[c[\"full_name\"]] = \"\"\n            continue\n        for i, c in enumerate(batch):\n            repo_data = data.get(f\"repo{i}\")\n            text = \"\"\n            if repo_data:\n                for key in (\"readmeMd\", \"readmeRst\"):\n                    obj = repo_data.get(key)\n                    if obj and \"text\" in obj:\n                        text = obj[\"text\"]\n                        break\n            result[c[\"full_name\"]] = text\n    return result\n\n\n# --- Fetch dep files ---\n\n\ndef _build_fixed_dep_fragment():\n    \"\"\"Build GraphQL fields for fetching fixed dep files\n    + requirements/ dir listing.\n    \"\"\"\n    fields = []\n    for dep_file, alias in _FIXED_DEP_FILES.items():\n        fields.append(\n            f\"    {alias}: object(\"\n            f'expression: \"HEAD:{dep_file}\") {{\\n'\n            f\"      ... on Blob {{ text }}\\n\"\n            f\"    }}\"\n        )\n    # Also fetch the requirements/ directory listing.\n    fields.append(\n        \"    requirementsDir: object(\"\n        \"expression: \\\"HEAD:requirements\\\") {\\n\"  # noqa: Q003\n        \"      ... on Tree { entries { name } }\\n\"\n        \"    }\"\n    )\n    return \"\\n\".join(fields)\n\n\ndef _make_req_alias(filename):\n    \"\"\"Turn a requirements/*.txt filename into a GraphQL alias.\"\"\"\n    base = filename.replace(\".txt\", \"\")\n    base = re.sub(r\"[^a-zA-Z0-9]\", \"_\", base)\n    return f\"req_{base}\"\n\n\ndef fetch_dep_files(session, repos):\n    \"\"\"Batch-fetch dependency files for a list of repos.\n\n    Phase 1: fetch fixed dep files + requirements/ dir listing.\n    Phase 2: for repos with a requirements/ dir, fetch all *.txt\n    files found there.\n\n    Returns a dict mapping full_name to a dict of\n    {dep_file: content}.\n    \"\"\"\n    fixed_fragment = _build_fixed_dep_fragment()\n    result = {}\n    # Track which repos have requirements/ entries.\n    req_dir_entries = {}  # full_name -> [filename, ...]\n\n    # --- Phase 1: fixed files + dir listing ---\n    total = len(repos)\n    stderr(\n        \"  phase 1: fixed files + requirements/ dir listing \"\n        f\"({total} repos)...\"\n    )\n    for start in range(0, total, _BATCH_SIZE):\n        batch = repos[start : start + _BATCH_SIZE]\n        stderr(f\"    batch {start + 1}-{start + len(batch)}/{total}...\")\n        repo_parts = []\n        for i, c in enumerate(batch):\n            repo_parts.append(\n                f\"  repo{i}: repository(\"\n                f'owner: \"{c[\"owner\"]}\", '\n                f'name: \"{c[\"repo\"]}\") {{\\n'\n                f\"{fixed_fragment}\\n\"\n                f\"  }}\"\n            )\n        query = \"query {\\n\" + \"\\n\".join(repo_parts) + \"\\n}\"\n        data = graphql(session, query)\n        if data is None:\n            for c in batch:\n                result[c[\"full_name\"]] = {}\n            continue\n        for i, c in enumerate(batch):\n            repo_data = data.get(f\"repo{i}\")\n            files = {}\n            if repo_data:\n                for dep_file, alias in _FIXED_DEP_FILES.items():\n                    obj = repo_data.get(alias)\n                    if obj and \"text\" in obj:\n                        files[dep_file] = obj[\"text\"]\n                # Check for requirements/ directory.\n                req_obj = repo_data.get(\"requirementsDir\")\n                if req_obj and \"entries\" in req_obj:\n                    txt_files = [\n                        e[\"name\"]\n                        for e in req_obj[\"entries\"]\n                        if e[\"name\"].endswith(\".txt\")\n                    ]\n                    if txt_files:\n                        req_dir_entries[c[\"full_name\"]] = txt_files\n            result[c[\"full_name\"]] = files\n\n    # --- Phase 2: fetch discovered requirements/*.txt files ---\n    if req_dir_entries:\n        # Collect repos that need follow-up.\n        need_fetch = [c for c in repos if c[\"full_name\"] in req_dir_entries]\n        stderr(\n            \"  phase 2: fetching requirements/*.txt from \"\n            f\"{len(need_fetch)} repos...\"\n        )\n        for start in range(0, len(need_fetch), _BATCH_SIZE):\n            batch = need_fetch[start : start + _BATCH_SIZE]\n            repo_parts = []\n            for i, c in enumerate(batch):\n                txt_files = req_dir_entries[c[\"full_name\"]]\n                file_fields = []\n                for fname in txt_files:\n                    alias = _make_req_alias(fname)\n                    path = f\"requirements/{fname}\"\n                    file_fields.append(\n                        f\"      {alias}: object(\"\n                        f'expression: \"HEAD:{path}\") {{\\n'\n                        f\"        ... on Blob {{ text }}\\n\"\n                        f\"      }}\"\n                    )\n                repo_parts.append(\n                    f\"  repo{i}: repository(\"\n                    f'owner: \"{c[\"owner\"]}\", '\n                    f'name: \"{c[\"repo\"]}\") {{\\n'\n                    + \"\\n\".join(file_fields)\n                    + \"\\n  }\"\n                )\n            query = \"query {\\n\" + \"\\n\".join(repo_parts) + \"\\n}\"\n            data = graphql(session, query)\n            if data is None:\n                continue\n            for i, c in enumerate(batch):\n                repo_data = data.get(f\"repo{i}\")\n                if not repo_data:\n                    continue\n                txt_files = req_dir_entries[c[\"full_name\"]]\n                for fname in txt_files:\n                    alias = _make_req_alias(fname)\n                    obj = repo_data.get(alias)\n                    if obj and \"text\" in obj:\n                        path = f\"requirements/{fname}\"\n                        result[c[\"full_name\"]][path] = obj[\"text\"]\n\n    return result\n\n\n# --- Classify ---\n\n\ndef classify_dependency(file_contents):\n    \"\"\"Classify the dependency type from fetched file contents.\n\n    Returns a tuple (status, detail) where status is one of:\n    - \"direct\"    : PROJECT in install/runtime dependencies\n    - \"build\"     : PROJECT in build/setup dependencies only\n    - \"test\"      : PROJECT in test/dev dependencies only\n    - \"optional\"  : PROJECT in optional/extras dependencies\n    - \"no\"        : not found in any dependency file\n    \"\"\"\n    pat = re.escape(PROJECT)\n    found_in = []\n    for dep_file, content in file_contents.items():\n        if content is None:\n            continue\n        if not re.search(r\"\\b\" + pat + r\"\\b\", content):\n            continue\n        found_in.append(dep_file)\n\n    if not found_in:\n        return \"no\", \"\"\n\n    # Classify the dependency type based on which files it was\n    # found in.\n    for f in found_in:\n        content = file_contents[f]\n        if f == \"pyproject.toml\":\n            if re.search(\n                r\"\\[project\\].*?dependencies\\s*=\\s*\\[.*?\" + pat,\n                content,\n                re.DOTALL,\n            ):\n                return \"direct\", f\n            if re.search(\n                r\"\\[tool\\.poetry\\.dependencies\\].*?\" + pat,\n                content,\n                re.DOTALL,\n            ):\n                return \"direct\", f\n            if re.search(\n                r\"\\[build-system\\].*?requires\\s*=\\s*\\[.*?\" + pat,\n                content,\n                re.DOTALL,\n            ):\n                return \"build\", f\n            if r\"optional-dependencies\" in content:\n                return \"optional\", f\n            if re.search(r\"test|dev\", content):\n                return \"test\", f\n            return \"direct\", f\n        elif f == \"setup.py\":\n            if re.search(r\"install_requires.*?\" + pat, content, re.DOTALL):\n                return \"direct\", f\n            if re.search(r\"setup_requires.*?\" + pat, content, re.DOTALL):\n                return \"build\", f\n            if re.search(r\"tests_require.*?\" + pat, content, re.DOTALL):\n                return \"test\", f\n            if re.search(r\"extras_require.*?\" + pat, content, re.DOTALL):\n                return \"optional\", f\n            return \"direct\", f\n        elif f == \"setup.cfg\":\n            if re.search(r\"install_requires.*?\" + pat, content, re.DOTALL):\n                return \"direct\", f\n            if re.search(r\"extras_require.*?\" + pat, content, re.DOTALL):\n                return \"optional\", f\n            return \"direct\", f\n        elif \"requirements\" in f:\n            return \"direct\", f\n\n    return \"direct\", \", \".join(found_in)\n\n\n# --- Misc ---\n\n\ndef make_subst_name(full_name):\n    \"\"\"Turn 'owner/repo' into a substitution-safe base name.\"\"\"\n    name = full_name.split(\"/\")[1]\n    # Replace underscores and dots with hyphens.\n    name = re.sub(r\"[_.]\", \"-\", name)\n    return name.lower()\n\n\ndef tier_label(stars):\n    if stars >= 40000:\n        return 1\n    elif stars >= 10000:\n        return 2\n    else:\n        return 3\n\n\ndef generate_rst(projects):\n    \"\"\"Generate RST output for adoption.rst.\"\"\"\n    tiers = {1: [], 2: [], 3: []}\n    for p in projects:\n        t = tier_label(p[\"stars\"])\n        tiers[t].append(p)\n\n    lines = []\n    star_badges = []\n    logo_images = []\n\n    tier_headers = {\n        1: \"Tier 1 (>40k GitHub stars)\",\n        2: \"Tier 2 (10k-40k GitHub stars)\",\n        3: \"Tier 3 (1k-10k GitHub stars)\",\n    }\n\n    for tier_num in (1, 2, 3):\n        tier_projects = sorted(tiers[tier_num], key=lambda x: -x[\"stars\"])\n        if not tier_projects:\n            continue\n\n        header = tier_headers[tier_num]\n        lines.extend([\n            header,\n            \"-\" * len(header),\n            \"\",\n            \".. list-table::\",\n            \"   :header-rows: 1\",\n            \"   :widths: 18 42 12 28\",\n            \"\",\n            \"   * - Project\",\n            \"     - Description\",\n            \"     - Stars\",\n            \"     - Usage\",\n        ])\n\n        for p in tier_projects:\n            name = make_subst_name(p[\"full_name\"])\n            owner = p[\"owner\"]\n            repo = p[\"repo\"]\n            full = p[\"full_name\"]\n            desc = p[\"description\"]\n            # Truncate description to fit RST table.\n            if len(desc) > 60:\n                desc = desc[:57] + \"...\"\n            dep_type = p.get(\"dep_type\", \"\")\n            usage = \"\"\n            if dep_type == \"build\":\n                usage = \"build-time dependency\"\n            elif dep_type == \"test\":\n                usage = \"test dependency\"\n            elif dep_type == \"optional\":\n                usage = \"optional dependency\"\n\n            proj_link = f\"|{name}-logo| `{repo} <https://github.com/{full}>`__\"\n            lines.extend([\n                f\"   * - {proj_link}\",\n                f\"     - {desc}\",\n                f\"     - |{name}-stars|\",\n                f\"     - {usage}\",\n            ])\n\n            star_badges.append(\n                f\".. |{name}-stars| image:: \"\n                \"https://img.shields.io/github/stars/\"\n                f\"{full}.svg?style=plastic\"\n            )\n\n            logo_images.append(\n                f\".. |{name}-logo| image:: \"\n                f\"https://github.com/{owner}.png?s=28 :height: 28\"\n            )\n\n        lines.append(\"\")\n\n    # Combine everything.\n    output = []\n    output.extend(lines)\n    output.extend([\n        \"\",\n        \".. Star badges\",\n        \"\",\n    ])\n    output.extend(star_badges)\n    output.extend([\n        \"\",\n        \".. Logo images\",\n        \"\",\n    ])\n    output.extend(logo_images)\n    return \"\\n\".join(output)\n\n\n# --- Cache ---\n\n\ndef load_cache():\n    \"\"\"Load cached data from disk.\n\n    Returns a dict with keys: min_stars, repos, readmes, dep_files.\n    Returns None if cache is missing, stale, or --no-cache is set.\n    The cache is invalidated if the current MIN_STARS is lower\n    than the min_stars used to build the cache (we'd be missing\n    repos).\n    \"\"\"\n    if NO_CACHE:\n        return None\n    if not os.path.exists(_CACHE_FILE):\n        return None\n    try:\n        with open(_CACHE_FILE, \"rb\") as f:\n            data = pickle.load(f)\n    except (OSError, pickle.UnpicklingError, EOFError) as err:\n        stderr(f\"  cache load error: {err}\")\n        return None\n    cached_min_stars = data.get(\"min_stars\", 0)\n    if cached_min_stars > MIN_STARS:\n        stderr(\n            f\"  cache built with min_stars={cached_min_stars}, \"\n            f\"but current min_stars={MIN_STARS}; ignoring cache\"\n        )\n        return None\n    stderr(\n        f\"  loaded cache ({len(data.get('repos', []))} repos, \"\n        f\"min_stars={cached_min_stars})\"\n    )\n    return data\n\n\ndef save_cache(repos, readmes, dep_files):\n    \"\"\"Save fetched data to disk.\"\"\"\n    data = {\n        \"min_stars\": MIN_STARS,\n        \"repos\": repos,\n        \"readmes\": readmes,\n        \"dep_files\": dep_files,\n    }\n    with open(_CACHE_FILE, \"wb\") as f:\n        pickle.dump(data, f)\n    stderr(f\"  saved cache to {_CACHE_FILE}\")\n\n\n# --- CLI ---\n\n\ndef parse_cli():\n    \"\"\"Parse CLI arguments and set global constants.\"\"\"\n    global PROJECT, MIN_STARS, MAX_STARS, TOKEN, SKIP, INREADME, INDEPS, NO_CACHE  # noqa: E501\n\n    parser = argparse.ArgumentParser(\n        description=(\n            \"Find notable GitHub projects using a given \"\n            \"project as a dependency.\"\n        )\n    )\n    parser.add_argument(\n        \"--project\",\n        required=True,\n        help=\"Project name to search for (e.g. 'psutil').\",\n    )\n    parser.add_argument(\n        \"--min-stars\",\n        type=int,\n        default=300,\n        help=\"Minimum GitHub stars to consider (default: 300).\",\n    )\n    parser.add_argument(\n        \"--max-stars\",\n        type=int,\n        default=0,\n        help=\"Maximum GitHub stars (default: no limit).\",\n    )\n    parser.add_argument(\n        \"--token\",\n        required=True,\n        help=\"Path to a file containing the GitHub token.\",\n    )\n    parser.add_argument(\n        \"--skip\",\n        nargs=\"*\",\n        default=[],\n        help=\"Repos URLs to skip.\",\n    )\n    parser.add_argument(\n        \"--skip-file-urls\",\n        default=None,\n        help=\"Path to file with GitHub repo URLs to skip (found via regex).\",\n    )\n    parser.add_argument(\n        \"--inreadme\",\n        action=\"store_true\",\n        default=False,\n        help=\"Filter: PROJECT must be mentioned in README.\",\n    )\n    parser.add_argument(\n        \"--indeps\",\n        action=\"store_true\",\n        default=False,\n        help=(\n            \"Filter: PROJECT must be mentioned in dep files \"\n            \"(pyproject.toml, setup.py, setup.cfg, \"\n            \"requirements*.txt).\"\n        ),\n    )\n    parser.add_argument(\n        \"--no-cache\",\n        action=\"store_true\",\n        default=False,\n        help=\"Force fresh fetch, ignoring cached data.\",\n    )\n    args = parser.parse_args()\n\n    if not args.inreadme and not args.indeps:\n        parser.error(\"at least one of --inreadme, --indeps is required\")\n\n    PROJECT = args.project\n    MIN_STARS = args.min_stars\n    MAX_STARS = args.max_stars\n    with open(os.path.expanduser(args.token)) as f:\n        TOKEN = f.read().strip()\n    INREADME = args.inreadme\n    INDEPS = args.indeps\n\n    SKIP = set(args.skip)\n    SKIP.add(\"https://github.com/vinta/awesome-python\")\n    NO_CACHE = args.no_cache\n    if args.skip_file_urls:\n        path = args.skip_file_urls\n        with open(path) as f:\n            text = f.read()\n        urls = [\n            m.group(1)\n            for m in re.finditer(\n                r\"(https://github\\.com/[\\w.-]+/[\\w.-]+)\", text\n            )\n        ]\n        SKIP.update(urls)\n\n\n# --- Main ---\n\n\ndef main():\n    parse_cli()\n    session = get_session(TOKEN)\n\n    cached = load_cache()\n    if cached is not None:\n        repos = cached[\"repos\"]\n        readmes = cached.get(\"readmes\", {})\n        dep_files = cached.get(\"dep_files\", {})\n        need_save = False\n    else:\n        repos = None\n        readmes = {}\n        dep_files = {}\n        need_save = True\n\n    # Step 1: enumerate all popular Python repos.\n    if repos is None:\n        stderr(f\"Enumerating Python repos (>={MIN_STARS} stars)...\")\n        repos = enumerate_repos(session)\n        stderr(f\"Found {len(repos)} repos.\")\n\n    # Filter out skipped and archived repos.\n    filtered = []\n    for repo in repos:\n        if repo[\"html_url\"] in SKIP:\n            print(f\"skipping {yellow(repo['html_url'])}\")\n            continue\n        if repo[\"archived\"]:\n            print(f\"skipping {yellow(repo['html_url'])}\")\n            continue\n        filtered.append(repo)\n    active_repos = filtered\n    stderr(f\"After skip/archive filtering: {len(active_repos)} repos.\")\n\n    # Step 2: fetch data needed for the requested filters.\n    # Only fetch what's missing from cache.\n    if INREADME and not readmes:\n        stderr(\"Fetching READMEs...\")\n        readmes = fetch_readmes(session, active_repos)\n        need_save = True\n    if INDEPS and not dep_files:\n        stderr(\"Fetching dependency files...\")\n        dep_files = fetch_dep_files(session, active_repos)\n        need_save = True\n\n    if need_save:\n        save_cache(repos, readmes, dep_files)\n\n    # Step 3: apply filters.\n    confirmed = []\n    for repo in active_repos:\n        name = repo[\"full_name\"]\n        pat = re.escape(PROJECT)\n\n        if INREADME:\n            readme = readmes.get(name, \"\")\n            if not re.search(r\"\\b\" + pat + r\"\\b\", readme):\n                continue\n\n        if INDEPS:\n            files = dep_files.get(name, {})\n            status, detail = classify_dependency(files)\n            if status == \"no\":\n                continue\n            repo[\"dep_type\"] = status\n            repo[\"dep_detail\"] = detail\n\n        confirmed.append(repo)\n\n    stderr()\n    stderr(f\"Confirmed {len(confirmed)} projects:\")\n    for c in confirmed:\n        stderr(f\"  {c['stars']:,} stars: {green(c['html_url'])}\")\n\n    # Generate RST.\n    if confirmed:\n        ans = input(\"\\nGenerate RsT content? [y/N] \").strip().lower()\n        if ans in {\"y\", \"yes\"}:\n            rst = generate_rst(confirmed)\n            print(rst)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/find_broken_links.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola', Himanshu Shekhar.\n# All rights reserved. Use of this source code is governed by a\n# BSD-style license that can be found in the LICENSE file.\n\n\"\"\"Checks for broken links in file names specified as command line\nparameters.\n\nThere are a ton of a solutions available for validating URLs in string\nusing regex, but less for searching, of which very few are accurate.\nThis snippet is intended to just do the required work, and avoid\ncomplexities. Django Validator has pretty good regex for validation,\nbut we have to find urls instead of validating them (REFERENCES [7]).\nThere's always room for improvement.\n\nMethod:\n* Match URLs using regex (REFERENCES [1]])\n* Some URLs need to be fixed, as they have < (or) > due to inefficient\n  regex.\n* Remove duplicates (because regex is not 100% efficient as of now).\n* Check validity of URL, using HEAD request. (HEAD to save bandwidth)\n  Uses requests module for others are painful to use. REFERENCES[9]\n  Handles redirects, http, https, ftp as well.\n\nREFERENCES:\nUsing [1] with some modifications for including ftp\n[1] http://stackoverflow.com/a/6883094/5163807\n[2] http://stackoverflow.com/a/31952097/5163807\n[3] http://daringfireball.net/2010/07/improved_regex_for_matching_urls\n[4] https://mathiasbynens.be/demo/url-regex\n[5] https://github.com/django/django/blob/master/django/core/validators.py\n[6] https://data.iana.org/TLD/tlds-alpha-by-domain.txt\n[7] https://codereview.stackexchange.com/questions/19663/http-url-validating\n[8] https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD\n[9] http://docs.python-requests.org/\n\nAuthor: Himanshu Shekhar <https://github.com/himanshub16> (2017)\n\"\"\"\n\nimport argparse\nimport concurrent.futures\nimport functools\nimport os\nimport re\nimport sys\nimport traceback\n\nimport requests\n\nREGEX = re.compile(\n    r'(?:http|ftp|https)?://'\n    r'(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'\n)\nREQUEST_TIMEOUT = 15\n# There are some status codes sent by websites on HEAD request.\n# Like 503 by Microsoft, and 401 by Apple\n# They need to be sent GET request\nRETRY_STATUSES = [503, 401, 403]\n\n\ndef memoize(fun):\n    \"\"\"A memoize decorator.\"\"\"\n\n    @functools.wraps(fun)\n    def wrapper(*args, **kwargs):\n        key = (args, frozenset(sorted(kwargs.items())))\n        try:\n            return cache[key]\n        except KeyError:\n            ret = cache[key] = fun(*args, **kwargs)\n            return ret\n\n    cache = {}\n    return wrapper\n\n\ndef sanitize_url(url):\n    url = url.rstrip(',')\n    url = url.rstrip('.')\n    url = url.lstrip('(')\n    url = url.rstrip(')')\n    url = url.lstrip('[')\n    url = url.rstrip(']')\n    url = url.lstrip('<')\n    url = url.rstrip('>')\n    return url\n\n\ndef find_urls(s):\n    matches = REGEX.findall(s) or []\n    return list({sanitize_url(x) for x in matches})\n\n\ndef parse_rst(fname):\n    \"\"\"Look for links in a .rst file.\"\"\"\n    with open(fname) as f:\n        text = f.read()\n    return find_urls(text)\n\n\ndef parse_py(fname):\n    \"\"\"Look for links in a .py file.\"\"\"\n    with open(fname) as f:\n        lines = f.readlines()\n    urls = set()\n    for i, line in enumerate(lines):\n        for url in find_urls(line):\n            # comment block\n            if line.lstrip().startswith('# '):\n                subidx = i + 1\n                while True:\n                    nextline = lines[subidx].strip()\n                    if re.match(r\"^#     .+\", nextline):\n                        url += nextline[1:].strip()\n                    else:\n                        break\n                    subidx += 1\n            urls.add(url)\n    return list(urls)\n\n\ndef parse_c(fname):\n    \"\"\"Look for links in a .py file.\"\"\"\n    with open(fname) as f:\n        lines = f.readlines()\n    urls = set()\n    for i, line in enumerate(lines):\n        for url in find_urls(line):\n            # comment block //\n            if line.lstrip().startswith('// '):\n                subidx = i + 1\n                while True:\n                    nextline = lines[subidx].strip()\n                    if re.match(r\"^//     .+\", nextline):\n                        url += nextline[2:].strip()\n                    else:\n                        break\n                    subidx += 1\n            # comment block /*\n            elif line.lstrip().startswith('* '):\n                subidx = i + 1\n                while True:\n                    nextline = lines[subidx].strip()\n                    if re.match(r'^\\*     .+', nextline):\n                        url += nextline[1:].strip()\n                    else:\n                        break\n                    subidx += 1\n            urls.add(url)\n    return list(urls)\n\n\ndef parse_generic(fname):\n    with open(fname, errors='ignore') as f:\n        text = f.read()\n    return find_urls(text)\n\n\ndef get_urls(fname):\n    \"\"\"Extracts all URLs in fname and return them as a list.\"\"\"\n    if fname.endswith('.rst'):\n        return parse_rst(fname)\n    elif fname.endswith('.py'):\n        return parse_py(fname)\n    elif fname.endswith(('.c', '.h')):\n        return parse_c(fname)\n    else:\n        with open(fname, errors='ignore') as f:\n            if f.readline().strip().startswith('#!/usr/bin/env python3'):\n                return parse_py(fname)\n        return parse_generic(fname)\n\n\n@functools.lru_cache\ndef validate_url(url):\n    \"\"\"Validate the URL by attempting an HTTP connection.\n    Makes an HTTP-HEAD request for each URL.\n    \"\"\"\n    try:\n        res = requests.head(url, timeout=REQUEST_TIMEOUT)\n        # some websites deny 503, like Microsoft\n        # and some send 401, like Apple, observations\n        if (not res.ok) and (res.status_code in RETRY_STATUSES):\n            res = requests.get(url, timeout=REQUEST_TIMEOUT)\n        return res.ok\n    except requests.exceptions.RequestException:\n        return False\n\n\ndef parallel_validator(urls):\n    \"\"\"Validates all urls in parallel\n    urls: tuple(filename, url).\n    \"\"\"\n    fails = []  # list of tuples (filename, url)\n    current = 0\n    total = len(urls)\n    with concurrent.futures.ThreadPoolExecutor() as executor:\n        fut_to_url = {\n            executor.submit(validate_url, url[1]): url for url in urls\n        }\n        for fut in concurrent.futures.as_completed(fut_to_url):\n            current += 1\n            sys.stdout.write(f\"\\r{current} / {total}\")\n            sys.stdout.flush()\n            fname, url = fut_to_url[fut]\n            try:\n                ok = fut.result()\n            except Exception:  # noqa: BLE001\n                fails.append((fname, url))\n                print()\n                print(f\"warn: error while validating {url}\", file=sys.stderr)\n                traceback.print_exc()\n            else:\n                if not ok:\n                    fails.append((fname, url))\n\n    print()\n    return fails\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=__doc__, formatter_class=argparse.RawTextHelpFormatter\n    )\n    parser.add_argument('files', nargs=\"+\")\n    parser.parse_args()\n    args = parser.parse_args()\n\n    all_urls = []\n    for fname in args.files:\n        urls = get_urls(fname)\n        if urls:\n            print(f\"{len(urls):4} {fname}\")\n            all_urls.extend((fname, url) for url in urls)\n\n    fails = parallel_validator(all_urls)\n    if not fails:\n        print(\"all links are valid; cheers!\")\n    else:\n        for fail in fails:\n            fname, url = fail\n            print(f\"{fname:<30}: {url} \")\n        print('-' * 20)\n        print(f\"total: {len(fails)} fails!\")\n        sys.exit(1)\n\n\nif __name__ == '__main__':\n    try:\n        main()\n    except (KeyboardInterrupt, SystemExit):\n        os._exit(0)\n"
  },
  {
    "path": "scripts/internal/generate_manifest.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Generate MANIFEST.in file.\"\"\"\n\nimport os\nimport shlex\nimport subprocess\n\nSKIP_EXTS = ('.png', '.jpg', '.jpeg', '.svg')\nSKIP_FILES = ()\nSKIP_PREFIXES = ('.ci/', '.github/')\n\n\ndef sh(cmd):\n    return subprocess.check_output(\n        shlex.split(cmd), universal_newlines=True\n    ).strip()\n\n\ndef main():\n    files = set()\n    for file in sh(\"git ls-files\").split('\\n'):\n        if (\n            file.startswith(SKIP_PREFIXES)\n            or os.path.splitext(file)[1].lower() in SKIP_EXTS\n            or file in SKIP_FILES\n        ):\n            continue\n        files.add(file)\n\n    for file in sorted(files):\n        print(\"include \" + file)\n\n    print(\"recursive-exclude docs/_static *\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/git_pre_commit.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"This gets executed on 'git commit' and rejects the commit in case\nthe submitted code does not pass validation. Validation is run only\nagainst the files which were modified in the commit.\n\"\"\"\n\nimport os\nimport pathlib\nimport shlex\nimport subprocess\nimport sys\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent\nsys.path.insert(0, str(ROOT_DIR))\nfrom _bootstrap import load_module  # noqa: E402\n\n_common = load_module(ROOT_DIR / \"psutil\" / \"_common.py\")\nhilite = _common.hilite\n\nPYTHON = sys.executable\n\n\ndef log(msg=\"\", color=None, bold=None):\n    msg = \"Git pre-commit > \" + msg\n    if msg:\n        msg = hilite(msg, color=color, bold=bold, force_color=True)\n    print(msg, flush=True)\n\n\ndef exit_with(msg):\n    log(msg + \" Commit aborted.\", color=\"red\")\n    sys.exit(1)\n\n\ndef sh(cmd):\n    if isinstance(cmd, str):\n        cmd = shlex.split(cmd)\n    p = subprocess.Popen(\n        cmd,\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n        universal_newlines=True,\n    )\n    stdout, stderr = p.communicate()\n    if p.returncode != 0:\n        raise RuntimeError(stderr)\n    if stderr:\n        log(stderr)\n    return stdout.rstrip()\n\n\ndef git_commit_files():\n    out = [\n        f\n        for f in sh([\"git\", \"diff\", \"--cached\", \"--name-only\"]).splitlines()\n        if os.path.exists(f)\n    ]\n\n    py = [f for f in out if f.endswith(\".py\")]\n    c = [f for f in out if f.endswith((\".c\", \".h\"))]\n    rst = [f for f in out if f.endswith(\".rst\")]\n    toml = [f for f in out if f.endswith(\".toml\")]\n    new_rm_mv = sh(\n        [\"git\", \"diff\", \"--name-only\", \"--diff-filter=ADR\", \"--cached\"]\n    ).split()\n    return py, c, rst, toml, new_rm_mv\n\n\ndef lint_manifest():\n    out = sh([PYTHON, \"scripts/internal/generate_manifest.py\"])\n    with open(\"MANIFEST.in\", encoding=\"utf8\") as f:\n        if out.strip() != f.read().strip():\n            exit_with(\n                \"Some files were added, deleted or renamed. \"\n                \"Run 'make generate-manifest' and commit again.\"\n            )\n\n\ndef run_make(target, files):\n    ls = \", \".join([os.path.basename(x) for x in files])\n    plural = \"s\" if len(files) > 1 else \"\"\n    msg = f\"Running 'make {target}' against {len(files)} file{plural}: {ls}\"\n    log(msg, color=\"lightblue\")\n    files = \"FILES=\" + \" \".join(shlex.quote(f) for f in files)\n    if subprocess.call([\"make\", target, files]) != 0:\n        exit_with(f\"'make {target}' failed.\")\n\n\ndef main():\n    py, c, rst, toml, new_rm_mv = git_commit_files()\n    if py:\n        run_make(\"black\", py)\n        run_make(\"ruff\", py)\n    if c:\n        run_make(\"lint-c\", c)\n    if rst:\n        run_make(\"lint-rst\", rst)\n    if toml:\n        run_make(\"lint-toml\", toml)\n    if new_rm_mv:\n        lint_manifest()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/install-sysdeps.sh",
    "content": "#!/bin/sh\n\n# Depending on the UNIX platform, install the necessary system dependencies to:\n# * compile psutil\n# * run those unit tests that rely on CLI tools (netstat, ps, etc.)\n# NOTE: this script MUST be kept compatible with the `sh` shell.\n\nset -e\n\nUNAME_S=$(uname -s)\n\ncase \"$UNAME_S\" in\n    Linux)\n        if command -v apt > /dev/null 2>&1; then\n            HAS_APT=true  # debian / ubuntu\n        elif command -v yum > /dev/null 2>&1; then\n            HAS_YUM=true  # redhat / centos\n        elif command -v pacman > /dev/null 2>&1; then\n            HAS_PACMAN=true  # arch\n        elif command -v apk > /dev/null 2>&1; then\n            HAS_APK=true  # musl\n        fi\n        ;;\n    FreeBSD)\n        FREEBSD=true\n        ;;\n    NetBSD)\n        NETBSD=true\n        ;;\n    OpenBSD)\n        OPENBSD=true\n        ;;\n    SunOS)\n        SUNOS=true\n        ;;\nesac\n\n# Check if running as root\nif [ \"$(id -u)\" -ne 0 ]; then\n    SUDO=sudo\nfi\n\n# Function to install system dependencies\nmain() {\n    if [ $HAS_APT ]; then\n        $SUDO apt-get install -y python3-dev gcc\n        $SUDO apt-get install -y net-tools coreutils util-linux sudo  # for tests\n    elif [ $HAS_YUM ]; then\n        $SUDO yum install -y python3-devel gcc\n        $SUDO yum install -y net-tools coreutils-single util-linux sudo procps-ng  # for tests\n    elif [ $HAS_PACMAN ]; then\n        $SUDO pacman -S --noconfirm python gcc\n        $SUDO pacman -S --noconfirm net-tools coreutils util-linux sudo  # for tests\n    elif [ $HAS_APK ]; then\n        $SUDO apk add --no-interactive python3-dev gcc musl-dev linux-headers\n        $SUDO apk add --no-interactive coreutils util-linux procps  # for tests\n    elif [ $FREEBSD ]; then\n        $SUDO pkg install -y python3 gcc\n    elif [ $NETBSD ]; then\n        $SUDO /usr/sbin/pkg_add -v pkgin\n        $SUDO pkgin update\n        $SUDO pkgin -y install python311-* gcc12-*\n        if [ ! -e /usr/pkg/bin/python3 ]; then\n            $SUDO ln -s /usr/pkg/bin/python3.11 /usr/pkg/bin/python3\n        fi\n    elif [ $OPENBSD ]; then\n        $SUDO pkg_add gcc python3\n    elif [ $SUNOS ]; then\n        $SUDO pkg install developer/gcc\n    else\n        echo \"Unsupported platform '$UNAME_S'. Ignoring.\"\n    fi\n}\n\nmain\n"
  },
  {
    "path": "scripts/internal/install_pip.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\nimport sys\n\ntry:\n    import pip  # noqa: F401\nexcept ImportError:\n    pass\nelse:\n    print(\"pip already installed\")\n    sys.exit(0)\n\nimport os\nimport ssl\nimport tempfile\nfrom urllib.request import urlopen\n\nURL = \"https://bootstrap.pypa.io/get-pip.py\"\n\n\ndef main():\n    ssl_context = (\n        ssl._create_unverified_context()\n        if hasattr(ssl, \"_create_unverified_context\")\n        else None\n    )\n    with tempfile.NamedTemporaryFile(suffix=\".py\") as f:\n        print(f\"downloading {URL} into {f.name}\")\n        kwargs = dict(context=ssl_context) if ssl_context else {}\n        req = urlopen(URL, **kwargs)\n        data = req.read()\n        req.close()\n\n        f.write(data)\n        f.flush()\n        print(\"download finished, installing pip\")\n\n        code = os.system(\n            f\"{sys.executable} {f.name} --user --upgrade\"\n            \" --break-system-packages\"\n        )\n\n    sys.exit(code)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/print_access_denied.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Helper script iterates over all processes and .\nIt prints how many AccessDenied exceptions are raised in total and\nfor what Process method.\n\n$ make print-access-denied\nAPI                  AD    Percent   Outcome\nmemory_info          0        0.0%   SUCCESS\nuids                 0        0.0%   SUCCESS\ncmdline              0        0.0%   SUCCESS\ncreate_time          0        0.0%   SUCCESS\nstatus               0        0.0%   SUCCESS\nnum_ctx_switches     0        0.0%   SUCCESS\nusername             0        0.0%   SUCCESS\nionice               0        0.0%   SUCCESS\nmemory_percent       0        0.0%   SUCCESS\ngids                 0        0.0%   SUCCESS\ncpu_times            0        0.0%   SUCCESS\nnice                 0        0.0%   SUCCESS\npid                  0        0.0%   SUCCESS\ncpu_percent          0        0.0%   SUCCESS\nnum_threads          0        0.0%   SUCCESS\ncpu_num              0        0.0%   SUCCESS\nppid                 0        0.0%   SUCCESS\nterminal             0        0.0%   SUCCESS\nname                 0        0.0%   SUCCESS\nthreads              0        0.0%   SUCCESS\ncpu_affinity         0        0.0%   SUCCESS\nmemory_maps          71      21.3%   ACCESS DENIED\nmemory_footprint     71      21.3%   ACCESS DENIED\nexe                  174     52.1%   ACCESS DENIED\nenviron              238     71.3%   ACCESS DENIED\nnum_fds              238     71.3%   ACCESS DENIED\nio_counters          238     71.3%   ACCESS DENIED\ncwd                  238     71.3%   ACCESS DENIED\nconnections          238     71.3%   ACCESS DENIED\nopen_files           238     71.3%   ACCESS DENIED\n--------------------------------------------------\nTotals: access-denied=1744, calls=10020, processes=334\n\"\"\"\n\nimport time\nfrom collections import defaultdict\n\nimport psutil\nfrom psutil._common import print_color\n\n\ndef main():\n    # collect\n    tot_procs = 0\n    tot_ads = 0\n    tot_calls = 0\n    signaler = object()\n    d = defaultdict(int)\n    start = time.time()\n    for p in psutil.process_iter(attrs=[], ad_value=signaler):\n        tot_procs += 1\n        for methname, value in p.info.items():\n            tot_calls += 1\n            if value is signaler:\n                tot_ads += 1\n                d[methname] += 1\n            else:\n                d[methname] += 0\n    elapsed = time.time() - start\n\n    # print\n    templ = \"{:<20} {:<5} {:<9} {}\"\n    s = templ.format(\"API\", \"AD\", \"Percent\", \"Outcome\")\n    print_color(s, color=None, bold=True)\n    for methname, ads in sorted(d.items(), key=lambda x: (x[1], x[0])):\n        perc = (ads / tot_procs) * 100\n        outcome = \"SUCCESS\" if not ads else \"ACCESS DENIED\"\n        s = templ.format(methname, ads, f\"{perc:6.1f}%\", outcome)\n        print_color(s, \"red\" if ads else None)\n    tot_perc = round((tot_ads / tot_calls) * 100, 1)\n    print(\"-\" * 50)\n    print(\n        f\"Totals: access-denied={tot_ads} ({tot_perc}%%), calls={tot_calls},\"\n        f\" processes={tot_procs}, elapsed={round(elapsed, 2)}s\"\n    )\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/print_announce.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Prints release announce based on docs/changelog.rst file content.\nSee: https://pip.pypa.io/en/stable/reference/pip_install/#hash-checking-mode.\n\n\"\"\"\n\nimport pathlib\nimport re\nimport subprocess\nimport sys\n\nfrom psutil import __version__\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent\nCHANGELOG = ROOT_DIR / 'docs' / 'changelog.rst'\nPRINT_HASHES_PY = ROOT_DIR / 'scripts' / 'internal' / 'print_hashes.py'\n\nPRJ_NAME = 'psutil'\nPRJ_VERSION = __version__\nPRJ_URL_HOME = 'https://github.com/giampaolo/psutil'\nPRJ_URL_DOC = 'http://psutil.readthedocs.io'\nPRJ_URL_DOWNLOAD = 'https://pypi.org/project/psutil/#files'\nPRJ_URL_WHATSNEW = 'https://psutil.readthedocs.io/en/latest/changelog.html'\n\ntemplate = \"\"\"\\\nHello all,\nI'm glad to announce the release of {prj_name} {prj_version}:\n{prj_urlhome}\n\nAbout\n=====\n\npsutil (process and system utilities) is a cross-platform library for \\\nretrieving information on running processes and system utilization (CPU, \\\nmemory, disks, network) in Python. It is useful mainly for system \\\nmonitoring, profiling and limiting process resources and management of \\\nrunning processes. It implements many functionalities offered by command \\\nline tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, \\\nnice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It \\\ncurrently supports Linux, Windows, macOS, Sun Solaris, FreeBSD, OpenBSD, \\\nNetBSD and AIX. Supported Python versions are cPython 3.7+ and PyPy.\n\nWhat's new\n==========\n\n{changes}\n\nLinks\n=====\n\n- Home page: {prj_urlhome}\n- Download: {prj_urldownload}\n- Documentation: {prj_urldoc}\n- What's new: {prj_urlwhatsnew}\n\nHashes\n======\n\n{hashes}\n\n--\n\nGiampaolo - https://gmpy.dev/about\n\"\"\"\n\n\ndef rst_to_text(s):\n    \"\"\"Strip RST/Sphinx markup, returning plain text.\"\"\"\n    # :gh:`123` -> #123\n    s = re.sub(r':gh:`(\\d+)`', r'#\\1', s)\n    # :meth:, :func:, :class:, :attr:, :exc:, :mod:, :data:, etc.\n    # :role:`text` -> text (also handles :role:`~text` and :role:`mod.text`)\n    s = re.sub(r':[a-z]+:`~?([^`]+)`', r'\\1', s)\n    # ``code`` -> `code`\n    s = re.sub(r'``([^`]+)``', r'`\\1`', s)\n    # **bold** -> bold\n    s = re.sub(r'\\*\\*([^*]+)\\*\\*', r'\\1', s)\n    # *italic* -> italic\n    s = re.sub(r'\\*([^*]+)\\*', r'\\1', s)\n    return s\n\n\ndef get_changes():\n    \"\"\"Get the most recent changes for this release by parsing\n    docs/changelog.rst file.\n    \"\"\"\n    with open(CHANGELOG) as f:\n        lines = f.readlines()\n\n    block = []\n\n    # eliminate the part preceding the first block\n    while lines:\n        line = lines.pop(0)\n        if line.startswith('^^^^'):\n            break\n    else:\n        raise ValueError(\"something wrong\")\n\n    lines.pop(0)\n    while lines:\n        line = lines.pop(0)\n        line = line.rstrip()\n        if re.match(r\"^- \\d+_\", line):\n            line = re.sub(r\"^- (\\d+)_\", r\"- #\\1\", line)\n\n        if line.startswith('^^^^'):\n            break\n        block.append(line)\n    else:\n        raise ValueError(\"something wrong\")\n\n    # eliminate bottom empty lines\n    block.pop(-1)\n    while not block[-1]:\n        block.pop(-1)\n\n    text = \"\\n\".join(block)\n    text = rst_to_text(text)\n    return text\n\n\ndef main():\n    changes = get_changes()\n    hashes = (\n        subprocess.check_output([sys.executable, PRINT_HASHES_PY, 'dist/'])\n        .strip()\n        .decode()\n    )\n    text = template.format(\n        prj_name=PRJ_NAME,\n        prj_version=PRJ_VERSION,\n        prj_urlhome=PRJ_URL_HOME,\n        prj_urldownload=PRJ_URL_DOWNLOAD,\n        prj_urldoc=PRJ_URL_DOC,\n        prj_urlwhatsnew=PRJ_URL_WHATSNEW,\n        changes=changes,\n        hashes=hashes,\n    )\n    print(text)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/print_api_speed.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Benchmark all API calls and print them from fastest to slowest.\n\n$ make print_api_speed\nSYSTEM APIS                NUM CALLS      SECONDS\n-------------------------------------------------\ndisk_usage                       300      0.00157\ncpu_count                        300      0.00255\npid_exists                       300      0.00792\ncpu_times                        300      0.01044\nboot_time                        300      0.01136\ncpu_percent                      300      0.01290\ncpu_times_percent                300      0.01515\nvirtual_memory                   300      0.01594\nusers                            300      0.01964\nnet_io_counters                  300      0.02027\ncpu_stats                        300      0.02034\nnet_if_addrs                     300      0.02962\nswap_memory                      300      0.03209\nsensors_battery                  300      0.05186\npids                             300      0.07954\nnet_if_stats                     300      0.09321\ndisk_io_counters                 300      0.09406\ncpu_count (cores)                300      0.10293\ndisk_partitions                  300      0.10345\ncpu_freq                         300      0.20817\nsensors_fans                     300      0.63476\nsensors_temperatures             231      2.00039\nprocess_iter (all)               171      2.01300\nnet_connections                   97      2.00206\n\nPROCESS APIS               NUM CALLS      SECONDS\n-------------------------------------------------\ncreate_time                      300      0.00009\nexe                              300      0.00015\nnice                             300      0.00057\nionice                           300      0.00091\ncpu_affinity                     300      0.00091\ncwd                              300      0.00151\nnum_fds                          300      0.00391\nmemory_info                      300      0.00597\nmemory_percent                   300      0.00648\nmemory_info_ex                   300      0.00701\nio_counters                      300      0.00707\nname                             300      0.00894\nstatus                           300      0.00900\nppid                             300      0.00906\nnum_threads                      300      0.00932\ncpu_num                          300      0.00933\nnum_ctx_switches                 300      0.00943\nuids                             300      0.00979\ngids                             300      0.01002\ncpu_times                        300      0.01008\ncmdline                          300      0.01009\nterminal                         300      0.01059\nis_running                       300      0.01063\nthreads                          300      0.01209\nconnections                      300      0.01276\ncpu_percent                      300      0.01463\nopen_files                       300      0.01630\nusername                         300      0.01655\nenviron                          300      0.02250\nmemory_foorprint                 300      0.07066\nmemory_maps                      300      0.74281\n\"\"\"\n\nimport argparse\nimport inspect\nimport os\nimport sys\nfrom timeit import default_timer as timer\n\nimport psutil\nfrom psutil._common import print_color\n\nTIMES = 300\ntimings = []\ntempl = \"{:<25} {:>10}   {:>10}\"\n\n\ndef print_header(what):\n    s = templ.format(what, \"NUM CALLS\", \"SECONDS\")\n    print_color(s, color=None, bold=True)\n    print(\"-\" * len(s))\n\n\ndef print_timings():\n    timings.sort(key=lambda x: (x[1], -x[2]), reverse=True)\n    i = 0\n    while timings[:]:\n        title, times, elapsed = timings.pop(0)\n        s = templ.format(title, str(times), f\"{elapsed:.5f}\")\n        if i > len(timings) - 5:\n            print_color(s, color=\"red\")\n        else:\n            print(s)\n\n\ndef timecall(title, fun, *args, **kw):\n    print(f\"{title:<50}\", end=\"\")\n    sys.stdout.flush()\n    t = timer()\n    for n in range(TIMES):\n        fun(*args, **kw)\n        elapsed = timer() - t\n        if elapsed > 2:\n            break\n    print(\"\\r\", end=\"\")\n    sys.stdout.flush()\n    timings.append((title, n + 1, elapsed))\n\n\ndef set_highest_priority():\n    \"\"\"Set highest CPU and I/O priority (requires root).\"\"\"\n    p = psutil.Process()\n    if psutil.WINDOWS:\n        p.nice(psutil.HIGH_PRIORITY_CLASS)\n    else:\n        p.nice(-20)\n\n    if psutil.LINUX:\n        p.ionice(psutil.IOPRIO_CLASS_RT, value=7)\n    elif psutil.WINDOWS:\n        p.ionice(psutil.IOPRIO_HIGH)\n\n\ndef main():\n    global TIMES\n\n    parser = argparse.ArgumentParser(\n        description=__doc__, formatter_class=argparse.RawTextHelpFormatter\n    )\n    parser.add_argument('-t', '--times', type=int, default=TIMES)\n    args = parser.parse_args()\n    TIMES = args.times\n    assert TIMES > 1, TIMES\n\n    try:\n        set_highest_priority()\n    except psutil.AccessDenied:\n        prio_set = False\n    else:\n        prio_set = True\n\n    # --- system\n\n    public_apis = []\n    ignore = [\n        'wait_procs',\n        'process_iter',\n        'win_service_get',\n        'win_service_iter',\n    ]\n    if psutil.MACOS:\n        ignore.append('net_connections')  # raises AD\n    for name in psutil.__all__:\n        obj = getattr(psutil, name, None)\n        if inspect.isfunction(obj):\n            if name not in ignore:\n                public_apis.append(name)\n\n    print_header(\"SYSTEM APIS\")\n    for name in public_apis:\n        fun = getattr(psutil, name)\n        args = ()\n        if name == 'pid_exists':\n            args = (os.getpid(),)\n        elif name == 'disk_usage':\n            args = (os.getcwd(),)\n        timecall(name, fun, *args)\n    timecall('cpu_count (cores)', psutil.cpu_count, logical=False)\n    timecall('process_iter (all)', lambda: list(psutil.process_iter()))\n    print_timings()\n\n    # --- process\n    print()\n    print_header(\"PROCESS APIS\")\n    ignore = [\n        'send_signal',\n        'suspend',\n        'resume',\n        'terminate',\n        'kill',\n        'wait',\n        'as_dict',\n        'parent',\n        'parents',\n        'oneshot',\n        'pid',\n        'rlimit',\n        'children',\n    ]\n    if psutil.MACOS:\n        ignore.append('memory_maps')  # XXX\n    p = psutil.Process()\n    for name in sorted(dir(p)):\n        if not name.startswith('_') and name not in ignore:\n            fun = getattr(p, name)\n            timecall(name, fun)\n    print_timings()\n\n    if not prio_set:\n        msg = \"\\nWARN: couldn't set highest process priority \"\n        msg += \"(requires root)\"\n        print_color(msg, \"red\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/print_dist.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"List and pretty print tarball & wheel files in the dist/ directory.\"\"\"\n\nimport argparse\nimport collections\nimport os\nimport pathlib\nimport sys\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent\nsys.path.insert(0, str(ROOT_DIR))\nfrom _bootstrap import load_module  # noqa: E402\n\n_common = load_module(ROOT_DIR / \"psutil\" / \"_common.py\")\nbytes2human = _common.bytes2human\nprint_color = _common.print_color\n\n\nclass Wheel:\n    def __init__(self, path):\n        self._path = path\n        self._name = os.path.basename(path)\n\n    def __repr__(self):\n        return \"<{}(name={}, plat={}, arch={}, pyver={})>\".format(\n            self.__class__.__name__,\n            self.name,\n            self.platform(),\n            self.arch(),\n            self.pyver(),\n        )\n\n    __str__ = __repr__\n\n    @property\n    def name(self):\n        return self._name\n\n    def platform(self):\n        plat = self.name.split('-')[-1]\n        pyimpl = self.name.split('-')[3]\n        ispypy = 'pypy' in pyimpl\n        if 'linux' in plat:\n            if ispypy:\n                return 'pypy_on_linux'\n            else:\n                return 'linux'\n        elif 'win' in plat:\n            if ispypy:\n                return 'pypy_on_windows'\n            else:\n                return 'windows'\n        elif 'macosx' in plat:\n            if ispypy:\n                return 'pypy_on_macos'\n            else:\n                return 'macos'\n        else:\n            raise ValueError(f\"unknown platform {self.name!r}\")\n\n    def arch(self):\n        if self.name.endswith(('x86_64.whl', 'amd64.whl')):\n            return '64-bit'\n        if self.name.endswith((\"i686.whl\", \"win32.whl\")):\n            return '32-bit'\n        if self.name.endswith(\"arm64.whl\"):\n            return 'arm64'\n        if self.name.endswith(\"aarch64.whl\"):\n            return 'aarch64'\n        return '?'\n\n    def pyver(self):\n        pyver = 'pypy' if self.name.split('-')[3].startswith('pypy') else 'py'\n        pyver += self.name.split('-')[2][2:]\n        return pyver\n\n    def size(self):\n        return os.path.getsize(self._path)\n\n\nclass Tarball(Wheel):\n    def platform(self):\n        return \"source\"\n\n    def arch(self):\n        return \"-\"\n\n    def pyver(self):\n        return \"-\"\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\n        'dir',\n        nargs=\"?\",\n        default=\"dist\",\n        help='directory containing tar.gz or wheel files',\n    )\n    args = parser.parse_args()\n\n    groups = collections.defaultdict(list)\n    ls = sorted(os.listdir(args.dir), key=lambda x: x.endswith(\"tar.gz\"))\n    for name in ls:\n        path = os.path.join(args.dir, name)\n        if path.endswith(\".whl\"):\n            pkg = Wheel(path)\n        elif path.endswith(\".tar.gz\"):\n            pkg = Tarball(path)\n        else:\n            raise ValueError(f\"invalid package {path!r}\")\n        groups[pkg.platform()].append(pkg)\n\n    tot_files = 0\n    tot_size = 0\n    templ = \"{:<120} {:>7} {:>8} {:>7}\"\n    for platf, pkgs in groups.items():\n        ppn = f\"{platf} ({len(pkgs)})\"\n        s = templ.format(ppn, \"size\", \"arch\", \"pyver\")\n        print_color('\\n' + s, color=None, bold=True)\n        for pkg in sorted(pkgs, key=lambda x: x.name):\n            tot_files += 1\n            tot_size += pkg.size()\n            s = templ.format(\n                \"  \" + pkg.name,\n                bytes2human(pkg.size()),\n                pkg.arch(),\n                pkg.pyver(),\n            )\n            if 'pypy' in pkg.pyver():\n                print_color(s, color='violet')\n            else:\n                print_color(s, color='brown')\n\n    print_color(\n        f\"\\n\\ntotals: files={tot_files}, size={bytes2human(tot_size)}\",\n        bold=True,\n    )\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/internal/print_downloads.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Print PYPI statistics in MarkDown format.\nUseful sites:\n* https://pepy.tech/project/psutil\n* https://pypistats.org/packages/psutil\n* https://hugovk.github.io/top-pypi-packages/.\n\"\"\"\n\nimport functools\nimport json\nimport os\nimport shlex\nimport subprocess\nimport sys\n\nimport pypinfo  # noqa: F401\n\nAUTH_FILE = os.path.expanduser(\"~/.pypinfo.json\")\nPKGNAME = 'psutil'\nDAYS = 30\nLIMIT = 100\nGITHUB_SCRIPT_URL = (\n    \"https://github.com/giampaolo/psutil/blob/master/\"\n    \"scripts/internal/pypistats.py\"\n)\nLAST_UPDATE = None\nbytes_billed = 0\n\n\n# --- get\n\n\n@functools.lru_cache\ndef sh(cmd):\n    assert os.path.exists(AUTH_FILE)\n    env = os.environ.copy()\n    env['GOOGLE_APPLICATION_CREDENTIALS'] = AUTH_FILE\n    p = subprocess.Popen(\n        shlex.split(cmd),\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n        universal_newlines=True,\n        env=env,\n    )\n    stdout, stderr = p.communicate()\n    if p.returncode != 0:\n        raise RuntimeError(stderr)\n    assert not stderr, stderr\n    return stdout.strip()\n\n\n@functools.lru_cache\ndef query(cmd):\n    global bytes_billed\n    ret = json.loads(sh(cmd))\n    bytes_billed += ret['query']['bytes_billed']\n    return ret\n\n\ndef top_packages():\n    global LAST_UPDATE\n    ret = query(\n        f\"pypinfo --all --json --days {DAYS} --limit {LIMIT} '' project\"\n    )\n    LAST_UPDATE = ret['last_update']\n    return [(x['project'], x['download_count']) for x in ret['rows']]\n\n\ndef ranking():\n    data = top_packages()\n    for i, (name, downloads) in enumerate(data, start=1):\n        if name == PKGNAME:\n            return i\n    raise ValueError(f\"can't find {PKGNAME}\")\n\n\ndef downloads():\n    data = top_packages()\n    for name, downloads in data:\n        if name == PKGNAME:\n            return downloads\n    raise ValueError(f\"can't find {PKGNAME}\")\n\n\ndef downloads_pyver():\n    return query(f\"pypinfo --json --days {DAYS} {PKGNAME} pyversion\")\n\n\ndef downloads_by_country():\n    return query(f\"pypinfo --json --days {DAYS} {PKGNAME} country\")\n\n\ndef downloads_by_system():\n    return query(f\"pypinfo --json --days {DAYS} {PKGNAME} system\")\n\n\ndef downloads_by_distro():\n    return query(f\"pypinfo --json --days {DAYS} {PKGNAME} distro\")\n\n\n# --- print\n\n\ntempl = \"| {:<30} | {:>15} |\"\n\n\ndef print_row(left, right):\n    if isinstance(right, int):\n        right = f\"{right:,}\"\n    print(templ.format(left, right))\n\n\ndef print_header(left, right=\"Downloads\"):\n    print_row(left, right)\n    s = templ.format(\"-\" * 30, \"-\" * 15)\n    print(\"|:\" + s[2:-2] + \":|\")\n\n\ndef print_markdown_table(title, left, rows):\n    pleft = left.replace('_', ' ').capitalize()\n    print(\"### \" + title)\n    print()\n    print_header(pleft)\n    for row in rows:\n        lval = row[left]\n        print_row(lval, row['download_count'])\n    print()\n\n\ndef main():\n    downs = downloads()\n\n    print(\"# Download stats\")\n    print()\n    s = f\"psutil download statistics of the last {DAYS} days (last update \"\n    s += f\"*{LAST_UPDATE}*).\\n\"\n    s += f\"Generated via [pypistats.py]({GITHUB_SCRIPT_URL}) script.\\n\"\n    print(s)\n\n    data = [\n        {'what': 'Per month', 'download_count': downs},\n        {'what': 'Per day', 'download_count': int(downs / 30)},\n        {'what': 'PYPI ranking', 'download_count': ranking()},\n    ]\n    print_markdown_table('Overview', 'what', data)\n    print_markdown_table(\n        'Operating systems', 'system_name', downloads_by_system()['rows']\n    )\n    print_markdown_table(\n        'Distros', 'distro_name', downloads_by_distro()['rows']\n    )\n    print_markdown_table(\n        'Python versions', 'python_version', downloads_pyver()['rows']\n    )\n    print_markdown_table(\n        'Countries', 'country', downloads_by_country()['rows']\n    )\n\n\nif __name__ == '__main__':\n    try:\n        main()\n    finally:\n        print(f\"bytes billed: {bytes_billed}\", file=sys.stderr)\n"
  },
  {
    "path": "scripts/internal/print_hashes.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Prints files hashes, see:\nhttps://pip.pypa.io/en/stable/reference/pip_install/#hash-checking-mode.\n\"\"\"\n\nimport argparse\nimport hashlib\nimport os\n\n\ndef csum(file, kind):\n    h = hashlib.new(kind)\n    with open(file, \"rb\") as f:\n        h.update(f.read())\n        return h.hexdigest()\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\n        \"dir\",\n        type=str,\n        nargs=\"?\",\n        help=\"directory containing tar.gz or wheel files\",\n        default=\"dist/\",\n    )\n    args = parser.parse_args()\n    for name in sorted(os.listdir(args.dir)):\n        file = os.path.join(args.dir, name)\n        if os.path.isfile(file):\n            md5 = csum(file, \"md5\")\n            sha256 = csum(file, \"sha256\")\n            print(f\"{os.path.basename(file)}\\nmd5: {md5}\\nsha256: {sha256}\\n\")\n        else:\n            print(f\"skipping {file!r} (not a file)\")\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/print_sysinfo.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Print system information. Run before CI test run.\"\"\"\n\nimport datetime\nimport getpass\nimport locale\nimport os\nimport pathlib\nimport platform\nimport shlex\nimport shutil\nimport subprocess\nimport sys\n\nimport psutil\nfrom psutil._common import bytes2human\n\ntry:\n    import pip\nexcept ImportError:\n    pip = None\ntry:\n    import wheel\nexcept ImportError:\n    wheel = None\n\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent\nsys.path.insert(0, str(ROOT_DIR))\nfrom _bootstrap import load_module  # noqa: E402\n\n\ndef sh(cmd):\n    if isinstance(cmd, str):\n        cmd = shlex.split(cmd)\n    return subprocess.check_output(cmd, universal_newlines=True).strip()\n\n\ntests_init = ROOT_DIR / \"tests\" / \"__init__.py\"\n\ntests_init_mod = load_module(tests_init)\n\n\ndef main():\n    info = {}\n\n    # python\n    info['python'] = ', '.join([\n        platform.python_implementation(),\n        platform.python_version(),\n        platform.python_compiler(),\n    ])\n\n    # OS\n    if psutil.LINUX and shutil.which(\"lsb_release\"):\n        info['OS'] = sh('lsb_release -d -s')\n    elif psutil.OSX:\n        info['OS'] = f\"Darwin {platform.mac_ver()[0]}\"\n    elif psutil.WINDOWS:\n        info['OS'] = \"Windows \" + ' '.join(map(str, platform.win32_ver()))\n        if hasattr(platform, 'win32_edition'):\n            info['OS'] += \", \" + platform.win32_edition()\n    else:\n        info['OS'] = f\"{platform.system()} {platform.version()}\"\n    info['arch'] = ', '.join(\n        list(platform.architecture()) + [platform.machine()]\n    )\n    if psutil.POSIX:\n        info['kernel'] = platform.uname()[2]\n\n    # pip\n    info['pip'] = getattr(pip, '__version__', 'not installed')\n    if wheel is not None:\n        info['pip'] += f\" (wheel={wheel.__version__})\"\n\n    # UNIX\n    if psutil.POSIX:\n        if shutil.which(\"gcc\"):\n            out = sh(['gcc', '--version'])\n            info['gcc'] = str(out).split('\\n')[0]\n        else:\n            info['gcc'] = 'not installed'\n        s = platform.libc_ver()[1]\n        if s:\n            info['glibc'] = s\n\n    # system\n    info['fs-encoding'] = sys.getfilesystemencoding()\n    lang = locale.getlocale()\n    info['lang'] = f\"{lang[0]}, {lang[1]}\"\n    info['boot-time'] = datetime.datetime.fromtimestamp(\n        psutil.boot_time()\n    ).strftime(\"%Y-%m-%d %H:%M:%S\")\n    info['time'] = datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n    info['user'] = getpass.getuser()\n    info['home'] = os.path.expanduser(\"~\")\n    info['cwd'] = os.getcwd()\n    info['pyexe'] = tests_init_mod.PYTHON_EXE\n    info['hostname'] = platform.node()\n    info['PID'] = os.getpid()\n\n    # metrics\n    info['cpus'] = psutil.cpu_count()\n    loadavg = tuple(x / psutil.cpu_count() * 100 for x in psutil.getloadavg())\n    info['loadavg'] = (\n        f\"{loadavg[0]:.1f}%, {loadavg[1]:.1f}%, {loadavg[2]:.1f}%\"\n    )\n    mem = psutil.virtual_memory()\n    info['memory'] = \"{}%%, used={}, total={}\".format(\n        int(mem.percent),\n        bytes2human(mem.used),\n        bytes2human(mem.total),\n    )\n    swap = psutil.swap_memory()\n    info['swap'] = \"{}%%, used={}, total={}\".format(\n        int(swap.percent),\n        bytes2human(swap.used),\n        bytes2human(swap.total),\n    )\n\n    # constants\n    constants = sorted([\n        x\n        for x in dir(tests_init_mod)\n        if x.isupper() and getattr(tests_init_mod, x) is True\n    ])\n    info['constants'] = \"\\n                  \".join(constants)\n\n    # processes\n    # info['pids'] = len(psutil.pids())\n    # pinfo = psutil.Process().as_dict()\n    # pinfo.pop('memory_maps', None)\n    # pinfo[\"environ\"] = {k: os.environ[k] for k in sorted(os.environ)}\n    # info['proc'] = pprint.pformat(pinfo)\n\n    # print\n    print(\"=\" * 70, file=sys.stderr)\n    for k, v in info.items():\n        print(\"{:<17} {}\".format(k + \":\", v), file=sys.stderr)\n    print(\"=\" * 70, file=sys.stderr)\n    sys.stdout.flush()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/purge_installation.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Purge psutil installation by removing psutil-related files and\ndirectories found in site-packages directories. This is needed mainly\nbecause sometimes \"import psutil\" imports a leftover installation\nfrom site-packages directory instead of the main working directory.\n\"\"\"\n\nimport os\nimport shutil\nimport site\n\nPKGNAME = \"psutil\"\n\nlocations = [site.getusersitepackages()] + site.getsitepackages()\n\n\ndef rmpath(path):\n    if os.path.isdir(path):\n        print(\"rmdir \" + path)\n        shutil.rmtree(path)\n    else:\n        print(\"rm \" + path)\n        os.remove(path)\n\n\ndef purge():\n    for root in locations:\n        if os.path.isdir(root):\n            for name in os.listdir(root):\n                if PKGNAME in name:\n                    abspath = os.path.join(root, name)\n                    rmpath(abspath)\n\n\ndef purge_windows():\n    r\"\"\"Uninstalling psutil on Windows is more tricky. On \"import\n    psutil\" tests may import a psutil version living in\n    C:\\PythonXY\\Lib\\site-packages which is not what we want, so other\n    than \"pip uninstall psutil\" we also manually remove stuff from\n    site-packages dirs.\n    \"\"\"\n    for dir in locations:\n        for name in os.listdir(dir):\n            path = os.path.join(dir, name)\n            if name.startswith(PKGNAME):\n                rmpath(path)\n            elif name == 'easy-install.pth':\n                # easy_install can add a line (installation path) into\n                # easy-install.pth; that line alters sys.path.\n                path = os.path.join(dir, name)\n                with open(path) as f:\n                    lines = f.readlines()\n                    hasit = False\n                    for line in lines:\n                        if PKGNAME in line:\n                            hasit = True\n                            break\n                if hasit:\n                    with open(path, \"w\") as f:\n                        for line in lines:\n                            if PKGNAME not in line:\n                                f.write(line)\n                            else:\n                                print(f\"removed line {line!r} from {path!r}\")\n\n\ndef main():\n    purge()\n    if os.name == \"nt\":\n        purge_windows()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/internal/rst_check_dead_refs.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can\n# be found in the LICENSE file.\n\n\"\"\"Check RST files for two classes of reference errors:\n\n1. Hyperlink targets with a URL that are defined but never referenced.\n2. Backtick references (`name`_) that point to an undefined target.\n\nTargets and references are resolved across all files passed as arguments,\nso cross-file references work correctly.\n\nUsage::\n\n    python3 scripts/internal/rst_check_dead_refs.py docs/*.rst\n\"\"\"\n\nimport argparse\nimport os\nimport re\nimport sys\n\n# .. _`Foo Bar`: https://...   or   .. _foo: https://...  (URL targets only)\nRE_URL_TARGET = re.compile(\n    r'^\\.\\. _`?([^`\\n:]+)`?\\s*:\\s*(https?://\\S+)',\n    re.MULTILINE,\n)\n# .. _`Foo Bar`:   or   .. _foo:   (any target, with or without URL)\nRE_ANY_TARGET = re.compile(\n    r'^\\.\\. _`?([^`\\n:]+)`?\\s*:',\n    re.MULTILINE,\n)\n# `Foo Bar`_  but NOT  `text <url>`_  and NOT  `text`__\nRE_REF = re.compile(r'`([^`<\\n]+)`_(?!_)')\n# .. include:: somefile.rst\nRE_INCLUDE = re.compile(r'^\\.\\. include::\\s*(\\S+)', re.MULTILINE)\n\n\ndef line_number(text, pos):\n    return text.count('\\n', 0, pos) + 1\n\n\ndef read_with_includes(path, _seen=None):\n    \"\"\"Return file text plus the text of any ``.. include::`` files.\"\"\"\n    if _seen is None:\n        _seen = set()\n    path = os.path.normpath(path)\n    if path in _seen:\n        return \"\"\n    _seen.add(path)\n    try:\n        with open(path) as f:\n            text = f.read()\n    except OSError:\n        return \"\"\n    base = os.path.dirname(path)\n    for m in RE_INCLUDE.finditer(text):\n        inc = os.path.join(base, m.group(1))\n        text += read_with_includes(inc, _seen)\n    return text\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\"files\", nargs=\"+\", metavar=\"FILE\")\n    args = parser.parse_args()\n\n    # Pass 1: collect all defined targets across all files.\n    # Targets from \".. include::\" files are also collected so that\n    # cross-file references resolve correctly.\n    all_targets = set()\n    # URL-only targets defined directly in a file (not via include), used\n    # to detect targets that are defined but never referenced.\n    url_targets = {}\n\n    for path in args.files:\n        # Use expanded text (with includes) so cross-file references resolve.\n        expanded = read_with_includes(path)\n        for m in RE_ANY_TARGET.finditer(expanded):  # noqa: FURB142\n            all_targets.add(m.group(1).strip().lower())\n        # Only record URL targets from the file itself, not from includes,\n        # so we don't falsely report included targets as unreferenced.\n        with open(path) as f:\n            own_text = f.read()\n        for m in RE_URL_TARGET.finditer(own_text):\n            name = m.group(1).strip()\n            url_targets[name.lower()] = (\n                name,\n                path,\n                line_number(own_text, m.start()),\n            )\n\n    # Pass 2: collect all backtick references (with file + line).\n    all_refs = []  # list of (lower-case name, original, file, lineno)\n    referenced = set()  # lower-case names of all referenced targets\n\n    for path in args.files:\n        with open(path) as f:\n            text = f.read()\n        for m in RE_REF.finditer(text):  # references from the file itself only\n            name = m.group(1).strip()\n            all_refs.append((\n                name.lower(),\n                name,\n                path,\n                line_number(text, m.start()),\n            ))\n            referenced.add(name.lower())\n\n    errors = []\n\n    # Check 1: URL targets that are never referenced.\n    for key, (name, path, lineno) in url_targets.items():\n        if key not in referenced:\n            errors.append(\n                (path, lineno, f\"unreferenced hyperlink target: {name!r}\")\n            )\n\n    # Check 2: backtick references with no matching target.\n    for key, name, path, lineno in all_refs:\n        if key not in all_targets:\n            errors.append((path, lineno, f\"unknown target name: {name!r}\"))\n\n    errors.sort()\n    for path, lineno, msg in errors:\n        print(f\"{path}:{lineno}: {msg}\")\n\n    if errors:\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/iotop.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of iotop (http://guichaz.free.fr/iotop/) showing real time\ndisk I/O statistics.\n\nIt works on Linux only (FreeBSD and macOS are missing support for IO\ncounters).\nIt doesn't work on Windows as curses module is required.\n\nExample output:\n\n$ python3 scripts/iotop.py\nTotal DISK READ: 0.00 B/s | Total DISK WRITE: 472.00 K/s\nPID   USER      DISK READ  DISK WRITE  COMMAND\n13155 giampao    0.00 B/s  428.00 K/s  /usr/bin/google-chrome-beta\n3260  giampao    0.00 B/s    0.00 B/s  bash\n3779  giampao    0.00 B/s    0.00 B/s  gnome-session --session=ubuntu\n3830  giampao    0.00 B/s    0.00 B/s  /usr/bin/dbus-launch\n3831  giampao    0.00 B/s    0.00 B/s  //bin/dbus-daemon --fork --print-pid 5\n3841  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi-bus-launcher\n3845  giampao    0.00 B/s    0.00 B/s  /bin/dbus-daemon\n3848  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi2-core/at-spi2-registryd\n3862  giampao    0.00 B/s    0.00 B/s  /usr/lib/gnome-settings-daemon\n\nAuthor: Giampaolo Rodola' <g.rodola@gmail.com>\n\"\"\"\n\nimport sys\nimport time\n\ntry:\n    import curses\nexcept ImportError:\n    sys.exit('platform not supported')\n\nimport psutil\nfrom psutil._common import bytes2human\n\nwin = curses.initscr()\nlineno = 0\n\n\ndef printl(line, highlight=False):\n    \"\"\"A thin wrapper around curses's addstr().\"\"\"\n    global lineno\n    try:\n        if highlight:\n            line += \" \" * (win.getmaxyx()[1] - len(line))\n            win.addstr(lineno, 0, line, curses.A_REVERSE)\n        else:\n            win.addstr(lineno, 0, line, 0)\n    except curses.error:\n        lineno = 0\n        win.refresh()\n        raise\n    else:\n        lineno += 1\n\n\ndef poll(interval):\n    \"\"\"Calculate IO usage by comparing IO statistics before and\n    after the interval.\n    Return a tuple including all currently running processes\n    sorted by IO activity and total disks I/O activity.\n    \"\"\"\n    # first get a list of all processes and disk io counters\n    procs = list(psutil.process_iter())\n    for p in procs.copy():\n        try:\n            p._before = p.io_counters()\n        except psutil.Error:\n            procs.remove(p)\n            continue\n    disks_before = psutil.disk_io_counters()\n\n    # sleep some time\n    time.sleep(interval)\n\n    # then retrieve the same info again\n    for p in procs.copy():\n        with p.oneshot():\n            try:\n                p._after = p.io_counters()\n                p._cmdline = ' '.join(p.cmdline())\n                if not p._cmdline:\n                    p._cmdline = p.name()\n                p._username = p.username()\n            except (psutil.NoSuchProcess, psutil.ZombieProcess):\n                procs.remove(p)\n    disks_after = psutil.disk_io_counters()\n\n    # finally calculate results by comparing data before and\n    # after the interval\n    for p in procs:\n        p._read_per_sec = p._after.read_bytes - p._before.read_bytes\n        p._write_per_sec = p._after.write_bytes - p._before.write_bytes\n        p._total = p._read_per_sec + p._write_per_sec\n\n    disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes\n    disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes\n\n    # sort processes by total disk IO so that the more intensive\n    # ones get listed first\n    processes = sorted(procs, key=lambda p: p._total, reverse=True)\n\n    return (processes, disks_read_per_sec, disks_write_per_sec)\n\n\ndef refresh_window(procs, disks_read, disks_write):\n    \"\"\"Print results on screen by using curses.\"\"\"\n    curses.endwin()\n    templ = \"{:<5} {:<7} {:>11} {:>11}  {}\"\n    win.erase()\n\n    disks_tot = \"Total DISK READ: {} | Total DISK WRITE: {}\".format(\n        bytes2human(disks_read),\n        bytes2human(disks_write),\n    )\n    printl(disks_tot)\n\n    header = templ.format(\"PID\", \"USER\", \"DISK READ\", \"DISK WRITE\", \"COMMAND\")\n    printl(header, highlight=True)\n\n    for p in procs:\n        line = templ.format(\n            p.pid,\n            p._username[:7],\n            bytes2human(p._read_per_sec),\n            bytes2human(p._write_per_sec),\n            p._cmdline,\n        )\n        try:\n            printl(line)\n        except curses.error:\n            break\n    win.refresh()\n\n\ndef setup():\n    curses.start_color()\n    curses.use_default_colors()\n    for i in range(curses.COLORS):\n        curses.init_pair(i + 1, i, -1)\n    curses.endwin()\n    win.nodelay(1)\n\n\ndef tear_down():\n    win.keypad(0)\n    curses.nocbreak()\n    curses.echo()\n    curses.endwin()\n\n\ndef main():\n    global lineno\n    setup()\n    try:\n        interval = 0\n        while True:\n            if win.getch() == ord('q'):\n                break\n            args = poll(interval)\n            refresh_window(*args)\n            lineno = 0\n            interval = 0.5\n            time.sleep(interval)\n    except (KeyboardInterrupt, SystemExit):\n        pass\n    finally:\n        tear_down()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/killall.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Kill a process by name.\"\"\"\n\nimport os\nimport sys\n\nimport psutil\n\n\ndef main():\n    if len(sys.argv) != 2:\n        sys.exit(f\"usage: {__file__} name\")\n    else:\n        name = sys.argv[1]\n\n    killed = []\n    for proc in psutil.process_iter():\n        if proc.name() == name and proc.pid != os.getpid():\n            proc.kill()\n            killed.append(proc.pid)\n    if not killed:\n        sys.exit(f\"{name}: no process found\")\n    else:\n        sys.exit(0)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/meminfo.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Print system memory information.\n\n$ python3 scripts/meminfo.py\nMEMORY\n------\nTotal      :    9.7G\nAvailable  :    4.9G\nPercent    :    49.0\nUsed       :    8.2G\nFree       :    1.4G\nActive     :    5.6G\nInactive   :    2.1G\nBuffers    :  341.2M\nCached     :    3.2G\n\nSWAP\n----\nTotal      :      0B\nUsed       :      0B\nFree       :      0B\nPercent    :     0.0\nSin        :      0B\nSout       :      0B\n\"\"\"\n\nimport psutil\nfrom psutil._common import bytes2human\n\n\ndef pprint_ntuple(nt):\n    for name in nt._fields:\n        value = getattr(nt, name)\n        if name != 'percent':\n            value = bytes2human(value)\n        print('{:<10} : {:>7}'.format(name.capitalize(), value))\n\n\ndef main():\n    print('MEMORY\\n------')\n    pprint_ntuple(psutil.virtual_memory())\n    print('\\nSWAP\\n----')\n    pprint_ntuple(psutil.swap_memory())\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/netstat.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'netstat -antp' on Linux.\n\n$ python3 scripts/netstat.py\nProto Local address      Remote address   Status        PID    Program name\ntcp   127.0.0.1:48256    127.0.0.1:45884  ESTABLISHED   13646  chrome\ntcp   127.0.0.1:47073    127.0.0.1:45884  ESTABLISHED   13646  chrome\ntcp   127.0.0.1:47072    127.0.0.1:45884  ESTABLISHED   13646  chrome\ntcp   127.0.0.1:45884    -                LISTEN        13651  GoogleTalkPlugi\ntcp   127.0.0.1:60948    -                LISTEN        13651  GoogleTalkPlugi\ntcp   172.17.42.1:49102  127.0.0.1:19305  CLOSE_WAIT    13651  GoogleTalkPlugi\ntcp   172.17.42.1:55797  127.0.0.1:443    CLOSE_WAIT    13651  GoogleTalkPlugi\n...\n\"\"\"\n\nimport socket\nfrom socket import AF_INET\nfrom socket import SOCK_DGRAM\nfrom socket import SOCK_STREAM\n\nimport psutil\n\nAD = \"-\"\nAF_INET6 = getattr(socket, 'AF_INET6', object())\nproto_map = {\n    (AF_INET, SOCK_STREAM): 'tcp',\n    (AF_INET6, SOCK_STREAM): 'tcp6',\n    (AF_INET, SOCK_DGRAM): 'udp',\n    (AF_INET6, SOCK_DGRAM): 'udp6',\n}\n\n\ndef main():\n    templ = \"{:<5} {:<30} {:<30} {:<13} {:<6} {}\"\n    header = templ.format(\n        \"Proto\",\n        \"Local address\",\n        \"Remote address\",\n        \"Status\",\n        \"PID\",\n        \"Program name\",\n    )\n    print(header)\n    proc_names = {}\n    for p in psutil.process_iter(['pid', 'name']):\n        proc_names[p.info['pid']] = p.info['name']\n    for c in psutil.net_connections(kind='inet'):\n        laddr = f\"{c.laddr[0]}:{c.laddr[1]}\"\n        raddr = \"\"\n        if c.raddr:\n            raddr = f\"{c.raddr[0]}:{c.raddr[1]}\"\n        name = proc_names.get(c.pid, '?') or ''\n        line = templ.format(\n            proto_map[(c.family, c.type)],\n            laddr,\n            raddr or AD,\n            c.status,\n            c.pid or AD,\n            name[:15],\n        )\n        print(line)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/nettop.py",
    "content": "#!/usr/bin/env python3\n#\n# $Id: iotop.py 1160 2011-10-14 18:50:36Z g.rodola@gmail.com $\n#\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Shows real-time network statistics.\n\nAuthor: Giampaolo Rodola' <g.rodola@gmail.com>\n\n$ python3 scripts/nettop.py\n-----------------------------------------------------------\ntotal bytes:           sent: 1.49 G       received: 4.82 G\ntotal packets:         sent: 7338724      received: 8082712\n\nwlan0                     TOTAL         PER-SEC\n-----------------------------------------------------------\nbytes-sent               1.29 G        0.00 B/s\nbytes-recv               3.48 G        0.00 B/s\npkts-sent               7221782               0\npkts-recv               6753724               0\n\neth1                      TOTAL         PER-SEC\n-----------------------------------------------------------\nbytes-sent             131.77 M        0.00 B/s\nbytes-recv               1.28 G        0.00 B/s\npkts-sent                     0               0\npkts-recv               1214470               0\n\"\"\"\n\nimport sys\nimport time\n\ntry:\n    import curses\nexcept ImportError:\n    sys.exit('platform not supported')\n\nimport psutil\nfrom psutil._common import bytes2human\n\nlineno = 0\nwin = curses.initscr()\n\n\ndef printl(line, highlight=False):\n    \"\"\"A thin wrapper around curses's addstr().\"\"\"\n    global lineno\n    try:\n        if highlight:\n            line += \" \" * (win.getmaxyx()[1] - len(line))\n            win.addstr(lineno, 0, line, curses.A_REVERSE)\n        else:\n            win.addstr(lineno, 0, line, 0)\n    except curses.error:\n        lineno = 0\n        win.refresh()\n        raise\n    else:\n        lineno += 1\n\n\ndef poll(interval):\n    \"\"\"Retrieve raw stats within an interval window.\"\"\"\n    tot_before = psutil.net_io_counters()\n    pnic_before = psutil.net_io_counters(pernic=True)\n    # sleep some time\n    time.sleep(interval)\n    tot_after = psutil.net_io_counters()\n    pnic_after = psutil.net_io_counters(pernic=True)\n    return (tot_before, tot_after, pnic_before, pnic_after)\n\n\ndef refresh_window(tot_before, tot_after, pnic_before, pnic_after):\n    \"\"\"Print stats on screen.\"\"\"\n    global lineno\n\n    # totals\n    printl(\n        \"total bytes:           sent: {:<10}   received: {}\".format(\n            bytes2human(tot_after.bytes_sent),\n            bytes2human(tot_after.bytes_recv),\n        )\n    )\n\n    # per-network interface details: let's sort network interfaces so\n    # that the ones which generated more traffic are shown first\n    printl(\"\")\n    nic_names = list(pnic_after.keys())\n    nic_names.sort(key=lambda x: sum(pnic_after[x]), reverse=True)\n    for name in nic_names:\n        stats_before = pnic_before[name]\n        stats_after = pnic_after[name]\n        templ = \"{:<15s} {:>15} {:>15}\"\n        # fmt: off\n        printl(templ.format(name, \"TOTAL\", \"PER-SEC\"), highlight=True)\n        printl(templ.format(\n            \"bytes-sent\",\n            bytes2human(stats_after.bytes_sent),\n            bytes2human(\n                stats_after.bytes_sent - stats_before.bytes_sent) + '/s',\n        ))\n        printl(templ.format(\n            \"bytes-recv\",\n            bytes2human(stats_after.bytes_recv),\n            bytes2human(\n                stats_after.bytes_recv - stats_before.bytes_recv) + '/s',\n        ))\n        printl(templ.format(\n            \"pkts-sent\",\n            stats_after.packets_sent,\n            stats_after.packets_sent - stats_before.packets_sent,\n        ))\n        printl(templ.format(\n            \"pkts-recv\",\n            stats_after.packets_recv,\n            stats_after.packets_recv - stats_before.packets_recv,\n        ))\n        printl(\"\")\n        # fmt: on\n    win.refresh()\n    lineno = 0\n\n\ndef setup():\n    curses.start_color()\n    curses.use_default_colors()\n    for i in range(curses.COLORS):\n        curses.init_pair(i + 1, i, -1)\n    curses.endwin()\n    win.nodelay(1)\n\n\ndef tear_down():\n    win.keypad(0)\n    curses.nocbreak()\n    curses.echo()\n    curses.endwin()\n\n\ndef main():\n    setup()\n    try:\n        interval = 0\n        while True:\n            if win.getch() == ord('q'):\n                break\n            args = poll(interval)\n            refresh_window(*args)\n            interval = 0.5\n    except (KeyboardInterrupt, SystemExit):\n        pass\n    finally:\n        tear_down()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/pidof.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola', karthikrev. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\n\"\"\"A clone of 'pidof' cmdline utility.\n\n$ pidof python\n1140 1138 1136 1134 1133 1129 1127 1125 1121 1120 1119\n\"\"\"\n\nimport sys\n\nimport psutil\n\n\ndef pidof(pgname):\n    # search for matches in the process name and cmdline\n    return [\n        str(proc.pid)\n        for proc in psutil.process_iter(['name', 'cmdline'])\n        if proc.info[\"name\"] == pgname\n        or (proc.info[\"cmdline\"] and proc.info[\"cmdline\"][0] == pgname)\n    ]\n\n\ndef main():\n    if len(sys.argv) != 2:\n        sys.exit(f\"usage: {__file__} pgname\")\n    else:\n        pgname = sys.argv[1]\n    pids = pidof(pgname)\n    if pids:\n        print(\" \".join(pids))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/pmap.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'pmap' utility on Linux, 'vmmap' on macOS and 'procstat\n-v' on BSD. Report memory map of a process.\n\n$ python3 scripts/pmap.py 32402\nAddress                 RSS  Mode    Mapping\n0000000000400000      1200K  r-xp    /usr/bin/python3.7\n0000000000838000         4K  r--p    /usr/bin/python3.7\n0000000000839000       304K  rw-p    /usr/bin/python3.7\n00000000008ae000        68K  rw-p    [anon]\n000000000275e000      5396K  rw-p    [heap]\n00002b29bb1e0000       124K  r-xp    /lib/x86_64-linux-gnu/ld-2.17.so\n00002b29bb203000         8K  rw-p    [anon]\n00002b29bb220000       528K  rw-p    [anon]\n00002b29bb2d8000       768K  rw-p    [anon]\n00002b29bb402000         4K  r--p    /lib/x86_64-linux-gnu/ld-2.17.so\n00002b29bb403000         8K  rw-p    /lib/x86_64-linux-gnu/ld-2.17.so\n00002b29bb405000        60K  r-xp    /lib/x86_64-linux-gnu/libpthread-2.17.so\n00002b29bb41d000         0K  ---p    /lib/x86_64-linux-gnu/libpthread-2.17.so\n00007fff94be6000        48K  rw-p    [stack]\n00007fff94dd1000         4K  r-xp    [vdso]\nffffffffff600000         0K  r-xp    [vsyscall]\n...\n\"\"\"\n\nimport shutil\nimport sys\n\nimport psutil\nfrom psutil._common import bytes2human\n\n\ndef safe_print(s):\n    s = s[: shutil.get_terminal_size()[0]]\n    try:\n        print(s)\n    except UnicodeEncodeError:\n        print(s.encode('ascii', 'ignore').decode())\n\n\ndef main():\n    if len(sys.argv) != 2:\n        sys.exit('usage: pmap <pid>')\n    p = psutil.Process(int(sys.argv[1]))\n    templ = \"{:<20} {:>10}  {:<7} {}\"\n    print(templ.format(\"Address\", \"RSS\", \"Mode\", \"Mapping\"))\n    total_rss = 0\n    for m in p.memory_maps(grouped=False):\n        total_rss += m.rss\n        line = templ.format(\n            m.addr.split('-')[0].zfill(16),\n            bytes2human(m.rss),\n            m.perms,\n            m.path,\n        )\n        safe_print(line)\n    print(\"-\" * 31)\n    print(templ.format(\"Total\", bytes2human(total_rss), \"\", \"\"))\n    safe_print(f\"PID = {p.pid}, name = {p.name()}\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/procinfo.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Print detailed information about a process.\n\nAuthor: Giampaolo Rodola' <g.rodola@gmail.com>\n\n$ python3 scripts/procinfo.py\npid           4600\nname          chrome\nparent        4554 (bash)\nexe           /opt/google/chrome/chrome\ncwd           /home/giampaolo\ncmdline       /opt/google/chrome/chrome\nstarted       2016-09-19 11:12\ncpu-tspent    27:27.68\ncpu-times     user=8914.32, system=3530.59,\n              children_user=1.46, children_system=1.31\ncpu-affinity  [0, 1, 2, 3, 4, 5, 6, 7]\nmemory        rss=520.5M, vms=1.9G, shared=132.6M, text=95.0M, lib=0B,\n              data=816.5M, dirty=0B\nmemory %      3.26\nuser          giampaolo\nuids          real=1000, effective=1000, saved=1000\nuids          real=1000, effective=1000, saved=1000\nterminal      /dev/pts/2\nstatus        sleeping\nnice          0\nionice        class=IOPriority.IOPRIO_CLASS_NONE, value=0\nnum-threads   47\nnum-fds       379\nI/O           read_count=96.6M, write_count=80.7M,\n              read_bytes=293.2M, write_bytes=24.5G\nctx-switches  voluntary=30426463, involuntary=460108\nchildren      PID    NAME\n              4605   cat\n              4606   cat\n              4609   chrome\n              4669   chrome\nopen-files    PATH\n              /opt/google/chrome/icudtl.dat\n              /opt/google/chrome/snapshot_blob.bin\n              /opt/google/chrome/natives_blob.bin\n              /opt/google/chrome/chrome_100_percent.pak\n              [...]\nconnections   PROTO LOCAL ADDR            REMOTE ADDR               STATUS\n              UDP   10.0.0.3:3693         *:*                       NONE\n              TCP   10.0.0.3:55102        172.217.22.14:443         ESTABLISHED\n              UDP   10.0.0.3:35172        *:*                       NONE\n              TCP   10.0.0.3:32922        172.217.16.163:443        ESTABLISHED\n              UDP   :::5353               *:*                       NONE\n              UDP   10.0.0.3:59925        *:*                       NONE\nthreads       TID              USER          SYSTEM\n              11795             0.7            1.35\n              11796            0.68            1.37\n              15887            0.74            0.03\n              19055            0.77            0.01\n              [...]\n              total=47\nres-limits    RLIMIT                     SOFT       HARD\n              virtualmem             infinity   infinity\n              coredumpsize                  0   infinity\n              cputime                infinity   infinity\n              datasize               infinity   infinity\n              filesize               infinity   infinity\n              locks                  infinity   infinity\n              memlock                   65536      65536\n              msgqueue                 819200     819200\n              nice                          0          0\n              openfiles                  8192      65536\n              maxprocesses              63304      63304\n              rss                    infinity   infinity\n              realtimeprio                  0          0\n              rtimesched             infinity   infinity\n              sigspending               63304      63304\n              stack                   8388608   infinity\nmem-maps      RSS      PATH\n              381.4M   [anon]\n              62.8M    /opt/google/chrome/chrome\n              15.8M    /home/giampaolo/.config/google-chrome/Default/History\n              6.6M     /home/giampaolo/.config/google-chrome/Default/Favicons\n              [...]\n\"\"\"\n\nimport argparse\nimport datetime\nimport socket\nimport sys\n\nimport psutil\nfrom psutil._common import bytes2human\n\nACCESS_DENIED = ''\nNON_VERBOSE_ITERATIONS = 4\nRLIMITS_MAP = {\n    \"RLIMIT_AS\": \"virtualmem\",\n    \"RLIMIT_CORE\": \"coredumpsize\",\n    \"RLIMIT_CPU\": \"cputime\",\n    \"RLIMIT_DATA\": \"datasize\",\n    \"RLIMIT_FSIZE\": \"filesize\",\n    \"RLIMIT_MEMLOCK\": \"memlock\",\n    \"RLIMIT_MSGQUEUE\": \"msgqueue\",\n    \"RLIMIT_NICE\": \"nice\",\n    \"RLIMIT_NOFILE\": \"openfiles\",\n    \"RLIMIT_NPROC\": \"maxprocesses\",\n    \"RLIMIT_NPTS\": \"pseudoterms\",\n    \"RLIMIT_RSS\": \"rss\",\n    \"RLIMIT_RTPRIO\": \"realtimeprio\",\n    \"RLIMIT_RTTIME\": \"rtimesched\",\n    \"RLIMIT_SBSIZE\": \"sockbufsize\",\n    \"RLIMIT_SIGPENDING\": \"sigspending\",\n    \"RLIMIT_STACK\": \"stack\",\n    \"RLIMIT_SWAP\": \"swapuse\",\n}\n\n\ndef print_(a, b):\n    if sys.stdout.isatty() and psutil.POSIX:\n        fmt = \"\\x1b[1;32m{:<13}\\x1b[0m {}\".format(a, b)\n    else:\n        fmt = \"{:<11} {}\".format(a, b)\n    print(fmt)\n\n\ndef str_ntuple(nt, convert_bytes=False):\n    if nt == ACCESS_DENIED:\n        return \"\"\n    if not convert_bytes:\n        return \", \".join([f\"{x}={getattr(nt, x)}\" for x in nt._fields])\n    else:\n        return \", \".join(\n            [f\"{x}={bytes2human(getattr(nt, x))}\" for x in nt._fields]\n        )\n\n\ndef run(pid, verbose=False):\n    try:\n        proc = psutil.Process(pid)\n        pinfo = proc.as_dict(ad_value=ACCESS_DENIED)\n    except psutil.NoSuchProcess as err:\n        sys.exit(str(err))\n\n    # collect other proc info\n    with proc.oneshot():\n        try:\n            parent = proc.parent()\n            parent = f\"({parent.name()})\" if parent else \"\"\n        except psutil.Error:\n            parent = ''\n        try:\n            pinfo['children'] = proc.children()\n        except psutil.Error:\n            pinfo['children'] = []\n        if pinfo['create_time']:\n            started = datetime.datetime.fromtimestamp(\n                pinfo['create_time']\n            ).strftime('%Y-%m-%d %H:%M')\n        else:\n            started = ACCESS_DENIED\n\n    # here we go\n    print_('pid', pinfo['pid'])\n    print_('name', pinfo['name'])\n    print_('parent', f\"{pinfo['ppid']} {parent}\")\n    print_('exe', pinfo['exe'])\n    print_('cwd', pinfo['cwd'])\n    print_('cmdline', ' '.join(pinfo['cmdline']))\n    print_('started', started)\n\n    cpu_tot_time = datetime.timedelta(seconds=sum(pinfo['cpu_times']))\n    cpu_tot_time = \"{}:{}.{}\".format(\n        cpu_tot_time.seconds // 60 % 60,\n        str(cpu_tot_time.seconds % 60).zfill(2),\n        str(cpu_tot_time.microseconds)[:2],\n    )\n    print_('cpu-tspent', cpu_tot_time)\n    print_('cpu-times', str_ntuple(pinfo['cpu_times']))\n    if hasattr(proc, \"cpu_affinity\"):\n        print_(\"cpu-affinity\", pinfo[\"cpu_affinity\"])\n    if hasattr(proc, \"cpu_num\"):\n        print_(\"cpu-num\", pinfo[\"cpu_num\"])\n\n    print_('memory', str_ntuple(pinfo['memory_info'], convert_bytes=True))\n    print_('memory %', round(pinfo['memory_percent'], 2))\n    print_('user', pinfo['username'])\n    if psutil.POSIX:\n        print_('uids', str_ntuple(pinfo['uids']))\n    if psutil.POSIX:\n        print_('uids', str_ntuple(pinfo['uids']))\n    if psutil.POSIX:\n        print_('terminal', pinfo['terminal'] or '')\n\n    print_('status', pinfo['status'])\n    print_('nice', pinfo['nice'])\n    if hasattr(proc, \"ionice\"):\n        try:\n            ionice = proc.ionice()\n        except psutil.Error:\n            pass\n        else:\n            if psutil.WINDOWS:\n                print_(\"ionice\", ionice)\n            else:\n                print_(\n                    \"ionice\",\n                    f\"class={ionice.ioclass}, value={ionice.value}\",\n                )\n\n    print_('num-threads', pinfo['num_threads'])\n    if psutil.POSIX:\n        print_('num-fds', pinfo['num_fds'])\n    if psutil.WINDOWS:\n        print_('num-handles', pinfo['num_handles'])\n\n    if 'io_counters' in pinfo:\n        print_('I/O', str_ntuple(pinfo['io_counters'], convert_bytes=True))\n    if 'num_ctx_switches' in pinfo:\n        print_(\"ctx-switches\", str_ntuple(pinfo['num_ctx_switches']))\n    if pinfo['children']:\n        template = \"{:<6} {}\"\n        print_(\"children\", template.format(\"PID\", \"NAME\"))\n        for child in pinfo['children']:\n            try:\n                print_(\"\", template.format(child.pid, child.name()))\n            except psutil.AccessDenied:\n                print_(\"\", template.format(child.pid, \"\"))\n            except psutil.NoSuchProcess:\n                pass\n\n    if pinfo['open_files']:\n        print_('open-files', 'PATH')\n        for i, file in enumerate(pinfo['open_files']):\n            if not verbose and i >= NON_VERBOSE_ITERATIONS:\n                print_(\"\", \"[...]\")\n                break\n            print_('', file.path)\n    else:\n        print_('open-files', '')\n\n    if pinfo['net_connections']:\n        template = \"{:<5} {:<25} {:<25} {}\"\n        print_(\n            'connections',\n            template.format(\"PROTO\", \"LOCAL ADDR\", \"REMOTE ADDR\", \"STATUS\"),\n        )\n        for conn in pinfo['net_connections']:\n            if conn.type == socket.SOCK_STREAM:\n                type = 'TCP'\n            elif conn.type == socket.SOCK_DGRAM:\n                type = 'UDP'\n            else:\n                type = 'UNIX'\n            lip, lport = conn.laddr\n            if not conn.raddr:\n                rip, rport = '*', '*'\n            else:\n                rip, rport = conn.raddr\n            line = template.format(\n                type,\n                f\"{lip}:{lport}\",\n                f\"{rip}:{rport}\",\n                conn.status,\n            )\n            print_('', line)\n    else:\n        print_('connections', '')\n\n    if pinfo['threads'] and len(pinfo['threads']) > 1:\n        template = \"{:<5} {:>12} {:>12}\"\n        print_(\"threads\", template.format(\"TID\", \"USER\", \"SYSTEM\"))\n        for i, thread in enumerate(pinfo['threads']):\n            if not verbose and i >= NON_VERBOSE_ITERATIONS:\n                print_(\"\", \"[...]\")\n                break\n            print_(\"\", template.format(*thread))\n        print_('', f\"total={len(pinfo['threads'])}\")\n    else:\n        print_('threads', '')\n\n    if hasattr(proc, \"rlimit\"):\n        res_names = [x for x in dir(psutil) if x.startswith(\"RLIMIT\")]\n        resources = []\n        for res_name in res_names:\n            try:\n                soft, hard = proc.rlimit(getattr(psutil, res_name))\n            except psutil.AccessDenied:\n                pass\n            else:\n                resources.append((res_name, soft, hard))\n        if resources:\n            template = \"{:<12} {:>15} {:>15}\"\n            print_(\"res-limits\", template.format(\"RLIMIT\", \"SOFT\", \"HARD\"))\n            for res_name, soft, hard in resources:\n                if soft == psutil.RLIM_INFINITY:\n                    soft = \"infinity\"\n                if hard == psutil.RLIM_INFINITY:\n                    hard = \"infinity\"\n                print_(\n                    '',\n                    template.format(\n                        RLIMITS_MAP.get(res_name, res_name), soft, hard\n                    ),\n                )\n\n    if hasattr(proc, \"environ\") and pinfo['environ']:\n        template = \"{:<25} {}\"\n        print_(\"environ\", template.format(\"NAME\", \"VALUE\"))\n        for i, k in enumerate(sorted(pinfo['environ'])):\n            if not verbose and i >= NON_VERBOSE_ITERATIONS:\n                print_(\"\", \"[...]\")\n                break\n            print_(\"\", template.format(k, pinfo[\"environ\"][k]))\n\n    if pinfo.get('memory_maps', None):\n        template = \"{:<8} {}\"\n        print_(\"mem-maps\", template.format(\"RSS\", \"PATH\"))\n        maps = sorted(pinfo['memory_maps'], key=lambda x: x.rss, reverse=True)\n        for i, region in enumerate(maps):\n            if not verbose and i >= NON_VERBOSE_ITERATIONS:\n                print_(\"\", \"[...]\")\n                break\n            print_(\"\", template.format(bytes2human(region.rss), region.path))\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"print information about a process\"\n    )\n    parser.add_argument(\"pid\", type=int, help=\"process pid\", nargs='?')\n    parser.add_argument(\n        '--verbose', '-v', action='store_true', help=\"print more info\"\n    )\n    args = parser.parse_args()\n    run(args.pid, args.verbose)\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "scripts/procsmem.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Show detailed memory usage about all (querable) processes.\n\nProcesses are sorted by their \"USS\" (Unique Set Size) memory, which is\nprobably the most representative metric for determining how much memory\nis actually being used by a process.\n\nThis is similar to \"smem\" cmdline utility on Linux:\nhttps://www.selenic.com/smem/\n\nAuthor: Giampaolo Rodola' <g.rodola@gmail.com>\n\n~/svn/psutil$ ./scripts/procsmem.py\nPID     User    Cmdline                            USS     PSS    Swap     RSS\n==============================================================================\n...\n3986    giampao /usr/bin/python3 /usr/bin/indi   15.3M   16.6M      0B   25.6M\n3906    giampao /usr/lib/ibus/ibus-ui-gtk3       17.6M   18.1M      0B   26.7M\n3991    giampao python /usr/bin/hp-systray -x    19.0M   23.3M      0B   40.7M\n3830    giampao /usr/bin/ibus-daemon --daemoni   19.0M   19.0M      0B   21.4M\n20529   giampao /opt/sublime_text/plugin_host    19.9M   20.1M      0B   22.0M\n3990    giampao nautilus -n                      20.6M   29.9M      0B   50.2M\n3898    giampao /usr/lib/unity/unity-panel-ser   27.1M   27.9M      0B   37.7M\n4176    giampao /usr/lib/evolution/evolution-c   35.7M   36.2M      0B   41.5M\n20712   giampao /usr/bin/python -B /home/giamp   45.6M   45.9M      0B   49.4M\n3880    giampao /usr/lib/x86_64-linux-gnu/hud/   51.6M   52.7M      0B   61.3M\n20513   giampao /opt/sublime_text/sublime_text   65.8M   73.0M      0B   87.9M\n3976    giampao compiz                          115.0M  117.0M      0B  130.9M\n32486   giampao skype                           145.1M  147.5M      0B  149.6M\n\n\"\"\"\n\nimport sys\n\nimport psutil\n\nif not hasattr(psutil.Process, \"memory_footprint\"):\n    sys.exit(\"can't retrieve USS memory on this platform\")\n\n\ndef convert_bytes(n):\n    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')\n    prefix = {}\n    for i, s in enumerate(symbols):\n        prefix[s] = 1 << (i + 1) * 10\n    for s in reversed(symbols):\n        if n >= prefix[s]:\n            value = float(n) / prefix[s]\n            return f\"{value:.1f}{s}\"\n    return f\"{n}B\"\n\n\ndef main():\n    ad_pids = []\n    procs = []\n    for p in psutil.process_iter():\n        with p.oneshot():\n            try:\n                mem = p.memory_footprint()\n                info = p.as_dict([\"cmdline\", \"username\", \"memory_info\"])\n            except psutil.AccessDenied:\n                ad_pids.append(p.pid)\n            except psutil.NoSuchProcess:\n                pass\n            else:\n                p._uss = mem.uss\n                p._rss = info[\"memory_info\"].rss\n                p._vms = info[\"memory_info\"].vms\n                if not p._uss:\n                    continue\n                p._pss = getattr(mem, \"pss\", \"\")\n                p._swap = getattr(mem, \"swap\", \"\")\n                p._info = info\n                procs.append(p)\n\n    procs.sort(key=lambda p: p._uss)\n    templ = \"{:<7} {:<7} {:>7} {:>7} {:>7} {:>7} {:>7} {}\"\n    header = templ.format(\n        \"PID\", \"User\", \"USS\", \"PSS\", \"Swap\", \"RSS\", \"VMS\", \"Cmdline\"\n    )\n    print(header)\n    print(\"=\" * len(header))\n    for p in procs[:86]:\n        cmd = \" \".join(p._info[\"cmdline\"])[:50] if p._info[\"cmdline\"] else \"\"\n        line = templ.format(\n            p.pid,\n            p._info[\"username\"][:7] if p._info[\"username\"] else \"\",\n            convert_bytes(p._uss),\n            convert_bytes(p._pss) if p._pss else \"\",\n            convert_bytes(p._swap) if p._swap else \"\",\n            convert_bytes(p._rss),\n            convert_bytes(p._vms),\n            cmd,\n        )\n        print(line)\n    if ad_pids:\n        print(f\"warning: access denied for {len(ad_pids)} pids\")\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "scripts/ps.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'ps aux'.\n\n$ python3 scripts/ps.py\nUSER         PID  %MEM     VSZ     RSS  NICE STATUS  START   TIME  CMDLINE\nroot           1   0.0  220.9M    6.5M        sleep  Mar27  09:10  /lib/systemd\nroot           2   0.0    0.0B    0.0B        sleep  Mar27  00:00  kthreadd\nroot           4   0.0    0.0B    0.0B   -20   idle  Mar27  00:00  kworker/0:0H\nroot           6   0.0    0.0B    0.0B   -20   idle  Mar27  00:00  mm_percpu_wq\nroot           7   0.0    0.0B    0.0B        sleep  Mar27  00:06  ksoftirqd/0\nroot           8   0.0    0.0B    0.0B         idle  Mar27  03:32  rcu_sched\nroot           9   0.0    0.0B    0.0B         idle  Mar27  00:00  rcu_bh\nroot          10   0.0    0.0B    0.0B        sleep  Mar27  00:00  migration/0\nroot          11   0.0    0.0B    0.0B        sleep  Mar27  00:00  watchdog/0\nroot          12   0.0    0.0B    0.0B        sleep  Mar27  00:00  cpuhp/0\nroot          13   0.0    0.0B    0.0B        sleep  Mar27  00:00  cpuhp/1\nroot          14   0.0    0.0B    0.0B        sleep  Mar27  00:01  watchdog/1\nroot          15   0.0    0.0B    0.0B        sleep  Mar27  00:00  migration/1\n[...]\ngiampaolo  19704   1.5    1.9G  235.6M        sleep  17:39  01:11  firefox\nroot       20414   0.0    0.0B    0.0B         idle  Apr04  00:00  kworker/4:2\ngiampaolo  20952   0.0   10.7M  100.0K        sleep  Mar28  00:00  sh -c /usr\ngiampaolo  20953   0.0  269.0M  528.0K        sleep  Mar28  00:00  /usr/lib/\ngiampaolo  22150   3.3    2.4G  525.5M        sleep  Apr02  49:09  /usr/lib/\nroot       22338   0.0    0.0B    0.0B         idle  02:04  00:00  kworker/1:2\ngiampaolo  24123   0.0   35.0M    7.0M        sleep  02:12  00:02  bash\n\"\"\"\n\nimport datetime\nimport shutil\nimport time\n\nimport psutil\nfrom psutil._common import bytes2human\n\n\ndef main():\n    today_day = datetime.date.today()\n    # fmt: off\n    templ = \"{:<10} {:>5} {:>5} {:>7} {:>7} {:>5} {:>6} {:>6} {:>6}  {}\"\n    attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times',\n             'create_time', 'memory_info', 'status', 'nice', 'username']\n    print(templ.format(\"USER\", \"PID\", \"%MEM\", \"VSZ\", \"RSS\", \"NICE\",\n                       \"STATUS\", \"START\", \"TIME\", \"CMDLINE\"))\n    # fmt: on\n    for p in psutil.process_iter(attrs, ad_value=None):\n        if p.info['create_time']:\n            ctime = datetime.datetime.fromtimestamp(p.info['create_time'])\n            if ctime.date() == today_day:\n                ctime = ctime.strftime(\"%H:%M\")\n            else:\n                ctime = ctime.strftime(\"%b%d\")\n        else:\n            ctime = ''\n        if p.info['cpu_times']:\n            cputime = time.strftime(\n                \"%M:%S\", time.localtime(sum(p.info['cpu_times']))\n            )\n        else:\n            cputime = ''\n\n        user = p.info['username']\n        if not user and psutil.POSIX:\n            try:\n                user = p.uids()[0]\n            except psutil.Error:\n                pass\n        if user and psutil.WINDOWS and '\\\\' in user:\n            user = user.split('\\\\')[1]\n        if not user:\n            user = ''\n        user = user[:9]\n        vms = (\n            bytes2human(p.info['memory_info'].vms)\n            if p.info['memory_info'] is not None\n            else ''\n        )\n        rss = (\n            bytes2human(p.info['memory_info'].rss)\n            if p.info['memory_info'] is not None\n            else ''\n        )\n        memp = (\n            round(p.info['memory_percent'], 1)\n            if p.info['memory_percent'] is not None\n            else ''\n        )\n        nice = int(p.info['nice']) if p.info['nice'] else ''\n        if p.info['cmdline']:\n            cmdline = ' '.join(p.info['cmdline'])\n        else:\n            cmdline = p.info['name']\n        status = p.info['status'][:5] if p.info['status'] else ''\n\n        line = templ.format(\n            user,\n            p.info['pid'],\n            memp,\n            vms,\n            rss,\n            nice,\n            status,\n            ctime,\n            cputime,\n            cmdline,\n        )\n        print(line[: shutil.get_terminal_size()[0]])\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/pstree.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Similar to 'ps aux --forest' on Linux, prints the process list\nas a tree structure.\n\n$ python3 scripts/pstree.py\n0 ?\n|- 1 init\n| |- 289 cgmanager\n| |- 616 upstart-socket-bridge\n| |- 628 rpcbind\n| |- 892 upstart-file-bridge\n| |- 907 dbus-daemon\n| |- 978 avahi-daemon\n| | `_ 979 avahi-daemon\n| |- 987 NetworkManager\n| | |- 2242 dnsmasq\n| | `_ 10699 dhclient\n| |- 993 polkitd\n| |- 1061 getty\n| |- 1066 su\n| | `_ 1190 salt-minion...\n...\n\"\"\"\n\nimport collections\nimport sys\n\nimport psutil\n\n\ndef print_tree(parent, tree, indent=''):\n    try:\n        name = psutil.Process(parent).name()\n    except psutil.Error:\n        name = \"?\"\n    print(parent, name)\n    if parent not in tree:\n        return\n    children = tree[parent][:-1]\n    for child in children:\n        sys.stdout.write(indent + \"|- \")\n        print_tree(child, tree, indent + \"| \")\n    child = tree[parent][-1]\n    sys.stdout.write(indent + \"`_ \")\n    print_tree(child, tree, indent + \"  \")\n\n\ndef main():\n    # construct a dict where 'values' are all the processes\n    # having 'key' as their parent\n    tree = collections.defaultdict(list)\n    for p in psutil.process_iter():\n        try:\n            tree[p.ppid()].append(p.pid)\n        except (psutil.NoSuchProcess, psutil.ZombieProcess):\n            pass\n    # on systems supporting PID 0, PID 0's parent is usually 0\n    if 0 in tree and 0 in tree[0]:\n        tree[0].remove(0)\n    print_tree(min(tree), tree)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/sensors.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'sensors' utility on Linux printing hardware temperatures,\nfans speed and battery info.\n\n$ python3 scripts/sensors.py\nasus\n    Temperatures:\n        asus                 57.0°C (high=None°C, critical=None°C)\n    Fans:\n        cpu_fan              3500 RPM\nacpitz\n    Temperatures:\n        acpitz               57.0°C (high=108.0°C, critical=108.0°C)\ncoretemp\n    Temperatures:\n        Physical id 0        61.0°C (high=87.0°C, critical=105.0°C)\n        Core 0               61.0°C (high=87.0°C, critical=105.0°C)\n        Core 1               59.0°C (high=87.0°C, critical=105.0°C)\nBattery:\n    charge:     84.95%\n    status:     charging\n    plugged in: yes\n\"\"\"\n\nimport psutil\n\n\ndef secs2hours(secs):\n    mm, ss = divmod(secs, 60)\n    hh, mm = divmod(mm, 60)\n    return f\"{int(hh)}:{int(mm):02}:{int(ss):02}\"\n\n\ndef main():\n    if hasattr(psutil, \"sensors_temperatures\"):\n        temps = psutil.sensors_temperatures()\n    else:\n        temps = {}\n    fans = psutil.sensors_fans() if hasattr(psutil, \"sensors_fans\") else {}\n    if hasattr(psutil, \"sensors_battery\"):\n        battery = psutil.sensors_battery()\n    else:\n        battery = None\n\n    if not any((temps, fans, battery)):\n        print(\"can't read any temperature, fans or battery info\")\n        return\n\n    names = set(list(temps.keys()) + list(fans.keys()))\n    for name in names:\n        print(name)\n        # Temperatures.\n        if name in temps:\n            print(\"    Temperatures:\")\n            for entry in temps[name]:\n                s = \"        {:<20} {}°C (high={}°C, critical={}°C)\".format(\n                    entry.label or name,\n                    entry.current,\n                    entry.high,\n                    entry.critical,\n                )\n                print(s)\n        # Fans.\n        if name in fans:\n            print(\"    Fans:\")\n            for entry in fans[name]:\n                print(\n                    \"        {:<20} {} RPM\".format(\n                        entry.label or name, entry.current\n                    )\n                )\n\n    # Battery.\n    if battery:\n        print(\"Battery:\")\n        print(f\"    charge:     {round(battery.percent, 2)}%\")\n        if battery.power_plugged:\n            print(\n                \"    status:     {}\".format(\n                    \"charging\" if battery.percent < 100 else \"fully charged\"\n                )\n            )\n            print(\"    plugged in: yes\")\n        else:\n            print(f\"    left:       {secs2hours(battery.secsleft)}\")\n            print(\"    status:     discharging\")\n            print(\"    plugged in: no\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/temperatures.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'sensors' utility on Linux printing hardware temperatures.\n\n$ python3 scripts/sensors.py\nasus\n    asus                 47.0 °C (high = None °C, critical = None °C)\n\nacpitz\n    acpitz               47.0 °C (high = 103.0 °C, critical = 103.0 °C)\n\ncoretemp\n    Physical id 0        54.0 °C (high = 100.0 °C, critical = 100.0 °C)\n    Core 0               47.0 °C (high = 100.0 °C, critical = 100.0 °C)\n    Core 1               48.0 °C (high = 100.0 °C, critical = 100.0 °C)\n    Core 2               47.0 °C (high = 100.0 °C, critical = 100.0 °C)\n    Core 3               54.0 °C (high = 100.0 °C, critical = 100.0 °C)\n\"\"\"\n\nimport sys\n\nimport psutil\n\n\ndef main():\n    if not hasattr(psutil, \"sensors_temperatures\"):\n        sys.exit(\"platform not supported\")\n    temps = psutil.sensors_temperatures()\n    if not temps:\n        sys.exit(\"can't read any temperature\")\n    for name, entries in temps.items():\n        print(name)\n        for entry in entries:\n            line = \"    {:<20} {} °C (high = {} °C, critical = %{} °C)\".format(\n                entry.label or name,\n                entry.current,\n                entry.high,\n                entry.critical,\n            )\n            print(line)\n        print()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/top.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of top / htop.\n\nAuthor: Giampaolo Rodola' <g.rodola@gmail.com>\n\n$ python3 scripts/top.py\n CPU0  [||||                                    ]  10.9%\n CPU1  [|||||                                   ]  13.1%\n CPU2  [|||||                                   ]  12.8%\n CPU3  [||||                                    ]  11.5%\n Mem   [|||||||||||||||||||||||||||||           ]  73.0% 11017M / 15936M\n Swap  [                                        ]   1.3%   276M / 20467M\n Processes: 347 (sleeping=273, running=1, idle=73)\n Load average: 1.10 1.28 1.34  Uptime: 8 days, 21:15:40\n\nPID    USER       NI   VIRT    RES  CPU%  MEM%     TIME+  NAME\n5368   giampaol    0   7.2G   4.3G  41.8  27.7  56:34.18  VirtualBox\n24976  giampaol    0   2.1G 487.2M  18.7   3.1  22:05.16  Web Content\n22731  giampaol    0   3.2G 596.2M  11.6   3.7  35:04.90  firefox\n1202   root        0 807.4M 288.5M  10.6   1.8  12:22.12  Xorg\n22811  giampaol    0   2.8G 741.8M   9.0   4.7   2:26.61  Web Content\n2590   giampaol    0   2.3G 579.4M   5.5   3.6  28:02.70  compiz\n22990  giampaol    0   3.0G   1.2G   4.2   7.6   4:30.32  Web Content\n18412  giampaol    0  90.1M  14.5M   3.5   0.1   0:00.26  python3\n26971  netdata     0  20.8M   3.9M   2.9   0.0   3:17.14  apps.plugin\n2421   giampaol    0   3.3G  36.9M   2.3   0.2  57:14.21  pulseaudio\n...\n\"\"\"\n\nimport datetime\nimport sys\nimport time\n\ntry:\n    import curses\nexcept ImportError:\n    sys.exit('platform not supported')\n\nimport psutil\nfrom psutil._common import bytes2human\n\nwin = curses.initscr()\nlineno = 0\ncolors_map = dict(green=3, red=10, yellow=4)\n\n\ndef printl(line, color=None, bold=False, highlight=False):\n    \"\"\"A thin wrapper around curses's addstr().\"\"\"\n    global lineno\n    try:\n        flags = 0\n        if color:\n            flags |= curses.color_pair(colors_map[color])\n        if bold:\n            flags |= curses.A_BOLD\n        if highlight:\n            line += \" \" * (win.getmaxyx()[1] - len(line))\n            flags |= curses.A_STANDOUT\n        win.addstr(lineno, 0, line, flags)\n    except curses.error:\n        lineno = 0\n        win.refresh()\n        raise\n    else:\n        lineno += 1\n\n\n# --- /curses stuff\n\n\ndef poll(interval):\n    # sleep some time\n    time.sleep(interval)\n    procs = []\n    procs_status = {}\n    for p in psutil.process_iter():\n        try:\n            p.dict = p.as_dict([\n                'username',\n                'nice',\n                'memory_info',\n                'memory_percent',\n                'cpu_percent',\n                'cpu_times',\n                'name',\n                'status',\n            ])\n            try:\n                procs_status[p.dict['status']] += 1\n            except KeyError:\n                procs_status[p.dict['status']] = 1\n        except psutil.NoSuchProcess:\n            pass\n        else:\n            procs.append(p)\n\n    # return processes sorted by CPU percent usage\n    processes = sorted(\n        procs, key=lambda p: p.dict['cpu_percent'], reverse=True\n    )\n    return (processes, procs_status)\n\n\ndef get_color(perc):\n    if perc <= 30:\n        return \"green\"\n    elif perc <= 80:\n        return \"yellow\"\n    else:\n        return \"red\"\n\n\ndef print_header(procs_status, num_procs):\n    \"\"\"Print system-related info, above the process list.\"\"\"\n\n    def get_dashes(perc):\n        dashes = \"|\" * int(float(perc) / 10 * 4)\n        empty_dashes = \" \" * (40 - len(dashes))\n        return dashes, empty_dashes\n\n    # cpu usage\n    percs = psutil.cpu_percent(interval=0, percpu=True)\n    for cpu_num, perc in enumerate(percs):\n        dashes, empty_dashes = get_dashes(perc)\n        line = \" CPU{:<2} [{}{}] {:>5}%\".format(\n            cpu_num, dashes, empty_dashes, perc\n        )\n        printl(line, color=get_color(perc))\n\n    # memory usage\n    mem = psutil.virtual_memory()\n    dashes, empty_dashes = get_dashes(mem.percent)\n    line = \" Mem   [{}{}] {:>5}% {:>6} / {}\".format(\n        dashes,\n        empty_dashes,\n        mem.percent,\n        bytes2human(mem.used),\n        bytes2human(mem.total),\n    )\n    printl(line, color=get_color(mem.percent))\n\n    # swap usage\n    swap = psutil.swap_memory()\n    dashes, empty_dashes = get_dashes(swap.percent)\n    line = \" Swap  [{}{}] {:>5}% {:>6} / {}\".format(\n        dashes,\n        empty_dashes,\n        swap.percent,\n        bytes2human(swap.used),\n        bytes2human(swap.total),\n    )\n    printl(line, color=get_color(swap.percent))\n\n    # processes number and status\n    st = []\n    for x, y in procs_status.items():\n        if y:\n            st.append(f\"{x}={y}\")\n    st.sort(key=lambda x: x[:3] in {'run', 'sle'}, reverse=1)\n    printl(f\" Processes: {num_procs} ({', '.join(st)})\")\n    # load average, uptime\n    uptime = datetime.datetime.now() - datetime.datetime.fromtimestamp(\n        psutil.boot_time()\n    )\n    av1, av2, av3 = psutil.getloadavg()\n    line = \" Load average: {:.2f} {:.2f} {:.2f}  Uptime: {}\".format(\n        av1,\n        av2,\n        av3,\n        str(uptime).split('.')[0],\n    )\n    printl(line)\n\n\ndef refresh_window(procs, procs_status):\n    \"\"\"Print results on screen by using curses.\"\"\"\n    curses.endwin()\n    templ = \"{:<6} {:<8} {:>4} {:>6} {:>6} {:>5} {:>5} {:>9}  {:>2}\"\n    win.erase()\n    header = templ.format(\n        \"PID\",\n        \"USER\",\n        \"NI\",\n        \"VIRT\",\n        \"RES\",\n        \"CPU%\",\n        \"MEM%\",\n        \"TIME+\",\n        \"NAME\",\n    )\n    print_header(procs_status, len(procs))\n    printl(\"\")\n    printl(header, bold=True, highlight=True)\n    for p in procs:\n        # TIME+ column shows process CPU cumulative time and it\n        # is expressed as: \"mm:ss.ms\"\n        if p.dict['cpu_times'] is not None:\n            ctime = datetime.timedelta(seconds=sum(p.dict['cpu_times']))\n            ctime = \"{}:{}.{}\".format(\n                ctime.seconds // 60 % 60,\n                str(ctime.seconds % 60).zfill(2),\n                str(ctime.microseconds)[:2],\n            )\n        else:\n            ctime = ''\n        if p.dict['memory_percent'] is not None:\n            p.dict['memory_percent'] = round(p.dict['memory_percent'], 1)\n        else:\n            p.dict['memory_percent'] = ''\n        if p.dict['cpu_percent'] is None:\n            p.dict['cpu_percent'] = ''\n        username = p.dict['username'][:8] if p.dict['username'] else ''\n        line = templ.format(\n            p.pid,\n            username,\n            p.dict['nice'],\n            bytes2human(getattr(p.dict['memory_info'], 'vms', 0)),\n            bytes2human(getattr(p.dict['memory_info'], 'rss', 0)),\n            p.dict['cpu_percent'],\n            p.dict['memory_percent'],\n            ctime,\n            p.dict['name'] or '',\n        )\n        try:\n            printl(line)\n        except curses.error:\n            break\n        win.refresh()\n\n\ndef setup():\n    curses.start_color()\n    curses.use_default_colors()\n    for i in range(curses.COLORS):\n        curses.init_pair(i + 1, i, -1)\n    curses.endwin()\n    win.nodelay(1)\n\n\ndef tear_down():\n    win.keypad(0)\n    curses.nocbreak()\n    curses.echo()\n    curses.endwin()\n\n\ndef main():\n    setup()\n    try:\n        interval = 0\n        while True:\n            if win.getch() == ord('q'):\n                break\n            args = poll(interval)\n            refresh_window(*args)\n            interval = 1\n    except (KeyboardInterrupt, SystemExit):\n        pass\n    finally:\n        tear_down()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/who.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"A clone of 'who' command; print information about users who are\ncurrently logged in.\n\n$ python3 scripts/who.py\ngiampaolo    console    2017-03-25 22:24                loginwindow\ngiampaolo    ttys000    2017-03-25 23:28 (10.0.2.2)     sshd\n\"\"\"\n\nfrom datetime import datetime\n\nimport psutil\n\n\ndef main():\n    users = psutil.users()\n    for user in users:\n        proc_name = psutil.Process(user.pid).name() if user.pid else \"\"\n        line = \"{:<12} {:<10} {:<10} {:<14} {}\".format(\n            user.name,\n            user.terminal or '-',\n            datetime.fromtimestamp(user.started).strftime(\"%Y-%m-%d %H:%M\"),\n            f\"({user.host or ''})\",\n            proc_name,\n        )\n        print(line)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "scripts/winservices.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\nr\"\"\"List all Windows services installed.\n\n$ python3 scripts/winservices.py\nAeLookupSvc (Application Experience)\nstatus: stopped, start: manual, username: localSystem, pid: None\nbinpath: C:\\Windows\\system32\\svchost.exe -k netsvcs\n\nALG (Application Layer Gateway Service)\nstatus: stopped, start: manual, username: NT AUTHORITY\\LocalService, pid: None\nbinpath: C:\\Windows\\System32\\alg.exe\n\nAPNMCP (Ask Update Service)\nstatus: running, start: automatic, username: LocalSystem, pid: 1108\nbinpath: \"C:\\Program Files (x86)\\AskPartnerNetwork\\Toolbar\\apnmcp.exe\"\n\nAppIDSvc (Application Identity)\nstatus: stopped, start: manual, username: NT Authority\\LocalService, pid: None\nbinpath: C:\\Windows\\system32\\svchost.exe -k LocalServiceAndNoImpersonation\n\nAppinfo (Application Information)\nstatus: stopped, start: manual, username: LocalSystem, pid: None\nbinpath: C:\\Windows\\system32\\svchost.exe -k netsvcs\n...\n\"\"\"\n\nimport os\nimport sys\n\nimport psutil\n\nif os.name != 'nt':\n    sys.exit(\"platform not supported (Windows only)\")\n\n\ndef main():\n    for service in psutil.win_service_iter():\n        if service.name() == \"WaaSMedicSvc\":\n            # known issue in Windows 11 reading the description\n            # https://learn.microsoft.com/en-us/answers/questions/1320388/in-windows-11-version-22h2-there-it-shows-(failed\n            # https://github.com/giampaolo/psutil/issues/2383\n            continue\n        info = service.as_dict()\n        print(f\"{info['name']!r} ({info['display_name']!r})\")\n        s = \"status: {}, start: {}, username: {}, pid: {}\".format(\n            info['status'],\n            info['start_type'],\n            info['username'],\n            info['pid'],\n        )\n        print(s)\n        print(f\"binpath: {info['binpath']}\")\n        print()\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Cross-platform lib for process and system monitoring in Python.\"\"\"\n\nimport contextlib\nimport glob\nimport io\nimport os\nimport pathlib\nimport shutil\nimport struct\nimport subprocess\nimport sys\nimport sysconfig\nimport tempfile\nimport warnings\n\nwith warnings.catch_warnings():\n    warnings.simplefilter(\"ignore\")\n    try:\n        import setuptools\n        from setuptools import Extension\n        from setuptools import setup\n    except ImportError:\n        if \"CIBUILDWHEEL\" in os.environ:\n            raise\n        setuptools = None\n        from distutils.core import Extension\n        from distutils.core import setup\n\n\nROOT_DIR = pathlib.Path(__file__).resolve().parent\nsys.path.insert(0, str(ROOT_DIR))\n\nfrom _bootstrap import get_version  # noqa: E402\nfrom _bootstrap import load_module  # noqa: E402\n\n_common = load_module(ROOT_DIR / \"psutil\" / \"_common.py\")\n\nAIX = _common.AIX\nBSD = _common.BSD\nFREEBSD = _common.FREEBSD\nLINUX = _common.LINUX\nMACOS = _common.MACOS\nNETBSD = _common.NETBSD\nOPENBSD = _common.OPENBSD\nPOSIX = _common.POSIX\nSUNOS = _common.SUNOS\nWINDOWS = _common.WINDOWS\nhilite = _common.hilite\n\nPYPY = '__pypy__' in sys.builtin_module_names\nPY36_PLUS = sys.version_info[:2] >= (3, 6)\nPY37_PLUS = sys.version_info[:2] >= (3, 7)\nCP36_PLUS = PY36_PLUS and sys.implementation.name == \"cpython\"\nCP37_PLUS = PY37_PLUS and sys.implementation.name == \"cpython\"\nPy_GIL_DISABLED = sysconfig.get_config_var(\"Py_GIL_DISABLED\")\n\n# Test deps, installable via `pip install .[test]` or\n# `make install-pydeps-test`.\nTEST_DEPS = [\n    \"psleak\",\n    \"pytest\",\n    \"pytest-instafail\",\n    \"pytest-xdist\",\n    \"setuptools\",\n    'pywin32 ; os_name == \"nt\" and implementation_name != \"pypy\"',\n    'wheel ; os_name == \"nt\" and implementation_name != \"pypy\"',\n    'wmi ; os_name == \"nt\" and implementation_name != \"pypy\"',\n]\n\n# Linter deps, installable via `pip install .[lint]` or\n# `make install-pydeps-lint`.\nLINT_DEPS = [\n    \"black\",\n    \"ruff\",\n    \"sphinx-lint\",\n    \"toml-sort\",\n]\n\n# Development deps, installable via `pip install .[dev]` or\n# `make install-pydeps-dev`.\nDEV_DEPS = [\n    *TEST_DEPS,\n    *LINT_DEPS,\n    \"abi3audit\",\n    \"check-manifest\",\n    \"coverage\",\n    \"packaging\",\n    \"pylint\",  # not enforced\n    \"pyperf\",\n    \"pypinfo\",\n    \"pytest-cov\",\n    \"requests\",\n    \"sphinx\",\n    \"sphinx_rtd_theme\",\n    \"twine\",\n    \"validate-pyproject[all]\",\n    \"virtualenv\",\n    \"vulture\",\n    \"wheel\",\n    'colorama ; os_name == \"nt\"',\n    'pyreadline3 ; os_name == \"nt\"',\n]\n\n# The pre-processor macros that are passed to the C compiler when\n# building the extension.\nmacros = []\n\nif POSIX:\n    macros.append((\"PSUTIL_POSIX\", 1))\nif BSD:\n    macros.append((\"PSUTIL_BSD\", 1))\n\n# Needed to determine _Py_PARSE_PID in case it's missing (PyPy).\n# Taken from Lib/test/test_fcntl.py.\n# XXX: not bullet proof as the (long long) case is missing.\nif struct.calcsize('l') <= 8:\n    macros.append(('PSUTIL_SIZEOF_PID_T', '4'))  # int\nelse:\n    macros.append(('PSUTIL_SIZEOF_PID_T', '8'))  # long\n\n\nsources = glob.glob(\"psutil/arch/all/*.c\")\nif POSIX:\n    sources.extend(glob.glob(\"psutil/arch/posix/*.c\"))\n\n\nVERSION = get_version()\nmacros.append(('PSUTIL_VERSION', int(VERSION.replace('.', ''))))\n\n# Py_LIMITED_API lets us create a single wheel which works with multiple\n# python versions, including unreleased ones.\nif setuptools and CP36_PLUS and (MACOS or LINUX) and not Py_GIL_DISABLED:\n    py_limited_api = {\"py_limited_api\": True}\n    options = {\"bdist_wheel\": {\"py_limited_api\": \"cp36\"}}\n    macros.append(('Py_LIMITED_API', '0x03060000'))\nelif setuptools and CP37_PLUS and WINDOWS and not Py_GIL_DISABLED:\n    # PyErr_SetFromWindowsErr / PyErr_SetFromWindowsErrWithFilename are\n    # part of the stable API/ABI starting with CPython 3.7\n    py_limited_api = {\"py_limited_api\": True}\n    options = {\"bdist_wheel\": {\"py_limited_api\": \"cp37\"}}\n    macros.append(('Py_LIMITED_API', '0x03070000'))\nelse:\n    py_limited_api = {}\n    options = {}\n\n\ndef get_long_description():\n    script = ROOT_DIR / \"scripts\" / \"internal\" / \"convert_readme.py\"\n    readme = ROOT_DIR / 'README.rst'\n    p = subprocess.Popen(\n        [sys.executable, script, readme],\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n        universal_newlines=True,\n    )\n    stdout, stderr = p.communicate()\n    if p.returncode != 0:\n        raise RuntimeError(stderr)\n    return stdout\n\n\n@contextlib.contextmanager\ndef silenced_output():\n    with contextlib.redirect_stdout(io.StringIO()):\n        with contextlib.redirect_stderr(io.StringIO()):\n            yield\n\n\ndef has_python_h():\n    include_dir = sysconfig.get_path(\"include\")\n    return os.path.exists(os.path.join(include_dir, \"Python.h\"))\n\n\ndef get_sysdeps():\n    if LINUX:\n        pyimpl = \"pypy\" if PYPY else \"python\"\n        if shutil.which(\"dpkg\"):\n            return \"sudo apt-get install gcc {}3-dev\".format(pyimpl)\n        elif shutil.which(\"rpm\"):\n            return \"sudo yum install gcc {}3-devel\".format(pyimpl)\n        elif shutil.which(\"pacman\"):\n            return \"sudo pacman -S gcc python\"\n        elif shutil.which(\"apk\"):\n            return \"sudo apk add gcc {}3-dev musl-dev linux-headers\".format(\n                pyimpl\n            )\n    elif MACOS:\n        return \"xcode-select --install\"\n    elif FREEBSD:\n        if shutil.which(\"pkg\"):\n            return \"pkg install gcc python3\"\n        elif shutil.which(\"mport\"):  # MidnightBSD\n            return \"mport install gcc python3\"\n    elif OPENBSD:\n        return \"pkg_add -v gcc python3\"\n    elif NETBSD:\n        return \"pkgin install gcc python3\"\n    elif SUNOS:\n        return \"pkg install gcc\"\n\n\ndef print_install_instructions():\n    reasons = []\n    if not shutil.which(\"gcc\"):\n        reasons.append(\"gcc is not installed.\")\n    if not has_python_h():\n        reasons.append(\"Python header files are not installed.\")\n    if reasons:\n        sysdeps = get_sysdeps()\n        if sysdeps:\n            s = \"psutil could not be compiled from sources. \"\n            s += \" \".join(reasons)\n            s += \" Try running:\\n\"\n            s += \"  {}\".format(sysdeps)\n            print(hilite(s, color=\"red\", bold=True), file=sys.stderr)\n\n\ndef unix_can_compile(c_code):\n    from distutils.errors import CompileError\n    from distutils.unixccompiler import UnixCCompiler\n\n    with tempfile.NamedTemporaryFile(\n        suffix='.c', delete=False, mode=\"wt\"\n    ) as f:\n        f.write(c_code)\n\n    tempdir = tempfile.mkdtemp()\n    try:\n        compiler = UnixCCompiler()\n        # https://github.com/giampaolo/psutil/pull/1568\n        if os.getenv('CC'):\n            compiler.set_executable('compiler_so', os.getenv('CC'))\n        with silenced_output():\n            compiler.compile([f.name], output_dir=tempdir)\n        compiler.compile([f.name], output_dir=tempdir)\n    except CompileError:\n        return False\n    else:\n        return True\n    finally:\n        os.remove(f.name)\n        shutil.rmtree(tempdir)\n\n\nif WINDOWS:\n\n    def get_winver():\n        maj, min = sys.getwindowsversion()[0:2]\n        return \"0x0{}\".format((maj * 100) + min)\n\n    if sys.getwindowsversion()[0] < 6:\n        msg = \"this Windows version is too old (< Windows Vista); \"\n        msg += \"psutil 3.4.2 is the latest version which supports Windows \"\n        msg += \"2000, XP and 2003 server\"\n        raise RuntimeError(msg)\n\n    macros.append((\"PSUTIL_WINDOWS\", 1))\n    macros.extend([\n        # be nice to mingw, see:\n        # http://www.mingw.org/wiki/Use_more_recent_defined_functions\n        ('_WIN32_WINNT', get_winver()),\n        ('_AVAIL_WINVER_', get_winver()),\n        ('_CRT_SECURE_NO_WARNINGS', None),\n        # see: https://github.com/giampaolo/psutil/issues/348\n        ('PSAPI_VERSION', 1),\n    ])\n\n    if Py_GIL_DISABLED:\n        macros.append(('Py_GIL_DISABLED', 1))\n\n    ext = Extension(\n        'psutil._psutil_windows',\n        sources=(\n            sources\n            + [\"psutil/_psutil_windows.c\"]\n            + glob.glob(\"psutil/arch/windows/*.c\")\n        ),\n        define_macros=macros,\n        libraries=[\n            \"advapi32\",\n            \"kernel32\",\n            \"netapi32\",\n            \"pdh\",\n            \"PowrProf\",\n            \"psapi\",\n            \"shell32\",\n            \"ws2_32\",\n        ],\n        # extra_compile_args=[\"/W 4\"],\n        # extra_link_args=[\"/DEBUG\"],\n        **py_limited_api,\n    )\n\nelif MACOS:\n    macros.extend([(\"PSUTIL_OSX\", 1), (\"PSUTIL_MACOS\", 1)])\n    ext = Extension(\n        'psutil._psutil_osx',\n        sources=(\n            sources\n            + [\"psutil/_psutil_osx.c\"]\n            + glob.glob(\"psutil/arch/osx/*.c\")\n        ),\n        define_macros=macros,\n        extra_link_args=[\n            '-framework',\n            'CoreFoundation',\n            '-framework',\n            'IOKit',\n        ],\n        **py_limited_api,\n    )\n\nelif FREEBSD:\n    macros.append((\"PSUTIL_FREEBSD\", 1))\n\n    ext = Extension(\n        'psutil._psutil_bsd',\n        sources=(\n            sources\n            + [\"psutil/_psutil_bsd.c\"]\n            + glob.glob(\"psutil/arch/bsd/*.c\")\n            + glob.glob(\"psutil/arch/freebsd/*.c\")\n        ),\n        define_macros=macros,\n        libraries=[\"devstat\"],\n        **py_limited_api,\n    )\n\nelif OPENBSD:\n    macros.append((\"PSUTIL_OPENBSD\", 1))\n\n    ext = Extension(\n        'psutil._psutil_bsd',\n        sources=(\n            sources\n            + [\"psutil/_psutil_bsd.c\"]\n            + glob.glob(\"psutil/arch/bsd/*.c\")\n            + glob.glob(\"psutil/arch/openbsd/*.c\")\n        ),\n        define_macros=macros,\n        libraries=[\"kvm\"],\n        **py_limited_api,\n    )\n\nelif NETBSD:\n    macros.append((\"PSUTIL_NETBSD\", 1))\n\n    ext = Extension(\n        'psutil._psutil_bsd',\n        sources=(\n            sources\n            + [\"psutil/_psutil_bsd.c\"]\n            + glob.glob(\"psutil/arch/bsd/*.c\")\n            + glob.glob(\"psutil/arch/netbsd/*.c\")\n        ),\n        define_macros=macros,\n        libraries=[\"kvm\", \"jemalloc\"],\n        **py_limited_api,\n    )\n\nelif LINUX:\n    # see: https://github.com/giampaolo/psutil/issues/659\n    if not unix_can_compile(\"#include <linux/ethtool.h>\"):\n        macros.append((\"PSUTIL_ETHTOOL_MISSING_TYPES\", 1))\n\n    macros.append((\"PSUTIL_LINUX\", 1))\n    ext = Extension(\n        'psutil._psutil_linux',\n        sources=(\n            sources\n            + [\"psutil/_psutil_linux.c\"]\n            + glob.glob(\"psutil/arch/linux/*.c\")\n        ),\n        define_macros=macros,\n        **py_limited_api,\n    )\n\nelif SUNOS:\n    macros.append((\"PSUTIL_SUNOS\", 1))\n\n    ext = Extension(\n        'psutil._psutil_sunos',\n        sources=(\n            sources\n            + [\"psutil/_psutil_sunos.c\"]\n            + glob.glob(\"psutil/arch/sunos/*.c\")\n        ),\n        define_macros=macros,\n        libraries=[\"kstat\", \"nsl\", \"socket\"],\n        **py_limited_api,\n    )\n\nelif AIX:\n    macros.append((\"PSUTIL_AIX\", 1))\n\n    ext = Extension(\n        'psutil._psutil_aix',\n        sources=(\n            sources\n            + [\"psutil/_psutil_aix.c\"]\n            + glob.glob(\"psutil/arch/aix/*.c\")\n        ),\n        libraries=[\"perfstat\"],\n        define_macros=macros,\n        **py_limited_api,\n    )\n\nelse:\n    sys.exit(\"platform {} is not supported\".format(sys.platform))\n\n\ndef main():\n    kwargs = dict(\n        name='psutil',\n        version=VERSION,\n        description=\"Cross-platform lib for process and system monitoring.\",\n        long_description=get_long_description(),\n        long_description_content_type='text/x-rst',\n        # fmt: off\n        keywords=[\n            'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty',\n            'ionice', 'uptime', 'taskmgr', 'process', 'df', 'iotop', 'iostat',\n            'ifconfig', 'taskset', 'who', 'pidof', 'pmap', 'smem', 'pstree',\n            'monitoring', 'ulimit', 'prlimit', 'smem', 'performance',\n            'metrics', 'agent', 'observability',\n        ],\n        # fmt: on\n        author='Giampaolo Rodola',\n        author_email='g.rodola@gmail.com',\n        url='https://github.com/giampaolo/psutil',\n        platforms='Platform Independent',\n        license='BSD-3-Clause',\n        packages=['psutil'],\n        ext_modules=[ext],\n        options=options,\n        classifiers=[\n            'Development Status :: 5 - Production/Stable',\n            'Environment :: Console',\n            'Intended Audience :: Developers',\n            'Intended Audience :: Information Technology',\n            'Intended Audience :: System Administrators',\n            'Operating System :: MacOS :: MacOS X',\n            'Operating System :: Microsoft :: Windows :: Windows 10',\n            'Operating System :: Microsoft :: Windows :: Windows 11',\n            'Operating System :: Microsoft :: Windows :: Windows 7',\n            'Operating System :: Microsoft :: Windows :: Windows 8',\n            'Operating System :: Microsoft :: Windows :: Windows 8.1',\n            'Operating System :: Microsoft :: Windows :: Windows Server 2003',\n            'Operating System :: Microsoft :: Windows :: Windows Server 2008',\n            'Operating System :: Microsoft :: Windows :: Windows Vista',\n            'Operating System :: Microsoft :: Windows',\n            'Operating System :: Microsoft',\n            'Operating System :: OS Independent',\n            'Operating System :: POSIX :: AIX',\n            'Operating System :: POSIX :: BSD :: FreeBSD',\n            'Operating System :: POSIX :: BSD :: NetBSD',\n            'Operating System :: POSIX :: BSD :: OpenBSD',\n            'Operating System :: POSIX :: BSD',\n            'Operating System :: POSIX :: Linux',\n            'Operating System :: POSIX :: SunOS/Solaris',\n            'Operating System :: POSIX',\n            'Programming Language :: C',\n            'Programming Language :: Python :: 3',\n            'Programming Language :: Python :: Implementation :: CPython',\n            'Programming Language :: Python :: Implementation :: PyPy',\n            'Programming Language :: Python',\n            'Topic :: Software Development :: Libraries :: Python Modules',\n            'Topic :: Software Development :: Libraries',\n            'Topic :: System :: Benchmark',\n            'Topic :: System :: Hardware',\n            'Topic :: System :: Monitoring',\n            'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog',\n            'Topic :: System :: Networking :: Monitoring',\n            'Topic :: System :: Networking',\n            'Topic :: System :: Operating System',\n            'Topic :: System :: Systems Administration',\n            'Topic :: Utilities',\n        ],\n    )\n    if setuptools is not None:\n        extras_require = {\n            \"test\": TEST_DEPS,\n            \"lint\": LINT_DEPS,\n            \"dev\": DEV_DEPS,\n        }\n        kwargs.update(\n            python_requires=\">=3.7\",\n            extras_require=extras_require,\n            zip_safe=False,\n        )\n    success = False\n    try:\n        setup(**kwargs)\n        success = True\n    finally:\n        cmd = sys.argv[1] if len(sys.argv) >= 2 else ''\n        if (\n            not success\n            and POSIX\n            and cmd.startswith(\n                (\"build\", \"install\", \"sdist\", \"bdist\", \"develop\")\n            )\n        ):\n            print_install_instructions()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tests/README.md",
    "content": "# Instructions for running tests\n\nInstall deps (e.g. pytest):\n\n.. code-block:: bash\n\n    make install-pydeps-test\n\nRun tests:\n\n.. code-block:: bash\n\n    make test\n\nRun tests in parallel (faster):\n\n.. code-block:: bash\n\n    make test-parallel\n\nRun a specific test:\n\n.. code-block:: bash\n\n    make test ARGS=tests.test_system.TestDiskAPIs\n\nTest C extension memory leaks:\n\n.. code-block:: bash\n\n    make test-memleaks\n\n# Running tests on Windows\n\nSame as above, just replace `make` with `make.bat`, e.g.:\n\n.. code-block:: bash\n\n    make.bat install-pydeps-test\n    make.bat test\n    set ARGS=-k tests.test_system.TestDiskAPIs && make.bat test\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Test utilities.\"\"\"\n\nimport atexit\nimport contextlib\nimport ctypes\nimport enum\nimport errno\nimport functools\nimport importlib\nimport ipaddress\nimport os\nimport pathlib\nimport platform\nimport random\nimport re\nimport select\nimport shlex\nimport shutil\nimport signal\nimport socket\nimport stat\nimport subprocess\nimport sys\nimport tempfile\nimport textwrap\nimport threading\nimport time\nimport traceback\nimport types\nimport typing\nimport unittest\nimport warnings\nfrom socket import AF_INET\nfrom socket import AF_INET6\nfrom socket import SOCK_STREAM\n\ntry:\n    import pytest\nexcept ImportError:\n    pytest = None\n\nimport psutil\nimport psutil._ntuples as ntuples\nfrom psutil import AIX\nfrom psutil import BSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import SUNOS\nfrom psutil import WINDOWS\nfrom psutil import _enums\nfrom psutil._common import debug\nfrom psutil._common import supports_ipv6\n\nif POSIX:\n    from psutil._psposix import wait_pid\n\n\n# fmt: off\n__all__ = [\n    # constants\n    'DEVNULL', 'GLOBAL_TIMEOUT', 'TOLERANCE_SYS_MEM', 'NO_RETRIES',\n    'PYPY', 'PYTHON_EXE', 'PYTHON_EXE_ENV', 'ROOT_DIR',\n    'TESTFN_PREFIX', 'UNICODE_SUFFIX', 'INVALID_UNICODE_SUFFIX',\n    'CI_TESTING', 'VALID_PROC_STATUSES', 'TOLERANCE_DISK_USAGE', 'IS_64BIT',\n    \"HAS_PROC_CPU_AFFINITY\", \"HAS_CPU_FREQ\", \"HAS_PROC_ENVIRON\",\n    \"HAS_PROC_IO_COUNTERS\", \"HAS_PROC_IONICE\",\n    \"HAS_PROC_MEMORY_FOOTPRINT\", \"HAS_PROC_MEMORY_MAPS\",\n    \"HAS_PROC_CPU_NUM\", \"HAS_PROC_RLIMIT\", \"HAS_SENSORS_BATTERY\",\n    \"HAS_BATTERY\", \"HAS_SENSORS_FANS\", \"HAS_SENSORS_TEMPERATURES\",\n    \"HAS_NET_CONNECTIONS_UNIX\", \"MACOS_11PLUS\", \"MACOS_12PLUS\", \"COVERAGE\",\n    \"AARCH64\", \"PYTEST_PARALLEL\",\n    # subprocesses\n    'pyrun', 'terminate', 'reap_children', 'spawn_subproc', 'spawn_zombie',\n    'spawn_children_pair',\n    # threads\n    'ThreadTask',\n    # test utils\n    'unittest', 'skip_on_access_denied', 'skip_on_not_implemented',\n    'retry_on_failure', 'PsutilTestCase', 'process_namespace',\n    'system_namespace', 'is_win_secure_system_proc',\n    # type hints\n    'check_ntuple_type_hints', 'check_fun_type_hints',\n    # fs utils\n    'chdir', 'safe_rmpath', 'create_py_exe', 'create_c_exe', 'get_testfn',\n    # os\n    'get_winver', 'kernel_version',\n    # sync primitives\n    'call_until', 'wait_for_pid', 'wait_for_file',\n    # network\n    'check_net_address', 'filter_proc_net_connections',\n    'get_free_port', 'bind_socket', 'bind_unix_socket', 'tcp_socketpair',\n    'unix_socketpair', 'create_sockets',\n    # compat\n    'reload_module', 'import_module_by_path',\n    # others\n    'warn', 'copyload_shared_lib', 'is_namedtuple'\n]\n# fmt: on\n\n\n# ===================================================================\n# --- constants\n# ===================================================================\n\n# --- platforms\n\nPYPY = '__pypy__' in sys.builtin_module_names\n# whether we're running this test suite on a Continuous Integration service\nGITHUB_ACTIONS = 'GITHUB_ACTIONS' in os.environ or 'CIBUILDWHEEL' in os.environ\nCI_TESTING = GITHUB_ACTIONS\nCOVERAGE = 'COVERAGE_RUN' in os.environ\nPYTEST_PARALLEL = \"PYTEST_XDIST_WORKER\" in os.environ  # `make test-parallel`\n# are we a 64 bit process?\nIS_64BIT = sys.maxsize > 2**32\n# apparently they're the same\nAARCH64 = platform.machine().lower() in {\"aarch64\", \"arm64\"}\nRISCV64 = platform.machine() == \"riscv64\"\n\n\n@functools.lru_cache\ndef macos_version():\n    version_str = platform.mac_ver()[0]\n    version = tuple(map(int, version_str.split(\".\")[:2]))\n    if version == (10, 16):\n        # When built against an older macOS SDK, Python will report\n        # macOS 10.16 instead of the real version.\n        version_str = subprocess.check_output(\n            [\n                sys.executable,\n                \"-sS\",\n                \"-c\",\n                \"import platform; print(platform.mac_ver()[0])\",\n            ],\n            env={\"SYSTEM_VERSION_COMPAT\": \"0\"},\n            universal_newlines=True,\n        )\n        version = tuple(map(int, version_str.split(\".\")[:2]))\n    return version\n\n\nif MACOS:\n    MACOS_11PLUS = macos_version() > (10, 15)\n    MACOS_12PLUS = macos_version() >= (12, 0)\nelse:\n    MACOS_11PLUS = False\n    MACOS_12PLUS = False\n\n\n# --- configurable defaults\n\n# how many times retry_on_failure() decorator will retry\nNO_RETRIES = 10\n# bytes tolerance for system-wide related tests\nTOLERANCE_SYS_MEM = 5 * 1024 * 1024  # 5MB\nTOLERANCE_DISK_USAGE = 10 * 1024 * 1024  # 10MB\n# the timeout used in functions which have to wait\nGLOBAL_TIMEOUT = 5\n# be more tolerant if we're on CI in order to avoid false positives\nif CI_TESTING:\n    NO_RETRIES *= 3\n    GLOBAL_TIMEOUT *= 3\n    TOLERANCE_SYS_MEM *= 4\n    TOLERANCE_DISK_USAGE *= 3\n\n# --- file names\n\n# Disambiguate TESTFN with PID for parallel testing.\nTESTFN_PREFIX = f\"@psutil-{os.getpid()}-\"\nUNICODE_SUFFIX = \"-ƒőő\"\n# An invalid unicode string.\nINVALID_UNICODE_SUFFIX = b\"f\\xc0\\x80\".decode('utf8', 'surrogateescape')\nASCII_FS = sys.getfilesystemencoding().lower() in {\"ascii\", \"us-ascii\"}\n\n# --- paths\n\nROOT_DIR = os.environ.get(\"PSUTIL_ROOT\") or str(\n    pathlib.Path(__file__).resolve().parent.parent\n)\n\n# --- support\n\nHAS_HEAP_INFO = hasattr(psutil, \"heap_info\")\nHAS_NET_CONNECTIONS_UNIX = POSIX and not SUNOS\nHAS_NET_IO_COUNTERS = hasattr(psutil, \"net_io_counters\")\nHAS_SENSORS_BATTERY = hasattr(psutil, \"sensors_battery\")\nHAS_SENSORS_FANS = hasattr(psutil, \"sensors_fans\")\nHAS_SENSORS_TEMPERATURES = hasattr(psutil, \"sensors_temperatures\")\n\nHAS_PROC_CPU_AFFINITY = hasattr(psutil.Process, \"cpu_affinity\")\nHAS_PROC_CPU_NUM = hasattr(psutil.Process, \"cpu_num\")\nHAS_PROC_ENVIRON = hasattr(psutil.Process, \"environ\")\nHAS_PROC_IO_COUNTERS = hasattr(psutil.Process, \"io_counters\")\nHAS_PROC_IONICE = hasattr(psutil.Process, \"ionice\")\nHAS_PROC_MEMORY_FOOTPRINT = hasattr(psutil.Process, \"memory_footprint\")\nHAS_PROC_MEMORY_MAPS = hasattr(psutil.Process, \"memory_maps\")\nHAS_PROC_RLIMIT = hasattr(psutil.Process, \"rlimit\")\nHAS_PROC_THREADS = hasattr(psutil.Process, \"threads\")\n\nSKIP_SYSCONS = (MACOS or AIX) and os.getuid() != 0\n\ntry:\n    HAS_BATTERY = HAS_SENSORS_BATTERY and bool(psutil.sensors_battery())\nexcept Exception:  # noqa: BLE001\n    atexit.register(functools.partial(print, traceback.format_exc()))\n    HAS_BATTERY = False\ntry:\n    HAS_CPU_FREQ = hasattr(psutil, \"cpu_freq\") and bool(psutil.cpu_freq())\nexcept Exception:  # noqa: BLE001\n    atexit.register(functools.partial(print, traceback.format_exc()))\n    HAS_CPU_FREQ = False\n\n\n# --- misc\n\n\ndef _get_py_exe():\n    def attempt(exe):\n        try:\n            subprocess.check_call(\n                [exe, \"-V\"], stdout=subprocess.PIPE, stderr=subprocess.PIPE\n            )\n        except subprocess.CalledProcessError:\n            return None\n        else:\n            return exe\n\n    env = os.environ.copy()\n\n    # On Windows, starting with python 3.7, virtual environments use a\n    # venv launcher startup process. This does not play well when\n    # counting spawned processes, or when relying on the PID of the\n    # spawned process to do some checks, e.g. connections check per PID.\n    # Let's use the base python in this case.\n    base = getattr(sys, \"_base_executable\", None)\n    if WINDOWS and sys.version_info >= (3, 7) and base is not None:\n        # We need to set __PYVENV_LAUNCHER__ to sys.executable for the\n        # base python executable to know about the environment.\n        env[\"__PYVENV_LAUNCHER__\"] = sys.executable\n        return base, env\n    elif GITHUB_ACTIONS:\n        return sys.executable, env\n    elif MACOS:\n        exe = (\n            attempt(sys.executable)\n            or attempt(os.path.realpath(sys.executable))\n            or attempt(\n                shutil.which(\"python{}.{}\".format(*sys.version_info[:2]))\n            )\n            or attempt(psutil.Process().exe())\n        )\n        if not exe:\n            raise ValueError(\"can't find python exe real abspath\")\n        return exe, env\n    else:\n        exe = os.path.realpath(sys.executable)\n        assert os.path.exists(exe), exe\n        return exe, env\n\n\nPYTHON_EXE, PYTHON_EXE_ENV = _get_py_exe()\nDEVNULL = open(os.devnull, 'r+')  # noqa: SIM115\natexit.register(DEVNULL.close)\n\nVALID_PROC_STATUSES = [\n    getattr(psutil, x) for x in dir(psutil) if x.startswith('STATUS_')\n]\nAF_UNIX = getattr(socket, \"AF_UNIX\", object())\n\n_subprocesses_started = set()\n_pids_started = set()\n\n\n# ===================================================================\n# --- threads\n# ===================================================================\n\n\nclass ThreadTask(threading.Thread):\n    \"\"\"A thread task which does nothing expect staying alive.\"\"\"\n\n    def __init__(self):\n        super().__init__()\n        self._running = False\n        self._interval = 0.001\n        self._flag = threading.Event()\n\n    def __repr__(self):\n        name = self.__class__.__name__\n        return f\"<{name} running={self._running} at {id(self):#x}>\"\n\n    def __enter__(self):\n        self.start()\n        return self\n\n    def __exit__(self, *args, **kwargs):\n        self.stop()\n\n    def start(self):\n        \"\"\"Start thread and keep it running until an explicit\n        stop() request. Polls for shutdown every 'timeout' seconds.\n        \"\"\"\n        if self._running:\n            raise ValueError(\"already started\")\n        threading.Thread.start(self)\n        self._flag.wait()\n\n    def run(self):\n        self._running = True\n        self._flag.set()\n        while self._running:\n            time.sleep(self._interval)\n\n    def stop(self):\n        \"\"\"Stop thread execution and and waits until it is stopped.\"\"\"\n        if not self._running:\n            raise ValueError(\"already stopped\")\n        self._running = False\n        self.join()\n\n\n# ===================================================================\n# --- subprocesses\n# ===================================================================\n\n\ndef _reap_children_on_err(fun):\n    @functools.wraps(fun)\n    def wrapper(*args, **kwargs):\n        try:\n            return fun(*args, **kwargs)\n        except Exception:\n            reap_children()\n            raise\n\n    return wrapper\n\n\n@_reap_children_on_err\ndef spawn_subproc(cmd=None, **kwds):\n    \"\"\"Create a python subprocess which does nothing for some secs and\n    return it as a subprocess.Popen instance.\n    If \"cmd\" is specified that is used instead of python.\n    By default stdin and stdout are redirected to /dev/null.\n    It also attempts to make sure the process is in a reasonably\n    initialized state.\n    The process is registered for cleanup on reap_children().\n    \"\"\"\n    kwds.setdefault(\"stdin\", DEVNULL)\n    kwds.setdefault(\"stdout\", DEVNULL)\n    kwds.setdefault(\"cwd\", os.getcwd())\n    kwds.setdefault(\"env\", PYTHON_EXE_ENV)\n    if WINDOWS:\n        # Prevents the subprocess to open error dialogs. This will also\n        # cause stderr to be suppressed, which is suboptimal in order\n        # to debug broken tests.\n        # CREATE_NO_WINDOW = 0x8000000\n        # kwds.setdefault(\"creationflags\", CREATE_NO_WINDOW)\n\n        # New: hopefully this should achieve the same and not suppress\n        # stderr.\n        startupinfo = subprocess.STARTUPINFO()\n        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW\n        startupinfo.wShowWindow = subprocess.SW_HIDE\n        kwds.setdefault(\"startupinfo\", startupinfo)\n\n    if cmd is None:\n        testfn = get_testfn(dir=os.getcwd())\n        try:\n            safe_rmpath(testfn)\n            pyline = (\n                \"import time;\"\n                f\"open(r'{testfn}', 'w').close();\"\n                \"[time.sleep(0.1) for x in range(100)];\"  # 10 secs\n            )\n            cmd = [PYTHON_EXE, \"-c\", pyline]\n            sproc = subprocess.Popen(cmd, **kwds)\n            _subprocesses_started.add(sproc)\n            wait_for_file(testfn, delete=True, empty=True)\n        finally:\n            safe_rmpath(testfn)\n    else:\n        sproc = subprocess.Popen(cmd, **kwds)\n        _subprocesses_started.add(sproc)\n        wait_for_pid(sproc.pid)\n    return sproc\n\n\n@_reap_children_on_err\ndef spawn_children_pair():\n    \"\"\"Create a subprocess which creates another one as in:\n    A (us) -> B (child) -> C (grandchild).\n    Return a (child, grandchild) tuple.\n    The 2 processes are fully initialized and will live for 60 secs\n    and are registered for cleanup on reap_children().\n    \"\"\"\n    tfile = None\n    testfn = get_testfn(dir=os.getcwd())\n    try:\n        s = textwrap.dedent(f\"\"\"\\\n            import subprocess, os, sys, time\n            s = \"import os, time;\"\n            s += \"f = open('{os.path.basename(testfn)}', 'w');\"\n            s += \"f.write(str(os.getpid()));\"\n            s += \"f.close();\"\n            s += \"[time.sleep(0.1) for x in range(100 * 6)];\"\n            p = subprocess.Popen([r'{PYTHON_EXE}', '-c', s])\n            p.wait()\n            \"\"\")\n        # On Windows if we create a subprocess with CREATE_NO_WINDOW flag\n        # set (which is the default) a \"conhost.exe\" extra process will be\n        # spawned as a child. We don't want that.\n        if WINDOWS:\n            subp, tfile = pyrun(s, creationflags=0)\n        else:\n            subp, tfile = pyrun(s)\n        child = psutil.Process(subp.pid)\n        grandchild_pid = int(wait_for_file(testfn, delete=True, empty=False))\n        _pids_started.add(grandchild_pid)\n        grandchild = psutil.Process(grandchild_pid)\n        return (child, grandchild)\n    finally:\n        safe_rmpath(testfn)\n        if tfile is not None:\n            safe_rmpath(tfile)\n\n\ndef spawn_zombie():\n    \"\"\"Create a zombie process and return a (parent, zombie) process tuple.\n    In order to kill the zombie parent must be terminate()d first, then\n    zombie must be wait()ed on.\n    \"\"\"\n    assert psutil.POSIX\n    unix_file = get_testfn()\n    src = textwrap.dedent(f\"\"\"\\\n        import os, sys, time, socket, contextlib\n        child_pid = os.fork()\n        if child_pid > 0:\n            time.sleep(3000)\n        else:\n            # this is the zombie process\n            with socket.socket(socket.AF_UNIX) as s:\n                s.connect('{unix_file}')\n                pid = bytes(str(os.getpid()), 'ascii')\n                s.sendall(pid)\n        \"\"\")\n    tfile = None\n    sock = bind_unix_socket(unix_file)\n    try:\n        sock.settimeout(GLOBAL_TIMEOUT)\n        parent, tfile = pyrun(src)\n        conn, _ = sock.accept()\n        try:\n            select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT)\n            zpid = int(conn.recv(1024))\n            _pids_started.add(zpid)\n            zombie = psutil.Process(zpid)\n            call_until(lambda: zombie.status() == psutil.STATUS_ZOMBIE)\n            return (parent, zombie)\n        finally:\n            conn.close()\n    finally:\n        sock.close()\n        safe_rmpath(unix_file)\n        if tfile is not None:\n            safe_rmpath(tfile)\n\n\n@_reap_children_on_err\ndef pyrun(src, **kwds):\n    \"\"\"Run python 'src' code string in a separate interpreter.\n    Returns a subprocess.Popen instance and the test file where the source\n    code was written.\n    \"\"\"\n    kwds.setdefault(\"stdout\", None)\n    kwds.setdefault(\"stderr\", None)\n    srcfile = get_testfn()\n    try:\n        with open(srcfile, \"w\") as f:\n            f.write(src)\n        subp = spawn_subproc([PYTHON_EXE, f.name], **kwds)\n        wait_for_pid(subp.pid)\n        return (subp, srcfile)\n    except Exception:\n        safe_rmpath(srcfile)\n        raise\n\n\n@_reap_children_on_err\ndef sh(cmd, **kwds):\n    \"\"\"Run cmd in a subprocess and return its output.\n    raises RuntimeError on error.\n    \"\"\"\n    # Prevents subprocess to open error dialogs in case of error.\n    flags = 0x8000000 if WINDOWS else 0\n    kwds.setdefault(\"stdout\", subprocess.PIPE)\n    kwds.setdefault(\"stderr\", subprocess.PIPE)\n    kwds.setdefault(\"universal_newlines\", True)\n    kwds.setdefault(\"creationflags\", flags)\n    if isinstance(cmd, str):\n        cmd = shlex.split(cmd)\n    p = subprocess.Popen(cmd, **kwds)\n    _subprocesses_started.add(p)\n    stdout, stderr = p.communicate(timeout=GLOBAL_TIMEOUT)\n    if p.returncode != 0:\n        raise RuntimeError(stdout + stderr)\n    if stderr:\n        warn(stderr)\n    if stdout.endswith('\\n'):\n        stdout = stdout[:-1]\n    return stdout\n\n\ndef terminate(proc_or_pid, sig=signal.SIGTERM, wait_timeout=GLOBAL_TIMEOUT):\n    \"\"\"Terminate a process and wait() for it.\n    Process can be a PID or an instance of psutil.Process(),\n    subprocess.Popen() or psutil.Popen().\n    If it's a subprocess.Popen() or psutil.Popen() instance also closes\n    its stdin / stdout / stderr fds.\n    PID is wait()ed even if the process is already gone (kills zombies).\n    Does nothing if the process does not exist.\n    Return process exit status.\n    \"\"\"\n\n    def wait(proc, timeout):\n        proc.wait(timeout)\n        if WINDOWS and isinstance(proc, subprocess.Popen):\n            # Otherwise PID may still hang around.\n            try:\n                return psutil.Process(proc.pid).wait(timeout)\n            except psutil.NoSuchProcess:\n                pass\n\n    def sendsig(proc, sig):\n        # XXX: otherwise the build hangs for some reason.\n        if MACOS and GITHUB_ACTIONS:\n            sig = signal.SIGKILL\n        # If the process received SIGSTOP, SIGCONT is necessary first,\n        # otherwise SIGTERM won't work.\n        if POSIX and sig != signal.SIGKILL:\n            proc.send_signal(signal.SIGCONT)\n        proc.send_signal(sig)\n\n    def term_subprocess_proc(proc, timeout):\n        try:\n            sendsig(proc, sig)\n        except ProcessLookupError:\n            pass\n        except OSError as err:\n            if WINDOWS and err.winerror == 6:  # \"invalid handle\"\n                pass\n            raise\n        return wait(proc, timeout)\n\n    def term_psutil_proc(proc, timeout):\n        try:\n            sendsig(proc, sig)\n        except psutil.NoSuchProcess:\n            pass\n        return wait(proc, timeout)\n\n    def term_pid(pid, timeout):\n        try:\n            proc = psutil.Process(pid)\n        except psutil.NoSuchProcess:\n            # Needed to kill zombies.\n            if POSIX:\n                return wait_pid(pid, timeout)\n        else:\n            return term_psutil_proc(proc, timeout)\n\n    def flush_popen(proc):\n        if proc.stdout:\n            proc.stdout.close()\n        if proc.stderr:\n            proc.stderr.close()\n        # Flushing a BufferedWriter may raise an error.\n        if proc.stdin:\n            proc.stdin.close()\n\n    p = proc_or_pid\n    try:\n        if isinstance(p, int):\n            return term_pid(p, wait_timeout)\n        elif isinstance(p, (psutil.Process, psutil.Popen)):\n            return term_psutil_proc(p, wait_timeout)\n        elif isinstance(p, subprocess.Popen):\n            return term_subprocess_proc(p, wait_timeout)\n        else:\n            raise TypeError(f\"wrong type {p!r}\")\n    finally:\n        if isinstance(p, (subprocess.Popen, psutil.Popen)):\n            flush_popen(p)\n        pid = p if isinstance(p, int) else p.pid\n        assert not psutil.pid_exists(pid), pid\n\n\ndef reap_children(recursive=False):\n    \"\"\"Terminate and wait() any subprocess started by this test suite\n    and any children currently running, ensuring that no processes stick\n    around to hog resources.\n    If recursive is True it also tries to terminate and wait()\n    all grandchildren started by this process.\n    \"\"\"\n    # Get the children here before terminating them, as in case of\n    # recursive=True we don't want to lose the intermediate reference\n    # pointing to the grandchildren.\n    children = psutil.Process().children(recursive=recursive)\n\n    # Terminate subprocess.Popen.\n    while _subprocesses_started:\n        subp = _subprocesses_started.pop()\n        terminate(subp)\n\n    # Collect started pids.\n    while _pids_started:\n        pid = _pids_started.pop()\n        terminate(pid)\n\n    # Terminate children.\n    if children:\n        for p in children:\n            terminate(p, wait_timeout=None)\n        _, alive = psutil.wait_procs(children, timeout=GLOBAL_TIMEOUT)\n        for p in alive:\n            warn(f\"couldn't terminate process {p!r}; attempting kill()\")\n            terminate(p, sig=signal.SIGKILL)\n\n\n# ===================================================================\n# --- OS\n# ===================================================================\n\n\ndef kernel_version():\n    \"\"\"Return a tuple such as (2, 6, 36).\"\"\"\n    if not POSIX:\n        raise NotImplementedError(\"not POSIX\")\n    s = \"\"\n    uname = os.uname()[2]\n    for c in uname:\n        if c.isdigit() or c == '.':\n            s += c\n        else:\n            break\n    if not s:\n        raise ValueError(f\"can't parse {uname!r}\")\n    minor = 0\n    micro = 0\n    nums = s.split('.')\n    major = int(nums[0])\n    if len(nums) >= 2:\n        minor = int(nums[1])\n    if len(nums) >= 3:\n        micro = int(nums[2])\n    return (major, minor, micro)\n\n\ndef get_winver():\n    if not WINDOWS:\n        raise NotImplementedError(\"not WINDOWS\")\n    wv = sys.getwindowsversion()\n    sp = wv.service_pack_major or 0\n    return (wv[0], wv[1], sp)\n\n\n# ===================================================================\n# --- sync primitives\n# ===================================================================\n\n\nclass retry:\n    \"\"\"A retry decorator.\"\"\"\n\n    def __init__(\n        self,\n        exception=Exception,\n        timeout=None,\n        retries=None,\n        interval=0.001,\n        logfun=None,\n    ):\n        if timeout and retries:\n            raise ValueError(\"timeout and retries args are mutually exclusive\")\n        self.exception = exception\n        self.timeout = timeout\n        self.retries = retries\n        self.interval = interval\n        self.logfun = logfun\n\n    def __iter__(self):\n        if self.timeout:\n            stop_at = time.time() + self.timeout\n            while time.time() < stop_at:\n                yield\n        elif self.retries:\n            for _ in range(self.retries):\n                yield\n        else:\n            while True:\n                yield\n\n    def sleep(self):\n        if self.interval is not None:\n            time.sleep(self.interval)\n\n    def __call__(self, fun):\n        @functools.wraps(fun)\n        def wrapper(*args, **kwargs):\n            exc = None\n            for _ in self:\n                try:\n                    return fun(*args, **kwargs)\n                except self.exception as _:\n                    exc = _\n                    if self.logfun is not None:\n                        self.logfun(exc)\n                    self.sleep()\n                    continue\n\n            raise exc\n\n        # This way the user of the decorated function can change config\n        # parameters.\n        wrapper.decorator = self\n        return wrapper\n\n\n@retry(\n    exception=psutil.NoSuchProcess,\n    logfun=None,\n    timeout=GLOBAL_TIMEOUT,\n    interval=0.001,\n)\ndef wait_for_pid(pid):\n    \"\"\"Wait for pid to show up in the process list then return.\n    Used in the test suite to give time the sub process to initialize.\n    \"\"\"\n    if pid not in psutil.pids():\n        raise psutil.NoSuchProcess(pid)\n    psutil.Process(pid)\n\n\n@retry(\n    exception=(FileNotFoundError, AssertionError),\n    logfun=None,\n    timeout=GLOBAL_TIMEOUT,\n    interval=0.001,\n)\ndef wait_for_file(fname, delete=True, empty=False):\n    \"\"\"Wait for a file to be written on disk with some content.\"\"\"\n    with open(fname, \"rb\") as f:\n        data = f.read()\n    if not empty:\n        assert data\n    if delete:\n        safe_rmpath(fname)\n    return data\n\n\n@retry(\n    exception=(AssertionError, pytest.fail.Exception),\n    logfun=None,\n    timeout=GLOBAL_TIMEOUT,\n    interval=0.001,\n)\ndef call_until(fun):\n    \"\"\"Keep calling function until it evaluates to True.\"\"\"\n    ret = fun()\n    assert ret\n    return ret\n\n\n# ===================================================================\n# --- fs\n# ===================================================================\n\n\ndef safe_rmpath(path):\n    \"\"\"Convenience function for removing temporary test files or dirs.\"\"\"\n\n    def retry_fun(fun):\n        # On Windows it could happen that the file or directory has\n        # open handles or references preventing the delete operation\n        # to succeed immediately, so we retry for a while. See:\n        # https://bugs.python.org/issue33240\n        stop_at = time.time() + GLOBAL_TIMEOUT\n        while time.time() < stop_at:\n            try:\n                return fun()\n            except FileNotFoundError:\n                pass\n            except OSError as _:\n                err = _\n                warn(f\"ignoring {err}\")\n            time.sleep(0.01)\n        raise err\n\n    try:\n        st = os.stat(path)\n        if stat.S_ISDIR(st.st_mode):\n            fun = functools.partial(shutil.rmtree, path)\n        else:\n            fun = functools.partial(os.remove, path)\n        if POSIX:\n            fun()\n        else:\n            retry_fun(fun)\n    except FileNotFoundError:\n        pass\n\n\ndef safe_mkdir(dir):\n    \"\"\"Convenience function for creating a directory.\"\"\"\n    try:\n        os.mkdir(dir)\n    except FileExistsError:\n        pass\n\n\n@contextlib.contextmanager\ndef chdir(dirname):\n    \"\"\"Context manager which temporarily changes the current directory.\"\"\"\n    curdir = os.getcwd()\n    try:\n        os.chdir(dirname)\n        yield\n    finally:\n        os.chdir(curdir)\n\n\ndef create_py_exe(path):\n    \"\"\"Create a Python executable file in the given location.\"\"\"\n    assert not os.path.exists(path), path\n    atexit.register(safe_rmpath, path)\n    shutil.copyfile(PYTHON_EXE, path)\n    if POSIX:\n        st = os.stat(path)\n        os.chmod(path, st.st_mode | stat.S_IEXEC)\n    return path\n\n\ndef create_c_exe(path, c_code=None):\n    \"\"\"Create a compiled C executable in the given location.\"\"\"\n    assert not os.path.exists(path), path\n    if not shutil.which(\"gcc\"):\n        return pytest.skip(\"gcc is not installed\")\n    if c_code is None:\n        c_code = textwrap.dedent(\"\"\"\n            #include <unistd.h>\n            int main() {\n                pause();\n                return 1;\n            }\n            \"\"\")\n    else:\n        assert isinstance(c_code, str), c_code\n\n    atexit.register(safe_rmpath, path)\n    with open(get_testfn(suffix='.c'), \"w\") as f:\n        f.write(c_code)\n    try:\n        subprocess.check_call([\"gcc\", f.name, \"-o\", path])\n    finally:\n        safe_rmpath(f.name)\n    return path\n\n\ndef get_testfn(suffix=\"\", dir=None):\n    \"\"\"Return an absolute pathname of a file or dir that did not\n    exist at the time this call is made. Also schedule it for safe\n    deletion at interpreter exit. It's technically racy but probably\n    not really due to the time variant.\n    \"\"\"\n    name = tempfile.mktemp(prefix=TESTFN_PREFIX, suffix=suffix, dir=dir)\n    path = os.path.realpath(name)  # needed for OSX\n    atexit.register(safe_rmpath, path)\n    return path\n\n\n# ===================================================================\n# --- testing\n# ===================================================================\n\n\nclass PsutilTestCase(unittest.TestCase):\n    \"\"\"Test class providing auto-cleanup wrappers on top of process\n    test utilities. All test classes should derive from this one, even\n    if we use pytest.\n    \"\"\"\n\n    # Print a full path representation of the single unit test being\n    # run, similar to pytest output. Used only when running tests with\n    # the unittest runner.\n    def __str__(self):\n        fqmod = self.__class__.__module__\n        if not fqmod.startswith('psutil.'):\n            fqmod = 'tests.' + fqmod\n        return \"{}.{}.{}\".format(\n            fqmod,\n            self.__class__.__name__,\n            self._testMethodName,\n        )\n\n    def get_testfn(self, suffix=\"\", dir=None):\n        fname = get_testfn(suffix=suffix, dir=dir)\n        self.addCleanup(safe_rmpath, fname)\n        return fname\n\n    def spawn_subproc(self, *args, **kwds):\n        sproc = spawn_subproc(*args, **kwds)\n        self.addCleanup(terminate, sproc)\n        return sproc\n\n    def spawn_psproc(self, *args, **kwargs):\n        sproc = self.spawn_subproc(*args, **kwargs)\n        try:\n            return psutil.Process(sproc.pid)\n        except psutil.NoSuchProcess:\n            self.assert_pid_gone(sproc.pid)\n            raise\n\n    def spawn_children_pair(self):\n        child1, child2 = spawn_children_pair()\n        self.addCleanup(terminate, child2)\n        self.addCleanup(terminate, child1)  # executed first\n        return (child1, child2)\n\n    def spawn_zombie(self):\n        parent, zombie = spawn_zombie()\n        self.addCleanup(terminate, zombie)\n        self.addCleanup(terminate, parent)  # executed first\n        return (parent, zombie)\n\n    def pyrun(self, *args, **kwds):\n        sproc, srcfile = pyrun(*args, **kwds)\n        self.addCleanup(safe_rmpath, srcfile)\n        self.addCleanup(terminate, sproc)  # executed first\n        return sproc\n\n    def _check_proc_exc(self, proc, exc):\n        assert isinstance(exc, psutil.Error)\n        assert exc.pid == proc.pid\n        assert exc.name == proc._name\n        if exc.name:\n            assert exc.name\n        if isinstance(exc, psutil.ZombieProcess):\n            assert exc.ppid == proc._ppid\n            if exc.ppid is not None:\n                assert exc.ppid >= 0\n        str(exc)\n        repr(exc)\n\n    def assert_pid_gone(self, pid):\n        try:\n            proc = psutil.Process(pid)\n        except psutil.ZombieProcess:\n            raise AssertionError(\"wasn't supposed to raise ZombieProcess\")\n        except psutil.NoSuchProcess as exc:\n            assert exc.pid == pid  # noqa: PT017\n            assert exc.name is None  # noqa: PT017\n        else:\n            raise AssertionError(f\"did not raise NoSuchProcess ({proc})\")\n\n        assert not psutil.pid_exists(pid), pid\n        assert pid not in psutil.pids()\n        assert pid not in [x.pid for x in psutil.process_iter()]\n\n    def assert_proc_gone(self, proc):\n        self.assert_pid_gone(proc.pid)\n        ns = process_namespace(proc)\n        for fun, name in ns.iter(ns.all, clear_cache=True):\n            with self.subTest(proc=str(proc), name=name):\n                try:\n                    ret = fun()\n                except psutil.ZombieProcess:\n                    raise\n                except psutil.NoSuchProcess as exc:\n                    self._check_proc_exc(proc, exc)\n                else:\n                    msg = (\n                        f\"Process.{name}() didn't raise NSP and returned\"\n                        f\" {ret!r}\"\n                    )\n                    raise AssertionError(msg)\n        proc.wait(timeout=0)  # assert not raise TimeoutExpired\n\n    def assert_proc_zombie(self, proc):\n        def assert_in_pids(proc):\n            if MACOS:\n                # Even ps does not show zombie PIDs for some reason. Weird...\n                return\n            assert proc.pid in psutil.pids()\n            assert proc.pid in [x.pid for x in psutil.process_iter()]\n            psutil._pmap = {}\n            assert proc.pid in [x.pid for x in psutil.process_iter()]\n\n        # A zombie process should always be instantiable.\n        clone = psutil.Process(proc.pid)\n        # Cloned zombie on Open/NetBSD/illumos/Solaris has null creation\n        # time, see:\n        # https://github.com/giampaolo/psutil/issues/2287\n        # https://github.com/giampaolo/psutil/issues/2593\n        assert proc == clone\n        if not (OPENBSD or NETBSD or SUNOS):\n            assert hash(proc) == hash(clone)\n        # Its status always be querable.\n        assert proc.status() == psutil.STATUS_ZOMBIE\n        # It should be considered 'running'.\n        assert proc.is_running()\n        assert psutil.pid_exists(proc.pid)\n        # as_dict() shouldn't crash.\n        proc.as_dict()\n        # It should show up in pids() and process_iter().\n        assert_in_pids(proc)\n        # Call all methods.\n        ns = process_namespace(proc)\n        for fun, name in ns.iter(ns.all, clear_cache=True):\n            with self.subTest(proc=str(proc), name=name):\n                try:\n                    fun()\n                except (psutil.ZombieProcess, psutil.AccessDenied) as exc:\n                    self._check_proc_exc(proc, exc)\n        if LINUX:\n            # https://github.com/giampaolo/psutil/pull/2288\n            with pytest.raises(psutil.ZombieProcess) as cm:\n                proc.cmdline()\n            self._check_proc_exc(proc, cm.value)\n            with pytest.raises(psutil.ZombieProcess) as cm:\n                proc.exe()\n            self._check_proc_exc(proc, cm.value)\n            with pytest.raises(psutil.ZombieProcess) as cm:\n                proc.memory_maps()\n            self._check_proc_exc(proc, cm.value)\n        # Zombie cannot be signaled or terminated.\n        proc.suspend()\n        proc.resume()\n        proc.terminate()\n        proc.kill()\n        assert proc.is_running()\n        assert psutil.pid_exists(proc.pid)\n        assert_in_pids(proc)\n\n        # Its parent should 'see' it (edit: not true on BSD and MACOS).\n        # descendants = [x.pid for x in psutil.Process().children(\n        #                recursive=True)]\n        # assert proc.pid in descendants\n\n        # __eq__ can't be relied upon because creation time may not be\n        # querable.\n        # assert proc ==  psutil.Process(proc.pid)\n\n        # XXX should we also assume ppid() to be usable? Note: this\n        # would be an important use case as the only way to get\n        # rid of a zombie is to kill its parent.\n        # assert proc == ppid(), os.getpid()\n\n    def check_proc_memory(self, nt):\n        # Check the ntuple returned by Process.memory_*() methods.\n        check_ntuple_type_hints(nt)\n        for value in nt:\n            assert isinstance(value, int)\n            assert value >= 0\n        if hasattr(nt, \"peak_rss\"):\n            if BSD and nt.peak_rss == 0:\n                pass  # kernel threads don't have rusage tracking\n            else:\n                # VmHWM (from /proc/pid/status) and ru_maxrss both\n                # track peak RSS but are synced independently. Allow 5%\n                # tolerance.\n                diff = nt.rss - nt.peak_rss\n                assert diff <= nt.rss * 0.05\n\n\ndef is_win_secure_system_proc(pid):\n    # see: https://github.com/giampaolo/psutil/issues/2338\n    @functools.lru_cache\n    def get_procs():\n        ret = {}\n        out = sh(\"tasklist.exe /NH /FO csv\")\n        for line in out.splitlines()[1:]:\n            bits = [x.replace('\"', \"\") for x in line.split(\",\")]\n            name, pid = bits[0], int(bits[1])\n            ret[pid] = name\n        return ret\n\n    try:\n        return get_procs()[pid] == \"Secure System\"\n    except KeyError:\n        return False\n\n\ndef _get_eligible_cpu():\n    p = psutil.Process()\n    if hasattr(p, \"cpu_num\"):\n        return p.cpu_num()\n    elif hasattr(p, \"cpu_affinity\"):\n        return random.choice(p.cpu_affinity())\n    return 0\n\n\nclass process_namespace:\n    \"\"\"A container that lists all Process class method names + some\n    reasonable parameters to be called with. Utility methods (parent(),\n    children(), ...) are excluded.\n\n    >>> ns = process_namespace(psutil.Process())\n    >>> for fun, name in ns.iter(ns.getters):\n    ...    fun()\n    \"\"\"\n\n    utils = [('cpu_percent', (), {}), ('memory_percent', (), {})]\n\n    ignored = [\n        ('as_dict', (), {}),\n        ('children', (), {'recursive': True}),\n        ('connections', (), {}),  # deprecated\n        ('is_running', (), {}),\n        ('memory_full_info', (), {}),  # deprecated\n        ('oneshot', (), {}),\n        ('parent', (), {}),\n        ('parents', (), {}),\n        ('pid', (), {}),\n        ('wait', (0,), {}),\n    ]\n\n    getters = [\n        ('cmdline', (), {}),\n        ('cpu_times', (), {}),\n        ('create_time', (), {}),\n        ('cwd', (), {}),\n        ('exe', (), {}),\n        ('memory_info', (), {}),\n        ('memory_info_ex', (), {}),\n        ('name', (), {}),\n        ('net_connections', (), {'kind': 'all'}),\n        ('nice', (), {}),\n        ('num_ctx_switches', (), {}),\n        ('num_threads', (), {}),\n        ('open_files', (), {}),\n        ('page_faults', (), {}),\n        ('ppid', (), {}),\n        ('status', (), {}),\n        ('threads', (), {}),\n        ('username', (), {}),\n    ]\n    if POSIX:\n        getters += [('uids', (), {})]\n        getters += [('gids', (), {})]\n        getters += [('terminal', (), {})]\n        getters += [('num_fds', (), {})]\n    if HAS_PROC_IO_COUNTERS:\n        getters += [('io_counters', (), {})]\n    if HAS_PROC_IONICE:\n        getters += [('ionice', (), {})]\n    if HAS_PROC_RLIMIT:\n        getters += [('rlimit', (psutil.RLIMIT_NOFILE,), {})]\n    if HAS_PROC_CPU_AFFINITY:\n        getters += [('cpu_affinity', (), {})]\n    if HAS_PROC_CPU_NUM:\n        getters += [('cpu_num', (), {})]\n    if HAS_PROC_ENVIRON:\n        getters += [('environ', (), {})]\n    if WINDOWS:\n        getters += [('num_handles', (), {})]\n    if HAS_PROC_MEMORY_FOOTPRINT:\n        getters += [('memory_footprint', (), {})]\n    if HAS_PROC_MEMORY_MAPS:\n        getters += [('memory_maps', (), {'grouped': True})]\n        getters += [('memory_maps', (), {'grouped': False})]\n\n    setters = []\n    if POSIX:\n        setters += [('nice', (0,), {})]\n    else:\n        setters += [('nice', (psutil.NORMAL_PRIORITY_CLASS,), {})]\n    if HAS_PROC_RLIMIT:\n        setters += [('rlimit', (psutil.RLIMIT_NOFILE, (1024, 4096)), {})]\n    if HAS_PROC_IONICE:\n        if LINUX:\n            setters += [('ionice', (psutil.IOPRIO_CLASS_NONE, 0), {})]\n        else:\n            setters += [('ionice', (psutil.IOPRIO_NORMAL,), {})]\n    if HAS_PROC_CPU_AFFINITY:\n        setters += [('cpu_affinity', ([_get_eligible_cpu()],), {})]\n\n    killers = [\n        ('send_signal', (signal.SIGTERM,), {}),\n        ('suspend', (), {}),\n        ('resume', (), {}),\n        ('terminate', (), {}),\n        ('kill', (), {}),\n    ]\n    if WINDOWS:\n        killers += [('send_signal', (signal.CTRL_C_EVENT,), {})]\n        killers += [('send_signal', (signal.CTRL_BREAK_EVENT,), {})]\n\n    all = utils + getters + setters + killers\n\n    def __init__(self, proc):\n        self._proc = proc\n\n    def iter(self, ls, clear_cache=True):\n        \"\"\"Given a list of tuples yields a set of (fun, fun_name) tuples\n        in random order.\n        \"\"\"\n        ls = list(ls)\n        random.shuffle(ls)\n        for fun_name, args, kwds in ls:\n            if clear_cache:\n                self.clear_cache()\n            fun = getattr(self._proc, fun_name)\n            fun = functools.partial(fun, *args, **kwds)\n            yield (fun, fun_name)\n\n    def clear_cache(self):\n        \"\"\"Clear the cache of a Process instance.\"\"\"\n        self._proc._init(self._proc.pid, _ignore_nsp=True)\n\n    @classmethod\n    def test_class_coverage(cls, test_class, ls):\n        \"\"\"Given a TestCase instance and a list of tuples checks that\n        the class defines the required test method names.\n        \"\"\"\n        for fun_name, _, _ in ls:\n            meth_name = 'test_' + fun_name\n            if not hasattr(test_class, meth_name):\n                msg = (\n                    f\"{test_class.__class__.__name__!r} class should define a\"\n                    f\" {meth_name!r} method\"\n                )\n                raise AttributeError(msg)\n\n    @classmethod\n    def test(cls):\n        this = {x[0] for x in cls.all}\n        ignored = {x[0] for x in cls.ignored}\n        klass = {x for x in dir(psutil.Process) if x[0] != '_'}\n        leftout = (this | ignored) ^ klass\n        if leftout:\n            raise ValueError(f\"uncovered Process class names: {leftout!r}\")\n\n\nclass system_namespace:\n    \"\"\"A container that lists all the module-level, system-related APIs.\n    Utilities such as cpu_percent() are excluded. Usage:\n\n    >>> ns = system_namespace\n    >>> for fun, name in ns.iter(ns.getters):\n    ...    fun()\n    \"\"\"\n\n    getters = [\n        ('boot_time', (), {}),\n        ('cpu_count', (), {'logical': False}),\n        ('cpu_count', (), {'logical': True}),\n        ('cpu_stats', (), {}),\n        ('cpu_times', (), {'percpu': False}),\n        ('cpu_times', (), {'percpu': True}),\n        ('disk_io_counters', (), {'perdisk': False}),\n        ('disk_io_counters', (), {'perdisk': True}),\n        ('disk_partitions', (), {'all': False}),\n        ('disk_partitions', (), {'all': True}),\n        ('disk_usage', (os.getcwd(),), {}),\n        ('getloadavg', (), {}),\n        ('net_connections', (), {'kind': 'all'}),\n        ('net_if_addrs', (), {}),\n        ('net_if_stats', (), {}),\n        ('net_io_counters', (), {'pernic': False}),\n        ('net_io_counters', (), {'pernic': True}),\n        ('pid_exists', (os.getpid(),), {}),\n        ('pids', (), {}),\n        ('swap_memory', (), {}),\n        ('users', (), {}),\n        ('virtual_memory', (), {}),\n    ]\n\n    if HAS_CPU_FREQ:\n        getters += [('cpu_freq', (), {'percpu': False})]\n        getters += [('cpu_freq', (), {'percpu': True})]\n    if HAS_SENSORS_TEMPERATURES:\n        getters += [('sensors_temperatures', (), {})]\n    if HAS_SENSORS_FANS:\n        getters += [('sensors_fans', (), {})]\n    if HAS_SENSORS_BATTERY:\n        getters += [('sensors_battery', (), {})]\n    if HAS_HEAP_INFO:\n        getters += [('heap_info', (), {})]\n        getters += [('heap_trim', (), {})]\n\n    if WINDOWS:\n        getters += [('win_service_iter', (), {})]\n        getters += [('win_service_get', ('alg',), {})]\n\n    ignored = [\n        ('process_iter', (), {}),\n        ('wait_procs', ([psutil.Process()],), {}),\n        ('cpu_percent', (), {}),\n        ('cpu_times_percent', (), {}),\n    ]\n\n    all = getters\n\n    @staticmethod\n    def iter(ls):\n        \"\"\"Given a list of tuples yields a set of (fun, fun_name) tuples\n        in random order.\n        \"\"\"\n        ls = list(ls)\n        random.shuffle(ls)\n        for fun_name, args, kwds in ls:\n            fun = getattr(psutil, fun_name)\n            fun = functools.partial(fun, *args, **kwds)\n            yield (fun, fun_name)\n\n    test_class_coverage = process_namespace.test_class_coverage\n\n\ndef retry_on_failure(retries=NO_RETRIES):\n    \"\"\"Decorator which runs a test function and retries N times before\n    giving up and failing.\n    \"\"\"\n\n    def decorator(test_method):\n        @functools.wraps(test_method)\n        def wrapper(self, *args, **kwargs):\n            err = None\n            for attempt in range(retries):\n                try:\n                    return test_method(self, *args, **kwargs)\n                except (AssertionError, pytest.fail.Exception) as _:\n                    err = _\n                    prefix = \"\\n\" if attempt == 0 else \"\"\n                    short_err = str(err).split(\"\\n\")[0]\n                    print(  # noqa: T201\n                        f\"{prefix}{short_err}, retrying\"\n                        f\" {attempt + 1}/{retries} ...\",\n                        file=sys.stderr,\n                    )\n                    if hasattr(self, \"tearDown\"):\n                        self.tearDown()\n                    if hasattr(self, \"teardown_method\"):\n                        self.teardown_method()\n                    if hasattr(self, \"setUp\"):\n                        self.setUp()\n                    if hasattr(self, \"setup_method\"):\n                        self.setup_method()\n\n            raise err\n\n        return wrapper\n\n    assert retries > 1, retries\n    return decorator\n\n\ndef skip_on_access_denied(only_if=None):\n    \"\"\"Decorator to Ignore AccessDenied exceptions.\"\"\"\n\n    def decorator(fun):\n        @functools.wraps(fun)\n        def wrapper(*args, **kwargs):\n            try:\n                return fun(*args, **kwargs)\n            except psutil.AccessDenied:\n                if only_if is not None:\n                    if not only_if:\n                        raise\n                return pytest.skip(\"raises AccessDenied\")\n\n        return wrapper\n\n    return decorator\n\n\ndef skip_on_not_implemented(only_if=None):\n    \"\"\"Decorator to Ignore NotImplementedError exceptions.\"\"\"\n\n    def decorator(fun):\n        @functools.wraps(fun)\n        def wrapper(*args, **kwargs):\n            try:\n                return fun(*args, **kwargs)\n            except NotImplementedError:\n                if only_if is not None:\n                    if not only_if:\n                        raise\n                msg = (\n                    f\"{fun.__name__!r} was skipped because it raised\"\n                    \" NotImplementedError\"\n                )\n                return pytest.skip(msg)\n\n        return wrapper\n\n    return decorator\n\n\n# ===================================================================\n# --- network\n# ===================================================================\n\n\n# XXX: no longer used\ndef get_free_port(host='127.0.0.1'):\n    \"\"\"Return an unused TCP port. Subject to race conditions.\"\"\"\n    with socket.socket() as sock:\n        sock.bind((host, 0))\n        return sock.getsockname()[1]\n\n\ndef bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None):\n    \"\"\"Binds a generic socket.\"\"\"\n    if addr is None and family in {AF_INET, AF_INET6}:\n        addr = (\"\", 0)\n    sock = socket.socket(family, type)\n    try:\n        if os.name not in {'nt', 'cygwin'}:\n            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n        sock.bind(addr)\n        if type == socket.SOCK_STREAM:\n            sock.listen(5)\n        return sock\n    except Exception:\n        sock.close()\n        raise\n\n\ndef bind_unix_socket(name, type=socket.SOCK_STREAM):\n    \"\"\"Bind a UNIX socket.\"\"\"\n    assert psutil.POSIX\n    assert not os.path.exists(name), name\n    sock = socket.socket(socket.AF_UNIX, type)\n    try:\n        sock.bind(name)\n        if type == socket.SOCK_STREAM:\n            sock.listen(5)\n    except Exception:\n        sock.close()\n        raise\n    return sock\n\n\ndef tcp_socketpair(family, addr=(\"\", 0)):\n    \"\"\"Build a pair of TCP sockets connected to each other.\n    Return a (server, client) tuple.\n    \"\"\"\n    with socket.socket(family, SOCK_STREAM) as ll:\n        ll.bind(addr)\n        ll.listen(5)\n        addr = ll.getsockname()\n        c = socket.socket(family, SOCK_STREAM)\n        try:\n            c.connect(addr)\n            caddr = c.getsockname()\n            while True:\n                a, addr = ll.accept()\n                # check that we've got the correct client\n                if addr == caddr:\n                    return (a, c)\n                a.close()\n        except OSError:\n            c.close()\n            raise\n\n\ndef unix_socketpair(name):\n    \"\"\"Build a pair of UNIX sockets connected to each other through\n    the same UNIX file name.\n    Return a (server, client) tuple.\n    \"\"\"\n    assert psutil.POSIX\n    server = client = None\n    try:\n        server = bind_unix_socket(name, type=socket.SOCK_STREAM)\n        server.setblocking(0)\n        client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n        client.setblocking(0)\n        client.connect(name)\n        # new = server.accept()\n    except Exception:\n        if server is not None:\n            server.close()\n        if client is not None:\n            client.close()\n        raise\n    return (server, client)\n\n\n@contextlib.contextmanager\ndef create_sockets():\n    \"\"\"Open as many socket families / types as possible.\"\"\"\n    socks = []\n    fname1 = fname2 = None\n    try:\n        socks.extend((\n            bind_socket(socket.AF_INET, socket.SOCK_STREAM),\n            bind_socket(socket.AF_INET, socket.SOCK_DGRAM),\n        ))\n        if supports_ipv6():\n            socks.extend((\n                bind_socket(socket.AF_INET6, socket.SOCK_STREAM),\n                bind_socket(socket.AF_INET6, socket.SOCK_DGRAM),\n            ))\n        if POSIX and HAS_NET_CONNECTIONS_UNIX:\n            fname1 = get_testfn()\n            fname2 = get_testfn()\n            s1, s2 = unix_socketpair(fname1)\n            s3 = bind_unix_socket(fname2, type=socket.SOCK_DGRAM)\n            for s in (s1, s2, s3):\n                socks.append(s)\n        yield socks\n    finally:\n        for s in socks:\n            s.close()\n        for fname in (fname1, fname2):\n            if fname is not None:\n                safe_rmpath(fname)\n\n\ndef check_net_address(addr, family):\n    \"\"\"Check a net address validity. Supported families are IPv4,\n    IPv6 and MAC addresses.\n    \"\"\"\n    assert isinstance(family, enum.IntEnum), family\n    if family == socket.AF_INET:\n        octs = [int(x) for x in addr.split('.')]\n        assert len(octs) == 4, addr\n        for num in octs:\n            assert 0 <= num <= 255, addr\n        ipaddress.IPv4Address(addr)\n    elif family == socket.AF_INET6:\n        assert isinstance(addr, str), addr\n        ipaddress.IPv6Address(addr)\n    elif family == psutil.AF_LINK:\n        assert re.match(r'([a-fA-F0-9]{2}[:|\\-]?){6}', addr) is not None, addr\n    else:\n        raise ValueError(f\"unknown family {family!r}\")\n\n\ndef check_connection_ntuple(conn):\n    \"\"\"Check validity of a connection named tuple.\"\"\"\n\n    def check_ntuple(conn):\n        has_pid = len(conn) == 7\n        assert len(conn) in {6, 7}, len(conn)\n        assert conn[0] == conn.fd, conn.fd\n        assert conn[1] == conn.family, conn.family\n        assert conn[2] == conn.type, conn.type\n        assert conn[3] == conn.laddr, conn.laddr\n        assert conn[4] == conn.raddr, conn.raddr\n        assert conn[5] == conn.status, conn.status\n        if has_pid:\n            assert conn[6] == conn.pid, conn.pid\n\n    def check_family(conn):\n        assert conn.family in {AF_INET, AF_INET6, AF_UNIX}, conn.family\n        assert isinstance(conn.family, enum.IntEnum), conn\n        if conn.family == AF_INET:\n            # actually try to bind the local socket; ignore IPv6\n            # sockets as their address might be represented as\n            # an IPv4-mapped-address (e.g. \"::127.0.0.1\")\n            # and that's rejected by bind()\n            with socket.socket(conn.family, conn.type) as s:\n                try:\n                    s.bind((conn.laddr[0], 0))\n                except OSError as err:\n                    if err.errno != errno.EADDRNOTAVAIL:\n                        raise\n        elif conn.family == AF_UNIX:\n            assert conn.status == psutil.CONN_NONE, conn.status\n\n    def check_type(conn):\n        # SOCK_SEQPACKET may happen in case of AF_UNIX socks\n        SOCK_SEQPACKET = getattr(socket, \"SOCK_SEQPACKET\", object())\n        assert conn.type in {\n            socket.SOCK_STREAM,\n            socket.SOCK_DGRAM,\n            SOCK_SEQPACKET,\n        }, conn.type\n        assert isinstance(conn.type, enum.IntEnum), conn\n        if conn.type == socket.SOCK_DGRAM:\n            assert conn.status == psutil.CONN_NONE, conn.status\n\n    def check_addrs(conn):\n        # check IP address and port sanity\n        for addr in (conn.laddr, conn.raddr):\n            if conn.family in {AF_INET, AF_INET6}:\n                assert isinstance(addr, tuple), type(addr)\n                if not addr:\n                    continue\n                assert isinstance(addr.port, int), type(addr.port)\n                assert 0 <= addr.port <= 65535, addr.port\n                check_net_address(addr.ip, conn.family)\n            elif conn.family == AF_UNIX:\n                assert isinstance(addr, str), type(addr)\n\n    def check_status(conn):\n        assert isinstance(conn.status, str), conn.status\n        valids = [\n            getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_')\n        ]\n        assert conn.status in valids, conn.status\n        if conn.family in {AF_INET, AF_INET6} and conn.type == SOCK_STREAM:\n            assert conn.status != psutil.CONN_NONE, conn.status\n        else:\n            assert conn.status == psutil.CONN_NONE, conn.status\n\n    check_ntuple_type_hints(conn)\n    check_ntuple(conn)\n    check_family(conn)\n    check_type(conn)\n    check_addrs(conn)\n    check_status(conn)\n\n\ndef filter_proc_net_connections(cons):\n    \"\"\"Our process may start with some open UNIX sockets which are not\n    initialized by us, invalidating unit tests.\n    \"\"\"\n    new = []\n    for conn in cons:\n        if POSIX and conn.family == socket.AF_UNIX:\n            if MACOS and \"/syslog\" in conn.raddr:\n                debug(f\"skipping {conn}\")\n                continue\n        new.append(conn)\n    return new\n\n\n# =====================================================================\n# --- type hints\n# =====================================================================\n\n\nclass TypeHintsChecker:\n    try:\n        UNION_TYPES = (typing.Union, types.UnionType)\n    except AttributeError:  # Python < 3.10\n        UNION_TYPES = (typing.Union,)\n\n    @staticmethod\n    @functools.lru_cache(maxsize=None)\n    def _get_ntuple_hints(nt):\n        cls = type(nt)\n        try:\n            localns = {\n                name: obj\n                for name, obj in vars(_enums).items()\n                if isinstance(obj, type) and issubclass(obj, enum.Enum)\n            }\n            localns['socket'] = socket\n            return typing.get_type_hints(\n                cls,\n                globalns=vars(ntuples),\n                localns=localns,\n            )\n        except TypeError:\n            # Python < 3.10 can't evaluate \"X | Y\" union syntax.\n            return {}\n\n    @staticmethod\n    def _hint_to_types(hint):\n        \"\"\"Flatten a type hint into a tuple of concrete types suitable\n        for isinstance(). Returns None if the hint cannot be checked.\n        \"\"\"\n        if not hasattr(typing, \"get_origin\") and sys.version_info[:2] <= (\n            3,\n            7,\n        ):\n            return None\n        origin = typing.get_origin(hint)\n        if origin in TypeHintsChecker.UNION_TYPES:\n            result = []\n            for arg in typing.get_args(hint):\n                inner = typing.get_origin(arg)\n                if inner is not None:\n                    result.append(inner)\n                elif isinstance(arg, type):\n                    result.append(arg)\n            return tuple(result) if result else None\n        if origin is not None:\n            return (origin,)\n        if isinstance(hint, type):\n            return (hint,)\n        return None\n\n    @staticmethod\n    def check_ntuple_type_hints(nt):\n        \"\"\"Uses type hints from _ntuples.py to verify field types. `nt`\n        is a named tuple returned by one of psutil APIs.\n        \"\"\"\n        assert is_namedtuple(nt)\n        hints = TypeHintsChecker._get_ntuple_hints(nt)\n        if not hints:\n            return\n        for field in nt._fields:\n            if field not in hints:\n                # field is not annotated\n                continue\n            value = getattr(nt, field)\n            types_ = TypeHintsChecker._hint_to_types(hints[field])\n            if types_ is None:\n                continue\n            # For IntEnum hints (e.g. socket.AddressFamily), psutil may\n            # return a platform-specific IntEnum subclass rather than\n            # the annotated one, so we broaden the check to int.\n            types_ = tuple(\n                (\n                    int\n                    if isinstance(t, type) and issubclass(t, enum.IntEnum)\n                    else t\n                )\n                for t in types_\n            )\n            assert isinstance(value, types_), (field, value, types_)\n\n    @staticmethod\n    @functools.lru_cache(maxsize=None)\n    def _get_return_hint(fun):\n        \"\"\"Get the 'return' type hint for a psutil API function or\n        method. Resolves annotation strings using a combined namespace\n        of psutil globals (Any, Generator, Process, ...) and ntuple\n        types (scputimes, svmem, pmem, ...). Returns None if hints\n        cannot be resolved or there is no return annotation.\n        \"\"\"\n        while hasattr(fun, 'func'):\n            fun = fun.func\n        # Build a namespace that can resolve all annotations.\n        psp = vars(psutil).get('_psplatform')\n        psp_ns = vars(psp) if psp is not None else {}\n        ns = {\n            **psp_ns,\n            **vars(psutil),\n            **vars(ntuples),\n            **vars(typing),\n        }\n        underlying = getattr(fun, '__func__', fun)\n        try:\n            hints = typing.get_type_hints(underlying, globalns=ns)\n        except TypeError:\n            # X | Y union syntax in annotations requires Python 3.10+\n            # to evaluate. On older versions skip the check entirely.\n            if sys.version_info < (3, 10):\n                msg = f\"skip X|Y type check on old python for {fun.__name__!r}\"\n                warn(msg)\n                return None\n            else:\n                raise\n        return hints.get('return')\n\n    @staticmethod\n    def _check_container_items(hint, value):\n        \"\"\"For list[T] and dict[K, V] hints, verify element types.\"\"\"\n        origin = typing.get_origin(hint)\n        args = typing.get_args(hint)\n        if origin is list and args:\n            elem_types = TypeHintsChecker._hint_to_types(args[0])\n            if elem_types:\n                for item in value:\n                    assert isinstance(item, elem_types), (item, elem_types)\n        elif origin is dict and len(args) == 2:\n            key_types = TypeHintsChecker._hint_to_types(args[0])\n            val_types = TypeHintsChecker._hint_to_types(args[1])\n            for k, v in value.items():\n                if key_types:\n                    assert isinstance(k, key_types), (k, key_types)\n                if val_types:\n                    assert isinstance(v, val_types), (v, val_types)\n\n    @staticmethod\n    def check_fun_type_hints(fun, retval):\n        \"\"\"Use the 'return' type hint of *fun* from psutil/__init__.py\n        to verify that *retval* is an instance of the annotated type.\n        \"\"\"\n        hint = TypeHintsChecker._get_return_hint(fun)\n        if hint is None:\n            if not hasattr(types, \"UnionType\"):\n                # added in python 3.10\n                return\n            raise ValueError(f\"no type hints defined for {fun}\")\n        types_ = TypeHintsChecker._hint_to_types(hint)\n        assert types_, hint\n        assert isinstance(retval, types_), (fun, retval, types_)\n        TypeHintsChecker._check_container_items(hint, retval)\n\n\ncheck_ntuple_type_hints = TypeHintsChecker.check_ntuple_type_hints\ncheck_fun_type_hints = TypeHintsChecker.check_fun_type_hints\n\n\n# ===================================================================\n# --- import utils\n# ===================================================================\n\n\ndef reload_module(module):\n    return importlib.reload(module)\n\n\ndef import_module_by_path(path):\n    from _bootstrap import load_module\n\n    return load_module(path)\n\n\n# ===================================================================\n# --- others\n# ===================================================================\n\n\ndef warn(msg):\n    \"\"\"Raise a warning msg.\"\"\"\n    warnings.warn(msg, UserWarning, stacklevel=2)\n\n\ndef is_namedtuple(x):\n    \"\"\"Check if object is an instance of named tuple.\"\"\"\n    t = type(x)\n    if tuple not in t.__mro__:\n        return False\n    f = getattr(t, '_fields', None)\n    if not isinstance(f, tuple):\n        return False\n    return all(isinstance(n, str) for n in f)\n\n\nif POSIX:\n\n    @contextlib.contextmanager\n    def copyload_shared_lib(suffix=\"\"):\n        \"\"\"Ctx manager which picks up a random shared CO lib used\n        by this process, copies it in another location and loads it\n        in memory via ctypes. Return the new absolutized path.\n        \"\"\"\n        exe = 'pypy' if PYPY else 'python'\n        ext = \".so\"\n        dst = get_testfn(suffix=suffix + ext)\n        libs = [\n            x.path\n            for x in psutil.Process().memory_maps()\n            if os.path.splitext(x.path)[1] == ext and exe in x.path.lower()\n        ]\n        src = random.choice(libs)\n        shutil.copyfile(src, dst)\n        try:\n            ctypes.CDLL(dst)\n            yield dst\n        finally:\n            safe_rmpath(dst)\n\nelse:\n\n    @contextlib.contextmanager\n    def copyload_shared_lib(suffix=\"\"):\n        \"\"\"Ctx manager which picks up a random shared DLL lib used\n        by this process, copies it in another location and loads it\n        in memory via ctypes.\n        Return the new absolutized, normcased path.\n        \"\"\"\n        from ctypes import WinError\n        from ctypes import wintypes\n\n        ext = \".dll\"\n        dst = get_testfn(suffix=suffix + ext)\n        libs = [\n            x.path\n            for x in psutil.Process().memory_maps()\n            if x.path.lower().endswith(ext)\n            and 'python' in os.path.basename(x.path).lower()\n            and 'wow64' not in x.path.lower()\n        ]\n        if PYPY and not libs:\n            libs = [\n                x.path\n                for x in psutil.Process().memory_maps()\n                if 'pypy' in os.path.basename(x.path).lower()\n            ]\n        src = random.choice(libs)\n        shutil.copyfile(src, dst)\n        cfile = None\n        try:\n            cfile = ctypes.WinDLL(dst)\n            yield dst\n        finally:\n            # Work around OverflowError:\n            # - https://ci.appveyor.com/project/giampaolo/psutil/build/1207/\n            #       job/o53330pbnri9bcw7\n            # - http://bugs.python.org/issue30286\n            # - http://stackoverflow.com/questions/23522055\n            if cfile is not None:\n                FreeLibrary = ctypes.windll.kernel32.FreeLibrary\n                FreeLibrary.argtypes = [wintypes.HMODULE]\n                ret = FreeLibrary(cfile._handle)\n                if ret == 0:\n                    raise WinError()\n            safe_rmpath(dst)\n\n\n# ===================================================================\n# --- Exit funs (first is executed last)\n# ===================================================================\n\n\n# this is executed first\n@atexit.register\ndef cleanup_test_procs():\n    reap_children(recursive=True)\n\n\n# atexit module does not execute exit functions in case of SIGTERM, which\n# gets sent to test subprocesses, which is a problem if they import this\n# module. With this it will. See:\n# https://gmpy.dev/blog/2016/how-to-always-execute-exit-functions-in-python\nif POSIX:\n    signal.signal(signal.SIGTERM, lambda sig, _: sys.exit(sig))\n"
  },
  {
    "path": "tests/test_aix.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'\n# Copyright (c) 2017, Arnon Yaari\n# All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"AIX specific tests.\"\"\"\n\nimport re\n\nimport psutil\nfrom psutil import AIX\n\nfrom . import PsutilTestCase\nfrom . import pytest\nfrom . import sh\n\n\n@pytest.mark.skipif(not AIX, reason=\"AIX only\")\nclass AIXSpecificTestCase(PsutilTestCase):\n    def test_virtual_memory(self):\n        out = sh('/usr/bin/svmon -O unit=KB')\n        re_pattern = r\"memory\\s*\"\n        for field in [\n            \"size\",\n            \"inuse\",\n            \"free\",\n            \"pin\",\n            \"virtual\",\n            \"available\",\n            \"mmode\",\n        ]:\n            re_pattern += rf\"(?P<{field}>\\S+)\\s+\"\n        matchobj = re.search(re_pattern, out)\n\n        assert matchobj is not None\n\n        KB = 1024\n        total = int(matchobj.group(\"size\")) * KB\n        available = int(matchobj.group(\"available\")) * KB\n        used = int(matchobj.group(\"inuse\")) * KB\n        free = int(matchobj.group(\"free\")) * KB\n\n        psutil_result = psutil.virtual_memory()\n\n        # TOLERANCE_SYS_MEM is not enough. For some reason we're seeing\n        # differences of ~1.2 MB. 2 MB is still a good tolerance when\n        # compared to GBs.\n        TOLERANCE_SYS_MEM = 2 * KB * KB  # 2 MB\n        assert psutil_result.total == total\n        assert abs(psutil_result.used - used) < TOLERANCE_SYS_MEM\n        assert abs(psutil_result.available - available) < TOLERANCE_SYS_MEM\n        assert abs(psutil_result.free - free) < TOLERANCE_SYS_MEM\n\n    def test_swap_memory(self):\n        out = sh('/usr/sbin/lsps -a')\n        # From the man page, \"The size is given in megabytes\" so we assume\n        # we'll always have 'MB' in the result\n        # TODO maybe try to use \"swap -l\" to check \"used\" too, but its units\n        # are not guaranteed to be \"MB\" so parsing may not be consistent\n        matchobj = re.search(\n            r\"(?P<space>\\S+)\\s+\"\n            r\"(?P<vol>\\S+)\\s+\"\n            r\"(?P<vg>\\S+)\\s+\"\n            r\"(?P<size>\\d+)MB\",\n            out,\n        )\n\n        assert matchobj is not None\n\n        total_mb = int(matchobj.group(\"size\"))\n        MB = 1024**2\n        psutil_result = psutil.swap_memory()\n        # we divide our result by MB instead of multiplying the lsps value by\n        # MB because lsps may round down, so we round down too\n        assert int(psutil_result.total / MB) == total_mb\n\n    def test_cpu_stats(self):\n        out = sh('/usr/bin/mpstat -a')\n\n        re_pattern = r\"ALL\\s*\"\n        for field in [\n            \"min\",\n            \"maj\",\n            \"mpcs\",\n            \"mpcr\",\n            \"dev\",\n            \"soft\",\n            \"dec\",\n            \"ph\",\n            \"cs\",\n            \"ics\",\n            \"bound\",\n            \"rq\",\n            \"push\",\n            \"S3pull\",\n            \"S3grd\",\n            \"S0rd\",\n            \"S1rd\",\n            \"S2rd\",\n            \"S3rd\",\n            \"S4rd\",\n            \"S5rd\",\n            \"sysc\",\n        ]:\n            re_pattern += rf\"(?P<{field}>\\S+)\\s+\"\n        matchobj = re.search(re_pattern, out)\n\n        assert matchobj is not None\n\n        # numbers are usually in the millions so 1000 is ok for tolerance\n        CPU_STATS_TOLERANCE = 1000\n        psutil_result = psutil.cpu_stats()\n        assert (\n            abs(psutil_result.ctx_switches - int(matchobj.group(\"cs\")))\n            < CPU_STATS_TOLERANCE\n        )\n        assert (\n            abs(psutil_result.syscalls - int(matchobj.group(\"sysc\")))\n            < CPU_STATS_TOLERANCE\n        )\n        assert (\n            abs(psutil_result.interrupts - int(matchobj.group(\"dev\")))\n            < CPU_STATS_TOLERANCE\n        )\n        assert (\n            abs(psutil_result.soft_interrupts - int(matchobj.group(\"soft\")))\n            < CPU_STATS_TOLERANCE\n        )\n\n    def test_cpu_count_logical(self):\n        out = sh('/usr/bin/mpstat -a')\n        mpstat_lcpu = int(re.search(r\"lcpu=(\\d+)\", out).group(1))\n        psutil_lcpu = psutil.cpu_count(logical=True)\n        assert mpstat_lcpu == psutil_lcpu\n\n    def test_net_if_addrs_names(self):\n        out = sh('/etc/ifconfig -l')\n        ifconfig_names = set(out.split())\n        psutil_names = set(psutil.net_if_addrs().keys())\n        assert ifconfig_names == psutil_names\n"
  },
  {
    "path": "tests/test_bsd.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n# TODO: (FreeBSD) add test for comparing connections with 'sockstat' cmd.\n\n\n\"\"\"Tests specific to all BSD platforms.\"\"\"\n\nimport datetime\nimport os\nimport re\nimport shutil\nimport time\n\nimport psutil\nfrom psutil import BSD\nfrom psutil import FREEBSD\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\n\nfrom . import HAS_BATTERY\nfrom . import TOLERANCE_SYS_MEM\nfrom . import PsutilTestCase\nfrom . import pytest\nfrom . import retry_on_failure\nfrom . import sh\nfrom . import spawn_subproc\nfrom . import terminate\n\nif BSD:\n    PAGESIZE = psutil._psplatform.cext.getpagesize()\n    # muse requires root privileges\n    MUSE_AVAILABLE = os.getuid() == 0 and shutil.which(\"muse\")\nelse:\n    PAGESIZE = None\n    MUSE_AVAILABLE = False\n\n\ndef sysctl(cmdline):\n    \"\"\"Expects a sysctl command with an argument and parse the result\n    returning only the value of interest.\n    \"\"\"\n    result = sh(\"sysctl \" + cmdline)\n    if FREEBSD:\n        result = result[result.find(\": \") + 2 :]\n    elif OPENBSD or NETBSD:\n        result = result[result.find(\"=\") + 1 :]\n    try:\n        return int(result)\n    except ValueError:\n        return result\n\n\ndef muse(field):\n    \"\"\"Thin wrapper around 'muse' cmdline utility.\"\"\"\n    out = sh('muse')\n    for line in out.split('\\n'):\n        if line.startswith(field):\n            break\n    else:\n        raise ValueError(\"line not found\")\n    return int(line.split()[1])\n\n\n# =====================================================================\n# --- All BSD*\n# =====================================================================\n\n\n@pytest.mark.skipif(not BSD, reason=\"BSD only\")\nclass BSDTestCase(PsutilTestCase):\n    \"\"\"Generic tests common to all BSD variants.\"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc().pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    @pytest.mark.skipif(NETBSD, reason=\"-o lstart doesn't work on NETBSD\")\n    def test_process_create_time(self):\n        output = sh(f\"ps -o lstart -p {self.pid}\")\n        start_ps = output.replace('STARTED', '').strip()\n        start_psutil = psutil.Process(self.pid).create_time()\n        start_psutil = time.strftime(\n            \"%a %b %e %H:%M:%S %Y\", time.localtime(start_psutil)\n        )\n        assert start_ps == start_psutil\n\n    def test_disks(self):\n        # test psutil.disk_usage() and psutil.disk_partitions()\n        # against \"df -a\"\n        def df(path):\n            out = sh(f'df -k \"{path}\"').strip()\n            lines = out.split('\\n')\n            lines.pop(0)\n            line = lines.pop(0)\n            dev, total, used, free = line.split()[:4]\n            if dev == 'none':\n                dev = ''\n            total = int(total) * 1024\n            used = int(used) * 1024\n            free = int(free) * 1024\n            return dev, total, used, free\n\n        for part in psutil.disk_partitions(all=False):\n            usage = psutil.disk_usage(part.mountpoint)\n            dev, total, used, free = df(part.mountpoint)\n            assert part.device == dev\n            assert usage.total == total\n            # 10 MB tolerance\n            if abs(usage.free - free) > 10 * 1024 * 1024:\n                return pytest.fail(f\"psutil={usage.free}, df={free}\")\n            if abs(usage.used - used) > 10 * 1024 * 1024:\n                return pytest.fail(f\"psutil={usage.used}, df={used}\")\n\n    @pytest.mark.skipif(\n        not shutil.which(\"sysctl\"), reason=\"sysctl cmd not available\"\n    )\n    def test_cpu_count_logical(self):\n        syst = sysctl(\"hw.ncpu\")\n        assert psutil.cpu_count(logical=True) == syst\n\n    @pytest.mark.skipif(\n        not shutil.which(\"sysctl\"), reason=\"sysctl cmd not available\"\n    )\n    @pytest.mark.skipif(\n        NETBSD, reason=\"skipped on NETBSD\"  # we check /proc/meminfo\n    )\n    def test_virtual_memory_total(self):\n        num = sysctl('hw.physmem')\n        assert num == psutil.virtual_memory().total\n\n    @pytest.mark.skipif(\n        not shutil.which(\"ifconfig\"), reason=\"ifconfig cmd not available\"\n    )\n    def test_net_if_stats(self):\n        for name, stats in psutil.net_if_stats().items():\n            try:\n                out = sh(f\"ifconfig {name}\")\n            except RuntimeError:\n                pass\n            else:\n                assert stats.isup == ('RUNNING' in out)\n                if \"mtu\" in out:\n                    assert stats.mtu == int(re.findall(r'mtu (\\d+)', out)[0])\n\n\n# =====================================================================\n# --- FreeBSD\n# =====================================================================\n\n\n@pytest.mark.skipif(not FREEBSD, reason=\"FREEBSD only\")\nclass FreeBSDTestCase(PsutilTestCase):\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc().pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    @retry_on_failure()\n    def test_memory_maps(self):\n        out = sh(f\"procstat -v {self.pid}\")\n        maps = psutil.Process(self.pid).memory_maps(grouped=False)\n        lines = out.split('\\n')[1:]\n        while lines:\n            line = lines.pop()\n            fields = line.split()\n            _, start, stop, _perms, res = fields[:5]\n            map = maps.pop()\n            assert f\"{start}-{stop}\" == map.addr\n            assert int(res) * PAGESIZE == map.rss\n            if not map.path.startswith('['):\n                assert fields[10] == map.path\n\n    def test_exe(self):\n        out = sh(f\"procstat -b {self.pid}\")\n        assert psutil.Process(self.pid).exe() == out.split('\\n')[1].split()[-1]\n\n    def test_cmdline(self):\n        out = sh(f\"procstat -c {self.pid}\")\n        assert ' '.join(psutil.Process(self.pid).cmdline()) == ' '.join(\n            out.split('\\n')[1].split()[2:]\n        )\n\n    def test_uids_gids(self):\n        out = sh(f\"procstat -s {self.pid}\")\n        euid, ruid, suid, egid, rgid, sgid = out.split('\\n')[1].split()[2:8]\n        p = psutil.Process(self.pid)\n        uids = p.uids()\n        gids = p.gids()\n        assert uids.real == int(ruid)\n        assert uids.effective == int(euid)\n        assert uids.saved == int(suid)\n        assert gids.real == int(rgid)\n        assert gids.effective == int(egid)\n        assert gids.saved == int(sgid)\n\n    @retry_on_failure()\n    def test_ctx_switches(self):\n        tested = []\n        out = sh(f\"procstat -r {self.pid}\")\n        p = psutil.Process(self.pid)\n        for line in out.split('\\n'):\n            line = line.lower().strip()\n            if ' voluntary context' in line:\n                pstat_value = int(line.split()[-1])\n                psutil_value = p.num_ctx_switches().voluntary\n                assert pstat_value == psutil_value\n                tested.append(None)\n            elif ' involuntary context' in line:\n                pstat_value = int(line.split()[-1])\n                psutil_value = p.num_ctx_switches().involuntary\n                assert pstat_value == psutil_value\n                tested.append(None)\n        if len(tested) != 2:\n            raise RuntimeError(\"couldn't find lines match in procstat out\")\n\n    @retry_on_failure()\n    def test_cpu_times(self):\n        tested = []\n        out = sh(f\"procstat -r {self.pid}\")\n        p = psutil.Process(self.pid)\n        for line in out.split('\\n'):\n            line = line.lower().strip()\n            if 'user time' in line:\n                pstat_value = float('0.' + line.split()[-1].split('.')[-1])\n                psutil_value = p.cpu_times().user\n                assert pstat_value == psutil_value\n                tested.append(None)\n            elif 'system time' in line:\n                pstat_value = float('0.' + line.split()[-1].split('.')[-1])\n                psutil_value = p.cpu_times().system\n                assert pstat_value == psutil_value\n                tested.append(None)\n        if len(tested) != 2:\n            raise RuntimeError(\"couldn't find lines match in procstat out\")\n\n\n@pytest.mark.skipif(not FREEBSD, reason=\"FREEBSD only\")\nclass FreeBSDSystemTestCase(PsutilTestCase):\n    @staticmethod\n    def parse_swapinfo():\n        # the last line is always the total\n        output = sh(\"swapinfo -k\").splitlines()[-1]\n        parts = re.split(r'\\s+', output)\n\n        if not parts:\n            raise ValueError(f\"Can't parse swapinfo: {output}\")\n\n        # the size is in 1k units, so multiply by 1024\n        total, used, free = (int(p) * 1024 for p in parts[1:4])\n        return total, used, free\n\n    def test_cpu_frequency_against_sysctl(self):\n        # Currently only cpu 0 is frequency is supported in FreeBSD\n        # All other cores use the same frequency.\n        sensor = \"dev.cpu.0.freq\"\n        try:\n            sysctl_result = int(sysctl(sensor))\n        except RuntimeError:\n            return pytest.skip(\"frequencies not supported by kernel\")\n        assert psutil.cpu_freq().current == sysctl_result\n\n        sensor = \"dev.cpu.0.freq_levels\"\n        sysctl_result = sysctl(sensor)\n        # sysctl returns a string of the format:\n        # <freq_level_1>/<voltage_level_1> <freq_level_2>/<voltage_level_2>...\n        # Ordered highest available to lowest available.\n        max_freq = int(sysctl_result.split()[0].split(\"/\")[0])\n        min_freq = int(sysctl_result.split()[-1].split(\"/\")[0])\n        assert psutil.cpu_freq().max == max_freq\n        assert psutil.cpu_freq().min == min_freq\n\n    # --- virtual_memory(); tests against sysctl\n\n    @retry_on_failure()\n    def test_vmem_active(self):\n        syst = sysctl(\"vm.stats.vm.v_active_count\") * PAGESIZE\n        assert abs(psutil.virtual_memory().active - syst) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_vmem_inactive(self):\n        syst = sysctl(\"vm.stats.vm.v_inactive_count\") * PAGESIZE\n        assert abs(psutil.virtual_memory().inactive - syst) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_vmem_wired(self):\n        syst = sysctl(\"vm.stats.vm.v_wire_count\") * PAGESIZE\n        assert abs(psutil.virtual_memory().wired - syst) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_vmem_cached(self):\n        syst = sysctl(\"vm.stats.vm.v_cache_count\") * PAGESIZE\n        assert abs(psutil.virtual_memory().cached - syst) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_vmem_free(self):\n        syst = sysctl(\"vm.stats.vm.v_free_count\") * PAGESIZE\n        assert abs(psutil.virtual_memory().free - syst) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_vmem_buffers(self):\n        syst = sysctl(\"vfs.bufspace\")\n        assert abs(psutil.virtual_memory().buffers - syst) < TOLERANCE_SYS_MEM\n\n    # --- virtual_memory(); tests against muse\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    def test_muse_vmem_total(self):\n        num = muse('Total')\n        assert psutil.virtual_memory().total == num\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    @retry_on_failure()\n    def test_muse_vmem_active(self):\n        num = muse('Active')\n        assert abs(psutil.virtual_memory().active - num) < TOLERANCE_SYS_MEM\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    @retry_on_failure()\n    def test_muse_vmem_inactive(self):\n        num = muse('Inactive')\n        assert abs(psutil.virtual_memory().inactive - num) < TOLERANCE_SYS_MEM\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    @retry_on_failure()\n    def test_muse_vmem_wired(self):\n        num = muse('Wired')\n        assert abs(psutil.virtual_memory().wired - num) < TOLERANCE_SYS_MEM\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    @retry_on_failure()\n    def test_muse_vmem_cached(self):\n        num = muse('Cache')\n        assert abs(psutil.virtual_memory().cached - num) < TOLERANCE_SYS_MEM\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    @retry_on_failure()\n    def test_muse_vmem_free(self):\n        num = muse('Free')\n        assert abs(psutil.virtual_memory().free - num) < TOLERANCE_SYS_MEM\n\n    @pytest.mark.skipif(not MUSE_AVAILABLE, reason=\"muse not installed\")\n    @retry_on_failure()\n    def test_muse_vmem_buffers(self):\n        num = muse('Buffer')\n        assert abs(psutil.virtual_memory().buffers - num) < TOLERANCE_SYS_MEM\n\n    def test_cpu_stats_ctx_switches(self):\n        assert (\n            abs(\n                psutil.cpu_stats().ctx_switches\n                - sysctl('vm.stats.sys.v_swtch')\n            )\n            < 1000\n        )\n\n    def test_cpu_stats_interrupts(self):\n        assert (\n            abs(psutil.cpu_stats().interrupts - sysctl('vm.stats.sys.v_intr'))\n            < 1000\n        )\n\n    def test_cpu_stats_soft_interrupts(self):\n        assert (\n            abs(\n                psutil.cpu_stats().soft_interrupts\n                - sysctl('vm.stats.sys.v_soft')\n            )\n            < 1000\n        )\n\n    @retry_on_failure()\n    def test_cpu_stats_syscalls(self):\n        # pretty high tolerance but it looks like it's OK.\n        assert (\n            abs(psutil.cpu_stats().syscalls - sysctl('vm.stats.sys.v_syscall'))\n            < 200000\n        )\n\n    # --- swap memory\n\n    def test_swapmem_free(self):\n        _total, _used, free = self.parse_swapinfo()\n        assert abs(psutil.swap_memory().free - free) < TOLERANCE_SYS_MEM\n\n    def test_swapmem_used(self):\n        _total, used, _free = self.parse_swapinfo()\n        assert abs(psutil.swap_memory().used - used) < TOLERANCE_SYS_MEM\n\n    def test_swapmem_total(self):\n        total, _used, _free = self.parse_swapinfo()\n        assert abs(psutil.swap_memory().total - total) < TOLERANCE_SYS_MEM\n\n    # --- others\n\n    def test_boot_time(self):\n        s = sysctl('sysctl kern.boottime')\n        s = s[s.find(\" sec = \") + 7 :]\n        s = s[: s.find(',')]\n        btime = int(s)\n        assert btime == psutil.boot_time()\n\n    # --- sensors_battery\n\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_sensors_battery(self):\n        def secs2hours(secs):\n            m, _s = divmod(secs, 60)\n            h, m = divmod(m, 60)\n            return f\"{int(h)}:{int(m):02}\"\n\n        out = sh(\"acpiconf -i 0\")\n        fields = {x.split('\\t')[0]: x.split('\\t')[-1] for x in out.split(\"\\n\")}\n        metrics = psutil.sensors_battery()\n        percent = int(fields['Remaining capacity:'].replace('%', ''))\n        remaining_time = fields['Remaining time:']\n        assert metrics.percent == percent\n        if remaining_time == 'unknown':\n            assert metrics.secsleft == psutil.POWER_TIME_UNLIMITED\n        else:\n            assert secs2hours(metrics.secsleft) == remaining_time\n\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_sensors_battery_against_sysctl(self):\n        assert psutil.sensors_battery().percent == sysctl(\n            \"hw.acpi.battery.life\"\n        )\n        assert psutil.sensors_battery().power_plugged == (\n            sysctl(\"hw.acpi.acline\") == 1\n        )\n        secsleft = psutil.sensors_battery().secsleft\n        if secsleft < 0:\n            assert sysctl(\"hw.acpi.battery.time\") == -1\n        else:\n            assert secsleft == sysctl(\"hw.acpi.battery.time\") * 60\n\n    @pytest.mark.skipif(HAS_BATTERY, reason=\"has battery\")\n    def test_sensors_battery_no_battery(self):\n        # If no battery is present one of these calls is supposed\n        # to fail, see:\n        # https://github.com/giampaolo/psutil/issues/1074\n        with pytest.raises(RuntimeError):\n            sysctl(\"hw.acpi.battery.life\")\n            sysctl(\"hw.acpi.battery.time\")\n            sysctl(\"hw.acpi.acline\")\n        assert psutil.sensors_battery() is None\n\n    # --- sensors_temperatures\n\n    def test_sensors_temperatures_against_sysctl(self):\n        num_cpus = psutil.cpu_count(True)\n        for cpu in range(num_cpus):\n            sensor = f\"dev.cpu.{cpu}.temperature\"\n            # sysctl returns a string in the format 46.0C\n            try:\n                sysctl_result = int(float(sysctl(sensor)[:-1]))\n            except RuntimeError:\n                return pytest.skip(\"temperatures not supported by kernel\")\n            assert (\n                abs(\n                    psutil.sensors_temperatures()[\"coretemp\"][cpu].current\n                    - sysctl_result\n                )\n                < 10\n            )\n\n            sensor = f\"dev.cpu.{cpu}.coretemp.tjmax\"\n            sysctl_result = int(float(sysctl(sensor)[:-1]))\n            assert (\n                psutil.sensors_temperatures()[\"coretemp\"][cpu].high\n                == sysctl_result\n            )\n\n\n# =====================================================================\n# --- OpenBSD\n# =====================================================================\n\n\n@pytest.mark.skipif(not OPENBSD, reason=\"OPENBSD only\")\nclass OpenBSDTestCase(PsutilTestCase):\n    def test_boot_time(self):\n        s = sysctl('kern.boottime')\n        sys_bt = datetime.datetime.strptime(s, \"%a %b %d %H:%M:%S %Y\")\n        psutil_bt = datetime.datetime.fromtimestamp(psutil.boot_time())\n        assert sys_bt == psutil_bt\n\n\n# =====================================================================\n# --- NetBSD\n# =====================================================================\n\n\n@pytest.mark.skipif(not NETBSD, reason=\"NETBSD only\")\nclass NetBSDTestCase(PsutilTestCase):\n    @staticmethod\n    def parse_meminfo(look_for):\n        with open('/proc/meminfo') as f:\n            for line in f:\n                if line.startswith(look_for):\n                    return int(line.split()[1]) * 1024\n        raise ValueError(f\"can't find {look_for}\")\n\n    # --- virtual mem\n\n    def test_vmem_total(self):\n        assert psutil.virtual_memory().total == self.parse_meminfo(\"MemTotal:\")\n\n    def test_vmem_free(self):\n        assert (\n            abs(psutil.virtual_memory().free - self.parse_meminfo(\"MemFree:\"))\n            < TOLERANCE_SYS_MEM\n        )\n\n    def test_vmem_buffers(self):\n        assert (\n            abs(\n                psutil.virtual_memory().buffers\n                - self.parse_meminfo(\"Buffers:\")\n            )\n            < TOLERANCE_SYS_MEM\n        )\n\n    def test_vmem_shared(self):\n        assert (\n            abs(\n                psutil.virtual_memory().shared\n                - self.parse_meminfo(\"MemShared:\")\n            )\n            < TOLERANCE_SYS_MEM\n        )\n\n    def test_vmem_cached(self):\n        assert (\n            abs(psutil.virtual_memory().cached - self.parse_meminfo(\"Cached:\"))\n            < TOLERANCE_SYS_MEM\n        )\n\n    # --- swap mem\n\n    def test_swapmem_total(self):\n        assert (\n            abs(psutil.swap_memory().total - self.parse_meminfo(\"SwapTotal:\"))\n            < TOLERANCE_SYS_MEM\n        )\n\n    def test_swapmem_free(self):\n        assert (\n            abs(psutil.swap_memory().free - self.parse_meminfo(\"SwapFree:\"))\n            < TOLERANCE_SYS_MEM\n        )\n\n    def test_swapmem_used(self):\n        smem = psutil.swap_memory()\n        assert smem.used == smem.total - smem.free\n\n    # --- others\n\n    def test_cpu_stats_interrupts(self):\n        with open('/proc/stat', 'rb') as f:\n            for line in f:\n                if line.startswith(b'intr'):\n                    interrupts = int(line.split()[1])\n                    break\n            else:\n                raise ValueError(\"couldn't find line\")\n        assert abs(psutil.cpu_stats().interrupts - interrupts) < 1000\n\n    def test_cpu_stats_ctx_switches(self):\n        with open('/proc/stat', 'rb') as f:\n            for line in f:\n                if line.startswith(b'ctxt'):\n                    ctx_switches = int(line.split()[1])\n                    break\n            else:\n                raise ValueError(\"couldn't find line\")\n        assert abs(psutil.cpu_stats().ctx_switches - ctx_switches) < 1000\n"
  },
  {
    "path": "tests/test_connections.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Tests for psutil.net_connections() and Process.net_connections() APIs.\"\"\"\n\nimport os\nimport socket\nimport textwrap\nfrom contextlib import closing\nfrom socket import AF_INET\nfrom socket import AF_INET6\nfrom socket import SOCK_DGRAM\nfrom socket import SOCK_STREAM\n\nimport psutil\nfrom psutil import FREEBSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import SUNOS\nfrom psutil import WINDOWS\nfrom psutil._common import supports_ipv6\n\nfrom . import AF_UNIX\nfrom . import HAS_NET_CONNECTIONS_UNIX\nfrom . import ROOT_DIR\nfrom . import SKIP_SYSCONS\nfrom . import PsutilTestCase\nfrom . import bind_socket\nfrom . import bind_unix_socket\nfrom . import check_connection_ntuple\nfrom . import create_sockets\nfrom . import filter_proc_net_connections\nfrom . import pytest\nfrom . import reap_children\nfrom . import retry_on_failure\nfrom . import skip_on_access_denied\nfrom . import tcp_socketpair\nfrom . import unix_socketpair\nfrom . import wait_for_file\n\nSOCK_SEQPACKET = getattr(socket, \"SOCK_SEQPACKET\", object())\n\n\ndef this_proc_net_connections(kind):\n    cons = psutil.Process().net_connections(kind=kind)\n    if kind in {\"all\", \"unix\"}:\n        return filter_proc_net_connections(cons)\n    return cons\n\n\n@pytest.mark.xdist_group(name=\"serial\")\nclass ConnectionTestCase(PsutilTestCase):\n    def setUp(self):\n        assert this_proc_net_connections(kind='all') == []\n\n    def tearDown(self):\n        # Make sure we closed all resources.\n        assert this_proc_net_connections(kind='all') == []\n\n    def compare_procsys_connections(self, pid, proc_cons, kind='all'):\n        \"\"\"Given a process PID and its list of connections compare\n        those against system-wide connections retrieved via\n        psutil.net_connections.\n        \"\"\"\n        try:\n            sys_cons = psutil.net_connections(kind=kind)\n        except psutil.AccessDenied:\n            # On MACOS, system-wide connections are retrieved by iterating\n            # over all processes\n            if MACOS:\n                return\n            else:\n                raise\n        # Filter for this proc PID and exlucde PIDs from the tuple.\n        sys_cons = [c[:-1] for c in sys_cons if c.pid == pid]\n        sys_cons.sort()\n        proc_cons.sort()\n        assert proc_cons == sys_cons\n\n\nclass TestBasicOperations(ConnectionTestCase):\n    @pytest.mark.skipif(SKIP_SYSCONS, reason=\"requires root\")\n    def test_system(self):\n        with create_sockets():\n            for conn in psutil.net_connections(kind='all'):\n                check_connection_ntuple(conn)\n\n    def test_process(self):\n        with create_sockets():\n            for conn in this_proc_net_connections(kind='all'):\n                check_connection_ntuple(conn)\n\n    def test_invalid_kind(self):\n        with pytest.raises(ValueError):\n            this_proc_net_connections(kind='???')\n        with pytest.raises(ValueError):\n            psutil.net_connections(kind='???')\n\n\n@pytest.mark.xdist_group(name=\"serial\")\nclass TestUnconnectedSockets(ConnectionTestCase):\n    \"\"\"Tests sockets which are open but not connected to anything.\"\"\"\n\n    def get_conn_from_sock(self, sock):\n        cons = this_proc_net_connections(kind='all')\n        smap = {c.fd: c for c in cons}\n        if NETBSD or FREEBSD:\n            # NetBSD opens a UNIX socket to /var/log/run\n            # so there may be more connections.\n            return smap[sock.fileno()]\n        else:\n            assert len(cons) == 1\n            if cons[0].fd != -1:\n                assert smap[sock.fileno()].fd == sock.fileno()\n            return cons[0]\n\n    def check_socket(self, sock):\n        \"\"\"Given a socket, makes sure it matches the one obtained\n        via psutil. It assumes this process created one connection\n        only (the one supposed to be checked).\n        \"\"\"\n        conn = self.get_conn_from_sock(sock)\n        check_connection_ntuple(conn)\n\n        # fd, family, type\n        if conn.fd != -1:\n            assert conn.fd == sock.fileno()\n        assert conn.family == sock.family\n        # see: http://bugs.python.org/issue30204\n        assert conn.type == sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)\n\n        # local address\n        laddr = sock.getsockname()\n        if not laddr and isinstance(laddr, bytes):\n            # See: http://bugs.python.org/issue30205\n            laddr = laddr.decode()\n        if sock.family == AF_INET6:\n            laddr = laddr[:2]\n        assert conn.laddr == laddr\n\n        # XXX Solaris can't retrieve system-wide UNIX sockets\n        if sock.family == AF_UNIX and HAS_NET_CONNECTIONS_UNIX:\n            cons = this_proc_net_connections(kind='all')\n            self.compare_procsys_connections(os.getpid(), cons, kind='all')\n        return conn\n\n    def test_tcp_v4(self):\n        addr = (\"127.0.0.1\", 0)\n        with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock:\n            conn = self.check_socket(sock)\n            assert conn.raddr == ()\n            assert conn.status == psutil.CONN_LISTEN\n\n    @pytest.mark.skipif(not supports_ipv6(), reason=\"IPv6 not supported\")\n    def test_tcp_v6(self):\n        addr = (\"::1\", 0)\n        with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock:\n            conn = self.check_socket(sock)\n            assert conn.raddr == ()\n            assert conn.status == psutil.CONN_LISTEN\n\n    def test_udp_v4(self):\n        addr = (\"127.0.0.1\", 0)\n        with closing(bind_socket(AF_INET, SOCK_DGRAM, addr=addr)) as sock:\n            conn = self.check_socket(sock)\n            assert conn.raddr == ()\n            assert conn.status == psutil.CONN_NONE\n\n    @pytest.mark.skipif(not supports_ipv6(), reason=\"IPv6 not supported\")\n    def test_udp_v6(self):\n        addr = (\"::1\", 0)\n        with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock:\n            conn = self.check_socket(sock)\n            assert conn.raddr == ()\n            assert conn.status == psutil.CONN_NONE\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_unix_tcp(self):\n        testfn = self.get_testfn()\n        with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock:\n            conn = self.check_socket(sock)\n            assert conn.raddr == \"\"\n            assert conn.status == psutil.CONN_NONE\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_unix_udp(self):\n        testfn = self.get_testfn()\n        with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock:\n            conn = self.check_socket(sock)\n            assert conn.raddr == \"\"\n            assert conn.status == psutil.CONN_NONE\n\n\n@pytest.mark.xdist_group(name=\"serial\")\nclass TestConnectedSocket(ConnectionTestCase):\n    \"\"\"Test socket pairs which are actually connected to\n    each other.\n    \"\"\"\n\n    # On SunOS, even after we close() it, the server socket stays around\n    # in TIME_WAIT state.\n    @pytest.mark.skipif(SUNOS, reason=\"unreliable on SUNOS\")\n    def test_tcp(self):\n        addr = (\"127.0.0.1\", 0)\n        assert this_proc_net_connections(kind='tcp4') == []\n        server, client = tcp_socketpair(AF_INET, addr=addr)\n        try:\n            cons = this_proc_net_connections(kind='tcp4')\n            assert len(cons) == 2\n            assert cons[0].status == psutil.CONN_ESTABLISHED\n            assert cons[1].status == psutil.CONN_ESTABLISHED\n            # May not be fast enough to change state so it stays\n            # commenteed.\n            # client.close()\n            # cons = this_proc_net_connections(kind='all')\n            # assert len(cons) == 1\n            # assert cons[0].status == psutil.CONN_CLOSE_WAIT\n        finally:\n            server.close()\n            client.close()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    @pytest.mark.skipif(\n        not HAS_NET_CONNECTIONS_UNIX, reason=\"can't list UNIX sockets\"\n    )\n    def test_unix(self):\n        testfn = self.get_testfn()\n        server, client = unix_socketpair(testfn)\n        try:\n            cons = this_proc_net_connections(kind='unix')\n            assert not (cons[0].laddr and cons[0].raddr), cons\n            assert not (cons[1].laddr and cons[1].raddr), cons\n            if NETBSD or FREEBSD:\n                # On NetBSD creating a UNIX socket will cause\n                # a UNIX connection to  /var/run/log.\n                cons = [c for c in cons if c.raddr != '/var/run/log']\n            assert len(cons) == 2\n            if LINUX or FREEBSD or SUNOS or OPENBSD:\n                # remote path is never set\n                assert cons[0].raddr == \"\"\n                assert cons[1].raddr == \"\"\n                # one local address should though\n                assert testfn == (cons[0].laddr or cons[1].laddr)\n            else:\n                # On other systems either the laddr or raddr\n                # of both peers are set.\n                assert (cons[0].laddr or cons[1].laddr) == testfn\n        finally:\n            server.close()\n            client.close()\n\n\nclass TestFilters(ConnectionTestCase):\n    def test_filters(self):\n        def check(kind, families, types):\n            for conn in this_proc_net_connections(kind=kind):\n                assert conn.family in families\n                assert conn.type in types\n            if not SKIP_SYSCONS:\n                for conn in psutil.net_connections(kind=kind):\n                    assert conn.family in families\n                    assert conn.type in types\n\n        with create_sockets():\n            check(\n                'all',\n                [AF_INET, AF_INET6, AF_UNIX],\n                [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET],\n            )\n            check('inet', [AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM])\n            check('inet4', [AF_INET], [SOCK_STREAM, SOCK_DGRAM])\n            check('tcp', [AF_INET, AF_INET6], [SOCK_STREAM])\n            check('tcp4', [AF_INET], [SOCK_STREAM])\n            check('tcp6', [AF_INET6], [SOCK_STREAM])\n            check('udp', [AF_INET, AF_INET6], [SOCK_DGRAM])\n            check('udp4', [AF_INET], [SOCK_DGRAM])\n            check('udp6', [AF_INET6], [SOCK_DGRAM])\n            if HAS_NET_CONNECTIONS_UNIX:\n                check(\n                    'unix',\n                    [AF_UNIX],\n                    [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET],\n                )\n\n    @skip_on_access_denied(only_if=MACOS)\n    def test_combos(self):\n        reap_children()\n\n        def check_conn(proc, conn, family, type, laddr, raddr, status, kinds):\n            all_kinds = (\n                \"all\",\n                \"inet\",\n                \"inet4\",\n                \"inet6\",\n                \"tcp\",\n                \"tcp4\",\n                \"tcp6\",\n                \"udp\",\n                \"udp4\",\n                \"udp6\",\n            )\n            check_connection_ntuple(conn)\n            assert conn.family == family\n            assert conn.type == type\n            assert conn.laddr == laddr\n            assert conn.raddr == raddr\n            assert conn.status == status\n            for kind in all_kinds:\n                cons = proc.net_connections(kind=kind)\n                if kind in kinds:\n                    assert cons != []\n                else:\n                    assert cons == []\n            # compare against system-wide connections\n            # XXX Solaris can't retrieve system-wide UNIX\n            # sockets.\n            if HAS_NET_CONNECTIONS_UNIX:\n                self.compare_procsys_connections(proc.pid, [conn])\n\n        tcp_template = textwrap.dedent(\"\"\"\n            import socket, time\n            s = socket.socket({family}, socket.SOCK_STREAM)\n            s.bind(('{addr}', 0))\n            s.listen(5)\n            with open('{testfn}', 'w') as f:\n                f.write(str(s.getsockname()[:2]))\n            [time.sleep(0.1) for x in range(100)]\n            \"\"\")\n\n        udp_template = textwrap.dedent(\"\"\"\n            import socket, time\n            s = socket.socket({family}, socket.SOCK_DGRAM)\n            s.bind(('{addr}', 0))\n            with open('{testfn}', 'w') as f:\n                f.write(str(s.getsockname()[:2]))\n            [time.sleep(0.1) for x in range(100)]\n            \"\"\")\n\n        # must be relative on Windows\n        testfile = os.path.basename(self.get_testfn(dir=os.getcwd()))\n        tcp4_template = tcp_template.format(\n            family=int(AF_INET), addr=\"127.0.0.1\", testfn=testfile\n        )\n        udp4_template = udp_template.format(\n            family=int(AF_INET), addr=\"127.0.0.1\", testfn=testfile\n        )\n        tcp6_template = tcp_template.format(\n            family=int(AF_INET6), addr=\"::1\", testfn=testfile\n        )\n        udp6_template = udp_template.format(\n            family=int(AF_INET6), addr=\"::1\", testfn=testfile\n        )\n\n        # launch various subprocess instantiating a socket of various\n        # families and types to enrich psutil results\n        tcp4_proc = self.pyrun(tcp4_template)\n        tcp4_addr = eval(wait_for_file(testfile, delete=True))\n        udp4_proc = self.pyrun(udp4_template)\n        udp4_addr = eval(wait_for_file(testfile, delete=True))\n        if supports_ipv6():\n            tcp6_proc = self.pyrun(tcp6_template)\n            tcp6_addr = eval(wait_for_file(testfile, delete=True))\n            udp6_proc = self.pyrun(udp6_template)\n            udp6_addr = eval(wait_for_file(testfile, delete=True))\n        else:\n            tcp6_proc = None\n            udp6_proc = None\n            tcp6_addr = None\n            udp6_addr = None\n\n        for p in psutil.Process().children():\n            cons = p.net_connections()\n            assert len(cons) == 1\n            for conn in cons:\n                # TCP v4\n                if p.pid == tcp4_proc.pid:\n                    check_conn(\n                        p,\n                        conn,\n                        AF_INET,\n                        SOCK_STREAM,\n                        tcp4_addr,\n                        (),\n                        psutil.CONN_LISTEN,\n                        (\"all\", \"inet\", \"inet4\", \"tcp\", \"tcp4\"),\n                    )\n                # UDP v4\n                elif p.pid == udp4_proc.pid:\n                    check_conn(\n                        p,\n                        conn,\n                        AF_INET,\n                        SOCK_DGRAM,\n                        udp4_addr,\n                        (),\n                        psutil.CONN_NONE,\n                        (\"all\", \"inet\", \"inet4\", \"udp\", \"udp4\"),\n                    )\n                # TCP v6\n                elif p.pid == getattr(tcp6_proc, \"pid\", None):\n                    check_conn(\n                        p,\n                        conn,\n                        AF_INET6,\n                        SOCK_STREAM,\n                        tcp6_addr,\n                        (),\n                        psutil.CONN_LISTEN,\n                        (\"all\", \"inet\", \"inet6\", \"tcp\", \"tcp6\"),\n                    )\n                # UDP v6\n                elif p.pid == getattr(udp6_proc, \"pid\", None):\n                    check_conn(\n                        p,\n                        conn,\n                        AF_INET6,\n                        SOCK_DGRAM,\n                        udp6_addr,\n                        (),\n                        psutil.CONN_NONE,\n                        (\"all\", \"inet\", \"inet6\", \"udp\", \"udp6\"),\n                    )\n\n    def test_count(self):\n        with create_sockets():\n            # tcp\n            cons = this_proc_net_connections(kind='tcp')\n            assert len(cons) == (2 if supports_ipv6() else 1)\n            for conn in cons:\n                assert conn.family in {AF_INET, AF_INET6}\n                assert conn.type == SOCK_STREAM\n            # tcp4\n            cons = this_proc_net_connections(kind='tcp4')\n            assert len(cons) == 1\n            assert cons[0].family == AF_INET\n            assert cons[0].type == SOCK_STREAM\n            # tcp6\n            if supports_ipv6():\n                cons = this_proc_net_connections(kind='tcp6')\n                assert len(cons) == 1\n                assert cons[0].family == AF_INET6\n                assert cons[0].type == SOCK_STREAM\n            # udp\n            cons = this_proc_net_connections(kind='udp')\n            assert len(cons) == (2 if supports_ipv6() else 1)\n            for conn in cons:\n                assert conn.family in {AF_INET, AF_INET6}\n                assert conn.type == SOCK_DGRAM\n            # udp4\n            cons = this_proc_net_connections(kind='udp4')\n            assert len(cons) == 1\n            assert cons[0].family == AF_INET\n            assert cons[0].type == SOCK_DGRAM\n            # udp6\n            if supports_ipv6():\n                cons = this_proc_net_connections(kind='udp6')\n                assert len(cons) == 1\n                assert cons[0].family == AF_INET6\n                assert cons[0].type == SOCK_DGRAM\n            # inet\n            cons = this_proc_net_connections(kind='inet')\n            assert len(cons) == (4 if supports_ipv6() else 2)\n            for conn in cons:\n                assert conn.family in {AF_INET, AF_INET6}\n                assert conn.type in {SOCK_STREAM, SOCK_DGRAM}\n            # inet6\n            if supports_ipv6():\n                cons = this_proc_net_connections(kind='inet6')\n                assert len(cons) == 2\n                for conn in cons:\n                    assert conn.family == AF_INET6\n                    assert conn.type in {SOCK_STREAM, SOCK_DGRAM}\n            # Skipped on BSD becayse by default the Python process\n            # creates a UNIX socket to '/var/run/log'.\n            if HAS_NET_CONNECTIONS_UNIX and not (FREEBSD or NETBSD):\n                cons = this_proc_net_connections(kind='unix')\n                assert len(cons) == 3\n                for conn in cons:\n                    assert conn.family == AF_UNIX\n                    assert conn.type in {SOCK_STREAM, SOCK_DGRAM}\n\n\n@pytest.mark.skipif(SKIP_SYSCONS, reason=\"requires root\")\nclass TestSystemWideConnections(ConnectionTestCase):\n    \"\"\"Tests for net_connections().\"\"\"\n\n    def test_it(self):\n        def check(cons, families, types_):\n            for conn in cons:\n                assert conn.family in families\n                if conn.family != AF_UNIX:\n                    assert conn.type in types_\n                check_connection_ntuple(conn)\n\n        with create_sockets():\n            from psutil._common import conn_tmap\n\n            for kind, groups in conn_tmap.items():\n                # XXX: SunOS does not retrieve UNIX sockets.\n                if kind == 'unix' and not HAS_NET_CONNECTIONS_UNIX:\n                    continue\n                families, types_ = groups\n                cons = psutil.net_connections(kind)\n                assert len(cons) == len(set(cons))\n                check(cons, families, types_)\n\n    @retry_on_failure()\n    def test_multi_sockets_procs(self):\n        # Creates multiple sub processes, each creating different\n        # sockets. For each process check that proc.net_connections()\n        # and psutil.net_connections() return the same results.\n        # This is done mainly to check whether net_connections()'s\n        # pid is properly set, see:\n        # https://github.com/giampaolo/psutil/issues/1013\n        with create_sockets() as socks:\n            expected = len(socks)\n        pids = []\n        times = 10\n        fnames = []\n        for _ in range(times):\n            fname = self.get_testfn()\n            fnames.append(fname)\n            src = textwrap.dedent(f\"\"\"\\\n                import time, os, sys\n                if 'CIBUILDWHEEL' not in os.environ:\n                    sys.path.insert(0, r'{ROOT_DIR}')\n                from tests import create_sockets\n                with create_sockets():\n                    with open(r'{fname}', 'w') as f:\n                        f.write(\"hello\")\n                    [time.sleep(0.1) for x in range(100)]\n                \"\"\")\n            sproc = self.pyrun(src)\n            pids.append(sproc.pid)\n\n        # sync\n        for fname in fnames:\n            wait_for_file(fname)\n\n        syscons = [\n            x for x in psutil.net_connections(kind='all') if x.pid in pids\n        ]\n        for pid in pids:\n            assert len([x for x in syscons if x.pid == pid]) == expected\n            p = psutil.Process(pid)\n            assert len(p.net_connections('all')) == expected\n\n\nclass TestMisc(PsutilTestCase):\n    def test_net_connection_constants(self):\n        ints = []\n        strs = []\n        for name in dir(psutil):\n            if name.startswith('CONN_'):\n                num = getattr(psutil, name)\n                str_ = str(num)\n                assert str_.isupper(), str_\n                assert str not in strs\n                assert num not in ints\n                ints.append(num)\n                strs.append(str_)\n        if SUNOS:\n            psutil.CONN_IDLE  # noqa: B018\n            psutil.CONN_BOUND  # noqa: B018\n        if WINDOWS:\n            psutil.CONN_DELETE_TCB  # noqa: B018\n"
  },
  {
    "path": "tests/test_contracts.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Contracts tests. These tests mainly check API sanity in terms of\nreturned types and APIs availability.\nSome of these are duplicates of tests test_system.py and test_process.py.\n\"\"\"\n\nimport platform\n\nimport psutil\nfrom psutil import AIX\nfrom psutil import BSD\nfrom psutil import FREEBSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import SUNOS\nfrom psutil import WINDOWS\nfrom psutil import BatteryTime\nfrom psutil import ConnectionStatus\nfrom psutil import NicDuplex\nfrom psutil import ProcessStatus\n\nfrom . import AARCH64\nfrom . import GITHUB_ACTIONS\nfrom . import HAS_CPU_FREQ\nfrom . import HAS_NET_IO_COUNTERS\nfrom . import HAS_SENSORS_FANS\nfrom . import HAS_SENSORS_TEMPERATURES\nfrom . import SKIP_SYSCONS\nfrom . import PsutilTestCase\nfrom . import create_sockets\nfrom . import enum\nfrom . import is_namedtuple\nfrom . import kernel_version\nfrom . import pytest\n\n# ===================================================================\n# --- APIs availability\n# ===================================================================\n\n# Make sure code reflects what doc promises in terms of APIs\n# availability.\n\n\nclass TestAvailConstantsAPIs(PsutilTestCase):\n\n    def check_constants(self, names, are_avail):\n        for name in names:\n            with self.subTest(name=name):\n                # assert CONSTANT is/isn't in psutil namespace\n                assert hasattr(psutil, name) == are_avail\n                # assert CONSTANT is/isn't in psutil.__all__\n                if are_avail:\n                    assert name in psutil.__all__\n                else:\n                    assert name not in psutil.__all__\n\n    def test_PROCFS_PATH(self):\n        self.check_constants((\"PROCFS_PATH\",), LINUX or SUNOS or AIX)\n\n    def test_proc_status(self):\n        names = (\n            \"STATUS_RUNNING\",\n            \"STATUS_SLEEPING\",\n            \"STATUS_DISK_SLEEP\",\n            \"STATUS_STOPPED\",\n            \"STATUS_TRACING_STOP\",\n            \"STATUS_ZOMBIE\",\n            \"STATUS_DEAD\",\n            \"STATUS_WAKE_KILL\",\n            \"STATUS_WAKING\",\n            \"STATUS_IDLE\",\n            \"STATUS_LOCKED\",\n            \"STATUS_WAITING\",\n            \"STATUS_SUSPENDED\",\n            \"STATUS_PARKED\",\n        )\n        self.check_constants(names, True)\n        assert sorted(ProcessStatus.__members__.keys()) == sorted(names)\n\n    def test_proc_status_strenum(self):\n        mapping = (\n            (psutil.STATUS_RUNNING, \"running\"),\n            (psutil.STATUS_SLEEPING, \"sleeping\"),\n            (psutil.STATUS_DISK_SLEEP, \"disk-sleep\"),\n            (psutil.STATUS_STOPPED, \"stopped\"),\n            (psutil.STATUS_TRACING_STOP, \"tracing-stop\"),\n            (psutil.STATUS_ZOMBIE, \"zombie\"),\n            (psutil.STATUS_DEAD, \"dead\"),\n            (psutil.STATUS_WAKE_KILL, \"wake-kill\"),\n            (psutil.STATUS_WAKING, \"waking\"),\n            (psutil.STATUS_IDLE, \"idle\"),\n            (psutil.STATUS_LOCKED, \"locked\"),\n            (psutil.STATUS_WAITING, \"waiting\"),\n            (psutil.STATUS_SUSPENDED, \"suspended\"),\n            (psutil.STATUS_PARKED, \"parked\"),\n        )\n        for en, str_ in mapping:\n            assert en == str_\n            assert str(en) == str_\n            assert repr(en) != str_\n\n    def test_conn_status(self):\n        names = [\n            \"CONN_ESTABLISHED\",\n            \"CONN_SYN_SENT\",\n            \"CONN_SYN_RECV\",\n            \"CONN_FIN_WAIT1\",\n            \"CONN_FIN_WAIT2\",\n            \"CONN_TIME_WAIT\",\n            \"CONN_CLOSE\",\n            \"CONN_CLOSE_WAIT\",\n            \"CONN_LAST_ACK\",\n            \"CONN_LISTEN\",\n            \"CONN_CLOSING\",\n            \"CONN_NONE\",\n        ]\n        if WINDOWS:\n            names.append(\"CONN_DELETE_TCB\")\n        if SUNOS:\n            names.extend([\"CONN_IDLE\", \"CONN_BOUND\"])\n\n        self.check_constants(names, True)\n        assert sorted(ConnectionStatus.__members__.keys()) == sorted(names)\n\n    def test_conn_status_strenum(self):\n        mapping = (\n            (psutil.CONN_ESTABLISHED, \"ESTABLISHED\"),\n            (psutil.CONN_SYN_SENT, \"SYN_SENT\"),\n            (psutil.CONN_SYN_RECV, \"SYN_RECV\"),\n            (psutil.CONN_FIN_WAIT1, \"FIN_WAIT1\"),\n            (psutil.CONN_FIN_WAIT2, \"FIN_WAIT2\"),\n            (psutil.CONN_TIME_WAIT, \"TIME_WAIT\"),\n            (psutil.CONN_CLOSE, \"CLOSE\"),\n            (psutil.CONN_CLOSE_WAIT, \"CLOSE_WAIT\"),\n            (psutil.CONN_LAST_ACK, \"LAST_ACK\"),\n            (psutil.CONN_LISTEN, \"LISTEN\"),\n            (psutil.CONN_CLOSING, \"CLOSING\"),\n            (psutil.CONN_NONE, \"NONE\"),\n        )\n        for en, str_ in mapping:\n            assert en == str_\n            assert str(en) == str_\n            assert repr(en) != str_\n\n    def test_nic_duplex(self):\n        names = (\"NIC_DUPLEX_FULL\", \"NIC_DUPLEX_HALF\", \"NIC_DUPLEX_UNKNOWN\")\n        self.check_constants(names, True)\n        assert sorted(NicDuplex.__members__.keys()) == sorted(names)\n\n    def test_battery_time(self):\n        names = (\"POWER_TIME_UNKNOWN\", \"POWER_TIME_UNLIMITED\")\n        self.check_constants(names, True)\n        assert sorted(BatteryTime.__members__.keys()) == sorted(names)\n\n    def test_proc_ioprio_class_linux(self):\n        names = (\n            \"IOPRIO_CLASS_NONE\",\n            \"IOPRIO_CLASS_RT\",\n            \"IOPRIO_CLASS_BE\",\n            \"IOPRIO_CLASS_IDLE\",\n        )\n        self.check_constants(names, LINUX)\n        if LINUX:\n            assert sorted(\n                psutil.ProcessIOPriority.__members__.keys()\n            ) == sorted(names)\n        else:\n            not hasattr(psutil, \"ProcessIOPriority\")\n\n    def test_proc_ioprio_value_windows(self):\n        names = (\n            \"IOPRIO_HIGH\",\n            \"IOPRIO_NORMAL\",\n            \"IOPRIO_LOW\",\n            \"IOPRIO_VERYLOW\",\n        )\n        self.check_constants(names, WINDOWS)\n        if WINDOWS:\n            assert sorted(\n                psutil.ProcessIOPriority.__members__.keys()\n            ) == sorted(names)\n\n    def test_proc_priority_windows(self):\n        names = (\n            \"ABOVE_NORMAL_PRIORITY_CLASS\",\n            \"BELOW_NORMAL_PRIORITY_CLASS\",\n            \"HIGH_PRIORITY_CLASS\",\n            \"IDLE_PRIORITY_CLASS\",\n            \"NORMAL_PRIORITY_CLASS\",\n            \"REALTIME_PRIORITY_CLASS\",\n        )\n        self.check_constants(names, WINDOWS)\n        if WINDOWS:\n            assert sorted(psutil.ProcessPriority.__members__.keys()) == sorted(\n                names\n            )\n        else:\n            not hasattr(psutil, \"ProcessPriority\")\n\n    @pytest.mark.skipif(\n        GITHUB_ACTIONS and LINUX,\n        reason=\"unsupported on GITHUB_ACTIONS + LINUX\",\n    )\n    def test_rlimit(self):\n        names = (\n            \"RLIM_INFINITY\",\n            \"RLIMIT_AS\",\n            \"RLIMIT_CORE\",\n            \"RLIMIT_CPU\",\n            \"RLIMIT_DATA\",\n            \"RLIMIT_FSIZE\",\n            \"RLIMIT_MEMLOCK\",\n            \"RLIMIT_NOFILE\",\n            \"RLIMIT_NPROC\",\n            \"RLIMIT_RSS\",\n            \"RLIMIT_STACK\",\n        )\n        self.check_constants(names, LINUX or FREEBSD)\n        self.check_constants((\"RLIMIT_LOCKS\",), LINUX)\n        self.check_constants(\n            (\"RLIMIT_SWAP\", \"RLIMIT_SBSIZE\", \"RLIMIT_NPTS\"), FREEBSD\n        )\n\n        if POSIX:\n            if kernel_version() >= (2, 6, 8):\n                self.check_constants((\"RLIMIT_MSGQUEUE\",), LINUX)\n            if kernel_version() >= (2, 6, 12):\n                self.check_constants((\"RLIMIT_NICE\", \"RLIMIT_RTPRIO\"), LINUX)\n            if kernel_version() >= (2, 6, 25):\n                self.check_constants((\"RLIMIT_RTTIME\",), LINUX)\n            if kernel_version() >= (2, 6, 8):\n                self.check_constants((\"RLIMIT_SIGPENDING\",), LINUX)\n\n    def test_enum_containers(self):\n        self.check_constants((\"ProcessStatus\",), True)\n        self.check_constants((\"ProcessPriority\",), WINDOWS)\n        self.check_constants((\"ProcessIOPriority\",), LINUX or WINDOWS)\n        self.check_constants((\"ConnectionStatus\",), True)\n        self.check_constants((\"NicDuplex\",), True)\n        self.check_constants((\"BatteryTime\",), True)\n\n\nclass TestAvailSystemAPIs(PsutilTestCase):\n    def test_win_service_iter(self):\n        assert hasattr(psutil, \"win_service_iter\") == WINDOWS\n\n    def test_win_service_get(self):\n        assert hasattr(psutil, \"win_service_get\") == WINDOWS\n\n    @pytest.mark.skipif(\n        MACOS and AARCH64 and not HAS_CPU_FREQ, reason=\"not supported\"\n    )\n    def test_cpu_freq(self):\n        assert hasattr(psutil, \"cpu_freq\") == (\n            LINUX or MACOS or WINDOWS or FREEBSD or OPENBSD\n        )\n\n    def test_sensors_temperatures(self):\n        assert hasattr(psutil, \"sensors_temperatures\") == (LINUX or FREEBSD)\n\n    def test_sensors_fans(self):\n        assert hasattr(psutil, \"sensors_fans\") == LINUX\n\n    def test_battery(self):\n        assert hasattr(psutil, \"sensors_battery\") == (\n            LINUX or WINDOWS or FREEBSD or MACOS\n        )\n\n    def test_heap_info(self):\n        hasit = hasattr(psutil, \"heap_info\")\n        if LINUX:\n            assert hasit == bool(platform.libc_ver() != (\"\", \"\"))\n        else:\n            assert hasit == MACOS or WINDOWS or BSD\n\n    def test_heap_trim(self):\n        hasit = hasattr(psutil, \"heap_trim\")\n        if LINUX:\n            assert hasit == bool(platform.libc_ver() != (\"\", \"\"))\n        else:\n            assert hasit == MACOS or WINDOWS or BSD\n\n\nclass TestAvailProcessAPIs(PsutilTestCase):\n    def test_environ(self):\n        assert hasattr(psutil.Process, \"environ\") == (\n            LINUX\n            or MACOS\n            or WINDOWS\n            or AIX\n            or SUNOS\n            or FREEBSD\n            or OPENBSD\n            or NETBSD\n        )\n\n    def test_uids(self):\n        assert hasattr(psutil.Process, \"uids\") == POSIX\n\n    def test_gids(self):\n        assert hasattr(psutil.Process, \"uids\") == POSIX\n\n    def test_terminal(self):\n        assert hasattr(psutil.Process, \"terminal\") == POSIX\n\n    def test_ionice(self):\n        assert hasattr(psutil.Process, \"ionice\") == (LINUX or WINDOWS)\n\n    @pytest.mark.skipif(\n        GITHUB_ACTIONS and LINUX,\n        reason=\"unsupported on GITHUB_ACTIONS + LINUX\",\n    )\n    def test_rlimit(self):\n        assert hasattr(psutil.Process, \"rlimit\") == (LINUX or FREEBSD)\n\n    def test_io_counters(self):\n        hasit = hasattr(psutil.Process, \"io_counters\")\n        assert hasit == (not (MACOS or SUNOS))\n\n    def test_num_fds(self):\n        assert hasattr(psutil.Process, \"num_fds\") == POSIX\n\n    def test_num_handles(self):\n        assert hasattr(psutil.Process, \"num_handles\") == WINDOWS\n\n    def test_cpu_affinity(self):\n        assert hasattr(psutil.Process, \"cpu_affinity\") == (\n            LINUX or WINDOWS or FREEBSD\n        )\n\n    def test_cpu_num(self):\n        assert hasattr(psutil.Process, \"cpu_num\") == (\n            LINUX or FREEBSD or SUNOS\n        )\n\n    def test_memory_maps(self):\n        hasit = hasattr(psutil.Process, \"memory_maps\")\n        assert hasit == (not (OPENBSD or NETBSD or AIX or MACOS))\n\n    def test_memory_footprint(self):\n        hasit = hasattr(psutil.Process, \"memory_footprint\")\n        assert hasit == (LINUX or MACOS or WINDOWS)\n\n\n# ===================================================================\n# --- API types\n# ===================================================================\n\n\nclass TestSystemAPITypes(PsutilTestCase):\n    \"\"\"Check the return types of system related APIs.\n    https://github.com/giampaolo/psutil/issues/1039.\n    \"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        cls.proc = psutil.Process()\n\n    def assert_ntuple_of_nums(self, nt, type_=float, gezero=True):\n        assert is_namedtuple(nt)\n        for n in nt:\n            assert isinstance(n, type_)\n            if gezero:\n                assert n >= 0\n\n    def test_cpu_times(self):\n        self.assert_ntuple_of_nums(psutil.cpu_times())\n        for nt in psutil.cpu_times(percpu=True):\n            self.assert_ntuple_of_nums(nt)\n\n    def test_cpu_percent(self):\n        assert isinstance(psutil.cpu_percent(interval=None), float)\n        assert isinstance(psutil.cpu_percent(interval=0.00001), float)\n\n    def test_cpu_times_percent(self):\n        self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=None))\n        self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=0.0001))\n\n    def test_cpu_count(self):\n        assert isinstance(psutil.cpu_count(), int)\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    def test_cpu_freq(self):\n        if psutil.cpu_freq() is None:\n            return pytest.skip(\"cpu_freq() returns None\")\n        self.assert_ntuple_of_nums(psutil.cpu_freq(), type_=(float, int))\n\n    def test_disk_io_counters(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for k, v in psutil.disk_io_counters(perdisk=True).items():\n            assert isinstance(k, str)\n            self.assert_ntuple_of_nums(v, type_=int)\n\n    def test_disk_partitions(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for disk in psutil.disk_partitions():\n            assert isinstance(disk.device, str)\n            assert isinstance(disk.mountpoint, str)\n            assert isinstance(disk.fstype, str)\n            assert isinstance(disk.opts, str)\n\n    @pytest.mark.skipif(SKIP_SYSCONS, reason=\"requires root\")\n    def test_net_connections(self):\n        with create_sockets():\n            ret = psutil.net_connections('all')\n            assert len(ret) == len(set(ret))\n            for conn in ret:\n                assert is_namedtuple(conn)\n\n    def test_net_if_addrs(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for ifname, addrs in psutil.net_if_addrs().items():\n            assert isinstance(ifname, str)\n            for addr in addrs:\n                assert isinstance(addr.family, enum.IntEnum)\n                assert isinstance(addr.address, str)\n                assert isinstance(addr.netmask, (str, type(None)))\n                assert isinstance(addr.broadcast, (str, type(None)))\n\n    def test_net_if_stats(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for ifname, info in psutil.net_if_stats().items():\n            assert isinstance(ifname, str)\n            assert isinstance(info.isup, bool)\n            assert isinstance(info.duplex, enum.IntEnum)\n            assert isinstance(info.speed, int)\n            assert isinstance(info.mtu, int)\n\n    @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason=\"not supported\")\n    def test_net_io_counters(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for ifname in psutil.net_io_counters(pernic=True):\n            assert isinstance(ifname, str)\n\n    @pytest.mark.skipif(not HAS_SENSORS_FANS, reason=\"not supported\")\n    def test_sensors_fans(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for name, units in psutil.sensors_fans().items():\n            assert isinstance(name, str)\n            for unit in units:\n                assert isinstance(unit.label, str)\n                assert isinstance(unit.current, (float, int, type(None)))\n\n    @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason=\"not supported\")\n    def test_sensors_temperatures(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for name, units in psutil.sensors_temperatures().items():\n            assert isinstance(name, str)\n            for unit in units:\n                assert isinstance(unit.label, str)\n                assert isinstance(unit.current, (float, int, type(None)))\n                assert isinstance(unit.high, (float, int, type(None)))\n                assert isinstance(unit.critical, (float, int, type(None)))\n\n    def test_boot_time(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        assert isinstance(psutil.boot_time(), float)\n\n    def test_users(self):\n        # Duplicate of test_system.py. Keep it anyway.\n        for user in psutil.users():\n            assert isinstance(user.name, str)\n            assert isinstance(user.terminal, (str, type(None)))\n            assert isinstance(user.host, (str, type(None)))\n            assert isinstance(user.pid, (int, type(None)))\n            if isinstance(user.pid, int):\n                assert user.pid > 0\n"
  },
  {
    "path": "tests/test_heap.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Tests for `psutil.heap_info()`.\n\nThis module deliberately creates **controlled memory leaks** by calling\nlow-level C allocation functions (`malloc()`, `HeapAlloc()`,\n`VirtualAllocEx()`, etc.) **without** freeing them - exactly how\nreal-world memory leaks occur in native C extensions code.\n\nBy bypassing Python's memory manager entirely (via `ctypes`), we\ndirectly exercise the underlying system allocator:\n\nUNIX\n\n- Small `malloc()` allocations (≤ 128KB on glibc) without `free()`\n  increase `heap_used`.\n- Large `malloc()` allocations  without `free()` trigger `mmap()` and\n  increase `mmap_used`.\n    - Note: direct `mmap()` / `munmap()` via `ctypes` was attempted but\n      proved unreliable.\n\nWindows\n\n- `HeapAlloc()` without `HeapFree()` increases `heap_used`.\n- `VirtualAllocEx()` without `VirtualFreeEx()` increases `mmap_used`.\n- `HeapCreate()` without `HeapDestroy()` increases `heap_count`.\n\nThese tests ensure that `psutil.heap_info()` detects unreleased\nnative memory across different allocators (glibc on Linux,\njemalloc on BSD/macOS, Windows CRT).\n\"\"\"\n\nimport ctypes\nimport gc\n\nimport pytest\n\nimport psutil\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import POSIX\nfrom psutil import WINDOWS\n\nfrom . import HAS_HEAP_INFO\nfrom . import PsutilTestCase\nfrom . import retry_on_failure\n\n# Small allocation (64 KiB), below M_MMAP_THRESHOLD (128 KiB).\n# Increases heap_used (uordblks) without triggering mmap().\nHEAP_SIZE = 64 * 1024\n# Large allocation (64 MiB), exceeds DEFAULT_MMAP_THRESHOLD_MAX (32\n# MiB). Forces malloc() to use mmap() internally and increases\n# mmap_used (hblkhd). See `man mallopt`.\nMMAP_SIZE = 64 * 1024 * 1024\n\n\n# =====================================================================\n# --- Utils\n# =====================================================================\n\n\nif POSIX:  # noqa: SIM108\n    libc = ctypes.CDLL(None)\nelse:\n    libc = ctypes.CDLL(\"msvcrt.dll\")\n\n\ndef malloc(size):\n    \"\"\"Allocate memory via malloc(). If passed a small size, usually\n    affects heap_used, else mmap_used (not on Windows).\n    \"\"\"\n    fun = libc.malloc\n    fun.argtypes = [ctypes.c_size_t]\n    fun.restype = ctypes.c_void_p\n    ptr = fun(size)\n    assert ptr, \"malloc() failed\"\n    return ptr\n\n\ndef free(ptr):\n    \"\"\"Free malloc() memory.\"\"\"\n    fun = libc.free\n    fun.argtypes = [ctypes.c_void_p]\n    fun.restype = None\n    fun(ptr)\n\n\nif WINDOWS:\n    from ctypes import wintypes\n\n    import win32api\n    import win32con\n    import win32process\n\n    kernel32 = ctypes.windll.kernel32\n    HEAP_NO_SERIALIZE = 0x00000001\n\n    # --- for `heap_used`\n\n    def GetProcessHeap():\n        fun = kernel32.GetProcessHeap\n        fun.argtypes = []\n        fun.restype = wintypes.HANDLE\n        heap = fun()\n        assert heap != 0, \"GetProcessHeap failed\"\n        return heap\n\n    def HeapAlloc(heap, size):\n        fun = kernel32.HeapAlloc\n        fun.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.c_size_t]\n        fun.restype = ctypes.c_void_p\n        addr = fun(heap, 0, size)\n        assert addr, \"HeapAlloc failed\"\n        return addr\n\n    def HeapFree(heap, addr):\n        fun = kernel32.HeapFree\n        fun.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.c_void_p]\n        fun.restype = wintypes.BOOL\n        assert fun(heap, 0, addr) != 0, \"HeapFree failed\"\n\n    # --- for `mmap_used`\n\n    def VirtualAllocEx(size):\n        return win32process.VirtualAllocEx(\n            win32api.GetCurrentProcess(),\n            0,\n            size,\n            win32con.MEM_COMMIT | win32con.MEM_RESERVE,\n            win32con.PAGE_READWRITE,\n        )\n\n    def VirtualFreeEx(addr):\n        win32process.VirtualFreeEx(\n            win32api.GetCurrentProcess(), addr, 0, win32con.MEM_RELEASE\n        )\n\n    # --- for `heap_count`\n\n    def HeapCreate(initial_size, max_size):\n        fun = kernel32.HeapCreate\n        fun.argtypes = [\n            wintypes.DWORD,\n            ctypes.c_size_t,\n            ctypes.c_size_t,\n        ]\n        fun.restype = wintypes.HANDLE\n        heap = fun(HEAP_NO_SERIALIZE, initial_size, max_size)\n        assert heap != 0, \"HeapCreate failed\"\n        return heap\n\n    def HeapDestroy(heap):\n        fun = kernel32.HeapDestroy\n        fun.argtypes = [wintypes.HANDLE]\n        fun.restype = wintypes.BOOL\n        assert fun(heap) != 0, \"HeapDestroy failed\"\n\n\n# =====================================================================\n# --- Tests\n# =====================================================================\n\n\ndef trim_memory():\n    gc.collect()\n    psutil.heap_trim()\n\n\ndef assert_within_percent(actual, expected, percent):\n    \"\"\"Assert that `actual` is within `percent` tolerance of `expected`.\"\"\"\n    lower = expected * (1 - percent / 100)\n    upper = expected * (1 + percent / 100)\n    if not (lower <= actual <= upper):\n        raise AssertionError(\n            f\"{actual} is not within {percent}% tolerance of expected\"\n            f\" {expected} (allowed range: {lower} - {upper})\"\n        )\n\n\n@pytest.mark.skipif(not HAS_HEAP_INFO, reason=\"heap_info() not supported\")\nclass HeapTestCase(PsutilTestCase):\n    def setUp(self):\n        trim_memory()\n\n    @classmethod\n    def tearDownClass(cls):\n        trim_memory()\n\n\nclass TestHeap(HeapTestCase):\n\n    # On Windows malloc() increases mmap_used\n    @pytest.mark.skipif(WINDOWS, reason=\"not on WINDOWS\")\n    @retry_on_failure()\n    def test_heap_used(self):\n        \"\"\"Test that a small malloc() allocation without free()\n        increases heap_used.\n        \"\"\"\n        size = HEAP_SIZE\n\n        mem1 = psutil.heap_info()\n        ptr = malloc(size)\n        mem2 = psutil.heap_info()\n\n        try:\n            # heap_used should increase (roughly) by the requested size\n            diff = mem2.heap_used - mem1.heap_used\n            assert diff > 0\n            assert_within_percent(diff, size, percent=10)\n\n            # mmap_used should not increase for small allocations, but\n            # sometimes it does.\n            diff = mem2.mmap_used - mem1.mmap_used\n            if diff != 0:\n                assert diff > 0\n                assert_within_percent(diff, size, percent=10)\n        finally:\n            free(ptr)\n\n        # assert we returned close to the baseline (mem1) after free()\n        trim_memory()\n        mem3 = psutil.heap_info()\n        assert_within_percent(mem3.heap_used, mem1.heap_used, percent=10)\n        assert_within_percent(mem3.mmap_used, mem1.mmap_used, percent=10)\n\n    @pytest.mark.skipif(MACOS, reason=\"not supported on MACOS\")\n    @retry_on_failure()\n    def test_mmap_used(self):\n        \"\"\"Test that a large malloc allocation increases mmap_used.\n        NOTE: `mmap()` / `munmap()` via ctypes proved to be unreliable.\n        \"\"\"\n        size = MMAP_SIZE\n\n        mem1 = psutil.heap_info()\n        ptr = malloc(size)\n        mem2 = psutil.heap_info()\n\n        try:\n            # mmap_used should increase (roughly) by the requested size\n            diff = mem2.mmap_used - mem1.mmap_used\n            assert diff > 0\n            assert_within_percent(diff, size, percent=10)\n\n            diff = mem2.heap_used - mem1.heap_used\n            if diff != 0:\n                if LINUX:\n                    # heap_used should not increase significantly\n                    assert diff >= 0\n                    assert_within_percent(diff, 0, percent=5)\n                else:\n                    # On BSD jemalloc allocates big memory both into\n                    # heap_used and mmap_used.\n                    assert_within_percent(diff, size, percent=10)\n\n        finally:\n            free(ptr)\n\n        # assert we returned close to the baseline (mem1) after free()\n        trim_memory()\n        mem3 = psutil.heap_info()\n        assert_within_percent(mem3.heap_used, mem1.heap_used, percent=10)\n        assert_within_percent(mem3.mmap_used, mem1.mmap_used, percent=10)\n\n        if WINDOWS:\n            assert mem1.heap_count == mem2.heap_count == mem3.heap_count\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n@pytest.mark.xdist_group(name=\"serial\")\nclass TestHeapWindows(HeapTestCase):\n\n    @retry_on_failure()\n    def test_heap_used(self):\n        \"\"\"Test that HeapAlloc() without HeapFree() increases heap_used.\"\"\"\n        size = HEAP_SIZE\n\n        mem1 = psutil.heap_info()\n        heap = GetProcessHeap()\n        addr = HeapAlloc(heap, size)\n        mem2 = psutil.heap_info()\n\n        try:\n            assert mem2.heap_used - mem1.heap_used == size\n        finally:\n            HeapFree(heap, addr)\n\n        trim_memory()\n        mem3 = psutil.heap_info()\n        assert mem3.heap_used == mem1.heap_used\n\n    @retry_on_failure()\n    def test_mmap_used(self):\n        \"\"\"Test that VirtualAllocEx() without VirtualFreeEx() increases\n        mmap_used.\n        \"\"\"\n        size = MMAP_SIZE\n\n        mem1 = psutil.heap_info()\n        addr = VirtualAllocEx(size)\n        mem2 = psutil.heap_info()\n\n        try:\n            assert mem2.mmap_used - mem1.mmap_used == size\n        finally:\n            VirtualFreeEx(addr)\n\n        trim_memory()\n        mem3 = psutil.heap_info()\n        assert mem3.mmap_used == mem1.mmap_used\n\n    @retry_on_failure()\n    def test_heap_count(self):\n        \"\"\"Test that HeapCreate() without HeapDestroy() increases\n        heap_count.\n        \"\"\"\n        mem1 = psutil.heap_info()\n        heap = HeapCreate(HEAP_SIZE, 0)\n        mem2 = psutil.heap_info()\n        try:\n            assert mem2.heap_count == mem1.heap_count + 1\n        finally:\n            HeapDestroy(heap)\n\n        trim_memory()\n        mem3 = psutil.heap_info()\n        assert mem3.heap_count == mem1.heap_count\n"
  },
  {
    "path": "tests/test_linux.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Linux specific tests.\"\"\"\n\nimport collections\nimport contextlib\nimport errno\nimport io\nimport os\nimport platform\nimport re\nimport shutil\nimport socket\nimport struct\nimport textwrap\nimport time\nimport warnings\nfrom unittest import mock\n\nimport psutil\nfrom psutil import LINUX\n\nfrom . import AARCH64\nfrom . import GITHUB_ACTIONS\nfrom . import GLOBAL_TIMEOUT\nfrom . import HAS_BATTERY\nfrom . import HAS_CPU_FREQ\nfrom . import HAS_PROC_RLIMIT\nfrom . import RISCV64\nfrom . import TOLERANCE_DISK_USAGE\nfrom . import TOLERANCE_SYS_MEM\nfrom . import PsutilTestCase\nfrom . import ThreadTask\nfrom . import call_until\nfrom . import pytest\nfrom . import reload_module\nfrom . import retry_on_failure\nfrom . import safe_rmpath\nfrom . import sh\nfrom . import skip_on_not_implemented\n\nif LINUX:\n    from psutil._pslinux import CLOCK_TICKS\n    from psutil._pslinux import RootFsDeviceFinder\n    from psutil._pslinux import calculate_avail_vmem\n    from psutil._pslinux import open_binary\n\n\nSIOCGIFADDR = 0x8915\nSIOCGIFHWADDR = 0x8927\nSIOCGIFNETMASK = 0x891B\nSIOCGIFBRDADDR = 0x8919\nif LINUX:\n    SECTOR_SIZE = 512\n\n\n@pytest.mark.skipif(not LINUX, reason=\"LINUX only\")\nclass LinuxTestCase(PsutilTestCase):\n    pass\n\n\n# =====================================================================\n# --- utils\n# =====================================================================\n\n\ndef get_ipv4_address(ifname):\n    import fcntl\n\n    ifname = bytes(ifname[:15], \"ascii\")\n    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:\n        return socket.inet_ntoa(\n            fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', ifname))[\n                20:24\n            ]\n        )\n\n\ndef get_ipv4_netmask(ifname):\n    import fcntl\n\n    ifname = bytes(ifname[:15], \"ascii\")\n    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:\n        return socket.inet_ntoa(\n            fcntl.ioctl(\n                s.fileno(), SIOCGIFNETMASK, struct.pack('256s', ifname)\n            )[20:24]\n        )\n\n\ndef get_ipv4_broadcast(ifname):\n    import fcntl\n\n    ifname = bytes(ifname[:15], \"ascii\")\n    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:\n        return socket.inet_ntoa(\n            fcntl.ioctl(\n                s.fileno(), SIOCGIFBRDADDR, struct.pack('256s', ifname)\n            )[20:24]\n        )\n\n\ndef get_ipv6_addresses(ifname):\n    with open(\"/proc/net/if_inet6\") as f:\n        all_fields = []\n        for line in f:\n            fields = line.split()\n            if fields[-1] == ifname:\n                all_fields.append(fields)\n\n        if len(all_fields) == 0:\n            raise ValueError(f\"could not find interface {ifname!r}\")\n\n    for i in range(len(all_fields)):\n        unformatted = all_fields[i][0]\n        groups = [\n            unformatted[j : j + 4] for j in range(0, len(unformatted), 4)\n        ]\n        formatted = \":\".join(groups)\n        packed = socket.inet_pton(socket.AF_INET6, formatted)\n        all_fields[i] = socket.inet_ntop(socket.AF_INET6, packed)\n    return all_fields\n\n\ndef get_mac_address(ifname):\n    import fcntl\n\n    ifname = bytes(ifname[:15], \"ascii\")\n    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:\n        info = fcntl.ioctl(\n            s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)\n        )\n        return \"\".join([f\"{char:02x}:\" for char in info[18:24]])[:-1]\n\n\ndef free_swap():\n    \"\"\"Parse 'free' cmd and return swap memory's s total, used and free\n    values.\n    \"\"\"\n    out = sh([\"free\", \"-b\"], env={\"LANG\": \"C.UTF-8\"})\n    lines = out.split('\\n')\n    for line in lines:\n        if line.startswith('Swap'):\n            _, total, used, free = line.split()\n            nt = collections.namedtuple('free', 'total used free')\n            return nt(int(total), int(used), int(free))\n    raise ValueError(f\"can't find 'Swap' in 'free' output:\\n{out}\")\n\n\ndef free_physmem():\n    \"\"\"Parse 'free' cmd and return physical memory's total, used\n    and free values.\n    \"\"\"\n    # Note: free can have 2 different formats, invalidating 'shared'\n    # and 'cached' memory which may have different positions so we\n    # do not return them.\n    # https://github.com/giampaolo/psutil/issues/538#issuecomment-57059946\n    out = sh([\"free\", \"-b\"], env={\"LANG\": \"C.UTF-8\"})\n    lines = out.split('\\n')\n    for line in lines:\n        if line.startswith('Mem'):\n            total, used, free, shared = (int(x) for x in line.split()[1:5])\n            nt = collections.namedtuple(\n                'free', 'total used free shared output'\n            )\n            return nt(total, used, free, shared, out)\n    raise ValueError(f\"can't find 'Mem' in 'free' output:\\n{out}\")\n\n\ndef vmstat(stat):\n    out = sh([\"vmstat\", \"-s\"], env={\"LANG\": \"C.UTF-8\"})\n    for line in out.split(\"\\n\"):\n        line = line.strip()\n        if stat in line:\n            return int(line.split(' ')[0])\n    raise ValueError(f\"can't find {stat!r} in 'vmstat' output\")\n\n\ndef get_free_version_info():\n    out = sh([\"free\", \"-V\"]).strip()\n    if 'UNKNOWN' in out:\n        return pytest.skip(\"can't determine free version\")\n    return tuple(map(int, re.findall(r'\\d+', out.split()[-1])))\n\n\n@contextlib.contextmanager\ndef mock_open_content(pairs):\n    \"\"\"Mock open() builtin and forces it to return a certain content\n    for a given path. `pairs` is a {\"path\": \"content\", ...} dict.\n    \"\"\"\n\n    def open_mock(name, *args, **kwargs):\n        if name in pairs:\n            content = pairs[name]\n            if isinstance(content, str):\n                return io.StringIO(content)\n            else:\n                return io.BytesIO(content)\n        else:\n            return orig_open(name, *args, **kwargs)\n\n    orig_open = open\n    with mock.patch(\"builtins.open\", create=True, side_effect=open_mock) as m:\n        yield m\n\n\n@contextlib.contextmanager\ndef mock_open_exception(for_path, exc):\n    \"\"\"Mock open() builtin and raises `exc` if the path being opened\n    matches `for_path`.\n    \"\"\"\n\n    def open_mock(name, *args, **kwargs):\n        if name == for_path:\n            raise exc\n        return orig_open(name, *args, **kwargs)\n\n    orig_open = open\n    with mock.patch(\"builtins.open\", create=True, side_effect=open_mock) as m:\n        yield m\n\n\n# =====================================================================\n# --- system virtual memory\n# =====================================================================\n\n\nclass TestSystemVirtualMemoryAgainstFree(LinuxTestCase):\n    def test_total(self):\n        cli_value = free_physmem().total\n        psutil_value = psutil.virtual_memory().total\n        assert cli_value == psutil_value\n\n    @retry_on_failure()\n    def test_used(self):\n        # Older versions of procps used slab memory to calculate used memory.\n        # This got changed in:\n        # https://gitlab.com/procps-ng/procps/-/commit/05d751c4f07\n        # Newer versions of procps (>=4.0.1) are using yet another way to\n        # compute used memory.\n        # https://gitlab.com/procps-ng/procps/-/commit/2184e90d2e7\n        if get_free_version_info() < (4, 0, 1):\n            return pytest.skip(\"free version too old\")\n        cli_value = free_physmem().used\n        psutil_value = psutil.virtual_memory().used\n        assert abs(cli_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_free(self):\n        cli_value = free_physmem().free\n        psutil_value = psutil.virtual_memory().free\n        assert abs(cli_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_shared(self):\n        free = free_physmem()\n        free_value = free.shared\n        if free_value == 0:\n            return pytest.skip(\"free does not support 'shared' column\")\n        psutil_value = psutil.virtual_memory().shared\n        assert (\n            abs(free_value - psutil_value) < TOLERANCE_SYS_MEM\n        ), f\"{free_value} {psutil_value} \\n{free.output}\"\n\n    @retry_on_failure()\n    def test_available(self):\n        # \"free\" output format has changed at some point:\n        # https://github.com/giampaolo/psutil/issues/538#issuecomment-147192098\n        out = sh([\"free\", \"-b\"])\n        lines = out.split('\\n')\n        if 'available' not in lines[0]:\n            return pytest.skip(\"free does not support 'available' column\")\n        free_value = int(lines[1].split()[-1])\n        psutil_value = psutil.virtual_memory().available\n        assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM\n\n\nclass TestSystemVirtualMemoryAgainstVmstat(LinuxTestCase):\n    def test_total(self):\n        vmstat_value = vmstat('total memory') * 1024\n        psutil_value = psutil.virtual_memory().total\n        assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_used(self):\n        # Older versions of procps used slab memory to calculate used memory.\n        # This got changed in:\n        # https://gitlab.com/procps-ng/procps/-/commit/05d751c4f07\n        # Newer versions of procps (>=4.0.1) are using yet another way to\n        # compute used memory.\n        # https://gitlab.com/procps-ng/procps/-/commit/2184e90d2e7\n        if get_free_version_info() < (4, 0, 1):\n            return pytest.skip(\"free version too old\")\n        vmstat_value = vmstat('used memory') * 1024\n        psutil_value = psutil.virtual_memory().used\n        assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_free(self):\n        vmstat_value = vmstat('free memory') * 1024\n        psutil_value = psutil.virtual_memory().free\n        assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_buffers(self):\n        vmstat_value = vmstat('buffer memory') * 1024\n        psutil_value = psutil.virtual_memory().buffers\n        assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_active(self):\n        vmstat_value = vmstat('active memory') * 1024\n        psutil_value = psutil.virtual_memory().active\n        assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_inactive(self):\n        vmstat_value = vmstat('inactive memory') * 1024\n        psutil_value = psutil.virtual_memory().inactive\n        assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM\n\n\nclass TestSystemVirtualMemoryMocks(LinuxTestCase):\n    def test_warnings_on_misses(self):\n        # Emulate a case where /proc/meminfo provides few info.\n        # psutil is supposed to set the missing fields to 0 and\n        # raise a warning.\n        content = textwrap.dedent(\"\"\"\\\n            Active(anon):    6145416 kB\n            Active(file):    2950064 kB\n            Inactive(anon):   574764 kB\n            Inactive(file):  1567648 kB\n            MemAvailable:         -1 kB\n            MemFree:         2057400 kB\n            MemTotal:       16325648 kB\n            SReclaimable:     346648 kB\n            \"\"\").encode()\n        with mock_open_content({'/proc/meminfo': content}) as m:\n            with warnings.catch_warnings(record=True) as ws:\n                warnings.simplefilter(\"always\")\n                ret = psutil.virtual_memory()\n                assert m.called\n                assert len(ws) == 1\n                w = ws[0]\n                assert \"memory stats couldn't be determined\" in str(w.message)\n                assert \"cached\" in str(w.message)\n                assert \"shared\" in str(w.message)\n                assert \"active\" in str(w.message)\n                assert \"inactive\" in str(w.message)\n                assert \"buffers\" in str(w.message)\n                assert \"available\" in str(w.message)\n                assert ret.cached == 0\n                assert ret.active == 0\n                assert ret.inactive == 0\n                assert ret.shared == 0\n                assert ret.buffers == 0\n                assert ret.available == 0\n                assert ret.slab == 0\n\n    @retry_on_failure()\n    def test_avail_old_percent(self):\n        # Make sure that our calculation of avail mem for old kernels\n        # is off by max 15%.\n        mems = {}\n        with open_binary('/proc/meminfo') as f:\n            for line in f:\n                fields = line.split()\n                mems[fields[0]] = int(fields[1]) * 1024\n\n        a = calculate_avail_vmem(mems)\n        if b'MemAvailable:' in mems:\n            b = mems[b'MemAvailable:']\n            diff_percent = abs(a - b) / a * 100\n            assert diff_percent < 15\n\n    def test_avail_old_comes_from_kernel(self):\n        # Make sure \"MemAvailable:\" coluimn is used instead of relying\n        # on our internal algorithm to calculate avail mem.\n        content = textwrap.dedent(\"\"\"\\\n            Active:          9444728 kB\n            Active(anon):    6145416 kB\n            Active(file):    2950064 kB\n            Buffers:          287952 kB\n            Cached:          4818144 kB\n            Inactive(file):  1578132 kB\n            Inactive(anon):   574764 kB\n            Inactive(file):  1567648 kB\n            MemAvailable:    6574984 kB\n            MemFree:         2057400 kB\n            MemTotal:       16325648 kB\n            Shmem:            577588 kB\n            SReclaimable:     346648 kB\n            \"\"\").encode()\n        with mock_open_content({'/proc/meminfo': content}) as m:\n            with warnings.catch_warnings(record=True) as ws:\n                ret = psutil.virtual_memory()\n            assert m.called\n            assert ret.available == 6574984 * 1024\n            w = ws[0]\n            assert \"inactive memory stats couldn't be determined\" in str(\n                w.message\n            )\n\n    def test_avail_old_missing_fields(self):\n        # Remove Active(file), Inactive(file) and SReclaimable\n        # from /proc/meminfo and make sure the fallback is used\n        # (free + cached),\n        content = textwrap.dedent(\"\"\"\\\n            Active:          9444728 kB\n            Active(anon):    6145416 kB\n            Buffers:          287952 kB\n            Cached:          4818144 kB\n            Inactive(file):  1578132 kB\n            Inactive(anon):   574764 kB\n            MemFree:         2057400 kB\n            MemTotal:       16325648 kB\n            Shmem:            577588 kB\n            \"\"\").encode()\n        with mock_open_content({\"/proc/meminfo\": content}) as m:\n            with warnings.catch_warnings(record=True) as ws:\n                ret = psutil.virtual_memory()\n            assert m.called\n            assert ret.available == 2057400 * 1024 + 4818144 * 1024\n            w = ws[0]\n            assert \"inactive memory stats couldn't be determined\" in str(\n                w.message\n            )\n\n    def test_avail_old_missing_zoneinfo(self):\n        # Remove /proc/zoneinfo file. Make sure fallback is used\n        # (free + cached).\n        content = textwrap.dedent(\"\"\"\\\n            Active:          9444728 kB\n            Active(anon):    6145416 kB\n            Active(file):    2950064 kB\n            Buffers:          287952 kB\n            Cached:          4818144 kB\n            Inactive(file):  1578132 kB\n            Inactive(anon):   574764 kB\n            Inactive(file):  1567648 kB\n            MemFree:         2057400 kB\n            MemTotal:       16325648 kB\n            Shmem:            577588 kB\n            SReclaimable:     346648 kB\n            \"\"\").encode()\n        with mock_open_content({\"/proc/meminfo\": content}):\n            with mock_open_exception(\"/proc/zoneinfo\", FileNotFoundError):\n                with warnings.catch_warnings(record=True) as ws:\n                    ret = psutil.virtual_memory()\n                    assert ret.available == 2057400 * 1024 + 4818144 * 1024\n                    w = ws[0]\n                    assert (\n                        \"inactive memory stats couldn't be determined\"\n                        in str(w.message)\n                    )\n\n    def test_virtual_memory_mocked(self):\n        # Emulate /proc/meminfo because neither vmstat nor free return slab.\n        content = textwrap.dedent(\"\"\"\\\n            MemTotal:              100 kB\n            MemFree:               2 kB\n            MemAvailable:          3 kB\n            Buffers:               4 kB\n            Cached:                5 kB\n            SwapCached:            6 kB\n            Active:                7 kB\n            Inactive:              8 kB\n            Active(anon):          9 kB\n            Inactive(anon):        10 kB\n            Active(file):          11 kB\n            Inactive(file):        12 kB\n            Unevictable:           13 kB\n            Mlocked:               14 kB\n            SwapTotal:             15 kB\n            SwapFree:              16 kB\n            Dirty:                 17 kB\n            Writeback:             18 kB\n            AnonPages:             19 kB\n            Mapped:                20 kB\n            Shmem:                 21 kB\n            Slab:                  22 kB\n            SReclaimable:          23 kB\n            SUnreclaim:            24 kB\n            KernelStack:           25 kB\n            PageTables:            26 kB\n            NFS_Unstable:          27 kB\n            Bounce:                28 kB\n            WritebackTmp:          29 kB\n            CommitLimit:           30 kB\n            Committed_AS:          31 kB\n            VmallocTotal:          32 kB\n            VmallocUsed:           33 kB\n            VmallocChunk:          34 kB\n            HardwareCorrupted:     35 kB\n            AnonHugePages:         36 kB\n            ShmemHugePages:        37 kB\n            ShmemPmdMapped:        38 kB\n            CmaTotal:              39 kB\n            CmaFree:               40 kB\n            HugePages_Total:       41 kB\n            HugePages_Free:        42 kB\n            HugePages_Rsvd:        43 kB\n            HugePages_Surp:        44 kB\n            Hugepagesize:          45 kB\n            DirectMap46k:          46 kB\n            DirectMap47M:          47 kB\n            DirectMap48G:          48 kB\n            \"\"\").encode()\n        with mock_open_content({\"/proc/meminfo\": content}) as m:\n            mem = psutil.virtual_memory()\n            assert m.called\n            assert mem.total == 100 * 1024\n            assert mem.free == 2 * 1024\n            assert mem.buffers == 4 * 1024\n            # cached mem also includes reclaimable memory\n            assert mem.cached == (5 + 23) * 1024\n            assert mem.shared == 21 * 1024\n            assert mem.active == 7 * 1024\n            assert mem.inactive == 8 * 1024\n            assert mem.slab == 22 * 1024\n            assert mem.available == 3 * 1024\n\n\n# =====================================================================\n# --- system swap memory\n# =====================================================================\n\n\nclass TestSystemSwapMemory(LinuxTestCase):\n    @staticmethod\n    def meminfo_has_swap_info():\n        \"\"\"Return True if /proc/meminfo provides swap metrics.\"\"\"\n        with open(\"/proc/meminfo\") as f:\n            data = f.read()\n        return 'SwapTotal:' in data and 'SwapFree:' in data\n\n    def test_total(self):\n        free_value = free_swap().total\n        psutil_value = psutil.swap_memory().total\n        assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_used(self):\n        free_value = free_swap().used\n        psutil_value = psutil.swap_memory().used\n        assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_free(self):\n        free_value = free_swap().free\n        psutil_value = psutil.swap_memory().free\n        assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM\n\n    def test_missing_sin_sout(self):\n        with mock.patch('psutil._common.open', create=True) as m:\n            with warnings.catch_warnings(record=True) as ws:\n                warnings.simplefilter(\"always\")\n                ret = psutil.swap_memory()\n                assert m.called\n                assert len(ws) == 1\n                w = ws[0]\n                assert (\n                    \"'sin' and 'sout' swap memory stats couldn't be determined\"\n                    in str(w.message)\n                )\n                assert ret.sin == 0\n                assert ret.sout == 0\n\n    def test_no_vmstat_mocked(self):\n        # see https://github.com/giampaolo/psutil/issues/722\n        with mock_open_exception(\"/proc/vmstat\", FileNotFoundError) as m:\n            with warnings.catch_warnings(record=True) as ws:\n                warnings.simplefilter(\"always\")\n                ret = psutil.swap_memory()\n                assert m.called\n                assert len(ws) == 1\n                w = ws[0]\n                assert (\n                    \"'sin' and 'sout' swap memory stats couldn't \"\n                    \"be determined and were set to 0\"\n                    in str(w.message)\n                )\n                assert ret.sin == 0\n                assert ret.sout == 0\n\n    def test_meminfo_against_sysinfo(self):\n        # Make sure the content of /proc/meminfo about swap memory\n        # matches sysinfo() syscall, see:\n        # https://github.com/giampaolo/psutil/issues/1015\n        if not self.meminfo_has_swap_info():\n            return pytest.skip(\"/proc/meminfo has no swap metrics\")\n        with mock.patch('psutil._pslinux.cext.linux_sysinfo') as m:\n            swap = psutil.swap_memory()\n        assert not m.called\n        import psutil._psutil_linux as cext\n\n        _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()\n        total *= unit_multiplier\n        free *= unit_multiplier\n        assert swap.total == total\n        assert abs(swap.free - free) < TOLERANCE_SYS_MEM\n\n    def test_emulate_meminfo_has_no_metrics(self):\n        # Emulate a case where /proc/meminfo provides no swap metrics\n        # in which case sysinfo() syscall is supposed to be used\n        # as a fallback.\n        with mock_open_content({\"/proc/meminfo\": b\"\"}) as m:\n            psutil.swap_memory()\n            assert m.called\n\n\n# =====================================================================\n# --- system CPU\n# =====================================================================\n\n\nclass TestSystemCPUCountLogical(LinuxTestCase):\n    @pytest.mark.skipif(\n        not os.path.exists(\"/sys/devices/system/cpu/online\"),\n        reason=\"/sys/devices/system/cpu/online does not exist\",\n    )\n    def test_against_sysdev_cpu_online(self):\n        with open(\"/sys/devices/system/cpu/online\") as f:\n            value = f.read().strip()\n        if \"-\" in str(value):\n            value = int(value.split('-')[1]) + 1\n            assert psutil.cpu_count() == value\n\n    @pytest.mark.skipif(\n        not os.path.exists(\"/sys/devices/system/cpu\"),\n        reason=\"/sys/devices/system/cpu does not exist\",\n    )\n    def test_against_sysdev_cpu_num(self):\n        ls = os.listdir(\"/sys/devices/system/cpu\")\n        count = len([x for x in ls if re.search(r\"cpu\\d+$\", x) is not None])\n        assert psutil.cpu_count() == count\n\n    @pytest.mark.skipif(\n        not shutil.which(\"nproc\"), reason=\"nproc utility not available\"\n    )\n    def test_against_nproc(self):\n        num = int(sh(\"nproc --all\"))\n        assert psutil.cpu_count(logical=True) == num\n\n    @pytest.mark.skipif(\n        not shutil.which(\"lscpu\"), reason=\"lscpu utility not available\"\n    )\n    def test_against_lscpu(self):\n        out = sh(\"lscpu -p\")\n        num = len([x for x in out.split('\\n') if not x.startswith('#')])\n        assert psutil.cpu_count(logical=True) == num\n\n    def test_emulate_fallbacks(self):\n        import psutil._pslinux\n\n        original = psutil._pslinux.cpu_count_logical()\n        # Here we want to mock os.sysconf(\"SC_NPROCESSORS_ONLN\") in\n        # order to cause the parsing of /proc/cpuinfo and /proc/stat.\n        with mock.patch(\n            'psutil._pslinux.os.sysconf', side_effect=ValueError\n        ) as m:\n            assert psutil._pslinux.cpu_count_logical() == original\n            assert m.called\n\n            # Let's have open() return empty data and make sure None is\n            # returned ('cause we mimic os.cpu_count()).\n            with mock.patch('psutil._common.open', create=True) as m:\n                assert psutil._pslinux.cpu_count_logical() is None\n                assert m.call_count == 2\n                # /proc/stat should be the last one\n                assert m.call_args[0][0] == '/proc/stat'\n\n            # Let's push this a bit further and make sure /proc/cpuinfo\n            # parsing works as expected.\n            with open('/proc/cpuinfo', 'rb') as f:\n                cpuinfo_data = f.read()\n            fake_file = io.BytesIO(cpuinfo_data)\n            with mock.patch(\n                'psutil._common.open', return_value=fake_file, create=True\n            ) as m:\n                assert psutil._pslinux.cpu_count_logical() == original\n\n            # Finally, let's make /proc/cpuinfo return meaningless data;\n            # this way we'll fall back on relying on /proc/stat\n            with mock_open_content({\"/proc/cpuinfo\": b\"\"}) as m:\n                assert psutil._pslinux.cpu_count_logical() == original\n                assert m.called\n\n\nclass TestSystemCPUCountCores(LinuxTestCase):\n    @pytest.mark.skipif(\n        not shutil.which(\"lscpu\"), reason=\"lscpu utility not available\"\n    )\n    def test_against_lscpu(self):\n        out = sh(\"lscpu -p\")\n        core_ids = set()\n        for line in out.split('\\n'):\n            if not line.startswith('#'):\n                fields = line.split(',')\n                core_ids.add(fields[1])\n        assert psutil.cpu_count(logical=False) == len(core_ids)\n\n    @pytest.mark.skipif(\n        platform.machine() not in {\"x86_64\", \"i686\"}, reason=\"x86_64/i686 only\"\n    )\n    def test_method_2(self):\n        meth_1 = psutil._pslinux.cpu_count_cores()\n        with mock.patch('glob.glob', return_value=[]) as m:\n            meth_2 = psutil._pslinux.cpu_count_cores()\n            assert m.called\n        if meth_1 is not None:\n            assert meth_1 == meth_2\n\n    def test_emulate_none(self):\n        with mock.patch('glob.glob', return_value=[]) as m1:\n            with mock.patch('psutil._common.open', create=True) as m2:\n                assert psutil._pslinux.cpu_count_cores() is None\n        assert m1.called\n        assert m2.called\n\n\nclass TestSystemCPUFrequency(LinuxTestCase):\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    @pytest.mark.skipif(\n        AARCH64, reason=\"aarch64 does not always expose frequency\"\n    )\n    def test_emulate_use_second_file(self):\n        # https://github.com/giampaolo/psutil/issues/981\n        def path_exists_mock(path):\n            if path.startswith(\"/sys/devices/system/cpu/cpufreq/policy\"):\n                return False\n            else:\n                return orig_exists(path)\n\n        orig_exists = os.path.exists\n        with mock.patch(\n            \"os.path.exists\", side_effect=path_exists_mock, create=True\n        ):\n            assert psutil.cpu_freq()\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    @pytest.mark.skipif(\n        AARCH64 or RISCV64,\n        reason=f\"{platform.machine()} does not report mhz in /proc/cpuinfo\",\n    )\n    def test_emulate_use_cpuinfo(self):\n        # Emulate a case where /sys/devices/system/cpu/cpufreq* does not\n        # exist and /proc/cpuinfo is used instead.\n        def path_exists_mock(path):\n            if path.startswith('/sys/devices/system/cpu/'):\n                return False\n            else:\n                return os_path_exists(path)\n\n        os_path_exists = os.path.exists\n        try:\n            with mock.patch(\"os.path.exists\", side_effect=path_exists_mock):\n                reload_module(psutil._pslinux)\n                ret = psutil.cpu_freq()\n                assert ret, ret\n                assert ret.max == 0.0\n                assert ret.min == 0.0\n                for freq in psutil.cpu_freq(percpu=True):\n                    assert freq.max == 0.0\n                    assert freq.min == 0.0\n        finally:\n            reload_module(psutil._pslinux)\n            reload_module(psutil)\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    def test_emulate_data(self):\n        def open_mock(name, *args, **kwargs):\n            if name.endswith('/scaling_cur_freq') and name.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy\"\n            ):\n                return io.BytesIO(b\"500000\")\n            elif name.endswith('/scaling_min_freq') and name.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy\"\n            ):\n                return io.BytesIO(b\"600000\")\n            elif name.endswith('/scaling_max_freq') and name.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy\"\n            ):\n                return io.BytesIO(b\"700000\")\n            elif name == '/proc/cpuinfo':\n                return io.BytesIO(b\"cpu MHz     : 500\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock):\n            with mock.patch('os.path.exists', return_value=True):\n                freq = psutil.cpu_freq()\n                assert freq.current == 500.0\n                # when /proc/cpuinfo is used min and max frequencies are not\n                # available and are set to 0.\n                if freq.min != 0.0:\n                    assert freq.min == 600.0\n                if freq.max != 0.0:\n                    assert freq.max == 700.0\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    def test_emulate_multi_cpu(self):\n        def open_mock(name, *args, **kwargs):\n            n = name\n            if n.endswith('/scaling_cur_freq') and n.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy0\"\n            ):\n                return io.BytesIO(b\"100000\")\n            elif n.endswith('/scaling_min_freq') and n.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy0\"\n            ):\n                return io.BytesIO(b\"200000\")\n            elif n.endswith('/scaling_max_freq') and n.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy0\"\n            ):\n                return io.BytesIO(b\"300000\")\n            elif n.endswith('/scaling_cur_freq') and n.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy1\"\n            ):\n                return io.BytesIO(b\"400000\")\n            elif n.endswith('/scaling_min_freq') and n.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy1\"\n            ):\n                return io.BytesIO(b\"500000\")\n            elif n.endswith('/scaling_max_freq') and n.startswith(\n                \"/sys/devices/system/cpu/cpufreq/policy1\"\n            ):\n                return io.BytesIO(b\"600000\")\n            elif name == '/proc/cpuinfo':\n                return io.BytesIO(b\"cpu MHz     : 100\\ncpu MHz     : 400\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock):\n            with mock.patch('os.path.exists', return_value=True):\n                with mock.patch(\n                    'psutil._pslinux.cpu_count_logical', return_value=2\n                ):\n                    freq = psutil.cpu_freq(percpu=True)\n                    assert freq[0].current == 100.0\n                    if freq[0].min != 0.0:\n                        assert freq[0].min == 200.0\n                    if freq[0].max != 0.0:\n                        assert freq[0].max == 300.0\n                    assert freq[1].current == 400.0\n                    if freq[1].min != 0.0:\n                        assert freq[1].min == 500.0\n                    if freq[1].max != 0.0:\n                        assert freq[1].max == 600.0\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    def test_emulate_no_scaling_cur_freq_file(self):\n        # See: https://github.com/giampaolo/psutil/issues/1071\n        def open_mock(name, *args, **kwargs):\n            if name.endswith('/scaling_cur_freq'):\n                raise FileNotFoundError\n            if name.endswith('/cpuinfo_cur_freq'):\n                return io.BytesIO(b\"200000\")\n            elif name == '/proc/cpuinfo':\n                return io.BytesIO(b\"cpu MHz     : 200\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock):\n            with mock.patch('os.path.exists', return_value=True):\n                with mock.patch(\n                    'psutil._pslinux.cpu_count_logical', return_value=1\n                ):\n                    freq = psutil.cpu_freq()\n                    assert freq.current == 200\n\n\nclass TestSystemCPUStats(LinuxTestCase):\n\n    # XXX: fails too often.\n    # def test_ctx_switches(self):\n    #     vmstat_value = vmstat(\"context switches\")\n    #     psutil_value = psutil.cpu_stats().ctx_switches\n    #     assert abs(vmstat_value - psutil_value) < 500\n\n    def test_interrupts(self):\n        vmstat_value = vmstat(\"interrupts\")\n        psutil_value = psutil.cpu_stats().interrupts\n        assert abs(vmstat_value - psutil_value) < 500\n\n\nclass TestLoadAvg(LinuxTestCase):\n    def test_getloadavg(self):\n        psutil_value = psutil.getloadavg()\n        with open(\"/proc/loadavg\") as f:\n            proc_value = f.read().split()\n\n        assert abs(float(proc_value[0]) - psutil_value[0]) < 1\n        assert abs(float(proc_value[1]) - psutil_value[1]) < 1\n        assert abs(float(proc_value[2]) - psutil_value[2]) < 1\n\n\n# =====================================================================\n# --- system network\n# =====================================================================\n\n\nclass TestSystemNetIfAddrs(LinuxTestCase):\n    def test_ips(self):\n        for name, addrs in psutil.net_if_addrs().items():\n            for addr in addrs:\n                if addr.family == psutil.AF_LINK:\n                    assert addr.address == get_mac_address(name)\n                elif addr.family == socket.AF_INET:\n                    assert addr.address == get_ipv4_address(name)\n                    assert addr.netmask == get_ipv4_netmask(name)\n                    if addr.broadcast is not None:\n                        assert addr.broadcast == get_ipv4_broadcast(name)\n                    else:\n                        assert get_ipv4_broadcast(name) == '0.0.0.0'\n                elif addr.family == socket.AF_INET6:\n                    # IPv6 addresses can have a percent symbol at the end.\n                    # E.g. these 2 are equivalent:\n                    # \"fe80::1ff:fe23:4567:890a\"\n                    # \"fe80::1ff:fe23:4567:890a%eth0\"\n                    # That is the \"zone id\" portion, which usually is the name\n                    # of the network interface.\n                    address = addr.address.split('%')[0]\n                    assert address in get_ipv6_addresses(name)\n\n    @pytest.mark.skipif(\n        not shutil.which(\"ip\"), reason=\"'ip' command not available\"\n    )\n    @retry_on_failure()\n    def test_against_ip_addr_v4(self):\n        # Parse IPv4 addresses per interface from `ip addr` output and\n        # compare against psutil. Use the label at the end of each inet\n        # line as the interface name, since it reflects aliases like\n        # \"vboxnet0:avahi\" that psutil also uses as keys.\n        out = sh(\"ip addr\")\n        ip_addrs = {}  # {ifname: [addr, ...]}\n        for line in out.splitlines():\n            # \"    inet 1.2.3.4/24 brd ... scope global eth0\"\n            m = re.match(r'^\\s+inet\\s+(\\S+).*\\s+(\\S+)$', line)\n            if m:\n                addr = m.group(1).split('/')[0]\n                ifname = m.group(2)\n                ip_addrs.setdefault(ifname, []).append(addr)\n        psutil_addrs = psutil.net_if_addrs()\n        for ifname, addrs in ip_addrs.items():\n            if ifname not in psutil_addrs:\n                continue\n            psutil_ipv4 = {\n                a.address\n                for a in psutil_addrs[ifname]\n                if a.family == socket.AF_INET\n            }\n            for addr in addrs:\n                assert addr in psutil_ipv4\n\n    @pytest.mark.skipif(\n        not shutil.which(\"ip\"), reason=\"'ip' command not available\"\n    )\n    @retry_on_failure()\n    def test_against_ip_addr_v6(self):\n        # Parse IPv6 addresses per interface from `ip addr` output and\n        # compare against psutil. Unlike inet, inet6 lines have no label,\n        # so the interface name comes from the header line.\n        out = sh(\"ip addr\")\n        ip_addrs = {}  # {ifname: [addr, ...]}\n        current_if = None\n        for line in out.splitlines():\n            m = re.match(r'^\\d+:\\s+(\\S+):', line)\n            if m:\n                current_if = m.group(1).rstrip(':')\n            m = re.match(r'^\\s+inet6\\s+(\\S+)', line)\n            if m and current_if:\n                addr = m.group(1).split('/')[0]\n                ip_addrs.setdefault(current_if, []).append(addr)\n        psutil_addrs = psutil.net_if_addrs()\n        for ifname, addrs in ip_addrs.items():\n            if ifname not in psutil_addrs:\n                continue\n            # psutil may append %ifname zone ID to link-local addresses.\n            psutil_ipv6 = {\n                a.address.split('%')[0]\n                for a in psutil_addrs[ifname]\n                if a.family == socket.AF_INET6\n            }\n            for addr in addrs:\n                assert addr in psutil_ipv6\n\n    # XXX - not reliable when having virtual NICs installed by Docker.\n    # @pytest.mark.skipif(not shutil.which(\"ip\"),\n    #                     reason=\"'ip' utility not available\")\n    # def test_net_if_names(self):\n    #     out = sh(\"ip addr\").strip()\n    #     nics = [x for x in psutil.net_if_addrs().keys() if ':' not in x]\n    #     found = 0\n    #     for line in out.split('\\n'):\n    #         line = line.strip()\n    #         if re.search(r\"^\\d+:\", line):\n    #             found += 1\n    #             name = line.split(':')[1].strip()\n    #             assert name in nics\n    #     assert len(nics) == found\n\n\nclass TestSystemNetIfStats(LinuxTestCase):\n    @pytest.mark.skipif(\n        not shutil.which(\"ifconfig\"), reason=\"ifconfig utility not available\"\n    )\n    def test_against_ifconfig(self):\n        for name, stats in psutil.net_if_stats().items():\n            try:\n                out = sh(f\"ifconfig {name}\")\n            except RuntimeError:\n                pass\n            else:\n                assert stats.isup == ('RUNNING' in out), out\n                assert stats.mtu == int(\n                    re.findall(r'(?i)MTU[: ](\\d+)', out)[0]\n                )\n\n    def test_mtu(self):\n        for name, stats in psutil.net_if_stats().items():\n            with open(f\"/sys/class/net/{name}/mtu\") as f:\n                assert stats.mtu == int(f.read().strip())\n\n    @pytest.mark.skipif(\n        not shutil.which(\"ifconfig\"), reason=\"ifconfig utility not available\"\n    )\n    def test_flags(self):\n        # first line looks like this:\n        # \"eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500\"\n        matches_found = 0\n        for name, stats in psutil.net_if_stats().items():\n            try:\n                out = sh(f\"ifconfig {name}\")\n            except RuntimeError:\n                pass\n            else:\n                match = re.search(r\"flags=(\\d+)?<(.*?)>\", out)\n                if match and len(match.groups()) >= 2:\n                    matches_found += 1\n                    ifconfig_flags = set(match.group(2).lower().split(\",\"))\n                    psutil_flags = set(stats.flags.split(\",\"))\n                    assert ifconfig_flags == psutil_flags\n                else:\n                    # ifconfig has a different output on CentOS 6\n                    # let's try that\n                    match = re.search(r\"(.*)  MTU:(\\d+)  Metric:(\\d+)\", out)\n                    if match and len(match.groups()) >= 3:\n                        matches_found += 1\n                        ifconfig_flags = set(match.group(1).lower().split())\n                        psutil_flags = set(stats.flags.split(\",\"))\n                        assert ifconfig_flags == psutil_flags\n\n        if not matches_found:\n            return pytest.fail(\"no matches were found\")\n\n\nclass TestSystemNetIOCounters(LinuxTestCase):\n    @pytest.mark.skipif(\n        not shutil.which(\"ifconfig\"), reason=\"ifconfig utility not available\"\n    )\n    @retry_on_failure()\n    def test_against_ifconfig(self):\n        def ifconfig(nic):\n            ret = {}\n            out = sh(f\"ifconfig {nic}\")\n            ret['packets_recv'] = int(\n                re.findall(r'RX packets[: ](\\d+)', out)[0]\n            )\n            ret['packets_sent'] = int(\n                re.findall(r'TX packets[: ](\\d+)', out)[0]\n            )\n            ret['errin'] = int(re.findall(r'errors[: ](\\d+)', out)[0])\n            ret['errout'] = int(re.findall(r'errors[: ](\\d+)', out)[1])\n            ret['dropin'] = int(re.findall(r'dropped[: ](\\d+)', out)[0])\n            ret['dropout'] = int(re.findall(r'dropped[: ](\\d+)', out)[1])\n            ret['bytes_recv'] = int(\n                re.findall(r'RX (?:packets \\d+ +)?bytes[: ](\\d+)', out)[0]\n            )\n            ret['bytes_sent'] = int(\n                re.findall(r'TX (?:packets \\d+ +)?bytes[: ](\\d+)', out)[0]\n            )\n            return ret\n\n        nio = psutil.net_io_counters(pernic=True, nowrap=False)\n        for name, stats in nio.items():\n            try:\n                ifconfig_ret = ifconfig(name)\n            except RuntimeError:\n                continue\n            assert (\n                abs(stats.bytes_recv - ifconfig_ret['bytes_recv']) < 1024 * 10\n            )\n            assert (\n                abs(stats.bytes_sent - ifconfig_ret['bytes_sent']) < 1024 * 10\n            )\n            assert (\n                abs(stats.packets_recv - ifconfig_ret['packets_recv']) < 1024\n            )\n            assert (\n                abs(stats.packets_sent - ifconfig_ret['packets_sent']) < 1024\n            )\n            assert abs(stats.errin - ifconfig_ret['errin']) < 10\n            assert abs(stats.errout - ifconfig_ret['errout']) < 10\n            assert abs(stats.dropin - ifconfig_ret['dropin']) < 10\n            assert abs(stats.dropout - ifconfig_ret['dropout']) < 10\n\n\nclass TestSystemNetConnections(LinuxTestCase):\n    @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError)\n    @mock.patch('psutil._pslinux.supports_ipv6', return_value=False)\n    def test_emulate_ipv6_unsupported(self, supports_ipv6, inet_ntop):\n        # see: https://github.com/giampaolo/psutil/issues/623\n        with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n            try:\n                s.bind((\"::1\", 0))\n            except OSError:\n                pass\n            psutil.net_connections(kind='inet6')\n\n    def test_emulate_unix(self):\n        content = textwrap.dedent(\"\"\"\\\n            0: 00000003 000 000 0001 03 462170 @/tmp/dbus-Qw2hMPIU3n\n            0: 00000003 000 000 0001 03 35010 @/tmp/dbus-tB2X8h69BQ\n            0: 00000003 000 000 0001 03 34424 @/tmp/dbus-cHy80Y8O\n            000000000000000000000000000000000000000000000000000000\n            \"\"\")\n        with mock_open_content({\"/proc/net/unix\": content}) as m:\n            psutil.net_connections(kind='unix')\n            assert m.called\n\n\n# =====================================================================\n# --- system disks\n# =====================================================================\n\n\nclass TestSystemDiskPartitions(LinuxTestCase):\n    @pytest.mark.skipif(\n        not hasattr(os, 'statvfs'), reason=\"os.statvfs() not available\"\n    )\n    @skip_on_not_implemented()\n    def test_against_df(self):\n        # test psutil.disk_usage() and psutil.disk_partitions()\n        # against \"df -a\"\n        def df(path):\n            out = sh(f'df -P -B 1 \"{path}\"').strip()\n            lines = out.split('\\n')\n            lines.pop(0)\n            line = lines.pop(0)\n            dev, total, used, free = line.split()[:4]\n            if dev == 'none':\n                dev = ''\n            total, used, free = int(total), int(used), int(free)\n            return dev, total, used, free\n\n        for part in psutil.disk_partitions(all=False):\n            usage = psutil.disk_usage(part.mountpoint)\n            _, total, used, free = df(part.mountpoint)\n            assert usage.total == total\n            assert abs(usage.free - free) < TOLERANCE_DISK_USAGE\n            assert abs(usage.used - used) < TOLERANCE_DISK_USAGE\n\n    def test_zfs_fs(self):\n        # Test that ZFS partitions are returned.\n        with open(\"/proc/filesystems\") as f:\n            data = f.read()\n        if 'zfs' in data:\n            for part in psutil.disk_partitions():\n                if part.fstype == 'zfs':\n                    return\n\n        # No ZFS partitions on this system. Let's fake one.\n        fake_file = io.StringIO(\"nodev\\tzfs\\n\")\n        with mock.patch(\n            'psutil._common.open', return_value=fake_file, create=True\n        ) as m1:\n            with mock.patch(\n                'psutil._pslinux.cext.disk_partitions',\n                return_value=[('/dev/sdb3', '/', 'zfs', 'rw')],\n            ) as m2:\n                ret = psutil.disk_partitions()\n                assert m1.called\n                assert m2.called\n                assert ret\n                assert ret[0].fstype == 'zfs'\n\n    def test_emulate_realpath_fail(self):\n        # See: https://github.com/giampaolo/psutil/issues/1307\n        try:\n            with mock.patch(\n                'os.path.realpath', return_value='/non/existent'\n            ) as m:\n                with pytest.raises(FileNotFoundError):\n                    psutil.disk_partitions()\n                assert m.called\n        finally:\n            psutil.PROCFS_PATH = \"/proc\"\n\n\nclass TestSystemDiskIoCounters(LinuxTestCase):\n    def test_emulate_kernel_2_4(self):\n        # Tests /proc/diskstats parsing format for 2.4 kernels, see:\n        # https://github.com/giampaolo/psutil/issues/767\n        content = \"   3     0   1 hda 2 3 4 5 6 7 8 9 10 11 12\"\n        with mock_open_content({'/proc/diskstats': content}):\n            with mock.patch(\n                'psutil._pslinux.is_storage_device', return_value=True\n            ):\n                ret = psutil.disk_io_counters(nowrap=False)\n                assert ret.read_count == 1\n                assert ret.read_merged_count == 2\n                assert ret.read_bytes == 3 * SECTOR_SIZE\n                assert ret.read_time == 4\n                assert ret.write_count == 5\n                assert ret.write_merged_count == 6\n                assert ret.write_bytes == 7 * SECTOR_SIZE\n                assert ret.write_time == 8\n                assert ret.busy_time == 10\n\n    def test_emulate_kernel_2_6_full(self):\n        # Tests /proc/diskstats parsing format for 2.6 kernels,\n        # lines reporting all metrics:\n        # https://github.com/giampaolo/psutil/issues/767\n        content = \"   3    0   hda 1 2 3 4 5 6 7 8 9 10 11\"\n        with mock_open_content({\"/proc/diskstats\": content}):\n            with mock.patch(\n                'psutil._pslinux.is_storage_device', return_value=True\n            ):\n                ret = psutil.disk_io_counters(nowrap=False)\n                assert ret.read_count == 1\n                assert ret.read_merged_count == 2\n                assert ret.read_bytes == 3 * SECTOR_SIZE\n                assert ret.read_time == 4\n                assert ret.write_count == 5\n                assert ret.write_merged_count == 6\n                assert ret.write_bytes == 7 * SECTOR_SIZE\n                assert ret.write_time == 8\n                assert ret.busy_time == 10\n\n    def test_emulate_kernel_2_6_limited(self):\n        # Tests /proc/diskstats parsing format for 2.6 kernels,\n        # where one line of /proc/partitions return a limited\n        # amount of metrics when it bumps into a partition\n        # (instead of a disk). See:\n        # https://github.com/giampaolo/psutil/issues/767\n        with mock_open_content({\"/proc/diskstats\": \"   3    1   hda 1 2 3 4\"}):\n            with mock.patch(\n                'psutil._pslinux.is_storage_device', return_value=True\n            ):\n                ret = psutil.disk_io_counters(nowrap=False)\n                assert ret.read_count == 1\n                assert ret.read_bytes == 2 * SECTOR_SIZE\n                assert ret.write_count == 3\n                assert ret.write_bytes == 4 * SECTOR_SIZE\n\n                assert ret.read_merged_count == 0\n                assert ret.read_time == 0\n                assert ret.write_merged_count == 0\n                assert ret.write_time == 0\n                assert ret.busy_time == 0\n\n    def test_emulate_include_partitions(self):\n        # Make sure that when perdisk=True disk partitions are returned,\n        # see:\n        # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842\n        content = textwrap.dedent(\"\"\"\\\n            3    0   nvme0n1 1 2 3 4 5 6 7 8 9 10 11\n            3    0   nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11\n            \"\"\")\n        with mock_open_content({\"/proc/diskstats\": content}):\n            with mock.patch(\n                'psutil._pslinux.is_storage_device', return_value=False\n            ):\n                ret = psutil.disk_io_counters(perdisk=True, nowrap=False)\n                assert len(ret) == 2\n                assert ret['nvme0n1'].read_count == 1\n                assert ret['nvme0n1p1'].read_count == 1\n                assert ret['nvme0n1'].write_count == 5\n                assert ret['nvme0n1p1'].write_count == 5\n\n    def test_emulate_exclude_partitions(self):\n        # Make sure that when perdisk=False partitions (e.g. 'sda1',\n        # 'nvme0n1p1') are skipped and not included in the total count.\n        # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842\n        content = textwrap.dedent(\"\"\"\\\n            3    0   nvme0n1 1 2 3 4 5 6 7 8 9 10 11\n            3    0   nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11\n            \"\"\")\n        with mock_open_content({\"/proc/diskstats\": content}):\n            with mock.patch(\n                'psutil._pslinux.is_storage_device', return_value=False\n            ):\n                ret = psutil.disk_io_counters(perdisk=False, nowrap=False)\n                assert ret is None\n\n        def is_storage_device(name):\n            return name == 'nvme0n1'\n\n        content = textwrap.dedent(\"\"\"\\\n            3    0   nvme0n1 1 2 3 4 5 6 7 8 9 10 11\n            3    0   nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11\n            \"\"\")\n        with mock_open_content({\"/proc/diskstats\": content}):\n            with mock.patch(\n                'psutil._pslinux.is_storage_device',\n                create=True,\n                side_effect=is_storage_device,\n            ):\n                ret = psutil.disk_io_counters(perdisk=False, nowrap=False)\n                assert ret.read_count == 1\n                assert ret.write_count == 5\n\n    def test_emulate_use_sysfs(self):\n        def exists(path):\n            return path == '/proc/diskstats'\n\n        wprocfs = psutil.disk_io_counters(perdisk=True)\n        with mock.patch(\n            'psutil._pslinux.os.path.exists', create=True, side_effect=exists\n        ):\n            wsysfs = psutil.disk_io_counters(perdisk=True)\n        assert len(wprocfs) == len(wsysfs)\n\n    def test_emulate_not_impl(self):\n        def exists(path):\n            return False\n\n        with mock.patch(\n            'psutil._pslinux.os.path.exists', create=True, side_effect=exists\n        ):\n            with pytest.raises(NotImplementedError):\n                psutil.disk_io_counters()\n\n\nclass TestRootFsDeviceFinder(LinuxTestCase):\n    def setUp(self):\n        dev = os.stat(\"/\").st_dev\n        self.major = os.major(dev)\n        self.minor = os.minor(dev)\n\n    def test_call_methods(self):\n        finder = RootFsDeviceFinder()\n        if os.path.exists(\"/proc/partitions\"):\n            finder.ask_proc_partitions()\n        else:\n            with pytest.raises(FileNotFoundError):\n                finder.ask_proc_partitions()\n        if os.path.exists(f\"/sys/dev/block/{self.major}:{self.minor}/uevent\"):\n            finder.ask_sys_dev_block()\n        else:\n            with pytest.raises(FileNotFoundError):\n                finder.ask_sys_dev_block()\n        finder.ask_sys_class_block()\n\n    @pytest.mark.skipif(GITHUB_ACTIONS, reason=\"unsupported on GITHUB_ACTIONS\")\n    def test_comparisons(self):\n        finder = RootFsDeviceFinder()\n        assert finder.find() is not None\n\n        a = b = c = None\n        if os.path.exists(\"/proc/partitions\"):\n            a = finder.ask_proc_partitions()\n        if os.path.exists(f\"/sys/dev/block/{self.major}:{self.minor}/uevent\"):\n            b = finder.ask_sys_class_block()\n        c = finder.ask_sys_dev_block()\n\n        base = a or b or c\n        if base and a:\n            assert base == a\n        if base and b:\n            assert base == b\n        if base and c:\n            assert base == c\n\n    @pytest.mark.skipif(\n        not shutil.which(\"findmnt\"), reason=\"findmnt utility not available\"\n    )\n    @pytest.mark.skipif(GITHUB_ACTIONS, reason=\"unsupported on GITHUB_ACTIONS\")\n    def test_against_findmnt(self):\n        psutil_value = RootFsDeviceFinder().find()\n        findmnt_value = sh(\"findmnt -o SOURCE -rn /\")\n        assert psutil_value == findmnt_value\n\n    def test_disk_partitions_mocked(self):\n        with mock.patch(\n            'psutil._pslinux.cext.disk_partitions',\n            return_value=[('/dev/root', '/', 'ext4', 'rw')],\n        ) as m:\n            part = psutil.disk_partitions()[0]\n            assert m.called\n            if not GITHUB_ACTIONS:\n                assert part.device != \"/dev/root\"\n                assert part.device == RootFsDeviceFinder().find()\n            else:\n                assert part.device == \"/dev/root\"\n\n\n# =====================================================================\n# --- misc\n# =====================================================================\n\n\nclass TestMisc(LinuxTestCase):\n    def test_boot_time(self):\n        vmstat_value = vmstat('boot time')\n        psutil_value = psutil.boot_time()\n        assert int(vmstat_value) == int(psutil_value)\n\n    def test_no_procfs_on_import(self):\n        my_procfs = self.get_testfn()\n        os.mkdir(my_procfs)\n\n        with open(os.path.join(my_procfs, 'stat'), 'w') as f:\n            f.write('cpu   0 0 0 0 0 0 0 0 0 0\\n')\n            f.write('cpu0  0 0 0 0 0 0 0 0 0 0\\n')\n            f.write('cpu1  0 0 0 0 0 0 0 0 0 0\\n')\n\n        try:\n            orig_open = open\n\n            def open_mock(name, *args, **kwargs):\n                if name.startswith('/proc'):\n                    raise FileNotFoundError\n                return orig_open(name, *args, **kwargs)\n\n            with mock.patch(\"builtins.open\", side_effect=open_mock):\n                reload_module(psutil)\n\n                with pytest.raises(OSError):\n                    psutil.cpu_times()\n                with pytest.raises(OSError):\n                    psutil.cpu_times(percpu=True)\n                with pytest.raises(OSError):\n                    psutil.cpu_percent()\n                with pytest.raises(OSError):\n                    psutil.cpu_percent(percpu=True)\n                with pytest.raises(OSError):\n                    psutil.cpu_times_percent()\n                with pytest.raises(OSError):\n                    psutil.cpu_times_percent(percpu=True)\n\n                psutil.PROCFS_PATH = my_procfs\n\n                assert psutil.cpu_percent() == 0\n                assert sum(psutil.cpu_times_percent()) == 0\n\n                # since we don't know the number of CPUs at import time,\n                # we awkwardly say there are none until the second call\n                per_cpu_percent = psutil.cpu_percent(percpu=True)\n                assert sum(per_cpu_percent) == 0\n\n                # ditto awkward length\n                per_cpu_times_percent = psutil.cpu_times_percent(percpu=True)\n                assert sum(map(sum, per_cpu_times_percent)) == 0\n\n                # much user, very busy\n                with open(os.path.join(my_procfs, 'stat'), 'w') as f:\n                    f.write('cpu   1 0 0 0 0 0 0 0 0 0\\n')\n                    f.write('cpu0  1 0 0 0 0 0 0 0 0 0\\n')\n                    f.write('cpu1  1 0 0 0 0 0 0 0 0 0\\n')\n\n                assert psutil.cpu_percent() != 0\n                assert sum(psutil.cpu_percent(percpu=True)) != 0\n                assert sum(psutil.cpu_times_percent()) != 0\n                assert (\n                    sum(map(sum, psutil.cpu_times_percent(percpu=True))) != 0\n                )\n        finally:\n            shutil.rmtree(my_procfs)\n            reload_module(psutil)\n\n        assert psutil.PROCFS_PATH == '/proc'\n\n    def test_cpu_steal_decrease(self):\n        # Test cumulative cpu stats decrease. We should ignore this.\n        # See issue #1210.\n        content = textwrap.dedent(\"\"\"\\\n            cpu   0 0 0 0 0 0 0 1 0 0\n            cpu0  0 0 0 0 0 0 0 1 0 0\n            cpu1  0 0 0 0 0 0 0 1 0 0\n            \"\"\").encode()\n        with mock_open_content({\"/proc/stat\": content}) as m:\n            # first call to \"percent\" functions should read the new stat file\n            # and compare to the \"real\" file read at import time - so the\n            # values are meaningless\n            psutil.cpu_percent()\n            assert m.called\n            psutil.cpu_percent(percpu=True)\n            psutil.cpu_times_percent()\n            psutil.cpu_times_percent(percpu=True)\n\n        content = textwrap.dedent(\"\"\"\\\n            cpu   1 0 0 0 0 0 0 0 0 0\n            cpu0  1 0 0 0 0 0 0 0 0 0\n            cpu1  1 0 0 0 0 0 0 0 0 0\n            \"\"\").encode()\n        with mock_open_content({\"/proc/stat\": content}):\n            # Increase \"user\" while steal goes \"backwards\" to zero.\n            cpu_percent = psutil.cpu_percent()\n            assert m.called\n            cpu_percent_percpu = psutil.cpu_percent(percpu=True)\n            cpu_times_percent = psutil.cpu_times_percent()\n            cpu_times_percent_percpu = psutil.cpu_times_percent(percpu=True)\n            assert cpu_percent != 0\n            assert sum(cpu_percent_percpu) != 0\n            assert sum(cpu_times_percent) != 0\n            assert sum(cpu_times_percent) != 100.0\n            assert sum(map(sum, cpu_times_percent_percpu)) != 0\n            assert sum(map(sum, cpu_times_percent_percpu)) != 100.0\n            assert cpu_times_percent.steal == 0\n            assert cpu_times_percent.user != 0\n\n    def test_boot_time_mocked(self):\n        with mock.patch('psutil._common.open', create=True) as m:\n            with pytest.raises(RuntimeError):\n                psutil._pslinux.boot_time()\n            assert m.called\n\n    def test_users(self):\n        # Make sure the C extension converts ':0' and ':0.0' to\n        # 'localhost'.\n        for user in psutil.users():\n            assert user.host not in {\":0\", \":0.0\"}\n\n    def test_procfs_path(self):\n        tdir = self.get_testfn()\n        os.mkdir(tdir)\n        try:\n            psutil.PROCFS_PATH = tdir\n            with pytest.raises(OSError):\n                psutil.virtual_memory()\n            with pytest.raises(OSError):\n                psutil.cpu_times()\n            with pytest.raises(OSError):\n                psutil.cpu_times(percpu=True)\n            with pytest.raises(OSError):\n                psutil.boot_time()\n            with pytest.raises(OSError):\n                psutil.net_connections()\n            with pytest.raises(OSError):\n                psutil.net_io_counters()\n            with pytest.raises(OSError):\n                psutil.net_if_stats()\n            with pytest.raises(OSError):\n                psutil.disk_partitions()\n            with pytest.raises(psutil.NoSuchProcess):\n                psutil.Process()\n        finally:\n            psutil.PROCFS_PATH = \"/proc\"\n\n    @retry_on_failure()\n    @pytest.mark.xdist_group(name=\"serial\")\n    def test_issue_687(self):\n        # In case of thread ID:\n        # - pid_exists() is supposed to return False\n        # - Process(tid) is supposed to work\n        # - pids() should not return the TID\n        # See: https://github.com/giampaolo/psutil/issues/687\n\n        p = psutil.Process()\n        nthreads = len(p.threads())\n        with ThreadTask():\n            threads = p.threads()\n            assert len(threads) == nthreads + 1\n            tid = sorted(threads, key=lambda x: x.id)[1].id\n            assert p.pid != tid\n            pt = psutil.Process(tid)\n            pt.as_dict()\n            assert tid not in psutil.pids()\n\n    def test_pid_exists_no_proc_status(self):\n        # Internally pid_exists relies on /proc/{pid}/status.\n        # Emulate a case where this file is empty in which case\n        # psutil is supposed to fall back on using pids().\n        with mock_open_content({\"/proc/%s/status\": \"\"}) as m:\n            assert psutil.pid_exists(os.getpid())\n            assert m.called\n\n\n# =====================================================================\n# --- sensors\n# =====================================================================\n\n\n@pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\nclass TestSensorsBattery(LinuxTestCase):\n    @pytest.mark.skipif(\n        not shutil.which(\"acpi\"), reason=\"acpi utility not available\"\n    )\n    def test_percent(self):\n        out = sh(\"acpi -b\")\n        acpi_value = int(out.split(\",\")[1].strip().replace('%', ''))\n        psutil_value = psutil.sensors_battery().percent\n        assert abs(acpi_value - psutil_value) < 1\n\n    def test_emulate_power_plugged(self):\n        # Pretend the AC power cable is connected.\n        def open_mock(name, *args, **kwargs):\n            if name.endswith(('AC0/online', 'AC/online')):\n                return io.BytesIO(b\"1\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock) as m:\n            assert psutil.sensors_battery().power_plugged is True\n            assert (\n                psutil.sensors_battery().secsleft\n                == psutil.POWER_TIME_UNLIMITED\n            )\n            assert m.called\n\n    def test_emulate_power_plugged_2(self):\n        # Same as above but pretend /AC0/online does not exist in which\n        # case code relies on /status file.\n        def open_mock(name, *args, **kwargs):\n            if name.endswith(('AC0/online', 'AC/online')):\n                raise FileNotFoundError\n            if name.endswith(\"/status\"):\n                return io.StringIO(\"charging\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock) as m:\n            assert psutil.sensors_battery().power_plugged is True\n            assert m.called\n\n    def test_emulate_power_not_plugged(self):\n        # Pretend the AC power cable is not connected.\n        def open_mock(name, *args, **kwargs):\n            if name.endswith(('AC0/online', 'AC/online')):\n                return io.BytesIO(b\"0\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock) as m:\n            assert psutil.sensors_battery().power_plugged is False\n            assert m.called\n\n    def test_emulate_power_not_plugged_2(self):\n        # Same as above but pretend /AC0/online does not exist in which\n        # case code relies on /status file.\n        def open_mock(name, *args, **kwargs):\n            if name.endswith(('AC0/online', 'AC/online')):\n                raise FileNotFoundError\n            if name.endswith(\"/status\"):\n                return io.StringIO(\"discharging\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock) as m:\n            assert psutil.sensors_battery().power_plugged is False\n            assert m.called\n\n    def test_emulate_power_undetermined(self):\n        # Pretend we can't know whether the AC power cable not\n        # connected (assert fallback to False).\n        def open_mock(name, *args, **kwargs):\n            if name.startswith((\n                '/sys/class/power_supply/AC0/online',\n                '/sys/class/power_supply/AC/online',\n            )):\n                raise FileNotFoundError\n            if name.startswith(\"/sys/class/power_supply/BAT0/status\"):\n                return io.BytesIO(b\"???\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock) as m:\n            assert psutil.sensors_battery().power_plugged is None\n            assert m.called\n\n    def test_emulate_energy_full_0(self):\n        # Emulate a case where energy_full files returns 0.\n        with mock_open_content(\n            {\"/sys/class/power_supply/BAT0/energy_full\": b\"0\"}\n        ) as m:\n            assert psutil.sensors_battery().percent == 0\n            assert m.called\n\n    def test_emulate_energy_full_not_avail(self):\n        # Emulate a case where energy_full file does not exist.\n        # Expected fallback on /capacity.\n        with mock_open_exception(\n            \"/sys/class/power_supply/BAT0/energy_full\",\n            FileNotFoundError,\n        ):\n            with mock_open_exception(\n                \"/sys/class/power_supply/BAT0/charge_full\",\n                FileNotFoundError,\n            ):\n                with mock_open_content(\n                    {\"/sys/class/power_supply/BAT0/capacity\": b\"88\"}\n                ):\n                    assert psutil.sensors_battery().percent == 88\n\n    @pytest.mark.skipif(\n        not os.path.isfile(\"/sys/class/power_supply/BAT0/capacity\"),\n        reason=\"BAT /capacity file don't exist\",\n    )\n    def test_percent_against_capacity(self):\n        # Internally, if we have /energy_full, the percentage will be\n        # calculated by NOT reading the /capacity file, to get more\n        # accuracy. Check againt /capacity to make sure our percentage\n        # is calculated correctly.\n        with open(\"/sys/class/power_supply/BAT0/capacity\") as f:\n            capacity = float(f.read())\n        assert psutil.sensors_battery().percent == pytest.approx(\n            capacity, abs=1\n        )\n\n    def test_emulate_no_power(self):\n        # Emulate a case where /AC0/online file nor /BAT0/status exist.\n        with mock_open_exception(\n            \"/sys/class/power_supply/AC/online\", FileNotFoundError\n        ):\n            with mock_open_exception(\n                \"/sys/class/power_supply/AC0/online\", FileNotFoundError\n            ):\n                with mock_open_exception(\n                    \"/sys/class/power_supply/BAT0/status\",\n                    FileNotFoundError,\n                ):\n                    assert psutil.sensors_battery().power_plugged is None\n\n\nclass TestSensorsBatteryEmulated(LinuxTestCase):\n    def test_it(self):\n        def open_mock(name, *args, **kwargs):\n            if name.endswith(\"/energy_now\"):\n                return io.StringIO(\"60000000\")\n            elif name.endswith(\"/power_now\"):\n                return io.StringIO(\"0\")\n            elif name.endswith(\"/energy_full\"):\n                return io.StringIO(\"60000001\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch('os.listdir', return_value=[\"BAT0\"]) as mlistdir:\n            with mock.patch(\"builtins.open\", side_effect=open_mock) as mopen:\n                assert psutil.sensors_battery() is not None\n        assert mlistdir.called\n        assert mopen.called\n\n\nclass TestSensorsTemperatures(LinuxTestCase):\n    def test_emulate_class_hwmon(self):\n        def open_mock(name, *args, **kwargs):\n            if name.endswith('/name'):\n                return io.StringIO(\"name\")\n            elif name.endswith('/temp1_label'):\n                return io.StringIO(\"label\")\n            elif name.endswith('/temp1_input'):\n                return io.BytesIO(b\"30000\")\n            elif name.endswith('/temp1_max'):\n                return io.BytesIO(b\"40000\")\n            elif name.endswith('/temp1_crit'):\n                return io.BytesIO(b\"50000\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock):\n            # Test case with /sys/class/hwmon\n            with mock.patch(\n                'glob.glob', return_value=['/sys/class/hwmon/hwmon0/temp1']\n            ):\n                temp = psutil.sensors_temperatures()['name'][0]\n                assert temp.label == 'label'\n                assert temp.current == 30.0\n                assert temp.high == 40.0\n                assert temp.critical == 50.0\n\n    def test_emulate_class_thermal(self):\n        def open_mock(name, *args, **kwargs):\n            if name.endswith('0_temp'):\n                return io.BytesIO(b\"50000\")\n            elif name.endswith('temp'):\n                return io.BytesIO(b\"30000\")\n            elif name.endswith('0_type'):\n                return io.StringIO(\"critical\")\n            elif name.endswith('type'):\n                return io.StringIO(\"name\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        def glob_mock(path):\n            if path in {\n                '/sys/class/hwmon/hwmon*/temp*_*',\n                '/sys/class/hwmon/hwmon*/device/temp*_*',\n            }:\n                return []\n            elif path == '/sys/class/thermal/thermal_zone*':\n                return ['/sys/class/thermal/thermal_zone0']\n            elif path == '/sys/class/thermal/thermal_zone0/trip_point*':\n                return [\n                    '/sys/class/thermal/thermal_zone1/trip_point_0_type',\n                    '/sys/class/thermal/thermal_zone1/trip_point_0_temp',\n                ]\n            return []\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock):\n            with mock.patch('glob.glob', create=True, side_effect=glob_mock):\n                temp = psutil.sensors_temperatures()['name'][0]\n                assert temp.label == ''\n                assert temp.current == 30.0\n                assert temp.high == 50.0\n                assert temp.critical == 50.0\n\n\nclass TestSensorsFans(LinuxTestCase):\n    def test_emulate_data(self):\n        def open_mock(name, *args, **kwargs):\n            if name.endswith('/name'):\n                return io.StringIO(\"name\")\n            elif name.endswith('/fan1_label'):\n                return io.StringIO(\"label\")\n            elif name.endswith('/fan1_input'):\n                return io.StringIO(\"2000\")\n            else:\n                return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock):\n            with mock.patch(\n                'glob.glob', return_value=['/sys/class/hwmon/hwmon2/fan1']\n            ):\n                fan = psutil.sensors_fans()['name'][0]\n                assert fan.label == 'label'\n                assert fan.current == 2000\n\n\n# =====================================================================\n# --- test process\n# =====================================================================\n\n\nclass TestProcess(LinuxTestCase):\n    @retry_on_failure()\n    def test_parse_smaps_vs_memory_maps(self):\n        sproc = self.spawn_subproc()\n        uss, pss, swap = psutil._pslinux.Process(sproc.pid)._parse_smaps()\n        maps = psutil.Process(sproc.pid).memory_maps(grouped=False)\n        assert (\n            abs(uss - sum(x.private_dirty + x.private_clean for x in maps))\n            < 4096\n        )\n        assert abs(pss - sum(x.pss for x in maps)) < 4096\n        assert abs(swap - sum(x.swap for x in maps)) < 4096\n\n    def test_parse_smaps_mocked(self):\n        # See: https://github.com/giampaolo/psutil/issues/1222\n        content = textwrap.dedent(\"\"\"\\\n            fffff0 r-xp 00000000 00:00 0                  [vsyscall]\n            Size:                  1 kB\n            Rss:                   2 kB\n            Pss:                   3 kB\n            Shared_Clean:          4 kB\n            Shared_Dirty:          5 kB\n            Private_Clean:         6 kB\n            Private_Dirty:         7 kB\n            Referenced:            8 kB\n            Anonymous:             9 kB\n            LazyFree:              10 kB\n            AnonHugePages:         11 kB\n            ShmemPmdMapped:        12 kB\n            Shared_Hugetlb:        13 kB\n            Private_Hugetlb:       14 kB\n            Swap:                  15 kB\n            SwapPss:               16 kB\n            KernelPageSize:        17 kB\n            MMUPageSize:           18 kB\n            Locked:                19 kB\n            VmFlags: rd ex\n            \"\"\").encode()\n        with mock_open_content({f\"/proc/{os.getpid()}/smaps\": content}) as m:\n            p = psutil._pslinux.Process(os.getpid())\n            uss, pss, swap = p._parse_smaps()\n            assert m.called\n            assert uss == (6 + 7 + 14) * 1024\n            assert pss == 3 * 1024\n            assert swap == 15 * 1024\n\n    def test_open_files_mode(self):\n        def get_test_file(fname):\n            p = psutil.Process()\n            giveup_at = time.time() + GLOBAL_TIMEOUT\n            while True:\n                for file in p.open_files():\n                    if file.path == os.path.abspath(fname):\n                        return file\n                    elif time.time() > giveup_at:\n                        break\n            raise RuntimeError(\"timeout looking for test file\")\n\n        testfn = self.get_testfn()\n        with open(testfn, \"w\"):\n            assert get_test_file(testfn).mode == \"w\"\n        with open(testfn):\n            assert get_test_file(testfn).mode == \"r\"\n        with open(testfn, \"a\"):\n            assert get_test_file(testfn).mode == \"a\"\n        with open(testfn, \"r+\"):\n            assert get_test_file(testfn).mode == \"r+\"\n        with open(testfn, \"w+\"):\n            assert get_test_file(testfn).mode == \"r+\"\n        with open(testfn, \"a+\"):\n            assert get_test_file(testfn).mode == \"a+\"\n\n        safe_rmpath(testfn)\n        with open(testfn, \"x\"):\n            assert get_test_file(testfn).mode == \"w\"\n        safe_rmpath(testfn)\n        with open(testfn, \"x+\"):\n            assert get_test_file(testfn).mode == \"r+\"\n\n    def test_open_files_file_gone(self):\n        # simulates a file which gets deleted during open_files()\n        # execution\n        p = psutil.Process()\n        files = p.open_files()\n        with open(self.get_testfn(), 'w'):\n            # give the kernel some time to see the new file\n            call_until(lambda: len(p.open_files()) != len(files))\n            with mock.patch(\n                'psutil._pslinux.os.readlink',\n                side_effect=FileNotFoundError,\n            ) as m:\n                assert p.open_files() == []\n                assert m.called\n            # also simulate the case where os.readlink() returns EINVAL\n            # in which case psutil is supposed to 'continue'\n            with mock.patch(\n                'psutil._pslinux.os.readlink',\n                side_effect=OSError(errno.EINVAL, \"\"),\n            ) as m:\n                assert p.open_files() == []\n                assert m.called\n\n    def test_open_files_fd_gone(self):\n        # Simulate a case where /proc/{pid}/fdinfo/{fd} disappears\n        # while iterating through fds.\n        # https://travis-ci.org/giampaolo/psutil/jobs/225694530\n        p = psutil.Process()\n        files = p.open_files()\n        with open(self.get_testfn(), 'w'):\n            # give the kernel some time to see the new file\n            call_until(lambda: len(p.open_files()) != len(files))\n            with mock.patch(\n                \"builtins.open\", side_effect=FileNotFoundError\n            ) as m:\n                assert p.open_files() == []\n                assert m.called\n\n    def test_open_files_enametoolong(self):\n        # Simulate a case where /proc/{pid}/fd/{fd} symlink\n        # points to a file with full path longer than PATH_MAX, see:\n        # https://github.com/giampaolo/psutil/issues/1940\n        p = psutil.Process()\n        files = p.open_files()\n        with open(self.get_testfn(), 'w'):\n            # give the kernel some time to see the new file\n            call_until(lambda: len(p.open_files()) != len(files))\n            patch_point = 'psutil._pslinux.os.readlink'\n            with mock.patch(\n                patch_point, side_effect=OSError(errno.ENAMETOOLONG, \"\")\n            ) as m:\n                with mock.patch(\"psutil._pslinux.debug\"):\n                    assert p.open_files() == []\n                    assert m.called\n\n    # --- mocked tests\n\n    def test_terminal_mocked(self):\n        with mock.patch(\n            'psutil._pslinux._psposix.get_terminal_map', return_value={}\n        ) as m:\n            assert psutil._pslinux.Process(os.getpid()).terminal() is None\n            assert m.called\n\n    def test_cmdline_mocked(self):\n        # see: https://github.com/giampaolo/psutil/issues/639\n        p = psutil.Process()\n        fake_file = io.StringIO('foo\\x00bar\\x00')\n        with mock.patch(\n            'psutil._common.open', return_value=fake_file, create=True\n        ) as m:\n            assert p.cmdline() == ['foo', 'bar']\n            assert m.called\n        fake_file = io.StringIO('foo\\x00bar\\x00\\x00')\n        with mock.patch(\n            'psutil._common.open', return_value=fake_file, create=True\n        ) as m:\n            assert p.cmdline() == ['foo', 'bar', '']\n            assert m.called\n\n    def test_cmdline_spaces_mocked(self):\n        # see: https://github.com/giampaolo/psutil/issues/1179\n        p = psutil.Process()\n        fake_file = io.StringIO('foo bar ')\n        with mock.patch(\n            'psutil._common.open', return_value=fake_file, create=True\n        ) as m:\n            assert p.cmdline() == ['foo', 'bar']\n            assert m.called\n        fake_file = io.StringIO('foo bar  ')\n        with mock.patch(\n            'psutil._common.open', return_value=fake_file, create=True\n        ) as m:\n            assert p.cmdline() == ['foo', 'bar', '']\n            assert m.called\n\n    def test_cmdline_mixed_separators(self):\n        # https://github.com/giampaolo/psutil/issues/1179#issuecomment-552984549\n        p = psutil.Process()\n        fake_file = io.StringIO('foo\\x20bar\\x00')\n        with mock.patch(\n            'psutil._common.open', return_value=fake_file, create=True\n        ) as m:\n            assert p.cmdline() == ['foo', 'bar']\n            assert m.called\n\n    def test_readlink_path_deleted_mocked(self):\n        with mock.patch(\n            'psutil._pslinux.os.readlink', return_value='/home/foo (deleted)'\n        ):\n            assert psutil.Process().exe() == \"/home/foo\"\n            assert psutil.Process().cwd() == \"/home/foo\"\n\n    def test_threads_mocked(self):\n        # Test the case where os.listdir() returns a file (thread)\n        # which no longer exists by the time we open() it (race\n        # condition). threads() is supposed to ignore that instead\n        # of raising NSP.\n        def open_mock_1(name, *args, **kwargs):\n            if name.startswith(f\"/proc/{os.getpid()}/task\"):\n                raise FileNotFoundError\n            return orig_open(name, *args, **kwargs)\n\n        orig_open = open\n        with mock.patch(\"builtins.open\", side_effect=open_mock_1) as m:\n            ret = psutil.Process().threads()\n            assert m.called\n            assert ret == []\n\n        # ...but if it bumps into something != ENOENT we want an\n        # exception.\n        def open_mock_2(name, *args, **kwargs):\n            if name.startswith(f\"/proc/{os.getpid()}/task\"):\n                raise PermissionError\n            return orig_open(name, *args, **kwargs)\n\n        with mock.patch(\"builtins.open\", side_effect=open_mock_2):\n            with pytest.raises(psutil.AccessDenied):\n                psutil.Process().threads()\n\n    def test_exe_mocked(self):\n        with mock.patch(\n            'psutil._pslinux.readlink', side_effect=FileNotFoundError\n        ) as m:\n            # de-activate guessing from cmdline()\n            with mock.patch(\n                'psutil._pslinux.Process.cmdline', return_value=[]\n            ):\n                ret = psutil.Process().exe()\n                assert m.called\n                assert ret == \"\"\n\n    def test_cwd_mocked(self):\n        # https://github.com/giampaolo/psutil/issues/2514\n        with mock.patch(\n            'psutil._pslinux.readlink', side_effect=FileNotFoundError\n        ) as m:\n            ret = psutil.Process().cwd()\n            assert m.called\n            assert ret == \"\"\n\n    def test_issue_1014(self):\n        # Emulates a case where smaps file does not exist. In this case\n        # wrap_exception decorator should not raise NoSuchProcess.\n        with mock_open_exception(\n            f\"/proc/{os.getpid()}/smaps\", FileNotFoundError\n        ) as m:\n            p = psutil.Process()\n            with pytest.raises(FileNotFoundError):\n                p.memory_maps()\n            assert m.called\n\n    def test_issue_2418(self):\n        p = psutil.Process()\n        with mock_open_exception(\n            f\"/proc/{os.getpid()}/statm\", FileNotFoundError\n        ):\n            with mock.patch(\"os.path.exists\", return_value=False):\n                with pytest.raises(psutil.NoSuchProcess):\n                    p.memory_info()\n\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_zombie(self):\n        # Emulate a case where rlimit() raises ENOSYS, which may\n        # happen in case of zombie process:\n        # https://travis-ci.org/giampaolo/psutil/jobs/51368273\n        with mock.patch(\n            \"resource.prlimit\", side_effect=OSError(errno.ENOSYS, \"\")\n        ) as m1:\n            with mock.patch(\n                \"psutil._pslinux.Process._is_zombie\", return_value=True\n            ) as m2:\n                p = psutil.Process()\n                p.name()\n                with pytest.raises(psutil.ZombieProcess) as cm:\n                    p.rlimit(psutil.RLIMIT_NOFILE)\n        assert m1.called\n        assert m2.called\n        assert cm.value.pid == p.pid\n        assert cm.value.name == p.name()\n\n    def test_stat_file_parsing(self):\n        args = [\n            \"0\",  # pid\n            \"(cat)\",  # name\n            \"Z\",  # status\n            \"1\",  # ppid\n            \"0\",  # pgrp\n            \"0\",  # session\n            \"0\",  # tty\n            \"0\",  # tpgid\n            \"0\",  # flags\n            \"0\",  # minflt\n            \"0\",  # cminflt\n            \"0\",  # majflt\n            \"0\",  # cmajflt\n            \"2\",  # utime\n            \"3\",  # stime\n            \"4\",  # cutime\n            \"5\",  # cstime\n            \"0\",  # priority\n            \"0\",  # nice\n            \"0\",  # num_threads\n            \"0\",  # itrealvalue\n            \"6\",  # starttime\n            \"0\",  # vsize\n            \"0\",  # rss\n            \"0\",  # rsslim\n            \"0\",  # startcode\n            \"0\",  # endcode\n            \"0\",  # startstack\n            \"0\",  # kstkesp\n            \"0\",  # kstkeip\n            \"0\",  # signal\n            \"0\",  # blocked\n            \"0\",  # sigignore\n            \"0\",  # sigcatch\n            \"0\",  # wchan\n            \"0\",  # nswap\n            \"0\",  # cnswap\n            \"0\",  # exit_signal\n            \"6\",  # processor\n            \"0\",  # rt priority\n            \"0\",  # policy\n            \"7\",  # delayacct_blkio_ticks\n        ]\n        content = \" \".join(args).encode()\n        with mock_open_content({f\"/proc/{os.getpid()}/stat\": content}):\n            p = psutil.Process()\n            assert p.name() == 'cat'\n            assert p.status() == psutil.STATUS_ZOMBIE\n            assert p.ppid() == 1\n            assert p.create_time() == 6 / CLOCK_TICKS + psutil.boot_time()\n            cpu = p.cpu_times()\n            assert cpu.user == 2 / CLOCK_TICKS\n            assert cpu.system == 3 / CLOCK_TICKS\n            assert cpu.children_user == 4 / CLOCK_TICKS\n            assert cpu.children_system == 5 / CLOCK_TICKS\n            assert cpu.iowait == 7 / CLOCK_TICKS\n            assert p.cpu_num() == 6\n\n    def test_status_file_parsing(self):\n        content = textwrap.dedent(\"\"\"\\\n            Uid:\\t1000\\t1001\\t1002\\t1003\n            Gid:\\t1004\\t1005\\t1006\\t1007\n            Threads:\\t66\n            Cpus_allowed:\\tf\n            Cpus_allowed_list:\\t0-7\n            voluntary_ctxt_switches:\\t12\n            nonvoluntary_ctxt_switches:\\t13\"\"\").encode()\n        with mock_open_content({f\"/proc/{os.getpid()}/status\": content}):\n            p = psutil.Process()\n            assert p.num_ctx_switches().voluntary == 12\n            assert p.num_ctx_switches().involuntary == 13\n            assert p.num_threads() == 66\n            uids = p.uids()\n            assert uids.real == 1000\n            assert uids.effective == 1001\n            assert uids.saved == 1002\n            gids = p.gids()\n            assert gids.real == 1004\n            assert gids.effective == 1005\n            assert gids.saved == 1006\n            assert p._proc._get_eligible_cpus() == list(range(8))\n\n    def test_net_connections_enametoolong(self):\n        # Simulate a case where /proc/{pid}/fd/{fd} symlink points to\n        # a file with full path longer than PATH_MAX, see:\n        # https://github.com/giampaolo/psutil/issues/1940\n        with mock.patch(\n            'psutil._pslinux.os.readlink',\n            side_effect=OSError(errno.ENAMETOOLONG, \"\"),\n        ) as m:\n            p = psutil.Process()\n            with mock.patch(\"psutil._pslinux.debug\"):\n                assert p.net_connections() == []\n                assert m.called\n\n    def test_create_time_monotonic(self):\n        p = psutil.Process()\n        assert p._proc.create_time() != p._proc.create_time(monotonic=True)\n        assert p._get_ident()[1] == p._proc.create_time(monotonic=True)\n\n    def test_memory_info_ex(self):\n        mem = psutil.Process().memory_info_ex()\n        assert mem.rss == mem.rss_anon + mem.rss_file + mem.rss_shmem\n\n\nclass TestProcessAgainstStatus(LinuxTestCase):\n    \"\"\"/proc/pid/stat and /proc/pid/status have many values in common.\n    Whenever possible, psutil uses /proc/pid/stat (it's faster).\n    For all those cases we check that the value found in\n    /proc/pid/stat (by psutil) matches the one found in\n    /proc/pid/status.\n    \"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        cls.proc = psutil.Process()\n\n    def read_status_file(self, linestart):\n        with psutil._psplatform.open_text(\n            f\"/proc/{self.proc.pid}/status\"\n        ) as f:\n            for line in f:\n                line = line.strip()\n                if line.startswith(linestart):\n                    value = line.partition('\\t')[2]\n                    try:\n                        return int(value)\n                    except ValueError:\n                        return value\n            raise ValueError(f\"can't find {linestart!r}\")\n\n    def test_name(self):\n        value = self.read_status_file(\"Name:\")\n        assert self.proc.name() == value\n\n    def test_status(self):\n        value = self.read_status_file(\"State:\")\n        value = value[value.find('(') + 1 : value.rfind(')')]\n        value = value.replace(' ', '-')\n        assert self.proc.status() == value\n\n    def test_ppid(self):\n        value = self.read_status_file(\"PPid:\")\n        assert self.proc.ppid() == value\n\n    def test_num_threads(self):\n        value = self.read_status_file(\"Threads:\")\n        assert self.proc.num_threads() == value\n\n    def test_uids(self):\n        value = self.read_status_file(\"Uid:\")\n        value = tuple(map(int, value.split()[1:4]))\n        assert self.proc.uids() == value\n\n    def test_gids(self):\n        value = self.read_status_file(\"Gid:\")\n        value = tuple(map(int, value.split()[1:4]))\n        assert self.proc.gids() == value\n\n    @retry_on_failure()\n    def test_num_ctx_switches(self):\n        value = self.read_status_file(\"voluntary_ctxt_switches:\")\n        assert self.proc.num_ctx_switches().voluntary == value\n        value = self.read_status_file(\"nonvoluntary_ctxt_switches:\")\n        assert self.proc.num_ctx_switches().involuntary == value\n\n    def test_cpu_affinity(self):\n        value = self.read_status_file(\"Cpus_allowed_list:\")\n        if '-' in str(value):\n            min_, max_ = map(int, value.split('-'))\n            assert self.proc.cpu_affinity() == list(range(min_, max_ + 1))\n\n    def test_cpu_affinity_eligible_cpus(self):\n        value = self.read_status_file(\"Cpus_allowed_list:\")\n        with mock.patch(\"psutil._pslinux.per_cpu_times\") as m:\n            self.proc._proc._get_eligible_cpus()\n        if '-' in str(value):\n            assert not m.called\n        else:\n            assert m.called\n\n\n# =====================================================================\n# --- test utils\n# =====================================================================\n\n\nclass TestUtils(LinuxTestCase):\n    def test_readlink(self):\n        with mock.patch(\"os.readlink\", return_value=\"foo (deleted)\") as m:\n            assert psutil._psplatform.readlink(\"bar\") == \"foo\"\n            assert m.called\n"
  },
  {
    "path": "tests/test_memleaks.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Regression test suite for detecting memory leaks in the underlying C\nextension. Requires https://github.com/giampaolo/psleak.\n\"\"\"\n\nimport functools\nimport os\n\nfrom psleak import MemoryLeakTestCase\n\nimport psutil\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import SUNOS\nfrom psutil import WINDOWS\n\nfrom . import HAS_CPU_FREQ\nfrom . import HAS_HEAP_INFO\nfrom . import HAS_NET_IO_COUNTERS\nfrom . import HAS_PROC_CPU_AFFINITY\nfrom . import HAS_PROC_CPU_NUM\nfrom . import HAS_PROC_ENVIRON\nfrom . import HAS_PROC_IO_COUNTERS\nfrom . import HAS_PROC_IONICE\nfrom . import HAS_PROC_MEMORY_FOOTPRINT\nfrom . import HAS_PROC_MEMORY_MAPS\nfrom . import HAS_PROC_RLIMIT\nfrom . import HAS_SENSORS_BATTERY\nfrom . import HAS_SENSORS_FANS\nfrom . import HAS_SENSORS_TEMPERATURES\nfrom . import create_sockets\nfrom . import get_testfn\nfrom . import process_namespace\nfrom . import pytest\nfrom . import skip_on_access_denied\nfrom . import spawn_subproc\nfrom . import system_namespace\nfrom . import terminate\n\ncext = psutil._psplatform.cext\nthisproc = psutil.Process()\n\n\nMemoryLeakTestCase.retries = 30  # minimize false positives\nMemoryLeakTestCase.verbosity = 1\n\nTIMES = MemoryLeakTestCase.times\nFEW_TIMES = int(TIMES / 10)\n\n# ===================================================================\n# Process class\n# ===================================================================\n\n\nclass TestProcessObjectLeaks(MemoryLeakTestCase):\n    \"\"\"Test leaks of Process class methods.\"\"\"\n\n    proc = thisproc\n\n    def test_coverage(self):\n        ns = process_namespace(None)\n        ns.test_class_coverage(self, ns.getters + ns.setters)\n\n    def test_name(self):\n        self.execute(self.proc.name)\n\n    def test_cmdline(self):\n        if WINDOWS and self.proc.is_running():\n            self.proc.cmdline()\n        self.execute(self.proc.cmdline)\n\n    def test_exe(self):\n        self.execute(self.proc.exe)\n\n    def test_ppid(self):\n        self.execute(self.proc.ppid)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_uids(self):\n        self.execute(self.proc.uids)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_gids(self):\n        self.execute(self.proc.gids)\n\n    def test_status(self):\n        self.execute(self.proc.status)\n\n    def test_nice(self):\n        self.execute(self.proc.nice)\n\n    def test_nice_set(self):\n        niceness = thisproc.nice()\n        self.execute(lambda: self.proc.nice(niceness))\n\n    @pytest.mark.skipif(not HAS_PROC_IONICE, reason=\"not supported\")\n    def test_ionice(self):\n        self.execute(self.proc.ionice)\n\n    @pytest.mark.skipif(not HAS_PROC_IONICE, reason=\"not supported\")\n    def test_ionice_set(self):\n        if WINDOWS:\n            value = thisproc.ionice()\n            self.execute(lambda: self.proc.ionice(value))\n        else:\n            self.execute(lambda: self.proc.ionice(psutil.IOPRIO_CLASS_NONE))\n\n    @pytest.mark.skipif(not HAS_PROC_IONICE, reason=\"not supported\")\n    @pytest.mark.skipif(WINDOWS, reason=\"not on WINDOWS\")\n    def test_ionice_set_badarg(self):\n        fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0)\n        self.execute_w_exc(OSError, fun, retries=20)\n\n    @pytest.mark.skipif(not HAS_PROC_IO_COUNTERS, reason=\"not supported\")\n    def test_io_counters(self):\n        self.execute(self.proc.io_counters)\n\n    @pytest.mark.skipif(POSIX, reason=\"worthless on POSIX\")\n    def test_username(self):\n        # always open 1 handle on Windows (only once)\n        psutil.Process().username()\n        self.execute(self.proc.username)\n\n    def test_create_time(self):\n        self.execute(self.proc.create_time)\n\n    @skip_on_access_denied(only_if=OPENBSD)\n    def test_num_threads(self):\n        self.execute(self.proc.num_threads)\n\n    @pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n    def test_num_handles(self):\n        self.execute(self.proc.num_handles)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_num_fds(self):\n        self.execute(self.proc.num_fds)\n\n    def test_num_ctx_switches(self):\n        self.execute(self.proc.num_ctx_switches)\n\n    @skip_on_access_denied(only_if=OPENBSD)\n    def test_threads(self):\n        kw = {\"times\": 50} if WINDOWS else {}\n        self.execute(self.proc.threads, **kw)\n\n    def test_cpu_times(self):\n        self.execute(self.proc.cpu_times)\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_NUM, reason=\"not supported\")\n    def test_cpu_num(self):\n        self.execute(self.proc.cpu_num)\n\n    def test_memory_info(self):\n        self.execute(self.proc.memory_info)\n\n    def test_memory_info_ex(self):\n        self.execute(self.proc.memory_info_ex)\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_FOOTPRINT, reason=\"not supported\")\n    def test_memory_footprint(self):\n        self.execute(self.proc.memory_footprint)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_terminal(self):\n        self.execute(self.proc.terminal)\n\n    def test_resume(self):\n        times = FEW_TIMES if POSIX else self.times\n        self.execute(self.proc.resume, times=times)\n\n    def test_cwd(self):\n        self.execute(self.proc.cwd)\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_AFFINITY, reason=\"not supported\")\n    def test_cpu_affinity(self):\n        self.execute(self.proc.cpu_affinity)\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_AFFINITY, reason=\"not supported\")\n    def test_cpu_affinity_set(self):\n        affinity = thisproc.cpu_affinity()\n        self.execute(lambda: self.proc.cpu_affinity(affinity))\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_AFFINITY, reason=\"not supported\")\n    def test_cpu_affinity_set_badarg(self):\n        self.execute_w_exc(\n            ValueError, lambda: self.proc.cpu_affinity([-1]), retries=20\n        )\n\n    def test_open_files(self):\n        kw = {\"times\": 10, \"retries\": 30} if WINDOWS else {}\n        with open(get_testfn(), 'w'):\n            self.execute(self.proc.open_files, **kw)\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_MAPS, reason=\"not supported\")\n    @pytest.mark.skipif(LINUX, reason=\"too slow on LINUX\")\n    def test_memory_maps(self):\n        self.execute(self.proc.memory_maps, times=60, retries=10)\n\n    def test_page_faults(self):\n        self.execute(self.proc.page_faults)\n\n    @pytest.mark.skipif(not LINUX, reason=\"LINUX only\")\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit(self):\n        self.execute(lambda: self.proc.rlimit(psutil.RLIMIT_NOFILE))\n\n    @pytest.mark.skipif(not LINUX, reason=\"LINUX only\")\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_set(self):\n        limit = thisproc.rlimit(psutil.RLIMIT_NOFILE)\n        self.execute(lambda: self.proc.rlimit(psutil.RLIMIT_NOFILE, limit))\n\n    @pytest.mark.skipif(not LINUX, reason=\"LINUX only\")\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_set_badarg(self):\n        self.execute_w_exc(\n            (OSError, ValueError), lambda: self.proc.rlimit(-1), retries=20\n        )\n\n    # Windows implementation is based on a single system-wide\n    # function (tested later).\n    @pytest.mark.skipif(WINDOWS, reason=\"worthless on WINDOWS\")\n    def test_net_connections(self):\n        # TODO: UNIX sockets are temporarily implemented by parsing\n        # 'pfiles' cmd  output; we don't want that part of the code to\n        # be executed.\n        times = FEW_TIMES if LINUX else self.times\n        with create_sockets():\n            kind = 'inet' if SUNOS else 'all'\n            self.execute(lambda: self.proc.net_connections(kind), times=times)\n\n    @pytest.mark.skipif(not HAS_PROC_ENVIRON, reason=\"not supported\")\n    def test_environ(self):\n        self.execute(self.proc.environ)\n\n    @pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n    def test_proc_oneshot(self):\n        self.execute(lambda: cext.proc_oneshot(os.getpid()))\n\n\nclass TestTerminatedProcessLeaks(TestProcessObjectLeaks):\n    \"\"\"Repeat the tests above looking for leaks occurring when dealing\n    with terminated processes raising NoSuchProcess exception.\n    The C functions are still invoked but will follow different code\n    paths. We'll check those code paths.\n    \"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        cls.subp = spawn_subproc()\n        cls.proc = psutil.Process(cls.subp.pid)\n        cls.proc.kill()\n        cls.proc.wait()\n\n    @classmethod\n    def tearDownClass(cls):\n        super().tearDownClass()\n        terminate(cls.subp)\n\n    def call(self, fun):\n        try:\n            fun()\n        except psutil.NoSuchProcess:\n            pass\n\n    def test_cpu_affinity_set_badarg(self):\n        raise pytest.skip(\"skip\")\n\n    if WINDOWS:\n\n        def test_kill(self):\n            self.execute(self.proc.kill)\n\n        def test_terminate(self):\n            self.execute(self.proc.terminate)\n\n        def test_suspend(self):\n            self.execute(self.proc.suspend)\n\n        def test_resume(self):\n            self.execute(self.proc.resume)\n\n        def test_wait(self):\n            self.execute(self.proc.wait)\n\n        def test_proc_oneshot(self):\n            # test dual implementation\n            def call():\n                try:\n                    return cext.proc_oneshot(self.proc.pid)\n                except ProcessLookupError:\n                    pass\n\n            self.execute(call)\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\nclass TestProcessDualImplementation(MemoryLeakTestCase):\n    def test_cmdline_peb_true(self):\n        self.execute(lambda: cext.proc_cmdline(os.getpid(), use_peb=True))\n\n    def test_cmdline_peb_false(self):\n        self.execute(lambda: cext.proc_cmdline(os.getpid(), use_peb=False))\n\n\n# ===================================================================\n# system APIs\n# ===================================================================\n\n\nclass TestModuleFunctionsLeaks(MemoryLeakTestCase):\n    \"\"\"Test leaks of psutil module functions.\"\"\"\n\n    def test_coverage(self):\n        ns = system_namespace()\n        ns.test_class_coverage(self, ns.all)\n\n    # --- cpu\n\n    def test_cpu_count(self):  # logical\n        self.execute(lambda: psutil.cpu_count(logical=True))\n\n    def test_cpu_count_cores(self):\n        self.execute(lambda: psutil.cpu_count(logical=False))\n\n    def test_cpu_times(self):\n        self.execute(psutil.cpu_times)\n\n    def test_per_cpu_times(self):\n        self.execute(lambda: psutil.cpu_times(percpu=True))\n\n    def test_cpu_stats(self):\n        self.execute(psutil.cpu_stats)\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    def test_cpu_freq(self):\n        times = FEW_TIMES if LINUX else self.times\n        self.execute(psutil.cpu_freq, times=times)\n\n    @pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n    def test_getloadavg(self):\n        psutil.getloadavg()\n        self.execute(psutil.getloadavg)\n\n    # --- mem\n\n    def test_virtual_memory(self):\n        self.execute(psutil.virtual_memory)\n\n    # TODO: remove this skip when this gets fixed\n    @pytest.mark.skipif(SUNOS, reason=\"worthless on SUNOS (uses a subprocess)\")\n    def test_swap_memory(self):\n        self.execute(psutil.swap_memory)\n\n    def test_pid_exists(self):\n        times = FEW_TIMES if POSIX else self.times\n        self.execute(lambda: psutil.pid_exists(os.getpid()), times=times)\n\n    # --- disk\n\n    def test_disk_usage(self):\n        times = FEW_TIMES if POSIX else self.times\n        self.execute(lambda: psutil.disk_usage('.'), times=times)\n\n    def test_disk_partitions(self):\n        self.execute(psutil.disk_partitions)\n\n    @pytest.mark.skipif(\n        LINUX and not os.path.exists('/proc/diskstats'),\n        reason=\"/proc/diskstats not available on this Linux version\",\n    )\n    def test_disk_io_counters(self):\n        self.execute(lambda: psutil.disk_io_counters(nowrap=False))\n\n    # --- proc\n\n    def test_pids(self):\n        self.execute(psutil.pids)\n\n    # --- net\n\n    @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason=\"not supported\")\n    def test_net_io_counters(self):\n        self.execute(lambda: psutil.net_io_counters(nowrap=False))\n\n    @pytest.mark.skipif(MACOS and os.getuid() != 0, reason=\"need root access\")\n    def test_net_connections(self):\n        # always opens and handle on Windows() (once)\n        psutil.net_connections(kind='all')\n        times = FEW_TIMES if LINUX else self.times\n        with create_sockets():\n            self.execute(\n                lambda: psutil.net_connections(kind='all'), times=times\n            )\n\n    def test_net_if_addrs(self):\n        if WINDOWS:\n            # Calling GetAdaptersAddresses() for the first time\n            # allocates internal OS handles. These handles persist for\n            # the lifetime of the process, causing psleak to report\n            # \"unclosed handles\". Call it here first to avoid false\n            # positives.\n            psutil.net_if_addrs()\n\n        # Note: verified that on Windows this was a false positive.\n        tolerance = 80 * 1024 if WINDOWS else self.tolerance\n        self.execute(psutil.net_if_addrs, tolerance=tolerance)\n\n    def test_net_if_stats(self):\n        self.execute(psutil.net_if_stats)\n\n    # --- sensors\n\n    @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason=\"not supported\")\n    def test_sensors_battery(self):\n        self.execute(psutil.sensors_battery)\n\n    @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason=\"not supported\")\n    @pytest.mark.skipif(LINUX, reason=\"too slow on LINUX\")\n    def test_sensors_temperatures(self):\n        times = FEW_TIMES if LINUX else self.times\n        self.execute(psutil.sensors_temperatures, times=times)\n\n    @pytest.mark.skipif(not HAS_SENSORS_FANS, reason=\"not supported\")\n    def test_sensors_fans(self):\n        times = FEW_TIMES if LINUX else self.times\n        self.execute(psutil.sensors_fans, times=times)\n\n    # --- others\n\n    def test_boot_time(self):\n        self.execute(psutil.boot_time)\n\n    def test_users(self):\n        if WINDOWS:\n            # The first time this is called it allocates internal OS\n            # handles. These handles persist for the lifetime of the\n            # process, causing psleak to report \"unclosed handles\".\n            # Call it here first to avoid false positives.\n            psutil.users()\n        self.execute(psutil.users)\n\n    def test_set_debug(self):\n        self.execute(lambda: psutil._set_debug(False))\n\n    @pytest.mark.skipif(not HAS_HEAP_INFO, reason=\"not supported\")\n    def test_heap_info(self):\n        self.execute(psutil.heap_info)\n\n    @pytest.mark.skipif(not HAS_HEAP_INFO, reason=\"not supported\")\n    def test_heap_trim(self):\n        self.execute(psutil.heap_trim)\n\n    if WINDOWS:\n\n        # --- win services\n\n        def test_win_service_iter(self):\n            self.execute(cext.winservice_enumerate)\n\n        def test_win_service_get(self):\n            pass\n\n        def test_win_service_get_config(self):\n            name = next(psutil.win_service_iter()).name()\n            self.execute(lambda: cext.winservice_query_config(name))\n\n        def test_win_service_get_status(self):\n            name = next(psutil.win_service_iter()).name()\n            self.execute(lambda: cext.winservice_query_status(name))\n\n        def test_win_service_get_description(self):\n            name = next(psutil.win_service_iter()).name()\n            self.execute(lambda: cext.winservice_query_descr(name))\n"
  },
  {
    "path": "tests/test_misc.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Miscellaneous tests.\"\"\"\n\nimport collections\nimport contextlib\nimport io\nimport json\nimport os\nimport pickle\nimport socket\nimport sys\nfrom unittest import mock\n\nimport psutil\nfrom psutil import WINDOWS\nfrom psutil._common import bcat\nfrom psutil._common import cat\nfrom psutil._common import debug\nfrom psutil._common import isfile_strict\nfrom psutil._common import memoize_when_activated\nfrom psutil._common import parse_environ_block\nfrom psutil._common import supports_ipv6\nfrom psutil._common import wrap_numbers\n\nfrom . import HAS_NET_IO_COUNTERS\nfrom . import PsutilTestCase\nfrom . import process_namespace\nfrom . import pytest\nfrom . import reload_module\nfrom . import system_namespace\n\n# ===================================================================\n# --- Test classes' repr(), str(), ...\n# ===================================================================\n\n\nclass TestSpecialMethods(PsutilTestCase):\n    def test_check_pid_range(self):\n        with pytest.raises(OverflowError):\n            psutil._psplatform.cext.check_pid_range(2**128)\n        with pytest.raises(psutil.NoSuchProcess):\n            psutil.Process(2**128)\n\n    def test_process__repr__(self, func=repr):\n        p = psutil.Process(self.spawn_subproc().pid)\n        r = func(p)\n        assert \"psutil.Process\" in r\n        assert f\"pid={p.pid}\" in r\n        assert f\"name='{p.name()}'\" in r.replace(\"name=u'\", \"name='\")\n        assert \"status=\" in r\n        assert \"exitcode=\" not in r\n        p.terminate()\n        p.wait()\n        r = func(p)\n        assert \"status='terminated'\" in r\n        assert \"exitcode=\" in r\n\n        with mock.patch.object(\n            psutil.Process,\n            \"name\",\n            side_effect=psutil.ZombieProcess(os.getpid()),\n        ):\n            p = psutil.Process()\n            r = func(p)\n            assert f\"pid={p.pid}\" in r\n            assert \"status='zombie'\" in r\n            assert \"name=\" not in r\n        with mock.patch.object(\n            psutil.Process,\n            \"name\",\n            side_effect=psutil.NoSuchProcess(os.getpid()),\n        ):\n            p = psutil.Process()\n            r = func(p)\n            assert f\"pid={p.pid}\" in r\n            assert \"terminated\" in r\n            assert \"name=\" not in r\n        with mock.patch.object(\n            psutil.Process,\n            \"name\",\n            side_effect=psutil.AccessDenied(os.getpid()),\n        ):\n            p = psutil.Process()\n            r = func(p)\n            assert f\"pid={p.pid}\" in r\n            assert \"name=\" not in r\n\n    def test_process__str__(self):\n        self.test_process__repr__(func=str)\n\n    def test_error__repr__(self):\n        assert repr(psutil.Error()) == \"psutil.Error()\"\n\n    def test_error__str__(self):\n        assert str(psutil.Error()) == \"\"\n\n    def test_no_such_process__repr__(self):\n        assert (\n            repr(psutil.NoSuchProcess(321))\n            == \"psutil.NoSuchProcess(pid=321, msg='process no longer exists')\"\n        )\n        assert (\n            repr(psutil.NoSuchProcess(321, name=\"name\", msg=\"msg\"))\n            == \"psutil.NoSuchProcess(pid=321, name='name', msg='msg')\"\n        )\n\n    def test_no_such_process__str__(self):\n        assert (\n            str(psutil.NoSuchProcess(321))\n            == \"process no longer exists (pid=321)\"\n        )\n        assert (\n            str(psutil.NoSuchProcess(321, name=\"name\", msg=\"msg\"))\n            == \"msg (pid=321, name='name')\"\n        )\n\n    def test_zombie_process__repr__(self):\n        assert (\n            repr(psutil.ZombieProcess(321))\n            == 'psutil.ZombieProcess(pid=321, msg=\"PID still '\n            'exists but it\\'s a zombie\")'\n        )\n        assert (\n            repr(psutil.ZombieProcess(321, name=\"name\", ppid=320, msg=\"foo\"))\n            == \"psutil.ZombieProcess(pid=321, ppid=320, name='name',\"\n            \" msg='foo')\"\n        )\n\n    def test_zombie_process__str__(self):\n        assert (\n            str(psutil.ZombieProcess(321))\n            == \"PID still exists but it's a zombie (pid=321)\"\n        )\n        assert (\n            str(psutil.ZombieProcess(321, name=\"name\", ppid=320, msg=\"foo\"))\n            == \"foo (pid=321, ppid=320, name='name')\"\n        )\n\n    def test_access_denied__repr__(self):\n        assert repr(psutil.AccessDenied(321)) == \"psutil.AccessDenied(pid=321)\"\n        assert (\n            repr(psutil.AccessDenied(321, name=\"name\", msg=\"msg\"))\n            == \"psutil.AccessDenied(pid=321, name='name', msg='msg')\"\n        )\n\n    def test_access_denied__str__(self):\n        assert str(psutil.AccessDenied(321)) == \"(pid=321)\"\n        assert (\n            str(psutil.AccessDenied(321, name=\"name\", msg=\"msg\"))\n            == \"msg (pid=321, name='name')\"\n        )\n\n    def test_timeout_expired__repr__(self):\n        assert (\n            repr(psutil.TimeoutExpired(5))\n            == \"psutil.TimeoutExpired(seconds=5, msg='timeout after 5\"\n            \" seconds')\"\n        )\n        assert (\n            repr(psutil.TimeoutExpired(5, pid=321, name=\"name\"))\n            == \"psutil.TimeoutExpired(pid=321, name='name', seconds=5, \"\n            \"msg='timeout after 5 seconds')\"\n        )\n\n    def test_timeout_expired__str__(self):\n        assert str(psutil.TimeoutExpired(5)) == \"timeout after 5 seconds\"\n        assert (\n            str(psutil.TimeoutExpired(5, pid=321, name=\"name\"))\n            == \"timeout after 5 seconds (pid=321, name='name')\"\n        )\n\n    def test_process__eq__(self):\n        p1 = psutil.Process()\n        p2 = psutil.Process()\n        assert p1 == p2\n        p2._ident = (0, 0)\n        assert p1 != p2\n        assert p1 != 'foo'\n\n    def test_process__hash__(self):\n        s = {psutil.Process(), psutil.Process()}\n        assert len(s) == 1\n\n\n# ===================================================================\n# --- Misc, generic, corner cases\n# ===================================================================\n\n\nclass TestMisc(PsutilTestCase):\n    def test__all__(self):\n        dir_psutil = dir(psutil)\n        # assert there's no duplicates\n        assert len(dir_psutil) == len(set(dir_psutil))\n        for name in dir_psutil:\n            if name in {\n                'debug',\n                'tests',\n                'test',\n                'PermissionError',\n                'ProcessLookupError',\n            }:\n                continue\n            if not name.startswith('_'):\n                try:\n                    __import__(name)\n                except ImportError:\n                    if name not in psutil.__all__:\n                        fun = getattr(psutil, name)\n                        if fun is None:\n                            continue\n                        if (\n                            fun.__doc__ is not None\n                            and 'deprecated' not in fun.__doc__.lower()\n                        ):\n                            return pytest.fail(\n                                f\"{name!r} not in psutil.__all__\"\n                            )\n\n        # Import 'star' will break if __all__ is inconsistent, see:\n        # https://github.com/giampaolo/psutil/issues/656\n        # Can't do `from psutil import *` as it won't work\n        # so we simply iterate over __all__.\n        for name in psutil.__all__:\n            assert name in dir_psutil\n\n    def test_version(self):\n        assert (\n            '.'.join([str(x) for x in psutil.version_info])\n            == psutil.__version__\n        )\n\n    def test_process_as_dict_no_new_names(self):\n        # See https://github.com/giampaolo/psutil/issues/813\n        p = psutil.Process()\n        p.foo = '1'\n        assert 'foo' not in p.as_dict()\n\n    def test_serialization(self):\n        def check(ret):\n            json.loads(json.dumps(ret))\n\n            a = pickle.dumps(ret)\n            b = pickle.loads(a)\n            assert ret == b\n\n        # --- process APIs\n\n        proc = psutil.Process()\n        check(psutil.Process().as_dict())\n\n        ns = process_namespace(proc)\n        for fun, name in ns.iter(ns.getters, clear_cache=True):\n            with self.subTest(proc=str(proc), name=name):\n                try:\n                    ret = fun()\n                except psutil.Error:\n                    pass\n                else:\n                    check(ret)\n\n        # --- system APIs\n\n        ns = system_namespace()\n        for fun, name in ns.iter(ns.getters):\n            if name in {\"win_service_iter\", \"win_service_get\"}:\n                continue\n            with self.subTest(name=name):\n                try:\n                    ret = fun()\n                except psutil.AccessDenied:\n                    pass\n                else:\n                    check(ret)\n\n        # --- exception classes\n\n        b = pickle.loads(\n            pickle.dumps(\n                psutil.NoSuchProcess(pid=4567, name='name', msg='msg')\n            )\n        )\n        assert isinstance(b, psutil.NoSuchProcess)\n        assert b.pid == 4567\n        assert b.name == 'name'\n        assert b.msg == 'msg'\n\n        b = pickle.loads(\n            pickle.dumps(\n                psutil.ZombieProcess(pid=4567, name='name', ppid=42, msg='msg')\n            )\n        )\n        assert isinstance(b, psutil.ZombieProcess)\n        assert b.pid == 4567\n        assert b.ppid == 42\n        assert b.name == 'name'\n        assert b.msg == 'msg'\n\n        b = pickle.loads(\n            pickle.dumps(psutil.AccessDenied(pid=123, name='name', msg='msg'))\n        )\n        assert isinstance(b, psutil.AccessDenied)\n        assert b.pid == 123\n        assert b.name == 'name'\n        assert b.msg == 'msg'\n\n        b = pickle.loads(\n            pickle.dumps(\n                psutil.TimeoutExpired(seconds=33, pid=4567, name='name')\n            )\n        )\n        assert isinstance(b, psutil.TimeoutExpired)\n        assert b.seconds == 33\n        assert b.pid == 4567\n        assert b.name == 'name'\n\n    def test_ad_on_process_creation(self):\n        # We are supposed to be able to instantiate Process also in case\n        # of zombie processes or access denied.\n        with mock.patch.object(\n            psutil.Process, '_get_ident', side_effect=psutil.AccessDenied\n        ) as meth:\n            psutil.Process()\n            assert meth.called\n\n        with mock.patch.object(\n            psutil.Process, '_get_ident', side_effect=psutil.ZombieProcess(1)\n        ) as meth:\n            psutil.Process()\n            assert meth.called\n\n        with mock.patch.object(\n            psutil.Process, '_get_ident', side_effect=ValueError\n        ) as meth:\n            with pytest.raises(ValueError):\n                psutil.Process()\n            assert meth.called\n\n        with mock.patch.object(\n            psutil.Process, '_get_ident', side_effect=psutil.NoSuchProcess(1)\n        ) as meth:\n            with pytest.raises(psutil.NoSuchProcess):\n                psutil.Process()\n            assert meth.called\n\n    def test_sanity_version_check(self):\n        # see: https://github.com/giampaolo/psutil/issues/564\n        with mock.patch(\n            \"psutil._psplatform.cext.version\", return_value=\"0.0.0\"\n        ):\n            with pytest.raises(ImportError) as cm:\n                reload_module(psutil)\n            assert \"version conflict\" in str(cm.value).lower()\n\n\n# ===================================================================\n# --- psutil/_common.py utils\n# ===================================================================\n\n\nclass TestCommonModule(PsutilTestCase):\n    def test_memoize_when_activated(self):\n        class Foo:\n            @memoize_when_activated\n            def foo(self):\n                calls.append(None)\n\n        f = Foo()\n        calls = []\n        f.foo()\n        f.foo()\n        assert len(calls) == 2\n\n        # activate\n        calls = []\n        f.foo.cache_activate(f)\n        f.foo()\n        f.foo()\n        assert len(calls) == 1\n\n        # deactivate\n        calls = []\n        f.foo.cache_deactivate(f)\n        f.foo()\n        f.foo()\n        assert len(calls) == 2\n\n    def test_parse_environ_block(self):\n        def k(s):\n            return s.upper() if WINDOWS else s\n\n        assert parse_environ_block(\"a=1\\0\") == {k(\"a\"): \"1\"}\n        assert parse_environ_block(\"a=1\\0b=2\\0\\0\") == {\n            k(\"a\"): \"1\",\n            k(\"b\"): \"2\",\n        }\n        assert parse_environ_block(\"a=1\\0b=\\0\\0\") == {k(\"a\"): \"1\", k(\"b\"): \"\"}\n        # ignore everything after \\0\\0\n        assert parse_environ_block(\"a=1\\0b=2\\0\\0c=3\\0\") == {\n            k(\"a\"): \"1\",\n            k(\"b\"): \"2\",\n        }\n        # ignore everything that is not an assignment\n        assert parse_environ_block(\"xxx\\0a=1\\0\") == {k(\"a\"): \"1\"}\n        assert parse_environ_block(\"a=1\\0=b=2\\0\") == {k(\"a\"): \"1\"}\n        # do not fail if the block is incomplete\n        assert parse_environ_block(\"a=1\\0b=2\") == {k(\"a\"): \"1\"}\n\n    def test_supports_ipv6(self):\n        if supports_ipv6():\n            with mock.patch('psutil._common.socket') as s:\n                s.has_ipv6 = False\n                assert not supports_ipv6()\n\n            with mock.patch(\n                'psutil._common.socket.socket', side_effect=OSError\n            ) as s:\n                assert not supports_ipv6()\n                assert s.called\n\n            with mock.patch(\n                'psutil._common.socket.socket', side_effect=socket.gaierror\n            ) as s:\n                assert not supports_ipv6()\n                assert s.called\n\n            with mock.patch(\n                'psutil._common.socket.socket.bind',\n                side_effect=socket.gaierror,\n            ) as s:\n                assert not supports_ipv6()\n                assert s.called\n        else:\n            with pytest.raises(OSError):\n                sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)\n                try:\n                    sock.bind((\"::1\", 0))\n                finally:\n                    sock.close()\n\n    def test_isfile_strict(self):\n        this_file = os.path.abspath(__file__)\n        assert isfile_strict(this_file)\n        assert not isfile_strict(os.path.dirname(this_file))\n        with mock.patch('psutil._common.os.stat', side_effect=PermissionError):\n            with pytest.raises(OSError):\n                isfile_strict(this_file)\n        with mock.patch(\n            'psutil._common.os.stat', side_effect=FileNotFoundError\n        ):\n            assert not isfile_strict(this_file)\n        with mock.patch('psutil._common.stat.S_ISREG', return_value=False):\n            assert not isfile_strict(this_file)\n\n    def test_debug(self):\n        with mock.patch.object(psutil._common, \"PSUTIL_DEBUG\", True):\n            with contextlib.redirect_stderr(io.StringIO()) as f:\n                debug(\"hello\")\n                sys.stderr.flush()\n        msg = f.getvalue()\n        assert msg.startswith(\"psutil-debug\"), msg\n        assert \"hello\" in msg\n        assert __file__.replace('.pyc', '.py') in msg\n\n        # supposed to use repr(exc)\n        with mock.patch.object(psutil._common, \"PSUTIL_DEBUG\", True):\n            with contextlib.redirect_stderr(io.StringIO()) as f:\n                debug(ValueError(\"this is an error\"))\n        msg = f.getvalue()\n        assert \"ignoring ValueError\" in msg\n        assert \"'this is an error'\" in msg\n\n        # supposed to use str(exc), because of extra info about file name\n        with mock.patch.object(psutil._common, \"PSUTIL_DEBUG\", True):\n            with contextlib.redirect_stderr(io.StringIO()) as f:\n                exc = OSError(2, \"no such file\")\n                exc.filename = \"/foo\"\n                debug(exc)\n        msg = f.getvalue()\n        assert \"no such file\" in msg\n        assert \"/foo\" in msg\n\n    def test_cat_bcat(self):\n        testfn = self.get_testfn()\n        with open(testfn, \"w\") as f:\n            f.write(\"foo\")\n        assert cat(testfn) == \"foo\"\n        assert bcat(testfn) == b\"foo\"\n        with pytest.raises(FileNotFoundError):\n            cat(testfn + '-invalid')\n        with pytest.raises(FileNotFoundError):\n            bcat(testfn + '-invalid')\n        assert cat(testfn + '-invalid', fallback=\"bar\") == \"bar\"\n        assert bcat(testfn + '-invalid', fallback=\"bar\") == \"bar\"\n\n\n# ===================================================================\n# --- Tests for wrap_numbers() function.\n# ===================================================================\n\n\nnt = collections.namedtuple('foo', 'a b c')\n\n\nclass TestWrapNumbers(PsutilTestCase):\n    def setUp(self):\n        wrap_numbers.cache_clear()\n\n    tearDown = setUp\n\n    def test_first_call(self):\n        input = {'disk1': nt(5, 5, 5)}\n        assert wrap_numbers(input, 'disk_io') == input\n\n    def test_input_hasnt_changed(self):\n        input = {'disk1': nt(5, 5, 5)}\n        assert wrap_numbers(input, 'disk_io') == input\n        assert wrap_numbers(input, 'disk_io') == input\n\n    def test_increase_but_no_wrap(self):\n        input = {'disk1': nt(5, 5, 5)}\n        assert wrap_numbers(input, 'disk_io') == input\n        input = {'disk1': nt(10, 15, 20)}\n        assert wrap_numbers(input, 'disk_io') == input\n        input = {'disk1': nt(20, 25, 30)}\n        assert wrap_numbers(input, 'disk_io') == input\n        input = {'disk1': nt(20, 25, 30)}\n        assert wrap_numbers(input, 'disk_io') == input\n\n    def test_wrap(self):\n        # let's say 100 is the threshold\n        input = {'disk1': nt(100, 100, 100)}\n        assert wrap_numbers(input, 'disk_io') == input\n        # first wrap restarts from 10\n        input = {'disk1': nt(100, 100, 10)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 110)}\n        # then it remains the same\n        input = {'disk1': nt(100, 100, 10)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 110)}\n        # then it goes up\n        input = {'disk1': nt(100, 100, 90)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 190)}\n        # then it wraps again\n        input = {'disk1': nt(100, 100, 20)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 210)}\n        # and remains the same\n        input = {'disk1': nt(100, 100, 20)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 210)}\n        # now wrap another num\n        input = {'disk1': nt(50, 100, 20)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(150, 100, 210)}\n        # and again\n        input = {'disk1': nt(40, 100, 20)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(190, 100, 210)}\n        # keep it the same\n        input = {'disk1': nt(40, 100, 20)}\n        assert wrap_numbers(input, 'disk_io') == {'disk1': nt(190, 100, 210)}\n\n    def test_changing_keys(self):\n        # Emulate a case where the second call to disk_io()\n        # (or whatever) provides a new disk, then the new disk\n        # disappears on the third call.\n        input = {'disk1': nt(5, 5, 5)}\n        assert wrap_numbers(input, 'disk_io') == input\n        input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)}\n        assert wrap_numbers(input, 'disk_io') == input\n        input = {'disk1': nt(8, 8, 8)}\n        assert wrap_numbers(input, 'disk_io') == input\n\n    def test_changing_keys_w_wrap(self):\n        input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)}\n        assert wrap_numbers(input, 'disk_io') == input\n        # disk 2 wraps\n        input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)}\n        assert wrap_numbers(input, 'disk_io') == {\n            'disk1': nt(50, 50, 50),\n            'disk2': nt(100, 100, 110),\n        }\n        # disk 2 disappears\n        input = {'disk1': nt(50, 50, 50)}\n        assert wrap_numbers(input, 'disk_io') == input\n\n        # then it appears again; the old wrap is supposed to be\n        # gone.\n        input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)}\n        assert wrap_numbers(input, 'disk_io') == input\n        # remains the same\n        input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)}\n        assert wrap_numbers(input, 'disk_io') == input\n        # and then wraps again\n        input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)}\n        assert wrap_numbers(input, 'disk_io') == {\n            'disk1': nt(50, 50, 50),\n            'disk2': nt(100, 100, 110),\n        }\n\n    def test_real_data(self):\n        d = {\n            'nvme0n1': (300, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048),\n            'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8),\n            'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28),\n            'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348),\n        }\n        assert wrap_numbers(d, 'disk_io') == d\n        assert wrap_numbers(d, 'disk_io') == d\n        # decrease this   ↓\n        d = {\n            'nvme0n1': (100, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048),\n            'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8),\n            'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28),\n            'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348),\n        }\n        out = wrap_numbers(d, 'disk_io')\n        assert out['nvme0n1'][0] == 400\n\n    # --- cache tests\n\n    def test_cache_first_call(self):\n        input = {'disk1': nt(5, 5, 5)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        assert cache[1] == {'disk_io': {}}\n        assert cache[2] == {'disk_io': {}}\n\n    def test_cache_call_twice(self):\n        input = {'disk1': nt(5, 5, 5)}\n        wrap_numbers(input, 'disk_io')\n        input = {'disk1': nt(10, 10, 10)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        assert cache[1] == {\n            'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}\n        }\n        assert cache[2] == {'disk_io': {}}\n\n    def test_cache_wrap(self):\n        # let's say 100 is the threshold\n        input = {'disk1': nt(100, 100, 100)}\n        wrap_numbers(input, 'disk_io')\n\n        # first wrap restarts from 10\n        input = {'disk1': nt(100, 100, 10)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        assert cache[1] == {\n            'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100}\n        }\n        assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}}\n\n        def check_cache_info():\n            cache = wrap_numbers.cache_info()\n            assert cache[1] == {\n                'disk_io': {\n                    ('disk1', 0): 0,\n                    ('disk1', 1): 0,\n                    ('disk1', 2): 100,\n                }\n            }\n            assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}}\n\n        # then it remains the same\n        input = {'disk1': nt(100, 100, 10)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        check_cache_info()\n\n        # then it goes up\n        input = {'disk1': nt(100, 100, 90)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        check_cache_info()\n\n        # then it wraps again\n        input = {'disk1': nt(100, 100, 20)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        assert cache[1] == {\n            'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190}\n        }\n        assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}}\n\n    def test_cache_changing_keys(self):\n        input = {'disk1': nt(5, 5, 5)}\n        wrap_numbers(input, 'disk_io')\n        input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)}\n        wrap_numbers(input, 'disk_io')\n        cache = wrap_numbers.cache_info()\n        assert cache[0] == {'disk_io': input}\n        assert cache[1] == {\n            'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}\n        }\n        assert cache[2] == {'disk_io': {}}\n\n    def test_cache_clear(self):\n        input = {'disk1': nt(5, 5, 5)}\n        wrap_numbers(input, 'disk_io')\n        wrap_numbers(input, 'disk_io')\n        wrap_numbers.cache_clear('disk_io')\n        assert wrap_numbers.cache_info() == ({}, {}, {})\n        wrap_numbers.cache_clear('disk_io')\n        wrap_numbers.cache_clear('?!?')\n\n    @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason=\"not supported\")\n    def test_cache_clear_public_apis(self):\n        if not psutil.disk_io_counters() or not psutil.net_io_counters():\n            return pytest.skip(\"no disks or NICs available\")\n        psutil.disk_io_counters()\n        psutil.net_io_counters()\n        caches = wrap_numbers.cache_info()\n        for cache in caches:\n            assert 'psutil.disk_io_counters' in cache\n            assert 'psutil.net_io_counters' in cache\n\n        psutil.disk_io_counters.cache_clear()\n        caches = wrap_numbers.cache_info()\n        for cache in caches:\n            assert 'psutil.net_io_counters' in cache\n            assert 'psutil.disk_io_counters' not in cache\n\n        psutil.net_io_counters.cache_clear()\n        caches = wrap_numbers.cache_info()\n        assert caches == ({}, {}, {})\n"
  },
  {
    "path": "tests/test_osx.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"macOS specific tests.\"\"\"\n\nimport re\nimport time\n\nimport psutil\nfrom psutil import MACOS\n\nfrom . import AARCH64\nfrom . import CI_TESTING\nfrom . import HAS_BATTERY\nfrom . import HAS_CPU_FREQ\nfrom . import TOLERANCE_DISK_USAGE\nfrom . import TOLERANCE_SYS_MEM\nfrom . import PsutilTestCase\nfrom . import pytest\nfrom . import retry_on_failure\nfrom . import sh\nfrom . import spawn_subproc\nfrom . import terminate\n\n\ndef sysctl(cmdline):\n    \"\"\"Expects a sysctl command with an argument and parse the result\n    returning only the value of interest.\n    \"\"\"\n    out = sh(cmdline)\n    result = out.split()[1]\n    try:\n        return int(result)\n    except ValueError:\n        return result\n\n\ndef vm_stat(field):\n    \"\"\"Wrapper around 'vm_stat' cmdline utility.\"\"\"\n    out = sh('vm_stat')\n    for line in out.split('\\n'):\n        if field in line:\n            break\n    else:\n        raise ValueError(\"line not found\")\n    return (\n        int(re.search(r'\\d+', line).group(0))\n        * psutil._psplatform.cext.getpagesize()\n    )\n\n\n@pytest.mark.skipif(not MACOS, reason=\"MACOS only\")\nclass MacosTestCase(PsutilTestCase):\n    pass\n\n\nclass TestProcess(MacosTestCase):\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc().pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    def test_process_create_time(self):\n        output = sh(f\"ps -o lstart -p {self.pid}\")\n        start_ps = output.replace('STARTED', '').strip()\n        hhmmss = start_ps.split(' ')[-2]\n        year = start_ps.split(' ')[-1]\n        start_psutil = psutil.Process(self.pid).create_time()\n        assert hhmmss == time.strftime(\n            \"%H:%M:%S\", time.localtime(start_psutil)\n        )\n        assert year == time.strftime(\"%Y\", time.localtime(start_psutil))\n\n\nclass TestSystemAPIs(MacosTestCase):\n\n    # --- disk\n\n    @retry_on_failure()\n    def test_disks(self):\n        # test psutil.disk_usage() and psutil.disk_partitions()\n        # against \"df -a\"\n        def df(path):\n            out = sh(f'df -k \"{path}\"').strip()\n            lines = out.split('\\n')\n            lines.pop(0)\n            line = lines.pop(0)\n            dev, total, used, free = line.split()[:4]\n            if dev == 'none':\n                dev = ''\n            total = int(total) * 1024\n            used = int(used) * 1024\n            free = int(free) * 1024\n            return dev, total, used, free\n\n        for part in psutil.disk_partitions(all=False):\n            usage = psutil.disk_usage(part.mountpoint)\n            dev, total, used, free = df(part.mountpoint)\n            assert part.device == dev\n            assert usage.total == total\n            assert abs(usage.free - free) < TOLERANCE_DISK_USAGE\n            assert abs(usage.used - used) < TOLERANCE_DISK_USAGE\n\n    # --- cpu\n\n    def test_cpu_count_logical(self):\n        num = sysctl(\"sysctl hw.logicalcpu\")\n        assert num == psutil.cpu_count(logical=True)\n\n    def test_cpu_count_cores(self):\n        num = sysctl(\"sysctl hw.physicalcpu\")\n        assert num == psutil.cpu_count(logical=False)\n\n    @pytest.mark.skipif(\n        MACOS and AARCH64 and not HAS_CPU_FREQ,\n        reason=\"not available on MACOS + AARCH64\",\n    )\n    def test_cpu_freq(self):\n        freq = psutil.cpu_freq()\n        assert freq.current * 1000 * 1000 == sysctl(\"sysctl hw.cpufrequency\")\n        assert freq.min * 1000 * 1000 == sysctl(\"sysctl hw.cpufrequency_min\")\n        assert freq.max * 1000 * 1000 == sysctl(\"sysctl hw.cpufrequency_max\")\n\n    # --- virtual mem\n\n    def test_vmem_total(self):\n        sysctl_hwphymem = sysctl('sysctl hw.memsize')\n        assert sysctl_hwphymem == psutil.virtual_memory().total\n\n    @pytest.mark.skipif(\n        CI_TESTING and MACOS and AARCH64,\n        reason=\"skipped on MACOS + ARM64 + CI_TESTING\",\n    )\n    @retry_on_failure()\n    def test_vmem_free(self):\n        vmstat_val = vm_stat(\"free\")\n        psutil_val = psutil.virtual_memory().free\n        assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM\n\n    @pytest.mark.skipif(\n        CI_TESTING and MACOS and AARCH64,\n        reason=\"skipped on MACOS + ARM64 + CI_TESTING\",\n    )\n    @retry_on_failure()\n    def test_vmem_active(self):\n        vmstat_val = vm_stat(\"active\")\n        psutil_val = psutil.virtual_memory().active\n        assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM\n\n    # XXX: fails too often\n    @pytest.mark.skipif(CI_TESTING, reason=\"skipped on CI_TESTING\")\n    @retry_on_failure()\n    def test_vmem_inactive(self):\n        vmstat_val = vm_stat(\"inactive\")\n        psutil_val = psutil.virtual_memory().inactive\n        assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_vmem_wired(self):\n        vmstat_val = vm_stat(\"wired\")\n        psutil_val = psutil.virtual_memory().wired\n        assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM\n\n    # --- swap mem\n\n    @retry_on_failure()\n    def test_swapmem_sin(self):\n        vmstat_val = vm_stat(\"Pageins\")\n        psutil_val = psutil.swap_memory().sin\n        assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM\n\n    @retry_on_failure()\n    def test_swapmem_sout(self):\n        vmstat_val = vm_stat(\"Pageout\")\n        psutil_val = psutil.swap_memory().sout\n        assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM\n\n    # --- network\n\n    def test_net_if_stats(self):\n        for name, stats in psutil.net_if_stats().items():\n            try:\n                out = sh(f\"ifconfig {name}\")\n            except RuntimeError:\n                pass\n            else:\n                assert stats.isup == ('RUNNING' in out), out\n                assert stats.mtu == int(re.findall(r'mtu (\\d+)', out)[0])\n\n    # --- sensors_battery\n\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_sensors_battery(self):\n        out = sh(\"pmset -g batt\")\n        percent = re.search(r\"(\\d+)%\", out).group(1)\n        drawing_from = re.search(r\"Now drawing from '([^']+)'\", out).group(1)\n        power_plugged = drawing_from == \"AC Power\"\n        psutil_result = psutil.sensors_battery()\n        assert psutil_result.power_plugged == power_plugged\n        assert psutil_result.percent == int(percent)\n\n    # --- others\n\n    def test_boot_time(self):\n        out = sh('sysctl kern.boottime')\n        a = float(re.search(r\"sec\\s*=\\s*(\\d+)\", out).groups(0)[0])\n        b = psutil.boot_time()\n        assert a == b\n"
  },
  {
    "path": "tests/test_posix.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"POSIX specific tests.\"\"\"\n\nimport datetime\nimport errno\nimport os\nimport re\nimport shutil\nimport subprocess\nimport time\nfrom unittest import mock\n\nimport psutil\nfrom psutil import AIX\nfrom psutil import BSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import SUNOS\n\nfrom . import AARCH64\nfrom . import HAS_NET_IO_COUNTERS\nfrom . import PYTHON_EXE\nfrom . import PsutilTestCase\nfrom . import pytest\nfrom . import retry_on_failure\nfrom . import sh\nfrom . import skip_on_access_denied\nfrom . import spawn_subproc\nfrom . import terminate\n\nif POSIX:\n    import mmap\n    import resource\n\n\ndef ps(fmt, pid=None):\n    \"\"\"Wrapper for calling the ps command with a little bit of cross-platform\n    support for a narrow range of features.\n    \"\"\"\n\n    cmd = ['ps']\n\n    if LINUX:\n        cmd.append('--no-headers')\n\n    if pid is not None:\n        cmd.extend(['-p', str(pid)])\n    elif SUNOS or AIX:\n        cmd.append('-A')\n    else:\n        cmd.append('ax')\n\n    if SUNOS:\n        fmt = fmt.replace(\"start\", \"stime\")\n\n    cmd.extend(['-o', fmt])\n\n    output = sh(cmd)\n\n    output = output.splitlines() if LINUX else output.splitlines()[1:]\n\n    all_output = []\n    for line in output:\n        line = line.strip()\n\n        try:\n            line = int(line)\n        except ValueError:\n            pass\n\n        all_output.append(line)\n\n    if pid is None:\n        return all_output\n    else:\n        return all_output[0]\n\n\n# ps \"-o\" field names differ wildly between platforms.\n# \"comm\" means \"only executable name\" but is not available on BSD platforms.\n# \"args\" means \"command with all its arguments\", and is also not available\n# on BSD platforms.\n# \"command\" is like \"args\" on most platforms, but like \"comm\" on AIX,\n# and not available on SUNOS.\n# so for the executable name we can use \"comm\" on Solaris and split \"command\"\n# on other platforms.\n# to get the cmdline (with args) we have to use \"args\" on AIX and\n# Solaris, and can use \"command\" on all others.\n\n\ndef ps_name(pid):\n    field = \"command\"\n    if SUNOS:\n        field = \"comm\"\n    command = ps(field, pid).split()\n    return command[0]\n\n\ndef ps_args(pid):\n    field = \"command\"\n    if AIX or SUNOS:\n        field = \"args\"\n    out = ps(field, pid)\n    # observed on BSD + Github CI: '/usr/local/bin/python3 -E -O (python3.9)'\n    out = re.sub(r\"\\(python.*?\\)$\", \"\", out)\n    return out.strip()\n\n\ndef ps_rss(pid):\n    field = \"rss\"\n    if AIX:\n        field = \"rssize\"\n    return ps(field, pid)\n\n\ndef ps_vsz(pid):\n    field = \"vsz\"\n    if AIX:\n        field = \"vsize\"\n    return ps(field, pid)\n\n\ndef df(device):\n    try:\n        out = sh(f\"df -k {device}\").strip()\n    except RuntimeError as err:\n        if \"device busy\" in str(err).lower():\n            return pytest.skip(\"df returned EBUSY\")\n        raise\n    line = out.split('\\n')[1]\n    fields = line.split()\n    sys_total = int(fields[1]) * 1024\n    sys_used = int(fields[2]) * 1024\n    sys_free = int(fields[3]) * 1024\n    sys_percent = float(fields[4].replace('%', ''))\n    return (sys_total, sys_used, sys_free, sys_percent)\n\n\n@pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\nclass PosixTestCase(PsutilTestCase):\n    pass\n\n\nclass TestProcess(PosixTestCase):\n    \"\"\"Compare psutil results against 'ps' command line utility (mainly).\"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc(\n            [PYTHON_EXE, \"-E\", \"-O\"], stdin=subprocess.PIPE\n        ).pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    def test_ppid(self):\n        ppid_ps = ps('ppid', self.pid)\n        ppid_psutil = psutil.Process(self.pid).ppid()\n        assert ppid_ps == ppid_psutil\n\n    def test_uid(self):\n        uid_ps = ps('uid', self.pid)\n        uid_psutil = psutil.Process(self.pid).uids().real\n        assert uid_ps == uid_psutil\n\n    def test_gid(self):\n        gid_ps = ps('rgid', self.pid)\n        gid_psutil = psutil.Process(self.pid).gids().real\n        assert gid_ps == gid_psutil\n\n    def test_username(self):\n        username_ps = ps('user', self.pid)\n        username_psutil = psutil.Process(self.pid).username()\n        assert username_ps == username_psutil\n\n    def test_username_no_resolution(self):\n        # Emulate a case where the system can't resolve the uid to\n        # a username in which case psutil is supposed to return\n        # the stringified uid.\n        p = psutil.Process()\n        with mock.patch(\"psutil.pwd.getpwuid\", side_effect=KeyError) as fun:\n            assert p.username() == str(p.uids().real)\n            assert fun.called\n\n    @skip_on_access_denied()\n    @retry_on_failure()\n    def test_rss_memory(self):\n        # give python interpreter some time to properly initialize\n        # so that the results are the same\n        time.sleep(0.1)\n        rss_ps = ps_rss(self.pid)\n        rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024\n        assert rss_ps == rss_psutil\n\n    @skip_on_access_denied()\n    @retry_on_failure()\n    def test_vsz_memory(self):\n        # give python interpreter some time to properly initialize\n        # so that the results are the same\n        time.sleep(0.1)\n        vsz_ps = ps_vsz(self.pid)\n        vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024\n        assert vsz_ps == vsz_psutil\n\n    def test_name(self):\n        name_ps = ps_name(self.pid)\n        # remove path if there is any, from the command\n        name_ps = os.path.basename(name_ps).lower()\n        name_psutil = psutil.Process(self.pid).name().lower()\n        # ...because of how we calculate PYTHON_EXE; on MACOS this may\n        # be \"pythonX.Y\".\n        name_ps = re.sub(r\"\\d.\\d\", \"\", name_ps)\n        name_psutil = re.sub(r\"\\d.\\d\", \"\", name_psutil)\n        # ...may also be \"python.X\"\n        name_ps = re.sub(r\"\\d\", \"\", name_ps)\n        name_psutil = re.sub(r\"\\d\", \"\", name_psutil)\n        assert name_ps == name_psutil\n\n    def test_name_long(self):\n        # On UNIX the kernel truncates the name to the first 15\n        # characters. In such a case psutil tries to determine the\n        # full name from the cmdline.\n        name = \"long-program-name\"\n        cmdline = [\"long-program-name-extended\", \"foo\", \"bar\"]\n        with mock.patch(\"psutil._psplatform.Process.name\", return_value=name):\n            with mock.patch(\n                \"psutil._psplatform.Process.cmdline\", return_value=cmdline\n            ):\n                p = psutil.Process()\n                assert p.name() == \"long-program-name-extended\"\n\n    def test_name_long_cmdline_ad_exc(self):\n        # Same as above but emulates a case where cmdline() raises\n        # AccessDenied in which case psutil is supposed to return\n        # the truncated name instead of crashing.\n        name = \"long-program-name\"\n        with mock.patch(\"psutil._psplatform.Process.name\", return_value=name):\n            with mock.patch(\n                \"psutil._psplatform.Process.cmdline\",\n                side_effect=psutil.AccessDenied(0, \"\"),\n            ):\n                p = psutil.Process()\n                assert p.name() == \"long-program-name\"\n\n    def test_name_long_cmdline_nsp_exc(self):\n        # Same as above but emulates a case where cmdline() raises NSP\n        # which is supposed to propagate.\n        name = \"long-program-name\"\n        with mock.patch(\"psutil._psplatform.Process.name\", return_value=name):\n            with mock.patch(\n                \"psutil._psplatform.Process.cmdline\",\n                side_effect=psutil.NoSuchProcess(0, \"\"),\n            ):\n                p = psutil.Process()\n                with pytest.raises(psutil.NoSuchProcess):\n                    p.name()\n\n    @pytest.mark.skipif(MACOS or BSD, reason=\"ps -o start not available\")\n    def test_create_time(self):\n        time_ps = ps('start', self.pid)\n        time_psutil = psutil.Process(self.pid).create_time()\n        time_psutil_tstamp = datetime.datetime.fromtimestamp(\n            time_psutil\n        ).strftime(\"%H:%M:%S\")\n        # sometimes ps shows the time rounded up instead of down, so we check\n        # for both possible values\n        round_time_psutil = round(time_psutil)\n        round_time_psutil_tstamp = datetime.datetime.fromtimestamp(\n            round_time_psutil\n        ).strftime(\"%H:%M:%S\")\n        assert time_ps in {time_psutil_tstamp, round_time_psutil_tstamp}\n\n    def test_exe(self):\n        ps_pathname = ps_name(self.pid)\n        psutil_pathname = psutil.Process(self.pid).exe()\n        try:\n            assert ps_pathname == psutil_pathname\n        except AssertionError:\n            # certain platforms such as BSD are more accurate returning:\n            # \"/usr/local/bin/python3.7\"\n            # ...instead of:\n            # \"/usr/local/bin/python\"\n            # We do not want to consider this difference in accuracy\n            # an error.\n            adjusted_ps_pathname = ps_pathname[: len(ps_pathname)]\n            assert ps_pathname == adjusted_ps_pathname\n\n    # On macOS the official python installer exposes a python wrapper that\n    # executes a python executable hidden inside an application bundle inside\n    # the Python framework.\n    # There's a race condition between the ps call & the psutil call below\n    # depending on the completion of the execve call so let's retry on failure\n    @retry_on_failure()\n    def test_cmdline(self):\n        ps_cmdline = ps_args(self.pid)\n        psutil_cmdline = \" \".join(psutil.Process(self.pid).cmdline())\n        if AARCH64 and len(ps_cmdline) < len(psutil_cmdline):\n            assert psutil_cmdline.startswith(ps_cmdline)\n        else:\n            assert ps_cmdline == psutil_cmdline\n\n    # On SUNOS \"ps\" reads niceness /proc/pid/psinfo which returns an\n    # incorrect value (20); the real deal is getpriority(2) which\n    # returns 0; psutil relies on it, see:\n    # https://github.com/giampaolo/psutil/issues/1082\n    # AIX has the same issue\n    @pytest.mark.skipif(SUNOS, reason=\"not reliable on SUNOS\")\n    @pytest.mark.skipif(AIX, reason=\"not reliable on AIX\")\n    def test_nice(self):\n        ps_nice = ps('nice', self.pid)\n        psutil_nice = psutil.Process().nice()\n        assert ps_nice == psutil_nice\n\n    @retry_on_failure()\n    def test_num_ctx_switches(self):\n        ru = resource.getrusage(resource.RUSAGE_SELF)\n        cws = psutil.Process().num_ctx_switches()\n        tol = 50\n        if \"PYTEST_XDIST_WORKER_COUNT\" in os.environ:\n            tol *= int(os.environ[\"PYTEST_XDIST_WORKER_COUNT\"])\n        if MACOS:\n            assert cws.voluntary + cws.involuntary == pytest.approx(\n                ru.ru_nvcsw + ru.ru_nivcsw, abs=tol * 2\n            )\n        else:\n            assert cws.voluntary == pytest.approx(ru.ru_nvcsw, abs=tol)\n            assert cws.involuntary == pytest.approx(ru.ru_nivcsw, abs=tol)\n\n    @retry_on_failure()\n    def test_cpu_times(self):\n        ru = resource.getrusage(resource.RUSAGE_SELF)\n        cws = psutil.Process().cpu_times()\n        assert cws.user == pytest.approx(ru.ru_utime, abs=0.3)\n        assert cws.system == pytest.approx(ru.ru_stime, abs=0.3)\n\n    @retry_on_failure()\n    def test_page_faults(self):\n        ru = resource.getrusage(resource.RUSAGE_SELF)\n        pf = psutil.Process().page_faults()\n        tol = 5\n        assert pf.minor == pytest.approx(ru.ru_minflt, abs=tol)\n        assert pf.major == pytest.approx(ru.ru_majflt, abs=tol)\n\n    @pytest.mark.skipif(not LINUX and not MACOS, reason=\"Linux, macOS only\")\n    def test_page_faults_minor_increase(self):\n        # Access 200 new anonymous pages; each first access triggers a\n        # minor fault.\n        p = psutil.Process()\n        pf_before = p.page_faults()\n        with mmap.mmap(-1, 200 * mmap.PAGESIZE) as m:\n            for i in range(0, 200 * mmap.PAGESIZE, mmap.PAGESIZE):\n                m[i : i + 1] = b'\\x00'\n        pf_after = p.page_faults()\n        assert pf_after.minor > pf_before.minor\n\n    def test_memory_peak_rss(self):\n        mem = psutil.Process().memory_info_ex()\n        if not hasattr(mem, \"peak_rss\"):\n            return pytest.skip(\"not supported\")\n        ru = resource.getrusage(resource.RUSAGE_SELF)\n        # VmHWM (from /proc/pid/status) and ru_maxrss both track peak\n        # RSS but are synced independently. Allow 5% tolerance.\n        if MACOS:\n            rss_diff = abs(mem.peak_rss - ru.ru_maxrss)\n        else:\n            rss_diff = abs(mem.peak_rss - ru.ru_maxrss * 1024)\n        assert rss_diff <= mem.peak_rss * 0.05\n\n\nclass TestSystemAPIs(PosixTestCase):\n    \"\"\"Test some system APIs.\"\"\"\n\n    @retry_on_failure()\n    def test_pids(self):\n        # Note: this test might fail if the OS is starting/killing\n        # other processes in the meantime\n        pids_ps = sorted(ps(\"pid\"))\n        pids_psutil = psutil.pids()\n\n        # on MACOS and OPENBSD ps doesn't show pid 0\n        if MACOS or (OPENBSD and 0 not in pids_ps):\n            pids_ps.insert(0, 0)\n\n        # There will often be one more process in pids_ps for ps itself\n        if len(pids_ps) - len(pids_psutil) > 1:\n            difference = [x for x in pids_psutil if x not in pids_ps] + [\n                x for x in pids_ps if x not in pids_psutil\n            ]\n            return pytest.fail(\"difference: \" + str(difference))\n\n    # for some reason ifconfig -a does not report all interfaces\n    # returned by psutil\n    @pytest.mark.skipif(SUNOS, reason=\"unreliable on SUNOS\")\n    @pytest.mark.skipif(not shutil.which(\"ifconfig\"), reason=\"no ifconfig cmd\")\n    @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason=\"not supported\")\n    def test_nic_names(self):\n        output = sh(\"ifconfig -a\")\n        for nic in psutil.net_io_counters(pernic=True):\n            for line in output.split():\n                if line.startswith(nic):\n                    break\n            else:\n                return pytest.fail(\n                    f\"couldn't find {nic} nic in 'ifconfig -a'\"\n                    f\" output\\n{output}\"\n                )\n\n    @retry_on_failure()\n    def test_users(self):\n        out = sh(\"who -u\")\n        if not out.strip():\n            return pytest.skip(\"no users on this system\")\n\n        susers = []\n        for line in out.splitlines():\n            user = line.split()[0]\n            terminal = line.split()[1]\n            if LINUX or MACOS:\n                try:\n                    pid = int(line.split()[-2])\n                except ValueError:\n                    pid = int(line.split()[-1])\n                susers.append((user, terminal, pid))\n            else:\n                susers.append((user, terminal))\n\n        if LINUX or MACOS:\n            pusers = [(u.name, u.terminal, u.pid) for u in psutil.users()]\n        else:\n            pusers = [(u.name, u.terminal) for u in psutil.users()]\n\n        assert len(susers) == len(pusers)\n        assert sorted(susers) == sorted(pusers)\n\n        for user in psutil.users():\n            if user.pid is not None:\n                assert user.pid > 0\n\n    @retry_on_failure()\n    def test_users_started(self):\n        out = sh(\"who -u\")\n        if not out.strip():\n            return pytest.skip(\"no users on this system\")\n        tstamp = None\n        # '2023-04-11 09:31' (Linux)\n        started = re.findall(r\"\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d\", out)\n        if started:\n            tstamp = \"%Y-%m-%d %H:%M\"\n        else:\n            # 'Apr 10 22:27' (macOS)\n            started = re.findall(r\"[A-Z][a-z][a-z] \\d\\d \\d\\d:\\d\\d\", out)\n            if started:\n                tstamp = \"%b %d %H:%M\"\n            else:\n                # 'Apr 10'\n                started = re.findall(r\"[A-Z][a-z][a-z] \\d\\d\", out)\n                if started:\n                    tstamp = \"%b %d\"\n                else:\n                    # 'apr 10' (sunOS)\n                    started = re.findall(r\"[a-z][a-z][a-z] \\d\\d\", out)\n                    if started:\n                        tstamp = \"%b %d\"\n                        started = [x.capitalize() for x in started]\n\n        if not tstamp:\n            return pytest.skip(f\"cannot interpret tstamp in who output\\n{out}\")\n\n        with self.subTest(psutil=str(psutil.users()), who=out):\n            for idx, u in enumerate(psutil.users()):\n                psutil_value = datetime.datetime.fromtimestamp(\n                    u.started\n                ).strftime(tstamp)\n                assert psutil_value == started[idx]\n\n    def test_pid_exists_let_raise(self):\n        # According to \"man 2 kill\" possible error values for kill\n        # are (EINVAL, EPERM, ESRCH). Test that any other errno\n        # results in an exception.\n        with mock.patch(\n            \"psutil._psposix.os.kill\", side_effect=OSError(errno.EBADF, \"\")\n        ) as m:\n            with pytest.raises(OSError):\n                psutil._psposix.pid_exists(os.getpid())\n            assert m.called\n\n    # AIX can return '-' in df output instead of numbers, e.g. for /proc\n    @pytest.mark.skipif(AIX, reason=\"unreliable on AIX\")\n    @retry_on_failure()\n    def test_disk_usage(self):\n        tolerance = 4 * 1024 * 1024  # 4MB\n        for part in psutil.disk_partitions(all=False):\n            usage = psutil.disk_usage(part.mountpoint)\n            try:\n                sys_total, sys_used, sys_free, sys_percent = df(part.device)\n            except RuntimeError as err:\n                # see:\n                # https://travis-ci.org/giampaolo/psutil/jobs/138338464\n                # https://travis-ci.org/giampaolo/psutil/jobs/138343361\n                err = str(err).lower()\n                if (\n                    \"no such file or directory\" in err\n                    or \"raw devices not supported\" in err\n                    or \"permission denied\" in err\n                ):\n                    continue\n                raise\n            else:\n                assert abs(usage.total - sys_total) < tolerance\n                assert abs(usage.used - sys_used) < tolerance\n                assert abs(usage.free - sys_free) < tolerance\n                assert abs(usage.percent - sys_percent) <= 1\n\n\nclass TestMisc(PosixTestCase):\n    def test_getpagesize(self):\n        pagesize = psutil._psplatform.cext.getpagesize()\n        assert pagesize > 0\n        assert pagesize == resource.getpagesize()\n        assert pagesize == mmap.PAGESIZE\n"
  },
  {
    "path": "tests/test_process.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Tests for psutil.Process class.\"\"\"\n\nimport collections\nimport contextlib\nimport enum\nimport errno\nimport getpass\nimport io\nimport itertools\nimport os\nimport random\nimport select\nimport signal\nimport socket\nimport stat\nimport string\nimport subprocess\nimport sys\nimport textwrap\nimport time\nfrom unittest import mock\n\nimport psutil\nfrom psutil import AIX\nfrom psutil import BSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import OSX\nfrom psutil import POSIX\nfrom psutil import WINDOWS\nfrom psutil._common import open_text\n\nfrom . import CI_TESTING\nfrom . import GITHUB_ACTIONS\nfrom . import GLOBAL_TIMEOUT\nfrom . import HAS_PROC_CPU_AFFINITY\nfrom . import HAS_PROC_CPU_NUM\nfrom . import HAS_PROC_ENVIRON\nfrom . import HAS_PROC_IO_COUNTERS\nfrom . import HAS_PROC_IONICE\nfrom . import HAS_PROC_MEMORY_FOOTPRINT\nfrom . import HAS_PROC_MEMORY_MAPS\nfrom . import HAS_PROC_RLIMIT\nfrom . import HAS_PROC_THREADS\nfrom . import MACOS_11PLUS\nfrom . import PYPY\nfrom . import PYTHON_EXE\nfrom . import PYTHON_EXE_ENV\nfrom . import PsutilTestCase\nfrom . import ThreadTask\nfrom . import call_until\nfrom . import copyload_shared_lib\nfrom . import create_c_exe\nfrom . import create_py_exe\nfrom . import process_namespace\nfrom . import pytest\nfrom . import reap_children\nfrom . import retry_on_failure\nfrom . import sh\nfrom . import skip_on_access_denied\nfrom . import skip_on_not_implemented\nfrom . import wait_for_pid\n\n# ===================================================================\n# --- psutil.Process class tests\n# ===================================================================\n\n\nclass TestProcess(PsutilTestCase):\n    \"\"\"Tests for psutil.Process class.\"\"\"\n\n    def test_pid(self):\n        p = psutil.Process()\n        assert p.pid == os.getpid()\n        with pytest.raises(AttributeError):\n            p.pid = 33\n\n    def test_kill(self):\n        p = self.spawn_psproc()\n        p.kill()\n        code = p.wait()\n        if WINDOWS:\n            assert code == signal.SIGTERM\n        else:\n            assert code == -signal.SIGKILL\n        self.assert_proc_gone(p)\n\n    def test_terminate(self):\n        p = self.spawn_psproc()\n        p.terminate()\n        code = p.wait()\n        if WINDOWS:\n            assert code == signal.SIGTERM\n        else:\n            assert code == -signal.SIGTERM\n        self.assert_proc_gone(p)\n\n    def test_send_signal(self):\n        sig = signal.SIGKILL if POSIX else signal.SIGTERM\n        p = self.spawn_psproc()\n        p.send_signal(sig)\n        code = p.wait()\n        if WINDOWS:\n            assert code == sig\n        else:\n            assert code == -sig\n        self.assert_proc_gone(p)\n\n    @pytest.mark.skipif(not POSIX, reason=\"not POSIX\")\n    def test_send_signal_mocked(self):\n        sig = signal.SIGTERM\n        p = self.spawn_psproc()\n        with mock.patch('psutil.os.kill', side_effect=ProcessLookupError):\n            with pytest.raises(psutil.NoSuchProcess):\n                p.send_signal(sig)\n\n        p = self.spawn_psproc()\n        with mock.patch('psutil.os.kill', side_effect=PermissionError):\n            with pytest.raises(psutil.AccessDenied):\n                p.send_signal(sig)\n\n    def test_cpu_percent(self):\n        p = psutil.Process()\n        p.cpu_percent(interval=0.001)\n        p.cpu_percent(interval=0.001)\n        for _ in range(100):\n            percent = p.cpu_percent(interval=None)\n            assert isinstance(percent, float)\n            assert percent >= 0.0\n        with pytest.raises(ValueError):\n            p.cpu_percent(interval=-1)\n\n    def test_cpu_percent_numcpus_none(self):\n        # See: https://github.com/giampaolo/psutil/issues/1087\n        with mock.patch('psutil.cpu_count', return_value=None) as m:\n            psutil.Process().cpu_percent()\n            assert m.called\n\n    def test_cpu_times(self):\n        times = psutil.Process().cpu_times()\n        assert times.user >= 0.0, times\n        assert times.system >= 0.0, times\n        assert times.children_user >= 0.0, times\n        assert times.children_system >= 0.0, times\n        if LINUX:\n            assert times.iowait >= 0.0, times\n        # make sure returned values can be pretty printed with strftime\n        for name in times._fields:\n            time.strftime(\"%H:%M:%S\", time.localtime(getattr(times, name)))\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_NUM, reason=\"not supported\")\n    def test_cpu_num(self):\n        p = psutil.Process()\n        num = p.cpu_num()\n        assert num >= 0\n        if psutil.cpu_count() == 1:\n            assert num == 0\n        assert p.cpu_num() in range(psutil.cpu_count())\n\n    def test_create_time(self):\n        p = self.spawn_psproc()\n        now = time.time()\n        # Fail if the difference with current time is > 2s.\n        assert abs(p.create_time() - now) < 2\n        # make sure returned value can be pretty printed with strftime\n        time.strftime(\"%Y %m %d %H:%M:%S\", time.localtime(p.create_time()))\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_terminal(self):\n        terminal = psutil.Process().terminal()\n        if terminal is not None:\n            try:\n                tty = os.path.realpath(sh('tty'))\n            except RuntimeError:\n                # Note: happens if pytest is run without the `-s` opt.\n                return pytest.skip(\"can't rely on `tty` CLI\")\n            else:\n                assert terminal == tty\n\n    @pytest.mark.skipif(not HAS_PROC_IO_COUNTERS, reason=\"not supported\")\n    @skip_on_not_implemented(only_if=LINUX)\n    def test_io_counters(self):\n        p = psutil.Process()\n        # test reads\n        io1 = p.io_counters()\n        with open(PYTHON_EXE, 'rb') as f:\n            f.read()\n        io2 = p.io_counters()\n        if not BSD and not AIX:\n            assert io2.read_count > io1.read_count\n            assert io2.write_count == io1.write_count\n            if LINUX:\n                assert io2.read_chars > io1.read_chars\n                assert io2.write_chars == io1.write_chars\n        else:\n            assert io2.read_bytes >= io1.read_bytes\n            assert io2.write_bytes >= io1.write_bytes\n\n        # test writes\n        io1 = p.io_counters()\n        with open(self.get_testfn(), 'wb') as f:\n            f.write(bytes(\"x\" * 1000000, 'ascii'))\n        io2 = p.io_counters()\n        assert io2.write_count >= io1.write_count\n        assert io2.write_bytes >= io1.write_bytes\n        assert io2.read_count >= io1.read_count\n        assert io2.read_bytes >= io1.read_bytes\n        if LINUX:\n            assert io2.write_chars > io1.write_chars\n            assert io2.read_chars >= io1.read_chars\n\n        # sanity check\n        for i in range(len(io2)):\n            if BSD and i >= 2:\n                # On BSD read_bytes and write_bytes are always set to -1.\n                continue\n            assert io2[i] >= 0\n            assert io2[i] >= 0\n\n    @pytest.mark.skipif(not HAS_PROC_IONICE, reason=\"not supported\")\n    @pytest.mark.skipif(not LINUX, reason=\"linux only\")\n    def test_ionice_linux(self):\n        def cleanup(init):\n            ioclass, value = init\n            if ioclass == psutil.IOPRIO_CLASS_NONE:\n                value = 0\n            p.ionice(ioclass, value)\n\n        p = psutil.Process()\n        if not CI_TESTING:\n            assert p.ionice()[0] == psutil.IOPRIO_CLASS_NONE\n        assert psutil.IOPRIO_CLASS_NONE == 0\n        assert psutil.IOPRIO_CLASS_RT == 1  # high\n        assert psutil.IOPRIO_CLASS_BE == 2  # normal\n        assert psutil.IOPRIO_CLASS_IDLE == 3  # low\n        init = p.ionice()\n        self.addCleanup(cleanup, init)\n\n        # low\n        p.ionice(psutil.IOPRIO_CLASS_IDLE)\n        assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_IDLE, 0)\n        with pytest.raises(ValueError):  # accepts no value\n            p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7)\n        # normal\n        p.ionice(psutil.IOPRIO_CLASS_BE)\n        assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_BE, 0)\n        p.ionice(psutil.IOPRIO_CLASS_BE, value=7)\n        assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_BE, 7)\n        with pytest.raises(ValueError):\n            p.ionice(psutil.IOPRIO_CLASS_BE, value=8)\n        try:\n            p.ionice(psutil.IOPRIO_CLASS_RT, value=7)\n        except psutil.AccessDenied:\n            pass\n        # errs\n        with pytest.raises(ValueError, match=\"ioclass accepts no value\"):\n            p.ionice(psutil.IOPRIO_CLASS_NONE, 1)\n        with pytest.raises(ValueError, match=\"ioclass accepts no value\"):\n            p.ionice(psutil.IOPRIO_CLASS_IDLE, 1)\n        with pytest.raises(\n            ValueError, match=\"'ioclass' argument must be specified\"\n        ):\n            p.ionice(value=1)\n\n    @pytest.mark.skipif(not HAS_PROC_IONICE, reason=\"not supported\")\n    @pytest.mark.skipif(\n        not WINDOWS, reason=\"not supported on this win version\"\n    )\n    def test_ionice_win(self):\n        p = psutil.Process()\n        if not CI_TESTING:\n            assert p.ionice() == psutil.IOPRIO_NORMAL\n        init = p.ionice()\n        self.addCleanup(p.ionice, init)\n\n        # base\n        p.ionice(psutil.IOPRIO_VERYLOW)\n        assert p.ionice() == psutil.IOPRIO_VERYLOW\n        p.ionice(psutil.IOPRIO_LOW)\n        assert p.ionice() == psutil.IOPRIO_LOW\n        try:\n            p.ionice(psutil.IOPRIO_HIGH)\n        except psutil.AccessDenied:\n            pass\n        else:\n            assert p.ionice() == psutil.IOPRIO_HIGH\n        # errs\n        with pytest.raises(\n            TypeError, match=\"value argument not accepted on Windows\"\n        ):\n            p.ionice(psutil.IOPRIO_NORMAL, value=1)\n        with pytest.raises(ValueError, match=\"is not a valid priority\"):\n            p.ionice(psutil.IOPRIO_HIGH + 1)\n\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_get(self):\n        import resource\n\n        p = psutil.Process(os.getpid())\n        names = [x for x in dir(psutil) if x.startswith('RLIMIT')]\n        assert names, names\n        for name in names:\n            value = getattr(psutil, name)\n            assert value >= 0\n            if name in dir(resource):\n                assert value == getattr(resource, name)\n                # XXX - On PyPy RLIMIT_INFINITY returned by\n                # resource.getrlimit() is reported as a very big long\n                # number instead of -1. It looks like a bug with PyPy.\n                if PYPY:\n                    continue\n                assert p.rlimit(value) == resource.getrlimit(value)\n            else:\n                ret = p.rlimit(value)\n                assert len(ret) == 2\n                assert ret[0] >= -1\n                assert ret[1] >= -1\n\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_set(self):\n        p = self.spawn_psproc()\n        p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))\n        assert p.rlimit(psutil.RLIMIT_NOFILE) == (5, 5)\n        # If pid is 0 prlimit() applies to the calling process and\n        # we don't want that.\n        if LINUX:\n            with pytest.raises(ValueError, match=\"can't use prlimit\"):\n                psutil._psplatform.Process(0).rlimit(0)\n        with pytest.raises(ValueError):\n            p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5))\n\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit(self):\n        p = psutil.Process()\n        testfn = self.get_testfn()\n        soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)\n        try:\n            p.rlimit(psutil.RLIMIT_FSIZE, (1024, hard))\n            with open(testfn, \"wb\") as f:\n                f.write(b\"X\" * 1024)\n            # write() or flush() doesn't always cause the exception\n            # but close() will.\n            with pytest.raises(OSError) as exc:\n                with open(testfn, \"wb\") as f:\n                    f.write(b\"X\" * 1025)\n            assert exc.value.errno == errno.EFBIG\n        finally:\n            p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))\n            assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard)\n\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_infinity(self):\n        # First set a limit, then re-set it by specifying INFINITY\n        # and assume we overridden the previous limit.\n        p = psutil.Process()\n        soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)\n        try:\n            p.rlimit(psutil.RLIMIT_FSIZE, (1024, hard))\n            p.rlimit(psutil.RLIMIT_FSIZE, (psutil.RLIM_INFINITY, hard))\n            with open(self.get_testfn(), \"wb\") as f:\n                f.write(b\"X\" * 2048)\n        finally:\n            p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))\n            assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard)\n\n    @pytest.mark.skipif(not HAS_PROC_RLIMIT, reason=\"not supported\")\n    def test_rlimit_infinity_value(self):\n        # RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really\n        # big number on a platform with large file support.  On these\n        # platforms we need to test that the get/setrlimit functions\n        # properly convert the number to a C long long and that the\n        # conversion doesn't raise an error.\n        p = psutil.Process()\n        soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)\n        assert hard == psutil.RLIM_INFINITY\n        p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))\n\n    @pytest.mark.xdist_group(name=\"serial\")\n    def test_num_threads(self):\n        # on certain platforms such as Linux we might test for exact\n        # thread number, since we always have with 1 thread per process,\n        # but this does not apply across all platforms (MACOS, Windows)\n        p = psutil.Process()\n        if OPENBSD:\n            try:\n                step1 = p.num_threads()\n            except psutil.AccessDenied:\n                return pytest.skip(\"on OpenBSD this requires root access\")\n        else:\n            step1 = p.num_threads()\n\n        with ThreadTask():\n            step2 = p.num_threads()\n            assert step2 == step1 + 1\n\n    @pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n    def test_num_handles(self):\n        # a better test is done later into test/_windows.py\n        p = psutil.Process()\n        assert p.num_handles() > 0\n\n    @pytest.mark.skipif(not HAS_PROC_THREADS, reason=\"not supported\")\n    def test_threads(self):\n        p = psutil.Process()\n        if OPENBSD:\n            try:\n                step1 = p.threads()\n            except psutil.AccessDenied:\n                return pytest.skip(\"on OpenBSD this requires root access\")\n        else:\n            step1 = p.threads()\n\n        with ThreadTask():\n            step2 = p.threads()\n            assert len(step2) == len(step1) + 1\n            athread = step2[0]\n            # test named tuple\n            assert athread.id == athread[0]\n            assert athread.user_time == athread[1]\n            assert athread.system_time == athread[2]\n\n    @retry_on_failure()\n    @skip_on_access_denied(only_if=MACOS)\n    @pytest.mark.skipif(not HAS_PROC_THREADS, reason=\"not supported\")\n    def test_threads_2(self):\n        p = self.spawn_psproc()\n        if OPENBSD:\n            try:\n                p.threads()\n            except psutil.AccessDenied:\n                return pytest.skip(\"on OpenBSD this requires root access\")\n        assert (\n            abs(p.cpu_times().user - sum(x.user_time for x in p.threads()))\n            < 0.1\n        )\n        assert (\n            abs(p.cpu_times().system - sum(x.system_time for x in p.threads()))\n            < 0.1\n        )\n\n    @retry_on_failure()\n    def test_memory_info(self):\n        p = psutil.Process()\n        self.check_proc_memory(p.memory_info())\n\n        # step 1 - get a base value to compare our results\n        rss1, vms1 = p.memory_info()[:2]\n        percent1 = p.memory_percent()\n        assert rss1 > 0\n        assert vms1 > 0\n\n        # step 2 - allocate some memory\n        memarr = [None] * 1500000\n\n        rss2, vms2 = p.memory_info()[:2]\n        percent2 = p.memory_percent()\n\n        # step 3 - make sure that the memory usage bumped up\n        assert rss2 > rss1\n        assert vms2 >= vms1  # vms might be equal\n        assert percent2 > percent1\n        del memarr\n\n    def test_memory_info_ex(self):\n        p = psutil.Process()\n        mem = p.memory_info_ex()\n        self.check_proc_memory(mem)\n        total = psutil.virtual_memory().total\n        for name in mem._fields:\n            if name != \"vms\":\n                value = getattr(mem, name)\n                assert value <= total\n\n    def test_memory_info_ex_fields_order(self):\n        mem = psutil.Process().memory_info_ex()\n        common = (\"rss\", \"vms\")\n        assert mem._fields[:2] == common\n        if LINUX:\n            assert mem._fields[2:] == (\n                \"shared\",\n                \"text\",\n                \"data\",\n                \"peak_rss\",\n                \"peak_vms\",\n                \"rss_anon\",\n                \"rss_file\",\n                \"rss_shmem\",\n                \"swap\",\n                \"hugetlb\",\n            )\n        elif MACOS:\n            assert mem._fields[2:] == (\n                \"peak_rss\",\n                \"rss_anon\",\n                \"rss_file\",\n                \"wired\",\n                \"compressed\",\n                \"phys_footprint\",\n            )\n        elif WINDOWS:\n            assert mem._fields[2:] == (\n                \"peak_rss\",\n                \"peak_vms\",\n                \"virtual\",\n                \"peak_virtual\",\n                \"paged_pool\",\n                \"nonpaged_pool\",\n                \"peak_paged_pool\",\n                \"peak_nonpaged_pool\",\n            )\n        else:\n            assert mem._fields == psutil.Process().memory_info_ex()._fields\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_FOOTPRINT, reason=\"not supported\")\n    def test_memory_footprint(self):\n        p = psutil.Process()\n        mem = p.memory_footprint()\n        self.check_proc_memory(mem)\n\n    def test_memory_full_info(self):\n        p = psutil.Process()\n        with pytest.warns(DeprecationWarning):\n            mem = p.memory_full_info()\n        # not returned by default\n        assert 'memory_full_info' not in p.as_dict()\n        # but explicitly requesting it should work\n        with pytest.warns(DeprecationWarning):\n            d = p.as_dict(attrs=['memory_full_info'])\n        assert 'memory_full_info' in d\n        # fields should be memory_info() + memory_footprint() (if avail)\n        expected = p.memory_info()._fields\n        if HAS_PROC_MEMORY_FOOTPRINT:\n            expected += p.memory_footprint()._fields\n        assert mem._fields == expected\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_MAPS, reason=\"not supported\")\n    def test_memory_maps(self):\n        p = psutil.Process()\n        maps = p.memory_maps()\n        assert len(maps) == len(set(maps))\n        ext_maps = p.memory_maps(grouped=False)\n\n        for nt in maps:\n            if nt.path.startswith('['):\n                continue\n            if BSD and nt.path == \"pvclock\":\n                continue\n            assert os.path.isabs(nt.path), nt.path\n\n            if POSIX:\n                try:\n                    assert os.path.exists(nt.path) or os.path.islink(\n                        nt.path\n                    ), nt.path\n                except AssertionError:\n                    if not LINUX:\n                        raise\n                    # https://github.com/giampaolo/psutil/issues/759\n                    with open_text('/proc/self/smaps') as f:\n                        data = f.read()\n                    if f\"{nt.path} (deleted)\" not in data:\n                        raise\n            elif '64' not in os.path.basename(nt.path):\n                # XXX - On Windows we have this strange behavior with\n                # 64 bit dlls: they are visible via explorer but cannot\n                # be accessed via os.stat() (wtf?).\n                try:\n                    st = os.stat(nt.path)\n                except FileNotFoundError:\n                    pass\n                else:\n                    assert stat.S_ISREG(st.st_mode), nt.path\n\n        for nt in ext_maps:\n            for fname in nt._fields:\n                value = getattr(nt, fname)\n                if fname == 'path':\n                    continue\n                if fname in {'addr', 'perms'}:\n                    assert value, value\n                else:\n                    assert isinstance(value, int)\n                    assert value >= 0, value\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_MAPS, reason=\"not supported\")\n    def test_memory_maps_lists_lib(self):\n        # Make sure a newly loaded shared lib is listed.\n        p = psutil.Process()\n        with copyload_shared_lib() as path:\n\n            def normpath(p):\n                return os.path.realpath(os.path.normcase(p))\n\n            libpaths = [normpath(x.path) for x in p.memory_maps()]\n            assert normpath(path) in libpaths\n\n    def test_memory_percent(self):\n        p = psutil.Process()\n        p.memory_percent()\n        with pytest.raises(ValueError):\n            p.memory_percent(memtype=\"?!?\")\n        if LINUX or MACOS or WINDOWS:\n            p.memory_percent(memtype='uss')\n\n    def test_page_faults(self):\n        p = psutil.Process()\n        pfaults = p.page_faults()\n        assert pfaults.minor > 0\n        assert pfaults.major >= 0\n\n    def test_is_running(self):\n        p = self.spawn_psproc()\n        assert p.is_running()\n        assert p.is_running()\n        p.kill()\n        p.wait()\n        assert not p.is_running()\n        assert not p.is_running()\n\n    def test_exe(self):\n        p = self.spawn_psproc()\n        exe = p.exe()\n        try:\n            assert exe == PYTHON_EXE\n        except AssertionError:\n            if WINDOWS and len(exe) == len(PYTHON_EXE):\n                # on Windows we don't care about case sensitivity\n                normcase = os.path.normcase\n                assert normcase(exe) == normcase(PYTHON_EXE)\n            else:\n                # certain platforms such as BSD are more accurate returning:\n                # \"/usr/local/bin/python3.7\"\n                # ...instead of:\n                # \"/usr/local/bin/python\"\n                # We do not want to consider this difference in accuracy\n                # an error.\n                ver = f\"{sys.version_info[0]}.{sys.version_info[1]}\"\n                try:\n                    assert exe.replace(ver, '') == PYTHON_EXE.replace(ver, '')\n                except AssertionError:\n                    # Typically MACOS. Really not sure what to do here.\n                    pass\n\n        out = sh([exe, \"-c\", \"import os; print('hey')\"])\n        assert out == 'hey'\n\n    def test_cmdline(self):\n        cmdline = [\n            PYTHON_EXE,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)]\",\n        ]\n        p = self.spawn_psproc(cmdline)\n\n        if NETBSD and p.cmdline() == []:\n            # https://github.com/giampaolo/psutil/issues/2250\n            return pytest.skip(\"OPENBSD: returned EBUSY\")\n\n        # XXX - most of the times the underlying sysctl() call on Net\n        # and Open BSD returns a truncated string.\n        # Also /proc/pid/cmdline behaves the same so it looks\n        # like this is a kernel bug.\n        # XXX - AIX truncates long arguments in /proc/pid/cmdline\n        if NETBSD or OPENBSD or AIX:\n            assert p.cmdline()[0] == PYTHON_EXE\n        else:\n            if MACOS and CI_TESTING:\n                pyexe = p.cmdline()[0]\n                if pyexe != PYTHON_EXE:\n                    assert ' '.join(p.cmdline()[1:]) == ' '.join(cmdline[1:])\n                    return None\n            assert ' '.join(p.cmdline()) == ' '.join(cmdline)\n\n    def test_long_cmdline(self):\n        cmdline = [PYTHON_EXE]\n        cmdline.extend([\"-v\"] * 50)\n        cmdline.extend(\n            [\"-c\", \"import time; [time.sleep(0.1) for x in range(100)]\"]\n        )\n        p = self.spawn_psproc(cmdline)\n\n        # XXX - flaky test: exclude the python exe which, for some\n        # reason, and only sometimes, on OSX appears different.\n        cmdline = cmdline[1:]\n\n        if OPENBSD:\n            # XXX: for some reason the test process may turn into a\n            # zombie (don't know why).\n            try:\n                assert p.cmdline()[1:] == cmdline\n            except psutil.ZombieProcess:\n                return pytest.skip(\"OPENBSD: process turned into zombie\")\n        else:\n            ret = p.cmdline()[1:]\n            if NETBSD and ret == []:\n                # https://github.com/giampaolo/psutil/issues/2250\n                return pytest.skip(\"OPENBSD: returned EBUSY\")\n            assert ret == cmdline\n\n    def test_name(self):\n        p = self.spawn_psproc()\n        name = p.name().lower()\n        if name.endswith(\"t\"):  # in the free-threaded build\n            name = name[:-1]\n        pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()\n        assert pyexe.startswith(name), (pyexe, name)\n\n    @retry_on_failure()\n    def test_long_name(self):\n        pyexe = create_py_exe(self.get_testfn(suffix=string.digits * 2))\n        cmdline = [\n            pyexe,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)]\",\n        ]\n        p = self.spawn_psproc(cmdline)\n        if OPENBSD:\n            # XXX: for some reason the test process may turn into a\n            # zombie (don't know why). Because the name() is long, all\n            # UNIX kernels truncate it to 15 chars, so internally psutil\n            # tries to guess the full name() from the cmdline(). But the\n            # cmdline() of a zombie on OpenBSD fails (internally), so we\n            # just compare the first 15 chars. Full explanation:\n            # https://github.com/giampaolo/psutil/issues/2239\n            try:\n                assert p.name() == os.path.basename(pyexe)\n            except AssertionError:\n                if p.status() == psutil.STATUS_ZOMBIE:\n                    assert os.path.basename(pyexe).startswith(p.name())\n                else:\n                    raise\n        else:\n            assert p.name() == os.path.basename(pyexe)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_uids(self):\n        p = psutil.Process()\n        real, effective, _saved = p.uids()\n        # os.getuid() refers to \"real\" uid\n        assert real == os.getuid()\n        # os.geteuid() refers to \"effective\" uid\n        assert effective == os.geteuid()\n        # No such thing as os.getsuid() (\"saved\" uid), but we have\n        # os.getresuid() which returns all of them.\n        if hasattr(os, \"getresuid\"):\n            assert os.getresuid() == p.uids()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_gids(self):\n        p = psutil.Process()\n        real, effective, _saved = p.gids()\n        # os.getuid() refers to \"real\" uid\n        assert real == os.getgid()\n        # os.geteuid() refers to \"effective\" uid\n        assert effective == os.getegid()\n        # No such thing as os.getsgid() (\"saved\" gid), but we have\n        # os.getresgid() which returns all of them.\n        if hasattr(os, \"getresuid\"):\n            assert os.getresgid() == p.gids()\n\n    def test_nice(self):\n        def cleanup(init):\n            try:\n                p.nice(init)\n            except psutil.AccessDenied:\n                pass\n\n        p = psutil.Process()\n        with pytest.raises(TypeError):\n            p.nice(\"str\")\n        init = p.nice()\n        self.addCleanup(cleanup, init)\n\n        if WINDOWS:\n            highest_prio = None\n            for prio in [\n                psutil.IDLE_PRIORITY_CLASS,\n                psutil.BELOW_NORMAL_PRIORITY_CLASS,\n                psutil.NORMAL_PRIORITY_CLASS,\n                psutil.ABOVE_NORMAL_PRIORITY_CLASS,\n                psutil.HIGH_PRIORITY_CLASS,\n                psutil.REALTIME_PRIORITY_CLASS,\n            ]:\n                with self.subTest(prio=prio):\n                    try:\n                        p.nice(prio)\n                    except psutil.AccessDenied:\n                        pass\n                    else:\n                        new_prio = p.nice()\n                        # The OS may limit our maximum priority,\n                        # even if the function succeeds. For higher\n                        # priorities, we match either the expected\n                        # value or the highest so far.\n                        if prio in {\n                            psutil.ABOVE_NORMAL_PRIORITY_CLASS,\n                            psutil.HIGH_PRIORITY_CLASS,\n                            psutil.REALTIME_PRIORITY_CLASS,\n                        }:\n                            if new_prio == prio or highest_prio is None:\n                                highest_prio = prio\n                                assert new_prio == highest_prio\n                        else:\n                            assert new_prio == prio\n        else:\n            try:\n                if hasattr(os, \"getpriority\"):\n                    assert (\n                        os.getpriority(os.PRIO_PROCESS, os.getpid())\n                        == p.nice()\n                    )\n                p.nice(1)\n                assert p.nice() == 1\n                if hasattr(os, \"getpriority\"):\n                    assert (\n                        os.getpriority(os.PRIO_PROCESS, os.getpid())\n                        == p.nice()\n                    )\n                # XXX - going back to previous nice value raises\n                # AccessDenied on MACOS\n                if not MACOS:\n                    p.nice(0)\n                    assert p.nice() == 0\n            except psutil.AccessDenied:\n                pass\n\n    def test_status(self):\n        p = psutil.Process()\n        assert p.status() == psutil.STATUS_RUNNING\n\n    def test_username(self):\n        p = self.spawn_psproc()\n        username = p.username()\n        if WINDOWS:\n            domain, username = username.split('\\\\')\n            getpass_user = getpass.getuser()\n            if getpass_user.endswith('$'):\n                # When running as a service account (most likely to be\n                # NetworkService), these user name calculations don't produce\n                # the same result, causing the test to fail.\n                return pytest.skip('running as service account')\n            assert username == getpass_user\n            if 'USERDOMAIN' in os.environ:\n                assert domain == os.environ['USERDOMAIN']\n        else:\n            assert username == getpass.getuser()\n\n    def test_cwd(self):\n        p = self.spawn_psproc()\n        assert p.cwd() == os.getcwd()\n\n    def test_cwd_2(self):\n        cmd = [\n            PYTHON_EXE,\n            \"-c\",\n            (\n                \"import os, time; os.chdir('..'); [time.sleep(0.1) for x in\"\n                \" range(100)]\"\n            ),\n        ]\n        p = self.spawn_psproc(cmd)\n        call_until(lambda: p.cwd() == os.path.dirname(os.getcwd()))\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_AFFINITY, reason=\"not supported\")\n    def test_cpu_affinity(self):\n        p = psutil.Process()\n        initial = p.cpu_affinity()\n        assert initial, initial\n        self.addCleanup(p.cpu_affinity, initial)\n\n        if hasattr(os, \"sched_getaffinity\"):\n            assert initial == list(os.sched_getaffinity(p.pid))\n        assert len(initial) == len(set(initial))\n\n        all_cpus = list(range(len(psutil.cpu_percent(percpu=True))))\n        for n in all_cpus:\n            p.cpu_affinity([n])\n            assert p.cpu_affinity() == [n]\n            if hasattr(os, \"sched_getaffinity\"):\n                assert p.cpu_affinity() == list(os.sched_getaffinity(p.pid))\n            # also test num_cpu()\n            if hasattr(p, \"num_cpu\"):\n                assert p.cpu_affinity()[0] == p.num_cpu()\n\n        # [] is an alias for \"all eligible CPUs\"; on Linux this may\n        # not be equal to all available CPUs, see:\n        # https://github.com/giampaolo/psutil/issues/956\n        p.cpu_affinity([])\n        if LINUX:\n            assert p.cpu_affinity() == p._proc._get_eligible_cpus()\n        else:\n            assert p.cpu_affinity() == all_cpus\n        if hasattr(os, \"sched_getaffinity\"):\n            assert p.cpu_affinity() == list(os.sched_getaffinity(p.pid))\n\n        with pytest.raises(TypeError):\n            p.cpu_affinity(1)\n        p.cpu_affinity(initial)\n        # it should work with all iterables, not only lists\n        p.cpu_affinity(set(all_cpus))\n        p.cpu_affinity(tuple(all_cpus))\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_AFFINITY, reason=\"not supported\")\n    def test_cpu_affinity_errs(self):\n        p = self.spawn_psproc()\n        invalid_cpu = [len(psutil.cpu_times(percpu=True)) + 10]\n        with pytest.raises(ValueError):\n            p.cpu_affinity(invalid_cpu)\n        with pytest.raises(ValueError):\n            p.cpu_affinity(range(10000, 11000))\n        with pytest.raises((TypeError, ValueError)):\n            p.cpu_affinity([0, \"1\"])\n        with pytest.raises(ValueError):\n            p.cpu_affinity([0, -1])\n\n    @pytest.mark.skipif(not HAS_PROC_CPU_AFFINITY, reason=\"not supported\")\n    def test_cpu_affinity_all_combinations(self):\n        p = psutil.Process()\n        initial = p.cpu_affinity()\n        assert initial, initial\n        self.addCleanup(p.cpu_affinity, initial)\n\n        # All possible CPU set combinations.\n        if len(initial) > 12:\n            initial = initial[:12]  # ...otherwise it will take forever\n        combos = []\n        for i in range(len(initial) + 1):\n            combos.extend(\n                list(subset)\n                for subset in itertools.combinations(initial, i)\n                if subset\n            )\n\n        for combo in combos:\n            p.cpu_affinity(combo)\n            assert sorted(p.cpu_affinity()) == sorted(combo)\n\n    # TODO: #595\n    @pytest.mark.skipif(BSD, reason=\"broken on BSD\")\n    def test_open_files(self):\n        p = psutil.Process()\n        testfn = self.get_testfn()\n        files = p.open_files()\n        assert testfn not in files\n        with open(testfn, 'wb') as f:\n            f.write(b'x' * 1024)\n            f.flush()\n            # give the kernel some time to see the new file\n            call_until(lambda: len(p.open_files()) != len(files))\n            files = p.open_files()\n            filenames = [os.path.normcase(x.path) for x in files]\n            assert os.path.normcase(testfn) in filenames\n            if LINUX:\n                for file in files:\n                    if file.path == testfn:\n                        assert file.position == 1024\n        for file in files:\n            assert os.path.isfile(file.path), file\n\n        # another process\n        cmdline = (\n            f\"import time; f = open(r'{testfn}', 'r'); [time.sleep(0.1) for x\"\n            \" in range(100)];\"\n        )\n        p = self.spawn_psproc([PYTHON_EXE, \"-c\", cmdline])\n\n        for x in range(100):\n            filenames = [os.path.normcase(x.path) for x in p.open_files()]\n            if testfn in filenames:\n                break\n            time.sleep(0.01)\n        else:\n            assert os.path.normcase(testfn) in filenames\n        for file in filenames:\n            assert os.path.isfile(file), file\n\n    # TODO: #595\n    @pytest.mark.skipif(BSD, reason=\"broken on BSD\")\n    def test_open_files_2(self):\n        # test fd and path fields\n        p = psutil.Process()\n        normcase = os.path.normcase\n        testfn = self.get_testfn()\n        with open(testfn, 'w') as fileobj:\n            for file in p.open_files():\n                if (\n                    normcase(file.path) == normcase(fileobj.name)\n                    or file.fd == fileobj.fileno()\n                ):\n                    break\n            else:\n                return pytest.fail(f\"no file found; files={p.open_files()!r}\")\n            assert normcase(file.path) == normcase(fileobj.name)\n            if WINDOWS:\n                assert file.fd == -1\n            else:\n                assert file.fd == fileobj.fileno()\n            # test positions\n            ntuple = p.open_files()[0]\n            assert ntuple[0] == ntuple.path\n            assert ntuple[1] == ntuple.fd\n            # test file is gone\n            assert fileobj.name not in p.open_files()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    @pytest.mark.xdist_group(name=\"serial\")\n    def test_num_fds(self):\n        p = psutil.Process()\n        testfn = self.get_testfn()\n        start = p.num_fds()\n        with open(testfn, 'w'):\n            assert p.num_fds() == start + 1\n            with socket.socket():\n                assert p.num_fds() == start + 2\n        assert p.num_fds() == start\n\n    @skip_on_not_implemented(only_if=LINUX)\n    @pytest.mark.skipif(\n        OPENBSD or NETBSD, reason=\"not reliable on OPENBSD & NETBSD\"\n    )\n    def test_num_ctx_switches(self):\n        p = psutil.Process()\n        before = sum(p.num_ctx_switches())\n        for _ in range(2):\n            time.sleep(0.05)  # this shall ensure a context switch happens\n            after = sum(p.num_ctx_switches())\n            if after > before:\n                return None\n        return pytest.fail(\n            \"num ctx switches still the same after 2 iterations\"\n        )\n\n    def test_ppid(self):\n        p = psutil.Process()\n        if hasattr(os, 'getppid'):\n            assert p.ppid() == os.getppid()\n        p = self.spawn_psproc()\n        assert p.ppid() == os.getpid()\n\n    def test_parent(self):\n        p = self.spawn_psproc()\n        assert p.parent().pid == os.getpid()\n\n        lowest_pid = psutil.pids()[0]\n        assert psutil.Process(lowest_pid).parent() is None\n\n    def test_parent_mocked_ctime(self):\n        # Make sure we get a fresh copy of the ctime before processing\n        # parent().We make the assumption that the parent pid MUST have\n        # a creation time < than the child. If system clock is updated\n        # this assumption was broken.\n        # https://github.com/giampaolo/psutil/issues/2542\n        p = self.spawn_psproc()\n        p.create_time()  # trigger cache\n        assert p._create_time\n        p._create_time = 1\n        assert p.parent().pid == os.getpid()\n\n    def test_parent_multi(self):\n        parent = psutil.Process()\n        child, grandchild = self.spawn_children_pair()\n        assert grandchild.parent() == child\n        assert child.parent() == parent\n\n    @retry_on_failure()\n    def test_parents(self):\n        parent = psutil.Process()\n        assert parent.parents()\n        child, grandchild = self.spawn_children_pair()\n        assert child.parents()[0] == parent\n        assert grandchild.parents()[0] == child\n        assert grandchild.parents()[1] == parent\n\n    def test_children(self):\n        parent = psutil.Process()\n        assert not parent.children()\n        assert not parent.children(recursive=True)\n        # On Windows we set the flag to 0 in order to cancel out the\n        # CREATE_NO_WINDOW flag (enabled by default) which creates\n        # an extra \"conhost.exe\" child.\n        child = self.spawn_psproc(creationflags=0)\n        children1 = parent.children()\n        children2 = parent.children(recursive=True)\n        for children in (children1, children2):\n            assert len(children) == 1\n            assert children[0].pid == child.pid\n            assert children[0].ppid() == parent.pid\n\n    def test_children_mocked_ctime(self):\n        # Make sure we get a fresh copy of the ctime before processing\n        # children(). We make the assumption that process children MUST\n        # have a creation time > than the parent. If system clock is\n        # updated this assumption was broken.\n        # https://github.com/giampaolo/psutil/issues/2542\n        parent = psutil.Process()\n        parent.create_time()  # trigger cache\n        assert parent._create_time\n        parent._create_time += 100000\n\n        assert not parent.children()\n        assert not parent.children(recursive=True)\n        # On Windows we set the flag to 0 in order to cancel out the\n        # CREATE_NO_WINDOW flag (enabled by default) which creates\n        # an extra \"conhost.exe\" child.\n        child = self.spawn_psproc(creationflags=0)\n        children1 = parent.children()\n        children2 = parent.children(recursive=True)\n        for children in (children1, children2):\n            assert len(children) == 1\n            assert children[0].pid == child.pid\n            assert children[0].ppid() == parent.pid\n\n    def test_children_recursive(self):\n        # Test children() against two sub processes, p1 and p2, where\n        # p1 (our child) spawned p2 (our grandchild).\n        parent = psutil.Process()\n        child, grandchild = self.spawn_children_pair()\n        assert parent.children() == [child]\n        assert parent.children(recursive=True) == [child, grandchild]\n        # If the intermediate process is gone there's no way for\n        # children() to recursively find it.\n        child.terminate()\n        child.wait()\n        assert not parent.children(recursive=True)\n\n    def test_children_duplicates(self):\n        # find the process which has the highest number of children\n        table = collections.defaultdict(int)\n        for p in psutil.process_iter():\n            try:\n                table[p.ppid()] += 1\n            except psutil.Error:\n                pass\n        # this is the one, now let's make sure there are no duplicates\n        pid = max(table.items(), key=lambda x: x[1])[0]\n        if LINUX and pid == 0:\n            return pytest.skip(\"PID 0\")\n        p = psutil.Process(pid)\n        try:\n            c = p.children(recursive=True)\n        except psutil.AccessDenied:  # windows\n            pass\n        else:\n            assert len(c) == len(set(c))\n\n    def test_parents_and_children(self):\n        parent = psutil.Process()\n        child, grandchild = self.spawn_children_pair()\n        # forward\n        children = parent.children(recursive=True)\n        assert len(children) == 2\n        assert children[0] == child\n        assert children[1] == grandchild\n        # backward\n        parents = grandchild.parents()\n        assert parents[0] == child\n        assert parents[1] == parent\n\n    def test_suspend_resume(self):\n        p = self.spawn_psproc()\n        p.suspend()\n        for _ in range(100):\n            if p.status() == psutil.STATUS_STOPPED:\n                break\n            time.sleep(0.01)\n        p.resume()\n        assert p.status() != psutil.STATUS_STOPPED\n\n    def test_invalid_pid(self):\n        with pytest.raises(TypeError):\n            psutil.Process(\"1\")\n        with pytest.raises(ValueError):\n            psutil.Process(-1)\n\n    def test_as_dict(self):\n        p = psutil.Process()\n        d = p.as_dict(attrs=['exe', 'name'])\n        assert sorted(d.keys()) == ['exe', 'name']\n\n        p = psutil.Process(min(psutil.pids()))\n        d = p.as_dict(attrs=['net_connections'], ad_value='foo')\n        if not isinstance(d['net_connections'], list):\n            assert d['net_connections'] == 'foo'\n\n        # Test ad_value is set on AccessDenied.\n        with mock.patch(\n            'psutil.Process.nice', create=True, side_effect=psutil.AccessDenied\n        ):\n            assert p.as_dict(attrs=[\"nice\"], ad_value=1) == {\"nice\": 1}\n\n        # Test that NoSuchProcess bubbles up.\n        with mock.patch(\n            'psutil.Process.nice',\n            create=True,\n            side_effect=psutil.NoSuchProcess(p.pid, \"name\"),\n        ):\n            with pytest.raises(psutil.NoSuchProcess):\n                p.as_dict(attrs=[\"nice\"])\n\n        # Test that ZombieProcess is swallowed.\n        with mock.patch(\n            'psutil.Process.nice',\n            create=True,\n            side_effect=psutil.ZombieProcess(p.pid, \"name\"),\n        ):\n            assert p.as_dict(attrs=[\"nice\"], ad_value=\"foo\") == {\"nice\": \"foo\"}\n\n        # By default APIs raising NotImplementedError are\n        # supposed to be skipped.\n        with mock.patch(\n            'psutil.Process.nice', create=True, side_effect=NotImplementedError\n        ):\n            d = p.as_dict()\n            assert 'nice' not in list(d.keys())\n            # ...unless the user explicitly asked for some attr.\n            with pytest.raises(NotImplementedError):\n                p.as_dict(attrs=[\"nice\"])\n\n        # errors\n        with pytest.raises(TypeError):\n            p.as_dict('name')\n        with pytest.raises(ValueError):\n            p.as_dict(['foo'])\n        with pytest.raises(ValueError):\n            p.as_dict(['foo', 'bar'])\n\n    def test_oneshot(self):\n        p = psutil.Process()\n        with mock.patch(\"psutil._psplatform.Process.cpu_times\") as m:\n            with p.oneshot():\n                p.cpu_times()\n                p.cpu_times()\n            assert m.call_count == 1\n\n        with mock.patch(\"psutil._psplatform.Process.cpu_times\") as m:\n            p.cpu_times()\n            p.cpu_times()\n        assert m.call_count == 2\n\n    def test_oneshot_twice(self):\n        # Test the case where the ctx manager is __enter__ed twice.\n        # The second __enter__ is supposed to resut in a NOOP.\n        p = psutil.Process()\n        with mock.patch(\"psutil._psplatform.Process.cpu_times\") as m1:\n            with mock.patch(\"psutil._psplatform.Process.oneshot_enter\") as m2:\n                with p.oneshot():\n                    p.cpu_times()\n                    p.cpu_times()\n                    with p.oneshot():\n                        p.cpu_times()\n                        p.cpu_times()\n                assert m1.call_count == 1\n                assert m2.call_count == 1\n\n        with mock.patch(\"psutil._psplatform.Process.cpu_times\") as m:\n            p.cpu_times()\n            p.cpu_times()\n        assert m.call_count == 2\n\n    def test_oneshot_cache(self):\n        # Make sure oneshot() cache is nonglobal. Instead it's\n        # supposed to be bound to the Process instance, see:\n        # https://github.com/giampaolo/psutil/issues/1373\n        p1, p2 = self.spawn_children_pair()\n        p1_ppid = p1.ppid()\n        p2_ppid = p2.ppid()\n        assert p1_ppid != p2_ppid\n        with p1.oneshot():\n            assert p1.ppid() == p1_ppid\n            assert p2.ppid() == p2_ppid\n        with p2.oneshot():\n            assert p1.ppid() == p1_ppid\n            assert p2.ppid() == p2_ppid\n\n    def test_halfway_terminated_process(self):\n        # Test that NoSuchProcess exception gets raised in case the\n        # process dies after we create the Process object.\n        # Example:\n        # >>> proc = Process(1234)\n        # >>> time.sleep(2)  # time-consuming task, process dies in meantime\n        # >>> proc.name()\n        # Refers to Issue #15\n        def assert_raises_nsp(fun, fun_name):\n            try:\n                ret = fun()\n            except psutil.ZombieProcess:  # differentiate from NSP\n                raise\n            except psutil.NoSuchProcess:\n                pass\n            except psutil.AccessDenied:\n                if OPENBSD and fun_name in {'threads', 'num_threads'}:\n                    return None\n                raise\n            else:\n                # NtQuerySystemInformation succeeds even if process is gone.\n                if WINDOWS and fun_name in {'exe', 'name'}:\n                    return None\n                return pytest.fail(\n                    f\"{fun!r} didn't raise NSP and returned {ret!r} instead\"\n                )\n\n        p = self.spawn_psproc()\n        p.terminate()\n        p.wait()\n        if WINDOWS:  # XXX\n            call_until(lambda: p.pid not in psutil.pids())\n        self.assert_proc_gone(p)\n\n        ns = process_namespace(p)\n        for fun, name in ns.iter(ns.all):\n            assert_raises_nsp(fun, name)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_zombie_process(self):\n        parent, zombie = self.spawn_zombie()\n        self.assert_proc_zombie(zombie)\n        if hasattr(psutil._psplatform.cext, \"proc_is_zombie\"):\n            assert not psutil._psplatform.cext.proc_is_zombie(os.getpid())\n            assert psutil._psplatform.cext.proc_is_zombie(zombie.pid)\n        parent.terminate()\n        parent.wait()\n        zombie.wait()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_zombie_process_is_running_w_exc(self):\n        # Emulate a case where internally is_running() raises\n        # ZombieProcess.\n        p = psutil.Process()\n        with mock.patch(\n            \"psutil.Process\", side_effect=psutil.ZombieProcess(0)\n        ) as m:\n            assert p.is_running()\n            assert m.called\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_zombie_process_status_w_exc(self):\n        # Emulate a case where internally status() raises\n        # ZombieProcess.\n        p = psutil.Process()\n        with mock.patch(\n            \"psutil._psplatform.Process.status\",\n            side_effect=psutil.ZombieProcess(0),\n        ) as m:\n            assert p.status() == psutil.STATUS_ZOMBIE\n            assert m.called\n\n    def test_reused_pid(self):\n        # Emulate a case where PID has been reused by another process.\n        subp = self.spawn_subproc()\n        p = psutil.Process(subp.pid)\n        p._ident = (p.pid, p.create_time() + 100)\n\n        list(psutil.process_iter())\n        assert p.pid in psutil._pmap\n        assert not p.is_running()\n\n        # make sure is_running() removed PID from process_iter()\n        # internal cache\n        with mock.patch.object(psutil._common, \"PSUTIL_DEBUG\", True):\n            with contextlib.redirect_stderr(io.StringIO()) as f:\n                list(psutil.process_iter())\n        assert (\n            f\"refreshing Process instance for reused PID {p.pid}\"\n            in f.getvalue()\n        )\n        assert p.pid not in psutil._pmap\n\n        assert p != psutil.Process(subp.pid)\n        msg = \"process no longer exists and its PID has been reused\"\n        ns = process_namespace(p)\n        for fun, name in ns.iter(ns.setters + ns.killers, clear_cache=False):\n            with self.subTest(name=name):\n                with pytest.raises(psutil.NoSuchProcess, match=msg):\n                    fun()\n\n        assert \"terminated + PID reused\" in str(p)\n        assert \"terminated + PID reused\" in repr(p)\n\n        with pytest.raises(psutil.NoSuchProcess, match=msg):\n            p.ppid()\n        with pytest.raises(psutil.NoSuchProcess, match=msg):\n            p.parent()\n        with pytest.raises(psutil.NoSuchProcess, match=msg):\n            p.parents()\n        with pytest.raises(psutil.NoSuchProcess, match=msg):\n            p.children()\n\n    def test_pid_0(self):\n        # Process(0) is supposed to work on all platforms except Linux\n        if 0 not in psutil.pids():\n            with pytest.raises(psutil.NoSuchProcess):\n                psutil.Process(0)\n            # These 2 are a contradiction, but \"ps\" says PID 1's parent\n            # is PID 0.\n            assert not psutil.pid_exists(0)\n            assert psutil.Process(1).ppid() == 0\n            return\n\n        p = psutil.Process(0)\n        exc = psutil.AccessDenied if WINDOWS else ValueError\n        with pytest.raises(ValueError):\n            p.wait()\n        with pytest.raises(exc):\n            p.terminate()\n        with pytest.raises(exc):\n            p.suspend()\n        with pytest.raises(exc):\n            p.resume()\n        with pytest.raises(exc):\n            p.kill()\n        with pytest.raises(exc):\n            p.send_signal(signal.SIGTERM)\n\n        # test all methods\n        ns = process_namespace(p)\n        for fun, name in ns.iter(ns.getters + ns.setters):\n            try:\n                ret = fun()\n            except psutil.AccessDenied:\n                pass\n            else:\n                if name in {\"uids\", \"gids\"}:\n                    assert ret.real == 0\n                elif name == \"username\":\n                    user = 'NT AUTHORITY\\\\SYSTEM' if WINDOWS else 'root'\n                    assert p.username() == user\n                elif name == \"name\":\n                    assert name, name\n\n        if not OPENBSD:\n            assert 0 in psutil.pids()\n            assert psutil.pid_exists(0)\n\n    @pytest.mark.skipif(not HAS_PROC_ENVIRON, reason=\"not supported\")\n    def test_environ(self):\n        def clean_dict(d):\n            exclude = {\"PLAT\", \"HOME\"}\n            if MACOS:\n                exclude.update([\n                    \"__CF_USER_TEXT_ENCODING\",\n                    \"VERSIONER_PYTHON_PREFER_32_BIT\",\n                    \"VERSIONER_PYTHON_VERSION\",\n                ])\n            for name in list(d.keys()):\n                if name in exclude or name.startswith(\"PYTEST_\"):\n                    d.pop(name)\n            return {\n                k.replace(\"\\r\", \"\").replace(\"\\n\", \"\"): (\n                    v.replace(\"\\r\", \"\").replace(\"\\n\", \"\")\n                )\n                for k, v in d.items()\n            }\n\n        self.maxDiff = None\n        p = psutil.Process()\n        d1 = clean_dict(p.environ())\n        d2 = clean_dict(os.environ.copy())\n        if not OSX and GITHUB_ACTIONS:\n            assert d1 == d2\n\n    @pytest.mark.skipif(not HAS_PROC_ENVIRON, reason=\"not supported\")\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    @pytest.mark.skipif(\n        MACOS_11PLUS,\n        reason=\"macOS 11+ can't get another process environment, issue #2084\",\n    )\n    @pytest.mark.skipif(\n        NETBSD, reason=\"sometimes fails on `assert is_running()`\"\n    )\n    def test_weird_environ(self):\n        # environment variables can contain values without an equals sign\n        code = textwrap.dedent(\"\"\"\n            #include <unistd.h>\n            #include <fcntl.h>\n\n            char * const argv[] = {\"cat\", 0};\n            char * const envp[] = {\"A=1\", \"X\", \"C=3\", 0};\n\n            int main(void) {\n                // Close stderr on exec so parent can wait for the\n                // execve to finish.\n                if (fcntl(2, F_SETFD, FD_CLOEXEC) != 0)\n                    return 0;\n                return execve(\"/bin/cat\", argv, envp);\n            }\n            \"\"\")\n        cexe = create_c_exe(self.get_testfn(), c_code=code)\n        sproc = self.spawn_subproc(\n            [cexe], stdin=subprocess.PIPE, stderr=subprocess.PIPE\n        )\n        p = psutil.Process(sproc.pid)\n        wait_for_pid(p.pid)\n        assert p.is_running()\n        # Wait for process to exec or exit.\n        assert sproc.stderr.read() == b\"\"\n        if MACOS and CI_TESTING:\n            try:\n                env = p.environ()\n            except psutil.AccessDenied:\n                # XXX: fails sometimes with:\n                # PermissionError from 'sysctl(KERN_PROCARGS2) -> EIO'\n                return\n        else:\n            env = p.environ()\n        assert env == {\"A\": \"1\", \"C\": \"3\"}\n        sproc.communicate()\n        assert sproc.returncode == 0\n\n\n# ===================================================================\n# --- psutil.Process.wait tests\n# ===================================================================\n\n\nclass TestProcessWait(PsutilTestCase):\n\n    def test_wait_exited(self):\n        # Test waitpid() + WIFEXITED -> WEXITSTATUS.\n        # normal return, same as exit(0)\n        cmd = [PYTHON_EXE, \"-c\", \"pass\"]\n        p = self.spawn_psproc(cmd)\n        code = p.wait()\n        assert code == 0\n        self.assert_proc_gone(p)\n        # exit(1), implicit in case of error\n        cmd = [PYTHON_EXE, \"-c\", \"1 / 0\"]\n        p = self.spawn_psproc(cmd, stderr=subprocess.PIPE)\n        code = p.wait()\n        assert code == 1\n        self.assert_proc_gone(p)\n        # via sys.exit()\n        cmd = [PYTHON_EXE, \"-c\", \"import sys; sys.exit(5);\"]\n        p = self.spawn_psproc(cmd)\n        code = p.wait()\n        assert code == 5\n        self.assert_proc_gone(p)\n        # via os._exit()\n        cmd = [PYTHON_EXE, \"-c\", \"import os; os._exit(5);\"]\n        p = self.spawn_psproc(cmd)\n        code = p.wait()\n        assert code == 5\n        self.assert_proc_gone(p)\n\n    @pytest.mark.skipif(not POSIX, reason=\"not POSIX\")\n    def test_wait_signaled(self):\n        p = psutil.Process(self.spawn_subproc().pid)\n        p.terminate()\n        code = p.wait()\n        assert code == -signal.SIGTERM\n        assert isinstance(code, enum.IntEnum)\n        # second call is cached\n        assert code == -signal.SIGTERM\n        # Call the underlying implementation. Done to exercise the\n        # poll/kqueue machinery on a gone PID. Also, waitpid() is\n        # supposed to fail with ESRCH.\n        assert p._proc.wait() is None\n\n    @pytest.mark.skipif(NETBSD, reason=\"fails on NETBSD\")\n    def test_wait_stopped(self):\n        p = self.spawn_psproc()\n        if POSIX:\n            # Test waitpid() + WIFSTOPPED and WIFCONTINUED.\n            # Note: if a process is stopped it ignores SIGTERM.\n            p.send_signal(signal.SIGSTOP)\n            with pytest.raises(psutil.TimeoutExpired):\n                p.wait(timeout=0.001)\n            p.send_signal(signal.SIGCONT)\n            with pytest.raises(psutil.TimeoutExpired):\n                p.wait(timeout=0.001)\n            p.send_signal(signal.SIGTERM)\n            assert p.wait() == -signal.SIGTERM\n            assert p.wait() == -signal.SIGTERM\n        else:\n            p.suspend()\n            with pytest.raises(psutil.TimeoutExpired):\n                p.wait(timeout=0.001)\n            p.resume()\n            with pytest.raises(psutil.TimeoutExpired):\n                p.wait(timeout=0.001)\n            p.terminate()\n            assert p.wait() == signal.SIGTERM\n            assert p.wait() == signal.SIGTERM\n\n    def test_wait_non_children(self):\n        # Test wait() against a process which is not our direct\n        # child.\n        child, grandchild = self.spawn_children_pair()\n        with pytest.raises(psutil.TimeoutExpired):\n            child.wait(0.01)\n        with pytest.raises(psutil.TimeoutExpired):\n            grandchild.wait(0.01)\n        # We also terminate the direct child otherwise the\n        # grandchild will hang until the parent is gone.\n        child.terminate()\n        grandchild.terminate()\n        child_ret = child.wait()\n        grandchild_ret = grandchild.wait()\n        if POSIX:\n            assert child_ret == -signal.SIGTERM\n            # For processes which are not our children we're supposed\n            # to get None.\n            assert grandchild_ret is None\n        else:\n            assert child_ret == signal.SIGTERM\n            assert child_ret == signal.SIGTERM\n\n    def test_wait_timeout(self):\n        p = self.spawn_psproc()\n        p.name()\n        with pytest.raises(psutil.TimeoutExpired):\n            p.wait(0.01)\n        with pytest.raises(psutil.TimeoutExpired):\n            p.wait(0)\n\n    def test_wait_timeout_nonblocking(self):\n        p = self.spawn_psproc()\n        with pytest.raises(psutil.TimeoutExpired):\n            p.wait(0)\n        p.kill()\n        stop_at = time.time() + GLOBAL_TIMEOUT\n        while time.time() < stop_at:\n            try:\n                code = p.wait(0)\n                break\n            except psutil.TimeoutExpired:\n                pass\n        else:\n            return pytest.fail('timeout')\n        if POSIX:\n            assert code == -signal.SIGKILL\n        else:\n            assert code == signal.SIGTERM\n        self.assert_proc_gone(p)\n\n    def test_wait_errors(self):\n        p = psutil.Process()\n        with pytest.raises(TypeError, match=\"must be an int or float\"):\n            p.wait(\"foo\")\n        with pytest.raises(ValueError, match=\"must be positive or zero\"):\n            p.wait(-1)\n        p._pid = 0\n        with pytest.raises(ValueError, match=\"can't wait for PID 0\"):\n            p.wait()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_wait_zombie(self):\n        parent, zombie = self.spawn_zombie()\n        with pytest.raises(psutil.TimeoutExpired):\n            zombie.wait(0.001)\n        parent.terminate()\n        parent.wait()\n        zombie.wait()\n\n    # --- tests for wait_pid_posix()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_os_waitpid_error(self):\n        # os.waitpid() is supposed to catch ECHILD only.\n        # Test that any other errno results in an exception.\n        with mock.patch(\n            \"os.waitpid\", side_effect=OSError(errno.EBADF, \"\")\n        ) as m:\n            with pytest.raises(OSError):\n                psutil._psposix.wait_pid_posix(os.getpid())\n            assert m.called\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_os_waitpid_bad_ret_status(self):\n        # Simulate os.waitpid() returning a bad status.\n        with mock.patch(\"os.waitpid\", return_value=(1, -1)) as m:\n            with pytest.raises(ValueError):\n                psutil._psposix.wait_pid_posix(os.getpid())\n            assert m.called\n\n    # --- tests for pidfd_open() and kqueue()\n\n    def assert_wait_pid_errors(self, patch_target, wait_func, errors):\n        # Test that all errors are caught and wait_pid_posix()\n        # fallback is used.\n        sproc = self.spawn_subproc()\n        sproc.terminate()\n\n        errors = list(errors)\n        random.shuffle(errors)\n        for idx, err in enumerate(errors):\n            with mock.patch(\n                patch_target,\n                side_effect=OSError(err, os.strerror(err)),\n            ) as m:\n                # the second time waitpid() does not return the exit code\n                code = -signal.SIGTERM if idx == 0 else None\n                assert wait_func(sproc.pid) == code\n            assert m.called\n\n    @pytest.mark.skipif(\n        not hasattr(os, \"pidfd_open\"),\n        reason=\"LINUX only\" if not LINUX else \"not supported\",\n    )\n    def test_pidfd_open_errors(self):\n        from psutil._psposix import wait_pid_pidfd_open\n\n        self.assert_wait_pid_errors(\n            \"os.pidfd_open\",\n            wait_pid_pidfd_open,\n            [errno.ESRCH, errno.EMFILE, errno.ENFILE, errno.EBADF],\n        )\n\n    @pytest.mark.skipif(\n        not hasattr(select, \"kqueue\"), reason=\"MACOS and BSD only\"\n    )\n    def test_kqueue_errors(self):\n        from psutil._psposix import wait_pid_kqueue\n\n        self.assert_wait_pid_errors(\n            \"select.kqueue\",\n            wait_pid_kqueue,\n            [errno.EMFILE, errno.ENFILE, errno.EBADF],\n        )\n\n    def assert_wait_pid_race(self, patch_target, real_func):\n        # Kill process after patch_target succeeds but before wait()\n        # completes, then verify Process.wait() still works.\n        sproc = self.spawn_subproc()\n        psproc = psutil.Process(sproc.pid)\n\n        def wrapper(*args, **kwargs):\n            ret = real_func(*args, **kwargs)\n            sproc.terminate()\n            sproc.wait()\n            return ret\n\n        with mock.patch(patch_target, side_effect=wrapper) as m:\n            psproc.wait()\n        assert m.called\n\n    @pytest.mark.skipif(\n        not hasattr(os, \"pidfd_open\"),\n        reason=\"LINUX only\" if not LINUX else \"not supported\",\n    )\n    def test_pidfd_open_race(self):\n        self.assert_wait_pid_race(\"os.pidfd_open\", os.pidfd_open)\n\n    @pytest.mark.skipif(\n        not hasattr(select, \"kqueue\"), reason=\"MACOS and BSD only\"\n    )\n    def test_kqueue_race(self):\n        self.assert_wait_pid_race(\"select.kqueue\", select.kqueue)\n\n    @pytest.mark.skipif(\n        not hasattr(select, \"kqueue\"), reason=\"MACOS and BSD only\"\n    )\n    def test_kqueue_control_errors(self):\n        # Test that kqueue.control() errors are caught and fallback is used.\n        from psutil._psposix import wait_pid_kqueue\n\n        sproc = self.spawn_subproc()\n        sproc.terminate()\n\n        errors = [errno.EACCES, errno.EPERM, errno.ESRCH]\n        random.shuffle(errors)\n        for idx, err in enumerate(errors):\n            kq_mock = mock.Mock()\n            kq_mock.control.side_effect = OSError(err, os.strerror(err))\n            kq_mock.close = mock.Mock()\n\n            with mock.patch(\"select.kqueue\", return_value=kq_mock):\n                # the second time waitpid() does not return the exit code\n                code = -signal.SIGTERM if idx == 0 else None\n                assert wait_pid_kqueue(sproc.pid) == code\n            assert kq_mock.control.called\n\n        # illegitimate error\n        kq_mock = mock.Mock()\n        kq_mock.control.side_effect = OSError(\n            errno.EBADF, os.strerror(errno.EBADF)\n        )\n        kq_mock.close = mock.Mock()\n        with mock.patch(\"select.kqueue\", return_value=kq_mock):\n            with pytest.raises(OSError):\n                wait_pid_kqueue(sproc.pid)\n\n\n# ===================================================================\n# --- psutil.Popen tests\n# ===================================================================\n\n\nclass TestPopen(PsutilTestCase):\n    \"\"\"Tests for psutil.Popen class.\"\"\"\n\n    @classmethod\n    def tearDownClass(cls):\n        reap_children()\n\n    @pytest.mark.skipif(MACOS and GITHUB_ACTIONS, reason=\"hangs on OSX + CI\")\n    def test_misc(self):\n        # XXX this test causes a ResourceWarning because\n        # psutil.__subproc instance doesn't get properly freed.\n        # Not sure what to do though.\n        cmd = [\n            PYTHON_EXE,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)];\",\n        ]\n        with psutil.Popen(\n            cmd,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            env=PYTHON_EXE_ENV,\n        ) as proc:\n            proc.name()\n            proc.cpu_times()\n            proc.stdin  # noqa: B018\n            assert dir(proc)\n            with pytest.raises(AttributeError):\n                proc.foo  # noqa: B018\n            proc.terminate()\n        if POSIX:\n            assert proc.wait(5) == -signal.SIGTERM\n        else:\n            assert proc.wait(5) == signal.SIGTERM\n\n    def test_ctx_manager(self):\n        with psutil.Popen(\n            [PYTHON_EXE, \"-V\"],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            stdin=subprocess.PIPE,\n            env=PYTHON_EXE_ENV,\n        ) as proc:\n            proc.communicate()\n        assert proc.stdout.closed\n        assert proc.stderr.closed\n        assert proc.stdin.closed\n        assert proc.returncode == 0\n\n    def test_kill_terminate(self):\n        # subprocess.Popen()'s terminate(), kill() and send_signal() do\n        # not raise exception after the process is gone. psutil.Popen\n        # diverges from that.\n        cmd = [\n            PYTHON_EXE,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)];\",\n        ]\n        with psutil.Popen(\n            cmd,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            env=PYTHON_EXE_ENV,\n        ) as proc:\n            proc.terminate()\n            proc.wait()\n            with pytest.raises(psutil.NoSuchProcess):\n                proc.terminate()\n            with pytest.raises(psutil.NoSuchProcess):\n                proc.kill()\n            with pytest.raises(psutil.NoSuchProcess):\n                proc.send_signal(signal.SIGTERM)\n            if WINDOWS:\n                with pytest.raises(psutil.NoSuchProcess):\n                    proc.send_signal(signal.CTRL_C_EVENT)\n                with pytest.raises(psutil.NoSuchProcess):\n                    proc.send_signal(signal.CTRL_BREAK_EVENT)\n\n    def test__getattribute__(self):\n        cmd = [\n            PYTHON_EXE,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)];\",\n        ]\n        with psutil.Popen(\n            cmd,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            env=PYTHON_EXE_ENV,\n        ) as proc:\n            proc.terminate()\n            proc.wait()\n            with pytest.raises(AttributeError):\n                proc.foo  # noqa: B018\n"
  },
  {
    "path": "tests/test_process_all.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Iterate over all process PIDs and for each one of them invoke and\ntest all psutil.Process() methods.\n\"\"\"\n\nimport enum\nimport errno\nimport multiprocessing\nimport os\nimport stat\nimport time\nimport traceback\n\nimport pytest\n\nimport psutil\nfrom psutil import AIX\nfrom psutil import BSD\nfrom psutil import FREEBSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import WINDOWS\n\nfrom . import CI_TESTING\nfrom . import PYTEST_PARALLEL\nfrom . import VALID_PROC_STATUSES\nfrom . import PsutilTestCase\nfrom . import check_connection_ntuple\nfrom . import check_fun_type_hints\nfrom . import check_ntuple_type_hints\nfrom . import create_sockets\nfrom . import is_namedtuple\nfrom . import is_win_secure_system_proc\nfrom . import process_namespace\n\n# Cuts the time in half, but (e.g.) on macOS the process pool stays\n# alive after join() (multiprocessing bug?), messing up other tests.\nUSE_PROC_POOL = LINUX and not CI_TESTING and not PYTEST_PARALLEL\n\n\ndef proc_info(pid):\n    tcase = PsutilTestCase()\n\n    def check_exception(exc, proc, name, ppid):\n        assert exc.pid == pid\n        if exc.name is not None:\n            assert exc.name == name\n        if isinstance(exc, psutil.ZombieProcess):\n            tcase.assert_proc_zombie(proc)\n            if exc.ppid is not None:\n                assert exc.ppid >= 0\n                assert exc.ppid == ppid\n        elif isinstance(exc, psutil.NoSuchProcess):\n            tcase.assert_proc_gone(proc)\n        str(exc)\n        repr(exc)\n\n    def do_wait():\n        if pid != 0:\n            try:\n                proc.wait(0)\n            except psutil.Error as exc:\n                check_exception(exc, proc, name, ppid)\n\n    try:\n        proc = psutil.Process(pid)\n    except psutil.NoSuchProcess:\n        tcase.assert_pid_gone(pid)\n        return {}\n    try:\n        d = proc.as_dict(['ppid', 'name'])\n    except psutil.NoSuchProcess:\n        tcase.assert_proc_gone(proc)\n    else:\n        name, ppid = d['name'], d['ppid']\n        info = {'pid': proc.pid}\n        ns = process_namespace(proc)\n        # We don't use oneshot() because in order not to fool\n        # check_exception() in case of NSP.\n        for fun, fun_name in ns.iter(ns.getters, clear_cache=False):\n            try:\n                ret = fun()\n            except psutil.Error as exc:\n                check_exception(exc, proc, name, ppid)\n                continue\n            else:\n                check_fun_type_hints(fun, ret)\n                if is_namedtuple(ret):\n                    check_ntuple_type_hints(ret)\n                info[fun_name] = ret\n        do_wait()\n        return info\n\n\nclass TestFetchAllProcesses(PsutilTestCase):\n    \"\"\"Test which iterates over all running processes and performs\n    some sanity checks against Process API's returned values.\n    Uses a process pool to get info about all processes.\n    \"\"\"\n\n    def setUp(self):\n        psutil._set_debug(False)\n        # Using a pool in a CI env may result in deadlock, see:\n        # https://github.com/giampaolo/psutil/issues/2104\n        if USE_PROC_POOL:\n            # The 'fork' method is the only one that does not\n            # create a \"resource_tracker\" process. The problem\n            # when creating this process is that it ignores\n            # SIGTERM and SIGINT, and this makes \"reap_children\"\n            # hang... The following code should run on python-3.4\n            # and later.\n            multiprocessing.set_start_method('fork')\n            self.pool = multiprocessing.Pool()\n\n    def tearDown(self):\n        psutil._set_debug(True)\n        if USE_PROC_POOL:\n            self.pool.terminate()\n            self.pool.join()\n\n    def iter_proc_info(self):\n        # Fixes \"can't pickle <function proc_info>: it's not the\n        # same object as test_process_all.proc_info\".\n        from tests.test_process_all import proc_info\n\n        if USE_PROC_POOL:\n            return self.pool.imap_unordered(proc_info, psutil.pids())\n        else:\n            ls = [proc_info(pid) for pid in psutil.pids()]\n            return ls\n\n    def test_all(self):\n        failures = []\n        for info in self.iter_proc_info():\n            for name, value in info.items():\n                meth = getattr(self, name)\n                try:\n                    meth(value, info)\n                except Exception:  # noqa: BLE001\n                    s = '\\n' + '=' * 70 + '\\n'\n                    s += \"FAIL: name=test_{}, pid={}, ret={}\\n\\n\".format(\n                        name,\n                        info['pid'],\n                        repr(value),\n                    )\n                    s += '-' * 70\n                    s += f\"\\n{traceback.format_exc()}\"\n                    s = \"\\n\".join((\" \" * 4) + i for i in s.splitlines()) + \"\\n\"\n                    failures.append(s)\n                else:\n                    if value not in (0, 0.0, [], None, '', {}):\n                        assert value, value\n        if failures:\n            return pytest.fail(''.join(failures))\n\n    def cmdline(self, ret, info):\n        assert isinstance(ret, list)\n        for part in ret:\n            assert isinstance(part, str)\n\n    def exe(self, ret, info):\n        assert isinstance(ret, str)\n        assert ret.strip() == ret\n        if ret:\n            if WINDOWS and not ret.endswith('.exe'):\n                return  # May be \"Registry\", \"MemCompression\", ...\n            assert os.path.isabs(ret), ret\n            # Note: os.stat() may return False even if the file is there\n            # hence we skip the test, see:\n            # http://stackoverflow.com/questions/3112546/os-path-exists-lies\n            if POSIX and os.path.isfile(ret):\n                if hasattr(os, 'access') and hasattr(os, \"X_OK\"):\n                    # XXX: may fail on MACOS\n                    try:\n                        assert os.access(ret, os.X_OK)\n                    except AssertionError:\n                        if os.path.exists(ret) and not CI_TESTING:\n                            raise\n\n    def pid(self, ret, info):\n        assert isinstance(ret, int)\n        assert ret >= 0\n\n    def ppid(self, ret, info):\n        assert isinstance(ret, int)\n        assert ret >= 0\n        proc_info(ret)\n\n    def name(self, ret, info):\n        assert isinstance(ret, str)\n        if WINDOWS and not ret and is_win_secure_system_proc(info['pid']):\n            # https://github.com/giampaolo/psutil/issues/2338\n            return\n        # on AIX, \"<exiting>\" processes don't have names\n        if not AIX:\n            assert ret, repr(ret)\n\n    def create_time(self, ret, info):\n        assert isinstance(ret, float)\n        try:\n            assert ret >= 0\n        except AssertionError:\n            # XXX\n            if OPENBSD and info['status'] == psutil.STATUS_ZOMBIE:\n                pass\n            else:\n                raise\n        # this can't be taken for granted on all platforms\n        # assert ret >= psutil.boot_time())\n        # make sure returned value can be pretty printed\n        # with strftime\n        time.strftime(\"%Y %m %d %H:%M:%S\", time.localtime(ret))\n\n    def uids(self, ret, info):\n        for uid in ret:\n            assert isinstance(uid, int)\n            assert uid >= 0\n\n    def gids(self, ret, info):\n        # note: testing all gids as above seems not to be reliable for\n        # gid == 30 (nodoby); not sure why.\n        for gid in ret:\n            assert isinstance(gid, int)\n            if not MACOS and not NETBSD:\n                assert gid >= 0\n\n    def username(self, ret, info):\n        assert isinstance(ret, str)\n        assert ret.strip() == ret\n        assert ret.strip()\n\n    def status(self, ret, info):\n        assert isinstance(ret, str)\n        assert ret, ret\n        assert ret != '?'  # XXX\n        assert ret in VALID_PROC_STATUSES\n\n    def io_counters(self, ret, info):\n        for field in ret:\n            assert isinstance(field, int)\n            if field != -1:\n                assert field >= 0\n\n    def ionice(self, ret, info):\n        if LINUX:\n            assert isinstance(ret.ioclass, int)\n            assert isinstance(ret.value, int)\n            assert ret.ioclass >= 0\n            assert ret.value >= 0\n        else:  # Windows, Cygwin\n            choices = [\n                psutil.IOPRIO_VERYLOW,\n                psutil.IOPRIO_LOW,\n                psutil.IOPRIO_NORMAL,\n                psutil.IOPRIO_HIGH,\n            ]\n            assert isinstance(ret, int)\n            assert ret >= 0\n            assert ret in choices\n\n    def num_threads(self, ret, info):\n        assert isinstance(ret, int)\n        if WINDOWS and ret == 0 and is_win_secure_system_proc(info['pid']):\n            # https://github.com/giampaolo/psutil/issues/2338\n            return\n        assert ret >= 1\n\n    def threads(self, ret, info):\n        assert isinstance(ret, list)\n        for t in ret:\n            assert t.id >= 0\n            assert t.user_time >= 0\n            assert t.system_time >= 0\n            for field in t:\n                assert isinstance(field, (int, float))\n\n    def cpu_times(self, ret, info):\n        for n in ret:\n            assert isinstance(n, float)\n            assert n >= 0\n        # TODO: check ntuple fields\n\n    def cpu_percent(self, ret, info):\n        assert isinstance(ret, float)\n        assert 0.0 <= ret <= 100.0, ret\n\n    def cpu_num(self, ret, info):\n        assert isinstance(ret, int)\n        if FREEBSD and ret == -1:\n            return\n        assert ret >= 0\n        if psutil.cpu_count() == 1:\n            assert ret == 0\n        assert ret in list(range(psutil.cpu_count()))\n\n    def memory_info(self, ret, info):\n        self.check_proc_memory(ret)\n\n    def memory_info_ex(self, ret, info):\n        self.check_proc_memory(ret)\n\n    def memory_footprint(self, ret, info):\n        for name in ret._fields:\n            value = getattr(ret, name)\n            assert isinstance(value, int)\n            assert value >= 0\n\n    def open_files(self, ret, info):\n        assert isinstance(ret, list)\n        for f in ret:\n            assert isinstance(f.fd, int)\n            assert isinstance(f.path, str)\n            assert f.path.strip() == f.path\n            if WINDOWS:\n                assert f.fd == -1\n            elif LINUX:\n                assert isinstance(f.position, int)\n                assert isinstance(f.mode, str)\n                assert isinstance(f.flags, int)\n                assert f.position >= 0\n                assert f.mode in {'r', 'w', 'a', 'r+', 'a+'}\n                assert f.flags > 0\n            elif BSD and not f.path:\n                # XXX see: https://github.com/giampaolo/psutil/issues/595\n                continue\n            assert os.path.isabs(f.path), f\n            try:\n                st = os.stat(f.path)\n            except FileNotFoundError:\n                pass\n            else:\n                assert stat.S_ISREG(st.st_mode), f\n\n    def num_fds(self, ret, info):\n        assert isinstance(ret, int)\n        assert ret >= 0\n\n    def net_connections(self, ret, info):\n        with create_sockets():\n            assert len(ret) == len(set(ret))\n            for conn in ret:\n                check_connection_ntuple(conn)\n\n    def cwd(self, ret, info):\n        assert isinstance(ret, str)\n        assert ret.strip() == ret\n        if ret:\n            assert os.path.isabs(ret), ret\n            try:\n                st = os.stat(ret)\n            except OSError as err:\n                if WINDOWS and psutil._psplatform.is_permission_err(err):\n                    pass\n                # directory has been removed in mean time\n                elif err.errno != errno.ENOENT:\n                    raise\n            else:\n                assert stat.S_ISDIR(st.st_mode)\n\n    def memory_percent(self, ret, info):\n        assert isinstance(ret, float)\n        assert 0 <= ret <= 100, ret\n\n    def is_running(self, ret, info):\n        assert isinstance(ret, bool)\n\n    def cpu_affinity(self, ret, info):\n        assert isinstance(ret, list)\n        assert ret != []\n        cpus = list(range(psutil.cpu_count()))\n        for n in ret:\n            assert isinstance(n, int)\n            assert n in cpus\n\n    def terminal(self, ret, info):\n        assert isinstance(ret, (str, type(None)))\n        if ret is not None:\n            assert os.path.isabs(ret), ret\n            assert os.path.exists(ret), ret\n\n    def memory_maps(self, ret, info):\n        for nt in ret:\n            if hasattr(nt, \"addr\"):\n                assert isinstance(nt.addr, str)\n            if hasattr(nt, \"perms\"):\n                assert isinstance(nt.perms, str)\n            assert isinstance(nt.path, str)\n            for fname in nt._fields:\n                value = getattr(nt, fname)\n                if fname == 'path':\n                    if value.startswith((\"[\", \"anon_inode:\")):  # linux\n                        continue\n                    if BSD and value == \"pvclock\":  # seen on FreeBSD\n                        continue\n                    assert os.path.isabs(nt.path), nt.path\n                    # commented as on Linux we might get\n                    # '/foo/bar (deleted)'\n                    # assert os.path.exists(nt.path), nt.path\n                elif fname == 'addr':\n                    assert value, repr(value)\n                elif fname == 'perms':\n                    if not WINDOWS:\n                        assert value, repr(value)\n                else:\n                    assert isinstance(value, int)\n                    assert value >= 0\n\n    def num_handles(self, ret, info):\n        assert isinstance(ret, int)\n        assert ret >= 0\n\n    def page_faults(self, ret, info):\n        assert isinstance(ret.minor, int)\n        assert isinstance(ret.major, int)\n        assert ret.minor >= 0\n        assert ret.major >= 0\n\n    def nice(self, ret, info):\n        assert isinstance(ret, int)\n        if POSIX:\n            assert -20 <= ret <= 20, ret\n        else:\n            priorities = [\n                getattr(psutil, x)\n                for x in dir(psutil)\n                if x.endswith('_PRIORITY_CLASS')\n            ]\n            assert ret in priorities\n            assert isinstance(ret, enum.IntEnum)\n\n    def num_ctx_switches(self, ret, info):\n        for value in ret:\n            assert isinstance(value, int)\n            assert value >= 0\n\n    def rlimit(self, ret, info):\n        assert isinstance(ret, tuple)\n        assert len(ret) == 2\n        assert ret[0] >= -1\n        assert ret[1] >= -1\n\n    def environ(self, ret, info):\n        assert isinstance(ret, dict)\n        for k, v in ret.items():\n            assert isinstance(k, str)\n            assert isinstance(v, str)\n\n\nclass TestPidsRange(PsutilTestCase):\n    \"\"\"Given pid_exists() return value for a range of PIDs which may or\n    may not exist, make sure that psutil.Process() and psutil.pids()\n    agree with pid_exists(). This guarantees that the 3 APIs are all\n    consistent with each other. See:\n    https://github.com/giampaolo/psutil/issues/2359\n\n    XXX - Note about Windows: it turns out there are some \"hidden\" PIDs\n    which are not returned by psutil.pids() and are also not revealed\n    by taskmgr.exe and ProcessHacker, still they can be instantiated by\n    psutil.Process() and queried. One of such PIDs is \"conhost.exe\".\n    Running as_dict() for it reveals that some Process() APIs\n    erroneously raise NoSuchProcess, so we know we have problem there.\n    Let's ignore this for now, since it's quite a corner case (who even\n    imagined hidden PIDs existed on Windows?).\n    \"\"\"\n\n    def setUp(self):\n        psutil._set_debug(False)\n\n    def tearDown(self):\n        psutil._set_debug(True)\n\n    def test_it(self):\n        def is_linux_tid(pid):\n            try:\n                f = open(f\"/proc/{pid}/status\", \"rb\")  # noqa: SIM115\n            except FileNotFoundError:\n                return False\n            else:\n                with f:\n                    for line in f:\n                        if line.startswith(b\"Tgid:\"):\n                            tgid = int(line.split()[1])\n                            # If tgid and pid are different then we're\n                            # dealing with a process TID.\n                            return tgid != pid\n                    raise ValueError(\"'Tgid' line not found\")\n\n        def check(pid):\n            # In case of failure retry up to 3 times in order to avoid\n            # race conditions, especially when running in a CI\n            # environment where PIDs may appear and disappear at any\n            # time.\n            x = 3\n            while True:\n                exists = psutil.pid_exists(pid)\n                try:\n                    if exists:\n                        psutil.Process(pid)\n                        if not WINDOWS:  # see docstring\n                            assert pid in psutil.pids()\n                    else:\n                        # On OpenBSD thread IDs can be instantiated,\n                        # and oneshot() succeeds, but other APIs fail\n                        # with EINVAL.\n                        if not OPENBSD:\n                            with pytest.raises(psutil.NoSuchProcess):\n                                psutil.Process(pid)\n                        if not WINDOWS:  # see docstring\n                            assert pid not in psutil.pids()\n                except (psutil.Error, AssertionError):\n                    x -= 1\n                    if x == 0:\n                        raise\n                else:\n                    return\n\n        for pid in range(1, 3000):\n            if LINUX and is_linux_tid(pid):\n                # On Linux a TID (thread ID) can be passed to the\n                # Process class and is querable like a PID (process\n                # ID). Skip it.\n                continue\n            check(pid)\n"
  },
  {
    "path": "tests/test_scripts.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Test various scripts.\"\"\"\n\nimport ast\nimport os\nimport pathlib\nimport stat\n\nimport pytest\n\nfrom psutil import LINUX\nfrom psutil import POSIX\nfrom psutil import WINDOWS\n\nfrom . import CI_TESTING\nfrom . import HAS_BATTERY\nfrom . import HAS_PROC_MEMORY_FOOTPRINT\nfrom . import HAS_PROC_MEMORY_MAPS\nfrom . import HAS_SENSORS_BATTERY\nfrom . import HAS_SENSORS_FANS\nfrom . import HAS_SENSORS_TEMPERATURES\nfrom . import PYTHON_EXE\nfrom . import PYTHON_EXE_ENV\nfrom . import ROOT_DIR\nfrom . import PsutilTestCase\nfrom . import import_module_by_path\nfrom . import psutil\nfrom . import sh\n\nSCRIPTS_DIR = pathlib.Path(ROOT_DIR) / \"scripts\"\nINTERNAL_SCRIPTS_DIR = SCRIPTS_DIR / \"internal\"\n\n\n# ===================================================================\n# --- Tests scripts in scripts/ directory\n# ===================================================================\n\n\n@pytest.mark.skipif(\n    CI_TESTING and not os.path.exists(SCRIPTS_DIR),\n    reason=\"can't find scripts/ directory\",\n)\nclass TestExampleScripts(PsutilTestCase):\n    @staticmethod\n    def assert_stdout(exe, *args):\n        env = PYTHON_EXE_ENV.copy()\n        env.pop(\"PSUTIL_DEBUG\", None)  # avoid spamming to stderr\n        exe = os.path.join(SCRIPTS_DIR, exe)\n        cmd = [PYTHON_EXE, exe, *args]\n        try:\n            out = sh(cmd, env=env).strip()\n        except RuntimeError as err:\n            if 'AccessDenied' in str(err):\n                return str(err)\n            else:\n                raise\n        assert out, out\n        return out\n\n    @staticmethod\n    def assert_syntax(exe):\n        exe = os.path.join(SCRIPTS_DIR, exe)\n        with open(exe, encoding=\"utf8\") as f:\n            src = f.read()\n        ast.parse(src)\n\n    def test_coverage(self):\n        # make sure all example scripts have a test method defined\n        meths = dir(self)\n        for name in os.listdir(SCRIPTS_DIR):\n            if name.endswith('.py') and not name.startswith(\"_\"):\n                if 'test_' + os.path.splitext(name)[0] not in meths:\n                    # self.assert_stdout(name)\n                    return pytest.fail(\n                        \"no test defined for\"\n                        f\" {os.path.join(SCRIPTS_DIR, name)!r} script\"\n                    )\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_executable(self):\n        for root, dirs, files in os.walk(SCRIPTS_DIR):\n            for file in files:\n                if file.endswith('.py'):\n                    path = os.path.join(root, file)\n                    if not stat.S_IXUSR & os.stat(path)[stat.ST_MODE]:\n                        return pytest.fail(f\"{path!r} is not executable\")\n\n    def test_disk_usage(self):\n        self.assert_stdout('disk_usage.py')\n\n    def test_free(self):\n        self.assert_stdout('free.py')\n\n    def test_meminfo(self):\n        self.assert_stdout('meminfo.py')\n\n    def test_procinfo(self):\n        self.assert_stdout('procinfo.py', str(os.getpid()))\n\n    @pytest.mark.skipif(CI_TESTING and not psutil.users(), reason=\"no users\")\n    def test_who(self):\n        self.assert_stdout('who.py')\n\n    def test_ps(self):\n        self.assert_stdout('ps.py')\n\n    def test_pstree(self):\n        self.assert_stdout('pstree.py')\n\n    def test_netstat(self):\n        self.assert_stdout('netstat.py')\n\n    def test_ifconfig(self):\n        self.assert_stdout('ifconfig.py')\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_MAPS, reason=\"not supported\")\n    def test_pmap(self):\n        self.assert_stdout('pmap.py', str(os.getpid()))\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_FOOTPRINT, reason=\"not supported\")\n    def test_procsmem(self):\n        self.assert_stdout('procsmem.py')\n\n    def test_killall(self):\n        self.assert_syntax('killall.py')\n\n    def test_nettop(self):\n        self.assert_syntax('nettop.py')\n\n    def test_top(self):\n        self.assert_syntax('top.py')\n\n    def test_iotop(self):\n        self.assert_syntax('iotop.py')\n\n    def test_pidof(self):\n        output = self.assert_stdout('pidof.py', psutil.Process().name())\n        assert str(os.getpid()) in output\n\n    @pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n    def test_winservices(self):\n        self.assert_stdout('winservices.py')\n\n    def test_cpu_distribution(self):\n        self.assert_syntax('cpu_distribution.py')\n\n    @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason=\"not supported\")\n    def test_temperatures(self):\n        if not psutil.sensors_temperatures():\n            return pytest.skip(\"no temperatures\")\n        self.assert_stdout('temperatures.py')\n\n    @pytest.mark.skipif(not HAS_SENSORS_FANS, reason=\"not supported\")\n    def test_fans(self):\n        if not psutil.sensors_fans():\n            return pytest.skip(\"no fans\")\n        self.assert_stdout('fans.py')\n\n    @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason=\"not supported\")\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_battery(self):\n        self.assert_stdout('battery.py')\n\n    @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason=\"not supported\")\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_sensors(self):\n        self.assert_stdout('sensors.py')\n\n\n# ===================================================================\n# --- Tests scripts in scripts/internal/ directory\n# ===================================================================\n\n\n@pytest.mark.skipif(\n    CI_TESTING and not os.path.exists(INTERNAL_SCRIPTS_DIR),\n    reason=\"can't find scripts/internal/ directory\",\n)\nclass TestInternalScripts(PsutilTestCase):\n    @staticmethod\n    def ls():\n        for name in os.listdir(INTERNAL_SCRIPTS_DIR):\n            if name.endswith(\".py\"):\n                yield os.path.join(INTERNAL_SCRIPTS_DIR, name)\n\n    def test_syntax_all(self):\n        for path in self.ls():\n            with open(path, encoding=\"utf8\") as f:\n                data = f.read()\n            ast.parse(data)\n\n    # don't care about other platforms, this is really just for myself\n    @pytest.mark.skipif(not LINUX, reason=\"not on LINUX\")\n    @pytest.mark.skipif(CI_TESTING, reason=\"not on CI\")\n    def test_import_all(self):\n        for path in self.ls():\n            try:\n                import_module_by_path(path)\n            except SystemExit:\n                pass\n"
  },
  {
    "path": "tests/test_sudo.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Tests which are meant to be run as root.\n\nNOTE: keep this module compatible with unittest: we want to run this\nfile with the unittest runner, since pytest may not be installed for\nthe root user.\n\"\"\"\n\nimport datetime\nimport time\nimport unittest\n\nimport psutil\nfrom psutil import FREEBSD\nfrom psutil import LINUX\nfrom psutil import OPENBSD\nfrom psutil import WINDOWS\n\nfrom . import CI_TESTING\nfrom . import PsutilTestCase\n\n\ndef get_systime():\n    if hasattr(time, \"clock_gettime\") and hasattr(time, \"CLOCK_REALTIME\"):\n        return time.clock_gettime(time.CLOCK_REALTIME)\n    return time.time()\n\n\ndef set_systime(secs):  # secs since the epoch\n    if hasattr(time, \"clock_settime\") and hasattr(time, \"CLOCK_REALTIME\"):\n        try:\n            time.clock_settime(time.CLOCK_REALTIME, secs)\n        except PermissionError:\n            raise unittest.SkipTest(\"needs root\")\n    elif WINDOWS:\n        import pywintypes\n        import win32api\n\n        dt = datetime.datetime.fromtimestamp(secs, datetime.timezone.utc)\n        try:\n            win32api.SetSystemTime(\n                dt.year,\n                dt.month,\n                dt.isoweekday() % 7,\n                dt.day,\n                dt.hour,\n                dt.minute,\n                dt.second,\n                int(dt.microsecond / 1000),\n            )\n        except pywintypes.error as err:\n            if err.winerror == 1314:\n                raise unittest.SkipTest(\"needs Administrator user\")\n            raise\n    else:\n        raise unittest.SkipTest(\"setting systime not supported\")\n\n\nclass TestUpdatedSystemTime(PsutilTestCase):\n    \"\"\"Tests which update the system clock.\"\"\"\n\n    def setUp(self):\n        self.time_updated = False\n        self.orig_time = get_systime()\n        self.time_started = time.monotonic()\n\n    def tearDown(self):\n        if self.time_updated:\n            extra_t = time.monotonic() - self.time_started\n            set_systime(self.orig_time + extra_t)\n\n    def update_systime(self):\n        # set system time 1 hour later\n        set_systime(self.orig_time + 3600)\n        self.time_updated = True\n\n    def test_boot_time(self):\n        # Test that boot_time() reflects system clock updates.\n        t1 = psutil.boot_time()\n        self.update_systime()\n        t2 = psutil.boot_time()\n        self.assertGreater(t2, t1)\n        diff = int(t2 - t1)\n        self.assertAlmostEqual(diff, 3600, delta=1)\n\n    @unittest.skipIf(WINDOWS, \"broken on WINDOWS\")  # TODO: fix it\n    def test_proc_create_time(self):\n        # Test that Process.create_time() reflects system clock\n        # updates. On systems such as Linux this is added on top of the\n        # process monotonic time returned by the kernel.\n        t1 = psutil.Process().create_time()\n        self.update_systime()\n        t2 = psutil.Process().create_time()\n        diff = int(t2 - t1)\n        self.assertAlmostEqual(diff, 3600, delta=1)\n\n    @unittest.skipIf(CI_TESTING, \"skipped on CI for now\")  # TODO: fix it\n    @unittest.skipIf(OPENBSD, \"broken on OPENBSD\")  # TODO: fix it\n    @unittest.skipIf(FREEBSD, \"broken on FREEBSD\")  # TODO: fix it\n    def test_proc_ident(self):\n        p1 = psutil.Process()\n        self.update_systime()\n        p2 = psutil.Process()\n        self.assertEqual(p1._get_ident(), p2._get_ident())\n        self.assertEqual(p1, p2)\n\n    @unittest.skipIf(not LINUX, \"LINUX only\")\n    def test_linux_monotonic_proc_time(self):\n        t1 = psutil.Process()._proc.create_time(monotonic=True)\n        self.update_systime()\n        time.sleep(0.05)\n        t2 = psutil.Process()._proc.create_time(monotonic=True)\n        self.assertEqual(t1, t2)\n"
  },
  {
    "path": "tests/test_sunos.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Sun OS specific tests.\"\"\"\n\nimport os\n\nimport psutil\nfrom psutil import SUNOS\n\nfrom . import PsutilTestCase\nfrom . import pytest\nfrom . import sh\n\n\n@pytest.mark.skipif(not SUNOS, reason=\"SUNOS only\")\nclass SunOSSpecificTestCase(PsutilTestCase):\n    def test_swap_memory(self):\n        out = sh(f\"env PATH=/usr/sbin:/sbin:{os.environ['PATH']} swap -l\")\n        lines = out.strip().split('\\n')[1:]\n        if not lines:\n            raise ValueError('no swap device(s) configured')\n        total = free = 0\n        for line in lines:\n            fields = line.split()\n            total = int(fields[3]) * 512\n            free = int(fields[4]) * 512\n        used = total - free\n\n        psutil_swap = psutil.swap_memory()\n        assert psutil_swap.total == total\n        assert psutil_swap.used == used\n        assert psutil_swap.free == free\n\n    def test_cpu_count(self):\n        out = sh(\"/usr/sbin/psrinfo\")\n        assert psutil.cpu_count() == len(out.split('\\n'))\n"
  },
  {
    "path": "tests/test_system.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Tests for system APIS.\"\"\"\n\nimport datetime\nimport enum\nimport errno\nimport os\nimport pprint\nimport shutil\nimport signal\nimport socket\nimport sys\nimport time\nfrom unittest import mock\n\nimport psutil\nfrom psutil import AIX\nfrom psutil import BSD\nfrom psutil import FREEBSD\nfrom psutil import LINUX\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import SUNOS\nfrom psutil import WINDOWS\nfrom psutil._common import broadcast_addr\n\nfrom . import AARCH64\nfrom . import ASCII_FS\nfrom . import CI_TESTING\nfrom . import GITHUB_ACTIONS\nfrom . import GLOBAL_TIMEOUT\nfrom . import HAS_BATTERY\nfrom . import HAS_CPU_FREQ\nfrom . import HAS_HEAP_INFO\nfrom . import HAS_NET_IO_COUNTERS\nfrom . import HAS_SENSORS_BATTERY\nfrom . import HAS_SENSORS_FANS\nfrom . import HAS_SENSORS_TEMPERATURES\nfrom . import MACOS_12PLUS\nfrom . import PYPY\nfrom . import UNICODE_SUFFIX\nfrom . import PsutilTestCase\nfrom . import check_net_address\nfrom . import pytest\nfrom . import retry_on_failure\n\n# ===================================================================\n# --- System-related API tests\n# ===================================================================\n\n\nclass TestProcessIter(PsutilTestCase):\n    def test_pid_presence(self):\n        assert os.getpid() in [x.pid for x in psutil.process_iter()]\n        sproc = self.spawn_subproc()\n        assert sproc.pid in [x.pid for x in psutil.process_iter()]\n        p = psutil.Process(sproc.pid)\n        p.kill()\n        p.wait()\n        assert sproc.pid not in [x.pid for x in psutil.process_iter()]\n\n    def test_no_duplicates(self):\n        ls = list(psutil.process_iter())\n        assert sorted(ls, key=lambda x: x.pid) == sorted(\n            set(ls), key=lambda x: x.pid\n        )\n\n    def test_emulate_nsp(self):\n        list(psutil.process_iter())  # populate cache\n        for x in range(2):\n            with mock.patch(\n                'psutil.Process.as_dict',\n                side_effect=psutil.NoSuchProcess(os.getpid()),\n            ):\n                assert not list(psutil.process_iter(attrs=[\"cpu_times\"]))\n            psutil.process_iter.cache_clear()  # repeat test without cache\n\n    def test_emulate_access_denied(self):\n        list(psutil.process_iter())  # populate cache\n        for x in range(2):\n            with mock.patch(\n                'psutil.Process.as_dict',\n                side_effect=psutil.AccessDenied(os.getpid()),\n            ):\n                with pytest.raises(psutil.AccessDenied):\n                    list(psutil.process_iter(attrs=[\"cpu_times\"]))\n            psutil.process_iter.cache_clear()  # repeat test without cache\n\n    def test_attrs(self):\n        for p in psutil.process_iter(attrs=['pid']):\n            assert list(p.info.keys()) == ['pid']\n        # yield again\n        for p in psutil.process_iter(attrs=['pid']):\n            assert list(p.info.keys()) == ['pid']\n        with pytest.raises(ValueError):\n            list(psutil.process_iter(attrs=['foo']))\n        with mock.patch(\n            \"psutil._psplatform.Process.cpu_times\",\n            side_effect=psutil.AccessDenied(0, \"\"),\n        ) as m:\n            for p in psutil.process_iter(attrs=[\"pid\", \"cpu_times\"]):\n                assert p.info['cpu_times'] is None\n                assert p.info['pid'] >= 0\n            assert m.called\n        with mock.patch(\n            \"psutil._psplatform.Process.cpu_times\",\n            side_effect=psutil.AccessDenied(0, \"\"),\n        ) as m:\n            flag = object()\n            for p in psutil.process_iter(\n                attrs=[\"pid\", \"cpu_times\"], ad_value=flag\n            ):\n                assert p.info['cpu_times'] is flag\n                assert p.info['pid'] >= 0\n            assert m.called\n\n    def test_cache_clear(self):\n        list(psutil.process_iter())  # populate cache\n        assert psutil._pmap\n        psutil.process_iter.cache_clear()\n        assert not psutil._pmap\n\n\nclass TestProcessAPIs(PsutilTestCase):\n    @pytest.mark.skipif(\n        PYPY and WINDOWS,\n        reason=\"spawn_subproc() unreliable on PYPY + WINDOWS\",\n    )\n    def test_wait_procs(self):\n        def callback(p):\n            pids.append(p.pid)\n\n        pids = []\n        sproc1 = self.spawn_subproc()\n        sproc2 = self.spawn_subproc()\n        sproc3 = self.spawn_subproc()\n        procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]\n        with pytest.raises(ValueError):\n            psutil.wait_procs(procs, timeout=-1)\n        with pytest.raises(TypeError):\n            psutil.wait_procs(procs, callback=1)\n        t = time.time()\n        gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback)\n\n        assert time.time() - t < 0.5\n        assert not gone\n        assert len(alive) == 3\n        assert not pids\n        for p in alive:\n            assert not hasattr(p, 'returncode')\n\n        @retry_on_failure(30)\n        def test_1(procs, callback):\n            gone, alive = psutil.wait_procs(\n                procs, timeout=0.03, callback=callback\n            )\n            assert len(gone) == 1\n            assert len(alive) == 2\n            return gone, alive\n\n        sproc3.terminate()\n        gone, alive = test_1(procs, callback)\n        assert sproc3.pid in [x.pid for x in gone]\n        if POSIX:\n            assert gone.pop().returncode == -signal.SIGTERM\n        else:\n            assert gone.pop().returncode == 1\n        assert pids == [sproc3.pid]\n        for p in alive:\n            assert not hasattr(p, 'returncode')\n\n        @retry_on_failure(30)\n        def test_2(procs, callback):\n            gone, alive = psutil.wait_procs(\n                procs, timeout=0.03, callback=callback\n            )\n            assert len(gone) == 3\n            assert len(alive) == 0\n            return gone, alive\n\n        sproc1.terminate()\n        sproc2.terminate()\n        gone, alive = test_2(procs, callback)\n        assert set(pids) == {sproc1.pid, sproc2.pid, sproc3.pid}\n        for p in gone:\n            assert hasattr(p, 'returncode')\n\n    @pytest.mark.skipif(\n        PYPY and WINDOWS,\n        reason=\"spawn_subproc() unreliable on PYPY + WINDOWS\",\n    )\n    def test_wait_procs_no_timeout(self):\n        sproc1 = self.spawn_subproc()\n        sproc2 = self.spawn_subproc()\n        sproc3 = self.spawn_subproc()\n        procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]\n        for p in procs:\n            p.terminate()\n        psutil.wait_procs(procs)\n\n    def test_pid_exists(self):\n        sproc = self.spawn_subproc()\n        assert psutil.pid_exists(sproc.pid)\n        p = psutil.Process(sproc.pid)\n        p.kill()\n        p.wait()\n        assert not psutil.pid_exists(sproc.pid)\n        assert not psutil.pid_exists(-1)\n        assert psutil.pid_exists(0) == (0 in psutil.pids())\n\n    def test_pid_exists_2(self):\n        pids = psutil.pids()\n        for pid in pids:\n            try:\n                assert psutil.pid_exists(pid)\n            except AssertionError:\n                # in case the process disappeared in meantime fail only\n                # if it is no longer in psutil.pids()\n                time.sleep(0.1)\n                assert pid not in psutil.pids()\n        pids = range(max(pids) + 15000, max(pids) + 16000)\n        for pid in pids:\n            assert not psutil.pid_exists(pid)\n\n\nclass TestMiscAPIs(PsutilTestCase):\n    def test_boot_time(self):\n        bt = psutil.boot_time()\n        assert isinstance(bt, float)\n        assert bt > 0\n        assert bt < time.time()\n\n    @pytest.mark.skipif(\n        CI_TESTING and not psutil.users(), reason=\"unreliable on CI\"\n    )\n    def test_users(self):\n        users = psutil.users()\n        assert users\n        for user in users:\n            with self.subTest(user=str(user)):\n                assert user.name\n                assert isinstance(user.name, str)\n                assert isinstance(user.terminal, (str, type(None)))\n                if user.host is not None:\n                    assert isinstance(user.host, (str, type(None)))\n                user.terminal  # noqa: B018\n                user.host  # noqa: B018\n                assert user.started > 0.0\n                datetime.datetime.fromtimestamp(user.started)\n                if WINDOWS or OPENBSD:\n                    assert user.pid is None\n                else:\n                    psutil.Process(user.pid)\n\n    @pytest.mark.skipif(not HAS_HEAP_INFO, reason=\"not supported\")\n    def test_heap_info(self):\n        m = psutil.heap_info()\n        assert m.heap_used > 0\n        if MACOS:\n            assert m.mmap_used == 0  # not supported\n        else:\n            assert m.mmap_used > 0\n        if WINDOWS:\n            assert m.heap_count >= 0\n\n    @pytest.mark.skipif(not HAS_HEAP_INFO, reason=\"not supported\")\n    def test_heap_trim(self):\n        psutil.heap_trim()\n\n    def test_os_constants(self):\n        names = [\n            \"POSIX\",\n            \"WINDOWS\",\n            \"LINUX\",\n            \"MACOS\",\n            \"FREEBSD\",\n            \"OPENBSD\",\n            \"NETBSD\",\n            \"BSD\",\n            \"SUNOS\",\n        ]\n        for name in names:\n            assert isinstance(getattr(psutil, name), bool), name\n\n        if os.name == 'posix':\n            assert psutil.POSIX\n            assert not psutil.WINDOWS\n            names.remove(\"POSIX\")\n            if \"linux\" in sys.platform.lower():\n                assert psutil.LINUX\n                names.remove(\"LINUX\")\n            elif \"bsd\" in sys.platform.lower():\n                assert psutil.BSD\n                assert [psutil.FREEBSD, psutil.OPENBSD, psutil.NETBSD].count(\n                    True\n                ) == 1\n                names.remove(\"BSD\")\n                names.remove(\"FREEBSD\")\n                names.remove(\"OPENBSD\")\n                names.remove(\"NETBSD\")\n            elif (\n                \"sunos\" in sys.platform.lower()\n                or \"solaris\" in sys.platform.lower()\n            ):\n                assert psutil.SUNOS\n                names.remove(\"SUNOS\")\n            elif \"darwin\" in sys.platform.lower():\n                assert psutil.MACOS\n                names.remove(\"MACOS\")\n        else:\n            assert psutil.WINDOWS\n            assert not psutil.POSIX\n            names.remove(\"WINDOWS\")\n\n        # assert all other constants are set to False\n        for name in names:\n            assert not getattr(psutil, name), name\n\n\nclass TestMemoryAPIs(PsutilTestCase):\n    def test_virtual_memory(self):\n        mem = psutil.virtual_memory()\n        assert mem.total > 0, mem\n        assert mem.available > 0, mem\n        assert 0 <= mem.percent <= 100, mem\n        assert mem.used > 0, mem\n        assert mem.free >= 0, mem\n        for name in mem._fields:\n            value = getattr(mem, name)\n            if name != 'percent':\n                assert isinstance(value, int)\n            if name != 'total':\n                if not value >= 0:\n                    return pytest.fail(f\"{name!r} < 0 ({value})\")\n                if value > mem.total:\n                    return pytest.fail(\n                        f\"{name!r} > total (total={mem.total}, {name}={value})\"\n                    )\n\n    def test_virtual_memory_fields_order(self):\n        mem = psutil.virtual_memory()\n        common = (\"total\", \"available\", \"percent\", \"used\", \"free\")\n        assert mem._fields[:5] == common\n        if LINUX:\n            assert mem._fields[5:] == (\n                \"active\",\n                \"inactive\",\n                \"buffers\",\n                \"cached\",\n                \"shared\",\n                \"slab\",\n            )\n        elif MACOS:\n            assert mem._fields[5:] == (\n                \"active\",\n                \"inactive\",\n                \"wired\",\n            )\n        elif BSD:\n            assert mem._fields[5:] == (\n                \"active\",\n                \"inactive\",\n                \"buffers\",\n                \"cached\",\n                \"shared\",\n                \"wired\",\n            )\n        elif WINDOWS:\n            assert mem._fields[5:] == (\n                \"cached\",\n                \"wired\",\n            )\n        elif SUNOS or AIX:\n            assert mem._fields[5:] == ()\n\n    def test_swap_memory(self):\n        mem = psutil.swap_memory()\n        assert mem._fields == (\n            'total',\n            'used',\n            'free',\n            'percent',\n            'sin',\n            'sout',\n        )\n\n        assert mem.total >= 0, mem\n        assert mem.used >= 0, mem\n        if mem.total > 0:\n            # likely a system with no swap partition\n            assert mem.free > 0, mem\n        else:\n            assert mem.free == 0, mem\n        assert 0 <= mem.percent <= 100, mem\n        assert mem.sin >= 0, mem\n        assert mem.sout >= 0, mem\n\n\nclass TestCpuAPIs(PsutilTestCase):\n    def test_cpu_count_logical(self):\n        logical = psutil.cpu_count()\n        assert logical is not None\n        assert logical == len(psutil.cpu_times(percpu=True))\n        assert logical >= 1\n\n        if os.path.exists(\"/proc/cpuinfo\"):\n            with open(\"/proc/cpuinfo\") as fd:\n                cpuinfo_data = fd.read()\n            if \"physical id\" not in cpuinfo_data:\n                return pytest.skip(\"cpuinfo doesn't include physical id\")\n\n    def test_cpu_count_cores(self):\n        logical = psutil.cpu_count()\n        cores = psutil.cpu_count(logical=False)\n        if cores is None:\n            return pytest.skip(\"cpu_count_cores() is None\")\n        if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1):  # <= Vista\n            assert cores is None\n        else:\n            assert cores >= 1\n            assert logical >= cores\n\n    def test_cpu_count_none(self):\n        # https://github.com/giampaolo/psutil/issues/1085\n        for val in (-1, 0, None):\n            with mock.patch(\n                'psutil._psplatform.cpu_count_logical', return_value=val\n            ) as m:\n                assert psutil.cpu_count() is None\n                assert m.called\n            with mock.patch(\n                'psutil._psplatform.cpu_count_cores', return_value=val\n            ) as m:\n                assert psutil.cpu_count(logical=False) is None\n                assert m.called\n\n    def test_cpu_times(self):\n        # Check type, value >= 0, str().\n        total = 0\n        times = psutil.cpu_times()\n        sum(times)\n        for cp_time in times:\n            assert isinstance(cp_time, float)\n            assert cp_time >= 0.0\n            total += cp_time\n        assert round(abs(total - sum(times)), 6) == 0\n        str(times)\n        # CPU times are always supposed to increase over time\n        # or at least remain the same and that's because time\n        # cannot go backwards.\n        # Surprisingly sometimes this might not be the case (at\n        # least on Windows and Linux), see:\n        # https://github.com/giampaolo/psutil/issues/392\n        # https://github.com/giampaolo/psutil/issues/645\n        # if not WINDOWS:\n        #     last = psutil.cpu_times()\n        #     for x in range(100):\n        #         new = psutil.cpu_times()\n        #         for field in new._fields:\n        #             new_t = getattr(new, field)\n        #             last_t = getattr(last, field)\n        #             assert new_t >= last_t\n        #         last = new\n\n    def test_cpu_times_time_increases(self):\n        # Make sure time increases between calls.\n        t1 = sum(psutil.cpu_times())\n        stop_at = time.time() + GLOBAL_TIMEOUT\n        while time.time() < stop_at:\n            t2 = sum(psutil.cpu_times())\n            if t2 > t1:\n                return None\n        return pytest.fail(\"time remained the same\")\n\n    def test_per_cpu_times(self):\n        # Check type, value >= 0, str().\n        for times in psutil.cpu_times(percpu=True):\n            total = 0\n            sum(times)\n            for cp_time in times:\n                assert isinstance(cp_time, float)\n                assert cp_time >= 0.0\n                total += cp_time\n            assert round(abs(total - sum(times)), 6) == 0\n            str(times)\n        assert len(psutil.cpu_times(percpu=True)[0]) == len(\n            psutil.cpu_times(percpu=False)\n        )\n\n        # Note: in theory CPU times are always supposed to increase over\n        # time or remain the same but never go backwards. In practice\n        # sometimes this is not the case.\n        # This issue seemd to be afflict Windows:\n        # https://github.com/giampaolo/psutil/issues/392\n        # ...but it turns out also Linux (rarely) behaves the same.\n        # last = psutil.cpu_times(percpu=True)\n        # for x in range(100):\n        #     new = psutil.cpu_times(percpu=True)\n        #     for index in range(len(new)):\n        #         newcpu = new[index]\n        #         lastcpu = last[index]\n        #         for field in newcpu._fields:\n        #             new_t = getattr(newcpu, field)\n        #             last_t = getattr(lastcpu, field)\n        #             assert new_t >= last_t\n        #     last = new\n\n    def test_per_cpu_times_2(self):\n        # Simulate some work load then make sure time have increased\n        # between calls.\n        tot1 = psutil.cpu_times(percpu=True)\n        giveup_at = time.time() + GLOBAL_TIMEOUT\n        while True:\n            if time.time() >= giveup_at:\n                return pytest.fail(\"timeout\")\n            tot2 = psutil.cpu_times(percpu=True)\n            for t1, t2 in zip(tot1, tot2):\n                t1, t2 = psutil._cpu_busy_time(t1), psutil._cpu_busy_time(t2)\n                difference = t2 - t1\n                if difference >= 0.05:\n                    return None\n\n    @pytest.mark.skipif(\n        (CI_TESTING and OPENBSD) or MACOS, reason=\"unreliable on OPENBSD + CI\"\n    )\n    @retry_on_failure(30)\n    def test_cpu_times_comparison(self):\n        # Make sure the sum of all per cpu times is almost equal to\n        # base \"one cpu\" times. On OpenBSD the sum of per-CPUs is\n        # higher for some reason.\n        base = psutil.cpu_times()\n        per_cpu = psutil.cpu_times(percpu=True)\n        summed_values = base._make([sum(num) for num in zip(*per_cpu)])\n        for field in base._fields:\n            with self.subTest(\n                field=field, base=str(base), per_cpu=str(per_cpu)\n            ):\n                assert (\n                    abs(getattr(base, field) - getattr(summed_values, field))\n                    < 2\n                )\n\n    def _test_cpu_percent(self, percent, last_ret, new_ret):\n        try:\n            assert isinstance(percent, float)\n            assert percent >= 0.0\n            assert percent <= 100.0 * psutil.cpu_count()\n        except AssertionError as err:\n            raise AssertionError(\n                \"\\n{}\\nlast={}\\nnew={}\".format(\n                    err, pprint.pformat(last_ret), pprint.pformat(new_ret)\n                )\n            )\n\n    def test_cpu_percent(self):\n        last = psutil.cpu_percent(interval=0.001)\n        for _ in range(100):\n            new = psutil.cpu_percent(interval=None)\n            self._test_cpu_percent(new, last, new)\n            last = new\n        with pytest.raises(ValueError):\n            psutil.cpu_percent(interval=-1)\n\n    def test_per_cpu_percent(self):\n        last = psutil.cpu_percent(interval=0.001, percpu=True)\n        assert len(last) == psutil.cpu_count()\n        for _ in range(100):\n            new = psutil.cpu_percent(interval=None, percpu=True)\n            for percent in new:\n                self._test_cpu_percent(percent, last, new)\n            last = new\n        with pytest.raises(ValueError):\n            psutil.cpu_percent(interval=-1, percpu=True)\n\n    def test_cpu_times_percent(self):\n        last = psutil.cpu_times_percent(interval=0.001)\n        for _ in range(100):\n            new = psutil.cpu_times_percent(interval=None)\n            for percent in new:\n                self._test_cpu_percent(percent, last, new)\n            self._test_cpu_percent(sum(new), last, new)\n            last = new\n        with pytest.raises(ValueError):\n            psutil.cpu_times_percent(interval=-1)\n\n    def test_per_cpu_times_percent(self):\n        last = psutil.cpu_times_percent(interval=0.001, percpu=True)\n        assert len(last) == psutil.cpu_count()\n        for _ in range(100):\n            new = psutil.cpu_times_percent(interval=None, percpu=True)\n            for cpu in new:\n                for percent in cpu:\n                    self._test_cpu_percent(percent, last, new)\n                self._test_cpu_percent(sum(cpu), last, new)\n            last = new\n\n    def test_per_cpu_times_percent_negative(self):\n        # see: https://github.com/giampaolo/psutil/issues/645\n        psutil.cpu_times_percent(percpu=True)\n        zero_times = [\n            x._make([0 for x in range(len(x._fields))])\n            for x in psutil.cpu_times(percpu=True)\n        ]\n        with mock.patch('psutil.cpu_times', return_value=zero_times):\n            for cpu in psutil.cpu_times_percent(percpu=True):\n                for percent in cpu:\n                    self._test_cpu_percent(percent, None, None)\n\n    def test_cpu_stats(self):\n        # Tested more extensively in per-platform test modules.\n        infos = psutil.cpu_stats()\n        assert infos._fields == (\n            'ctx_switches',\n            'interrupts',\n            'soft_interrupts',\n            'syscalls',\n        )\n        for name in infos._fields:\n            value = getattr(infos, name)\n            assert value >= 0\n            # on AIX, ctx_switches is always 0\n            if not AIX and name in {'ctx_switches', 'interrupts'}:\n                assert value > 0\n\n    @pytest.mark.skipif(not HAS_CPU_FREQ, reason=\"not supported\")\n    def test_cpu_freq(self):\n        def check_ls(ls):\n            for nt in ls:\n                assert nt._fields == ('current', 'min', 'max')\n                for name in nt._fields:\n                    value = getattr(nt, name)\n                    assert isinstance(value, (int, float))\n                    assert value >= 0\n\n        ls = psutil.cpu_freq(percpu=True)\n        if (FREEBSD or AARCH64) and not ls:\n            return pytest.skip(\n                \"returns empty list on FreeBSD and Linux aarch64\"\n            )\n\n        assert ls, ls\n        check_ls([psutil.cpu_freq(percpu=False)])\n\n        if LINUX:\n            assert len(ls) == psutil.cpu_count()\n\n    def test_getloadavg(self):\n        loadavg = psutil.getloadavg()\n        assert len(loadavg) == 3\n        for load in loadavg:\n            assert isinstance(load, float)\n            assert load >= 0.0\n\n\nclass TestDiskAPIs(PsutilTestCase):\n    def test_disk_usage(self):\n        usage = psutil.disk_usage(os.getcwd())\n        assert usage._fields == ('total', 'used', 'free', 'percent')\n        assert usage.total > 0, usage\n        assert usage.used > 0, usage\n        assert usage.free > 0, usage\n        assert usage.total > usage.used, usage\n        assert usage.total > usage.free, usage\n        assert 0 <= usage.percent <= 100, usage.percent\n\n        shutil_usage = shutil.disk_usage(os.getcwd())\n        tolerance = 5 * 1024 * 1024  # 5MB\n        assert usage.total == shutil_usage.total\n        assert abs(usage.free - shutil_usage.free) < tolerance\n        if not MACOS_12PLUS:\n            # see https://github.com/giampaolo/psutil/issues/2147\n            assert abs(usage.used - shutil_usage.used) < tolerance\n\n        # if path does not exist OSError ENOENT is expected across\n        # all platforms\n        fname = self.get_testfn()\n        with pytest.raises(FileNotFoundError):\n            psutil.disk_usage(fname)\n\n    @pytest.mark.skipif(not ASCII_FS, reason=\"not an ASCII fs\")\n    def test_disk_usage_unicode(self):\n        # See: https://github.com/giampaolo/psutil/issues/416\n        with pytest.raises(UnicodeEncodeError):\n            psutil.disk_usage(UNICODE_SUFFIX)\n\n    def test_disk_usage_bytes(self):\n        psutil.disk_usage(b'.')\n\n    def test_disk_partitions(self):\n        def check_ntuple(nt):\n            assert isinstance(nt.device, str)\n            assert isinstance(nt.mountpoint, str)\n            assert isinstance(nt.fstype, str)\n            assert isinstance(nt.opts, str)\n\n        # all = False\n        ls = psutil.disk_partitions(all=False)\n        assert ls\n        for disk in ls:\n            check_ntuple(disk)\n            if WINDOWS and 'cdrom' in disk.opts:\n                continue\n            if not POSIX:\n                assert os.path.exists(disk.device), disk\n            else:\n                # we cannot make any assumption about this, see:\n                # http://goo.gl/p9c43\n                disk.device  # noqa: B018\n            # on modern systems mount points can also be files\n            assert os.path.exists(disk.mountpoint), disk\n            assert disk.fstype, disk\n\n        # all = True\n        ls = psutil.disk_partitions(all=True)\n        assert ls\n        for disk in psutil.disk_partitions(all=True):\n            check_ntuple(disk)\n            if not WINDOWS and disk.mountpoint:\n                try:\n                    os.stat(disk.mountpoint)\n                except OSError as err:\n                    if GITHUB_ACTIONS and MACOS and err.errno == errno.EIO:\n                        continue\n                    # http://mail.python.org/pipermail/python-dev/2012-June/120787.html\n                    if err.errno not in {errno.EPERM, errno.EACCES}:\n                        raise\n                else:\n                    assert os.path.exists(disk.mountpoint), disk\n\n        # ---\n\n        def find_mount_point(path):\n            path = os.path.abspath(path)\n            while not os.path.ismount(path):\n                path = os.path.dirname(path)\n            return path.lower()\n\n        mount = find_mount_point(__file__)\n        mounts = [\n            x.mountpoint.lower()\n            for x in psutil.disk_partitions(all=True)\n            if x.mountpoint\n        ]\n        assert mount in mounts\n\n    @pytest.mark.skipif(\n        LINUX and not os.path.exists('/proc/diskstats'),\n        reason=\"/proc/diskstats not available on this linux version\",\n    )\n    @pytest.mark.skipif(\n        CI_TESTING and not psutil.disk_io_counters(), reason=\"unreliable on CI\"\n    )  # no visible disks\n    def test_disk_io_counters(self):\n        def check_ntuple(nt):\n            assert nt[0] == nt.read_count\n            assert nt[1] == nt.write_count\n            assert nt[2] == nt.read_bytes\n            assert nt[3] == nt.write_bytes\n            if not (OPENBSD or NETBSD):\n                assert nt[4] == nt.read_time\n                assert nt[5] == nt.write_time\n                if LINUX:\n                    assert nt[6] == nt.read_merged_count\n                    assert nt[7] == nt.write_merged_count\n                    assert nt[8] == nt.busy_time\n                elif FREEBSD:\n                    assert nt[6] == nt.busy_time\n            for name in nt._fields:\n                assert getattr(nt, name) >= 0, nt\n\n        ret = psutil.disk_io_counters(perdisk=False)\n        assert ret is not None, \"no disks on this system?\"\n        check_ntuple(ret)\n        ret = psutil.disk_io_counters(perdisk=True)\n        # make sure there are no duplicates\n        assert len(ret) == len(set(ret))\n        for key in ret:\n            assert key, key\n            check_ntuple(ret[key])\n\n    def test_disk_io_counters_no_disks(self):\n        # Emulate a case where no disks are installed, see:\n        # https://github.com/giampaolo/psutil/issues/1062\n        with mock.patch(\n            'psutil._psplatform.disk_io_counters', return_value={}\n        ) as m:\n            assert psutil.disk_io_counters(perdisk=False) is None\n            assert psutil.disk_io_counters(perdisk=True) == {}\n            assert m.called\n\n\nclass TestNetAPIs(PsutilTestCase):\n    @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason=\"not supported\")\n    def test_net_io_counters(self):\n        def check_ntuple(nt):\n            assert nt[0] == nt.bytes_sent\n            assert nt[1] == nt.bytes_recv\n            assert nt[2] == nt.packets_sent\n            assert nt[3] == nt.packets_recv\n            assert nt[4] == nt.errin\n            assert nt[5] == nt.errout\n            assert nt[6] == nt.dropin\n            assert nt[7] == nt.dropout\n            assert nt.bytes_sent >= 0, nt\n            assert nt.bytes_recv >= 0, nt\n            assert nt.packets_sent >= 0, nt\n            assert nt.packets_recv >= 0, nt\n            assert nt.errin >= 0, nt\n            assert nt.errout >= 0, nt\n            assert nt.dropin >= 0, nt\n            assert nt.dropout >= 0, nt\n\n        ret = psutil.net_io_counters(pernic=False)\n        check_ntuple(ret)\n        ret = psutil.net_io_counters(pernic=True)\n        assert ret != []\n        for key in ret:\n            assert key\n            assert isinstance(key, str)\n            check_ntuple(ret[key])\n\n    @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason=\"not supported\")\n    def test_net_io_counters_no_nics(self):\n        # Emulate a case where no NICs are installed, see:\n        # https://github.com/giampaolo/psutil/issues/1062\n        with mock.patch(\n            'psutil._psplatform.net_io_counters', return_value={}\n        ) as m:\n            assert psutil.net_io_counters(pernic=False) is None\n            assert psutil.net_io_counters(pernic=True) == {}\n            assert m.called\n\n    def test_net_if_addrs(self):\n        nics = psutil.net_if_addrs()\n        assert nics, nics\n\n        nic_stats = psutil.net_if_stats()\n\n        # Not reliable on all platforms (net_if_addrs() reports more\n        # interfaces).\n        # assert sorted(nics.keys()) == sorted(\n        #     psutil.net_io_counters(pernic=True).keys()\n        # )\n\n        families = {socket.AF_INET, socket.AF_INET6, psutil.AF_LINK}\n        for nic, addrs in nics.items():\n            assert isinstance(nic, str)\n            assert len(set(addrs)) == len(addrs)\n            for addr in addrs:\n                assert isinstance(addr.family, int)\n                assert isinstance(addr.address, str)\n                assert isinstance(addr.netmask, (str, type(None)))\n                assert isinstance(addr.broadcast, (str, type(None)))\n                assert addr.family in families\n                assert isinstance(addr.family, enum.IntEnum)\n                if nic_stats[nic].isup:\n                    # Do not test binding to addresses of interfaces\n                    # that are down\n                    if addr.family == socket.AF_INET:\n                        with socket.socket(addr.family) as s:\n                            s.bind((addr.address, 0))\n                    elif addr.family == socket.AF_INET6:\n                        info = socket.getaddrinfo(\n                            addr.address,\n                            0,\n                            socket.AF_INET6,\n                            socket.SOCK_STREAM,\n                            0,\n                            socket.AI_PASSIVE,\n                        )[0]\n                        af, socktype, proto, _canonname, sa = info\n                        with socket.socket(af, socktype, proto) as s:\n                            s.bind(sa)\n                for ip in (\n                    addr.address,\n                    addr.netmask,\n                    addr.broadcast,\n                    addr.ptp,\n                ):\n                    if ip is not None:\n                        # TODO: skip AF_INET6 for now because I get:\n                        # AddressValueError: Only hex digits permitted in\n                        # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0'\n                        if addr.family != socket.AF_INET6:\n                            check_net_address(ip, addr.family)\n                # broadcast and ptp addresses are mutually exclusive\n                if addr.broadcast:\n                    assert addr.ptp is None\n                elif addr.ptp:\n                    assert addr.broadcast is None\n\n                # check broadcast address\n                if (\n                    addr.broadcast\n                    and addr.netmask\n                    and addr.family in {socket.AF_INET, socket.AF_INET6}\n                ):\n                    assert addr.broadcast == broadcast_addr(addr)\n\n        if BSD or MACOS or SUNOS:\n            if hasattr(socket, \"AF_LINK\"):\n                assert psutil.AF_LINK == socket.AF_LINK\n        elif LINUX:\n            assert psutil.AF_LINK == socket.AF_PACKET\n        elif WINDOWS:\n            assert psutil.AF_LINK == -1\n\n    def test_net_if_addrs_mac_null_bytes(self):\n        # Simulate that the underlying C function returns an incomplete\n        # MAC address. psutil is supposed to fill it with null bytes.\n        # https://github.com/giampaolo/psutil/issues/786\n        if POSIX:\n            ret = [('em1', psutil.AF_LINK, '06:3d:29', None, None, None)]\n        else:\n            ret = [('em1', -1, '06-3d-29', None, None, None)]\n        with mock.patch(\n            'psutil._psplatform.net_if_addrs', return_value=ret\n        ) as m:\n            addr = psutil.net_if_addrs()['em1'][0]\n            assert m.called\n            if POSIX:\n                assert addr.address == '06:3d:29:00:00:00'\n            else:\n                assert addr.address == '06-3d-29-00-00-00'\n\n    def test_net_if_stats(self):\n        nics = psutil.net_if_stats()\n        assert nics, nics\n        all_duplexes = (\n            psutil.NIC_DUPLEX_FULL,\n            psutil.NIC_DUPLEX_HALF,\n            psutil.NIC_DUPLEX_UNKNOWN,\n        )\n        for name, stats in nics.items():\n            assert isinstance(name, str)\n            isup, duplex, speed, mtu, flags = stats\n            assert isinstance(isup, bool)\n            assert duplex in all_duplexes\n            assert duplex in all_duplexes\n            assert speed >= 0\n            assert mtu >= 0\n            assert isinstance(flags, str)\n\n    @pytest.mark.skipif(\n        not (LINUX or BSD or MACOS), reason=\"LINUX or BSD or MACOS specific\"\n    )\n    def test_net_if_stats_enodev(self):\n        # See: https://github.com/giampaolo/psutil/issues/1279\n        with mock.patch(\n            'psutil._psplatform.cext.net_if_mtu',\n            side_effect=OSError(errno.ENODEV, \"\"),\n        ) as m:\n            ret = psutil.net_if_stats()\n            assert ret == {}\n            assert m.called\n\n\nclass TestSensorsAPIs(PsutilTestCase):\n    @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason=\"not supported\")\n    def test_sensors_temperatures(self):\n        temps = psutil.sensors_temperatures()\n        for name, entries in temps.items():\n            assert isinstance(name, str)\n            for entry in entries:\n                assert isinstance(entry.label, str)\n                if entry.current is not None:\n                    assert entry.current >= 0\n                if entry.high is not None:\n                    assert entry.high >= 0\n                if entry.critical is not None:\n                    assert entry.critical >= 0\n\n    @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason=\"not supported\")\n    def test_sensors_temperatures_fahreneit(self):\n        d = {'coretemp': [('label', 50.0, 60.0, 70.0)]}\n        with mock.patch(\n            \"psutil._psplatform.sensors_temperatures\", return_value=d\n        ) as m:\n            temps = psutil.sensors_temperatures(fahrenheit=True)['coretemp'][0]\n            assert m.called\n            assert temps.current == 122.0\n            assert temps.high == 140.0\n            assert temps.critical == 158.0\n\n    @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason=\"not supported\")\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_sensors_battery(self):\n        ret = psutil.sensors_battery()\n        assert ret.percent >= 0\n        assert ret.percent <= 100\n        if ret.secsleft not in {\n            psutil.POWER_TIME_UNKNOWN,\n            psutil.POWER_TIME_UNLIMITED,\n        }:\n            assert ret.secsleft >= 0\n        elif ret.secsleft == psutil.POWER_TIME_UNLIMITED:\n            assert ret.power_plugged\n        assert isinstance(ret.power_plugged, bool)\n\n    @pytest.mark.skipif(not HAS_SENSORS_FANS, reason=\"not supported\")\n    def test_sensors_fans(self):\n        fans = psutil.sensors_fans()\n        for name, entries in fans.items():\n            assert isinstance(name, str)\n            for entry in entries:\n                assert isinstance(entry.label, str)\n                assert isinstance(entry.current, int)\n                assert entry.current >= 0\n"
  },
  {
    "path": "tests/test_testutils.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Tests for testing utils.\"\"\"\n\nimport collections\nimport errno\nimport os\nimport socket\nimport stat\nimport subprocess\nfrom unittest import mock\n\nimport psutil\nimport tests\nfrom psutil import FREEBSD\nfrom psutil import NETBSD\nfrom psutil import POSIX\nfrom psutil._common import open_binary\nfrom psutil._common import open_text\nfrom psutil._common import supports_ipv6\n\nfrom . import HAS_NET_CONNECTIONS_UNIX\nfrom . import PYTHON_EXE\nfrom . import PYTHON_EXE_ENV\nfrom . import PsutilTestCase\nfrom . import bind_socket\nfrom . import bind_unix_socket\nfrom . import call_until\nfrom . import chdir\nfrom . import create_sockets\nfrom . import filter_proc_net_connections\nfrom . import get_free_port\nfrom . import is_namedtuple\nfrom . import process_namespace\nfrom . import pytest\nfrom . import reap_children\nfrom . import retry\nfrom . import safe_mkdir\nfrom . import safe_rmpath\nfrom . import system_namespace\nfrom . import tcp_socketpair\nfrom . import terminate\nfrom . import unix_socketpair\nfrom . import wait_for_file\nfrom . import wait_for_pid\n\n# ===================================================================\n# --- Unit tests for test utilities.\n# ===================================================================\n\n\nclass TestRetryDecorator(PsutilTestCase):\n    @mock.patch('time.sleep')\n    def test_retry_success(self, sleep):\n        # Fail 3 times out of 5; make sure the decorated fun returns.\n\n        @retry(retries=5, interval=1, logfun=None)\n        def foo():\n            while queue:\n                queue.pop()\n                1 / 0  # noqa: B018\n            return 1\n\n        queue = list(range(3))\n        assert foo() == 1\n        assert sleep.call_count == 3\n\n    @mock.patch('time.sleep')\n    def test_retry_failure(self, sleep):\n        # Fail 6 times out of 5; th function is supposed to raise exc.\n        @retry(retries=5, interval=1, logfun=None)\n        def foo():\n            while queue:\n                queue.pop()\n                1 / 0  # noqa: B018\n            return 1\n\n        queue = list(range(6))\n        with pytest.raises(ZeroDivisionError):\n            foo()\n        assert sleep.call_count == 5\n\n    @mock.patch('time.sleep')\n    def test_exception_arg(self, sleep):\n        @retry(exception=ValueError, interval=1)\n        def foo():\n            raise TypeError\n\n        with pytest.raises(TypeError):\n            foo()\n        assert sleep.call_count == 0\n\n    @mock.patch('time.sleep')\n    def test_no_interval_arg(self, sleep):\n        # if interval is not specified sleep is not supposed to be called\n\n        @retry(retries=5, interval=None, logfun=None)\n        def foo():\n            1 / 0  # noqa: B018\n\n        with pytest.raises(ZeroDivisionError):\n            foo()\n        assert sleep.call_count == 0\n\n    @mock.patch('time.sleep')\n    def test_retries_arg(self, sleep):\n        @retry(retries=5, interval=1, logfun=None)\n        def foo():\n            1 / 0  # noqa: B018\n\n        with pytest.raises(ZeroDivisionError):\n            foo()\n        assert sleep.call_count == 5\n\n    @mock.patch('time.sleep')\n    def test_retries_and_timeout_args(self, sleep):\n        with pytest.raises(ValueError):\n            retry(retries=5, timeout=1)\n\n\nclass TestSyncTestUtils(PsutilTestCase):\n    def test_wait_for_pid(self):\n        wait_for_pid(os.getpid())\n        nopid = max(psutil.pids()) + 99999\n        with mock.patch('tests.retry.__iter__', return_value=iter([0])):\n            with pytest.raises(psutil.NoSuchProcess):\n                wait_for_pid(nopid)\n\n    def test_wait_for_file(self):\n        testfn = self.get_testfn()\n        with open(testfn, 'w') as f:\n            f.write('foo')\n        wait_for_file(testfn)\n        assert not os.path.exists(testfn)\n\n    def test_wait_for_file_empty(self):\n        testfn = self.get_testfn()\n        with open(testfn, 'w'):\n            pass\n        wait_for_file(testfn, empty=True)\n        assert not os.path.exists(testfn)\n\n    def test_wait_for_file_no_file(self):\n        testfn = self.get_testfn()\n        with mock.patch('tests.retry.__iter__', return_value=iter([0])):\n            with pytest.raises(OSError):\n                wait_for_file(testfn)\n\n    def test_wait_for_file_no_delete(self):\n        testfn = self.get_testfn()\n        with open(testfn, 'w') as f:\n            f.write('foo')\n        wait_for_file(testfn, delete=False)\n        assert os.path.exists(testfn)\n\n    def test_call_until(self):\n        call_until(lambda: 1)\n        # TODO: test for timeout\n\n\nclass TestFSTestUtils(PsutilTestCase):\n    def test_open_text(self):\n        with open_text(__file__) as f:\n            assert f.mode == 'r'\n\n    def test_open_binary(self):\n        with open_binary(__file__) as f:\n            assert f.mode == 'rb'\n\n    def test_safe_mkdir(self):\n        testfn = self.get_testfn()\n        safe_mkdir(testfn)\n        assert os.path.isdir(testfn)\n        safe_mkdir(testfn)\n        assert os.path.isdir(testfn)\n\n    def test_safe_rmpath(self):\n        # test file is removed\n        testfn = self.get_testfn()\n        open(testfn, 'w').close()\n        safe_rmpath(testfn)\n        assert not os.path.exists(testfn)\n        # test no exception if path does not exist\n        safe_rmpath(testfn)\n        # test dir is removed\n        os.mkdir(testfn)\n        safe_rmpath(testfn)\n        assert not os.path.exists(testfn)\n        # test other exceptions are raised\n        with mock.patch(\n            'tests.os.stat', side_effect=OSError(errno.EINVAL, \"\")\n        ) as m:\n            with pytest.raises(OSError):\n                safe_rmpath(testfn)\n            assert m.called\n\n    def test_chdir(self):\n        testfn = self.get_testfn()\n        base = os.getcwd()\n        os.mkdir(testfn)\n        with chdir(testfn):\n            assert os.getcwd() == os.path.join(base, testfn)\n        assert os.getcwd() == base\n\n\nclass TestProcessUtils(PsutilTestCase):\n    def test_reap_children(self):\n        subp = self.spawn_subproc()\n        p = psutil.Process(subp.pid)\n        assert p.is_running()\n        reap_children()\n        assert not p.is_running()\n        assert not tests._pids_started\n        assert not tests._subprocesses_started\n\n    def test_spawn_children_pair(self):\n        child, grandchild = self.spawn_children_pair()\n        assert child.pid != grandchild.pid\n        assert child.is_running()\n        assert grandchild.is_running()\n        children = psutil.Process().children()\n        assert children == [child]\n        children = psutil.Process().children(recursive=True)\n        assert len(children) == 2\n        assert child in children\n        assert grandchild in children\n        assert child.ppid() == os.getpid()\n        assert grandchild.ppid() == child.pid\n\n        terminate(child)\n        assert not child.is_running()\n        assert grandchild.is_running()\n\n        terminate(grandchild)\n        assert not grandchild.is_running()\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_spawn_zombie(self):\n        _parent, zombie = self.spawn_zombie()\n        assert zombie.status() == psutil.STATUS_ZOMBIE\n\n    def test_terminate(self):\n        # by subprocess.Popen\n        p = self.spawn_subproc()\n        terminate(p)\n        self.assert_pid_gone(p.pid)\n        terminate(p)\n        # by psutil.Process\n        p = psutil.Process(self.spawn_subproc().pid)\n        terminate(p)\n        self.assert_pid_gone(p.pid)\n        terminate(p)\n        # by psutil.Popen\n        cmd = [\n            PYTHON_EXE,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)];\",\n        ]\n        p = psutil.Popen(\n            cmd,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            env=PYTHON_EXE_ENV,\n        )\n        terminate(p)\n        self.assert_pid_gone(p.pid)\n        terminate(p)\n        # by PID\n        pid = self.spawn_subproc().pid\n        terminate(pid)\n        self.assert_pid_gone(p.pid)\n        terminate(pid)\n        # zombie\n        if POSIX:\n            parent, zombie = self.spawn_zombie()\n            terminate(parent)\n            terminate(zombie)\n            self.assert_pid_gone(parent.pid)\n            self.assert_pid_gone(zombie.pid)\n\n\nclass TestNetUtils(PsutilTestCase):\n    def bind_socket(self):\n        port = get_free_port()\n        with bind_socket(addr=('', port)) as s:\n            assert s.getsockname()[1] == port\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    def test_bind_unix_socket(self):\n        name = self.get_testfn()\n        with bind_unix_socket(name) as sock:\n            assert sock.family == socket.AF_UNIX\n            assert sock.type == socket.SOCK_STREAM\n            assert sock.getsockname() == name\n            assert os.path.exists(name)\n            assert stat.S_ISSOCK(os.stat(name).st_mode)\n        # UDP\n        name = self.get_testfn()\n        with bind_unix_socket(name, type=socket.SOCK_DGRAM) as sock:\n            assert sock.type == socket.SOCK_DGRAM\n\n    def test_tcp_socketpair(self):\n        addr = (\"127.0.0.1\", get_free_port())\n        server, client = tcp_socketpair(socket.AF_INET, addr=addr)\n        with server, client:\n            # Ensure they are connected and the positions are correct.\n            assert server.getsockname() == addr\n            assert client.getpeername() == addr\n            assert client.getsockname() != addr\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    @pytest.mark.skipif(\n        NETBSD or FREEBSD, reason=\"/var/run/log UNIX socket opened by default\"\n    )\n    @pytest.mark.skipif(\n        not HAS_NET_CONNECTIONS_UNIX, reason=\"can't list UNIX sockets\"\n    )\n    def test_unix_socketpair(self):\n        p = psutil.Process()\n        num_fds = p.num_fds()\n        assert not filter_proc_net_connections(p.net_connections(kind='unix'))\n        name = self.get_testfn()\n        server, client = unix_socketpair(name)\n        try:\n            assert os.path.exists(name)\n            assert stat.S_ISSOCK(os.stat(name).st_mode)\n            assert p.num_fds() - num_fds == 2\n            assert (\n                len(\n                    filter_proc_net_connections(p.net_connections(kind='unix'))\n                )\n                == 2\n            )\n            assert server.getsockname() == name\n            assert client.getpeername() == name\n        finally:\n            client.close()\n            server.close()\n\n    def test_create_sockets(self):\n        with create_sockets() as socks:\n            fams = collections.defaultdict(int)\n            types = collections.defaultdict(int)\n            for s in socks:\n                fams[s.family] += 1\n                # work around http://bugs.python.org/issue30204\n                types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1\n            assert fams[socket.AF_INET] >= 2\n            if supports_ipv6():\n                assert fams[socket.AF_INET6] >= 2\n            if POSIX and HAS_NET_CONNECTIONS_UNIX:\n                assert fams[socket.AF_UNIX] >= 2\n            assert types[socket.SOCK_STREAM] >= 2\n            assert types[socket.SOCK_DGRAM] >= 2\n\n\nclass TestTestingUtils(PsutilTestCase):\n    def test_process_namespace(self):\n        p = psutil.Process()\n        ns = process_namespace(p)\n        ns.test()\n        fun = next(x for x in ns.iter(ns.getters) if x[1] == 'ppid')[0]\n        assert fun() == p.ppid()\n\n    def test_system_namespace(self):\n        ns = system_namespace()\n        fun = next(x for x in ns.iter(ns.getters) if x[1] == 'net_if_addrs')[0]\n        assert fun() == psutil.net_if_addrs()\n\n\nclass TestOtherUtils(PsutilTestCase):\n    def test_is_namedtuple(self):\n        assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3))\n        assert not is_namedtuple(tuple())\n"
  },
  {
    "path": "tests/test_type_hints.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\nfrom __future__ import annotations\n\nimport functools\nimport sys\nimport types\n\nimport pytest\n\nimport psutil\n\nfrom . import PsutilTestCase\nfrom . import check_fun_type_hints\nfrom . import check_ntuple_type_hints\nfrom . import is_namedtuple\nfrom . import process_namespace\nfrom . import system_namespace\n\n\n@pytest.mark.skipif(\n    sys.version_info[:2] <= (3, 7), reason=\"not supported on Python <= 3.7\"\n)\nclass TypeHintTestCase(PsutilTestCase):\n    pass\n\n\n# ===================================================================\n# --- named tuples type hints\n# ===================================================================\n\n\nclass TestTypeHintsNtuples(TypeHintTestCase):\n    \"\"\"Check that named tuple field values match the type annotations\n    defined in psutil/_ntuples.py.\n    \"\"\"\n\n    def check_result(self, ret):\n        if is_namedtuple(ret):\n            check_ntuple_type_hints(ret)\n        elif isinstance(ret, list):\n            for item in ret:\n                if is_namedtuple(item):\n                    check_ntuple_type_hints(item)\n\n    def test_system_ntuple_types(self):\n        for fun, name in system_namespace.iter(system_namespace.getters):\n            try:\n                ret = fun()\n            except psutil.Error:\n                continue\n            with self.subTest(name=name, fun=str(fun)):\n                if isinstance(ret, dict):\n                    for v in ret.values():\n                        if isinstance(v, list):\n                            for item in v:\n                                self.check_result(item)\n                        else:\n                            self.check_result(v)\n                else:\n                    self.check_result(ret)\n\n    def test_process_ntuple_types(self):\n        p = psutil.Process()\n        ns = process_namespace(p)\n        for fun, name in ns.iter(ns.getters):\n            with self.subTest(name=name, fun=str(fun)):\n                try:\n                    ret = fun()\n                except psutil.Error:\n                    continue\n                self.check_result(ret)\n\n\n# ===================================================================\n# --- returned type hints\n# ===================================================================\n\n\nclass TestTypeHintsReturned(TypeHintTestCase):\n    \"\"\"Check that annotated return types in psutil/__init__.py match\n    the actual values returned at runtime.\n    \"\"\"\n\n    def check(self, fun, name):\n        try:\n            ret = fun()\n        except psutil.Error:\n            return\n        check_fun_type_hints(fun, ret)\n\n    def test_system_return_types(self):\n        for fun, name in system_namespace.iter(system_namespace.getters):\n            with self.subTest(name=name, fun=str(fun)):\n                self.check(fun, name)\n\n    def test_process_return_types(self):\n        p = psutil.Process()\n        ns = process_namespace(p)\n        for fun, name in ns.iter(ns.getters):\n            with self.subTest(name=name, fun=str(fun)):\n                self.check(fun, name)\n\n\n# =====================================================================\n# --- Tests for check_ntuple_type_hints() test utility fun\n# =====================================================================\n\n\nclass TestCheckNtupleTypeHints(TypeHintTestCase):\n    def test_not_namedtuple(self):\n        # plain tuple is rejected\n        with pytest.raises(AssertionError):\n            check_ntuple_type_hints((1, 2, 3))\n\n    def test_ok(self):\n        # addr(ip: str, port: int) - correct types\n        from psutil._ntuples import addr\n\n        check_ntuple_type_hints(addr('127.0.0.1', 80))\n\n    def test_wrong_type(self):\n        # ip should be str, passing int instead\n        from psutil._ntuples import addr\n\n        with pytest.raises(AssertionError):\n            check_ntuple_type_hints(addr(127, 80))\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_union_with_none(self):\n        # suser has terminal: str | None and host: str | None\n        from psutil._ntuples import suser\n\n        check_ntuple_type_hints(suser('user', None, None, 1.0, None))\n        check_ntuple_type_hints(suser('user', '/dev/tty1', 'host', 1.0, 1))\n        with pytest.raises(AssertionError):\n            check_ntuple_type_hints(suser(1, None, None, 1.0, None))\n\n    def test_intenum_broadening(self):\n        # NicDuplex is an IntEnum; check_ntuple_type_hints broadens it to int\n        from psutil._ntuples import snicstats\n\n        nt = snicstats(True, psutil.NIC_DUPLEX_FULL, 1000, 1500, '')\n        check_ntuple_type_hints(nt)\n\n\n# =====================================================================\n# --- Tests for check_fun_type_hints() test utility fun\n# =====================================================================\n\n\nclass TestCheckFunTypeHints(TypeHintTestCase):\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_no_annotation(self):\n        def foo():\n            return 1\n\n        with pytest.raises(ValueError, match=\"no type hints defined\"):\n            check_fun_type_hints(foo, 1)\n\n    def test_float(self):\n        def foo() -> float:\n            return 1.0\n\n        check_fun_type_hints(foo, 1.0)\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    def test_bool(self):\n        def foo() -> bool:\n            return True\n\n        check_fun_type_hints(foo, True)\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_list(self):\n        def foo() -> list[int]:\n            return [1]\n\n        check_fun_type_hints(foo, foo())\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_list_container(self):\n        def foo() -> list[str]:\n            pass\n\n        check_fun_type_hints(foo, [\"a\", \"b\"])\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, [\"a\", 1])\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_dict(self):\n        def foo() -> dict[str, int]:\n            return {'a': 1}\n\n        check_fun_type_hints(foo, foo())\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_dict_container(self):\n        def foo() -> dict[str, str]:\n            pass\n\n        check_fun_type_hints(foo, {\"a\": \"a\", \"b\": \"b\"})\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, {\"a\": \"a\", \"1\": 1})\n\n    def test_namedtuple(self):\n        from psutil._ntuples import addr\n\n        def foo() -> addr:\n            return addr('127.0.0.1', 80)\n\n        check_fun_type_hints(foo, addr('127.0.0.1', 80))\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_union_with_none(self):\n        def foo() -> int | None:\n            return 1\n\n        check_fun_type_hints(foo, 1)\n        check_fun_type_hints(foo, None)\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    @pytest.mark.skipif(\n        not hasattr(types, \"UnionType\"), reason=\"Python 3.10+ only\"\n    )\n    def test_union_or_dict_or_none(self):\n        def foo() -> int | dict[str, int] | None:\n            return 1\n\n        check_fun_type_hints(foo, 1)\n        check_fun_type_hints(foo, {'a': 1})\n        check_fun_type_hints(foo, None)\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    def test_generator(self):\n        from typing import Generator\n\n        def foo() -> Generator[int, None, None]:\n            yield 1\n\n        check_fun_type_hints(foo, foo())\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(foo, \"str\")\n\n    def test_partial(self):\n        def foo(x) -> int:\n            return x\n\n        fun = functools.partial(foo, 1)\n        check_fun_type_hints(fun, 1)\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(fun, \"str\")\n\n    def test_bound_method(self):\n        class MyClass:\n            def foo(self) -> int:\n                return 1\n\n        obj = MyClass()\n        check_fun_type_hints(obj.foo, 1)\n        with pytest.raises(AssertionError):\n            check_fun_type_hints(obj.foo, \"str\")\n"
  },
  {
    "path": "tests/test_unicode.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Notes about unicode handling in psutil\n======================================.\n\nStarting from version 5.3.0 psutil adds unicode support, see:\nhttps://github.com/giampaolo/psutil/issues/1040\nThe notes below apply to *any* API returning a string such as\nprocess exe(), cwd() or username():\n\n* all strings are encoded by using the OS filesystem encoding\n  (sys.getfilesystemencoding()) which varies depending on the platform\n  (e.g. \"UTF-8\" on macOS, \"mbcs\" on Win)\n* no API call is supposed to crash with UnicodeDecodeError\n* instead, in case of badly encoded data returned by the OS, the\n  following error handlers are used to replace the corrupted characters in\n  the string:\n    * sys.getfilesystemencodeerrors() or \"surrogatescape\" on POSIX and\n      \"replace\" on Windows.\n\nFor a detailed explanation of how psutil handles unicode see #1040.\n\nTests\n=====\n\nList of APIs returning or dealing with a string:\n('not tested' means they are not tested to deal with non-ASCII strings):\n\n* Process.cmdline()\n* Process.cwd()\n* Process.environ()\n* Process.exe()\n* Process.memory_maps()\n* Process.name()\n* Process.net_connections('unix')\n* Process.open_files()\n* Process.username()             (not tested)\n\n* disk_io_counters()             (not tested)\n* disk_partitions()              (not tested)\n* disk_usage(str)\n* net_connections('unix')\n* net_if_addrs()                 (not tested)\n* net_if_stats()                 (not tested)\n* net_io_counters()              (not tested)\n* sensors_fans()                 (not tested)\n* sensors_temperatures()         (not tested)\n* users()                        (not tested)\n\n* WindowsService.binpath()       (not tested)\n* WindowsService.description()   (not tested)\n* WindowsService.display_name()  (not tested)\n* WindowsService.name()          (not tested)\n* WindowsService.status()        (not tested)\n* WindowsService.username()      (not tested)\n\nIn here we create a unicode path with a funky non-ASCII name and (where\npossible) make psutil return it back (e.g. on name(), exe(), open_files(),\netc.) and make sure that:\n\n* psutil never crashes with UnicodeDecodeError\n* the returned path matches\n\"\"\"\n\nimport os\nimport shutil\nimport warnings\nfrom contextlib import closing\n\nimport psutil\nfrom psutil import BSD\nfrom psutil import MACOS\nfrom psutil import NETBSD\nfrom psutil import OPENBSD\nfrom psutil import POSIX\nfrom psutil import WINDOWS\n\nfrom . import ASCII_FS\nfrom . import CI_TESTING\nfrom . import HAS_NET_CONNECTIONS_UNIX\nfrom . import HAS_PROC_ENVIRON\nfrom . import HAS_PROC_MEMORY_MAPS\nfrom . import INVALID_UNICODE_SUFFIX\nfrom . import PYPY\nfrom . import TESTFN_PREFIX\nfrom . import UNICODE_SUFFIX\nfrom . import PsutilTestCase\nfrom . import bind_unix_socket\nfrom . import chdir\nfrom . import copyload_shared_lib\nfrom . import create_py_exe\nfrom . import get_testfn\nfrom . import pytest\nfrom . import safe_mkdir\nfrom . import safe_rmpath\nfrom . import skip_on_access_denied\nfrom . import spawn_subproc\nfrom . import terminate\n\n\ndef try_unicode(suffix):\n    \"\"\"Return True if both the fs and the subprocess module can\n    deal with a unicode file name.\n    \"\"\"\n    sproc = None\n    testfn = get_testfn(suffix=suffix)\n    try:\n        safe_rmpath(testfn)\n        create_py_exe(testfn)\n        sproc = spawn_subproc(cmd=[testfn])\n        shutil.copyfile(testfn, testfn + '-2')\n        safe_rmpath(testfn + '-2')\n    except (UnicodeEncodeError, OSError):\n        return False\n    else:\n        return True\n    finally:\n        if sproc is not None:\n            terminate(sproc)\n        safe_rmpath(testfn)\n\n\n# ===================================================================\n# FS APIs\n# ===================================================================\n\n\nclass BaseUnicodeTest(PsutilTestCase):\n    funky_suffix = None\n\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        cls.skip_tests = False\n        cls.funky_name = None\n        if cls.funky_suffix is not None:\n            if not try_unicode(cls.funky_suffix):\n                cls.skip_tests = True\n            else:\n                cls.funky_name = get_testfn(suffix=cls.funky_suffix)\n                create_py_exe(cls.funky_name)\n\n    def setUp(self):\n        super().setUp()\n        if self.skip_tests:\n            return pytest.skip(\"can't handle unicode str\")\n\n\n@pytest.mark.xdist_group(name=\"serial\")\n@pytest.mark.skipif(ASCII_FS, reason=\"ASCII fs\")\nclass TestFSAPIs(BaseUnicodeTest):\n    \"\"\"Test FS APIs with a funky, valid, UTF8 path name.\"\"\"\n\n    funky_suffix = UNICODE_SUFFIX\n\n    def expect_exact_path_match(self):\n        with warnings.catch_warnings():\n            warnings.simplefilter(\"ignore\")\n            return self.funky_name in os.listdir(\".\")\n\n    # ---\n\n    def test_proc_exe(self):\n        cmd = [\n            self.funky_name,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)]\",\n        ]\n        subp = self.spawn_subproc(cmd)\n        p = psutil.Process(subp.pid)\n        exe = p.exe()\n        assert isinstance(exe, str)\n        if self.expect_exact_path_match():\n            assert os.path.normcase(exe) == os.path.normcase(self.funky_name)\n\n    def test_proc_name(self):\n        cmd = [\n            self.funky_name,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)]\",\n        ]\n        subp = self.spawn_subproc(cmd)\n        name = psutil.Process(subp.pid).name()\n        assert isinstance(name, str)\n        if self.expect_exact_path_match():\n            assert name == os.path.basename(self.funky_name)\n\n    def test_proc_cmdline(self):\n        cmd = [\n            self.funky_name,\n            \"-c\",\n            \"import time; [time.sleep(0.1) for x in range(100)]\",\n        ]\n        subp = self.spawn_subproc(cmd)\n        p = psutil.Process(subp.pid)\n        cmdline = p.cmdline()\n        for part in cmdline:\n            assert isinstance(part, str)\n        if self.expect_exact_path_match():\n            assert cmdline == cmd\n\n    def test_proc_cwd(self):\n        dname = self.funky_name + \"2\"\n        self.addCleanup(safe_rmpath, dname)\n        safe_mkdir(dname)\n        with chdir(dname):\n            p = psutil.Process()\n            cwd = p.cwd()\n        assert isinstance(p.cwd(), str)\n        if self.expect_exact_path_match():\n            assert cwd == dname\n\n    @pytest.mark.skipif(PYPY and WINDOWS, reason=\"fails on PYPY + WINDOWS\")\n    @pytest.mark.skipif(\n        NETBSD or OPENBSD, reason=\"broken on NETBSD or OPENBSD\"\n    )\n    def test_proc_open_files(self):\n        p = psutil.Process()\n        start = set(p.open_files())\n        with open(self.funky_name, 'rb'):\n            new = set(p.open_files())\n        path = (new - start).pop().path\n        assert isinstance(path, str)\n        if BSD and not path:\n            # XXX - see https://github.com/giampaolo/psutil/issues/595\n            return pytest.skip(\"open_files on BSD is broken\")\n        if self.expect_exact_path_match():\n            assert os.path.normcase(path) == os.path.normcase(self.funky_name)\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    @pytest.mark.skipif(\n        not HAS_NET_CONNECTIONS_UNIX, reason=\"can't list UNIX sockets\"\n    )\n    def test_proc_net_connections(self):\n        name = self.get_testfn(suffix=self.funky_suffix)\n        sock = bind_unix_socket(name)\n        with closing(sock):\n            conn = psutil.Process().net_connections('unix')[0]\n            assert isinstance(conn.laddr, str)\n            if not conn.laddr and MACOS and CI_TESTING:\n                return pytest.skip(\"unreliable on OSX\")\n            assert conn.laddr == name\n\n    @pytest.mark.skipif(not POSIX, reason=\"POSIX only\")\n    @pytest.mark.skipif(\n        not HAS_NET_CONNECTIONS_UNIX, reason=\"can't list UNIX sockets\"\n    )\n    @skip_on_access_denied()\n    def test_net_connections(self):\n        def find_sock(cons):\n            for conn in cons:\n                if os.path.basename(conn.laddr).startswith(TESTFN_PREFIX):\n                    return conn\n            raise ValueError(\"connection not found\")\n\n        name = self.get_testfn(suffix=self.funky_suffix)\n        sock = bind_unix_socket(name)\n        with closing(sock):\n            cons = psutil.net_connections(kind='unix')\n            conn = find_sock(cons)\n            assert isinstance(conn.laddr, str)\n            assert conn.laddr == name\n\n    def test_disk_usage(self):\n        dname = self.funky_name + \"2\"\n        self.addCleanup(safe_rmpath, dname)\n        safe_mkdir(dname)\n        psutil.disk_usage(dname)\n\n    @pytest.mark.skipif(not HAS_PROC_MEMORY_MAPS, reason=\"not supported\")\n    def test_memory_maps(self):\n        with copyload_shared_lib(suffix=self.funky_suffix) as funky_path:\n\n            def normpath(p):\n                return os.path.realpath(os.path.normcase(p))\n\n            libpaths = [\n                normpath(x.path) for x in psutil.Process().memory_maps()\n            ]\n            # ...just to have a clearer msg in case of failure\n            libpaths = [x for x in libpaths if TESTFN_PREFIX in x]\n            assert normpath(funky_path) in libpaths\n            for path in libpaths:\n                assert isinstance(path, str)\n\n\n@pytest.mark.skipif(CI_TESTING, reason=\"unreliable on CI\")\nclass TestFSAPIsWithInvalidPath(TestFSAPIs):\n    \"\"\"Test FS APIs with a funky, invalid path name.\"\"\"\n\n    funky_suffix = INVALID_UNICODE_SUFFIX\n\n    def expect_exact_path_match(self):\n        return not MACOS\n\n\n# ===================================================================\n# Non fs APIs\n# ===================================================================\n\n\nclass TestNonFSAPIS(BaseUnicodeTest):\n    \"\"\"Unicode tests for non fs-related APIs.\"\"\"\n\n    funky_suffix = UNICODE_SUFFIX\n\n    @pytest.mark.skipif(not HAS_PROC_ENVIRON, reason=\"not supported\")\n    @pytest.mark.skipif(PYPY and WINDOWS, reason=\"segfaults on PYPY + WINDOWS\")\n    def test_proc_environ(self):\n        # Note: differently from others, this test does not deal\n        # with fs paths.\n        env = os.environ.copy()\n        env['FUNNY_ARG'] = self.funky_suffix\n        sproc = self.spawn_subproc(env=env)\n        p = psutil.Process(sproc.pid)\n        env = p.environ()\n        for k, v in env.items():\n            assert isinstance(k, str)\n            assert isinstance(v, str)\n        assert env['FUNNY_ARG'] == self.funky_suffix\n"
  },
  {
    "path": "tests/test_windows.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"Windows specific tests.\"\"\"\n\nimport ctypes\nimport datetime\nimport glob\nimport os\nimport platform\nimport re\nimport signal\nimport socket\nimport subprocess\nimport sys\nimport time\nimport warnings\nfrom unittest import mock\n\nimport psutil\nfrom psutil import WINDOWS\n\nfrom . import GITHUB_ACTIONS\nfrom . import HAS_BATTERY\nfrom . import IS_64BIT\nfrom . import PYPY\nfrom . import TOLERANCE_DISK_USAGE\nfrom . import TOLERANCE_SYS_MEM\nfrom . import PsutilTestCase\nfrom . import pytest\nfrom . import retry_on_failure\nfrom . import sh\nfrom . import spawn_subproc\nfrom . import terminate\n\nif WINDOWS and not PYPY:\n    import ctypes\n    import ctypes.wintypes\n\n    with warnings.catch_warnings():\n        warnings.simplefilter(\"ignore\")\n        import win32api  # requires \"pip install pywin32\"\n        import win32con\n        import win32process\n        import wmi  # requires \"pip install wmi\" / \"make install-pydeps-test\"\n\nif WINDOWS:\n    from psutil._pswindows import convert_oserror\n\n\ncext = psutil._psplatform.cext\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\n@pytest.mark.skipif(PYPY, reason=\"pywin32 not available on PYPY\")\nclass WindowsTestCase(PsutilTestCase):\n\n    def OpenProcess(self, pid=None):\n        handle = win32api.OpenProcess(\n            win32con.PROCESS_QUERY_INFORMATION,\n            win32con.FALSE,\n            pid or os.getpid(),\n        )\n        self.addCleanup(win32api.CloseHandle, handle)\n        return handle\n\n\n# Note used but could be useful in the future\ndef is_bash_env():\n    env = os.environ\n    if \"MSYSTEM\" in env or \"MINGW_PREFIX\" in env or \"MINGW_CHOST\" in env:\n        return True\n    return \"bash\" in env.get(\"SHELL\", \"\")\n\n\ndef powershell(cmd):\n    \"\"\"Run a powershell command and return its output.\n    Example usage:\n    >>> powershell(\n        \"Get-CIMInstance Win32_PageFileUsage | Select AllocatedBaseSize\"\n    )\n    \"\"\"\n    exe = \"C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\"\n    cmdline = [\n        exe,\n        \"-ExecutionPolicy\",\n        \"Bypass\",\n        \"-NoLogo\",\n        \"-NonInteractive\",\n        \"-NoProfile\",\n        \"-WindowStyle\",\n        \"Hidden\",\n        \"-Command\",\n        cmd,\n    ]\n    return sh(cmdline)\n\n\ndef wmic(path, what, converter=int):\n    \"\"\"Currently not used, but available just in case. Usage:\n\n    >>> wmic(\"Win32_OperatingSystem\", \"FreePhysicalMemory\")\n    2134124534\n    \"\"\"\n    out = sh(f\"wmic path {path} get {what}\").strip()\n    data = \"\".join(out.splitlines()[1:]).strip()  # get rid of the header\n    if converter is not None:\n        if \",\" in what:\n            return tuple(converter(x) for x in data.split())\n        else:\n            return converter(data)\n    else:\n        return data\n\n\n# ===================================================================\n# System APIs\n# ===================================================================\n\n\nclass TestCpuAPIs(WindowsTestCase):\n    @pytest.mark.skipif(\n        'NUMBER_OF_PROCESSORS' not in os.environ,\n        reason=\"NUMBER_OF_PROCESSORS env var is not available\",\n    )\n    def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self):\n        # Will likely fail on many-cores systems:\n        # https://stackoverflow.com/questions/31209256\n        num_cpus = int(os.environ['NUMBER_OF_PROCESSORS'])\n        assert num_cpus == psutil.cpu_count()\n\n    def test_cpu_count_vs_GetSystemInfo(self):\n        # Will likely fail on many-cores systems:\n        # https://stackoverflow.com/questions/31209256\n        assert psutil.cpu_count() == win32api.GetSystemInfo()[5]\n\n    def test_cpu_count_logical_vs_wmi(self):\n        w = wmi.WMI()\n        procs = sum(\n            proc.NumberOfLogicalProcessors for proc in w.Win32_Processor()\n        )\n        assert psutil.cpu_count() == procs\n\n    def test_cpu_count_cores_vs_wmi(self):\n        w = wmi.WMI()\n        cores = sum(proc.NumberOfCores for proc in w.Win32_Processor())\n        assert psutil.cpu_count(logical=False) == cores\n\n    def test_cpu_count_vs_cpu_times(self):\n        assert psutil.cpu_count() == len(psutil.cpu_times(percpu=True))\n\n    def test_cpu_times_irq_field(self):\n        t = psutil.cpu_times()\n        assert t.irq >= 0\n        with pytest.warns(DeprecationWarning, match=\"interrupt\"):\n            assert t.interrupt == t.irq\n\n    def test_cpu_freq(self):\n        w = wmi.WMI()\n        proc = w.Win32_Processor()[0]\n        assert abs(proc.CurrentClockSpeed - psutil.cpu_freq().current) < 100\n        assert proc.MaxClockSpeed == psutil.cpu_freq().max\n\n\nclass TestVirtualMemory(WindowsTestCase):\n\n    def test_total(self):\n        w = wmi.WMI().Win32_ComputerSystem()[0]\n        assert int(w.TotalPhysicalMemory) == psutil.virtual_memory().total\n\n    def test_free(self):\n        w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0]\n        assert (\n            abs(int(w.AvailableBytes) - psutil.virtual_memory().free)\n            < TOLERANCE_SYS_MEM\n        )\n\n    @retry_on_failure()\n    def test_wired(self):\n        w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0]\n        assert (\n            abs(int(w.PoolNonpagedBytes) - psutil.virtual_memory().wired)\n            < TOLERANCE_SYS_MEM\n        )\n\n\nclass TestSystemAPIs(WindowsTestCase):\n    def test_nic_names(self):\n        out = sh('ipconfig /all')\n        nics = psutil.net_io_counters(pernic=True).keys()\n        for nic in nics:\n            if \"pseudo-interface\" in nic.replace(' ', '-').lower():\n                continue\n            if nic not in out:\n                return pytest.fail(\n                    f\"{nic!r} nic wasn't found in 'ipconfig /all' output\"\n                )\n\n    def test_net_if_addrs(self):\n        ps_addrs = set()\n        for addrs in psutil.net_if_addrs().values():\n            for addr in addrs:\n                if addr.family == socket.AF_INET:\n                    ps_addrs.add(addr.address)\n        out = powershell(\n            \"(Get-NetIPAddress -AddressFamily IPv4).IPAddress -join ','\"\n        )\n        win_addrs = set(out.strip().split(','))\n        assert win_addrs == ps_addrs\n\n    def test_net_connections(self):\n        # Compare listening TCP ports; they're stable unlike active\n        # connections.\n        ps_ports = {\n            c.laddr.port\n            for c in psutil.net_connections(kind='tcp')\n            if c.status == psutil.CONN_LISTEN\n        }\n        out = powershell(\n            \"(Get-NetTCPConnection -State Listen).LocalPort -join ','\"\n        )\n        win_ports = {int(p) for p in out.strip().split(',') if p.strip()}\n        assert ps_ports == win_ports\n\n    def test_total_swapmem(self):\n        w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0]\n        assert (\n            int(w.CommitLimit) - psutil.virtual_memory().total\n            == psutil.swap_memory().total\n        )\n        if psutil.swap_memory().total == 0:\n            assert psutil.swap_memory().free == 0\n            assert psutil.swap_memory().used == 0\n\n    def test_percent_swapmem(self):\n        if psutil.swap_memory().total > 0:\n            w = wmi.WMI().Win32_PerfRawData_PerfOS_PagingFile(Name=\"_Total\")[0]\n            # calculate swap usage to percent\n            percentSwap = int(w.PercentUsage) * 100 / int(w.PercentUsage_Base)\n            # exact percent may change but should be reasonable\n            # assert within +/- 5% and between 0 and 100%\n            assert psutil.swap_memory().percent >= 0\n            assert abs(psutil.swap_memory().percent - percentSwap) < 5\n            assert psutil.swap_memory().percent <= 100\n\n    # @pytest.mark.skipif(wmi is None, reason=\"wmi module is not installed\")\n    # def test__UPTIME(self):\n    #     # _UPTIME constant is not public but it is used internally\n    #     # as value to return for pid 0 creation time.\n    #     # WMI behaves the same.\n    #     w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n    #     p = psutil.Process(0)\n    #     wmic_create = str(w.CreationDate.split('.')[0])\n    #     psutil_create = time.strftime(\"%Y%m%d%H%M%S\",\n    #                                   time.localtime(p.create_time()))\n\n    # Note: this test is not very reliable\n    @retry_on_failure()\n    def test_pids(self):\n        # Note: this test might fail if the OS is starting/killing\n        # other processes in the meantime\n        w = wmi.WMI().Win32_Process()\n        wmi_pids = {x.ProcessId for x in w}\n        psutil_pids = set(psutil.pids())\n        assert wmi_pids == psutil_pids\n\n    @retry_on_failure()\n    def test_disks(self):\n        ps_parts = psutil.disk_partitions(all=True)\n        wmi_parts = wmi.WMI().Win32_LogicalDisk()\n        for ps_part in ps_parts:\n            for wmi_part in wmi_parts:\n                if ps_part.device.replace('\\\\', '') == wmi_part.DeviceID:\n                    if not ps_part.mountpoint:\n                        # this is usually a CD-ROM with no disk inserted\n                        break\n                    if 'cdrom' in ps_part.opts:\n                        break\n                    if ps_part.mountpoint.startswith('A:'):\n                        break  # floppy\n                    try:\n                        usage = psutil.disk_usage(ps_part.mountpoint)\n                    except FileNotFoundError:\n                        # usually this is the floppy\n                        break\n                    assert usage.total == int(wmi_part.Size)\n                    wmi_free = int(wmi_part.FreeSpace)\n                    assert usage.free == wmi_free\n                    # 10 MB tolerance\n                    if abs(usage.free - wmi_free) > 10 * 1024 * 1024:\n                        return pytest.fail(\n                            f\"psutil={usage.free}, wmi={wmi_free}\"\n                        )\n                    break\n            else:\n                return pytest.fail(f\"can't find partition {ps_part!r}\")\n\n    @retry_on_failure()\n    def test_disk_usage(self):\n        for disk in psutil.disk_partitions():\n            if 'cdrom' in disk.opts:\n                continue\n            win = win32api.GetDiskFreeSpaceEx(disk.mountpoint)\n            ps = psutil.disk_usage(disk.mountpoint)\n            assert abs(win[0] - ps.free) < TOLERANCE_DISK_USAGE\n            assert abs(win[1] - ps.total) < TOLERANCE_DISK_USAGE\n            assert ps.used == ps.total - ps.free\n\n    def test_disk_partitions(self):\n        win = [\n            x + '\\\\'\n            for x in win32api.GetLogicalDriveStrings().split(\"\\\\\\x00\")\n            if x and not x.startswith('A:')\n        ]\n        ps = [\n            x.mountpoint\n            for x in psutil.disk_partitions(all=True)\n            if not x.mountpoint.startswith('A:')\n        ]\n        assert win == ps\n\n    def test_convert_dos_path_drive(self):\n        winpath = 'C:\\\\Windows\\\\Temp'\n        driveletter = 'C:'\n        # Mocked NT device path for C:\n        devicepath = '\\\\Device\\\\HarddiskVolume1'\n\n        # Path returned by RtlDosPathNameToNtPathName\n        ntpath1 = '\\\\??\\\\C:\\\\Windows\\\\Temp'\n        # Mocked normalized NT path\n        ntpath2 = '\\\\Device\\\\HarddiskVolume1\\\\Windows\\\\Temp'\n\n        devices = {devicepath: driveletter}\n\n        with mock.patch(\n            'psutil._pswindows.cext.QueryDosDevice', side_effect=devices.get\n        ) as m:\n            assert psutil._pswindows.convert_dos_path(ntpath1) == winpath\n            assert psutil._pswindows.convert_dos_path(ntpath2) == winpath\n            assert m.called\n\n    def test_convert_dos_path_unc(self):\n        # UNC path\n        winpath = '\\\\\\\\localhost\\\\C$\\\\Windows\\\\Temp'\n        # Path returned by RtlDosPathNameToNtPathName\n        ntpath1 = '\\\\??\\\\UNC\\\\localhost\\\\C$\\\\Windows\\\\Temp'\n        # Normalized NT path\n        ntpath2 = '\\\\Device\\\\Mup\\\\localhost\\\\C$\\\\Windows\\\\Temp'\n\n        assert psutil._pswindows.convert_dos_path(winpath) == winpath\n        assert psutil._pswindows.convert_dos_path(ntpath1) == winpath\n        assert psutil._pswindows.convert_dos_path(ntpath2) == winpath\n\n    def test_net_if_stats(self):\n        ps_names = set(cext.net_if_stats())\n        wmi_adapters = wmi.WMI().Win32_NetworkAdapter()\n        wmi_names = set()\n        for wmi_adapter in wmi_adapters:\n            wmi_names.add(wmi_adapter.Name)\n            wmi_names.add(wmi_adapter.NetConnectionID)\n        assert (\n            ps_names & wmi_names\n        ), f\"no common entries in {ps_names}, {wmi_names}\"\n\n    def test_boot_time(self):\n        wmi_os = wmi.WMI().Win32_OperatingSystem()\n        wmi_btime_str = wmi_os[0].LastBootUpTime.split('.')[0]\n        wmi_btime_dt = datetime.datetime.strptime(\n            wmi_btime_str, \"%Y%m%d%H%M%S\"\n        )\n        psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time())\n        diff = abs((wmi_btime_dt - psutil_dt).total_seconds())\n        assert diff <= 5, (psutil_dt, wmi_btime_dt)\n\n    def test_uptime(self):\n        # ...against GetTickCount64() (Windows < 7, does not include\n        # time spent during suspend / hybernate).\n        ms = ctypes.windll.kernel32.GetTickCount64()\n        secs = ms / 1000.0\n        assert abs(cext.uptime() - secs) < 0.5\n\n\n# ===================================================================\n# sensors_battery()\n# ===================================================================\n\n\nclass TestSensorsBattery(WindowsTestCase):\n    def test_has_battery(self):\n        if win32api.GetPwrCapabilities()['SystemBatteriesPresent']:\n            assert psutil.sensors_battery() is not None\n        else:\n            assert psutil.sensors_battery() is None\n\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_percent(self):\n        w = wmi.WMI()\n        battery_wmi = w.query('select * from Win32_Battery')[0]\n        battery_psutil = psutil.sensors_battery()\n        assert (\n            abs(battery_psutil.percent - battery_wmi.EstimatedChargeRemaining)\n            < 1\n        )\n\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_power_plugged(self):\n        w = wmi.WMI()\n        battery_wmi = w.query('select * from Win32_Battery')[0]\n        battery_psutil = psutil.sensors_battery()\n        # Status codes:\n        # https://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx\n        assert battery_psutil.power_plugged == (battery_wmi.BatteryStatus == 2)\n\n    @pytest.mark.skipif(not HAS_BATTERY, reason=\"no battery\")\n    def test_secsleft(self):\n        class SYSTEM_POWER_STATUS(ctypes.Structure):\n            _fields_ = [\n                ('ACLineStatus', ctypes.c_byte),\n                ('BatteryFlag', ctypes.c_byte),\n                ('BatteryLifePercent', ctypes.c_byte),\n                ('SystemStatusFlag', ctypes.c_byte),\n                ('BatteryLifeTime', ctypes.c_ulong),\n                ('BatteryFullLifeTime', ctypes.c_ulong),\n            ]\n\n        status = SYSTEM_POWER_STATUS()\n        ctypes.windll.kernel32.GetSystemPowerStatus(ctypes.byref(status))\n        bat = psutil.sensors_battery()\n        if status.ACLineStatus == 1 or status.BatteryFlag & 8:\n            # plugged/charging\n            assert bat.secsleft == psutil.POWER_TIME_UNLIMITED\n        elif status.BatteryLifeTime == 0xFFFFFFFF:\n            assert bat.secsleft == psutil.POWER_TIME_UNKNOWN\n        else:\n            assert bat.secsleft == status.BatteryLifeTime\n\n    def test_emulate_no_battery(self):\n        with mock.patch(\n            \"psutil._pswindows.cext.sensors_battery\",\n            return_value=(0, 128, 0, 0),\n        ) as m:\n            assert psutil.sensors_battery() is None\n            assert m.called\n\n    def test_emulate_power_connected(self):\n        with mock.patch(\n            \"psutil._pswindows.cext.sensors_battery\", return_value=(1, 0, 0, 0)\n        ) as m:\n            assert (\n                psutil.sensors_battery().secsleft\n                == psutil.POWER_TIME_UNLIMITED\n            )\n            assert m.called\n\n    def test_emulate_power_charging(self):\n        with mock.patch(\n            \"psutil._pswindows.cext.sensors_battery\", return_value=(0, 8, 0, 0)\n        ) as m:\n            assert (\n                psutil.sensors_battery().secsleft\n                == psutil.POWER_TIME_UNLIMITED\n            )\n            assert m.called\n\n    def test_emulate_secs_left_unknown(self):\n        with mock.patch(\n            \"psutil._pswindows.cext.sensors_battery\",\n            return_value=(0, 0, 0, -1),\n        ) as m:\n            assert (\n                psutil.sensors_battery().secsleft == psutil.POWER_TIME_UNKNOWN\n            )\n            assert m.called\n\n\n# ===================================================================\n# Process APIs\n# ===================================================================\n\n\nclass TestProcess(WindowsTestCase):\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc().pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    def test_issue_24(self):\n        p = psutil.Process(0)\n        with pytest.raises(psutil.AccessDenied):\n            p.kill()\n\n    def test_special_pid(self):\n        p = psutil.Process(4)\n        assert p.name() == 'System'\n        # use __str__ to access all common Process properties to check\n        # that nothing strange happens\n        str(p)\n        p.username()\n        assert p.create_time() >= 0.0\n        try:\n            rss, _vms = p.memory_info()[:2]\n        except psutil.AccessDenied:\n            # expected on Windows Vista and Windows 7\n            if platform.uname()[1] not in {'vista', 'win-7', 'win7'}:\n                raise\n        else:\n            assert rss > 0\n\n    def test_send_signal(self):\n        p = psutil.Process(self.pid)\n        with pytest.raises(ValueError):\n            p.send_signal(signal.SIGINT)\n\n    def test_num_handles_increment(self):\n        p = psutil.Process(os.getpid())\n        before = p.num_handles()\n        handle = self.OpenProcess()\n        after = p.num_handles()\n        assert after == before + 1\n        win32api.CloseHandle(handle)\n        assert p.num_handles() == before\n\n    def test_ctrl_signals(self):\n        p = psutil.Process(self.spawn_subproc().pid)\n        p.send_signal(signal.CTRL_C_EVENT)\n        p.send_signal(signal.CTRL_BREAK_EVENT)\n        p.kill()\n        p.wait()\n        with pytest.raises(psutil.NoSuchProcess):\n            p.send_signal(signal.CTRL_C_EVENT)\n        with pytest.raises(psutil.NoSuchProcess):\n            p.send_signal(signal.CTRL_BREAK_EVENT)\n\n    def test_username(self):\n        name = win32api.GetUserNameEx(win32con.NameSamCompatible)\n        if name.endswith('$'):\n            # When running as a service account (most likely to be\n            # NetworkService), these user name calculations don't produce the\n            # same result, causing the test to fail.\n            return pytest.skip('running as service account')\n        assert psutil.Process().username() == name\n\n    def test_cmdline(self):\n        win = re.sub(r\"[ ]+\", \" \", win32api.GetCommandLine()).strip()\n        ps = \" \".join(psutil.Process().cmdline())\n        # The PyWin32 command line may retain quotes around argv[0] if they\n        # were used unnecessarily, while psutil will omit them. So remove\n        # the first 2 quotes from win if not in ps.\n        # A path to an executable will not contain quotes, so this is safe.\n        win = win.replace('\"', \"\")\n        ps = ps.replace('\"', \"\")\n        assert win == ps\n\n    # XXX - occasional failures\n\n    # def test_cpu_times(self):\n    #     a = psutil.Process().cpu_times()\n    #     b = win32process.GetProcessTimes(self.OpenProcess())\n    #     assert abs(a.user - b['UserTime'] / 10000000.0) < 0.2\n    #     assert abs(a.user - b['KernelTime'] / 10000000.0) < 0.2\n\n    def test_nice(self):\n        win = win32process.GetPriorityClass(self.OpenProcess())\n        ps = psutil.Process().nice()\n        assert ps == win\n\n    def test_memory_info(self):\n        win = win32process.GetProcessMemoryInfo(self.OpenProcess(self.pid))\n        ps = psutil.Process(self.pid).memory_info()\n        assert ps.rss == win[\"WorkingSetSize\"]\n        assert ps.vms == win[\"PagefileUsage\"]\n        assert ps.peak_rss == win[\"PeakWorkingSetSize\"]\n        assert ps.peak_vms == win[\"PeakPagefileUsage\"]\n        with pytest.warns(DeprecationWarning):\n            assert ps.num_page_faults == win[\"PageFaultCount\"]\n\n    def test_memory_info_deprecated_fields(self):\n        win = win32process.GetProcessMemoryInfo(self.OpenProcess(self.pid))\n        ps = psutil.Process(self.pid).memory_info()\n\n        # old aliases\n        with pytest.warns(DeprecationWarning, match=\"wset is deprecated\"):\n            assert ps.wset == ps.rss\n        with pytest.warns(DeprecationWarning, match=\"peak_wset is deprecated\"):\n            assert ps.peak_wset == ps.peak_rss\n        with pytest.warns(DeprecationWarning, match=\"pagefile is deprecated\"):\n            assert ps.pagefile == ps.vms\n        with pytest.warns(DeprecationWarning, match=\"private is deprecated\"):\n            assert ps.private == ps.vms\n        with pytest.warns(\n            DeprecationWarning, match=\"peak_pagefile is deprecated\"\n        ):\n            assert ps.peak_pagefile == ps.peak_vms\n\n        # fields moved to memory_info_ex()\n        with pytest.warns(\n            DeprecationWarning, match=\"paged_pool is deprecated\"\n        ):\n            assert ps.paged_pool == win['QuotaPagedPoolUsage']\n        with pytest.warns(\n            DeprecationWarning, match=\"nonpaged_pool is deprecated\"\n        ):\n            assert ps.nonpaged_pool == win['QuotaNonPagedPoolUsage']\n        with pytest.warns(\n            DeprecationWarning, match=\"peak_paged_pool is deprecated\"\n        ):\n            assert ps.peak_paged_pool == win['QuotaPeakPagedPoolUsage']\n        with pytest.warns(\n            DeprecationWarning, match=\"peak_nonpaged_pool is deprecated\"\n        ):\n            assert ps.peak_nonpaged_pool == win['QuotaPeakNonPagedPoolUsage']\n\n        # field moved to pages_fault()\n        with pytest.warns(\n            DeprecationWarning, match=\"num_page_faults is deprecated\"\n        ):\n            assert ps.num_page_faults == win['PageFaultCount']\n\n        # test ntuple's __getattr__ override\n        with pytest.raises(AttributeError, match=\"foo\"):\n            ps.foo  # noqa: B018\n\n    def test_memory_info_ex(self):\n        win = win32process.GetProcessMemoryInfo(self.OpenProcess(self.pid))\n        ps = psutil.Process(self.pid).memory_info_ex()\n        assert ps.paged_pool == win[\"QuotaPagedPoolUsage\"]\n        assert ps.nonpaged_pool == win[\"QuotaNonPagedPoolUsage\"]\n        assert ps.peak_paged_pool == win[\"QuotaPeakPagedPoolUsage\"]\n        assert ps.peak_nonpaged_pool == win[\"QuotaPeakNonPagedPoolUsage\"]\n\n        assert ps.virtual > 0\n        assert ps.peak_virtual > 0\n\n        assert ps.peak_virtual >= ps.virtual\n        assert ps.peak_paged_pool >= ps.paged_pool\n        assert ps.peak_nonpaged_pool >= ps.nonpaged_pool\n\n    def test_wait(self):\n        p = psutil.Process(self.pid)\n        p.terminate()\n        ps = p.wait()\n        win = win32process.GetExitCodeProcess(self.OpenProcess(self.pid))\n        assert ps == win\n\n    def test_num_threads(self):\n        ps = psutil.Process(self.pid).num_threads()\n        win = int(powershell(f\"(Get-Process -Id {self.pid}).Threads.Count\"))\n        assert ps == win\n\n    def test_cpu_affinity(self):\n        def from_bitmask(x):\n            return [i for i in range(64) if (1 << i) & x]\n\n        win = from_bitmask(\n            win32process.GetProcessAffinityMask(self.OpenProcess(self.pid))[0]\n        )\n        ps = psutil.Process(self.pid).cpu_affinity()\n        assert ps == win\n\n    def test_io_counters(self):\n        win = win32process.GetProcessIoCounters(self.OpenProcess())\n        ps = psutil.Process().io_counters()\n        assert ps.read_count == win['ReadOperationCount']\n        assert ps.write_count == win['WriteOperationCount']\n        assert ps.read_bytes == win['ReadTransferCount']\n        assert ps.write_bytes == win['WriteTransferCount']\n        assert ps.other_count == win['OtherOperationCount']\n        assert ps.other_bytes == win['OtherTransferCount']\n\n    def test_num_handles(self):\n        PROCESS_QUERY_INFORMATION = 0x400\n        handle = ctypes.windll.kernel32.OpenProcess(\n            PROCESS_QUERY_INFORMATION, 0, self.pid\n        )\n        self.addCleanup(ctypes.windll.kernel32.CloseHandle, handle)\n\n        hndcnt = ctypes.wintypes.DWORD()\n        ctypes.windll.kernel32.GetProcessHandleCount(\n            handle, ctypes.byref(hndcnt)\n        )\n        win = hndcnt.value\n        ps = psutil.Process(self.pid).num_handles()\n        assert ps == win\n\n    def test_error_partial_copy(self):\n        # https://github.com/giampaolo/psutil/issues/875\n        exc = OSError()\n        exc.winerror = 299\n        with mock.patch(\"psutil._psplatform.cext.proc_cwd\", side_effect=exc):\n            with mock.patch(\"time.sleep\") as m:\n                p = psutil.Process()\n                with pytest.raises(psutil.AccessDenied):\n                    p.cwd()\n        assert m.call_count >= 5\n\n    def test_exe(self):\n        # NtQuerySystemInformation succeeds if process is gone. Make sure\n        # it raises NSP for a non existent pid.\n        pid = psutil.pids()[-1] + 99999\n        proc = psutil._psplatform.Process(pid)\n        with pytest.raises(psutil.NoSuchProcess):\n            proc.exe()\n\n    @retry_on_failure()\n    def test_page_faults(self):\n        # memory_info() value comes from GetProcessMemoryInfo ->\n        # PageFaultCount. page_faults() value comes from\n        # NtQuerySystemInformation -> HardFaultCount / PageFaultCount\n        p = psutil.Process()\n        mem = p.memory_info()\n        pfaults = p.page_faults()\n        tol = 500\n        assert mem.num_page_faults == pytest.approx(pfaults.minor, abs=tol)\n\n\nclass TestProcessWMI(WindowsTestCase):\n    \"\"\"Compare Process API results with WMI.\"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc().pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    def test_name(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        assert p.name() == w.Caption\n\n    # This fail on github because using virtualenv for test environment\n    @pytest.mark.skipif(\n        GITHUB_ACTIONS, reason=\"unreliable path on GITHUB_ACTIONS\"\n    )\n    def test_exe(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        # Note: wmi reports the exe as a lower case string.\n        # Being Windows paths case-insensitive we ignore that.\n        assert p.exe().lower() == w.ExecutablePath.lower()\n\n    def test_cmdline(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        assert ' '.join(p.cmdline()) == w.CommandLine.replace('\"', '')\n\n    def test_username(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        domain, _, username = w.GetOwner()\n        username = f\"{domain}\\\\{username}\"\n        assert p.username() == username\n\n    @retry_on_failure()\n    def test_memory_rss(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        rss = p.memory_info().rss\n        assert rss == int(w.WorkingSetSize)\n\n    @retry_on_failure()\n    def test_memory_vms(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        vms = p.memory_info().vms\n        # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx\n        # ...claims that PageFileUsage is represented in Kilo\n        # bytes but funnily enough on certain platforms bytes are\n        # returned instead.\n        wmi_usage = int(w.PageFileUsage)\n        if vms not in {wmi_usage, wmi_usage * 1024}:\n            return pytest.fail(f\"wmi={wmi_usage}, psutil={vms}\")\n\n    def test_cpu_times(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        ps = p.cpu_times()\n        assert abs(ps.user - int(w.UserModeTime) / 1e7) < 0.1\n        assert abs(ps.system - int(w.KernelModeTime) / 1e7) < 0.1\n\n    def test_ppid(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        assert p.ppid() == int(w.ParentProcessId)\n\n    def test_create_time(self):\n        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]\n        p = psutil.Process(self.pid)\n        wmic_create = str(w.CreationDate.split('.')[0])\n        psutil_create = time.strftime(\n            \"%Y%m%d%H%M%S\", time.localtime(p.create_time())\n        )\n        assert wmic_create == psutil_create\n\n\n# ---\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\nclass TestDualProcessImplementation(PsutilTestCase):\n    \"\"\"Certain APIs on Windows have 2 internal implementations, one\n    based on documented Windows APIs, another one based\n    NtQuerySystemInformation() which gets called as fallback in\n    case the first fails because of limited permission error.\n    Here we test that the two methods return the exact same value,\n    see:\n    https://github.com/giampaolo/psutil/issues/304.\n    \"\"\"\n\n    @classmethod\n    def setUpClass(cls):\n        cls.pid = spawn_subproc().pid\n\n    @classmethod\n    def tearDownClass(cls):\n        terminate(cls.pid)\n\n    def test_memory_info(self):\n        mem_1 = psutil.Process(self.pid).memory_info()\n        with mock.patch(\n            \"psutil._psplatform.cext.proc_memory_info\",\n            side_effect=PermissionError,\n        ) as fun:\n            mem_2 = psutil.Process(self.pid).memory_info()\n            assert len(mem_1) == len(mem_2)\n            for i in range(len(mem_1)):\n                assert mem_1[i] >= 0\n                assert mem_2[i] >= 0\n                assert abs(mem_1[i] - mem_2[i]) < 512\n            assert fun.called\n\n    def test_create_time(self):\n        ctime = psutil.Process(self.pid).create_time()\n        with mock.patch(\n            \"psutil._psplatform.cext.proc_times\",\n            side_effect=PermissionError,\n        ) as fun:\n            assert psutil.Process(self.pid).create_time() == ctime\n            assert fun.called\n\n    def test_cpu_times(self):\n        cpu_times_1 = psutil.Process(self.pid).cpu_times()\n        with mock.patch(\n            \"psutil._psplatform.cext.proc_times\",\n            side_effect=PermissionError,\n        ) as fun:\n            cpu_times_2 = psutil.Process(self.pid).cpu_times()\n            assert fun.called\n            assert abs(cpu_times_1.user - cpu_times_2.user) < 0.01\n            assert abs(cpu_times_1.system - cpu_times_2.system) < 0.01\n\n    def test_io_counters(self):\n        io_counters_1 = psutil.Process(self.pid).io_counters()\n        with mock.patch(\n            \"psutil._psplatform.cext.proc_io_counters\",\n            side_effect=PermissionError,\n        ) as fun:\n            io_counters_2 = psutil.Process(self.pid).io_counters()\n            for i in range(len(io_counters_1)):\n                assert abs(io_counters_1[i] - io_counters_2[i]) < 5\n            assert fun.called\n\n    def test_num_handles(self):\n        num_handles = psutil.Process(self.pid).num_handles()\n        with mock.patch(\n            \"psutil._psplatform.cext.proc_num_handles\",\n            side_effect=PermissionError,\n        ) as fun:\n            assert psutil.Process(self.pid).num_handles() == num_handles\n            assert fun.called\n\n    def test_cmdline(self):\n        for pid in psutil.pids():\n            try:\n                a = cext.proc_cmdline(pid, use_peb=True)\n                b = cext.proc_cmdline(pid, use_peb=False)\n            except OSError as err:\n                err = convert_oserror(err)\n                if not isinstance(\n                    err, (psutil.AccessDenied, psutil.NoSuchProcess)\n                ):\n                    raise\n            else:\n                assert a == b\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\nclass RemoteProcessTestCase(PsutilTestCase):\n    \"\"\"Certain functions require calling ReadProcessMemory.\n    This trivially works when called on the current process.\n    Check that this works on other processes, especially when they\n    have a different bitness.\n    \"\"\"\n\n    @staticmethod\n    def find_other_interpreter():\n        # find a python interpreter that is of the opposite bitness from us\n        code = \"import sys; sys.stdout.write(str(sys.maxsize > 2**32))\"\n\n        # XXX: a different and probably more stable approach might be to access\n        # the registry but accessing 64 bit paths from a 32 bit process\n        for filename in glob.glob(r\"C:\\Python*\\python.exe\"):\n            proc = subprocess.Popen(\n                args=[filename, \"-c\", code],\n                stdout=subprocess.PIPE,\n                stderr=subprocess.STDOUT,\n            )\n            output, _ = proc.communicate()\n            proc.wait()\n            if output == str(not IS_64BIT):\n                return filename\n\n    test_args = [\"-c\", \"import sys; sys.stdin.read()\"]\n\n    def setUp(self):\n        super().setUp()\n\n        other_python = self.find_other_interpreter()\n        if other_python is None:\n            return pytest.skip(\n                \"could not find interpreter with opposite bitness\"\n            )\n        if IS_64BIT:\n            self.python64 = sys.executable\n            self.python32 = other_python\n        else:\n            self.python64 = other_python\n            self.python32 = sys.executable\n\n        env = os.environ.copy()\n        env[\"THINK_OF_A_NUMBER\"] = str(os.getpid())\n        self.proc32 = self.spawn_subproc(\n            [self.python32] + self.test_args, env=env, stdin=subprocess.PIPE\n        )\n        self.proc64 = self.spawn_subproc(\n            [self.python64] + self.test_args, env=env, stdin=subprocess.PIPE\n        )\n\n    def tearDown(self):\n        super().tearDown()\n        self.proc32.communicate()\n        self.proc64.communicate()\n\n    def test_cmdline_32(self):\n        p = psutil.Process(self.proc32.pid)\n        assert len(p.cmdline()) == 3\n        assert p.cmdline()[1:] == self.test_args\n\n    def test_cmdline_64(self):\n        p = psutil.Process(self.proc64.pid)\n        assert len(p.cmdline()) == 3\n        assert p.cmdline()[1:] == self.test_args\n\n    def test_cwd_32(self):\n        p = psutil.Process(self.proc32.pid)\n        assert p.cwd() == os.getcwd()\n\n    def test_cwd_64(self):\n        p = psutil.Process(self.proc64.pid)\n        assert p.cwd() == os.getcwd()\n\n    def test_environ_32(self):\n        p = psutil.Process(self.proc32.pid)\n        e = p.environ()\n        assert \"THINK_OF_A_NUMBER\" in e\n        assert e[\"THINK_OF_A_NUMBER\"] == str(os.getpid())\n\n    def test_environ_64(self):\n        p = psutil.Process(self.proc64.pid)\n        try:\n            p.environ()\n        except psutil.AccessDenied:\n            pass\n\n\n# ===================================================================\n# Windows services\n# ===================================================================\n\n\n@pytest.mark.skipif(not WINDOWS, reason=\"WINDOWS only\")\nclass TestServices(PsutilTestCase):\n    def test_win_service_iter(self):\n        valid_statuses = {\n            \"running\",\n            \"paused\",\n            \"start\",\n            \"pause\",\n            \"continue\",\n            \"stop\",\n            \"stopped\",\n        }\n        valid_start_types = {\"automatic\", \"manual\", \"disabled\"}\n        valid_statuses = {\n            \"running\",\n            \"paused\",\n            \"start_pending\",\n            \"pause_pending\",\n            \"continue_pending\",\n            \"stop_pending\",\n            \"stopped\",\n        }\n        for serv in psutil.win_service_iter():\n            if serv.name() == \"WaaSMedicSvc\":\n                # known issue in Windows 11 reading the description\n                # https://learn.microsoft.com/en-us/answers/questions/1320388/in-windows-11-version-22h2-there-it-shows-(failed\n                # https://github.com/giampaolo/psutil/issues/2383\n                continue\n            data = serv.as_dict()\n            assert isinstance(data['name'], str)\n            assert data['name'].strip()\n            assert isinstance(data['display_name'], str)\n            assert isinstance(data['username'], str)\n            assert data['status'] in valid_statuses\n            if data['pid'] is not None:\n                psutil.Process(data['pid'])\n            assert isinstance(data['binpath'], str)\n            assert isinstance(data['username'], str)\n            assert isinstance(data['start_type'], str)\n            assert data['start_type'] in valid_start_types\n            assert data['status'] in valid_statuses\n            assert isinstance(data['description'], str)\n            pid = serv.pid()\n            if pid is not None:\n                p = psutil.Process(pid)\n                assert p.is_running()\n            # win_service_get\n            s = psutil.win_service_get(serv.name())\n            # test __eq__\n            assert serv == s\n\n    def test_win_service_get(self):\n        ERROR_SERVICE_DOES_NOT_EXIST = (\n            psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST\n        )\n        ERROR_ACCESS_DENIED = psutil._psplatform.cext.ERROR_ACCESS_DENIED\n\n        name = next(psutil.win_service_iter()).name()\n        with pytest.raises(psutil.NoSuchProcess) as cm:\n            psutil.win_service_get(name + '???')\n        assert cm.value.name == name + '???'\n\n        # test NoSuchProcess\n        service = psutil.win_service_get(name)\n        exc = OSError(0, \"msg\", 0)\n        exc.winerror = ERROR_SERVICE_DOES_NOT_EXIST\n        with mock.patch(\n            \"psutil._psplatform.cext.winservice_query_status\", side_effect=exc\n        ):\n            with pytest.raises(psutil.NoSuchProcess):\n                service.status()\n        with mock.patch(\n            \"psutil._psplatform.cext.winservice_query_config\", side_effect=exc\n        ):\n            with pytest.raises(psutil.NoSuchProcess):\n                service.username()\n\n        # test AccessDenied\n        exc = OSError(0, \"msg\", 0)\n        exc.winerror = ERROR_ACCESS_DENIED\n        with mock.patch(\n            \"psutil._psplatform.cext.winservice_query_status\", side_effect=exc\n        ):\n            with pytest.raises(psutil.AccessDenied):\n                service.status()\n        with mock.patch(\n            \"psutil._psplatform.cext.winservice_query_config\", side_effect=exc\n        ):\n            with pytest.raises(psutil.AccessDenied):\n                service.username()\n\n        # test __str__ and __repr__\n        assert service.name() in str(service)\n        assert service.display_name() in str(service)\n        assert service.name() in repr(service)\n        assert service.display_name() in repr(service)\n"
  }
]