diffoscope 39__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. diffoscope-39/PKG-INFO +103 -0
  2. diffoscope-39/README.rst +88 -0
  3. diffoscope-39/diffoscope/__init__.py +116 -0
  4. diffoscope-39/diffoscope/__main__.py +158 -0
  5. diffoscope-39/diffoscope/changes.py +340 -0
  6. diffoscope-39/diffoscope/comparators/__init__.py +184 -0
  7. diffoscope-39/diffoscope/comparators/binary.py +325 -0
  8. diffoscope-39/diffoscope/comparators/bzip2.py +69 -0
  9. diffoscope-39/diffoscope/comparators/cbfs.py +137 -0
  10. diffoscope-39/diffoscope/comparators/cpio.py +50 -0
  11. diffoscope-39/diffoscope/comparators/deb.py +156 -0
  12. diffoscope-39/diffoscope/comparators/debian.py +176 -0
  13. diffoscope-39/diffoscope/comparators/device.py +55 -0
  14. diffoscope-39/diffoscope/comparators/directory.py +178 -0
  15. diffoscope-39/diffoscope/comparators/elf.py +108 -0
  16. diffoscope-39/diffoscope/comparators/fonts.py +45 -0
  17. diffoscope-39/diffoscope/comparators/gettext.py +68 -0
  18. diffoscope-39/diffoscope/comparators/gzip.py +74 -0
  19. diffoscope-39/diffoscope/comparators/haskell.py +88 -0
  20. diffoscope-39/diffoscope/comparators/ipk.py +30 -0
  21. diffoscope-39/diffoscope/comparators/iso9660.py +84 -0
  22. diffoscope-39/diffoscope/comparators/java.py +53 -0
  23. diffoscope-39/diffoscope/comparators/libarchive.py +147 -0
  24. diffoscope-39/diffoscope/comparators/mono.py +43 -0
  25. diffoscope-39/diffoscope/comparators/pdf.py +54 -0
  26. diffoscope-39/diffoscope/comparators/png.py +48 -0
  27. diffoscope-39/diffoscope/comparators/ppu.py +55 -0
  28. diffoscope-39/diffoscope/comparators/rpm.py +113 -0
  29. diffoscope-39/diffoscope/comparators/rpm_fallback.py +36 -0
  30. diffoscope-39/diffoscope/comparators/sqlite.py +40 -0
  31. diffoscope-39/diffoscope/comparators/squashfs.py +205 -0
  32. diffoscope-39/diffoscope/comparators/symlink.py +52 -0
  33. diffoscope-39/diffoscope/comparators/tar.py +147 -0
  34. diffoscope-39/diffoscope/comparators/text.py +55 -0
  35. diffoscope-39/diffoscope/comparators/utils.py +325 -0
  36. diffoscope-39/diffoscope/comparators/xz.py +69 -0
  37. diffoscope-39/diffoscope/comparators/zip.py +105 -0
  38. diffoscope-39/diffoscope/config.py +80 -0
  39. diffoscope-39/diffoscope/difference.py +431 -0
  40. diffoscope-39/diffoscope/presenters/__init__.py +0 -0
  41. diffoscope-39/diffoscope/presenters/html.py +522 -0
  42. diffoscope-39/diffoscope/presenters/icon.py +20 -0
  43. diffoscope-39/diffoscope/presenters/text.py +56 -0
  44. diffoscope-39/diffoscope.egg-info/PKG-INFO +103 -0
  45. diffoscope-39/diffoscope.egg-info/SOURCES.txt +49 -0
  46. diffoscope-39/diffoscope.egg-info/dependency_links.txt +1 -0
  47. diffoscope-39/diffoscope.egg-info/entry_points.txt +3 -0
  48. diffoscope-39/diffoscope.egg-info/requires.txt +4 -0
  49. diffoscope-39/diffoscope.egg-info/top_level.txt +1 -0
  50. diffoscope-39/setup.cfg +5 -0
  51. diffoscope-39/setup.py +53 -0
