[Python-checkins] bpo-42299: Remove formatter module (GH-23476)

corona10 webhook-mailer at python.org
Wed Nov 25 08:17:39 EST 2020


https://github.com/python/cpython/commit/be319c0c108e308fb7ed6ec9522e969fdffd1253
commit: be319c0c108e308fb7ed6ec9522e969fdffd1253
branch: master
author: Dong-hee Na <donghee.na at python.org>
committer: corona10 <donghee.na92 at gmail.com>
date: 2020-11-25T22:17:30+09:00
summary:

bpo-42299: Remove formatter module (GH-23476)

files:
A Misc/NEWS.d/next/Library/2020-11-23-23-42-08.bpo-42299.Fdn4Wf.rst
D Doc/library/formatter.rst
D Doc/library/misc.rst
D Lib/formatter.py
M Doc/library/index.rst
M Doc/whatsnew/3.10.rst
M Lib/test/test_sundry.py

diff --git a/Doc/library/formatter.rst b/Doc/library/formatter.rst
deleted file mode 100644
index 6c10ac6fab50e..0000000000000
--- a/Doc/library/formatter.rst
+++ /dev/null
@@ -1,351 +0,0 @@
-:mod:`formatter` --- Generic output formatting
-==============================================
-
-.. module:: formatter
-   :synopsis: Generic output formatter and device interface.
-   :deprecated:
-
-.. deprecated:: 3.4
-   Due to lack of usage, the formatter module has been deprecated.
-
---------------
-
-This module supports two interface definitions, each with multiple
-implementations: The *formatter* interface, and the *writer* interface which is
-required by the formatter interface.
-
-Formatter objects transform an abstract flow of formatting events into specific
-output events on writer objects.  Formatters manage several stack structures to
-allow various properties of a writer object to be changed and restored; writers
-need not be able to handle relative changes nor any sort of "change back"
-operation.  Specific writer properties which may be controlled via formatter
-objects are horizontal alignment, font, and left margin indentations.  A
-mechanism is provided which supports providing arbitrary, non-exclusive style
-settings to a writer as well.  Additional interfaces facilitate formatting
-events which are not reversible, such as paragraph separation.
-
-Writer objects encapsulate device interfaces.  Abstract devices, such as file
-formats, are supported as well as physical devices.  The provided
-implementations all work with abstract devices.  The interface makes available
-mechanisms for setting the properties which formatter objects manage and
-inserting data into the output.
-
-
-.. _formatter-interface:
-
-The Formatter Interface
------------------------
-
-Interfaces to create formatters are dependent on the specific formatter class
-being instantiated.  The interfaces described below are the required interfaces
-which all formatters must support once initialized.
-
-One data element is defined at the module level:
-
-
-.. data:: AS_IS
-
-   Value which can be used in the font specification passed to the ``push_font()``
-   method described below, or as the new value to any other ``push_property()``
-   method.  Pushing the ``AS_IS`` value allows the corresponding ``pop_property()``
-   method to be called without having to track whether the property was changed.
-
-The following attributes are defined for formatter instance objects:
-
-
-.. attribute:: formatter.writer
-
-   The writer instance with which the formatter interacts.
-
-
-.. method:: formatter.end_paragraph(blanklines)
-
-   Close any open paragraphs and insert at least *blanklines* before the next
-   paragraph.
-
-
-.. method:: formatter.add_line_break()
-
-   Add a hard line break if one does not already exist.  This does not break the
-   logical paragraph.
-
-
-.. method:: formatter.add_hor_rule(*args, **kw)
-
-   Insert a horizontal rule in the output.  A hard break is inserted if there is
-   data in the current paragraph, but the logical paragraph is not broken.  The
-   arguments and keywords are passed on to the writer's :meth:`send_line_break`
-   method.
-
-
-.. method:: formatter.add_flowing_data(data)
-
-   Provide data which should be formatted with collapsed whitespace. Whitespace
-   from preceding and successive calls to :meth:`add_flowing_data` is considered as
-   well when the whitespace collapse is performed.  The data which is passed to
-   this method is expected to be word-wrapped by the output device.  Note that any
-   word-wrapping still must be performed by the writer object due to the need to
-   rely on device and font information.
-
-
-.. method:: formatter.add_literal_data(data)
-
-   Provide data which should be passed to the writer unchanged. Whitespace,
-   including newline and tab characters, are considered legal in the value of
-   *data*.
-
-
-.. method:: formatter.add_label_data(format, counter)
-
-   Insert a label which should be placed to the left of the current left margin.
-   This should be used for constructing bulleted or numbered lists.  If the
-   *format* value is a string, it is interpreted as a format specification for
-   *counter*, which should be an integer. The result of this formatting becomes the
-   value of the label; if *format* is not a string it is used as the label value
-   directly. The label value is passed as the only argument to the writer's
-   :meth:`send_label_data` method.  Interpretation of non-string label values is
-   dependent on the associated writer.
-
-   Format specifications are strings which, in combination with a counter value,
-   are used to compute label values.  Each character in the format string is copied
-   to the label value, with some characters recognized to indicate a transform on
-   the counter value.  Specifically, the character ``'1'`` represents the counter
-   value formatter as an Arabic number, the characters ``'A'`` and ``'a'``
-   represent alphabetic representations of the counter value in upper and lower
-   case, respectively, and ``'I'`` and ``'i'`` represent the counter value in Roman
-   numerals, in upper and lower case.  Note that the alphabetic and roman
-   transforms require that the counter value be greater than zero.
-
-
-.. method:: formatter.flush_softspace()
-
-   Send any pending whitespace buffered from a previous call to
-   :meth:`add_flowing_data` to the associated writer object.  This should be called
-   before any direct manipulation of the writer object.
-
-
-.. method:: formatter.push_alignment(align)
-
-   Push a new alignment setting onto the alignment stack.  This may be
-   :const:`AS_IS` if no change is desired.  If the alignment value is changed from
-   the previous setting, the writer's :meth:`new_alignment` method is called with
-   the *align* value.
-
-
-.. method:: formatter.pop_alignment()
-
-   Restore the previous alignment.
-
-
-.. method:: formatter.push_font((size, italic, bold, teletype))
-
-   Change some or all font properties of the writer object.  Properties which are
-   not set to :const:`AS_IS` are set to the values passed in while others are
-   maintained at their current settings.  The writer's :meth:`new_font` method is
-   called with the fully resolved font specification.
-
-
-.. method:: formatter.pop_font()
-
-   Restore the previous font.
-
-
-.. method:: formatter.push_margin(margin)
-
-   Increase the number of left margin indentations by one, associating the logical
-   tag *margin* with the new indentation.  The initial margin level is ``0``.
-   Changed values of the logical tag must be true values; false values other than
-   :const:`AS_IS` are not sufficient to change the margin.
-
-
-.. method:: formatter.pop_margin()
-
-   Restore the previous margin.
-
-
-.. method:: formatter.push_style(*styles)
-
-   Push any number of arbitrary style specifications.  All styles are pushed onto
-   the styles stack in order.  A tuple representing the entire stack, including
-   :const:`AS_IS` values, is passed to the writer's :meth:`new_styles` method.
-
-
-.. method:: formatter.pop_style(n=1)
-
-   Pop the last *n* style specifications passed to :meth:`push_style`.  A tuple
-   representing the revised stack, including :const:`AS_IS` values, is passed to
-   the writer's :meth:`new_styles` method.
-
-
-.. method:: formatter.set_spacing(spacing)
-
-   Set the spacing style for the writer.
-
-
-.. method:: formatter.assert_line_data(flag=1)
-
-   Inform the formatter that data has been added to the current paragraph
-   out-of-band.  This should be used when the writer has been manipulated
-   directly.  The optional *flag* argument can be set to false if the writer
-   manipulations produced a hard line break at the end of the output.
-
-
-.. _formatter-impls:
-
-Formatter Implementations
--------------------------
-
-Two implementations of formatter objects are provided by this module. Most
-applications may use one of these classes without modification or subclassing.
-
-
-.. class:: NullFormatter(writer=None)
-
-   A formatter which does nothing.  If *writer* is omitted, a :class:`NullWriter`
-   instance is created.  No methods of the writer are called by
-   :class:`NullFormatter` instances.  Implementations should inherit from this
-   class if implementing a writer interface but don't need to inherit any
-   implementation.
-
-
-.. class:: AbstractFormatter(writer)
-
-   The standard formatter.  This implementation has demonstrated wide applicability
-   to many writers, and may be used directly in most circumstances.  It has been
-   used to implement a full-featured World Wide Web browser.
-
-
-.. _writer-interface:
-
-The Writer Interface
---------------------
-
-Interfaces to create writers are dependent on the specific writer class being
-instantiated.  The interfaces described below are the required interfaces which
-all writers must support once initialized. Note that while most applications can
-use the :class:`AbstractFormatter` class as a formatter, the writer must
-typically be provided by the application.
-
-
-.. method:: writer.flush()
-
-   Flush any buffered output or device control events.
-
-
-.. method:: writer.new_alignment(align)
-
-   Set the alignment style.  The *align* value can be any object, but by convention
-   is a string or ``None``, where ``None`` indicates that the writer's "preferred"
-   alignment should be used. Conventional *align* values are ``'left'``,
-   ``'center'``, ``'right'``, and ``'justify'``.
-
-
-.. method:: writer.new_font(font)
-
-   Set the font style.  The value of *font* will be ``None``, indicating that the
-   device's default font should be used, or a tuple of the form ``(size,
-   italic, bold, teletype)``.  Size will be a string indicating the size of
-   font that should be used; specific strings and their interpretation must be
-   defined by the application.  The *italic*, *bold*, and *teletype* values are
-   Boolean values specifying which of those font attributes should be used.
-
-
-.. method:: writer.new_margin(margin, level)
-
-   Set the margin level to the integer *level* and the logical tag to *margin*.
-   Interpretation of the logical tag is at the writer's discretion; the only
-   restriction on the value of the logical tag is that it not be a false value for
-   non-zero values of *level*.
-
-
-.. method:: writer.new_spacing(spacing)
-
-   Set the spacing style to *spacing*.
-
-
-.. method:: writer.new_styles(styles)
-
-   Set additional styles.  The *styles* value is a tuple of arbitrary values; the
-   value :const:`AS_IS` should be ignored.  The *styles* tuple may be interpreted
-   either as a set or as a stack depending on the requirements of the application
-   and writer implementation.
-
-
-.. method:: writer.send_line_break()
-
-   Break the current line.
-
-
-.. method:: writer.send_paragraph(blankline)
-
-   Produce a paragraph separation of at least *blankline* blank lines, or the
-   equivalent.  The *blankline* value will be an integer.  Note that the
-   implementation will receive a call to :meth:`send_line_break` before this call
-   if a line break is needed;  this method should not include ending the last line
-   of the paragraph. It is only responsible for vertical spacing between
-   paragraphs.
-
-
-.. method:: writer.send_hor_rule(*args, **kw)
-
-   Display a horizontal rule on the output device.  The arguments to this method
-   are entirely application- and writer-specific, and should be interpreted with
-   care.  The method implementation may assume that a line break has already been
-   issued via :meth:`send_line_break`.
-
-
-.. method:: writer.send_flowing_data(data)
-
-   Output character data which may be word-wrapped and re-flowed as needed.  Within
-   any sequence of calls to this method, the writer may assume that spans of
-   multiple whitespace characters have been collapsed to single space characters.
-
-
-.. method:: writer.send_literal_data(data)
-
-   Output character data which has already been formatted for display.  Generally,
-   this should be interpreted to mean that line breaks indicated by newline
-   characters should be preserved and no new line breaks should be introduced.  The
-   data may contain embedded newline and tab characters, unlike data provided to
-   the :meth:`send_formatted_data` interface.
-
-
-.. method:: writer.send_label_data(data)
-
-   Set *data* to the left of the current left margin, if possible. The value of
-   *data* is not restricted; treatment of non-string values is entirely
-   application- and writer-dependent.  This method will only be called at the
-   beginning of a line.
-
-
-.. _writer-impls:
-
-Writer Implementations
-----------------------
-
-Three implementations of the writer object interface are provided as examples by
-this module.  Most applications will need to derive new writer classes from the
-:class:`NullWriter` class.
-
-
-.. class:: NullWriter()
-
-   A writer which only provides the interface definition; no actions are taken on
-   any methods.  This should be the base class for all writers which do not need to
-   inherit any implementation methods.
-
-
-.. class:: AbstractWriter()
-
-   A writer which can be used in debugging formatters, but not much else.  Each
-   method simply announces itself by printing its name and arguments on standard
-   output.
-
-
-.. class:: DumbWriter(file=None, maxcol=72)
-
-   Simple writer class which writes output on the :term:`file object` passed
-   in as *file* or, if *file* is omitted, on standard output.  The output is
-   simply word-wrapped to the number of columns specified by *maxcol*.  This
-   class is suitable for reflowing a sequence of paragraphs.
-
diff --git a/Doc/library/index.rst b/Doc/library/index.rst
index bebf7429b0e63..42f994f8b87d1 100644
--- a/Doc/library/index.rst
+++ b/Doc/library/index.rst
@@ -72,7 +72,6 @@ the `Python Package Index <https://pypi.org>`_.
    custominterp.rst
    modules.rst
    language.rst
