mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Remove deprecated modules binhex and formatter. (#5003)
These modules have been deprecated as of Python 3.11 and can safely be removed from the standard library.
This commit is contained in:
committed by
GitHub
parent
4721b7df92
commit
6fb15535ed
502
Lib/binhex.py
vendored
502
Lib/binhex.py
vendored
@@ -1,502 +0,0 @@
|
||||
"""Macintosh binhex compression/decompression.
|
||||
|
||||
easy interface:
|
||||
binhex(inputfilename, outputfilename)
|
||||
hexbin(inputfilename, outputfilename)
|
||||
"""
|
||||
|
||||
#
|
||||
# Jack Jansen, CWI, August 1995.
|
||||
#
|
||||
# The module is supposed to be as compatible as possible. Especially the
|
||||
# easy interface should work "as expected" on any platform.
|
||||
# XXXX Note: currently, textfiles appear in mac-form on all platforms.
|
||||
# We seem to lack a simple character-translate in python.
|
||||
# (we should probably use ISO-Latin-1 on all but the mac platform).
|
||||
# XXXX The simple routines are too simple: they expect to hold the complete
|
||||
# files in-core. Should be fixed.
|
||||
# XXXX It would be nice to handle AppleDouble format on unix
|
||||
# (for servers serving macs).
|
||||
# XXXX I don't understand what happens when you get 0x90 times the same byte on
|
||||
# input. The resulting code (xx 90 90) would appear to be interpreted as an
|
||||
# escaped *value* of 0x90. All coders I've seen appear to ignore this nicety...
|
||||
#
|
||||
import binascii
|
||||
import contextlib
|
||||
import io
|
||||
import os
|
||||
import struct
|
||||
import warnings
|
||||
|
||||
warnings.warn('the binhex module is deprecated', DeprecationWarning,
|
||||
stacklevel=2)
|
||||
|
||||
|
||||
__all__ = ["binhex","hexbin","Error"]
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
# States (what have we written)
|
||||
_DID_HEADER = 0
|
||||
_DID_DATA = 1
|
||||
|
||||
# Various constants
|
||||
REASONABLY_LARGE = 32768 # Minimal amount we pass the rle-coder
|
||||
LINELEN = 64
|
||||
RUNCHAR = b"\x90"
|
||||
|
||||
#
|
||||
# This code is no longer byte-order dependent
|
||||
|
||||
|
||||
class FInfo:
|
||||
def __init__(self):
|
||||
self.Type = '????'
|
||||
self.Creator = '????'
|
||||
self.Flags = 0
|
||||
|
||||
def getfileinfo(name):
|
||||
finfo = FInfo()
|
||||
with io.open(name, 'rb') as fp:
|
||||
# Quick check for textfile
|
||||
data = fp.read(512)
|
||||
if 0 not in data:
|
||||
finfo.Type = 'TEXT'
|
||||
fp.seek(0, 2)
|
||||
dsize = fp.tell()
|
||||
dir, file = os.path.split(name)
|
||||
file = file.replace(':', '-', 1)
|
||||
return file, finfo, dsize, 0
|
||||
|
||||
class openrsrc:
|
||||
def __init__(self, *args):
|
||||
pass
|
||||
|
||||
def read(self, *args):
|
||||
return b''
|
||||
|
||||
def write(self, *args):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
# DeprecationWarning is already emitted on "import binhex". There is no need
|
||||
# to repeat the warning at each call to deprecated binascii functions.
|
||||
@contextlib.contextmanager
|
||||
def _ignore_deprecation_warning():
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
yield
|
||||
|
||||
|
||||
class _Hqxcoderengine:
|
||||
"""Write data to the coder in 3-byte chunks"""
|
||||
|
||||
def __init__(self, ofp):
|
||||
self.ofp = ofp
|
||||
self.data = b''
|
||||
self.hqxdata = b''
|
||||
self.linelen = LINELEN - 1
|
||||
|
||||
def write(self, data):
|
||||
self.data = self.data + data
|
||||
datalen = len(self.data)
|
||||
todo = (datalen // 3) * 3
|
||||
data = self.data[:todo]
|
||||
self.data = self.data[todo:]
|
||||
if not data:
|
||||
return
|
||||
with _ignore_deprecation_warning():
|
||||
self.hqxdata = self.hqxdata + binascii.b2a_hqx(data)
|
||||
self._flush(0)
|
||||
|
||||
def _flush(self, force):
|
||||
first = 0
|
||||
while first <= len(self.hqxdata) - self.linelen:
|
||||
last = first + self.linelen
|
||||
self.ofp.write(self.hqxdata[first:last] + b'\r')
|
||||
self.linelen = LINELEN
|
||||
first = last
|
||||
self.hqxdata = self.hqxdata[first:]
|
||||
if force:
|
||||
self.ofp.write(self.hqxdata + b':\r')
|
||||
|
||||
def close(self):
|
||||
if self.data:
|
||||
with _ignore_deprecation_warning():
|
||||
self.hqxdata = self.hqxdata + binascii.b2a_hqx(self.data)
|
||||
self._flush(1)
|
||||
self.ofp.close()
|
||||
del self.ofp
|
||||
|
||||
class _Rlecoderengine:
|
||||
"""Write data to the RLE-coder in suitably large chunks"""
|
||||
|
||||
def __init__(self, ofp):
|
||||
self.ofp = ofp
|
||||
self.data = b''
|
||||
|
||||
def write(self, data):
|
||||
self.data = self.data + data
|
||||
if len(self.data) < REASONABLY_LARGE:
|
||||
return
|
||||
with _ignore_deprecation_warning():
|
||||
rledata = binascii.rlecode_hqx(self.data)
|
||||
self.ofp.write(rledata)
|
||||
self.data = b''
|
||||
|
||||
def close(self):
|
||||
if self.data:
|
||||
with _ignore_deprecation_warning():
|
||||
rledata = binascii.rlecode_hqx(self.data)
|
||||
self.ofp.write(rledata)
|
||||
self.ofp.close()
|
||||
del self.ofp
|
||||
|
||||
class BinHex:
|
||||
def __init__(self, name_finfo_dlen_rlen, ofp):
|
||||
name, finfo, dlen, rlen = name_finfo_dlen_rlen
|
||||
close_on_error = False
|
||||
if isinstance(ofp, str):
|
||||
ofname = ofp
|
||||
ofp = io.open(ofname, 'wb')
|
||||
close_on_error = True
|
||||
try:
|
||||
ofp.write(b'(This file must be converted with BinHex 4.0)\r\r:')
|
||||
hqxer = _Hqxcoderengine(ofp)
|
||||
self.ofp = _Rlecoderengine(hqxer)
|
||||
self.crc = 0
|
||||
if finfo is None:
|
||||
finfo = FInfo()
|
||||
self.dlen = dlen
|
||||
self.rlen = rlen
|
||||
self._writeinfo(name, finfo)
|
||||
self.state = _DID_HEADER
|
||||
except:
|
||||
if close_on_error:
|
||||
ofp.close()
|
||||
raise
|
||||
|
||||
def _writeinfo(self, name, finfo):
|
||||
nl = len(name)
|
||||
if nl > 63:
|
||||
raise Error('Filename too long')
|
||||
d = bytes([nl]) + name.encode("latin-1") + b'\0'
|
||||
tp, cr = finfo.Type, finfo.Creator
|
||||
if isinstance(tp, str):
|
||||
tp = tp.encode("latin-1")
|
||||
if isinstance(cr, str):
|
||||
cr = cr.encode("latin-1")
|
||||
d2 = tp + cr
|
||||
|
||||
# Force all structs to be packed with big-endian
|
||||
d3 = struct.pack('>h', finfo.Flags)
|
||||
d4 = struct.pack('>ii', self.dlen, self.rlen)
|
||||
info = d + d2 + d3 + d4
|
||||
self._write(info)
|
||||
self._writecrc()
|
||||
|
||||
def _write(self, data):
|
||||
self.crc = binascii.crc_hqx(data, self.crc)
|
||||
self.ofp.write(data)
|
||||
|
||||
def _writecrc(self):
|
||||
# XXXX Should this be here??
|
||||
# self.crc = binascii.crc_hqx('\0\0', self.crc)
|
||||
if self.crc < 0:
|
||||
fmt = '>h'
|
||||
else:
|
||||
fmt = '>H'
|
||||
self.ofp.write(struct.pack(fmt, self.crc))
|
||||
self.crc = 0
|
||||
|
||||
def write(self, data):
|
||||
if self.state != _DID_HEADER:
|
||||
raise Error('Writing data at the wrong time')
|
||||
self.dlen = self.dlen - len(data)
|
||||
self._write(data)
|
||||
|
||||
def close_data(self):
|
||||
if self.dlen != 0:
|
||||
raise Error('Incorrect data size, diff=%r' % (self.rlen,))
|
||||
self._writecrc()
|
||||
self.state = _DID_DATA
|
||||
|
||||
def write_rsrc(self, data):
|
||||
if self.state < _DID_DATA:
|
||||
self.close_data()
|
||||
if self.state != _DID_DATA:
|
||||
raise Error('Writing resource data at the wrong time')
|
||||
self.rlen = self.rlen - len(data)
|
||||
self._write(data)
|
||||
|
||||
def close(self):
|
||||
if self.state is None:
|
||||
return
|
||||
try:
|
||||
if self.state < _DID_DATA:
|
||||
self.close_data()
|
||||
if self.state != _DID_DATA:
|
||||
raise Error('Close at the wrong time')
|
||||
if self.rlen != 0:
|
||||
raise Error("Incorrect resource-datasize, diff=%r" % (self.rlen,))
|
||||
self._writecrc()
|
||||
finally:
|
||||
self.state = None
|
||||
ofp = self.ofp
|
||||
del self.ofp
|
||||
ofp.close()
|
||||
|
||||
def binhex(inp, out):
|
||||
"""binhex(infilename, outfilename): create binhex-encoded copy of a file"""
|
||||
finfo = getfileinfo(inp)
|
||||
ofp = BinHex(finfo, out)
|
||||
|
||||
with io.open(inp, 'rb') as ifp:
|
||||
# XXXX Do textfile translation on non-mac systems
|
||||
while True:
|
||||
d = ifp.read(128000)
|
||||
if not d: break
|
||||
ofp.write(d)
|
||||
ofp.close_data()
|
||||
|
||||
ifp = openrsrc(inp, 'rb')
|
||||
while True:
|
||||
d = ifp.read(128000)
|
||||
if not d: break
|
||||
ofp.write_rsrc(d)
|
||||
ofp.close()
|
||||
ifp.close()
|
||||
|
||||
class _Hqxdecoderengine:
|
||||
"""Read data via the decoder in 4-byte chunks"""
|
||||
|
||||
def __init__(self, ifp):
|
||||
self.ifp = ifp
|
||||
self.eof = 0
|
||||
|
||||
def read(self, totalwtd):
|
||||
"""Read at least wtd bytes (or until EOF)"""
|
||||
decdata = b''
|
||||
wtd = totalwtd
|
||||
#
|
||||
# The loop here is convoluted, since we don't really now how
|
||||
# much to decode: there may be newlines in the incoming data.
|
||||
while wtd > 0:
|
||||
if self.eof: return decdata
|
||||
wtd = ((wtd + 2) // 3) * 4
|
||||
data = self.ifp.read(wtd)
|
||||
#
|
||||
# Next problem: there may not be a complete number of
|
||||
# bytes in what we pass to a2b. Solve by yet another
|
||||
# loop.
|
||||
#
|
||||
while True:
|
||||
try:
|
||||
with _ignore_deprecation_warning():
|
||||
decdatacur, self.eof = binascii.a2b_hqx(data)
|
||||
break
|
||||
except binascii.Incomplete:
|
||||
pass
|
||||
newdata = self.ifp.read(1)
|
||||
if not newdata:
|
||||
raise Error('Premature EOF on binhex file')
|
||||
data = data + newdata
|
||||
decdata = decdata + decdatacur
|
||||
wtd = totalwtd - len(decdata)
|
||||
if not decdata and not self.eof:
|
||||
raise Error('Premature EOF on binhex file')
|
||||
return decdata
|
||||
|
||||
def close(self):
|
||||
self.ifp.close()
|
||||
|
||||
class _Rledecoderengine:
|
||||
"""Read data via the RLE-coder"""
|
||||
|
||||
def __init__(self, ifp):
|
||||
self.ifp = ifp
|
||||
self.pre_buffer = b''
|
||||
self.post_buffer = b''
|
||||
self.eof = 0
|
||||
|
||||
def read(self, wtd):
|
||||
if wtd > len(self.post_buffer):
|
||||
self._fill(wtd - len(self.post_buffer))
|
||||
rv = self.post_buffer[:wtd]
|
||||
self.post_buffer = self.post_buffer[wtd:]
|
||||
return rv
|
||||
|
||||
def _fill(self, wtd):
|
||||
self.pre_buffer = self.pre_buffer + self.ifp.read(wtd + 4)
|
||||
if self.ifp.eof:
|
||||
with _ignore_deprecation_warning():
|
||||
self.post_buffer = self.post_buffer + \
|
||||
binascii.rledecode_hqx(self.pre_buffer)
|
||||
self.pre_buffer = b''
|
||||
return
|
||||
|
||||
#
|
||||
# Obfuscated code ahead. We have to take care that we don't
|
||||
# end up with an orphaned RUNCHAR later on. So, we keep a couple
|
||||
# of bytes in the buffer, depending on what the end of
|
||||
# the buffer looks like:
|
||||
# '\220\0\220' - Keep 3 bytes: repeated \220 (escaped as \220\0)
|
||||
# '?\220' - Keep 2 bytes: repeated something-else
|
||||
# '\220\0' - Escaped \220: Keep 2 bytes.
|
||||
# '?\220?' - Complete repeat sequence: decode all
|
||||
# otherwise: keep 1 byte.
|
||||
#
|
||||
mark = len(self.pre_buffer)
|
||||
if self.pre_buffer[-3:] == RUNCHAR + b'\0' + RUNCHAR:
|
||||
mark = mark - 3
|
||||
elif self.pre_buffer[-1:] == RUNCHAR:
|
||||
mark = mark - 2
|
||||
elif self.pre_buffer[-2:] == RUNCHAR + b'\0':
|
||||
mark = mark - 2
|
||||
elif self.pre_buffer[-2:-1] == RUNCHAR:
|
||||
pass # Decode all
|
||||
else:
|
||||
mark = mark - 1
|
||||
|
||||
with _ignore_deprecation_warning():
|
||||
self.post_buffer = self.post_buffer + \
|
||||
binascii.rledecode_hqx(self.pre_buffer[:mark])
|
||||
self.pre_buffer = self.pre_buffer[mark:]
|
||||
|
||||
def close(self):
|
||||
self.ifp.close()
|
||||
|
||||
class HexBin:
|
||||
def __init__(self, ifp):
|
||||
if isinstance(ifp, str):
|
||||
ifp = io.open(ifp, 'rb')
|
||||
#
|
||||
# Find initial colon.
|
||||
#
|
||||
while True:
|
||||
ch = ifp.read(1)
|
||||
if not ch:
|
||||
raise Error("No binhex data found")
|
||||
# Cater for \r\n terminated lines (which show up as \n\r, hence
|
||||
# all lines start with \r)
|
||||
if ch == b'\r':
|
||||
continue
|
||||
if ch == b':':
|
||||
break
|
||||
|
||||
hqxifp = _Hqxdecoderengine(ifp)
|
||||
self.ifp = _Rledecoderengine(hqxifp)
|
||||
self.crc = 0
|
||||
self._readheader()
|
||||
|
||||
def _read(self, len):
|
||||
data = self.ifp.read(len)
|
||||
self.crc = binascii.crc_hqx(data, self.crc)
|
||||
return data
|
||||
|
||||
def _checkcrc(self):
|
||||
filecrc = struct.unpack('>h', self.ifp.read(2))[0] & 0xffff
|
||||
#self.crc = binascii.crc_hqx('\0\0', self.crc)
|
||||
# XXXX Is this needed??
|
||||
self.crc = self.crc & 0xffff
|
||||
if filecrc != self.crc:
|
||||
raise Error('CRC error, computed %x, read %x'
|
||||
% (self.crc, filecrc))
|
||||
self.crc = 0
|
||||
|
||||
def _readheader(self):
|
||||
len = self._read(1)
|
||||
fname = self._read(ord(len))
|
||||
rest = self._read(1 + 4 + 4 + 2 + 4 + 4)
|
||||
self._checkcrc()
|
||||
|
||||
type = rest[1:5]
|
||||
creator = rest[5:9]
|
||||
flags = struct.unpack('>h', rest[9:11])[0]
|
||||
self.dlen = struct.unpack('>l', rest[11:15])[0]
|
||||
self.rlen = struct.unpack('>l', rest[15:19])[0]
|
||||
|
||||
self.FName = fname
|
||||
self.FInfo = FInfo()
|
||||
self.FInfo.Creator = creator
|
||||
self.FInfo.Type = type
|
||||
self.FInfo.Flags = flags
|
||||
|
||||
self.state = _DID_HEADER
|
||||
|
||||
def read(self, *n):
|
||||
if self.state != _DID_HEADER:
|
||||
raise Error('Read data at wrong time')
|
||||
if n:
|
||||
n = n[0]
|
||||
n = min(n, self.dlen)
|
||||
else:
|
||||
n = self.dlen
|
||||
rv = b''
|
||||
while len(rv) < n:
|
||||
rv = rv + self._read(n-len(rv))
|
||||
self.dlen = self.dlen - n
|
||||
return rv
|
||||
|
||||
def close_data(self):
|
||||
if self.state != _DID_HEADER:
|
||||
raise Error('close_data at wrong time')
|
||||
if self.dlen:
|
||||
dummy = self._read(self.dlen)
|
||||
self._checkcrc()
|
||||
self.state = _DID_DATA
|
||||
|
||||
def read_rsrc(self, *n):
|
||||
if self.state == _DID_HEADER:
|
||||
self.close_data()
|
||||
if self.state != _DID_DATA:
|
||||
raise Error('Read resource data at wrong time')
|
||||
if n:
|
||||
n = n[0]
|
||||
n = min(n, self.rlen)
|
||||
else:
|
||||
n = self.rlen
|
||||
self.rlen = self.rlen - n
|
||||
return self._read(n)
|
||||
|
||||
def close(self):
|
||||
if self.state is None:
|
||||
return
|
||||
try:
|
||||
if self.rlen:
|
||||
dummy = self.read_rsrc(self.rlen)
|
||||
self._checkcrc()
|
||||
finally:
|
||||
self.state = None
|
||||
self.ifp.close()
|
||||
|
||||
def hexbin(inp, out):
|
||||
"""hexbin(infilename, outfilename) - Decode binhexed file"""
|
||||
ifp = HexBin(inp)
|
||||
finfo = ifp.FInfo
|
||||
if not out:
|
||||
out = ifp.FName
|
||||
|
||||
with io.open(out, 'wb') as ofp:
|
||||
# XXXX Do translation on non-mac systems
|
||||
while True:
|
||||
d = ifp.read(128000)
|
||||
if not d: break
|
||||
ofp.write(d)
|
||||
ifp.close_data()
|
||||
|
||||
d = ifp.read_rsrc(128000)
|
||||
if d:
|
||||
ofp = openrsrc(out, 'wb')
|
||||
ofp.write(d)
|
||||
while True:
|
||||
d = ifp.read_rsrc(128000)
|
||||
if not d: break
|
||||
ofp.write(d)
|
||||
ofp.close()
|
||||
|
||||
ifp.close()
|
||||
452
Lib/formatter.py
vendored
452
Lib/formatter.py
vendored
@@ -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()
|
||||
Reference in New Issue
Block a user