Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix for term.split_seqs('term.right(3)') #197 #199

Merged
merged 7 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ matrix:
env: PATH=/c/Python38:/c/Python38/Scripts:$PATH TOXENV=py38,codecov COVERAGE_ID=travis-ci TEST_KEYBOARD=no

allow_failures:
- python: 3.9-dev
jquast marked this conversation as resolved.
Show resolved Hide resolved
- python: 3.10-dev

install:
Expand Down
2 changes: 1 addition & 1 deletion blessed/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
'support due to http://bugs.python.org/issue10570.')

__all__ = ('Terminal',)
__version__ = '1.18.0'
__version__ = "1.18.1"
22 changes: 19 additions & 3 deletions blessed/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1100,20 +1100,36 @@ def strip_seqs(self, text):
"""
return Sequence(text, self).strip_seqs()

def split_seqs(self, text, **kwds):
def split_seqs(self, text, maxsplit=0, flags=0):
jquast marked this conversation as resolved.
Show resolved Hide resolved
r"""
Return ``text`` split by individual character elements and sequences.

:arg str text: String containing sequences
:arg kwds: remaining keyword arguments for :func:`re.split`.
:arg int maxsplit: When maxsplit is nonzero, at most maxsplit splits
occur, and the remainder of the string is returned as the final element
of the list (same meaning is argument for :func:`re.split`).
:arg int flags: regex flags, combined integer flag attributes documented
beginning with flag attribute :attr:`re.A` (same meaning is argument
for :func:`re.split`).
:rtype: list[str]
:returns: List of sequences and individual characters

>>> term.split_seqs(term.underline(u'xyz'))
['\x1b[4m', 'x', 'y', 'z', '\x1b(B', '\x1b[m']

>>> term.split_seqs(term.underline(u'xyz'), 1)
['\x1b[4m', r'xyz\x1b(B\x1b[m']
"""
pattern = self._caps_unnamed_any
return list(filter(None, re.split(pattern, text, **kwds)))
result = []
for idx, match in enumerate(re.finditer(pattern, text, flags)):
result.append(match.group())
if maxsplit and idx == maxsplit:
remaining = text[match.end():]
if remaining:
result[-1] += remaining
break
return result

def wrap(self, text, width=None, **kwargs):
"""
Expand Down
2 changes: 1 addition & 1 deletion blessed/terminal.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class Terminal:
def rstrip(self, text: str, chars: Optional[str] = ...) -> str: ...
def lstrip(self, text: str, chars: Optional[str] = ...) -> str: ...
def strip_seqs(self, text: str) -> str: ...
def split_seqs(self, text: str, **kwds: Any) -> List[str]: ...
def split_seqs(self, text: str, maxsplit: int, flags: int) -> List[str]: ...
def wrap(
self, text: str, width: Optional[int] = ..., **kwargs: Any
) -> List[str]: ...
Expand Down
2 changes: 2 additions & 0 deletions docs/history.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Version History
===============
1.18
* bugfix: :meth:`~Terminal.split_seqs` for some sequences
like ``term.move_left(3)``, :ghissue:`197`.
* introduced: type annotations, :ghissue:`192` by :ghuser:`dlax`.
* bugfix: do not fail when ``sys.stdin`` is unset, :ghissue:`195` by
:ghuser:`Olen`
Expand Down
47 changes: 0 additions & 47 deletions run_codecov.py

This file was deleted.