-   misc.rst
    windows.rst
    unix.rst
    superseded.rst
diff --git a/Doc/library/misc.rst b/Doc/library/misc.rst
deleted file mode 100644
index 0943235246a91..0000000000000
--- a/Doc/library/misc.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-.. _misc:
-
-**********************
-Miscellaneous Services
-**********************
-
-The modules described in this chapter provide miscellaneous services that are
-available in all Python versions.  Here's an overview:
-
-
-.. toctree::
-
-   formatter.rst
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index 704de8156f312..f96a3bcbca95f 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -449,6 +449,12 @@ Removed
   :c:func:`PyParser_SimpleParseFileFlags` and :c:func:`PyNode_Compile`
   that were deprecated in 3.9 due to the switch to the new PEG parser.
 
+* Removed the ``formatter`` module, which was deprecated in Python 3.4.
+  It is somewhat obsolete, little used, and not tested. It was originally
+  scheduled to be removed in Python 3.6, but such removals were delayed until
+  after Python 2.7 EOL. Existing users should copy whatever classes they use
+  into their code.
+  (Contributed by Dong-hee Na and Terry J. Reedy in :issue:`42299`.)
 
 Porting to Python 3.10
 ======================
diff --git a/Lib/formatter.py b/Lib/formatter.py
deleted file mode 100644
index e2394de8c2919..0000000000000
--- a/Lib/formatter.py
+++ /dev/null
@@ -1,452 +0,0 @@
-"""Generic output formatting.
-
-Formatter objects transform an abstract flow of formatting events into
-specific output events on writer objects. Formatters manage several stack
-structures to allow various properties of a writer object to be changed and
-restored; writers need not be able to handle relative changes nor any sort
-of ``change back'' operation. Specific writer properties which may be
-controlled via formatter objects are horizontal alignment, font, and left
-margin indentations. A mechanism is provided which supports providing
-arbitrary, non-exclusive style settings to a writer as well. Additional
-interfaces facilitate formatting events which are not reversible, such as
-paragraph separation.
-
-Writer objects encapsulate device interfaces. Abstract devices, such as
-file formats, are supported as well as physical devices. The provided
-implementations all work with abstract devices. The interface makes
-available mechanisms for setting the properties which formatter objects
-manage and inserting data into the output.
-"""
-
-import sys
-import warnings
-warnings.warn('the formatter module is deprecated', DeprecationWarning,
-              stacklevel=2)
-
-
-AS_IS = None
-
-
-class NullFormatter:
-    """A formatter which does nothing.
-
-    If the writer parameter is omitted, a NullWriter instance is created.
-    No methods of the writer are called by NullFormatter instances.
-
-    Implementations should inherit from this class if implementing a writer
-    interface but don't need to inherit any implementation.
-
-    """
-
-    def __init__(self, writer=None):
-        if writer is None:
-            writer = NullWriter()
-        self.writer = writer
-    def end_paragraph(self, blankline): pass
-    def add_line_break(self): pass
-    def add_hor_rule(self, *args, **kw): pass
-    def add_label_data(self, format, counter, blankline=None): pass
-    def add_flowing_data(self, data): pass
-    def add_literal_data(self, data): pass
-    def flush_softspace(self): pass
-    def push_alignment(self, align): pass
-    def pop_alignment(self): pass
-    def push_font(self, x): pass
-    def pop_font(self): pass
-    def push_margin(self, margin): pass
-    def pop_margin(self): pass
-    def set_spacing(self, spacing): pass
-    def push_style(self, *styles): pass
-    def pop_style(self, n=1): pass
-    def assert_line_data(self, flag=1): pass
-
-
-class AbstractFormatter:
-    """The standard formatter.
-
-    This implementation has demonstrated wide applicability to many writers,
-    and may be used directly in most circumstances.  It has been used to
-    implement a full-featured World Wide Web browser.
-
-    """
-
-    #  Space handling policy:  blank spaces at the boundary between elements
-    #  are handled by the outermost context.  "Literal" data is not checked
-    #  to determine context, so spaces in literal data are handled directly
-    #  in all circumstances.
-
-    def __init__(self, writer):
-        self.writer = writer            # Output device
-        self.align = None               # Current alignment
-        self.align_stack = []           # Alignment stack
-        self.font_stack = []            # Font state
-        self.margin_stack = []          # Margin state
-        self.spacing = None             # Vertical spacing state
-        self.style_stack = []           # Other state, e.g. color
-        self.nospace = 1                # Should leading space be suppressed
-        self.softspace = 0              # Should a space be inserted
-        self.para_end = 1               # Just ended a paragraph
-        self.parskip = 0                # Skipped space between paragraphs?
-        self.hard_break = 1             # Have a hard break
-        self.have_label = 0
-
-    def end_paragraph(self, blankline):
-        if not self.hard_break:
-            self.writer.send_line_break()
-            self.have_label = 0
-        if self.parskip < blankline and not self.have_label:
-            self.writer.send_paragraph(blankline - self.parskip)
-            self.parskip = blankline
-            self.have_label = 0
-        self.hard_break = self.nospace = self.para_end = 1
-        self.softspace = 0
-
-    def add_line_break(self):
-        if not (self.hard_break or self.para_end):
-            self.writer.send_line_break()
-            self.have_label = self.parskip = 0
-        self.hard_break = self.nospace = 1
-        self.softspace = 0
-
-    def add_hor_rule(self, *args, **kw):
-        if not self.hard_break:
-            self.writer.send_line_break()
-        self.writer.send_hor_rule(*args, **kw)
-        self.hard_break = self.nospace = 1
-        self.have_label = self.para_end = self.softspace = self.parskip = 0
-
-    def add_label_data(self, format, counter, blankline = None):
-        if self.have_label or not self.hard_break:
-            self.writer.send_line_break()
-        if not self.para_end:
-            self.writer.send_paragraph((blankline and 1) or 0)
-        if isinstance(format, str):
-            self.writer.send_label_data(self.format_counter(format, counter))
-        else:
-            self.writer.send_label_data(format)
-        self.nospace = self.have_label = self.hard_break = self.para_end = 1
-        self.softspace = self.parskip = 0
-
-    def format_counter(self, format, counter):
-        label = ''
-        for c in format:
-            if c == '1':
-                label = label + ('%d' % counter)
-            elif c in 'aA':
-                if counter > 0:
-                    label = label + self.format_letter(c, counter)
-            elif c in 'iI':
-                if counter > 0:
-                    label = label + self.format_roman(c, counter)
-            else:
-                label = label + c
-        return label
-
-    def format_letter(self, case, counter):
-        label = ''
-        while counter > 0:
-            counter, x = divmod(counter-1, 26)
-            # This makes a strong assumption that lowercase letters
-            # and uppercase letters form two contiguous blocks, with
-            # letters in order!
-            s = chr(ord(case) + x)
-            label = s + label
-        return label
-
-    def format_roman(self, case, counter):
-        ones = ['i', 'x', 'c', 'm']
-        fives = ['v', 'l', 'd']
-        label, index = '', 0
-        # This will die of IndexError when counter is too big
-        while counter > 0:
-            counter, x = divmod(counter, 10)
-            if x == 9:
-                label = ones[index] + ones[index+1] + label
-            elif x == 4:
-                label = ones[index] + fives[index] + label
-            else:
-                if x >= 5:
-                    s = fives[index]
-                    x = x-5
-                else:
-                    s = ''
-                s = s + ones[index]*x
-                label = s + label
-            index = index + 1
-        if case == 'I':
-            return label.upper()
-        return label
-
-    def add_flowing_data(self, data):
-        if not data: return
-        prespace = data[:1].isspace()
-        postspace = data[-1:].isspace()
-        data = " ".join(data.split())
-        if self.nospace and not data:
-            return
-        elif prespace or self.softspace:
-            if not data:
-                if not self.nospace:
-                    self.softspace = 1
-                    self.parskip = 0
-                return
-            if not self.nospace:
-                data = ' ' + data
-        self.hard_break = self.nospace = self.para_end = \
-                          self.parskip = self.have_label = 0
-        self.softspace = postspace
-        self.writer.send_flowing_data(data)
-
-    def add_literal_data(self, data):
-        if not data: return
-        if self.softspace:
-            self.writer.send_flowing_data(" ")
-        self.hard_break = data[-1:] == '\n'
-        self.nospace = self.para_end = self.softspace = \
-                       self.parskip = self.have_label = 0
-        self.writer.send_literal_data(data)
-
-    def flush_softspace(self):
-        if self.softspace:
-            self.hard_break = self.para_end = self.parskip = \
-                              self.have_label = self.softspace = 0
-            self.nospace = 1
-            self.writer.send_flowing_data(' ')
-
-    def push_alignment(self, align):
-        if align and align != self.align:
-            self.writer.new_alignment(align)
-            self.align = align
-            self.align_stack.append(align)
-        else:
-            self.align_stack.append(self.align)
-
-    def pop_alignment(self):
-        if self.align_stack:
-            del self.align_stack[-1]
-        if self.align_stack:
-            self.align = align = self.align_stack[-1]
-            self.writer.new_alignment(align)
-        else:
-            self.align = None
-            self.writer.new_alignment(None)
-
-    def push_font(self, font):
-        size, i, b, tt = font
-        if self.softspace:
-            self.hard_break = self.para_end = self.softspace = 0
-            self.nospace = 1
-            self.writer.send_flowing_data(' ')
-        if self.font_stack:
-            csize, ci, cb, ctt = self.font_stack[-1]
-            if size is AS_IS: size = csize
-            if i is AS_IS: i = ci
-            if b is AS_IS: b = cb
-            if tt is AS_IS: tt = ctt
-        font = (size, i, b, tt)
-        self.font_stack.append(font)
-        self.writer.new_font(font)
-
-    def pop_font(self):
-        if self.font_stack:
-            del self.font_stack[-1]
-        if self.font_stack:
-            font = self.font_stack[-1]
-        else:
-            font = None
-        self.writer.new_font(font)
-
-    def push_margin(self, margin):
-        self.margin_stack.append(margin)
-        fstack = [m for m in self.margin_stack if m]
-        if not margin and fstack:
-            margin = fstack[-1]
-        self.writer.new_margin(margin, len(fstack))
-
-    def pop_margin(self):
-        if self.margin_stack:
-            del self.margin_stack[-1]
-        fstack = [m for m in self.margin_stack if m]
-        if fstack:
-            margin = fstack[-1]
-        else:
-            margin = None
-        self.writer.new_margin(margin, len(fstack))
-
-    def set_spacing(self, spacing):
-        self.spacing = spacing
-        self.writer.new_spacing(spacing)
-
-    def push_style(self, *styles):
-        if self.softspace:
-            self.hard_break = self.para_end = self.softspace = 0
-            self.nospace = 1
-            self.writer.send_flowing_data(' ')
-        for style in styles:
-            self.style_stack.append(style)
-        self.writer.new_styles(tuple(self.style_stack))
-
-    def pop_style(self, n=1):
-        del self.style_stack[-n:]
-        self.writer.new_styles(tuple(self.style_stack))
-
-    def assert_line_data(self, flag=1):
-        self.nospace = self.hard_break = not flag
-        self.para_end = self.parskip = self.have_label = 0
-
-
-class NullWriter:
-    """Minimal writer interface to use in testing & inheritance.
-
-    A writer which only provides the interface definition; no actions are
-    taken on any methods.  This should be the base class for all writers
-    which do not need to inherit any implementation methods.
-
-    """
-    def __init__(self): pass
-    def flush(self): pass
-    def new_alignment(self, align): pass
-    def new_font(self, font): pass
-    def new_margin(self, margin, level): pass
-    def new_spacing(self, spacing): pass
-    def new_styles(self, styles): pass
-    def send_paragraph(self, blankline): pass
-    def send_line_break(self): pass
-    def send_hor_rule(self, *args, **kw): pass
-    def send_label_data(self, data): pass
-    def send_flowing_data(self, data): pass
-    def send_literal_data(self, data): pass
-
-
-class AbstractWriter(NullWriter):
-    """A writer which can be used in debugging formatters, but not much else.
-
-    Each method simply announces itself by printing its name and
-    arguments on standard output.
-
-    """
-
-    def new_alignment(self, align):
-        print("new_alignment(%r)" % (align,))
-
-    def new_font(self, font):
-        print("new_font(%r)" % (font,))
-
-    def new_margin(self, margin, level):
-        print("new_margin(%r, %d)" % (margin, level))
-
-    def new_spacing(self, spacing):
-        print("new_spacing(%r)" % (spacing,))
-
-    def new_styles(self, styles):
-        print("new_styles(%r)" % (styles,))
-
-    def send_paragraph(self, blankline):
-        print("send_paragraph(%r)" % (blankline,))
-
-    def send_line_break(self):
-        print("send_line_break()")
-
-    def send_hor_rule(self, *args, **kw):
-        print("send_hor_rule()")
-
-    def send_label_data(self, data):
-        print("send_label_data(%r)" % (data,))
-
-    def send_flowing_data(self, data):
-        print("send_flowing_data(%r)" % (data,))
-
-    def send_literal_data(self, data):
-        print("send_literal_data(%r)" % (data,))
-
-
-class DumbWriter(NullWriter):
-    """Simple writer class which writes output on the file object passed in
-    as the file parameter or, if file is omitted, on standard output.  The
-    output is simply word-wrapped to the number of columns specified by
-    the maxcol parameter.  This class is suitable for reflowing a sequence
-    of paragraphs.
-
-    """
-
-    def __init__(self, file=None, maxcol=72):
-        self.file = file or sys.stdout
-        self.maxcol = maxcol
-        NullWriter.__init__(self)
-        self.reset()
-
-    def reset(self):
-        self.col = 0
-        self.atbreak = 0
-
-    def send_paragraph(self, blankline):
-        self.file.write('\n'*blankline)
-        self.col = 0
-        self.atbreak = 0
-
-    def send_line_break(self):
-        self.file.write('\n')
-        self.col = 0
-        self.atbreak = 0
-
-    def send_hor_rule(self, *args, **kw):
-        self.file.write('\n')
-        self.file.write('-'*self.maxcol)
-        self.file.write('\n')
-        self.col = 0
-        self.atbreak = 0
-
-    def send_literal_data(self, data):
-        self.file.write(data)
-        i = data.rfind('\n')
-        if i >= 0:
-            self.col = 0
-            data = data[i+1:]
-        data = data.expandtabs()
-        self.col = self.col + len(data)
-        self.atbreak = 0
-
-    def send_flowing_data(self, data):
-        if not data: return
-        atbreak = self.atbreak or data[0].isspace()
-        col = self.col
-        maxcol = self.maxcol
-        write = self.file.write
-        for word in data.split():
-            if atbreak:
-                if col + len(word) >= maxcol:
-                    write('\n')
-                    col = 0
-                else:
-                    write(' ')
-                    col = col + 1
-            write(word)
-            col = col + len(word)
-            atbreak = 1
-        self.col = col
-        self.atbreak = data[-1].isspace()
-
-
-def test(file = None):
-    w = DumbWriter()
-    f = AbstractFormatter(w)
-    if file is not None:
-        fp = open(file)
-    elif sys.argv[1:]:
-        fp = open(sys.argv[1])
-    else:
-        fp = sys.stdin
-    try:
-        for line in fp:
-            if line == '\n':
-                f.end_paragraph(1)
-            else:
-                f.add_flowing_data(line)
-    finally:
-        if fp is not sys.stdin:
-            fp.close()
-    f.end_paragraph(0)
-
-
-if __name__ == '__main__':
-    test()
diff --git a/Lib/test/test_sundry.py b/Lib/test/test_sundry.py
index 04e572c00a196..aab074b8a9b27 100644
--- a/Lib/test/test_sundry.py
+++ b/Lib/test/test_sundry.py
@@ -9,7 +9,7 @@
 
 class TestUntestedModules(unittest.TestCase):
     def test_untested_modules_can_be_imported(self):
-        untested = ('encodings', 'formatter')
+        untested = ('encodings',)
         with warnings_helper.check_warnings(quiet=True):
             for name in untested:
                 try:
diff --git a/Misc/NEWS.d/next/Library/2020-11-23-23-42-08.bpo-42299.Fdn4Wf.rst b/Misc/NEWS.d/next/Library/2020-11-23-23-42-08.bpo-42299.Fdn4Wf.rst
new file mode 100644
index 0000000000000..a8e156c100379
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-11-23-23-42-08.bpo-42299.Fdn4Wf.rst
@@ -0,0 +1,5 @@
+Removed the ``formatter`` module, which was deprecated in Python 3.4.
+It is somewhat obsolete, little used, and not tested. It was originally
+scheduled to be removed in Python 3.6, but such removals were delayed until
+after Python 2.7 EOL. Existing users should copy whatever classes they use
+into their code. Patch by Dong-hee Na and and Terry J. Reedy.



More information about the Python-checkins mailing list