Commit 494687b6 authored by Jérémy Bobbio's avatar Jérémy Bobbio

Fix coding styling according to PEP8

parent 2c64b9b3
......@@ -27,20 +27,26 @@ from debbindiff import logger
import debbindiff.comparators
from debbindiff.presenters.html import output_html
def create_parser():
parser = argparse.ArgumentParser(description='Highlight differences between two builds of Debian packages')
parser.add_argument('--debug', dest='debug', action='store_true', default=False)
parser = argparse.ArgumentParser(
description='Highlight differences between two builds '
'of Debian packages')
parser.add_argument('--debug', dest='debug', action='store_true',
default=False)
parser.add_argument('--html', metavar='output', dest='html_output')
parser.add_argument('file1', help='first file to compare')
parser.add_argument('file2', help='second file to compare')
return parser
def main():
parser = create_parser()
parsed_args = parser.parse_args(sys.argv[1:])
if parsed_args.debug:
logger.setLevel(logging.DEBUG)
differences = debbindiff.comparators.compare_files(parsed_args.file1, parsed_args.file2)
differences = debbindiff.comparators.compare_files(
parsed_args.file1, parsed_args.file2)
if len(differences) > 0 and parsed_args.html_output:
output = open(parsed_args.html_output, 'w')
def print_func(*args, **kwargs):
......
......@@ -48,9 +48,11 @@ import subprocess
from debian import deb822
from debbindiff import logger
class ChangesFileException(Exception):
pass
class Changes(object):
"""
Changes object to help process and store information regarding Debian
......@@ -111,7 +113,8 @@ class Changes(object):
def get_path(self, filename):
"""
Return the full, absolute path to a file referenced by the changes file.
Return the full, absolute path to a file referenced by the changes
file.
"""
return os.path.join(self._directory, filename)
......@@ -245,8 +248,9 @@ class Changes(object):
Throws a :class:`dput.exceptions.ChangesFileException` if there's
an issue with the GPG signature. Returns the GPG key ID.
"""
pipe = subprocess.Popen(
["gpg", "--status-fd", "1", "--verify", "--batch", self.get_changes_file()],
pipe = subprocess.Popen(
["gpg", "--status-fd", "1", "--verify", "--batch",
self.get_changes_file()],
shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
gpg_output, gpg_output_stderr = pipe.communicate()
print gpg_output
......
......@@ -26,7 +26,8 @@ from debbindiff.comparators.binary import compare_binary_files
from debbindiff.comparators.bzip2 import compare_bzip2_files
from debbindiff.comparators.changes import compare_changes_files
from debbindiff.comparators.deb import compare_deb_files, compare_md5sums_files
from debbindiff.comparators.elf import compare_elf_files, compare_static_lib_files
from debbindiff.comparators.elf import \
compare_elf_files, compare_static_lib_files
from debbindiff.comparators.gettext import compare_mo_files
from debbindiff.comparators.gzip import compare_gzip_files
from debbindiff.comparators.haskell import compare_hi_files
......@@ -35,12 +36,14 @@ from debbindiff.comparators.tar import compare_tar_files
from debbindiff.comparators.xz import compare_xz_files
from debbindiff.comparators.zip import compare_zip_files
def guess_mime_type(path):
if not hasattr(guess_mime_type, 'mimedb'):
guess_mime_type.mimedb = magic.open(magic.MIME)
guess_mime_type.mimedb.load()
return guess_mime_type.mimedb.file(path)
def compare_unknown(path1, path2, source=None):
logger.debug("compare unknown path: %s and %s" % (path1, path2))
mime_type1 = guess_mime_type(path1)
......@@ -56,22 +59,25 @@ def compare_unknown(path1, path2, source=None):
return compare_text_files(path1, path2, encoding, source)
return compare_binary_files(path1, path2, source)
COMPARATORS = [
(None, r'\.changes$', compare_changes_files),
(None, r'\.(p_)?hi$', compare_hi_files),
(None, r'\/\./md5sums$', compare_md5sums_files),
(None, r'\.mo$', compare_mo_files),
(r'^application/x-xz(;|$)', r'\.xz$', compare_xz_files),
(r'^application/x-tar(;|$)', r'\.tar$', compare_tar_files),
(r'^application/zip(;|$)', r'\.(zip|jar)$', compare_zip_files),
(r'^application/x-debian-package(;|$)', r'\.deb$', compare_deb_files),
(r'^application/x-gzip(;|$)', r'\.gz$', compare_gzip_files),
(r'^application/x-bzip2(;|$)', r'\.bzip2$', compare_bzip2_files),
(r'^application/x-executable(;|$)', None, compare_elf_files),
(r'^application/x-sharedlib(;|$)', r'\.so($|\.[0-9.]+$)', compare_elf_files),
(None, r'\.a$', compare_static_lib_files),
(None, r'\.changes$', compare_changes_files),
(None, r'\.(p_)?hi$', compare_hi_files),
(None, r'\/\./md5sums$', compare_md5sums_files),
(None, r'\.mo$', compare_mo_files),
(r'^application/x-xz(;|$)', r'\.xz$', compare_xz_files),
(r'^application/x-tar(;|$)', r'\.tar$', compare_tar_files),
(r'^application/zip(;|$)', r'\.(zip|jar)$', compare_zip_files),
(r'^application/x-debian-package(;|$)', r'\.deb$', compare_deb_files),
(r'^application/x-gzip(;|$)', r'\.gz$', compare_gzip_files),
(r'^application/x-bzip2(;|$)', r'\.bzip2$', compare_bzip2_files),
(r'^application/x-executable(;|$)', None, compare_elf_files),
(r'^application/x-sharedlib(;|$)', r'\.so($|\.[0-9.]+$)',
compare_elf_files),
(None, r'\.a$', compare_static_lib_files),
]
def compare_files(path1, path2, source=None):
if not os.path.isfile(path1):
logger.critical("%s is not a file" % path1)
......@@ -80,11 +86,13 @@ def compare_files(path1, path2, source=None):
logger.critical("%s is not a file" % path2)
sys.exit(2)
for mime_type_regex, filename_regex, comparator in COMPARATORS:
if filename_regex and re.search(filename_regex, path1) and re.search(filename_regex, path2):
if filename_regex and re.search(filename_regex, path1) \
and re.search(filename_regex, path2):
return comparator(path1, path2, source)
if mime_type_regex:
mime_type1 = guess_mime_type(path1)
mime_type2 = guess_mime_type(path2)
if re.search(mime_type_regex, mime_type1) and re.search(mime_type_regex, mime_type2):
if re.search(mime_type_regex, mime_type1) and \
re.search(mime_type_regex, mime_type2):
return comparator(path1, path2, source)
return compare_unknown(path1, path2, source)
......@@ -20,12 +20,15 @@
from debbindiff.difference import Difference
import subprocess
def get_hexdump(path):
return subprocess.check_output(['xxd', path], shell=False)
def compare_binary_files(path1, path2, source=None):
hexdump1 = get_hexdump(path1)
hexdump2 = get_hexdump(path2)
if hexdump1 == hexdump2:
return []
return [Difference(hexdump1.splitlines(1), hexdump2.splitlines(1), path1, path2, source)]
return [Difference(hexdump1.splitlines(1), hexdump2.splitlines(1),
path1, path2, source)]
......@@ -24,6 +24,7 @@ import debbindiff.comparators
from debbindiff.comparators.utils import binary_fallback, make_temp_directory
from debbindiff.difference import get_source
@contextmanager
def decompress_bzip2(path):
with make_temp_directory() as temp_dir:
......@@ -37,6 +38,7 @@ def decompress_bzip2(path):
shell=False, stdout=temp_file, stderr=None)
yield temp_path
@binary_fallback
def compare_bzip2_files(path1, path2, source=None):
with decompress_bzip2(path1) as new_path1:
......
......@@ -23,13 +23,15 @@ from debbindiff.changes import Changes
import debbindiff.comparators
from debbindiff.difference import Difference, get_source
DOT_CHANGES_FIELDS = [
"Format", "Source", "Binary", "Architecture",
"Version", "Distribution", "Urgency",
"Maintainer", "Changed-By", "Description",
"Changes"
"Format", "Source", "Binary", "Architecture",
"Version", "Distribution", "Urgency",
"Maintainer", "Changed-By", "Description",
"Changes",
]
def compare_changes_files(path1, path2, source=None):
try:
dot_changes1 = Changes(filename=path1)
......@@ -73,12 +75,14 @@ def compare_changes_files(path1, path2, source=None):
d1 = files1[filename]
d2 = files2[filename]
if d1['md5sum'] != d2['md5sum']:
logger.debug("%s mentioned in .changes have differences" % filename)
logger.debug("%s mentioned in .changes have "
"differences" % filename)
files_difference.add_details(
debbindiff.comparators.compare_files(dot_changes1.get_path(filename),
dot_changes2.get_path(filename),
source=get_source(dot_changes1.get_path(filename),
dot_changes2.get_path(filename))))
debbindiff.comparators.compare_files(
dot_changes1.get_path(filename),
dot_changes2.get_path(filename),
source=get_source(dot_changes1.get_path(filename),
dot_changes2.get_path(filename))))
differences.append(files_difference)
return differences
......@@ -22,7 +22,9 @@ from debian.arfile import ArFile
from debbindiff import logger
from debbindiff.difference import Difference, get_source
import debbindiff.comparators
from debbindiff.comparators.utils import binary_fallback, make_temp_directory, are_same_binaries, get_ar_content
from debbindiff.comparators.utils import \
binary_fallback, make_temp_directory, are_same_binaries, get_ar_content
@binary_fallback
def compare_deb_files(path1, path2, source=None):
......@@ -34,7 +36,8 @@ def compare_deb_files(path1, path2, source=None):
with make_temp_directory() as temp_dir2:
logger.debug('content1 %s' % (ar1.getnames(),))
logger.debug('content2 %s' % (ar2.getnames(),))
for name in sorted(set(ar1.getnames()).intersection(ar2.getnames())):
for name in sorted(set(ar1.getnames())
.intersection(ar2.getnames())):
logger.debug('extract member %s' % (name,))
member1 = ar1.getmember(name)
member2 = ar2.getmember(name)
......@@ -53,10 +56,15 @@ def compare_deb_files(path1, path2, source=None):
content1 = get_ar_content(path1)
content2 = get_ar_content(path2)
if content1 != content2:
differences.append(Difference(content1.splitlines(1), content2.splitlines(1), path1, path2, source="metadata"))
differences.append(Difference(
content1.splitlines(1), content2.splitlines(1),
path1, path2, source="metadata"))
return differences
def compare_md5sums_files(path1, path2, source=None):
if are_same_binaries(path1, path2):
return []
return [Difference(None, None, path1, path2, source=get_source(path1, path2), comment="Files in package differs")]
return [Difference(None, None, path1, path2,
source=get_source(path1, path2),
comment="Files in package differs")]
......@@ -23,21 +23,31 @@ import subprocess
from debbindiff.comparators.utils import binary_fallback, get_ar_content
from debbindiff.difference import Difference
def readelf_all(path):
output = subprocess.check_output(['readelf', '--all', path], shell=False)
output = subprocess.check_output(
['readelf', '--all', path],
shell=False)
# the full path can appear in the output, we need to remove it
return re.sub(re.escape(path), os.path.basename(path), output)
def readelf_debug_dump(path):
output = subprocess.check_output(['readelf', '--debug-dump', path], shell=False)
output = subprocess.check_output(
['readelf', '--debug-dump', path],
shell=False)
# the full path can appear in the output, we need to remove it
return re.sub(re.escape(path), os.path.basename(path), output)
def objdump_disassemble(path):
output = subprocess.check_output(['objdump', '--disassemble', path], shell=False)
output = subprocess.check_output(
['objdump', '--disassemble', path],
shell=False)
# the full path appears in the output, we need to remove it
return re.sub(re.escape(path), os.path.basename(path), output)
# this one is not wrapped with binary_fallback and is used
# by both compare_elf_files and compare_static_lib_files
def _compare_elf_data(path1, path2, source=None):
......@@ -45,21 +55,29 @@ def _compare_elf_data(path1, path2, source=None):
all1 = readelf_all(path1)
all2 = readelf_all(path2)
if all1 != all2:
differences.append(Difference(all1.splitlines(1), all2.splitlines(1), path1, path2, source='readelf --all'))
differences.append(Difference(
all1.splitlines(1), all2.splitlines(1),
path1, path2, source='readelf --all'))
debug_dump1 = readelf_debug_dump(path1)
debug_dump2 = readelf_debug_dump(path2)
if debug_dump1 != debug_dump2:
differences.append(Difference(debug_dump1.splitlines(1), debug_dump2.splitlines(1), path1, path2, source='readelf --debug-dump'))
differences.append(Difference(
debug_dump1.splitlines(1), debug_dump2.splitlines(1),
path1, path2, source='readelf --debug-dump'))
objdump1 = objdump_disassemble(path1)
objdump2 = objdump_disassemble(path2)
if objdump1 != objdump2:
differences.append(Difference(objdump1.splitlines(1), objdump2.splitlines(1), path1, path2, source='objdump --disassemble'))
differences.append(Difference(
objdump1.splitlines(1), objdump2.splitlines(1),
path1, path2, source='objdump --disassemble'))
return differences
@binary_fallback
def compare_elf_files(path1, path2, source=None):
return _compare_elf_data(path1, path2, source=None)
@binary_fallback
def compare_static_lib_files(path1, path2, source=None):
differences = []
......@@ -67,6 +85,8 @@ def compare_static_lib_files(path1, path2, source=None):
content1 = get_ar_content(path1)
content2 = get_ar_content(path2)
if content1 != content2:
differences.append(Difference(content1.splitlines(1), content2.splitlines(1), path1, path2, source="metadata"))
differences.append(Difference(
content1.splitlines(1), content2.splitlines(1),
path1, path2, source="metadata"))
differences.extend(_compare_elf_data(path1, path2, source))
return differences
......@@ -21,13 +21,16 @@ import subprocess
from debbindiff.comparators.utils import binary_fallback
from debbindiff.difference import Difference
def msgunfmt(path):
return subprocess.check_output(['msgunfmt', path], shell=False)
@binary_fallback
def compare_mo_files(path1, path2, source=None):
mo1 = msgunfmt(path1)
mo2 = msgunfmt(path2)
if mo1 != mo2:
return [Difference(mo1.splitlines(1), mo2.splitlines(1), path1, path2, source='msgunfmt')]
return [Difference(mo1.splitlines(1), mo2.splitlines(1),
path1, path2, source='msgunfmt')]
return []
......@@ -24,6 +24,7 @@ import debbindiff.comparators
from debbindiff.comparators.utils import binary_fallback, make_temp_directory
from debbindiff.difference import Difference, get_source
@contextmanager
def decompress_gzip(path):
with make_temp_directory() as temp_dir:
......@@ -37,9 +38,11 @@ def decompress_gzip(path):
shell=False, stdout=temp_file, stderr=None)
yield temp_path
def get_gzip_metadata(path):
return subprocess.check_output(['file', '--brief', path])
@binary_fallback
def compare_gzip_files(path1, path2, source=None):
differences = []
......@@ -47,7 +50,9 @@ def compare_gzip_files(path1, path2, source=None):
metadata1 = get_gzip_metadata(path1)
metadata2 = get_gzip_metadata(path2)
if metadata1 != metadata2:
differences.append(Difference(metadata1.splitlines(1), metadata2.splitlines(1), path1, path2, source='metadata'))
differences.append(Difference(
metadata1.splitlines(1), metadata2.splitlines(1),
path1, path2, source='metadata'))
# check content
with decompress_gzip(path1) as new_path1:
with decompress_gzip(path2) as new_path2:
......
......@@ -21,13 +21,16 @@ import subprocess
from debbindiff.comparators.utils import binary_fallback
from debbindiff.difference import Difference
def show_iface(path):
return subprocess.check_output(['ghc', '--show-iface', path], shell=False)
@binary_fallback
def compare_hi_files(path1, path2, source=None):
iface1 = show_iface(path1)
iface2 = show_iface(path2)
if iface1 != iface2:
return [Difference(iface1.splitlines(1), iface2.splitlines(1), path1, path2, source='ghc --show-iface')]
return [Difference(iface1.splitlines(1), iface2.splitlines(1),
path1, path2, source='ghc --show-iface')]
return []
......@@ -26,6 +26,7 @@ from debbindiff.difference import Difference
import debbindiff.comparators
from debbindiff.comparators.utils import binary_fallback, make_temp_directory
def get_tar_content(tar):
orig_stdout = sys.stdout
output = StringIO()
......@@ -36,6 +37,7 @@ def get_tar_content(tar):
finally:
sys.stdout = orig_stdout
@binary_fallback
def compare_tar_files(path1, path2, source=None):
differences = []
......@@ -46,7 +48,8 @@ def compare_tar_files(path1, path2, source=None):
with make_temp_directory() as temp_dir2:
logger.debug('content1 %s' % (tar1.getnames(),))
logger.debug('content2 %s' % (tar2.getnames(),))
for name in sorted(set(tar1.getnames()).intersection(tar2.getnames())):
for name in sorted(set(tar1.getnames())
.intersection(tar2.getnames())):
member1 = tar1.getmember(name)
member2 = tar2.getmember(name)
if not member1.isfile() or not member2.isfile():
......@@ -66,5 +69,7 @@ def compare_tar_files(path1, path2, source=None):
content1 = get_tar_content(tar1)
content2 = get_tar_content(tar2)
if content1 != content2:
differences.append(Difference(content1.splitlines(1), content2.splitlines(1), path1, path2, source="metadata"))
differences.append(Difference(
content1.splitlines(1), content2.splitlines(1),
path1, path2, source="metadata"))
return differences
......@@ -20,6 +20,7 @@
import codecs
from debbindiff.difference import Difference
def compare_text_files(path1, path2, encoding, source=None):
lines1 = codecs.open(path1, 'r', encoding=encoding).readlines()
lines2 = codecs.open(path2, 'r', encoding=encoding).readlines()
......
......@@ -25,8 +25,9 @@ import tempfile
from debbindiff.comparators.binary import compare_binary_files
from debbindiff.difference import Difference, get_source
def are_same_binaries(path1, path2):
BUF_SIZE = 20 * 2 ** 10 # 20 kB
BUF_SIZE = 20 * 2 ** 10 # 20 kB
h1 = hashlib.md5()
f1 = open(path1, 'rb')
h2 = hashlib.md5()
......@@ -42,6 +43,7 @@ def are_same_binaries(path1, path2):
return False
return True
# decorator that will create a fallback on binary diff if no differences
# are detected
def binary_fallback(original_function):
......@@ -52,18 +54,22 @@ def binary_fallback(original_function):
# no differences detected inside? let's at least do a binary diff
if len(inside_differences) == 0:
difference = compare_binary_files(path1, path2)[0]
difference.comment = "No differences found inside, yet data differs"
difference.comment = \
"No differences found inside, yet data differs"
else:
difference = Difference(None, None, path1, path2, source=get_source(path1, path2))
difference = Difference(None, None, path1, path2,
source=get_source(path1, path2))
difference.add_details(inside_differences)
return [difference]
return with_fallback
@contextmanager
def make_temp_directory():
temp_dir = tempfile.mkdtemp(suffix='debbindiff')
yield temp_dir
shutil.rmtree(temp_dir)
def get_ar_content(path):
return subprocess.check_output(['ar', 'tv', path], shell=False)
......@@ -24,6 +24,7 @@ import debbindiff.comparators
from debbindiff.comparators.utils import binary_fallback, make_temp_directory
from debbindiff.difference import get_source
@contextmanager
def decompress_xz(path):
with make_temp_directory() as temp_dir:
......@@ -37,6 +38,7 @@ def decompress_xz(path):
shell=False, stdout=temp_file, stderr=None)
yield temp_path
@binary_fallback
def compare_xz_files(path1, path2, source=None):
with decompress_xz(path1) as new_path1:
......@@ -44,4 +46,3 @@ def compare_xz_files(path1, path2, source=None):
return debbindiff.comparators.compare_files(
new_path1, new_path2,
source=get_source(new_path1, new_path2))
......@@ -26,11 +26,13 @@ from debbindiff.difference import Difference
import debbindiff.comparators
from debbindiff.comparators.utils import binary_fallback, make_temp_directory
def get_zipinfo(path):
output = subprocess.check_output(['zipinfo', path], shell=False)
# the full path appears in the output, we need to remove it
return re.sub(re.escape(path), os.path.basename(path), output)
@binary_fallback
def compare_zip_files(path1, path2, source=None):
differences = []
......@@ -39,7 +41,8 @@ def compare_zip_files(path1, path2, source=None):
# look up differences in content
with make_temp_directory() as temp_dir1:
with make_temp_directory() as temp_dir2:
for name in sorted(set(zip1.namelist()).intersection(zip2.namelist())):
for name in sorted(set(zip1.namelist())
.intersection(zip2.namelist())):
# skip directories
if name.endswith('/'):
continue
......@@ -58,5 +61,7 @@ def compare_zip_files(path1, path2, source=None):
zipinfo1 = get_zipinfo(path1)
zipinfo2 = get_zipinfo(path2)
if zipinfo1 != zipinfo2:
differences.append(Difference(zipinfo1.splitlines(1), zipinfo2.splitlines(1), path1, path2, source="metadata"))
differences.append(Difference(
zipinfo1.splitlines(1), zipinfo2.splitlines(1),
path1, path2, source="metadata"))
return differences
......@@ -19,9 +19,12 @@
import os.path
class Difference(object):
def __init__(self, lines1, lines2, path1, path2, source=None, comment=None):
# allow to override declared file paths, useful when comparing tempfiles
def __init__(self, lines1, lines2, path1, path2, source=None,
comment=None):
# allow to override declared file paths, useful when comparing
# tempfiles
if source:
self._source1 = source
self._source2 = source
......@@ -64,8 +67,8 @@ class Difference(object):
def add_details(self, differences):
self._details.extend(differences)
def get_source(path1, path2):
if os.path.basename(path1) == os.path.basename(path2):
return os.path.basename(path1)
return None
......@@ -86,11 +86,13 @@ FOOTER = """
</html>
"""
MAX_PAGE_SIZE = 2000 * 2 ** 10 # 2000 kB
MAX_PAGE_SIZE = 2000 * 2 ** 10 # 2000 kB
class PrintLimitReached(Exception):
pass
def create_limited_print_func(print_func):
def limited_print_func(s, force=False):
if not hasattr(limited_print_func, 'char_count'):
......@@ -101,6 +103,7 @@ def create_limited_print_func(print_func):
raise PrintLimitReached()
return limited_print_func
# Huge thanks to Stefaan Himpe for this solution:
# http://technogems.blogspot.com/2011/09/generate-side-by-side-diffs-in-html.html
def create_diff(lines1, lines2):
......@@ -124,21 +127,28 @@ def create_diff(lines1, lines2):
], shell=False, close_fds=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = open(diff_path).read()
output = re.search(r'(<table.*</table>)', output, flags=re.MULTILINE | re.DOTALL).group(1)
output = re.sub(r'<th.*</th>', '', output, flags=re.MULTILINE | re.DOTALL)
output = re.search(r'(<table.*</table>)', output,
flags=re.MULTILINE | re.DOTALL).group(1)
output = re.sub(r'<th.*</th>', '', output,
flags=re.MULTILINE | re.DOTALL)
return output
def output_difference(difference, print_func):
logger.debug('html output for %s' % (difference.source1,))
print_func("<div class='difference'>")
try:
if difference.source1 == difference.source2:
print_func("<div><span class='source'>%s</div>" % escape(difference.source1))
print_func("<div><span class='source'>%s</div>"
% escape(difference.source1))
else:
print_func("<div><span class='source'>%s</span> vs.</div>" % escape(difference.source1))
print_func("<div><span class='source'>%s</span></div>" % escape(difference.source2))
print_func("<div><span class='source'>%s</span> vs.</div>"
% escape(difference.source1))
print_func("<div><span class='source'>%s</span></div>"
% escape(difference.source2))
if difference.comment:
print_func("<div class='comment'>%s</div>" % escape(difference.comment))
print_func("<div class='comment'>%s</div>"
% escape(difference.comment))
if difference.lines1 and difference.lines2:
print_func(create_diff(difference.lines1, difference.lines2))
for detail in difference.details:
......@@ -149,15 +159,17 @@ def output_difference(difference, print_func):
finally:
print_func("</div>", force=True)
def output_html(differences, print_func=None):
if print_func is None:
print_func = print
print_func = create_limited_print_func(print_func)
try:
print_func(HEADER % { 'title': escape(' '.join(sys.argv)) })
print_func(HEADER % {'title': escape(' '.join(sys.argv))})
for difference in differences:
output_difference(difference, print_func)
except PrintLimitReached:
logger.debug('print limit reached')
print_func("<div class='error'>Max output size reached.</div>", force=True)
print_func("<div class='error'>Max output size reached.</div>",
force=True)
print_func(FOOTER, force=True)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment