bpo-42299: Remove formatter module (GH-23476)
This commit is contained in:
parent
b9127dd6ee
commit
be319c0c10
|
@ -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.
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ the `Python Package Index <https://pypi.org>`_.
|
||||||
custominterp.rst
|
custominterp.rst
|
||||||
modules.rst
|
modules.rst
|
||||||
language.rst
|
language.rst
|
||||||
misc.rst
|
|
||||||
windows.rst
|
windows.rst
|
||||||
unix.rst
|
unix.rst
|
||||||
superseded.rst
|
superseded.rst
|
||||||
|
|
|
@ -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
|
|
|
@ -449,6 +449,12 @@ Removed
|
||||||
:c:func:`PyParser_SimpleParseFileFlags` and :c:func:`PyNode_Compile`
|
:c:func:`PyParser_SimpleParseFileFlags` and :c:func:`PyNode_Compile`
|
||||||
that were deprecated in 3.9 due to the switch to the new PEG parser.
|
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
|
Porting to Python 3.10
|
||||||
======================
|
======================
|
||||||
|
|
452
Lib/formatter.py
452
Lib/formatter.py
|
@ -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()
|
|
|
@ -9,7 +9,7 @@ import unittest
|
||||||
|
|
||||||
class TestUntestedModules(unittest.TestCase):
|
class TestUntestedModules(unittest.TestCase):
|
||||||
def test_untested_modules_can_be_imported(self):
|
def test_untested_modules_can_be_imported(self):
|
||||||
untested = ('encodings', 'formatter')
|
untested = ('encodings',)
|
||||||
with warnings_helper.check_warnings(quiet=True):
|
with warnings_helper.check_warnings(quiet=True):
|
||||||
for name in untested:
|
for name in untested:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue