From 24306559e1ba9b932a3d0044ed512fdcee8514f7 Mon Sep 17 00:00:00 2001 From: Expertcoderz Date: Fri, 18 Apr 2025 11:48:27 +0000 Subject: [PATCH 1/4] cut: Add proper description --- userland/utilities/cut.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/userland/utilities/cut.py b/userland/utilities/cut.py index b87daed..c7808d3 100644 --- a/userland/utilities/cut.py +++ b/userland/utilities/cut.py @@ -65,7 +65,13 @@ def cut_and_print_stream(stream: Iterable[bytes], cutter: Cutter) -> None: sys.stdout.buffer.flush() -parser = core.ExtendedOptionParser(usage="%prog OPTION... [FILE]...", description="wow") +parser = core.ExtendedOptionParser( + usage="%prog OPTION... [FILE]...", + description="Print selected parts of lines from each FILE or from standard input.", + epilog="Each LIST contains comma-separated numbers or ranges in the format [START]-[END]." + " For example: 10,-5,12-13,20- means to select the bytes/fields at position 10, up to" + " position 5, from positions 12 to 13, and starting from position 20.", +) parser.add_option("-b", "--bytes", metavar="LIST", help="select bytes in LIST") parser.add_option("-c", "--characters", metavar="LIST", help="identical to -b") From b67e76f8aad53d8dfdd566149a826f92092d39e2 Mon Sep 17 00:00:00 2001 From: Expertcoderz Date: Sat, 19 Apr 2025 13:19:58 +0000 Subject: [PATCH 2/4] Mark some function parameters as positional- or keyword-only --- userland/core/command.py | 14 +++++++++----- userland/core/users.py | 10 +++++----- userland/utilities/factor.py | 10 +++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/userland/core/command.py b/userland/core/command.py index 3743fd6..1b75d39 100644 --- a/userland/core/command.py +++ b/userland/core/command.py @@ -6,7 +6,7 @@ class ExtendedOptionParser(OptionParserUsersMixin, OptionParser): - def __init__(self, usage: str | tuple[str, ...], **kwargs): + def __init__(self, /, usage: str | tuple[str, ...], **kwargs): super().__init__( usage="Usage: " + f"\n{7 * " "}".join(usage if isinstance(usage, tuple) else (usage,)), @@ -20,7 +20,9 @@ def __init__(self, usage: str | tuple[str, ...], **kwargs): help="show usage information and exit", ) - def expect_nargs(self, args: list[str], nargs: int | tuple[int] | tuple[int, int]): + def expect_nargs( + self, /, args: list[str], nargs: int | tuple[int] | tuple[int, int] + ): if isinstance(nargs, int): nargs = (nargs, nargs) @@ -41,13 +43,15 @@ def expect_nargs(self, args: list[str], nargs: int | tuple[int] | tuple[int, int def command(parser: OptionParser | None = None): def create_utility( - func: Callable[[Values, list[str]], int], + func: Callable[[Values, list[str]], int], / ) -> Callable[[], None]: def execute_utility(): try: sys.exit( - func(*parser.parse_args()) if parser else func(Values(), sys.argv[1:]) - ) + func(*parser.parse_args()) + if parser + else func(Values(), sys.argv[1:]) + ) except KeyboardInterrupt: print() sys.exit(130) diff --git a/userland/core/users.py b/userland/core/users.py index 550ff36..d855055 100644 --- a/userland/core/users.py +++ b/userland/core/users.py @@ -6,7 +6,7 @@ class OptionParserUsersMixin(OptionParser): - def parse_owner_spec(self, owner_spec: str) -> tuple[int | None, int | None]: + def parse_owner_spec(self, owner_spec: str, /) -> tuple[int | None, int | None]: """ Accept a string in the form ``[USER][:[GROUP]]`` and return the UID and GID. Either or both may be None if omitted from the input string. @@ -26,7 +26,7 @@ def parse_owner_spec(self, owner_spec: str) -> tuple[int | None, int | None]: return uid, gid # pylint: disable=inconsistent-return-statements - def parse_user(self, user: str) -> int: + def parse_user(self, user: str, /) -> int: """ Accept a string representing a username or UID and return the UID. An appropriate parser error is thrown if obtaining the UID fails. @@ -40,7 +40,7 @@ def parse_user(self, user: str) -> int: self.error(f"invalid user: {user}") # pylint: disable=inconsistent-return-statements - def parse_group(self, group: str) -> int: + def parse_group(self, group: str, /) -> int: """ Accept a string representing a group name or GID and return the GID. An appropriate parser error is thrown if obtaining the GID fails. @@ -55,7 +55,7 @@ def parse_group(self, group: str) -> int: @functools.lru_cache(1000) -def user_display_name_from_id(uid: int) -> str: +def user_display_name_from_id(uid: int, /) -> str: try: return pwd.getpwuid(uid).pw_name except KeyError: @@ -63,7 +63,7 @@ def user_display_name_from_id(uid: int) -> str: @functools.lru_cache(1000) -def group_display_name_from_id(gid: int) -> str: +def group_display_name_from_id(gid: int, /) -> str: try: return grp.getgrgid(gid).gr_name except KeyError: diff --git a/userland/utilities/factor.py b/userland/utilities/factor.py index c6e1655..b1fa981 100644 --- a/userland/utilities/factor.py +++ b/userland/utilities/factor.py @@ -8,7 +8,7 @@ SMALL_PRIMES = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] -def miller_rabin(n: int) -> bool: +def miller_rabin(n: int, /) -> bool: """ A deterministic version of the Miller-Rabin primality test. """ @@ -32,7 +32,7 @@ def miller_rabin(n: int) -> bool: return True -def pollards_rho(n: int, step: int) -> int | None: +def pollards_rho(n: int, /, step: int) -> int | None: """ Pollard's rho algorithm for factorization. @@ -49,7 +49,7 @@ def pollards_rho(n: int, step: int) -> int | None: def factorize( - n: int, cache: dict[int, int] = {p: p for p in SMALL_PRIMES} + n: int, /, *, cache: dict[int, int] = {p: p for p in SMALL_PRIMES} ) -> Generator[int]: """ Generates prime factors of the integer n. @@ -70,7 +70,7 @@ def factorize( else: for i in range(1, n - 1): if (factor := pollards_rho(n, i)) and factor != n: - yield from factorize(min(n, factor), cache) + yield from factorize(min(n, factor), cache=cache) break if factor == n: @@ -79,7 +79,7 @@ def factorize( n //= cast(int, factor) -def format_exponents(factors: Iterable[int]) -> str: +def format_exponents(factors: Iterable[int], /) -> str: processed: list[str] = [] last_factor = 0 From 99ddc673e3e1eb8bcf420dd8cedf6221eefc26f5 Mon Sep 17 00:00:00 2001 From: Expertcoderz Date: Mon, 21 Apr 2025 04:16:59 +0000 Subject: [PATCH 3/4] Replace `Generator` in type hints with `Iterator` --- userland/core/io.py | 8 ++++---- userland/core/paths.py | 6 +++--- userland/utilities/cat.py | 12 ++++++------ userland/utilities/factor.py | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/userland/core/io.py b/userland/core/io.py index e891a74..8bb61f4 100644 --- a/userland/core/io.py +++ b/userland/core/io.py @@ -1,6 +1,6 @@ import os import sys -from typing import Any, Generator, IO +from typing import Any, IO, Iterator def perror(*errors: Any) -> None: @@ -10,12 +10,12 @@ def perror(*errors: Any) -> None: ) -def readwords_stdin() -> Generator[str]: +def readwords_stdin() -> Iterator[str]: for line in sys.stdin: yield from line.split() -def readwords_stdin_raw() -> Generator[bytes]: +def readwords_stdin_raw() -> Iterator[bytes]: for line in sys.stdin.buffer: yield from line.split() @@ -23,7 +23,7 @@ def readwords_stdin_raw() -> Generator[bytes]: def get_lines_by_delimiter[T: ( str, bytes, -)](stream: IO[T], delimiter: T) -> Generator[T]: +)](stream: IO[T], delimiter: T) -> Iterator[T]: joiner = type(delimiter)() line = [] diff --git a/userland/core/paths.py b/userland/core/paths.py index f6935c8..843a30c 100644 --- a/userland/core/paths.py +++ b/userland/core/paths.py @@ -1,18 +1,18 @@ import sys from pathlib import Path -from typing import Generator, Iterable, Literal +from typing import Iterable, Iterator, Literal def traverse_files( filenames: Iterable[str], recurse_mode: Literal["L", "H", "P"] | None = None, preserve_root: bool = False, -) -> Generator[Path | None]: +) -> Iterator[Path | None]: if not recurse_mode: yield from map(Path, filenames) return - def traverse(file: Path) -> Generator[Path]: + def traverse(file: Path) -> Iterator[Path]: for child in file.iterdir(): if child.is_dir(follow_symlinks=recurse_mode == "L"): yield from traverse(child) diff --git a/userland/utilities/cat.py b/userland/utilities/cat.py index 00dfa48..d9d021e 100644 --- a/userland/utilities/cat.py +++ b/userland/utilities/cat.py @@ -1,12 +1,12 @@ import itertools import sys from io import BufferedReader -from typing import Generator, Iterable +from typing import Iterable, Iterator from .. import core -def squeeze_blank_lines(stream: Iterable[bytes]) -> Generator[bytes]: +def squeeze_blank_lines(stream: Iterable[bytes]) -> Iterator[bytes]: was_blank = False for line in stream: @@ -19,12 +19,12 @@ def squeeze_blank_lines(stream: Iterable[bytes]) -> Generator[bytes]: was_blank = is_blank -def number_lines(stream: Iterable[bytes]) -> Generator[bytes]: +def number_lines(stream: Iterable[bytes]) -> Iterator[bytes]: for i, _ in enumerate(stream): yield f"{i + 1:>6} ".encode() -def number_nonblank_lines(stream: Iterable[bytes]) -> Generator[bytes]: +def number_nonblank_lines(stream: Iterable[bytes]) -> Iterator[bytes]: i = 1 for line in stream: if len(line) > 1: @@ -36,7 +36,7 @@ def number_nonblank_lines(stream: Iterable[bytes]) -> Generator[bytes]: def format_chars( data: bytes, show_ends: bool, show_tabs: bool, show_nonprinting: bool -) -> Generator[bytes]: +) -> Iterator[bytes]: for n in data: if n == ord(b"\n"): if show_ends: @@ -62,7 +62,7 @@ def format_chars( yield n.to_bytes() -def format_lines(stream: Iterable[bytes], *args) -> Generator[bytes]: +def format_lines(stream: Iterable[bytes], *args) -> Iterator[bytes]: for line in stream: yield b"".join(format_chars(line, *args)) diff --git a/userland/utilities/factor.py b/userland/utilities/factor.py index b1fa981..05fe067 100644 --- a/userland/utilities/factor.py +++ b/userland/utilities/factor.py @@ -1,5 +1,5 @@ import math -from typing import Generator, Iterable, cast +from typing import Iterable, Iterator, cast from .. import core @@ -50,7 +50,7 @@ def pollards_rho(n: int, /, step: int) -> int | None: def factorize( n: int, /, *, cache: dict[int, int] = {p: p for p in SMALL_PRIMES} -) -> Generator[int]: +) -> Iterator[int]: """ Generates prime factors of the integer n. From 3b0532727d0fd041b30ac1ef8fc49b5e71ffb2a7 Mon Sep 17 00:00:00 2001 From: Expertcoderz Date: Mon, 28 Apr 2025 05:23:10 +0000 Subject: [PATCH 4/4] Replace deprecated `from typing import Callable` --- userland/core/command.py | 2 +- userland/utilities/cut.py | 3 ++- userland/utilities/readlink.py | 3 ++- userland/utilities/truncate.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/userland/core/command.py b/userland/core/command.py index 1b75d39..d3b552e 100644 --- a/userland/core/command.py +++ b/userland/core/command.py @@ -1,6 +1,6 @@ import sys +from collections.abc import Callable from optparse import OptionParser, Values -from typing import Callable from .users import OptionParserUsersMixin diff --git a/userland/utilities/cut.py b/userland/utilities/cut.py index c7808d3..b123199 100644 --- a/userland/utilities/cut.py +++ b/userland/utilities/cut.py @@ -1,5 +1,6 @@ import sys -from typing import BinaryIO, Callable, Iterable, cast +from collections.abc import Callable +from typing import BinaryIO, Iterable, cast from .. import core diff --git a/userland/utilities/readlink.py b/userland/utilities/readlink.py index 0136a25..b7c6c5a 100644 --- a/userland/utilities/readlink.py +++ b/userland/utilities/readlink.py @@ -1,5 +1,6 @@ +from collections.abc import Callable from pathlib import Path -from typing import Callable, Literal +from typing import Literal from .. import core diff --git a/userland/utilities/truncate.py b/userland/utilities/truncate.py index d29d193..31fe41b 100644 --- a/userland/utilities/truncate.py +++ b/userland/utilities/truncate.py @@ -1,6 +1,6 @@ import operator +from collections.abc import Callable from pathlib import Path -from typing import Callable from tqdm import tqdm