[diffoscope] 01/01: WIP py2/pypy support
Ximin Luo
infinity0 at debian.org
Thu Jun 1 19:01:16 CEST 2017
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch WIP/py2-pypy
in repository diffoscope.
commit 5bad26d0095c5324d8656a4c4aa40b03687307bc
Author: Ximin Luo <infinity0 at debian.org>
Date: Thu Jun 1 18:59:36 2017 +0200
WIP py2/pypy support
$ PYTHONPATH=$PWD:/usr/lib/python2.7/dist-packages pypy -m diffoscope.main --debug tests/data/test?.iso
$ PYTHONPATH=$PWD:/usr/lib/python2.7/dist-packages pypy -m diffoscope.main --debug tests/data/test?.deb
$ PYTHONPATH=$PWD:/usr/lib/python2.7/dist-packages pypy -m diffoscope.main --debug tests/data/test?.tar
$ PYTHONPATH=$PWD:/usr/lib/python2.7/dist-packages pypy -m diffoscope.main --debug tests/data/test?.xz
$ PYTHONPATH=$PWD:/usr/lib/python2.7/dist-packages pypy -m diffoscope.main --debug tests/data/test?.png
---
diffoscope/comparators/__init__.py | 2 +
diffoscope/comparators/apk.py | 2 +-
diffoscope/comparators/ar.py | 2 +-
diffoscope/comparators/binary.py | 2 +-
diffoscope/comparators/cbfs.py | 2 +-
diffoscope/comparators/deb.py | 9 +--
diffoscope/comparators/debian.py | 6 +-
diffoscope/comparators/device.py | 2 +-
diffoscope/comparators/elf.py | 18 +++---
diffoscope/comparators/gettext.py | 4 +-
diffoscope/comparators/iso9660.py | 2 +-
diffoscope/comparators/java.py | 2 +-
diffoscope/comparators/ps.py | 2 +-
diffoscope/comparators/symlink.py | 2 +-
diffoscope/comparators/utils/archive.py | 8 +--
diffoscope/comparators/utils/command.py | 25 +++++++--
diffoscope/comparators/utils/container.py | 2 +-
diffoscope/comparators/utils/file.py | 11 +++-
diffoscope/comparators/utils/libarchive.py | 8 ++-
diffoscope/diff.py | 13 +++--
diffoscope/difference.py | 6 +-
diffoscope/logging.py | 5 ++
diffoscope/main.py | 11 +++-
diffoscope/presenters/html/html.py | 4 +-
diffoscope/presenters/json.py | 4 +-
diffoscope/presenters/text.py | 8 ++-
diffoscope/presenters/utils.py | 2 +
diffoscope/profiling.py | 10 ++--
diffoscope/progress.py | 6 +-
diffoscope/tempfiles.py | 89 +++++++++++++++++++++++++++++-
diffoscope/tools.py | 2 +-
31 files changed, 208 insertions(+), 63 deletions(-)
diff --git a/diffoscope/comparators/__init__.py b/diffoscope/comparators/__init__.py
index 38d9e58..5947f12 100644
--- a/diffoscope/comparators/__init__.py
+++ b/diffoscope/comparators/__init__.py
@@ -108,6 +108,8 @@ class ComparatorManager(object):
'diffoscope.comparators.{}'.format(package)
)
except ImportError:
+ import traceback
+ traceback.print_exc()
continue
self.classes.append(getattr(mod, klass_name))
diff --git a/diffoscope/comparators/apk.py b/diffoscope/comparators/apk.py
index 1d4aa0d..98580c7 100644
--- a/diffoscope/comparators/apk.py
+++ b/diffoscope/comparators/apk.py
@@ -142,7 +142,7 @@ class ApkContainer(Archive):
differences.append(self.compare_manifests(other))
except AttributeError: # no apk-specific methods, e.g. MissingArchive
pass
- differences.extend(super().compare(other, *args, **kwargs))
+ differences.extend(super(ApkContainer, self).compare(other, *args, **kwargs))
return differences
class ApkFile(File):
diff --git a/diffoscope/comparators/ar.py b/diffoscope/comparators/ar.py
index 0de0bff..e54eb52 100644
--- a/diffoscope/comparators/ar.py
+++ b/diffoscope/comparators/ar.py
@@ -38,7 +38,7 @@ logger = logging.getLogger(__name__)
class ArContainer(LibarchiveContainer):
def get_adjusted_members(self):
- members = list(super().get_adjusted_members())
+ members = list(super(ArContainer, self).get_adjusted_members())
known_ignores = {
"/" : "this is the symbol table, already accounted for in other output",
"//" : "this is the table for GNU long names, already accounted for in the archive filelist",
diff --git a/diffoscope/comparators/binary.py b/diffoscope/comparators/binary.py
index 47352d0..30e50d2 100644
--- a/diffoscope/comparators/binary.py
+++ b/diffoscope/comparators/binary.py
@@ -25,7 +25,7 @@ from .utils.file import File
class FilesystemFile(File):
def __init__(self, path, container=None):
- super().__init__(container=container)
+ super(FilesystemFile, self).__init__(container=container)
self._name = path
@property
diff --git a/diffoscope/comparators/cbfs.py b/diffoscope/comparators/cbfs.py
index 0002bbb..6a702ed 100644
--- a/diffoscope/comparators/cbfs.py
+++ b/diffoscope/comparators/cbfs.py
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
class CbfsListing(Command):
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(CbfsListing, self).__init__(*args, **kwargs)
self._header_re = re.compile(r'^.*: ([^,]+, bootblocksize [0-9]+, romsize [0-9]+, offset 0x[0-9A-Fa-f]+)$')
@tool_required('cbfstool')
diff --git a/diffoscope/comparators/deb.py b/diffoscope/comparators/deb.py
index 66a6d05..c28f8c6 100644
--- a/diffoscope/comparators/deb.py
+++ b/diffoscope/comparators/deb.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+import codecs
import re
import logging
@@ -101,7 +102,7 @@ class DebFile(File):
if not hasattr(self, '_control'):
control_file = self.as_container.control_tar.as_container.lookup_file('./control')
if control_file:
- with open(control_file.path, 'rb') as f:
+ with codecs.open(control_file.path, 'rb') as f:
self._control = deb822.Deb822(f)
return self._control
@@ -124,7 +125,7 @@ class Md5sumsFile(File):
def parse(self):
try:
md5sums = {}
- with open(self.path, 'r', encoding='utf-8') as f:
+ with codecs.open(self.path, 'r', encoding='utf-8') as f:
for line in f:
md5sum, path = re.split(r'\s+', line.strip(), maxsplit=1)
md5sums['./%s' % path] = md5sum
@@ -134,7 +135,7 @@ class Md5sumsFile(File):
return {}
def strip_checksum(self, path):
- with open(path, 'r', encoding='utf-8') as f:
+ with codecs.open(path, 'r', encoding='utf-8') as f:
for line in f:
yield " ".join(line.split(" ")[2:])
@@ -155,7 +156,7 @@ class DebTarContainer(TarContainer):
other_md5sums = other.source.container.source.container.source.md5sums
else:
other_md5sums = {}
- for my_member, other_member, comment in super().comparisons(other):
+ for my_member, other_member, comment in super(DebTarContainer, self).comparisons(other):
if my_member.name == other_member.name and \
my_md5sums.get(my_member.name, 'my') == other_md5sums.get(other_member.name, 'other'):
logger.debug('Skip %s: identical md5sum', my_member.name)
diff --git a/diffoscope/comparators/debian.py b/diffoscope/comparators/debian.py
index a5e7900..78baa83 100644
--- a/diffoscope/comparators/debian.py
+++ b/diffoscope/comparators/debian.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import absolute_import
+
import re
import os.path
import hashlib
@@ -67,7 +69,7 @@ class DebControlMember(File):
class DebControlContainer(Container):
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(DebControlContainer, self).__init__(*args, **kwargs)
self._version_re = DebControlContainer.get_version_trimming_re(self)
@staticmethod
@@ -179,7 +181,7 @@ class DotChangesFile(DebControlFile):
return True
def compare(self, other, *args, **kwargs):
- differences = super().compare(other, *args, **kwargs)
+ differences = super(DotChangesFile, self).compare(other, *args, **kwargs)
if differences is None:
return None
diff --git a/diffoscope/comparators/device.py b/diffoscope/comparators/device.py
index 9743bda..8434669 100644
--- a/diffoscope/comparators/device.py
+++ b/diffoscope/comparators/device.py
@@ -65,7 +65,7 @@ class Device(File):
if hasattr(self, '_placeholder'):
os.remove(self._placeholder)
del self._placeholder
- super().cleanup()
+ super(Device, self).cleanup()
def compare(self, other, source=None):
with open(self.path) as my_content, \
diff --git a/diffoscope/comparators/elf.py b/diffoscope/comparators/elf.py
index 17b0f26..a74e14c 100644
--- a/diffoscope/comparators/elf.py
+++ b/diffoscope/comparators/elf.py
@@ -56,7 +56,7 @@ logger = logging.getLogger(__name__)
class Readelf(Command):
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(Readelf, self).__init__(*args, **kwargs)
# we don't care about the name of the archive
self._archive_re = re.compile(r'^File: %s\(' % re.escape(self.path))
@@ -151,7 +151,7 @@ class ReadelfDebugDump(Readelf):
def __init__(self, debug_section_group, *args, **kwargs):
self._debug_section_group = debug_section_group
- super().__init__(*args, **kwargs)
+ super(ReadelfDebugDump, self).__init__(*args, **kwargs)
def readelf_options(self):
return ['--debug-dump=%s' % self._debug_section_group]
@@ -182,7 +182,7 @@ class ReadElfSection(Readelf):
def __init__(self, path, section_name, *args, **kwargs):
self._path = path
self._section_name = section_name
- super().__init__(path, *args, **kwargs)
+ super(ReadElfSection, self).__init__(path, *args, **kwargs)
@property
def section_name(self):
@@ -200,7 +200,7 @@ class ObjdumpSection(Command):
self._path = path
self._path_bin = path.encode('utf-8')
self._section_name = section_name
- super().__init__(path, *args, **kwargs)
+ super(ObjdumpSection, self).__init__(path, *args, **kwargs)
def objdump_options(self):
return []
@@ -224,7 +224,7 @@ class ObjdumpSection(Command):
return line
class ObjdumpDisassembleSection(ObjdumpSection):
- RE_SYMBOL_COMMENT = re.compile(rb'^( +[0-9a-f]+:[^#]+)# [0-9a-f]+ <[^>]+>$')
+ RE_SYMBOL_COMMENT = re.compile(r'^( +[0-9a-f]+:[^#]+)# [0-9a-f]+ <[^>]+>$')
def objdump_options(self):
# With '--line-numbers' we get the source filename and line within the
@@ -234,7 +234,7 @@ class ObjdumpDisassembleSection(ObjdumpSection):
return ['--line-numbers', '--disassemble', '--demangle']
def filter(self, line):
- line = super().filter(line)
+ line = super(ObjdumpDisassembleSection, self).filter(line)
return ObjdumpDisassembleSection.RE_SYMBOL_COMMENT.sub(r'\1', line)
@@ -270,7 +270,7 @@ def _should_skip_section(name, type):
class ElfSection(File):
def __init__(self, elf_container, member_name):
- super().__init__(container=elf_container)
+ super(ElfSection, self).__init__(container=elf_container)
self._name = member_name
@property
@@ -281,7 +281,7 @@ class ElfSection(File):
def progress_name(self):
return "{} [{}]".format(
self.container.source.progress_name,
- super().progress_name,
+ super(ElfSection, self).progress_name,
)
@property
@@ -385,7 +385,7 @@ class ElfContainer(Container):
@tool_required('readelf')
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(ElfContainer, self).__init__(*args, **kwargs)
logger.debug("Creating ElfContainer for %s", self.source.path)
cmd = ['readelf', '--wide', '--section-headers', self.source.path]
diff --git a/diffoscope/comparators/gettext.py b/diffoscope/comparators/gettext.py
index 5801e91..3bb7610 100644
--- a/diffoscope/comparators/gettext.py
+++ b/diffoscope/comparators/gettext.py
@@ -31,10 +31,10 @@ logger = logging.getLogger(__name__)
class Msgunfmt(Command):
- CHARSET_RE = re.compile(rb'^"Content-Type: [^;]+; charset=([^\\]+)\\n"$')
+ CHARSET_RE = re.compile(r'^"Content-Type: [^;]+; charset=([^\\]+)\\n"$')
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(Msgunfmt, self).__init__(*args, **kwargs)
self._header = io.BytesIO()
self._encoding = None
diff --git a/diffoscope/comparators/iso9660.py b/diffoscope/comparators/iso9660.py
index b354c74..8c38db1 100644
--- a/diffoscope/comparators/iso9660.py
+++ b/diffoscope/comparators/iso9660.py
@@ -48,7 +48,7 @@ class ISO9660PVD(Command):
class ISO9660Listing(Command):
def __init__(self, path, extension=None, *args, **kwargs):
self._extension = extension
- super().__init__(path, *args, **kwargs)
+ super(ISO9660Listing, self).__init__(path, *args, **kwargs)
@tool_required('isoinfo')
def cmdline(self):
diff --git a/diffoscope/comparators/java.py b/diffoscope/comparators/java.py
index 4b3e724..7291de5 100644
--- a/diffoscope/comparators/java.py
+++ b/diffoscope/comparators/java.py
@@ -30,7 +30,7 @@ from .utils.command import Command
class Javap(Command):
def __init__(self, path, *args, **kwargs):
- super().__init__(path, *args, **kwargs)
+ super(Javap, self).__init__(path, *args, **kwargs)
self.real_path = os.path.realpath(path)
@tool_required('javap')
diff --git a/diffoscope/comparators/ps.py b/diffoscope/comparators/ps.py
index 650da45..4bd2d0f 100644
--- a/diffoscope/comparators/ps.py
+++ b/diffoscope/comparators/ps.py
@@ -40,7 +40,7 @@ class PsFile(TextFile):
RE_FILE_TYPE = re.compile(r'^PostScript document\b')
def compare(self, other, *args, **kwargs):
- differences = super().compare(other, *args, **kwargs)
+ differences = super(PsFile, self).compare(other, *args, **kwargs)
details = None
try:
details = Difference.from_command(Pstotext, self.path, other.path)
diff --git a/diffoscope/comparators/symlink.py b/diffoscope/comparators/symlink.py
index 400297e..a4b6bd5 100644
--- a/diffoscope/comparators/symlink.py
+++ b/diffoscope/comparators/symlink.py
@@ -53,7 +53,7 @@ class Symlink(File):
if hasattr(self, '_placeholder'):
os.remove(self._placeholder)
del self._placeholder
- super().cleanup()
+ super(Symlink, self).cleanup()
def compare(self, other, source=None):
with open(self.path) as my_content, \
diff --git a/diffoscope/comparators/utils/archive.py b/diffoscope/comparators/utils/archive.py
index 510dff0..7616f48 100644
--- a/diffoscope/comparators/utils/archive.py
+++ b/diffoscope/comparators/utils/archive.py
@@ -32,7 +32,7 @@ from .container import Container
logger = logging.getLogger(__name__)
-class Archive(Container, metaclass=abc.ABCMeta):
+class Archive(Container):
def __new__(cls, source, *args, **kwargs):
if isinstance(source, MissingFile):
return super(Container, MissingArchive).__new__(MissingArchive)
@@ -40,7 +40,7 @@ class Archive(Container, metaclass=abc.ABCMeta):
return super(Container, cls).__new__(cls)
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(Archive, self).__init__(*args, **kwargs)
with profile('open_archive', self):
self._archive = self.open_archive()
@@ -82,7 +82,7 @@ class Archive(Container, metaclass=abc.ABCMeta):
class ArchiveMember(File):
def __init__(self, container, member_name):
- super().__init__(container=container)
+ super(ArchiveMember, self).__init__(container=container)
self._name = member_name
self._temp_dir = None
self._path = None
@@ -103,7 +103,7 @@ class ArchiveMember(File):
if self._temp_dir is not None:
self._temp_dir.cleanup()
self._temp_dir = None
- super().cleanup()
+ super(ArchiveMember, self).cleanup()
def is_directory(self):
return False
diff --git a/diffoscope/comparators/utils/command.py b/diffoscope/comparators/utils/command.py
index 5014140..1767006 100644
--- a/diffoscope/comparators/utils/command.py
+++ b/diffoscope/comparators/utils/command.py
@@ -20,19 +20,34 @@
import io
import abc
import logging
-import shlex
import subprocess
import threading
+try:
+ from shlex import quote as shlex_quote
+except ImportError:
+ import re
+ _find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
+ def shlex_quote(s):
+ """Return a shell-escaped version of the string *s*."""
+ if not s:
+ return "''"
+ if _find_unsafe(s) is None:
+ return s
+
+ # use single quotes, and put single quotes into double quotes
+ # the string $'b is then quoted as '$'"'"'b'
+ return "'" + s.replace("'", "'\"'\"'") + "'"
+
logger = logging.getLogger(__name__)
-class Command(object, metaclass=abc.ABCMeta):
+class Command(object):
def __init__(self, path):
self._path = path
def start(self):
- logger.debug("Executing %s", ' '.join([shlex.quote(x) for x in self.cmdline()]))
+ logger.debug("Executing %s", ' '.join([shlex_quote(x) for x in self.cmdline()]))
self._process = subprocess.Popen(self.cmdline(),
shell=False, close_fds=True,
env=self.env(),
@@ -61,7 +76,7 @@ class Command(object, metaclass=abc.ABCMeta):
raise NotImplementedError()
def shell_cmdline(self):
- return ' '.join(map(lambda x: '{}' if x == self.path else shlex.quote(x), self.cmdline()))
+ return ' '.join(map(lambda x: '{}' if x == self.path else shlex_quote(x), self.cmdline()))
def env(self):
return None # inherit parent environment by default
@@ -92,7 +107,7 @@ class Command(object, metaclass=abc.ABCMeta):
returncode = self._process.wait()
logger.debug(
"%s returned (exit code: %d)",
- ' '.join([shlex.quote(x) for x in self.cmdline()]),
+ ' '.join([shlex_quote(x) for x in self.cmdline()]),
returncode,
)
return returncode
diff --git a/diffoscope/comparators/utils/container.py b/diffoscope/comparators/utils/container.py
index b35cfa3..8652033 100644
--- a/diffoscope/comparators/utils/container.py
+++ b/diffoscope/comparators/utils/container.py
@@ -37,7 +37,7 @@ NO_COMMENT = None
logger = logging.getLogger(__name__)
-class Container(object, metaclass=abc.ABCMeta):
+class Container(object):
def __new__(cls, source):
if isinstance(source, MissingFile):
new = super(Container, MissingContainer).__new__(MissingContainer)
diff --git a/diffoscope/comparators/utils/file.py b/diffoscope/comparators/utils/file.py
index f38cead..b608f6e 100644
--- a/diffoscope/comparators/utils/file.py
+++ b/diffoscope/comparators/utils/file.py
@@ -36,6 +36,11 @@ try:
except ImportError: # noqa
tlsh = None
+try:
+ from os import scandir
+except ImportError:
+ from scandir import scandir
+
SMALL_FILE_THRESHOLD = 65536 # 64 kiB
logger = logging.getLogger(__name__)
@@ -44,10 +49,10 @@ logger = logging.getLogger(__name__)
def path_apparent_size(path=".", visited=None):
# should output the same as `du --apparent-size -bs "$path"`
if not visited:
- stat = os.stat(path, follow_symlinks=False)
+ stat = os.lstat(path)
visited = { stat.st_ino: stat.st_size }
if os.path.isdir(path) and not os.path.islink(path):
- for entry in os.scandir(path):
+ for entry in scandir(path):
inode = entry.inode()
if inode in visited:
continue
@@ -57,7 +62,7 @@ def path_apparent_size(path=".", visited=None):
return sum(visited.values())
-class File(object, metaclass=abc.ABCMeta):
+class File(object):
RE_FILE_TYPE = None
RE_FILE_EXTENSION = None
diff --git a/diffoscope/comparators/utils/libarchive.py b/diffoscope/comparators/utils/libarchive.py
index f427ede..50aafc8 100644
--- a/diffoscope/comparators/utils/libarchive.py
+++ b/diffoscope/comparators/utils/libarchive.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import absolute_import
import time
import os.path
@@ -95,7 +96,7 @@ def list_libarchive(path):
class LibarchiveMember(ArchiveMember):
def __init__(self, archive, entry):
- super().__init__(archive, entry.pathname)
+ super(LibarchiveMember, self).__init__(archive, entry.pathname)
def is_directory(self):
return False
@@ -229,7 +230,8 @@ class LibarchiveContainer(Archive):
logger.debug("Extracting %s to %s", entry.pathname, dst)
- os.makedirs(os.path.dirname(dst), exist_ok=True)
+ if not os.path.exists(os.path.dirname(dst)):
+ os.makedirs(os.path.dirname(dst))
try:
with open(dst, 'wb') as f:
for block in entry.get_blocks():
@@ -246,4 +248,4 @@ class LibarchiveContainer(Archive):
def hide_trivial_dirs(item):
file1, file2, comment = item
return not (isinstance(file1, Directory) and isinstance(file2, Directory) and comment is None)
- return filter(hide_trivial_dirs, super().comparisons(other))
+ return filter(hide_trivial_dirs, super(LibarchiveContainer, self).comparisons(other))
diff --git a/diffoscope/diff.py b/diffoscope/diff.py
index a300217..edbcd72 100644
--- a/diffoscope/diff.py
+++ b/diffoscope/diff.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import print_function
+
import re
import io
import os
@@ -187,9 +189,10 @@ def run_diff(fifo1, fifo2, end_nl_q1, end_nl_q2):
return parser.diff
class FIFOFeeder(threading.Thread):
- def __init__(self, feeder, fifo_path, end_nl_q=None, *, daemon=True):
+ def __init__(self, feeder, fifo_path, end_nl_q=None, daemon=True):
os.mkfifo(fifo_path)
- super().__init__(daemon=daemon)
+ super(FIFOFeeder, self).__init__()
+ self.setDaemon(daemon)
self.feeder = feeder
self.fifo_path = fifo_path
self.end_nl_q = Queue() if end_nl_q is None else end_nl_q
@@ -221,17 +224,19 @@ class FIFOFeeder(threading.Thread):
# Now clear the fd's nonblocking flag to let writes block normally.
fcntl.fcntl(fifo_fd, fcntl.F_SETFL, 0)
- with open(fifo_fd, 'wb') as fifo:
+ with os.fdopen(fifo_fd, 'wb') as fifo:
# The queue works around a unified diff limitation: if there's
# no newlines in both don't make it a difference
end_nl = self.feeder(fifo)
self.end_nl_q.put(end_nl)
except Exception as error:
+ import traceback
+ traceback.print_exc()
self._exception = error
def join(self):
self._want_join.set()
- super().join()
+ super(FIFOFeeder, self).join()
if self._exception is not None:
raise self._exception
diff --git a/diffoscope/difference.py b/diffoscope/difference.py
index ca45041..5f2e454 100644
--- a/diffoscope/difference.py
+++ b/diffoscope/difference.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import print_function
+
import signal
import hashlib
import logging
@@ -76,9 +78,9 @@ class Difference(object):
self._source1 = path1
self._source2 = path2
# Ensure renderable types
- if not isinstance(self._source1, str):
+ if not isinstance(self._source1, (str, unicode)):
raise TypeError("path1/source[0] is not a string")
- if not isinstance(self._source2, str):
+ if not isinstance(self._source2, (str, unicode)):
raise TypeError("path2/source[1] is not a string")
# Whether the unified_diff already contains line numbers inside itself
self._has_internal_linenos = has_internal_linenos
diff --git a/diffoscope/logging.py b/diffoscope/logging.py
index 38bb9bf..a59c447 100644
--- a/diffoscope/logging.py
+++ b/diffoscope/logging.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import absolute_import
+
import logging
@@ -33,3 +35,6 @@ def setup_logging(debug, log_handler):
'%Y-%m-%d %H:%M:%S',
)
ch.setFormatter(formatter)
+
+# helps us avoid "absolute_import" in all modules that "import logging"
+getLogger = logging.getLogger
diff --git a/diffoscope/main.py b/diffoscope/main.py
index 8ce6e1c..6483776 100644
--- a/diffoscope/main.py
+++ b/diffoscope/main.py
@@ -20,6 +20,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import print_function
+
import os
import sys
import signal
@@ -27,6 +29,13 @@ import logging
import argparse
import traceback
+try:
+ from subprocess import DEVNULL # py3k
+except ImportError:
+ DEVNULL = open(os.devnull, 'wb')
+ import subprocess
+ subprocess.DEVNULL = DEVNULL
+
from . import VERSION
from .path import set_path
from .tools import tool_required, OS_NAMES, get_current_os
@@ -340,7 +349,7 @@ def main(args=None):
except KeyboardInterrupt:
logger.info('Keyboard Interrupt')
sys.exit(2)
- except BrokenPipeError:
+ except OSError:
sys.exit(2)
except Exception:
traceback.print_exc()
diff --git a/diffoscope/presenters/html/html.py b/diffoscope/presenters/html/html.py
index c502fa2..2188a9a 100644
--- a/diffoscope/presenters/html/html.py
+++ b/diffoscope/presenters/html/html.py
@@ -31,6 +31,8 @@
# Dave Burt <dave (at) burt.id.au> (mainly for html theme)
#
+from __future__ import unicode_literals
+
import io
import os
import re
@@ -537,7 +539,7 @@ def output_html(difference, css_url=None, print_func=None):
Default presenter, all in one HTML file
"""
if print_func is None:
- print_func = print
+ print_func = getattr(__builtins__, "print")
print_func = create_limited_print_func(print_func, Config().max_report_size)
try:
output_header(css_url, print_func)
diff --git a/diffoscope/presenters/json.py b/diffoscope/presenters/json.py
index 18d8f56..edf7fd2 100644
--- a/diffoscope/presenters/json.py
+++ b/diffoscope/presenters/json.py
@@ -31,12 +31,12 @@ class JSONPresenter(Presenter):
self.stack = []
self.print_func = print_func
- super().__init__()
+ super(JSONPresenter, self).__init__()
def start(self, difference):
root = []
self.stack = [root]
- super().start(difference)
+ super(JSONPresenter, self).start(difference)
root[0][JSON_FORMAT_MAGIC] = JSON_FORMAT_VERSION
root[0].move_to_end(JSON_FORMAT_MAGIC, last=False)
diff --git a/diffoscope/presenters/text.py b/diffoscope/presenters/text.py
index 7a16385..63d729a 100644
--- a/diffoscope/presenters/text.py
+++ b/diffoscope/presenters/text.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import unicode_literals
+
import re
import sys
import logging
@@ -41,7 +43,7 @@ class TextPresenter(Presenter):
)
self.color = color
- super().__init__()
+ super(TextPresenter, self).__init__()
@classmethod
def run(cls, data, difference, parsed_args):
@@ -57,6 +59,8 @@ class TextPresenter(Presenter):
try:
presenter.start(difference)
except UnicodeEncodeError:
+ import traceback
+ traceback.print_exc()
logger.critical(
"Console is unable to print Unicode characters. Set e.g. "
"PYTHONIOENCODING=utf-8"
@@ -65,7 +69,7 @@ class TextPresenter(Presenter):
def start(self, difference):
try:
- super().start(difference)
+ super(TextPresenter, self).start(difference)
except PrintLimitReached:
self.print_func("Max output size reached.", force=True)
diff --git a/diffoscope/presenters/utils.py b/diffoscope/presenters/utils.py
index 09f1b74..02d0af0 100644
--- a/diffoscope/presenters/utils.py
+++ b/diffoscope/presenters/utils.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import print_function, unicode_literals
+
import sys
import codecs
import contextlib
diff --git a/diffoscope/profiling.py b/diffoscope/profiling.py
index e381b78..c23727c 100644
--- a/diffoscope/profiling.py
+++ b/diffoscope/profiling.py
@@ -70,11 +70,11 @@ class ProfileManager(object):
with make_printer(parsed_args.profile_output) as fn:
self.output(fn)
- def output(self, print):
+ def output(self, printf):
title = "Profiling output for: {}".format(' '.join(sys.argv))
- print(title)
- print("=" * len(title))
+ printf(title)
+ printf("=" * len(title))
for namespace, keys in sorted(self.data.items(), key=lambda x: x[0]):
subtitle = "{} (total time: {:.3f}s)".format(
@@ -82,10 +82,10 @@ class ProfileManager(object):
sum(x['time'] for x in keys.values()),
)
- print("\n{}\n{}\n".format(subtitle, "-" * len(subtitle)))
+ printf("\n{}\n{}\n".format(subtitle, "-" * len(subtitle)))
for value, totals in sorted(keys.items(), key=lambda x: x[1]['time'], reverse=True):
- print(" {:10.3f}s {:5d} call{} {}".format(
+ printf(" {:10.3f}s {:5d} call{} {}".format(
totals['time'],
totals['count'],
' ' if totals['count'] == 1 else 's',
diff --git a/diffoscope/progress.py b/diffoscope/progress.py
index b00dd48..e020e36 100644
--- a/diffoscope/progress.py
+++ b/diffoscope/progress.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import print_function, absolute_import, unicode_literals
+
import os
import sys
import json
@@ -29,14 +31,14 @@ class ProgressLoggingHandler(logging.StreamHandler):
def __init__(self, progressbar):
self.progressbar = progressbar
- super().__init__()
+ super(ProgressLoggingHandler, self).__init__()
def emit(self, record):
try:
# delete the current line (i.e. the progress bar)
self.stream.write("\r\033[K")
self.flush()
- super().emit(record)
+ super(ProgressLoggingHandler, self).emit(record)
if not self.progressbar.bar.finished:
self.progressbar.bar.update()
except Exception:
diff --git a/diffoscope/tempfiles.py b/diffoscope/tempfiles.py
index b63bb16..054f781 100644
--- a/diffoscope/tempfiles.py
+++ b/diffoscope/tempfiles.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+from __future__ import print_function
+
import os
import logging
import tempfile
@@ -37,7 +39,7 @@ def get_named_temporary_file(*args, **kwargs):
def get_temporary_directory(*args, **kwargs):
kwargs['suffix'] = kwargs.pop('suffix', '_diffoscope')
- d = tempfile.TemporaryDirectory(*args, **kwargs)
+ d = TemporaryDirectory(*args, **kwargs)
_DIRS.append(d)
return d
@@ -60,3 +62,88 @@ def clean_all_temp_files():
x.cleanup()
except:
logger.exception("Unable to delete %s", x)
+
+
+import warnings
+from tempfile import mkdtemp
+
+class TemporaryDirectory(object):
+ """Create and return a temporary directory. This has the same
+ behavior as mkdtemp but can be used as a context manager. For
+ example:
+
+ with TemporaryDirectory() as tmpdir:
+ ...
+
+ Upon exiting the context, the directory and everything contained
+ in it are removed.
+ """
+
+ def __init__(self, suffix="", prefix="tmp", dir=None):
+ self._closed = False
+ self.name = None # Handle mkdtemp raising an exception
+ self.name = mkdtemp(suffix, prefix, dir)
+
+ def __repr__(self):
+ return "<{} {!r}>".format(self.__class__.__name__, self.name)
+
+ def __enter__(self):
+ return self.name
+
+ def cleanup(self, _warn=False):
+ if self.name and not self._closed:
+ try:
+ self._rmtree(self.name)
+ except (TypeError, AttributeError) as ex:
+ # Issue #10188: Emit a warning on stderr
+ # if the directory could not be cleaned
+ # up due to missing globals
+ if "None" not in str(ex):
+ raise
+ print("ERROR: {!r} while cleaning up {!r}".format(ex, self,),
+ file=_sys.stderr)
+ return
+ self._closed = True
+ if _warn:
+ self._warn("Implicitly cleaning up {!r}".format(self),
+ ResourceWarning)
+
+ def __exit__(self, exc, value, tb):
+ self.cleanup()
+
+ def __del__(self):
+ # Issue a ResourceWarning if implicit cleanup needed
+ self.cleanup(_warn=True)
+
+ # XXX (ncoghlan): The following code attempts to make
+ # this class tolerant of the module nulling out process
+ # that happens during CPython interpreter shutdown
+ # Alas, it doesn't actually manage it. See issue #10188
+ _listdir = staticmethod(os.listdir)
+ _path_join = staticmethod(os.path.join)
+ _isdir = staticmethod(os.path.isdir)
+ _islink = staticmethod(os.path.islink)
+ _remove = staticmethod(os.remove)
+ _rmdir = staticmethod(os.rmdir)
+ _warn = warnings.warn
+
+ def _rmtree(self, path):
+ # Essentially a stripped down version of shutil.rmtree. We can't
+ # use globals because they may be None'ed out at shutdown.
+ for name in self._listdir(path):
+ fullname = self._path_join(path, name)
+ try:
+ isdir = self._isdir(fullname) and not self._islink(fullname)
+ except OSError:
+ isdir = False
+ if isdir:
+ self._rmtree(fullname)
+ else:
+ try:
+ self._remove(fullname)
+ except OSError:
+ pass
+ try:
+ self._rmdir(path)
+ except OSError:
+ pass
diff --git a/diffoscope/tools.py b/diffoscope/tools.py
index 2882b01..fbe617f 100644
--- a/diffoscope/tools.py
+++ b/diffoscope/tools.py
@@ -28,7 +28,7 @@ from .profiling import profile
# Memoize calls to ``distutils.spawn.find_executable`` to avoid excessive stat
# calls
-find_executable = functools.lru_cache()(find_executable)
+#find_executable = functools.lru_cache()(find_executable)
# The output of --help and --list-tools will use the order of this dict.
# Please keep it alphabetized.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/diffoscope.git
More information about the diffoscope
mailing list