Hello community,

here is the log from the commit of package python-pyte for openSUSE:Factory 
checked in at 2017-11-10 14:56:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyte (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyte.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyte"

Fri Nov 10 14:56:36 2017 rev:4 rq:539575 version:0.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyte/python-pyte.changes  2017-09-05 
15:19:32.600776416 +0200
+++ /work/SRC/openSUSE:Factory/.python-pyte.new/python-pyte.changes     
2017-11-10 14:56:55.185114772 +0100
@@ -1,0 +2,25 @@
+Fri Nov  3 15:54:05 UTC 2017 - [email protected]
+
+- update to version 0.7.0:
+  * Removed deprecated "only" parameter of "Stream.attach".
+  * Removed deprecated "encoding" parameter of "ByteStream".
+  * Fixed "how == 3" handling in "DiffScreen.erase_in_display".
+  * Deprecated "DiffScreen". Its functionality has been backported to
+    the base "Screen" class.
+  * Fixed a bug in "DiffScreen.draw" which incorrectly handled the
+    case when the input of "draw" required several lines.
+  * Fixed a bug in "Screen" which did not ignore "ESC (" argument in
+    UTF8 mode. See issue #88 on GitHub.
+  * Changed "Screen.resize" to do nothing if the requested size
+    matches the current one.
+  * Disallowed private mode for
+    "Screen.report_device_attributes". This was causing an infinite
+    loop in Emacs and Vim. See issue #81 on GitHub.
+  * Fixed a bug in `OSC` parsing, which caused "Stream" to hang upon
+    receiving a palette reset request "ESC ] R".
+  * Changed "Screen.reset" not to reset `DECOM`. See discussion in
+    issue #95 on Github.
+  * Changed the first tabstop to be at the 9-th column. See PR #98 on
+    GitHub. Thanks to @gordon-quad!
+
+-------------------------------------------------------------------

Old:
----
  pyte-0.6.0.tar.gz

New:
----
  pyte-0.7.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyte.spec ++++++
--- /var/tmp/diff_new_pack.g8YTpi/_old  2017-11-10 14:56:56.277075276 +0100
+++ /var/tmp/diff_new_pack.g8YTpi/_new  2017-11-10 14:56:56.281075132 +0100
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %bcond_with     test
 Name:           python-pyte
-Version:        0.6.0
+Version:        0.7.0
 Release:        0
 Summary:        VTXXX-compatible linux terminal emulator
 License:        LGPL-3.0

++++++ pyte-0.6.0.tar.gz -> pyte-0.7.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/AUTHORS new/pyte-0.7.0/AUTHORS
--- old/pyte-0.6.0/AUTHORS      2017-03-26 22:07:50.000000000 +0200
+++ new/pyte-0.7.0/AUTHORS      2017-10-07 23:04:31.000000000 +0200
@@ -14,3 +14,4 @@
 - Andreas Stührk
 - Dmitriy Novozhilov
 - Sergey Zavgorodniy
+- Byron Roosa
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/CHANGES new/pyte-0.7.0/CHANGES
--- old/pyte-0.6.0/CHANGES      2017-05-28 03:52:57.000000000 +0200
+++ new/pyte-0.7.0/CHANGES      2017-10-07 23:07:08.000000000 +0200
@@ -3,6 +3,33 @@
 
 Here you can see the full list of changes between each pyte release.
 
+Version 0.7.0
+-------------
+
+Released on October 7th 2017.
+
+This release is NOT backward compatible with 0.6.X branch!
+
+- Removed deprecated ``only`` parameter of ``Stream.attach``.
+- Removed deprecated ``encoding`` parameter of ``ByteStream``.
+- Fixed ``how == 3`` handling in ``DiffScreen.erase_in_display``.
+- Deprecated ``DiffScreen``. Its functionality has been backported
+  to the base ``Screen`` class.
+- Fixed a bug in ``DiffScreen.draw`` which incorrectly handled the
+  case when the input of ``draw`` required several lines.
+- Fixed a bug in ``Screen`` which did not ignore ``ESC (`` argument in
+  UTF8 mode. See issue #88 on GitHub.
+- Changed ``Screen.resize`` to do nothing if the requested size matches
+  the current one.
+- Disallowed private mode for ``Screen.report_device_attributes``. This
+  was causing an infinite loop in Emacs and Vim. See issue #81 on GitHub.
+- Fixed a bug in `OSC` parsing, which caused ``Stream`` to hang upon
+  receiving a palette reset request ``ESC ] R``.
+- Changed ``Screen.reset`` not to reset `DECOM`. See discussion in issue
+  #95 on Github.
+- Changed the first tabstop to be at the 9-th column. See PR #98 on
+  GitHub. Thanks to @gordon-quad!
+
 Version 0.6.0
 -------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/PKG-INFO new/pyte-0.7.0/PKG-INFO
--- old/pyte-0.6.0/PKG-INFO     2017-05-28 03:55:53.000000000 +0200
+++ new/pyte-0.7.0/PKG-INFO     2017-10-07 23:08:35.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyte
-Version: 0.6.0
+Version: 0.7.0
 Summary: Simple VTXXX-compatible terminal emulator.
 Home-page: https://github.com/selectel/pyte
 Author: Sergei Lebedev
@@ -17,7 +17,7 @@
                 | |_) || |_| || |_|  __/
                 | .__/  \__, | \__|\___|
                 | |      __/ |
-                |_|     |___/      0.6.0
+                |_|     |___/      0.7.0
         
         
         What is ``pyte``?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/README new/pyte-0.7.0/README
--- old/pyte-0.6.0/README       2016-06-30 01:40:08.000000000 +0200
+++ new/pyte-0.7.0/README       2017-10-07 23:07:42.000000000 +0200
@@ -9,7 +9,7 @@
         | |_) || |_| || |_|  __/
         | .__/  \__, | \__|\___|
         | |      __/ |
-        |_|     |___/      0.6.0
+        |_|     |___/      0.7.0
 
 
 What is ``pyte``?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/docs/api.rst new/pyte-0.7.0/docs/api.rst
--- old/pyte-0.6.0/docs/api.rst 2016-01-10 13:06:21.000000000 +0100
+++ new/pyte-0.7.0/docs/api.rst 2017-05-29 22:01:56.000000000 +0200
@@ -4,14 +4,49 @@
 API reference
 =============
 
-.. automodule:: pyte
-
 .. automodule:: pyte.streams
-    :members:
+
+   pyte.Stream
+   ^^^^^^^^^^^
+
+   .. autoclass:: pyte.Stream
+
+   pyte.ByteStream
+   ^^^^^^^^^^^^^^^
+
+   .. autoclass:: pyte.ByteStream
 
 .. automodule:: pyte.screens
-    :members:
-    :exclude-members: Margins, Savepoint, _Char, Char, History, take
+
+   pyte.screens.Screen
+   ^^^^^^^^^^^^^^^^^^^
+
+   .. autoclass:: pyte.screens.Cursor
+      :members:
+
+   .. autoclass:: pyte.screens.Char
+      :members:
+
+   .. autoclass:: pyte.screens.Screen
+      :members:
+
+   pyte.screens.DiffScreen
+   ^^^^^^^^^^^^^^^^^^^^^^^
+
+   .. autoclass:: pyte.screens.DiffScreen
+
+   pyte.screens.HistoryScreen
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+   .. autoclass:: pyte.screens.History
+
+   .. autoclass:: pyte.screens.HistoryScreen
+      :members:
+
+   pyte.screens.DebugScreen
+   ^^^^^^^^^^^^^^^^^^^^^^^^
+
+   .. autoclass:: pyte.screens.DebugScreen
 
 .. automodule:: pyte.modes
     :members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/docs/conf.py new/pyte-0.7.0/docs/conf.py
--- old/pyte-0.6.0/docs/conf.py 2017-03-11 13:57:43.000000000 +0100
+++ new/pyte-0.7.0/docs/conf.py 2017-10-07 23:07:51.000000000 +0200
@@ -52,9 +52,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '0.6.0'
+version = '0.7.0'
 # The full version, including alpha/beta/rc tags.
-release = '0.6.0'
+release = '0.7.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/docs/index.rst 
new/pyte-0.7.0/docs/index.rst
--- old/pyte-0.6.0/docs/index.rst       2016-03-20 12:54:03.000000000 +0100
+++ new/pyte-0.7.0/docs/index.rst       2017-06-11 21:30:40.000000000 +0200
@@ -14,6 +14,9 @@
   multiplexor.
 * `BastionSSH <https://github.com/wcc526/bastion-ssh>`_ -- a tool for
   protecting, monitoring and accessing multiple SSH resources.
+* `Jumpserver <https://github.com/jumpserver/jumpserver>`_ -- an open source
+  springboard machine(fortress machine): authentication, authorization,
+  audit, automated operation and maintenance.
 
 .. note:: Using ``pyte``? Add yourself to this list and submit a pull request.
 
@@ -27,7 +30,6 @@
 
 
 .. toctree::
-   :hidden:
 
    tutorial
    api
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/examples/webterm.py 
new/pyte-0.7.0/examples/webterm.py
--- old/pyte-0.6.0/examples/webterm.py  2017-05-28 03:27:49.000000000 +0200
+++ new/pyte-0.7.0/examples/webterm.py  2017-10-07 23:02:28.000000000 +0200
@@ -5,9 +5,19 @@
     An example showing how to use :mod:`pyte` to implement a basic
     single-user web terminal.
 
+    Client-side ``webterm.js`` supports
+    * incremental rendering via :data:`~pyte.screens.DiffScreen.dirty`,
+    * most of the common keyboard events,
+    * pagination on Meta + P/Meta + A.
+
     .. note:: This example requires at least Python 3.5 and a recent
               version of ``aiohttp`` library.
 
+    .. seealso::
+
+       `The TTY demystified <http://www.linusakesson.net/programming/tty>`_
+       for an introduction to the inner workings of the TTY subsystem.
+
     :copyright: (c) 2017 by pyte authors and contributors,
                 see AUTHORS for details.
     :license: LGPL, see LICENSE for more details.
@@ -30,7 +40,7 @@
 
 class Terminal:
     def __init__(self, columns, lines, p_in):
-        self.screen = pyte.DiffScreen(columns, lines)
+        self.screen = pyte.HistoryScreen(columns, lines)
         self.screen.set_mode(pyte.modes.LNM)
         self.screen.write_process_input = \
             lambda data: p_in.write(data.encode())
@@ -84,7 +94,14 @@
     try:
         async for msg in ws:
             if msg.type == aiohttp.WSMsgType.TEXT:
-                p_out.write(msg.data.encode())
+                if msg.data == pyte.control.ESC + "N":
+                    terminal.screen.next_page()
+                    ws.send_str(terminal.dumps())
+                elif msg.data == pyte.control.ESC + "P":
+                    terminal.screen.prev_page()
+                    ws.send_str(terminal.dumps())
+                else:
+                    p_out.write(msg.data.encode())
             elif msg.type == aiohttp.WSMsgType.ERROR:
                 raise ws.exception()
     except (asyncio.CancelledError,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/pyte/graphics.py 
new/pyte-0.7.0/pyte/graphics.py
--- old/pyte-0.6.0/pyte/graphics.py     2017-03-11 13:57:20.000000000 +0100
+++ new/pyte-0.7.0/pyte/graphics.py     2017-06-11 21:34:13.000000000 +0200
@@ -23,7 +23,7 @@
 #: >>> text[9]
 #: '+strikethrough'
 TEXT = {
-    1: "+bold" ,
+    1: "+bold",
     3: "+italics",
     4: "+underscore",
     7: "+reverse",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/pyte/screens.py 
new/pyte-0.7.0/pyte/screens.py
--- old/pyte-0.6.0/pyte/screens.py      2017-05-28 03:52:05.000000000 +0200
+++ new/pyte-0.7.0/pyte/screens.py      2017-10-07 23:02:16.000000000 +0200
@@ -28,13 +28,13 @@
 
 from __future__ import absolute_import, unicode_literals, division
 
-import codecs
 import copy
 import json
 import math
 import os
 import sys
 import unicodedata
+import warnings
 from collections import deque, namedtuple, defaultdict
 
 from wcwidth import wcwidth
@@ -63,14 +63,12 @@
     "g0_charset",
     "g1_charset",
     "charset",
-    "use_utf8",
     "origin",
     "wrap"
 ])
 
-#: A container for a single character, field names are *hopefully*
-#: self-explanatory.
-_Char = namedtuple("_Char", [
+
+class Char(namedtuple("Char", [
     "data",
     "fg",
     "bg",
@@ -79,18 +77,28 @@
     "underscore",
     "strikethrough",
     "reverse",
-])
+])):
+    """A single styled on-screen character.
 
-
-class Char(_Char):
-    """A wrapper around :class:`_Char`, providing some useful defaults
-    for most of the attributes.
+    :param str data: unicode character. Invariant: ``len(data) == 1``.
+    :param str fg: foreground colour. Defaults to ``"default"``.
+    :param str bg: background colour. Defaults to ``"default"``.
+    :param bool bold: flag for rendering the character using bold font.
+                      Defaults to ``False``.
+    :param bool italics: flag for rendering the character using italic font.
+                         Defaults to ``False``.
+    :param bool underline: flag for rendering the character underlined.
+                           Defaults to ``False``.
+    :param bool strikethrough: flag for rendering the character with a
+                               strike-through line. Defaults to ``False``.
+    :param bool reverse: flag for swapping foreground and background colours
+                         during rendering. Defaults to ``False``.
     """
     __slots__ = ()
 
     def __new__(cls, data, fg="default", bg="default", bold=False,
-                italics=False, underscore=False, reverse=False,
-                strikethrough=False):
+                italics=False, underscore=False,
+                strikethrough=False, reverse=False):
         return super(Char, cls).__new__(cls, data, fg, bg, bold, italics,
                                         underscore, strikethrough, reverse)
 
@@ -143,6 +151,19 @@
 
        A sparse ``lines x columns`` :class:`~pyte.screens.Char` matrix.
 
+    .. attribute:: dirty
+
+       A set of line numbers, which should be re-drawn. The user is responsible
+       for clearing this set when changes have been applied.
+
+       >>> screen = Screen(80, 24)
+       >>> screen.dirty.clear()
+       >>> screen.draw("!")
+       >>> list(screen.dirty)
+       [0]
+
+       .. versionadded:: 0.7.0
+
     .. attribute:: cursor
 
        Reference to the :class:`~pyte.screens.Cursor` object, holding
@@ -150,8 +171,12 @@
 
     .. attribute:: margins
 
-       Top and bottom screen margins, defining the scrolling region;
-       the actual values are top and bottom line.
+       Margins determine which screen lines move during scrolling
+       (see :meth:`index` and :meth:`reverse_index`). Characters added
+       outside the scrolling region do not make the screen to scroll.
+
+       The value is ``None`` if margins are set to screen boundaries,
+       otherwise -- a pair 0-based top and bottom line indices.
 
     .. attribute:: charset
 
@@ -168,7 +193,7 @@
     .. warning::
 
        :data:`~pyte.modes.LNM` is reset by default, to match VT220
-       specification. Unfortunatelly this makes :mode:`pyte` fail
+       specification. Unfortunatelly this makes :mod:`pyte` fail
        ``vttest`` for cursor movement.
 
     .. versionchanged:: 0.4.8
@@ -185,8 +210,7 @@
        for a description of the presentational component, implemented
        by ``Screen``.
     """
-    #: A plain empty character with default foreground and background
-    #: colors.
+    #: An empty character with default foreground and background colors.
     default_char = Char(data=" ", fg="default", bg="default")
 
     def __init__(self, columns, lines):
@@ -194,6 +218,7 @@
         self.columns = columns
         self.lines = lines
         self.buffer = defaultdict(lambda: StaticDefaultDict(self.default_char))
+        self.dirty = set()
         self.reset()
 
     def __repr__(self):
@@ -219,12 +244,13 @@
     def reset(self):
         """Reset the terminal to its initial state.
 
-        * Scroll margins are reset to screen boundaries.
+        * Scrolling margins are reset to screen boundaries.
         * Cursor is moved to home location -- ``(0, 0)`` and its
           attributes are set to defaults (see :attr:`default_char`).
         * Screen is cleared -- each character is reset to
           :attr:`default_char`.
         * Tabstops are reset to "every eight columns".
+        * All lines are marked as :attr:`dirty`.
 
         .. note::
 
@@ -232,9 +258,11 @@
            and tabstops should be reset as well, thanks to
            :manpage:`xterm` -- we now know that.
         """
+        self.dirty.update(range(self.lines))
         self.buffer.clear()
+        self.margins = None
+
         self.mode = set([mo.DECAWM, mo.DECTCEM])
-        self.margins = Margins(0, self.lines - 1)
 
         self.title = ""
         self.icon_name = ""
@@ -242,13 +270,11 @@
         self.charset = 0
         self.g0_charset = cs.LAT1_MAP
         self.g1_charset = cs.VT100_MAP
-        self.use_utf8 = True
-        self.utf8_decoder = codecs.getincrementaldecoder("utf-8")("replace")
 
         # From ``man terminfo`` -- "... hardware tabs are initially
         # set every `n` spaces when the terminal is powered up. Since
         # we aim to support VT102 / VT220 and linux -- we use n = 8.
-        self.tabstops = set(range(7, self.columns, 8))
+        self.tabstops = set(range(8, self.columns, 8))
 
         self.cursor = Cursor(0, 0)
         self.cursor_position()
@@ -264,68 +290,69 @@
         be added at the right, and if it has more -- columns will be
         clipped at the right.
 
-        .. note:: According to `xterm`, we should also reset origin
-                  mode and screen margins, see ``xterm/screen.c:1761``.
-
         :param int lines: number of lines in the new screen.
         :param int columns: number of columns in the new screen.
+
+        .. versionchanged:: 0.7.0
+
+           If the requested screen size is identical to the current screen
+           size, the method does nothing.
         """
         lines = lines or self.lines
         columns = columns or self.columns
 
-        # First resize the lines:
-        diff = self.lines - lines
+        if lines == self.lines and columns == self.columns:
+            return  # No changes.
 
-        # if the current display size is greater than requested
-        #    size, take lines off the top.
-        if diff > 0:
+        self.dirty.update(range(lines))
+
+        if lines < self.lines:
             self.save_cursor()
             self.cursor_position(0, 0)
-            self.delete_lines(diff)
+            self.delete_lines(self.lines - lines)  # Drop from the top.
             self.restore_cursor()
 
-        # Then resize the columns:
-        diff = self.columns - columns
-
-        # if the current display size is greater than requested
-        #    size, trim each line from the right to the new size.
-        if diff > 0:
+        if columns < self.columns:
             for line in self.buffer.values():
-                for x in range(self.columns - diff, self.columns):
+                for x in range(columns, self.columns):
                     line.pop(x, None)
 
         self.lines, self.columns = lines, columns
         self.set_margins()
-        self.reset_mode(mo.DECOM)
 
     def set_margins(self, top=None, bottom=None):
         """Select top and bottom margins for the scrolling region.
 
-        Margins determine which screen lines move during scrolling
-        (see :meth:`index` and :meth:`reverse_index`). Characters added
-        outside the scrolling region do not cause the screen to scroll.
-
         :param int top: the smallest line number that is scrolled.
         :param int bottom: the biggest line number that is scrolled.
         """
-        if top is None or bottom is None:
-            self.margins = Margins(0, self.lines - 1)
+        if top is None and bottom is None:
+            self.margins = None
+            return
+
+        margins = self.margins or Margins(0, self.lines - 1)
+
+        # Arguments are 1-based, while :attr:`margins` are zero
+        # based -- so we have to decrement them by one. We also
+        # make sure that both of them is bounded by [0, lines - 1].
+        if top is None:
+            top = margins.top
         else:
-            # Arguments are 1-based, while :attr:`margins` are zero
-            # based -- so we have to decrement them by one. We also
-            # make sure that both of them is bounded by [0, lines - 1].
             top = max(0, min(top - 1, self.lines - 1))
+        if bottom is None:
+            bottom = margins.bottom
+        else:
             bottom = max(0, min(bottom - 1, self.lines - 1))
 
-            # Even though VT102 and VT220 require DECSTBM to ignore
-            # regions of width less than 2, some programs (like aptitude
-            # for example) rely on it. Practicality beats purity.
-            if bottom - top >= 1:
-                self.margins = Margins(top, bottom)
-
-                # The cursor moves to the home position when the top and
-                # bottom margins of the scrolling region (DECSTBM) changes.
-                self.cursor_position()
+        # Even though VT102 and VT220 require DECSTBM to ignore
+        # regions of width less than 2, some programs (like aptitude
+        # for example) rely on it. Practicality beats purity.
+        if bottom - top >= 1:
+            self.margins = Margins(top, bottom)
+
+            # The cursor moves to the home position when the top and
+            # bottom margins of the scrolling region (DECSTBM) changes.
+            self.cursor_position()
 
     def set_mode(self, *modes, **kwargs):
         """Set (enable) a given list of modes.
@@ -337,6 +364,8 @@
         # private ones.
         if kwargs.get("private"):
             modes = [mode << 5 for mode in modes]
+            if mo.DECSCNM in modes:
+                self.dirty.update(range(self.lines))
 
         self.mode.update(modes)
 
@@ -347,8 +376,7 @@
             self.erase_in_display(2)
             self.cursor_position()
 
-        # According to `vttest`, DECOM should also home the cursor, see
-        # vttest/main.c:303.
+        # According to VT520 manual, DECOM should also home the cursor.
         if mo.DECOM in modes:
             self.cursor_position()
 
@@ -374,6 +402,8 @@
         # private ones.
         if kwargs.get("private"):
             modes = [mode << 5 for mode in modes]
+            if mo.DECSCNM in modes:
+                self.dirty.update(range(self.lines))
 
         self.mode.difference_update(modes)
 
@@ -445,6 +475,7 @@
             # entered.
             if self.cursor.x == self.columns:
                 if mo.DECAWM in self.mode:
+                    self.dirty.add(self.cursor.y)
                     self.carriage_return()
                     self.linefeed()
                 elif char_width > 0:
@@ -463,7 +494,8 @@
                 # A two-cell character has a stub slot after it.
                 line[self.cursor.x] = self.cursor.attrs._replace(data=char)
                 if self.cursor.x + 1 < self.columns:
-                    line[self.cursor.x + 1] = 
self.cursor.attrs._replace(data=" ")
+                    line[self.cursor.x + 1] = self.cursor.attrs \
+                        ._replace(data="")
             elif char_width == 0 and unicodedata.combining(char):
                 # A zero-cell character is combined with the previous
                 # character either on this or preceeding line.
@@ -477,13 +509,15 @@
                     self.buffer[self.cursor.y - 1][self.columns - 1] = \
                         last._replace(data=normalized)
             else:
-                pass  # Unprintable character or doesn't advance the cursor.
+                break  # Unprintable character or doesn't advance the cursor.
 
             # .. note:: We can't use :meth:`cursor_forward()`, because that
             #           way, we'll never know when to linefeed.
             if char_width > 0:
                 self.cursor.x = min(self.cursor.x + char_width, self.columns)
 
+        self.dirty.add(self.cursor.y)
+
     def set_title(self, param):
         """Set terminal title.
 
@@ -506,11 +540,12 @@
         """Move the cursor down one line in the same column. If the
         cursor is at the last line, create a new line at the bottom.
         """
-        top, bottom = self.margins
-
+        top, bottom = self.margins or Margins(0, self.lines - 1)
         if self.cursor.y == bottom:
-            for line in range(top, bottom):
-                self.buffer[line] = self.buffer[line + 1]
+            # TODO: mark only the lines within margins?
+            self.dirty.update(range(self.lines))
+            for y in range(top, bottom):
+                self.buffer[y] = self.buffer[y + 1]
             self.buffer.pop(bottom, None)
         else:
             self.cursor_down()
@@ -519,11 +554,12 @@
         """Move the cursor up one line in the same column. If the cursor
         is at the first line, create a new line at the top.
         """
-        top, bottom = self.margins
-
+        top, bottom = self.margins or Margins(0, self.lines - 1)
         if self.cursor.y == top:
-            for line in range(bottom, top, -1):
-                self.buffer[line] = self.buffer[line - 1]
+            # TODO: mark only the lines within margins?
+            self.dirty.update(range(self.lines))
+            for y in range(bottom, top, -1):
+                self.buffer[y] = self.buffer[y - 1]
             self.buffer.pop(top, None)
         else:
             self.cursor_up()
@@ -562,7 +598,6 @@
                                          self.g0_charset,
                                          self.g1_charset,
                                          self.charset,
-                                         self.use_utf8,
                                          mo.DECOM in self.mode,
                                          mo.DECAWM in self.mode))
 
@@ -576,7 +611,6 @@
             self.g0_charset = savepoint.g0_charset
             self.g1_charset = savepoint.g1_charset
             self.charset = savepoint.charset
-            self.use_utf8 = savepoint.use_utf8
 
             if savepoint.origin:
                 self.set_mode(mo.DECOM)
@@ -600,10 +634,11 @@
         :param count: number of lines to insert.
         """
         count = count or 1
-        top, bottom = self.margins
+        top, bottom = self.margins or Margins(0, self.lines - 1)
 
         # If cursor is outside scrolling margins it -- do nothin'.
         if top <= self.cursor.y <= bottom:
+            self.dirty.update(range(self.cursor.y, self.lines))
             for y in range(bottom, self.cursor.y - 1, -1):
                 if y + count <= bottom and y in self.buffer:
                     self.buffer[y + count] = self.buffer[y]
@@ -620,10 +655,11 @@
         :param int count: number of lines to delete.
         """
         count = count or 1
-        top, bottom = self.margins
+        top, bottom = self.margins or Margins(0, self.lines - 1)
 
         # If cursor is outside scrolling margins -- do nothin'.
         if top <= self.cursor.y <= bottom:
+            self.dirty.update(range(self.cursor.y, self.lines))
             for y in range(self.cursor.y, bottom + 1):
                 if y + count <= bottom:
                     if y + count in self.buffer:
@@ -641,6 +677,8 @@
 
         :param int count: number of characters to insert.
         """
+        self.dirty.add(self.cursor.y)
+
         count = count or 1
         line = self.buffer[self.cursor.y]
         for x in range(self.columns, self.cursor.x - 1, -1):