31 changes: 1 addition & 30 deletions tests/accessories.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""Accessories for automated py.test runner."""
# standard imports
from __future__ import print_function, with_statement

# std imports
Expand Down Expand Up @@ -40,15 +39,6 @@
TestTerminal = functools.partial(Terminal, kind=test_kind) # type: Callable[..., Terminal]
SEND_SEMAPHORE = SEMAPHORE = b'SEMAPHORE\n'
RECV_SEMAPHORE = b'SEMAPHORE\r\n'
many_lines_params = [40, 80]
# we must test a '1' column for conditional in _handle_long_word
many_columns_params = [1, 10]

if os.environ.get('TEST_QUICK'):
many_lines_params = [80, ]
many_columns_params = [25, ]

all_terms_params = 'xterm screen ansi vt220 rxvt cons25 linux'.split()

if os.environ.get('TEST_FULL'):
try:
Expand Down Expand Up @@ -164,8 +154,7 @@ def __call__(self, *args, **kwargs):
assert os.WEXITSTATUS(status) == 0


def read_until_semaphore(fd, semaphore=RECV_SEMAPHORE,
encoding='utf8', timeout=10):
def read_until_semaphore(fd, semaphore=RECV_SEMAPHORE, encoding='utf8'):
"""
Read file descriptor ``fd`` until ``semaphore`` is found.

Expand Down Expand Up @@ -254,21 +243,3 @@ def unicode_parm(cap, *parms):
if val:
return val.decode('latin1')
return u''


@pytest.fixture(params=all_terms_params)
def all_terms(request):
"""Common kind values for all kinds of terminals."""
return request.param


@pytest.fixture(params=many_lines_params)
def many_lines(request):
"""Various number of lines for screen height."""
return request.param


@pytest.fixture(params=many_columns_params)
def many_columns(request):
"""Various number of columns for screen width."""
return request.param
32 changes: 32 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# std imports
import os

# 3rd party
import pytest

all_terms_params = 'xterm screen ansi vt220 rxvt cons25 linux'.split()
jquast marked this conversation as resolved.
Show resolved Hide resolved
many_lines_params = [40, 80]
# we must test a '1' column for conditional in _handle_long_word
many_columns_params = [1, 10]

if os.environ.get('TEST_QUICK'):
many_lines_params = [80, ]
many_columns_params = [25, ]


@pytest.fixture(params=all_terms_params)
def all_terms(request):
"""Common kind values for all kinds of terminals."""
return request.param


@pytest.fixture(params=many_lines_params)
def many_lines(request):
"""Various number of lines for screen height."""
return request.param


@pytest.fixture(params=many_columns_params)
def many_columns(request):
"""Various number of columns for screen width."""
return request.param
2 changes: 1 addition & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from six.moves import reload_module

# local
from .accessories import TestTerminal, all_terms, unicode_cap, as_subprocess
from .accessories import TestTerminal, unicode_cap, as_subprocess


def test_export_only_Terminal():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import pytest

# local
from .accessories import TestTerminal, all_terms, as_subprocess
from .accessories import TestTerminal, as_subprocess

if platform.system() != 'Windows':
import curses
Expand Down
4 changes: 2 additions & 2 deletions tests/test_length_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import six
import pytest

from .accessories import ( # isort:skip
TestTerminal, as_subprocess, all_terms, many_lines, many_columns)
# local
from .accessories import TestTerminal, as_subprocess

if platform.system() != 'Windows':
import fcntl
Expand Down
52 changes: 50 additions & 2 deletions tests/test_sequences.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pytest

# local
from .accessories import TestTerminal, all_terms, unicode_cap, unicode_parm, as_subprocess
from .accessories import TestTerminal, unicode_cap, unicode_parm, as_subprocess


@pytest.mark.skipif(platform.system() == 'Windows', reason="requires real tty")
Expand Down Expand Up @@ -543,9 +543,57 @@ def child(kind):
result = list(term.split_seqs(given_text))
assert result == expected

child(all_terms)


def test_split_seqs_maxsplit1(all_terms):
"""Test Terminal.split_seqs with maxsplit=1."""
@as_subprocess
def child(kind):
from blessed import Terminal
term = Terminal(kind)

if term.bold:
given_text = term.bold + 'bbq'
expected = [term.bold, 'b', 'b', 'q']
expected = [term.bold, 'bbq']
result = list(term.split_seqs(given_text, 1))
assert result == expected

