# Path: diffoscope/exc.py
# class RequiredToolNotFound(Exception):
#     def __init__(self, command):
#         self.command = command
# 
#     def get_package(self):
#         try:
#             providers = EXTERNAL_TOOLS[self.command]
#         except KeyError:  # noqa
#             return None
# 
#         return providers.get(get_current_os(), None)
# 
# class OutputParsingError(Exception):
#     def __init__(self, command, object):
#         self.command = command
#         self.object_class = object.__class__
# 
# Path: diffoscope/tools.py
# def tool_required(command):
#     """
#     Decorator that checks if the specified tool is installed
#     """
#     if not hasattr(tool_required, 'all'):
#         tool_required.all = set()
#     tool_required.all.add(command)
#     def wrapper(original_function):
#         if find_executable(command):
#             @functools.wraps(original_function)
#             def tool_check(*args, **kwargs):
#                 with profile('command', command):
#                     return original_function(*args, **kwargs)
#         else:
#             @functools.wraps(original_function)
#             def tool_check(*args, **kwargs):
#                 from .exc import RequiredToolNotFound
#                 raise RequiredToolNotFound(command)
#         return tool_check
#     return wrapper


import os
import re
import abc
import magic
import logging
import subprocess
    import tlsh
from diffoscope.exc import RequiredToolNotFound, OutputParsingError
from diffoscope.tools import tool_required
from diffoscope.profiling import profile
from diffoscope.difference import Difference
        from .compare import compare_binary_files
            ('cmp', '-s', self.path, other.path),
            shell=False,
            close_fds=True,
        ) == 0

    # To be specialized directly, or by implementing compare_details
    def compare(self, other, source=None):
        if hasattr(self, 'compare_details') or self.as_container:
            try:
                difference = self._compare_using_details(other, source)
                # no differences detected inside? let's at least do a binary diff
                if difference is None:
                    difference = self.compare_bytes(other, source=source)
                    if difference is None:
                        return None
                    difference.add_comment(
                        "No file format specific differences found inside, "
                        "yet data differs ({})".format(self.magic_file_type),
                    )
            except subprocess.CalledProcessError as e:
                difference = self.compare_bytes(other, source=source)
                if e.output:
                    output = re.sub(r'^', '    ', e.output.decode('utf-8', errors='replace'), flags=re.MULTILINE)
                else:
                    output = '<none>'
                cmd = ' '.join(e.cmd)
                if difference is None:
                    return None
                difference.add_comment("Command `%s` exited with %d. Output:\n%s"
                                       % (cmd, e.returncode, output))

# Path: diffoscope/exc.py
# class RequiredToolNotFound(Exception):
#     def __init__(self, command):
#         self.command = command
# 
#     def get_package(self):
#         try:
#             providers = EXTERNAL_TOOLS[self.command]
#         except KeyError:  # noqa
#             return None
# 
#         return providers.get(get_current_os(), None)
# 
# class OutputParsingError(Exception):
#     def __init__(self, command, object):
#         self.command = command
#         self.object_class = object.__class__
# 
# Path: diffoscope/tools.py
# def tool_required(command):
#     """
#     Decorator that checks if the specified tool is installed
#     """
#     if not hasattr(tool_required, 'all'):
#         tool_required.all = set()
#     tool_required.all.add(command)
#     def wrapper(original_function):
#         if find_executable(command):
#             @functools.wraps(original_function)
#             def tool_check(*args, **kwargs):
#                 with profile('command', command):
#                     return original_function(*args, **kwargs)
#         else:
#             @functools.wraps(original_function)
#             def tool_check(*args, **kwargs):
#                 from .exc import RequiredToolNotFound
#                 raise RequiredToolNotFound(command)
#         return tool_check
#     return wrapper


# Path: diffoscope/comparators/utils/file.py
import os
import re
import abc
import magic
import logging
import subprocess
    import tlsh
from diffoscope.exc import RequiredToolNotFound, OutputParsingError
from diffoscope.tools import tool_required
from diffoscope.profiling import profile
from diffoscope.difference import Difference
        from .compare import compare_binary_files

            ('cmp', '-s', self.path, other.path),
            shell=False,
            close_fds=True,
        ) == 0

    # To be specialized directly, or by implementing compare_details
    def compare(self, other, source=None):
        if hasattr(self, 'compare_details') or self.as_container:
            try:
                difference = self._compare_using_details(other, source)
                # no differences detected inside? let's at least do a binary diff
                if difference is None:
                    difference = self.compare_bytes(other, source=source)
                    if difference is None:
                        return None
                    difference.add_comment(
                        "No file format specific differences found inside, "
                        "yet data differs ({})".format(self.magic_file_type),
                    )
            except subprocess.CalledProcessError as e:
                difference = self.compare_bytes(other, source=source)
                if e.output:
                    output = re.sub(r'^', '    ', e.output.decode('utf-8', errors='replace'), flags=re.MULTILINE)
                else:
                    output = '<none>'
                cmd = ' '.join(e.cmd)
                if difference is None:
                    return None
                difference.add_comment("Command `%s` exited with %d. Output:\n%s"
                                       % (cmd, e.returncode, output))

            except RequiredToolNotFound as e: