diff --git a/06d2fd5b90c29cbfe9b938676cc85c514cbbcca1.patch b/06d2fd5b90c29cbfe9b938676cc85c514cbbcca1.patch new file mode 100644 index 0000000..d558b6c --- /dev/null +++ b/06d2fd5b90c29cbfe9b938676cc85c514cbbcca1.patch @@ -0,0 +1,657 @@ +From 06d2fd5b90c29cbfe9b938676cc85c514cbbcca1 Mon Sep 17 00:00:00 2001 +From: Shlomi Fish +Date: Wed, 11 Aug 2021 19:19:01 +0300 +Subject: [PATCH] issue217 : formatting.py deprecated fix. + +This file is derived from /usr/lib/python3.9/formatter.py . All changes +are placed under CC0. + +See: + +* https://github.com/shlomif/PySolFC/issues/217 + +* https://bugzilla.redhat.com/show_bug.cgi?id=1990043 +--- + pysollib/formatter.py | 474 +++++++++++++++++++++++++++++++++++ + pysollib/htmllib2.py | 10 +- + pysollib/kivy/tkhtml.py | 8 +- + pysollib/pysolgtk/tkhtml.py | 8 +- + pysollib/ui/tktile/tkhtml.py | 8 +- + 5 files changed, 492 insertions(+), 16 deletions(-) + create mode 100644 pysollib/formatter.py + +diff --git a/pysollib/formatter.py b/pysollib/formatter.py +new file mode 100644 +index 00000000..f572a7bb +--- /dev/null ++++ b/pysollib/formatter.py +@@ -0,0 +1,474 @@ ++""" ++ ++This file is derived from /usr/lib/python3.9/formatter.py . All changes ++are placed under CC0. ++ ++See: ++ ++* https://github.com/shlomif/PySolFC/issues/217 ++ ++* https://bugzilla.redhat.com/show_bug.cgi?id=1990043 ++ ++---- ++ ++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 ++if False: ++ 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/pysollib/htmllib2.py b/pysollib/htmllib2.py +index 7ff6b2a9..dab753e7 100644 +--- a/pysollib/htmllib2.py ++++ b/pysollib/htmllib2.py +@@ -4,7 +4,7 @@ + http://www.w3.org/hypertext/WWW/MarkUp/html-spec/html-spec_toc.html + """ + +-from formatter import AS_IS ++from pysollib.formatter import AS_IS + + from six.moves import html_parser + +@@ -483,7 +483,7 @@ def unknown_endtag(self, tag): + + def test(args=None): + import sys +- import formatter ++ import pysollib.formatter + + if not args: + args = sys.argv[1:] +@@ -508,9 +508,11 @@ def test(args=None): + sys.exit(1) + + if silent: +- f = formatter.NullFormatter() ++ f = pysollib.formatter.NullFormatter() + else: +- f = formatter.AbstractFormatter(formatter.DumbWriter()) ++ f = pysollib.formatter.AbstractFormatter( ++ pysollib.formatter.DumbWriter() ++ ) + + p = HTMLParser(f) + p.feed(data) +diff --git a/pysollib/kivy/tkhtml.py b/pysollib/kivy/tkhtml.py +index 152b281c..54c0db0e 100644 +--- a/pysollib/kivy/tkhtml.py ++++ b/pysollib/kivy/tkhtml.py +@@ -21,7 +21,6 @@ + # + # ---------------------------------------------------------------------------# + +-import formatter + import os + import sys + +@@ -29,6 +28,7 @@ + from kivy.uix.button import Button + from kivy.uix.label import Label + ++import pysollib.formatter + import pysollib.htmllib2 as htmllib + from pysollib.kivy.LApp import LPopCommander + from pysollib.kivy.LApp import LScrollView +@@ -84,9 +84,9 @@ def cmp2(a, b): + return (a > b) - (a < b) + + +-class tkHTMLWriter(formatter.NullWriter): ++class tkHTMLWriter(pysollib.formatter.NullWriter): + def __init__(self, text, viewer, app): +- formatter.NullWriter.__init__(self) ++ pysollib.formatter.NullWriter.__init__(self) + + self.text = text + self.viewer = viewer +@@ -630,7 +630,7 @@ def display(self, url, add=1, relpath=1, xview=0, yview=0): + # self.images = {} + self.text.textbuffer = '' + writer = tkHTMLWriter(self.text, self, self.app) +- fmt = formatter.AbstractFormatter(writer) ++ fmt = pysollib.formatter.AbstractFormatter(writer) + parser = tkHTMLParser(fmt) + parser.feed(data) + parser.close() +diff --git a/pysollib/pysolgtk/tkhtml.py b/pysollib/pysolgtk/tkhtml.py +index 798efda2..f1c18659 100644 +--- a/pysollib/pysolgtk/tkhtml.py ++++ b/pysollib/pysolgtk/tkhtml.py +@@ -21,7 +21,6 @@ + # + # --------------------------------------------------------------------------- + +-import formatter + import htmllib + import os + import sys +@@ -34,6 +33,7 @@ + + import pango + ++import pysollib.formatter + from pysollib.mfxutil import Struct, openURL + from pysollib.mygettext import _ + from pysollib.settings import TITLE +@@ -55,9 +55,9 @@ + # * + # ************************************************************************ + +-class tkHTMLWriter(formatter.NullWriter): ++class tkHTMLWriter(pysollib.formatter.NullWriter): + def __init__(self, text, viewer, app): +- formatter.NullWriter.__init__(self) ++ pysollib.formatter.NullWriter.__init__(self) + + self.text = text # gtk.TextBuffer + self.viewer = viewer # HTMLViewer +@@ -489,7 +489,7 @@ def display(self, url, add=1, relpath=1, position=(0, 0)): + self.textbuffer.delete(start, end) + + writer = tkHTMLWriter(self.textbuffer, self, self.app) +- fmt = formatter.AbstractFormatter(writer) ++ fmt = pysollib.formatter.AbstractFormatter(writer) + parser = tkHTMLParser(fmt) + parser.feed(data) + parser.close() +diff --git a/pysollib/ui/tktile/tkhtml.py b/pysollib/ui/tktile/tkhtml.py +index acab5711..afcb7257 100644 +--- a/pysollib/ui/tktile/tkhtml.py ++++ b/pysollib/ui/tktile/tkhtml.py +@@ -21,10 +21,10 @@ + # + # --------------------------------------------------------------------------- + +-import formatter + import os + import sys + ++import pysollib.formatter + import pysollib.htmllib2 as htmllib + from pysollib.mfxutil import openURL + from pysollib.mygettext import _ +@@ -40,9 +40,9 @@ + # ************************************************************************ + + +-class tkHTMLWriter(formatter.NullWriter): ++class tkHTMLWriter(pysollib.formatter.NullWriter): + def __init__(self, text, viewer, app): +- formatter.NullWriter.__init__(self) ++ pysollib.formatter.NullWriter.__init__(self) + + self.text = text + self.viewer = viewer +@@ -379,7 +379,7 @@ def display(self, url, add=1, relpath=1, xview=0, yview=0): + self.text.delete("1.0", "end") + # self.images = {} + writer = tkHTMLWriter(self.text, self, self.app) +- fmt = formatter.AbstractFormatter(writer) ++ fmt = pysollib.formatter.AbstractFormatter(writer) + parser = tkHTMLParser(fmt) + parser.feed(data) + parser.close()