Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ jobs:
python-version: "3.14"

- uses: astral-sh/setup-uv@v7
- run: uvx --with tox-uv tox -e style
- run: uvx --with tox-uv tox -e style,typecheck
28 changes: 23 additions & 5 deletions docs/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,35 @@
History
=======

5.6.0 (2026-03-16)
5.6.0 (2026-03-18)
------------------

* ``which()`` looks at current venv ``bin/`` folder first

* ``ClickRunner.default_main`` and ``ClickRunner.context_wrapper`` replace deprecated ``cli.default_main`` and ``cli.context``

* Removed:

* ``assert_printed()`` from ``CapturedStream``, ``TrackedOutput``, and ``ClickRunner``

* ``runez.SYS_INFO.platform_id.is_windows``

* ``runez.inspector.AutoInstall``

* ``runez.inspector.ImportTime``

* Not exposing in top-level ``runez`` import (use ``from runez.<submodule> import ...`` if needed):

* ``AdaptedProperty``, ``ascii``, ``config`` , ``is_subfolder``, ``PsInfo``, ``Slotted``

* Simplified ``runez.schema`` internals

* Turned on flake8-simplify and tryceratops, fixed corresponding issues

* Added tests

* Modernized GH actions and project metadata

* Removed ``is_subfolder()`` from ``runez.file``

* Removed ``assert_printed()`` from ``CapturedStream``, ``TrackedOutput``, and ``ClickRunner``

* ``ClickRunner.run()`` now accepts ``pathlib.Path`` objects for ``main``/script args

* Fixed operator precedence bug in ``Version.__le__()``
Expand All @@ -27,6 +43,8 @@ History

* Using ``extractall(filter="data")`` from stdlib for path traversal protection

* Adapted type declarations, pyright now reports 0 errors


5.5.0 (2026-03-12)
------------------
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ dynamic = ["version"]
[project.urls]
Source = "https://github.com/codrsquad/runez"

[tool.pyright]
pythonVersion = "3.10"
include = ["src"]

[tool.ruff]
cache-dir = ".tox/.ruff_cache"
line-length = 140
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from setuptools import setup

setup(
setup_requires="setupmeta",
setup_requires=["setupmeta"],
versioning="dev",
)
20 changes: 10 additions & 10 deletions src/runez/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

# fmt: off
from runez import ascii, click, config, date, file, program, serialize, system
from runez import click, date, file, program, serialize, system
from runez.colors import ActivateColors, ColorManager as color
from runez.colors.named import black, blue, brown, gray, green, orange, plain, purple, red, teal, white, yellow
from runez.colors.named import blink, bold, dim, invert, italic, strikethrough, underline
Expand All @@ -15,16 +15,16 @@
from runez.file import basename, checksum, ensure_folder, parent_folder, readlines, TempFolder, to_path, touch, write
from runez.file import compress, copy, decompress, delete, filesize, ls_dir, move, symlink
from runez.logsetup import LogManager as log, ProgressBar
from runez.program import check_pid, is_executable, make_executable, PsInfo, run, shell, which
from runez.serialize import from_json, read_json, represented_json, save_json, Serializable
from runez.system import abort, abort_if, AdaptedProperty, cached_property, uncolored, Undefined, UNSET, wcswidth
from runez.system import Anchored, CaptureOutput, CurrentFolder, OverrideDryrun, Slotted, TempArgv, TrackedOutput
from runez.program import check_pid, is_executable, make_executable, run, shell, which
from runez.serialize import from_json, json_sanitized, read_json, represented_json, save_json, Serializable
from runez.system import abort, abort_if, cached_property, uncolored, Undefined, UNSET, wcswidth
from runez.system import Anchored, CaptureOutput, CurrentFolder, OverrideDryrun, TempArgv, TrackedOutput
from runez.system import capped, decode, DEV, flattened, joined, quoted, resolved_path, short, stringified, SYS_INFO
from runez.system import first_line, get_version, is_basetype, is_iterable, ltattr

__all__ = [ # noqa: RUF022, grouped and sorted by provenance module
"DRYRUN",
"ascii", "click", "config", "date", "file", "program", "serialize", "system",
"click", "date", "file", "program", "serialize", "system",
"ActivateColors", "color",
"black", "blue", "brown", "gray", "green", "orange", "plain", "purple", "red", "teal", "white", "yellow",
"blink", "bold", "dim", "invert", "italic", "strikethrough", "underline",
Expand All @@ -36,10 +36,10 @@
"basename", "checksum", "ensure_folder", "parent_folder", "readlines", "TempFolder", "to_path", "touch", "write",
"compress", "copy", "decompress", "delete", "filesize", "ls_dir", "move", "symlink",
"log", "ProgressBar",
"check_pid", "is_executable", "make_executable", "PsInfo", "run", "shell", "which",
"from_json", "read_json", "represented_json", "save_json", "Serializable",
"abort", "abort_if", "AdaptedProperty", "cached_property", "uncolored", "Undefined", "UNSET", "wcswidth",
"Anchored", "CaptureOutput", "CurrentFolder", "OverrideDryrun", "Slotted", "TempArgv", "TrackedOutput",
"check_pid", "is_executable", "make_executable", "run", "shell", "which",
"from_json", "json_sanitized", "read_json", "represented_json", "save_json", "Serializable",
"abort", "abort_if", "cached_property", "uncolored", "Undefined", "UNSET", "wcswidth",
"Anchored", "CaptureOutput", "CurrentFolder", "OverrideDryrun", "TempArgv", "TrackedOutput",
"capped", "decode", "DEV", "flattened", "joined", "quoted", "resolved_path", "short", "stringified", "SYS_INFO",
"first_line", "get_version", "is_basetype", "is_iterable", "ltattr"
]
Expand Down
94 changes: 2 additions & 92 deletions src/runez/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
import contextlib
import logging
import os
import re
import sys
import sysconfig
import time

import runez
from runez.ascii import AsciiAnimation
from runez.inspector import ImportTime
from runez.render import NAMED_BORDERS, PrettyTable


Expand Down Expand Up @@ -61,63 +58,6 @@ def cmd_diagnostics():
print(PrettyTable.two_column_diagnostics(runez.SYS_INFO.diagnostics(), available, border=args.border))


def cmd_import_speed():
"""Show average import time of top-level python packages installed in this venv"""
parser = runez.cli.parser()
parser.add_argument("--all", action="store_true", help="Show all.")
parser.add_argument("--border", choices=NAMED_BORDERS, default="reddit", help="Use custom border.")
parser.add_argument("--iterations", "-i", type=int, default=3, help="Number of measurements to average.")
parser.add_argument("name", nargs="*", help="Names of modules to show (by default: all).")
args = parser.parse_args()
names = runez.flattened(args.name, split=",")
if args.all:
names.extend(_interesting_top_levels())

if not names:
sys.exit("Please specify module names, or use --all")

names = sorted(set(names))
times = []
fastest = None
slowest = None
for name in names:
t = ImportTime(name, iterations=args.iterations)
times.append(t)
if t.cumulative is None:
continue

if fastest is None or (t.cumulative < fastest.cumulative):
fastest = t

if slowest is None or t.cumulative > slowest.cumulative:
slowest = t

table = PrettyTable("Module,-X cumulative,Elapsed,Vs fastest,Note", border=args.border)
table.header[3].align = "center"
mid = _get_mid(times)
for t in sorted(times):
if t.cumulative is None:
c = e = f = None

else:
factor = t.elapsed / fastest.elapsed
c = runez.represented_duration(t.cumulative / 1000000, span=-2)
e = runez.represented_duration(t.elapsed, span=-2)
f = "x%.2f" % factor
if t is fastest:
f = ""

elif t is slowest:
f = runez.red(f)

elif t.elapsed and t.elapsed > mid:
f = runez.orange(f)

table.add_row(t.module_name, c, e, f, t.problem or "")

print(table)


def cmd_passthrough():
"""
Capture pass-through test
Expand Down Expand Up @@ -172,10 +112,10 @@ def cmd_progress_bar():
else:
runez.log.trace("At iteration %s" % i)

if args.verbose and i % 10 == 0: # pragma: no cover
if args.verbose and i % 10 == 0:
print("iteration %s" % runez.bold(i))

if i == 42: # pragma: no cover
if i == 42:
runez.log.progress.show("some progress msg") # debug() and trace() messages don't appear any more after this
for _ in runez.ProgressBar(range(10)):
time.sleep(0.1)
Expand All @@ -193,24 +133,13 @@ def cmd_progress_bar():
time.sleep(args.sleep)


def _get_mid(times):
elapsed = 0
times = [t for t in times if t.elapsed]
if times:
times = sorted(times, key=lambda x: -x.elapsed) # Don't fail if no elapsed available
elapsed = times[int(len(times) / 2)].elapsed

return elapsed


def main():
runez.cli.run_cmds()


def _show_fgcolors(bg=runez.plain, border=None):
print("")
table = PrettyTable("Color,Blink,Bold,Dim,Invert,Italic,Strikethrough,Underline", border=border)
table.header.style = "bold"
for color in runez.color.fg:
color_name = color.name
text = color(color.name)
Expand All @@ -227,24 +156,5 @@ def _show_fgcolors(bg=runez.plain, border=None):
print(table)


def _interesting_top_levels():
"""
Convenience for `-mrunez import-time --all` command
Return list of import top level names, ignoring things like top levels starting with an `_`, and other uninteresting libs
"""
uninteresting = re.compile(r"^(_|pip|pkg_resources|pydev|pytest|setuptools).*$")
result = set()
base = sysconfig.get_path("purelib")
for item in runez.ls_dir(base):
if item.name.endswith(".dist-info") and item.is_dir():
top_levels = item / "top_level.txt"
if top_levels.exists():
for line in runez.readlines(top_levels):
if line and not uninteresting.match(line):
result.add(line)

return sorted(result)


if __name__ == "__main__":
main()
6 changes: 3 additions & 3 deletions src/runez/click.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ def main(debug, dryrun, log, ...):
import logging
import os
import sys
import types

try:
import click

except ImportError: # pragma: no cover, click used only if installed
click = None
click: types.ModuleType = None # type: ignore[assignment]

import runez.config
from runez.colors import ColorManager
Expand Down Expand Up @@ -126,15 +127,14 @@ def run_cmds(cls, prog=None):
prog = short(caller.folder)

epilog = PrettyTable(2)
epilog.header[0].style = "bold"
for cmd, func in available_commands.items():
epilog.add_row(" " + cmd, first_line(func.__doc__, default=""))

epilog = "Available commands:\n%s" % epilog
cls._prog = prog or package
parser = cls.parser(epilog=epilog, help=caller.module_docstring, prog=prog)
if cls.version and package:
parser.add_argument(*cls.version, action="version", version=get_version(package), help="Show version and exit")
parser.add_argument(*cls.version, action="version", version=get_version(package) or "0.0.0", help="Show version and exit")

if cls.color:
parser.add_argument(*cls.color, action="store_true", help="Do not use colors (even if on tty)")
Expand Down
Loading