diffoscope-39/PKG-INFO ADDED
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 1.1
2
+ Name: diffoscope
3
+ Version: 39
4
+ Summary: in-depth comparison of files, archives, and directories
5
+ Home-page: https://diffoscope.org/
6
+ Author: Lunar
7
+ Author-email: lunar@debian.org
8
+ License: GPL-3+
9
+ Description: diffoscope
10
+ ==========
11
+
12
+ diffoscope will try to get to the bottom of what makes files or
13
+ directories different. It will recursively unpack archives of many kinds
14
+ and transform various binary formats into more human readable form to
15
+ compare them. It can compare two tarballs, ISO images, or PDF just as
16
+ easily.
17
+
18
+ It can be scripted through error codes, and a report can be produced
19
+ with the detected differences. The report can be text or HTML.
20
+ When no type of report has been selected, diffoscope defaults
21
+ to write a text report on the standard output.
22
+
23
+ diffoscope will exit with 0 if there's no differences and 1 if there
24
+ are. An HTML report can be produced with the detected differences.
25
+
26
+ diffoscope is developed as part of the `“reproducible builds” Debian
27
+ project <https://wiki.debian.org/ReproducibleBuilds>`_.
28
+ It is meant to be able to quickly understand why two builds of the same
29
+ package produce different outputs. diffoscope was previously named
30
+ debbindiff.
31
+
32
+ Example
33
+ -------
34
+
35
+ To compare two files in-depth and produce an HTML report, run something like::
36
+
37
+ $ bin/diffoscope --html output.html build1.changes build2.changes
38
+
39
+ *diffoscope* can also compare non-existent files::
40
+
41
+ $ bin/diffoscope /nonexistent archive.zip
42
+
43
+ To get all possible options, run::
44
+
45
+ $ bin/diffoscope --help
46
+
47
+ External dependencies
48
+ ---------------------
49
+
50
+ diffoscope requires Python 3 and the following modules available on PyPI:
51
+ `libarchive-c <https://pypi.python.org/pypi/libarchive-c>`_,
52
+ `python-debian <https://pypi.python.org/pypi/python-debian>`_,
53
+ `python-magic <https://pypi.python.org/pypi/python-debian>`_.
54
+
55
+ Optionally, the following modules will enhance it:
56
+
57
+ * ``tlsh`` is used to recognize file renames.
58
+ It is build from `tlsh source
59
+ <https://github.com/trendmicro/tlsh>`_.
60
+ Available on Debian as ``python3-tlsh``.
61
+ * ``rpm-python`` is used to inspect RPM files.
62
+ It is built from `rpm
63
+ <http://rpm.org/>`_.
64
+ Available on Debian and Fedora as ``python3-rpm``.
65
+ * ``Magic-file-extension`` can be used instead of
66
+ ``python-magic``. It is built from `file
67
+ <http://www.darwinsys.com/file/>`_.
68
+ Available on Debian and Fedora as ``python3-magic``.
69
+
70
+ The various comparators rely on external commands being available. To
71
+ get a list of them, please run::
72
+
73
+ $ bin/diffoscope --list-tools
74
+
75
+ Contributors
76
+ ------------
77
+
78
+ Lunar, Reiner Herrmann, Chris Lamb, Helmut Grohne, Holger Levsen,
79
+ Mattia Rizzolo, Daniel Kahn Gillmor, Paul Gevers, Peter De Wachter,
80
+ Yasushi SHOJI.
81
+
82
+ License
83
+ -------
84
+
85
+ diffoscope is free software: you can redistribute it and/or modify
86
+ it under the terms of the GNU General Public License as published by
87
+ the Free Software Foundation, either version 3 of the License, or
88
+ (at your option) any later version.
89
+
90
+ diffoscope is distributed in the hope that it will be useful,
91
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
92
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
93
+ GNU General Public License for more details.
94
+
95
+ You should have received a copy of the GNU General Public License
96
+ along with diffoscope. If not, see <http://www.gnu.org/licenses/>.
97
+
98
+ Platform: UNKNOWN
99
+ Classifier: Development Status :: 3 - Alpha
100
+ Classifier: Intended Audience :: Developers
101
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
102
+ Classifier: Operating System :: POSIX
103
+ Classifier: Topic :: Utilities
@@ -0,0 +1,88 @@
1
+ diffoscope
2
+ ==========
3
+
4
+ diffoscope will try to get to the bottom of what makes files or
5
+ directories different. It will recursively unpack archives of many kinds
6
+ and transform various binary formats into more human readable form to
7
+ compare them. It can compare two tarballs, ISO images, or PDF just as
8
+ easily.
9
+
10
+ It can be scripted through error codes, and a report can be produced
11
+ with the detected differences. The report can be text or HTML.
12
+ When no type of report has been selected, diffoscope defaults
13
+ to write a text report on the standard output.
14
+
15
+ diffoscope will exit with 0 if there's no differences and 1 if there
16
+ are. An HTML report can be produced with the detected differences.
17
+
18
+ diffoscope is developed as part of the `“reproducible builds” Debian
19
+ project <https://wiki.debian.org/ReproducibleBuilds>`_.
20
+ It is meant to be able to quickly understand why two builds of the same
21
+ package produce different outputs. diffoscope was previously named
22
+ debbindiff.
23
+
24
+ Example
25
+ -------
26
+
27
+ To compare two files in-depth and produce an HTML report, run something like::
28
+
29
+ $ bin/diffoscope --html output.html build1.changes build2.changes
30
+
31
+ *diffoscope* can also compare non-existent files::
32
+
33
+ $ bin/diffoscope /nonexistent archive.zip
34
+
35
+ To get all possible options, run::
36
+
37
+ $ bin/diffoscope --help
38
+
39
+ External dependencies
40
+ ---------------------
41
+
42
+ diffoscope requires Python 3 and the following modules available on PyPI:
43
+ `libarchive-c <https://pypi.python.org/pypi/libarchive-c>`_,
44
+ `python-debian <https://pypi.python.org/pypi/python-debian>`_,
45
+ `python-magic <https://pypi.python.org/pypi/python-debian>`_.
46
+
47
+ Optionally, the following modules will enhance it:
48
+
49
+ * ``tlsh`` is used to recognize file renames.
50
+ It is build from `tlsh source
51
+ <https://github.com/trendmicro/tlsh>`_.
52
+ Available on Debian as ``python3-tlsh``.
53
+ * ``rpm-python`` is used to inspect RPM files.
54
+ It is built from `rpm
55
+ <http://rpm.org/>`_.
56
+ Available on Debian and Fedora as ``python3-rpm``.
57
+ * ``Magic-file-extension`` can be used instead of
58
+ ``python-magic``. It is built from `file
59
+ <http://www.darwinsys.com/file/>`_.
60
+ Available on Debian and Fedora as ``python3-magic``.
61
+
62
+ The various comparators rely on external commands being available. To
63
+ get a list of them, please run::
64
+
65
+ $ bin/diffoscope --list-tools
66
+
67
+ Contributors
68
+ ------------
69
+
70
+ Lunar, Reiner Herrmann, Chris Lamb, Helmut Grohne, Holger Levsen,
71
+ Mattia Rizzolo, Daniel Kahn Gillmor, Paul Gevers, Peter De Wachter,
72
+ Yasushi SHOJI.
73
+
74
+ License
75
+ -------
76
+
77
+ diffoscope is free software: you can redistribute it and/or modify
78
+ it under the terms of the GNU General Public License as published by
79
+ the Free Software Foundation, either version 3 of the License, or
80
+ (at your option) any later version.
81
+
82
+ diffoscope is distributed in the hope that it will be useful,
83
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
84
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
85
+ GNU General Public License for more details.
86
+
87
+ You should have received a copy of the GNU General Public License
88
+ along with diffoscope. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # diffoscope: in-depth comparison of files, archives, and directories
4
+ #
5
+ # Copyright © 2014-2015 Jérémy Bobbio <lunar@debian.org>
6
+ #
7
+ # diffoscope is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # diffoscope is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with diffoscope. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ from functools import wraps
21
+ import logging
22
+ from distutils.spawn import find_executable
23
+ import os
24
+
25
+ VERSION = "39"
26
+
27
+ logger = logging.getLogger("diffoscope")
28
+ logger.setLevel(logging.WARNING)
29
+ ch = logging.StreamHandler()
30
+ ch.setLevel(logging.DEBUG)
31
+ logger.addHandler(ch)
32
+ formatter = logging.Formatter('%(levelname)8s %(message)s')
33
+ ch.setFormatter(formatter)
34
+
35
+
36
+ class RequiredToolNotFound(Exception):
37
+ PROVIDERS = { 'ar': { 'debian': 'binutils-multiarch' }
38
+ , 'bzip2': { 'debian': 'bzip2' }
39
+ , 'cbfstool': {}
40
+ , 'cmp': { 'debian': 'diffutils' }
41
+ , 'cpio': { 'debian': 'cpio' }
42
+ , 'diff': { 'debian': 'diffutils' }
43
+ , 'file': { 'debian': 'file' }
44
+ , 'find': { 'debian': 'findutils' }
45
+ , 'getfacl': { 'debian': 'acl' }
46
+ , 'ghc': { 'debian': 'ghc' }
47
+ , 'gpg': { 'debian': 'gnupg' }
48
+ , 'gzip': { 'debian': 'gzip' }
49
+ , 'isoinfo': { 'debian': 'genisoimage' }
50
+ , 'javap': { 'debian': 'default-jdk | java-sdk' }
51
+ , 'ls': { 'debian': 'coreutils' }
52
+ , 'lsattr': { 'debian': 'e2fsprogs' }
53
+ , 'msgunfmt': { 'debian': 'gettext' }
54
+ , 'objdump': { 'debian': 'binutils-multiarch' }
55
+ , 'pdftk': { 'debian': 'pdftk' }
56
+ , 'pdftotext': { 'debian': 'poppler-utils' }
57
+ , 'pedump': { 'debian': 'mono-utils' }
58
+ , 'ppudump': { 'debian': 'fp-utils' }
59
+ , 'readelf': { 'debian': 'binutils-multiarch' }
60
+ , 'rpm2cpio': { 'debian': 'rpm2cpio' }
61
+ , 'showttf': { 'debian': 'fontforge-extras' }
62
+ , 'sng': { 'debian': 'sng' }
63
+ , 'stat': { 'debian': 'coreutils' }
64
+ , 'sqlite3': { 'debian': 'sqlite3'}
65
+ , 'tar': { 'debian': 'tar'}
66
+ , 'unsquashfs': { 'debian': 'squashfs-tools' }
67
+ , 'xxd': { 'debian': 'vim-common' }
68
+ , 'xz': { 'debian': 'xz-utils' }
69
+ , 'zipinfo': { 'debian': 'unzip' }
70
+ }
71
+
72
+ def __init__(self, command):
73
+ self.command = command
74
+
75
+ def get_package(self):
76
+ providers = RequiredToolNotFound.PROVIDERS.get(self.command, None)
77
+ if not providers:
78
+ return None
79
+ # XXX: hardcode Debian for now
80
+ return providers['debian']
81
+
82
+
83
+ # decorator that checks if the specified tool is installed
84
+ def tool_required(command):
85
+ if not hasattr(tool_required, 'all'):
86
+ tool_required.all = set()
87
+ tool_required.all.add(command)
88
+ def wrapper(original_function):
89
+ if find_executable(command):
90
+ @wraps(original_function)
91
+ def tool_check(*args, **kwargs):
92
+ return original_function(*args, **kwargs)
93
+ else:
94
+ @wraps(original_function)
95
+ def tool_check(*args, **kwargs):
96
+ raise RequiredToolNotFound(command)
97
+ return tool_check
98
+ return wrapper
99
+
100
+
101
+ def set_locale():
102
+ """Normalize locale so external tool gives us stable and properly
103
+ encoded output"""
104
+
105
+ for var in ['LANGUAGE', 'LC_ALL']:
106
+ if var in os.environ:
107
+ del os.environ[var]
108
+ for var in ['LANG', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MONETARY',
109
+ 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
110
+ 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION']:
111
+ os.environ[var] = 'C'
112
+ os.environ['LC_CTYPE'] = 'C.UTF-8'
113
+ os.environ['TZ'] = 'UTC'
114
+
115
+
116
+
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # diffoscope: in-depth comparison of files, archives, and directories
5
+ #
6
+ # Copyright © 2014-2015 Jérémy Bobbio <lunar@debian.org>
7
+ #
8
+ # diffoscope is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # diffoscope is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with diffoscope. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ import argparse
22
+ from contextlib import contextmanager
23
+ import logging
24
+ import codecs
25
+ import os
26
+ import signal
27
+ import sys
28
+ import traceback
29
+ try:
30
+ import tlsh
31
+ except ImportError:
32
+ tlsh = None
33
+ from diffoscope import logger, VERSION, set_locale
34
+ import diffoscope.comparators
35
+ from diffoscope.config import Config
36
+ from diffoscope.presenters.html import output_html
37
+ from diffoscope.presenters.text import output_text
38
+
39
+
40
+ def create_parser():
41
+ parser = argparse.ArgumentParser(
42
+ description='Highlight differences between two builds '
43
+ 'of Debian packages')
44
+ parser.add_argument('--version', action='version',
45
+ version='diffoscope %s' % VERSION)
46
+ parser.add_argument('--list-tools', nargs=0, action=ListToolsAction,
47
+ help='show external tools required and exit')
48
+ parser.add_argument('--debug', dest='debug', action='store_true',
49
+ default=False, help='display debug messages')
50
+ parser.add_argument('--debugger', action='store_true',
51
+ help='Open the python debugger in case of crashes.')
52
+ parser.add_argument('--html', metavar='output', dest='html_output',
53
+ help='write HTML report to given file (use - for stdout)')
54
+ parser.add_argument('--text', metavar='output', dest='text_output',
55
+ help='write plain text output to given file (use - for stdout)')
56
+ parser.add_argument('--max-report-size', metavar='BYTES',
57
+ dest='max_report_size', type=int,
58
+ help='maximum bytes written in report (default: %d)' %
59
+ Config.general.max_report_size,
60
+ default=Config.general.max_report_size)
61
+ parser.add_argument('--max-diff-block-lines', dest='max_diff_block_lines', type=int,
62
+ help='maximum number of lines per diff block (default: %d)' %
63
+ Config.general.max_diff_block_lines,
64
+ default=Config.general.max_diff_block_lines)
65
+ parser.add_argument('--max-diff-input-lines', dest='max_diff_input_lines', type=int,
66
+ help='maximum number of lines fed to diff (default: %d)' %
67
+ Config.general.max_diff_input_lines,
68
+ default=Config.general.max_diff_input_lines)
69
+ parser.add_argument('--fuzzy-threshold', dest='fuzzy_threshold', type=int,
70
+ help='threshold for fuzzy-matching '
71
+ '(0 to disable, %d is default, 400 is high fuzziness)' %
72
+ (Config.general.fuzzy_threshold),
73
+ default=Config.general.fuzzy_threshold)
74
+ parser.add_argument('--new-file', dest='new_file', action='store_true',
75
+ help='treat absent files as empty')
76
+ parser.add_argument('--css', metavar='url', dest='css_url',
77
+ help='link to an extra CSS for the HTML report')
78
+ parser.add_argument('file1', help='first file to compare')
79
+ parser.add_argument('file2', help='second file to compare')
80
+ if not tlsh:
81
+ parser.epilog = 'File renaming detection based on fuzzy-matching is currently disabled. It can be enabled by installing the “tlsh” module available at https://github.com/trendmicro/tlsh'
82
+ return parser
83
+
84
+
85
+ @contextmanager
86
+ def make_printer(path):
87
+ if path == '-':
88
+ output = sys.stdout
89
+ else:
90
+ output = codecs.open(path, 'w', encoding='utf-8')
91
+ def print_func(*args, **kwargs):
92
+ kwargs['file'] = output
93
+ print(*args, **kwargs)
94
+ yield print_func
95
+ if path != '-':
96
+ output.close()
97
+
98
+
99
+ class ListToolsAction(argparse.Action):
100
+ def __call__(self, parser, namespace, values, option_string=None):
101
+ from diffoscope import tool_required, RequiredToolNotFound
102
+ print("External tools required:")
103
+ print(', '.join(tool_required.all))
104
+ print()
105
+ print("Available in packages:")
106
+ print(', '.join(sorted(filter(None, { RequiredToolNotFound.PROVIDERS[k].get('debian', None) for k in tool_required.all }))))
107
+ sys.exit(0)
108
+
109
+
110
+ def run_diffoscope(parsed_args):
111
+ if not tlsh and Config.general.fuzzy_threshold != parsed_args.fuzzy_threshold:
112
+ logger.warning('Fuzzy-matching is currently disabled as the “tlsh” module is unavailable.')
113
+ Config.general.max_diff_block_lines = parsed_args.max_diff_block_lines
114
+ Config.general.max_diff_input_lines = parsed_args.max_diff_input_lines
115
+ Config.general.max_report_size = parsed_args.max_report_size
116
+ Config.general.fuzzy_threshold = parsed_args.fuzzy_threshold
117
+ Config.general.new_file = parsed_args.new_file
118
+ if parsed_args.debug:
119
+ logger.setLevel(logging.DEBUG)
120
+ set_locale()
121
+ difference = diffoscope.comparators.compare_root_paths(
122
+ parsed_args.file1, parsed_args.file2)
123
+ if difference:
124
+ if parsed_args.html_output:
125
+ with make_printer(parsed_args.html_output) as print_func:
126
+ output_html(difference, css_url=parsed_args.css_url, print_func=print_func)
127
+ if (parsed_args.text_output and parsed_args.text_output != parsed_args.html_output) or not parsed_args.html_output:
128
+ with make_printer(parsed_args.text_output or '-') as print_func:
129
+ output_text(difference, print_func=print_func)
130
+ return 1
131
+ return 0
132
+
133
+
134
+ def sigterm_handler(signo, stack_frame):
135
+ sys.exit(2)
136
+
137
+
138
+ def main(args=None):
139
+ if args is None:
140
+ args = sys.argv[1:]
141
+ signal.signal(signal.SIGTERM, sigterm_handler)
142
+ parsed_args = None
143
+ try:
144
+ parser = create_parser()
145
+ parsed_args = parser.parse_args(args)
146
+ sys.exit(run_diffoscope(parsed_args))
147
+ except KeyboardInterrupt:
148
+ logger.info('Keyboard Interrupt')
149
+ sys.exit(2)
150
+ except Exception:
151
+ traceback.print_exc()
152
+ if parsed_args and parsed_args.debugger:
153
+ import pdb
154
+ pdb.post_mortem()
155
+ sys.exit(2)
156
+
157
+ if __name__ == '__main__':
158
+ main()