@@ -656,7 +694,9 @@
 
         :param int count: number of characters to delete.
         """
+        self.dirty.add(self.cursor.y)
         count = count or 1
+
         line = self.buffer[self.cursor.y]
         for x in range(self.cursor.x, self.columns):
             if x + count <= self.columns:
@@ -671,22 +711,26 @@
 
         :param int count: number of characters to erase.
 
-        .. warning::
+        .. note::
 
-           Even though *ALL* of the VTXXX manuals state that character
-           attributes **should be reset to defaults**, ``libvte``,
-           ``xterm`` and ``ROTE`` completely ignore this. Same applies
-           too all ``erase_*()`` and ``delete_*()`` methods.
+           Using cursor attributes for character attributes may seem
+           illogical, but if recall that a terminal emulator emulates
+           a type writer, it starts to make sense. The only way a type
+           writer could erase a character is by typing over it.
         """
+        self.dirty.add(self.cursor.y)
         count = count or 1
 
+        line = self.buffer[self.cursor.y]
         for x in range(self.cursor.x,
                        min(self.cursor.x + count, self.columns)):
-            self.buffer[self.cursor.y][x] = self.cursor.attrs
+            line[x] = self.cursor.attrs
 
     def erase_in_line(self, how=0, private=False):
         """Erase a line in a specific way.
 
+        Character attributes are set to cursor attributes.
+
         :param int how: defines the way the line should be erased in:
 
             * ``0`` -- Erases from cursor to end of line, including cursor
@@ -694,19 +738,15 @@
             * ``1`` -- Erases from beginning of line to cursor,
               including cursor position.
             * ``2`` -- Erases complete line.
-        :param bool private: when ``True`` character attributes are left
-                             unchanged **not implemented**.
+        :param bool private: when ``True`` only characters marked as
+                             eraseable are affected **not implemented**.
         """
+        self.dirty.add(self.cursor.y)
         if how == 0:
-            # a) erase from the cursor to the end of line, including
-            #    the cursor,
             interval = range(self.cursor.x, self.columns)
         elif how == 1:
-            # b) erase from the beginning of the line to the cursor,
-            #    including it,
             interval = range(self.cursor.x + 1)
         elif how == 2:
-            # c) erase the entire line.
             interval = range(self.columns)
 
         line = self.buffer[self.cursor.y]
@@ -716,6 +756,8 @@
     def erase_in_display(self, how=0, private=False):
         """Erases display in a specific way.
 
+        Character attributes are set to cursor attributes.
+
         :param int how: defines the way the line should be erased in:
 
             * ``0`` -- Erases from cursor to end of screen, including
@@ -725,27 +767,22 @@
             * ``2`` and ``3`` -- Erases complete display. All lines
               are erased and changed to single-width. Cursor does not
               move.
-        :param bool private: when ``True`` character attributes are left
-                             unchanged **not implemented**.
+        :param bool private: when ``True`` only characters marked as
+                             eraseable are affected **not implemented**.
         """
         if how == 0:
-            # a) erase from cursor to the end of the display, including
-            #    the cursor,
             interval = range(self.cursor.y + 1, self.lines)
         elif how == 1:
-            # b) erase from the beginning of the display to the cursor,
-            #    including it,
             interval = range(self.cursor.y)
         elif how == 2 or how == 3:
-            # c) erase the whole display.
             interval = range(self.lines)
 
+        self.dirty.update(interval)
         for y in interval:
             line = self.buffer[y]
             for x in line:
                 line[x] = self.cursor.attrs
 
-        # In case of 0 or 1 we have to erase the line with the cursor.
         if how == 0 or how == 1:
             self.erase_in_line(how)
 
@@ -781,7 +818,7 @@
                                  cursor is bounded by top and and bottom
                                  margins, instead of ``[0; lines - 1]``.
         """
-        if use_margins or mo.DECOM in self.mode:
+        if (use_margins or mo.DECOM in self.mode) and self.margins is not None:
             top, bottom = self.margins
         else:
             top, bottom = 0, self.lines - 1
@@ -794,7 +831,8 @@
 
         :param int count: number of lines to skip.
         """
-        self.cursor.y = max(self.cursor.y - (count or 1), self.margins.top)
+        top, _bottom = self.margins or Margins(0, self.lines - 1)
+        self.cursor.y = max(self.cursor.y - (count or 1), top)
 
     def cursor_up1(self, count=None):
         """Move cursor up the indicated # of lines to column 1. Cursor
@@ -811,7 +849,8 @@
 
         :param int count: number of lines to skip.
         """
-        self.cursor.y = min(self.cursor.y + (count or 1), self.margins.bottom)
+        _top, bottom = self.margins or Margins(0, self.lines - 1)
+        self.cursor.y = min(self.cursor.y + (count or 1), bottom)
 
     def cursor_down1(self, count=None):
         """Move cursor down the indicated # of lines to column 1.
@@ -860,7 +899,7 @@
 
         # If origin mode (DECOM) is set, line number are relative to
         # the top scrolling margin.
-        if mo.DECOM in self.mode:
+        if self.margins is not None and mo.DECOM in self.mode:
             line += self.margins.top
 
             # Cursor is not allowed to move out of the scrolling region.
@@ -904,6 +943,7 @@
 
     def alignment_display(self):
         """Fills screen with uppercase E's for screen focus and alignment."""
+        self.dirty.update(range(self.lines))
         for y in range(self.lines):
             for x in range(self.columns):
                 self.buffer[y][x] = self.buffer[y][x]._replace(data="E")
@@ -957,10 +997,15 @@
         """Report terminal identity.
 
         .. versionadded:: 0.5.0
+
+        .. versionchanged:: 0.7.0
+
+           If ``private`` keyword argument is set, the method does nothing.
+           This behaviour is consistent with VT220 manual.
         """
         # We only implement "primary" DA which is the only DA request
         # VT102 understood, see ``VT102ID`` in ``linux/drivers/tty/vt.c``.
-        if mode == 0:
+        if mode == 0 and not kwargs.get("private"):
             self.write_process_input(ctrl.CSI + "?6c")
 
     def report_device_status(self, mode):
@@ -1000,105 +1045,31 @@
 
 
 class DiffScreen(Screen):
-    """A screen subclass, which maintains a set of dirty lines in its
+    """
+    A screen subclass, which maintains a set of dirty lines in its
     :attr:`dirty` attribute. The end user is responsible for emptying
     a set, when a diff is applied.
 
-    .. attribute:: dirty
-
-       A set of line numbers, which should be re-drawn.
+    .. deprecated:: 0.7.0
 
-       >>> screen = DiffScreen(80, 24)
-       >>> screen.dirty.clear()
-       >>> screen.draw("!")
-       >>> list(screen.dirty)
-       [0]
+       The functionality contained in this class has been merged into
+       :class:`~pyte.screens.Screen` and will be removed in 0.8.0.
+       Please update your code accordingly.
     """
-    def __init__(self, *args):
-        self.dirty = set()
-        super(DiffScreen, self).__init__(*args)
-
-    def set_mode(self, *modes, **kwargs):
-        if mo.DECSCNM >> 5 in modes and kwargs.get("private"):
-            self.dirty.update(range(self.lines))
-        super(DiffScreen, self).set_mode(*modes, **kwargs)
-
-    def reset_mode(self, *modes, **kwargs):
-        if mo.DECSCNM >> 5 in modes and kwargs.get("private"):
-            self.dirty.update(range(self.lines))
-        super(DiffScreen, self).reset_mode(*modes, **kwargs)
-
-    def reset(self):
-        self.dirty.update(range(self.lines))
-        super(DiffScreen, self).reset()
-
-    def resize(self, *args, **kwargs):
-        self.dirty.update(range(self.lines))
-        super(DiffScreen, self).resize(*args, **kwargs)
-
-    def draw(self, *args):
-        # Call the superclass's method before marking the row as
-        # dirty, as when wrapping is enabled, draw() might change
-        # self.cursor.y.
-        super(DiffScreen, self).draw(*args)
-        self.dirty.add(self.cursor.y)
-
-    def index(self):
-        if self.cursor.y == self.margins.bottom:
-            self.dirty.update(range(self.lines))
+    def __init__(self, *args, **kwargs):
+        warnings.warn(
+            "The functionality of ``DiffScreen` has been merged into "
+            "``Screen`` and will be removed in 0.8.0. Please update "
+            "your code accordingly.", DeprecationWarning)
 
-        super(DiffScreen, self).index()
-
-    def reverse_index(self):
-        if self.cursor.y == self.margins.top:
-            self.dirty.update(range(self.lines))
-
-        super(DiffScreen, self).reverse_index()
-
-    def insert_lines(self, *args):
-        self.dirty.update(range(self.cursor.y, self.lines))
-        super(DiffScreen, self).insert_lines(*args)
-
-    def delete_lines(self, *args):
-        self.dirty.update(range(self.cursor.y, self.lines))
-        super(DiffScreen, self).delete_lines(*args)
-
-    def insert_characters(self, *args):
-        self.dirty.add(self.cursor.y)
-        super(DiffScreen, self).insert_characters(*args)
-
-    def delete_characters(self, *args):
-        self.dirty.add(self.cursor.y)
-        super(DiffScreen, self).delete_characters(*args)
-
-    def erase_characters(self, *args):
-        self.dirty.add(self.cursor.y)
-        super(DiffScreen, self).erase_characters(*args)
-
-    def erase_in_line(self, *args):
-        self.dirty.add(self.cursor.y)
-        super(DiffScreen, self).erase_in_line(*args)
-
-    def erase_in_display(self, how=0):
-        if how == 0:
-            self.dirty.update(range(self.cursor.y + 1, self.lines))
-        elif how == 1:
-            self.dirty.update(range(self.cursor.y))
-        elif how == 2:
-            self.dirty.update(range(self.lines))
-
-        super(DiffScreen, self).erase_in_display(how)
-
-    def alignment_display(self):
-        self.dirty.update(range(self.lines))
-        super(DiffScreen, self).alignment_display()
+        super(DiffScreen, self).__init__(*args, **kwargs)
 
 
 History = namedtuple("History", "top bottom ratio size position")
 
 
-class HistoryScreen(DiffScreen):
-    """A :class:~`pyte.screens.DiffScreen` subclass, which keeps track
+class HistoryScreen(Screen):
+    """A :class:~`pyte.screens.Screen` subclass, which keeps track
     of screen history and allows pagination. This is not linux-specific,
     but still useful; see page 462 of VT520 User's Manual.
 
@@ -1215,7 +1186,7 @@
 
     def index(self):
         """Overloaded to update top history with the removed lines."""
-        top, bottom = self.margins
+        top, bottom = self.margins or Margins(0, self.lines - 1)
 
         if self.cursor.y == bottom:
             self.history.top.append(self.buffer[top])
@@ -1224,7 +1195,7 @@
 
     def reverse_index(self):
         """Overloaded to update bottom history with the removed lines."""
-        top, bottom = self.margins
+        top, bottom = self.margins or Margins(0, self.lines - 1)
 
         if self.cursor.y == top:
             self.history.bottom.append(self.buffer[bottom])
@@ -1241,16 +1212,16 @@
             mid = min(len(self.history.top),
                       int(math.ceil(self.lines * self.history.ratio)))
 
-            self.history.bottom.extendleft([
-                self.buffer[line]
-                for line in range(self.lines - 1, self.lines - mid - 1, -1)])
+            self.history.bottom.extendleft(
+                self.buffer[y]
+                for y in range(self.lines - 1, self.lines - mid - 1, -1))
             self.history = self.history \
                 ._replace(position=self.history.position - self.lines)
 
-            for line in range(self.lines - 1, mid - 1, -1):
-                self.buffer[line] = self.buffer[line - mid]
-            for line in range(mid - 1, -1, -1):
-                self.buffer[line] = self.history.top.pop()
+            for y in range(self.lines - 1, mid - 1, -1):
+                self.buffer[y] = self.buffer[y - mid]
+            for y in range(mid - 1, -1, -1):
+                self.buffer[y] = self.history.top.pop()
 
             self.dirty = set(range(self.lines))
 
@@ -1260,14 +1231,14 @@
             mid = min(len(self.history.bottom),
                       int(math.ceil(self.lines * self.history.ratio)))
 
-            self.history.top.extend([self.buffer[line] for line in range(mid)])
+            self.history.top.extend(self.buffer[y] for y in range(mid))
             self.history = self.history \
                 ._replace(position=self.history.position + self.lines)
 
-            for line in range(self.lines - mid):
-                self.buffer[line] = self.buffer[line + mid]
-            for line in range(self.lines - mid, self.lines):
-                self.buffer[line] = self.history.bottom.popleft()
+            for y in range(self.lines - mid):
+                self.buffer[y] = self.buffer[y + mid]
+            for y in range(self.lines - mid, self.lines):
+                self.buffer[y] = self.history.bottom.popleft()
 
             self.dirty = set(range(self.lines))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/pyte/streams.py 
new/pyte-0.7.0/pyte/streams.py
--- old/pyte-0.6.0/pyte/streams.py      2017-05-28 02:23:48.000000000 +0200
+++ new/pyte-0.7.0/pyte/streams.py      2017-06-19 21:56:05.000000000 +0200
@@ -57,7 +57,7 @@
 
         `man console_codes <http://linux.die.net/man/4/console_codes>`_
             For details on console codes listed bellow in :attr:`basic`,
-            :attr:`escape`, :attr:`csi`, :attr:`sharp` and :attr:`percent`.
+            :attr:`escape`, :attr:`csi`, :attr:`sharp`.
     """
 
     #: Control sequences, which don't require any arguments.
@@ -143,13 +143,10 @@
         if screen is not None:
             self.attach(screen)
 
-    def attach(self, screen, only=()):
+    def attach(self, screen):
         """Adds a given screen to the listener queue.
 
         :param pyte.screens.Screen screen: a screen to attach to.
-        :param list only: a list of events you want to dispatch to a
-                          given screen (empty by default, which means
-                          -- dispatch all events).
         """
         if self.listener is not None:
             warnings.warn("As of version 0.6.0 the listener queue is "
@@ -157,12 +154,7 @@
                           "listener {0} will be replaced."
                           .format(self.listener), DeprecationWarning)
 
-        if only:
-            warnings.warn(
-                "Passing ``only`` to ``Stream.attach`` is deprecated "
-                "and will be removed in 0.7.0", DeprecationWarning)
-            screen = _RestrictedListener(screen, only)
-        elif self.strict:
+        if self.strict:
             for event in self.events:
                 if not hasattr(screen, event):
                     raise TypeError("{0} is missing {1}".format(screen, event))
@@ -267,10 +259,14 @@
                         sharp_dispatch[(yield)]()
                     if char == "%":
                         self.select_other_charset((yield))
-                    elif char in "()" and not self.use_utf8:
+                    elif char in "()":
+                        code = yield
+                        if self.use_utf8:
+                            continue
+
                         # See http://www.cl.cam.ac.uk/~mgk25/unicode.html#term
                         # for the why on the UTF-8 restriction.
-                        listener.define_charset((yield), mode=char)
+                        listener.define_charset(code, mode=char)
                     else:
                         escape_dispatch[char]()
                     continue    # Don't go to CSI.
@@ -330,6 +326,11 @@
                             break  # CSI is finished.
             elif char == OSC:
                 code = yield
+                if code == "R":
+                    continue  # Reset palette. Not implemented.
+                elif code == "P":
+                    continue  # Set palette. Not implemented.
+
                 param = ""
                 while True:
                     char = yield
@@ -366,7 +367,6 @@
         # A noop since all input is Unicode-only.
 
 
-
 class ByteStream(Stream):
     """A stream which takes bytes as input.
 
@@ -379,13 +379,6 @@
        using UTF-8. Defaults to ``True``.
     """
     def __init__(self, *args, **kwargs):
-        if kwargs.pop("encodings", None):
-            warnings.warn(
-                "As of version 0.6.0 ``pyte.streams.ByteStream`` no longer "
-                "accepts an encoding as an argument an instead uses the "
-                "encoding provided via a CSI command. ``encoding`` will "
-                "be removed in 0.7.0", UserWarning)
-
         super(ByteStream, self).__init__(*args, **kwargs)
 
         self.utf8_decoder = codecs.getincrementaldecoder("utf-8")("replace")
@@ -404,20 +397,3 @@
             self.utf8_decoder.reset()
         elif code in "G8":
             self.use_utf8 = True
-
-
-class _RestrictedListener(object):
-    def __init__(self, listener, only):
-        if not only:
-            raise ValueError("``only`` must be non-empty")
-
-        self.listener = listener
-        self.only = only
-
-    def __getattribute__(self, attr):
-        if attr not in Stream.events:
-            return super(_RestrictedListener, self).__getattribute__(attr)
-        elif attr in self.only:
-            return getattr(self.listener, attr)
-        else:
-            return lambda *args, **kwargs: None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/pyte.egg-info/PKG-INFO 
new/pyte-0.7.0/pyte.egg-info/PKG-INFO
--- old/pyte-0.6.0/pyte.egg-info/PKG-INFO       2017-05-28 03:55:51.000000000 
+0200
+++ new/pyte-0.7.0/pyte.egg-info/PKG-INFO       2017-10-07 23:08:32.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyte
-Version: 0.6.0
+Version: 0.7.0
 Summary: Simple VTXXX-compatible terminal emulator.
 Home-page: https://github.com/selectel/pyte
 Author: Sergei Lebedev
@@ -17,7 +17,7 @@
                 | |_) || |_| || |_|  __/
                 | .__/  \__, | \__|\___|
                 | |      __/ |
-                |_|     |___/      0.6.0
+                |_|     |___/      0.7.0
         
         
         What is ``pyte``?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/setup.py new/pyte-0.7.0/setup.py