child(all_terms)


def test_split_seqs_term_right(all_terms):
"""Test Terminal.split_seqs with parameterized sequence"""
@as_subprocess
def child(kind):
from blessed import Terminal
term = Terminal(kind)

if term.move_up:
given_text = 'XY' + term.move_right + 'VK'
expected = ['X', 'Y', term.move_right, 'V', 'K']
result = list(term.split_seqs(given_text))
assert result == expected

child(all_terms)


def test_split_seqs_maxsplit3_and_term_right(all_terms):
"""Test Terminal.split_seqs with parameterized sequence."""
@as_subprocess
def child(kind):
from blessed import Terminal
term = Terminal(kind)

if term.move_right(32):
given_text = 'PQ' + term.move_right(32) + 'RS'
expected = ['P', 'Q', term.move_right(32), 'RS']
result = list(term.split_seqs(given_text, 3))
assert result == expected

if term.move_up(45):
given_text = 'XY' + term.move_up(45) + 'VK'
expected = ['X', 'Y', term.move_up(45), 'V', 'K']
result = list(term.split_seqs(given_text))
assert result == expected

Expand Down
2 changes: 1 addition & 1 deletion tests/test_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytest

# local
from .accessories import TestTerminal, many_columns, as_subprocess
from .accessories import TestTerminal, as_subprocess

TEXTWRAP_KEYWORD_COMBINATIONS = [
dict(break_long_words=False,
Expand Down
10 changes: 6 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[tox]
envlist = about
stamp
autopep8
docformatter
isort
Expand Down Expand Up @@ -138,6 +139,9 @@ commands = python {toxinidir}/bin/display-sighandlers.py
python {toxinidir}/bin/display-terminalinfo.py
python {toxinidir}/bin/display-fpathconf.py

[testenv:stamp]
commands = python {toxinidir}/version.py

[testenv:autopep8]
deps = autopep8==1.4.4
commands =
Expand Down Expand Up @@ -208,10 +212,8 @@ commands = {envbindir}/sphinx-build -v -W -d {toxinidir}/docs/_build/doctrees -b
[testenv:codecov]
basepython = python{env:TOXPYTHON:{env:TRAVIS_PYTHON_VERSION:3.8}}
passenv = TOXENV CI TRAVIS TRAVIS_* CODECOV_*
deps = codecov>=1.4.0
# commands = codecov -e TOXENV
# Workaround for https://github.com/codecov/codecov-python/issues/158
commands = {envpython} run_codecov.py -e TOXENV
avylove marked this conversation as resolved.
Show resolved Hide resolved
deps = codecov>=2.1
commands = codecov -e TOXENV

[testenv:publish_static]
# Synchronize the artifacts in docs/_static/ with https://dxtz6bzwq9sxx.cloudfront.net/
Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version": "1.18.0"}
{"version": "1.18.1"}
24 changes: 24 additions & 0 deletions version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env python3
Copy link
Collaborator

@avylove avylove Mar 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better than maintaining it in two places, but what is version.json used for? I typically set __version__ in __init__.py and parse it in setup.py.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# std imports
import os
import re
import json


def main():
# I don't know why, we maintain __version__ in blessed, because that's
# how it was done a long time ago before pip, anyway we do basic
# code generation, version.json -> __init__.py
fpath_json = os.path.join(os.path.dirname(__file__), 'version.json')
version = json.load(open(fpath_json, 'r'))['version']
fpath_py = os.path.join(os.path.dirname(__file__), 'blessed', '__init__.py')
prev_text = open(fpath_py, 'r').read()
next_text = re.sub(r"(__version__ = )(.*)$", r'\1"{0}"'.format(version),
prev_text, flags=re.MULTILINE)
if prev_text != next_text:
print('Updating blessed.__version__ to {}'.format(version))
open(fpath_py, 'w').write(next_text)


if __name__ == '__main__':
main()