Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-curtsies for openSUSE:Factory checked in at 2025-07-14 10:51:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-curtsies (Old) and /work/SRC/openSUSE:Factory/.python-curtsies.new.7373 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-curtsies" Mon Jul 14 10:51:43 2025 rev:17 rq:1292442 version:0.4.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-curtsies/python-curtsies.changes 2025-05-26 18:41:46.335135838 +0200 +++ /work/SRC/openSUSE:Factory/.python-curtsies.new.7373/python-curtsies.changes 2025-07-14 10:57:02.369906848 +0200 @@ -1,0 +2,7 @@ +Sat Jul 12 17:32:38 UTC 2025 - Dirk Müller <dmuel...@suse.com> + +- update to 0.4.3: + * Drop support for Python 3.7, 3.8, and 3.9. + * Add support for italic. + +------------------------------------------------------------------- Old: ---- curtsies-0.4.2.tar.gz New: ---- curtsies-0.4.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-curtsies.spec ++++++ --- /var/tmp/diff_new_pack.INBVhM/_old 2025-07-14 10:57:03.029934210 +0200 +++ /var/tmp/diff_new_pack.INBVhM/_new 2025-07-14 10:57:03.029934210 +0200 @@ -19,7 +19,7 @@ %define skip_python2 1 %define skip_python36 1 Name: python-curtsies -Version: 0.4.2 +Version: 0.4.3 Release: 0 Summary: Curses-like terminal wrapper, with colored strings! License: MIT ++++++ curtsies-0.4.2.tar.gz -> curtsies-0.4.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/PKG-INFO new/curtsies-0.4.3/PKG-INFO --- old/curtsies-0.4.2/PKG-INFO 2023-07-31 22:18:29.564929200 +0200 +++ new/curtsies-0.4.3/PKG-INFO 2025-06-05 08:33:12.418394000 +0200 @@ -1,6 +1,6 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: curtsies -Version: 0.4.2 +Version: 0.4.3 Summary: Curses-like terminal wrapper, with colored strings! Home-page: https://github.com/bpython/curtsies Author: Thomas Ballinger @@ -13,9 +13,12 @@ Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Requires-Python: >=3.7 +Requires-Python: >=3.10 Description-Content-Type: text/markdown License-File: LICENSE +Requires-Dist: blessed>=1.5 +Requires-Dist: cwcwidth +Dynamic: license-file [](https://readthedocs.org/projects/curtsies/?badge=latest)  diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/__init__.py new/curtsies-0.4.3/curtsies/__init__.py --- old/curtsies-0.4.2/curtsies/__init__.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/__init__.py 2025-06-05 08:33:08.000000000 +0200 @@ -1,5 +1,6 @@ """Terminal-formatted strings""" -__version__ = "0.4.2" + +__version__ = "0.4.3" from .window import FullscreenWindow, CursorAwareWindow from .input import Input diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/configfile_keynames.py new/curtsies-0.4.3/curtsies/configfile_keynames.py --- old/curtsies-0.4.2/curtsies/configfile_keynames.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/configfile_keynames.py 2025-06-05 08:33:08.000000000 +0200 @@ -15,7 +15,7 @@ class KeyMap: """Maps config file key syntax to Curtsies names""" - def __getitem__(self, key: str) -> Tuple[str, ...]: + def __getitem__(self, key: str) -> tuple[str, ...]: if not key: # Unbound key return () elif key in SPECIALS: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/curtsieskeys.py new/curtsies-0.4.3/curtsies/curtsieskeys.py --- old/curtsies-0.4.2/curtsies/curtsieskeys.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/curtsieskeys.py 2025-06-05 08:33:08.000000000 +0200 @@ -1,4 +1,5 @@ """All the key sequences""" + # If you add a binding, add something about your setup # if you can figure out why it's different diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/escseqparse.py new/curtsies-0.4.3/curtsies/escseqparse.py --- old/curtsies-0.4.2/curtsies/escseqparse.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/escseqparse.py 2025-06-05 08:33:08.000000000 +0200 @@ -11,15 +11,15 @@ from typing import ( List, - Mapping, Union, Tuple, - Match, cast, Dict, Any, Optional, ) +from collections.abc import Mapping +from re import Match import re @@ -34,14 +34,14 @@ ) -Token = Dict[str, Union[str, List[int]]] +Token = dict[str, Union[str, list[int]]] def remove_ansi(s: str) -> str: return re.sub(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]", "", s) -def parse(s: str) -> List[Union[str, Dict[str, Union[str, bool, None]]]]: +def parse(s: str) -> list[str | dict[str, str | bool | None]]: r""" Returns a list of strings or format dictionaries to describe the strings. @@ -52,7 +52,7 @@ >>> parse("\x1b[33m[\x1b[39m\x1b[33m]\x1b[39m\x1b[33m[\x1b[39m\x1b[33m]\x1b[39m\x1b[33m[\x1b[39m\x1b[33m]\x1b[39m\x1b[33m[\x1b[39m") [{'fg': 'yellow'}, '[', {'fg': None}, {'fg': 'yellow'}, ']', {'fg': None}, {'fg': 'yellow'}, '[', {'fg': None}, {'fg': 'yellow'}, ']', {'fg': None}, {'fg': 'yellow'}, '[', {'fg': None}, {'fg': 'yellow'}, ']', {'fg': None}, {'fg': 'yellow'}, '[', {'fg': None}] """ - stuff: List[Union[str, Dict[str, Union[str, bool, None]]]] = [] + stuff: list[str | dict[str, str | bool | None]] = [] rest = s while True: front, token, rest = peel_off_esc_code(rest) @@ -73,7 +73,7 @@ return stuff -def peel_off_esc_code(s: str) -> Tuple[str, Optional[Token], str]: +def peel_off_esc_code(s: str) -> tuple[str, Token | None, str]: r"""Returns processed text, the next token, and unprocessed text >>> front, d, rest = peel_off_esc_code('some[2Astuff') @@ -114,7 +114,7 @@ m = None if m: - d: Dict[str, Any] = m.groupdict() + d: dict[str, Any] = m.groupdict() del d["front"] del d["rest"] if "numbers" in d and all(d["numbers"].split(";")): @@ -125,12 +125,12 @@ return s, None, "" -def token_type(info: Token) -> Optional[List[Dict[str, Union[str, bool, None]]]]: +def token_type(info: Token) -> list[dict[str, str | bool | None]] | None: if info["command"] == "m": # The default action for ESC[m is to act like ESC[0m # Ref: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes - values = cast(List[int], info["numbers"]) if len(info["numbers"]) else [0] - tokens: List[Dict[str, Union[str, bool, None]]] = [] + values = cast(list[int], info["numbers"]) if len(info["numbers"]) else [0] + tokens: list[dict[str, str | bool | None]] = [] for value in values: if value in FG_NUMBER_TO_COLOR: tokens.append({"fg": FG_NUMBER_TO_COLOR[value]}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/events.py new/curtsies-0.4.3/curtsies/events.py --- old/curtsies-0.4.2/curtsies/events.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/events.py 2025-06-05 08:33:08.000000000 +0200 @@ -1,9 +1,11 @@ """Events for keystrokes and other input events""" + import codecs import itertools import sys from enum import Enum, auto -from typing import Optional, List, Sequence, Union +from typing import Optional, List, Union +from collections.abc import Sequence from .termhelpers import Termmode from .curtsieskeys import CURTSIES_NAMES as special_curtsies_names @@ -103,9 +105,7 @@ class WindowChangeEvent(Event): - def __init__( - self, rows: int, columns: int, cursor_dy: Optional[int] = None - ) -> None: + def __init__(self, rows: int, columns: int, cursor_dy: int | None = None) -> None: self.rows = rows self.columns = columns self.cursor_dy = cursor_dy @@ -143,7 +143,7 @@ """ def __init__(self) -> None: - self.events: List[str] = [] + self.events: list[str] = [] def __repr__(self) -> str: return "<Paste Event with data: %r>" % self.events @@ -200,7 +200,7 @@ encoding: str, keynames: Keynames = Keynames.CURTSIES, full: bool = False, -) -> Optional[str]: +) -> str | None: """Return key pressed from bytes_ or None Return a key name or None meaning it's an incomplete sequence of bytes @@ -279,7 +279,7 @@ ) -def pp_event(seq: Union[Event, str]) -> Union[str, bytes]: +def pp_event(seq: Event | str) -> str | bytes: """Returns pretty representation of an Event or keypress""" if isinstance(seq, Event): @@ -288,7 +288,7 @@ # Get the original sequence back if seq is a pretty name already rev_curses = {v: k for k, v in CURSES_NAMES.items()} rev_curtsies = {v: k for k, v in CURTSIES_NAMES.items()} - bytes_seq: Optional[bytes] = None + bytes_seq: bytes | None = None if seq in rev_curses: bytes_seq = rev_curses[seq] elif seq in rev_curtsies: @@ -301,7 +301,7 @@ return repr(seq).lstrip("u")[1:-1] -def curtsies_name(seq: bytes) -> Union[str, bytes]: +def curtsies_name(seq: bytes) -> str | bytes: return CURTSIES_NAMES.get(seq, seq) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/fmtfuncs.py new/curtsies-0.4.3/curtsies/fmtfuncs.py --- old/curtsies-0.4.2/curtsies/fmtfuncs.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/fmtfuncs.py 2025-06-05 08:33:08.000000000 +0200 @@ -22,6 +22,7 @@ bold = _partial(fmtstr, style="bold") dark = _partial(fmtstr, style="dark") +italic = _partial(fmtstr, style="italic") underline = _partial(fmtstr, style="underline") blink = _partial(fmtstr, style="blink") invert = _partial(fmtstr, style="invert") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/formatstring.py new/curtsies-0.4.3/curtsies/formatstring.py --- old/curtsies-0.4.2/curtsies/formatstring.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/formatstring.py 2025-06-05 08:33:08.000000000 +0200 @@ -21,27 +21,19 @@ import re from cwcwidth import wcswidth, wcwidth +from functools import cached_property from itertools import chain from typing import ( Any, - Callable, Dict, - Iterable, - Iterator, List, - Mapping, - MutableMapping, Optional, Tuple, Union, cast, no_type_check, ) - -try: - from functools import cached_property -except ImportError: - from backports.cached_property import cached_property # type: ignore +from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping from .escseqparse import parse, remove_ansi from .termformatconstants import ( @@ -59,27 +51,28 @@ one_arg_xforms: Mapping[str, Callable[[str], str]] = { "bold": lambda s: seq(STYLES["bold"]) + s + seq(RESET_ALL), "dark": lambda s: seq(STYLES["dark"]) + s + seq(RESET_ALL), + "italic": lambda s: seq(STYLES["italic"]) + s + seq(RESET_ALL), "underline": lambda s: seq(STYLES["underline"]) + s + seq(RESET_ALL), "blink": lambda s: seq(STYLES["blink"]) + s + seq(RESET_ALL), "invert": lambda s: seq(STYLES["invert"]) + s + seq(RESET_ALL), } two_arg_xforms: Mapping[str, Callable[[str, int], str]] = { - "fg": lambda s, v: "{}{}{}".format(seq(v), s, seq(RESET_FG)), + "fg": lambda s, v: f"{seq(v)}{s}{seq(RESET_FG)}", "bg": lambda s, v: seq(v) + s + seq(RESET_BG), } -class FrozenAttributes(Dict[str, Union[int, bool]]): +class FrozenAttributes(dict[str, Union[int, bool]]): """Immutable dictionary class for format string attributes""" - def __setitem__(self, key: str, value: Union[int, bool]) -> None: + def __setitem__(self, key: str, value: int | bool) -> None: raise Exception("Cannot change value.") def update(self, *args: Any, **kwds: Any) -> None: raise Exception("Cannot change value.") - def extend(self, dictlike: Mapping[str, Union[int, bool]]) -> "FrozenAttributes": + def extend(self, dictlike: Mapping[str, int | bool]) -> "FrozenAttributes": return FrozenAttributes(chain(self.items(), dictlike.items())) def remove(self, *keys: str) -> "FrozenAttributes": @@ -92,9 +85,11 @@ Does not work for dicts with unicode strings as values.""" inner = ", ".join( "{}: {}".format( - repr(k)[1:] - if repr(k).startswith("u'") or repr(k).startswith('u"') - else repr(k), + ( + repr(k)[1:] + if repr(k).startswith("u'") or repr(k).startswith('u"') + else repr(k) + ), v, ) for k, v in sorted(d.items()) @@ -107,9 +102,7 @@ Subject to change, not part of the API""" - def __init__( - self, string: str, atts: Optional[Mapping[str, Union[int, bool]]] = None - ): + def __init__(self, string: str, atts: Mapping[str, int | bool] | None = None): if not isinstance(string, str): raise ValueError("unicode string required, got %r" % string) self._s = string @@ -216,7 +209,7 @@ divides.append(divides[-1] + wcwidth(c)) self.divides = divides - def request(self, max_width: int) -> Optional[Tuple[int, Chunk]]: + def request(self, max_width: int) -> tuple[int, Chunk] | None: """Requests a sub-chunk of max_width or shorter. Returns None if no chunks left.""" if max_width < 1: raise ValueError("requires positive integer max_width") @@ -283,10 +276,10 @@ self.chunks = list(components) # caching these leads to a significant speedup - self._unicode: Optional[str] = None - self._len: Optional[int] = None - self._s: Optional[str] = None - self._width: Optional[int] = None + self._unicode: str | None = None + self._len: int | None = None + self._s: str | None = None + self._width: int | None = None @staticmethod def from_str(s: str) -> "FmtStr": @@ -353,7 +346,7 @@ return result def splice( - self, new_str: Union[str, "FmtStr"], start: int, end: Optional[int] = None + self, new_str: Union[str, "FmtStr"], start: int, end: int | None = None ) -> "FmtStr": """Returns a new FmtStr with the input string spliced into the the original FmtStr at start and end. @@ -405,7 +398,7 @@ def append(self, string: Union[str, "FmtStr"]) -> "FmtStr": return self.splice(string, len(self.s)) - def copy_with_new_atts(self, **attributes: Union[bool, int]) -> "FmtStr": + def copy_with_new_atts(self, **attributes: bool | int) -> "FmtStr": """Returns a new FmtStr with the same content but new formatting""" return FmtStr( @@ -414,8 +407,8 @@ def join(self, iterable: Iterable[Union[str, "FmtStr"]]) -> "FmtStr": """Joins an iterable yielding strings or FmtStrs with self as separator""" - before: List[Chunk] = [] - chunks: List[Chunk] = [] + before: list[Chunk] = [] + chunks: list[Chunk] = [] for s in iterable: chunks.extend(before) before = self.chunks @@ -430,10 +423,10 @@ # TODO make this split work like str.split def split( self, - sep: Optional[str] = None, - maxsplit: Optional[int] = None, + sep: str | None = None, + maxsplit: int | None = None, regex: bool = False, - ) -> List["FmtStr"]: + ) -> list["FmtStr"]: """Split based on separator, optionally using a regex. Capture groups are ignored in regex, the whole pattern is matched @@ -454,7 +447,7 @@ ) ] - def splitlines(self, keepends: bool = False) -> List["FmtStr"]: + def splitlines(self, keepends: bool = False) -> list["FmtStr"]: """Return a list of lines, split on newline characters, include line boundaries, if keepends is true.""" lines = self.split("\n") @@ -466,7 +459,7 @@ # proxying to the string via __getattr__ is insufficient # because we shouldn't drop foreground or formatting info - def ljust(self, width: int, fillchar: Optional[str] = None) -> "FmtStr": + def ljust(self, width: int, fillchar: str | None = None) -> "FmtStr": """S.ljust(width[, fillchar]) -> string If a fillchar is provided, less formatting information will be preserved @@ -481,7 +474,7 @@ uniform = self.new_with_atts_removed("bg") return uniform + fmtstr(to_add, **self.shared_atts) if to_add else uniform - def rjust(self, width: int, fillchar: Optional[str] = None) -> "FmtStr": + def rjust(self, width: int, fillchar: str | None = None) -> "FmtStr": """S.rjust(width[, fillchar]) -> string If a fillchar is provided, less formatting information will be preserved @@ -561,7 +554,7 @@ # TODO ensure empty FmtStr isn't a problem @property - def shared_atts(self) -> Dict[str, Union[int, bool]]: + def shared_atts(self) -> dict[str, int | bool]: """Gets atts shared among all nonzero length component Chunks""" # TODO cache this, could get ugly for large FmtStrs atts = {} @@ -583,7 +576,7 @@ return result @no_type_check - def __getattr__(self, att): + def __getattr__(self, att: str): # thanks to @aerenchyma/@jczett if not hasattr(self.s, att): raise AttributeError(f"No attribute {att!r}") @@ -601,7 +594,7 @@ return func_help @property - def divides(self) -> List[int]: + def divides(self) -> list[int]: """List of indices of divisions between the constituent chunks.""" acc = [0] for s in self.chunks: @@ -615,7 +608,7 @@ self._s = "".join(fs.s for fs in self.chunks) return self._s - def __getitem__(self, index: Union[int, slice]) -> "FmtStr": + def __getitem__(self, index: int | slice) -> "FmtStr": index = normalize_slice(len(self), index) counter = 0 parts = [] @@ -635,7 +628,7 @@ break return FmtStr(*parts) if parts else fmtstr("") - def width_aware_slice(self, index: Union[int, slice]) -> "FmtStr": + def width_aware_slice(self, index: int | slice) -> "FmtStr": """Slice based on the number of columns it would take to display the substring.""" if wcswidth(self.s, None) == -1: raise ValueError("bad values for width aware slicing") @@ -691,7 +684,7 @@ if chunks_of_line: yield FmtStr(*chunks_of_line) - def _getitem_normalized(self, index: Union[int, slice]) -> "FmtStr": + def _getitem_normalized(self, index: int | slice) -> "FmtStr": """Builds the more compact fmtstrs by using fromstr( of the control sequences)""" index = normalize_slice(len(self), index) counter = 0 @@ -753,7 +746,7 @@ return "".join(new_chunk_chars) -def linesplit(string: Union[str, FmtStr], columns: int) -> List[FmtStr]: +def linesplit(string: str | FmtStr, columns: int) -> list[FmtStr]: """Returns a list of lines, split on the last possible space of each line. Split spaces will be removed. Whitespaces will be normalized to one space. @@ -798,7 +791,7 @@ return lines -def normalize_slice(length: int, index: Union[int, slice]) -> slice: +def normalize_slice(length: int, index: int | slice) -> slice: "Fill in the Nones in a slice." is_int = False if isinstance(index, int): @@ -821,9 +814,9 @@ def parse_args( - args: Tuple[str, ...], - kwargs: MutableMapping[str, Union[int, bool, str]], -) -> Mapping[str, Union[int, bool]]: + args: tuple[str, ...], + kwargs: MutableMapping[str, int | bool | str], +) -> MutableMapping[str, int | bool]: """Returns a kwargs dictionary by turning args into kwargs""" if "style" in kwargs: args += (cast(str, kwargs["style"]),) @@ -859,7 +852,7 @@ return cast(MutableMapping[str, Union[int, bool]], kwargs) -def fmtstr(string: Union[str, FmtStr], *args: Any, **kwargs: Any) -> FmtStr: +def fmtstr(string: str | FmtStr, *args: Any, **kwargs: Any) -> FmtStr: """ Convenience function for creating a FmtStr diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/formatstringarray.py new/curtsies-0.4.3/curtsies/formatstringarray.py --- old/curtsies-0.4.2/curtsies/formatstringarray.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/formatstringarray.py 2025-06-05 08:33:08.000000000 +0200 @@ -34,12 +34,12 @@ Optional, Union, List, - Sequence, overload, Tuple, cast, no_type_check, ) +from collections.abc import Sequence logger = logging.getLogger(__name__) @@ -60,7 +60,7 @@ self, num_rows: int, num_columns: int, *args: Any, **kwargs: Any ) -> None: self.saved_args, self.saved_kwargs = args, kwargs - self.rows: List[FmtStr] = [fmtstr("", *args, **kwargs) for _ in range(num_rows)] + self.rows: list[FmtStr] = [fmtstr("", *args, **kwargs) for _ in range(num_rows)] self.num_columns = num_columns @overload @@ -68,18 +68,16 @@ pass @overload - def __getitem__(self, slicetuple: slice) -> List[FmtStr]: + def __getitem__(self, slicetuple: slice) -> list[FmtStr]: pass @overload - def __getitem__( - self, slicetuple: Tuple[Union[slice, int], Union[slice, int]] - ) -> List[FmtStr]: + def __getitem__(self, slicetuple: tuple[slice | int, slice | int]) -> list[FmtStr]: pass def __getitem__( - self, slicetuple: Union[int, slice, Tuple[Union[int, slice], Union[int, slice]]] - ) -> Union[FmtStr, List[FmtStr]]: + self, slicetuple: int | slice | tuple[int | slice, int | slice] + ) -> FmtStr | list[FmtStr]: if isinstance(slicetuple, int): if slicetuple < 0: slicetuple = len(self.rows) - slicetuple @@ -99,7 +97,7 @@ return len(self.rows) @property - def shape(self) -> Tuple[int, int]: + def shape(self) -> tuple[int, int]: """Tuple of (len(rows, len(num_columns)) numpy-style shape""" return len(self.rows), self.num_columns @@ -250,8 +248,8 @@ def fsarray( - strings: Sequence[Union[FmtStr, str]], - width: Optional[int] = None, + strings: Sequence[FmtStr | str], + width: int | None = None, *args: Any, **kwargs: Any, ) -> FSArray: @@ -283,7 +281,7 @@ return arr -def simple_format(x: Union[FSArray, Sequence[FmtStr]]) -> str: +def simple_format(x: FSArray | Sequence[FmtStr]) -> str: return "\n".join(str(l) for l in x) @@ -301,7 +299,7 @@ def assertFSArraysEqualIgnoringFormatting(a: FSArray, b: FSArray) -> None: """Also accepts arrays of strings""" - assert len(a) == len(b), "fsarray heights do not match: %s %s \n%s \n%s" % ( + assert len(a) == len(b), "fsarray heights do not match: {} {} \n{} \n{}".format( len(a), len(b), simple_format(a), @@ -310,7 +308,7 @@ for i, (a_row, b_row) in enumerate(zip(a, b)): a_row = a_row.s if isinstance(a_row, FmtStr) else a_row b_row = b_row.s if isinstance(b_row, FmtStr) else b_row - assert a_row == b_row, "FSArrays differ first on line %s:\n%s" % ( + assert a_row == b_row, "FSArrays differ first on line {}:\n{}".format( i, FSArray.diff(a, b, ignore_formatting=True), ) @@ -319,7 +317,7 @@ if __name__ == "__main__": a = FSArray(3, 14, bg="blue") a[0:2, 5:11] = cast( - Tuple[FmtStr, ...], + tuple[FmtStr, ...], (fmtstr("hey", "on_blue") + " " + fmtstr("yo", "on_red"), fmtstr("qwe qw")), ) a.dumb_display() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/input.py new/curtsies-0.4.3/curtsies/input.py --- old/curtsies-0.4.2/curtsies/input.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/input.py 2025-06-05 08:33:08.000000000 +0200 @@ -13,7 +13,6 @@ from . import events from typing import ( - Callable, ContextManager, Type, TextIO, @@ -24,6 +23,7 @@ Tuple, Any, ) +from collections.abc import Callable from types import TracebackType, FrameType @@ -47,9 +47,9 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: signal.signal(signal.SIGINT, self.orig_sigint_handler) @@ -57,11 +57,13 @@ class Input(ContextManager["Input"]): """Keypress and control event generator""" + in_stream: TextIO + def __init__( self, - in_stream: Optional[TextIO] = None, - keynames: Union[events.Keynames, str] = events.Keynames.CURTSIES, - paste_threshold: Optional[int] = events.MAX_KEYPRESS_SIZE + 1, + in_stream: TextIO | None = None, + keynames: events.Keynames | str = events.Keynames.CURTSIES, + paste_threshold: int | None = events.MAX_KEYPRESS_SIZE + 1, sigint_event: bool = False, disable_terminal_start_stop: bool = False, ) -> None: @@ -82,8 +84,9 @@ """ if in_stream is None: in_stream = sys.__stdin__ + assert in_stream is not None self.in_stream = in_stream - self.unprocessed_bytes: List[bytes] = [] # leftover from stdin, unprocessed yet + self.unprocessed_bytes: list[bytes] = [] # leftover from stdin, unprocessed yet if isinstance(keynames, str): # TODO: Remove this block with the next API breaking release. if keynames == "curtsies": @@ -99,14 +102,14 @@ self.paste_threshold = paste_threshold self.sigint_event = sigint_event self.disable_terminal_start_stop = disable_terminal_start_stop - self.sigints: List[events.SigIntEvent] = [] - self.wakeup_read_fd: Optional[int] = None - self.wakeup_write_fd: Optional[int] = None - - self.readers: List[int] = [] - self.queued_interrupting_events: List[Union[events.Event, str]] = [] - self.queued_events: List[Union[events.Event, None]] = [] - self.queued_scheduled_events: List[Tuple[float, events.ScheduledEvent]] = [] + self.sigints: list[events.SigIntEvent] = [] + self.wakeup_read_fd: int | None = None + self.wakeup_write_fd: int | None = None + + self.readers: list[int] = [] + self.queued_interrupting_events: list[events.Event | str] = [] + self.queued_events: list[events.Event | None] = [] + self.queued_scheduled_events: list[tuple[float, events.ScheduledEvent]] = [] # prospective: this could be useful for an external select loop def fileno(self) -> int: @@ -118,7 +121,7 @@ if self.disable_terminal_start_stop: attrs = termios.tcgetattr(self.in_stream) - tty_cc = cast(List[Union[bytes, int]], attrs[-1]) + tty_cc = cast(list[Union[bytes, int]], attrs[-1]) tty_cc[termios.VSTOP] = 0 # Ctrl-s tty_cc[termios.VSTART] = 0 # Ctrl-q termios.tcsetattr(self.in_stream, termios.TCSANOW, attrs) @@ -126,7 +129,7 @@ if sys.platform == "darwin": attrs = termios.tcgetattr(self.in_stream) VDSUSP = termios.VSUSP + 1 - tty_cc = cast(List[Union[bytes, int]], attrs[-1]) + tty_cc = cast(list[Union[bytes, int]], attrs[-1]) tty_cc[VDSUSP] = 0 termios.tcsetattr(self.in_stream, termios.TCSANOW, attrs) @@ -145,9 +148,9 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: if ( self.sigint_event @@ -164,14 +167,14 @@ termios.tcsetattr(self.in_stream, termios.TCSANOW, self.original_stty) def sigint_handler( - self, signum: Union[signal.Signals, int], frame: Optional[FrameType] + self, signum: signal.Signals | int, frame: FrameType | None ) -> None: self.sigints.append(events.SigIntEvent()) def __iter__(self) -> "Input": return self - def __next__(self) -> Union[None, str, events.Event]: + def __next__(self) -> None | str | events.Event: return self.send(None) def unget_bytes(self, string: bytes) -> None: @@ -183,8 +186,8 @@ self.unprocessed_bytes.extend(string[i : i + 1] for i in range(len(string))) def _wait_for_read_ready_or_timeout( - self, timeout: Union[float, int, None] - ) -> Tuple[bool, Optional[Union[events.Event, str]]]: + self, timeout: float | int | None + ) -> tuple[bool, events.Event | str | None]: """Returns tuple of whether stdin is ready to read and an event. If an event is returned, that event is more pressing than reading @@ -229,9 +232,7 @@ if remaining_timeout is not None: remaining_timeout = max(remaining_timeout - (time.time() - t0), 0) - def send( - self, timeout: Optional[Union[float, None]] = None - ) -> Union[None, str, events.Event]: + def send(self, timeout: float | None | None = None) -> None | str | events.Event: """Returns an event or None if no events occur before timeout.""" if self.sigint_event and is_main_thread(): with ReplacedSigIntHandler(self.sigint_handler): @@ -239,8 +240,8 @@ else: return self._send(timeout) - def _send(self, timeout: Union[float, int, None]) -> Union[None, str, events.Event]: - def find_key() -> Optional[str]: + def _send(self, timeout: float | int | None) -> None | str | events.Event: + def find_key() -> str | None: """Returns keypress identified by adding unprocessed bytes or None""" current_bytes = [] while self.unprocessed_bytes: @@ -340,7 +341,7 @@ return 0 def event_trigger( - self, event_type: Union[Type[events.Event], Callable[..., None]] + self, event_type: type[events.Event] | Callable[..., None] ) -> Callable[..., None]: """Returns a callback that creates events. @@ -353,7 +354,7 @@ return callback def scheduled_event_trigger( - self, event_type: Type[events.ScheduledEvent] + self, event_type: type[events.ScheduledEvent] ) -> Callable[[float], None]: """Returns a callback that schedules events for the future. @@ -366,7 +367,7 @@ return callback def threadsafe_event_trigger( - self, event_type: Union[Type[events.Event], Callable[..., None]] + self, event_type: type[events.Event] | Callable[..., None] ) -> Callable[..., None]: """Returns a callback to creates events, interrupting current event requests. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/termformatconstants.py new/curtsies-0.4.3/curtsies/termformatconstants.py --- old/curtsies-0.4.2/curtsies/termformatconstants.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/termformatconstants.py 2025-06-05 08:33:08.000000000 +0200 @@ -1,12 +1,12 @@ """Constants for terminal formatting""" -from typing import Mapping +from collections.abc import Mapping colors = "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray" FG_COLORS: Mapping[str, int] = dict(zip(colors, range(30, 38))) BG_COLORS: Mapping[str, int] = dict(zip(colors, range(40, 48))) STYLES: Mapping[str, int] = dict( - zip(("bold", "dark", "underline", "blink", "invert"), (1, 2, 4, 5, 7)) + zip(("bold", "dark", "italic", "underline", "blink", "invert"), (1, 2, 3, 4, 5, 7)) ) FG_NUMBER_TO_COLOR: Mapping[int, str] = dict(zip(FG_COLORS.values(), FG_COLORS.keys())) BG_NUMBER_TO_COLOR: Mapping[int, str] = dict(zip(BG_COLORS.values(), BG_COLORS.keys())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/termhelpers.py new/curtsies-0.4.3/curtsies/termhelpers.py --- old/curtsies-0.4.2/curtsies/termhelpers.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/termhelpers.py 2025-06-05 08:33:08.000000000 +0200 @@ -6,7 +6,7 @@ from typing import IO, ContextManager, Type, List, Union, Optional from types import TracebackType -_Attr = List[Union[int, List[Union[bytes, int]]]] +_Attr = list[Union[int, list[Union[bytes, int]]]] class Nonblocking(ContextManager): @@ -24,9 +24,9 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: fcntl.fcntl(self.fd, fcntl.F_SETFL, self.orig_fl) @@ -42,9 +42,9 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty) @@ -60,8 +60,8 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies/window.py new/curtsies-0.4.3/curtsies/window.py --- old/curtsies-0.4.2/curtsies/window.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/curtsies/window.py 2025-06-05 08:33:08.000000000 +0200 @@ -7,16 +7,15 @@ Optional, IO, Dict, - Sequence, TypeVar, Type, Tuple, - Callable, cast, TextIO, Union, List, ) +from collections.abc import Callable, Sequence from types import TracebackType import logging @@ -36,18 +35,17 @@ class BaseWindow(ContextManager): - def __init__( - self, out_stream: Optional[IO] = None, hide_cursor: bool = True - ) -> None: + def __init__(self, out_stream: IO | None = None, hide_cursor: bool = True) -> None: logger.debug("-------initializing Window object %r------" % self) if out_stream is None: out_stream = sys.__stdout__ + assert out_stream is not None self.t = blessed.Terminal(stream=out_stream, force_styling=True) self.out_stream = out_stream self.hide_cursor = hide_cursor - self._last_lines_by_row: Dict[int, Optional[FmtStr]] = {} - self._last_rendered_width: Optional[int] = None - self._last_rendered_height: Optional[int] = None + self._last_lines_by_row: dict[int, FmtStr | None] = {} + self._last_rendered_width: int | None = None + self._last_rendered_height: int | None = None def scroll_down(self) -> None: logger.debug("sending scroll down message w/ cursor on bottom line") @@ -68,9 +66,9 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: logger.debug("running BaseWindow.__exit__") if self.hide_cursor: @@ -84,11 +82,11 @@ self._last_rendered_height = height def render_to_terminal( - self, array: Union[FSArray, List[FmtStr]], cursor_pos: Tuple[int, int] = (0, 0) - ) -> Optional[int]: + self, array: FSArray | list[FmtStr], cursor_pos: tuple[int, int] = (0, 0) + ) -> int | None: raise NotImplementedError - def get_term_hw(self) -> Tuple[int, int]: + def get_term_hw(self) -> tuple[int, int]: """Returns current terminal height and width""" return self.t.height, self.t.width @@ -144,9 +142,7 @@ its out_stream; cached writes will be inaccurate. """ - def __init__( - self, out_stream: Optional[IO] = None, hide_cursor: bool = True - ) -> None: + def __init__(self, out_stream: IO | None = None, hide_cursor: bool = True) -> None: """Constructs a FullscreenWindow Args: @@ -162,15 +158,15 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: self.fullscreen_ctx.__exit__(type, value, traceback) super().__exit__(type, value, traceback) def render_to_terminal( - self, array: Union[FSArray, List[FmtStr]], cursor_pos: Tuple[int, int] = (0, 0) + self, array: FSArray | list[FmtStr], cursor_pos: tuple[int, int] = (0, 0) ) -> None: """Renders array to terminal and places (0-indexed) cursor @@ -197,7 +193,7 @@ if height != self._last_rendered_height or width != self._last_rendered_width: self.on_terminal_size_change(height, width) - current_lines_by_row: Dict[int, Optional[FmtStr]] = {} + current_lines_by_row: dict[int, FmtStr | None] = {} # rows which we have content for and don't require scrolling for row, line in enumerate(array): @@ -241,13 +237,15 @@ Only use the render_to_terminal interface for moving the cursor. """ + in_stream: TextIO + def __init__( self, - out_stream: Optional[IO] = None, - in_stream: Optional[IO] = None, + out_stream: TextIO | None = None, + in_stream: TextIO | None = None, keep_last_line: bool = False, hide_cursor: bool = True, - extra_bytes_callback: Optional[Callable[[bytes], None]] = None, + extra_bytes_callback: Callable[[bytes], None] | None = None, ): """Constructs a CursorAwareWindow @@ -264,13 +262,14 @@ super().__init__(out_stream=out_stream, hide_cursor=hide_cursor) if in_stream is None: in_stream = sys.__stdin__ + assert in_stream is not None self.in_stream = in_stream # whether we can use blessed to handle some operations self._use_blessed = ( self.out_stream == sys.__stdout__ and self.in_stream == sys.__stdin__ ) - self._last_cursor_column: Optional[int] = None - self._last_cursor_row: Optional[int] = None + self._last_cursor_column: int | None = None + self._last_cursor_row: int | None = None self.keep_last_line = keep_last_line self.extra_bytes_callback = extra_bytes_callback @@ -292,9 +291,9 @@ def __exit__( self, - type: Optional[Type[BaseException]] = None, - value: Optional[BaseException] = None, - traceback: Optional[TracebackType] = None, + type: type[BaseException] | None = None, + value: BaseException | None = None, + traceback: TracebackType | None = None, ) -> None: if self.keep_last_line: # just moves cursor down if not on last line @@ -306,7 +305,7 @@ self.cbreak.__exit__(type, value, traceback) super().__exit__(type, value, traceback) - def get_cursor_position(self) -> Tuple[int, int]: + def get_cursor_position(self) -> tuple[int, int]: """Returns the terminal (row, column) of the cursor 0-indexed, like blessed cursor positions""" @@ -426,8 +425,8 @@ def render_to_terminal( self, - array: Union[FSArray, Sequence[FmtStr]], - cursor_pos: Tuple[int, int] = (0, 0), + array: FSArray | Sequence[FmtStr], + cursor_pos: tuple[int, int] = (0, 0), ) -> int: """Renders array to terminal, returns the number of lines scrolled offscreen @@ -458,7 +457,7 @@ if height != self._last_rendered_height or width != self._last_rendered_width: self.on_terminal_size_change(height, width) - current_lines_by_row: Dict[int, Optional[FmtStr]] = {} + current_lines_by_row: dict[int, FmtStr | None] = {} rows_for_use = list(range(self.top_usable_row, height)) # rows which we have content for and don't require scrolling @@ -527,9 +526,7 @@ if c == "": sys.exit() # same as raise SystemExit() elif c == "h": - a: Union[List[FmtStr], FSArray] = w.array_from_text( - "a for small array" - ) + a: list[FmtStr] | FSArray = w.array_from_text("a for small array") elif c == "a": a = [fmtstr(c * columns) for _ in range(rows)] elif c == "s": diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies.egg-info/PKG-INFO new/curtsies-0.4.3/curtsies.egg-info/PKG-INFO --- old/curtsies-0.4.2/curtsies.egg-info/PKG-INFO 2023-07-31 22:18:29.000000000 +0200 +++ new/curtsies-0.4.3/curtsies.egg-info/PKG-INFO 2025-06-05 08:33:12.000000000 +0200 @@ -1,6 +1,6 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: curtsies -Version: 0.4.2 +Version: 0.4.3 Summary: Curses-like terminal wrapper, with colored strings! Home-page: https://github.com/bpython/curtsies Author: Thomas Ballinger @@ -13,9 +13,12 @@ Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Requires-Python: >=3.7 +Requires-Python: >=3.10 Description-Content-Type: text/markdown License-File: LICENSE +Requires-Dist: blessed>=1.5 +Requires-Dist: cwcwidth +Dynamic: license-file [](https://readthedocs.org/projects/curtsies/?badge=latest)  diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies.egg-info/SOURCES.txt new/curtsies-0.4.3/curtsies.egg-info/SOURCES.txt --- old/curtsies-0.4.2/curtsies.egg-info/SOURCES.txt 2023-07-31 22:18:29.000000000 +0200 +++ new/curtsies-0.4.3/curtsies.egg-info/SOURCES.txt 2025-06-05 08:33:12.000000000 +0200 @@ -3,7 +3,6 @@ README.md pyproject.toml setup.cfg -setup.py curtsies/__init__.py curtsies/configfile_keynames.py curtsies/curtsieskeys.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/curtsies.egg-info/requires.txt new/curtsies-0.4.3/curtsies.egg-info/requires.txt --- old/curtsies-0.4.2/curtsies.egg-info/requires.txt 2023-07-31 22:18:29.000000000 +0200 +++ new/curtsies-0.4.3/curtsies.egg-info/requires.txt 2025-06-05 08:33:12.000000000 +0200 @@ -1,5 +1,2 @@ blessed>=1.5 cwcwidth - -[:python_version < "3.8"] -backports.cached-property diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/examples/demo_fullscreen_with_input.py new/curtsies-0.4.3/examples/demo_fullscreen_with_input.py --- old/curtsies-0.4.2/examples/demo_fullscreen_with_input.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/examples/demo_fullscreen_with_input.py 2025-06-05 08:33:08.000000000 +0200 @@ -10,7 +10,7 @@ print('this should be just off-screen') w = FullscreenWindow(sys.stdout) def sigwinch_handler(signum, frame): - print('sigwinch! Changed from {!r} to {!r}'.format((rows, columns), (w.height, w.width))) + print(f'sigwinch! Changed from {(rows, columns)!r} to {(w.height, w.width)!r}') signal.signal(signal.SIGWINCH, sigwinch_handler) with w: with Cbreak(sys.stdin): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/examples/demo_scrolling.py new/curtsies-0.4.3/examples/demo_scrolling.py --- old/curtsies-0.4.2/examples/demo_scrolling.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/examples/demo_scrolling.py 2025-06-05 08:33:08.000000000 +0200 @@ -17,7 +17,7 @@ dy = w.get_cursor_vertical_diff() old_rows, old_columns = rows, columns rows, columns = w.height, w.width - print('sigwinch! Changed from {!r} to {!r}'.format((old_rows, old_columns), (rows, columns))) + print(f'sigwinch! Changed from {(old_rows, old_columns)!r} to {(rows, columns)!r}') print('cursor moved %d lines down' % dy) w.write(w.t.move_up) w.write(w.t.move_up) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/pyproject.toml new/curtsies-0.4.3/pyproject.toml --- old/curtsies-0.4.2/pyproject.toml 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/pyproject.toml 2025-06-05 08:33:08.000000000 +0200 @@ -1,12 +1,12 @@ [build-system] requires = [ - "setuptools >= 43", + "setuptools >= 46.4.0", ] build-backend = "setuptools.build_meta" [tool.black] line-length = 88 -target_version = ["py36"] +target_version = ["py310"] exclude = ''' ( /( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/setup.cfg new/curtsies-0.4.3/setup.cfg --- old/curtsies-0.4.2/setup.cfg 2023-07-31 22:18:29.568929400 +0200 +++ new/curtsies-0.4.3/setup.cfg 2025-06-05 08:33:12.419394300 +0200 @@ -1,5 +1,6 @@ [metadata] name = curtsies +version = attr: curtsies.__version__ description = Curses-like terminal wrapper, with colored strings! long_description = file: README.md, long_description_content_type = text/markdown @@ -18,13 +19,12 @@ Programming Language :: Python :: 3 [options] -python_requires = >=3.7 +python_requires = >=3.10 zip_safe = False packages = curtsies install_requires = blessed>=1.5 cwcwidth - backports.cached-property; python_version < "3.8" tests_require = pyte pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/setup.py new/curtsies-0.4.3/setup.py --- old/curtsies-0.4.2/setup.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/setup.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,16 +0,0 @@ -from setuptools import setup -import ast -import os - - -def version(): - """Return version string.""" - with open(os.path.join("curtsies", "__init__.py")) as input_file: - for line in input_file: - if line.startswith("__version__"): - return ast.parse(line).body[0].value.s - - -setup( - version=version(), -) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/curtsies-0.4.2/tests/test_terminal.py new/curtsies-0.4.3/tests/test_terminal.py --- old/curtsies-0.4.2/tests/test_terminal.py 2023-07-31 22:18:21.000000000 +0200 +++ new/curtsies-0.4.3/tests/test_terminal.py 2025-06-05 08:33:08.000000000 +0200 @@ -63,11 +63,7 @@ to.write(event.upper() + " ") to.write("; ".join(map(repr, args))) to.write(" ") - to.write( - ", ".join( - "{}: {}".format(name, repr(arg)) for name, arg in flags.items() - ) - ) + to.write(", ".join(f"{name}: {repr(arg)}" for name, arg in flags.items())) to.write(os.linesep) return inner