--- old/pyte-0.6.0/setup.py     2017-03-26 22:12:16.000000000 +0200
+++ new/pyte-0.7.0/setup.py     2017-10-07 23:07:28.000000000 +0200
@@ -31,7 +31,7 @@
 
 
 setup(name="pyte",
-      version="0.6.0",
+      version="0.7.0",
       packages=["pyte"],
       install_requires=["wcwidth"],
       setup_requires=["pytest-runner"],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/tests/test_diff.py 
new/pyte-0.7.0/tests/test_diff.py
--- old/pyte-0.6.0/tests/test_diff.py   2017-03-11 14:47:29.000000000 +0100
+++ new/pyte-0.7.0/tests/test_diff.py   2017-06-18 19:29:31.000000000 +0200
@@ -24,7 +24,7 @@
 
     # c) resize().
     screen.dirty.clear()
-    screen.resize()
+    screen.resize(130, 24)
     assert screen.dirty == set(range(screen.lines))
 
     # d) alignment_display().
@@ -122,6 +122,10 @@
     screen.erase_in_display(2)
     assert screen.dirty == set(range(0, screen.lines))
 
+    screen.dirty.clear()
+    screen.erase_in_display(3)
+    assert screen.dirty == set(range(0, screen.lines))
+
 
 def test_draw_wrap():
     screen = pyte.DiffScreen(80, 24)
@@ -138,4 +142,12 @@
     assert screen.cursor.y == 1
     # regression test issue #36 where the wrong line was marked as
     # dirty
-    assert screen.dirty == set([1])
+    assert screen.dirty == set([0, 1])
+
+
+def test_draw_multiple_chars_wrap():
+    screen = pyte.Screen(5, 2)
+    screen.dirty.clear()
+    screen.draw("1234567890")
+    assert screen.cursor.y == 1
+    assert screen.dirty == set([0, 1])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/tests/test_history.py 
new/pyte-0.7.0/tests/test_history.py
--- old/pyte-0.6.0/tests/test_history.py        2017-05-28 03:53:55.000000000 
+0200
+++ new/pyte-0.7.0/tests/test_history.py        2017-05-28 14:14:04.000000000 
+0200
@@ -2,17 +2,16 @@
 
 from __future__ import unicode_literals
 
-import operator
 import os
 
 import pyte
 from pyte import control as ctrl, modes as mo
-from pyte.compat import map, str
+from pyte.compat import str
 
 
 def chars(history_lines, columns):
-    return ["".join(history_lines[line][col].data for col in range(columns))
-            for line in range(len(history_lines))]
+    return ["".join(history_lines[y][x].data for x in range(columns))
+            for y in range(len(history_lines))]
 
 
 def test_index():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/tests/test_screen.py 
new/pyte-0.7.0/tests/test_screen.py
--- old/pyte-0.6.0/tests/test_screen.py 2017-05-28 01:40:55.000000000 +0200
+++ new/pyte-0.7.0/tests/test_screen.py 2017-10-07 23:02:16.000000000 +0200
@@ -27,6 +27,7 @@
 
     return screen
 
+
 def tolist(screen):
     return [[screen.buffer[y][x] for x in range(screen.columns)]
             for y in range(screen.lines)]
@@ -185,31 +186,25 @@
     screen.set_mode(mo.DECOM)
     screen.set_margins(0, 1)
     assert screen.columns == screen.lines == 2
-    assert len(tolist(screen)[0]) == 2
     assert tolist(screen) == [[screen.default_char, screen.default_char]] * 2
 
     screen.resize(3, 3)
     assert screen.columns == screen.lines == 3
-    assert len(tolist(screen)) == 3
-    assert len(tolist(screen)[0]) == 3
     assert tolist(screen) == [
         [screen.default_char, screen.default_char, screen.default_char]
     ] * 3
-    assert mo.DECOM not in screen.mode
-    assert screen.margins == (0, 2)
+    assert mo.DECOM in screen.mode
+    assert screen.margins is None
 
     screen.resize(2, 2)
     assert screen.columns == screen.lines == 2
-    assert len(tolist(screen)) == 2
-    assert len(tolist(screen)[0]) == 2
     assert tolist(screen) == [[screen.default_char, screen.default_char]] * 2
 
     # Quirks:
-    # a) if the current display is thinner than the requested size,
+    # a) if the current display is narrower than the requested size,
     #    new columns should be added to the right.
     screen = update(pyte.Screen(2, 2), ["bo", "sh"], [None, None])
     screen.resize(2, 3)
-    dsp = screen.display
     assert screen.display == ["bo ", "sh "]
 
     # b) if the current display is wider than the requested size,
@@ -232,6 +227,13 @@
     assert screen.display == ["sh"]
 
 
+def test_resize_same():
+    screen = pyte.Screen(2, 2)
+    screen.dirty.clear()
+    screen.resize(2, 2)
+    assert not screen.dirty
+
+
 def test_set_mode():
     # Test mo.DECCOLM mode
     screen = update(pyte.Screen(3, 3), ["sam", "is ", "foo"])
@@ -635,7 +637,7 @@
     screen = pyte.Screen(10, 10)
 
     # Making sure initial tabstops are in place ...
-    assert screen.tabstops == set([7])
+    assert screen.tabstops == set([8])
 
     # ... and clearing them.
     screen.clear_tab_stop(3)
@@ -1419,7 +1421,7 @@
 def test_set_margins():
     screen = pyte.Screen(10, 10)
 
-    assert screen.margins == (0, 9)
+    assert screen.margins is None
 
     # a) ok-case
     screen.set_margins(1, 5)
@@ -1430,9 +1432,9 @@
     assert screen.margins != (99, 9)
     assert screen.margins == (0, 4)
 
-    # c) no margins provided
+    # c) no margins provided -- reset to full screen.
     screen.set_margins()
-    assert screen.margins == (0, screen.lines - 1)
+    assert screen.margins is None
 
 
 def test_hide_cursor():
@@ -1466,18 +1468,6 @@
     assert acc.pop() == ctrl.CSI + "?6c"
 
 
-def test_private_report_device_attributes():
-    # Some console apps (e.g. ADOM) might add ``?`` to the DA request,
-    # even though the VT102/VT220 spec does not allow this.
-    screen = pyte.Screen(10, 10)
-    stream = pyte.Stream(screen)
-
-    acc = []
-    screen.write_process_input = acc.append
-    stream.feed(ctrl.CSI + "?0c")
-    assert acc.pop() == ctrl.CSI + "?6c"
-
-
 def test_report_device_status():
     screen = pyte.Screen(10, 10)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyte-0.6.0/tests/test_stream.py 
new/pyte-0.7.0/tests/test_stream.py
--- old/pyte-0.6.0/tests/test_stream.py 2017-03-22 21:29:23.000000000 +0100
+++ new/pyte-0.7.0/tests/test_stream.py 2017-06-11 21:30:40.000000000 +0200
@@ -3,7 +3,6 @@
 from __future__ import unicode_literals
 
 import sys
-import warnings
 
 if sys.version_info[0] == 2:
     from cStringIO import StringIO
@@ -13,7 +12,7 @@
 import pytest
 
 import pyte
-from pyte import control as ctrl, escape as esc
+from pyte import charsets as cs, control as ctrl, escape as esc
 
 
 class counter(object):
@@ -221,23 +220,12 @@
     stream.detach(screen)
 
 
-def test_attach_only():
-    drawn = []
-
-    class DrawOnly(object):
-        def linefeed(self):
-            raise RuntimeError
-
-        def draw(self, data):
-            drawn.append(data)
-
-    screen = DrawOnly()
-    stream = pyte.Stream()
-    with warnings.catch_warnings():
-        stream.attach(screen, only=["draw"])
-
-    stream.feed("foo\nbar")
-    assert drawn == ["foo", "bar"]
+def test_define_charset():
+    # Should be a noop. All input is UTF8.
+    screen = pyte.Screen(3, 3)
+    stream = pyte.Stream(screen)
+    stream.feed(ctrl.ESC + "(B")
+    assert screen.display[0] == " " * 3
 
 
 @pytest.mark.parametrize("input,expected", [
@@ -266,6 +254,28 @@
     assert handler.args == ("Нерусский текст", )
 
 
+def test_byte_stream_define_charset_unknown():
+    screen = pyte.Screen(3, 3)
+    stream = pyte.ByteStream(screen)
+    stream.select_other_charset("@")
+    default_g0_charset = screen.g0_charset
+    # ``"Z"`` is not supported by Linux terminal, so expect a noop.
+    assert "Z" not in cs.MAPS
+    stream.feed((ctrl.ESC + "(Z").encode())
+    assert screen.display[0] == " " * 3
+    assert screen.g0_charset == default_g0_charset
+
+
[email protected]("charset,mapping", cs.MAPS.items())
+def test_byte_stream_define_charset(charset, mapping):
+    screen = pyte.Screen(3, 3)
+    stream = pyte.ByteStream(screen)
+    stream.select_other_charset("@")
+    stream.feed((ctrl.ESC + "(" + charset).encode())
+    assert screen.display[0] == " " * 3
+    assert screen.g0_charset == mapping
+
+
 def test_byte_stream_select_other_charset():
     stream = pyte.ByteStream(pyte.Screen(3, 3))
     assert stream.use_utf8  # on by default.


Reply via email to