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 2022-09-12 19:08:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-curtsies (Old)
and /work/SRC/openSUSE:Factory/.python-curtsies.new.2083 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-curtsies"
Mon Sep 12 19:08:22 2022 rev:13 rq:1002735 version:0.4.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-curtsies/python-curtsies.changes
2022-03-28 17:01:39.533058849 +0200
+++
/work/SRC/openSUSE:Factory/.python-curtsies.new.2083/python-curtsies.changes
2022-09-12 19:08:23.658560968 +0200
@@ -1,0 +2,13 @@
+Sat Sep 10 15:48:54 UTC 2022 - Arun Persaud <[email protected]>
+
+- specfile:
+ * skip python 3.6
+ * require python-blessed
+
+- update to version 0.4.0:
+ * Clean up both wakeup_fds
+ * Drop support for Python 3.6
+ * Switch to blessed
+ * Typing: add more annotations
+
+-------------------------------------------------------------------
Old:
----
curtsies-0.3.10.tar.gz
New:
----
curtsies-0.4.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-curtsies.spec ++++++
--- /var/tmp/diff_new_pack.2tlE16/_old 2022-09-12 19:08:24.114562250 +0200
+++ /var/tmp/diff_new_pack.2tlE16/_new 2022-09-12 19:08:24.118562261 +0200
@@ -18,22 +18,23 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
+%define skip_python36 1
Name: python-curtsies
-Version: 0.3.10
+Version: 0.4.0
Release: 0
Summary: Curses-like terminal wrapper, with colored strings!
License: MIT
Group: Development/Languages/Python
URL: https://github.com/bpython/curtsies
Source:
https://files.pythonhosted.org/packages/source/c/curtsies/curtsies-%{version}.tar.gz
-BuildRequires: %{python_module blessings}
+BuildRequires: %{python_module blessed >= 1.5}
BuildRequires: %{python_module cwcwidth}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module pyte}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-blessings
+Requires: python-blessed >= 1.5
Requires: python-cwcwidth
BuildArch: noarch
%python_subpackages
++++++ curtsies-0.3.10.tar.gz -> curtsies-0.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/PKG-INFO new/curtsies-0.4.0/PKG-INFO
--- old/curtsies-0.3.10/PKG-INFO 2021-10-09 04:42:20.741536100 +0200
+++ new/curtsies-0.4.0/PKG-INFO 2022-08-28 22:39:27.821959300 +0200
@@ -1,12 +1,11 @@
Metadata-Version: 2.1
Name: curtsies
-Version: 0.3.10
+Version: 0.4.0
Summary: Curses-like terminal wrapper, with colored strings!
Home-page: https://github.com/bpython/curtsies
Author: Thomas Ballinger
Author-email: [email protected]
License: MIT
-Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
@@ -14,7 +13,7 @@
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Requires-Python: >=3.6
+Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
@@ -109,5 +108,3 @@
* Thanks to the many contributors!
* If all you need are colored strings, consider one of these [other
libraries](http://curtsies.readthedocs.io/en/latest/FmtStr.html#fmtstr-rationale)!
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/__init__.py
new/curtsies-0.4.0/curtsies/__init__.py
--- old/curtsies-0.3.10/curtsies/__init__.py 2021-10-09 04:39:20.000000000
+0200
+++ new/curtsies-0.4.0/curtsies/__init__.py 2022-08-28 22:39:24.000000000
+0200
@@ -1,5 +1,5 @@
"""Terminal-formatted strings"""
-__version__ = "0.3.10"
+__version__ = "0.4.0"
from .window import FullscreenWindow, CursorAwareWindow
from .input import Input
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/curtsieskeys.py
new/curtsies-0.4.0/curtsies/curtsieskeys.py
--- old/curtsies-0.3.10/curtsies/curtsieskeys.py 2021-09-25
20:39:51.000000000 +0200
+++ new/curtsies-0.4.0/curtsies/curtsieskeys.py 2022-08-28 22:39:24.000000000
+0200
@@ -92,6 +92,10 @@
b"\x1b[3~": '<DELETE>', # delete (.), "Execute"
b"\x1b[3;5~": '<Ctrl-DELETE>',
+ # st (simple terminal) see issue #169
+ b"\x1b[4h": '<INSERT>',
+ b"\x1b[P": '<DELETE>',
+
# not fixing for back compat.
# (b"\x1b[4~": u'<SELECT>', # select
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/events.py
new/curtsies-0.4.0/curtsies/events.py
--- old/curtsies-0.3.10/curtsies/events.py 2021-10-08 23:51:39.000000000
+0200
+++ new/curtsies-0.4.0/curtsies/events.py 2022-08-28 22:39:24.000000000
+0200
@@ -1,9 +1,9 @@
"""Events for keystrokes and other input events"""
import codecs
-import encodings
import itertools
import sys
-from typing import Optional, List, Union
+from enum import Enum, auto
+from typing import Dict, Optional, List, Union
from .termhelpers import Termmode
from .curtsieskeys import CURTSIES_NAMES as special_curtsies_names
@@ -12,7 +12,7 @@
chr_uni = chr
-CURTSIES_NAMES = {}
+CURTSIES_NAMES: Dict[bytes, str] = {}
control_chars = {chr_byte(i): "<Ctrl-%s>" % chr(i + 0x60) for i in range(0x00,
0x1B)}
CURTSIES_NAMES.update(control_chars)
for i in range(0x00, 0x80):
@@ -81,6 +81,12 @@
)
+class Keynames(Enum):
+ CURTSIES = auto()
+ CURSES = auto()
+ BYTES = auto()
+
+
class Event:
pass
@@ -159,7 +165,10 @@
def get_key(
- bytes_: List[bytes], encoding: str, keynames: str = "curtsies", full: bool
= False
+ bytes_: List[bytes],
+ encoding: str,
+ keynames: Keynames = Keynames.CURTSIES,
+ full: bool = False,
) -> Optional[str]:
"""Return key pressed from bytes_ or None
@@ -193,16 +202,14 @@
(for 'asdf', first on 'a', then on 'as', then on 'asd' - until a non-None
value is returned)
"""
- if not all(isinstance(c, type(b"")) for c in bytes_):
- raise ValueError("get key expects bytes, got %r" % bytes_) # expects
raw bytes
- if keynames not in ["curtsies", "curses", "bytes"]:
- raise ValueError("keynames must be one of 'curtsies', 'curses' or
'bytes'")
+ if not all(isinstance(c, bytes) for c in bytes_):
+ raise TypeError("get key expects bytes, got %r" % bytes_) # expects
raw bytes
seq = b"".join(bytes_)
if len(seq) > MAX_KEYPRESS_SIZE:
raise ValueError("unable to decode bytes %r" % seq)
def key_name() -> str:
- if keynames == "curses":
+ if keynames == Keynames.CURSES:
# may not be here (and still not decodable) curses names incomplete
if seq in CURSES_NAMES:
return CURSES_NAMES[seq]
@@ -224,14 +231,14 @@
"x%02X" % ord(seq[i : i + 1]) for i in range(len(seq))
)
# TODO if this isn't possible, return multiple meta keys
as a paste event if paste events enabled
- elif keynames == "curtsies":
+ elif keynames == Keynames.CURTSIES:
if seq in CURTSIES_NAMES:
return CURTSIES_NAMES[seq]
return seq.decode(
encoding
) # assumes that curtsies names are a subset of curses ones
else:
- assert keynames == "bytes"
+ assert keynames == Keynames.BYTES
return seq # type: ignore
key_known = seq in CURTSIES_NAMES or seq in CURSES_NAMES or decodable(seq,
encoding)
@@ -306,9 +313,6 @@
print(
"press a bunch of keys (not at the same time, but you can hit them
pretty quickly)"
)
- import tty
- import termios
- import fcntl
import os
from .termhelpers import Cbreak
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/formatstring.py
new/curtsies-0.4.0/curtsies/formatstring.py
--- old/curtsies-0.3.10/curtsies/formatstring.py 2021-10-07
20:41:22.000000000 +0200
+++ new/curtsies-0.4.0/curtsies/formatstring.py 2022-08-28 22:39:24.000000000
+0200
@@ -19,24 +19,23 @@
red('hello')
"""
-import itertools
import re
-import sys
from cwcwidth import wcswidth, wcwidth
+from itertools import chain
from typing import (
+ Any,
+ Callable,
+ Dict,
+ Iterable,
Iterator,
- Tuple,
List,
- Union,
- Optional,
- Any,
Mapping,
- cast,
MutableMapping,
+ Optional,
+ Tuple,
+ Union,
+ cast,
no_type_check,
- Type,
- Callable,
- Iterable,
)
try:
@@ -76,8 +75,8 @@
xforms.update(two_arg_xforms)
-class FrozenDict(dict):
- """Immutable dictionary class"""
+class FrozenAttributes(Dict[str, Union[int, bool]]):
+ """Immutable dictionary class for format string attributes"""
@no_type_check
def __setitem__(self, key, value):
@@ -87,11 +86,11 @@
def update(self, *args, **kwds):
raise Exception("Cannot change value.")
- def extend(self, dictlike: Mapping[str, Union[int, bool]]) -> "FrozenDict":
- return FrozenDict(itertools.chain(self.items(), dictlike.items()))
+ def extend(self, dictlike: Mapping[str, Union[int, bool]]) ->
"FrozenAttributes":
+ return FrozenAttributes(chain(self.items(), dictlike.items()))
- def remove(self, *keys: str) -> "FrozenDict":
- return FrozenDict((k, v) for k, v in self.items() if k not in keys)
+ def remove(self, *keys: str) -> "FrozenAttributes":
+ return FrozenAttributes((k, v) for k, v in self.items() if k not in
keys)
def stable_format_dict(d: Mapping) -> str:
@@ -121,14 +120,14 @@
if not isinstance(string, str):
raise ValueError("unicode string required, got %r" % string)
self._s = string
- self._atts = FrozenDict(atts if atts else {})
+ self._atts = FrozenAttributes(atts if atts else {})
@property
def s(self) -> str:
return self._s
@property
- def atts(self) -> Mapping[str, Union[int, bool]]:
+ def atts(self) -> FrozenAttributes:
"Attributes, e.g. {'fg': 34, 'bold': True} where 34 is the escape code
for ..."
return self._atts
@@ -137,7 +136,7 @@
@property
def width(self) -> int:
- width = wcswidth(self._s, None)
+ width = wcswidth(self._s)
if len(self._s) > 0 and width < 1:
raise ValueError("Can't calculate width of string %r" % self._s)
return width
@@ -145,8 +144,8 @@
@cached_property
def color_str(self) -> str:
"Return an escape-coded string to write to the terminal."
- s = self.s
- for k, v in sorted(self.atts.items()):
+ s = self._s
+ for k, v in sorted(self._atts.items()):
# (self.atts sorted for the sake of always acting the same.)
if k not in xforms:
# Unsupported SGR code
@@ -156,6 +155,7 @@
elif v is True:
s = one_arg_xforms[k](s)
else:
+ # TODO: What's the purpose of this code? It will never be
executed.
s = two_arg_xforms[k](s, v)
return s
@@ -168,16 +168,16 @@
def __eq__(self, other: Any) -> bool:
if not isinstance(other, Chunk):
return NotImplemented
- return self.s == other.s and self.atts == other.atts
+ return self._s == other._s and self._atts == other._atts
def __hash__(self) -> int:
- return hash((self.s, self.atts))
+ return hash((self._s, self._atts))
def __repr__(self) -> str:
return "Chunk({s}{sep}{atts})".format(
- s=repr(self.s),
- sep=", " if self.atts else "",
- atts=stable_format_dict(self.atts) if self.atts else "",
+ s=repr(self._s),
+ sep=", " if self._atts else "",
+ atts=stable_format_dict(self._atts) if self._atts else "",
)
def repr_part(self) -> str:
@@ -191,10 +191,10 @@
else:
return att
- atts_out = {k: v for (k, v) in self.atts.items() if v}
+ atts_out = {k: v for (k, v) in self._atts.items() if v}
return (
"".join(pp_att(att) + "(" for att in sorted(atts_out))
- + repr(self.s)
+ + repr(self._s)
+ ")" * len(atts_out)
)
@@ -416,8 +416,9 @@
def copy_with_new_atts(self, **attributes: Union[bool, int]) -> "FmtStr":
"""Returns a new FmtStr with the same content but new formatting"""
- result = FmtStr(*(Chunk(bfs.s, bfs.atts.extend(attributes)) for bfs in
self.chunks)) # type: ignore
- return result
+ return FmtStr(
+ *(Chunk(bfs.s, bfs.atts.extend(attributes)) for bfs in self.chunks)
+ )
def join(self, iterable: Iterable[Union[str, "FmtStr"]]) -> "FmtStr":
"""Joins an iterable yielding strings or FmtStrs with self as
separator"""
@@ -456,8 +457,8 @@
return [
self[start:end]
for start, end in zip(
- [0] + [m.end() for m in matches],
- [m.start() for m in matches] + [len(s)],
+ chain((0,), (m.end() for m in matches)),
+ chain((m.start() for m in matches), (len(s),)),
)
]
@@ -828,7 +829,7 @@
def parse_args(
- args: Tuple[Union[bytes, str], ...],
+ args: Tuple[str, ...],
kwargs: MutableMapping[str, Union[int, bool, str]],
) -> Mapping[str, Union[int, bool]]:
"""Returns a kwargs dictionary by turning args into kwargs"""
@@ -836,9 +837,8 @@
args += (cast(str, kwargs["style"]),)
del kwargs["style"]
for arg in args:
- arg = cast(str, arg)
- if not isinstance(arg, (bytes, str)):
- raise ValueError("args must be strings:" + repr(args))
+ if not isinstance(arg, str):
+ raise ValueError(f"args must be strings: {arg!r}")
if arg.lower() in FG_COLORS:
if "fg" in kwargs:
raise ValueError("fg specified twice")
@@ -850,20 +850,20 @@
elif arg.lower() in STYLES:
kwargs[arg] = True
else:
- raise ValueError("couldn't process arg: " + repr(arg))
+ raise ValueError(f"couldn't process arg: {args!r}")
for k in kwargs:
- if k not in ["fg", "bg"] + list(STYLES.keys()):
+ if k not in ("fg", "bg") and k not in STYLES.keys():
raise ValueError("Can't apply that transformation")
if "fg" in kwargs:
if kwargs["fg"] in FG_COLORS:
kwargs["fg"] = FG_COLORS[cast(str, kwargs["fg"])]
if kwargs["fg"] not in list(FG_COLORS.values()):
- raise ValueError("Bad fg value: %r" % kwargs["fg"])
+ raise ValueError(f"Bad fg value: {kwargs['fg']!r}")
if "bg" in kwargs:
if kwargs["bg"] in BG_COLORS:
kwargs["bg"] = BG_COLORS[cast(str, kwargs["bg"])]
if kwargs["bg"] not in list(BG_COLORS.values()):
- raise ValueError("Bad bg value: %r" % kwargs["bg"])
+ raise ValueError(f"Bad bg value: {kwargs['bg']!r}")
return cast(MutableMapping[str, Union[int, bool]], kwargs)
@@ -877,14 +877,10 @@
on_red(bold(blue('blarg')))
"""
atts = parse_args(args, kwargs)
- if isinstance(string, FmtStr):
- pass
- elif isinstance(string, (bytes, str)):
+ if isinstance(string, str):
string = FmtStr.from_str(string)
- else:
+ elif not isinstance(string, FmtStr):
raise ValueError(
- "Bad Args: {!r} (of type {}), {!r}, {!r}".format(
- string, type(string), args, kwargs
- )
+ f"Bad Args: {string!r} (of type {type(string)}), {args!r},
{kwargs!r}"
)
return string.copy_with_new_atts(**atts)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/formatstringarray.py
new/curtsies-0.4.0/curtsies/formatstringarray.py
--- old/curtsies-0.3.10/curtsies/formatstringarray.py 2021-09-25
20:39:51.000000000 +0200
+++ new/curtsies-0.4.0/curtsies/formatstringarray.py 2022-08-28
22:39:24.000000000 +0200
@@ -88,10 +88,7 @@
if isinstance(slicetuple, slice):
rowslice = normalize_slice(len(self.rows), slicetuple)
return self.rows[rowslice]
- (
- row_slice_or_int,
- col_slice_or_int,
- ) = slicetuple # type: Tuple[Union[int, slice], Union[int, slice]]
+ row_slice_or_int, col_slice_or_int = slicetuple
rowslice = normalize_slice(len(self.rows), row_slice_or_int)
colslice = normalize_slice(self.num_columns, col_slice_or_int)
# TODO clean up slices
@@ -122,7 +119,7 @@
logger.debug("slice: %r", slicetuple)
if isinstance(slicetuple, slice):
rowslice, colslice = slicetuple, slice(None)
- if isinstance(value, (bytes, str)):
+ if isinstance(value, str):
raise ValueError(
"if slice is 2D, value must be 2D as in of list type []"
)
@@ -251,7 +248,9 @@
)
-def fsarray(strings: List[Union[FmtStr, str]], *args: Any, **kwargs: Any) ->
FSArray:
+def fsarray(
+ strings: Sequence[Union[FmtStr, str]], *args: Any, **kwargs: Any
+) -> FSArray:
"""fsarray(list_of_FmtStrs_or_strings, width=None) -> FSArray
Returns a new FSArray of width of the maximum size of the provided
@@ -279,7 +278,7 @@
return arr
-def simple_format(x: Union[FSArray, List[FmtStr]]) -> str:
+def simple_format(x: Union[FSArray, Sequence[FmtStr]]) -> str:
return "\n".join(str(l) for l in x)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/input.py
new/curtsies-0.4.0/curtsies/input.py
--- old/curtsies-0.3.10/curtsies/input.py 2021-10-09 03:40:58.000000000
+0200
+++ new/curtsies-0.4.0/curtsies/input.py 2022-08-28 22:39:24.000000000
+0200
@@ -12,7 +12,18 @@
from .termhelpers import Nonblocking
from . import events
-from typing import Callable, Type, TextIO, Optional, List, Union, cast, Tuple,
Any
+from typing import (
+ Callable,
+ ContextManager,
+ Type,
+ TextIO,
+ Optional,
+ List,
+ Union,
+ cast,
+ Tuple,
+ Any,
+)
from types import TracebackType, FrameType
@@ -27,7 +38,7 @@
return threading.current_thread() == threading.main_thread()
-class ReplacedSigIntHandler:
+class ReplacedSigIntHandler(ContextManager):
def __init__(self, handler: Callable) -> None:
self.handler = handler
@@ -43,13 +54,13 @@
signal.signal(signal.SIGINT, self.orig_sigint_handler)
-class Input:
+class Input(ContextManager["Input"]):
"""Keypress and control event generator"""
def __init__(
self,
in_stream: Optional[TextIO] = None,
- keynames: str = "curtsies",
+ keynames: Union[events.Keynames, str] = events.Keynames.CURTSIES,
paste_threshold: Optional[int] = events.MAX_KEYPRESS_SIZE + 1,
sigint_event: bool = False,
disable_terminal_start_stop: bool = False,
@@ -73,11 +84,24 @@
in_stream = sys.__stdin__
self.in_stream = in_stream
self.unprocessed_bytes: List[bytes] = [] # leftover from stdin,
unprocessed yet
- self.keynames = keynames
+ if isinstance(keynames, str):
+ # TODO: Remove this block with the next API breaking release.
+ if keynames == "curtsies":
+ self.keynames = events.Keynames.CURTSIES
+ elif keynames == "curses":
+ self.keynames = events.Keynames.CURSES
+ elif keynames == "bytes":
+ self.keynames = events.Keynames.BYTES
+ else:
+ raise ValueError("keyname is invalid")
+ else:
+ self.keynames = keynames
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]] = []
@@ -110,11 +134,11 @@
self.orig_sigint_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, self.sigint_handler)
- self.wakeup_read_fd, wfd = os.pipe()
- os.set_blocking(wfd, False)
- if sys.version_info[0] == 3 and 5 <= sys.version_info[1] < 7:
- signal.set_wakeup_fd(wfd)
- elif sys.version_info[0] == 3 and 7 <= sys.version_info[1]:
+ # Non-main threads don't receive signals
+ if is_main_thread():
+ self.wakeup_read_fd, self.wakeup_write_fd = os.pipe()
+ wfd = self.wakeup_write_fd
+ os.set_blocking(wfd, False)
signal.set_wakeup_fd(wfd, warn_on_full_buffer=False)
return self
@@ -131,12 +155,16 @@
and self.orig_sigint_handler is not None
):
signal.signal(signal.SIGINT, self.orig_sigint_handler)
- if sys.version_info[0] == 3 and sys.version_info[1] > 4:
+ if is_main_thread():
signal.set_wakeup_fd(-1)
+ if self.wakeup_read_fd is not None:
+ os.close(self.wakeup_read_fd)
+ if self.wakeup_write_fd is not None:
+ os.close(self.wakeup_write_fd)
termios.tcsetattr(self.in_stream, termios.TCSANOW, self.original_stty)
def sigint_handler(
- self, signum: Union[signal.Signals, int], frame: FrameType
+ self, signum: Union[signal.Signals, int], frame: Optional[FrameType]
) -> None:
self.sigints.append(events.SigIntEvent())
@@ -168,10 +196,8 @@
while True:
try:
(rs, _, _) = select.select(
- [
- self.in_stream.fileno(),
- self.wakeup_read_fd,
- ]
+ [self.in_stream.fileno()]
+ + ([] if self.wakeup_read_fd is None else
[self.wakeup_read_fd])
+ self.readers,
[],
[],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/termhelpers.py
new/curtsies-0.4.0/curtsies/termhelpers.py
--- old/curtsies-0.3.10/curtsies/termhelpers.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/curtsies/termhelpers.py 2022-08-28 22:39:24.000000000
+0200
@@ -3,13 +3,13 @@
import fcntl
import os
-from typing import IO, Type, List, Union, Optional
+from typing import IO, ContextManager, Type, List, Union, Optional
from types import TracebackType
_Attr = List[Union[int, List[Union[bytes, int]]]]
-class Nonblocking:
+class Nonblocking(ContextManager):
"""
A context manager for making an input stream nonblocking.
"""
@@ -31,7 +31,7 @@
fcntl.fcntl(self.fd, fcntl.F_SETFL, self.orig_fl)
-class Termmode:
+class Termmode(ContextManager):
def __init__(self, stream: IO, attrs: _Attr) -> None:
self.stream = stream
self.attrs = attrs
@@ -49,7 +49,7 @@
termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty)
-class Cbreak:
+class Cbreak(ContextManager[Termmode]):
def __init__(self, stream: IO) -> None:
self.stream = stream
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies/window.py
new/curtsies-0.4.0/curtsies/window.py
--- old/curtsies-0.3.10/curtsies/window.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/curtsies/window.py 2022-08-28 22:39:24.000000000
+0200
@@ -1,11 +1,13 @@
# All windows write only unicode to the terminal -
-# that's what blessings does, so we match it.
+# that's what blessed does, so we match it.
from typing import (
+ ContextManager,
Optional,
IO,
Dict,
+ Sequence,
TypeVar,
Type,
Tuple,
@@ -21,7 +23,7 @@
import re
import sys
-import blessings
+import blessed
from .formatstring import fmtstr, FmtStr
from .formatstringarray import FSArray
@@ -29,21 +31,18 @@
logger = logging.getLogger(__name__)
-SCROLL_DOWN = "\x1bD"
-FIRST_COLUMN = "\x1b[1G"
-
T = TypeVar("T", bound="BaseWindow")
-class BaseWindow:
+class BaseWindow(ContextManager):
def __init__(
self, out_stream: Optional[IO] = None, hide_cursor: bool = True
) -> None:
logger.debug("-------initializing Window object %r------" % self)
if out_stream is None:
out_stream = sys.__stdout__
- self.t = blessings.Terminal(stream=out_stream, force_styling=True)
+ 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]] = {}
@@ -55,7 +54,7 @@
# since scroll-down only moves the screen if cursor is at bottom
with self.t.location(x=0, y=1000000):
- self.write(SCROLL_DOWN) # TODO will blessings do this?
+ self.write(self.t.move_down)
def write(self, msg: str) -> None:
self.out_stream.write(msg)
@@ -129,7 +128,7 @@
return for_stdout
-class FullscreenWindow(BaseWindow):
+class FullscreenWindow(BaseWindow, ContextManager["FullscreenWindow"]):
"""2D-text rendering window that disappears when its context is left
FullscreenWindow will only render arrays the size of the terminal
@@ -199,12 +198,9 @@
self.on_terminal_size_change(height, width)
current_lines_by_row: Dict[int, Optional[FmtStr]] = {}
- rows = list(range(height))
- rows_for_use = rows[: len(array)]
- rest_of_rows = rows[len(array) :]
# rows which we have content for and don't require scrolling
- for row, line in zip(rows_for_use, array):
+ for row, line in enumerate(array):
current_lines_by_row[row] = line
if line == self._last_lines_by_row.get(row, None):
continue
@@ -214,7 +210,7 @@
self.write(self.t.clear_eol)
# rows onscreen that we don't have content for
- for row in rest_of_rows:
+ for row in range(len(array), height):
if self._last_lines_by_row and row not in self._last_lines_by_row:
continue
self.write(self.t.move(row, 0))
@@ -230,7 +226,7 @@
self.write(self.t.normal_cursor)
-class CursorAwareWindow(BaseWindow):
+class CursorAwareWindow(BaseWindow, ContextManager["CursorAwareWindow"]):
"""
Renders to the normal terminal screen and
can find the location of the cursor.
@@ -269,10 +265,16 @@
if in_stream is None:
in_stream = sys.__stdin__
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.keep_last_line = keep_last_line
- self.cbreak = Cbreak(self.in_stream)
+ self.cbreak = (
+ Cbreak(self.in_stream) if not self._use_blessed else
self.t.cbreak()
+ )
self.extra_bytes_callback = extra_bytes_callback
# whether another SIGWINCH is queued up
@@ -296,9 +298,9 @@
) -> None:
if self.keep_last_line:
# just moves cursor down if not on last line
- self.write(SCROLL_DOWN)
+ self.write(self.t.move_down)
- self.write(FIRST_COLUMN)
+ self.write(self.t.move_x(0))
self.write(self.t.clear_eos)
self.write(self.t.clear_eol)
self.cbreak.__exit__(type, value, traceback)
@@ -307,7 +309,11 @@
def get_cursor_position(self) -> Tuple[int, int]:
"""Returns the terminal (row, column) of the cursor
- 0-indexed, like blessings cursor positions"""
+ 0-indexed, like blessed cursor positions"""
+
+ if self._use_blessed:
+ return self.t.get_location()
+
# TODO would this be cleaner as a parameter?
in_stream = self.in_stream
@@ -419,7 +425,9 @@
return cursor_dy
def render_to_terminal(
- self, array: Union[FSArray, List[FmtStr]], cursor_pos: Tuple[int, int]
= (0, 0)
+ self,
+ array: Union[FSArray, Sequence[FmtStr]],
+ cursor_pos: Tuple[int, int] = (0, 0),
) -> int:
"""Renders array to terminal, returns the number of lines scrolled
offscreen
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies.egg-info/PKG-INFO
new/curtsies-0.4.0/curtsies.egg-info/PKG-INFO
--- old/curtsies-0.3.10/curtsies.egg-info/PKG-INFO 2021-10-09
04:42:20.000000000 +0200
+++ new/curtsies-0.4.0/curtsies.egg-info/PKG-INFO 2022-08-28
22:39:27.000000000 +0200
@@ -1,12 +1,11 @@
Metadata-Version: 2.1
Name: curtsies
-Version: 0.3.10
+Version: 0.4.0
Summary: Curses-like terminal wrapper, with colored strings!
Home-page: https://github.com/bpython/curtsies
Author: Thomas Ballinger
Author-email: [email protected]
License: MIT
-Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
@@ -14,7 +13,7 @@
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Requires-Python: >=3.6
+Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
@@ -109,5 +108,3 @@
* Thanks to the many contributors!
* If all you need are colored strings, consider one of these [other
libraries](http://curtsies.readthedocs.io/en/latest/FmtStr.html#fmtstr-rationale)!
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies.egg-info/SOURCES.txt
new/curtsies-0.4.0/curtsies.egg-info/SOURCES.txt
--- old/curtsies-0.3.10/curtsies.egg-info/SOURCES.txt 2021-10-09
04:42:20.000000000 +0200
+++ new/curtsies-0.4.0/curtsies.egg-info/SOURCES.txt 2022-08-28
22:39:27.000000000 +0200
@@ -2,7 +2,6 @@
MANIFEST.in
README.md
pyproject.toml
-readme.md
setup.cfg
setup.py
curtsies/__init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/curtsies.egg-info/requires.txt
new/curtsies-0.4.0/curtsies.egg-info/requires.txt
--- old/curtsies-0.3.10/curtsies.egg-info/requires.txt 2021-10-09
04:42:20.000000000 +0200
+++ new/curtsies-0.4.0/curtsies.egg-info/requires.txt 2022-08-28
22:39:27.000000000 +0200
@@ -1,4 +1,4 @@
-blessings>=1.5
+blessed>=1.5
cwcwidth
[:python_version < "3.8"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/setup.cfg
new/curtsies-0.4.0/setup.cfg
--- old/curtsies-0.3.10/setup.cfg 2021-10-09 04:42:20.743643000 +0200
+++ new/curtsies-0.4.0/setup.cfg 2022-08-28 22:39:27.821959300 +0200
@@ -18,11 +18,11 @@
Programming Language :: Python :: 3
[options]
-python_requires = >=3.6
+python_requires = >=3.7
zip_safe = False
packages = curtsies
install_requires =
- blessings>=1.5
+ blessed>=1.5
cwcwidth
backports.cached-property; python_version < "3.8"
tests_require =
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/tests/test_events.py
new/curtsies-0.4.0/tests/test_events.py
--- old/curtsies-0.3.10/tests/test_events.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/tests/test_events.py 2022-08-28 22:39:24.000000000
+0200
@@ -40,25 +40,34 @@
class TestGetKey(unittest.TestCase):
def test_utf8_full(self):
get_utf_full = partial(
- events.get_key, encoding="utf-8", keynames="curtsies", full=True
+ events.get_key,
+ encoding="utf-8",
+ keynames=events.Keynames.CURTSIES,
+ full=True,
)
self.assertEqual(get_utf_full([b"h"]), "h")
self.assertEqual(get_utf_full([b"\x1b", b"["]), "<Esc+[>")
self.assertRaises(UnicodeDecodeError, get_utf_full, [b"\xfe\xfe"])
- self.assertRaises(ValueError, get_utf_full, "a")
+ self.assertRaises(TypeError, get_utf_full, "a")
def test_utf8(self):
get_utf = partial(
- events.get_key, encoding="utf-8", keynames="curtsies", full=False
+ events.get_key,
+ encoding="utf-8",
+ keynames=events.Keynames.CURTSIES,
+ full=False,
)
self.assertEqual(get_utf([b"h"]), "h")
self.assertEqual(get_utf([b"\x1b", b"["]), None)
self.assertEqual(get_utf([b"\xe2"]), None)
- self.assertRaises(ValueError, get_utf, "a")
+ self.assertRaises(TypeError, get_utf, "a")
def test_multibyte_utf8(self):
get_utf = partial(
- events.get_key, encoding="utf-8", keynames="curtsies", full=False
+ events.get_key,
+ encoding="utf-8",
+ keynames=events.Keynames.CURTSIES,
+ full=False,
)
self.assertEqual(get_utf([b"\xc3"]), None)
self.assertEqual(get_utf([b"\xe2"]), None)
@@ -70,10 +79,15 @@
def test_sequences_without_names(self):
get_utf = partial(
- events.get_key, encoding="utf-8", keynames="curtsies", full=False
+ events.get_key,
+ encoding="utf-8",
+ keynames=events.Keynames.CURTSIES,
+ full=False,
)
self.assertEqual(get_utf([b"\xc3"], full=True), "<Meta-C>")
- self.assertEqual(get_utf([b"\xc3"], full=True, keynames="curses"),
"xC3")
+ self.assertEqual(
+ get_utf([b"\xc3"], full=True, keynames=events.Keynames.CURSES),
"xC3"
+ )
def test_key_names(self):
"Every key sequence with a Curses name should have a Curtsies name
too."
@@ -86,22 +100,33 @@
class TestGetKeyAscii(unittest.TestCase):
def test_full(self):
get_ascii_full = partial(
- events.get_key, encoding="ascii", keynames="curtsies", full=True
+ events.get_key,
+ encoding="ascii",
+ keynames=events.Keynames.CURTSIES,
+ full=True,
)
self.assertEqual(get_ascii_full([b"a"]), "a")
self.assertEqual(get_ascii_full([b"\xe1"]), "<Meta-a>")
- self.assertEqual(get_ascii_full([b"\xe1"], keynames="curses"), "xE1")
+ self.assertEqual(
+ get_ascii_full([b"\xe1"], keynames=events.Keynames.CURSES), "xE1"
+ )
def test_simple(self):
- get_ascii_full = partial(events.get_key, encoding="ascii",
keynames="curtsies")
+ get_ascii_full = partial(
+ events.get_key, encoding="ascii", keynames=events.Keynames.CURTSIES
+ )
self.assertEqual(get_ascii_full([b"a"]), "a")
self.assertEqual(get_ascii_full([b"\xe1"]), "<Meta-a>")
- self.assertEqual(get_ascii_full([b"\xe1"], keynames="curses"), "xE1")
+ self.assertEqual(
+ get_ascii_full([b"\xe1"], keynames=events.Keynames.CURSES), "xE1"
+ )
class TestUnknownEncoding(unittest.TestCase):
def test_simple(self):
- get_utf16 = partial(events.get_key, encoding="utf16",
keynames="curtsies")
+ get_utf16 = partial(
+ events.get_key, encoding="utf16", keynames=events.Keynames.CURTSIES
+ )
self.assertEqual(get_utf16([b"a"]), None)
self.assertEqual(get_utf16([b"a"], full=True), None)
self.assertEqual(get_utf16([b"\xe1"]), None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/tests/test_fmtstr.py
new/curtsies-0.4.0/tests/test_fmtstr.py
--- old/curtsies-0.3.10/tests/test_fmtstr.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/tests/test_fmtstr.py 2022-08-28 22:39:24.000000000
+0200
@@ -20,18 +20,18 @@
bold,
)
from curtsies.termformatconstants import FG_COLORS
-from curtsies.formatstringarray import fsarray, FSArray
+from curtsies.formatstringarray import fsarray, FSArray, simple_format
from unittest import skip
-def repr_without_leading_u(s):
+def repr_without_leading_u(s: str) -> str:
assert isinstance(s, str)
return repr(s)
class TestFmtStrInitialization(unittest.TestCase):
- def test_bad(self):
+ def test_bad(self) -> None:
# Can't specify fg or bg color two ways
self.assertRaises(ValueError, fmtstr, "hello", "blue", {"fg": 30})
self.assertRaises(ValueError, fmtstr, "hello", "on_blue", {"bg": 40})
@@ -41,15 +41,15 @@
# Only existing xforms can be used in kwargs
self.assertRaises(ValueError, fmtstr, "hello", "make it big")
- def test_actual_init(self):
+ def test_actual_init(self) -> None:
FmtStr()
class TestFmtStrParsing(unittest.TestCase):
- def test_no_escapes(self):
+ def test_no_escapes(self) -> None:
self.assertEqual(str(fmtstr("abc")), "abc")
- def test_simple_escapes(self):
+ def test_simple_escapes(self) -> None:
self.assertEqual(str(fmtstr("\x1b[33mhello\x1b[0m")),
"\x1b[33mhello\x1b[39m")
self.assertEqual(str(fmtstr("\x1b[33mhello\x1b[39m")),
"\x1b[33mhello\x1b[39m")
self.assertEqual(str(fmtstr("\x1b[33mhello")), "\x1b[33mhello\x1b[39m")
@@ -68,13 +68,13 @@
"\x1b[33m\x1b[43mhello\x1b[49m\x1b[39m",
)
- def test_out_of_order(self):
+ def test_out_of_order(self) -> None:
self.assertEqual(
str(fmtstr("\x1b[33m\x1b[43mhello\x1b[39m\x1b[49m")),
"\x1b[33m\x1b[43mhello\x1b[49m\x1b[39m",
)
- def test_noncurtsies_output(self):
+ def test_noncurtsies_output(self) -> None:
fmtstr("\x1b[35mx\x1b[m")
self.assertEqual(fmtstr("\x1b[Ahello"), "hello")
self.assertEqual(fmtstr("\x1b[20Ahello"), "hello")
@@ -82,7 +82,9 @@
class TestImmutability(unittest.TestCase):
- def
test_fmt_strings_remain_unchanged_when_used_to_construct_other_ones(self):
+ def test_fmt_strings_remain_unchanged_when_used_to_construct_other_ones(
+ self,
+ ) -> None:
a = fmtstr("hi", "blue")
b = fmtstr("there", "red")
c = a + b
@@ -90,7 +92,7 @@
self.assertEqual(a.shared_atts["fg"], FG_COLORS["blue"])
self.assertEqual(b.shared_atts["fg"], FG_COLORS["red"])
- def test_immutibility_of_FmtStr(self):
+ def test_immutibility_of_FmtStr(self) -> None:
a = fmtstr("hi", "blue")
b = green(a)
self.assertEqual(a.shared_atts["fg"], FG_COLORS["blue"])
@@ -98,11 +100,11 @@
class TestFmtStrSplice(unittest.TestCase):
- def test_simple_beginning_splice(self):
+ def test_simple_beginning_splice(self) -> None:
self.assertEqual(fmtstr("abc").splice("d", 0), fmtstr("dabc"))
self.assertEqual(fmtstr("abc").splice("d", 0), "d" + fmtstr("abc"))
- def test_various_splices(self):
+ def test_various_splices(self) -> None:
a = blue("hi")
b = a + green("bye")
c = b + red("!")
@@ -115,10 +117,10 @@
)
self.assertEqual(c.splice("asdfg", 1, 5), blue("h") + "asdfg" +
red("!"))
- def test_splice_of_empty_fmtstr(self):
+ def test_splice_of_empty_fmtstr(self) -> None:
self.assertEqual(fmtstr("ab").splice("", 1), fmtstr("ab"))
- def test_splice_with_multiple_chunks(self):
+ def test_splice_with_multiple_chunks(self) -> None:
a = fmtstr("notion")
b = a.splice("te", 2, 6)
c = b.splice("de", 0)
@@ -128,7 +130,7 @@
self.assertEqual(c.s, "denote")
self.assertEqual(len(c.chunks), 3)
- def test_splice_fmtstr_with_end_without_atts(self):
+ def test_splice_fmtstr_with_end_without_atts(self) -> None:
a = fmtstr("notion")
b = a.splice("te", 2, 6)
@@ -136,7 +138,7 @@
self.assertEqual(b.s, "note")
self.assertEqual(len(b.chunks), 2)
- def test_splice_fmtstr_with_end_with_atts(self):
+ def test_splice_fmtstr_with_end_with_atts(self) -> None:
# Need to test with fmtstr consisting of multiple chunks
# and with attributes
a = fmtstr("notion", "blue")
@@ -151,21 +153,21 @@
self.assertEqual(b.chunks[1].atts, {})
self.assertEqual(len(b.chunks), 2)
- def test_splice_fmtstr_without_end(self):
+ def test_splice_fmtstr_without_end(self) -> None:
a = fmtstr("notion")
b = a.splice(fmtstr("ta"), 2)
self.assertEqual(a.s, "notion")
self.assertEqual(b.s, "notation")
self.assertEqual(len(b.chunks), 3)
- def test_splice_string_without_end(self):
+ def test_splice_string_without_end(self) -> None:
a = fmtstr("notion")
b = a.splice("ta", 2)
self.assertEqual(a.s, "notion")
self.assertEqual(b.s, "notation")
self.assertEqual(len(b.chunks), 3)
- def test_multiple_bfs_splice(self):
+ def test_multiple_bfs_splice(self) -> None:
self.assertEqual(
fmtstr("a") + blue("b"),
on_blue(" " * 2).splice(fmtstr("a") + blue("b"), 0, 2),
@@ -188,13 +190,13 @@
class TestFmtStr(unittest.TestCase):
- def test_copy_with_new_atts(self):
+ def test_copy_with_new_atts(self) -> None:
a = fmtstr("hello")
b = a.copy_with_new_atts(bold=True)
self.assertEqual(a.shared_atts, {})
self.assertEqual(b.shared_atts, {"bold": True})
- def test_copy_with_new_str(self):
+ def test_copy_with_new_str(self) -> None:
# Change string but not attributes
a = fmtstr("hello", "blue")
b = a.copy_with_new_str("bye")
@@ -202,21 +204,21 @@
self.assertEqual(b.s, "bye")
self.assertEqual(a.chunks[0].atts, b.chunks[0].atts)
- def test_append_without_atts(self):
+ def test_append_without_atts(self) -> None:
a = fmtstr("no")
b = a.append("te")
self.assertEqual(a.s, "no")
self.assertEqual(b.s, "note")
self.assertEqual(len(b.chunks), 2)
- def test_shared_atts(self):
+ def test_shared_atts(self) -> None:
a = fmtstr("hi", "blue")
b = fmtstr("there", "blue")
c = a + b
self.assertTrue("fg" in a.shared_atts)
self.assertTrue("fg" in c.shared_atts)
- def test_new_with_atts_removed(self):
+ def test_new_with_atts_removed(self) -> None:
a = fmtstr("hi", "blue", "on_green")
b = fmtstr("there", "blue", "on_red")
c = a + b
@@ -224,13 +226,13 @@
c.new_with_atts_removed("fg"), on_green("hi") + on_red("there")
)
- def setUp(self):
+ def setUp(self) -> None:
self.s = fmtstr("hello!", "on_blue", fg="red")
- def test_length(self):
+ def test_length(self) -> None:
self.assertEqual(len(self.s), len(self.s.s))
- def test_split(self):
+ def test_split(self) -> None:
self.assertEqual(blue("hello there").split(" "), [blue("hello"),
blue("there")])
s = blue("hello there")
self.assertEqual(s.split(" "), [s[:5], s[6:]])
@@ -244,14 +246,14 @@
)
self.assertEqual(blue("abcbd").split("b"), [blue("a"), blue("c"),
blue("d")])
- def test_split_with_spaces(self):
+ def test_split_with_spaces(self) -> None:
self.assertEqual(blue("a\nb").split(), [blue("a"), blue("b")])
self.assertEqual(blue("a \t\n\nb").split(), [blue("a"), blue("b")])
self.assertEqual(
blue("hello \t\n\nthere").split(), [blue("hello"), blue("there")]
)
- def test_ljust_rjust(self):
+ def test_ljust_rjust(self) -> None:
b = fmtstr("ab", "blue", "on_red", "bold")
g = fmtstr("cd", "green", "on_red", "bold")
s = b + g
@@ -278,7 +280,7 @@
# formatted string passed in
# preserve some non-uniform styles (bold, dark, blink) but not others
(underline, invert)
- def test_linessplit(self):
+ def test_linessplit(self) -> None:
text = blue("the sum of the squares of the sideways")
result = [
blue("the") + blue(" ") + blue("sum"),
@@ -290,7 +292,7 @@
]
self.assertEqual(linesplit(text, 7), result)
- def test_mul(self):
+ def test_mul(self) -> None:
self.assertEqual(fmtstr("heyhey"), fmtstr("hey") * 2)
pass
# TODO raise common attributes when doing equality or when
@@ -300,17 +302,17 @@
# bold(blue('hey')+green('there')+blue('hey')+green('there')),
# bold(blue('hey')+green('there'))*2)
- def test_change_color(self):
+ def test_change_color(self) -> None:
a = blue(red("hello"))
self.assertEqual(a, blue("hello"))
- def test_repr(self):
+ def test_repr(self) -> None:
self.assertEqual(fmtstr("hello", "red", bold=False), red("hello"))
self.assertEqual(fmtstr("hello", "red", bold=True), bold(red("hello")))
class TestDoubleUnders(unittest.TestCase):
- def test_equality(self):
+ def test_equality(self) -> None:
x = fmtstr("adfs")
self.assertEqual(x, x)
self.assertTrue(fmtstr("adfs"), fmtstr("adfs"))
@@ -318,28 +320,28 @@
class TestConvenience(unittest.TestCase):
- def test_fg(self):
+ def test_fg(self) -> None:
red("asdf")
blue("asdf")
self.assertTrue(True)
- def test_bg(self):
+ def test_bg(self) -> None:
on_red("asdf")
on_blue("asdf")
self.assertTrue(True)
- def test_styles(self):
+ def test_styles(self) -> None:
underline("asdf")
blink("asdf")
self.assertTrue(True)
class TestSlicing(unittest.TestCase):
- def test_index(self):
+ def test_index(self) -> None:
self.assertEqual(fmtstr("Hi!", "blue")[0], fmtstr("H", "blue"))
self.assertRaises(IndexError, fmtstr("Hi!", "blue").__getitem__, 5)
- def test_slice(self):
+ def test_slice(self) -> None:
self.assertEqual(fmtstr("Hi!", "blue")[1:2], fmtstr("i", "blue"))
self.assertEqual(fmtstr("Hi!", "blue")[1:], fmtstr("i!", "blue"))
s = fmtstr("imp") + " "
@@ -351,7 +353,7 @@
class TestComposition(unittest.TestCase):
- def test_simple_composition(self):
+ def test_simple_composition(self) -> None:
a = fmtstr("hello ", "underline", "on_blue")
b = fmtstr("there", "red", "on_blue")
c = a + b
@@ -360,21 +362,21 @@
class TestUnicode(unittest.TestCase):
- def test_output_type(self):
+ def test_output_type(self) -> None:
self.assertEqual(type(str(fmtstr("hello", "blue"))), str)
- def test_normal_chars(self):
+ def test_normal_chars(self) -> None:
fmtstr("a", "blue")
str(fmtstr("a", "blue"))
self.assertTrue(True)
- def test_funny_chars(self):
+ def test_funny_chars(self) -> None:
fmtstr("???", "blue")
str(Chunk("???", {"fg": "blue"}))
str(fmtstr("???", "blue"))
self.assertTrue(True)
- def test_right_sequence_in_py3(self):
+ def test_right_sequence_in_py3(self) -> None:
red_on_blue = fmtstr("hello", "red", "on_blue")
blue_on_red = fmtstr("there", fg="blue", bg="red")
green_s = fmtstr("!", "green")
@@ -387,7 +389,7 @@
"\x1b[31m\x1b[44mhello\x1b[49m\x1b[39m
\x1b[34m\x1b[41mthere\x1b[49m\x1b[39m\x1b[32m!\x1b[39m",
)
- def test_len_of_unicode(self):
+ def test_len_of_unicode(self) -> None:
self.assertEqual(len(fmtstr("??????")), 2)
lines = ["??????", "an", "??????"]
r = fsarray(lines)
@@ -398,7 +400,7 @@
# always coerce everything to unicode?
# self.assertEqual(len(fmtstr('??????')), 2)
- def test_len_of_unicode_in_fsarray(self):
+ def test_len_of_unicode_in_fsarray(self) -> None:
fsa = FSArray(3, 2)
fsa.rows[0] = fsa.rows[0].setslice_with_length(0, 2, "??????", 2)
@@ -406,7 +408,7 @@
fsa.rows[0] = fsa.rows[0].setslice_with_length(0, 2, fmtstr("??????",
"blue"), 2)
self.assertEqual(fsa.shape, (3, 2))
- def test_add_unicode_to_byte(self):
+ def test_add_unicode_to_byte(self) -> None:
fmtstr("???") + fmtstr("a")
fmtstr("a") + fmtstr("???")
"???" + fmtstr("???")
@@ -414,30 +416,30 @@
fmtstr("???") + "???"
fmtstr("a") + "???"
- def test_unicode_slicing(self):
+ def test_unicode_slicing(self) -> None:
self.assertEqual(fmtstr("???adfs", "blue")[:2], fmtstr("???a", "blue"))
self.assertEqual(
type(fmtstr("???adfs", "blue")[:2].s), type(fmtstr("???a",
"blue").s)
)
self.assertEqual(len(fmtstr("???adfs", "blue")[:2]), 2)
- def test_unicode_repr(self):
+ def test_unicode_repr(self) -> None:
repr(Chunk("???"))
self.assertEqual(repr(fmtstr("???")), repr_without_leading_u("???"))
class TestCharacterWidth(unittest.TestCase):
- def test_doublewide_width(self):
+ def test_doublewide_width(self) -> None:
self.assertEqual(len(fmtstr("???", "blue")), 1)
self.assertEqual(fmtstr("???", "blue").width, 2)
self.assertEqual(len(fmtstr("??????")), 2)
self.assertEqual(fmtstr("??????").width, 4)
- def test_multi_width(self):
+ def test_multi_width(self) -> None:
self.assertEqual(len(fmtstr("a\u0300")), 2)
self.assertEqual(fmtstr("a\u0300").width, 1)
- def test_width_aware_slice(self):
+ def test_width_aware_slice(self) -> None:
self.assertEqual(fmtstr("???").width_aware_slice(slice(None, 1,
None)).s, " ")
self.assertEqual(fmtstr("???").width_aware_slice(slice(None, 2,
None)).s, "???")
self.assertEqual(
@@ -449,7 +451,7 @@
fmtstr("???", "blue"),
)
- def test_width_aware_splitlines(self):
+ def test_width_aware_splitlines(self) -> None:
s = fmtstr("abcd")
self.assertEqual(
list(s.width_aware_splitlines(2)), [fmtstr("ab"), fmtstr("cd")]
@@ -469,7 +471,7 @@
with self.assertRaises(ValueError):
s.width_aware_splitlines(1)
- def test_width_at_offset(self):
+ def test_width_at_offset(self) -> None:
self.assertEqual(fmtstr("ab???cdef").width_at_offset(0), 0)
self.assertEqual(fmtstr("ab???cdef").width_at_offset(2), 2)
self.assertEqual(fmtstr("ab???cdef").width_at_offset(3), 4)
@@ -482,7 +484,7 @@
class TestWidthHelpers(unittest.TestCase):
- def test_combining_char_aware_slice(self):
+ def test_combining_char_aware_slice(self) -> None:
self.assertEqual(width_aware_slice("abc", 0, 2), "ab")
self.assertEqual(width_aware_slice("abc", 1, 3), "bc")
self.assertEqual(width_aware_slice("abc", 0, 3), "abc")
@@ -493,7 +495,7 @@
self.assertEqual(width_aware_slice("ab\u0300\u0300c", 0, 2),
"ab\u0300\u0300")
self.assertEqual(width_aware_slice("ab\u0300\u0300c", 2, 3), "c")
- def test_char_width_aware_slice(self):
+ def test_char_width_aware_slice(self) -> None:
self.assertEqual(width_aware_slice("abc", 1, 2), "b")
self.assertEqual(width_aware_slice("a???bc", 0, 4), "a???b")
self.assertEqual(width_aware_slice("a???bc", 1, 4), "???b")
@@ -502,19 +504,19 @@
class TestChunk(unittest.TestCase):
- def test_repr(self):
+ def test_repr(self) -> None:
c = Chunk("a", {"fg": 32})
self.assertEqual(repr(c), """Chunk('a', {'fg': 32})""")
class TestChunkSplitter(unittest.TestCase):
- def test_chunk_splitter(self):
+ def test_chunk_splitter(self) -> None:
splitter = Chunk("asdf", {"fg": 32}).splitter()
self.assertEqual(splitter.request(1), (1, Chunk("a", {"fg": 32})))
self.assertEqual(splitter.request(4), (3, Chunk("sdf", {"fg": 32})))
self.assertEqual(splitter.request(4), None)
- def test_reusing_same_splitter(self):
+ def test_reusing_same_splitter(self) -> None:
c = Chunk("asdf", {"fg": 32})
s1 = c.splitter()
self.assertEqual(s1.request(3), (3, Chunk("asd", {"fg": 32})))
@@ -527,8 +529,8 @@
s1.reinit(c2)
self.assertEqual(s1.request(3), (3, Chunk("abc")))
- def test_width_awareness(self):
- s = Chunk("asdf")
+ def test_width_awareness(self) -> None:
+ Chunk("asdf")
self.assertEqual(
Chunk("ab\u0300c").splitter().request(3), (3, Chunk("ab\u0300c"))
)
@@ -550,19 +552,19 @@
class TestFSArray(unittest.TestCase):
- def test_no_hanging_space(self):
+ def test_no_hanging_space(self) -> None:
a = FSArray(4, 2)
self.assertEqual(len(a.rows[0]), 0)
- def test_assignment_working(self):
+ def test_assignment_working(self) -> None:
t = FSArray(10, 10)
t[2, 2] = "a"
# TODO: is this supposed to check something?
t[2, 2] == "a"
- def test_normalize_slice(self):
+ def test_normalize_slice(self) -> None:
class SliceBuilder:
- def __getitem__(self, slice):
+ def __getitem__(self, slice: slice) -> slice:
return slice
Slice = SliceBuilder()
@@ -571,14 +573,13 @@
self.assertEqual(normalize_slice(11, Slice[3:]), slice(3, 11, None))
@skip("TODO")
- def test_oomerror(self):
+ def test_oomerror(self) -> None:
a = FSArray(10, 40)
a[2:-2, 2:-2] = fsarray(["asdf", "zxcv"])
class FormatStringTest(unittest.TestCase):
- def assertFSArraysEqual(self, a, b):
- # type: (FSArray, FSArray) -> None
+ def assertFSArraysEqual(self, a: FSArray, b: FSArray) -> None:
self.assertIsInstance(a, FSArray)
self.assertIsInstance(b, FSArray)
self.assertEqual(
@@ -593,8 +594,7 @@
"FSArrays differ first on line {}:\n{}".format(i,
FSArray.diff(a, b)),
)
- def assertFSArraysEqualIgnoringFormatting(self, a, b):
- # type: (FSArray, FSArray) -> None
+ def assertFSArraysEqualIgnoringFormatting(self, a: FSArray, b: FSArray) ->
None:
"""Also accepts arrays of strings"""
self.assertEqual(
len(a),
@@ -614,7 +614,7 @@
class TestFSArrayWithDiff(FormatStringTest):
- def test_diff_testing(self):
+ def test_diff_testing(self) -> None:
a = fsarray(["abc", "def"])
b = fsarray(["abc", "dqf"])
self.assertRaises(AssertionError, self.assertFSArraysEqual, a, b)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/tests/test_input.py
new/curtsies-0.4.0/tests/test_input.py
--- old/curtsies-0.3.10/tests/test_input.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/tests/test_input.py 2022-08-28 22:39:24.000000000
+0200
@@ -141,3 +141,9 @@
t = threading.Thread(target=use)
t.start()
t.join()
+
+ def test_cleanup(self):
+ input_generator = Input()
+ for i in range(1000):
+ with input_generator:
+ pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/tests/test_terminal.py
new/curtsies-0.4.0/tests/test_terminal.py
--- old/curtsies-0.3.10/tests/test_terminal.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/tests/test_terminal.py 2022-08-28 22:39:24.000000000
+0200
@@ -7,7 +7,7 @@
from io import StringIO
from unittest import skipUnless, skipIf, expectedFailure
-import blessings
+import blessed
import pyte
from pyte import control as ctrl, Stream, Screen
@@ -88,7 +88,7 @@
pass
-@skipUnless(sys.stdin.isatty(), "blessings Terminal needs streams open")
+@skipUnless(sys.stdin.isatty(), "blessed Terminal needs streams open")
class TestFullscreenWindow(unittest.TestCase):
def setUp(self):
self.screen = pyte.Screen(10, 3)
@@ -121,7 +121,7 @@
pass
-@skipUnless(sys.stdin.isatty(), "blessings Terminal needs streams open")
+@skipUnless(sys.stdin.isatty(), "blessed Terminal needs streams open")
class TestCursorAwareWindow(unittest.TestCase):
def setUp(self):
self.screen = ReportingScreen(6, 3)
@@ -133,8 +133,8 @@
out_stream=stdout, in_stream=self.screen._report_file
)
self.window.cbreak = NopContext()
- blessings.Terminal.height = 3
- blessings.Terminal.width = 6
+ blessed.Terminal.height = 3
+ blessed.Terminal.width = 6
# This isn't passing locally for me anymore :/
@expectedFailure
@@ -162,7 +162,7 @@
self.assertEqual(self.screen.display, [" ", "hi ", "there
"])
-@skipUnless(sys.stdin.isatty(), "blessings Terminal needs streams open")
+@skipUnless(sys.stdin.isatty(), "blessed Terminal needs streams open")
class TestCursorAwareWindowWithExtraInput(unittest.TestCase):
def setUp(self):
self.screen = ReportingScreenWithExtra(6, 3)
@@ -177,8 +177,8 @@
extra_bytes_callback=self.extra_bytes_callback,
)
self.window.cbreak = NopContext()
- blessings.Terminal.height = 3
- blessings.Terminal.width = 6
+ blessed.Terminal.height = 3
+ blessed.Terminal.width = 6
def extra_bytes_callback(self, bytes):
self.extra_bytes.append(bytes)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/curtsies-0.3.10/tests/test_window.py
new/curtsies-0.4.0/tests/test_window.py
--- old/curtsies-0.3.10/tests/test_window.py 2021-09-25 20:39:51.000000000
+0200
+++ new/curtsies-0.4.0/tests/test_window.py 2022-08-28 22:39:24.000000000
+0200
@@ -14,7 +14,7 @@
height = property(lambda self: 4)
-@skipIf(fds_closed, "blessings Terminal needs streams open")
+@skipIf(fds_closed, "blessed Terminal needs streams open")
class TestBaseWindow(unittest.TestCase):
"""Pretty pathetic tests for window"""