New Upstream Release - px
Ready changes
Summary
Merged new upstream version: 3.3.1 (was: 3.1.0).
Resulting package
Built on 2023-05-30T08:01 (took 5m11s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases px
Lintian Result
Diff
diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml
index b05a8a3..da8afec 100644
--- a/.github/workflows/linux-ci.yml
+++ b/.github/workflows/linux-ci.yml
@@ -29,13 +29,18 @@ jobs:
uses: actions/checkout@v2
with:
fetch-depth: 0 # For getting tags from the repo
+ - name: Cache .tox directory
+ uses: actions/cache@v2
+ with:
+ path: .tox
+ key: ${{ runner.os }}-${{ hashFiles('tox.ini') }}
- name: Create a virtualenv
run: |
python3 -m venv env
- name: Install tox in our virtualenv
run: |
. ./env/bin/activate
- pip install tox
+ pip install tox==4.0.9
- name: Run tox in our virtualenv
run: |
. ./env/bin/activate
diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml
index 8440d5b..c4c09a9 100644
--- a/.github/workflows/macos-ci.yml
+++ b/.github/workflows/macos-ci.yml
@@ -9,19 +9,24 @@ on:
jobs:
tox:
- runs-on: macos-10.15
+ runs-on: macos-12
steps:
- name: Check out repository code
uses: actions/checkout@v2
with:
fetch-depth: 0 # For getting tags from the repo
+ - name: Cache .tox directory
+ uses: actions/cache@v2
+ with:
+ path: .tox
+ key: ${{ runner.os }}-${{ hashFiles('tox.ini') }}
- name: Create a virtualenv
run: |
python3 -m venv env
- name: Install tox in our virtualenv
run: |
. ./env/bin/activate
- pip install tox
+ pip install tox==4.0.9
- name: Run tox in our virtualenv
run: |
. ./env/bin/activate
diff --git a/README.rst b/README.rst
index b47f9fa..498597a 100644
--- a/README.rst
+++ b/README.rst
@@ -5,30 +5,45 @@
See below for `how to install`_.
+``ptop`` is what I usually use when `Bubblemon`_ shows something unexpected is
+going on.
+
+``px`` I use for figuring out things like "do I still have any `Flutter`_
+processes running in the background"?
+
Output
======
``ptop``
--------
+
+If you're coming from ``htop`` or some other ``top`` variant, here's what to
+expect from ``ptop``, with explanations below the screenshot:
+
|ptop screenshot|
-* Note how the default sort order of CPU-usage-since-``ptop``-started makes the
- display mostly stable.
* Note the core count right next to the system load number, for easy comparison.
-* Note the load history graph next to the load numbers. On this system the
- load went up during the last minute. This is a visualization of the numbers
+* Note the load history graph next to the load numbers. On this system the load
+ has been high for the last 15 minutes. This is a visualization of the numbers
you get from ``uptime``.
-* Note the ``IO Load`` number, this shows which IO device had the highest
- average throughput since ``ptop`` launched.
+* Note the bars showing which programs / users are using your memory below the
+ memory numbers
+* Note the ``IO Load`` number, showing which IO device had the highest average
+ throughput since ``ptop`` launched.
+* Note how the default sort order of CPUTIME-since-``ptop``-started makes the
+ display mostly stable and enables you to sort by CPU usage.
* Note that binaries launched while ``ptop`` is running are listed at the bottom
of the display.
-* Note the visualization of which programs / users are using your memory below
- the memory numbers
+* Note how the Python program on the second to last line is shown as
+ ``run_adapter.py`` (the program) rather than ``python3`` (the runtime). `This
+ support is available for many VMs`_ like Java, Node, ...
* Selecting a process with Enter will offer you to see detailed information
about that process, in ``$PAGER``, `moar`_ or ``less``. Or to kill it.
* After you press ``q`` to quit, the display is retained and some lines at the
bottom are removed to prevent the information you want from scrolling out of
view.
+* A help text on the bottom hints you how to search / filter (interactively),
+ change sort order or how to pick processes for further inspection or killing.
``px``
-------------
@@ -176,12 +191,16 @@ Installation
------------
On `Debian 10 Buster`_ or later, and on `Ubuntu 19.04 Disco`_ and later, install using::
- sudo apt-get install px
+ sudo apt install px
If you have `Homebrew`_ on your system (likely on macOS)::
brew install px
+On `Arch Linux`_::
+
+ paru -S px_ptop
+
On other systems, install into ``/usr/local/bin`` by copy / pasting this command
into a terminal::
@@ -340,9 +359,12 @@ DONE
* ptop: Let user switch between CPU time sort and memory sort
.. _how to install: #installation
+.. _Bubblemon: https://walles.github.io/bubblemon/
+.. _Flutter: https://flutter.dev
.. _Debian 10 Buster: https://wiki.debian.org/DebianBuster
.. _Ubuntu 19.04 Disco: https://launchpad.net/ubuntu/disco/
.. _Homebrew: https://brew.sh
+.. _Arch Linux: https://archlinux.org/
.. _download the latest px.pex: https://github.com/walles/px/releases/latest
.. _Unix domain sockets: https://en.wikipedia.org/wiki/Unix_domain_socket
.. _This support is available for many VMs: https://github.com/walles/px/blob/python/tests/px_commandline_test.py
diff --git a/debian/changelog b/debian/changelog
index ac566af..5ebb081 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,9 @@
-px (3.1.0-2) UNRELEASED; urgency=medium
+px (3.3.1-1) UNRELEASED; urgency=medium
* Set upstream metadata fields: Bug-Database, Bug-Submit, Repository-Browse.
+ * New upstream release.
- -- Debian Janitor <janitor@jelmer.uk> Sun, 20 Nov 2022 01:00:08 -0000
+ -- Debian Janitor <janitor@jelmer.uk> Tue, 30 May 2023 07:56:50 -0000
px (3.1.0-1) unstable; urgency=medium
diff --git a/debian/patches/000-avoid-version-gen.patch b/debian/patches/000-avoid-version-gen.patch
index 6c541a8..81ce435 100644
--- a/debian/patches/000-avoid-version-gen.patch
+++ b/debian/patches/000-avoid-version-gen.patch
@@ -5,9 +5,11 @@ Author: Josue Ortega <josue@debian.org>
Last-Update: 2022-07-17
Forwarded: not-needed
---- a/setup.py
-+++ b/setup.py
-@@ -9,33 +9,6 @@
+Index: px.git/setup.py
+===================================================================
+--- px.git.orig/setup.py
++++ px.git/setup.py
+@@ -9,33 +9,6 @@ import subprocess
from setuptools import setup
@@ -41,7 +43,7 @@ Forwarded: not-needed
with open("requirements.txt", encoding="utf-8") as reqsfile:
requirements = reqsfile.readlines()
-@@ -44,13 +17,9 @@
+@@ -44,13 +17,9 @@ with open(
) as fp:
LONG_DESCRIPTION = fp.read()
@@ -56,9 +58,11 @@ Forwarded: not-needed
description="ps and top for Human Beings",
long_description=LONG_DESCRIPTION,
author="Johan Walles",
---- a/px/px.py
-+++ b/px/px.py
-@@ -53,6 +53,7 @@
+Index: px.git/px/px.py
+===================================================================
+--- px.git.orig/px/px.py
++++ px.git/px/px.py
+@@ -53,6 +53,7 @@ from . import px_processinfo
from typing import Optional, List
@@ -66,7 +70,7 @@ Forwarded: not-needed
ERROR_REPORTING_HEADER = """
---
-@@ -133,9 +134,8 @@
+@@ -133,9 +134,8 @@ def handleLogMessages(messages: Optional
# even if we don't use it. And this will make test avoidance fail to avoid
# px.py tests every time you make a new commit (because committing recreates
# version.py).
@@ -77,7 +81,7 @@ Forwarded: not-needed
sys.stderr.write("\n")
sys.stderr.write("Python version: " + sys.version + "\n")
-@@ -163,9 +163,7 @@
+@@ -163,9 +163,7 @@ def _main(argv: List[str]) -> None:
# NOTE: If we "import version" at the top of this file, we will depend on it even if
# we don't use it. And this will make test avoidance fail to avoid px.py tests every
# time you make a new commit (because committing recreates version.py).
diff --git a/debian/patches/remove-install-requires.patch b/debian/patches/remove-install-requires.patch
index 738723e..0019f7f 100644
--- a/debian/patches/remove-install-requires.patch
+++ b/debian/patches/remove-install-requires.patch
@@ -4,9 +4,11 @@ Author: Josue Ortega <josue@debian.org>
Last-Update: 2021-11-01
Forwarded: not-needed
---- a/setup.py
-+++ b/setup.py
-@@ -43,7 +43,7 @@
+Index: px.git/setup.py
+===================================================================
+--- px.git.orig/setup.py
++++ px.git/setup.py
+@@ -39,7 +39,7 @@ setup(
"Topic :: Utilities",
],
packages=["px"],
diff --git a/devbin/release.sh b/devbin/release.sh
index 7a665a1..b6ad8ee 100755
--- a/devbin/release.sh
+++ b/devbin/release.sh
@@ -111,7 +111,7 @@ virtualenv "${ENVDIR}"
# https://github.com/pypa/twine/issues/273#issuecomment-334911815
pip install "ndg-httpsclient == 0.4.3"
-pip install "twine == 1.9.1"
+pip install "twine == 4.0.2"
# Upload!
echo
diff --git a/install.sh b/install.sh
index 4630527..8d5230a 100644
--- a/install.sh
+++ b/install.sh
@@ -11,32 +11,33 @@ PXPREFIX=${PXPREFIX:-/usr/local/bin}
# Get the download URL for the latest release
TEMPFILE=$(mktemp || mktemp -t px-install-releasesjson.XXXXXXXX)
-curl -s https://api.github.com/repos/$REPO/releases > "${TEMPFILE}"
-if grep "API rate limit exceeded" "${TEMPFILE}" > /dev/null ; then
+curl -s https://api.github.com/repos/${REPO}/releases >"${TEMPFILE}"
+if grep "API rate limit exceeded" "${TEMPFILE}" >/dev/null; then
cat "${TEMPFILE}" >&2
exit 1
fi
URL=$(
- grep browser_download_url "$TEMPFILE" \
- | cut -d '"' -f 4 \
- | head -n 1)
+ grep browser_download_url "${TEMPFILE}" |
+ cut -d '"' -f 4 |
+ head -n 1
+)
rm "${TEMPFILE}"
echo "Downloading the latest release..."
-echo " $URL"
+echo " ${URL}"
TEMPFILE=$(mktemp || mktemp -t px-install.XXXXXXXX)
-curl -L -s "$URL" > "$TEMPFILE"
-chmod a+x "$TEMPFILE"
+curl -L -s "${URL}" >"${TEMPFILE}"
+chmod a+x "${TEMPFILE}"
echo "Installing the latest release..."
echo
-echo "sudo install px.pex /usr/local/bin/px"
-sudo install "$TEMPFILE" "${PXPREFIX}/px"
-echo "sudo install px.pex /usr/local/bin/ptop"
-sudo install "$TEMPFILE" "${PXPREFIX}/ptop"
+echo "sudo install px.pex ${PXPREFIX}/px"
+sudo install "${TEMPFILE}" "${PXPREFIX}/px"
+echo "sudo ln px ${PXPREFIX}/ptop"
+sudo ln -sf px "${PXPREFIX}/ptop"
-rm -f "$TEMPFILE"
+rm -f "${TEMPFILE}"
echo
echo "Installation done, now run one or both of:"
diff --git a/mypy.ini b/mypy.ini
index 0f6badb..12bdfc7 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,7 +1,6 @@
[mypy]
check_untyped_defs = True
-disallow_any_explicit = True
disallow_any_generics = True
disallow_any_unimported = True
disallow_subclassing_any = True
@@ -13,9 +12,3 @@ warn_incomplete_stub = True
warn_redundant_casts = True
warn_return_any = True
warn_unused_ignores = True
-
-[mypy-setuptools]
-ignore_missing_imports = True
-
-[mypy-pytest]
-ignore_missing_imports = True
diff --git a/px/px.py b/px/px.py
index ee20afa..dbfef46 100644
--- a/px/px.py
+++ b/px/px.py
@@ -251,6 +251,10 @@ def _main(argv: List[str]) -> None:
if sort_cpupercent:
procs = list(filter(lambda p: p.cpu_percent is not None, procs))
procs = sorted(procs, key=operator.attrgetter("cpu_percent"))
+ if search:
+ # Put exact search matches last. Useful for "px cat" or other short
+ # search strings with tons of hits.
+ procs = sorted(procs, key=lambda p: p.command == search)
lines = px_terminal.to_screen_lines(procs, None, None, with_username)
if columns:
diff --git a/px/px_commandline.py b/px/px_commandline.py
index c01a082..e658129 100644
--- a/px/px_commandline.py
+++ b/px/px_commandline.py
@@ -2,9 +2,11 @@
import re
import os.path
+import logging
-from typing import List
-from typing import Optional
+from typing import List, Optional
+
+LOG = logging.getLogger(__name__)
# Match "[kworker/0:0H]", no grouping
@@ -76,6 +78,20 @@ def try_clarify_electron(commandline: str) -> Optional[str]:
return None
+def faillog(commandline: str, parse_result: Optional[str]) -> str:
+ """
+ If successful, just return the result. If unsuccessful log the problem and
+ return the VM name.
+ """
+ if parse_result:
+ return parse_result
+
+ LOG.debug("Parsing failed, using fallback: <%s>", commandline)
+
+ vm = os.path.basename(to_array(commandline)[0])
+ return vm
+
+
def get_command(commandline: str) -> str:
"""
Extracts the command from the command line.
@@ -95,7 +111,7 @@ def get_command(commandline: str) -> str:
command = os.path.basename(to_array(commandline)[0])
if command.startswith("python") or command == "Python":
- return get_python_command(commandline)
+ return faillog(commandline, get_python_command(commandline))
if command == "Electron":
clarified = try_clarify_electron(commandline)
@@ -103,48 +119,82 @@ def get_command(commandline: str) -> str:
return clarified
if command == "java":
- return get_java_command(commandline)
+ return faillog(commandline, get_java_command(commandline))
if command == "ruby":
# Switches list inspired by ruby 2.3.7p456 --help output
- return get_generic_script_command(
+ return faillog(
commandline,
- [
- "-a",
- "-d",
- "--debug",
- "--disable",
- #
- # Quickfix for #74, better implementations welcome!
- # https://github.com/walles/px/issues/74
- "-Eascii-8bit:ascii-8bit",
- #
- "-l",
- "-n",
- "-p",
- "-s",
- "-S",
- "-v",
- "--verbose",
- "-w",
- "-W0",
- "-W1",
- "-W2",
- "--",
- ],
+ get_generic_script_command(
+ commandline,
+ ignore_switches=[
+ "-a",
+ "-d",
+ "--debug",
+ "--disable",
+ #
+ # Quickfix for #74, better implementations welcome!
+ # https://github.com/walles/px/issues/74
+ "-Eascii-8bit:ascii-8bit",
+ #
+ "-l",
+ "-n",
+ "-p",
+ "-s",
+ "-S",
+ "-v",
+ "--verbose",
+ "-w",
+ "-W0",
+ "-W1",
+ "-W2",
+ "--",
+ ],
+ ),
)
if command == "sudo":
- return get_sudo_command(commandline)
+ return faillog(commandline, get_sudo_command(commandline))
+
+ if command in [
+ # NOTE: This list contains binaries that are mostly used with
+ # subcommands. Scripts (like brew.rb) are handled in
+ # get_generic_script_command().
+ #
+ # "gradle" and "mvn" are not handled at all, could be added to
+ # get_java_command().
+ "apt-get",
+ "apt",
+ "cargo",
+ "docker",
+ "docker-compose",
+ "git",
+ "go",
+ "npm",
+ "pip",
+ "pip3",
+ "rustup",
+ ]:
+ return faillog(commandline, get_with_subcommand(commandline))
+
+ if command == "terraform":
+ return faillog(
+ commandline, get_with_subcommand(commandline, ignore_switches=["-chdir"])
+ )
if command == "node":
- return get_generic_script_command(commandline, ["--max_old_space_size"])
+ return faillog(
+ commandline,
+ get_generic_script_command(
+ commandline, ignore_switches=["--max_old_space_size"]
+ ),
+ )
if command in ["bash", "sh"]:
- return get_generic_script_command(commandline)
+ return faillog(commandline, get_generic_script_command(commandline))
if PERL_BIN.match(command):
- return get_generic_script_command(commandline)
+ return faillog(commandline, get_generic_script_command(commandline))
app_name_prefix = get_app_name_prefix(commandline)
if is_human_friendly(command):
@@ -156,19 +206,24 @@ def get_command(commandline: str) -> str:
command_split = command.split(".")
if len(command_split) > 1:
+ command_suggestion = ""
if len(command_split[-1]) > 4:
# Pretend all the dots are a kind of path and go for the last
# part only
- command = command_split[-1]
+ command_suggestion = command_split[-1]
else:
# Assume last part is a file suffix (like ".exe") and take the
# next to last part
- command = command_split[-2]
+ command_suggestion = command_split[-2]
+ if len(command_suggestion) >= 5:
+ # Good enough!
+ command = command_suggestion
return app_name_prefix + command
-def get_python_command(commandline: str) -> str:
+def get_python_command(commandline: str) -> Optional[str]:
+ """Returns None if we failed to figure out the script name"""
array = to_array(commandline)
array = list(filter(lambda s: s, array))
@@ -207,17 +262,18 @@ def get_python_command(commandline: str) -> str:
if array[1] == "-m" and not array[2].startswith("-"):
return os.path.basename(array[2])
- return python
+ return None
-def get_sudo_command(commandline: str) -> str:
+def get_sudo_command(commandline: str) -> Optional[str]:
+ """Returns None if we failed to figure out the script name"""
without_sudo = commandline[5:].strip()
if not without_sudo:
return "sudo"
if without_sudo.startswith("-"):
# Give up on options
- return "sudo"
+ return None
return "sudo " + get_command(without_sudo)
@@ -234,7 +290,8 @@ def prettify_fully_qualified_java_class(class_name: str) -> str:
return split[-1]
-def get_java_command(commandline: str) -> str:
+def get_java_command(commandline: str) -> Optional[str]:
+ """Returns None if we failed to figure out the script name"""
array = to_array(commandline)
java = os.path.basename(array[0])
if len(array) == 1:
@@ -248,16 +305,14 @@ def get_java_command(commandline: str) -> str:
if state == "skip next":
if component.startswith("-"):
- # Skipping switches doesn't make sense. We're lost, fall back to
- # just returning the command name
- return java
+ # Skipping switches doesn't make sense. We're lost.
+ return None
state = "scanning"
continue
if state == "return next":
if component.startswith("-"):
- # Returning switches doesn't make sense. We're lost, fall back
- # to just returning the command name
- return java
+ # Returning switches doesn't make sense. We're lost.
+ return None
return os.path.basename(component)
if state == "scanning":
if component.startswith("-X"):
@@ -309,18 +364,40 @@ def get_java_command(commandline: str) -> str:
continue
if component.startswith("-"):
# Unsupported switch, give up
- return java
+ return None
return prettify_fully_qualified_java_class(component)
raise ValueError(f"Unhandled state <{state}> at <{component}> for: {array}")
# We got to the end without being able to come up with a better name, give up
- return java
+ return None
+
+
+def get_with_subcommand(
+ commandline: str, ignore_switches: Optional[List[str]] = None
+) -> Optional[str]:
+ array = to_array(commandline)
+
+ if ignore_switches is None:
+ ignore_switches = []
+ while len(array) > 1 and array[1].split("=")[0] in ignore_switches:
+ del array[1]
+
+ command = os.path.basename(array[0])
+ if len(array) == 1:
+ return command
+
+ if array[1].startswith("-"):
+ # Unknown option, help!
+ return command
+
+ return f"{command} {array[1]}"
def get_generic_script_command(
commandline: str, ignore_switches: Optional[List[str]] = None
-) -> str:
+) -> Optional[str]:
+ """Returns None if we failed to figure out the script name"""
array = to_array(commandline)
if ignore_switches is None:
@@ -333,7 +410,20 @@ def get_generic_script_command(
return vm
if array[1].startswith("-"):
- # This is some option, we don't do options
- return vm
-
- return os.path.basename(array[1])
+ # Unknown option, help!
+ return None
+
+ script = os.path.basename(array[1])
+ if len(array) == 2:
+ # vm + script
+ return script
+ if script not in ["brew.rb", "yarn.js"]:
+ return script
+ script = os.path.splitext(script)[0]
+
+ subcommand = array[2]
+ if subcommand.startswith("-"):
+ # Unknown option before the subcommand
+ return script
+
+ return f"{script} {subcommand}"
diff --git a/px/px_load.py b/px/px_load.py
index 383f2a4..a8773ec 100644
--- a/px/px_load.py
+++ b/px/px_load.py
@@ -9,7 +9,7 @@ import os
from . import px_cpuinfo
from . import px_terminal
-from typing import Tuple
+from typing import Tuple, Optional
physical, logical = px_cpuinfo.get_core_count()
@@ -84,7 +84,7 @@ def get_load_values() -> Tuple[float, float, float]:
return (avg0to1, avg1to5, avg5to15)
-def get_load_string(load_values: Tuple[float, float, float] = None) -> str:
+def get_load_string(load_values: Optional[Tuple[float, float, float]] = None) -> str:
"""
Example return string, underlines indicate bold:
"1.5 [4 cores | 8 virtual] [15m history: GRAPH]"
diff --git a/px/px_pager.py b/px/px_pager.py
index 537377c..a4466f0 100644
--- a/px/px_pager.py
+++ b/px/px_pager.py
@@ -126,7 +126,12 @@ def page_process_info(
info_thread = threading.Thread(
target=_pump_info_to_fd, args=(pager_stdin, process, processes)
)
- info_thread.setDaemon(True) # Terminating ptop while this is running is fine
+
+ # Terminating ptop while this is running is fine. This is deprecated since
+ # Python 3.10, but we want to support older Pythons as well so let's keep it
+ # this way for now.
+ info_thread.setDaemon(True) # pylint: disable=deprecated-method
+
info_thread.start()
pagerExitcode = pager.wait()
diff --git a/px/px_process.py b/px/px_process.py
index f721564..69f5c4e 100644
--- a/px/px_process.py
+++ b/px/px_process.py
@@ -214,6 +214,9 @@ class PxProcess:
if string in self.cmdline.lower():
return True
+ if str(self.pid).startswith(string):
+ return True
+
return False
def get_command_line_array(self):
diff --git a/px/px_terminal.py b/px/px_terminal.py
index fb31b1c..91fa0ee 100644
--- a/px/px_terminal.py
+++ b/px/px_terminal.py
@@ -119,7 +119,7 @@ def read_select(
def getch(
- timeout_seconds: Optional[int] = None, fd: int = None
+ timeout_seconds: Optional[int] = None, fd: Optional[int] = None
) -> Optional[ConsumableString]:
"""
Wait at most timeout_seconds for a character to become available on stdin.
@@ -219,7 +219,6 @@ def filter_out_unchanged_screen_lines(
def draw_screen_lines(lines: List[str], columns: int) -> None:
-
unfiltered_screen_lines = raw_lines_to_screen_lines(lines, columns)
screen_lines = filter_out_unchanged_screen_lines(unfiltered_screen_lines, columns)
diff --git a/px/px_top.py b/px/px_top.py
index d583d1d..2329466 100644
--- a/px/px_top.py
+++ b/px/px_top.py
@@ -290,6 +290,19 @@ def get_screen_lines(
filter(lambda p: p.match(search, require_exact_user=False), toplist)
)
+ # Put exact search matches first. Useful for "px cat" or other short
+ # search strings with tons of hits.
+ search_pid = -1
+ try:
+ search_pid = int(search)
+ except ValueError:
+ pass
+ toplist = sorted(
+ toplist,
+ key=lambda p: search in (p.command, p.username) or p.pid == search_pid,
+ reverse=True,
+ )
+
# Hand out different amount of lines to the different sections
footer_height = 0
cputop_minheight = 10
diff --git a/tests/px_commandline_test.py b/tests/px_commandline_test.py
index f68d832..d0024be 100644
--- a/tests/px_commandline_test.py
+++ b/tests/px_commandline_test.py
@@ -276,7 +276,7 @@ def test_get_command_ruby_switches():
px_commandline.get_command(
"/usr/bin/ruby -W0 /usr/local/bin/brew.rb install rust"
)
- == "brew.rb"
+ == "brew install"
)
# https://github.com/walles/px/issues/87
@@ -394,10 +394,43 @@ def test_get_homebrew_commandline():
]
)
)
- == "brew.rb"
+ == "brew upgrade"
)
+def test_get_terraform_provider_commandline():
+ # Source: https://github.com/walles/px/issues/105
+ assert (
+ px_commandline.get_command(
+ ".terraform/providers/registry.terraform.io/heroku/heroku/4.8.0/darwin_amd64/terraform-provider-heroku_v4.8.0"
+ )
+ == "terraform-provider-heroku_v4.8.0"
+ )
+
+
+def test_get_terraform_commandline():
+ # Source: https://github.com/walles/px/issues/113
+ assert (
+ px_commandline.get_command("terraform -chdir=dev apply -target=abc123")
+ == "terraform apply"
+ )
+
+
+def test_get_go_commandline():
+ assert px_commandline.get_command("go build ./...") == "go build"
+ assert px_commandline.get_command("go --version") == "go"
+ assert px_commandline.get_command("/usr/local/bin/go") == "go"
+
+
+def test_get_git_commandline():
+ assert (
+ px_commandline.get_command("git clone git@github.com:walles/riff")
+ == "git clone"
+ )
+ assert px_commandline.get_command("git --version") == "git"
+ assert px_commandline.get_command("/usr/local/bin/git") == "git"
+
+
def test_node_max_old_space():
assert (
px_commandline.get_command("node --max_old_space_size=4096 scripts/start.js")
diff --git a/tests/px_process_test.py b/tests/px_process_test.py
index 26b03bf..cd4ed0c 100644
--- a/tests/px_process_test.py
+++ b/tests/px_process_test.py
@@ -278,6 +278,14 @@ def test_match():
assert p.match("air")
assert p.match("play")
+ # Match PID by prefix but not substring. Exact matches are used for
+ # searching in ptop. Prefix matching is used to not throw the right answer
+ # away while the user is typing their search in ptop. Substring matching has
+ # no value.
+ assert p.match("47536")
+ assert p.match("4753")
+ assert not p.match("7536")
+
def test_seconds_to_str():
assert px_process.seconds_to_str(0.54321) == "0.54s"
diff --git a/tests/testutils.py b/tests/testutils.py
index 15ba6ee..9744c18 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -75,9 +75,9 @@ def create_file(
name: str,
device: Optional[str],
pid: int,
- access: str = None,
- inode: str = None,
- fd: int = None,
+ access: Optional[str] = None,
+ inode: Optional[str] = None,
+ fd: Optional[int] = None,
fdtype: Optional[str] = None,
):
# type (...) -> px_file.PxFile
diff --git a/tox.ini b/tox.ini
index 38d477e..c7f52ce 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,14 +5,14 @@
# https://github.com/tox-dev/tox/pull/1202
minversion = 3.8.0
-mypy_version = 0.790
+mypy_version = 1.2.0
pylint_version = 2.13.9
-pytest_version = 5.4.1
+pytest_version = 7.1.3
envlist=
version.py
black
- mypy3
+ mypy
pylint
shellcheck
installtest
@@ -23,6 +23,7 @@ envlist=
[testenv]
skip_install = true
basepython = python3
+allowlist_externals = /bin/bash
[testenv:version.py]
commands =
@@ -39,15 +40,18 @@ deps =
# Ubuntu version named in tox runs-on in linux-ci.yml.
commands = /bin/bash -c 'if [ "{env:CI:}" ] ; then export CHECK="--check --diff --color" ; fi ; black --target-version=py38 $CHECK ./*.py ./*/*.py'
-[testenv:mypy3]
-# NOTE: In theory mypy3 should probably depend on Black to get line numbers in
-# any error messages right. But since mypy3 tends to finish last, and being a
-# bit off isn't the end of the world, let's not depend on Black for now and hope
+[testenv:mypy]
+# NOTE: In theory mypy should probably depend on Black to get line numbers in
+# any error messages right. But since mypy tends to finish last, and being a bit
+# off isn't the end of the world, let's not depend on Black for now and hope
# nobody notices.
depends = version.py
deps =
mypy=={[tox]mypy_version}
+ pytest=={[tox]pytest_version}
+ types-setuptools==67.7.0.1 # Matches what was on Johan's laptop 2023-05-08
+ types-python-dateutil==2.8.19
commands =
/bin/bash -c 'mypy --pretty ./*.py ./*/*.py'
@@ -66,6 +70,7 @@ commands =
/bin/bash -c 'shellcheck ./*.sh ./*/*.sh'
[testenv:installtest]
+allowlist_externals = {toxinidir}/tests/installtest.sh
commands =
{toxinidir}/tests/installtest.sh
@@ -92,7 +97,7 @@ commands =
depends = package
commands =
# Verify we have the correct shebang
- /bin/bash -c 'head -n1 {toxinidir}/px.pex | grep -Eq "^#!/usr/bin/env python3$"'
+ /bin/bash -c 'head -n1 {toxinidir}/px.pex | grep -Eq "^\#!/usr/bin/env python3$"'
# Test that there are no natively compiled dependencies. They make
# distribution a lot harder. If this triggers, fix your dependencies!
/bin/bash -c '! unzip -qq -l "{toxinidir}/px.pex" "*.so"'
@@ -104,7 +109,9 @@ commands =
[testenv:test-wheel]
# Test installing using pip
depends = version.py black
-allowlist_externals = /bin/rm
+allowlist_externals =
+ /bin/bash
+ /bin/rm
deps =
setuptools == 44.1.1
wheel == 0.35.1