aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2023-12-04 15:32:14 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2023-12-05 01:22:50 +0300
commitc21ed9eedf73010bc81342518177dfdfb0d56bd7 (patch)
tree72f8fde4463080cfe5a38eb0babc051cfe32c51e /contrib/python
parentec1311bf2e8cc231723b8b5e484ca576663a1309 (diff)
downloadydb-c21ed9eedf73010bc81342518177dfdfb0d56bd7.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/coverage/plugins/coveragerc.txt29
-rw-r--r--contrib/python/coverage/plugins/ya.make19
-rw-r--r--contrib/python/coverage/plugins/yarcadia/plugin.py114
-rw-r--r--contrib/python/coverage/py2/.dist-info/METADATA190
-rw-r--r--contrib/python/coverage/py2/.dist-info/entry_points.txt5
-rw-r--r--contrib/python/coverage/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/coverage/py2/LICENSE.txt177
-rw-r--r--contrib/python/coverage/py2/NOTICE.txt14
-rw-r--r--contrib/python/coverage/py2/README.rst151
-rw-r--r--contrib/python/coverage/py2/coverage/__init__.py36
-rw-r--r--contrib/python/coverage/py2/coverage/__main__.py8
-rw-r--r--contrib/python/coverage/py2/coverage/annotate.py108
-rw-r--r--contrib/python/coverage/py2/coverage/backward.py267
-rw-r--r--contrib/python/coverage/py2/coverage/bytecode.py19
-rw-r--r--contrib/python/coverage/py2/coverage/cmdline.py910
-rw-r--r--contrib/python/coverage/py2/coverage/collector.py455
-rw-r--r--contrib/python/coverage/py2/coverage/config.py605
-rw-r--r--contrib/python/coverage/py2/coverage/context.py91
-rw-r--r--contrib/python/coverage/py2/coverage/control.py1162
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/datastack.c50
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/datastack.h45
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/filedisp.c85
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/filedisp.h26
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/module.c108
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/stats.h31
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/tracer.c1149
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/tracer.h75
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/util.h67
-rw-r--r--contrib/python/coverage/py2/coverage/data.py125
-rw-r--r--contrib/python/coverage/py2/coverage/debug.py406
-rw-r--r--contrib/python/coverage/py2/coverage/disposition.py37
-rw-r--r--contrib/python/coverage/py2/coverage/env.py130
-rw-r--r--contrib/python/coverage/py2/coverage/execfile.py362
-rw-r--r--contrib/python/coverage/py2/coverage/files.py441
-rw-r--r--contrib/python/coverage/py2/coverage/fullcoverage/encodings.py60
-rw-r--r--contrib/python/coverage/py2/coverage/html.py539
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js616
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.pngbin1732 -> 0 bytes
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/index.html119
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js9
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js99
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js53
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js4
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js2
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.pngbin112 -> 0 bytes
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.pngbin112 -> 0 bytes
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html113
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/style.css291
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/style.scss660
-rw-r--r--contrib/python/coverage/py2/coverage/inorout.py513
-rw-r--r--contrib/python/coverage/py2/coverage/jsonreport.py103
-rw-r--r--contrib/python/coverage/py2/coverage/misc.py361
-rw-r--r--contrib/python/coverage/py2/coverage/multiproc.py117
-rw-r--r--contrib/python/coverage/py2/coverage/numbits.py163
-rw-r--r--contrib/python/coverage/py2/coverage/parser.py1276
-rw-r--r--contrib/python/coverage/py2/coverage/phystokens.py297
-rw-r--r--contrib/python/coverage/py2/coverage/plugin.py533
-rw-r--r--contrib/python/coverage/py2/coverage/plugin_support.py281
-rw-r--r--contrib/python/coverage/py2/coverage/python.py261
-rw-r--r--contrib/python/coverage/py2/coverage/pytracer.py274
-rw-r--r--contrib/python/coverage/py2/coverage/report.py86
-rw-r--r--contrib/python/coverage/py2/coverage/results.py343
-rw-r--r--contrib/python/coverage/py2/coverage/sqldata.py1123
-rw-r--r--contrib/python/coverage/py2/coverage/summary.py152
-rw-r--r--contrib/python/coverage/py2/coverage/templite.py302
-rw-r--r--contrib/python/coverage/py2/coverage/tomlconfig.py168
-rw-r--r--contrib/python/coverage/py2/coverage/version.py33
-rw-r--r--contrib/python/coverage/py2/coverage/xmlreport.py234
-rw-r--r--contrib/python/coverage/py2/ya.make98
-rw-r--r--contrib/python/coverage/py3/.dist-info/METADATA190
-rw-r--r--contrib/python/coverage/py3/.dist-info/entry_points.txt5
-rw-r--r--contrib/python/coverage/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/coverage/py3/LICENSE.txt177
-rw-r--r--contrib/python/coverage/py3/NOTICE.txt14
-rw-r--r--contrib/python/coverage/py3/README.rst151
-rw-r--r--contrib/python/coverage/py3/coverage/__init__.py36
-rw-r--r--contrib/python/coverage/py3/coverage/__main__.py8
-rw-r--r--contrib/python/coverage/py3/coverage/annotate.py108
-rw-r--r--contrib/python/coverage/py3/coverage/backward.py267
-rw-r--r--contrib/python/coverage/py3/coverage/bytecode.py19
-rw-r--r--contrib/python/coverage/py3/coverage/cmdline.py910
-rw-r--r--contrib/python/coverage/py3/coverage/collector.py455
-rw-r--r--contrib/python/coverage/py3/coverage/config.py605
-rw-r--r--contrib/python/coverage/py3/coverage/context.py91
-rw-r--r--contrib/python/coverage/py3/coverage/control.py1162
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/datastack.c50
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/datastack.h45
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/filedisp.c85
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/filedisp.h26
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/module.c108
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/stats.h31
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/tracer.c1149
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/tracer.h75
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/util.h103
-rw-r--r--contrib/python/coverage/py3/coverage/data.py125
-rw-r--r--contrib/python/coverage/py3/coverage/debug.py406
-rw-r--r--contrib/python/coverage/py3/coverage/disposition.py37
-rw-r--r--contrib/python/coverage/py3/coverage/env.py130
-rw-r--r--contrib/python/coverage/py3/coverage/execfile.py362
-rw-r--r--contrib/python/coverage/py3/coverage/files.py441
-rw-r--r--contrib/python/coverage/py3/coverage/fullcoverage/encodings.py60
-rw-r--r--contrib/python/coverage/py3/coverage/html.py539
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js616
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.pngbin1732 -> 0 bytes
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/index.html119
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js9
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js99
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js53
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js4
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js2
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.pngbin112 -> 0 bytes
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.pngbin112 -> 0 bytes
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html113
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/style.css291
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/style.scss660
-rw-r--r--contrib/python/coverage/py3/coverage/inorout.py513
-rw-r--r--contrib/python/coverage/py3/coverage/jsonreport.py103
-rw-r--r--contrib/python/coverage/py3/coverage/misc.py361
-rw-r--r--contrib/python/coverage/py3/coverage/multiproc.py117
-rw-r--r--contrib/python/coverage/py3/coverage/numbits.py163
-rw-r--r--contrib/python/coverage/py3/coverage/parser.py1276
-rw-r--r--contrib/python/coverage/py3/coverage/phystokens.py297
-rw-r--r--contrib/python/coverage/py3/coverage/plugin.py533
-rw-r--r--contrib/python/coverage/py3/coverage/plugin_support.py281
-rw-r--r--contrib/python/coverage/py3/coverage/python.py261
-rw-r--r--contrib/python/coverage/py3/coverage/pytracer.py274
-rw-r--r--contrib/python/coverage/py3/coverage/report.py86
-rw-r--r--contrib/python/coverage/py3/coverage/results.py343
-rw-r--r--contrib/python/coverage/py3/coverage/sqldata.py1123
-rw-r--r--contrib/python/coverage/py3/coverage/summary.py152
-rw-r--r--contrib/python/coverage/py3/coverage/templite.py302
-rw-r--r--contrib/python/coverage/py3/coverage/tomlconfig.py168
-rw-r--r--contrib/python/coverage/py3/coverage/version.py33
-rw-r--r--contrib/python/coverage/py3/coverage/xmlreport.py234
-rw-r--r--contrib/python/coverage/py3/ya.make98
-rw-r--r--contrib/python/coverage/ya.make19
-rw-r--r--contrib/python/diff-match-patch/py2/.dist-info/METADATA112
-rw-r--r--contrib/python/diff-match-patch/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/diff-match-patch/py2/AUTHORS10
-rw-r--r--contrib/python/diff-match-patch/py2/LICENSE202
-rw-r--r--contrib/python/diff-match-patch/py2/README.md84
-rw-r--r--contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py9
-rw-r--r--contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py2037
-rw-r--r--contrib/python/diff-match-patch/py2/ya.make27
-rw-r--r--contrib/python/diff-match-patch/py3/.dist-info/METADATA108
-rw-r--r--contrib/python/diff-match-patch/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/diff-match-patch/py3/AUTHORS10
-rw-r--r--contrib/python/diff-match-patch/py3/LICENSE202
-rw-r--r--contrib/python/diff-match-patch/py3/README.md84
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py10
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py7
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py2019
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt230
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt188
-rw-r--r--contrib/python/diff-match-patch/py3/ya.make28
-rw-r--r--contrib/python/diff-match-patch/ya.make18
-rw-r--r--contrib/python/humanfriendly/py2/LICENSE.txt20
-rw-r--r--contrib/python/humanfriendly/py2/README.rst170
-rw-r--r--contrib/python/humanfriendly/py3/.dist-info/METADATA216
-rw-r--r--contrib/python/humanfriendly/py3/.dist-info/entry_points.txt3
-rw-r--r--contrib/python/humanfriendly/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/humanfriendly/py3/LICENSE.txt20
-rw-r--r--contrib/python/humanfriendly/py3/README.rst170
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/__init__.py838
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/case.py157
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/cli.py291
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/compat.py146
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/decorators.py43
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/deprecation.py251
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/prompts.py376
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/sphinx.py315
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/tables.py341
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py776
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py423
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py310
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/testing.py669
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/text.py449
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/usage.py351
-rw-r--r--contrib/python/humanfriendly/py3/ya.make41
-rw-r--r--contrib/python/humanfriendly/ya.make18
-rw-r--r--contrib/python/marisa-trie/agent.pxd22
-rw-r--r--contrib/python/marisa-trie/base.pxd63
-rw-r--r--contrib/python/marisa-trie/iostream.pxd7
-rw-r--r--contrib/python/marisa-trie/key.pxd22
-rw-r--r--contrib/python/marisa-trie/keyset.pxd30
-rw-r--r--contrib/python/marisa-trie/marisa/agent.cc51
-rw-r--r--contrib/python/marisa-trie/marisa/agent.h75
-rw-r--r--contrib/python/marisa-trie/marisa/base.h196
-rw-r--r--contrib/python/marisa-trie/marisa/exception.h84
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/algorithm.h27
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h197
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/intrin.h116
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io.h19
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc163
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/mapper.h68
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/reader.cc147
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/reader.h67
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/writer.cc148
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/writer.h66
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie.h17
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/cache.h82
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/config.h156
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/entry.h83
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/header.h62
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/history.h66
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/key.h227
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc877
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h135
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/range.h116
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/state.h118
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc218
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/tail.h73
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector.h19
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc825
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h180
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h206
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h111
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h83
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/vector.h257
-rw-r--r--contrib/python/marisa-trie/marisa/iostream.h19
-rw-r--r--contrib/python/marisa-trie/marisa/key.h86
-rw-r--r--contrib/python/marisa-trie/marisa/keyset.cc181
-rw-r--r--contrib/python/marisa-trie/marisa/keyset.h81
-rw-r--r--contrib/python/marisa-trie/marisa/query.h72
-rw-r--r--contrib/python/marisa-trie/marisa/scoped-array.h49
-rw-r--r--contrib/python/marisa-trie/marisa/scoped-ptr.h53
-rw-r--r--contrib/python/marisa-trie/marisa/stdio.h16
-rw-r--r--contrib/python/marisa-trie/marisa/trie.cc249
-rw-r--r--contrib/python/marisa-trie/marisa/trie.h65
-rw-r--r--contrib/python/marisa-trie/marisa_trie.pyx763
-rw-r--r--contrib/python/marisa-trie/query.pxd20
-rw-r--r--contrib/python/marisa-trie/std_iostream.pxd18
-rw-r--r--contrib/python/marisa-trie/trie.pxd41
-rw-r--r--contrib/python/marisa-trie/ya.make33
-rw-r--r--contrib/python/path.py/py2/LICENSE7
-rw-r--r--contrib/python/path.py/py2/README.rst134
-rw-r--r--contrib/python/path.py/py3/.dist-info/METADATA36
-rw-r--r--contrib/python/path.py/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/path.py/py3/LICENSE7
-rw-r--r--contrib/python/path.py/py3/README.rst1
-rw-r--r--contrib/python/path.py/py3/ya.make21
-rw-r--r--contrib/python/path.py/ya.make18
-rw-r--r--contrib/python/path/.dist-info/METADATA201
-rw-r--r--contrib/python/path/.dist-info/top_level.txt1
-rw-r--r--contrib/python/path/LICENSE17
-rw-r--r--contrib/python/path/README.rst163
-rw-r--r--contrib/python/path/path/__init__.py1665
-rw-r--r--contrib/python/path/path/classes.py27
-rw-r--r--contrib/python/path/path/masks.py159
-rw-r--r--contrib/python/path/path/matchers.py59
-rw-r--r--contrib/python/path/path/py.typed0
-rw-r--r--contrib/python/path/ya.make30
-rw-r--r--contrib/python/pygtrie/py2/LICENSE202
-rw-r--r--contrib/python/pygtrie/py2/README.rst66
-rw-r--r--contrib/python/pygtrie/py3/.dist-info/METADATA220
-rw-r--r--contrib/python/pygtrie/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pygtrie/py3/LICENSE202
-rw-r--r--contrib/python/pygtrie/py3/README.rst66
-rw-r--r--contrib/python/pygtrie/py3/pygtrie.py1939
-rw-r--r--contrib/python/pygtrie/py3/ya.make26
-rw-r--r--contrib/python/pygtrie/ya.make18
-rw-r--r--contrib/python/pyre2/py2/AUTHORS12
-rw-r--r--contrib/python/pyre2/py2/LICENSE9
-rw-r--r--contrib/python/pyre2/py2/README.rst250
-rw-r--r--contrib/python/pyre2/py2/tests/test_charliterals.txt47
-rw-r--r--contrib/python/pyre2/py2/tests/test_count.txt40
-rw-r--r--contrib/python/pyre2/py2/tests/test_emptygroups.txt36
-rw-r--r--contrib/python/pyre2/py2/tests/test_findall.txt42
-rw-r--r--contrib/python/pyre2/py2/tests/test_finditer.txt28
-rw-r--r--contrib/python/pyre2/py2/tests/test_match_expand.txt29
-rw-r--r--contrib/python/pyre2/py2/tests/test_mmap.txt18
-rw-r--r--contrib/python/pyre2/py2/tests/test_namedgroups.txt56
-rw-r--r--contrib/python/pyre2/py2/tests/test_pattern.txt12
-rw-r--r--contrib/python/pyre2/py2/tests/test_search.txt29
-rw-r--r--contrib/python/pyre2/py2/tests/test_split.txt17
-rw-r--r--contrib/python/pyre2/py2/tests/test_sub.txt31
-rw-r--r--contrib/python/pyre2/py2/tests/test_unicode.txt71
-rw-r--r--contrib/python/pyre2/py3/.dist-info/METADATA275
-rw-r--r--contrib/python/pyre2/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pyre2/py3/AUTHORS12
-rw-r--r--contrib/python/pyre2/py3/LICENSE9
-rw-r--r--contrib/python/pyre2/py3/README.rst250
-rw-r--r--contrib/python/pyre2/py3/src/_re2macros.h13
-rw-r--r--contrib/python/pyre2/py3/src/compile.pxi234
-rw-r--r--contrib/python/pyre2/py3/src/includes.pxi109
-rw-r--r--contrib/python/pyre2/py3/src/match.pxi280
-rw-r--r--contrib/python/pyre2/py3/src/pattern.pxi650
-rw-r--r--contrib/python/pyre2/py3/src/re2.pyx458
-rw-r--r--contrib/python/pyre2/py3/tests/test_charliterals.txt47
-rw-r--r--contrib/python/pyre2/py3/tests/test_count.txt40
-rw-r--r--contrib/python/pyre2/py3/tests/test_emptygroups.txt36
-rw-r--r--contrib/python/pyre2/py3/tests/test_findall.txt42
-rw-r--r--contrib/python/pyre2/py3/tests/test_finditer.txt28
-rw-r--r--contrib/python/pyre2/py3/tests/test_match_expand.txt29
-rw-r--r--contrib/python/pyre2/py3/tests/test_mmap.txt18
-rw-r--r--contrib/python/pyre2/py3/tests/test_namedgroups.txt56
-rw-r--r--contrib/python/pyre2/py3/tests/test_pattern.txt12
-rw-r--r--contrib/python/pyre2/py3/tests/test_search.txt29
-rw-r--r--contrib/python/pyre2/py3/tests/test_split.txt17
-rw-r--r--contrib/python/pyre2/py3/tests/test_sub.txt31
-rw-r--r--contrib/python/pyre2/py3/tests/test_unicode.txt71
-rw-r--r--contrib/python/pyre2/py3/ya.make39
-rw-r--r--contrib/python/pyre2/ya.make18
-rw-r--r--contrib/python/python-magic/py2/LICENSE58
-rw-r--r--contrib/python/python-magic/py2/README.md144
-rw-r--r--contrib/python/python-magic/py3/.dist-info/METADATA171
-rw-r--r--contrib/python/python-magic/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/python-magic/py3/LICENSE58
-rw-r--r--contrib/python/python-magic/py3/README.md144
-rw-r--r--contrib/python/python-magic/py3/magic/__init__.py469
-rw-r--r--contrib/python/python-magic/py3/magic/compat.py287
-rw-r--r--contrib/python/python-magic/py3/magic/loader.py50
-rw-r--r--contrib/python/python-magic/py3/magic/py.typed0
-rw-r--r--contrib/python/python-magic/py3/ya.make31
-rw-r--r--contrib/python/python-magic/ya.make18
315 files changed, 0 insertions, 62711 deletions
diff --git a/contrib/python/coverage/plugins/coveragerc.txt b/contrib/python/coverage/plugins/coveragerc.txt
deleted file mode 100644
index 83bfed8690..0000000000
--- a/contrib/python/coverage/plugins/coveragerc.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-[report]
-skip_empty = True
-
-exclude_lines =
- pragma\s*:\s*no\s*cover
- def __repr__
- raise AssertionError
- raise NotImplementedError
- if 0:
- if False:
- if __name__ == .__main__.:
- if self\.debug:
- if settings\.DEBUG
-
-[run]
-suppress_plugin_errors = False
-plugins =
- contrib.python.coverage.plugins.yarcadia.plugin
- contrib.tools.cython.Cython.Coverage
-
-[contrib.python.coverage.plugins.yarcadia.plugin]
-pylib_paths =
- # don't trace contrib
- contrib/python
- contrib/python3
- # don't trace python sources
- contrib/tools/python
- contrib/tools/python3
- contrib/libs/protobuf
diff --git a/contrib/python/coverage/plugins/ya.make b/contrib/python/coverage/plugins/ya.make
deleted file mode 100644
index 30be33f72a..0000000000
--- a/contrib/python/coverage/plugins/ya.make
+++ /dev/null
@@ -1,19 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Apache-2.0)
-
-PEERDIR(
- build/plugins/lib/test_const
- contrib/tools/cython/Cython
- library/python/testing/coverage_utils
-)
-
-PY_SRCS(
- yarcadia/plugin.py
-)
-
-RESOURCE(
- coveragerc.txt /coverage_plugins/coveragerc.txt
-)
-
-END()
diff --git a/contrib/python/coverage/plugins/yarcadia/plugin.py b/contrib/python/coverage/plugins/yarcadia/plugin.py
deleted file mode 100644
index 44d9b003ca..0000000000
--- a/contrib/python/coverage/plugins/yarcadia/plugin.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# coding: utf-8
-
-import os
-
-import coverage.config
-import coverage.files
-import coverage.misc
-import coverage.parser
-import coverage.plugin
-import coverage.python
-
-from build.plugins.lib import test_const
-from library.python.testing import coverage_utils
-
-
-SKIP_FILENAME = '__SKIP_FILENAME__'
-
-
-class YarcadiaPlugin(
- coverage.plugin.CoveragePlugin,
- coverage.plugin.FileTracer
-):
-
- def __init__(self, options):
- self.config = coverage.config.CoverageConfig()
- self.config.from_args(**options)
-
- dirs = options.get("pylib_paths", "").split("\n")
- dirs = [d for d in dirs if d and not d.startswith("#")]
- self.pylib_paths = dirs
-
- self._filename = None
- self._exclude = None
-
- self._setup_file_filter()
-
- def _setup_file_filter(self):
- prefix_filter = os.environ.get('PYTHON_COVERAGE_PREFIX_FILTER', '')
- exclude_regexp = os.environ.get('PYTHON_COVERAGE_EXCLUDE_REGEXP', '')
- self.file_filter = coverage_utils.make_filter(prefix_filter, exclude_regexp)
-
- def configure(self, config):
- self._exclude = coverage.misc.join_regex(config.get_option('report:exclude_lines'))
-
- def get_pylib_paths(self):
- return self.pylib_paths
-
- def file_tracer(self, filename):
- if not filename.endswith(test_const.COVERAGE_PYTHON_EXTS):
- # Catch all generated modules (__file__ without proper extension)
- self._filename = SKIP_FILENAME
- return self
-
- if not self.file_filter(filename):
- # we need to catch all filtered out files (including cython) to pass them to get_source
- self._filename = SKIP_FILENAME
- return self
-
- if filename.endswith(".py"):
- self._filename = filename
- return self
-
- # Let cython plugin register it's own file tracer for pyx/pxi files
- return None
-
- def has_dynamic_source_filename(self):
- return False
-
- def source_filename(self):
- return self._filename
-
- def file_reporter(self, morf):
- source_root = os.environ.get("PYTHON_COVERAGE_ARCADIA_SOURCE_ROOT")
- if source_root:
- return FileReporter(morf, source_root, self, self._exclude)
- # use default file reporter
- return "python"
-
-
-class FileReporter(coverage.python.PythonFileReporter):
-
- def __init__(self, morf, source_root, coverage=None, exclude=None):
- super(FileReporter, self).__init__(morf, coverage)
- self._source = get_source(morf, source_root)
- # use custom parser to provide proper way to get required source
- self._parser = Parser(morf, self._source, exclude)
- self._parser.parse_source()
-
-
-class Parser(coverage.parser.PythonParser):
-
- def __init__(self, morf, source_code, exclude):
- # provide source code to avoid default way to get it
- super(Parser, self).__init__(text=source_code, filename=morf, exclude=exclude)
-
-
-def get_source(filename, source_root):
- assert source_root
-
- if filename == SKIP_FILENAME:
- return ''
-
- abs_filename = os.path.join(source_root, filename)
- if not os.path.isfile(abs_filename):
- # it's fake generated package
- return u''
-
- return coverage.python.get_python_source(abs_filename, force_fs=True)
-
-
-def coverage_init(reg, options):
- plugin = YarcadiaPlugin(options)
- reg.add_configurer(plugin)
- reg.add_file_tracer(plugin)
diff --git a/contrib/python/coverage/py2/.dist-info/METADATA b/contrib/python/coverage/py2/.dist-info/METADATA
deleted file mode 100644
index 25a6049c45..0000000000
--- a/contrib/python/coverage/py2/.dist-info/METADATA
+++ /dev/null
@@ -1,190 +0,0 @@
-Metadata-Version: 2.1
-Name: coverage
-Version: 5.5
-Summary: Code coverage measurement for Python
-Home-page: https://github.com/nedbat/coveragepy
-Author: Ned Batchelder and 142 others
-Author-email: ned@nedbatchelder.com
-License: Apache 2.0
-Project-URL: Documentation, https://coverage.readthedocs.io
-Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi
-Project-URL: Issues, https://github.com/nedbat/coveragepy/issues
-Keywords: code coverage testing
-Platform: UNKNOWN
-Classifier: Environment :: Console
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Software Development :: Quality Assurance
-Classifier: Topic :: Software Development :: Testing
-Classifier: Development Status :: 5 - Production/Stable
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4
-Description-Content-Type: text/x-rst
-Provides-Extra: toml
-Requires-Dist: toml ; extra == 'toml'
-
-.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-===========
-Coverage.py
-===========
-
-Code coverage testing for Python.
-
-| |license| |versions| |status|
-| |test-status| |quality-status| |docs| |codecov|
-| |kit| |format| |repos| |downloads|
-| |stars| |forks| |contributors|
-| |tidelift| |twitter-coveragepy| |twitter-nedbat|
-
-Coverage.py measures code coverage, typically during test execution. It uses
-the code analysis tools and tracing hooks provided in the Python standard
-library to determine which lines are executable, and which have been executed.
-
-Coverage.py runs on many versions of Python:
-
-* CPython 2.7.
-* CPython 3.5 through 3.10 alpha.
-* PyPy2 7.3.3 and PyPy3 7.3.3.
-
-Documentation is on `Read the Docs`_. Code repository and issue tracker are on
-`GitHub`_.
-
-.. _Read the Docs: https://coverage.readthedocs.io/
-.. _GitHub: https://github.com/nedbat/coveragepy
-
-
-**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
-dropped support for Python 2.6, 3.3 and 3.4.
-
-
-For Enterprise
---------------
-
-.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
- :alt: Tidelift
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
-
-.. list-table::
- :widths: 10 100
-
- * - |tideliftlogo|
- - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
- Coverage and thousands of other packages are working with
- Tidelift to deliver one enterprise subscription that covers all of the open
- source you use. If you want the flexibility of open source and the confidence
- of commercial-grade software, this is for you.
- `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
-
-
-Getting Started
----------------
-
-See the `Quick Start section`_ of the docs.
-
-.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
-
-
-Change history
---------------
-
-The complete history of changes is on the `change history page`_.
-
-.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
-
-
-Contributing
-------------
-
-See the `Contributing section`_ of the docs.
-
-.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
-
-
-Security
---------
-
-To report a security vulnerability, please use the `Tidelift security
-contact`_. Tidelift will coordinate the fix and disclosure.
-
-.. _Tidelift security contact: https://tidelift.com/security
-
-
-License
--------
-
-Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
-
-.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
-.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-
-.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
- :alt: Test suite status
-.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
- :alt: Quality check status
-.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
- :target: https://coverage.readthedocs.io/
- :alt: Documentation
-.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
- :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
- :alt: Requirements status
-.. |kit| image:: https://badge.fury.io/py/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: PyPI status
-.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Kit format
-.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Weekly PyPI downloads
-.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
- :target: https://pypi.org/project/coverage/
- :alt: Python versions supported
-.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Package stability
-.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: License
-.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
- :target: https://codecov.io/github/nedbat/coveragepy?branch=master
- :alt: Coverage!
-.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
- :target: https://repology.org/metapackage/python:coverage/versions
- :alt: Packaging status
-.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
- :alt: Tidelift
-.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/stargazers
- :alt: Github stars
-.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/network/members
- :alt: Github forks
-.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/graphs/contributors
- :alt: Contributors
-.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/coveragepy
- :alt: coverage.py on Twitter
-.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/nedbat
- :alt: nedbat on Twitter
-
-
diff --git a/contrib/python/coverage/py2/.dist-info/entry_points.txt b/contrib/python/coverage/py2/.dist-info/entry_points.txt
deleted file mode 100644
index cd083fc1ff..0000000000
--- a/contrib/python/coverage/py2/.dist-info/entry_points.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-[console_scripts]
-coverage = coverage.cmdline:main
-coverage-3.9 = coverage.cmdline:main
-coverage3 = coverage.cmdline:main
-
diff --git a/contrib/python/coverage/py2/.dist-info/top_level.txt b/contrib/python/coverage/py2/.dist-info/top_level.txt
deleted file mode 100644
index 4ebc8aea50..0000000000
--- a/contrib/python/coverage/py2/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-coverage
diff --git a/contrib/python/coverage/py2/LICENSE.txt b/contrib/python/coverage/py2/LICENSE.txt
deleted file mode 100644
index f433b1a53f..0000000000
--- a/contrib/python/coverage/py2/LICENSE.txt
+++ /dev/null
@@ -1,177 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
diff --git a/contrib/python/coverage/py2/NOTICE.txt b/contrib/python/coverage/py2/NOTICE.txt
deleted file mode 100644
index 37ded535bf..0000000000
--- a/contrib/python/coverage/py2/NOTICE.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright 2001 Gareth Rees. All rights reserved.
-Copyright 2004-2021 Ned Batchelder. All rights reserved.
-
-Except where noted otherwise, this software is licensed under the Apache
-License, Version 2.0 (the "License"); you may not use this work except in
-compliance with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/contrib/python/coverage/py2/README.rst b/contrib/python/coverage/py2/README.rst
deleted file mode 100644
index 072f30ffeb..0000000000
--- a/contrib/python/coverage/py2/README.rst
+++ /dev/null
@@ -1,151 +0,0 @@
-.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-===========
-Coverage.py
-===========
-
-Code coverage testing for Python.
-
-| |license| |versions| |status|
-| |test-status| |quality-status| |docs| |codecov|
-| |kit| |format| |repos| |downloads|
-| |stars| |forks| |contributors|
-| |tidelift| |twitter-coveragepy| |twitter-nedbat|
-
-Coverage.py measures code coverage, typically during test execution. It uses
-the code analysis tools and tracing hooks provided in the Python standard
-library to determine which lines are executable, and which have been executed.
-
-Coverage.py runs on many versions of Python:
-
-* CPython 2.7.
-* CPython 3.5 through 3.10 alpha.
-* PyPy2 7.3.3 and PyPy3 7.3.3.
-
-Documentation is on `Read the Docs`_. Code repository and issue tracker are on
-`GitHub`_.
-
-.. _Read the Docs: https://coverage.readthedocs.io/
-.. _GitHub: https://github.com/nedbat/coveragepy
-
-
-**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
-dropped support for Python 2.6, 3.3 and 3.4.
-
-
-For Enterprise
---------------
-
-.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
- :alt: Tidelift
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
-
-.. list-table::
- :widths: 10 100
-
- * - |tideliftlogo|
- - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
- Coverage and thousands of other packages are working with
- Tidelift to deliver one enterprise subscription that covers all of the open
- source you use. If you want the flexibility of open source and the confidence
- of commercial-grade software, this is for you.
- `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
-
-
-Getting Started
----------------
-
-See the `Quick Start section`_ of the docs.
-
-.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
-
-
-Change history
---------------
-
-The complete history of changes is on the `change history page`_.
-
-.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
-
-
-Contributing
-------------
-
-See the `Contributing section`_ of the docs.
-
-.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
-
-
-Security
---------
-
-To report a security vulnerability, please use the `Tidelift security
-contact`_. Tidelift will coordinate the fix and disclosure.
-
-.. _Tidelift security contact: https://tidelift.com/security
-
-
-License
--------
-
-Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
-
-.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
-.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-
-.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
- :alt: Test suite status
-.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
- :alt: Quality check status
-.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
- :target: https://coverage.readthedocs.io/
- :alt: Documentation
-.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
- :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
- :alt: Requirements status
-.. |kit| image:: https://badge.fury.io/py/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: PyPI status
-.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Kit format
-.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Weekly PyPI downloads
-.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
- :target: https://pypi.org/project/coverage/
- :alt: Python versions supported
-.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Package stability
-.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: License
-.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
- :target: https://codecov.io/github/nedbat/coveragepy?branch=master
- :alt: Coverage!
-.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
- :target: https://repology.org/metapackage/python:coverage/versions
- :alt: Packaging status
-.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
- :alt: Tidelift
-.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/stargazers
- :alt: Github stars
-.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/network/members
- :alt: Github forks
-.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/graphs/contributors
- :alt: Contributors
-.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/coveragepy
- :alt: coverage.py on Twitter
-.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/nedbat
- :alt: nedbat on Twitter
diff --git a/contrib/python/coverage/py2/coverage/__init__.py b/contrib/python/coverage/py2/coverage/__init__.py
deleted file mode 100644
index 331b304b68..0000000000
--- a/contrib/python/coverage/py2/coverage/__init__.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Code coverage measurement for Python.
-
-Ned Batchelder
-https://nedbatchelder.com/code/coverage
-
-"""
-
-import sys
-
-from coverage.version import __version__, __url__, version_info
-
-from coverage.control import Coverage, process_startup
-from coverage.data import CoverageData
-from coverage.misc import CoverageException
-from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
-from coverage.pytracer import PyTracer
-
-# Backward compatibility.
-coverage = Coverage
-
-# On Windows, we encode and decode deep enough that something goes wrong and
-# the encodings.utf_8 module is loaded and then unloaded, I don't know why.
-# Adding a reference here prevents it from being unloaded. Yuk.
-import encodings.utf_8 # pylint: disable=wrong-import-position, wrong-import-order
-
-# Because of the "from coverage.control import fooey" lines at the top of the
-# file, there's an entry for coverage.coverage in sys.modules, mapped to None.
-# This makes some inspection tools (like pydoc) unable to find the class
-# coverage.coverage. So remove that entry.
-try:
- del sys.modules['coverage.coverage']
-except KeyError:
- pass
diff --git a/contrib/python/coverage/py2/coverage/__main__.py b/contrib/python/coverage/py2/coverage/__main__.py
deleted file mode 100644
index 79aa4e2b35..0000000000
--- a/contrib/python/coverage/py2/coverage/__main__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Coverage.py's main entry point."""
-
-import sys
-from coverage.cmdline import main
-sys.exit(main())
diff --git a/contrib/python/coverage/py2/coverage/annotate.py b/contrib/python/coverage/py2/coverage/annotate.py
deleted file mode 100644
index 999ab6e557..0000000000
--- a/contrib/python/coverage/py2/coverage/annotate.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Source file annotation for coverage.py."""
-
-import io
-import os
-import re
-
-from coverage.files import flat_rootname
-from coverage.misc import ensure_dir, isolate_module
-from coverage.report import get_analysis_to_report
-
-os = isolate_module(os)
-
-
-class AnnotateReporter(object):
- """Generate annotated source files showing line coverage.
-
- This reporter creates annotated copies of the measured source files. Each
- .py file is copied as a .py,cover file, with a left-hand margin annotating
- each line::
-
- > def h(x):
- - if 0: #pragma: no cover
- - pass
- > if x == 1:
- ! a = 1
- > else:
- > a = 2
-
- > h(2)
-
- Executed lines use '>', lines not executed use '!', lines excluded from
- consideration use '-'.
-
- """
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
- self.directory = None
-
- blank_re = re.compile(r"\s*(#|$)")
- else_re = re.compile(r"\s*else\s*:\s*(#|$)")
-
- def report(self, morfs, directory=None):
- """Run the report.
-
- See `coverage.report()` for arguments.
-
- """
- self.directory = directory
- self.coverage.get_data()
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.annotate_file(fr, analysis)
-
- def annotate_file(self, fr, analysis):
- """Annotate a single file.
-
- `fr` is the FileReporter for the file to annotate.
-
- """
- statements = sorted(analysis.statements)
- missing = sorted(analysis.missing)
- excluded = sorted(analysis.excluded)
-
- if self.directory:
- ensure_dir(self.directory)
- dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename()))
- if dest_file.endswith("_py"):
- dest_file = dest_file[:-3] + ".py"
- dest_file += ",cover"
- else:
- dest_file = fr.filename + ",cover"
-
- with io.open(dest_file, 'w', encoding='utf8') as dest:
- i = 0
- j = 0
- covered = True
- source = fr.source()
- for lineno, line in enumerate(source.splitlines(True), start=1):
- while i < len(statements) and statements[i] < lineno:
- i += 1
- while j < len(missing) and missing[j] < lineno:
- j += 1
- if i < len(statements) and statements[i] == lineno:
- covered = j >= len(missing) or missing[j] > lineno
- if self.blank_re.match(line):
- dest.write(u' ')
- elif self.else_re.match(line):
- # Special logic for lines containing only 'else:'.
- if i >= len(statements) and j >= len(missing):
- dest.write(u'! ')
- elif i >= len(statements) or j >= len(missing):
- dest.write(u'> ')
- elif statements[i] == missing[j]:
- dest.write(u'! ')
- else:
- dest.write(u'> ')
- elif lineno in excluded:
- dest.write(u'- ')
- elif covered:
- dest.write(u'> ')
- else:
- dest.write(u'! ')
-
- dest.write(line)
diff --git a/contrib/python/coverage/py2/coverage/backward.py b/contrib/python/coverage/py2/coverage/backward.py
deleted file mode 100644
index ac781ab96a..0000000000
--- a/contrib/python/coverage/py2/coverage/backward.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Add things to old Pythons so I can pretend they are newer."""
-
-# This file's purpose is to provide modules to be imported from here.
-# pylint: disable=unused-import
-
-import os
-import sys
-
-from datetime import datetime
-
-from coverage import env
-
-
-# Pythons 2 and 3 differ on where to get StringIO.
-try:
- from cStringIO import StringIO
-except ImportError:
- from io import StringIO
-
-# In py3, ConfigParser was renamed to the more-standard configparser.
-# But there's a py3 backport that installs "configparser" in py2, and I don't
-# want it because it has annoying deprecation warnings. So try the real py2
-# import first.
-try:
- import ConfigParser as configparser
-except ImportError:
- import configparser
-
-# What's a string called?
-try:
- string_class = basestring
-except NameError:
- string_class = str
-
-# What's a Unicode string called?
-try:
- unicode_class = unicode
-except NameError:
- unicode_class = str
-
-# range or xrange?
-try:
- range = xrange # pylint: disable=redefined-builtin
-except NameError:
- range = range
-
-try:
- from itertools import zip_longest
-except ImportError:
- from itertools import izip_longest as zip_longest
-
-# Where do we get the thread id from?
-try:
- from thread import get_ident as get_thread_id
-except ImportError:
- from threading import get_ident as get_thread_id
-
-try:
- os.PathLike
-except AttributeError:
- # This is Python 2 and 3
- path_types = (bytes, string_class, unicode_class)
-else:
- # 3.6+
- path_types = (bytes, str, os.PathLike)
-
-# shlex.quote is new, but there's an undocumented implementation in "pipes",
-# who knew!?
-try:
- from shlex import quote as shlex_quote
-except ImportError:
- # Useful function, available under a different (undocumented) name
- # in Python versions earlier than 3.3.
- from pipes import quote as shlex_quote
-
-try:
- import reprlib
-except ImportError: # pragma: not covered
- # We need this on Python 2, but in testing environments, a backport is
- # installed, so this import isn't used.
- import repr as reprlib
-
-# A function to iterate listlessly over a dict's items, and one to get the
-# items as a list.
-try:
- {}.iteritems
-except AttributeError:
- # Python 3
- def iitems(d):
- """Produce the items from dict `d`."""
- return d.items()
-
- def litems(d):
- """Return a list of items from dict `d`."""
- return list(d.items())
-else:
- # Python 2
- def iitems(d):
- """Produce the items from dict `d`."""
- return d.iteritems()
-
- def litems(d):
- """Return a list of items from dict `d`."""
- return d.items()
-
-# Getting the `next` function from an iterator is different in 2 and 3.
-try:
- iter([]).next
-except AttributeError:
- def iternext(seq):
- """Get the `next` function for iterating over `seq`."""
- return iter(seq).__next__
-else:
- def iternext(seq):
- """Get the `next` function for iterating over `seq`."""
- return iter(seq).next
-
-# Python 3.x is picky about bytes and strings, so provide methods to
-# get them right, and make them no-ops in 2.x
-if env.PY3:
- def to_bytes(s):
- """Convert string `s` to bytes."""
- return s.encode('utf8')
-
- def to_string(b):
- """Convert bytes `b` to string."""
- return b.decode('utf8')
-
- def binary_bytes(byte_values):
- """Produce a byte string with the ints from `byte_values`."""
- return bytes(byte_values)
-
- def byte_to_int(byte):
- """Turn a byte indexed from a bytes object into an int."""
- return byte
-
- def bytes_to_ints(bytes_value):
- """Turn a bytes object into a sequence of ints."""
- # In Python 3, iterating bytes gives ints.
- return bytes_value
-
-else:
- def to_bytes(s):
- """Convert string `s` to bytes (no-op in 2.x)."""
- return s
-
- def to_string(b):
- """Convert bytes `b` to string."""
- return b
-
- def binary_bytes(byte_values):
- """Produce a byte string with the ints from `byte_values`."""
- return "".join(chr(b) for b in byte_values)
-
- def byte_to_int(byte):
- """Turn a byte indexed from a bytes object into an int."""
- return ord(byte)
-
- def bytes_to_ints(bytes_value):
- """Turn a bytes object into a sequence of ints."""
- for byte in bytes_value:
- yield ord(byte)
-
-
-try:
- # In Python 2.x, the builtins were in __builtin__
- BUILTINS = sys.modules['__builtin__']
-except KeyError:
- # In Python 3.x, they're in builtins
- BUILTINS = sys.modules['builtins']
-
-
-# imp was deprecated in Python 3.3
-try:
- import importlib
- import importlib.util
- imp = None
-except ImportError:
- importlib = None
-
-# We only want to use importlib if it has everything we need.
-try:
- importlib_util_find_spec = importlib.util.find_spec
-except Exception:
- import imp
- importlib_util_find_spec = None
-
-# What is the .pyc magic number for this version of Python?
-try:
- PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER
-except AttributeError:
- PYC_MAGIC_NUMBER = imp.get_magic()
-
-
-def code_object(fn):
- """Get the code object from a function."""
- try:
- return fn.func_code
- except AttributeError:
- return fn.__code__
-
-
-try:
- from types import SimpleNamespace
-except ImportError:
- # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace
- class SimpleNamespace:
- """Python implementation of SimpleNamespace, for Python 2."""
- def __init__(self, **kwargs):
- self.__dict__.update(kwargs)
-
- def __repr__(self):
- keys = sorted(self.__dict__)
- items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
- return "{}({})".format(type(self).__name__, ", ".join(items))
-
-
-def format_local_datetime(dt):
- """Return a string with local timezone representing the date.
- If python version is lower than 3.6, the time zone is not included.
- """
- try:
- return dt.astimezone().strftime('%Y-%m-%d %H:%M %z')
- except (TypeError, ValueError):
- # Datetime.astimezone in Python 3.5 can not handle naive datetime
- return dt.strftime('%Y-%m-%d %H:%M')
-
-
-def invalidate_import_caches():
- """Invalidate any import caches that may or may not exist."""
- if importlib and hasattr(importlib, "invalidate_caches"):
- importlib.invalidate_caches()
-
-
-def import_local_file(modname, modfile=None):
- """Import a local file as a module.
-
- Opens a file in the current directory named `modname`.py, imports it
- as `modname`, and returns the module object. `modfile` is the file to
- import if it isn't in the current directory.
-
- """
- try:
- import importlib.util as importlib_util
- except ImportError:
- importlib_util = None
-
- if modfile is None:
- modfile = modname + '.py'
- if importlib_util:
- spec = importlib_util.spec_from_file_location(modname, modfile)
- mod = importlib_util.module_from_spec(spec)
- sys.modules[modname] = mod
- spec.loader.exec_module(mod)
- else:
- for suff in imp.get_suffixes(): # pragma: part covered
- if suff[0] == '.py':
- break
-
- with open(modfile, 'r') as f:
- # pylint: disable=undefined-loop-variable
- mod = imp.load_module(modname, f, modfile, suff)
-
- return mod
diff --git a/contrib/python/coverage/py2/coverage/bytecode.py b/contrib/python/coverage/py2/coverage/bytecode.py
deleted file mode 100644
index ceb18cf374..0000000000
--- a/contrib/python/coverage/py2/coverage/bytecode.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Bytecode manipulation for coverage.py"""
-
-import types
-
-
-def code_objects(code):
- """Iterate over all the code objects in `code`."""
- stack = [code]
- while stack:
- # We're going to return the code object on the stack, but first
- # push its children for later returning.
- code = stack.pop()
- for c in code.co_consts:
- if isinstance(c, types.CodeType):
- stack.append(c)
- yield code
diff --git a/contrib/python/coverage/py2/coverage/cmdline.py b/contrib/python/coverage/py2/coverage/cmdline.py
deleted file mode 100644
index 0be0cca19f..0000000000
--- a/contrib/python/coverage/py2/coverage/cmdline.py
+++ /dev/null
@@ -1,910 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Command-line support for coverage.py."""
-
-from __future__ import print_function
-
-import glob
-import optparse
-import os.path
-import shlex
-import sys
-import textwrap
-import traceback
-
-import coverage
-from coverage import Coverage
-from coverage import env
-from coverage.collector import CTracer
-from coverage.data import line_counts
-from coverage.debug import info_formatter, info_header, short_stack
-from coverage.execfile import PyRunner
-from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding
-from coverage.results import should_fail_under
-
-
-class Opts(object):
- """A namespace class for individual options we'll build parsers from."""
-
- append = optparse.make_option(
- '-a', '--append', action='store_true',
- help="Append coverage data to .coverage, otherwise it starts clean each time.",
- )
- keep = optparse.make_option(
- '', '--keep', action='store_true',
- help="Keep original coverage files, otherwise they are deleted.",
- )
- branch = optparse.make_option(
- '', '--branch', action='store_true',
- help="Measure branch coverage in addition to statement coverage.",
- )
- CONCURRENCY_CHOICES = [
- "thread", "gevent", "greenlet", "eventlet", "multiprocessing",
- ]
- concurrency = optparse.make_option(
- '', '--concurrency', action='store', metavar="LIB",
- choices=CONCURRENCY_CHOICES,
- help=(
- "Properly measure code using a concurrency library. "
- "Valid values are: %s."
- ) % ", ".join(CONCURRENCY_CHOICES),
- )
- context = optparse.make_option(
- '', '--context', action='store', metavar="LABEL",
- help="The context label to record for this coverage run.",
- )
- debug = optparse.make_option(
- '', '--debug', action='store', metavar="OPTS",
- help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
- )
- directory = optparse.make_option(
- '-d', '--directory', action='store', metavar="DIR",
- help="Write the output files to DIR.",
- )
- fail_under = optparse.make_option(
- '', '--fail-under', action='store', metavar="MIN", type="float",
- help="Exit with a status of 2 if the total coverage is less than MIN.",
- )
- help = optparse.make_option(
- '-h', '--help', action='store_true',
- help="Get help on this command.",
- )
- ignore_errors = optparse.make_option(
- '-i', '--ignore-errors', action='store_true',
- help="Ignore errors while reading source files.",
- )
- include = optparse.make_option(
- '', '--include', action='store',
- metavar="PAT1,PAT2,...",
- help=(
- "Include only files whose paths match one of these patterns. "
- "Accepts shell-style wildcards, which must be quoted."
- ),
- )
- pylib = optparse.make_option(
- '-L', '--pylib', action='store_true',
- help=(
- "Measure coverage even inside the Python installed library, "
- "which isn't done by default."
- ),
- )
- sort = optparse.make_option(
- '--sort', action='store', metavar='COLUMN',
- help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. "
- "Default is name."
- )
- show_missing = optparse.make_option(
- '-m', '--show-missing', action='store_true',
- help="Show line numbers of statements in each module that weren't executed.",
- )
- skip_covered = optparse.make_option(
- '--skip-covered', action='store_true',
- help="Skip files with 100% coverage.",
- )
- no_skip_covered = optparse.make_option(
- '--no-skip-covered', action='store_false', dest='skip_covered',
- help="Disable --skip-covered.",
- )
- skip_empty = optparse.make_option(
- '--skip-empty', action='store_true',
- help="Skip files with no code.",
- )
- show_contexts = optparse.make_option(
- '--show-contexts', action='store_true',
- help="Show contexts for covered lines.",
- )
- omit = optparse.make_option(
- '', '--omit', action='store',
- metavar="PAT1,PAT2,...",
- help=(
- "Omit files whose paths match one of these patterns. "
- "Accepts shell-style wildcards, which must be quoted."
- ),
- )
- contexts = optparse.make_option(
- '', '--contexts', action='store',
- metavar="REGEX1,REGEX2,...",
- help=(
- "Only display data from lines covered in the given contexts. "
- "Accepts Python regexes, which must be quoted."
- ),
- )
- output_xml = optparse.make_option(
- '-o', '', action='store', dest="outfile",
- metavar="OUTFILE",
- help="Write the XML report to this file. Defaults to 'coverage.xml'",
- )
- output_json = optparse.make_option(
- '-o', '', action='store', dest="outfile",
- metavar="OUTFILE",
- help="Write the JSON report to this file. Defaults to 'coverage.json'",
- )
- json_pretty_print = optparse.make_option(
- '', '--pretty-print', action='store_true',
- help="Format the JSON for human readers.",
- )
- parallel_mode = optparse.make_option(
- '-p', '--parallel-mode', action='store_true',
- help=(
- "Append the machine name, process id and random number to the "
- ".coverage data file name to simplify collecting data from "
- "many processes."
- ),
- )
- module = optparse.make_option(
- '-m', '--module', action='store_true',
- help=(
- "<pyfile> is an importable Python module, not a script path, "
- "to be run as 'python -m' would run it."
- ),
- )
- precision = optparse.make_option(
- '', '--precision', action='store', metavar='N', type=int,
- help=(
- "Number of digits after the decimal point to display for "
- "reported coverage percentages."
- ),
- )
- rcfile = optparse.make_option(
- '', '--rcfile', action='store',
- help=(
- "Specify configuration file. "
- "By default '.coveragerc', 'setup.cfg', 'tox.ini', and "
- "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
- ),
- )
- source = optparse.make_option(
- '', '--source', action='store', metavar="SRC1,SRC2,...",
- help="A list of packages or directories of code to be measured.",
- )
- timid = optparse.make_option(
- '', '--timid', action='store_true',
- help=(
- "Use a simpler but slower trace method. Try this if you get "
- "seemingly impossible results!"
- ),
- )
- title = optparse.make_option(
- '', '--title', action='store', metavar="TITLE",
- help="A text string to use as the title on the HTML.",
- )
- version = optparse.make_option(
- '', '--version', action='store_true',
- help="Display version information and exit.",
- )
-
-
-class CoverageOptionParser(optparse.OptionParser, object):
- """Base OptionParser for coverage.py.
-
- Problems don't exit the program.
- Defaults are initialized for all options.
-
- """
-
- def __init__(self, *args, **kwargs):
- super(CoverageOptionParser, self).__init__(
- add_help_option=False, *args, **kwargs
- )
- self.set_defaults(
- action=None,
- append=None,
- branch=None,
- concurrency=None,
- context=None,
- debug=None,
- directory=None,
- fail_under=None,
- help=None,
- ignore_errors=None,
- include=None,
- keep=None,
- module=None,
- omit=None,
- contexts=None,
- parallel_mode=None,
- precision=None,
- pylib=None,
- rcfile=True,
- show_missing=None,
- skip_covered=None,
- skip_empty=None,
- show_contexts=None,
- sort=None,
- source=None,
- timid=None,
- title=None,
- version=None,
- )
-
- self.disable_interspersed_args()
-
- class OptionParserError(Exception):
- """Used to stop the optparse error handler ending the process."""
- pass
-
- def parse_args_ok(self, args=None, options=None):
- """Call optparse.parse_args, but return a triple:
-
- (ok, options, args)
-
- """
- try:
- options, args = super(CoverageOptionParser, self).parse_args(args, options)
- except self.OptionParserError:
- return False, None, None
- return True, options, args
-
- def error(self, msg):
- """Override optparse.error so sys.exit doesn't get called."""
- show_help(msg)
- raise self.OptionParserError
-
-
-class GlobalOptionParser(CoverageOptionParser):
- """Command-line parser for coverage.py global option arguments."""
-
- def __init__(self):
- super(GlobalOptionParser, self).__init__()
-
- self.add_options([
- Opts.help,
- Opts.version,
- ])
-
-
-class CmdOptionParser(CoverageOptionParser):
- """Parse one of the new-style commands for coverage.py."""
-
- def __init__(self, action, options, defaults=None, usage=None, description=None):
- """Create an OptionParser for a coverage.py command.
-
- `action` is the slug to put into `options.action`.
- `options` is a list of Option's for the command.
- `defaults` is a dict of default value for options.
- `usage` is the usage string to display in help.
- `description` is the description of the command, for the help text.
-
- """
- if usage:
- usage = "%prog " + usage
- super(CmdOptionParser, self).__init__(
- usage=usage,
- description=description,
- )
- self.set_defaults(action=action, **(defaults or {}))
- self.add_options(options)
- self.cmd = action
-
- def __eq__(self, other):
- # A convenience equality, so that I can put strings in unit test
- # results, and they will compare equal to objects.
- return (other == "<CmdOptionParser:%s>" % self.cmd)
-
- __hash__ = None # This object doesn't need to be hashed.
-
- def get_prog_name(self):
- """Override of an undocumented function in optparse.OptionParser."""
- program_name = super(CmdOptionParser, self).get_prog_name()
-
- # Include the sub-command for this parser as part of the command.
- return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd)
-
-
-GLOBAL_ARGS = [
- Opts.debug,
- Opts.help,
- Opts.rcfile,
- ]
-
-CMDS = {
- 'annotate': CmdOptionParser(
- "annotate",
- [
- Opts.directory,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description=(
- "Make annotated copies of the given files, marking statements that are executed "
- "with > and statements that are missed with !."
- ),
- ),
-
- 'combine': CmdOptionParser(
- "combine",
- [
- Opts.append,
- Opts.keep,
- ] + GLOBAL_ARGS,
- usage="[options] <path1> <path2> ... <pathN>",
- description=(
- "Combine data from multiple coverage files collected "
- "with 'run -p'. The combined results are written to a single "
- "file representing the union of the data. The positional "
- "arguments are data files or directories containing data files. "
- "If no paths are provided, data files in the default data file's "
- "directory are combined."
- ),
- ),
-
- 'debug': CmdOptionParser(
- "debug", GLOBAL_ARGS,
- usage="<topic>",
- description=(
- "Display information about the internals of coverage.py, "
- "for diagnosing problems. "
- "Topics are: "
- "'data' to show a summary of the collected data; "
- "'sys' to show installation information; "
- "'config' to show the configuration; "
- "'premain' to show what is calling coverage."
- ),
- ),
-
- 'erase': CmdOptionParser(
- "erase", GLOBAL_ARGS,
- description="Erase previously collected coverage data.",
- ),
-
- 'help': CmdOptionParser(
- "help", GLOBAL_ARGS,
- usage="[command]",
- description="Describe how to use coverage.py",
- ),
-
- 'html': CmdOptionParser(
- "html",
- [
- Opts.contexts,
- Opts.directory,
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.precision,
- Opts.show_contexts,
- Opts.skip_covered,
- Opts.no_skip_covered,
- Opts.skip_empty,
- Opts.title,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description=(
- "Create an HTML report of the coverage of the files. "
- "Each file gets its own page, with the source decorated to show "
- "executed, excluded, and missed lines."
- ),
- ),
-
- 'json': CmdOptionParser(
- "json",
- [
- Opts.contexts,
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.output_json,
- Opts.json_pretty_print,
- Opts.show_contexts,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description="Generate a JSON report of coverage results."
- ),
-
- 'report': CmdOptionParser(
- "report",
- [
- Opts.contexts,
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.precision,
- Opts.sort,
- Opts.show_missing,
- Opts.skip_covered,
- Opts.no_skip_covered,
- Opts.skip_empty,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description="Report coverage statistics on modules."
- ),
-
- 'run': CmdOptionParser(
- "run",
- [
- Opts.append,
- Opts.branch,
- Opts.concurrency,
- Opts.context,
- Opts.include,
- Opts.module,
- Opts.omit,
- Opts.pylib,
- Opts.parallel_mode,
- Opts.source,
- Opts.timid,
- ] + GLOBAL_ARGS,
- usage="[options] <pyfile> [program options]",
- description="Run a Python program, measuring code execution."
- ),
-
- 'xml': CmdOptionParser(
- "xml",
- [
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.output_xml,
- Opts.skip_empty,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description="Generate an XML report of coverage results."
- ),
-}
-
-
-def show_help(error=None, topic=None, parser=None):
- """Display an error message, or the named topic."""
- assert error or topic or parser
-
- program_path = sys.argv[0]
- if program_path.endswith(os.path.sep + '__main__.py'):
- # The path is the main module of a package; get that path instead.
- program_path = os.path.dirname(program_path)
- program_name = os.path.basename(program_path)
- if env.WINDOWS:
- # entry_points={'console_scripts':...} on Windows makes files
- # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
- # invoke coverage-script.py, coverage3-script.py, and
- # coverage-3.5-script.py. argv[0] is the .py file, but we want to
- # get back to the original form.
- auto_suffix = "-script.py"
- if program_name.endswith(auto_suffix):
- program_name = program_name[:-len(auto_suffix)]
-
- help_params = dict(coverage.__dict__)
- help_params['program_name'] = program_name
- if CTracer is not None:
- help_params['extension_modifier'] = 'with C extension'
- else:
- help_params['extension_modifier'] = 'without C extension'
-
- if error:
- print(error, file=sys.stderr)
- print("Use '%s help' for help." % (program_name,), file=sys.stderr)
- elif parser:
- print(parser.format_help().strip())
- print()
- else:
- help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip()
- if help_msg:
- print(help_msg.format(**help_params))
- else:
- print("Don't know topic %r" % topic)
- print("Full documentation is at {__url__}".format(**help_params))
-
-
-OK, ERR, FAIL_UNDER = 0, 1, 2
-
-
-class CoverageScript(object):
- """The command-line interface to coverage.py."""
-
- def __init__(self):
- self.global_option = False
- self.coverage = None
-
- def command_line(self, argv):
- """The bulk of the command line interface to coverage.py.
-
- `argv` is the argument list to process.
-
- Returns 0 if all is well, 1 if something went wrong.
-
- """
- # Collect the command-line options.
- if not argv:
- show_help(topic='minimum_help')
- return OK
-
- # The command syntax we parse depends on the first argument. Global
- # switch syntax always starts with an option.
- self.global_option = argv[0].startswith('-')
- if self.global_option:
- parser = GlobalOptionParser()
- else:
- parser = CMDS.get(argv[0])
- if not parser:
- show_help("Unknown command: '%s'" % argv[0])
- return ERR
- argv = argv[1:]
-
- ok, options, args = parser.parse_args_ok(argv)
- if not ok:
- return ERR
-
- # Handle help and version.
- if self.do_help(options, args, parser):
- return OK
-
- # Listify the list options.
- source = unshell_list(options.source)
- omit = unshell_list(options.omit)
- include = unshell_list(options.include)
- debug = unshell_list(options.debug)
- contexts = unshell_list(options.contexts)
-
- # Do something.
- self.coverage = Coverage(
- data_suffix=options.parallel_mode,
- cover_pylib=options.pylib,
- timid=options.timid,
- branch=options.branch,
- config_file=options.rcfile,
- source=source,
- omit=omit,
- include=include,
- debug=debug,
- concurrency=options.concurrency,
- check_preimported=True,
- context=options.context,
- )
-
- if options.action == "debug":
- return self.do_debug(args)
-
- elif options.action == "erase":
- self.coverage.erase()
- return OK
-
- elif options.action == "run":
- return self.do_run(options, args)
-
- elif options.action == "combine":
- if options.append:
- self.coverage.load()
- data_dirs = args or None
- self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep))
- self.coverage.save()
- return OK
-
- # Remaining actions are reporting, with some common options.
- report_args = dict(
- morfs=unglob_args(args),
- ignore_errors=options.ignore_errors,
- omit=omit,
- include=include,
- contexts=contexts,
- )
-
- # We need to be able to import from the current directory, because
- # plugins may try to, for example, to read Django settings.
- sys.path.insert(0, '')
-
- self.coverage.load()
-
- total = None
- if options.action == "report":
- total = self.coverage.report(
- show_missing=options.show_missing,
- skip_covered=options.skip_covered,
- skip_empty=options.skip_empty,
- precision=options.precision,
- sort=options.sort,
- **report_args
- )
- elif options.action == "annotate":
- self.coverage.annotate(directory=options.directory, **report_args)
- elif options.action == "html":
- total = self.coverage.html_report(
- directory=options.directory,
- title=options.title,
- skip_covered=options.skip_covered,
- skip_empty=options.skip_empty,
- show_contexts=options.show_contexts,
- precision=options.precision,
- **report_args
- )
- elif options.action == "xml":
- outfile = options.outfile
- total = self.coverage.xml_report(
- outfile=outfile, skip_empty=options.skip_empty,
- **report_args
- )
- elif options.action == "json":
- outfile = options.outfile
- total = self.coverage.json_report(
- outfile=outfile,
- pretty_print=options.pretty_print,
- show_contexts=options.show_contexts,
- **report_args
- )
-
- if total is not None:
- # Apply the command line fail-under options, and then use the config
- # value, so we can get fail_under from the config file.
- if options.fail_under is not None:
- self.coverage.set_option("report:fail_under", options.fail_under)
-
- fail_under = self.coverage.get_option("report:fail_under")
- precision = self.coverage.get_option("report:precision")
- if should_fail_under(total, fail_under, precision):
- msg = "total of {total:.{p}f} is less than fail-under={fail_under:.{p}f}".format(
- total=total, fail_under=fail_under, p=precision,
- )
- print("Coverage failure:", msg)
- return FAIL_UNDER
-
- return OK
-
- def do_help(self, options, args, parser):
- """Deal with help requests.
-
- Return True if it handled the request, False if not.
-
- """
- # Handle help.
- if options.help:
- if self.global_option:
- show_help(topic='help')
- else:
- show_help(parser=parser)
- return True
-
- if options.action == "help":
- if args:
- for a in args:
- parser = CMDS.get(a)
- if parser:
- show_help(parser=parser)
- else:
- show_help(topic=a)
- else:
- show_help(topic='help')
- return True
-
- # Handle version.
- if options.version:
- show_help(topic='version')
- return True
-
- return False
-
- def do_run(self, options, args):
- """Implementation of 'coverage run'."""
-
- if not args:
- if options.module:
- # Specified -m with nothing else.
- show_help("No module specified for -m")
- return ERR
- command_line = self.coverage.get_option("run:command_line")
- if command_line is not None:
- args = shlex.split(command_line)
- if args and args[0] == "-m":
- options.module = True
- args = args[1:]
- if not args:
- show_help("Nothing to do.")
- return ERR
-
- if options.append and self.coverage.get_option("run:parallel"):
- show_help("Can't append to data files in parallel mode.")
- return ERR
-
- if options.concurrency == "multiprocessing":
- # Can't set other run-affecting command line options with
- # multiprocessing.
- for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
- # As it happens, all of these options have no default, meaning
- # they will be None if they have not been specified.
- if getattr(options, opt_name) is not None:
- show_help(
- "Options affecting multiprocessing must only be specified "
- "in a configuration file.\n"
- "Remove --{} from the command line.".format(opt_name)
- )
- return ERR
-
- runner = PyRunner(args, as_module=bool(options.module))
- runner.prepare()
-
- if options.append:
- self.coverage.load()
-
- # Run the script.
- self.coverage.start()
- code_ran = True
- try:
- runner.run()
- except NoSource:
- code_ran = False
- raise
- finally:
- self.coverage.stop()
- if code_ran:
- self.coverage.save()
-
- return OK
-
- def do_debug(self, args):
- """Implementation of 'coverage debug'."""
-
- if not args:
- show_help("What information would you like: config, data, sys, premain?")
- return ERR
-
- for info in args:
- if info == 'sys':
- sys_info = self.coverage.sys_info()
- print(info_header("sys"))
- for line in info_formatter(sys_info):
- print(" %s" % line)
- elif info == 'data':
- self.coverage.load()
- data = self.coverage.get_data()
- print(info_header("data"))
- print("path: %s" % data.data_filename())
- if data:
- print("has_arcs: %r" % data.has_arcs())
- summary = line_counts(data, fullpath=True)
- filenames = sorted(summary.keys())
- print("\n%d files:" % len(filenames))
- for f in filenames:
- line = "%s: %d lines" % (f, summary[f])
- plugin = data.file_tracer(f)
- if plugin:
- line += " [%s]" % plugin
- print(line)
- else:
- print("No data collected")
- elif info == 'config':
- print(info_header("config"))
- config_info = self.coverage.config.__dict__.items()
- for line in info_formatter(config_info):
- print(" %s" % line)
- elif info == "premain":
- print(info_header("premain"))
- print(short_stack())
- else:
- show_help("Don't know what you mean by %r" % info)
- return ERR
-
- return OK
-
-
-def unshell_list(s):
- """Turn a command-line argument into a list."""
- if not s:
- return None
- if env.WINDOWS:
- # When running coverage.py as coverage.exe, some of the behavior
- # of the shell is emulated: wildcards are expanded into a list of
- # file names. So you have to single-quote patterns on the command
- # line, but (not) helpfully, the single quotes are included in the
- # argument, so we have to strip them off here.
- s = s.strip("'")
- return s.split(',')
-
-
-def unglob_args(args):
- """Interpret shell wildcards for platforms that need it."""
- if env.WINDOWS:
- globbed = []
- for arg in args:
- if '?' in arg or '*' in arg:
- globbed.extend(glob.glob(arg))
- else:
- globbed.append(arg)
- args = globbed
- return args
-
-
-HELP_TOPICS = {
- 'help': """\
- Coverage.py, version {__version__} {extension_modifier}
- Measure, collect, and report on code coverage in Python programs.
-
- usage: {program_name} <command> [options] [args]
-
- Commands:
- annotate Annotate source files with execution information.
- combine Combine a number of data files.
- debug Display information about the internals of coverage.py
- erase Erase previously collected coverage data.
- help Get help on using coverage.py.
- html Create an HTML report.
- json Create a JSON report of coverage results.
- report Report coverage stats on modules.
- run Run a Python program and measure code execution.
- xml Create an XML report of coverage results.
-
- Use "{program_name} help <command>" for detailed help on any command.
- """,
-
- 'minimum_help': """\
- Code coverage for Python, version {__version__} {extension_modifier}. Use '{program_name} help' for help.
- """,
-
- 'version': """\
- Coverage.py, version {__version__} {extension_modifier}
- """,
-}
-
-
-def main(argv=None):
- """The main entry point to coverage.py.
-
- This is installed as the script entry point.
-
- """
- if argv is None:
- argv = sys.argv[1:]
- try:
- status = CoverageScript().command_line(argv)
- except ExceptionDuringRun as err:
- # An exception was caught while running the product code. The
- # sys.exc_info() return tuple is packed into an ExceptionDuringRun
- # exception.
- traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
- status = ERR
- except BaseCoverageException as err:
- # A controlled error inside coverage.py: print the message to the user.
- msg = err.args[0]
- if env.PY2:
- msg = msg.encode(output_encoding())
- print(msg)
- status = ERR
- except SystemExit as err:
- # The user called `sys.exit()`. Exit with their argument, if any.
- if err.args:
- status = err.args[0]
- else:
- status = None
- return status
-
-# Profiling using ox_profile. Install it from GitHub:
-# pip install git+https://github.com/emin63/ox_profile.git
-#
-# $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
-_profile = os.environ.get("COVERAGE_PROFILE", "")
-if _profile: # pragma: debugging
- from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
- original_main = main
-
- def main(argv=None): # pylint: disable=function-redefined
- """A wrapper around main that profiles."""
- profiler = SimpleLauncher.launch()
- try:
- return original_main(argv)
- finally:
- data, _ = profiler.query(re_filter='coverage', max_records=100)
- print(profiler.show(query=data, limit=100, sep='', col=''))
- profiler.cancel()
diff --git a/contrib/python/coverage/py2/coverage/collector.py b/contrib/python/coverage/py2/coverage/collector.py
deleted file mode 100644
index c42d29feec..0000000000
--- a/contrib/python/coverage/py2/coverage/collector.py
+++ /dev/null
@@ -1,455 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Raw data collector for coverage.py."""
-
-import os
-import sys
-
-from coverage import env
-from coverage.backward import litems, range # pylint: disable=redefined-builtin
-from coverage.debug import short_stack
-from coverage.disposition import FileDisposition
-from coverage.misc import CoverageException, isolate_module
-from coverage.pytracer import PyTracer
-
-os = isolate_module(os)
-
-
-try:
- # Use the C extension code when we can, for speed.
- from coverage.tracer import CTracer, CFileDisposition
-except ImportError:
- # Couldn't import the C extension, maybe it isn't built.
- if os.getenv('COVERAGE_TEST_TRACER') == 'c':
- # During testing, we use the COVERAGE_TEST_TRACER environment variable
- # to indicate that we've fiddled with the environment to test this
- # fallback code. If we thought we had a C tracer, but couldn't import
- # it, then exit quickly and clearly instead of dribbling confusing
- # errors. I'm using sys.exit here instead of an exception because an
- # exception here causes all sorts of other noise in unittest.
- sys.stderr.write("*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n")
- sys.exit(1)
- CTracer = None
-
-
-class Collector(object):
- """Collects trace data.
-
- Creates a Tracer object for each thread, since they track stack
- information. Each Tracer points to the same shared data, contributing
- traced data points.
-
- When the Collector is started, it creates a Tracer for the current thread,
- and installs a function to create Tracers for each new thread started.
- When the Collector is stopped, all active Tracers are stopped.
-
- Threads started while the Collector is stopped will never have Tracers
- associated with them.
-
- """
-
- # The stack of active Collectors. Collectors are added here when started,
- # and popped when stopped. Collectors on the stack are paused when not
- # the top, and resumed when they become the top again.
- _collectors = []
-
- # The concurrency settings we support here.
- SUPPORTED_CONCURRENCIES = {"greenlet", "eventlet", "gevent", "thread"}
-
- def __init__(
- self, should_trace, check_include, should_start_context, file_mapper,
- timid, branch, warn, concurrency,
- ):
- """Create a collector.
-
- `should_trace` is a function, taking a file name and a frame, and
- returning a `coverage.FileDisposition object`.
-
- `check_include` is a function taking a file name and a frame. It returns
- a boolean: True if the file should be traced, False if not.
-
- `should_start_context` is a function taking a frame, and returning a
- string. If the frame should be the start of a new context, the string
- is the new context. If the frame should not be the start of a new
- context, return None.
-
- `file_mapper` is a function taking a filename, and returning a Unicode
- filename. The result is the name that will be recorded in the data
- file.
-
- If `timid` is true, then a slower simpler trace function will be
- used. This is important for some environments where manipulation of
- tracing functions make the faster more sophisticated trace function not
- operate properly.
-
- If `branch` is true, then branches will be measured. This involves
- collecting data on which statements followed each other (arcs). Use
- `get_arc_data` to get the arc data.
-
- `warn` is a warning function, taking a single string message argument
- and an optional slug argument which will be a string or None, to be
- used if a warning needs to be issued.
-
- `concurrency` is a list of strings indicating the concurrency libraries
- in use. Valid values are "greenlet", "eventlet", "gevent", or "thread"
- (the default). Of these four values, only one can be supplied. Other
- values are ignored.
-
- """
- self.should_trace = should_trace
- self.check_include = check_include
- self.should_start_context = should_start_context
- self.file_mapper = file_mapper
- self.warn = warn
- self.branch = branch
- self.threading = None
- self.covdata = None
-
- self.static_context = None
-
- self.origin = short_stack()
-
- self.concur_id_func = None
- self.mapped_file_cache = {}
-
- # We can handle a few concurrency options here, but only one at a time.
- these_concurrencies = self.SUPPORTED_CONCURRENCIES.intersection(concurrency)
- if len(these_concurrencies) > 1:
- raise CoverageException("Conflicting concurrency settings: %s" % concurrency)
- self.concurrency = these_concurrencies.pop() if these_concurrencies else ''
-
- try:
- if self.concurrency == "greenlet":
- import greenlet
- self.concur_id_func = greenlet.getcurrent
- elif self.concurrency == "eventlet":
- import eventlet.greenthread # pylint: disable=import-error,useless-suppression
- self.concur_id_func = eventlet.greenthread.getcurrent
- elif self.concurrency == "gevent":
- import gevent # pylint: disable=import-error,useless-suppression
- self.concur_id_func = gevent.getcurrent
- elif self.concurrency == "thread" or not self.concurrency:
- # It's important to import threading only if we need it. If
- # it's imported early, and the program being measured uses
- # gevent, then gevent's monkey-patching won't work properly.
- import threading
- self.threading = threading
- else:
- raise CoverageException("Don't understand concurrency=%s" % concurrency)
- except ImportError:
- raise CoverageException(
- "Couldn't trace with concurrency=%s, the module isn't installed." % (
- self.concurrency,
- )
- )
-
- self.reset()
-
- if timid:
- # Being timid: use the simple Python trace function.
- self._trace_class = PyTracer
- else:
- # Being fast: use the C Tracer if it is available, else the Python
- # trace function.
- self._trace_class = CTracer or PyTracer
-
- if self._trace_class is CTracer:
- self.file_disposition_class = CFileDisposition
- self.supports_plugins = True
- else:
- self.file_disposition_class = FileDisposition
- self.supports_plugins = False
-
- def __repr__(self):
- return "<Collector at 0x%x: %s>" % (id(self), self.tracer_name())
-
- def use_data(self, covdata, context):
- """Use `covdata` for recording data."""
- self.covdata = covdata
- self.static_context = context
- self.covdata.set_context(self.static_context)
-
- def tracer_name(self):
- """Return the class name of the tracer we're using."""
- return self._trace_class.__name__
-
- def _clear_data(self):
- """Clear out existing data, but stay ready for more collection."""
- # We used to used self.data.clear(), but that would remove filename
- # keys and data values that were still in use higher up the stack
- # when we are called as part of switch_context.
- for d in self.data.values():
- d.clear()
-
- for tracer in self.tracers:
- tracer.reset_activity()
-
- def reset(self):
- """Clear collected data, and prepare to collect more."""
- # A dictionary mapping file names to dicts with line number keys (if not
- # branch coverage), or mapping file names to dicts with line number
- # pairs as keys (if branch coverage).
- self.data = {}
-
- # A dictionary mapping file names to file tracer plugin names that will
- # handle them.
- self.file_tracers = {}
-
- self.disabled_plugins = set()
-
- # The .should_trace_cache attribute is a cache from file names to
- # coverage.FileDisposition objects, or None. When a file is first
- # considered for tracing, a FileDisposition is obtained from
- # Coverage.should_trace. Its .trace attribute indicates whether the
- # file should be traced or not. If it should be, a plugin with dynamic
- # file names can decide not to trace it based on the dynamic file name
- # being excluded by the inclusion rules, in which case the
- # FileDisposition will be replaced by None in the cache.
- if env.PYPY:
- import __pypy__ # pylint: disable=import-error
- # Alex Gaynor said:
- # should_trace_cache is a strictly growing key: once a key is in
- # it, it never changes. Further, the keys used to access it are
- # generally constant, given sufficient context. That is to say, at
- # any given point _trace() is called, pypy is able to know the key.
- # This is because the key is determined by the physical source code
- # line, and that's invariant with the call site.
- #
- # This property of a dict with immutable keys, combined with
- # call-site-constant keys is a match for PyPy's module dict,
- # which is optimized for such workloads.
- #
- # This gives a 20% benefit on the workload described at
- # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage
- self.should_trace_cache = __pypy__.newdict("module")
- else:
- self.should_trace_cache = {}
-
- # Our active Tracers.
- self.tracers = []
-
- self._clear_data()
-
- def _start_tracer(self):
- """Start a new Tracer object, and store it in self.tracers."""
- tracer = self._trace_class()
- tracer.data = self.data
- tracer.trace_arcs = self.branch
- tracer.should_trace = self.should_trace
- tracer.should_trace_cache = self.should_trace_cache
- tracer.warn = self.warn
-
- if hasattr(tracer, 'concur_id_func'):
- tracer.concur_id_func = self.concur_id_func
- elif self.concur_id_func:
- raise CoverageException(
- "Can't support concurrency=%s with %s, only threads are supported" % (
- self.concurrency, self.tracer_name(),
- )
- )
-
- if hasattr(tracer, 'file_tracers'):
- tracer.file_tracers = self.file_tracers
- if hasattr(tracer, 'threading'):
- tracer.threading = self.threading
- if hasattr(tracer, 'check_include'):
- tracer.check_include = self.check_include
- if hasattr(tracer, 'should_start_context'):
- tracer.should_start_context = self.should_start_context
- tracer.switch_context = self.switch_context
- if hasattr(tracer, 'disable_plugin'):
- tracer.disable_plugin = self.disable_plugin
-
- fn = tracer.start()
- self.tracers.append(tracer)
-
- return fn
-
- # The trace function has to be set individually on each thread before
- # execution begins. Ironically, the only support the threading module has
- # for running code before the thread main is the tracing function. So we
- # install this as a trace function, and the first time it's called, it does
- # the real trace installation.
-
- def _installation_trace(self, frame, event, arg):
- """Called on new threads, installs the real tracer."""
- # Remove ourselves as the trace function.
- sys.settrace(None)
- # Install the real tracer.
- fn = self._start_tracer()
- # Invoke the real trace function with the current event, to be sure
- # not to lose an event.
- if fn:
- fn = fn(frame, event, arg)
- # Return the new trace function to continue tracing in this scope.
- return fn
-
- def start(self):
- """Start collecting trace information."""
- if self._collectors:
- self._collectors[-1].pause()
-
- self.tracers = []
-
- # Check to see whether we had a fullcoverage tracer installed. If so,
- # get the stack frames it stashed away for us.
- traces0 = []
- fn0 = sys.gettrace()
- if fn0:
- tracer0 = getattr(fn0, '__self__', None)
- if tracer0:
- traces0 = getattr(tracer0, 'traces', [])
-
- try:
- # Install the tracer on this thread.
- fn = self._start_tracer()
- except:
- if self._collectors:
- self._collectors[-1].resume()
- raise
-
- # If _start_tracer succeeded, then we add ourselves to the global
- # stack of collectors.
- self._collectors.append(self)
-
- # Replay all the events from fullcoverage into the new trace function.
- for args in traces0:
- (frame, event, arg), lineno = args
- try:
- fn(frame, event, arg, lineno=lineno)
- except TypeError:
- raise Exception("fullcoverage must be run with the C trace function.")
-
- # Install our installation tracer in threading, to jump-start other
- # threads.
- if self.threading:
- self.threading.settrace(self._installation_trace)
-
- def stop(self):
- """Stop collecting trace information."""
- assert self._collectors
- if self._collectors[-1] is not self:
- print("self._collectors:")
- for c in self._collectors:
- print(" {!r}\n{}".format(c, c.origin))
- assert self._collectors[-1] is self, (
- "Expected current collector to be %r, but it's %r" % (self, self._collectors[-1])
- )
-
- self.pause()
-
- # Remove this Collector from the stack, and resume the one underneath
- # (if any).
- self._collectors.pop()
- if self._collectors:
- self._collectors[-1].resume()
-
- def pause(self):
- """Pause tracing, but be prepared to `resume`."""
- for tracer in self.tracers:
- tracer.stop()
- stats = tracer.get_stats()
- if stats:
- print("\nCoverage.py tracer stats:")
- for k in sorted(stats.keys()):
- print("%20s: %s" % (k, stats[k]))
- if self.threading:
- self.threading.settrace(None)
-
- def resume(self):
- """Resume tracing after a `pause`."""
- for tracer in self.tracers:
- tracer.start()
- if self.threading:
- self.threading.settrace(self._installation_trace)
- else:
- self._start_tracer()
-
- def _activity(self):
- """Has any activity been traced?
-
- Returns a boolean, True if any trace function was invoked.
-
- """
- return any(tracer.activity() for tracer in self.tracers)
-
- def switch_context(self, new_context):
- """Switch to a new dynamic context."""
- self.flush_data()
- if self.static_context:
- context = self.static_context
- if new_context:
- context += "|" + new_context
- else:
- context = new_context
- self.covdata.set_context(context)
-
- def disable_plugin(self, disposition):
- """Disable the plugin mentioned in `disposition`."""
- file_tracer = disposition.file_tracer
- plugin = file_tracer._coverage_plugin
- plugin_name = plugin._coverage_plugin_name
- self.warn("Disabling plug-in {!r} due to previous exception".format(plugin_name))
- plugin._coverage_enabled = False
- disposition.trace = False
-
- def cached_mapped_file(self, filename):
- """A locally cached version of file names mapped through file_mapper."""
- key = (type(filename), filename)
- try:
- return self.mapped_file_cache[key]
- except KeyError:
- return self.mapped_file_cache.setdefault(key, self.file_mapper(filename))
-
- def mapped_file_dict(self, d):
- """Return a dict like d, but with keys modified by file_mapper."""
- # The call to litems() ensures that the GIL protects the dictionary
- # iterator against concurrent modifications by tracers running
- # in other threads. We try three times in case of concurrent
- # access, hoping to get a clean copy.
- runtime_err = None
- for _ in range(3):
- try:
- items = litems(d)
- except RuntimeError as ex:
- runtime_err = ex
- else:
- break
- else:
- raise runtime_err
-
- if getattr(sys, 'is_standalone_binary', False):
- # filenames should stay relative to the arcadia root, because files may not exist
- return dict((k, v) for k, v in items if v)
-
- return dict((self.cached_mapped_file(k), v) for k, v in items if v)
-
- def plugin_was_disabled(self, plugin):
- """Record that `plugin` was disabled during the run."""
- self.disabled_plugins.add(plugin._coverage_plugin_name)
-
- def flush_data(self):
- """Save the collected data to our associated `CoverageData`.
-
- Data may have also been saved along the way. This forces the
- last of the data to be saved.
-
- Returns True if there was data to save, False if not.
- """
- if not self._activity():
- return False
-
- if self.branch:
- self.covdata.add_arcs(self.mapped_file_dict(self.data))
- else:
- self.covdata.add_lines(self.mapped_file_dict(self.data))
-
- file_tracers = {
- k: v for k, v in self.file_tracers.items()
- if v not in self.disabled_plugins
- }
- self.covdata.add_file_tracers(self.mapped_file_dict(file_tracers))
-
- self._clear_data()
- return True
diff --git a/contrib/python/coverage/py2/coverage/config.py b/contrib/python/coverage/py2/coverage/config.py
deleted file mode 100644
index ceb7201b65..0000000000
--- a/contrib/python/coverage/py2/coverage/config.py
+++ /dev/null
@@ -1,605 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Config file for coverage.py"""
-
-import collections
-import copy
-import os
-import os.path
-import re
-import sys
-
-from coverage import env
-from coverage.backward import configparser, iitems, string_class
-from coverage.misc import contract, CoverageException, isolate_module
-from coverage.misc import substitute_variables
-
-from coverage.tomlconfig import TomlConfigParser, TomlDecodeError
-
-os = isolate_module(os)
-
-
-class HandyConfigParser(configparser.RawConfigParser):
- """Our specialization of ConfigParser."""
-
- def __init__(self, our_file):
- """Create the HandyConfigParser.
-
- `our_file` is True if this config file is specifically for coverage,
- False if we are examining another config file (tox.ini, setup.cfg)
- for possible settings.
- """
-
- configparser.RawConfigParser.__init__(self)
- self.section_prefixes = ["coverage:"]
- if our_file:
- self.section_prefixes.append("")
-
- def read(self, filenames, encoding=None):
- """Read a file name as UTF-8 configuration data."""
- kwargs = {}
- if env.PYVERSION >= (3, 2):
- kwargs['encoding'] = encoding or "utf-8"
- return configparser.RawConfigParser.read(self, filenames, **kwargs)
-
- def has_option(self, section, option):
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- has = configparser.RawConfigParser.has_option(self, real_section, option)
- if has:
- return has
- return False
-
- def has_section(self, section):
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- has = configparser.RawConfigParser.has_section(self, real_section)
- if has:
- return real_section
- return False
-
- def options(self, section):
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- if configparser.RawConfigParser.has_section(self, real_section):
- return configparser.RawConfigParser.options(self, real_section)
- raise configparser.NoSectionError(section)
-
- def get_section(self, section):
- """Get the contents of a section, as a dictionary."""
- d = {}
- for opt in self.options(section):
- d[opt] = self.get(section, opt)
- return d
-
- def get(self, section, option, *args, **kwargs):
- """Get a value, replacing environment variables also.
-
- The arguments are the same as `RawConfigParser.get`, but in the found
- value, ``$WORD`` or ``${WORD}`` are replaced by the value of the
- environment variable ``WORD``.
-
- Returns the finished value.
-
- """
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- if configparser.RawConfigParser.has_option(self, real_section, option):
- break
- else:
- raise configparser.NoOptionError(option, section)
-
- v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
- v = substitute_variables(v, os.environ)
- return v
-
- def getlist(self, section, option):
- """Read a list of strings.
-
- The value of `section` and `option` is treated as a comma- and newline-
- separated list of strings. Each value is stripped of whitespace.
-
- Returns the list of strings.
-
- """
- value_list = self.get(section, option)
- values = []
- for value_line in value_list.split('\n'):
- for value in value_line.split(','):
- value = value.strip()
- if value:
- values.append(value)
- return values
-
- def getregexlist(self, section, option):
- """Read a list of full-line regexes.
-
- The value of `section` and `option` is treated as a newline-separated
- list of regexes. Each value is stripped of whitespace.
-
- Returns the list of strings.
-
- """
- line_list = self.get(section, option)
- value_list = []
- for value in line_list.splitlines():
- value = value.strip()
- try:
- re.compile(value)
- except re.error as e:
- raise CoverageException(
- "Invalid [%s].%s value %r: %s" % (section, option, value, e)
- )
- if value:
- value_list.append(value)
- return value_list
-
-
-# The default line exclusion regexes.
-DEFAULT_EXCLUDE = [
- r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)',
-]
-
-# The default partial branch regexes, to be modified by the user.
-DEFAULT_PARTIAL = [
- r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)',
-]
-
-# The default partial branch regexes, based on Python semantics.
-# These are any Python branching constructs that can't actually execute all
-# their branches.
-DEFAULT_PARTIAL_ALWAYS = [
- 'while (True|1|False|0):',
- 'if (True|1|False|0):',
-]
-
-
-class CoverageConfig(object):
- """Coverage.py configuration.
-
- The attributes of this class are the various settings that control the
- operation of coverage.py.
-
- """
- # pylint: disable=too-many-instance-attributes
-
- def __init__(self):
- """Initialize the configuration attributes to their defaults."""
- # Metadata about the config.
- # We tried to read these config files.
- self.attempted_config_files = []
- # We did read these config files, but maybe didn't find any content for us.
- self.config_files_read = []
- # The file that gave us our configuration.
- self.config_file = None
- self._config_contents = None
-
- # Defaults for [run] and [report]
- self._include = None
- self._omit = None
-
- # Defaults for [run]
- self.branch = False
- self.command_line = None
- self.concurrency = None
- self.context = None
- self.cover_pylib = False
- self.data_file = ".coverage"
- self.debug = []
- self.disable_warnings = []
- self.dynamic_context = None
- self.note = None
- self.parallel = False
- self.plugins = []
- self.relative_files = False
- self.run_include = None
- self.run_omit = None
- self.source = None
- self.source_pkgs = []
- self.timid = False
- self._crash = None
-
- # Defaults for [report]
- self.exclude_list = DEFAULT_EXCLUDE[:]
- self.fail_under = 0.0
- self.ignore_errors = False
- self.report_include = None
- self.report_omit = None
- self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
- self.partial_list = DEFAULT_PARTIAL[:]
- self.precision = 0
- self.report_contexts = None
- self.show_missing = False
- self.skip_covered = False
- self.skip_empty = False
- self.sort = None
-
- # Defaults for [html]
- self.extra_css = None
- self.html_dir = "htmlcov"
- self.html_skip_covered = None
- self.html_skip_empty = None
- self.html_title = "Coverage report"
- self.show_contexts = False
-
- # Defaults for [xml]
- self.xml_output = "coverage.xml"
- self.xml_package_depth = 99
-
- # Defaults for [json]
- self.json_output = "coverage.json"
- self.json_pretty_print = False
- self.json_show_contexts = False
-
- # Defaults for [paths]
- self.paths = collections.OrderedDict()
-
- # Options for plugins
- self.plugin_options = {}
- self.suppress_plugin_errors = True
-
- MUST_BE_LIST = [
- "debug", "concurrency", "plugins",
- "report_omit", "report_include",
- "run_omit", "run_include",
- ]
-
- def from_args(self, **kwargs):
- """Read config values from `kwargs`."""
- for k, v in iitems(kwargs):
- if v is not None:
- if k in self.MUST_BE_LIST and isinstance(v, string_class):
- v = [v]
- setattr(self, k, v)
-
- def from_resource(self, resource_name):
- assert getattr(sys, 'is_standalone_binary', False), 'You have used method by mistake in script, not binary'
- cp, self._config_contents = _load_config_from_resource(resource_name)
- return self._parse_config(cp, resource_name, True)
-
- @contract(filename=str)
- def from_file(self, filename, our_file):
- """Read configuration from a .rc file.
-
- `filename` is a file name to read.
-
- `our_file` is True if this config file is specifically for coverage,
- False if we are examining another config file (tox.ini, setup.cfg)
- for possible settings.
-
- Returns True or False, whether the file could be read, and it had some
- coverage.py settings in it.
-
- """
- _, ext = os.path.splitext(filename)
- if ext == '.toml':
- cp = TomlConfigParser(our_file)
- else:
- cp = HandyConfigParser(our_file)
-
- self.attempted_config_files.append(filename)
-
- try:
- files_read = cp.read(filename)
- except (configparser.Error, TomlDecodeError) as err:
- raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
- if not files_read:
- return False
-
- self.config_files_read.extend(map(os.path.abspath, files_read))
-
- return self._parse_config(cp, filename, our_file)
-
- def _parse_config(self, cp, filename, our_file):
- any_set = False
- try:
- for option_spec in self.CONFIG_FILE_OPTIONS:
- was_set = self._set_attr_from_config_option(cp, *option_spec)
- if was_set:
- any_set = True
- except ValueError as err:
- raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
-
- # Check that there are no unrecognized options.
- all_options = collections.defaultdict(set)
- for option_spec in self.CONFIG_FILE_OPTIONS:
- section, option = option_spec[1].split(":")
- all_options[section].add(option)
-
- for section, options in iitems(all_options):
- real_section = cp.has_section(section)
- if real_section:
- for unknown in set(cp.options(section)) - options:
- raise CoverageException(
- "Unrecognized option '[%s] %s=' in config file %s" % (
- real_section, unknown, filename
- )
- )
-
- # [paths] is special
- if cp.has_section('paths'):
- for option in cp.options('paths'):
- self.paths[option] = cp.getlist('paths', option)
- any_set = True
-
- # plugins can have options
- for plugin in self.plugins:
- if cp.has_section(plugin):
- self.plugin_options[plugin] = cp.get_section(plugin)
- any_set = True
-
- # Was this file used as a config file? If it's specifically our file,
- # then it was used. If we're piggybacking on someone else's file,
- # then it was only used if we found some settings in it.
- if our_file:
- used = True
- else:
- used = any_set
-
- if used:
- self.config_file = os.path.abspath(filename)
- if not getattr(sys, 'is_standalone_binary', False):
- with open(filename, "rb") as f:
- self._config_contents = f.read()
-
- return used
-
- def copy(self):
- """Return a copy of the configuration."""
- return copy.deepcopy(self)
-
- CONFIG_FILE_OPTIONS = [
- # These are *args for _set_attr_from_config_option:
- # (attr, where, type_="")
- #
- # attr is the attribute to set on the CoverageConfig object.
- # where is the section:name to read from the configuration file.
- # type_ is the optional type to apply, by using .getTYPE to read the
- # configuration value from the file.
-
- # [run]
- ('branch', 'run:branch', 'boolean'),
- ('command_line', 'run:command_line'),
- ('concurrency', 'run:concurrency', 'list'),
- ('context', 'run:context'),
- ('cover_pylib', 'run:cover_pylib', 'boolean'),
- ('data_file', 'run:data_file'),
- ('debug', 'run:debug', 'list'),
- ('disable_warnings', 'run:disable_warnings', 'list'),
- ('dynamic_context', 'run:dynamic_context'),
- ('note', 'run:note'),
- ('parallel', 'run:parallel', 'boolean'),
- ('plugins', 'run:plugins', 'list'),
- ('relative_files', 'run:relative_files', 'boolean'),
- ('run_include', 'run:include', 'list'),
- ('run_omit', 'run:omit', 'list'),
- ('source', 'run:source', 'list'),
- ('source_pkgs', 'run:source_pkgs', 'list'),
- ('timid', 'run:timid', 'boolean'),
- ('_crash', 'run:_crash'),
- ('suppress_plugin_errors', 'run:suppress_plugin_errors', 'boolean'),
-
- # [report]
- ('exclude_list', 'report:exclude_lines', 'regexlist'),
- ('fail_under', 'report:fail_under', 'float'),
- ('ignore_errors', 'report:ignore_errors', 'boolean'),
- ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
- ('partial_list', 'report:partial_branches', 'regexlist'),
- ('precision', 'report:precision', 'int'),
- ('report_contexts', 'report:contexts', 'list'),
- ('report_include', 'report:include', 'list'),
- ('report_omit', 'report:omit', 'list'),
- ('show_missing', 'report:show_missing', 'boolean'),
- ('skip_covered', 'report:skip_covered', 'boolean'),
- ('skip_empty', 'report:skip_empty', 'boolean'),
- ('sort', 'report:sort'),
-
- # [html]
- ('extra_css', 'html:extra_css'),
- ('html_dir', 'html:directory'),
- ('html_skip_covered', 'html:skip_covered', 'boolean'),
- ('html_skip_empty', 'html:skip_empty', 'boolean'),
- ('html_title', 'html:title'),
- ('show_contexts', 'html:show_contexts', 'boolean'),
-
- # [xml]
- ('xml_output', 'xml:output'),
- ('xml_package_depth', 'xml:package_depth', 'int'),
-
- # [json]
- ('json_output', 'json:output'),
- ('json_pretty_print', 'json:pretty_print', 'boolean'),
- ('json_show_contexts', 'json:show_contexts', 'boolean'),
- ]
-
- def _set_attr_from_config_option(self, cp, attr, where, type_=''):
- """Set an attribute on self if it exists in the ConfigParser.
-
- Returns True if the attribute was set.
-
- """
- section, option = where.split(":")
- if cp.has_option(section, option):
- method = getattr(cp, 'get' + type_)
- setattr(self, attr, method(section, option))
- return True
- return False
-
- def get_plugin_options(self, plugin):
- """Get a dictionary of options for the plugin named `plugin`."""
- return self.plugin_options.get(plugin, {})
-
- def set_option(self, option_name, value):
- """Set an option in the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with `"run:branch"`.
-
- `value` is the new value for the option.
-
- """
- # Special-cased options.
- if option_name == "paths":
- self.paths = value
- return
-
- # Check all the hard-coded options.
- for option_spec in self.CONFIG_FILE_OPTIONS:
- attr, where = option_spec[:2]
- if where == option_name:
- setattr(self, attr, value)
- return
-
- # See if it's a plugin option.
- plugin_name, _, key = option_name.partition(":")
- if key and plugin_name in self.plugins:
- self.plugin_options.setdefault(plugin_name, {})[key] = value
- return
-
- # If we get here, we didn't find the option.
- raise CoverageException("No such option: %r" % option_name)
-
- def get_option(self, option_name):
- """Get an option from the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with `"run:branch"`.
-
- Returns the value of the option.
-
- """
- # Special-cased options.
- if option_name == "paths":
- return self.paths
-
- # Check all the hard-coded options.
- for option_spec in self.CONFIG_FILE_OPTIONS:
- attr, where = option_spec[:2]
- if where == option_name:
- return getattr(self, attr)
-
- # See if it's a plugin option.
- plugin_name, _, key = option_name.partition(":")
- if key and plugin_name in self.plugins:
- return self.plugin_options.get(plugin_name, {}).get(key)
-
- # If we get here, we didn't find the option.
- raise CoverageException("No such option: %r" % option_name)
-
- def post_process_file(self, path):
- """Make final adjustments to a file path to make it usable."""
- return os.path.expanduser(path)
-
- def post_process(self):
- """Make final adjustments to settings to make them usable."""
- self.data_file = self.post_process_file(self.data_file)
- self.html_dir = self.post_process_file(self.html_dir)
- self.xml_output = self.post_process_file(self.xml_output)
- self.paths = collections.OrderedDict(
- (k, [self.post_process_file(f) for f in v])
- for k, v in self.paths.items()
- )
-
-
-def config_files_to_try(config_file):
- """What config files should we try to read?
-
- Returns a list of tuples:
- (filename, is_our_file, was_file_specified)
- """
-
- # Some API users were specifying ".coveragerc" to mean the same as
- # True, so make it so.
- if config_file == ".coveragerc":
- config_file = True
- specified_file = (config_file is not True)
- if not specified_file:
- # No file was specified. Check COVERAGE_RCFILE.
- config_file = os.environ.get('COVERAGE_RCFILE')
- if config_file:
- specified_file = True
- if not specified_file:
- # Still no file specified. Default to .coveragerc
- config_file = ".coveragerc"
- files_to_try = [
- (config_file, True, specified_file),
- ("setup.cfg", False, False),
- ("tox.ini", False, False),
- ("pyproject.toml", False, False),
- ]
- return files_to_try
-
-
-def read_coverage_config(config_file, **kwargs):
- """Read the coverage.py configuration.
-
- Arguments:
- config_file: a boolean or string, see the `Coverage` class for the
- tricky details.
- all others: keyword arguments from the `Coverage` class, used for
- setting values in the configuration.
-
- Returns:
- config:
- config is a CoverageConfig object read from the appropriate
- configuration file.
-
- """
- # Build the configuration from a number of sources:
- # 1) defaults:
- config = CoverageConfig()
-
- # 1.1 built-in config
- if getattr(sys, 'is_standalone_binary', False):
- config.from_resource("/coverage_plugins/coveragerc.txt")
-
- # 2) from a file:
- if config_file:
- files_to_try = config_files_to_try(config_file)
-
- for fname, our_file, specified_file in files_to_try:
- if getattr(sys, 'is_standalone_binary', False) and fname == "/coverage_plugins/coveragerc.txt":
- continue
- config_read = config.from_file(fname, our_file=our_file)
- if config_read:
- break
- if specified_file:
- raise CoverageException("Couldn't read '%s' as a config file" % fname)
-
- # $set_env.py: COVERAGE_DEBUG - Options for --debug.
- # 3) from environment variables:
- env_data_file = os.environ.get('COVERAGE_FILE')
- if env_data_file:
- config.data_file = env_data_file
- debugs = os.environ.get('COVERAGE_DEBUG')
- if debugs:
- config.debug.extend(d.strip() for d in debugs.split(","))
-
- # 4) from constructor arguments:
- config.from_args(**kwargs)
-
- # Once all the config has been collected, there's a little post-processing
- # to do.
- config.post_process()
-
- return config
-
-
-def _load_config_from_resource(resource_name):
- from io import StringIO
- from library.python import resource
-
- config_data = resource.find(resource_name)
- if config_data is None:
- raise IOError("No such resource: " + resource_name)
-
- config_data = config_data.decode('utf-8')
- cp = HandyConfigParser(True)
- try:
- cp.readfp(StringIO(config_data))
- except configparser.Error as err:
- raise CoverageException("Couldn't read config %s: %s" % (resource_name, err))
- return cp, config_data
diff --git a/contrib/python/coverage/py2/coverage/context.py b/contrib/python/coverage/py2/coverage/context.py
deleted file mode 100644
index ea13da21ed..0000000000
--- a/contrib/python/coverage/py2/coverage/context.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Determine contexts for coverage.py"""
-
-
-def combine_context_switchers(context_switchers):
- """Create a single context switcher from multiple switchers.
-
- `context_switchers` is a list of functions that take a frame as an
- argument and return a string to use as the new context label.
-
- Returns a function that composites `context_switchers` functions, or None
- if `context_switchers` is an empty list.
-
- When invoked, the combined switcher calls `context_switchers` one-by-one
- until a string is returned. The combined switcher returns None if all
- `context_switchers` return None.
- """
- if not context_switchers:
- return None
-
- if len(context_switchers) == 1:
- return context_switchers[0]
-
- def should_start_context(frame):
- """The combiner for multiple context switchers."""
- for switcher in context_switchers:
- new_context = switcher(frame)
- if new_context is not None:
- return new_context
- return None
-
- return should_start_context
-
-
-def should_start_context_test_function(frame):
- """Is this frame calling a test_* function?"""
- co_name = frame.f_code.co_name
- if co_name.startswith("test") or co_name == "runTest":
- return qualname_from_frame(frame)
- return None
-
-
-def qualname_from_frame(frame):
- """Get a qualified name for the code running in `frame`."""
- co = frame.f_code
- fname = co.co_name
- method = None
- if co.co_argcount and co.co_varnames[0] == "self":
- self = frame.f_locals["self"]
- method = getattr(self, fname, None)
-
- if method is None:
- func = frame.f_globals.get(fname)
- if func is None:
- return None
- return func.__module__ + '.' + fname
-
- func = getattr(method, '__func__', None)
- if func is None:
- cls = self.__class__
- return cls.__module__ + '.' + cls.__name__ + "." + fname
-
- if hasattr(func, '__qualname__'):
- qname = func.__module__ + '.' + func.__qualname__
- else:
- for cls in getattr(self.__class__, '__mro__', ()):
- f = cls.__dict__.get(fname, None)
- if f is None:
- continue
- if f is func:
- qname = cls.__module__ + '.' + cls.__name__ + "." + fname
- break
- else:
- # Support for old-style classes.
- def mro(bases):
- for base in bases:
- f = base.__dict__.get(fname, None)
- if f is func:
- return base.__module__ + '.' + base.__name__ + "." + fname
- for base in bases:
- qname = mro(base.__bases__)
- if qname is not None:
- return qname
- return None
- qname = mro([self.__class__])
- if qname is None:
- qname = func.__module__ + '.' + fname
-
- return qname
diff --git a/contrib/python/coverage/py2/coverage/control.py b/contrib/python/coverage/py2/coverage/control.py
deleted file mode 100644
index 605b50c26b..0000000000
--- a/contrib/python/coverage/py2/coverage/control.py
+++ /dev/null
@@ -1,1162 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Core control stuff for coverage.py."""
-
-import atexit
-import collections
-import contextlib
-import os
-import os.path
-import platform
-import sys
-import time
-import json
-
-from coverage import env
-from coverage.annotate import AnnotateReporter
-from coverage.backward import string_class, iitems
-from coverage.collector import Collector, CTracer
-from coverage.config import read_coverage_config
-from coverage.context import should_start_context_test_function, combine_context_switchers
-from coverage.data import CoverageData, combine_parallel_data
-from coverage.debug import DebugControl, short_stack, write_formatted_info
-from coverage.disposition import disposition_debug_msg
-from coverage.files import PathAliases, abs_file, canonical_filename, relative_filename, set_relative_directory
-from coverage.html import HtmlReporter
-from coverage.inorout import InOrOut
-from coverage.jsonreport import JsonReporter
-from coverage.misc import CoverageException, bool_or_none, join_regex
-from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module
-from coverage.plugin import FileReporter
-from coverage.plugin_support import Plugins
-from coverage.python import PythonFileReporter
-from coverage.report import render_report
-from coverage.results import Analysis, Numbers
-from coverage.summary import SummaryReporter
-from coverage.xmlreport import XmlReporter
-
-try:
- from coverage.multiproc import patch_multiprocessing
-except ImportError: # pragma: only jython
- # Jython has no multiprocessing module.
- patch_multiprocessing = None
-
-os = isolate_module(os)
-
-@contextlib.contextmanager
-def override_config(cov, **kwargs):
- """Temporarily tweak the configuration of `cov`.
-
- The arguments are applied to `cov.config` with the `from_args` method.
- At the end of the with-statement, the old configuration is restored.
- """
- original_config = cov.config
- cov.config = cov.config.copy()
- try:
- cov.config.from_args(**kwargs)
- yield
- finally:
- cov.config = original_config
-
-
-_DEFAULT_DATAFILE = DefaultValue("MISSING")
-
-class Coverage(object):
- """Programmatic access to coverage.py.
-
- To use::
-
- from coverage import Coverage
-
- cov = Coverage()
- cov.start()
- #.. call your code ..
- cov.stop()
- cov.html_report(directory='covhtml')
-
- Note: in keeping with Python custom, names starting with underscore are
- not part of the public API. They might stop working at any point. Please
- limit yourself to documented methods to avoid problems.
-
- """
-
- # The stack of started Coverage instances.
- _instances = []
-
- @classmethod
- def current(cls):
- """Get the latest started `Coverage` instance, if any.
-
- Returns: a `Coverage` instance, or None.
-
- .. versionadded:: 5.0
-
- """
- if cls._instances:
- return cls._instances[-1]
- else:
- return None
-
- def __init__(
- self, data_file=_DEFAULT_DATAFILE, data_suffix=None, cover_pylib=None,
- auto_data=False, timid=None, branch=None, config_file=True,
- source=None, source_pkgs=None, omit=None, include=None, debug=None,
- concurrency=None, check_preimported=False, context=None,
- ): # pylint: disable=too-many-arguments
- """
- Many of these arguments duplicate and override values that can be
- provided in a configuration file. Parameters that are missing here
- will use values from the config file.
-
- `data_file` is the base name of the data file to use. The config value
- defaults to ".coverage". None can be provided to prevent writing a data
- file. `data_suffix` is appended (with a dot) to `data_file` to create
- the final file name. If `data_suffix` is simply True, then a suffix is
- created with the machine and process identity included.
-
- `cover_pylib` is a boolean determining whether Python code installed
- with the Python interpreter is measured. This includes the Python
- standard library and any packages installed with the interpreter.
-
- If `auto_data` is true, then any existing data file will be read when
- coverage measurement starts, and data will be saved automatically when
- measurement stops.
-
- If `timid` is true, then a slower and simpler trace function will be
- used. This is important for some environments where manipulation of
- tracing functions breaks the faster trace function.
-
- If `branch` is true, then branch coverage will be measured in addition
- to the usual statement coverage.
-
- `config_file` determines what configuration file to read:
-
- * If it is ".coveragerc", it is interpreted as if it were True,
- for backward compatibility.
-
- * If it is a string, it is the name of the file to read. If the
- file can't be read, it is an error.
-
- * If it is True, then a few standard files names are tried
- (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for
- these files to not be found.
-
- * If it is False, then no configuration file is read.
-
- `source` is a list of file paths or package names. Only code located
- in the trees indicated by the file paths or package names will be
- measured.
-
- `source_pkgs` is a list of package names. It works the same as
- `source`, but can be used to name packages where the name can also be
- interpreted as a file path.
-
- `include` and `omit` are lists of file name patterns. Files that match
- `include` will be measured, files that match `omit` will not. Each
- will also accept a single string argument.
-
- `debug` is a list of strings indicating what debugging information is
- desired.
-
- `concurrency` is a string indicating the concurrency library being used
- in the measured code. Without this, coverage.py will get incorrect
- results if these libraries are in use. Valid strings are "greenlet",
- "eventlet", "gevent", "multiprocessing", or "thread" (the default).
- This can also be a list of these strings.
-
- If `check_preimported` is true, then when coverage is started, the
- already-imported files will be checked to see if they should be
- measured by coverage. Importing measured files before coverage is
- started can mean that code is missed.
-
- `context` is a string to use as the :ref:`static context
- <static_contexts>` label for collected data.
-
- .. versionadded:: 4.0
- The `concurrency` parameter.
-
- .. versionadded:: 4.2
- The `concurrency` parameter can now be a list of strings.
-
- .. versionadded:: 5.0
- The `check_preimported` and `context` parameters.
-
- .. versionadded:: 5.3
- The `source_pkgs` parameter.
-
- """
- # data_file=None means no disk file at all. data_file missing means
- # use the value from the config file.
- self._no_disk = data_file is None
- if data_file is _DEFAULT_DATAFILE:
- data_file = None
-
- # Build our configuration from a number of sources.
- self.config = read_coverage_config(
- config_file=config_file,
- data_file=data_file, cover_pylib=cover_pylib, timid=timid,
- branch=branch, parallel=bool_or_none(data_suffix),
- source=source, source_pkgs=source_pkgs, run_omit=omit, run_include=include, debug=debug,
- report_omit=omit, report_include=include,
- concurrency=concurrency, context=context,
- )
-
- # This is injectable by tests.
- self._debug_file = None
-
- self._auto_load = self._auto_save = auto_data
- self._data_suffix_specified = data_suffix
-
- # Is it ok for no data to be collected?
- self._warn_no_data = True
- self._warn_unimported_source = True
- self._warn_preimported_source = check_preimported
- self._no_warn_slugs = None
-
- # A record of all the warnings that have been issued.
- self._warnings = []
-
- # Other instance attributes, set later.
- self._data = self._collector = None
- self._plugins = None
- self._inorout = None
- self._data_suffix = self._run_suffix = None
- self._exclude_re = None
- self._debug = None
- self._file_mapper = None
-
- # State machine variables:
- # Have we initialized everything?
- self._inited = False
- self._inited_for_start = False
- # Have we started collecting and not stopped it?
- self._started = False
- # Should we write the debug output?
- self._should_write_debug = True
-
- # If we have sub-process measurement happening automatically, then we
- # want any explicit creation of a Coverage object to mean, this process
- # is already coverage-aware, so don't auto-measure it. By now, the
- # auto-creation of a Coverage object has already happened. But we can
- # find it and tell it not to save its data.
- if not env.METACOV:
- _prevent_sub_process_measurement()
-
- # Store constructor args to reproduce Coverage object in a subprocess created via multiprocessing.Process
- self._dumped_args = json.dumps(dict(
- data_file=data_file, data_suffix=data_suffix, cover_pylib=cover_pylib,
- auto_data=auto_data, timid=timid, branch=branch, config_file=config_file,
- source=source, omit=omit, include=include, debug=debug,
- concurrency=concurrency
- ))
-
- def _init(self):
- """Set all the initial state.
-
- This is called by the public methods to initialize state. This lets us
- construct a :class:`Coverage` object, then tweak its state before this
- function is called.
-
- """
- if self._inited:
- return
-
- self._inited = True
-
- # Create and configure the debugging controller. COVERAGE_DEBUG_FILE
- # is an environment variable, the name of a file to append debug logs
- # to.
- self._debug = DebugControl(self.config.debug, self._debug_file)
-
- if "multiprocessing" in (self.config.concurrency or ()):
- # Multi-processing uses parallel for the subprocesses, so also use
- # it for the main process.
- self.config.parallel = True
-
- # _exclude_re is a dict that maps exclusion list names to compiled regexes.
- self._exclude_re = {}
-
- set_relative_directory()
-
- if getattr(sys, 'is_standalone_binary', False):
- self._file_mapper = canonical_filename
- else:
- self._file_mapper = relative_filename if self.config.relative_files else abs_file
-
- # Load plugins
- self._plugins = Plugins.load_plugins(self.config.plugins, self.config, self._debug)
-
- # Run configuring plugins.
- for plugin in self._plugins.configurers:
- # We need an object with set_option and get_option. Either self or
- # self.config will do. Choosing randomly stops people from doing
- # other things with those objects, against the public API. Yes,
- # this is a bit childish. :)
- plugin.configure([self, self.config][int(time.time()) % 2])
-
- def _post_init(self):
- """Stuff to do after everything is initialized."""
- if self._should_write_debug:
- self._should_write_debug = False
- self._write_startup_debug()
-
- # '[run] _crash' will raise an exception if the value is close by in
- # the call stack, for testing error handling.
- if self.config._crash and self.config._crash in short_stack(limit=4):
- raise Exception("Crashing because called by {}".format(self.config._crash))
-
- def _write_startup_debug(self):
- """Write out debug info at startup if needed."""
- wrote_any = False
- with self._debug.without_callers():
- if self._debug.should('config'):
- config_info = sorted(self.config.__dict__.items())
- config_info = [(k, v) for k, v in config_info if not k.startswith('_')]
- write_formatted_info(self._debug, "config", config_info)
- wrote_any = True
-
- if self._debug.should('sys'):
- write_formatted_info(self._debug, "sys", self.sys_info())
- for plugin in self._plugins:
- header = "sys: " + plugin._coverage_plugin_name
- info = plugin.sys_info()
- write_formatted_info(self._debug, header, info)
- wrote_any = True
-
- if wrote_any:
- write_formatted_info(self._debug, "end", ())
-
- def _should_trace(self, filename, frame):
- """Decide whether to trace execution in `filename`.
-
- Calls `_should_trace_internal`, and returns the FileDisposition.
-
- """
- disp = self._inorout.should_trace(filename, frame)
- if self._debug.should('trace'):
- self._debug.write(disposition_debug_msg(disp))
- return disp
-
- def _check_include_omit_etc(self, filename, frame):
- """Check a file name against the include/omit/etc, rules, verbosely.
-
- Returns a boolean: True if the file should be traced, False if not.
-
- """
- reason = self._inorout.check_include_omit_etc(filename, frame)
- if self._debug.should('trace'):
- if not reason:
- msg = "Including %r" % (filename,)
- else:
- msg = "Not including %r: %s" % (filename, reason)
- self._debug.write(msg)
-
- return not reason
-
- def _warn(self, msg, slug=None, once=False):
- """Use `msg` as a warning.
-
- For warning suppression, use `slug` as the shorthand.
-
- If `once` is true, only show this warning once (determined by the
- slug.)
-
- """
- if self._no_warn_slugs is None:
- self._no_warn_slugs = list(self.config.disable_warnings)
-
- if slug in self._no_warn_slugs:
- # Don't issue the warning
- return
-
- self._warnings.append(msg)
- if slug:
- msg = "%s (%s)" % (msg, slug)
- if self._debug.should('pid'):
- msg = "[%d] %s" % (os.getpid(), msg)
- sys.stderr.write("Coverage.py warning: %s\n" % msg)
-
- if once:
- self._no_warn_slugs.append(slug)
-
- def get_option(self, option_name):
- """Get an option from the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with `"run:branch"`.
-
- Returns the value of the option. The type depends on the option
- selected.
-
- As a special case, an `option_name` of ``"paths"`` will return an
- OrderedDict with the entire ``[paths]`` section value.
-
- .. versionadded:: 4.0
-
- """
- return self.config.get_option(option_name)
-
- def set_option(self, option_name, value):
- """Set an option in the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with ``"run:branch"``.
-
- `value` is the new value for the option. This should be an
- appropriate Python value. For example, use True for booleans, not the
- string ``"True"``.
-
- As an example, calling::
-
- cov.set_option("run:branch", True)
-
- has the same effect as this configuration file::
-
- [run]
- branch = True
-
- As a special case, an `option_name` of ``"paths"`` will replace the
- entire ``[paths]`` section. The value should be an OrderedDict.
-
- .. versionadded:: 4.0
-
- """
- self.config.set_option(option_name, value)
-
- def load(self):
- """Load previously-collected coverage data from the data file."""
- self._init()
- if self._collector:
- self._collector.reset()
- should_skip = self.config.parallel and not os.path.exists(self.config.data_file)
- if not should_skip:
- self._init_data(suffix=None)
- self._post_init()
- if not should_skip:
- self._data.read()
-
- def _init_for_start(self):
- """Initialization for start()"""
- # Construct the collector.
- concurrency = self.config.concurrency or ()
- if "multiprocessing" in concurrency:
- if not patch_multiprocessing:
- raise CoverageException( # pragma: only jython
- "multiprocessing is not supported on this Python"
- )
- patch_multiprocessing(rcfile=self.config.config_file, coverage_args=self._dumped_args)
-
- dycon = self.config.dynamic_context
- if not dycon or dycon == "none":
- context_switchers = []
- elif dycon == "test_function":
- context_switchers = [should_start_context_test_function]
- else:
- raise CoverageException(
- "Don't understand dynamic_context setting: {!r}".format(dycon)
- )
-
- context_switchers.extend(
- plugin.dynamic_context for plugin in self._plugins.context_switchers
- )
-
- should_start_context = combine_context_switchers(context_switchers)
-
- self._collector = Collector(
- should_trace=self._should_trace,
- check_include=self._check_include_omit_etc,
- should_start_context=should_start_context,
- file_mapper=self._file_mapper,
- timid=self.config.timid,
- branch=self.config.branch,
- warn=self._warn,
- concurrency=concurrency,
- )
-
- suffix = self._data_suffix_specified
- if suffix or self.config.parallel:
- if not isinstance(suffix, string_class):
- # if data_suffix=True, use .machinename.pid.random
- suffix = True
- else:
- suffix = None
-
- self._init_data(suffix)
-
- self._collector.use_data(self._data, self.config.context)
-
- # Early warning if we aren't going to be able to support plugins.
- if self._plugins.file_tracers and not self._collector.supports_plugins:
- self._warn(
- "Plugin file tracers (%s) aren't supported with %s" % (
- ", ".join(
- plugin._coverage_plugin_name
- for plugin in self._plugins.file_tracers
- ),
- self._collector.tracer_name(),
- )
- )
- for plugin in self._plugins.file_tracers:
- plugin._coverage_enabled = False
-
- # Create the file classifying substructure.
- self._inorout = InOrOut(
- warn=self._warn,
- debug=(self._debug if self._debug.should('trace') else None),
- )
- self._inorout.configure(self.config)
- self._inorout.plugins = self._plugins
- self._inorout.disp_class = self._collector.file_disposition_class
-
- # It's useful to write debug info after initing for start.
- self._should_write_debug = True
-
- atexit.register(self._atexit)
-
- def _init_data(self, suffix):
- """Create a data file if we don't have one yet."""
- if self._data is None:
- # Create the data file. We do this at construction time so that the
- # data file will be written into the directory where the process
- # started rather than wherever the process eventually chdir'd to.
- ensure_dir_for_file(self.config.data_file)
- self._data = CoverageData(
- basename=self.config.data_file,
- suffix=suffix,
- warn=self._warn,
- debug=self._debug,
- no_disk=self._no_disk,
- )
-
- def start(self):
- """Start measuring code coverage.
-
- Coverage measurement only occurs in functions called after
- :meth:`start` is invoked. Statements in the same scope as
- :meth:`start` won't be measured.
-
- Once you invoke :meth:`start`, you must also call :meth:`stop`
- eventually, or your process might not shut down cleanly.
-
- """
- self._init()
- if not self._inited_for_start:
- self._inited_for_start = True
- self._init_for_start()
- self._post_init()
-
- # Issue warnings for possible problems.
- self._inorout.warn_conflicting_settings()
-
- # See if we think some code that would eventually be measured has
- # already been imported.
- if self._warn_preimported_source:
- self._inorout.warn_already_imported_files()
-
- if self._auto_load:
- self.load()
-
- self._collector.start()
- self._started = True
- self._instances.append(self)
-
- def stop(self):
- """Stop measuring code coverage."""
- if self._instances:
- if self._instances[-1] is self:
- self._instances.pop()
- if self._started:
- self._collector.stop()
- self._started = False
-
- def _atexit(self):
- """Clean up on process shutdown."""
- if self._debug.should("process"):
- self._debug.write("atexit: pid: {}, instance: {!r}".format(os.getpid(), self))
- if self._started:
- self.stop()
- if self._auto_save:
- self.save()
-
- def erase(self):
- """Erase previously collected coverage data.
-
- This removes the in-memory data collected in this session as well as
- discarding the data file.
-
- """
- self._init()
- self._post_init()
- if self._collector:
- self._collector.reset()
- self._init_data(suffix=None)
- self._data.erase(parallel=self.config.parallel)
- self._data = None
- self._inited_for_start = False
-
- def switch_context(self, new_context):
- """Switch to a new dynamic context.
-
- `new_context` is a string to use as the :ref:`dynamic context
- <dynamic_contexts>` label for collected data. If a :ref:`static
- context <static_contexts>` is in use, the static and dynamic context
- labels will be joined together with a pipe character.
-
- Coverage collection must be started already.
-
- .. versionadded:: 5.0
-
- """
- if not self._started: # pragma: part started
- raise CoverageException(
- "Cannot switch context, coverage is not started"
- )
-
- if self._collector.should_start_context:
- self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True)
-
- self._collector.switch_context(new_context)
-
- def clear_exclude(self, which='exclude'):
- """Clear the exclude list."""
- self._init()
- setattr(self.config, which + "_list", [])
- self._exclude_regex_stale()
-
- def exclude(self, regex, which='exclude'):
- """Exclude source lines from execution consideration.
-
- A number of lists of regular expressions are maintained. Each list
- selects lines that are treated differently during reporting.
-
- `which` determines which list is modified. The "exclude" list selects
- lines that are not considered executable at all. The "partial" list
- indicates lines with branches that are not taken.
-
- `regex` is a regular expression. The regex is added to the specified
- list. If any of the regexes in the list is found in a line, the line
- is marked for special treatment during reporting.
-
- """
- self._init()
- excl_list = getattr(self.config, which + "_list")
- excl_list.append(regex)
- self._exclude_regex_stale()
-
- def _exclude_regex_stale(self):
- """Drop all the compiled exclusion regexes, a list was modified."""
- self._exclude_re.clear()
-
- def _exclude_regex(self, which):
- """Return a compiled regex for the given exclusion list."""
- if which not in self._exclude_re:
- excl_list = getattr(self.config, which + "_list")
- self._exclude_re[which] = join_regex(excl_list)
- return self._exclude_re[which]
-
- def get_exclude_list(self, which='exclude'):
- """Return a list of excluded regex patterns.
-
- `which` indicates which list is desired. See :meth:`exclude` for the
- lists that are available, and their meaning.
-
- """
- self._init()
- return getattr(self.config, which + "_list")
-
- def save(self):
- """Save the collected coverage data to the data file."""
- data = self.get_data()
- data.write()
-
- def combine(self, data_paths=None, strict=False, keep=False):
- """Combine together a number of similarly-named coverage data files.
-
- All coverage data files whose name starts with `data_file` (from the
- coverage() constructor) will be read, and combined together into the
- current measurements.
-
- `data_paths` is a list of files or directories from which data should
- be combined. If no list is passed, then the data files from the
- directory indicated by the current data file (probably the current
- directory) will be combined.
-
- If `strict` is true, then it is an error to attempt to combine when
- there are no data files to combine.
-
- If `keep` is true, then original input data files won't be deleted.
-
- .. versionadded:: 4.0
- The `data_paths` parameter.
-
- .. versionadded:: 4.3
- The `strict` parameter.
-
- .. versionadded: 5.5
- The `keep` parameter.
- """
- self._init()
- self._init_data(suffix=None)
- self._post_init()
- self.get_data()
-
- aliases = None
- if self.config.paths:
- aliases = PathAliases()
- for paths in self.config.paths.values():
- result = paths[0]
- for pattern in paths[1:]:
- aliases.add(pattern, result)
-
- combine_parallel_data(
- self._data,
- aliases=aliases,
- data_paths=data_paths,
- strict=strict,
- keep=keep,
- )
-
- def get_data(self):
- """Get the collected data.
-
- Also warn about various problems collecting data.
-
- Returns a :class:`coverage.CoverageData`, the collected coverage data.
-
- .. versionadded:: 4.0
-
- """
- self._init()
- self._init_data(suffix=None)
- self._post_init()
-
- for plugin in self._plugins:
- if not plugin._coverage_enabled:
- self._collector.plugin_was_disabled(plugin)
-
- if self._collector and self._collector.flush_data():
- self._post_save_work()
-
- return self._data
-
- def _post_save_work(self):
- """After saving data, look for warnings, post-work, etc.
-
- Warn about things that should have happened but didn't.
- Look for unexecuted files.
-
- """
- # If there are still entries in the source_pkgs_unmatched list,
- # then we never encountered those packages.
- if self._warn_unimported_source:
- self._inorout.warn_unimported_source()
-
- # Find out if we got any data.
- if not self._data and self._warn_no_data:
- self._warn("No data was collected.", slug="no-data-collected")
-
- # Touch all the files that could have executed, so that we can
- # mark completely unexecuted files as 0% covered.
- if self._data is not None:
- file_paths = collections.defaultdict(list)
- for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files():
- file_path = self._file_mapper(file_path)
- file_paths[plugin_name].append(file_path)
- for plugin_name, paths in file_paths.items():
- self._data.touch_files(paths, plugin_name)
-
- if self.config.note:
- self._warn("The '[run] note' setting is no longer supported.")
-
- # Backward compatibility with version 1.
- def analysis(self, morf):
- """Like `analysis2` but doesn't return excluded line numbers."""
- f, s, _, m, mf = self.analysis2(morf)
- return f, s, m, mf
-
- def analysis2(self, morf):
- """Analyze a module.
-
- `morf` is a module or a file name. It will be analyzed to determine
- its coverage statistics. The return value is a 5-tuple:
-
- * The file name for the module.
- * A list of line numbers of executable statements.
- * A list of line numbers of excluded statements.
- * A list of line numbers of statements not run (missing from
- execution).
- * A readable formatted string of the missing line numbers.
-
- The analysis uses the source file itself and the current measured
- coverage data.
-
- """
- analysis = self._analyze(morf)
- return (
- analysis.filename,
- sorted(analysis.statements),
- sorted(analysis.excluded),
- sorted(analysis.missing),
- analysis.missing_formatted(),
- )
-
- def _analyze(self, it):
- """Analyze a single morf or code unit.
-
- Returns an `Analysis` object.
-
- """
- # All reporting comes through here, so do reporting initialization.
- self._init()
- Numbers.set_precision(self.config.precision)
- self._post_init()
-
- data = self.get_data()
- if not isinstance(it, FileReporter):
- it = self._get_file_reporter(it)
-
- return Analysis(data, it, self._file_mapper)
-
- def _get_file_reporter(self, morf):
- """Get a FileReporter for a module or file name."""
- plugin = None
- file_reporter = "python"
-
- if isinstance(morf, string_class):
- if getattr(sys, 'is_standalone_binary', False):
- # Leave morf in canonical format - relative to the arcadia root
- mapped_morf = morf
- else:
- mapped_morf = self._file_mapper(morf)
- plugin_name = self._data.file_tracer(mapped_morf)
- if plugin_name:
- plugin = self._plugins.get(plugin_name)
-
- if plugin:
- file_reporter = plugin.file_reporter(mapped_morf)
- if file_reporter is None:
- raise CoverageException(
- "Plugin %r did not provide a file reporter for %r." % (
- plugin._coverage_plugin_name, morf
- )
- )
-
- if file_reporter == "python":
- file_reporter = PythonFileReporter(morf, self)
-
- return file_reporter
-
- def _get_file_reporters(self, morfs=None):
- """Get a list of FileReporters for a list of modules or file names.
-
- For each module or file name in `morfs`, find a FileReporter. Return
- the list of FileReporters.
-
- If `morfs` is a single module or file name, this returns a list of one
- FileReporter. If `morfs` is empty or None, then the list of all files
- measured is used to find the FileReporters.
-
- """
- if not morfs:
- morfs = self._data.measured_files()
-
- # Be sure we have a collection.
- if not isinstance(morfs, (list, tuple, set)):
- morfs = [morfs]
-
- file_reporters = [self._get_file_reporter(morf) for morf in morfs]
- return file_reporters
-
- def report(
- self, morfs=None, show_missing=None, ignore_errors=None,
- file=None, omit=None, include=None, skip_covered=None,
- contexts=None, skip_empty=None, precision=None, sort=None
- ):
- """Write a textual summary report to `file`.
-
- Each module in `morfs` is listed, with counts of statements, executed
- statements, missing statements, and a list of lines missed.
-
- If `show_missing` is true, then details of which lines or branches are
- missing will be included in the report. If `ignore_errors` is true,
- then a failure while reporting a single file will not stop the entire
- report.
-
- `file` is a file-like object, suitable for writing.
-
- `include` is a list of file name patterns. Files that match will be
- included in the report. Files matching `omit` will not be included in
- the report.
-
- If `skip_covered` is true, don't report on files with 100% coverage.
-
- If `skip_empty` is true, don't report on empty files (those that have
- no statements).
-
- `contexts` is a list of regular expressions. Only data from
- :ref:`dynamic contexts <dynamic_contexts>` that match one of those
- expressions (using :func:`re.search <python:re.search>`) will be
- included in the report.
-
- `precision` is the number of digits to display after the decimal
- point for percentages.
-
- All of the arguments default to the settings read from the
- :ref:`configuration file <config>`.
-
- Returns a float, the total percentage covered.
-
- .. versionadded:: 4.0
- The `skip_covered` parameter.
-
- .. versionadded:: 5.0
- The `contexts` and `skip_empty` parameters.
-
- .. versionadded:: 5.2
- The `precision` parameter.
-
- """
- with override_config(
- self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- show_missing=show_missing, skip_covered=skip_covered,
- report_contexts=contexts, skip_empty=skip_empty, precision=precision,
- sort=sort
- ):
- reporter = SummaryReporter(self)
- return reporter.report(morfs, outfile=file)
-
- def annotate(
- self, morfs=None, directory=None, ignore_errors=None,
- omit=None, include=None, contexts=None,
- ):
- """Annotate a list of modules.
-
- Each module in `morfs` is annotated. The source is written to a new
- file, named with a ",cover" suffix, with each line prefixed with a
- marker to indicate the coverage of the line. Covered lines have ">",
- excluded lines have "-", and missing lines have "!".
-
- See :meth:`report` for other arguments.
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit,
- report_include=include, report_contexts=contexts,
- ):
- reporter = AnnotateReporter(self)
- reporter.report(morfs, directory=directory)
-
- def html_report(
- self, morfs=None, directory=None, ignore_errors=None,
- omit=None, include=None, extra_css=None, title=None,
- skip_covered=None, show_contexts=None, contexts=None,
- skip_empty=None, precision=None,
- ):
- """Generate an HTML report.
-
- The HTML is written to `directory`. The file "index.html" is the
- overview starting point, with links to more detailed pages for
- individual modules.
-
- `extra_css` is a path to a file of other CSS to apply on the page.
- It will be copied into the HTML directory.
-
- `title` is a text string (not HTML) to use as the title of the HTML
- report.
-
- See :meth:`report` for other arguments.
-
- Returns a float, the total percentage covered.
-
- .. note::
- The HTML report files are generated incrementally based on the
- source files and coverage results. If you modify the report files,
- the changes will not be considered. You should be careful about
- changing the files in the report folder.
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- html_dir=directory, extra_css=extra_css, html_title=title,
- html_skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
- html_skip_empty=skip_empty, precision=precision,
- ):
- reporter = HtmlReporter(self)
- return reporter.report(morfs)
-
- def xml_report(
- self, morfs=None, outfile=None, ignore_errors=None,
- omit=None, include=None, contexts=None, skip_empty=None,
- ):
- """Generate an XML report of coverage results.
-
- The report is compatible with Cobertura reports.
-
- Each module in `morfs` is included in the report. `outfile` is the
- path to write the file to, "-" will write to stdout.
-
- See :meth:`report` for other arguments.
-
- Returns a float, the total percentage covered.
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- xml_output=outfile, report_contexts=contexts, skip_empty=skip_empty,
- ):
- return render_report(self.config.xml_output, XmlReporter(self), morfs)
-
- def json_report(
- self, morfs=None, outfile=None, ignore_errors=None,
- omit=None, include=None, contexts=None, pretty_print=None,
- show_contexts=None
- ):
- """Generate a JSON report of coverage results.
-
- Each module in `morfs` is included in the report. `outfile` is the
- path to write the file to, "-" will write to stdout.
-
- See :meth:`report` for other arguments.
-
- Returns a float, the total percentage covered.
-
- .. versionadded:: 5.0
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- json_output=outfile, report_contexts=contexts, json_pretty_print=pretty_print,
- json_show_contexts=show_contexts
- ):
- return render_report(self.config.json_output, JsonReporter(self), morfs)
-
- def sys_info(self):
- """Return a list of (key, value) pairs showing internal information."""
-
- import coverage as covmod
-
- self._init()
- self._post_init()
-
- def plugin_info(plugins):
- """Make an entry for the sys_info from a list of plug-ins."""
- entries = []
- for plugin in plugins:
- entry = plugin._coverage_plugin_name
- if not plugin._coverage_enabled:
- entry += " (disabled)"
- entries.append(entry)
- return entries
-
- info = [
- ('version', covmod.__version__),
- ('coverage', covmod.__file__),
- ('tracer', self._collector.tracer_name() if self._collector else "-none-"),
- ('CTracer', 'available' if CTracer else "unavailable"),
- ('plugins.file_tracers', plugin_info(self._plugins.file_tracers)),
- ('plugins.configurers', plugin_info(self._plugins.configurers)),
- ('plugins.context_switchers', plugin_info(self._plugins.context_switchers)),
- ('configs_attempted', self.config.attempted_config_files),
- ('configs_read', self.config.config_files_read),
- ('config_file', self.config.config_file),
- ('config_contents',
- repr(self.config._config_contents)
- if self.config._config_contents
- else '-none-'
- ),
- ('data_file', self._data.data_filename() if self._data is not None else "-none-"),
- ('python', sys.version.replace('\n', '')),
- ('platform', platform.platform()),
- ('implementation', platform.python_implementation()),
- ('executable', sys.executable),
- ('def_encoding', sys.getdefaultencoding()),
- ('fs_encoding', sys.getfilesystemencoding()),
- ('pid', os.getpid()),
- ('cwd', os.getcwd()),
- ('path', sys.path),
- ('environment', sorted(
- ("%s = %s" % (k, v))
- for k, v in iitems(os.environ)
- if any(slug in k for slug in ("COV", "PY"))
- )),
- ('command_line', " ".join(getattr(sys, 'argv', ['-none-']))),
- ]
-
- if self._inorout:
- info.extend(self._inorout.sys_info())
-
- info.extend(CoverageData.sys_info())
-
- return info
-
-
-# Mega debugging...
-# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage.
-if int(os.environ.get("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging
- from coverage.debug import decorate_methods, show_calls
-
- Coverage = decorate_methods(show_calls(show_args=True), butnot=['get_data'])(Coverage)
-
-
-def process_startup():
- """Call this at Python start-up to perhaps measure coverage.
-
- If the environment variable COVERAGE_PROCESS_START is defined, coverage
- measurement is started. The value of the variable is the config file
- to use.
-
- There are two ways to configure your Python installation to invoke this
- function when Python starts:
-
- #. Create or append to sitecustomize.py to add these lines::
-
- import coverage
- coverage.process_startup()
-
- #. Create a .pth file in your Python installation containing::
-
- import coverage; coverage.process_startup()
-
- Returns the :class:`Coverage` instance that was started, or None if it was
- not started by this call.
-
- """
- cps = os.environ.get("COVERAGE_PROCESS_START")
- if not cps:
- # No request for coverage, nothing to do.
- return None
-
- # This function can be called more than once in a process. This happens
- # because some virtualenv configurations make the same directory visible
- # twice in sys.path. This means that the .pth file will be found twice,
- # and executed twice, executing this function twice. We set a global
- # flag (an attribute on this function) to indicate that coverage.py has
- # already been started, so we can avoid doing it twice.
- #
- # https://github.com/nedbat/coveragepy/issues/340 has more details.
-
- if hasattr(process_startup, "coverage"):
- # We've annotated this function before, so we must have already
- # started coverage.py in this process. Nothing to do.
- return None
-
- cov = Coverage(config_file=cps)
- process_startup.coverage = cov
- cov._warn_no_data = False
- cov._warn_unimported_source = False
- cov._warn_preimported_source = False
- cov._auto_save = True
- cov.start()
-
- return cov
-
-
-def _prevent_sub_process_measurement():
- """Stop any subprocess auto-measurement from writing data."""
- auto_created_coverage = getattr(process_startup, "coverage", None)
- if auto_created_coverage is not None:
- auto_created_coverage._auto_save = False
diff --git a/contrib/python/coverage/py2/coverage/ctracer/datastack.c b/contrib/python/coverage/py2/coverage/ctracer/datastack.c
deleted file mode 100644
index a9cfcc2cf2..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/datastack.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#include "util.h"
-#include "datastack.h"
-
-#define STACK_DELTA 20
-
-int
-DataStack_init(Stats *pstats, DataStack *pdata_stack)
-{
- pdata_stack->depth = -1;
- pdata_stack->stack = NULL;
- pdata_stack->alloc = 0;
- return RET_OK;
-}
-
-void
-DataStack_dealloc(Stats *pstats, DataStack *pdata_stack)
-{
- int i;
-
- for (i = 0; i < pdata_stack->alloc; i++) {
- Py_XDECREF(pdata_stack->stack[i].file_data);
- }
- PyMem_Free(pdata_stack->stack);
-}
-
-int
-DataStack_grow(Stats *pstats, DataStack *pdata_stack)
-{
- pdata_stack->depth++;
- if (pdata_stack->depth >= pdata_stack->alloc) {
- /* We've outgrown our data_stack array: make it bigger. */
- int bigger = pdata_stack->alloc + STACK_DELTA;
- DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry));
- STATS( pstats->stack_reallocs++; )
- if (bigger_data_stack == NULL) {
- PyErr_NoMemory();
- pdata_stack->depth--;
- return RET_ERROR;
- }
- /* Zero the new entries. */
- memset(bigger_data_stack + pdata_stack->alloc, 0, STACK_DELTA * sizeof(DataStackEntry));
-
- pdata_stack->stack = bigger_data_stack;
- pdata_stack->alloc = bigger;
- }
- return RET_OK;
-}
diff --git a/contrib/python/coverage/py2/coverage/ctracer/datastack.h b/contrib/python/coverage/py2/coverage/ctracer/datastack.h
deleted file mode 100644
index 3b3078ba27..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/datastack.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_DATASTACK_H
-#define _COVERAGE_DATASTACK_H
-
-#include "util.h"
-#include "stats.h"
-
-/* An entry on the data stack. For each call frame, we need to record all
- * the information needed for CTracer_handle_line to operate as quickly as
- * possible.
- */
-typedef struct DataStackEntry {
- /* The current file_data dictionary. Owned. */
- PyObject * file_data;
-
- /* The disposition object for this frame. A borrowed instance of CFileDisposition. */
- PyObject * disposition;
-
- /* The FileTracer handling this frame, or None if it's Python. Borrowed. */
- PyObject * file_tracer;
-
- /* The line number of the last line recorded, for tracing arcs.
- -1 means there was no previous line, as when entering a code object.
- */
- int last_line;
-
- BOOL started_context;
-} DataStackEntry;
-
-/* A data stack is a dynamically allocated vector of DataStackEntry's. */
-typedef struct DataStack {
- int depth; /* The index of the last-used entry in stack. */
- int alloc; /* number of entries allocated at stack. */
- /* The file data at each level, or NULL if not recording. */
- DataStackEntry * stack;
-} DataStack;
-
-
-int DataStack_init(Stats * pstats, DataStack *pdata_stack);
-void DataStack_dealloc(Stats * pstats, DataStack *pdata_stack);
-int DataStack_grow(Stats * pstats, DataStack *pdata_stack);
-
-#endif /* _COVERAGE_DATASTACK_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/filedisp.c b/contrib/python/coverage/py2/coverage/ctracer/filedisp.c
deleted file mode 100644
index 47782ae090..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/filedisp.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#include "util.h"
-#include "filedisp.h"
-
-void
-CFileDisposition_dealloc(CFileDisposition *self)
-{
- Py_XDECREF(self->original_filename);
- Py_XDECREF(self->canonical_filename);
- Py_XDECREF(self->source_filename);
- Py_XDECREF(self->trace);
- Py_XDECREF(self->reason);
- Py_XDECREF(self->file_tracer);
- Py_XDECREF(self->has_dynamic_filename);
-}
-
-static PyMemberDef
-CFileDisposition_members[] = {
- { "original_filename", T_OBJECT, offsetof(CFileDisposition, original_filename), 0,
- PyDoc_STR("") },
-
- { "canonical_filename", T_OBJECT, offsetof(CFileDisposition, canonical_filename), 0,
- PyDoc_STR("") },
-
- { "source_filename", T_OBJECT, offsetof(CFileDisposition, source_filename), 0,
- PyDoc_STR("") },
-
- { "trace", T_OBJECT, offsetof(CFileDisposition, trace), 0,
- PyDoc_STR("") },
-
- { "reason", T_OBJECT, offsetof(CFileDisposition, reason), 0,
- PyDoc_STR("") },
-
- { "file_tracer", T_OBJECT, offsetof(CFileDisposition, file_tracer), 0,
- PyDoc_STR("") },
-
- { "has_dynamic_filename", T_OBJECT, offsetof(CFileDisposition, has_dynamic_filename), 0,
- PyDoc_STR("") },
-
- { NULL }
-};
-
-PyTypeObject
-CFileDispositionType = {
- MyType_HEAD_INIT
- "coverage.CFileDispositionType", /*tp_name*/
- sizeof(CFileDisposition), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)CFileDisposition_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "CFileDisposition objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- CFileDisposition_members, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
-};
diff --git a/contrib/python/coverage/py2/coverage/ctracer/filedisp.h b/contrib/python/coverage/py2/coverage/ctracer/filedisp.h
deleted file mode 100644
index 860f9a50b1..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/filedisp.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_FILEDISP_H
-#define _COVERAGE_FILEDISP_H
-
-#include "util.h"
-#include "structmember.h"
-
-typedef struct CFileDisposition {
- PyObject_HEAD
-
- PyObject * original_filename;
- PyObject * canonical_filename;
- PyObject * source_filename;
- PyObject * trace;
- PyObject * reason;
- PyObject * file_tracer;
- PyObject * has_dynamic_filename;
-} CFileDisposition;
-
-void CFileDisposition_dealloc(CFileDisposition *self);
-
-extern PyTypeObject CFileDispositionType;
-
-#endif /* _COVERAGE_FILEDISP_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/module.c b/contrib/python/coverage/py2/coverage/ctracer/module.c
deleted file mode 100644
index f308902b69..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/module.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#include "util.h"
-#include "tracer.h"
-#include "filedisp.h"
-
-/* Module definition */
-
-#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
-
-#if PY_MAJOR_VERSION >= 3
-
-static PyModuleDef
-moduledef = {
- PyModuleDef_HEAD_INIT,
- "coverage.tracer",
- MODULE_DOC,
- -1,
- NULL, /* methods */
- NULL,
- NULL, /* traverse */
- NULL, /* clear */
- NULL
-};
-
-
-PyObject *
-PyInit_tracer(void)
-{
- PyObject * mod = PyModule_Create(&moduledef);
- if (mod == NULL) {
- return NULL;
- }
-
- if (CTracer_intern_strings() < 0) {
- return NULL;
- }
-
- /* Initialize CTracer */
- CTracerType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CTracerType) < 0) {
- Py_DECREF(mod);
- return NULL;
- }
-
- Py_INCREF(&CTracerType);
- if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- return NULL;
- }
-
- /* Initialize CFileDisposition */
- CFileDispositionType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CFileDispositionType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- return NULL;
- }
-
- Py_INCREF(&CFileDispositionType);
- if (PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- Py_DECREF(&CFileDispositionType);
- return NULL;
- }
-
- return mod;
-}
-
-#else
-
-void
-inittracer(void)
-{
- PyObject * mod;
-
- mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
- if (mod == NULL) {
- return;
- }
-
- if (CTracer_intern_strings() < 0) {
- return;
- }
-
- /* Initialize CTracer */
- CTracerType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CTracerType) < 0) {
- return;
- }
-
- Py_INCREF(&CTracerType);
- PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
-
- /* Initialize CFileDisposition */
- CFileDispositionType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CFileDispositionType) < 0) {
- return;
- }
-
- Py_INCREF(&CFileDispositionType);
- PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType);
-}
-
-#endif /* Py3k */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/stats.h b/contrib/python/coverage/py2/coverage/ctracer/stats.h
deleted file mode 100644
index 05173369f7..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/stats.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_STATS_H
-#define _COVERAGE_STATS_H
-
-#include "util.h"
-
-#if COLLECT_STATS
-#define STATS(x) x
-#else
-#define STATS(x)
-#endif
-
-typedef struct Stats {
- unsigned int calls; /* Need at least one member, but the rest only if needed. */
-#if COLLECT_STATS
- unsigned int lines;
- unsigned int returns;
- unsigned int exceptions;
- unsigned int others;
- unsigned int files;
- unsigned int missed_returns;
- unsigned int stack_reallocs;
- unsigned int errors;
- unsigned int pycalls;
- unsigned int start_context_calls;
-#endif
-} Stats;
-
-#endif /* _COVERAGE_STATS_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/tracer.c b/contrib/python/coverage/py2/coverage/ctracer/tracer.c
deleted file mode 100644
index 00e4218d8e..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/tracer.c
+++ /dev/null
@@ -1,1149 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-/* C-based Tracer for coverage.py. */
-
-#include "util.h"
-#include "datastack.h"
-#include "filedisp.h"
-#include "tracer.h"
-
-/* Python C API helpers. */
-
-static int
-pyint_as_int(PyObject * pyint, int *pint)
-{
- int the_int = MyInt_AsInt(pyint);
- if (the_int == -1 && PyErr_Occurred()) {
- return RET_ERROR;
- }
-
- *pint = the_int;
- return RET_OK;
-}
-
-
-/* Interned strings to speed GetAttr etc. */
-
-static PyObject *str_trace;
-static PyObject *str_file_tracer;
-static PyObject *str__coverage_enabled;
-static PyObject *str__coverage_plugin;
-static PyObject *str__coverage_plugin_name;
-static PyObject *str_dynamic_source_filename;
-static PyObject *str_line_number_range;
-
-int
-CTracer_intern_strings(void)
-{
- int ret = RET_ERROR;
-
-#define INTERN_STRING(v, s) \
- v = MyText_InternFromString(s); \
- if (v == NULL) { \
- goto error; \
- }
-
- INTERN_STRING(str_trace, "trace")
- INTERN_STRING(str_file_tracer, "file_tracer")
- INTERN_STRING(str__coverage_enabled, "_coverage_enabled")
- INTERN_STRING(str__coverage_plugin, "_coverage_plugin")
- INTERN_STRING(str__coverage_plugin_name, "_coverage_plugin_name")
- INTERN_STRING(str_dynamic_source_filename, "dynamic_source_filename")
- INTERN_STRING(str_line_number_range, "line_number_range")
-
- ret = RET_OK;
-
-error:
- return ret;
-}
-
-static void CTracer_disable_plugin(CTracer *self, PyObject * disposition);
-
-static int
-CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
-{
- int ret = RET_ERROR;
-
- if (DataStack_init(&self->stats, &self->data_stack) < 0) {
- goto error;
- }
-
- self->pdata_stack = &self->data_stack;
-
- self->context = Py_None;
- Py_INCREF(self->context);
-
- ret = RET_OK;
- goto ok;
-
-error:
- STATS( self->stats.errors++; )
-
-ok:
- return ret;
-}
-
-static void
-CTracer_dealloc(CTracer *self)
-{
- int i;
-
- if (self->started) {
- PyEval_SetTrace(NULL, NULL);
- }
-
- Py_XDECREF(self->should_trace);
- Py_XDECREF(self->check_include);
- Py_XDECREF(self->warn);
- Py_XDECREF(self->concur_id_func);
- Py_XDECREF(self->data);
- Py_XDECREF(self->file_tracers);
- Py_XDECREF(self->should_trace_cache);
- Py_XDECREF(self->should_start_context);
- Py_XDECREF(self->switch_context);
- Py_XDECREF(self->context);
- Py_XDECREF(self->disable_plugin);
-
- DataStack_dealloc(&self->stats, &self->data_stack);
- if (self->data_stacks) {
- for (i = 0; i < self->data_stacks_used; i++) {
- DataStack_dealloc(&self->stats, self->data_stacks + i);
- }
- PyMem_Free(self->data_stacks);
- }
-
- Py_XDECREF(self->data_stack_index);
-
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-#if TRACE_LOG
-static const char *
-indent(int n)
-{
- static const char * spaces =
- " "
- " "
- " "
- " "
- ;
- return spaces + strlen(spaces) - n*2;
-}
-
-static BOOL logging = FALSE;
-/* Set these constants to be a file substring and line number to start logging. */
-static const char * start_file = "tests/views";
-static int start_line = 27;
-
-static void
-showlog(int depth, int lineno, PyObject * filename, const char * msg)
-{
- if (logging) {
- printf("%s%3d ", indent(depth), depth);
- if (lineno) {
- printf("%4d", lineno);
- }
- else {
- printf(" ");
- }
- if (filename) {
- PyObject *ascii = MyText_AS_BYTES(filename);
- printf(" %s", MyBytes_AS_STRING(ascii));
- Py_DECREF(ascii);
- }
- if (msg) {
- printf(" %s", msg);
- }
- printf("\n");
- }
-}
-
-#define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
-#else
-#define SHOWLOG(a,b,c,d)
-#endif /* TRACE_LOG */
-
-#if WHAT_LOG
-static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
-#endif
-
-/* Record a pair of integers in self->pcur_entry->file_data. */
-static int
-CTracer_record_pair(CTracer *self, int l1, int l2)
-{
- int ret = RET_ERROR;
-
- PyObject * t = NULL;
-
- t = Py_BuildValue("(ii)", l1, l2);
- if (t == NULL) {
- goto error;
- }
-
- if (PyDict_SetItem(self->pcur_entry->file_data, t, Py_None) < 0) {
- goto error;
- }
-
- ret = RET_OK;
-
-error:
- Py_XDECREF(t);
-
- return ret;
-}
-
-/* Set self->pdata_stack to the proper data_stack to use. */
-static int
-CTracer_set_pdata_stack(CTracer *self)
-{
- int ret = RET_ERROR;
- PyObject * co_obj = NULL;
- PyObject * stack_index = NULL;
-
- if (self->concur_id_func != Py_None) {
- int the_index = 0;
-
- if (self->data_stack_index == NULL) {
- PyObject * weakref = NULL;
-
- weakref = PyImport_ImportModule("weakref");
- if (weakref == NULL) {
- goto error;
- }
- STATS( self->stats.pycalls++; )
- self->data_stack_index = PyObject_CallMethod(weakref, "WeakKeyDictionary", NULL);
- Py_XDECREF(weakref);
-
- if (self->data_stack_index == NULL) {
- goto error;
- }
- }
-
- STATS( self->stats.pycalls++; )
- co_obj = PyObject_CallObject(self->concur_id_func, NULL);
- if (co_obj == NULL) {
- goto error;
- }
- stack_index = PyObject_GetItem(self->data_stack_index, co_obj);
- if (stack_index == NULL) {
- /* PyObject_GetItem sets an exception if it didn't find the thing. */
- PyErr_Clear();
-
- /* A new concurrency object. Make a new data stack. */
- the_index = self->data_stacks_used;
- stack_index = MyInt_FromInt(the_index);
- if (stack_index == NULL) {
- goto error;
- }
- if (PyObject_SetItem(self->data_stack_index, co_obj, stack_index) < 0) {
- goto error;
- }
- self->data_stacks_used++;
- if (self->data_stacks_used >= self->data_stacks_alloc) {
- int bigger = self->data_stacks_alloc + 10;
- DataStack * bigger_stacks = PyMem_Realloc(self->data_stacks, bigger * sizeof(DataStack));
- if (bigger_stacks == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- self->data_stacks = bigger_stacks;
- self->data_stacks_alloc = bigger;
- }
- DataStack_init(&self->stats, &self->data_stacks[the_index]);
- }
- else {
- if (pyint_as_int(stack_index, &the_index) < 0) {
- goto error;
- }
- }
-
- self->pdata_stack = &self->data_stacks[the_index];
- }
- else {
- self->pdata_stack = &self->data_stack;
- }
-
- ret = RET_OK;
-
-error:
-
- Py_XDECREF(co_obj);
- Py_XDECREF(stack_index);
-
- return ret;
-}
-
-/*
- * Parts of the trace function.
- */
-
-static int
-CTracer_check_missing_return(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
-
- if (self->last_exc_back) {
- if (frame == self->last_exc_back) {
- /* Looks like someone forgot to send a return event. We'll clear
- the exception state and do the RETURN code here. Notice that the
- frame we have in hand here is not the correct frame for the RETURN,
- that frame is gone. Our handling for RETURN doesn't need the
- actual frame, but we do log it, so that will look a little off if
- you're looking at the detailed log.
-
- If someday we need to examine the frame when doing RETURN, then
- we'll need to keep more of the missed frame's state.
- */
- STATS( self->stats.missed_returns++; )
- if (CTracer_set_pdata_stack(self) < 0) {
- goto error;
- }
- if (self->pdata_stack->depth >= 0) {
- if (self->tracing_arcs && self->pcur_entry->file_data) {
- if (CTracer_record_pair(self, self->pcur_entry->last_line, -self->last_exc_firstlineno) < 0) {
- goto error;
- }
- }
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "missedreturn");
- self->pdata_stack->depth--;
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
- }
- }
- self->last_exc_back = NULL;
- }
-
- ret = RET_OK;
-
-error:
-
- return ret;
-}
-
-static int
-CTracer_handle_call(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
- int ret2;
-
- /* Owned references that we clean up at the very end of the function. */
- PyObject * disposition = NULL;
- PyObject * plugin = NULL;
- PyObject * plugin_name = NULL;
- PyObject * next_tracename = NULL;
-
- /* Borrowed references. */
- PyObject * filename = NULL;
- PyObject * disp_trace = NULL;
- PyObject * tracename = NULL;
- PyObject * file_tracer = NULL;
- PyObject * has_dynamic_filename = NULL;
-
- CFileDisposition * pdisp = NULL;
-
- STATS( self->stats.calls++; )
-
- /* Grow the stack. */
- if (CTracer_set_pdata_stack(self) < 0) {
- goto error;
- }
- if (DataStack_grow(&self->stats, self->pdata_stack) < 0) {
- goto error;
- }
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
-
- /* See if this frame begins a new context. */
- if (self->should_start_context != Py_None && self->context == Py_None) {
- PyObject * context;
- /* We're looking for our context, ask should_start_context if this is the start. */
- STATS( self->stats.start_context_calls++; )
- STATS( self->stats.pycalls++; )
- context = PyObject_CallFunctionObjArgs(self->should_start_context, frame, NULL);
- if (context == NULL) {
- goto error;
- }
- if (context != Py_None) {
- PyObject * val;
- Py_DECREF(self->context);
- self->context = context;
- self->pcur_entry->started_context = TRUE;
- STATS( self->stats.pycalls++; )
- val = PyObject_CallFunctionObjArgs(self->switch_context, context, NULL);
- if (val == NULL) {
- goto error;
- }
- Py_DECREF(val);
- }
- else {
- Py_DECREF(context);
- self->pcur_entry->started_context = FALSE;
- }
- }
- else {
- self->pcur_entry->started_context = FALSE;
- }
-
- /* Check if we should trace this line. */
- filename = frame->f_code->co_filename;
- disposition = PyDict_GetItem(self->should_trace_cache, filename);
- if (disposition == NULL) {
- if (PyErr_Occurred()) {
- goto error;
- }
- STATS( self->stats.files++; )
-
- /* We've never considered this file before. */
- /* Ask should_trace about it. */
- STATS( self->stats.pycalls++; )
- disposition = PyObject_CallFunctionObjArgs(self->should_trace, filename, frame, NULL);
- if (disposition == NULL) {
- /* An error occurred inside should_trace. */
- goto error;
- }
- if (PyDict_SetItem(self->should_trace_cache, filename, disposition) < 0) {
- goto error;
- }
- }
- else {
- Py_INCREF(disposition);
- }
-
- if (disposition == Py_None) {
- /* A later check_include returned false, so don't trace it. */
- disp_trace = Py_False;
- }
- else {
- /* The object we got is a CFileDisposition, use it efficiently. */
- pdisp = (CFileDisposition *) disposition;
- disp_trace = pdisp->trace;
- if (disp_trace == NULL) {
- goto error;
- }
- }
-
- if (disp_trace == Py_True) {
- /* If tracename is a string, then we're supposed to trace. */
- tracename = pdisp->source_filename;
- if (tracename == NULL) {
- goto error;
- }
- file_tracer = pdisp->file_tracer;
- if (file_tracer == NULL) {
- goto error;
- }
- if (file_tracer != Py_None) {
- plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin);
- if (plugin == NULL) {
- goto error;
- }
- plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name);
- if (plugin_name == NULL) {
- goto error;
- }
- }
- has_dynamic_filename = pdisp->has_dynamic_filename;
- if (has_dynamic_filename == NULL) {
- goto error;
- }
- if (has_dynamic_filename == Py_True) {
- STATS( self->stats.pycalls++; )
- next_tracename = PyObject_CallMethodObjArgs(
- file_tracer, str_dynamic_source_filename,
- tracename, frame, NULL
- );
- if (next_tracename == NULL) {
- /* An exception from the function. Alert the user with a
- * warning and a traceback.
- */
- CTracer_disable_plugin(self, disposition);
- /* Because we handled the error, goto ok. */
- goto ok;
- }
- tracename = next_tracename;
-
- if (tracename != Py_None) {
- /* Check the dynamic source filename against the include rules. */
- PyObject * included = NULL;
- int should_include;
- included = PyDict_GetItem(self->should_trace_cache, tracename);
- if (included == NULL) {
- PyObject * should_include_bool;
- if (PyErr_Occurred()) {
- goto error;
- }
- STATS( self->stats.files++; )
- STATS( self->stats.pycalls++; )
- should_include_bool = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL);
- if (should_include_bool == NULL) {
- goto error;
- }
- should_include = (should_include_bool == Py_True);
- Py_DECREF(should_include_bool);
- if (PyDict_SetItem(self->should_trace_cache, tracename, should_include ? disposition : Py_None) < 0) {
- goto error;
- }
- }
- else {
- should_include = (included != Py_None);
- }
- if (!should_include) {
- tracename = Py_None;
- }
- }
- }
- }
- else {
- tracename = Py_None;
- }
-
- if (tracename != Py_None) {
- PyObject * file_data = PyDict_GetItem(self->data, tracename);
-
- if (file_data == NULL) {
- if (PyErr_Occurred()) {
- goto error;
- }
- file_data = PyDict_New();
- if (file_data == NULL) {
- goto error;
- }
- ret2 = PyDict_SetItem(self->data, tracename, file_data);
- if (ret2 < 0) {
- goto error;
- }
-
- /* If the disposition mentions a plugin, record that. */
- if (file_tracer != Py_None) {
- ret2 = PyDict_SetItem(self->file_tracers, tracename, plugin_name);
- if (ret2 < 0) {
- goto error;
- }
- }
- }
- else {
- /* PyDict_GetItem gives a borrowed reference. Own it. */
- Py_INCREF(file_data);
- }
-
- Py_XDECREF(self->pcur_entry->file_data);
- self->pcur_entry->file_data = file_data;
- self->pcur_entry->file_tracer = file_tracer;
-
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "traced");
- }
- else {
- Py_XDECREF(self->pcur_entry->file_data);
- self->pcur_entry->file_data = NULL;
- self->pcur_entry->file_tracer = Py_None;
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "skipped");
- }
-
- self->pcur_entry->disposition = disposition;
-
- /* Make the frame right in case settrace(gettrace()) happens. */
- Py_INCREF(self);
- My_XSETREF(frame->f_trace, (PyObject*)self);
-
- /* A call event is really a "start frame" event, and can happen for
- * re-entering a generator also. f_lasti is -1 for a true call, and a
- * real byte offset for a generator re-entry.
- */
- if (frame->f_lasti < 0) {
- self->pcur_entry->last_line = -frame->f_code->co_firstlineno;
- }
- else {
- self->pcur_entry->last_line = PyFrame_GetLineNumber(frame);
- }
-
-ok:
- ret = RET_OK;
-
-error:
- Py_XDECREF(next_tracename);
- Py_XDECREF(disposition);
- Py_XDECREF(plugin);
- Py_XDECREF(plugin_name);
-
- return ret;
-}
-
-
-static void
-CTracer_disable_plugin(CTracer *self, PyObject * disposition)
-{
- PyObject * ret;
- PyErr_Print();
-
- STATS( self->stats.pycalls++; )
- ret = PyObject_CallFunctionObjArgs(self->disable_plugin, disposition, NULL);
- if (ret == NULL) {
- goto error;
- }
- Py_DECREF(ret);
-
- return;
-
-error:
- /* This function doesn't return a status, so if an error happens, print it,
- * but don't interrupt the flow. */
- /* PySys_WriteStderr is nicer, but is not in the public API. */
- fprintf(stderr, "Error occurred while disabling plug-in:\n");
- PyErr_Print();
-}
-
-
-static int
-CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two)
-{
- int ret = RET_ERROR;
- int the_int;
- PyObject * pyint = NULL;
- int index;
-
- if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) {
- PyErr_SetString(
- PyExc_TypeError,
- "line_number_range must return 2-tuple"
- );
- goto error;
- }
-
- for (index = 0; index < 2; index++) {
- pyint = PyTuple_GetItem(pair, index);
- if (pyint == NULL) {
- goto error;
- }
- if (pyint_as_int(pyint, &the_int) < 0) {
- goto error;
- }
- *(index == 0 ? p_one : p_two) = the_int;
- }
-
- ret = RET_OK;
-
-error:
- return ret;
-}
-
-static int
-CTracer_handle_line(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
- int ret2;
-
- STATS( self->stats.lines++; )
- if (self->pdata_stack->depth >= 0) {
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "line");
- if (self->pcur_entry->file_data) {
- int lineno_from = -1;
- int lineno_to = -1;
-
- /* We're tracing in this frame: record something. */
- if (self->pcur_entry->file_tracer != Py_None) {
- PyObject * from_to = NULL;
- STATS( self->stats.pycalls++; )
- from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL);
- if (from_to == NULL) {
- CTracer_disable_plugin(self, self->pcur_entry->disposition);
- goto ok;
- }
- ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to);
- Py_DECREF(from_to);
- if (ret2 < 0) {
- CTracer_disable_plugin(self, self->pcur_entry->disposition);
- goto ok;
- }
- }
- else {
- lineno_from = lineno_to = PyFrame_GetLineNumber(frame);
- }
-
- if (lineno_from != -1) {
- for (; lineno_from <= lineno_to; lineno_from++) {
- if (self->tracing_arcs) {
- /* Tracing arcs: key is (last_line,this_line). */
- if (CTracer_record_pair(self, self->pcur_entry->last_line, lineno_from) < 0) {
- goto error;
- }
- }
- else {
- /* Tracing lines: key is simply this_line. */
- PyObject * this_line = MyInt_FromInt(lineno_from);
- if (this_line == NULL) {
- goto error;
- }
-
- ret2 = PyDict_SetItem(self->pcur_entry->file_data, this_line, Py_None);
- Py_DECREF(this_line);
- if (ret2 < 0) {
- goto error;
- }
- }
-
- self->pcur_entry->last_line = lineno_from;
- }
- }
- }
- }
-
-ok:
- ret = RET_OK;
-
-error:
-
- return ret;
-}
-
-static int
-CTracer_handle_return(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
-
- STATS( self->stats.returns++; )
- /* A near-copy of this code is above in the missing-return handler. */
- if (CTracer_set_pdata_stack(self) < 0) {
- goto error;
- }
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
-
- if (self->pdata_stack->depth >= 0) {
- if (self->tracing_arcs && self->pcur_entry->file_data) {
- /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read
- * the current bytecode to see what it is. In unusual circumstances
- * (Cython code), co_code can be the empty string, so range-check
- * f_lasti before reading the byte.
- */
- int bytecode = RETURN_VALUE;
- PyObject * pCode = frame->f_code->co_code;
- int lasti = frame->f_lasti;
-
- if (lasti < MyBytes_GET_SIZE(pCode)) {
- bytecode = MyBytes_AS_STRING(pCode)[lasti];
- }
- if (bytecode != YIELD_VALUE) {
- int first = frame->f_code->co_firstlineno;
- if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) {
- goto error;
- }
- }
- }
-
- /* If this frame started a context, then returning from it ends the context. */
- if (self->pcur_entry->started_context) {
- PyObject * val;
- Py_DECREF(self->context);
- self->context = Py_None;
- Py_INCREF(self->context);
- STATS( self->stats.pycalls++; )
-
- val = PyObject_CallFunctionObjArgs(self->switch_context, self->context, NULL);
- if (val == NULL) {
- goto error;
- }
- Py_DECREF(val);
- }
-
- /* Pop the stack. */
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "return");
- self->pdata_stack->depth--;
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
- }
-
- ret = RET_OK;
-
-error:
-
- return ret;
-}
-
-static int
-CTracer_handle_exception(CTracer *self, PyFrameObject *frame)
-{
- /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
- without a return event. To detect that, we'll keep a copy of the
- parent frame for an exception event. If the next event is in that
- frame, then we must have returned without a return event. We can
- synthesize the missing event then.
-
- Python itself fixed this problem in 2.4. Pyexpat still has the bug.
- I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
- If it gets fixed, this code should still work properly. Maybe some day
- the bug will be fixed everywhere coverage.py is supported, and we can
- remove this missing-return detection.
-
- More about this fix: https://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
- */
- STATS( self->stats.exceptions++; )
- self->last_exc_back = frame->f_back;
- self->last_exc_firstlineno = frame->f_code->co_firstlineno;
-
- return RET_OK;
-}
-
-/*
- * The Trace Function
- */
-static int
-CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
-{
- int ret = RET_ERROR;
-
- #if DO_NOTHING
- return RET_OK;
- #endif
-
- if (!self->started) {
- /* If CTracer.stop() has been called from another thread, the tracer
- is still active in the current thread. Let's deactivate ourselves
- now. */
- PyEval_SetTrace(NULL, NULL);
- return RET_OK;
- }
-
- #if WHAT_LOG || TRACE_LOG
- PyObject * ascii = NULL;
- #endif
-
- #if WHAT_LOG
- if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) {
- ascii = MyText_AS_BYTES(frame->f_code->co_filename);
- printf("trace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
- Py_DECREF(ascii);
- }
- #endif
-
- #if TRACE_LOG
- ascii = MyText_AS_BYTES(frame->f_code->co_filename);
- if (strstr(MyBytes_AS_STRING(ascii), start_file) && PyFrame_GetLineNumber(frame) == start_line) {
- logging = TRUE;
- }
- Py_DECREF(ascii);
- #endif
-
- /* See below for details on missing-return detection. */
- if (CTracer_check_missing_return(self, frame) < 0) {
- goto error;
- }
-
- self->activity = TRUE;
-
- switch (what) {
- case PyTrace_CALL:
- if (CTracer_handle_call(self, frame) < 0) {
- goto error;
- }
- break;
-
- case PyTrace_RETURN:
- if (CTracer_handle_return(self, frame) < 0) {
- goto error;
- }
- break;
-
- case PyTrace_LINE:
- if (CTracer_handle_line(self, frame) < 0) {
- goto error;
- }
- break;
-
- case PyTrace_EXCEPTION:
- if (CTracer_handle_exception(self, frame) < 0) {
- goto error;
- }
- break;
-
- default:
- STATS( self->stats.others++; )
- break;
- }
-
- ret = RET_OK;
- goto cleanup;
-
-error:
- STATS( self->stats.errors++; )
-
-cleanup:
- return ret;
-}
-
-
-/*
- * Python has two ways to set the trace function: sys.settrace(fn), which
- * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
- * a C function and a Python object. The way these work together is that
- * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
- * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
- * simply returns the Python object used as the second argument to
- * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
- * means it must be callable to be used in sys.settrace().
- *
- * So we make ourself callable, equivalent to invoking our trace function.
- *
- * To help with the process of replaying stored frames, this function has an
- * optional keyword argument:
- *
- * def CTracer_call(frame, event, arg, lineno=0)
- *
- * If provided, the lineno argument is used as the line number, and the
- * frame's f_lineno member is ignored.
- */
-static PyObject *
-CTracer_call(CTracer *self, PyObject *args, PyObject *kwds)
-{
- PyFrameObject *frame;
- PyObject *what_str;
- PyObject *arg;
- int lineno = 0;
- int what;
- int orig_lineno;
- PyObject *ret = NULL;
- PyObject * ascii = NULL;
-
- #if DO_NOTHING
- CRASH
- #endif
-
- static char *what_names[] = {
- "call", "exception", "line", "return",
- "c_call", "c_exception", "c_return",
- NULL
- };
-
- static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
- &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
- goto done;
- }
-
- /* In Python, the what argument is a string, we need to find an int
- for the C function. */
- for (what = 0; what_names[what]; what++) {
- int should_break;
- ascii = MyText_AS_BYTES(what_str);
- should_break = !strcmp(MyBytes_AS_STRING(ascii), what_names[what]);
- Py_DECREF(ascii);
- if (should_break) {
- break;
- }
- }
-
- #if WHAT_LOG
- ascii = MyText_AS_BYTES(frame->f_code->co_filename);
- printf("pytrace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
- Py_DECREF(ascii);
- #endif
-
- /* Save off the frame's lineno, and use the forced one, if provided. */
- orig_lineno = frame->f_lineno;
- if (lineno > 0) {
- frame->f_lineno = lineno;
- }
-
- /* Invoke the C function, and return ourselves. */
- if (CTracer_trace(self, frame, what, arg) == RET_OK) {
- Py_INCREF(self);
- ret = (PyObject *)self;
- }
-
- /* Clean up. */
- frame->f_lineno = orig_lineno;
-
- /* For better speed, install ourselves the C way so that future calls go
- directly to CTracer_trace, without this intermediate function.
-
- Only do this if this is a CALL event, since new trace functions only
- take effect then. If we don't condition it on CALL, then we'll clobber
- the new trace function before it has a chance to get called. To
- understand why, there are three internal values to track: frame.f_trace,
- c_tracefunc, and c_traceobj. They are explained here:
- https://nedbatchelder.com/text/trace-function.html
-
- Without the conditional on PyTrace_CALL, this is what happens:
-
- def func(): # f_trace c_tracefunc c_traceobj
- # -------------- -------------- --------------
- # CTracer CTracer.trace CTracer
- sys.settrace(my_func)
- # CTracer trampoline my_func
- # Now Python calls trampoline(CTracer), which calls this function
- # which calls PyEval_SetTrace below, setting us as the tracer again:
- # CTracer CTracer.trace CTracer
- # and it's as if the settrace never happened.
- */
- if (what == PyTrace_CALL) {
- PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
- }
-
-done:
- return ret;
-}
-
-static PyObject *
-CTracer_start(CTracer *self, PyObject *args_unused)
-{
- PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
- self->started = TRUE;
- self->tracing_arcs = self->trace_arcs && PyObject_IsTrue(self->trace_arcs);
-
- /* start() returns a trace function usable with sys.settrace() */
- Py_INCREF(self);
- return (PyObject *)self;
-}
-
-static PyObject *
-CTracer_stop(CTracer *self, PyObject *args_unused)
-{
- if (self->started) {
- /* Set the started flag only. The actual call to
- PyEval_SetTrace(NULL, NULL) is delegated to the callback
- itself to ensure that it called from the right thread.
- */
- self->started = FALSE;
- }
-
- Py_RETURN_NONE;
-}
-
-static PyObject *
-CTracer_activity(CTracer *self, PyObject *args_unused)
-{
- if (self->activity) {
- Py_RETURN_TRUE;
- }
- else {
- Py_RETURN_FALSE;
- }
-}
-
-static PyObject *
-CTracer_reset_activity(CTracer *self, PyObject *args_unused)
-{
- self->activity = FALSE;
- Py_RETURN_NONE;
-}
-
-static PyObject *
-CTracer_get_stats(CTracer *self, PyObject *args_unused)
-{
-#if COLLECT_STATS
- return Py_BuildValue(
- "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI,sI,sI}",
- "calls", self->stats.calls,
- "lines", self->stats.lines,
- "returns", self->stats.returns,
- "exceptions", self->stats.exceptions,
- "others", self->stats.others,
- "files", self->stats.files,
- "missed_returns", self->stats.missed_returns,
- "stack_reallocs", self->stats.stack_reallocs,
- "stack_alloc", self->pdata_stack->alloc,
- "errors", self->stats.errors,
- "pycalls", self->stats.pycalls,
- "start_context_calls", self->stats.start_context_calls
- );
-#else
- Py_RETURN_NONE;
-#endif /* COLLECT_STATS */
-}
-
-static PyMemberDef
-CTracer_members[] = {
- { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0,
- PyDoc_STR("Function indicating whether to trace a file.") },
-
- { "check_include", T_OBJECT, offsetof(CTracer, check_include), 0,
- PyDoc_STR("Function indicating whether to include a file.") },
-
- { "warn", T_OBJECT, offsetof(CTracer, warn), 0,
- PyDoc_STR("Function for issuing warnings.") },
-
- { "concur_id_func", T_OBJECT, offsetof(CTracer, concur_id_func), 0,
- PyDoc_STR("Function for determining concurrency context") },
-
- { "data", T_OBJECT, offsetof(CTracer, data), 0,
- PyDoc_STR("The raw dictionary of trace data.") },
-
- { "file_tracers", T_OBJECT, offsetof(CTracer, file_tracers), 0,
- PyDoc_STR("Mapping from file name to plugin name.") },
-
- { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0,
- PyDoc_STR("Dictionary caching should_trace results.") },
-
- { "trace_arcs", T_OBJECT, offsetof(CTracer, trace_arcs), 0,
- PyDoc_STR("Should we trace arcs, or just lines?") },
-
- { "should_start_context", T_OBJECT, offsetof(CTracer, should_start_context), 0,
- PyDoc_STR("Function for starting contexts.") },
-
- { "switch_context", T_OBJECT, offsetof(CTracer, switch_context), 0,
- PyDoc_STR("Function for switching to a new context.") },
-
- { "disable_plugin", T_OBJECT, offsetof(CTracer, disable_plugin), 0,
- PyDoc_STR("Function for disabling a plugin.") },
-
- { NULL }
-};
-
-static PyMethodDef
-CTracer_methods[] = {
- { "start", (PyCFunction) CTracer_start, METH_VARARGS,
- PyDoc_STR("Start the tracer") },
-
- { "stop", (PyCFunction) CTracer_stop, METH_VARARGS,
- PyDoc_STR("Stop the tracer") },
-
- { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS,
- PyDoc_STR("Get statistics about the tracing") },
-
- { "activity", (PyCFunction) CTracer_activity, METH_VARARGS,
- PyDoc_STR("Has there been any activity?") },
-
- { "reset_activity", (PyCFunction) CTracer_reset_activity, METH_VARARGS,
- PyDoc_STR("Reset the activity flag") },
-
- { NULL }
-};
-
-PyTypeObject
-CTracerType = {
- MyType_HEAD_INIT
- "coverage.CTracer", /*tp_name*/
- sizeof(CTracer), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)CTracer_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- (ternaryfunc)CTracer_call, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "CTracer objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- CTracer_methods, /* tp_methods */
- CTracer_members, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)CTracer_init, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
-};
diff --git a/contrib/python/coverage/py2/coverage/ctracer/tracer.h b/contrib/python/coverage/py2/coverage/ctracer/tracer.h
deleted file mode 100644
index 8994a9e3d6..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/tracer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_TRACER_H
-#define _COVERAGE_TRACER_H
-
-#include "util.h"
-#include "structmember.h"
-#include "frameobject.h"
-#include "opcode.h"
-
-#include "datastack.h"
-
-/* The CTracer type. */
-
-typedef struct CTracer {
- PyObject_HEAD
-
- /* Python objects manipulated directly by the Collector class. */
- PyObject * should_trace;
- PyObject * check_include;
- PyObject * warn;
- PyObject * concur_id_func;
- PyObject * data;
- PyObject * file_tracers;
- PyObject * should_trace_cache;
- PyObject * trace_arcs;
- PyObject * should_start_context;
- PyObject * switch_context;
- PyObject * disable_plugin;
-
- /* Has the tracer been started? */
- BOOL started;
- /* Are we tracing arcs, or just lines? */
- BOOL tracing_arcs;
- /* Have we had any activity? */
- BOOL activity;
- /* The current dynamic context. */
- PyObject * context;
-
- /*
- The data stack is a stack of dictionaries. Each dictionary collects
- data for a single source file. The data stack parallels the call stack:
- each call pushes the new frame's file data onto the data stack, and each
- return pops file data off.
-
- The file data is a dictionary whose form depends on the tracing options.
- If tracing arcs, the keys are line number pairs. If not tracing arcs,
- the keys are line numbers. In both cases, the value is irrelevant
- (None).
- */
-
- DataStack data_stack; /* Used if we aren't doing concurrency. */
-
- PyObject * data_stack_index; /* Used if we are doing concurrency. */
- DataStack * data_stacks;
- int data_stacks_alloc;
- int data_stacks_used;
- DataStack * pdata_stack;
-
- /* The current file's data stack entry. */
- DataStackEntry * pcur_entry;
-
- /* The parent frame for the last exception event, to fix missing returns. */
- PyFrameObject * last_exc_back;
- int last_exc_firstlineno;
-
- Stats stats;
-} CTracer;
-
-int CTracer_intern_strings(void);
-
-extern PyTypeObject CTracerType;
-
-#endif /* _COVERAGE_TRACER_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/util.h b/contrib/python/coverage/py2/coverage/ctracer/util.h
deleted file mode 100644
index 5cba9b3096..0000000000
--- a/contrib/python/coverage/py2/coverage/ctracer/util.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_UTIL_H
-#define _COVERAGE_UTIL_H
-
-#include <Python.h>
-
-/* Compile-time debugging helpers */
-#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
-#undef TRACE_LOG /* Define to log our bookkeeping. */
-#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
-#undef DO_NOTHING /* Define this to make the tracer do nothing. */
-
-/* Py 2.x and 3.x compatibility */
-
-#if PY_MAJOR_VERSION >= 3
-
-#define MyText_Type PyUnicode_Type
-#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
-#define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
-#define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o)
-#define MyText_AsString(o) PyUnicode_AsUTF8(o)
-#define MyText_FromFormat PyUnicode_FromFormat
-#define MyInt_FromInt(i) PyLong_FromLong((long)i)
-#define MyInt_AsInt(o) (int)PyLong_AsLong(o)
-#define MyText_InternFromString(s) PyUnicode_InternFromString(s)
-
-#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
-
-#else
-
-#define MyText_Type PyString_Type
-#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
-#define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o)
-#define MyBytes_AS_STRING(o) PyString_AS_STRING(o)
-#define MyText_AsString(o) PyString_AsString(o)
-#define MyText_FromFormat PyUnicode_FromFormat
-#define MyInt_FromInt(i) PyInt_FromLong((long)i)
-#define MyInt_AsInt(o) (int)PyInt_AsLong(o)
-#define MyText_InternFromString(s) PyString_InternFromString(s)
-
-#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
-
-#endif /* Py3k */
-
-// Undocumented, and not in all 2.7.x, so our own copy of it.
-#define My_XSETREF(op, op2) \
- do { \
- PyObject *_py_tmp = (PyObject *)(op); \
- (op) = (op2); \
- Py_XDECREF(_py_tmp); \
- } while (0)
-
-/* The values returned to indicate ok or error. */
-#define RET_OK 0
-#define RET_ERROR -1
-
-/* Nicer booleans */
-typedef int BOOL;
-#define FALSE 0
-#define TRUE 1
-
-/* Only for extreme machete-mode debugging! */
-#define CRASH { printf("*** CRASH! ***\n"); *((int*)1) = 1; }
-
-#endif /* _COVERAGE_UTIL_H */
diff --git a/contrib/python/coverage/py2/coverage/data.py b/contrib/python/coverage/py2/coverage/data.py
deleted file mode 100644
index 5dd1dfe3f0..0000000000
--- a/contrib/python/coverage/py2/coverage/data.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Coverage data for coverage.py.
-
-This file had the 4.x JSON data support, which is now gone. This file still
-has storage-agnostic helpers, and is kept to avoid changing too many imports.
-CoverageData is now defined in sqldata.py, and imported here to keep the
-imports working.
-
-"""
-
-import glob
-import os.path
-
-from coverage.misc import CoverageException, file_be_gone
-from coverage.sqldata import CoverageData
-
-
-def line_counts(data, fullpath=False):
- """Return a dict summarizing the line coverage data.
-
- Keys are based on the file names, and values are the number of executed
- lines. If `fullpath` is true, then the keys are the full pathnames of
- the files, otherwise they are the basenames of the files.
-
- Returns a dict mapping file names to counts of lines.
-
- """
- summ = {}
- if fullpath:
- filename_fn = lambda f: f
- else:
- filename_fn = os.path.basename
- for filename in data.measured_files():
- summ[filename_fn(filename)] = len(data.lines(filename))
- return summ
-
-
-def add_data_to_hash(data, filename, hasher):
- """Contribute `filename`'s data to the `hasher`.
-
- `hasher` is a `coverage.misc.Hasher` instance to be updated with
- the file's data. It should only get the results data, not the run
- data.
-
- """
- if data.has_arcs():
- hasher.update(sorted(data.arcs(filename) or []))
- else:
- hasher.update(sorted(data.lines(filename) or []))
- hasher.update(data.file_tracer(filename))
-
-
-def combine_parallel_data(data, aliases=None, data_paths=None, strict=False, keep=False):
- """Combine a number of data files together.
-
- Treat `data.filename` as a file prefix, and combine the data from all
- of the data files starting with that prefix plus a dot.
-
- If `aliases` is provided, it's a `PathAliases` object that is used to
- re-map paths to match the local machine's.
-
- If `data_paths` is provided, it is a list of directories or files to
- combine. Directories are searched for files that start with
- `data.filename` plus dot as a prefix, and those files are combined.
-
- If `data_paths` is not provided, then the directory portion of
- `data.filename` is used as the directory to search for data files.
-
- Unless `keep` is True every data file found and combined is then deleted from disk. If a file
- cannot be read, a warning will be issued, and the file will not be
- deleted.
-
- If `strict` is true, and no files are found to combine, an error is
- raised.
-
- """
- # Because of the os.path.abspath in the constructor, data_dir will
- # never be an empty string.
- data_dir, local = os.path.split(data.base_filename())
- localdot = local + '.*'
-
- data_paths = data_paths or [data_dir]
- files_to_combine = []
- for p in data_paths:
- if os.path.isfile(p):
- files_to_combine.append(os.path.abspath(p))
- elif os.path.isdir(p):
- pattern = os.path.join(os.path.abspath(p), localdot)
- files_to_combine.extend(glob.glob(pattern))
- else:
- raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,))
-
- if strict and not files_to_combine:
- raise CoverageException("No data to combine")
-
- files_combined = 0
- for f in files_to_combine:
- if f == data.data_filename():
- # Sometimes we are combining into a file which is one of the
- # parallel files. Skip that file.
- if data._debug.should('dataio'):
- data._debug.write("Skipping combining ourself: %r" % (f,))
- continue
- if data._debug.should('dataio'):
- data._debug.write("Combining data file %r" % (f,))
- try:
- new_data = CoverageData(f, debug=data._debug)
- new_data.read()
- except CoverageException as exc:
- if data._warn:
- # The CoverageException has the file name in it, so just
- # use the message as the warning.
- data._warn(str(exc))
- else:
- data.update(new_data, aliases=aliases)
- files_combined += 1
- if not keep:
- if data._debug.should('dataio'):
- data._debug.write("Deleting combined data file %r" % (f,))
- file_be_gone(f)
-
- if strict and not files_combined:
- raise CoverageException("No usable data files")
diff --git a/contrib/python/coverage/py2/coverage/debug.py b/contrib/python/coverage/py2/coverage/debug.py
deleted file mode 100644
index 194f16f50d..0000000000
--- a/contrib/python/coverage/py2/coverage/debug.py
+++ /dev/null
@@ -1,406 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Control of and utilities for debugging."""
-
-import contextlib
-import functools
-import inspect
-import itertools
-import os
-import pprint
-import sys
-try:
- import _thread
-except ImportError:
- import thread as _thread
-
-from coverage.backward import reprlib, StringIO
-from coverage.misc import isolate_module
-
-os = isolate_module(os)
-
-
-# When debugging, it can be helpful to force some options, especially when
-# debugging the configuration mechanisms you usually use to control debugging!
-# This is a list of forced debugging options.
-FORCED_DEBUG = []
-FORCED_DEBUG_FILE = None
-
-
-class DebugControl(object):
- """Control and output for debugging."""
-
- show_repr_attr = False # For SimpleReprMixin
-
- def __init__(self, options, output):
- """Configure the options and output file for debugging."""
- self.options = list(options) + FORCED_DEBUG
- self.suppress_callers = False
-
- filters = []
- if self.should('pid'):
- filters.append(add_pid_and_tid)
- self.output = DebugOutputFile.get_one(
- output,
- show_process=self.should('process'),
- filters=filters,
- )
- self.raw_output = self.output.outfile
-
- def __repr__(self):
- return "<DebugControl options=%r raw_output=%r>" % (self.options, self.raw_output)
-
- def should(self, option):
- """Decide whether to output debug information in category `option`."""
- if option == "callers" and self.suppress_callers:
- return False
- return (option in self.options)
-
- @contextlib.contextmanager
- def without_callers(self):
- """A context manager to prevent call stacks from being logged."""
- old = self.suppress_callers
- self.suppress_callers = True
- try:
- yield
- finally:
- self.suppress_callers = old
-
- def write(self, msg):
- """Write a line of debug output.
-
- `msg` is the line to write. A newline will be appended.
-
- """
- self.output.write(msg+"\n")
- if self.should('self'):
- caller_self = inspect.stack()[1][0].f_locals.get('self')
- if caller_self is not None:
- self.output.write("self: {!r}\n".format(caller_self))
- if self.should('callers'):
- dump_stack_frames(out=self.output, skip=1)
- self.output.flush()
-
-
-class DebugControlString(DebugControl):
- """A `DebugControl` that writes to a StringIO, for testing."""
- def __init__(self, options):
- super(DebugControlString, self).__init__(options, StringIO())
-
- def get_output(self):
- """Get the output text from the `DebugControl`."""
- return self.raw_output.getvalue()
-
-
-class NoDebugging(object):
- """A replacement for DebugControl that will never try to do anything."""
- def should(self, option): # pylint: disable=unused-argument
- """Should we write debug messages? Never."""
- return False
-
-
-def info_header(label):
- """Make a nice header string."""
- return "--{:-<60s}".format(" "+label+" ")
-
-
-def info_formatter(info):
- """Produce a sequence of formatted lines from info.
-
- `info` is a sequence of pairs (label, data). The produced lines are
- nicely formatted, ready to print.
-
- """
- info = list(info)
- if not info:
- return
- label_len = 30
- assert all(len(l) < label_len for l, _ in info)
- for label, data in info:
- if data == []:
- data = "-none-"
- if isinstance(data, (list, set, tuple)):
- prefix = "%*s:" % (label_len, label)
- for e in data:
- yield "%*s %s" % (label_len+1, prefix, e)
- prefix = ""
- else:
- yield "%*s: %s" % (label_len, label, data)
-
-
-def write_formatted_info(writer, header, info):
- """Write a sequence of (label,data) pairs nicely."""
- writer.write(info_header(header))
- for line in info_formatter(info):
- writer.write(" %s" % line)
-
-
-def short_stack(limit=None, skip=0):
- """Return a string summarizing the call stack.
-
- The string is multi-line, with one line per stack frame. Each line shows
- the function name, the file name, and the line number:
-
- ...
- start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
- import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
- import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
- ...
-
- `limit` is the number of frames to include, defaulting to all of them.
-
- `skip` is the number of frames to skip, so that debugging functions can
- call this and not be included in the result.
-
- """
- stack = inspect.stack()[limit:skip:-1]
- return "\n".join("%30s : %s:%d" % (t[3], t[1], t[2]) for t in stack)
-
-
-def dump_stack_frames(limit=None, out=None, skip=0):
- """Print a summary of the stack to stdout, or someplace else."""
- out = out or sys.stdout
- out.write(short_stack(limit=limit, skip=skip+1))
- out.write("\n")
-
-
-def clipped_repr(text, numchars=50):
- """`repr(text)`, but limited to `numchars`."""
- r = reprlib.Repr()
- r.maxstring = numchars
- return r.repr(text)
-
-
-def short_id(id64):
- """Given a 64-bit id, make a shorter 16-bit one."""
- id16 = 0
- for offset in range(0, 64, 16):
- id16 ^= id64 >> offset
- return id16 & 0xFFFF
-
-
-def add_pid_and_tid(text):
- """A filter to add pid and tid to debug messages."""
- # Thread ids are useful, but too long. Make a shorter one.
- tid = "{:04x}".format(short_id(_thread.get_ident()))
- text = "{:5d}.{}: {}".format(os.getpid(), tid, text)
- return text
-
-
-class SimpleReprMixin(object):
- """A mixin implementing a simple __repr__."""
- simple_repr_ignore = ['simple_repr_ignore', '$coverage.object_id']
-
- def __repr__(self):
- show_attrs = (
- (k, v) for k, v in self.__dict__.items()
- if getattr(v, "show_repr_attr", True)
- and not callable(v)
- and k not in self.simple_repr_ignore
- )
- return "<{klass} @0x{id:x} {attrs}>".format(
- klass=self.__class__.__name__,
- id=id(self),
- attrs=" ".join("{}={!r}".format(k, v) for k, v in show_attrs),
- )
-
-
-def simplify(v): # pragma: debugging
- """Turn things which are nearly dict/list/etc into dict/list/etc."""
- if isinstance(v, dict):
- return {k:simplify(vv) for k, vv in v.items()}
- elif isinstance(v, (list, tuple)):
- return type(v)(simplify(vv) for vv in v)
- elif hasattr(v, "__dict__"):
- return simplify({'.'+k: v for k, v in v.__dict__.items()})
- else:
- return v
-
-
-def pp(v): # pragma: debugging
- """Debug helper to pretty-print data, including SimpleNamespace objects."""
- # Might not be needed in 3.9+
- pprint.pprint(simplify(v))
-
-
-def filter_text(text, filters):
- """Run `text` through a series of filters.
-
- `filters` is a list of functions. Each takes a string and returns a
- string. Each is run in turn.
-
- Returns: the final string that results after all of the filters have
- run.
-
- """
- clean_text = text.rstrip()
- ending = text[len(clean_text):]
- text = clean_text
- for fn in filters:
- lines = []
- for line in text.splitlines():
- lines.extend(fn(line).splitlines())
- text = "\n".join(lines)
- return text + ending
-
-
-class CwdTracker(object): # pragma: debugging
- """A class to add cwd info to debug messages."""
- def __init__(self):
- self.cwd = None
-
- def filter(self, text):
- """Add a cwd message for each new cwd."""
- cwd = os.getcwd()
- if cwd != self.cwd:
- text = "cwd is now {!r}\n".format(cwd) + text
- self.cwd = cwd
- return text
-
-
-class DebugOutputFile(object): # pragma: debugging
- """A file-like object that includes pid and cwd information."""
- def __init__(self, outfile, show_process, filters):
- self.outfile = outfile
- self.show_process = show_process
- self.filters = list(filters)
-
- if self.show_process:
- self.filters.insert(0, CwdTracker().filter)
- self.write("New process: executable: %r\n" % (sys.executable,))
- self.write("New process: cmd: %r\n" % (getattr(sys, 'argv', None),))
- if hasattr(os, 'getppid'):
- self.write("New process: pid: %r, parent pid: %r\n" % (os.getpid(), os.getppid()))
-
- SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one'
-
- @classmethod
- def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False):
- """Get a DebugOutputFile.
-
- If `fileobj` is provided, then a new DebugOutputFile is made with it.
-
- If `fileobj` isn't provided, then a file is chosen
- (COVERAGE_DEBUG_FILE, or stderr), and a process-wide singleton
- DebugOutputFile is made.
-
- `show_process` controls whether the debug file adds process-level
- information, and filters is a list of other message filters to apply.
-
- `filters` are the text filters to apply to the stream to annotate with
- pids, etc.
-
- If `interim` is true, then a future `get_one` can replace this one.
-
- """
- if fileobj is not None:
- # Make DebugOutputFile around the fileobj passed.
- return cls(fileobj, show_process, filters)
-
- # Because of the way igor.py deletes and re-imports modules,
- # this class can be defined more than once. But we really want
- # a process-wide singleton. So stash it in sys.modules instead of
- # on a class attribute. Yes, this is aggressively gross.
- the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True))
- if the_one is None or is_interim:
- if fileobj is None:
- debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
- if debug_file_name:
- fileobj = open(debug_file_name, "a")
- else:
- fileobj = sys.stderr
- the_one = cls(fileobj, show_process, filters)
- sys.modules[cls.SYS_MOD_NAME] = (the_one, interim)
- return the_one
-
- def write(self, text):
- """Just like file.write, but filter through all our filters."""
- self.outfile.write(filter_text(text, self.filters))
- self.outfile.flush()
-
- def flush(self):
- """Flush our file."""
- self.outfile.flush()
-
-
-def log(msg, stack=False): # pragma: debugging
- """Write a log message as forcefully as possible."""
- out = DebugOutputFile.get_one(interim=True)
- out.write(msg+"\n")
- if stack:
- dump_stack_frames(out=out, skip=1)
-
-
-def decorate_methods(decorator, butnot=(), private=False): # pragma: debugging
- """A class decorator to apply a decorator to methods."""
- def _decorator(cls):
- for name, meth in inspect.getmembers(cls, inspect.isroutine):
- if name not in cls.__dict__:
- continue
- if name != "__init__":
- if not private and name.startswith("_"):
- continue
- if name in butnot:
- continue
- setattr(cls, name, decorator(meth))
- return cls
- return _decorator
-
-
-def break_in_pudb(func): # pragma: debugging
- """A function decorator to stop in the debugger for each call."""
- @functools.wraps(func)
- def _wrapper(*args, **kwargs):
- import pudb
- sys.stdout = sys.__stdout__
- pudb.set_trace()
- return func(*args, **kwargs)
- return _wrapper
-
-
-OBJ_IDS = itertools.count()
-CALLS = itertools.count()
-OBJ_ID_ATTR = "$coverage.object_id"
-
-def show_calls(show_args=True, show_stack=False, show_return=False): # pragma: debugging
- """A method decorator to debug-log each call to the function."""
- def _decorator(func):
- @functools.wraps(func)
- def _wrapper(self, *args, **kwargs):
- oid = getattr(self, OBJ_ID_ATTR, None)
- if oid is None:
- oid = "{:08d} {:04d}".format(os.getpid(), next(OBJ_IDS))
- setattr(self, OBJ_ID_ATTR, oid)
- extra = ""
- if show_args:
- eargs = ", ".join(map(repr, args))
- ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items())
- extra += "("
- extra += eargs
- if eargs and ekwargs:
- extra += ", "
- extra += ekwargs
- extra += ")"
- if show_stack:
- extra += " @ "
- extra += "; ".join(_clean_stack_line(l) for l in short_stack().splitlines())
- callid = next(CALLS)
- msg = "{} {:04d} {}{}\n".format(oid, callid, func.__name__, extra)
- DebugOutputFile.get_one(interim=True).write(msg)
- ret = func(self, *args, **kwargs)
- if show_return:
- msg = "{} {:04d} {} return {!r}\n".format(oid, callid, func.__name__, ret)
- DebugOutputFile.get_one(interim=True).write(msg)
- return ret
- return _wrapper
- return _decorator
-
-
-def _clean_stack_line(s): # pragma: debugging
- """Simplify some paths in a stack trace, for compactness."""
- s = s.strip()
- s = s.replace(os.path.dirname(__file__) + '/', '')
- s = s.replace(os.path.dirname(os.__file__) + '/', '')
- s = s.replace(sys.prefix + '/', '')
- return s
diff --git a/contrib/python/coverage/py2/coverage/disposition.py b/contrib/python/coverage/py2/coverage/disposition.py
deleted file mode 100644
index 9b9a997d8a..0000000000
--- a/contrib/python/coverage/py2/coverage/disposition.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Simple value objects for tracking what to do with files."""
-
-
-class FileDisposition(object):
- """A simple value type for recording what to do with a file."""
- pass
-
-
-# FileDisposition "methods": FileDisposition is a pure value object, so it can
-# be implemented in either C or Python. Acting on them is done with these
-# functions.
-
-def disposition_init(cls, original_filename):
- """Construct and initialize a new FileDisposition object."""
- disp = cls()
- disp.original_filename = original_filename
- disp.canonical_filename = original_filename
- disp.source_filename = None
- disp.trace = False
- disp.reason = ""
- disp.file_tracer = None
- disp.has_dynamic_filename = False
- return disp
-
-
-def disposition_debug_msg(disp):
- """Make a nice debug message of what the FileDisposition is doing."""
- if disp.trace:
- msg = "Tracing %r" % (disp.original_filename,)
- if disp.file_tracer:
- msg += ": will be traced by %r" % disp.file_tracer
- else:
- msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason)
- return msg
diff --git a/contrib/python/coverage/py2/coverage/env.py b/contrib/python/coverage/py2/coverage/env.py
deleted file mode 100644
index ea78a5be89..0000000000
--- a/contrib/python/coverage/py2/coverage/env.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Determine facts about the environment."""
-
-import os
-import platform
-import sys
-
-# Operating systems.
-WINDOWS = sys.platform == "win32"
-LINUX = sys.platform.startswith("linux")
-
-# Python implementations.
-CPYTHON = (platform.python_implementation() == "CPython")
-PYPY = (platform.python_implementation() == "PyPy")
-JYTHON = (platform.python_implementation() == "Jython")
-IRONPYTHON = (platform.python_implementation() == "IronPython")
-
-# Python versions. We amend version_info with one more value, a zero if an
-# official version, or 1 if built from source beyond an official version.
-PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),)
-PY2 = PYVERSION < (3, 0)
-PY3 = PYVERSION >= (3, 0)
-
-if PYPY:
- PYPYVERSION = sys.pypy_version_info
-
-PYPY2 = PYPY and PY2
-PYPY3 = PYPY and PY3
-
-# Python behavior.
-class PYBEHAVIOR(object):
- """Flags indicating this Python's behavior."""
-
- pep626 = CPYTHON and (PYVERSION > (3, 10, 0, 'alpha', 4))
-
- # Is "if __debug__" optimized away?
- if PYPY3:
- optimize_if_debug = True
- elif PYPY2:
- optimize_if_debug = False
- else:
- optimize_if_debug = not pep626
-
- # Is "if not __debug__" optimized away?
- optimize_if_not_debug = (not PYPY) and (PYVERSION >= (3, 7, 0, 'alpha', 4))
- if pep626:
- optimize_if_not_debug = False
- if PYPY3:
- optimize_if_not_debug = True
-
- # Is "if not __debug__" optimized away even better?
- optimize_if_not_debug2 = (not PYPY) and (PYVERSION >= (3, 8, 0, 'beta', 1))
- if pep626:
- optimize_if_not_debug2 = False
-
- # Do we have yield-from?
- yield_from = (PYVERSION >= (3, 3))
-
- # Do we have PEP 420 namespace packages?
- namespaces_pep420 = (PYVERSION >= (3, 3))
-
- # Do .pyc files have the source file size recorded in them?
- size_in_pyc = (PYVERSION >= (3, 3))
-
- # Do we have async and await syntax?
- async_syntax = (PYVERSION >= (3, 5))
-
- # PEP 448 defined additional unpacking generalizations
- unpackings_pep448 = (PYVERSION >= (3, 5))
-
- # Can co_lnotab have negative deltas?
- negative_lnotab = (PYVERSION >= (3, 6)) and not (PYPY and PYPYVERSION < (7, 2))
-
- # Do .pyc files conform to PEP 552? Hash-based pyc's.
- hashed_pyc_pep552 = (PYVERSION >= (3, 7, 0, 'alpha', 4))
-
- # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It
- # used to be an empty string (meaning the current directory). It changed
- # to be the actual path to the current directory, so that os.chdir wouldn't
- # affect the outcome.
- actual_syspath0_dash_m = CPYTHON and (PYVERSION >= (3, 7, 0, 'beta', 3))
-
- # 3.7 changed how functions with only docstrings are numbered.
- docstring_only_function = (not PYPY) and ((3, 7, 0, 'beta', 5) <= PYVERSION <= (3, 10))
-
- # When a break/continue/return statement in a try block jumps to a finally
- # block, does the finally block do the break/continue/return (pre-3.8), or
- # does the finally jump back to the break/continue/return (3.8) to do the
- # work?
- finally_jumps_back = ((3, 8) <= PYVERSION < (3, 10))
-
- # When a function is decorated, does the trace function get called for the
- # @-line and also the def-line (new behavior in 3.8)? Or just the @-line
- # (old behavior)?
- trace_decorated_def = (PYVERSION >= (3, 8))
-
- # Are while-true loops optimized into absolute jumps with no loop setup?
- nix_while_true = (PYVERSION >= (3, 8))
-
- # Python 3.9a1 made sys.argv[0] and other reported files absolute paths.
- report_absolute_files = (PYVERSION >= (3, 9))
-
- # Lines after break/continue/return/raise are no longer compiled into the
- # bytecode. They used to be marked as missing, now they aren't executable.
- omit_after_jump = pep626
-
- # PyPy has always omitted statements after return.
- omit_after_return = omit_after_jump or PYPY
-
- # Modules used to have firstlineno equal to the line number of the first
- # real line of code. Now they always start at 1.
- module_firstline_1 = pep626
-
- # Are "if 0:" lines (and similar) kept in the compiled code?
- keep_constant_test = pep626
-
-# Coverage.py specifics.
-
-# Are we using the C-implemented trace function?
-C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
-
-# Are we coverage-measuring ourselves?
-METACOV = os.getenv('COVERAGE_COVERAGE', '') != ''
-
-# Are we running our test suite?
-# Even when running tests, you can use COVERAGE_TESTING=0 to disable the
-# test-specific behavior like contracts.
-TESTING = os.getenv('COVERAGE_TESTING', '') == 'True'
diff --git a/contrib/python/coverage/py2/coverage/execfile.py b/contrib/python/coverage/py2/coverage/execfile.py
deleted file mode 100644
index 29409d517a..0000000000
--- a/contrib/python/coverage/py2/coverage/execfile.py
+++ /dev/null
@@ -1,362 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Execute files of Python code."""
-
-import inspect
-import marshal
-import os
-import struct
-import sys
-import types
-
-from coverage import env
-from coverage.backward import BUILTINS
-from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
-from coverage.files import canonical_filename, python_reported_file
-from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
-from coverage.phystokens import compile_unicode
-from coverage.python import get_python_source
-
-os = isolate_module(os)
-
-
-class DummyLoader(object):
- """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
-
- Currently only implements the .fullname attribute
- """
- def __init__(self, fullname, *_args):
- self.fullname = fullname
-
-
-if importlib_util_find_spec:
- def find_module(modulename):
- """Find the module named `modulename`.
-
- Returns the file path of the module, the name of the enclosing
- package, and the spec.
- """
- try:
- spec = importlib_util_find_spec(modulename)
- except ImportError as err:
- raise NoSource(str(err))
- if not spec:
- raise NoSource("No module named %r" % (modulename,))
- pathname = spec.origin
- packagename = spec.name
- if spec.submodule_search_locations:
- mod_main = modulename + ".__main__"
- spec = importlib_util_find_spec(mod_main)
- if not spec:
- raise NoSource(
- "No module named %s; "
- "%r is a package and cannot be directly executed"
- % (mod_main, modulename)
- )
- pathname = spec.origin
- packagename = spec.name
- packagename = packagename.rpartition(".")[0]
- return pathname, packagename, spec
-else:
- def find_module(modulename):
- """Find the module named `modulename`.
-
- Returns the file path of the module, the name of the enclosing
- package, and None (where a spec would have been).
- """
- openfile = None
- glo, loc = globals(), locals()
- try:
- # Search for the module - inside its parent package, if any - using
- # standard import mechanics.
- if '.' in modulename:
- packagename, name = modulename.rsplit('.', 1)
- package = __import__(packagename, glo, loc, ['__path__'])
- searchpath = package.__path__
- else:
- packagename, name = None, modulename
- searchpath = None # "top-level search" in imp.find_module()
- openfile, pathname, _ = imp.find_module(name, searchpath)
-
- # Complain if this is a magic non-file module.
- if openfile is None and pathname is None:
- raise NoSource(
- "module does not live in a file: %r" % modulename
- )
-
- # If `modulename` is actually a package, not a mere module, then we
- # pretend to be Python 2.7 and try running its __main__.py script.
- if openfile is None:
- packagename = modulename
- name = '__main__'
- package = __import__(packagename, glo, loc, ['__path__'])
- searchpath = package.__path__
- openfile, pathname, _ = imp.find_module(name, searchpath)
- except ImportError as err:
- raise NoSource(str(err))
- finally:
- if openfile:
- openfile.close()
-
- return pathname, packagename, None
-
-
-class PyRunner(object):
- """Multi-stage execution of Python code.
-
- This is meant to emulate real Python execution as closely as possible.
-
- """
- def __init__(self, args, as_module=False):
- self.args = args
- self.as_module = as_module
-
- self.arg0 = args[0]
- self.package = self.modulename = self.pathname = self.loader = self.spec = None
-
- def prepare(self):
- """Set sys.path properly.
-
- This needs to happen before any importing, and without importing anything.
- """
- if self.as_module:
- if env.PYBEHAVIOR.actual_syspath0_dash_m:
- path0 = os.getcwd()
- else:
- path0 = ""
- elif os.path.isdir(self.arg0):
- # Running a directory means running the __main__.py file in that
- # directory.
- path0 = self.arg0
- else:
- path0 = os.path.abspath(os.path.dirname(self.arg0))
-
- if os.path.isdir(sys.path[0]):
- # sys.path fakery. If we are being run as a command, then sys.path[0]
- # is the directory of the "coverage" script. If this is so, replace
- # sys.path[0] with the directory of the file we're running, or the
- # current directory when running modules. If it isn't so, then we
- # don't know what's going on, and just leave it alone.
- top_file = inspect.stack()[-1][0].f_code.co_filename
- sys_path_0_abs = os.path.abspath(sys.path[0])
- top_file_dir_abs = os.path.abspath(os.path.dirname(top_file))
- sys_path_0_abs = canonical_filename(sys_path_0_abs)
- top_file_dir_abs = canonical_filename(top_file_dir_abs)
- if sys_path_0_abs != top_file_dir_abs:
- path0 = None
-
- else:
- # sys.path[0] is a file. Is the next entry the directory containing
- # that file?
- if sys.path[1] == os.path.dirname(sys.path[0]):
- # Can it be right to always remove that?
- del sys.path[1]
-
- if path0 is not None:
- sys.path[0] = python_reported_file(path0)
-
- def _prepare2(self):
- """Do more preparation to run Python code.
-
- Includes finding the module to run and adjusting sys.argv[0].
- This method is allowed to import code.
-
- """
- if self.as_module:
- self.modulename = self.arg0
- pathname, self.package, self.spec = find_module(self.modulename)
- if self.spec is not None:
- self.modulename = self.spec.name
- self.loader = DummyLoader(self.modulename)
- self.pathname = os.path.abspath(pathname)
- self.args[0] = self.arg0 = self.pathname
- elif os.path.isdir(self.arg0):
- # Running a directory means running the __main__.py file in that
- # directory.
- for ext in [".py", ".pyc", ".pyo"]:
- try_filename = os.path.join(self.arg0, "__main__" + ext)
- if os.path.exists(try_filename):
- self.arg0 = try_filename
- break
- else:
- raise NoSource("Can't find '__main__' module in '%s'" % self.arg0)
-
- if env.PY2:
- self.arg0 = os.path.abspath(self.arg0)
-
- # Make a spec. I don't know if this is the right way to do it.
- try:
- import importlib.machinery
- except ImportError:
- pass
- else:
- try_filename = python_reported_file(try_filename)
- self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
- self.spec.has_location = True
- self.package = ""
- self.loader = DummyLoader("__main__")
- else:
- if env.PY3:
- self.loader = DummyLoader("__main__")
-
- self.arg0 = python_reported_file(self.arg0)
-
- def run(self):
- """Run the Python code!"""
-
- self._prepare2()
-
- # Create a module to serve as __main__
- main_mod = types.ModuleType('__main__')
-
- from_pyc = self.arg0.endswith((".pyc", ".pyo"))
- main_mod.__file__ = self.arg0
- if from_pyc:
- main_mod.__file__ = main_mod.__file__[:-1]
- if self.package is not None:
- main_mod.__package__ = self.package
- main_mod.__loader__ = self.loader
- if self.spec is not None:
- main_mod.__spec__ = self.spec
-
- main_mod.__builtins__ = BUILTINS
-
- sys.modules['__main__'] = main_mod
-
- # Set sys.argv properly.
- sys.argv = self.args
-
- try:
- # Make a code object somehow.
- if from_pyc:
- code = make_code_from_pyc(self.arg0)
- else:
- code = make_code_from_py(self.arg0)
- except CoverageException:
- raise
- except Exception as exc:
- msg = "Couldn't run '{filename}' as Python code: {exc.__class__.__name__}: {exc}"
- raise CoverageException(msg.format(filename=self.arg0, exc=exc))
-
- # Execute the code object.
- # Return to the original directory in case the test code exits in
- # a non-existent directory.
- cwd = os.getcwd()
- try:
- exec(code, main_mod.__dict__)
- except SystemExit: # pylint: disable=try-except-raise
- # The user called sys.exit(). Just pass it along to the upper
- # layers, where it will be handled.
- raise
- except Exception:
- # Something went wrong while executing the user code.
- # Get the exc_info, and pack them into an exception that we can
- # throw up to the outer loop. We peel one layer off the traceback
- # so that the coverage.py code doesn't appear in the final printed
- # traceback.
- typ, err, tb = sys.exc_info()
-
- # PyPy3 weirdness. If I don't access __context__, then somehow it
- # is non-None when the exception is reported at the upper layer,
- # and a nested exception is shown to the user. This getattr fixes
- # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
- getattr(err, '__context__', None)
-
- # Call the excepthook.
- try:
- if hasattr(err, "__traceback__"):
- err.__traceback__ = err.__traceback__.tb_next
- sys.excepthook(typ, err, tb.tb_next)
- except SystemExit: # pylint: disable=try-except-raise
- raise
- except Exception:
- # Getting the output right in the case of excepthook
- # shenanigans is kind of involved.
- sys.stderr.write("Error in sys.excepthook:\n")
- typ2, err2, tb2 = sys.exc_info()
- err2.__suppress_context__ = True
- if hasattr(err2, "__traceback__"):
- err2.__traceback__ = err2.__traceback__.tb_next
- sys.__excepthook__(typ2, err2, tb2.tb_next)
- sys.stderr.write("\nOriginal exception was:\n")
- raise ExceptionDuringRun(typ, err, tb.tb_next)
- else:
- sys.exit(1)
- finally:
- os.chdir(cwd)
-
-
-def run_python_module(args):
- """Run a Python module, as though with ``python -m name args...``.
-
- `args` is the argument array to present as sys.argv, including the first
- element naming the module being executed.
-
- This is a helper for tests, to encapsulate how to use PyRunner.
-
- """
- runner = PyRunner(args, as_module=True)
- runner.prepare()
- runner.run()
-
-
-def run_python_file(args):
- """Run a Python file as if it were the main program on the command line.
-
- `args` is the argument array to present as sys.argv, including the first
- element naming the file being executed. `package` is the name of the
- enclosing package, if any.
-
- This is a helper for tests, to encapsulate how to use PyRunner.
-
- """
- runner = PyRunner(args, as_module=False)
- runner.prepare()
- runner.run()
-
-
-def make_code_from_py(filename):
- """Get source from `filename` and make a code object of it."""
- # Open the source file.
- try:
- source = get_python_source(filename)
- except (IOError, NoSource):
- raise NoSource("No file to run: '%s'" % filename)
-
- code = compile_unicode(source, filename, "exec")
- return code
-
-
-def make_code_from_pyc(filename):
- """Get a code object from a .pyc file."""
- try:
- fpyc = open(filename, "rb")
- except IOError:
- raise NoCode("No file to run: '%s'" % filename)
-
- with fpyc:
- # First four bytes are a version-specific magic number. It has to
- # match or we won't run the file.
- magic = fpyc.read(4)
- if magic != PYC_MAGIC_NUMBER:
- raise NoCode("Bad magic number in .pyc file: {} != {}".format(magic, PYC_MAGIC_NUMBER))
-
- date_based = True
- if env.PYBEHAVIOR.hashed_pyc_pep552:
- flags = struct.unpack('<L', fpyc.read(4))[0]
- hash_based = flags & 0x01
- if hash_based:
- fpyc.read(8) # Skip the hash.
- date_based = False
- if date_based:
- # Skip the junk in the header that we don't need.
- fpyc.read(4) # Skip the moddate.
- if env.PYBEHAVIOR.size_in_pyc:
- # 3.3 added another long to the header (size), skip it.
- fpyc.read(4)
-
- # The rest of the file is the code object we want.
- code = marshal.load(fpyc)
-
- return code
diff --git a/contrib/python/coverage/py2/coverage/files.py b/contrib/python/coverage/py2/coverage/files.py
deleted file mode 100644
index 5133ad07f3..0000000000
--- a/contrib/python/coverage/py2/coverage/files.py
+++ /dev/null
@@ -1,441 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""File wrangling."""
-
-import hashlib
-import fnmatch
-import ntpath
-import os
-import os.path
-import posixpath
-import re
-import sys
-
-from coverage import env
-from coverage.backward import unicode_class
-from coverage.misc import contract, CoverageException, join_regex, isolate_module
-
-
-os = isolate_module(os)
-
-
-def set_relative_directory():
- """Set the directory that `relative_filename` will be relative to."""
- global RELATIVE_DIR, CANONICAL_FILENAME_CACHE
-
- # The absolute path to our current directory.
- RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep)
-
- # Cache of results of calling the canonical_filename() method, to
- # avoid duplicating work.
- CANONICAL_FILENAME_CACHE = {}
-
-
-def relative_directory():
- """Return the directory that `relative_filename` is relative to."""
- return RELATIVE_DIR
-
-
-@contract(returns='unicode')
-def relative_filename(filename):
- """Return the relative form of `filename`.
-
- The file name will be relative to the current directory when the
- `set_relative_directory` was called.
-
- """
- fnorm = os.path.normcase(filename)
- if fnorm.startswith(RELATIVE_DIR):
- filename = filename[len(RELATIVE_DIR):]
- return unicode_filename(filename)
-
-
-@contract(returns='unicode')
-def canonical_filename(filename):
- """Return a canonical file name for `filename`.
-
- An absolute path with no redundant components and normalized case.
-
- """
- if filename not in CANONICAL_FILENAME_CACHE:
- cf = filename
- if not os.path.isabs(filename):
- for path in [os.curdir] + sys.path:
- if path is None:
- continue
- f = os.path.join(path, filename)
- try:
- exists = os.path.exists(f)
- except UnicodeError:
- exists = False
- if exists:
- cf = f
- break
- cf = abs_file(cf)
- CANONICAL_FILENAME_CACHE[filename] = cf
- return CANONICAL_FILENAME_CACHE[filename]
-
-if getattr(sys, 'is_standalone_binary', False):
- # filename for py files in binary is already canonical,
- # it's relative to the arcadia root
- def canonical_filename(filename):
- # next assert does not needed in case when we load coverage from not arcadia source in arcadia binary
- # assert not filename.startswith("/"), filename
- return filename
-
-MAX_FLAT = 200
-
-@contract(filename='unicode', returns='unicode')
-def flat_rootname(filename):
- """A base for a flat file name to correspond to this file.
-
- Useful for writing files about the code where you want all the files in
- the same directory, but need to differentiate same-named files from
- different directories.
-
- For example, the file a/b/c.py will return 'a_b_c_py'
-
- """
- name = ntpath.splitdrive(filename)[1]
- name = re.sub(r"[\\/.:]", "_", name)
- if len(name) > MAX_FLAT:
- h = hashlib.sha1(name.encode('UTF-8')).hexdigest()
- name = name[-(MAX_FLAT-len(h)-1):] + '_' + h
- return name
-
-
-if env.WINDOWS:
-
- _ACTUAL_PATH_CACHE = {}
- _ACTUAL_PATH_LIST_CACHE = {}
-
- def actual_path(path):
- """Get the actual path of `path`, including the correct case."""
- if env.PY2 and isinstance(path, unicode_class):
- path = path.encode(sys.getfilesystemencoding())
- if path in _ACTUAL_PATH_CACHE:
- return _ACTUAL_PATH_CACHE[path]
-
- head, tail = os.path.split(path)
- if not tail:
- # This means head is the drive spec: normalize it.
- actpath = head.upper()
- elif not head:
- actpath = tail
- else:
- head = actual_path(head)
- if head in _ACTUAL_PATH_LIST_CACHE:
- files = _ACTUAL_PATH_LIST_CACHE[head]
- else:
- try:
- files = os.listdir(head)
- except Exception:
- # This will raise OSError, or this bizarre TypeError:
- # https://bugs.python.org/issue1776160
- files = []
- _ACTUAL_PATH_LIST_CACHE[head] = files
- normtail = os.path.normcase(tail)
- for f in files:
- if os.path.normcase(f) == normtail:
- tail = f
- break
- actpath = os.path.join(head, tail)
- _ACTUAL_PATH_CACHE[path] = actpath
- return actpath
-
-else:
- def actual_path(filename):
- """The actual path for non-Windows platforms."""
- return filename
-
-
-if env.PY2:
- @contract(returns='unicode')
- def unicode_filename(filename):
- """Return a Unicode version of `filename`."""
- if isinstance(filename, str):
- encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
- filename = filename.decode(encoding, "replace")
- return filename
-else:
- @contract(filename='unicode', returns='unicode')
- def unicode_filename(filename):
- """Return a Unicode version of `filename`."""
- return filename
-
-
-@contract(returns='unicode')
-def abs_file(path):
- """Return the absolute normalized form of `path`."""
- try:
- path = os.path.realpath(path)
- except UnicodeError:
- pass
- path = os.path.abspath(path)
- path = actual_path(path)
- path = unicode_filename(path)
- return path
-
-
-def python_reported_file(filename):
- """Return the string as Python would describe this file name."""
- if env.PYBEHAVIOR.report_absolute_files:
- filename = os.path.abspath(filename)
- return filename
-
-
-RELATIVE_DIR = None
-CANONICAL_FILENAME_CACHE = None
-set_relative_directory()
-
-
-def isabs_anywhere(filename):
- """Is `filename` an absolute path on any OS?"""
- return ntpath.isabs(filename) or posixpath.isabs(filename)
-
-
-def prep_patterns(patterns):
- """Prepare the file patterns for use in a `FnmatchMatcher`.
-
- If a pattern starts with a wildcard, it is used as a pattern
- as-is. If it does not start with a wildcard, then it is made
- absolute with the current directory.
-
- If `patterns` is None, an empty list is returned.
-
- """
- prepped = []
- for p in patterns or []:
- if p.startswith(("*", "?")):
- prepped.append(p)
- else:
- prepped.append(abs_file(p))
- return prepped
-
-
-class TreeMatcher(object):
- """A matcher for files in a tree.
-
- Construct with a list of paths, either files or directories. Paths match
- with the `match` method if they are one of the files, or if they are
- somewhere in a subtree rooted at one of the directories.
-
- """
- def __init__(self, paths):
- self.paths = list(paths)
-
- def __repr__(self):
- return "<TreeMatcher %r>" % self.paths
-
- def info(self):
- """A list of strings for displaying when dumping state."""
- return self.paths
-
- def match(self, fpath):
- """Does `fpath` indicate a file in one of our trees?"""
- for p in self.paths:
- if fpath.startswith(p):
- if fpath == p:
- # This is the same file!
- return True
- if fpath[len(p)] == os.sep:
- # This is a file in the directory
- return True
- return False
-
-
-class ModuleMatcher(object):
- """A matcher for modules in a tree."""
- def __init__(self, module_names):
- self.modules = list(module_names)
-
- def __repr__(self):
- return "<ModuleMatcher %r>" % (self.modules)
-
- def info(self):
- """A list of strings for displaying when dumping state."""
- return self.modules
-
- def match(self, module_name):
- """Does `module_name` indicate a module in one of our packages?"""
- if not module_name:
- return False
-
- for m in self.modules:
- if module_name.startswith(m):
- if module_name == m:
- return True
- if module_name[len(m)] == '.':
- # This is a module in the package
- return True
-
- return False
-
-
-class FnmatchMatcher(object):
- """A matcher for files by file name pattern."""
- def __init__(self, pats):
- self.pats = list(pats)
- self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS)
-
- def __repr__(self):
- return "<FnmatchMatcher %r>" % self.pats
-
- def info(self):
- """A list of strings for displaying when dumping state."""
- return self.pats
-
- def match(self, fpath):
- """Does `fpath` match one of our file name patterns?"""
- return self.re.match(fpath) is not None
-
-
-def sep(s):
- """Find the path separator used in this string, or os.sep if none."""
- sep_match = re.search(r"[\\/]", s)
- if sep_match:
- the_sep = sep_match.group(0)
- else:
- the_sep = os.sep
- return the_sep
-
-
-def fnmatches_to_regex(patterns, case_insensitive=False, partial=False):
- """Convert fnmatch patterns to a compiled regex that matches any of them.
-
- Slashes are always converted to match either slash or backslash, for
- Windows support, even when running elsewhere.
-
- If `partial` is true, then the pattern will match if the target string
- starts with the pattern. Otherwise, it must match the entire string.
-
- Returns: a compiled regex object. Use the .match method to compare target
- strings.
-
- """
- regexes = (fnmatch.translate(pattern) for pattern in patterns)
- # Python3.7 fnmatch translates "/" as "/". Before that, it translates as "\/",
- # so we have to deal with maybe a backslash.
- regexes = (re.sub(r"\\?/", r"[\\\\/]", regex) for regex in regexes)
-
- if partial:
- # fnmatch always adds a \Z to match the whole string, which we don't
- # want, so we remove the \Z. While removing it, we only replace \Z if
- # followed by paren (introducing flags), or at end, to keep from
- # destroying a literal \Z in the pattern.
- regexes = (re.sub(r'\\Z(\(\?|$)', r'\1', regex) for regex in regexes)
-
- flags = 0
- if case_insensitive:
- flags |= re.IGNORECASE
- compiled = re.compile(join_regex(regexes), flags=flags)
-
- return compiled
-
-
-class PathAliases(object):
- """A collection of aliases for paths.
-
- When combining data files from remote machines, often the paths to source
- code are different, for example, due to OS differences, or because of
- serialized checkouts on continuous integration machines.
-
- A `PathAliases` object tracks a list of pattern/result pairs, and can
- map a path through those aliases to produce a unified path.
-
- """
- def __init__(self):
- self.aliases = []
-
- def pprint(self): # pragma: debugging
- """Dump the important parts of the PathAliases, for debugging."""
- for regex, result in self.aliases:
- print("{!r} --> {!r}".format(regex.pattern, result))
-
- def add(self, pattern, result):
- """Add the `pattern`/`result` pair to the list of aliases.
-
- `pattern` is an `fnmatch`-style pattern. `result` is a simple
- string. When mapping paths, if a path starts with a match against
- `pattern`, then that match is replaced with `result`. This models
- isomorphic source trees being rooted at different places on two
- different machines.
-
- `pattern` can't end with a wildcard component, since that would
- match an entire tree, and not just its root.
-
- """
- pattern_sep = sep(pattern)
-
- if len(pattern) > 1:
- pattern = pattern.rstrip(r"\/")
-
- # The pattern can't end with a wildcard component.
- if pattern.endswith("*"):
- raise CoverageException("Pattern must not end with wildcards.")
-
- # The pattern is meant to match a filepath. Let's make it absolute
- # unless it already is, or is meant to match any prefix.
- if not pattern.startswith('*') and not isabs_anywhere(pattern +
- pattern_sep):
- pattern = abs_file(pattern)
- if not pattern.endswith(pattern_sep):
- pattern += pattern_sep
-
- # Make a regex from the pattern.
- regex = fnmatches_to_regex([pattern], case_insensitive=True, partial=True)
-
- # Normalize the result: it must end with a path separator.
- result_sep = sep(result)
- result = result.rstrip(r"\/") + result_sep
- self.aliases.append((regex, result))
-
- def map(self, path):
- """Map `path` through the aliases.
-
- `path` is checked against all of the patterns. The first pattern to
- match is used to replace the root of the path with the result root.
- Only one pattern is ever used. If no patterns match, `path` is
- returned unchanged.
-
- The separator style in the result is made to match that of the result
- in the alias.
-
- Returns the mapped path. If a mapping has happened, this is a
- canonical path. If no mapping has happened, it is the original value
- of `path` unchanged.
-
- """
- for regex, result in self.aliases:
- m = regex.match(path)
- if m:
- new = path.replace(m.group(0), result)
- new = new.replace(sep(path), sep(result))
- new = canonical_filename(new)
- return new
- return path
-
-
-def find_python_files(dirname):
- """Yield all of the importable Python files in `dirname`, recursively.
-
- To be importable, the files have to be in a directory with a __init__.py,
- except for `dirname` itself, which isn't required to have one. The
- assumption is that `dirname` was specified directly, so the user knows
- best, but sub-directories are checked for a __init__.py to be sure we only
- find the importable files.
-
- """
- for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)):
- if i > 0 and '__init__.py' not in filenames:
- # If a directory doesn't have __init__.py, then it isn't
- # importable and neither are its files
- del dirnames[:]
- continue
- for filename in filenames:
- # We're only interested in files that look like reasonable Python
- # files: Must end with .py or .pyw, and must not have certain funny
- # characters that probably mean they are editor junk.
- if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename):
- yield os.path.join(dirpath, filename)
diff --git a/contrib/python/coverage/py2/coverage/fullcoverage/encodings.py b/contrib/python/coverage/py2/coverage/fullcoverage/encodings.py
deleted file mode 100644
index aeb416e406..0000000000
--- a/contrib/python/coverage/py2/coverage/fullcoverage/encodings.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Imposter encodings module that installs a coverage-style tracer.
-
-This is NOT the encodings module; it is an imposter that sets up tracing
-instrumentation and then replaces itself with the real encodings module.
-
-If the directory that holds this file is placed first in the PYTHONPATH when
-using "coverage" to run Python's tests, then this file will become the very
-first module imported by the internals of Python 3. It installs a
-coverage.py-compatible trace function that can watch Standard Library modules
-execute from the very earliest stages of Python's own boot process. This fixes
-a problem with coverage.py - that it starts too late to trace the coverage of
-many of the most fundamental modules in the Standard Library.
-
-"""
-
-import sys
-
-class FullCoverageTracer(object):
- def __init__(self):
- # `traces` is a list of trace events. Frames are tricky: the same
- # frame object is used for a whole scope, with new line numbers
- # written into it. So in one scope, all the frame objects are the
- # same object, and will eventually all will point to the last line
- # executed. So we keep the line numbers alongside the frames.
- # The list looks like:
- #
- # traces = [
- # ((frame, event, arg), lineno), ...
- # ]
- #
- self.traces = []
-
- def fullcoverage_trace(self, *args):
- frame, event, arg = args
- self.traces.append((args, frame.f_lineno))
- return self.fullcoverage_trace
-
-sys.settrace(FullCoverageTracer().fullcoverage_trace)
-
-# In coverage/files.py is actual_filename(), which uses glob.glob. I don't
-# understand why, but that use of glob borks everything if fullcoverage is in
-# effect. So here we make an ugly hail-mary pass to switch off glob.glob over
-# there. This means when using fullcoverage, Windows path names will not be
-# their actual case.
-
-#sys.fullcoverage = True
-
-# Finally, remove our own directory from sys.path; remove ourselves from
-# sys.modules; and re-import "encodings", which will be the real package
-# this time. Note that the delete from sys.modules dictionary has to
-# happen last, since all of the symbols in this module will become None
-# at that exact moment, including "sys".
-
-parentdir = max(filter(__file__.startswith, sys.path), key=len)
-sys.path.remove(parentdir)
-del sys.modules['encodings']
-import encodings
diff --git a/contrib/python/coverage/py2/coverage/html.py b/contrib/python/coverage/py2/coverage/html.py
deleted file mode 100644
index 9d8e342716..0000000000
--- a/contrib/python/coverage/py2/coverage/html.py
+++ /dev/null
@@ -1,539 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""HTML reporting for coverage.py."""
-
-import datetime
-import json
-import os
-import re
-import shutil
-import sys
-
-import coverage
-from coverage import env
-from coverage.backward import iitems, SimpleNamespace, format_local_datetime
-from coverage.data import add_data_to_hash
-from coverage.files import flat_rootname
-from coverage.misc import CoverageException, ensure_dir, file_be_gone, Hasher, isolate_module
-from coverage.report import get_analysis_to_report
-from coverage.results import Numbers
-from coverage.templite import Templite
-
-os = isolate_module(os)
-
-
-# Static files are looked for in a list of places.
-STATIC_PATH = [
- # The place Debian puts system Javascript libraries.
- "/usr/share/javascript",
-
- # Our htmlfiles directory.
- os.path.join(os.path.dirname(__file__), "htmlfiles"),
-]
-
-
-def data_filename(fname, pkgdir=""):
- """Return the path to a data file of ours.
-
- The file is searched for on `STATIC_PATH`, and the first place it's found,
- is returned.
-
- Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
- is provided, at that sub-directory.
-
- """
- tried = []
- for static_dir in STATIC_PATH:
- static_filename = os.path.join(static_dir, fname)
- if os.path.exists(static_filename):
- return static_filename
- else:
- tried.append(static_filename)
- if pkgdir:
- static_filename = os.path.join(static_dir, pkgdir, fname)
- if os.path.exists(static_filename):
- return static_filename
- else:
- tried.append(static_filename)
- raise CoverageException(
- "Couldn't find static file %r from %r, tried: %r" % (fname, os.getcwd(), tried)
- )
-
-
-def get_htmlfiles_resource(name):
- import pkgutil
- return pkgutil.get_data(__package__, 'htmlfiles/' + name)
-
-
-def read_data(fname):
- """Return the contents of a data file of ours."""
- if getattr(sys, 'is_standalone_binary', False):
- res_buf = get_htmlfiles_resource(fname).decode()
- if res_buf is not None:
- return res_buf
-
- with open(data_filename(fname)) as data_file:
- return data_file.read()
-
-
-def write_html(fname, html):
- """Write `html` to `fname`, properly encoded."""
- html = re.sub(r"(\A\s+)|(\s+$)", "", html, flags=re.MULTILINE) + "\n"
- with open(fname, "wb") as fout:
- fout.write(html.encode('ascii', 'xmlcharrefreplace'))
-
-
-class HtmlDataGeneration(object):
- """Generate structured data to be turned into HTML reports."""
-
- EMPTY = "(empty)"
-
- def __init__(self, cov):
- self.coverage = cov
- self.config = self.coverage.config
- data = self.coverage.get_data()
- self.has_arcs = data.has_arcs()
- if self.config.show_contexts:
- if data.measured_contexts() == {""}:
- self.coverage._warn("No contexts were measured")
- data.set_query_contexts(self.config.report_contexts)
-
- def data_for_file(self, fr, analysis):
- """Produce the data needed for one file's report."""
- if self.has_arcs:
- missing_branch_arcs = analysis.missing_branch_arcs()
- arcs_executed = analysis.arcs_executed()
-
- if self.config.show_contexts:
- contexts_by_lineno = analysis.data.contexts_by_lineno(analysis.filename)
-
- lines = []
-
- for lineno, tokens in enumerate(fr.source_token_lines(), start=1):
- # Figure out how to mark this line.
- category = None
- short_annotations = []
- long_annotations = []
-
- if lineno in analysis.excluded:
- category = 'exc'
- elif lineno in analysis.missing:
- category = 'mis'
- elif self.has_arcs and lineno in missing_branch_arcs:
- category = 'par'
- for b in missing_branch_arcs[lineno]:
- if b < 0:
- short_annotations.append("exit")
- else:
- short_annotations.append(b)
- long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed))
- elif lineno in analysis.statements:
- category = 'run'
-
- contexts = contexts_label = None
- context_list = None
- if category and self.config.show_contexts:
- contexts = sorted(c or self.EMPTY for c in contexts_by_lineno[lineno])
- if contexts == [self.EMPTY]:
- contexts_label = self.EMPTY
- else:
- contexts_label = "{} ctx".format(len(contexts))
- context_list = contexts
-
- lines.append(SimpleNamespace(
- tokens=tokens,
- number=lineno,
- category=category,
- statement=(lineno in analysis.statements),
- contexts=contexts,
- contexts_label=contexts_label,
- context_list=context_list,
- short_annotations=short_annotations,
- long_annotations=long_annotations,
- ))
-
- file_data = SimpleNamespace(
- relative_filename=fr.relative_filename(),
- nums=analysis.numbers,
- lines=lines,
- )
-
- return file_data
-
-
-class HtmlReporter(object):
- """HTML reporting."""
-
- # These files will be copied from the htmlfiles directory to the output
- # directory.
- STATIC_FILES = [
- ("style.css", ""),
- ("jquery.min.js", "jquery"),
- ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"),
- ("jquery.hotkeys.js", "jquery-hotkeys"),
- ("jquery.isonscreen.js", "jquery-isonscreen"),
- ("jquery.tablesorter.min.js", "jquery-tablesorter"),
- ("coverage_html.js", ""),
- ("keybd_closed.png", ""),
- ("keybd_open.png", ""),
- ("favicon_32.png", ""),
- ]
-
- def __init__(self, cov):
- self.coverage = cov
- self.config = self.coverage.config
- self.directory = self.config.html_dir
-
- self.skip_covered = self.config.html_skip_covered
- if self.skip_covered is None:
- self.skip_covered = self.config.skip_covered
- self.skip_empty = self.config.html_skip_empty
- if self.skip_empty is None:
- self.skip_empty= self.config.skip_empty
-
- title = self.config.html_title
- if env.PY2:
- title = title.decode("utf8")
-
- if self.config.extra_css:
- self.extra_css = os.path.basename(self.config.extra_css)
- else:
- self.extra_css = None
-
- self.data = self.coverage.get_data()
- self.has_arcs = self.data.has_arcs()
-
- self.file_summaries = []
- self.all_files_nums = []
- self.incr = IncrementalChecker(self.directory)
- self.datagen = HtmlDataGeneration(self.coverage)
- self.totals = Numbers()
-
- self.template_globals = {
- # Functions available in the templates.
- 'escape': escape,
- 'pair': pair,
- 'len': len,
-
- # Constants for this report.
- '__url__': coverage.__url__,
- '__version__': coverage.__version__,
- 'title': title,
- 'time_stamp': format_local_datetime(datetime.datetime.now()),
- 'extra_css': self.extra_css,
- 'has_arcs': self.has_arcs,
- 'show_contexts': self.config.show_contexts,
-
- # Constants for all reports.
- # These css classes determine which lines are highlighted by default.
- 'category': {
- 'exc': 'exc show_exc',
- 'mis': 'mis show_mis',
- 'par': 'par run show_par',
- 'run': 'run',
- }
- }
- self.pyfile_html_source = read_data("pyfile.html")
- self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)
-
- def report(self, morfs):
- """Generate an HTML report for `morfs`.
-
- `morfs` is a list of modules or file names.
-
- """
- # Read the status data and check that this run used the same
- # global data as the last run.
- self.incr.read()
- self.incr.check_global_data(self.config, self.pyfile_html_source)
-
- # Process all the files.
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.html_file(fr, analysis)
-
- if not self.all_files_nums:
- raise CoverageException("No data to report.")
-
- self.totals = sum(self.all_files_nums)
-
- # Write the index file.
- self.index_file()
-
- self.make_local_static_report_files()
- return self.totals.n_statements and self.totals.pc_covered
-
- def make_local_static_report_files(self):
- """Make local instances of static files for HTML report."""
- # The files we provide must always be copied.
- for static, pkgdir in self.STATIC_FILES:
- if getattr(sys, 'is_standalone_binary', False):
- data = get_htmlfiles_resource(static)
- if data is None:
- raise IOError("No such resource: " + static)
-
- with open(os.path.join(self.directory, static), "wb") as afile:
- afile.write(data)
- else:
- shutil.copyfile(
- data_filename(static, pkgdir),
- os.path.join(self.directory, static)
- )
-
- # The user may have extra CSS they want copied.
- if self.extra_css:
- shutil.copyfile(
- self.config.extra_css,
- os.path.join(self.directory, self.extra_css)
- )
-
- def html_file(self, fr, analysis):
- """Generate an HTML file for one source file."""
- rootname = flat_rootname(fr.relative_filename())
- html_filename = rootname + ".html"
- ensure_dir(self.directory)
- html_path = os.path.join(self.directory, html_filename)
-
- # Get the numbers for this file.
- nums = analysis.numbers
- self.all_files_nums.append(nums)
-
- if self.skip_covered:
- # Don't report on 100% files.
- no_missing_lines = (nums.n_missing == 0)
- no_missing_branches = (nums.n_partial_branches == 0)
- if no_missing_lines and no_missing_branches:
- # If there's an existing file, remove it.
- file_be_gone(html_path)
- return
-
- if self.skip_empty:
- # Don't report on empty files.
- if nums.n_statements == 0:
- file_be_gone(html_path)
- return
-
- # Find out if the file on disk is already correct.
- if self.incr.can_skip_file(self.data, fr, rootname):
- self.file_summaries.append(self.incr.index_info(rootname))
- return
-
- # Write the HTML page for this file.
- file_data = self.datagen.data_for_file(fr, analysis)
- for ldata in file_data.lines:
- # Build the HTML for the line.
- html = []
- for tok_type, tok_text in ldata.tokens:
- if tok_type == "ws":
- html.append(escape(tok_text))
- else:
- tok_html = escape(tok_text) or '&nbsp;'
- html.append(
- u'<span class="{}">{}</span>'.format(tok_type, tok_html)
- )
- ldata.html = ''.join(html)
-
- if ldata.short_annotations:
- # 202F is NARROW NO-BREAK SPACE.
- # 219B is RIGHTWARDS ARROW WITH STROKE.
- ldata.annotate = u",&nbsp;&nbsp; ".join(
- u"{}&#x202F;&#x219B;&#x202F;{}".format(ldata.number, d)
- for d in ldata.short_annotations
- )
- else:
- ldata.annotate = None
-
- if ldata.long_annotations:
- longs = ldata.long_annotations
- if len(longs) == 1:
- ldata.annotate_long = longs[0]
- else:
- ldata.annotate_long = u"{:d} missed branches: {}".format(
- len(longs),
- u", ".join(
- u"{:d}) {}".format(num, ann_long)
- for num, ann_long in enumerate(longs, start=1)
- ),
- )
- else:
- ldata.annotate_long = None
-
- css_classes = []
- if ldata.category:
- css_classes.append(self.template_globals['category'][ldata.category])
- ldata.css_class = ' '.join(css_classes) or "pln"
-
- html = self.source_tmpl.render(file_data.__dict__)
- write_html(html_path, html)
-
- # Save this file's information for the index file.
- index_info = {
- 'nums': nums,
- 'html_filename': html_filename,
- 'relative_filename': fr.relative_filename(),
- }
- self.file_summaries.append(index_info)
- self.incr.set_index_info(rootname, index_info)
-
- def index_file(self):
- """Write the index.html file for this report."""
- index_tmpl = Templite(read_data("index.html"), self.template_globals)
-
- html = index_tmpl.render({
- 'files': self.file_summaries,
- 'totals': self.totals,
- })
-
- write_html(os.path.join(self.directory, "index.html"), html)
-
- # Write the latest hashes for next time.
- self.incr.write()
-
-
-class IncrementalChecker(object):
- """Logic and data to support incremental reporting."""
-
- STATUS_FILE = "status.json"
- STATUS_FORMAT = 2
-
- # pylint: disable=wrong-spelling-in-comment,useless-suppression
- # The data looks like:
- #
- # {
- # "format": 2,
- # "globals": "540ee119c15d52a68a53fe6f0897346d",
- # "version": "4.0a1",
- # "files": {
- # "cogapp___init__": {
- # "hash": "e45581a5b48f879f301c0f30bf77a50c",
- # "index": {
- # "html_filename": "cogapp___init__.html",
- # "relative_filename": "cogapp/__init__",
- # "nums": [ 1, 14, 0, 0, 0, 0, 0 ]
- # }
- # },
- # ...
- # "cogapp_whiteutils": {
- # "hash": "8504bb427fc488c4176809ded0277d51",
- # "index": {
- # "html_filename": "cogapp_whiteutils.html",
- # "relative_filename": "cogapp/whiteutils",
- # "nums": [ 1, 59, 0, 1, 28, 2, 2 ]
- # }
- # }
- # }
- # }
-
- def __init__(self, directory):
- self.directory = directory
- self.reset()
-
- def reset(self):
- """Initialize to empty. Causes all files to be reported."""
- self.globals = ''
- self.files = {}
-
- def read(self):
- """Read the information we stored last time."""
- usable = False
- try:
- status_file = os.path.join(self.directory, self.STATUS_FILE)
- with open(status_file) as fstatus:
- status = json.load(fstatus)
- except (IOError, ValueError):
- usable = False
- else:
- usable = True
- if status['format'] != self.STATUS_FORMAT:
- usable = False
- elif status['version'] != coverage.__version__:
- usable = False
-
- if usable:
- self.files = {}
- for filename, fileinfo in iitems(status['files']):
- fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums'])
- self.files[filename] = fileinfo
- self.globals = status['globals']
- else:
- self.reset()
-
- def write(self):
- """Write the current status."""
- status_file = os.path.join(self.directory, self.STATUS_FILE)
- files = {}
- for filename, fileinfo in iitems(self.files):
- fileinfo['index']['nums'] = fileinfo['index']['nums'].init_args()
- files[filename] = fileinfo
-
- status = {
- 'format': self.STATUS_FORMAT,
- 'version': coverage.__version__,
- 'globals': self.globals,
- 'files': files,
- }
- with open(status_file, "w") as fout:
- json.dump(status, fout, separators=(',', ':'))
-
- def check_global_data(self, *data):
- """Check the global data that can affect incremental reporting."""
- m = Hasher()
- for d in data:
- m.update(d)
- these_globals = m.hexdigest()
- if self.globals != these_globals:
- self.reset()
- self.globals = these_globals
-
- def can_skip_file(self, data, fr, rootname):
- """Can we skip reporting this file?
-
- `data` is a CoverageData object, `fr` is a `FileReporter`, and
- `rootname` is the name being used for the file.
- """
- m = Hasher()
- m.update(fr.source().encode('utf-8'))
- add_data_to_hash(data, fr.filename, m)
- this_hash = m.hexdigest()
-
- that_hash = self.file_hash(rootname)
-
- if this_hash == that_hash:
- # Nothing has changed to require the file to be reported again.
- return True
- else:
- self.set_file_hash(rootname, this_hash)
- return False
-
- def file_hash(self, fname):
- """Get the hash of `fname`'s contents."""
- return self.files.get(fname, {}).get('hash', '')
-
- def set_file_hash(self, fname, val):
- """Set the hash of `fname`'s contents."""
- self.files.setdefault(fname, {})['hash'] = val
-
- def index_info(self, fname):
- """Get the information for index.html for `fname`."""
- return self.files.get(fname, {}).get('index', {})
-
- def set_index_info(self, fname, info):
- """Set the information for index.html for `fname`."""
- self.files.setdefault(fname, {})['index'] = info
-
-
-# Helpers for templates and generating HTML
-
-def escape(t):
- """HTML-escape the text in `t`.
-
- This is only suitable for HTML text, not attributes.
-
- """
- # Convert HTML special chars into HTML entities.
- return t.replace("&", "&amp;").replace("<", "&lt;")
-
-
-def pair(ratio):
- """Format a pair of numbers so JavaScript can read them in an attribute."""
- return "%s %s" % ratio
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js b/contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js
deleted file mode 100644
index 27b49b36f9..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js
+++ /dev/null
@@ -1,616 +0,0 @@
-// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-// Coverage.py HTML report browser code.
-/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
-/*global coverage: true, document, window, $ */
-
-coverage = {};
-
-// Find all the elements with shortkey_* class, and use them to assign a shortcut key.
-coverage.assign_shortkeys = function () {
- $("*[class*='shortkey_']").each(function (i, e) {
- $.each($(e).attr("class").split(" "), function (i, c) {
- if (/^shortkey_/.test(c)) {
- $(document).bind('keydown', c.substr(9), function () {
- $(e).click();
- });
- }
- });
- });
-};
-
-// Create the events for the help panel.
-coverage.wire_up_help_panel = function () {
- $("#keyboard_icon").click(function () {
- // Show the help panel, and position it so the keyboard icon in the
- // panel is in the same place as the keyboard icon in the header.
- $(".help_panel").show();
- var koff = $("#keyboard_icon").offset();
- var poff = $("#panel_icon").position();
- $(".help_panel").offset({
- top: koff.top-poff.top,
- left: koff.left-poff.left
- });
- });
- $("#panel_icon").click(function () {
- $(".help_panel").hide();
- });
-};
-
-// Create the events for the filter box.
-coverage.wire_up_filter = function () {
- // Cache elements.
- var table = $("table.index");
- var table_rows = table.find("tbody tr");
- var table_row_names = table_rows.find("td.name a");
- var no_rows = $("#no_rows");
-
- // Create a duplicate table footer that we can modify with dynamic summed values.
- var table_footer = $("table.index tfoot tr");
- var table_dynamic_footer = table_footer.clone();
- table_dynamic_footer.attr('class', 'total_dynamic hidden');
- table_footer.after(table_dynamic_footer);
-
- // Observe filter keyevents.
- $("#filter").on("keyup change", $.debounce(150, function (event) {
- var filter_value = $(this).val();
-
- if (filter_value === "") {
- // Filter box is empty, remove all filtering.
- table_rows.removeClass("hidden");
-
- // Show standard footer, hide dynamic footer.
- table_footer.removeClass("hidden");
- table_dynamic_footer.addClass("hidden");
-
- // Hide placeholder, show table.
- if (no_rows.length > 0) {
- no_rows.hide();
- }
- table.show();
-
- }
- else {
- // Filter table items by value.
- var hidden = 0;
- var shown = 0;
-
- // Hide / show elements.
- $.each(table_row_names, function () {
- var element = $(this).parents("tr");
-
- if ($(this).text().indexOf(filter_value) === -1) {
- // hide
- element.addClass("hidden");
- hidden++;
- }
- else {
- // show
- element.removeClass("hidden");
- shown++;
- }
- });
-
- // Show placeholder if no rows will be displayed.
- if (no_rows.length > 0) {
- if (shown === 0) {
- // Show placeholder, hide table.
- no_rows.show();
- table.hide();
- }
- else {
- // Hide placeholder, show table.
- no_rows.hide();
- table.show();
- }
- }
-
- // Manage dynamic header:
- if (hidden > 0) {
- // Calculate new dynamic sum values based on visible rows.
- for (var column = 2; column < 20; column++) {
- // Calculate summed value.
- var cells = table_rows.find('td:nth-child(' + column + ')');
- if (!cells.length) {
- // No more columns...!
- break;
- }
-
- var sum = 0, numer = 0, denom = 0;
- $.each(cells.filter(':visible'), function () {
- var ratio = $(this).data("ratio");
- if (ratio) {
- var splitted = ratio.split(" ");
- numer += parseInt(splitted[0], 10);
- denom += parseInt(splitted[1], 10);
- }
- else {
- sum += parseInt(this.innerHTML, 10);
- }
- });
-
- // Get footer cell element.
- var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')');
-
- // Set value into dynamic footer cell element.
- if (cells[0].innerHTML.indexOf('%') > -1) {
- // Percentage columns use the numerator and denominator,
- // and adapt to the number of decimal places.
- var match = /\.([0-9]+)/.exec(cells[0].innerHTML);
- var places = 0;
- if (match) {
- places = match[1].length;
- }
- var pct = numer * 100 / denom;
- footer_cell.text(pct.toFixed(places) + '%');
- }
- else {
- footer_cell.text(sum);
- }
- }
-
- // Hide standard footer, show dynamic footer.
- table_footer.addClass("hidden");
- table_dynamic_footer.removeClass("hidden");
- }
- else {
- // Show standard footer, hide dynamic footer.
- table_footer.removeClass("hidden");
- table_dynamic_footer.addClass("hidden");
- }
- }
- }));
-
- // Trigger change event on setup, to force filter on page refresh
- // (filter value may still be present).
- $("#filter").trigger("change");
-};
-
-// Loaded on index.html
-coverage.index_ready = function ($) {
- // Look for a localStorage item containing previous sort settings:
- var sort_list = [];
- var storage_name = "COVERAGE_INDEX_SORT";
- var stored_list = undefined;
- try {
- stored_list = localStorage.getItem(storage_name);
- } catch(err) {}
-
- if (stored_list) {
- sort_list = JSON.parse('[[' + stored_list + ']]');
- }
-
- // Create a new widget which exists only to save and restore
- // the sort order:
- $.tablesorter.addWidget({
- id: "persistentSort",
-
- // Format is called by the widget before displaying:
- format: function (table) {
- if (table.config.sortList.length === 0 && sort_list.length > 0) {
- // This table hasn't been sorted before - we'll use
- // our stored settings:
- $(table).trigger('sorton', [sort_list]);
- }
- else {
- // This is not the first load - something has
- // already defined sorting so we'll just update
- // our stored value to match:
- sort_list = table.config.sortList;
- }
- }
- });
-
- // Configure our tablesorter to handle the variable number of
- // columns produced depending on report options:
- var headers = [];
- var col_count = $("table.index > thead > tr > th").length;
-
- headers[0] = { sorter: 'text' };
- for (i = 1; i < col_count-1; i++) {
- headers[i] = { sorter: 'digit' };
- }
- headers[col_count-1] = { sorter: 'percent' };
-
- // Enable the table sorter:
- $("table.index").tablesorter({
- widgets: ['persistentSort'],
- headers: headers
- });
-
- coverage.assign_shortkeys();
- coverage.wire_up_help_panel();
- coverage.wire_up_filter();
-
- // Watch for page unload events so we can save the final sort settings:
- $(window).on("unload", function () {
- try {
- localStorage.setItem(storage_name, sort_list.toString())
- } catch(err) {}
- });
-};
-
-// -- pyfile stuff --
-
-coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
-
-coverage.pyfile_ready = function ($) {
- // If we're directed to a particular line number, highlight the line.
- var frag = location.hash;
- if (frag.length > 2 && frag[1] === 't') {
- $(frag).addClass('highlight');
- coverage.set_sel(parseInt(frag.substr(2), 10));
- }
- else {
- coverage.set_sel(0);
- }
-
- $(document)
- .bind('keydown', 'j', coverage.to_next_chunk_nicely)
- .bind('keydown', 'k', coverage.to_prev_chunk_nicely)
- .bind('keydown', '0', coverage.to_top)
- .bind('keydown', '1', coverage.to_first_chunk)
- ;
-
- $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");});
- $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");});
- $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");});
- $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");});
-
- coverage.filters = undefined;
- try {
- coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
- } catch(err) {}
-
- if (coverage.filters) {
- coverage.filters = JSON.parse(coverage.filters);
- }
- else {
- coverage.filters = {run: false, exc: true, mis: true, par: true};
- }
-
- for (cls in coverage.filters) {
- coverage.set_line_visibilty(cls, coverage.filters[cls]);
- }
-
- coverage.assign_shortkeys();
- coverage.wire_up_help_panel();
-
- coverage.init_scroll_markers();
-
- // Rebuild scroll markers when the window height changes.
- $(window).resize(coverage.build_scroll_markers);
-};
-
-coverage.toggle_lines = function (btn, cls) {
- var onoff = !$(btn).hasClass("show_" + cls);
- coverage.set_line_visibilty(cls, onoff);
- coverage.build_scroll_markers();
- coverage.filters[cls] = onoff;
- try {
- localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
- } catch(err) {}
-};
-
-coverage.set_line_visibilty = function (cls, onoff) {
- var show = "show_" + cls;
- var btn = $(".button_toggle_" + cls);
- if (onoff) {
- $("#source ." + cls).addClass(show);
- btn.addClass(show);
- }
- else {
- $("#source ." + cls).removeClass(show);
- btn.removeClass(show);
- }
-};
-
-// Return the nth line div.
-coverage.line_elt = function (n) {
- return $("#t" + n);
-};
-
-// Return the nth line number div.
-coverage.num_elt = function (n) {
- return $("#n" + n);
-};
-
-// Set the selection. b and e are line numbers.
-coverage.set_sel = function (b, e) {
- // The first line selected.
- coverage.sel_begin = b;
- // The next line not selected.
- coverage.sel_end = (e === undefined) ? b+1 : e;
-};
-
-coverage.to_top = function () {
- coverage.set_sel(0, 1);
- coverage.scroll_window(0);
-};
-
-coverage.to_first_chunk = function () {
- coverage.set_sel(0, 1);
- coverage.to_next_chunk();
-};
-
-// Return a string indicating what kind of chunk this line belongs to,
-// or null if not a chunk.
-coverage.chunk_indicator = function (line_elt) {
- var klass = line_elt.attr('class');
- if (klass) {
- var m = klass.match(/\bshow_\w+\b/);
- if (m) {
- return m[0];
- }
- }
- return null;
-};
-
-coverage.to_next_chunk = function () {
- var c = coverage;
-
- // Find the start of the next colored chunk.
- var probe = c.sel_end;
- var chunk_indicator, probe_line;
- while (true) {
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- chunk_indicator = c.chunk_indicator(probe_line);
- if (chunk_indicator) {
- break;
- }
- probe++;
- }
-
- // There's a next chunk, `probe` points to it.
- var begin = probe;
-
- // Find the end of this chunk.
- var next_indicator = chunk_indicator;
- while (next_indicator === chunk_indicator) {
- probe++;
- probe_line = c.line_elt(probe);
- next_indicator = c.chunk_indicator(probe_line);
- }
- c.set_sel(begin, probe);
- c.show_selection();
-};
-
-coverage.to_prev_chunk = function () {
- var c = coverage;
-
- // Find the end of the prev colored chunk.
- var probe = c.sel_begin-1;
- var probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- var chunk_indicator = c.chunk_indicator(probe_line);
- while (probe > 0 && !chunk_indicator) {
- probe--;
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- chunk_indicator = c.chunk_indicator(probe_line);
- }
-
- // There's a prev chunk, `probe` points to its last line.
- var end = probe+1;
-
- // Find the beginning of this chunk.
- var prev_indicator = chunk_indicator;
- while (prev_indicator === chunk_indicator) {
- probe--;
- probe_line = c.line_elt(probe);
- prev_indicator = c.chunk_indicator(probe_line);
- }
- c.set_sel(probe+1, end);
- c.show_selection();
-};
-
-// Return the line number of the line nearest pixel position pos
-coverage.line_at_pos = function (pos) {
- var l1 = coverage.line_elt(1),
- l2 = coverage.line_elt(2),
- result;
- if (l1.length && l2.length) {
- var l1_top = l1.offset().top,
- line_height = l2.offset().top - l1_top,
- nlines = (pos - l1_top) / line_height;
- if (nlines < 1) {
- result = 1;
- }
- else {
- result = Math.ceil(nlines);
- }
- }
- else {
- result = 1;
- }
- return result;
-};
-
-// Returns 0, 1, or 2: how many of the two ends of the selection are on
-// the screen right now?
-coverage.selection_ends_on_screen = function () {
- if (coverage.sel_begin === 0) {
- return 0;
- }
-
- var top = coverage.line_elt(coverage.sel_begin);
- var next = coverage.line_elt(coverage.sel_end-1);
-
- return (
- (top.isOnScreen() ? 1 : 0) +
- (next.isOnScreen() ? 1 : 0)
- );
-};
-
-coverage.to_next_chunk_nicely = function () {
- coverage.finish_scrolling();
- if (coverage.selection_ends_on_screen() === 0) {
- // The selection is entirely off the screen: select the top line on
- // the screen.
- var win = $(window);
- coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop()));
- }
- coverage.to_next_chunk();
-};
-
-coverage.to_prev_chunk_nicely = function () {
- coverage.finish_scrolling();
- if (coverage.selection_ends_on_screen() === 0) {
- var win = $(window);
- coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height()));
- }
- coverage.to_prev_chunk();
-};
-
-// Select line number lineno, or if it is in a colored chunk, select the
-// entire chunk
-coverage.select_line_or_chunk = function (lineno) {
- var c = coverage;
- var probe_line = c.line_elt(lineno);
- if (probe_line.length === 0) {
- return;
- }
- var the_indicator = c.chunk_indicator(probe_line);
- if (the_indicator) {
- // The line is in a highlighted chunk.
- // Search backward for the first line.
- var probe = lineno;
- var indicator = the_indicator;
- while (probe > 0 && indicator === the_indicator) {
- probe--;
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- break;
- }
- indicator = c.chunk_indicator(probe_line);
- }
- var begin = probe + 1;
-
- // Search forward for the last line.
- probe = lineno;
- indicator = the_indicator;
- while (indicator === the_indicator) {
- probe++;
- probe_line = c.line_elt(probe);
- indicator = c.chunk_indicator(probe_line);
- }
-
- coverage.set_sel(begin, probe);
- }
- else {
- coverage.set_sel(lineno);
- }
-};
-
-coverage.show_selection = function () {
- var c = coverage;
-
- // Highlight the lines in the chunk
- $(".linenos .highlight").removeClass("highlight");
- for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
- c.num_elt(probe).addClass("highlight");
- }
-
- c.scroll_to_selection();
-};
-
-coverage.scroll_to_selection = function () {
- // Scroll the page if the chunk isn't fully visible.
- if (coverage.selection_ends_on_screen() < 2) {
- // Need to move the page. The html,body trick makes it scroll in all
- // browsers, got it from http://stackoverflow.com/questions/3042651
- var top = coverage.line_elt(coverage.sel_begin);
- var top_pos = parseInt(top.offset().top, 10);
- coverage.scroll_window(top_pos - 30);
- }
-};
-
-coverage.scroll_window = function (to_pos) {
- $("html,body").animate({scrollTop: to_pos}, 200);
-};
-
-coverage.finish_scrolling = function () {
- $("html,body").stop(true, true);
-};
-
-coverage.init_scroll_markers = function () {
- var c = coverage;
- // Init some variables
- c.lines_len = $('#source p').length;
- c.body_h = $('body').height();
- c.header_h = $('div#header').height();
-
- // Build html
- c.build_scroll_markers();
-};
-
-coverage.build_scroll_markers = function () {
- var c = coverage,
- min_line_height = 3,
- max_line_height = 10,
- visible_window_h = $(window).height();
-
- c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par');
- $('#scroll_marker').remove();
- // Don't build markers if the window has no scroll bar.
- if (c.body_h <= visible_window_h) {
- return;
- }
-
- $("body").append("<div id='scroll_marker'>&nbsp;</div>");
- var scroll_marker = $('#scroll_marker'),
- marker_scale = scroll_marker.height() / c.body_h,
- line_height = scroll_marker.height() / c.lines_len;
-
- // Line height must be between the extremes.
- if (line_height > min_line_height) {
- if (line_height > max_line_height) {
- line_height = max_line_height;
- }
- }
- else {
- line_height = min_line_height;
- }
-
- var previous_line = -99,
- last_mark,
- last_top,
- offsets = {};
-
- // Calculate line offsets outside loop to prevent relayouts
- c.lines_to_mark.each(function() {
- offsets[this.id] = $(this).offset().top;
- });
- c.lines_to_mark.each(function () {
- var id_name = $(this).attr('id'),
- line_top = Math.round(offsets[id_name] * marker_scale),
- line_number = parseInt(id_name.substring(1, id_name.length));
-
- if (line_number === previous_line + 1) {
- // If this solid missed block just make previous mark higher.
- last_mark.css({
- 'height': line_top + line_height - last_top
- });
- }
- else {
- // Add colored line in scroll_marker block.
- scroll_marker.append('<div id="m' + line_number + '" class="marker"></div>');
- last_mark = $('#m' + line_number);
- last_mark.css({
- 'height': line_height,
- 'top': line_top
- });
- last_top = line_top;
- }
-
- previous_line = line_number;
- });
-};
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.png b/contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.png
deleted file mode 100644
index 8649f0475d..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.png
+++ /dev/null
Binary files differ
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/index.html b/contrib/python/coverage/py2/coverage/htmlfiles/index.html
deleted file mode 100644
index 983db06125..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/index.html
+++ /dev/null
@@ -1,119 +0,0 @@
-{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
-{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
-
-<!DOCTYPE html>
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>{{ title|escape }}</title>
- <link rel="icon" sizes="32x32" href="favicon_32.png">
- <link rel="stylesheet" href="style.css" type="text/css">
- {% if extra_css %}
- <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
- {% endif %}
- <script type="text/javascript" src="jquery.min.js"></script>
- <script type="text/javascript" src="jquery.ba-throttle-debounce.min.js"></script>
- <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
- <script type="text/javascript" src="jquery.hotkeys.js"></script>
- <script type="text/javascript" src="coverage_html.js"></script>
- <script type="text/javascript">
- jQuery(document).ready(coverage.index_ready);
- </script>
-</head>
-<body class="indexfile">
-
-<div id="header">
- <div class="content">
- <h1>{{ title|escape }}:
- <span class="pc_cov">{{totals.pc_covered_str}}%</span>
- </h1>
-
- <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
-
- <form id="filter_container">
- <input id="filter" type="text" value="" placeholder="filter..." />
- </form>
- </div>
-</div>
-
-<div class="help_panel">
- <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
- <p class="legend">Hot-keys on this page</p>
- <div>
- <p class="keyhelp">
- <span class="key">n</span>
- <span class="key">s</span>
- <span class="key">m</span>
- <span class="key">x</span>
- {% if has_arcs %}
- <span class="key">b</span>
- <span class="key">p</span>
- {% endif %}
- <span class="key">c</span> &nbsp; change column sorting
- </p>
- </div>
-</div>
-
-<div id="index">
- <table class="index">
- <thead>
- {# The title="" attr doesn"t work in Safari. #}
- <tr class="tablehead" title="Click to sort">
- <th class="name left headerSortDown shortkey_n">Module</th>
- <th class="shortkey_s">statements</th>
- <th class="shortkey_m">missing</th>
- <th class="shortkey_x">excluded</th>
- {% if has_arcs %}
- <th class="shortkey_b">branches</th>
- <th class="shortkey_p">partial</th>
- {% endif %}
- <th class="right shortkey_c">coverage</th>
- </tr>
- </thead>
- {# HTML syntax requires thead, tfoot, tbody #}
- <tfoot>
- <tr class="total">
- <td class="name left">Total</td>
- <td>{{totals.n_statements}}</td>
- <td>{{totals.n_missing}}</td>
- <td>{{totals.n_excluded}}</td>
- {% if has_arcs %}
- <td>{{totals.n_branches}}</td>
- <td>{{totals.n_partial_branches}}</td>
- {% endif %}
- <td class="right" data-ratio="{{totals.ratio_covered|pair}}">{{totals.pc_covered_str}}%</td>
- </tr>
- </tfoot>
- <tbody>
- {% for file in files %}
- <tr class="file">
- <td class="name left"><a href="{{file.html_filename}}">{{file.relative_filename}}</a></td>
- <td>{{file.nums.n_statements}}</td>
- <td>{{file.nums.n_missing}}</td>
- <td>{{file.nums.n_excluded}}</td>
- {% if has_arcs %}
- <td>{{file.nums.n_branches}}</td>
- <td>{{file.nums.n_partial_branches}}</td>
- {% endif %}
- <td class="right" data-ratio="{{file.nums.ratio_covered|pair}}">{{file.nums.pc_covered_str}}%</td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
-
- <p id="no_rows">
- No items found using the specified filter.
- </p>
-</div>
-
-<div id="footer">
- <div class="content">
- <p>
- <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
- created at {{ time_stamp }}
- </p>
- </div>
-</div>
-
-</body>
-</html>
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
deleted file mode 100644
index 648fe5d3c2..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * jQuery throttle / debounce - v1.1 - 3/7/2010
- * http://benalman.com/projects/jquery-throttle-debounce-plugin/
- *
- * Copyright (c) 2010 "Cowboy" Ben Alman
- * Dual licensed under the MIT and GPL licenses.
- * http://benalman.com/about/license/
- */
-(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js
deleted file mode 100644
index 09b21e03c7..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * jQuery Hotkeys Plugin
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- *
- * Based upon the plugin by Tzury Bar Yochay:
- * http://github.com/tzuryby/hotkeys
- *
- * Original idea by:
- * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
-*/
-
-(function(jQuery){
-
- jQuery.hotkeys = {
- version: "0.8",
-
- specialKeys: {
- 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
- 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
- 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
- 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
- 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
- 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
- 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
- },
-
- shiftNums: {
- "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
- "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
- ".": ">", "/": "?", "\\": "|"
- }
- };
-
- function keyHandler( handleObj ) {
- // Only care when a possible input has been specified
- if ( typeof handleObj.data !== "string" ) {
- return;
- }
-
- var origHandler = handleObj.handler,
- keys = handleObj.data.toLowerCase().split(" ");
-
- handleObj.handler = function( event ) {
- // Don't fire in text-accepting inputs that we didn't directly bind to
- if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
- event.target.type === "text") ) {
- return;
- }
-
- // Keypress represents characters, not special keys
- var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
- character = String.fromCharCode( event.which ).toLowerCase(),
- key, modif = "", possible = {};
-
- // check combinations (alt|ctrl|shift+anything)
- if ( event.altKey && special !== "alt" ) {
- modif += "alt+";
- }
-
- if ( event.ctrlKey && special !== "ctrl" ) {
- modif += "ctrl+";
- }
-
- // TODO: Need to make sure this works consistently across platforms
- if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
- modif += "meta+";
- }
-
- if ( event.shiftKey && special !== "shift" ) {
- modif += "shift+";
- }
-
- if ( special ) {
- possible[ modif + special ] = true;
-
- } else {
- possible[ modif + character ] = true;
- possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
-
- // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
- if ( modif === "shift+" ) {
- possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
- }
- }
-
- for ( var i = 0, l = keys.length; i < l; i++ ) {
- if ( possible[ keys[i] ] ) {
- return origHandler.apply( this, arguments );
- }
- }
- };
- }
-
- jQuery.each([ "keydown", "keyup", "keypress" ], function() {
- jQuery.event.special[ this ] = { add: keyHandler };
- });
-
-})( jQuery );
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js
deleted file mode 100644
index 0182ebd213..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2010
- * @author Laurence Wheway
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
- *
- * @version 1.2.0
- */
-(function($) {
- jQuery.extend({
- isOnScreen: function(box, container) {
- //ensure numbers come in as intgers (not strings) and remove 'px' is it's there
- for(var i in box){box[i] = parseFloat(box[i])};
- for(var i in container){container[i] = parseFloat(container[i])};
-
- if(!container){
- container = {
- left: $(window).scrollLeft(),
- top: $(window).scrollTop(),
- width: $(window).width(),
- height: $(window).height()
- }
- }
-
- if( box.left+box.width-container.left > 0 &&
- box.left < container.width+container.left &&
- box.top+box.height-container.top > 0 &&
- box.top < container.height+container.top
- ) return true;
- return false;
- }
- })
-
-
- jQuery.fn.isOnScreen = function (container) {
- for(var i in container){container[i] = parseFloat(container[i])};
-
- if(!container){
- container = {
- left: $(window).scrollLeft(),
- top: $(window).scrollTop(),
- width: $(window).width(),
- height: $(window).height()
- }
- }
-
- if( $(this).offset().left+$(this).width()-container.left > 0 &&
- $(this).offset().left < container.width+container.left &&
- $(this).offset().top+$(this).height()-container.top > 0 &&
- $(this).offset().top < container.height+container.top
- ) return true;
- return false;
- }
-})(jQuery);
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js
deleted file mode 100644
index d1608e37ff..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
-if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
-},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m}); \ No newline at end of file
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js
deleted file mode 100644
index 64c7007129..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-
-(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,cells[i]);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,node){var l=parsers.length;for(var i=1;i<l;i++){if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)){return parsers[i];}}return parsers[0];}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=table.tBodies[0].rows[i],cols=[];cache.row.push($(c));for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));}cols.push(i);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){if(!node)return"";var t="";if(config.textExtraction=="simple"){if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){t=node.childNodes[0].innerHTML;}else{t=node.innerHTML;}}else{if(typeof(config.textExtraction)=="function"){t=config.textExtraction(node);}else{t=$(node).text();}}return t;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){rows.push(r[n[i][checkCell]]);if(!table.config.appender){var o=r[n[i][checkCell]];var l=o.length;for(var j=0;j<l;j++){tableBody[0].appendChild(o[j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false,tableHeadersRows=[];for(var i=0;i<table.tHead.rows.length;i++){tableHeadersRows[i]=0;};$tableHeaders=$("thead th",table);$tableHeaders.each(function(index){this.count=0;this.column=index;this.order=formatSortingOrder(table.config.sortInitialOrder);if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(!this.sortDisabled){$(this).addClass(table.config.cssHeader);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){i=(v.toLowerCase()=="desc")?1:0;}else{i=(v==(0||1))?v:0;}return i;}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(getCachedSortType(table.config.parsers,c)=="text")?((order==0)?"sortText":"sortTextDesc"):((order==0)?"sortNumeric":"sortNumericDesc");var e="e"+i;dynamicExp+="var "+e+" = "+s+"(a["+c+"],b["+c+"]); ";dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function sortText(a,b){return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){var DECIMAL='\\'+config.decimal;var exp='/(^[+]?0('+DECIMAL+'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)'+DECIMAL+'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*'+DECIMAL+'0+$)/';return RegExp(exp).test($.trim(s));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}$("tr:visible",table.tBodies[0]).filter(':even').removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]).end().filter(':odd').removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); \ No newline at end of file
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.png b/contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.png
deleted file mode 100644
index db114023f0..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.png
+++ /dev/null
Binary files differ
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.png b/contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.png
deleted file mode 100644
index db114023f0..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.png
+++ /dev/null
Binary files differ
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html b/contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html
deleted file mode 100644
index e15be066fb..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html
+++ /dev/null
@@ -1,113 +0,0 @@
-{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
-{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
-
-<!DOCTYPE html>
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #}
- {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #}
- <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
- <title>Coverage for {{relative_filename|escape}}: {{nums.pc_covered_str}}%</title>
- <link rel="icon" sizes="32x32" href="favicon_32.png">
- <link rel="stylesheet" href="style.css" type="text/css">
- {% if extra_css %}
- <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
- {% endif %}
- <script type="text/javascript" src="jquery.min.js"></script>
- <script type="text/javascript" src="jquery.hotkeys.js"></script>
- <script type="text/javascript" src="jquery.isonscreen.js"></script>
- <script type="text/javascript" src="coverage_html.js"></script>
- <script type="text/javascript">
- jQuery(document).ready(coverage.pyfile_ready);
- </script>
-</head>
-<body class="pyfile">
-
-<div id="header">
- <div class="content">
- <h1>Coverage for <b>{{relative_filename|escape}}</b> :
- <span class="pc_cov">{{nums.pc_covered_str}}%</span>
- </h1>
-
- <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
-
- <h2 class="stats">
- {{nums.n_statements}} statements &nbsp;
- <button type="button" class="{{category.run}} shortkey_r button_toggle_run" title="Toggle lines run">{{nums.n_executed}} run</button>
- <button type="button" class="{{category.mis}} shortkey_m button_toggle_mis" title="Toggle lines missing">{{nums.n_missing}} missing</button>
- <button type="button" class="{{category.exc}} shortkey_x button_toggle_exc" title="Toggle lines excluded">{{nums.n_excluded}} excluded</button>
-
- {% if has_arcs %}
- <button type="button" class="{{category.par}} shortkey_p button_toggle_par" title="Toggle lines partially run">{{nums.n_partial_branches}} partial</button>
- {% endif %}
- </h2>
- </div>
-</div>
-
-<div class="help_panel">
- <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
- <p class="legend">Hot-keys on this page</p>
- <div>
- <p class="keyhelp">
- <span class="key">r</span>
- <span class="key">m</span>
- <span class="key">x</span>
- <span class="key">p</span> &nbsp; toggle line displays
- </p>
- <p class="keyhelp">
- <span class="key">j</span>
- <span class="key">k</span> &nbsp; next/prev highlighted chunk
- </p>
- <p class="keyhelp">
- <span class="key">0</span> &nbsp; (zero) top of page
- </p>
- <p class="keyhelp">
- <span class="key">1</span> &nbsp; (one) first highlighted chunk
- </p>
- </div>
-</div>
-
-<div id="source">
- {% for line in lines -%}
- {% joined %}
- <p id="t{{line.number}}" class="{{line.css_class}}">
- <span class="n"><a href="#t{{line.number}}">{{line.number}}</a></span>
- <span class="t">{{line.html}}&nbsp;</span>
- {% if line.context_list %}
- <input type="checkbox" id="ctxs{{line.number}}" />
- {% endif %}
- {# Things that should float right in the line. #}
- <span class="r">
- {% if line.annotate %}
- <span class="annotate short">{{line.annotate}}</span>
- <span class="annotate long">{{line.annotate_long}}</span>
- {% endif %}
- {% if line.contexts %}
- <label for="ctxs{{line.number}}" class="ctx">{{ line.contexts_label }}</label>
- {% endif %}
- </span>
- {# Things that should appear below the line. #}
- {% if line.context_list %}
- <span class="ctxs">
- {% for context in line.context_list %}
- <span>{{context}}</span>
- {% endfor %}
- </span>
- {% endif %}
- </p>
- {% endjoined %}
- {% endfor %}
-</div>
-
-<div id="footer">
- <div class="content">
- <p>
- <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
- created at {{ time_stamp }}
- </p>
- </div>
-</div>
-
-</body>
-</html>
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/style.css b/contrib/python/coverage/py2/coverage/htmlfiles/style.css
deleted file mode 100644
index 36ee2a6e65..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/style.css
+++ /dev/null
@@ -1,291 +0,0 @@
-@charset "UTF-8";
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-/* Don't edit this .css file. Edit the .scss file instead! */
-html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
-
-body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; }
-
-@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } }
-
-@media (prefers-color-scheme: dark) { body { color: #eee; } }
-
-html > body { font-size: 16px; }
-
-a:active, a:focus { outline: 2px dashed #007acc; }
-
-p { font-size: .875em; line-height: 1.4em; }
-
-table { border-collapse: collapse; }
-
-td { vertical-align: top; }
-
-table tr.hidden { display: none !important; }
-
-p#no_rows { display: none; font-size: 1.2em; }
-
-a.nav { text-decoration: none; color: inherit; }
-
-a.nav:hover { text-decoration: underline; color: inherit; }
-
-#header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; }
-
-@media (prefers-color-scheme: dark) { #header { background: black; } }
-
-@media (prefers-color-scheme: dark) { #header { border-color: #333; } }
-
-.indexfile #footer { margin: 1rem 3.5rem; }
-
-.pyfile #footer { margin: 1rem 1rem; }
-
-#footer .content { padding: 0; color: #666; font-style: italic; }
-
-@media (prefers-color-scheme: dark) { #footer .content { color: #aaa; } }
-
-#index { margin: 1rem 0 0 3.5rem; }
-
-#header .content { padding: 1rem 3.5rem; }
-
-h1 { font-size: 1.25em; display: inline-block; }
-
-#filter_container { float: right; margin: 0 2em 0 0; }
-
-#filter_container input { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; }
-
-@media (prefers-color-scheme: dark) { #filter_container input { border-color: #444; } }
-
-@media (prefers-color-scheme: dark) { #filter_container input { background: #1e1e1e; } }
-
-@media (prefers-color-scheme: dark) { #filter_container input { color: #eee; } }
-
-#filter_container input:focus { border-color: #007acc; }
-
-h2.stats { margin-top: .5em; font-size: 1em; }
-
-.stats button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; color: inherit; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
-
-@media (prefers-color-scheme: dark) { .stats button { border-color: #444; } }
-
-.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
-
-.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
-
-.stats button.run { background: #eeffee; }
-
-@media (prefers-color-scheme: dark) { .stats button.run { background: #373d29; } }
-
-.stats button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.run.show_run { background: #373d29; } }
-
-.stats button.mis { background: #ffeeee; }
-
-@media (prefers-color-scheme: dark) { .stats button.mis { background: #4b1818; } }
-
-.stats button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.mis.show_mis { background: #4b1818; } }
-
-.stats button.exc { background: #f7f7f7; }
-
-@media (prefers-color-scheme: dark) { .stats button.exc { background: #333; } }
-
-.stats button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.exc.show_exc { background: #333; } }
-
-.stats button.par { background: #ffffd5; }
-
-@media (prefers-color-scheme: dark) { .stats button.par { background: #650; } }
-
-.stats button.par.show_par { background: #ffa; border: 2px solid #dddd00; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.par.show_par { background: #650; } }
-
-.help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; }
-
-#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
-
-#keyboard_icon { float: right; margin: 5px; cursor: pointer; }
-
-.help_panel { padding: .5em; border: 1px solid #883; }
-
-.help_panel .legend { font-style: italic; margin-bottom: 1em; }
-
-.indexfile .help_panel { width: 20em; min-height: 4em; }
-
-.pyfile .help_panel { width: 16em; min-height: 8em; }
-
-#panel_icon { float: right; cursor: pointer; }
-
-.keyhelp { margin: .75em; }
-
-.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; }
-
-#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
-
-#source p { position: relative; white-space: pre; }
-
-#source p * { box-sizing: border-box; }
-
-#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; }
-
-@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
-
-#source p .n a { text-decoration: none; color: #999; }
-
-@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
-
-#source p .n a:hover { text-decoration: underline; color: #999; }
-
-@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
-
-#source p.highlight .n { background: #ffdd00; }
-
-#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
-
-@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
-
-#source p .t:hover { background: #f2f2f2; }
-
-@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } }
-
-#source p .t:hover ~ .r .annotate.long { display: block; }
-
-#source p .t .com { color: #008000; font-style: italic; line-height: 1px; }
-
-@media (prefers-color-scheme: dark) { #source p .t .com { color: #6A9955; } }
-
-#source p .t .key { font-weight: bold; line-height: 1px; }
-
-#source p .t .str { color: #0451A5; }
-
-@media (prefers-color-scheme: dark) { #source p .t .str { color: #9CDCFE; } }
-
-#source p.mis .t { border-left: 0.2em solid #ff0000; }
-
-#source p.mis.show_mis .t { background: #fdd; }
-
-@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } }
-
-#source p.mis.show_mis .t:hover { background: #f2d2d2; }
-
-@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } }
-
-#source p.run .t { border-left: 0.2em solid #00dd00; }
-
-#source p.run.show_run .t { background: #dfd; }
-
-@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } }
-
-#source p.run.show_run .t:hover { background: #d2f2d2; }
-
-@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } }
-
-#source p.exc .t { border-left: 0.2em solid #808080; }
-
-#source p.exc.show_exc .t { background: #eee; }
-
-@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } }
-
-#source p.exc.show_exc .t:hover { background: #e2e2e2; }
-
-@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } }
-
-#source p.par .t { border-left: 0.2em solid #dddd00; }
-
-#source p.par.show_par .t { background: #ffa; }
-
-@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } }
-
-#source p.par.show_par .t:hover { background: #f2f2a2; }
-
-@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } }
-
-#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
-
-#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; }
-
-@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } }
-
-#source p .annotate.short:hover ~ .long { display: block; }
-
-#source p .annotate.long { width: 30em; right: 2.5em; }
-
-#source p input { display: none; }
-
-#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
-
-#source p input ~ .r label.ctx::before { content: "▶ "; }
-
-#source p input ~ .r label.ctx:hover { background: #d5f7ff; color: #666; }
-
-@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } }
-
-@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } }
-
-#source p input:checked ~ .r label.ctx { background: #aef; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
-
-@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } }
-
-@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } }
-
-#source p input:checked ~ .r label.ctx::before { content: "▼ "; }
-
-#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
-
-#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
-
-@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } }
-
-#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #aef; border-radius: .25em; margin-right: 1.75em; }
-
-@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } }
-
-#source p .ctxs span { display: block; text-align: right; }
-
-#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; }
-
-#index table.index { margin-left: -.5em; }
-
-#index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; }
-
-@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } }
-
-#index td.name, #index th.name { text-align: left; width: auto; }
-
-#index th { font-style: italic; color: #333; cursor: pointer; }
-
-@media (prefers-color-scheme: dark) { #index th { color: #ddd; } }
-
-#index th:hover { background: #eee; }
-
-@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } }
-
-#index th.headerSortDown, #index th.headerSortUp { white-space: nowrap; background: #eee; }
-
-@media (prefers-color-scheme: dark) { #index th.headerSortDown, #index th.headerSortUp { background: #333; } }
-
-#index th.headerSortDown:after { content: " ↑"; }
-
-#index th.headerSortUp:after { content: " ↓"; }
-
-#index td.name a { text-decoration: none; color: inherit; }
-
-#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }
-
-#index tr.file:hover { background: #eee; }
-
-@media (prefers-color-scheme: dark) { #index tr.file:hover { background: #333; } }
-
-#index tr.file:hover td.name { text-decoration: underline; color: inherit; }
-
-#scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }
-
-@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } }
-
-@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } }
-
-#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; }
-
-@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } }
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/style.scss b/contrib/python/coverage/py2/coverage/htmlfiles/style.scss
deleted file mode 100644
index 158d1fb493..0000000000
--- a/contrib/python/coverage/py2/coverage/htmlfiles/style.scss
+++ /dev/null
@@ -1,660 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-// CSS styles for coverage.py HTML reports.
-
-// When you edit this file, you need to run "make css" to get the CSS file
-// generated, and then check in both the .scss and the .css files.
-
-// When working on the file, this command is useful:
-// sass --watch --style=compact --sourcemap=none --no-cache coverage/htmlfiles/style.scss:htmlcov/style.css
-//
-// OR you can process sass purely in python with `pip install pysass`, then:
-// pysassc --style=compact coverage/htmlfiles/style.scss coverage/htmlfiles/style.css
-
-// Ignore this comment, it's for the CSS output file:
-/* Don't edit this .css file. Edit the .scss file instead! */
-
-// Dimensions
-$left-gutter: 3.5rem;
-
-
-//
-// Declare colors and variables
-//
-
-$font-normal: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
-$font-code: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
-
-$off-button-lighten: 50%;
-$hover-dark-amt: 95%;
-
-$focus-color: #007acc;
-
-$mis-color: #ff0000;
-$run-color: #00dd00;
-$exc-color: #808080;
-$par-color: #dddd00;
-
-$light-bg: #fff;
-$light-fg: #000;
-$light-gray1: #f8f8f8;
-$light-gray2: #eee;
-$light-gray3: #ccc;
-$light-gray4: #999;
-$light-gray5: #666;
-$light-gray6: #333;
-$light-pln-bg: $light-bg;
-$light-mis-bg: #fdd;
-$light-run-bg: #dfd;
-$light-exc-bg: $light-gray2;
-$light-par-bg: #ffa;
-$light-token-com: #008000;
-$light-token-str: #0451A5;
-$light-context-bg-color: #aef;
-
-$dark-bg: #1e1e1e;
-$dark-fg: #eee;
-$dark-gray1: #222;
-$dark-gray2: #333;
-$dark-gray3: #444;
-$dark-gray4: #777;
-$dark-gray5: #aaa;
-$dark-gray6: #ddd;
-$dark-pln-bg: $dark-bg;
-$dark-mis-bg: #4b1818;
-$dark-run-bg: #373d29;
-$dark-exc-bg: $dark-gray2;
-$dark-par-bg: #650;
-$dark-token-com: #6A9955;
-$dark-token-str: #9CDCFE;
-$dark-context-bg-color: #056;
-
-//
-// Mixins and utilities
-//
-@mixin background-dark($color) {
- @media (prefers-color-scheme: dark) {
- background: $color;
- }
-}
-@mixin color-dark($color) {
- @media (prefers-color-scheme: dark) {
- color: $color;
- }
-}
-@mixin border-color-dark($color) {
- @media (prefers-color-scheme: dark) {
- border-color: $color;
- }
-}
-
-// Add visual outline to navigable elements on focus improve accessibility.
-@mixin focus-border {
- &:active, &:focus {
- outline: 2px dashed $focus-color;
- }
-}
-
-// Page-wide styles
-html, body, h1, h2, h3, p, table, td, th {
- margin: 0;
- padding: 0;
- border: 0;
- font-weight: inherit;
- font-style: inherit;
- font-size: 100%;
- font-family: inherit;
- vertical-align: baseline;
-}
-
-// Set baseline grid to 16 pt.
-body {
- font-family: $font-normal;
- font-size: 1em;
- background: $light-bg;
- color: $light-fg;
- @include background-dark($dark-bg);
- @include color-dark($dark-fg);
-}
-
-html>body {
- font-size: 16px;
-}
-
-a {
- @include focus-border;
-}
-
-p {
- font-size: .875em;
- line-height: 1.4em;
-}
-
-table {
- border-collapse: collapse;
-}
-td {
- vertical-align: top;
-}
-table tr.hidden {
- display: none !important;
-}
-
-p#no_rows {
- display: none;
- font-size: 1.2em;
-}
-
-a.nav {
- text-decoration: none;
- color: inherit;
-
- &:hover {
- text-decoration: underline;
- color: inherit;
- }
-}
-
-// Page structure
-#header {
- background: $light-gray1;
- @include background-dark(black);
- width: 100%;
- border-bottom: 1px solid $light-gray2;
- @include border-color-dark($dark-gray2);
-}
-
-.indexfile #footer {
- margin: 1rem $left-gutter;
-}
-
-.pyfile #footer {
- margin: 1rem 1rem;
-}
-
-#footer .content {
- padding: 0;
- color: $light-gray5;
- @include color-dark($dark-gray5);
- font-style: italic;
-}
-
-#index {
- margin: 1rem 0 0 $left-gutter;
-}
-
-// Header styles
-#header .content {
- padding: 1rem $left-gutter;
-}
-
-h1 {
- font-size: 1.25em;
- display: inline-block;
-}
-
-#filter_container {
- float: right;
- margin: 0 2em 0 0;
-
- input {
- width: 10em;
- padding: 0.2em 0.5em;
- border: 2px solid $light-gray3;
- background: $light-bg;
- color: $light-fg;
- @include border-color-dark($dark-gray3);
- @include background-dark($dark-bg);
- @include color-dark($dark-fg);
- &:focus {
- border-color: $focus-color;
- }
- }
-}
-
-h2.stats {
- margin-top: .5em;
- font-size: 1em;
-}
-.stats button {
- font-family: inherit;
- font-size: inherit;
- border: 1px solid;
- border-radius: .2em;
- color: inherit;
- padding: .1em .5em;
- margin: 1px calc(.1em + 1px);
- cursor: pointer;
- border-color: $light-gray3;
- @include border-color-dark($dark-gray3);
- @include focus-border;
-
- @include focus-border;
-
- &.run {
- background: mix($light-run-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-run-bg);
- &.show_run {
- background: $light-run-bg;
- @include background-dark($dark-run-bg);
- border: 2px solid $run-color;
- margin: 0 .1em;
- }
- }
- &.mis {
- background: mix($light-mis-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-mis-bg);
- &.show_mis {
- background: $light-mis-bg;
- @include background-dark($dark-mis-bg);
- border: 2px solid $mis-color;
- margin: 0 .1em;
- }
- }
- &.exc {
- background: mix($light-exc-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-exc-bg);
- &.show_exc {
- background: $light-exc-bg;
- @include background-dark($dark-exc-bg);
- border: 2px solid $exc-color;
- margin: 0 .1em;
- }
- }
- &.par {
- background: mix($light-par-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-par-bg);
- &.show_par {
- background: $light-par-bg;
- @include background-dark($dark-par-bg);
- border: 2px solid $par-color;
- margin: 0 .1em;
- }
- }
-}
-
-// Yellow post-it things.
-%popup {
- display: none;
- position: absolute;
- z-index: 999;
- background: #ffffcc;
- border: 1px solid #888;
- border-radius: .2em;
- color: #333;
- padding: .25em .5em;
-}
-
-// Yellow post-it's in the text listings.
-%in-text-popup {
- @extend %popup;
- white-space: normal;
- float: right;
- top: 1.75em;
- right: 1em;
- height: auto;
-}
-
-// Help panel
-#keyboard_icon {
- float: right;
- margin: 5px;
- cursor: pointer;
-}
-
-.help_panel {
- @extend %popup;
- padding: .5em;
- border: 1px solid #883;
-
- .legend {
- font-style: italic;
- margin-bottom: 1em;
- }
-
- .indexfile & {
- width: 20em;
- min-height: 4em;
- }
-
- .pyfile & {
- width: 16em;
- min-height: 8em;
- }
-}
-
-#panel_icon {
- float: right;
- cursor: pointer;
-}
-
-.keyhelp {
- margin: .75em;
-
- .key {
- border: 1px solid black;
- border-color: #888 #333 #333 #888;
- padding: .1em .35em;
- font-family: $font-code;
- font-weight: bold;
- background: #eee;
- }
-}
-
-// Source file styles
-
-// The slim bar at the left edge of the source lines, colored by coverage.
-$border-indicator-width: .2em;
-
-#source {
- padding: 1em 0 1em $left-gutter;
- font-family: $font-code;
-
- p {
- // position relative makes position:absolute pop-ups appear in the right place.
- position: relative;
- white-space: pre;
-
- * {
- box-sizing: border-box;
- }
-
- .n {
- float: left;
- text-align: right;
- width: $left-gutter;
- box-sizing: border-box;
- margin-left: -$left-gutter;
- padding-right: 1em;
- color: $light-gray4;
- @include color-dark($dark-gray4);
-
- a {
- text-decoration: none;
- color: $light-gray4;
- @include color-dark($dark-gray4);
- &:hover {
- text-decoration: underline;
- color: $light-gray4;
- @include color-dark($dark-gray4);
- }
- }
- }
-
- &.highlight .n {
- background: #ffdd00;
- }
-
- .t {
- display: inline-block;
- width: 100%;
- box-sizing: border-box;
- margin-left: -.5em;
- padding-left: .5em - $border-indicator-width;
- border-left: $border-indicator-width solid $light-bg;
- @include border-color-dark($dark-bg);
-
- &:hover {
- background: mix($light-pln-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-pln-bg, $dark-fg, $hover-dark-amt));
-
- & ~ .r .annotate.long {
- display: block;
- }
- }
-
- // Syntax coloring
- .com {
- color: $light-token-com;
- @include color-dark($dark-token-com);
- font-style: italic;
- line-height: 1px;
- }
- .key {
- font-weight: bold;
- line-height: 1px;
- }
- .str {
- color: $light-token-str;
- @include color-dark($dark-token-str);
- }
- }
-
- &.mis {
- .t {
- border-left: $border-indicator-width solid $mis-color;
- }
-
- &.show_mis .t {
- background: $light-mis-bg;
- @include background-dark($dark-mis-bg);
-
- &:hover {
- background: mix($light-mis-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-mis-bg, $dark-fg, $hover-dark-amt));
- }
- }
- }
-
- &.run {
- .t {
- border-left: $border-indicator-width solid $run-color;
- }
-
- &.show_run .t {
- background: $light-run-bg;
- @include background-dark($dark-run-bg);
-
- &:hover {
- background: mix($light-run-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-run-bg, $dark-fg, $hover-dark-amt));
- }
- }
- }
-
- &.exc {
- .t {
- border-left: $border-indicator-width solid $exc-color;
- }
-
- &.show_exc .t {
- background: $light-exc-bg;
- @include background-dark($dark-exc-bg);
-
- &:hover {
- background: mix($light-exc-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-exc-bg, $dark-fg, $hover-dark-amt));
- }
- }
- }
-
- &.par {
- .t {
- border-left: $border-indicator-width solid $par-color;
- }
-
- &.show_par .t {
- background: $light-par-bg;
- @include background-dark($dark-par-bg);
-
- &:hover {
- background: mix($light-par-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-par-bg, $dark-fg, $hover-dark-amt));
- }
- }
-
- }
-
- .r {
- position: absolute;
- top: 0;
- right: 2.5em;
- font-family: $font-normal;
- }
-
- .annotate {
- font-family: $font-normal;
- color: $light-gray5;
- @include color-dark($dark-gray6);
- padding-right: .5em;
-
- &.short:hover ~ .long {
- display: block;
- }
-
- &.long {
- @extend %in-text-popup;
- width: 30em;
- right: 2.5em;
- }
- }
-
- input {
- display: none;
-
- & ~ .r label.ctx {
- cursor: pointer;
- border-radius: .25em;
- &::before {
- content: "▶ ";
- }
- &:hover {
- background: mix($light-context-bg-color, $light-bg, $off-button-lighten);
- @include background-dark(mix($dark-context-bg-color, $dark-bg, $off-button-lighten));
- color: $light-gray5;
- @include color-dark($dark-gray5);
- }
- }
-
- &:checked ~ .r label.ctx {
- background: $light-context-bg-color;
- @include background-dark($dark-context-bg-color);
- color: $light-gray5;
- @include color-dark($dark-gray5);
- border-radius: .75em .75em 0 0;
- padding: 0 .5em;
- margin: -.25em 0;
- &::before {
- content: "▼ ";
- }
- }
-
- &:checked ~ .ctxs {
- padding: .25em .5em;
- overflow-y: scroll;
- max-height: 10.5em;
- }
- }
-
- label.ctx {
- color: $light-gray4;
- @include color-dark($dark-gray4);
- display: inline-block;
- padding: 0 .5em;
- font-size: .8333em; // 10/12
- }
-
- .ctxs {
- display: block;
- max-height: 0;
- overflow-y: hidden;
- transition: all .2s;
- padding: 0 .5em;
- font-family: $font-normal;
- white-space: nowrap;
- background: $light-context-bg-color;
- @include background-dark($dark-context-bg-color);
- border-radius: .25em;
- margin-right: 1.75em;
- span {
- display: block;
- text-align: right;
- }
- }
- }
-}
-
-
-// index styles
-#index {
- font-family: $font-code;
- font-size: 0.875em;
-
- table.index {
- margin-left: -.5em;
- }
- td, th {
- text-align: right;
- width: 5em;
- padding: .25em .5em;
- border-bottom: 1px solid $light-gray2;
- @include border-color-dark($dark-gray2);
- &.name {
- text-align: left;
- width: auto;
- }
- }
- th {
- font-style: italic;
- color: $light-gray6;
- @include color-dark($dark-gray6);
- cursor: pointer;
- &:hover {
- background: $light-gray2;
- @include background-dark($dark-gray2);
- }
- &.headerSortDown, &.headerSortUp {
- white-space: nowrap;
- background: $light-gray2;
- @include background-dark($dark-gray2);
- }
- &.headerSortDown:after {
- content: " ↑";
- }
- &.headerSortUp:after {
- content: " ↓";
- }
- }
- td.name a {
- text-decoration: none;
- color: inherit;
- }
-
- tr.total td,
- tr.total_dynamic td {
- font-weight: bold;
- border-top: 1px solid #ccc;
- border-bottom: none;
- }
- tr.file:hover {
- background: $light-gray2;
- @include background-dark($dark-gray2);
- td.name {
- text-decoration: underline;
- color: inherit;
- }
- }
-}
-
-// scroll marker styles
-#scroll_marker {
- position: fixed;
- right: 0;
- top: 0;
- width: 16px;
- height: 100%;
- background: $light-bg;
- border-left: 1px solid $light-gray2;
- @include background-dark($dark-bg);
- @include border-color-dark($dark-gray2);
- will-change: transform; // for faster scrolling of fixed element in Chrome
-
- .marker {
- background: $light-gray3;
- @include background-dark($dark-gray3);
- position: absolute;
- min-height: 3px;
- width: 100%;
- }
-}
diff --git a/contrib/python/coverage/py2/coverage/inorout.py b/contrib/python/coverage/py2/coverage/inorout.py
deleted file mode 100644
index cbc80e8fb5..0000000000
--- a/contrib/python/coverage/py2/coverage/inorout.py
+++ /dev/null
@@ -1,513 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Determining whether files are being measured/reported or not."""
-
-# For finding the stdlib
-import atexit
-import inspect
-import itertools
-import os
-import platform
-import re
-import sys
-import traceback
-
-from coverage import env
-from coverage.backward import code_object
-from coverage.disposition import FileDisposition, disposition_init
-from coverage.files import TreeMatcher, FnmatchMatcher, ModuleMatcher
-from coverage.files import prep_patterns, find_python_files, canonical_filename
-from coverage.misc import CoverageException
-from coverage.python import source_for_file, source_for_morf
-
-
-# Pypy has some unusual stuff in the "stdlib". Consider those locations
-# when deciding where the stdlib is. These modules are not used for anything,
-# they are modules importable from the pypy lib directories, so that we can
-# find those directories.
-_structseq = _pypy_irc_topic = None
-if env.PYPY:
- try:
- import _structseq
- except ImportError:
- pass
-
- try:
- import _pypy_irc_topic
- except ImportError:
- pass
-
-
-def canonical_path(morf, directory=False):
- """Return the canonical path of the module or file `morf`.
-
- If the module is a package, then return its directory. If it is a
- module, then return its file, unless `directory` is True, in which
- case return its enclosing directory.
-
- """
- morf_path = canonical_filename(source_for_morf(morf))
- if morf_path.endswith("__init__.py") or directory:
- morf_path = os.path.split(morf_path)[0]
- return morf_path
-
-
-def name_for_module(filename, frame):
- """Get the name of the module for a filename and frame.
-
- For configurability's sake, we allow __main__ modules to be matched by
- their importable name.
-
- If loaded via runpy (aka -m), we can usually recover the "original"
- full dotted module name, otherwise, we resort to interpreting the
- file name to get the module's name. In the case that the module name
- can't be determined, None is returned.
-
- """
- module_globals = frame.f_globals if frame is not None else {}
- if module_globals is None: # pragma: only ironpython
- # IronPython doesn't provide globals: https://github.com/IronLanguages/main/issues/1296
- module_globals = {}
-
- dunder_name = module_globals.get('__name__', None)
-
- if isinstance(dunder_name, str) and dunder_name != '__main__':
- # This is the usual case: an imported module.
- return dunder_name
-
- loader = module_globals.get('__loader__', None)
- for attrname in ('fullname', 'name'): # attribute renamed in py3.2
- if hasattr(loader, attrname):
- fullname = getattr(loader, attrname)
- else:
- continue
-
- if isinstance(fullname, str) and fullname != '__main__':
- # Module loaded via: runpy -m
- return fullname
-
- # Script as first argument to Python command line.
- inspectedname = inspect.getmodulename(filename)
- if inspectedname is not None:
- return inspectedname
- else:
- return dunder_name
-
-
-def module_is_namespace(mod):
- """Is the module object `mod` a PEP420 namespace module?"""
- return hasattr(mod, '__path__') and getattr(mod, '__file__', None) is None
-
-
-def module_has_file(mod):
- """Does the module object `mod` have an existing __file__ ?"""
- mod__file__ = getattr(mod, '__file__', None)
- if mod__file__ is None:
- return False
- return os.path.exists(mod__file__)
-
-
-class InOrOut(object):
- """Machinery for determining what files to measure."""
-
- def __init__(self, warn, debug):
- self.warn = warn
- self.debug = debug
-
- # The matchers for should_trace.
- self.source_match = None
- self.source_pkgs_match = None
- self.pylib_paths = self.cover_paths = None
- self.pylib_match = self.cover_match = None
- self.include_match = self.omit_match = None
- self.plugins = []
- self.disp_class = FileDisposition
-
- # The source argument can be directories or package names.
- self.source = []
- self.source_pkgs = []
- self.source_pkgs_unmatched = []
- self.omit = self.include = None
-
- def configure(self, config):
- """Apply the configuration to get ready for decision-time."""
- self.config = config
- self.source_pkgs.extend(config.source_pkgs)
- for src in config.source or []:
- if os.path.isdir(src):
- self.source.append(canonical_filename(src))
- else:
- self.source_pkgs.append(src)
- self.source_pkgs_unmatched = self.source_pkgs[:]
-
- self.omit = prep_patterns(config.run_omit)
- if getattr(sys, 'is_standalone_binary', False):
- # don't trace contrib
- self.omit.append('contrib/python/*')
- self.omit.append('contrib/libs/protobuf/*')
- self.omit.append('library/python/pytest/*')
- self.include = prep_patterns(config.run_include)
-
- # The directories for files considered "installed with the interpreter".
- self.pylib_paths = set()
- if getattr(sys, 'is_standalone_binary', False):
- self.pylib_paths.add('contrib/tools/python')
- self.pylib_paths.add('contrib/tools/python3')
- if not self.pylib_paths and not config.cover_pylib:
- # Look at where some standard modules are located. That's the
- # indication for "installed with the interpreter". In some
- # environments (virtualenv, for example), these modules may be
- # spread across a few locations. Look at all the candidate modules
- # we've imported, and take all the different ones.
- for m in (atexit, inspect, os, platform, _pypy_irc_topic, re, _structseq, traceback):
- if m is not None and hasattr(m, "__file__"):
- self.pylib_paths.add(canonical_path(m, directory=True))
-
- if _structseq and not hasattr(_structseq, '__file__'):
- # PyPy 2.4 has no __file__ in the builtin modules, but the code
- # objects still have the file names. So dig into one to find
- # the path to exclude. The "filename" might be synthetic,
- # don't be fooled by those.
- structseq_file = code_object(_structseq.structseq_new).co_filename
- if not structseq_file.startswith("<"):
- self.pylib_paths.add(canonical_path(structseq_file))
-
- # To avoid tracing the coverage.py code itself, we skip anything
- # located where we are.
- if getattr(sys, 'is_standalone_binary', False):
- self.cover_paths = ["contrib/python/coverage"]
- else:
- self.cover_paths = [canonical_path(__file__, directory=True)]
- if env.TESTING:
- # Don't include our own test code.
- self.cover_paths.append(os.path.join(self.cover_paths[0], "tests"))
-
- # When testing, we use PyContracts, which should be considered
- # part of coverage.py, and it uses six. Exclude those directories
- # just as we exclude ourselves.
- import contracts
- import six
- for mod in [contracts, six]:
- self.cover_paths.append(canonical_path(mod))
-
- def debug(msg):
- if self.debug:
- self.debug.write(msg)
-
- # Create the matchers we need for should_trace
- if self.source or self.source_pkgs:
- against = []
- if self.source:
- self.source_match = TreeMatcher(self.source)
- against.append("trees {!r}".format(self.source_match))
- if self.source_pkgs:
- self.source_pkgs_match = ModuleMatcher(self.source_pkgs)
- against.append("modules {!r}".format(self.source_pkgs_match))
- debug("Source matching against " + " and ".join(against))
- else:
- if self.cover_paths:
- self.cover_match = TreeMatcher(self.cover_paths)
- debug("Coverage code matching: {!r}".format(self.cover_match))
- if self.pylib_paths:
- self.pylib_match = TreeMatcher(self.pylib_paths)
- debug("Python stdlib matching: {!r}".format(self.pylib_match))
- if self.include:
- self.include_match = FnmatchMatcher(self.include)
- debug("Include matching: {!r}".format(self.include_match))
- if self.omit:
- self.omit_match = FnmatchMatcher(self.omit)
- debug("Omit matching: {!r}".format(self.omit_match))
-
- def should_trace(self, filename, frame=None):
- """Decide whether to trace execution in `filename`, with a reason.
-
- This function is called from the trace function. As each new file name
- is encountered, this function determines whether it is traced or not.
-
- Returns a FileDisposition object.
-
- """
- original_filename = filename
- disp = disposition_init(self.disp_class, filename)
-
- def nope(disp, reason):
- """Simple helper to make it easy to return NO."""
- disp.trace = False
- disp.reason = reason
- return disp
-
- if frame is not None:
- # Compiled Python files have two file names: frame.f_code.co_filename is
- # the file name at the time the .pyc was compiled. The second name is
- # __file__, which is where the .pyc was actually loaded from. Since
- # .pyc files can be moved after compilation (for example, by being
- # installed), we look for __file__ in the frame and prefer it to the
- # co_filename value.
- dunder_file = frame.f_globals and frame.f_globals.get('__file__')
- if dunder_file:
- filename = source_for_file(dunder_file)
- if original_filename and not original_filename.startswith('<'):
- orig = os.path.basename(original_filename)
- if orig != os.path.basename(filename):
- # Files shouldn't be renamed when moved. This happens when
- # exec'ing code. If it seems like something is wrong with
- # the frame's file name, then just use the original.
- filename = original_filename
-
- if not filename:
- # Empty string is pretty useless.
- return nope(disp, "empty string isn't a file name")
-
- if filename.startswith('memory:'):
- return nope(disp, "memory isn't traceable")
-
- if filename.startswith('<'):
- # Lots of non-file execution is represented with artificial
- # file names like "<string>", "<doctest readme.txt[0]>", or
- # "<exec_function>". Don't ever trace these executions, since we
- # can't do anything with the data later anyway.
- return nope(disp, "not a real file name")
-
- # pyexpat does a dumb thing, calling the trace function explicitly from
- # C code with a C file name.
- if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename):
- return nope(disp, "pyexpat lies about itself")
-
- # Jython reports the .class file to the tracer, use the source file.
- if filename.endswith("$py.class"):
- filename = filename[:-9] + ".py"
-
- # XXX maybe we need to support both at the same time?
- # Don't trace modules imported from environment in standalone mode
- if getattr(sys, 'is_standalone_binary', False) and filename.startswith("/"):
- return nope(disp, "skip modules from environment")
-
- canonical = canonical_filename(filename)
- disp.canonical_filename = canonical
-
- # Try the plugins, see if they have an opinion about the file.
- plugin = None
- for plugin in self.plugins.file_tracers:
- if not plugin._coverage_enabled:
- continue
-
- try:
- file_tracer = plugin.file_tracer(canonical)
- if file_tracer is not None:
- file_tracer._coverage_plugin = plugin
- disp.trace = True
- disp.file_tracer = file_tracer
- if file_tracer.has_dynamic_source_filename():
- disp.has_dynamic_filename = True
- else:
- disp.source_filename = canonical_filename(
- file_tracer.source_filename()
- )
- break
- except Exception:
- if not self.config.suppress_plugin_errors:
- raise
- self.warn(
- "Disabling plug-in %r due to an exception:" % (plugin._coverage_plugin_name)
- )
- traceback.print_exc()
- plugin._coverage_enabled = False
- continue
- else:
- # No plugin wanted it: it's Python.
- disp.trace = True
- disp.source_filename = canonical
-
- if not disp.has_dynamic_filename:
- if not disp.source_filename:
- raise CoverageException(
- "Plugin %r didn't set source_filename for %r" %
- (plugin, disp.original_filename)
- )
- reason = self.check_include_omit_etc(disp.source_filename, frame)
- if reason:
- nope(disp, reason)
-
- return disp
-
- def check_include_omit_etc(self, filename, frame):
- """Check a file name against the include, omit, etc, rules.
-
- Returns a string or None. String means, don't trace, and is the reason
- why. None means no reason found to not trace.
-
- """
- modulename = name_for_module(filename, frame)
-
- # If the user specified source or include, then that's authoritative
- # about the outer bound of what to measure and we don't have to apply
- # any canned exclusions. If they didn't, then we have to exclude the
- # stdlib and coverage.py directories.
- if self.source_match or self.source_pkgs_match:
- extra = ""
- ok = False
- if self.source_pkgs_match:
- if self.source_pkgs_match.match(modulename):
- ok = True
- if modulename in self.source_pkgs_unmatched:
- self.source_pkgs_unmatched.remove(modulename)
- else:
- extra = "module {!r} ".format(modulename)
- if not ok and self.source_match:
- if self.source_match.match(filename):
- ok = True
- if not ok:
- return extra + "falls outside the --source spec"
- elif self.include_match:
- if not self.include_match.match(filename):
- return "falls outside the --include trees"
- else:
- # If we aren't supposed to trace installed code, then check if this
- # is near the Python standard library and skip it if so.
- if self.pylib_match and self.pylib_match.match(filename):
- return "is in the stdlib"
-
- # We exclude the coverage.py code itself, since a little of it
- # will be measured otherwise.
- if self.cover_match and self.cover_match.match(filename):
- return "is part of coverage.py"
-
- # Check the file against the omit pattern.
- if self.omit_match and self.omit_match.match(filename):
- return "is inside an --omit pattern"
-
- # No point tracing a file we can't later write to SQLite.
- try:
- filename.encode("utf8")
- except UnicodeEncodeError:
- return "non-encodable filename"
-
- # No reason found to skip this file.
- return None
-
- def warn_conflicting_settings(self):
- """Warn if there are settings that conflict."""
- if self.include:
- if self.source or self.source_pkgs:
- self.warn("--include is ignored because --source is set", slug="include-ignored")
-
- def warn_already_imported_files(self):
- """Warn if files have already been imported that we will be measuring."""
- if self.include or self.source or self.source_pkgs:
- warned = set()
- for mod in list(sys.modules.values()):
- filename = getattr(mod, "__file__", None)
- if filename is None:
- continue
- if filename in warned:
- continue
-
- disp = self.should_trace(filename)
- if disp.trace:
- msg = "Already imported a file that will be measured: {}".format(filename)
- self.warn(msg, slug="already-imported")
- warned.add(filename)
-
- def warn_unimported_source(self):
- """Warn about source packages that were of interest, but never traced."""
- for pkg in self.source_pkgs_unmatched:
- self._warn_about_unmeasured_code(pkg)
-
- def _warn_about_unmeasured_code(self, pkg):
- """Warn about a package or module that we never traced.
-
- `pkg` is a string, the name of the package or module.
-
- """
- mod = sys.modules.get(pkg)
- if mod is None:
- self.warn("Module %s was never imported." % pkg, slug="module-not-imported")
- return
-
- if module_is_namespace(mod):
- # A namespace package. It's OK for this not to have been traced,
- # since there is no code directly in it.
- return
-
- if not module_has_file(mod):
- self.warn("Module %s has no Python source." % pkg, slug="module-not-python")
- return
-
- # The module was in sys.modules, and seems like a module with code, but
- # we never measured it. I guess that means it was imported before
- # coverage even started.
- self.warn(
- "Module %s was previously imported, but not measured" % pkg,
- slug="module-not-measured",
- )
-
- def find_possibly_unexecuted_files(self):
- """Find files in the areas of interest that might be untraced.
-
- Yields pairs: file path, and responsible plug-in name.
- """
- for pkg in self.source_pkgs:
- if (not pkg in sys.modules or
- not module_has_file(sys.modules[pkg])):
- continue
- pkg_file = source_for_file(sys.modules[pkg].__file__)
- for ret in self._find_executable_files(canonical_path(pkg_file)):
- yield ret
-
- for src in self.source:
- for ret in self._find_executable_files(src):
- yield ret
-
- def _find_plugin_files(self, src_dir):
- """Get executable files from the plugins."""
- for plugin in self.plugins.file_tracers:
- for x_file in plugin.find_executable_files(src_dir):
- yield x_file, plugin._coverage_plugin_name
-
- def _find_executable_files(self, src_dir):
- """Find executable files in `src_dir`.
-
- Search for files in `src_dir` that can be executed because they
- are probably importable. Don't include ones that have been omitted
- by the configuration.
-
- Yield the file path, and the plugin name that handles the file.
-
- """
- py_files = ((py_file, None) for py_file in find_python_files(src_dir))
- plugin_files = self._find_plugin_files(src_dir)
-
- for file_path, plugin_name in itertools.chain(py_files, plugin_files):
- file_path = canonical_filename(file_path)
- if self.omit_match and self.omit_match.match(file_path):
- # Turns out this file was omitted, so don't pull it back
- # in as unexecuted.
- continue
- yield file_path, plugin_name
-
- def sys_info(self):
- """Our information for Coverage.sys_info.
-
- Returns a list of (key, value) pairs.
- """
- info = [
- ('cover_paths', self.cover_paths),
- ('pylib_paths', self.pylib_paths),
- ]
-
- matcher_names = [
- 'source_match', 'source_pkgs_match',
- 'include_match', 'omit_match',
- 'cover_match', 'pylib_match',
- ]
-
- for matcher_name in matcher_names:
- matcher = getattr(self, matcher_name)
- if matcher:
- matcher_info = matcher.info()
- else:
- matcher_info = '-none-'
- info.append((matcher_name, matcher_info))
-
- return info
diff --git a/contrib/python/coverage/py2/coverage/jsonreport.py b/contrib/python/coverage/py2/coverage/jsonreport.py
deleted file mode 100644
index 4287bc79a3..0000000000
--- a/contrib/python/coverage/py2/coverage/jsonreport.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# coding: utf-8
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Json reporting for coverage.py"""
-import datetime
-import json
-import sys
-
-from coverage import __version__
-from coverage.report import get_analysis_to_report
-from coverage.results import Numbers
-
-
-class JsonReporter(object):
- """A reporter for writing JSON coverage results."""
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
- self.total = Numbers()
- self.report_data = {}
-
- def report(self, morfs, outfile=None):
- """Generate a json report for `morfs`.
-
- `morfs` is a list of modules or file names.
-
- `outfile` is a file object to write the json to
-
- """
- outfile = outfile or sys.stdout
- coverage_data = self.coverage.get_data()
- coverage_data.set_query_contexts(self.config.report_contexts)
- self.report_data["meta"] = {
- "version": __version__,
- "timestamp": datetime.datetime.now().isoformat(),
- "branch_coverage": coverage_data.has_arcs(),
- "show_contexts": self.config.json_show_contexts,
- }
-
- measured_files = {}
- for file_reporter, analysis in get_analysis_to_report(self.coverage, morfs):
- measured_files[file_reporter.relative_filename()] = self.report_one_file(
- coverage_data,
- analysis
- )
-
- self.report_data["files"] = measured_files
-
- self.report_data["totals"] = {
- 'covered_lines': self.total.n_executed,
- 'num_statements': self.total.n_statements,
- 'percent_covered': self.total.pc_covered,
- 'missing_lines': self.total.n_missing,
- 'excluded_lines': self.total.n_excluded,
- }
-
- if coverage_data.has_arcs():
- self.report_data["totals"].update({
- 'num_branches': self.total.n_branches,
- 'num_partial_branches': self.total.n_partial_branches,
- 'covered_branches': self.total.n_executed_branches,
- 'missing_branches': self.total.n_missing_branches,
- })
-
- json.dump(
- self.report_data,
- outfile,
- indent=4 if self.config.json_pretty_print else None
- )
-
- return self.total.n_statements and self.total.pc_covered
-
- def report_one_file(self, coverage_data, analysis):
- """Extract the relevant report data for a single file"""
- nums = analysis.numbers
- self.total += nums
- summary = {
- 'covered_lines': nums.n_executed,
- 'num_statements': nums.n_statements,
- 'percent_covered': nums.pc_covered,
- 'missing_lines': nums.n_missing,
- 'excluded_lines': nums.n_excluded,
- }
- reported_file = {
- 'executed_lines': sorted(analysis.executed),
- 'summary': summary,
- 'missing_lines': sorted(analysis.missing),
- 'excluded_lines': sorted(analysis.excluded)
- }
- if self.config.json_show_contexts:
- reported_file['contexts'] = analysis.data.contexts_by_lineno(
- analysis.filename,
- )
- if coverage_data.has_arcs():
- reported_file['summary'].update({
- 'num_branches': nums.n_branches,
- 'num_partial_branches': nums.n_partial_branches,
- 'covered_branches': nums.n_executed_branches,
- 'missing_branches': nums.n_missing_branches,
- })
- return reported_file
diff --git a/contrib/python/coverage/py2/coverage/misc.py b/contrib/python/coverage/py2/coverage/misc.py
deleted file mode 100644
index 034e288eb9..0000000000
--- a/contrib/python/coverage/py2/coverage/misc.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Miscellaneous stuff for coverage.py."""
-
-import errno
-import hashlib
-import inspect
-import locale
-import os
-import os.path
-import random
-import re
-import socket
-import sys
-import types
-
-from coverage import env
-from coverage.backward import to_bytes, unicode_class
-
-ISOLATED_MODULES = {}
-
-
-def isolate_module(mod):
- """Copy a module so that we are isolated from aggressive mocking.
-
- If a test suite mocks os.path.exists (for example), and then we need to use
- it during the test, everything will get tangled up if we use their mock.
- Making a copy of the module when we import it will isolate coverage.py from
- those complications.
- """
- if mod not in ISOLATED_MODULES:
- new_mod = types.ModuleType(mod.__name__)
- ISOLATED_MODULES[mod] = new_mod
- for name in dir(mod):
- value = getattr(mod, name)
- if isinstance(value, types.ModuleType):
- value = isolate_module(value)
- setattr(new_mod, name, value)
- return ISOLATED_MODULES[mod]
-
-os = isolate_module(os)
-
-
-def dummy_decorator_with_args(*args_unused, **kwargs_unused):
- """Dummy no-op implementation of a decorator with arguments."""
- def _decorator(func):
- return func
- return _decorator
-
-
-# Environment COVERAGE_NO_CONTRACTS=1 can turn off contracts while debugging
-# tests to remove noise from stack traces.
-# $set_env.py: COVERAGE_NO_CONTRACTS - Disable PyContracts to simplify stack traces.
-USE_CONTRACTS = env.TESTING and not bool(int(os.environ.get("COVERAGE_NO_CONTRACTS", 0)))
-
-# Use PyContracts for assertion testing on parameters and returns, but only if
-# we are running our own test suite.
-if USE_CONTRACTS:
- from contracts import contract # pylint: disable=unused-import
- from contracts import new_contract as raw_new_contract
-
- def new_contract(*args, **kwargs):
- """A proxy for contracts.new_contract that doesn't mind happening twice."""
- try:
- raw_new_contract(*args, **kwargs)
- except ValueError:
- # During meta-coverage, this module is imported twice, and
- # PyContracts doesn't like redefining contracts. It's OK.
- pass
-
- # Define contract words that PyContract doesn't have.
- new_contract('bytes', lambda v: isinstance(v, bytes))
- if env.PY3:
- new_contract('unicode', lambda v: isinstance(v, unicode_class))
-
- def one_of(argnames):
- """Ensure that only one of the argnames is non-None."""
- def _decorator(func):
- argnameset = {name.strip() for name in argnames.split(",")}
- def _wrapper(*args, **kwargs):
- vals = [kwargs.get(name) for name in argnameset]
- assert sum(val is not None for val in vals) == 1
- return func(*args, **kwargs)
- return _wrapper
- return _decorator
-else: # pragma: not testing
- # We aren't using real PyContracts, so just define our decorators as
- # stunt-double no-ops.
- contract = dummy_decorator_with_args
- one_of = dummy_decorator_with_args
-
- def new_contract(*args_unused, **kwargs_unused):
- """Dummy no-op implementation of `new_contract`."""
- pass
-
-
-def nice_pair(pair):
- """Make a nice string representation of a pair of numbers.
-
- If the numbers are equal, just return the number, otherwise return the pair
- with a dash between them, indicating the range.
-
- """
- start, end = pair
- if start == end:
- return "%d" % start
- else:
- return "%d-%d" % (start, end)
-
-
-def expensive(fn):
- """A decorator to indicate that a method shouldn't be called more than once.
-
- Normally, this does nothing. During testing, this raises an exception if
- called more than once.
-
- """
- if env.TESTING:
- attr = "_once_" + fn.__name__
-
- def _wrapper(self):
- if hasattr(self, attr):
- raise AssertionError("Shouldn't have called %s more than once" % fn.__name__)
- setattr(self, attr, True)
- return fn(self)
- return _wrapper
- else:
- return fn # pragma: not testing
-
-
-def bool_or_none(b):
- """Return bool(b), but preserve None."""
- if b is None:
- return None
- else:
- return bool(b)
-
-
-def join_regex(regexes):
- """Combine a list of regexes into one that matches any of them."""
- return "|".join("(?:%s)" % r for r in regexes)
-
-
-def file_be_gone(path):
- """Remove a file, and don't get annoyed if it doesn't exist."""
- try:
- os.remove(path)
- except OSError as e:
- if e.errno != errno.ENOENT:
- raise
-
-
-def ensure_dir(directory):
- """Make sure the directory exists.
-
- If `directory` is None or empty, do nothing.
- """
- if directory and not os.path.isdir(directory):
- os.makedirs(directory)
-
-
-def ensure_dir_for_file(path):
- """Make sure the directory for the path exists."""
- ensure_dir(os.path.dirname(path))
-
-
-def output_encoding(outfile=None):
- """Determine the encoding to use for output written to `outfile` or stdout."""
- if outfile is None:
- outfile = sys.stdout
- encoding = (
- getattr(outfile, "encoding", None) or
- getattr(sys.__stdout__, "encoding", None) or
- locale.getpreferredencoding()
- )
- return encoding
-
-
-def filename_suffix(suffix):
- """Compute a filename suffix for a data file.
-
- If `suffix` is a string or None, simply return it. If `suffix` is True,
- then build a suffix incorporating the hostname, process id, and a random
- number.
-
- Returns a string or None.
-
- """
- if suffix is True:
- # If data_suffix was a simple true value, then make a suffix with
- # plenty of distinguishing information. We do this here in
- # `save()` at the last minute so that the pid will be correct even
- # if the process forks.
- dice = random.Random(os.urandom(8)).randint(0, 999999)
- suffix = "%s.%s.%06d" % (socket.gethostname(), os.getpid(), dice)
- return suffix
-
-
-class Hasher(object):
- """Hashes Python data into md5."""
- def __init__(self):
- self.md5 = hashlib.md5()
-
- def update(self, v):
- """Add `v` to the hash, recursively if needed."""
- self.md5.update(to_bytes(str(type(v))))
- if isinstance(v, unicode_class):
- self.md5.update(v.encode('utf8'))
- elif isinstance(v, bytes):
- self.md5.update(v)
- elif v is None:
- pass
- elif isinstance(v, (int, float)):
- self.md5.update(to_bytes(str(v)))
- elif isinstance(v, (tuple, list)):
- for e in v:
- self.update(e)
- elif isinstance(v, dict):
- keys = v.keys()
- for k in sorted(keys):
- self.update(k)
- self.update(v[k])
- else:
- for k in dir(v):
- if k.startswith('__'):
- continue
- a = getattr(v, k)
- if inspect.isroutine(a):
- continue
- self.update(k)
- self.update(a)
- self.md5.update(b'.')
-
- def hexdigest(self):
- """Retrieve the hex digest of the hash."""
- return self.md5.hexdigest()
-
-
-def _needs_to_implement(that, func_name):
- """Helper to raise NotImplementedError in interface stubs."""
- if hasattr(that, "_coverage_plugin_name"):
- thing = "Plugin"
- name = that._coverage_plugin_name
- else:
- thing = "Class"
- klass = that.__class__
- name = "{klass.__module__}.{klass.__name__}".format(klass=klass)
-
- raise NotImplementedError(
- "{thing} {name!r} needs to implement {func_name}()".format(
- thing=thing, name=name, func_name=func_name
- )
- )
-
-
-class DefaultValue(object):
- """A sentinel object to use for unusual default-value needs.
-
- Construct with a string that will be used as the repr, for display in help
- and Sphinx output.
-
- """
- def __init__(self, display_as):
- self.display_as = display_as
-
- def __repr__(self):
- return self.display_as
-
-
-def substitute_variables(text, variables):
- """Substitute ``${VAR}`` variables in `text` with their values.
-
- Variables in the text can take a number of shell-inspired forms::
-
- $VAR
- ${VAR}
- ${VAR?} strict: an error if VAR isn't defined.
- ${VAR-missing} defaulted: "missing" if VAR isn't defined.
- $$ just a dollar sign.
-
- `variables` is a dictionary of variable values.
-
- Returns the resulting text with values substituted.
-
- """
- dollar_pattern = r"""(?x) # Use extended regex syntax
- \$ # A dollar sign,
- (?: # then
- (?P<dollar>\$) | # a dollar sign, or
- (?P<word1>\w+) | # a plain word, or
- { # a {-wrapped
- (?P<word2>\w+) # word,
- (?:
- (?P<strict>\?) | # with a strict marker
- -(?P<defval>[^}]*) # or a default value
- )? # maybe.
- }
- )
- """
-
- def dollar_replace(match):
- """Called for each $replacement."""
- # Only one of the groups will have matched, just get its text.
- word = next(g for g in match.group('dollar', 'word1', 'word2') if g)
- if word == "$":
- return "$"
- elif word in variables:
- return variables[word]
- elif match.group('strict'):
- msg = "Variable {} is undefined: {!r}".format(word, text)
- raise CoverageException(msg)
- else:
- return match.group('defval')
-
- text = re.sub(dollar_pattern, dollar_replace, text)
- return text
-
-
-class BaseCoverageException(Exception):
- """The base of all Coverage exceptions."""
- pass
-
-
-class CoverageException(BaseCoverageException):
- """An exception raised by a coverage.py function."""
- pass
-
-
-class NoSource(CoverageException):
- """We couldn't find the source for a module."""
- pass
-
-
-class NoCode(NoSource):
- """We couldn't find any code at all."""
- pass
-
-
-class NotPython(CoverageException):
- """A source file turned out not to be parsable Python."""
- pass
-
-
-class ExceptionDuringRun(CoverageException):
- """An exception happened while running customer code.
-
- Construct it with three arguments, the values from `sys.exc_info`.
-
- """
- pass
-
-
-class StopEverything(BaseCoverageException):
- """An exception that means everything should stop.
-
- The CoverageTest class converts these to SkipTest, so that when running
- tests, raising this exception will automatically skip the test.
-
- """
- pass
diff --git a/contrib/python/coverage/py2/coverage/multiproc.py b/contrib/python/coverage/py2/coverage/multiproc.py
deleted file mode 100644
index 21ed2e2c95..0000000000
--- a/contrib/python/coverage/py2/coverage/multiproc.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Monkey-patching to add multiprocessing support for coverage.py"""
-
-import multiprocessing
-import multiprocessing.process
-import os
-import os.path
-import sys
-import traceback
-
-from coverage import env
-from coverage.misc import contract
-
-# An attribute that will be set on the module to indicate that it has been
-# monkey-patched.
-PATCHED_MARKER = "_coverage$patched"
-
-COVERAGE_CONFIGURATION_ENV = "_COVERAGE_CONFIGURATION_ENV"
-
-
-if env.PYVERSION >= (3, 4):
- OriginalProcess = multiprocessing.process.BaseProcess
-else:
- OriginalProcess = multiprocessing.Process
-
-original_bootstrap = OriginalProcess._bootstrap
-
-class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method
- """A replacement for multiprocess.Process that starts coverage."""
-
- def _bootstrap(self, *args, **kwargs):
- """Wrapper around _bootstrap to start coverage."""
- try:
- from coverage import Coverage # avoid circular import
- import json
- kwconf = json.loads(os.environ[COVERAGE_CONFIGURATION_ENV])
- cov = Coverage(**kwconf)
- cov._warn_preimported_source = False
- cov.start()
- debug = cov._debug
- if debug.should("multiproc"):
- debug.write("Calling multiprocessing bootstrap")
- except Exception:
- print("Exception during multiprocessing bootstrap init:")
- traceback.print_exc(file=sys.stdout)
- sys.stdout.flush()
- raise
- try:
- return original_bootstrap(self, *args, **kwargs)
- finally:
- if debug.should("multiproc"):
- debug.write("Finished multiprocessing bootstrap")
- cov.stop()
- cov.save()
- if debug.should("multiproc"):
- debug.write("Saved multiprocessing data")
-
-class Stowaway(object):
- """An object to pickle, so when it is unpickled, it can apply the monkey-patch."""
- def __init__(self, rcfile):
- self.rcfile = rcfile
-
- def __getstate__(self):
- return {'rcfile': self.rcfile}
-
- def __setstate__(self, state):
- patch_multiprocessing(state['rcfile'])
-
-
-@contract(rcfile=str)
-def patch_multiprocessing(rcfile, coverage_args):
- """Monkey-patch the multiprocessing module.
-
- This enables coverage measurement of processes started by multiprocessing.
- This involves aggressive monkey-patching.
-
- `rcfile` is the path to the rcfile being used.
-
- """
-
- if hasattr(multiprocessing, PATCHED_MARKER):
- return
-
- if env.PYVERSION >= (3, 4):
- OriginalProcess._bootstrap = ProcessWithCoverage._bootstrap
- else:
- multiprocessing.Process = ProcessWithCoverage
-
- # Set the value in ProcessWithCoverage that will be pickled into the child
- # process.
- os.environ["COVERAGE_RCFILE"] = os.path.abspath(rcfile)
-
- os.environ[COVERAGE_CONFIGURATION_ENV] = coverage_args
-
- # When spawning processes rather than forking them, we have no state in the
- # new process. We sneak in there with a Stowaway: we stuff one of our own
- # objects into the data that gets pickled and sent to the sub-process. When
- # the Stowaway is unpickled, it's __setstate__ method is called, which
- # re-applies the monkey-patch.
- # Windows only spawns, so this is needed to keep Windows working.
- try:
- from multiprocessing import spawn
- original_get_preparation_data = spawn.get_preparation_data
- except (ImportError, AttributeError):
- pass
- else:
- def get_preparation_data_with_stowaway(name):
- """Get the original preparation data, and also insert our stowaway."""
- d = original_get_preparation_data(name)
- d['stowaway'] = Stowaway(rcfile)
- return d
-
- spawn.get_preparation_data = get_preparation_data_with_stowaway
-
- setattr(multiprocessing, PATCHED_MARKER, True)
diff --git a/contrib/python/coverage/py2/coverage/numbits.py b/contrib/python/coverage/py2/coverage/numbits.py
deleted file mode 100644
index 6ca96fbcf7..0000000000
--- a/contrib/python/coverage/py2/coverage/numbits.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""
-Functions to manipulate packed binary representations of number sets.
-
-To save space, coverage stores sets of line numbers in SQLite using a packed
-binary representation called a numbits. A numbits is a set of positive
-integers.
-
-A numbits is stored as a blob in the database. The exact meaning of the bytes
-in the blobs should be considered an implementation detail that might change in
-the future. Use these functions to work with those binary blobs of data.
-
-"""
-import json
-
-from coverage import env
-from coverage.backward import byte_to_int, bytes_to_ints, binary_bytes, zip_longest
-from coverage.misc import contract, new_contract
-
-if env.PY3:
- def _to_blob(b):
- """Convert a bytestring into a type SQLite will accept for a blob."""
- return b
-
- new_contract('blob', lambda v: isinstance(v, bytes))
-else:
- def _to_blob(b):
- """Convert a bytestring into a type SQLite will accept for a blob."""
- return buffer(b) # pylint: disable=undefined-variable
-
- new_contract('blob', lambda v: isinstance(v, buffer)) # pylint: disable=undefined-variable
-
-
-@contract(nums='Iterable', returns='blob')
-def nums_to_numbits(nums):
- """Convert `nums` into a numbits.
-
- Arguments:
- nums: a reusable iterable of integers, the line numbers to store.
-
- Returns:
- A binary blob.
- """
- try:
- nbytes = max(nums) // 8 + 1
- except ValueError:
- # nums was empty.
- return _to_blob(b'')
- b = bytearray(nbytes)
- for num in nums:
- b[num//8] |= 1 << num % 8
- return _to_blob(bytes(b))
-
-
-@contract(numbits='blob', returns='list[int]')
-def numbits_to_nums(numbits):
- """Convert a numbits into a list of numbers.
-
- Arguments:
- numbits: a binary blob, the packed number set.
-
- Returns:
- A list of ints.
-
- When registered as a SQLite function by :func:`register_sqlite_functions`,
- this returns a string, a JSON-encoded list of ints.
-
- """
- nums = []
- for byte_i, byte in enumerate(bytes_to_ints(numbits)):
- for bit_i in range(8):
- if (byte & (1 << bit_i)):
- nums.append(byte_i * 8 + bit_i)
- return nums
-
-
-@contract(numbits1='blob', numbits2='blob', returns='blob')
-def numbits_union(numbits1, numbits2):
- """Compute the union of two numbits.
-
- Returns:
- A new numbits, the union of `numbits1` and `numbits2`.
- """
- byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
- return _to_blob(binary_bytes(b1 | b2 for b1, b2 in byte_pairs))
-
-
-@contract(numbits1='blob', numbits2='blob', returns='blob')
-def numbits_intersection(numbits1, numbits2):
- """Compute the intersection of two numbits.
-
- Returns:
- A new numbits, the intersection `numbits1` and `numbits2`.
- """
- byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
- intersection_bytes = binary_bytes(b1 & b2 for b1, b2 in byte_pairs)
- return _to_blob(intersection_bytes.rstrip(b'\0'))
-
-
-@contract(numbits1='blob', numbits2='blob', returns='bool')
-def numbits_any_intersection(numbits1, numbits2):
- """Is there any number that appears in both numbits?
-
- Determine whether two number sets have a non-empty intersection. This is
- faster than computing the intersection.
-
- Returns:
- A bool, True if there is any number in both `numbits1` and `numbits2`.
- """
- byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
- return any(b1 & b2 for b1, b2 in byte_pairs)
-
-
-@contract(num='int', numbits='blob', returns='bool')
-def num_in_numbits(num, numbits):
- """Does the integer `num` appear in `numbits`?
-
- Returns:
- A bool, True if `num` is a member of `numbits`.
- """
- nbyte, nbit = divmod(num, 8)
- if nbyte >= len(numbits):
- return False
- return bool(byte_to_int(numbits[nbyte]) & (1 << nbit))
-
-
-def register_sqlite_functions(connection):
- """
- Define numbits functions in a SQLite connection.
-
- This defines these functions for use in SQLite statements:
-
- * :func:`numbits_union`
- * :func:`numbits_intersection`
- * :func:`numbits_any_intersection`
- * :func:`num_in_numbits`
- * :func:`numbits_to_nums`
-
- `connection` is a :class:`sqlite3.Connection <python:sqlite3.Connection>`
- object. After creating the connection, pass it to this function to
- register the numbits functions. Then you can use numbits functions in your
- queries::
-
- import sqlite3
- from coverage.numbits import register_sqlite_functions
-
- conn = sqlite3.connect('example.db')
- register_sqlite_functions(conn)
- c = conn.cursor()
- # Kind of a nonsense query: find all the files and contexts that
- # executed line 47 in any file:
- c.execute(
- "select file_id, context_id from line_bits where num_in_numbits(?, numbits)",
- (47,)
- )
- """
- connection.create_function("numbits_union", 2, numbits_union)
- connection.create_function("numbits_intersection", 2, numbits_intersection)
- connection.create_function("numbits_any_intersection", 2, numbits_any_intersection)
- connection.create_function("num_in_numbits", 2, num_in_numbits)
- connection.create_function("numbits_to_nums", 1, lambda b: json.dumps(numbits_to_nums(b)))
diff --git a/contrib/python/coverage/py2/coverage/parser.py b/contrib/python/coverage/py2/coverage/parser.py
deleted file mode 100644
index 258f956039..0000000000
--- a/contrib/python/coverage/py2/coverage/parser.py
+++ /dev/null
@@ -1,1276 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Code parsing for coverage.py."""
-
-import ast
-import collections
-import os
-import re
-import token
-import tokenize
-
-from coverage import env
-from coverage.backward import range # pylint: disable=redefined-builtin
-from coverage.backward import bytes_to_ints, string_class
-from coverage.bytecode import code_objects
-from coverage.debug import short_stack
-from coverage.misc import contract, join_regex, new_contract, nice_pair, one_of
-from coverage.misc import NoSource, NotPython, StopEverything
-from coverage.phystokens import compile_unicode, generate_tokens, neuter_encoding_declaration
-
-
-class PythonParser(object):
- """Parse code to find executable lines, excluded lines, etc.
-
- This information is all based on static analysis: no code execution is
- involved.
-
- """
- @contract(text='unicode|None')
- def __init__(self, text=None, filename=None, exclude=None):
- """
- Source can be provided as `text`, the text itself, or `filename`, from
- which the text will be read. Excluded lines are those that match
- `exclude`, a regex.
-
- """
- assert text or filename, "PythonParser needs either text or filename"
- self.filename = filename or "<code>"
- self.text = text
- if not self.text:
- from coverage.python import get_python_source
- try:
- self.text = get_python_source(self.filename)
- except IOError as err:
- raise NoSource(
- "No source for code: '%s': %s" % (self.filename, err)
- )
-
- self.exclude = exclude
-
- # The text lines of the parsed code.
- self.lines = self.text.split('\n')
-
- # The normalized line numbers of the statements in the code. Exclusions
- # are taken into account, and statements are adjusted to their first
- # lines.
- self.statements = set()
-
- # The normalized line numbers of the excluded lines in the code,
- # adjusted to their first lines.
- self.excluded = set()
-
- # The raw_* attributes are only used in this class, and in
- # lab/parser.py to show how this class is working.
-
- # The line numbers that start statements, as reported by the line
- # number table in the bytecode.
- self.raw_statements = set()
-
- # The raw line numbers of excluded lines of code, as marked by pragmas.
- self.raw_excluded = set()
-
- # The line numbers of class definitions.
- self.raw_classdefs = set()
-
- # Function definitions (start, end, name)
- self._raw_funcdefs = set()
-
- # The line numbers of docstring lines.
- self.raw_docstrings = set()
-
- # Internal detail, used by lab/parser.py.
- self.show_tokens = False
-
- # A dict mapping line numbers to lexical statement starts for
- # multi-line statements.
- self._multiline = {}
-
- # Lazily-created ByteParser, arc data, and missing arc descriptions.
- self._byte_parser = None
- self._all_arcs = None
- self._missing_arc_fragments = None
-
- @property
- def byte_parser(self):
- """Create a ByteParser on demand."""
- if not self._byte_parser:
- self._byte_parser = ByteParser(self.text, filename=self.filename)
- return self._byte_parser
-
- @property
- def raw_funcdefs(self):
- return self._raw_funcdefs
-
- def lines_matching(self, *regexes):
- """Find the lines matching one of a list of regexes.
-
- Returns a set of line numbers, the lines that contain a match for one
- of the regexes in `regexes`. The entire line needn't match, just a
- part of it.
-
- """
- combined = join_regex(regexes)
- if env.PY2:
- combined = combined.decode("utf8")
- regex_c = re.compile(combined)
- matches = set()
- for i, ltext in enumerate(self.lines, start=1):
- if regex_c.search(ltext):
- matches.add(i)
- return matches
-
- def _raw_parse(self):
- """Parse the source to find the interesting facts about its lines.
-
- A handful of attributes are updated.
-
- """
- # Find lines which match an exclusion pattern.
- if self.exclude:
- self.raw_excluded = self.lines_matching(self.exclude)
-
- # Tokenize, to find excluded suites, to find docstrings, and to find
- # multi-line statements.
- indent = 0
- exclude_indent = 0
- excluding = False
- excluding_decorators = False
- prev_toktype = token.INDENT
- first_line = None
- empty = True
- first_on_line = True
-
- tokgen = generate_tokens(self.text)
- for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
- if self.show_tokens: # pragma: debugging
- print("%10s %5s %-20r %r" % (
- tokenize.tok_name.get(toktype, toktype),
- nice_pair((slineno, elineno)), ttext, ltext
- ))
- if toktype == token.INDENT:
- indent += 1
- elif toktype == token.DEDENT:
- indent -= 1
- elif toktype == token.NAME:
- if ttext == 'class':
- # Class definitions look like branches in the bytecode, so
- # we need to exclude them. The simplest way is to note the
- # lines with the 'class' keyword.
- self.raw_classdefs.add(slineno)
- elif toktype == token.OP:
- if ttext == ':':
- should_exclude = (elineno in self.raw_excluded) or excluding_decorators
- if not excluding and should_exclude:
- # Start excluding a suite. We trigger off of the colon
- # token so that the #pragma comment will be recognized on
- # the same line as the colon.
- self.raw_excluded.add(elineno)
- exclude_indent = indent
- excluding = True
- excluding_decorators = False
- elif ttext == '@' and first_on_line:
- # A decorator.
- if elineno in self.raw_excluded:
- excluding_decorators = True
- if excluding_decorators:
- self.raw_excluded.add(elineno)
- elif toktype == token.STRING and prev_toktype == token.INDENT:
- # Strings that are first on an indented line are docstrings.
- # (a trick from trace.py in the stdlib.) This works for
- # 99.9999% of cases. For the rest (!) see:
- # http://stackoverflow.com/questions/1769332/x/1769794#1769794
- self.raw_docstrings.update(range(slineno, elineno+1))
- elif toktype == token.NEWLINE:
- if first_line is not None and elineno != first_line:
- # We're at the end of a line, and we've ended on a
- # different line than the first line of the statement,
- # so record a multi-line range.
- for l in range(first_line, elineno+1):
- self._multiline[l] = first_line
- first_line = None
- first_on_line = True
-
- if ttext.strip() and toktype != tokenize.COMMENT:
- # A non-whitespace token.
- empty = False
- if first_line is None:
- # The token is not whitespace, and is the first in a
- # statement.
- first_line = slineno
- # Check whether to end an excluded suite.
- if excluding and indent <= exclude_indent:
- excluding = False
- if excluding:
- self.raw_excluded.add(elineno)
- first_on_line = False
-
- prev_toktype = toktype
-
- # Find the starts of the executable statements.
- if not empty:
- self.raw_statements.update(self.byte_parser._find_statements())
-
- # The first line of modules can lie and say 1 always, even if the first
- # line of code is later. If so, map 1 to the actual first line of the
- # module.
- if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
- self._multiline[1] = min(self.raw_statements)
-
- def first_line(self, line):
- """Return the first line number of the statement including `line`."""
- if line < 0:
- line = -self._multiline.get(-line, -line)
- else:
- line = self._multiline.get(line, line)
- return line
-
- def first_lines(self, lines):
- """Map the line numbers in `lines` to the correct first line of the
- statement.
-
- Returns a set of the first lines.
-
- """
- return {self.first_line(l) for l in lines}
-
- def translate_lines(self, lines):
- """Implement `FileReporter.translate_lines`."""
- return self.first_lines(lines)
-
- def translate_arcs(self, arcs):
- """Implement `FileReporter.translate_arcs`."""
- return [(self.first_line(a), self.first_line(b)) for (a, b) in arcs]
-
- def parse_source(self):
- """Parse source text to find executable lines, excluded lines, etc.
-
- Sets the .excluded and .statements attributes, normalized to the first
- line of multi-line statements.
-
- """
- try:
- self._raw_parse()
- except (tokenize.TokenError, IndentationError) as err:
- if hasattr(err, "lineno"):
- lineno = err.lineno # IndentationError
- else:
- lineno = err.args[1][0] # TokenError
- raise NotPython(
- u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
- self.filename, err.args[0], lineno
- )
- )
-
- self.excluded = self.first_lines(self.raw_excluded)
-
- ignore = self.excluded | self.raw_docstrings
- starts = self.raw_statements - ignore
- self.statements = self.first_lines(starts) - ignore
-
- def arcs(self):
- """Get information about the arcs available in the code.
-
- Returns a set of line number pairs. Line numbers have been normalized
- to the first line of multi-line statements.
-
- """
- if self._all_arcs is None:
- self._analyze_ast()
- return self._all_arcs
-
- def _analyze_ast(self):
- """Run the AstArcAnalyzer and save its results.
-
- `_all_arcs` is the set of arcs in the code.
-
- """
- aaa = AstArcAnalyzer(self.text, self.raw_statements, self._multiline)
- aaa.analyze()
-
- self._all_arcs = set()
- for l1, l2 in aaa.arcs:
- fl1 = self.first_line(l1)
- fl2 = self.first_line(l2)
- if fl1 != fl2:
- self._all_arcs.add((fl1, fl2))
-
- self._missing_arc_fragments = aaa.missing_arc_fragments
- self._raw_funcdefs = aaa.funcdefs
-
- def exit_counts(self):
- """Get a count of exits from that each line.
-
- Excluded lines are excluded.
-
- """
- exit_counts = collections.defaultdict(int)
- for l1, l2 in self.arcs():
- if l1 < 0:
- # Don't ever report -1 as a line number
- continue
- if l1 in self.excluded:
- # Don't report excluded lines as line numbers.
- continue
- if l2 in self.excluded:
- # Arcs to excluded lines shouldn't count.
- continue
- exit_counts[l1] += 1
-
- # Class definitions have one extra exit, so remove one for each:
- for l in self.raw_classdefs:
- # Ensure key is there: class definitions can include excluded lines.
- if l in exit_counts:
- exit_counts[l] -= 1
-
- return exit_counts
-
- def missing_arc_description(self, start, end, executed_arcs=None):
- """Provide an English sentence describing a missing arc."""
- if self._missing_arc_fragments is None:
- self._analyze_ast()
-
- actual_start = start
-
- if (
- executed_arcs and
- end < 0 and end == -start and
- (end, start) not in executed_arcs and
- (end, start) in self._missing_arc_fragments
- ):
- # It's a one-line callable, and we never even started it,
- # and we have a message about not starting it.
- start, end = end, start
-
- fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
-
- msgs = []
- for smsg, emsg in fragment_pairs:
- if emsg is None:
- if end < 0:
- # Hmm, maybe we have a one-line callable, let's check.
- if (-end, end) in self._missing_arc_fragments:
- return self.missing_arc_description(-end, end)
- emsg = "didn't jump to the function exit"
- else:
- emsg = "didn't jump to line {lineno}"
- emsg = emsg.format(lineno=end)
-
- msg = "line {start} {emsg}".format(start=actual_start, emsg=emsg)
- if smsg is not None:
- msg += ", because {smsg}".format(smsg=smsg.format(lineno=actual_start))
-
- msgs.append(msg)
-
- return " or ".join(msgs)
-
-
-class ByteParser(object):
- """Parse bytecode to understand the structure of code."""
-
- @contract(text='unicode')
- def __init__(self, text, code=None, filename=None):
- self.text = text
- if code:
- self.code = code
- else:
- try:
- self.code = compile_unicode(text, filename, "exec")
- except SyntaxError as synerr:
- raise NotPython(
- u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
- filename, synerr.msg, synerr.lineno
- )
- )
-
- # Alternative Python implementations don't always provide all the
- # attributes on code objects that we need to do the analysis.
- for attr in ['co_lnotab', 'co_firstlineno']:
- if not hasattr(self.code, attr):
- raise StopEverything( # pragma: only jython
- "This implementation of Python doesn't support code analysis.\n"
- "Run coverage.py under another Python for this command."
- )
-
- def child_parsers(self):
- """Iterate over all the code objects nested within this one.
-
- The iteration includes `self` as its first value.
-
- """
- return (ByteParser(self.text, code=c) for c in code_objects(self.code))
-
- def _line_numbers(self):
- """Yield the line numbers possible in this code object.
-
- Uses co_lnotab described in Python/compile.c to find the
- line numbers. Produces a sequence: l0, l1, ...
- """
- if hasattr(self.code, "co_lines"):
- for _, _, line in self.code.co_lines():
- if line is not None:
- yield line
- else:
- # Adapted from dis.py in the standard library.
- byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
- line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
-
- last_line_num = None
- line_num = self.code.co_firstlineno
- byte_num = 0
- for byte_incr, line_incr in zip(byte_increments, line_increments):
- if byte_incr:
- if line_num != last_line_num:
- yield line_num
- last_line_num = line_num
- byte_num += byte_incr
- if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
- line_incr -= 0x100
- line_num += line_incr
- if line_num != last_line_num:
- yield line_num
-
- def _find_statements(self):
- """Find the statements in `self.code`.
-
- Produce a sequence of line numbers that start statements. Recurses
- into all code objects reachable from `self.code`.
-
- """
- for bp in self.child_parsers():
- # Get all of the lineno information from this code.
- for l in bp._line_numbers():
- yield l
-
-
-#
-# AST analysis
-#
-
-class LoopBlock(object):
- """A block on the block stack representing a `for` or `while` loop."""
- @contract(start=int)
- def __init__(self, start):
- # The line number where the loop starts.
- self.start = start
- # A set of ArcStarts, the arcs from break statements exiting this loop.
- self.break_exits = set()
-
-
-class FunctionBlock(object):
- """A block on the block stack representing a function definition."""
- @contract(start=int, name=str)
- def __init__(self, start, name):
- # The line number where the function starts.
- self.start = start
- # The name of the function.
- self.name = name
-
-
-class TryBlock(object):
- """A block on the block stack representing a `try` block."""
- @contract(handler_start='int|None', final_start='int|None')
- def __init__(self, handler_start, final_start):
- # The line number of the first "except" handler, if any.
- self.handler_start = handler_start
- # The line number of the "finally:" clause, if any.
- self.final_start = final_start
-
- # The ArcStarts for breaks/continues/returns/raises inside the "try:"
- # that need to route through the "finally:" clause.
- self.break_from = set()
- self.continue_from = set()
- self.return_from = set()
- self.raise_from = set()
-
-
-class ArcStart(collections.namedtuple("Arc", "lineno, cause")):
- """The information needed to start an arc.
-
- `lineno` is the line number the arc starts from.
-
- `cause` is an English text fragment used as the `startmsg` for
- AstArcAnalyzer.missing_arc_fragments. It will be used to describe why an
- arc wasn't executed, so should fit well into a sentence of the form,
- "Line 17 didn't run because {cause}." The fragment can include "{lineno}"
- to have `lineno` interpolated into it.
-
- """
- def __new__(cls, lineno, cause=None):
- return super(ArcStart, cls).__new__(cls, lineno, cause)
-
-
-# Define contract words that PyContract doesn't have.
-# ArcStarts is for a list or set of ArcStart's.
-new_contract('ArcStarts', lambda seq: all(isinstance(x, ArcStart) for x in seq))
-
-
-# Turn on AST dumps with an environment variable.
-# $set_env.py: COVERAGE_AST_DUMP - Dump the AST nodes when parsing code.
-AST_DUMP = bool(int(os.environ.get("COVERAGE_AST_DUMP", 0)))
-
-class NodeList(object):
- """A synthetic fictitious node, containing a sequence of nodes.
-
- This is used when collapsing optimized if-statements, to represent the
- unconditional execution of one of the clauses.
-
- """
- def __init__(self, body):
- self.body = body
- self.lineno = body[0].lineno
-
-
-# TODO: some add_arcs methods here don't add arcs, they return them. Rename them.
-# TODO: the cause messages have too many commas.
-# TODO: Shouldn't the cause messages join with "and" instead of "or"?
-
-class AstArcAnalyzer(object):
- """Analyze source text with an AST to find executable code paths."""
-
- @contract(text='unicode', statements=set)
- def __init__(self, text, statements, multiline):
- self.root_node = ast.parse(neuter_encoding_declaration(text))
- # TODO: I think this is happening in too many places.
- self.statements = {multiline.get(l, l) for l in statements}
- self.multiline = multiline
-
- if AST_DUMP: # pragma: debugging
- # Dump the AST so that failing tests have helpful output.
- print("Statements: {}".format(self.statements))
- print("Multiline map: {}".format(self.multiline))
- ast_dump(self.root_node)
-
- self.arcs = set()
-
- # A map from arc pairs to a list of pairs of sentence fragments:
- # { (start, end): [(startmsg, endmsg), ...], }
- #
- # For an arc from line 17, they should be usable like:
- # "Line 17 {endmsg}, because {startmsg}"
- self.missing_arc_fragments = collections.defaultdict(list)
- self.block_stack = []
- self.funcdefs = set()
-
- # $set_env.py: COVERAGE_TRACK_ARCS - Trace every arc added while parsing code.
- self.debug = bool(int(os.environ.get("COVERAGE_TRACK_ARCS", 0)))
-
- def analyze(self):
- """Examine the AST tree from `root_node` to determine possible arcs.
-
- This sets the `arcs` attribute to be a set of (from, to) line number
- pairs.
-
- """
- for node in ast.walk(self.root_node):
- node_name = node.__class__.__name__
- code_object_handler = getattr(self, "_code_object__" + node_name, None)
- if code_object_handler is not None:
- code_object_handler(node)
-
- @contract(start=int, end=int)
- def add_arc(self, start, end, smsg=None, emsg=None):
- """Add an arc, including message fragments to use if it is missing."""
- if self.debug: # pragma: debugging
- print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg))
- print(short_stack(limit=6))
- self.arcs.add((start, end))
-
- if smsg is not None or emsg is not None:
- self.missing_arc_fragments[(start, end)].append((smsg, emsg))
-
- def nearest_blocks(self):
- """Yield the blocks in nearest-to-farthest order."""
- return reversed(self.block_stack)
-
- @contract(returns=int)
- def line_for_node(self, node):
- """What is the right line number to use for this node?
-
- This dispatches to _line__Node functions where needed.
-
- """
- node_name = node.__class__.__name__
- handler = getattr(self, "_line__" + node_name, None)
- if handler is not None:
- return handler(node)
- else:
- return node.lineno
-
- def _line_decorated(self, node):
- """Compute first line number for things that can be decorated (classes and functions)."""
- lineno = node.lineno
- if env.PYBEHAVIOR.trace_decorated_def:
- if node.decorator_list:
- lineno = node.decorator_list[0].lineno
- return lineno
-
- def _line__Assign(self, node):
- return self.line_for_node(node.value)
-
- _line__ClassDef = _line_decorated
-
- def _line__Dict(self, node):
- # Python 3.5 changed how dict literals are made.
- if env.PYVERSION >= (3, 5) and node.keys:
- if node.keys[0] is not None:
- return node.keys[0].lineno
- else:
- # Unpacked dict literals `{**{'a':1}}` have None as the key,
- # use the value in that case.
- return node.values[0].lineno
- else:
- return node.lineno
-
- _line__FunctionDef = _line_decorated
- _line__AsyncFunctionDef = _line_decorated
-
- def _line__List(self, node):
- if node.elts:
- return self.line_for_node(node.elts[0])
- else:
- return node.lineno
-
- def _line__Module(self, node):
- if env.PYBEHAVIOR.module_firstline_1:
- return 1
- elif node.body:
- return self.line_for_node(node.body[0])
- else:
- # Empty modules have no line number, they always start at 1.
- return 1
-
- # The node types that just flow to the next node with no complications.
- OK_TO_DEFAULT = {
- "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
- "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
- }
-
- @contract(returns='ArcStarts')
- def add_arcs(self, node):
- """Add the arcs for `node`.
-
- Return a set of ArcStarts, exits from this node to the next. Because a
- node represents an entire sub-tree (including its children), the exits
- from a node can be arbitrarily complex::
-
- if something(1):
- if other(2):
- doit(3)
- else:
- doit(5)
-
- There are two exits from line 1: they start at line 3 and line 5.
-
- """
- node_name = node.__class__.__name__
- handler = getattr(self, "_handle__" + node_name, None)
- if handler is not None:
- return handler(node)
- else:
- # No handler: either it's something that's ok to default (a simple
- # statement), or it's something we overlooked. Change this 0 to 1
- # to see if it's overlooked.
- if 0:
- if node_name not in self.OK_TO_DEFAULT:
- print("*** Unhandled: {}".format(node))
-
- # Default for simple statements: one exit from this node.
- return {ArcStart(self.line_for_node(node))}
-
- @one_of("from_start, prev_starts")
- @contract(returns='ArcStarts')
- def add_body_arcs(self, body, from_start=None, prev_starts=None):
- """Add arcs for the body of a compound statement.
-
- `body` is the body node. `from_start` is a single `ArcStart` that can
- be the previous line in flow before this body. `prev_starts` is a set
- of ArcStarts that can be the previous line. Only one of them should be
- given.
-
- Returns a set of ArcStarts, the exits from this body.
-
- """
- if prev_starts is None:
- prev_starts = {from_start}
- for body_node in body:
- lineno = self.line_for_node(body_node)
- first_line = self.multiline.get(lineno, lineno)
- if first_line not in self.statements:
- body_node = self.find_non_missing_node(body_node)
- if body_node is None:
- continue
- lineno = self.line_for_node(body_node)
- for prev_start in prev_starts:
- self.add_arc(prev_start.lineno, lineno, prev_start.cause)
- prev_starts = self.add_arcs(body_node)
- return prev_starts
-
- def find_non_missing_node(self, node):
- """Search `node` looking for a child that has not been optimized away.
-
- This might return the node you started with, or it will work recursively
- to find a child node in self.statements.
-
- Returns a node, or None if none of the node remains.
-
- """
- # This repeats work just done in add_body_arcs, but this duplication
- # means we can avoid a function call in the 99.9999% case of not
- # optimizing away statements.
- lineno = self.line_for_node(node)
- first_line = self.multiline.get(lineno, lineno)
- if first_line in self.statements:
- return node
-
- missing_fn = getattr(self, "_missing__" + node.__class__.__name__, None)
- if missing_fn:
- node = missing_fn(node)
- else:
- node = None
- return node
-
- # Missing nodes: _missing__*
- #
- # Entire statements can be optimized away by Python. They will appear in
- # the AST, but not the bytecode. These functions are called (by
- # find_non_missing_node) to find a node to use instead of the missing
- # node. They can return None if the node should truly be gone.
-
- def _missing__If(self, node):
- # If the if-node is missing, then one of its children might still be
- # here, but not both. So return the first of the two that isn't missing.
- # Use a NodeList to hold the clauses as a single node.
- non_missing = self.find_non_missing_node(NodeList(node.body))
- if non_missing:
- return non_missing
- if node.orelse:
- return self.find_non_missing_node(NodeList(node.orelse))
- return None
-
- def _missing__NodeList(self, node):
- # A NodeList might be a mixture of missing and present nodes. Find the
- # ones that are present.
- non_missing_children = []
- for child in node.body:
- child = self.find_non_missing_node(child)
- if child is not None:
- non_missing_children.append(child)
-
- # Return the simplest representation of the present children.
- if not non_missing_children:
- return None
- if len(non_missing_children) == 1:
- return non_missing_children[0]
- return NodeList(non_missing_children)
-
- def _missing__While(self, node):
- body_nodes = self.find_non_missing_node(NodeList(node.body))
- if not body_nodes:
- return None
- # Make a synthetic While-true node.
- new_while = ast.While()
- new_while.lineno = body_nodes.lineno
- new_while.test = ast.Name()
- new_while.test.lineno = body_nodes.lineno
- new_while.test.id = "True"
- new_while.body = body_nodes.body
- new_while.orelse = None
- return new_while
-
- def is_constant_expr(self, node):
- """Is this a compile-time constant?"""
- node_name = node.__class__.__name__
- if node_name in ["Constant", "NameConstant", "Num"]:
- return "Num"
- elif node_name == "Name":
- if node.id in ["True", "False", "None", "__debug__"]:
- return "Name"
- return None
-
- # In the fullness of time, these might be good tests to write:
- # while EXPR:
- # while False:
- # listcomps hidden deep in other expressions
- # listcomps hidden in lists: x = [[i for i in range(10)]]
- # nested function definitions
-
-
- # Exit processing: process_*_exits
- #
- # These functions process the four kinds of jump exits: break, continue,
- # raise, and return. To figure out where an exit goes, we have to look at
- # the block stack context. For example, a break will jump to the nearest
- # enclosing loop block, or the nearest enclosing finally block, whichever
- # is nearer.
-
- @contract(exits='ArcStarts')
- def process_break_exits(self, exits):
- """Add arcs due to jumps from `exits` being breaks."""
- for block in self.nearest_blocks():
- if isinstance(block, LoopBlock):
- block.break_exits.update(exits)
- break
- elif isinstance(block, TryBlock) and block.final_start is not None:
- block.break_from.update(exits)
- break
-
- @contract(exits='ArcStarts')
- def process_continue_exits(self, exits):
- """Add arcs due to jumps from `exits` being continues."""
- for block in self.nearest_blocks():
- if isinstance(block, LoopBlock):
- for xit in exits:
- self.add_arc(xit.lineno, block.start, xit.cause)
- break
- elif isinstance(block, TryBlock) and block.final_start is not None:
- block.continue_from.update(exits)
- break
-
- @contract(exits='ArcStarts')
- def process_raise_exits(self, exits):
- """Add arcs due to jumps from `exits` being raises."""
- for block in self.nearest_blocks():
- if isinstance(block, TryBlock):
- if block.handler_start is not None:
- for xit in exits:
- self.add_arc(xit.lineno, block.handler_start, xit.cause)
- break
- elif block.final_start is not None:
- block.raise_from.update(exits)
- break
- elif isinstance(block, FunctionBlock):
- for xit in exits:
- self.add_arc(
- xit.lineno, -block.start, xit.cause,
- "didn't except from function {!r}".format(block.name),
- )
- break
-
- @contract(exits='ArcStarts')
- def process_return_exits(self, exits):
- """Add arcs due to jumps from `exits` being returns."""
- for block in self.nearest_blocks():
- if isinstance(block, TryBlock) and block.final_start is not None:
- block.return_from.update(exits)
- break
- elif isinstance(block, FunctionBlock):
- for xit in exits:
- self.add_arc(
- xit.lineno, -block.start, xit.cause,
- "didn't return from function {!r}".format(block.name),
- )
- break
-
-
- # Handlers: _handle__*
- #
- # Each handler deals with a specific AST node type, dispatched from
- # add_arcs. Handlers return the set of exits from that node, and can
- # also call self.add_arc to record arcs they find. These functions mirror
- # the Python semantics of each syntactic construct. See the docstring
- # for add_arcs to understand the concept of exits from a node.
-
- @contract(returns='ArcStarts')
- def _handle__Break(self, node):
- here = self.line_for_node(node)
- break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed")
- self.process_break_exits([break_start])
- return set()
-
- @contract(returns='ArcStarts')
- def _handle_decorated(self, node):
- """Add arcs for things that can be decorated (classes and functions)."""
- main_line = last = node.lineno
- if node.decorator_list:
- if env.PYBEHAVIOR.trace_decorated_def:
- last = None
- for dec_node in node.decorator_list:
- dec_start = self.line_for_node(dec_node)
- if last is not None and dec_start != last:
- self.add_arc(last, dec_start)
- last = dec_start
- if env.PYBEHAVIOR.trace_decorated_def:
- self.add_arc(last, main_line)
- last = main_line
- # The definition line may have been missed, but we should have it
- # in `self.statements`. For some constructs, `line_for_node` is
- # not what we'd think of as the first line in the statement, so map
- # it to the first one.
- if node.body:
- body_start = self.line_for_node(node.body[0])
- body_start = self.multiline.get(body_start, body_start)
- for lineno in range(last+1, body_start):
- if lineno in self.statements:
- self.add_arc(last, lineno)
- last = lineno
- # The body is handled in collect_arcs.
- return {ArcStart(last)}
-
- _handle__ClassDef = _handle_decorated
-
- @contract(returns='ArcStarts')
- def _handle__Continue(self, node):
- here = self.line_for_node(node)
- continue_start = ArcStart(here, cause="the continue on line {lineno} wasn't executed")
- self.process_continue_exits([continue_start])
- return set()
-
- @contract(returns='ArcStarts')
- def _handle__For(self, node):
- start = self.line_for_node(node.iter)
- self.block_stack.append(LoopBlock(start=start))
- from_start = ArcStart(start, cause="the loop on line {lineno} never started")
- exits = self.add_body_arcs(node.body, from_start=from_start)
- # Any exit from the body will go back to the top of the loop.
- for xit in exits:
- self.add_arc(xit.lineno, start, xit.cause)
- my_block = self.block_stack.pop()
- exits = my_block.break_exits
- from_start = ArcStart(start, cause="the loop on line {lineno} didn't complete")
- if node.orelse:
- else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
- exits |= else_exits
- else:
- # No else clause: exit from the for line.
- exits.add(from_start)
- return exits
-
- _handle__AsyncFor = _handle__For
-
- _handle__FunctionDef = _handle_decorated
- _handle__AsyncFunctionDef = _handle_decorated
-
- @contract(returns='ArcStarts')
- def _handle__If(self, node):
- start = self.line_for_node(node.test)
- from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
- exits = self.add_body_arcs(node.body, from_start=from_start)
- from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
- exits |= self.add_body_arcs(node.orelse, from_start=from_start)
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__NodeList(self, node):
- start = self.line_for_node(node)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__Raise(self, node):
- here = self.line_for_node(node)
- raise_start = ArcStart(here, cause="the raise on line {lineno} wasn't executed")
- self.process_raise_exits([raise_start])
- # `raise` statement jumps away, no exits from here.
- return set()
-
- @contract(returns='ArcStarts')
- def _handle__Return(self, node):
- here = self.line_for_node(node)
- return_start = ArcStart(here, cause="the return on line {lineno} wasn't executed")
- self.process_return_exits([return_start])
- # `return` statement jumps away, no exits from here.
- return set()
-
- @contract(returns='ArcStarts')
- def _handle__Try(self, node):
- if node.handlers:
- handler_start = self.line_for_node(node.handlers[0])
- else:
- handler_start = None
-
- if node.finalbody:
- final_start = self.line_for_node(node.finalbody[0])
- else:
- final_start = None
-
- try_block = TryBlock(handler_start, final_start)
- self.block_stack.append(try_block)
-
- start = self.line_for_node(node)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
-
- # We're done with the `try` body, so this block no longer handles
- # exceptions. We keep the block so the `finally` clause can pick up
- # flows from the handlers and `else` clause.
- if node.finalbody:
- try_block.handler_start = None
- if node.handlers:
- # If there are `except` clauses, then raises in the try body
- # will already jump to them. Start this set over for raises in
- # `except` and `else`.
- try_block.raise_from = set()
- else:
- self.block_stack.pop()
-
- handler_exits = set()
-
- if node.handlers:
- last_handler_start = None
- for handler_node in node.handlers:
- handler_start = self.line_for_node(handler_node)
- if last_handler_start is not None:
- self.add_arc(last_handler_start, handler_start)
- last_handler_start = handler_start
- from_cause = "the exception caught by line {lineno} didn't happen"
- from_start = ArcStart(handler_start, cause=from_cause)
- handler_exits |= self.add_body_arcs(handler_node.body, from_start=from_start)
-
- if node.orelse:
- exits = self.add_body_arcs(node.orelse, prev_starts=exits)
-
- exits |= handler_exits
-
- if node.finalbody:
- self.block_stack.pop()
- final_from = ( # You can get to the `finally` clause from:
- exits | # the exits of the body or `else` clause,
- try_block.break_from | # or a `break`,
- try_block.continue_from | # or a `continue`,
- try_block.raise_from | # or a `raise`,
- try_block.return_from # or a `return`.
- )
-
- final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from)
-
- if try_block.break_from:
- if env.PYBEHAVIOR.finally_jumps_back:
- for break_line in try_block.break_from:
- lineno = break_line.lineno
- cause = break_line.cause.format(lineno=lineno)
- for final_exit in final_exits:
- self.add_arc(final_exit.lineno, lineno, cause)
- breaks = try_block.break_from
- else:
- breaks = self._combine_finally_starts(try_block.break_from, final_exits)
- self.process_break_exits(breaks)
-
- if try_block.continue_from:
- if env.PYBEHAVIOR.finally_jumps_back:
- for continue_line in try_block.continue_from:
- lineno = continue_line.lineno
- cause = continue_line.cause.format(lineno=lineno)
- for final_exit in final_exits:
- self.add_arc(final_exit.lineno, lineno, cause)
- continues = try_block.continue_from
- else:
- continues = self._combine_finally_starts(try_block.continue_from, final_exits)
- self.process_continue_exits(continues)
-
- if try_block.raise_from:
- self.process_raise_exits(
- self._combine_finally_starts(try_block.raise_from, final_exits)
- )
-
- if try_block.return_from:
- if env.PYBEHAVIOR.finally_jumps_back:
- for return_line in try_block.return_from:
- lineno = return_line.lineno
- cause = return_line.cause.format(lineno=lineno)
- for final_exit in final_exits:
- self.add_arc(final_exit.lineno, lineno, cause)
- returns = try_block.return_from
- else:
- returns = self._combine_finally_starts(try_block.return_from, final_exits)
- self.process_return_exits(returns)
-
- if exits:
- # The finally clause's exits are only exits for the try block
- # as a whole if the try block had some exits to begin with.
- exits = final_exits
-
- return exits
-
- @contract(starts='ArcStarts', exits='ArcStarts', returns='ArcStarts')
- def _combine_finally_starts(self, starts, exits):
- """Helper for building the cause of `finally` branches.
-
- "finally" clauses might not execute their exits, and the causes could
- be due to a failure to execute any of the exits in the try block. So
- we use the causes from `starts` as the causes for `exits`.
- """
- causes = []
- for start in sorted(starts):
- if start.cause is not None:
- causes.append(start.cause.format(lineno=start.lineno))
- cause = " or ".join(causes)
- exits = {ArcStart(xit.lineno, cause) for xit in exits}
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__TryExcept(self, node):
- # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
- # TryExcept, it means there was no finally, so fake it, and treat as
- # a general Try node.
- node.finalbody = []
- return self._handle__Try(node)
-
- @contract(returns='ArcStarts')
- def _handle__TryFinally(self, node):
- # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
- # TryFinally, see if there's a TryExcept nested inside. If so, merge
- # them. Otherwise, fake fields to complete a Try node.
- node.handlers = []
- node.orelse = []
-
- first = node.body[0]
- if first.__class__.__name__ == "TryExcept" and node.lineno == first.lineno:
- assert len(node.body) == 1
- node.body = first.body
- node.handlers = first.handlers
- node.orelse = first.orelse
-
- return self._handle__Try(node)
-
- @contract(returns='ArcStarts')
- def _handle__While(self, node):
- start = to_top = self.line_for_node(node.test)
- constant_test = self.is_constant_expr(node.test)
- top_is_body0 = False
- if constant_test and (env.PY3 or constant_test == "Num"):
- top_is_body0 = True
- if env.PYBEHAVIOR.keep_constant_test:
- top_is_body0 = False
- if top_is_body0:
- to_top = self.line_for_node(node.body[0])
- self.block_stack.append(LoopBlock(start=to_top))
- from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
- exits = self.add_body_arcs(node.body, from_start=from_start)
- for xit in exits:
- self.add_arc(xit.lineno, to_top, xit.cause)
- exits = set()
- my_block = self.block_stack.pop()
- exits.update(my_block.break_exits)
- from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
- if node.orelse:
- else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
- exits |= else_exits
- else:
- # No `else` clause: you can exit from the start.
- if not constant_test:
- exits.add(from_start)
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__With(self, node):
- start = self.line_for_node(node)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
- return exits
-
- _handle__AsyncWith = _handle__With
-
- def _code_object__Module(self, node):
- start = self.line_for_node(node)
- if node.body:
- exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
- for xit in exits:
- self.add_arc(xit.lineno, -start, xit.cause, "didn't exit the module")
- else:
- # Empty module.
- self.add_arc(-start, start)
- self.add_arc(start, -start)
-
- def _process_function_def(self, start, node):
- self.funcdefs.add((start, node.body[-1].lineno, node.name))
-
- def _code_object__FunctionDef(self, node):
- start = self.line_for_node(node)
- self.block_stack.append(FunctionBlock(start=start, name=node.name))
- exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
- self.process_return_exits(exits)
- self._process_function_def(start, node)
- self.block_stack.pop()
-
- _code_object__AsyncFunctionDef = _code_object__FunctionDef
-
- def _code_object__ClassDef(self, node):
- start = self.line_for_node(node)
- self.add_arc(-start, start)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
- for xit in exits:
- self.add_arc(
- xit.lineno, -start, xit.cause,
- "didn't exit the body of class {!r}".format(node.name),
- )
-
- def _make_oneline_code_method(noun): # pylint: disable=no-self-argument
- """A function to make methods for online callable _code_object__ methods."""
- def _code_object__oneline_callable(self, node):
- start = self.line_for_node(node)
- self.add_arc(-start, start, None, "didn't run the {} on line {}".format(noun, start))
- self.add_arc(
- start, -start, None,
- "didn't finish the {} on line {}".format(noun, start),
- )
- return _code_object__oneline_callable
-
- _code_object__Lambda = _make_oneline_code_method("lambda")
- _code_object__GeneratorExp = _make_oneline_code_method("generator expression")
- _code_object__DictComp = _make_oneline_code_method("dictionary comprehension")
- _code_object__SetComp = _make_oneline_code_method("set comprehension")
- if env.PY3:
- _code_object__ListComp = _make_oneline_code_method("list comprehension")
-
-
-if AST_DUMP: # pragma: debugging
- # Code only used when dumping the AST for debugging.
-
- SKIP_DUMP_FIELDS = ["ctx"]
-
- def _is_simple_value(value):
- """Is `value` simple enough to be displayed on a single line?"""
- return (
- value in [None, [], (), {}, set()] or
- isinstance(value, (string_class, int, float))
- )
-
- def ast_dump(node, depth=0):
- """Dump the AST for `node`.
-
- This recursively walks the AST, printing a readable version.
-
- """
- indent = " " * depth
- if not isinstance(node, ast.AST):
- print("{}<{} {!r}>".format(indent, node.__class__.__name__, node))
- return
-
- lineno = getattr(node, "lineno", None)
- if lineno is not None:
- linemark = " @ {}".format(node.lineno)
- else:
- linemark = ""
- head = "{}<{}{}".format(indent, node.__class__.__name__, linemark)
-
- named_fields = [
- (name, value)
- for name, value in ast.iter_fields(node)
- if name not in SKIP_DUMP_FIELDS
- ]
- if not named_fields:
- print("{}>".format(head))
- elif len(named_fields) == 1 and _is_simple_value(named_fields[0][1]):
- field_name, value = named_fields[0]
- print("{} {}: {!r}>".format(head, field_name, value))
- else:
- print(head)
- if 0:
- print("{}# mro: {}".format(
- indent, ", ".join(c.__name__ for c in node.__class__.__mro__[1:]),
- ))
- next_indent = indent + " "
- for field_name, value in named_fields:
- prefix = "{}{}:".format(next_indent, field_name)
- if _is_simple_value(value):
- print("{} {!r}".format(prefix, value))
- elif isinstance(value, list):
- print("{} [".format(prefix))
- for n in value:
- ast_dump(n, depth + 8)
- print("{}]".format(next_indent))
- else:
- print(prefix)
- ast_dump(value, depth + 8)
-
- print("{}>".format(indent))
diff --git a/contrib/python/coverage/py2/coverage/phystokens.py b/contrib/python/coverage/py2/coverage/phystokens.py
deleted file mode 100644
index 54378b3bc8..0000000000
--- a/contrib/python/coverage/py2/coverage/phystokens.py
+++ /dev/null
@@ -1,297 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Better tokenizing for coverage.py."""
-
-import codecs
-import keyword
-import re
-import sys
-import token
-import tokenize
-
-from coverage import env
-from coverage.backward import iternext, unicode_class
-from coverage.misc import contract
-
-
-def phys_tokens(toks):
- """Return all physical tokens, even line continuations.
-
- tokenize.generate_tokens() doesn't return a token for the backslash that
- continues lines. This wrapper provides those tokens so that we can
- re-create a faithful representation of the original source.
-
- Returns the same values as generate_tokens()
-
- """
- last_line = None
- last_lineno = -1
- last_ttext = None
- for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks:
- if last_lineno != elineno:
- if last_line and last_line.endswith("\\\n"):
- # We are at the beginning of a new line, and the last line
- # ended with a backslash. We probably have to inject a
- # backslash token into the stream. Unfortunately, there's more
- # to figure out. This code::
- #
- # usage = """\
- # HEY THERE
- # """
- #
- # triggers this condition, but the token text is::
- #
- # '"""\\\nHEY THERE\n"""'
- #
- # so we need to figure out if the backslash is already in the
- # string token or not.
- inject_backslash = True
- if last_ttext.endswith("\\"):
- inject_backslash = False
- elif ttype == token.STRING:
- if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\':
- # It's a multi-line string and the first line ends with
- # a backslash, so we don't need to inject another.
- inject_backslash = False
- if inject_backslash:
- # Figure out what column the backslash is in.
- ccol = len(last_line.split("\n")[-2]) - 1
- # Yield the token, with a fake token type.
- yield (
- 99999, "\\\n",
- (slineno, ccol), (slineno, ccol+2),
- last_line
- )
- last_line = ltext
- if ttype not in (tokenize.NEWLINE, tokenize.NL):
- last_ttext = ttext
- yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext
- last_lineno = elineno
-
-
-@contract(source='unicode')
-def source_token_lines(source):
- """Generate a series of lines, one for each line in `source`.
-
- Each line is a list of pairs, each pair is a token::
-
- [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
-
- Each pair has a token class, and the token text.
-
- If you concatenate all the token texts, and then join them with newlines,
- you should have your original `source` back, with two differences:
- trailing whitespace is not preserved, and a final line with no newline
- is indistinguishable from a final line with a newline.
-
- """
-
- ws_tokens = {token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL}
- line = []
- col = 0
-
- source = source.expandtabs(8).replace('\r\n', '\n')
- tokgen = generate_tokens(source)
-
- for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen):
- mark_start = True
- for part in re.split('(\n)', ttext):
- if part == '\n':
- yield line
- line = []
- col = 0
- mark_end = False
- elif part == '':
- mark_end = False
- elif ttype in ws_tokens:
- mark_end = False
- else:
- if mark_start and scol > col:
- line.append(("ws", u" " * (scol - col)))
- mark_start = False
- tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3]
- if ttype == token.NAME and keyword.iskeyword(ttext):
- tok_class = "key"
- line.append((tok_class, part))
- mark_end = True
- scol = 0
- if mark_end:
- col = ecol
-
- if line:
- yield line
-
-
-class CachedTokenizer(object):
- """A one-element cache around tokenize.generate_tokens.
-
- When reporting, coverage.py tokenizes files twice, once to find the
- structure of the file, and once to syntax-color it. Tokenizing is
- expensive, and easily cached.
-
- This is a one-element cache so that our twice-in-a-row tokenizing doesn't
- actually tokenize twice.
-
- """
- def __init__(self):
- self.last_text = None
- self.last_tokens = None
-
- @contract(text='unicode')
- def generate_tokens(self, text):
- """A stand-in for `tokenize.generate_tokens`."""
- if text != self.last_text:
- self.last_text = text
- readline = iternext(text.splitlines(True))
- self.last_tokens = list(tokenize.generate_tokens(readline))
- return self.last_tokens
-
-# Create our generate_tokens cache as a callable replacement function.
-generate_tokens = CachedTokenizer().generate_tokens
-
-
-COOKIE_RE = re.compile(r"^[ \t]*#.*coding[:=][ \t]*([-\w.]+)", flags=re.MULTILINE)
-
-@contract(source='bytes')
-def _source_encoding_py2(source):
- """Determine the encoding for `source`, according to PEP 263.
-
- `source` is a byte string, the text of the program.
-
- Returns a string, the name of the encoding.
-
- """
- assert isinstance(source, bytes)
-
- # Do this so the detect_encode code we copied will work.
- readline = iternext(source.splitlines(True))
-
- # This is mostly code adapted from Py3.2's tokenize module.
-
- def _get_normal_name(orig_enc):
- """Imitates get_normal_name in tokenizer.c."""
- # Only care about the first 12 characters.
- enc = orig_enc[:12].lower().replace("_", "-")
- if re.match(r"^utf-8($|-)", enc):
- return "utf-8"
- if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc):
- return "iso-8859-1"
- return orig_enc
-
- # From detect_encode():
- # It detects the encoding from the presence of a UTF-8 BOM or an encoding
- # cookie as specified in PEP-0263. If both a BOM and a cookie are present,
- # but disagree, a SyntaxError will be raised. If the encoding cookie is an
- # invalid charset, raise a SyntaxError. Note that if a UTF-8 BOM is found,
- # 'utf-8-sig' is returned.
-
- # If no encoding is specified, then the default will be returned.
- default = 'ascii'
-
- bom_found = False
- encoding = None
-
- def read_or_stop():
- """Get the next source line, or ''."""
- try:
- return readline()
- except StopIteration:
- return ''
-
- def find_cookie(line):
- """Find an encoding cookie in `line`."""
- try:
- line_string = line.decode('ascii')
- except UnicodeDecodeError:
- return None
-
- matches = COOKIE_RE.findall(line_string)
- if not matches:
- return None
- encoding = _get_normal_name(matches[0])
- try:
- codec = codecs.lookup(encoding)
- except LookupError:
- # This behavior mimics the Python interpreter
- raise SyntaxError("unknown encoding: " + encoding)
-
- if bom_found:
- # codecs in 2.3 were raw tuples of functions, assume the best.
- codec_name = getattr(codec, 'name', encoding)
- if codec_name != 'utf-8':
- # This behavior mimics the Python interpreter
- raise SyntaxError('encoding problem: utf-8')
- encoding += '-sig'
- return encoding
-
- first = read_or_stop()
- if first.startswith(codecs.BOM_UTF8):
- bom_found = True
- first = first[3:]
- default = 'utf-8-sig'
- if not first:
- return default
-
- encoding = find_cookie(first)
- if encoding:
- return encoding
-
- second = read_or_stop()
- if not second:
- return default
-
- encoding = find_cookie(second)
- if encoding:
- return encoding
-
- return default
-
-
-@contract(source='bytes')
-def _source_encoding_py3(source):
- """Determine the encoding for `source`, according to PEP 263.
-
- `source` is a byte string: the text of the program.
-
- Returns a string, the name of the encoding.
-
- """
- readline = iternext(source.splitlines(True))
- return tokenize.detect_encoding(readline)[0]
-
-
-if env.PY3:
- source_encoding = _source_encoding_py3
-else:
- source_encoding = _source_encoding_py2
-
-
-@contract(source='unicode')
-def compile_unicode(source, filename, mode):
- """Just like the `compile` builtin, but works on any Unicode string.
-
- Python 2's compile() builtin has a stupid restriction: if the source string
- is Unicode, then it may not have a encoding declaration in it. Why not?
- Who knows! It also decodes to utf8, and then tries to interpret those utf8
- bytes according to the encoding declaration. Why? Who knows!
-
- This function neuters the coding declaration, and compiles it.
-
- """
- source = neuter_encoding_declaration(source)
- if env.PY2 and isinstance(filename, unicode_class):
- filename = filename.encode(sys.getfilesystemencoding(), "replace")
- code = compile(source, filename, mode)
- return code
-
-
-@contract(source='unicode', returns='unicode')
-def neuter_encoding_declaration(source):
- """Return `source`, with any encoding declaration neutered."""
- if COOKIE_RE.search(source):
- source_lines = source.splitlines(True)
- for lineno in range(min(2, len(source_lines))):
- source_lines[lineno] = COOKIE_RE.sub("# (deleted declaration)", source_lines[lineno])
- source = "".join(source_lines)
- return source
diff --git a/contrib/python/coverage/py2/coverage/plugin.py b/contrib/python/coverage/py2/coverage/plugin.py
deleted file mode 100644
index 6997b489bb..0000000000
--- a/contrib/python/coverage/py2/coverage/plugin.py
+++ /dev/null
@@ -1,533 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""
-.. versionadded:: 4.0
-
-Plug-in interfaces for coverage.py.
-
-Coverage.py supports a few different kinds of plug-ins that change its
-behavior:
-
-* File tracers implement tracing of non-Python file types.
-
-* Configurers add custom configuration, using Python code to change the
- configuration.
-
-* Dynamic context switchers decide when the dynamic context has changed, for
- example, to record what test function produced the coverage.
-
-To write a coverage.py plug-in, create a module with a subclass of
-:class:`~coverage.CoveragePlugin`. You will override methods in your class to
-participate in various aspects of coverage.py's processing.
-Different types of plug-ins have to override different methods.
-
-Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info`
-to provide debugging information about their operation.
-
-Your module must also contain a ``coverage_init`` function that registers an
-instance of your plug-in class::
-
- import coverage
-
- class MyPlugin(coverage.CoveragePlugin):
- ...
-
- def coverage_init(reg, options):
- reg.add_file_tracer(MyPlugin())
-
-You use the `reg` parameter passed to your ``coverage_init`` function to
-register your plug-in object. The registration method you call depends on
-what kind of plug-in it is.
-
-If your plug-in takes options, the `options` parameter is a dictionary of your
-plug-in's options from the coverage.py configuration file. Use them however
-you want to configure your object before registering it.
-
-Coverage.py will store its own information on your plug-in object, using
-attributes whose names start with ``_coverage_``. Don't be startled.
-
-.. warning::
- Plug-ins are imported by coverage.py before it begins measuring code.
- If you write a plugin in your own project, it might import your product
- code before coverage.py can start measuring. This can result in your
- own code being reported as missing.
-
- One solution is to put your plugins in your project tree, but not in
- your importable Python package.
-
-
-.. _file_tracer_plugins:
-
-File Tracers
-============
-
-File tracers implement measurement support for non-Python files. File tracers
-implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim
-files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report
-on those files.
-
-In your ``coverage_init`` function, use the ``add_file_tracer`` method to
-register your file tracer.
-
-
-.. _configurer_plugins:
-
-Configurers
-===========
-
-.. versionadded:: 4.5
-
-Configurers modify the configuration of coverage.py during start-up.
-Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to
-change the configuration.
-
-In your ``coverage_init`` function, use the ``add_configurer`` method to
-register your configurer.
-
-
-.. _dynamic_context_plugins:
-
-Dynamic Context Switchers
-=========================
-
-.. versionadded:: 5.0
-
-Dynamic context switcher plugins implement the
-:meth:`~coverage.CoveragePlugin.dynamic_context` method to dynamically compute
-the context label for each measured frame.
-
-Computed context labels are useful when you want to group measured data without
-modifying the source code.
-
-For example, you could write a plugin that checks `frame.f_code` to inspect
-the currently executed method, and set the context label to a fully qualified
-method name if it's an instance method of `unittest.TestCase` and the method
-name starts with 'test'. Such a plugin would provide basic coverage grouping
-by test and could be used with test runners that have no built-in coveragepy
-support.
-
-In your ``coverage_init`` function, use the ``add_dynamic_context`` method to
-register your dynamic context switcher.
-
-"""
-
-from coverage import files
-from coverage.misc import contract, _needs_to_implement
-
-
-class CoveragePlugin(object):
- """Base class for coverage.py plug-ins."""
-
- def file_tracer(self, filename): # pylint: disable=unused-argument
- """Get a :class:`FileTracer` object for a file.
-
- Plug-in type: file tracer.
-
- Every Python source file is offered to your plug-in to give it a chance
- to take responsibility for tracing the file. If your plug-in can
- handle the file, it should return a :class:`FileTracer` object.
- Otherwise return None.
-
- There is no way to register your plug-in for particular files.
- Instead, this method is invoked for all files as they are executed,
- and the plug-in decides whether it can trace the file or not.
- Be prepared for `filename` to refer to all kinds of files that have
- nothing to do with your plug-in.
-
- The file name will be a Python file being executed. There are two
- broad categories of behavior for a plug-in, depending on the kind of
- files your plug-in supports:
-
- * Static file names: each of your original source files has been
- converted into a distinct Python file. Your plug-in is invoked with
- the Python file name, and it maps it back to its original source
- file.
-
- * Dynamic file names: all of your source files are executed by the same
- Python file. In this case, your plug-in implements
- :meth:`FileTracer.dynamic_source_filename` to provide the actual
- source file for each execution frame.
-
- `filename` is a string, the path to the file being considered. This is
- the absolute real path to the file. If you are comparing to other
- paths, be sure to take this into account.
-
- Returns a :class:`FileTracer` object to use to trace `filename`, or
- None if this plug-in cannot trace this file.
-
- """
- return None
-
- def file_reporter(self, filename): # pylint: disable=unused-argument
- """Get the :class:`FileReporter` class to use for a file.
-
- Plug-in type: file tracer.
-
- This will only be invoked if `filename` returns non-None from
- :meth:`file_tracer`. It's an error to return None from this method.
-
- Returns a :class:`FileReporter` object to use to report on `filename`,
- or the string `"python"` to have coverage.py treat the file as Python.
-
- """
- _needs_to_implement(self, "file_reporter")
-
- def dynamic_context(self, frame): # pylint: disable=unused-argument
- """Get the dynamically computed context label for `frame`.
-
- Plug-in type: dynamic context.
-
- This method is invoked for each frame when outside of a dynamic
- context, to see if a new dynamic context should be started. If it
- returns a string, a new context label is set for this and deeper
- frames. The dynamic context ends when this frame returns.
-
- Returns a string to start a new dynamic context, or None if no new
- context should be started.
-
- """
- return None
-
- def find_executable_files(self, src_dir): # pylint: disable=unused-argument
- """Yield all of the executable files in `src_dir`, recursively.
-
- Plug-in type: file tracer.
-
- Executability is a plug-in-specific property, but generally means files
- which would have been considered for coverage analysis, had they been
- included automatically.
-
- Returns or yields a sequence of strings, the paths to files that could
- have been executed, including files that had been executed.
-
- """
- return []
-
- def configure(self, config):
- """Modify the configuration of coverage.py.
-
- Plug-in type: configurer.
-
- This method is called during coverage.py start-up, to give your plug-in
- a chance to change the configuration. The `config` parameter is an
- object with :meth:`~coverage.Coverage.get_option` and
- :meth:`~coverage.Coverage.set_option` methods. Do not call any other
- methods on the `config` object.
-
- """
- pass
-
- def sys_info(self):
- """Get a list of information useful for debugging.
-
- Plug-in type: any.
-
- This method will be invoked for ``--debug=sys``. Your
- plug-in can return any information it wants to be displayed.
-
- Returns a list of pairs: `[(name, value), ...]`.
-
- """
- return []
-
-
-class FileTracer(object):
- """Support needed for files during the execution phase.
-
- File tracer plug-ins implement subclasses of FileTracer to return from
- their :meth:`~CoveragePlugin.file_tracer` method.
-
- You may construct this object from :meth:`CoveragePlugin.file_tracer` any
- way you like. A natural choice would be to pass the file name given to
- `file_tracer`.
-
- `FileTracer` objects should only be created in the
- :meth:`CoveragePlugin.file_tracer` method.
-
- See :ref:`howitworks` for details of the different coverage.py phases.
-
- """
-
- def source_filename(self):
- """The source file name for this file.
-
- This may be any file name you like. A key responsibility of a plug-in
- is to own the mapping from Python execution back to whatever source
- file name was originally the source of the code.
-
- See :meth:`CoveragePlugin.file_tracer` for details about static and
- dynamic file names.
-
- Returns the file name to credit with this execution.
-
- """
- _needs_to_implement(self, "source_filename")
-
- def has_dynamic_source_filename(self):
- """Does this FileTracer have dynamic source file names?
-
- FileTracers can provide dynamically determined file names by
- implementing :meth:`dynamic_source_filename`. Invoking that function
- is expensive. To determine whether to invoke it, coverage.py uses the
- result of this function to know if it needs to bother invoking
- :meth:`dynamic_source_filename`.
-
- See :meth:`CoveragePlugin.file_tracer` for details about static and
- dynamic file names.
-
- Returns True if :meth:`dynamic_source_filename` should be called to get
- dynamic source file names.
-
- """
- return False
-
- def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument
- """Get a dynamically computed source file name.
-
- Some plug-ins need to compute the source file name dynamically for each
- frame.
-
- This function will not be invoked if
- :meth:`has_dynamic_source_filename` returns False.
-
- Returns the source file name for this frame, or None if this frame
- shouldn't be measured.
-
- """
- return None
-
- def line_number_range(self, frame):
- """Get the range of source line numbers for a given a call frame.
-
- The call frame is examined, and the source line number in the original
- file is returned. The return value is a pair of numbers, the starting
- line number and the ending line number, both inclusive. For example,
- returning (5, 7) means that lines 5, 6, and 7 should be considered
- executed.
-
- This function might decide that the frame doesn't indicate any lines
- from the source file were executed. Return (-1, -1) in this case to
- tell coverage.py that no lines should be recorded for this frame.
-
- """
- lineno = frame.f_lineno
- return lineno, lineno
-
-
-class FileReporter(object):
- """Support needed for files during the analysis and reporting phases.
-
- File tracer plug-ins implement a subclass of `FileReporter`, and return
- instances from their :meth:`CoveragePlugin.file_reporter` method.
-
- There are many methods here, but only :meth:`lines` is required, to provide
- the set of executable lines in the file.
-
- See :ref:`howitworks` for details of the different coverage.py phases.
-
- """
-
- def __init__(self, filename):
- """Simple initialization of a `FileReporter`.
-
- The `filename` argument is the path to the file being reported. This
- will be available as the `.filename` attribute on the object. Other
- method implementations on this base class rely on this attribute.
-
- """
- self.filename = filename
-
- def __repr__(self):
- return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
-
- def relative_filename(self):
- """Get the relative file name for this file.
-
- This file path will be displayed in reports. The default
- implementation will supply the actual project-relative file path. You
- only need to supply this method if you have an unusual syntax for file
- paths.
-
- """
- return files.relative_filename(self.filename)
-
- @contract(returns='unicode')
- def source(self):
- """Get the source for the file.
-
- Returns a Unicode string.
-
- The base implementation simply reads the `self.filename` file and
- decodes it as UTF8. Override this method if your file isn't readable
- as a text file, or if you need other encoding support.
-
- """
- with open(self.filename, "rb") as f:
- return f.read().decode("utf8")
-
- def lines(self):
- """Get the executable lines in this file.
-
- Your plug-in must determine which lines in the file were possibly
- executable. This method returns a set of those line numbers.
-
- Returns a set of line numbers.
-
- """
- _needs_to_implement(self, "lines")
-
- def excluded_lines(self):
- """Get the excluded executable lines in this file.
-
- Your plug-in can use any method it likes to allow the user to exclude
- executable lines from consideration.
-
- Returns a set of line numbers.
-
- The base implementation returns the empty set.
-
- """
- return set()
-
- def translate_lines(self, lines):
- """Translate recorded lines into reported lines.
-
- Some file formats will want to report lines slightly differently than
- they are recorded. For example, Python records the last line of a
- multi-line statement, but reports are nicer if they mention the first
- line.
-
- Your plug-in can optionally define this method to perform these kinds
- of adjustment.
-
- `lines` is a sequence of integers, the recorded line numbers.
-
- Returns a set of integers, the adjusted line numbers.
-
- The base implementation returns the numbers unchanged.
-
- """
- return set(lines)
-
- def arcs(self):
- """Get the executable arcs in this file.
-
- To support branch coverage, your plug-in needs to be able to indicate
- possible execution paths, as a set of line number pairs. Each pair is
- a `(prev, next)` pair indicating that execution can transition from the
- `prev` line number to the `next` line number.
-
- Returns a set of pairs of line numbers. The default implementation
- returns an empty set.
-
- """
- return set()
-
- def no_branch_lines(self):
- """Get the lines excused from branch coverage in this file.
-
- Your plug-in can use any method it likes to allow the user to exclude
- lines from consideration of branch coverage.
-
- Returns a set of line numbers.
-
- The base implementation returns the empty set.
-
- """
- return set()
-
- def translate_arcs(self, arcs):
- """Translate recorded arcs into reported arcs.
-
- Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of
- line number pairs.
-
- Returns a set of line number pairs.
-
- The default implementation returns `arcs` unchanged.
-
- """
- return arcs
-
- def exit_counts(self):
- """Get a count of exits from that each line.
-
- To determine which lines are branches, coverage.py looks for lines that
- have more than one exit. This function creates a dict mapping each
- executable line number to a count of how many exits it has.
-
- To be honest, this feels wrong, and should be refactored. Let me know
- if you attempt to implement this method in your plug-in...
-
- """
- return {}
-
- def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument
- """Provide an English sentence describing a missing arc.
-
- The `start` and `end` arguments are the line numbers of the missing
- arc. Negative numbers indicate entering or exiting code objects.
-
- The `executed_arcs` argument is a set of line number pairs, the arcs
- that were executed in this file.
-
- By default, this simply returns the string "Line {start} didn't jump
- to {end}".
-
- """
- return "Line {start} didn't jump to line {end}".format(start=start, end=end)
-
- def source_token_lines(self):
- """Generate a series of tokenized lines, one for each line in `source`.
-
- These tokens are used for syntax-colored reports.
-
- Each line is a list of pairs, each pair is a token::
-
- [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
-
- Each pair has a token class, and the token text. The token classes
- are:
-
- * ``'com'``: a comment
- * ``'key'``: a keyword
- * ``'nam'``: a name, or identifier
- * ``'num'``: a number
- * ``'op'``: an operator
- * ``'str'``: a string literal
- * ``'ws'``: some white space
- * ``'txt'``: some other kind of text
-
- If you concatenate all the token texts, and then join them with
- newlines, you should have your original source back.
-
- The default implementation simply returns each line tagged as
- ``'txt'``.
-
- """
- for line in self.source().splitlines():
- yield [('txt', line)]
-
- # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
- # of them defined.
-
- def __eq__(self, other):
- return isinstance(other, FileReporter) and self.filename == other.filename
-
- def __ne__(self, other):
- return not (self == other)
-
- def __lt__(self, other):
- return self.filename < other.filename
-
- def __le__(self, other):
- return self.filename <= other.filename
-
- def __gt__(self, other):
- return self.filename > other.filename
-
- def __ge__(self, other):
- return self.filename >= other.filename
-
- __hash__ = None # This object doesn't need to be hashed.
diff --git a/contrib/python/coverage/py2/coverage/plugin_support.py b/contrib/python/coverage/py2/coverage/plugin_support.py
deleted file mode 100644
index 89c1c7658f..0000000000
--- a/contrib/python/coverage/py2/coverage/plugin_support.py
+++ /dev/null
@@ -1,281 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Support for plugins."""
-
-import os
-import os.path
-import sys
-
-from coverage.misc import CoverageException, isolate_module
-from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
-
-os = isolate_module(os)
-
-
-class Plugins(object):
- """The currently loaded collection of coverage.py plugins."""
-
- def __init__(self):
- self.order = []
- self.names = {}
- self.file_tracers = []
- self.configurers = []
- self.context_switchers = []
-
- self.current_module = None
- self.debug = None
-
- @classmethod
- def load_plugins(cls, modules, config, debug=None):
- """Load plugins from `modules`.
-
- Returns a Plugins object with the loaded and configured plugins.
-
- """
- plugins = cls()
- plugins.debug = debug
-
- for module in modules:
- plugins.current_module = module
- __import__(module)
- mod = sys.modules[module]
-
- coverage_init = getattr(mod, "coverage_init", None)
- if not coverage_init:
- raise CoverageException(
- "Plugin module %r didn't define a coverage_init function" % module
- )
-
- options = config.get_plugin_options(module)
- coverage_init(plugins, options)
-
- plugins.current_module = None
- return plugins
-
- def add_file_tracer(self, plugin):
- """Add a file tracer plugin.
-
- `plugin` is an instance of a third-party plugin class. It must
- implement the :meth:`CoveragePlugin.file_tracer` method.
-
- """
- self._add_plugin(plugin, self.file_tracers)
-
- def add_configurer(self, plugin):
- """Add a configuring plugin.
-
- `plugin` is an instance of a third-party plugin class. It must
- implement the :meth:`CoveragePlugin.configure` method.
-
- """
- self._add_plugin(plugin, self.configurers)
-
- def add_dynamic_context(self, plugin):
- """Add a dynamic context plugin.
-
- `plugin` is an instance of a third-party plugin class. It must
- implement the :meth:`CoveragePlugin.dynamic_context` method.
-
- """
- self._add_plugin(plugin, self.context_switchers)
-
- def add_noop(self, plugin):
- """Add a plugin that does nothing.
-
- This is only useful for testing the plugin support.
-
- """
- self._add_plugin(plugin, None)
-
- def _add_plugin(self, plugin, specialized):
- """Add a plugin object.
-
- `plugin` is a :class:`CoveragePlugin` instance to add. `specialized`
- is a list to append the plugin to.
-
- """
- plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__)
- if self.debug and self.debug.should('plugin'):
- self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin))
- labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug)
- plugin = DebugPluginWrapper(plugin, labelled)
-
- # pylint: disable=attribute-defined-outside-init
- plugin._coverage_plugin_name = plugin_name
- plugin._coverage_enabled = True
- self.order.append(plugin)
- self.names[plugin_name] = plugin
- if specialized is not None:
- specialized.append(plugin)
-
- def __nonzero__(self):
- return bool(self.order)
-
- __bool__ = __nonzero__
-
- def __iter__(self):
- return iter(self.order)
-
- def get(self, plugin_name):
- """Return a plugin by name."""
- return self.names[plugin_name]
-
-
-class LabelledDebug(object):
- """A Debug writer, but with labels for prepending to the messages."""
-
- def __init__(self, label, debug, prev_labels=()):
- self.labels = list(prev_labels) + [label]
- self.debug = debug
-
- def add_label(self, label):
- """Add a label to the writer, and return a new `LabelledDebug`."""
- return LabelledDebug(label, self.debug, self.labels)
-
- def message_prefix(self):
- """The prefix to use on messages, combining the labels."""
- prefixes = self.labels + ['']
- return ":\n".join(" "*i+label for i, label in enumerate(prefixes))
-
- def write(self, message):
- """Write `message`, but with the labels prepended."""
- self.debug.write("%s%s" % (self.message_prefix(), message))
-
-
-class DebugPluginWrapper(CoveragePlugin):
- """Wrap a plugin, and use debug to report on what it's doing."""
-
- def __init__(self, plugin, debug):
- super(DebugPluginWrapper, self).__init__()
- self.plugin = plugin
- self.debug = debug
-
- def file_tracer(self, filename):
- tracer = self.plugin.file_tracer(filename)
- self.debug.write("file_tracer(%r) --> %r" % (filename, tracer))
- if tracer:
- debug = self.debug.add_label("file %r" % (filename,))
- tracer = DebugFileTracerWrapper(tracer, debug)
- return tracer
-
- def file_reporter(self, filename):
- reporter = self.plugin.file_reporter(filename)
- self.debug.write("file_reporter(%r) --> %r" % (filename, reporter))
- if reporter:
- debug = self.debug.add_label("file %r" % (filename,))
- reporter = DebugFileReporterWrapper(filename, reporter, debug)
- return reporter
-
- def dynamic_context(self, frame):
- context = self.plugin.dynamic_context(frame)
- self.debug.write("dynamic_context(%r) --> %r" % (frame, context))
- return context
-
- def find_executable_files(self, src_dir):
- executable_files = self.plugin.find_executable_files(src_dir)
- self.debug.write("find_executable_files(%r) --> %r" % (src_dir, executable_files))
- return executable_files
-
- def configure(self, config):
- self.debug.write("configure(%r)" % (config,))
- self.plugin.configure(config)
-
- def sys_info(self):
- return self.plugin.sys_info()
-
-
-class DebugFileTracerWrapper(FileTracer):
- """A debugging `FileTracer`."""
-
- def __init__(self, tracer, debug):
- self.tracer = tracer
- self.debug = debug
-
- def _show_frame(self, frame):
- """A short string identifying a frame, for debug messages."""
- return "%s@%d" % (
- os.path.basename(frame.f_code.co_filename),
- frame.f_lineno,
- )
-
- def source_filename(self):
- sfilename = self.tracer.source_filename()
- self.debug.write("source_filename() --> %r" % (sfilename,))
- return sfilename
-
- def has_dynamic_source_filename(self):
- has = self.tracer.has_dynamic_source_filename()
- self.debug.write("has_dynamic_source_filename() --> %r" % (has,))
- return has
-
- def dynamic_source_filename(self, filename, frame):
- dyn = self.tracer.dynamic_source_filename(filename, frame)
- self.debug.write("dynamic_source_filename(%r, %s) --> %r" % (
- filename, self._show_frame(frame), dyn,
- ))
- return dyn
-
- def line_number_range(self, frame):
- pair = self.tracer.line_number_range(frame)
- self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair))
- return pair
-
-
-class DebugFileReporterWrapper(FileReporter):
- """A debugging `FileReporter`."""
-
- def __init__(self, filename, reporter, debug):
- super(DebugFileReporterWrapper, self).__init__(filename)
- self.reporter = reporter
- self.debug = debug
-
- def relative_filename(self):
- ret = self.reporter.relative_filename()
- self.debug.write("relative_filename() --> %r" % (ret,))
- return ret
-
- def lines(self):
- ret = self.reporter.lines()
- self.debug.write("lines() --> %r" % (ret,))
- return ret
-
- def excluded_lines(self):
- ret = self.reporter.excluded_lines()
- self.debug.write("excluded_lines() --> %r" % (ret,))
- return ret
-
- def translate_lines(self, lines):
- ret = self.reporter.translate_lines(lines)
- self.debug.write("translate_lines(%r) --> %r" % (lines, ret))
- return ret
-
- def translate_arcs(self, arcs):
- ret = self.reporter.translate_arcs(arcs)
- self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret))
- return ret
-
- def no_branch_lines(self):
- ret = self.reporter.no_branch_lines()
- self.debug.write("no_branch_lines() --> %r" % (ret,))
- return ret
-
- def exit_counts(self):
- ret = self.reporter.exit_counts()
- self.debug.write("exit_counts() --> %r" % (ret,))
- return ret
-
- def arcs(self):
- ret = self.reporter.arcs()
- self.debug.write("arcs() --> %r" % (ret,))
- return ret
-
- def source(self):
- ret = self.reporter.source()
- self.debug.write("source() --> %d chars" % (len(ret),))
- return ret
-
- def source_token_lines(self):
- ret = list(self.reporter.source_token_lines())
- self.debug.write("source_token_lines() --> %d tokens" % (len(ret),))
- return ret
diff --git a/contrib/python/coverage/py2/coverage/python.py b/contrib/python/coverage/py2/coverage/python.py
deleted file mode 100644
index 6ff19c34fe..0000000000
--- a/contrib/python/coverage/py2/coverage/python.py
+++ /dev/null
@@ -1,261 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Python source expertise for coverage.py"""
-
-import sys
-import os.path
-import types
-import zipimport
-
-from coverage import env, files
-from coverage.misc import contract, expensive, isolate_module, join_regex
-from coverage.misc import CoverageException, NoSource
-from coverage.parser import PythonParser
-from coverage.phystokens import source_token_lines, source_encoding
-from coverage.plugin import FileReporter
-
-os = isolate_module(os)
-
-
-@contract(returns='bytes')
-def read_python_source(filename):
- """Read the Python source text from `filename`.
-
- Returns bytes.
-
- """
- with open(filename, "rb") as f:
- source = f.read()
-
- if env.IRONPYTHON:
- # IronPython reads Unicode strings even for "rb" files.
- source = bytes(source)
-
- return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
-
-
-@contract(returns='unicode')
-def get_python_source(filename, force_fs=False):
- """Return the source code, as unicode."""
- if getattr(sys, 'is_standalone_binary', False) and not force_fs:
- import __res
-
- modname = __res.importer.file_source(filename)
- if modname:
- source = __res.find(modname)
- source = source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
- return source.decode('utf-8')
- else:
- # it's fake generated package
- return u''
- base, ext = os.path.splitext(filename)
- if ext == ".py" and env.WINDOWS:
- exts = [".py", ".pyw"]
- else:
- exts = [ext]
-
- for ext in exts:
- try_filename = base + ext
- if os.path.exists(try_filename):
- # A regular text file: open it.
- source = read_python_source(try_filename)
- break
-
- # Maybe it's in a zip file?
- source = get_zip_bytes(try_filename)
- if source is not None:
- break
- else:
- # Couldn't find source.
- exc_msg = "No source for code: '%s'.\n" % (filename,)
- exc_msg += "Aborting report output, consider using -i."
- raise NoSource(exc_msg)
-
- # Replace \f because of http://bugs.python.org/issue19035
- source = source.replace(b'\f', b' ')
- source = source.decode(source_encoding(source), "replace")
-
- # Python code should always end with a line with a newline.
- if source and source[-1] != '\n':
- source += '\n'
-
- return source
-
-
-@contract(returns='bytes|None')
-def get_zip_bytes(filename):
- """Get data from `filename` if it is a zip file path.
-
- Returns the bytestring data read from the zip file, or None if no zip file
- could be found or `filename` isn't in it. The data returned will be
- an empty string if the file is empty.
-
- """
- markers = ['.zip'+os.sep, '.egg'+os.sep, '.pex'+os.sep]
- for marker in markers:
- if marker in filename:
- parts = filename.split(marker)
- try:
- zi = zipimport.zipimporter(parts[0]+marker[:-1])
- except zipimport.ZipImportError:
- continue
- try:
- data = zi.get_data(parts[1])
- except IOError:
- continue
- return data
- return None
-
-
-def source_for_file(filename):
- """Return the source filename for `filename`.
-
- Given a file name being traced, return the best guess as to the source
- file to attribute it to.
-
- """
- if filename.endswith(".py"):
- # .py files are themselves source files.
- return filename
-
- elif filename.endswith((".pyc", ".pyo")):
- # Bytecode files probably have source files near them.
- py_filename = filename[:-1]
- if os.path.exists(py_filename):
- # Found a .py file, use that.
- return py_filename
- if env.WINDOWS:
- # On Windows, it could be a .pyw file.
- pyw_filename = py_filename + "w"
- if os.path.exists(pyw_filename):
- return pyw_filename
- # Didn't find source, but it's probably the .py file we want.
- return py_filename
-
- elif filename.endswith("$py.class"):
- # Jython is easy to guess.
- return filename[:-9] + ".py"
-
- # No idea, just use the file name as-is.
- return filename
-
-
-def source_for_morf(morf):
- """Get the source filename for the module-or-file `morf`."""
- if hasattr(morf, '__file__') and morf.__file__:
- filename = morf.__file__
- elif isinstance(morf, types.ModuleType):
- # A module should have had .__file__, otherwise we can't use it.
- # This could be a PEP-420 namespace package.
- raise CoverageException("Module {} has no file".format(morf))
- else:
- filename = morf
-
- filename = source_for_file(files.unicode_filename(filename))
- return filename
-
-
-class PythonFileReporter(FileReporter):
- """Report support for a Python file."""
-
- def __init__(self, morf, coverage=None):
- self.coverage = coverage
-
- filename = source_for_morf(morf)
-
- super(PythonFileReporter, self).__init__(files.canonical_filename(filename))
-
- if hasattr(morf, '__name__'):
- name = morf.__name__.replace(".", os.sep)
- if os.path.basename(filename).startswith('__init__.'):
- name += os.sep + "__init__"
- name += ".py"
- name = files.unicode_filename(name)
- else:
- name = files.relative_filename(filename)
- self.relname = name
-
- self._source = None
- self._parser = None
- self._excluded = None
-
- def __repr__(self):
- return "<PythonFileReporter {!r}>".format(self.filename)
-
- @contract(returns='unicode')
- def relative_filename(self):
- return self.relname
-
- @property
- def parser(self):
- """Lazily create a :class:`PythonParser`."""
- if self._parser is None:
- self._parser = PythonParser(
- filename=self.filename,
- exclude=self.coverage._exclude_regex('exclude'),
- )
- self._parser.parse_source()
- return self._parser
-
- def lines(self):
- """Return the line numbers of statements in the file."""
- return self.parser.statements
-
- def excluded_lines(self):
- """Return the line numbers of statements in the file."""
- return self.parser.excluded
-
- def translate_lines(self, lines):
- return self.parser.translate_lines(lines)
-
- def translate_arcs(self, arcs):
- return self.parser.translate_arcs(arcs)
-
- @expensive
- def no_branch_lines(self):
- no_branch = self.parser.lines_matching(
- join_regex(self.coverage.config.partial_list),
- join_regex(self.coverage.config.partial_always_list)
- )
- return no_branch
-
- @expensive
- def arcs(self):
- return self.parser.arcs()
-
- @expensive
- def exit_counts(self):
- return self.parser.exit_counts()
-
- def missing_arc_description(self, start, end, executed_arcs=None):
- return self.parser.missing_arc_description(start, end, executed_arcs)
-
- @contract(returns='unicode')
- def source(self):
- if self._source is None:
- self._source = get_python_source(self.filename)
- return self._source
-
- def should_be_python(self):
- """Does it seem like this file should contain Python?
-
- This is used to decide if a file reported as part of the execution of
- a program was really likely to have contained Python in the first
- place.
-
- """
- # Get the file extension.
- _, ext = os.path.splitext(self.filename)
-
- # Anything named *.py* should be Python.
- if ext.startswith('.py'):
- return True
- # A file with no extension should be Python.
- if not ext:
- return True
- # Everything else is probably not Python.
- return False
-
- def source_token_lines(self):
- return source_token_lines(self.source())
diff --git a/contrib/python/coverage/py2/coverage/pytracer.py b/contrib/python/coverage/py2/coverage/pytracer.py
deleted file mode 100644
index 7ab4d3ef92..0000000000
--- a/contrib/python/coverage/py2/coverage/pytracer.py
+++ /dev/null
@@ -1,274 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Raw data collector for coverage.py."""
-
-import atexit
-import dis
-import sys
-
-from coverage import env
-
-# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
-YIELD_VALUE = dis.opmap['YIELD_VALUE']
-if env.PY2:
- YIELD_VALUE = chr(YIELD_VALUE)
-
-# When running meta-coverage, this file can try to trace itself, which confuses
-# everything. Don't trace ourselves.
-
-THIS_FILE = __file__.rstrip("co")
-
-
-class PyTracer(object):
- """Python implementation of the raw data tracer."""
-
- # Because of poor implementations of trace-function-manipulating tools,
- # the Python trace function must be kept very simple. In particular, there
- # must be only one function ever set as the trace function, both through
- # sys.settrace, and as the return value from the trace function. Put
- # another way, the trace function must always return itself. It cannot
- # swap in other functions, or return None to avoid tracing a particular
- # frame.
- #
- # The trace manipulator that introduced this restriction is DecoratorTools,
- # which sets a trace function, and then later restores the pre-existing one
- # by calling sys.settrace with a function it found in the current frame.
- #
- # Systems that use DecoratorTools (or similar trace manipulations) must use
- # PyTracer to get accurate results. The command-line --timid argument is
- # used to force the use of this tracer.
-
- def __init__(self):
- # Attributes set from the collector:
- self.data = None
- self.trace_arcs = False
- self.should_trace = None
- self.should_trace_cache = None
- self.should_start_context = None
- self.warn = None
- # The threading module to use, if any.
- self.threading = None
-
- self.cur_file_dict = None
- self.last_line = 0 # int, but uninitialized.
- self.cur_file_name = None
- self.context = None
- self.started_context = False
-
- self.data_stack = []
- self.last_exc_back = None
- self.last_exc_firstlineno = 0
- self.thread = None
- self.stopped = False
- self._activity = False
-
- self.in_atexit = False
- # On exit, self.in_atexit = True
- atexit.register(setattr, self, 'in_atexit', True)
-
- def __repr__(self):
- return "<PyTracer at {}: {} lines in {} files>".format(
- id(self),
- sum(len(v) for v in self.data.values()),
- len(self.data),
- )
-
- def log(self, marker, *args):
- """For hard-core logging of what this tracer is doing."""
- with open("/tmp/debug_trace.txt", "a") as f:
- f.write("{} {}[{}]".format(
- marker,
- id(self),
- len(self.data_stack),
- ))
- if 0:
- f.write(".{:x}.{:x}".format(
- self.thread.ident,
- self.threading.currentThread().ident,
- ))
- f.write(" {}".format(" ".join(map(str, args))))
- if 0:
- f.write(" | ")
- stack = " / ".join(
- (fname or "???").rpartition("/")[-1]
- for _, fname, _, _ in self.data_stack
- )
- f.write(stack)
- f.write("\n")
-
- def _trace(self, frame, event, arg_unused):
- """The trace function passed to sys.settrace."""
-
- if THIS_FILE in frame.f_code.co_filename:
- return None
-
- #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event)
-
- if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable
- # The PyTrace.stop() method has been called, possibly by another
- # thread, let's deactivate ourselves now.
- if 0:
- self.log("---\nX", frame.f_code.co_filename, frame.f_lineno)
- f = frame
- while f:
- self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
- f = f.f_back
- sys.settrace(None)
- self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
- self.data_stack.pop()
- )
- return None
-
- if self.last_exc_back:
- if frame == self.last_exc_back:
- # Someone forgot a return event.
- if self.trace_arcs and self.cur_file_dict:
- pair = (self.last_line, -self.last_exc_firstlineno)
- self.cur_file_dict[pair] = None
- self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
- self.data_stack.pop()
- )
- self.last_exc_back = None
-
- # if event != 'call' and frame.f_code.co_filename != self.cur_file_name:
- # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno)
-
- if event == 'call':
- # Should we start a new context?
- if self.should_start_context and self.context is None:
- context_maybe = self.should_start_context(frame)
- if context_maybe is not None:
- self.context = context_maybe
- self.started_context = True
- self.switch_context(self.context)
- else:
- self.started_context = False
- else:
- self.started_context = False
-
- # Entering a new frame. Decide if we should trace
- # in this file.
- self._activity = True
- self.data_stack.append(
- (
- self.cur_file_dict,
- self.cur_file_name,
- self.last_line,
- self.started_context,
- )
- )
- filename = frame.f_code.co_filename
- self.cur_file_name = filename
- disp = self.should_trace_cache.get(filename)
- if disp is None:
- disp = self.should_trace(filename, frame)
- self.should_trace_cache[filename] = disp
-
- self.cur_file_dict = None
- if disp.trace:
- tracename = disp.source_filename
- if tracename not in self.data:
- self.data[tracename] = {}
- self.cur_file_dict = self.data[tracename]
- # The call event is really a "start frame" event, and happens for
- # function calls and re-entering generators. The f_lasti field is
- # -1 for calls, and a real offset for generators. Use <0 as the
- # line number for calls, and the real line number for generators.
- if getattr(frame, 'f_lasti', -1) < 0:
- self.last_line = -frame.f_code.co_firstlineno
- else:
- self.last_line = frame.f_lineno
- elif event == 'line':
- # Record an executed line.
- if self.cur_file_dict is not None:
- lineno = frame.f_lineno
-
- if self.trace_arcs:
- self.cur_file_dict[(self.last_line, lineno)] = None
- else:
- self.cur_file_dict[lineno] = None
- self.last_line = lineno
- elif event == 'return':
- if self.trace_arcs and self.cur_file_dict:
- # Record an arc leaving the function, but beware that a
- # "return" event might just mean yielding from a generator.
- # Jython seems to have an empty co_code, so just assume return.
- code = frame.f_code.co_code
- if (not code) or code[frame.f_lasti] != YIELD_VALUE:
- first = frame.f_code.co_firstlineno
- self.cur_file_dict[(self.last_line, -first)] = None
- # Leaving this function, pop the filename stack.
- self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
- self.data_stack.pop()
- )
- # Leaving a context?
- if self.started_context:
- self.context = None
- self.switch_context(None)
- elif event == 'exception':
- self.last_exc_back = frame.f_back
- self.last_exc_firstlineno = frame.f_code.co_firstlineno
- return self._trace
-
- def start(self):
- """Start this Tracer.
-
- Return a Python function suitable for use with sys.settrace().
-
- """
- self.stopped = False
- if self.threading:
- if self.thread is None:
- self.thread = self.threading.currentThread()
- else:
- if self.thread.ident != self.threading.currentThread().ident:
- # Re-starting from a different thread!? Don't set the trace
- # function, but we are marked as running again, so maybe it
- # will be ok?
- #self.log("~", "starting on different threads")
- return self._trace
-
- sys.settrace(self._trace)
- return self._trace
-
- def stop(self):
- """Stop this Tracer."""
- # Get the active tracer callback before setting the stop flag to be
- # able to detect if the tracer was changed prior to stopping it.
- tf = sys.gettrace()
-
- # Set the stop flag. The actual call to sys.settrace(None) will happen
- # in the self._trace callback itself to make sure to call it from the
- # right thread.
- self.stopped = True
-
- if self.threading and self.thread.ident != self.threading.currentThread().ident:
- # Called on a different thread than started us: we can't unhook
- # ourselves, but we've set the flag that we should stop, so we
- # won't do any more tracing.
- #self.log("~", "stopping on different threads")
- return
-
- if self.warn:
- # PyPy clears the trace function before running atexit functions,
- # so don't warn if we are in atexit on PyPy and the trace function
- # has changed to None.
- dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None)
- if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable
- self.warn(
- "Trace function changed, measurement is likely wrong: %r" % (tf,),
- slug="trace-changed",
- )
-
- def activity(self):
- """Has there been any activity?"""
- return self._activity
-
- def reset_activity(self):
- """Reset the activity() flag."""
- self._activity = False
-
- def get_stats(self):
- """Return a dictionary of statistics, or None."""
- return None
diff --git a/contrib/python/coverage/py2/coverage/report.py b/contrib/python/coverage/py2/coverage/report.py
deleted file mode 100644
index 64678ff95d..0000000000
--- a/contrib/python/coverage/py2/coverage/report.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Reporter foundation for coverage.py."""
-import sys
-
-from coverage import env
-from coverage.files import prep_patterns, FnmatchMatcher
-from coverage.misc import CoverageException, NoSource, NotPython, ensure_dir_for_file, file_be_gone
-
-
-def render_report(output_path, reporter, morfs):
- """Run the provided reporter ensuring any required setup and cleanup is done
-
- At a high level this method ensures the output file is ready to be written to. Then writes the
- report to it. Then closes the file and deletes any garbage created if necessary.
- """
- file_to_close = None
- delete_file = False
- if output_path:
- if output_path == '-':
- outfile = sys.stdout
- else:
- # Ensure that the output directory is created; done here
- # because this report pre-opens the output file.
- # HTMLReport does this using the Report plumbing because
- # its task is more complex, being multiple files.
- ensure_dir_for_file(output_path)
- open_kwargs = {}
- if env.PY3:
- open_kwargs['encoding'] = 'utf8'
- outfile = open(output_path, "w", **open_kwargs)
- file_to_close = outfile
- try:
- return reporter.report(morfs, outfile=outfile)
- except CoverageException:
- delete_file = True
- raise
- finally:
- if file_to_close:
- file_to_close.close()
- if delete_file:
- file_be_gone(output_path)
-
-
-def get_analysis_to_report(coverage, morfs):
- """Get the files to report on.
-
- For each morf in `morfs`, if it should be reported on (based on the omit
- and include configuration options), yield a pair, the `FileReporter` and
- `Analysis` for the morf.
-
- """
- file_reporters = coverage._get_file_reporters(morfs)
- config = coverage.config
-
- if config.report_include:
- matcher = FnmatchMatcher(prep_patterns(config.report_include))
- file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)]
-
- if config.report_omit:
- matcher = FnmatchMatcher(prep_patterns(config.report_omit))
- file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)]
-
- if not file_reporters:
- raise CoverageException("No data to report.")
-
- for fr in sorted(file_reporters):
- try:
- analysis = coverage._analyze(fr)
- except NoSource:
- if not config.ignore_errors:
- raise
- except NotPython:
- # Only report errors for .py files, and only if we didn't
- # explicitly suppress those errors.
- # NotPython is only raised by PythonFileReporter, which has a
- # should_be_python() method.
- if fr.should_be_python():
- if config.ignore_errors:
- msg = "Couldn't parse Python file '{}'".format(fr.filename)
- coverage._warn(msg, slug="couldnt-parse")
- else:
- raise
- else:
- yield (fr, analysis)
diff --git a/contrib/python/coverage/py2/coverage/results.py b/contrib/python/coverage/py2/coverage/results.py
deleted file mode 100644
index 4916864df3..0000000000
--- a/contrib/python/coverage/py2/coverage/results.py
+++ /dev/null
@@ -1,343 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Results of coverage measurement."""
-
-import collections
-
-from coverage.backward import iitems
-from coverage.debug import SimpleReprMixin
-from coverage.misc import contract, CoverageException, nice_pair
-
-
-class Analysis(object):
- """The results of analyzing a FileReporter."""
-
- def __init__(self, data, file_reporter, file_mapper):
- self.data = data
- self.file_reporter = file_reporter
- self.filename = file_mapper(self.file_reporter.filename)
- self.statements = self.file_reporter.lines()
- self.excluded = self.file_reporter.excluded_lines()
-
- # Identify missing statements.
- executed = self.data.lines(self.filename) or []
- executed = self.file_reporter.translate_lines(executed)
- self.executed = executed
- self.missing = self.statements - self.executed
-
- if self.data.has_arcs():
- self._arc_possibilities = sorted(self.file_reporter.arcs())
- self.exit_counts = self.file_reporter.exit_counts()
- self.no_branch = self.file_reporter.no_branch_lines()
- n_branches = self._total_branches()
- mba = self.missing_branch_arcs()
- n_partial_branches = sum(len(v) for k,v in iitems(mba) if k not in self.missing)
- n_missing_branches = sum(len(v) for k,v in iitems(mba))
- else:
- self._arc_possibilities = []
- self.exit_counts = {}
- self.no_branch = set()
- n_branches = n_partial_branches = n_missing_branches = 0
-
- self.numbers = Numbers(
- n_files=1,
- n_statements=len(self.statements),
- n_excluded=len(self.excluded),
- n_missing=len(self.missing),
- n_branches=n_branches,
- n_partial_branches=n_partial_branches,
- n_missing_branches=n_missing_branches,
- )
-
- def missing_formatted(self, branches=False):
- """The missing line numbers, formatted nicely.
-
- Returns a string like "1-2, 5-11, 13-14".
-
- If `branches` is true, includes the missing branch arcs also.
-
- """
- if branches and self.has_arcs():
- arcs = iitems(self.missing_branch_arcs())
- else:
- arcs = None
-
- return format_lines(self.statements, self.missing, arcs=arcs)
-
- def has_arcs(self):
- """Were arcs measured in this result?"""
- return self.data.has_arcs()
-
- @contract(returns='list(tuple(int, int))')
- def arc_possibilities(self):
- """Returns a sorted list of the arcs in the code."""
- return self._arc_possibilities
-
- @contract(returns='list(tuple(int, int))')
- def arcs_executed(self):
- """Returns a sorted list of the arcs actually executed in the code."""
- executed = self.data.arcs(self.filename) or []
- executed = self.file_reporter.translate_arcs(executed)
- return sorted(executed)
-
- @contract(returns='list(tuple(int, int))')
- def arcs_missing(self):
- """Returns a sorted list of the arcs in the code not executed."""
- possible = self.arc_possibilities()
- executed = self.arcs_executed()
- missing = (
- p for p in possible
- if p not in executed
- and p[0] not in self.no_branch
- )
- return sorted(missing)
-
- @contract(returns='list(tuple(int, int))')
- def arcs_unpredicted(self):
- """Returns a sorted list of the executed arcs missing from the code."""
- possible = self.arc_possibilities()
- executed = self.arcs_executed()
- # Exclude arcs here which connect a line to itself. They can occur
- # in executed data in some cases. This is where they can cause
- # trouble, and here is where it's the least burden to remove them.
- # Also, generators can somehow cause arcs from "enter" to "exit", so
- # make sure we have at least one positive value.
- unpredicted = (
- e for e in executed
- if e not in possible
- and e[0] != e[1]
- and (e[0] > 0 or e[1] > 0)
- )
- return sorted(unpredicted)
-
- def _branch_lines(self):
- """Returns a list of line numbers that have more than one exit."""
- return [l1 for l1,count in iitems(self.exit_counts) if count > 1]
-
- def _total_branches(self):
- """How many total branches are there?"""
- return sum(count for count in self.exit_counts.values() if count > 1)
-
- @contract(returns='dict(int: list(int))')
- def missing_branch_arcs(self):
- """Return arcs that weren't executed from branch lines.
-
- Returns {l1:[l2a,l2b,...], ...}
-
- """
- missing = self.arcs_missing()
- branch_lines = set(self._branch_lines())
- mba = collections.defaultdict(list)
- for l1, l2 in missing:
- if l1 in branch_lines:
- mba[l1].append(l2)
- return mba
-
- @contract(returns='dict(int: tuple(int, int))')
- def branch_stats(self):
- """Get stats about branches.
-
- Returns a dict mapping line numbers to a tuple:
- (total_exits, taken_exits).
- """
-
- missing_arcs = self.missing_branch_arcs()
- stats = {}
- for lnum in self._branch_lines():
- exits = self.exit_counts[lnum]
- missing = len(missing_arcs[lnum])
- stats[lnum] = (exits, exits - missing)
- return stats
-
-
-class Numbers(SimpleReprMixin):
- """The numerical results of measuring coverage.
-
- This holds the basic statistics from `Analysis`, and is used to roll
- up statistics across files.
-
- """
- # A global to determine the precision on coverage percentages, the number
- # of decimal places.
- _precision = 0
- _near0 = 1.0 # These will change when _precision is changed.
- _near100 = 99.0
-
- def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
- n_branches=0, n_partial_branches=0, n_missing_branches=0
- ):
- self.n_files = n_files
- self.n_statements = n_statements
- self.n_excluded = n_excluded
- self.n_missing = n_missing
- self.n_branches = n_branches
- self.n_partial_branches = n_partial_branches
- self.n_missing_branches = n_missing_branches
-
- def init_args(self):
- """Return a list for __init__(*args) to recreate this object."""
- return [
- self.n_files, self.n_statements, self.n_excluded, self.n_missing,
- self.n_branches, self.n_partial_branches, self.n_missing_branches,
- ]
-
- @classmethod
- def set_precision(cls, precision):
- """Set the number of decimal places used to report percentages."""
- assert 0 <= precision < 10
- cls._precision = precision
- cls._near0 = 1.0 / 10**precision
- cls._near100 = 100.0 - cls._near0
-
- @property
- def n_executed(self):
- """Returns the number of executed statements."""
- return self.n_statements - self.n_missing
-
- @property
- def n_executed_branches(self):
- """Returns the number of executed branches."""
- return self.n_branches - self.n_missing_branches
-
- @property
- def pc_covered(self):
- """Returns a single percentage value for coverage."""
- if self.n_statements > 0:
- numerator, denominator = self.ratio_covered
- pc_cov = (100.0 * numerator) / denominator
- else:
- pc_cov = 100.0
- return pc_cov
-
- @property
- def pc_covered_str(self):
- """Returns the percent covered, as a string, without a percent sign.
-
- Note that "0" is only returned when the value is truly zero, and "100"
- is only returned when the value is truly 100. Rounding can never
- result in either "0" or "100".
-
- """
- pc = self.pc_covered
- if 0 < pc < self._near0:
- pc = self._near0
- elif self._near100 < pc < 100:
- pc = self._near100
- else:
- pc = round(pc, self._precision)
- return "%.*f" % (self._precision, pc)
-
- @classmethod
- def pc_str_width(cls):
- """How many characters wide can pc_covered_str be?"""
- width = 3 # "100"
- if cls._precision > 0:
- width += 1 + cls._precision
- return width
-
- @property
- def ratio_covered(self):
- """Return a numerator and denominator for the coverage ratio."""
- numerator = self.n_executed + self.n_executed_branches
- denominator = self.n_statements + self.n_branches
- return numerator, denominator
-
- def __add__(self, other):
- nums = Numbers()
- nums.n_files = self.n_files + other.n_files
- nums.n_statements = self.n_statements + other.n_statements
- nums.n_excluded = self.n_excluded + other.n_excluded
- nums.n_missing = self.n_missing + other.n_missing
- nums.n_branches = self.n_branches + other.n_branches
- nums.n_partial_branches = (
- self.n_partial_branches + other.n_partial_branches
- )
- nums.n_missing_branches = (
- self.n_missing_branches + other.n_missing_branches
- )
- return nums
-
- def __radd__(self, other):
- # Implementing 0+Numbers allows us to sum() a list of Numbers.
- if other == 0:
- return self
- return NotImplemented # pragma: not covered (we never call it this way)
-
-
-def _line_ranges(statements, lines):
- """Produce a list of ranges for `format_lines`."""
- statements = sorted(statements)
- lines = sorted(lines)
-
- pairs = []
- start = None
- lidx = 0
- for stmt in statements:
- if lidx >= len(lines):
- break
- if stmt == lines[lidx]:
- lidx += 1
- if not start:
- start = stmt
- end = stmt
- elif start:
- pairs.append((start, end))
- start = None
- if start:
- pairs.append((start, end))
- return pairs
-
-
-def format_lines(statements, lines, arcs=None):
- """Nicely format a list of line numbers.
-
- Format a list of line numbers for printing by coalescing groups of lines as
- long as the lines represent consecutive statements. This will coalesce
- even if there are gaps between statements.
-
- For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and
- `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14".
-
- Both `lines` and `statements` can be any iterable. All of the elements of
- `lines` must be in `statements`, and all of the values must be positive
- integers.
-
- If `arcs` is provided, they are (start,[end,end,end]) pairs that will be
- included in the output as long as start isn't in `lines`.
-
- """
- line_items = [(pair[0], nice_pair(pair)) for pair in _line_ranges(statements, lines)]
- if arcs:
- line_exits = sorted(arcs)
- for line, exits in line_exits:
- for ex in sorted(exits):
- if line not in lines and ex not in lines:
- dest = (ex if ex > 0 else "exit")
- line_items.append((line, "%d->%s" % (line, dest)))
-
- ret = ', '.join(t[-1] for t in sorted(line_items))
- return ret
-
-
-@contract(total='number', fail_under='number', precision=int, returns=bool)
-def should_fail_under(total, fail_under, precision):
- """Determine if a total should fail due to fail-under.
-
- `total` is a float, the coverage measurement total. `fail_under` is the
- fail_under setting to compare with. `precision` is the number of digits
- to consider after the decimal point.
-
- Returns True if the total should fail.
-
- """
- # We can never achieve higher than 100% coverage, or less than zero.
- if not (0 <= fail_under <= 100.0):
- msg = "fail_under={} is invalid. Must be between 0 and 100.".format(fail_under)
- raise CoverageException(msg)
-
- # Special case for fail_under=100, it must really be 100.
- if fail_under == 100.0 and total != 100.0:
- return True
-
- return round(total, precision) < fail_under
diff --git a/contrib/python/coverage/py2/coverage/sqldata.py b/contrib/python/coverage/py2/coverage/sqldata.py
deleted file mode 100644
index a150fdfd0f..0000000000
--- a/contrib/python/coverage/py2/coverage/sqldata.py
+++ /dev/null
@@ -1,1123 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Sqlite coverage data."""
-
-# TODO: factor out dataop debugging to a wrapper class?
-# TODO: make sure all dataop debugging is in place somehow
-
-import collections
-import datetime
-import glob
-import itertools
-import os
-import re
-import sqlite3
-import sys
-import zlib
-
-from coverage import env
-from coverage.backward import get_thread_id, iitems, to_bytes, to_string
-from coverage.debug import NoDebugging, SimpleReprMixin, clipped_repr
-from coverage.files import PathAliases
-from coverage.misc import CoverageException, contract, file_be_gone, filename_suffix, isolate_module
-from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits
-from coverage.version import __version__
-
-os = isolate_module(os)
-
-# If you change the schema, increment the SCHEMA_VERSION, and update the
-# docs in docs/dbschema.rst also.
-
-SCHEMA_VERSION = 7
-
-# Schema versions:
-# 1: Released in 5.0a2
-# 2: Added contexts in 5.0a3.
-# 3: Replaced line table with line_map table.
-# 4: Changed line_map.bitmap to line_map.numbits.
-# 5: Added foreign key declarations.
-# 6: Key-value in meta.
-# 7: line_map -> line_bits
-
-SCHEMA = """\
-CREATE TABLE coverage_schema (
- -- One row, to record the version of the schema in this db.
- version integer
-);
-
-CREATE TABLE meta (
- -- Key-value pairs, to record metadata about the data
- key text,
- value text,
- unique (key)
- -- Keys:
- -- 'has_arcs' boolean -- Is this data recording branches?
- -- 'sys_argv' text -- The coverage command line that recorded the data.
- -- 'version' text -- The version of coverage.py that made the file.
- -- 'when' text -- Datetime when the file was created.
-);
-
-CREATE TABLE file (
- -- A row per file measured.
- id integer primary key,
- path text,
- unique (path)
-);
-
-CREATE TABLE context (
- -- A row per context measured.
- id integer primary key,
- context text,
- unique (context)
-);
-
-CREATE TABLE line_bits (
- -- If recording lines, a row per context per file executed.
- -- All of the line numbers for that file/context are in one numbits.
- file_id integer, -- foreign key to `file`.
- context_id integer, -- foreign key to `context`.
- numbits blob, -- see the numbits functions in coverage.numbits
- foreign key (file_id) references file (id),
- foreign key (context_id) references context (id),
- unique (file_id, context_id)
-);
-
-CREATE TABLE arc (
- -- If recording branches, a row per context per from/to line transition executed.
- file_id integer, -- foreign key to `file`.
- context_id integer, -- foreign key to `context`.
- fromno integer, -- line number jumped from.
- tono integer, -- line number jumped to.
- foreign key (file_id) references file (id),
- foreign key (context_id) references context (id),
- unique (file_id, context_id, fromno, tono)
-);
-
-CREATE TABLE tracer (
- -- A row per file indicating the tracer used for that file.
- file_id integer primary key,
- tracer text,
- foreign key (file_id) references file (id)
-);
-"""
-
-class CoverageData(SimpleReprMixin):
- """Manages collected coverage data, including file storage.
-
- This class is the public supported API to the data that coverage.py
- collects during program execution. It includes information about what code
- was executed. It does not include information from the analysis phase, to
- determine what lines could have been executed, or what lines were not
- executed.
-
- .. note::
-
- The data file is currently a SQLite database file, with a
- :ref:`documented schema <dbschema>`. The schema is subject to change
- though, so be careful about querying it directly. Use this API if you
- can to isolate yourself from changes.
-
- There are a number of kinds of data that can be collected:
-
- * **lines**: the line numbers of source lines that were executed.
- These are always available.
-
- * **arcs**: pairs of source and destination line numbers for transitions
- between source lines. These are only available if branch coverage was
- used.
-
- * **file tracer names**: the module names of the file tracer plugins that
- handled each file in the data.
-
- Lines, arcs, and file tracer names are stored for each source file. File
- names in this API are case-sensitive, even on platforms with
- case-insensitive file systems.
-
- A data file either stores lines, or arcs, but not both.
-
- A data file is associated with the data when the :class:`CoverageData`
- is created, using the parameters `basename`, `suffix`, and `no_disk`. The
- base name can be queried with :meth:`base_filename`, and the actual file
- name being used is available from :meth:`data_filename`.
-
- To read an existing coverage.py data file, use :meth:`read`. You can then
- access the line, arc, or file tracer data with :meth:`lines`, :meth:`arcs`,
- or :meth:`file_tracer`.
-
- The :meth:`has_arcs` method indicates whether arc data is available. You
- can get a set of the files in the data with :meth:`measured_files`. As
- with most Python containers, you can determine if there is any data at all
- by using this object as a boolean value.
-
- The contexts for each line in a file can be read with
- :meth:`contexts_by_lineno`.
-
- To limit querying to certain contexts, use :meth:`set_query_context` or
- :meth:`set_query_contexts`. These will narrow the focus of subsequent
- :meth:`lines`, :meth:`arcs`, and :meth:`contexts_by_lineno` calls. The set
- of all measured context names can be retrieved with
- :meth:`measured_contexts`.
-
- Most data files will be created by coverage.py itself, but you can use
- methods here to create data files if you like. The :meth:`add_lines`,
- :meth:`add_arcs`, and :meth:`add_file_tracers` methods add data, in ways
- that are convenient for coverage.py.
-
- To record data for contexts, use :meth:`set_context` to set a context to
- be used for subsequent :meth:`add_lines` and :meth:`add_arcs` calls.
-
- To add a source file without any measured data, use :meth:`touch_file`,
- or :meth:`touch_files` for a list of such files.
-
- Write the data to its file with :meth:`write`.
-
- You can clear the data in memory with :meth:`erase`. Two data collections
- can be combined by using :meth:`update` on one :class:`CoverageData`,
- passing it the other.
-
- Data in a :class:`CoverageData` can be serialized and deserialized with
- :meth:`dumps` and :meth:`loads`.
-
- """
-
- def __init__(self, basename=None, suffix=None, no_disk=False, warn=None, debug=None):
- """Create a :class:`CoverageData` object to hold coverage-measured data.
-
- Arguments:
- basename (str): the base name of the data file, defaulting to
- ".coverage".
- suffix (str or bool): has the same meaning as the `data_suffix`
- argument to :class:`coverage.Coverage`.
- no_disk (bool): if True, keep all data in memory, and don't
- write any disk file.
- warn: a warning callback function, accepting a warning message
- argument.
- debug: a `DebugControl` object (optional)
-
- """
- self._no_disk = no_disk
- self._basename = os.path.abspath(basename or ".coverage")
- self._suffix = suffix
- self._warn = warn
- self._debug = debug or NoDebugging()
-
- self._choose_filename()
- self._file_map = {}
- # Maps thread ids to SqliteDb objects.
- self._dbs = {}
- self._pid = os.getpid()
-
- # Are we in sync with the data file?
- self._have_used = False
-
- self._has_lines = False
- self._has_arcs = False
-
- self._current_context = None
- self._current_context_id = None
- self._query_context_ids = None
-
- def _choose_filename(self):
- """Set self._filename based on inited attributes."""
- if self._no_disk:
- self._filename = ":memory:"
- else:
- self._filename = self._basename
- suffix = filename_suffix(self._suffix)
- if suffix:
- self._filename += "." + suffix
-
- def _reset(self):
- """Reset our attributes."""
- if self._dbs:
- for db in self._dbs.values():
- db.close()
- self._dbs = {}
- self._file_map = {}
- self._have_used = False
- self._current_context_id = None
-
- def _create_db(self):
- """Create a db file that doesn't exist yet.
-
- Initializes the schema and certain metadata.
- """
- if self._debug.should('dataio'):
- self._debug.write("Creating data file {!r}".format(self._filename))
- self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
- with db:
- db.executescript(SCHEMA)
- db.execute("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
- db.executemany(
- "insert into meta (key, value) values (?, ?)",
- [
- ('sys_argv', str(getattr(sys, 'argv', None))),
- ('version', __version__),
- ('when', datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')),
- ]
- )
-
- def _open_db(self):
- """Open an existing db file, and read its metadata."""
- if self._debug.should('dataio'):
- self._debug.write("Opening data file {!r}".format(self._filename))
- self._dbs[get_thread_id()] = SqliteDb(self._filename, self._debug)
- self._read_db()
-
- def _read_db(self):
- """Read the metadata from a database so that we are ready to use it."""
- with self._dbs[get_thread_id()] as db:
- try:
- schema_version, = db.execute_one("select version from coverage_schema")
- except Exception as exc:
- raise CoverageException(
- "Data file {!r} doesn't seem to be a coverage data file: {}".format(
- self._filename, exc
- )
- )
- else:
- if schema_version != SCHEMA_VERSION:
- raise CoverageException(
- "Couldn't use data file {!r}: wrong schema: {} instead of {}".format(
- self._filename, schema_version, SCHEMA_VERSION
- )
- )
-
- for row in db.execute("select value from meta where key = 'has_arcs'"):
- self._has_arcs = bool(int(row[0]))
- self._has_lines = not self._has_arcs
-
- for path, file_id in db.execute("select path, id from file"):
- self._file_map[path] = file_id
-
- def _connect(self):
- """Get the SqliteDb object to use."""
- if get_thread_id() not in self._dbs:
- if os.path.exists(self._filename):
- self._open_db()
- else:
- self._create_db()
- return self._dbs[get_thread_id()]
-
- def __nonzero__(self):
- if (get_thread_id() not in self._dbs and not os.path.exists(self._filename)):
- return False
- try:
- with self._connect() as con:
- rows = con.execute("select * from file limit 1")
- return bool(list(rows))
- except CoverageException:
- return False
-
- __bool__ = __nonzero__
-
- @contract(returns='bytes')
- def dumps(self):
- """Serialize the current data to a byte string.
-
- The format of the serialized data is not documented. It is only
- suitable for use with :meth:`loads` in the same version of
- coverage.py.
-
- Returns:
- A byte string of serialized data.
-
- .. versionadded:: 5.0
-
- """
- if self._debug.should('dataio'):
- self._debug.write("Dumping data from data file {!r}".format(self._filename))
- with self._connect() as con:
- return b'z' + zlib.compress(to_bytes(con.dump()))
-
- @contract(data='bytes')
- def loads(self, data):
- """Deserialize data from :meth:`dumps`
-
- Use with a newly-created empty :class:`CoverageData` object. It's
- undefined what happens if the object already has data in it.
-
- Arguments:
- data: A byte string of serialized data produced by :meth:`dumps`.
-
- .. versionadded:: 5.0
-
- """
- if self._debug.should('dataio'):
- self._debug.write("Loading data into data file {!r}".format(self._filename))
- if data[:1] != b'z':
- raise CoverageException(
- "Unrecognized serialization: {!r} (head of {} bytes)".format(data[:40], len(data))
- )
- script = to_string(zlib.decompress(data[1:]))
- self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
- with db:
- db.executescript(script)
- self._read_db()
- self._have_used = True
-
- def _file_id(self, filename, add=False):
- """Get the file id for `filename`.
-
- If filename is not in the database yet, add it if `add` is True.
- If `add` is not True, return None.
- """
- if filename not in self._file_map:
- if add:
- with self._connect() as con:
- cur = con.execute("insert or replace into file (path) values (?)", (filename,))
- self._file_map[filename] = cur.lastrowid
- return self._file_map.get(filename)
-
- def _context_id(self, context):
- """Get the id for a context."""
- assert context is not None
- self._start_using()
- with self._connect() as con:
- row = con.execute_one("select id from context where context = ?", (context,))
- if row is not None:
- return row[0]
- else:
- return None
-
- def set_context(self, context):
- """Set the current context for future :meth:`add_lines` etc.
-
- `context` is a str, the name of the context to use for the next data
- additions. The context persists until the next :meth:`set_context`.
-
- .. versionadded:: 5.0
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Setting context: %r" % (context,))
- self._current_context = context
- self._current_context_id = None
-
- def _set_context_id(self):
- """Use the _current_context to set _current_context_id."""
- context = self._current_context or ""
- context_id = self._context_id(context)
- if context_id is not None:
- self._current_context_id = context_id
- else:
- with self._connect() as con:
- cur = con.execute("insert into context (context) values (?)", (context,))
- self._current_context_id = cur.lastrowid
-
- def base_filename(self):
- """The base filename for storing data.
-
- .. versionadded:: 5.0
-
- """
- return self._basename
-
- def data_filename(self):
- """Where is the data stored?
-
- .. versionadded:: 5.0
-
- """
- return self._filename
-
- def add_lines(self, line_data):
- """Add measured line data.
-
- `line_data` is a dictionary mapping file names to dictionaries::
-
- { filename: { lineno: None, ... }, ...}
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Adding lines: %d files, %d lines total" % (
- len(line_data), sum(len(lines) for lines in line_data.values())
- ))
- self._start_using()
- self._choose_lines_or_arcs(lines=True)
- if not line_data:
- return
- with self._connect() as con:
- self._set_context_id()
- for filename, linenos in iitems(line_data):
- linemap = nums_to_numbits(linenos)
- file_id = self._file_id(filename, add=True)
- query = "select numbits from line_bits where file_id = ? and context_id = ?"
- existing = list(con.execute(query, (file_id, self._current_context_id)))
- if existing:
- linemap = numbits_union(linemap, existing[0][0])
-
- con.execute(
- "insert or replace into line_bits "
- " (file_id, context_id, numbits) values (?, ?, ?)",
- (file_id, self._current_context_id, linemap),
- )
-
- def add_arcs(self, arc_data):
- """Add measured arc data.
-
- `arc_data` is a dictionary mapping file names to dictionaries::
-
- { filename: { (l1,l2): None, ... }, ...}
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Adding arcs: %d files, %d arcs total" % (
- len(arc_data), sum(len(arcs) for arcs in arc_data.values())
- ))
- self._start_using()
- self._choose_lines_or_arcs(arcs=True)
- if not arc_data:
- return
- with self._connect() as con:
- self._set_context_id()
- for filename, arcs in iitems(arc_data):
- file_id = self._file_id(filename, add=True)
- data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs]
- con.executemany(
- "insert or ignore into arc "
- "(file_id, context_id, fromno, tono) values (?, ?, ?, ?)",
- data,
- )
-
- def _choose_lines_or_arcs(self, lines=False, arcs=False):
- """Force the data file to choose between lines and arcs."""
- assert lines or arcs
- assert not (lines and arcs)
- if lines and self._has_arcs:
- raise CoverageException("Can't add line measurements to existing branch data")
- if arcs and self._has_lines:
- raise CoverageException("Can't add branch measurements to existing line data")
- if not self._has_arcs and not self._has_lines:
- self._has_lines = lines
- self._has_arcs = arcs
- with self._connect() as con:
- con.execute(
- "insert into meta (key, value) values (?, ?)",
- ('has_arcs', str(int(arcs)))
- )
-
- def add_file_tracers(self, file_tracers):
- """Add per-file plugin information.
-
- `file_tracers` is { filename: plugin_name, ... }
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Adding file tracers: %d files" % (len(file_tracers),))
- if not file_tracers:
- return
- self._start_using()
- with self._connect() as con:
- for filename, plugin_name in iitems(file_tracers):
- file_id = self._file_id(filename)
- if file_id is None:
- raise CoverageException(
- "Can't add file tracer data for unmeasured file '%s'" % (filename,)
- )
-
- existing_plugin = self.file_tracer(filename)
- if existing_plugin:
- if existing_plugin != plugin_name:
- raise CoverageException(
- "Conflicting file tracer name for '%s': %r vs %r" % (
- filename, existing_plugin, plugin_name,
- )
- )
- elif plugin_name:
- con.execute(
- "insert into tracer (file_id, tracer) values (?, ?)",
- (file_id, plugin_name)
- )
-
- def touch_file(self, filename, plugin_name=""):
- """Ensure that `filename` appears in the data, empty if needed.
-
- `plugin_name` is the name of the plugin responsible for this file. It is used
- to associate the right filereporter, etc.
- """
- self.touch_files([filename], plugin_name)
-
- def touch_files(self, filenames, plugin_name=""):
- """Ensure that `filenames` appear in the data, empty if needed.
-
- `plugin_name` is the name of the plugin responsible for these files. It is used
- to associate the right filereporter, etc.
- """
- if self._debug.should('dataop'):
- self._debug.write("Touching %r" % (filenames,))
- self._start_using()
- with self._connect(): # Use this to get one transaction.
- if not self._has_arcs and not self._has_lines:
- raise CoverageException("Can't touch files in an empty CoverageData")
-
- for filename in filenames:
- self._file_id(filename, add=True)
- if plugin_name:
- # Set the tracer for this file
- self.add_file_tracers({filename: plugin_name})
-
- def update(self, other_data, aliases=None):
- """Update this data with data from several other :class:`CoverageData` instances.
-
- If `aliases` is provided, it's a `PathAliases` object that is used to
- re-map paths to match the local machine's.
- """
- if self._debug.should('dataop'):
- self._debug.write("Updating with data from %r" % (
- getattr(other_data, '_filename', '???'),
- ))
- if self._has_lines and other_data._has_arcs:
- raise CoverageException("Can't combine arc data with line data")
- if self._has_arcs and other_data._has_lines:
- raise CoverageException("Can't combine line data with arc data")
-
- aliases = aliases or PathAliases()
-
- # Force the database we're writing to to exist before we start nesting
- # contexts.
- self._start_using()
-
- # Collector for all arcs, lines and tracers
- other_data.read()
- with other_data._connect() as conn:
- # Get files data.
- cur = conn.execute('select path from file')
- files = {path: aliases.map(path) for (path,) in cur}
- cur.close()
-
- # Get contexts data.
- cur = conn.execute('select context from context')
- contexts = [context for (context,) in cur]
- cur.close()
-
- # Get arc data.
- cur = conn.execute(
- 'select file.path, context.context, arc.fromno, arc.tono '
- 'from arc '
- 'inner join file on file.id = arc.file_id '
- 'inner join context on context.id = arc.context_id'
- )
- arcs = [(files[path], context, fromno, tono) for (path, context, fromno, tono) in cur]
- cur.close()
-
- # Get line data.
- cur = conn.execute(
- 'select file.path, context.context, line_bits.numbits '
- 'from line_bits '
- 'inner join file on file.id = line_bits.file_id '
- 'inner join context on context.id = line_bits.context_id'
- )
- lines = {
- (files[path], context): numbits
- for (path, context, numbits) in cur
- }
- cur.close()
-
- # Get tracer data.
- cur = conn.execute(
- 'select file.path, tracer '
- 'from tracer '
- 'inner join file on file.id = tracer.file_id'
- )
- tracers = {files[path]: tracer for (path, tracer) in cur}
- cur.close()
-
- with self._connect() as conn:
- conn.con.isolation_level = 'IMMEDIATE'
-
- # Get all tracers in the DB. Files not in the tracers are assumed
- # to have an empty string tracer. Since Sqlite does not support
- # full outer joins, we have to make two queries to fill the
- # dictionary.
- this_tracers = {path: '' for path, in conn.execute('select path from file')}
- this_tracers.update({
- aliases.map(path): tracer
- for path, tracer in conn.execute(
- 'select file.path, tracer from tracer '
- 'inner join file on file.id = tracer.file_id'
- )
- })
-
- # Create all file and context rows in the DB.
- conn.executemany(
- 'insert or ignore into file (path) values (?)',
- ((file,) for file in files.values())
- )
- file_ids = {
- path: id
- for id, path in conn.execute('select id, path from file')
- }
- conn.executemany(
- 'insert or ignore into context (context) values (?)',
- ((context,) for context in contexts)
- )
- context_ids = {
- context: id
- for id, context in conn.execute('select id, context from context')
- }
-
- # Prepare tracers and fail, if a conflict is found.
- # tracer_paths is used to ensure consistency over the tracer data
- # and tracer_map tracks the tracers to be inserted.
- tracer_map = {}
- for path in files.values():
- this_tracer = this_tracers.get(path)
- other_tracer = tracers.get(path, '')
- # If there is no tracer, there is always the None tracer.
- if this_tracer is not None and this_tracer != other_tracer:
- raise CoverageException(
- "Conflicting file tracer name for '%s': %r vs %r" % (
- path, this_tracer, other_tracer
- )
- )
- tracer_map[path] = other_tracer
-
- # Prepare arc and line rows to be inserted by converting the file
- # and context strings with integer ids. Then use the efficient
- # `executemany()` to insert all rows at once.
- arc_rows = (
- (file_ids[file], context_ids[context], fromno, tono)
- for file, context, fromno, tono in arcs
- )
-
- # Get line data.
- cur = conn.execute(
- 'select file.path, context.context, line_bits.numbits '
- 'from line_bits '
- 'inner join file on file.id = line_bits.file_id '
- 'inner join context on context.id = line_bits.context_id'
- )
- for path, context, numbits in cur:
- key = (aliases.map(path), context)
- if key in lines:
- numbits = numbits_union(lines[key], numbits)
- lines[key] = numbits
- cur.close()
-
- if arcs:
- self._choose_lines_or_arcs(arcs=True)
-
- # Write the combined data.
- conn.executemany(
- 'insert or ignore into arc '
- '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)',
- arc_rows
- )
-
- if lines:
- self._choose_lines_or_arcs(lines=True)
- conn.execute("delete from line_bits")
- conn.executemany(
- "insert into line_bits "
- "(file_id, context_id, numbits) values (?, ?, ?)",
- [
- (file_ids[file], context_ids[context], numbits)
- for (file, context), numbits in lines.items()
- ]
- )
- conn.executemany(
- 'insert or ignore into tracer (file_id, tracer) values (?, ?)',
- ((file_ids[filename], tracer) for filename, tracer in tracer_map.items())
- )
-
- # Update all internal cache data.
- self._reset()
- self.read()
-
- def erase(self, parallel=False):
- """Erase the data in this object.
-
- If `parallel` is true, then also deletes data files created from the
- basename by parallel-mode.
-
- """
- self._reset()
- if self._no_disk:
- return
- if self._debug.should('dataio'):
- self._debug.write("Erasing data file {!r}".format(self._filename))
- file_be_gone(self._filename)
- if parallel:
- data_dir, local = os.path.split(self._filename)
- localdot = local + '.*'
- pattern = os.path.join(os.path.abspath(data_dir), localdot)
- for filename in glob.glob(pattern):
- if self._debug.should('dataio'):
- self._debug.write("Erasing parallel data file {!r}".format(filename))
- file_be_gone(filename)
-
- def read(self):
- """Start using an existing data file."""
- with self._connect(): # TODO: doesn't look right
- self._have_used = True
-
- def write(self):
- """Ensure the data is written to the data file."""
- pass
-
- def _start_using(self):
- """Call this before using the database at all."""
- if self._pid != os.getpid():
- # Looks like we forked! Have to start a new data file.
- self._reset()
- self._choose_filename()
- self._pid = os.getpid()
- if not self._have_used:
- self.erase()
- self._have_used = True
-
- def has_arcs(self):
- """Does the database have arcs (True) or lines (False)."""
- return bool(self._has_arcs)
-
- def measured_files(self):
- """A set of all files that had been measured."""
- return set(self._file_map)
-
- def measured_contexts(self):
- """A set of all contexts that have been measured.
-
- .. versionadded:: 5.0
-
- """
- self._start_using()
- with self._connect() as con:
- contexts = {row[0] for row in con.execute("select distinct(context) from context")}
- return contexts
-
- def file_tracer(self, filename):
- """Get the plugin name of the file tracer for a file.
-
- Returns the name of the plugin that handles this file. If the file was
- measured, but didn't use a plugin, then "" is returned. If the file
- was not measured, then None is returned.
-
- """
- self._start_using()
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return None
- row = con.execute_one("select tracer from tracer where file_id = ?", (file_id,))
- if row is not None:
- return row[0] or ""
- return "" # File was measured, but no tracer associated.
-
- def set_query_context(self, context):
- """Set a context for subsequent querying.
-
- The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
- calls will be limited to only one context. `context` is a string which
- must match a context exactly. If it does not, no exception is raised,
- but queries will return no data.
-
- .. versionadded:: 5.0
-
- """
- self._start_using()
- with self._connect() as con:
- cur = con.execute("select id from context where context = ?", (context,))
- self._query_context_ids = [row[0] for row in cur.fetchall()]
-
- def set_query_contexts(self, contexts):
- """Set a number of contexts for subsequent querying.
-
- The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
- calls will be limited to the specified contexts. `contexts` is a list
- of Python regular expressions. Contexts will be matched using
- :func:`re.search <python:re.search>`. Data will be included in query
- results if they are part of any of the contexts matched.
-
- .. versionadded:: 5.0
-
- """
- self._start_using()
- if contexts:
- with self._connect() as con:
- context_clause = ' or '.join(['context regexp ?'] * len(contexts))
- cur = con.execute("select id from context where " + context_clause, contexts)
- self._query_context_ids = [row[0] for row in cur.fetchall()]
- else:
- self._query_context_ids = None
-
- def lines(self, filename):
- """Get the list of lines executed for a file.
-
- If the file was not measured, returns None. A file might be measured,
- and have no lines executed, in which case an empty list is returned.
-
- If the file was executed, returns a list of integers, the line numbers
- executed in the file. The list is in no particular order.
-
- """
- self._start_using()
- if self.has_arcs():
- arcs = self.arcs(filename)
- if arcs is not None:
- all_lines = itertools.chain.from_iterable(arcs)
- return list({l for l in all_lines if l > 0})
-
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return None
- else:
- query = "select numbits from line_bits where file_id = ?"
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and context_id in (" + ids_array + ")"
- data += self._query_context_ids
- bitmaps = list(con.execute(query, data))
- nums = set()
- for row in bitmaps:
- nums.update(numbits_to_nums(row[0]))
- return list(nums)
-
- def arcs(self, filename):
- """Get the list of arcs executed for a file.
-
- If the file was not measured, returns None. A file might be measured,
- and have no arcs executed, in which case an empty list is returned.
-
- If the file was executed, returns a list of 2-tuples of integers. Each
- pair is a starting line number and an ending line number for a
- transition from one line to another. The list is in no particular
- order.
-
- Negative numbers have special meaning. If the starting line number is
- -N, it represents an entry to the code object that starts at line N.
- If the ending ling number is -N, it's an exit from the code object that
- starts at line N.
-
- """
- self._start_using()
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return None
- else:
- query = "select distinct fromno, tono from arc where file_id = ?"
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and context_id in (" + ids_array + ")"
- data += self._query_context_ids
- arcs = con.execute(query, data)
- return list(arcs)
-
- def contexts_by_lineno(self, filename):
- """Get the contexts for each line in a file.
-
- Returns:
- A dict mapping line numbers to a list of context names.
-
- .. versionadded:: 5.0
-
- """
- lineno_contexts_map = collections.defaultdict(list)
- self._start_using()
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return lineno_contexts_map
- if self.has_arcs():
- query = (
- "select arc.fromno, arc.tono, context.context "
- "from arc, context "
- "where arc.file_id = ? and arc.context_id = context.id"
- )
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and arc.context_id in (" + ids_array + ")"
- data += self._query_context_ids
- for fromno, tono, context in con.execute(query, data):
- if context not in lineno_contexts_map[fromno]:
- lineno_contexts_map[fromno].append(context)
- if context not in lineno_contexts_map[tono]:
- lineno_contexts_map[tono].append(context)
- else:
- query = (
- "select l.numbits, c.context from line_bits l, context c "
- "where l.context_id = c.id "
- "and file_id = ?"
- )
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and l.context_id in (" + ids_array + ")"
- data += self._query_context_ids
- for numbits, context in con.execute(query, data):
- for lineno in numbits_to_nums(numbits):
- lineno_contexts_map[lineno].append(context)
- return lineno_contexts_map
-
- @classmethod
- def sys_info(cls):
- """Our information for `Coverage.sys_info`.
-
- Returns a list of (key, value) pairs.
-
- """
- with SqliteDb(":memory:", debug=NoDebugging()) as db:
- temp_store = [row[0] for row in db.execute("pragma temp_store")]
- compile_options = [row[0] for row in db.execute("pragma compile_options")]
-
- return [
- ('sqlite3_version', sqlite3.version),
- ('sqlite3_sqlite_version', sqlite3.sqlite_version),
- ('sqlite3_temp_store', temp_store),
- ('sqlite3_compile_options', compile_options),
- ]
-
-
-class SqliteDb(SimpleReprMixin):
- """A simple abstraction over a SQLite database.
-
- Use as a context manager, then you can use it like a
- :class:`python:sqlite3.Connection` object::
-
- with SqliteDb(filename, debug_control) as db:
- db.execute("insert into schema (version) values (?)", (SCHEMA_VERSION,))
-
- """
- def __init__(self, filename, debug):
- self.debug = debug if debug.should('sql') else None
- self.filename = filename
- self.nest = 0
- self.con = None
-
- def _connect(self):
- """Connect to the db and do universal initialization."""
- if self.con is not None:
- return
-
- # SQLite on Windows on py2 won't open a file if the filename argument
- # has non-ascii characters in it. Opening a relative file name avoids
- # a problem if the current directory has non-ascii.
- filename = self.filename
- if env.WINDOWS and env.PY2:
- try:
- filename = os.path.relpath(self.filename)
- except ValueError:
- # ValueError can be raised under Windows when os.getcwd() returns a
- # folder from a different drive than the drive of self.filename in
- # which case we keep the original value of self.filename unchanged,
- # hoping that we won't face the non-ascii directory problem.
- pass
-
- # It can happen that Python switches threads while the tracer writes
- # data. The second thread will also try to write to the data,
- # effectively causing a nested context. However, given the idempotent
- # nature of the tracer operations, sharing a connection among threads
- # is not a problem.
- if self.debug:
- self.debug.write("Connecting to {!r}".format(self.filename))
- self.con = sqlite3.connect(filename, check_same_thread=False)
- self.con.create_function('REGEXP', 2, _regexp)
-
- # This pragma makes writing faster. It disables rollbacks, but we never need them.
- # PyPy needs the .close() calls here, or sqlite gets twisted up:
- # https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on
- self.execute("pragma journal_mode=off").close()
- # This pragma makes writing faster.
- self.execute("pragma synchronous=off").close()
-
- def close(self):
- """If needed, close the connection."""
- if self.con is not None and self.filename != ":memory:":
- self.con.close()
- self.con = None
-
- def __enter__(self):
- if self.nest == 0:
- self._connect()
- self.con.__enter__()
- self.nest += 1
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.nest -= 1
- if self.nest == 0:
- try:
- self.con.__exit__(exc_type, exc_value, traceback)
- self.close()
- except Exception as exc:
- if self.debug:
- self.debug.write("EXCEPTION from __exit__: {}".format(exc))
- raise
-
- def execute(self, sql, parameters=()):
- """Same as :meth:`python:sqlite3.Connection.execute`."""
- if self.debug:
- tail = " with {!r}".format(parameters) if parameters else ""
- self.debug.write("Executing {!r}{}".format(sql, tail))
- try:
- try:
- return self.con.execute(sql, parameters)
- except Exception:
- # In some cases, an error might happen that isn't really an
- # error. Try again immediately.
- # https://github.com/nedbat/coveragepy/issues/1010
- return self.con.execute(sql, parameters)
- except sqlite3.Error as exc:
- msg = str(exc)
- try:
- # `execute` is the first thing we do with the database, so try
- # hard to provide useful hints if something goes wrong now.
- with open(self.filename, "rb") as bad_file:
- cov4_sig = b"!coverage.py: This is a private format"
- if bad_file.read(len(cov4_sig)) == cov4_sig:
- msg = (
- "Looks like a coverage 4.x data file. "
- "Are you mixing versions of coverage?"
- )
- except Exception:
- pass
- if self.debug:
- self.debug.write("EXCEPTION from execute: {}".format(msg))
- raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg))
-
- def execute_one(self, sql, parameters=()):
- """Execute a statement and return the one row that results.
-
- This is like execute(sql, parameters).fetchone(), except it is
- correct in reading the entire result set. This will raise an
- exception if more than one row results.
-
- Returns a row, or None if there were no rows.
- """
- rows = list(self.execute(sql, parameters))
- if len(rows) == 0:
- return None
- elif len(rows) == 1:
- return rows[0]
- else:
- raise CoverageException("Sql {!r} shouldn't return {} rows".format(sql, len(rows)))
-
- def executemany(self, sql, data):
- """Same as :meth:`python:sqlite3.Connection.executemany`."""
- if self.debug:
- data = list(data)
- self.debug.write("Executing many {!r} with {} rows".format(sql, len(data)))
- return self.con.executemany(sql, data)
-
- def executescript(self, script):
- """Same as :meth:`python:sqlite3.Connection.executescript`."""
- if self.debug:
- self.debug.write("Executing script with {} chars: {}".format(
- len(script), clipped_repr(script, 100),
- ))
- self.con.executescript(script)
-
- def dump(self):
- """Return a multi-line string, the SQL dump of the database."""
- return "\n".join(self.con.iterdump())
-
-
-def _regexp(text, pattern):
- """A regexp function for SQLite."""
- return re.search(text, pattern) is not None
diff --git a/contrib/python/coverage/py2/coverage/summary.py b/contrib/python/coverage/py2/coverage/summary.py
deleted file mode 100644
index 65f8047006..0000000000
--- a/contrib/python/coverage/py2/coverage/summary.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Summary reporting"""
-
-import sys
-
-from coverage import env
-from coverage.report import get_analysis_to_report
-from coverage.results import Numbers
-from coverage.misc import CoverageException, output_encoding
-
-
-class SummaryReporter(object):
- """A reporter for writing the summary report."""
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
- self.branches = coverage.get_data().has_arcs()
- self.outfile = None
- self.fr_analysis = []
- self.skipped_count = 0
- self.empty_count = 0
- self.total = Numbers()
- self.fmt_err = u"%s %s: %s"
-
- def writeout(self, line):
- """Write a line to the output, adding a newline."""
- if env.PY2:
- line = line.encode(output_encoding())
- self.outfile.write(line.rstrip())
- self.outfile.write("\n")
-
- def report(self, morfs, outfile=None):
- """Writes a report summarizing coverage statistics per module.
-
- `outfile` is a file object to write the summary to. It must be opened
- for native strings (bytes on Python 2, Unicode on Python 3).
-
- """
- self.outfile = outfile or sys.stdout
-
- self.coverage.get_data().set_query_contexts(self.config.report_contexts)
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.report_one_file(fr, analysis)
-
- # Prepare the formatting strings, header, and column sorting.
- max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5])
- fmt_name = u"%%- %ds " % max_name
- fmt_skip_covered = u"\n%s file%s skipped due to complete coverage."
- fmt_skip_empty = u"\n%s empty file%s skipped."
-
- header = (fmt_name % "Name") + u" Stmts Miss"
- fmt_coverage = fmt_name + u"%6d %6d"
- if self.branches:
- header += u" Branch BrPart"
- fmt_coverage += u" %6d %6d"
- width100 = Numbers.pc_str_width()
- header += u"%*s" % (width100+4, "Cover")
- fmt_coverage += u"%%%ds%%%%" % (width100+3,)
- if self.config.show_missing:
- header += u" Missing"
- fmt_coverage += u" %s"
- rule = u"-" * len(header)
-
- column_order = dict(name=0, stmts=1, miss=2, cover=-1)
- if self.branches:
- column_order.update(dict(branch=3, brpart=4))
-
- # Write the header
- self.writeout(header)
- self.writeout(rule)
-
- # `lines` is a list of pairs, (line text, line values). The line text
- # is a string that will be printed, and line values is a tuple of
- # sortable values.
- lines = []
-
- for (fr, analysis) in self.fr_analysis:
- nums = analysis.numbers
-
- args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
- if self.branches:
- args += (nums.n_branches, nums.n_partial_branches)
- args += (nums.pc_covered_str,)
- if self.config.show_missing:
- args += (analysis.missing_formatted(branches=True),)
- text = fmt_coverage % args
- # Add numeric percent coverage so that sorting makes sense.
- args += (nums.pc_covered,)
- lines.append((text, args))
-
- # Sort the lines and write them out.
- if getattr(self.config, 'sort', None):
- sort_option = self.config.sort.lower()
- reverse = False
- if sort_option[0] == '-':
- reverse = True
- sort_option = sort_option[1:]
- elif sort_option[0] == '+':
- sort_option = sort_option[1:]
-
- position = column_order.get(sort_option)
- if position is None:
- raise CoverageException("Invalid sorting option: {!r}".format(self.config.sort))
- lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse)
-
- for line in lines:
- self.writeout(line[0])
-
- # Write a TOTAL line if we had at least one file.
- if self.total.n_files > 0:
- self.writeout(rule)
- args = ("TOTAL", self.total.n_statements, self.total.n_missing)
- if self.branches:
- args += (self.total.n_branches, self.total.n_partial_branches)
- args += (self.total.pc_covered_str,)
- if self.config.show_missing:
- args += ("",)
- self.writeout(fmt_coverage % args)
-
- # Write other final lines.
- if not self.total.n_files and not self.skipped_count:
- raise CoverageException("No data to report.")
-
- if self.config.skip_covered and self.skipped_count:
- self.writeout(
- fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '')
- )
- if self.config.skip_empty and self.empty_count:
- self.writeout(
- fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '')
- )
-
- return self.total.n_statements and self.total.pc_covered
-
- def report_one_file(self, fr, analysis):
- """Report on just one file, the callback from report()."""
- nums = analysis.numbers
- self.total += nums
-
- no_missing_lines = (nums.n_missing == 0)
- no_missing_branches = (nums.n_partial_branches == 0)
- if self.config.skip_covered and no_missing_lines and no_missing_branches:
- # Don't report on 100% files.
- self.skipped_count += 1
- elif self.config.skip_empty and nums.n_statements == 0:
- # Don't report on empty files.
- self.empty_count += 1
- else:
- self.fr_analysis.append((fr, analysis))
diff --git a/contrib/python/coverage/py2/coverage/templite.py b/contrib/python/coverage/py2/coverage/templite.py
deleted file mode 100644
index 7d4024e0af..0000000000
--- a/contrib/python/coverage/py2/coverage/templite.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""A simple Python template renderer, for a nano-subset of Django syntax.
-
-For a detailed discussion of this code, see this chapter from 500 Lines:
-http://aosabook.org/en/500L/a-template-engine.html
-
-"""
-
-# Coincidentally named the same as http://code.activestate.com/recipes/496702/
-
-import re
-
-from coverage import env
-
-
-class TempliteSyntaxError(ValueError):
- """Raised when a template has a syntax error."""
- pass
-
-
-class TempliteValueError(ValueError):
- """Raised when an expression won't evaluate in a template."""
- pass
-
-
-class CodeBuilder(object):
- """Build source code conveniently."""
-
- def __init__(self, indent=0):
- self.code = []
- self.indent_level = indent
-
- def __str__(self):
- return "".join(str(c) for c in self.code)
-
- def add_line(self, line):
- """Add a line of source to the code.
-
- Indentation and newline will be added for you, don't provide them.
-
- """
- self.code.extend([" " * self.indent_level, line, "\n"])
-
- def add_section(self):
- """Add a section, a sub-CodeBuilder."""
- section = CodeBuilder(self.indent_level)
- self.code.append(section)
- return section
-
- INDENT_STEP = 4 # PEP8 says so!
-
- def indent(self):
- """Increase the current indent for following lines."""
- self.indent_level += self.INDENT_STEP
-
- def dedent(self):
- """Decrease the current indent for following lines."""
- self.indent_level -= self.INDENT_STEP
-
- def get_globals(self):
- """Execute the code, and return a dict of globals it defines."""
- # A check that the caller really finished all the blocks they started.
- assert self.indent_level == 0
- # Get the Python source as a single string.
- python_source = str(self)
- # Execute the source, defining globals, and return them.
- global_namespace = {}
- exec(python_source, global_namespace)
- return global_namespace
-
-
-class Templite(object):
- """A simple template renderer, for a nano-subset of Django syntax.
-
- Supported constructs are extended variable access::
-
- {{var.modifier.modifier|filter|filter}}
-
- loops::
-
- {% for var in list %}...{% endfor %}
-
- and ifs::
-
- {% if var %}...{% endif %}
-
- Comments are within curly-hash markers::
-
- {# This will be ignored #}
-
- Lines between `{% joined %}` and `{% endjoined %}` will have lines stripped
- and joined. Be careful, this could join words together!
-
- Any of these constructs can have a hyphen at the end (`-}}`, `-%}`, `-#}`),
- which will collapse the whitespace following the tag.
-
- Construct a Templite with the template text, then use `render` against a
- dictionary context to create a finished string::
-
- templite = Templite('''
- <h1>Hello {{name|upper}}!</h1>
- {% for topic in topics %}
- <p>You are interested in {{topic}}.</p>
- {% endif %}
- ''',
- {'upper': str.upper},
- )
- text = templite.render({
- 'name': "Ned",
- 'topics': ['Python', 'Geometry', 'Juggling'],
- })
-
- """
- def __init__(self, text, *contexts):
- """Construct a Templite with the given `text`.
-
- `contexts` are dictionaries of values to use for future renderings.
- These are good for filters and global values.
-
- """
- self.context = {}
- for context in contexts:
- self.context.update(context)
-
- self.all_vars = set()
- self.loop_vars = set()
-
- # We construct a function in source form, then compile it and hold onto
- # it, and execute it to render the template.
- code = CodeBuilder()
-
- code.add_line("def render_function(context, do_dots):")
- code.indent()
- vars_code = code.add_section()
- code.add_line("result = []")
- code.add_line("append_result = result.append")
- code.add_line("extend_result = result.extend")
- if env.PY2:
- code.add_line("to_str = unicode")
- else:
- code.add_line("to_str = str")
-
- buffered = []
-
- def flush_output():
- """Force `buffered` to the code builder."""
- if len(buffered) == 1:
- code.add_line("append_result(%s)" % buffered[0])
- elif len(buffered) > 1:
- code.add_line("extend_result([%s])" % ", ".join(buffered))
- del buffered[:]
-
- ops_stack = []
-
- # Split the text to form a list of tokens.
- tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
-
- squash = in_joined = False
-
- for token in tokens:
- if token.startswith('{'):
- start, end = 2, -2
- squash = (token[-3] == '-')
- if squash:
- end = -3
-
- if token.startswith('{#'):
- # Comment: ignore it and move on.
- continue
- elif token.startswith('{{'):
- # An expression to evaluate.
- expr = self._expr_code(token[start:end].strip())
- buffered.append("to_str(%s)" % expr)
- else:
- # token.startswith('{%')
- # Action tag: split into words and parse further.
- flush_output()
-
- words = token[start:end].strip().split()
- if words[0] == 'if':
- # An if statement: evaluate the expression to determine if.
- if len(words) != 2:
- self._syntax_error("Don't understand if", token)
- ops_stack.append('if')
- code.add_line("if %s:" % self._expr_code(words[1]))
- code.indent()
- elif words[0] == 'for':
- # A loop: iterate over expression result.
- if len(words) != 4 or words[2] != 'in':
- self._syntax_error("Don't understand for", token)
- ops_stack.append('for')
- self._variable(words[1], self.loop_vars)
- code.add_line(
- "for c_%s in %s:" % (
- words[1],
- self._expr_code(words[3])
- )
- )
- code.indent()
- elif words[0] == 'joined':
- ops_stack.append('joined')
- in_joined = True
- elif words[0].startswith('end'):
- # Endsomething. Pop the ops stack.
- if len(words) != 1:
- self._syntax_error("Don't understand end", token)
- end_what = words[0][3:]
- if not ops_stack:
- self._syntax_error("Too many ends", token)
- start_what = ops_stack.pop()
- if start_what != end_what:
- self._syntax_error("Mismatched end tag", end_what)
- if end_what == 'joined':
- in_joined = False
- else:
- code.dedent()
- else:
- self._syntax_error("Don't understand tag", words[0])
- else:
- # Literal content. If it isn't empty, output it.
- if in_joined:
- token = re.sub(r"\s*\n\s*", "", token.strip())
- elif squash:
- token = token.lstrip()
- if token:
- buffered.append(repr(token))
-
- if ops_stack:
- self._syntax_error("Unmatched action tag", ops_stack[-1])
-
- flush_output()
-
- for var_name in self.all_vars - self.loop_vars:
- vars_code.add_line("c_%s = context[%r]" % (var_name, var_name))
-
- code.add_line('return "".join(result)')
- code.dedent()
- self._render_function = code.get_globals()['render_function']
-
- def _expr_code(self, expr):
- """Generate a Python expression for `expr`."""
- if "|" in expr:
- pipes = expr.split("|")
- code = self._expr_code(pipes[0])
- for func in pipes[1:]:
- self._variable(func, self.all_vars)
- code = "c_%s(%s)" % (func, code)
- elif "." in expr:
- dots = expr.split(".")
- code = self._expr_code(dots[0])
- args = ", ".join(repr(d) for d in dots[1:])
- code = "do_dots(%s, %s)" % (code, args)
- else:
- self._variable(expr, self.all_vars)
- code = "c_%s" % expr
- return code
-
- def _syntax_error(self, msg, thing):
- """Raise a syntax error using `msg`, and showing `thing`."""
- raise TempliteSyntaxError("%s: %r" % (msg, thing))
-
- def _variable(self, name, vars_set):
- """Track that `name` is used as a variable.
-
- Adds the name to `vars_set`, a set of variable names.
-
- Raises an syntax error if `name` is not a valid name.
-
- """
- if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name):
- self._syntax_error("Not a valid name", name)
- vars_set.add(name)
-
- def render(self, context=None):
- """Render this template by applying it to `context`.
-
- `context` is a dictionary of values to use in this rendering.
-
- """
- # Make the complete context we'll use.
- render_context = dict(self.context)
- if context:
- render_context.update(context)
- return self._render_function(render_context, self._do_dots)
-
- def _do_dots(self, value, *dots):
- """Evaluate dotted expressions at run-time."""
- for dot in dots:
- try:
- value = getattr(value, dot)
- except AttributeError:
- try:
- value = value[dot]
- except (TypeError, KeyError):
- raise TempliteValueError(
- "Couldn't evaluate %r.%s" % (value, dot)
- )
- if callable(value):
- value = value()
- return value
diff --git a/contrib/python/coverage/py2/coverage/tomlconfig.py b/contrib/python/coverage/py2/coverage/tomlconfig.py
deleted file mode 100644
index 3ad581571c..0000000000
--- a/contrib/python/coverage/py2/coverage/tomlconfig.py
+++ /dev/null
@@ -1,168 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""TOML configuration support for coverage.py"""
-
-import io
-import os
-import re
-
-from coverage import env
-from coverage.backward import configparser, path_types
-from coverage.misc import CoverageException, substitute_variables
-
-# TOML support is an install-time extra option.
-try:
- import toml
-except ImportError: # pragma: not covered
- toml = None
-
-
-class TomlDecodeError(Exception):
- """An exception class that exists even when toml isn't installed."""
- pass
-
-
-class TomlConfigParser:
- """TOML file reading with the interface of HandyConfigParser."""
-
- # This class has the same interface as config.HandyConfigParser, no
- # need for docstrings.
- # pylint: disable=missing-function-docstring
-
- def __init__(self, our_file):
- self.our_file = our_file
- self.data = None
-
- def read(self, filenames):
- # RawConfigParser takes a filename or list of filenames, but we only
- # ever call this with a single filename.
- assert isinstance(filenames, path_types)
- filename = filenames
- if env.PYVERSION >= (3, 6):
- filename = os.fspath(filename)
-
- try:
- with io.open(filename, encoding='utf-8') as fp:
- toml_text = fp.read()
- except IOError:
- return []
- if toml:
- toml_text = substitute_variables(toml_text, os.environ)
- try:
- self.data = toml.loads(toml_text)
- except toml.TomlDecodeError as err:
- raise TomlDecodeError(*err.args)
- return [filename]
- else:
- has_toml = re.search(r"^\[tool\.coverage\.", toml_text, flags=re.MULTILINE)
- if self.our_file or has_toml:
- # Looks like they meant to read TOML, but we can't read it.
- msg = "Can't read {!r} without TOML support. Install with [toml] extra"
- raise CoverageException(msg.format(filename))
- return []
-
- def _get_section(self, section):
- """Get a section from the data.
-
- Arguments:
- section (str): A section name, which can be dotted.
-
- Returns:
- name (str): the actual name of the section that was found, if any,
- or None.
- data (str): the dict of data in the section, or None if not found.
-
- """
- prefixes = ["tool.coverage."]
- if self.our_file:
- prefixes.append("")
- for prefix in prefixes:
- real_section = prefix + section
- parts = real_section.split(".")
- try:
- data = self.data[parts[0]]
- for part in parts[1:]:
- data = data[part]
- except KeyError:
- continue
- break
- else:
- return None, None
- return real_section, data
-
- def _get(self, section, option):
- """Like .get, but returns the real section name and the value."""
- name, data = self._get_section(section)
- if data is None:
- raise configparser.NoSectionError(section)
- try:
- return name, data[option]
- except KeyError:
- raise configparser.NoOptionError(option, name)
-
- def has_option(self, section, option):
- _, data = self._get_section(section)
- if data is None:
- return False
- return option in data
-
- def has_section(self, section):
- name, _ = self._get_section(section)
- return name
-
- def options(self, section):
- _, data = self._get_section(section)
- if data is None:
- raise configparser.NoSectionError(section)
- return list(data.keys())
-
- def get_section(self, section):
- _, data = self._get_section(section)
- return data
-
- def get(self, section, option):
- _, value = self._get(section, option)
- return value
-
- def _check_type(self, section, option, value, type_, type_desc):
- if not isinstance(value, type_):
- raise ValueError(
- 'Option {!r} in section {!r} is not {}: {!r}'
- .format(option, section, type_desc, value)
- )
-
- def getboolean(self, section, option):
- name, value = self._get(section, option)
- self._check_type(name, option, value, bool, "a boolean")
- return value
-
- def getlist(self, section, option):
- name, values = self._get(section, option)
- self._check_type(name, option, values, list, "a list")
- return values
-
- def getregexlist(self, section, option):
- name, values = self._get(section, option)
- self._check_type(name, option, values, list, "a list")
- for value in values:
- value = value.strip()
- try:
- re.compile(value)
- except re.error as e:
- raise CoverageException(
- "Invalid [%s].%s value %r: %s" % (name, option, value, e)
- )
- return values
-
- def getint(self, section, option):
- name, value = self._get(section, option)
- self._check_type(name, option, value, int, "an integer")
- return value
-
- def getfloat(self, section, option):
- name, value = self._get(section, option)
- if isinstance(value, int):
- value = float(value)
- self._check_type(name, option, value, float, "a float")
- return value
diff --git a/contrib/python/coverage/py2/coverage/version.py b/contrib/python/coverage/py2/coverage/version.py
deleted file mode 100644
index d141a11da3..0000000000
--- a/contrib/python/coverage/py2/coverage/version.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""The version and URL for coverage.py"""
-# This file is exec'ed in setup.py, don't import anything!
-
-# Same semantics as sys.version_info.
-version_info = (5, 5, 0, "final", 0)
-
-
-def _make_version(major, minor, micro, releaselevel, serial):
- """Create a readable version string from version_info tuple components."""
- assert releaselevel in ['alpha', 'beta', 'candidate', 'final']
- version = "%d.%d" % (major, minor)
- if micro:
- version += ".%d" % (micro,)
- if releaselevel != 'final':
- short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel]
- version += "%s%d" % (short, serial)
- return version
-
-
-def _make_url(major, minor, micro, releaselevel, serial):
- """Make the URL people should start at for this version of coverage.py."""
- url = "https://coverage.readthedocs.io"
- if releaselevel != 'final':
- # For pre-releases, use a version-specific URL.
- url += "/en/coverage-" + _make_version(major, minor, micro, releaselevel, serial)
- return url
-
-
-__version__ = _make_version(*version_info)
-__url__ = _make_url(*version_info)
diff --git a/contrib/python/coverage/py2/coverage/xmlreport.py b/contrib/python/coverage/py2/coverage/xmlreport.py
deleted file mode 100644
index 6d012ee692..0000000000
--- a/contrib/python/coverage/py2/coverage/xmlreport.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# coding: utf-8
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""XML reporting for coverage.py"""
-
-import os
-import os.path
-import sys
-import time
-import xml.dom.minidom
-
-from coverage import env
-from coverage import __url__, __version__, files
-from coverage.backward import iitems
-from coverage.misc import isolate_module
-from coverage.report import get_analysis_to_report
-
-os = isolate_module(os)
-
-
-DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd'
-
-
-def rate(hit, num):
- """Return the fraction of `hit`/`num`, as a string."""
- if num == 0:
- return "1"
- else:
- return "%.4g" % (float(hit) / num)
-
-
-class XmlReporter(object):
- """A reporter for writing Cobertura-style XML coverage results."""
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
-
- self.source_paths = set()
- if self.config.source:
- for src in self.config.source:
- if os.path.exists(src):
- if not self.config.relative_files:
- src = files.canonical_filename(src)
- self.source_paths.add(src)
- self.packages = {}
- self.xml_out = None
-
- def report(self, morfs, outfile=None):
- """Generate a Cobertura-compatible XML report for `morfs`.
-
- `morfs` is a list of modules or file names.
-
- `outfile` is a file object to write the XML to.
-
- """
- # Initial setup.
- outfile = outfile or sys.stdout
- has_arcs = self.coverage.get_data().has_arcs()
-
- # Create the DOM that will store the data.
- impl = xml.dom.minidom.getDOMImplementation()
- self.xml_out = impl.createDocument(None, "coverage", None)
-
- # Write header stuff.
- xcoverage = self.xml_out.documentElement
- xcoverage.setAttribute("version", __version__)
- xcoverage.setAttribute("timestamp", str(int(time.time()*1000)))
- xcoverage.appendChild(self.xml_out.createComment(
- " Generated by coverage.py: %s " % __url__
- ))
- xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL))
-
- # Call xml_file for each file in the data.
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.xml_file(fr, analysis, has_arcs)
-
- xsources = self.xml_out.createElement("sources")
- xcoverage.appendChild(xsources)
-
- # Populate the XML DOM with the source info.
- for path in sorted(self.source_paths):
- xsource = self.xml_out.createElement("source")
- xsources.appendChild(xsource)
- txt = self.xml_out.createTextNode(path)
- xsource.appendChild(txt)
-
- lnum_tot, lhits_tot = 0, 0
- bnum_tot, bhits_tot = 0, 0
-
- xpackages = self.xml_out.createElement("packages")
- xcoverage.appendChild(xpackages)
-
- # Populate the XML DOM with the package info.
- for pkg_name, pkg_data in sorted(iitems(self.packages)):
- class_elts, lhits, lnum, bhits, bnum = pkg_data
- xpackage = self.xml_out.createElement("package")
- xpackages.appendChild(xpackage)
- xclasses = self.xml_out.createElement("classes")
- xpackage.appendChild(xclasses)
- for _, class_elt in sorted(iitems(class_elts)):
- xclasses.appendChild(class_elt)
- xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
- xpackage.setAttribute("line-rate", rate(lhits, lnum))
- if has_arcs:
- branch_rate = rate(bhits, bnum)
- else:
- branch_rate = "0"
- xpackage.setAttribute("branch-rate", branch_rate)
- xpackage.setAttribute("complexity", "0")
-
- lnum_tot += lnum
- lhits_tot += lhits
- bnum_tot += bnum
- bhits_tot += bhits
-
- xcoverage.setAttribute("lines-valid", str(lnum_tot))
- xcoverage.setAttribute("lines-covered", str(lhits_tot))
- xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot))
- if has_arcs:
- xcoverage.setAttribute("branches-valid", str(bnum_tot))
- xcoverage.setAttribute("branches-covered", str(bhits_tot))
- xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot))
- else:
- xcoverage.setAttribute("branches-covered", "0")
- xcoverage.setAttribute("branches-valid", "0")
- xcoverage.setAttribute("branch-rate", "0")
- xcoverage.setAttribute("complexity", "0")
-
- # Write the output file.
- outfile.write(serialize_xml(self.xml_out))
-
- # Return the total percentage.
- denom = lnum_tot + bnum_tot
- if denom == 0:
- pct = 0.0
- else:
- pct = 100.0 * (lhits_tot + bhits_tot) / denom
- return pct
-
- def xml_file(self, fr, analysis, has_arcs):
- """Add to the XML report for a single file."""
-
- if self.config.skip_empty:
- if analysis.numbers.n_statements == 0:
- return
-
- # Create the 'lines' and 'package' XML elements, which
- # are populated later. Note that a package == a directory.
- filename = fr.filename.replace("\\", "/")
- for source_path in self.source_paths:
- source_path = files.canonical_filename(source_path)
- if filename.startswith(source_path.replace("\\", "/") + "/"):
- rel_name = filename[len(source_path)+1:]
- break
- else:
- rel_name = fr.relative_filename()
- self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/"))
-
- dirname = os.path.dirname(rel_name) or u"."
- dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth])
- package_name = dirname.replace("/", ".")
-
- package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
-
- xclass = self.xml_out.createElement("class")
-
- xclass.appendChild(self.xml_out.createElement("methods"))
-
- xlines = self.xml_out.createElement("lines")
- xclass.appendChild(xlines)
-
- xclass.setAttribute("name", os.path.relpath(rel_name, dirname))
- xclass.setAttribute("filename", rel_name.replace("\\", "/"))
- xclass.setAttribute("complexity", "0")
-
- branch_stats = analysis.branch_stats()
- missing_branch_arcs = analysis.missing_branch_arcs()
-
- # For each statement, create an XML 'line' element.
- for line in sorted(analysis.statements):
- xline = self.xml_out.createElement("line")
- xline.setAttribute("number", str(line))
-
- # Q: can we get info about the number of times a statement is
- # executed? If so, that should be recorded here.
- xline.setAttribute("hits", str(int(line not in analysis.missing)))
-
- if has_arcs:
- if line in branch_stats:
- total, taken = branch_stats[line]
- xline.setAttribute("branch", "true")
- xline.setAttribute(
- "condition-coverage",
- "%d%% (%d/%d)" % (100*taken//total, taken, total)
- )
- if line in missing_branch_arcs:
- annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]]
- xline.setAttribute("missing-branches", ",".join(annlines))
- xlines.appendChild(xline)
-
- class_lines = len(analysis.statements)
- class_hits = class_lines - len(analysis.missing)
-
- if has_arcs:
- class_branches = sum(t for t, k in branch_stats.values())
- missing_branches = sum(t - k for t, k in branch_stats.values())
- class_br_hits = class_branches - missing_branches
- else:
- class_branches = 0.0
- class_br_hits = 0.0
-
- # Finalize the statistics that are collected in the XML DOM.
- xclass.setAttribute("line-rate", rate(class_hits, class_lines))
- if has_arcs:
- branch_rate = rate(class_br_hits, class_branches)
- else:
- branch_rate = "0"
- xclass.setAttribute("branch-rate", branch_rate)
-
- package[0][rel_name] = xclass
- package[1] += class_hits
- package[2] += class_lines
- package[3] += class_br_hits
- package[4] += class_branches
-
-
-def serialize_xml(dom):
- """Serialize a minidom node to XML."""
- out = dom.toprettyxml()
- if env.PY2:
- out = out.encode("utf8")
- return out
diff --git a/contrib/python/coverage/py2/ya.make b/contrib/python/coverage/py2/ya.make
deleted file mode 100644
index 7bff35eea3..0000000000
--- a/contrib/python/coverage/py2/ya.make
+++ /dev/null
@@ -1,98 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY2_LIBRARY()
-
-VERSION(5.5)
-
-LICENSE(Apache-2.0)
-
-PEERDIR(
- contrib/python/coverage/plugins
- library/python/resource
-)
-
-NO_COMPILER_WARNINGS()
-
-NO_LINT()
-
-NO_CHECK_IMPORTS(
- coverage.fullcoverage.encodings
-)
-
-SRCS(
- coverage/ctracer/datastack.c
- coverage/ctracer/filedisp.c
- coverage/ctracer/module.c
- coverage/ctracer/tracer.c
-)
-
-PY_REGISTER(
- coverage.tracer
-)
-
-PY_SRCS(
- TOP_LEVEL
- coverage/__init__.py
- coverage/__main__.py
- coverage/annotate.py
- coverage/backward.py
- coverage/bytecode.py
- coverage/cmdline.py
- coverage/collector.py
- coverage/config.py
- coverage/context.py
- coverage/control.py
- coverage/data.py
- coverage/debug.py
- coverage/disposition.py
- coverage/env.py
- coverage/execfile.py
- coverage/files.py
- coverage/fullcoverage/encodings.py
- coverage/html.py
- coverage/inorout.py
- coverage/jsonreport.py
- coverage/misc.py
- coverage/multiproc.py
- coverage/numbits.py
- coverage/parser.py
- coverage/phystokens.py
- coverage/plugin.py
- coverage/plugin_support.py
- coverage/python.py
- coverage/pytracer.py
- coverage/report.py
- coverage/results.py
- coverage/sqldata.py
- coverage/summary.py
- coverage/templite.py
- coverage/tomlconfig.py
- coverage/version.py
- coverage/xmlreport.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/coverage/py2/
- .dist-info/METADATA
- .dist-info/entry_points.txt
- .dist-info/top_level.txt
- coverage/htmlfiles/coverage_html.js
- coverage/htmlfiles/favicon_32.png
- coverage/htmlfiles/index.html
- coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
- coverage/htmlfiles/jquery.hotkeys.js
- coverage/htmlfiles/jquery.isonscreen.js
- coverage/htmlfiles/jquery.min.js
- coverage/htmlfiles/jquery.tablesorter.min.js
- coverage/htmlfiles/keybd_closed.png
- coverage/htmlfiles/keybd_open.png
- coverage/htmlfiles/pyfile.html
- coverage/htmlfiles/style.css
- coverage/htmlfiles/style.scss
-)
-
-END()
-
-RECURSE(
- bin
-)
diff --git a/contrib/python/coverage/py3/.dist-info/METADATA b/contrib/python/coverage/py3/.dist-info/METADATA
deleted file mode 100644
index 25a6049c45..0000000000
--- a/contrib/python/coverage/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,190 +0,0 @@
-Metadata-Version: 2.1
-Name: coverage
-Version: 5.5
-Summary: Code coverage measurement for Python
-Home-page: https://github.com/nedbat/coveragepy
-Author: Ned Batchelder and 142 others
-Author-email: ned@nedbatchelder.com
-License: Apache 2.0
-Project-URL: Documentation, https://coverage.readthedocs.io
-Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi
-Project-URL: Issues, https://github.com/nedbat/coveragepy/issues
-Keywords: code coverage testing
-Platform: UNKNOWN
-Classifier: Environment :: Console
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Software Development :: Quality Assurance
-Classifier: Topic :: Software Development :: Testing
-Classifier: Development Status :: 5 - Production/Stable
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4
-Description-Content-Type: text/x-rst
-Provides-Extra: toml
-Requires-Dist: toml ; extra == 'toml'
-
-.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-===========
-Coverage.py
-===========
-
-Code coverage testing for Python.
-
-| |license| |versions| |status|
-| |test-status| |quality-status| |docs| |codecov|
-| |kit| |format| |repos| |downloads|
-| |stars| |forks| |contributors|
-| |tidelift| |twitter-coveragepy| |twitter-nedbat|
-
-Coverage.py measures code coverage, typically during test execution. It uses
-the code analysis tools and tracing hooks provided in the Python standard
-library to determine which lines are executable, and which have been executed.
-
-Coverage.py runs on many versions of Python:
-
-* CPython 2.7.
-* CPython 3.5 through 3.10 alpha.
-* PyPy2 7.3.3 and PyPy3 7.3.3.
-
-Documentation is on `Read the Docs`_. Code repository and issue tracker are on
-`GitHub`_.
-
-.. _Read the Docs: https://coverage.readthedocs.io/
-.. _GitHub: https://github.com/nedbat/coveragepy
-
-
-**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
-dropped support for Python 2.6, 3.3 and 3.4.
-
-
-For Enterprise
---------------
-
-.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
- :alt: Tidelift
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
-
-.. list-table::
- :widths: 10 100
-
- * - |tideliftlogo|
- - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
- Coverage and thousands of other packages are working with
- Tidelift to deliver one enterprise subscription that covers all of the open
- source you use. If you want the flexibility of open source and the confidence
- of commercial-grade software, this is for you.
- `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
-
-
-Getting Started
----------------
-
-See the `Quick Start section`_ of the docs.
-
-.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
-
-
-Change history
---------------
-
-The complete history of changes is on the `change history page`_.
-
-.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
-
-
-Contributing
-------------
-
-See the `Contributing section`_ of the docs.
-
-.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
-
-
-Security
---------
-
-To report a security vulnerability, please use the `Tidelift security
-contact`_. Tidelift will coordinate the fix and disclosure.
-
-.. _Tidelift security contact: https://tidelift.com/security
-
-
-License
--------
-
-Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
-
-.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
-.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-
-.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
- :alt: Test suite status
-.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
- :alt: Quality check status
-.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
- :target: https://coverage.readthedocs.io/
- :alt: Documentation
-.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
- :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
- :alt: Requirements status
-.. |kit| image:: https://badge.fury.io/py/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: PyPI status
-.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Kit format
-.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Weekly PyPI downloads
-.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
- :target: https://pypi.org/project/coverage/
- :alt: Python versions supported
-.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Package stability
-.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: License
-.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
- :target: https://codecov.io/github/nedbat/coveragepy?branch=master
- :alt: Coverage!
-.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
- :target: https://repology.org/metapackage/python:coverage/versions
- :alt: Packaging status
-.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
- :alt: Tidelift
-.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/stargazers
- :alt: Github stars
-.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/network/members
- :alt: Github forks
-.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/graphs/contributors
- :alt: Contributors
-.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/coveragepy
- :alt: coverage.py on Twitter
-.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/nedbat
- :alt: nedbat on Twitter
-
-
diff --git a/contrib/python/coverage/py3/.dist-info/entry_points.txt b/contrib/python/coverage/py3/.dist-info/entry_points.txt
deleted file mode 100644
index cd083fc1ff..0000000000
--- a/contrib/python/coverage/py3/.dist-info/entry_points.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-[console_scripts]
-coverage = coverage.cmdline:main
-coverage-3.9 = coverage.cmdline:main
-coverage3 = coverage.cmdline:main
-
diff --git a/contrib/python/coverage/py3/.dist-info/top_level.txt b/contrib/python/coverage/py3/.dist-info/top_level.txt
deleted file mode 100644
index 4ebc8aea50..0000000000
--- a/contrib/python/coverage/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-coverage
diff --git a/contrib/python/coverage/py3/LICENSE.txt b/contrib/python/coverage/py3/LICENSE.txt
deleted file mode 100644
index f433b1a53f..0000000000
--- a/contrib/python/coverage/py3/LICENSE.txt
+++ /dev/null
@@ -1,177 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
diff --git a/contrib/python/coverage/py3/NOTICE.txt b/contrib/python/coverage/py3/NOTICE.txt
deleted file mode 100644
index 37ded535bf..0000000000
--- a/contrib/python/coverage/py3/NOTICE.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright 2001 Gareth Rees. All rights reserved.
-Copyright 2004-2021 Ned Batchelder. All rights reserved.
-
-Except where noted otherwise, this software is licensed under the Apache
-License, Version 2.0 (the "License"); you may not use this work except in
-compliance with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/contrib/python/coverage/py3/README.rst b/contrib/python/coverage/py3/README.rst
deleted file mode 100644
index 072f30ffeb..0000000000
--- a/contrib/python/coverage/py3/README.rst
+++ /dev/null
@@ -1,151 +0,0 @@
-.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-===========
-Coverage.py
-===========
-
-Code coverage testing for Python.
-
-| |license| |versions| |status|
-| |test-status| |quality-status| |docs| |codecov|
-| |kit| |format| |repos| |downloads|
-| |stars| |forks| |contributors|
-| |tidelift| |twitter-coveragepy| |twitter-nedbat|
-
-Coverage.py measures code coverage, typically during test execution. It uses
-the code analysis tools and tracing hooks provided in the Python standard
-library to determine which lines are executable, and which have been executed.
-
-Coverage.py runs on many versions of Python:
-
-* CPython 2.7.
-* CPython 3.5 through 3.10 alpha.
-* PyPy2 7.3.3 and PyPy3 7.3.3.
-
-Documentation is on `Read the Docs`_. Code repository and issue tracker are on
-`GitHub`_.
-
-.. _Read the Docs: https://coverage.readthedocs.io/
-.. _GitHub: https://github.com/nedbat/coveragepy
-
-
-**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
-dropped support for Python 2.6, 3.3 and 3.4.
-
-
-For Enterprise
---------------
-
-.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
- :alt: Tidelift
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
-
-.. list-table::
- :widths: 10 100
-
- * - |tideliftlogo|
- - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
- Coverage and thousands of other packages are working with
- Tidelift to deliver one enterprise subscription that covers all of the open
- source you use. If you want the flexibility of open source and the confidence
- of commercial-grade software, this is for you.
- `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
-
-
-Getting Started
----------------
-
-See the `Quick Start section`_ of the docs.
-
-.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
-
-
-Change history
---------------
-
-The complete history of changes is on the `change history page`_.
-
-.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
-
-
-Contributing
-------------
-
-See the `Contributing section`_ of the docs.
-
-.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
-
-
-Security
---------
-
-To report a security vulnerability, please use the `Tidelift security
-contact`_. Tidelift will coordinate the fix and disclosure.
-
-.. _Tidelift security contact: https://tidelift.com/security
-
-
-License
--------
-
-Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
-
-.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
-.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-
-.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
- :alt: Test suite status
-.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
- :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
- :alt: Quality check status
-.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
- :target: https://coverage.readthedocs.io/
- :alt: Documentation
-.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
- :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
- :alt: Requirements status
-.. |kit| image:: https://badge.fury.io/py/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: PyPI status
-.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Kit format
-.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Weekly PyPI downloads
-.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
- :target: https://pypi.org/project/coverage/
- :alt: Python versions supported
-.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: Package stability
-.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
- :target: https://pypi.org/project/coverage/
- :alt: License
-.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
- :target: https://codecov.io/github/nedbat/coveragepy?branch=master
- :alt: Coverage!
-.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
- :target: https://repology.org/metapackage/python:coverage/versions
- :alt: Packaging status
-.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
- :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
- :alt: Tidelift
-.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/stargazers
- :alt: Github stars
-.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/network/members
- :alt: Github forks
-.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
- :target: https://github.com/nedbat/coveragepy/graphs/contributors
- :alt: Contributors
-.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/coveragepy
- :alt: coverage.py on Twitter
-.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
- :target: https://twitter.com/nedbat
- :alt: nedbat on Twitter
diff --git a/contrib/python/coverage/py3/coverage/__init__.py b/contrib/python/coverage/py3/coverage/__init__.py
deleted file mode 100644
index 331b304b68..0000000000
--- a/contrib/python/coverage/py3/coverage/__init__.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Code coverage measurement for Python.
-
-Ned Batchelder
-https://nedbatchelder.com/code/coverage
-
-"""
-
-import sys
-
-from coverage.version import __version__, __url__, version_info
-
-from coverage.control import Coverage, process_startup
-from coverage.data import CoverageData
-from coverage.misc import CoverageException
-from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
-from coverage.pytracer import PyTracer
-
-# Backward compatibility.
-coverage = Coverage
-
-# On Windows, we encode and decode deep enough that something goes wrong and
-# the encodings.utf_8 module is loaded and then unloaded, I don't know why.
-# Adding a reference here prevents it from being unloaded. Yuk.
-import encodings.utf_8 # pylint: disable=wrong-import-position, wrong-import-order
-
-# Because of the "from coverage.control import fooey" lines at the top of the
-# file, there's an entry for coverage.coverage in sys.modules, mapped to None.
-# This makes some inspection tools (like pydoc) unable to find the class
-# coverage.coverage. So remove that entry.
-try:
- del sys.modules['coverage.coverage']
-except KeyError:
- pass
diff --git a/contrib/python/coverage/py3/coverage/__main__.py b/contrib/python/coverage/py3/coverage/__main__.py
deleted file mode 100644
index 79aa4e2b35..0000000000
--- a/contrib/python/coverage/py3/coverage/__main__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Coverage.py's main entry point."""
-
-import sys
-from coverage.cmdline import main
-sys.exit(main())
diff --git a/contrib/python/coverage/py3/coverage/annotate.py b/contrib/python/coverage/py3/coverage/annotate.py
deleted file mode 100644
index 999ab6e557..0000000000
--- a/contrib/python/coverage/py3/coverage/annotate.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Source file annotation for coverage.py."""
-
-import io
-import os
-import re
-
-from coverage.files import flat_rootname
-from coverage.misc import ensure_dir, isolate_module
-from coverage.report import get_analysis_to_report
-
-os = isolate_module(os)
-
-
-class AnnotateReporter(object):
- """Generate annotated source files showing line coverage.
-
- This reporter creates annotated copies of the measured source files. Each
- .py file is copied as a .py,cover file, with a left-hand margin annotating
- each line::
-
- > def h(x):
- - if 0: #pragma: no cover
- - pass
- > if x == 1:
- ! a = 1
- > else:
- > a = 2
-
- > h(2)
-
- Executed lines use '>', lines not executed use '!', lines excluded from
- consideration use '-'.
-
- """
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
- self.directory = None
-
- blank_re = re.compile(r"\s*(#|$)")
- else_re = re.compile(r"\s*else\s*:\s*(#|$)")
-
- def report(self, morfs, directory=None):
- """Run the report.
-
- See `coverage.report()` for arguments.
-
- """
- self.directory = directory
- self.coverage.get_data()
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.annotate_file(fr, analysis)
-
- def annotate_file(self, fr, analysis):
- """Annotate a single file.
-
- `fr` is the FileReporter for the file to annotate.
-
- """
- statements = sorted(analysis.statements)
- missing = sorted(analysis.missing)
- excluded = sorted(analysis.excluded)
-
- if self.directory:
- ensure_dir(self.directory)
- dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename()))
- if dest_file.endswith("_py"):
- dest_file = dest_file[:-3] + ".py"
- dest_file += ",cover"
- else:
- dest_file = fr.filename + ",cover"
-
- with io.open(dest_file, 'w', encoding='utf8') as dest:
- i = 0
- j = 0
- covered = True
- source = fr.source()
- for lineno, line in enumerate(source.splitlines(True), start=1):
- while i < len(statements) and statements[i] < lineno:
- i += 1
- while j < len(missing) and missing[j] < lineno:
- j += 1
- if i < len(statements) and statements[i] == lineno:
- covered = j >= len(missing) or missing[j] > lineno
- if self.blank_re.match(line):
- dest.write(u' ')
- elif self.else_re.match(line):
- # Special logic for lines containing only 'else:'.
- if i >= len(statements) and j >= len(missing):
- dest.write(u'! ')
- elif i >= len(statements) or j >= len(missing):
- dest.write(u'> ')
- elif statements[i] == missing[j]:
- dest.write(u'! ')
- else:
- dest.write(u'> ')
- elif lineno in excluded:
- dest.write(u'- ')
- elif covered:
- dest.write(u'> ')
- else:
- dest.write(u'! ')
-
- dest.write(line)
diff --git a/contrib/python/coverage/py3/coverage/backward.py b/contrib/python/coverage/py3/coverage/backward.py
deleted file mode 100644
index ac781ab96a..0000000000
--- a/contrib/python/coverage/py3/coverage/backward.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Add things to old Pythons so I can pretend they are newer."""
-
-# This file's purpose is to provide modules to be imported from here.
-# pylint: disable=unused-import
-
-import os
-import sys
-
-from datetime import datetime
-
-from coverage import env
-
-
-# Pythons 2 and 3 differ on where to get StringIO.
-try:
- from cStringIO import StringIO
-except ImportError:
- from io import StringIO
-
-# In py3, ConfigParser was renamed to the more-standard configparser.
-# But there's a py3 backport that installs "configparser" in py2, and I don't
-# want it because it has annoying deprecation warnings. So try the real py2
-# import first.
-try:
- import ConfigParser as configparser
-except ImportError:
- import configparser
-
-# What's a string called?
-try:
- string_class = basestring
-except NameError:
- string_class = str
-
-# What's a Unicode string called?
-try:
- unicode_class = unicode
-except NameError:
- unicode_class = str
-
-# range or xrange?
-try:
- range = xrange # pylint: disable=redefined-builtin
-except NameError:
- range = range
-
-try:
- from itertools import zip_longest
-except ImportError:
- from itertools import izip_longest as zip_longest
-
-# Where do we get the thread id from?
-try:
- from thread import get_ident as get_thread_id
-except ImportError:
- from threading import get_ident as get_thread_id
-
-try:
- os.PathLike
-except AttributeError:
- # This is Python 2 and 3
- path_types = (bytes, string_class, unicode_class)
-else:
- # 3.6+
- path_types = (bytes, str, os.PathLike)
-
-# shlex.quote is new, but there's an undocumented implementation in "pipes",
-# who knew!?
-try:
- from shlex import quote as shlex_quote
-except ImportError:
- # Useful function, available under a different (undocumented) name
- # in Python versions earlier than 3.3.
- from pipes import quote as shlex_quote
-
-try:
- import reprlib
-except ImportError: # pragma: not covered
- # We need this on Python 2, but in testing environments, a backport is
- # installed, so this import isn't used.
- import repr as reprlib
-
-# A function to iterate listlessly over a dict's items, and one to get the
-# items as a list.
-try:
- {}.iteritems
-except AttributeError:
- # Python 3
- def iitems(d):
- """Produce the items from dict `d`."""
- return d.items()
-
- def litems(d):
- """Return a list of items from dict `d`."""
- return list(d.items())
-else:
- # Python 2
- def iitems(d):
- """Produce the items from dict `d`."""
- return d.iteritems()
-
- def litems(d):
- """Return a list of items from dict `d`."""
- return d.items()
-
-# Getting the `next` function from an iterator is different in 2 and 3.
-try:
- iter([]).next
-except AttributeError:
- def iternext(seq):
- """Get the `next` function for iterating over `seq`."""
- return iter(seq).__next__
-else:
- def iternext(seq):
- """Get the `next` function for iterating over `seq`."""
- return iter(seq).next
-
-# Python 3.x is picky about bytes and strings, so provide methods to
-# get them right, and make them no-ops in 2.x
-if env.PY3:
- def to_bytes(s):
- """Convert string `s` to bytes."""
- return s.encode('utf8')
-
- def to_string(b):
- """Convert bytes `b` to string."""
- return b.decode('utf8')
-
- def binary_bytes(byte_values):
- """Produce a byte string with the ints from `byte_values`."""
- return bytes(byte_values)
-
- def byte_to_int(byte):
- """Turn a byte indexed from a bytes object into an int."""
- return byte
-
- def bytes_to_ints(bytes_value):
- """Turn a bytes object into a sequence of ints."""
- # In Python 3, iterating bytes gives ints.
- return bytes_value
-
-else:
- def to_bytes(s):
- """Convert string `s` to bytes (no-op in 2.x)."""
- return s
-
- def to_string(b):
- """Convert bytes `b` to string."""
- return b
-
- def binary_bytes(byte_values):
- """Produce a byte string with the ints from `byte_values`."""
- return "".join(chr(b) for b in byte_values)
-
- def byte_to_int(byte):
- """Turn a byte indexed from a bytes object into an int."""
- return ord(byte)
-
- def bytes_to_ints(bytes_value):
- """Turn a bytes object into a sequence of ints."""
- for byte in bytes_value:
- yield ord(byte)
-
-
-try:
- # In Python 2.x, the builtins were in __builtin__
- BUILTINS = sys.modules['__builtin__']
-except KeyError:
- # In Python 3.x, they're in builtins
- BUILTINS = sys.modules['builtins']
-
-
-# imp was deprecated in Python 3.3
-try:
- import importlib
- import importlib.util
- imp = None
-except ImportError:
- importlib = None
-
-# We only want to use importlib if it has everything we need.
-try:
- importlib_util_find_spec = importlib.util.find_spec
-except Exception:
- import imp
- importlib_util_find_spec = None
-
-# What is the .pyc magic number for this version of Python?
-try:
- PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER
-except AttributeError:
- PYC_MAGIC_NUMBER = imp.get_magic()
-
-
-def code_object(fn):
- """Get the code object from a function."""
- try:
- return fn.func_code
- except AttributeError:
- return fn.__code__
-
-
-try:
- from types import SimpleNamespace
-except ImportError:
- # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace
- class SimpleNamespace:
- """Python implementation of SimpleNamespace, for Python 2."""
- def __init__(self, **kwargs):
- self.__dict__.update(kwargs)
-
- def __repr__(self):
- keys = sorted(self.__dict__)
- items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
- return "{}({})".format(type(self).__name__, ", ".join(items))
-
-
-def format_local_datetime(dt):
- """Return a string with local timezone representing the date.
- If python version is lower than 3.6, the time zone is not included.
- """
- try:
- return dt.astimezone().strftime('%Y-%m-%d %H:%M %z')
- except (TypeError, ValueError):
- # Datetime.astimezone in Python 3.5 can not handle naive datetime
- return dt.strftime('%Y-%m-%d %H:%M')
-
-
-def invalidate_import_caches():
- """Invalidate any import caches that may or may not exist."""
- if importlib and hasattr(importlib, "invalidate_caches"):
- importlib.invalidate_caches()
-
-
-def import_local_file(modname, modfile=None):
- """Import a local file as a module.
-
- Opens a file in the current directory named `modname`.py, imports it
- as `modname`, and returns the module object. `modfile` is the file to
- import if it isn't in the current directory.
-
- """
- try:
- import importlib.util as importlib_util
- except ImportError:
- importlib_util = None
-
- if modfile is None:
- modfile = modname + '.py'
- if importlib_util:
- spec = importlib_util.spec_from_file_location(modname, modfile)
- mod = importlib_util.module_from_spec(spec)
- sys.modules[modname] = mod
- spec.loader.exec_module(mod)
- else:
- for suff in imp.get_suffixes(): # pragma: part covered
- if suff[0] == '.py':
- break
-
- with open(modfile, 'r') as f:
- # pylint: disable=undefined-loop-variable
- mod = imp.load_module(modname, f, modfile, suff)
-
- return mod
diff --git a/contrib/python/coverage/py3/coverage/bytecode.py b/contrib/python/coverage/py3/coverage/bytecode.py
deleted file mode 100644
index ceb18cf374..0000000000
--- a/contrib/python/coverage/py3/coverage/bytecode.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Bytecode manipulation for coverage.py"""
-
-import types
-
-
-def code_objects(code):
- """Iterate over all the code objects in `code`."""
- stack = [code]
- while stack:
- # We're going to return the code object on the stack, but first
- # push its children for later returning.
- code = stack.pop()
- for c in code.co_consts:
- if isinstance(c, types.CodeType):
- stack.append(c)
- yield code
diff --git a/contrib/python/coverage/py3/coverage/cmdline.py b/contrib/python/coverage/py3/coverage/cmdline.py
deleted file mode 100644
index 0be0cca19f..0000000000
--- a/contrib/python/coverage/py3/coverage/cmdline.py
+++ /dev/null
@@ -1,910 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Command-line support for coverage.py."""
-
-from __future__ import print_function
-
-import glob
-import optparse
-import os.path
-import shlex
-import sys
-import textwrap
-import traceback
-
-import coverage
-from coverage import Coverage
-from coverage import env
-from coverage.collector import CTracer
-from coverage.data import line_counts
-from coverage.debug import info_formatter, info_header, short_stack
-from coverage.execfile import PyRunner
-from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding
-from coverage.results import should_fail_under
-
-
-class Opts(object):
- """A namespace class for individual options we'll build parsers from."""
-
- append = optparse.make_option(
- '-a', '--append', action='store_true',
- help="Append coverage data to .coverage, otherwise it starts clean each time.",
- )
- keep = optparse.make_option(
- '', '--keep', action='store_true',
- help="Keep original coverage files, otherwise they are deleted.",
- )
- branch = optparse.make_option(
- '', '--branch', action='store_true',
- help="Measure branch coverage in addition to statement coverage.",
- )
- CONCURRENCY_CHOICES = [
- "thread", "gevent", "greenlet", "eventlet", "multiprocessing",
- ]
- concurrency = optparse.make_option(
- '', '--concurrency', action='store', metavar="LIB",
- choices=CONCURRENCY_CHOICES,
- help=(
- "Properly measure code using a concurrency library. "
- "Valid values are: %s."
- ) % ", ".join(CONCURRENCY_CHOICES),
- )
- context = optparse.make_option(
- '', '--context', action='store', metavar="LABEL",
- help="The context label to record for this coverage run.",
- )
- debug = optparse.make_option(
- '', '--debug', action='store', metavar="OPTS",
- help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
- )
- directory = optparse.make_option(
- '-d', '--directory', action='store', metavar="DIR",
- help="Write the output files to DIR.",
- )
- fail_under = optparse.make_option(
- '', '--fail-under', action='store', metavar="MIN", type="float",
- help="Exit with a status of 2 if the total coverage is less than MIN.",
- )
- help = optparse.make_option(
- '-h', '--help', action='store_true',
- help="Get help on this command.",
- )
- ignore_errors = optparse.make_option(
- '-i', '--ignore-errors', action='store_true',
- help="Ignore errors while reading source files.",
- )
- include = optparse.make_option(
- '', '--include', action='store',
- metavar="PAT1,PAT2,...",
- help=(
- "Include only files whose paths match one of these patterns. "
- "Accepts shell-style wildcards, which must be quoted."
- ),
- )
- pylib = optparse.make_option(
- '-L', '--pylib', action='store_true',
- help=(
- "Measure coverage even inside the Python installed library, "
- "which isn't done by default."
- ),
- )
- sort = optparse.make_option(
- '--sort', action='store', metavar='COLUMN',
- help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. "
- "Default is name."
- )
- show_missing = optparse.make_option(
- '-m', '--show-missing', action='store_true',
- help="Show line numbers of statements in each module that weren't executed.",
- )
- skip_covered = optparse.make_option(
- '--skip-covered', action='store_true',
- help="Skip files with 100% coverage.",
- )
- no_skip_covered = optparse.make_option(
- '--no-skip-covered', action='store_false', dest='skip_covered',
- help="Disable --skip-covered.",
- )
- skip_empty = optparse.make_option(
- '--skip-empty', action='store_true',
- help="Skip files with no code.",
- )
- show_contexts = optparse.make_option(
- '--show-contexts', action='store_true',
- help="Show contexts for covered lines.",
- )
- omit = optparse.make_option(
- '', '--omit', action='store',
- metavar="PAT1,PAT2,...",
- help=(
- "Omit files whose paths match one of these patterns. "
- "Accepts shell-style wildcards, which must be quoted."
- ),
- )
- contexts = optparse.make_option(
- '', '--contexts', action='store',
- metavar="REGEX1,REGEX2,...",
- help=(
- "Only display data from lines covered in the given contexts. "
- "Accepts Python regexes, which must be quoted."
- ),
- )
- output_xml = optparse.make_option(
- '-o', '', action='store', dest="outfile",
- metavar="OUTFILE",
- help="Write the XML report to this file. Defaults to 'coverage.xml'",
- )
- output_json = optparse.make_option(
- '-o', '', action='store', dest="outfile",
- metavar="OUTFILE",
- help="Write the JSON report to this file. Defaults to 'coverage.json'",
- )
- json_pretty_print = optparse.make_option(
- '', '--pretty-print', action='store_true',
- help="Format the JSON for human readers.",
- )
- parallel_mode = optparse.make_option(
- '-p', '--parallel-mode', action='store_true',
- help=(
- "Append the machine name, process id and random number to the "
- ".coverage data file name to simplify collecting data from "
- "many processes."
- ),
- )
- module = optparse.make_option(
- '-m', '--module', action='store_true',
- help=(
- "<pyfile> is an importable Python module, not a script path, "
- "to be run as 'python -m' would run it."
- ),
- )
- precision = optparse.make_option(
- '', '--precision', action='store', metavar='N', type=int,
- help=(
- "Number of digits after the decimal point to display for "
- "reported coverage percentages."
- ),
- )
- rcfile = optparse.make_option(
- '', '--rcfile', action='store',
- help=(
- "Specify configuration file. "
- "By default '.coveragerc', 'setup.cfg', 'tox.ini', and "
- "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
- ),
- )
- source = optparse.make_option(
- '', '--source', action='store', metavar="SRC1,SRC2,...",
- help="A list of packages or directories of code to be measured.",
- )
- timid = optparse.make_option(
- '', '--timid', action='store_true',
- help=(
- "Use a simpler but slower trace method. Try this if you get "
- "seemingly impossible results!"
- ),
- )
- title = optparse.make_option(
- '', '--title', action='store', metavar="TITLE",
- help="A text string to use as the title on the HTML.",
- )
- version = optparse.make_option(
- '', '--version', action='store_true',
- help="Display version information and exit.",
- )
-
-
-class CoverageOptionParser(optparse.OptionParser, object):
- """Base OptionParser for coverage.py.
-
- Problems don't exit the program.
- Defaults are initialized for all options.
-
- """
-
- def __init__(self, *args, **kwargs):
- super(CoverageOptionParser, self).__init__(
- add_help_option=False, *args, **kwargs
- )
- self.set_defaults(
- action=None,
- append=None,
- branch=None,
- concurrency=None,
- context=None,
- debug=None,
- directory=None,
- fail_under=None,
- help=None,
- ignore_errors=None,
- include=None,
- keep=None,
- module=None,
- omit=None,
- contexts=None,
- parallel_mode=None,
- precision=None,
- pylib=None,
- rcfile=True,
- show_missing=None,
- skip_covered=None,
- skip_empty=None,
- show_contexts=None,
- sort=None,
- source=None,
- timid=None,
- title=None,
- version=None,
- )
-
- self.disable_interspersed_args()
-
- class OptionParserError(Exception):
- """Used to stop the optparse error handler ending the process."""
- pass
-
- def parse_args_ok(self, args=None, options=None):
- """Call optparse.parse_args, but return a triple:
-
- (ok, options, args)
-
- """
- try:
- options, args = super(CoverageOptionParser, self).parse_args(args, options)
- except self.OptionParserError:
- return False, None, None
- return True, options, args
-
- def error(self, msg):
- """Override optparse.error so sys.exit doesn't get called."""
- show_help(msg)
- raise self.OptionParserError
-
-
-class GlobalOptionParser(CoverageOptionParser):
- """Command-line parser for coverage.py global option arguments."""
-
- def __init__(self):
- super(GlobalOptionParser, self).__init__()
-
- self.add_options([
- Opts.help,
- Opts.version,
- ])
-
-
-class CmdOptionParser(CoverageOptionParser):
- """Parse one of the new-style commands for coverage.py."""
-
- def __init__(self, action, options, defaults=None, usage=None, description=None):
- """Create an OptionParser for a coverage.py command.
-
- `action` is the slug to put into `options.action`.
- `options` is a list of Option's for the command.
- `defaults` is a dict of default value for options.
- `usage` is the usage string to display in help.
- `description` is the description of the command, for the help text.
-
- """
- if usage:
- usage = "%prog " + usage
- super(CmdOptionParser, self).__init__(
- usage=usage,
- description=description,
- )
- self.set_defaults(action=action, **(defaults or {}))
- self.add_options(options)
- self.cmd = action
-
- def __eq__(self, other):
- # A convenience equality, so that I can put strings in unit test
- # results, and they will compare equal to objects.
- return (other == "<CmdOptionParser:%s>" % self.cmd)
-
- __hash__ = None # This object doesn't need to be hashed.
-
- def get_prog_name(self):
- """Override of an undocumented function in optparse.OptionParser."""
- program_name = super(CmdOptionParser, self).get_prog_name()
-
- # Include the sub-command for this parser as part of the command.
- return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd)
-
-
-GLOBAL_ARGS = [
- Opts.debug,
- Opts.help,
- Opts.rcfile,
- ]
-
-CMDS = {
- 'annotate': CmdOptionParser(
- "annotate",
- [
- Opts.directory,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description=(
- "Make annotated copies of the given files, marking statements that are executed "
- "with > and statements that are missed with !."
- ),
- ),
-
- 'combine': CmdOptionParser(
- "combine",
- [
- Opts.append,
- Opts.keep,
- ] + GLOBAL_ARGS,
- usage="[options] <path1> <path2> ... <pathN>",
- description=(
- "Combine data from multiple coverage files collected "
- "with 'run -p'. The combined results are written to a single "
- "file representing the union of the data. The positional "
- "arguments are data files or directories containing data files. "
- "If no paths are provided, data files in the default data file's "
- "directory are combined."
- ),
- ),
-
- 'debug': CmdOptionParser(
- "debug", GLOBAL_ARGS,
- usage="<topic>",
- description=(
- "Display information about the internals of coverage.py, "
- "for diagnosing problems. "
- "Topics are: "
- "'data' to show a summary of the collected data; "
- "'sys' to show installation information; "
- "'config' to show the configuration; "
- "'premain' to show what is calling coverage."
- ),
- ),
-
- 'erase': CmdOptionParser(
- "erase", GLOBAL_ARGS,
- description="Erase previously collected coverage data.",
- ),
-
- 'help': CmdOptionParser(
- "help", GLOBAL_ARGS,
- usage="[command]",
- description="Describe how to use coverage.py",
- ),
-
- 'html': CmdOptionParser(
- "html",
- [
- Opts.contexts,
- Opts.directory,
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.precision,
- Opts.show_contexts,
- Opts.skip_covered,
- Opts.no_skip_covered,
- Opts.skip_empty,
- Opts.title,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description=(
- "Create an HTML report of the coverage of the files. "
- "Each file gets its own page, with the source decorated to show "
- "executed, excluded, and missed lines."
- ),
- ),
-
- 'json': CmdOptionParser(
- "json",
- [
- Opts.contexts,
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.output_json,
- Opts.json_pretty_print,
- Opts.show_contexts,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description="Generate a JSON report of coverage results."
- ),
-
- 'report': CmdOptionParser(
- "report",
- [
- Opts.contexts,
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.precision,
- Opts.sort,
- Opts.show_missing,
- Opts.skip_covered,
- Opts.no_skip_covered,
- Opts.skip_empty,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description="Report coverage statistics on modules."
- ),
-
- 'run': CmdOptionParser(
- "run",
- [
- Opts.append,
- Opts.branch,
- Opts.concurrency,
- Opts.context,
- Opts.include,
- Opts.module,
- Opts.omit,
- Opts.pylib,
- Opts.parallel_mode,
- Opts.source,
- Opts.timid,
- ] + GLOBAL_ARGS,
- usage="[options] <pyfile> [program options]",
- description="Run a Python program, measuring code execution."
- ),
-
- 'xml': CmdOptionParser(
- "xml",
- [
- Opts.fail_under,
- Opts.ignore_errors,
- Opts.include,
- Opts.omit,
- Opts.output_xml,
- Opts.skip_empty,
- ] + GLOBAL_ARGS,
- usage="[options] [modules]",
- description="Generate an XML report of coverage results."
- ),
-}
-
-
-def show_help(error=None, topic=None, parser=None):
- """Display an error message, or the named topic."""
- assert error or topic or parser
-
- program_path = sys.argv[0]
- if program_path.endswith(os.path.sep + '__main__.py'):
- # The path is the main module of a package; get that path instead.
- program_path = os.path.dirname(program_path)
- program_name = os.path.basename(program_path)
- if env.WINDOWS:
- # entry_points={'console_scripts':...} on Windows makes files
- # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
- # invoke coverage-script.py, coverage3-script.py, and
- # coverage-3.5-script.py. argv[0] is the .py file, but we want to
- # get back to the original form.
- auto_suffix = "-script.py"
- if program_name.endswith(auto_suffix):
- program_name = program_name[:-len(auto_suffix)]
-
- help_params = dict(coverage.__dict__)
- help_params['program_name'] = program_name
- if CTracer is not None:
- help_params['extension_modifier'] = 'with C extension'
- else:
- help_params['extension_modifier'] = 'without C extension'
-
- if error:
- print(error, file=sys.stderr)
- print("Use '%s help' for help." % (program_name,), file=sys.stderr)
- elif parser:
- print(parser.format_help().strip())
- print()
- else:
- help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip()
- if help_msg:
- print(help_msg.format(**help_params))
- else:
- print("Don't know topic %r" % topic)
- print("Full documentation is at {__url__}".format(**help_params))
-
-
-OK, ERR, FAIL_UNDER = 0, 1, 2
-
-
-class CoverageScript(object):
- """The command-line interface to coverage.py."""
-
- def __init__(self):
- self.global_option = False
- self.coverage = None
-
- def command_line(self, argv):
- """The bulk of the command line interface to coverage.py.
-
- `argv` is the argument list to process.
-
- Returns 0 if all is well, 1 if something went wrong.
-
- """
- # Collect the command-line options.
- if not argv:
- show_help(topic='minimum_help')
- return OK
-
- # The command syntax we parse depends on the first argument. Global
- # switch syntax always starts with an option.
- self.global_option = argv[0].startswith('-')
- if self.global_option:
- parser = GlobalOptionParser()
- else:
- parser = CMDS.get(argv[0])
- if not parser:
- show_help("Unknown command: '%s'" % argv[0])
- return ERR
- argv = argv[1:]
-
- ok, options, args = parser.parse_args_ok(argv)
- if not ok:
- return ERR
-
- # Handle help and version.
- if self.do_help(options, args, parser):
- return OK
-
- # Listify the list options.
- source = unshell_list(options.source)
- omit = unshell_list(options.omit)
- include = unshell_list(options.include)
- debug = unshell_list(options.debug)
- contexts = unshell_list(options.contexts)
-
- # Do something.
- self.coverage = Coverage(
- data_suffix=options.parallel_mode,
- cover_pylib=options.pylib,
- timid=options.timid,
- branch=options.branch,
- config_file=options.rcfile,
- source=source,
- omit=omit,
- include=include,
- debug=debug,
- concurrency=options.concurrency,
- check_preimported=True,
- context=options.context,
- )
-
- if options.action == "debug":
- return self.do_debug(args)
-
- elif options.action == "erase":
- self.coverage.erase()
- return OK
-
- elif options.action == "run":
- return self.do_run(options, args)
-
- elif options.action == "combine":
- if options.append:
- self.coverage.load()
- data_dirs = args or None
- self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep))
- self.coverage.save()
- return OK
-
- # Remaining actions are reporting, with some common options.
- report_args = dict(
- morfs=unglob_args(args),
- ignore_errors=options.ignore_errors,
- omit=omit,
- include=include,
- contexts=contexts,
- )
-
- # We need to be able to import from the current directory, because
- # plugins may try to, for example, to read Django settings.
- sys.path.insert(0, '')
-
- self.coverage.load()
-
- total = None
- if options.action == "report":
- total = self.coverage.report(
- show_missing=options.show_missing,
- skip_covered=options.skip_covered,
- skip_empty=options.skip_empty,
- precision=options.precision,
- sort=options.sort,
- **report_args
- )
- elif options.action == "annotate":
- self.coverage.annotate(directory=options.directory, **report_args)
- elif options.action == "html":
- total = self.coverage.html_report(
- directory=options.directory,
- title=options.title,
- skip_covered=options.skip_covered,
- skip_empty=options.skip_empty,
- show_contexts=options.show_contexts,
- precision=options.precision,
- **report_args
- )
- elif options.action == "xml":
- outfile = options.outfile
- total = self.coverage.xml_report(
- outfile=outfile, skip_empty=options.skip_empty,
- **report_args
- )
- elif options.action == "json":
- outfile = options.outfile
- total = self.coverage.json_report(
- outfile=outfile,
- pretty_print=options.pretty_print,
- show_contexts=options.show_contexts,
- **report_args
- )
-
- if total is not None:
- # Apply the command line fail-under options, and then use the config
- # value, so we can get fail_under from the config file.
- if options.fail_under is not None:
- self.coverage.set_option("report:fail_under", options.fail_under)
-
- fail_under = self.coverage.get_option("report:fail_under")
- precision = self.coverage.get_option("report:precision")
- if should_fail_under(total, fail_under, precision):
- msg = "total of {total:.{p}f} is less than fail-under={fail_under:.{p}f}".format(
- total=total, fail_under=fail_under, p=precision,
- )
- print("Coverage failure:", msg)
- return FAIL_UNDER
-
- return OK
-
- def do_help(self, options, args, parser):
- """Deal with help requests.
-
- Return True if it handled the request, False if not.
-
- """
- # Handle help.
- if options.help:
- if self.global_option:
- show_help(topic='help')
- else:
- show_help(parser=parser)
- return True
-
- if options.action == "help":
- if args:
- for a in args:
- parser = CMDS.get(a)
- if parser:
- show_help(parser=parser)
- else:
- show_help(topic=a)
- else:
- show_help(topic='help')
- return True
-
- # Handle version.
- if options.version:
- show_help(topic='version')
- return True
-
- return False
-
- def do_run(self, options, args):
- """Implementation of 'coverage run'."""
-
- if not args:
- if options.module:
- # Specified -m with nothing else.
- show_help("No module specified for -m")
- return ERR
- command_line = self.coverage.get_option("run:command_line")
- if command_line is not None:
- args = shlex.split(command_line)
- if args and args[0] == "-m":
- options.module = True
- args = args[1:]
- if not args:
- show_help("Nothing to do.")
- return ERR
-
- if options.append and self.coverage.get_option("run:parallel"):
- show_help("Can't append to data files in parallel mode.")
- return ERR
-
- if options.concurrency == "multiprocessing":
- # Can't set other run-affecting command line options with
- # multiprocessing.
- for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
- # As it happens, all of these options have no default, meaning
- # they will be None if they have not been specified.
- if getattr(options, opt_name) is not None:
- show_help(
- "Options affecting multiprocessing must only be specified "
- "in a configuration file.\n"
- "Remove --{} from the command line.".format(opt_name)
- )
- return ERR
-
- runner = PyRunner(args, as_module=bool(options.module))
- runner.prepare()
-
- if options.append:
- self.coverage.load()
-
- # Run the script.
- self.coverage.start()
- code_ran = True
- try:
- runner.run()
- except NoSource:
- code_ran = False
- raise
- finally:
- self.coverage.stop()
- if code_ran:
- self.coverage.save()
-
- return OK
-
- def do_debug(self, args):
- """Implementation of 'coverage debug'."""
-
- if not args:
- show_help("What information would you like: config, data, sys, premain?")
- return ERR
-
- for info in args:
- if info == 'sys':
- sys_info = self.coverage.sys_info()
- print(info_header("sys"))
- for line in info_formatter(sys_info):
- print(" %s" % line)
- elif info == 'data':
- self.coverage.load()
- data = self.coverage.get_data()
- print(info_header("data"))
- print("path: %s" % data.data_filename())
- if data:
- print("has_arcs: %r" % data.has_arcs())
- summary = line_counts(data, fullpath=True)
- filenames = sorted(summary.keys())
- print("\n%d files:" % len(filenames))
- for f in filenames:
- line = "%s: %d lines" % (f, summary[f])
- plugin = data.file_tracer(f)
- if plugin:
- line += " [%s]" % plugin
- print(line)
- else:
- print("No data collected")
- elif info == 'config':
- print(info_header("config"))
- config_info = self.coverage.config.__dict__.items()
- for line in info_formatter(config_info):
- print(" %s" % line)
- elif info == "premain":
- print(info_header("premain"))
- print(short_stack())
- else:
- show_help("Don't know what you mean by %r" % info)
- return ERR
-
- return OK
-
-
-def unshell_list(s):
- """Turn a command-line argument into a list."""
- if not s:
- return None
- if env.WINDOWS:
- # When running coverage.py as coverage.exe, some of the behavior
- # of the shell is emulated: wildcards are expanded into a list of
- # file names. So you have to single-quote patterns on the command
- # line, but (not) helpfully, the single quotes are included in the
- # argument, so we have to strip them off here.
- s = s.strip("'")
- return s.split(',')
-
-
-def unglob_args(args):
- """Interpret shell wildcards for platforms that need it."""
- if env.WINDOWS:
- globbed = []
- for arg in args:
- if '?' in arg or '*' in arg:
- globbed.extend(glob.glob(arg))
- else:
- globbed.append(arg)
- args = globbed
- return args
-
-
-HELP_TOPICS = {
- 'help': """\
- Coverage.py, version {__version__} {extension_modifier}
- Measure, collect, and report on code coverage in Python programs.
-
- usage: {program_name} <command> [options] [args]
-
- Commands:
- annotate Annotate source files with execution information.
- combine Combine a number of data files.
- debug Display information about the internals of coverage.py
- erase Erase previously collected coverage data.
- help Get help on using coverage.py.
- html Create an HTML report.
- json Create a JSON report of coverage results.
- report Report coverage stats on modules.
- run Run a Python program and measure code execution.
- xml Create an XML report of coverage results.
-
- Use "{program_name} help <command>" for detailed help on any command.
- """,
-
- 'minimum_help': """\
- Code coverage for Python, version {__version__} {extension_modifier}. Use '{program_name} help' for help.
- """,
-
- 'version': """\
- Coverage.py, version {__version__} {extension_modifier}
- """,
-}
-
-
-def main(argv=None):
- """The main entry point to coverage.py.
-
- This is installed as the script entry point.
-
- """
- if argv is None:
- argv = sys.argv[1:]
- try:
- status = CoverageScript().command_line(argv)
- except ExceptionDuringRun as err:
- # An exception was caught while running the product code. The
- # sys.exc_info() return tuple is packed into an ExceptionDuringRun
- # exception.
- traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
- status = ERR
- except BaseCoverageException as err:
- # A controlled error inside coverage.py: print the message to the user.
- msg = err.args[0]
- if env.PY2:
- msg = msg.encode(output_encoding())
- print(msg)
- status = ERR
- except SystemExit as err:
- # The user called `sys.exit()`. Exit with their argument, if any.
- if err.args:
- status = err.args[0]
- else:
- status = None
- return status
-
-# Profiling using ox_profile. Install it from GitHub:
-# pip install git+https://github.com/emin63/ox_profile.git
-#
-# $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
-_profile = os.environ.get("COVERAGE_PROFILE", "")
-if _profile: # pragma: debugging
- from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
- original_main = main
-
- def main(argv=None): # pylint: disable=function-redefined
- """A wrapper around main that profiles."""
- profiler = SimpleLauncher.launch()
- try:
- return original_main(argv)
- finally:
- data, _ = profiler.query(re_filter='coverage', max_records=100)
- print(profiler.show(query=data, limit=100, sep='', col=''))
- profiler.cancel()
diff --git a/contrib/python/coverage/py3/coverage/collector.py b/contrib/python/coverage/py3/coverage/collector.py
deleted file mode 100644
index c42d29feec..0000000000
--- a/contrib/python/coverage/py3/coverage/collector.py
+++ /dev/null
@@ -1,455 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Raw data collector for coverage.py."""
-
-import os
-import sys
-
-from coverage import env
-from coverage.backward import litems, range # pylint: disable=redefined-builtin
-from coverage.debug import short_stack
-from coverage.disposition import FileDisposition
-from coverage.misc import CoverageException, isolate_module
-from coverage.pytracer import PyTracer
-
-os = isolate_module(os)
-
-
-try:
- # Use the C extension code when we can, for speed.
- from coverage.tracer import CTracer, CFileDisposition
-except ImportError:
- # Couldn't import the C extension, maybe it isn't built.
- if os.getenv('COVERAGE_TEST_TRACER') == 'c':
- # During testing, we use the COVERAGE_TEST_TRACER environment variable
- # to indicate that we've fiddled with the environment to test this
- # fallback code. If we thought we had a C tracer, but couldn't import
- # it, then exit quickly and clearly instead of dribbling confusing
- # errors. I'm using sys.exit here instead of an exception because an
- # exception here causes all sorts of other noise in unittest.
- sys.stderr.write("*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n")
- sys.exit(1)
- CTracer = None
-
-
-class Collector(object):
- """Collects trace data.
-
- Creates a Tracer object for each thread, since they track stack
- information. Each Tracer points to the same shared data, contributing
- traced data points.
-
- When the Collector is started, it creates a Tracer for the current thread,
- and installs a function to create Tracers for each new thread started.
- When the Collector is stopped, all active Tracers are stopped.
-
- Threads started while the Collector is stopped will never have Tracers
- associated with them.
-
- """
-
- # The stack of active Collectors. Collectors are added here when started,
- # and popped when stopped. Collectors on the stack are paused when not
- # the top, and resumed when they become the top again.
- _collectors = []
-
- # The concurrency settings we support here.
- SUPPORTED_CONCURRENCIES = {"greenlet", "eventlet", "gevent", "thread"}
-
- def __init__(
- self, should_trace, check_include, should_start_context, file_mapper,
- timid, branch, warn, concurrency,
- ):
- """Create a collector.
-
- `should_trace` is a function, taking a file name and a frame, and
- returning a `coverage.FileDisposition object`.
-
- `check_include` is a function taking a file name and a frame. It returns
- a boolean: True if the file should be traced, False if not.
-
- `should_start_context` is a function taking a frame, and returning a
- string. If the frame should be the start of a new context, the string
- is the new context. If the frame should not be the start of a new
- context, return None.
-
- `file_mapper` is a function taking a filename, and returning a Unicode
- filename. The result is the name that will be recorded in the data
- file.
-
- If `timid` is true, then a slower simpler trace function will be
- used. This is important for some environments where manipulation of
- tracing functions make the faster more sophisticated trace function not
- operate properly.
-
- If `branch` is true, then branches will be measured. This involves
- collecting data on which statements followed each other (arcs). Use
- `get_arc_data` to get the arc data.
-
- `warn` is a warning function, taking a single string message argument
- and an optional slug argument which will be a string or None, to be
- used if a warning needs to be issued.
-
- `concurrency` is a list of strings indicating the concurrency libraries
- in use. Valid values are "greenlet", "eventlet", "gevent", or "thread"
- (the default). Of these four values, only one can be supplied. Other
- values are ignored.
-
- """
- self.should_trace = should_trace
- self.check_include = check_include
- self.should_start_context = should_start_context
- self.file_mapper = file_mapper
- self.warn = warn
- self.branch = branch
- self.threading = None
- self.covdata = None
-
- self.static_context = None
-
- self.origin = short_stack()
-
- self.concur_id_func = None
- self.mapped_file_cache = {}
-
- # We can handle a few concurrency options here, but only one at a time.
- these_concurrencies = self.SUPPORTED_CONCURRENCIES.intersection(concurrency)
- if len(these_concurrencies) > 1:
- raise CoverageException("Conflicting concurrency settings: %s" % concurrency)
- self.concurrency = these_concurrencies.pop() if these_concurrencies else ''
-
- try:
- if self.concurrency == "greenlet":
- import greenlet
- self.concur_id_func = greenlet.getcurrent
- elif self.concurrency == "eventlet":
- import eventlet.greenthread # pylint: disable=import-error,useless-suppression
- self.concur_id_func = eventlet.greenthread.getcurrent
- elif self.concurrency == "gevent":
- import gevent # pylint: disable=import-error,useless-suppression
- self.concur_id_func = gevent.getcurrent
- elif self.concurrency == "thread" or not self.concurrency:
- # It's important to import threading only if we need it. If
- # it's imported early, and the program being measured uses
- # gevent, then gevent's monkey-patching won't work properly.
- import threading
- self.threading = threading
- else:
- raise CoverageException("Don't understand concurrency=%s" % concurrency)
- except ImportError:
- raise CoverageException(
- "Couldn't trace with concurrency=%s, the module isn't installed." % (
- self.concurrency,
- )
- )
-
- self.reset()
-
- if timid:
- # Being timid: use the simple Python trace function.
- self._trace_class = PyTracer
- else:
- # Being fast: use the C Tracer if it is available, else the Python
- # trace function.
- self._trace_class = CTracer or PyTracer
-
- if self._trace_class is CTracer:
- self.file_disposition_class = CFileDisposition
- self.supports_plugins = True
- else:
- self.file_disposition_class = FileDisposition
- self.supports_plugins = False
-
- def __repr__(self):
- return "<Collector at 0x%x: %s>" % (id(self), self.tracer_name())
-
- def use_data(self, covdata, context):
- """Use `covdata` for recording data."""
- self.covdata = covdata
- self.static_context = context
- self.covdata.set_context(self.static_context)
-
- def tracer_name(self):
- """Return the class name of the tracer we're using."""
- return self._trace_class.__name__
-
- def _clear_data(self):
- """Clear out existing data, but stay ready for more collection."""
- # We used to used self.data.clear(), but that would remove filename
- # keys and data values that were still in use higher up the stack
- # when we are called as part of switch_context.
- for d in self.data.values():
- d.clear()
-
- for tracer in self.tracers:
- tracer.reset_activity()
-
- def reset(self):
- """Clear collected data, and prepare to collect more."""
- # A dictionary mapping file names to dicts with line number keys (if not
- # branch coverage), or mapping file names to dicts with line number
- # pairs as keys (if branch coverage).
- self.data = {}
-
- # A dictionary mapping file names to file tracer plugin names that will
- # handle them.
- self.file_tracers = {}
-
- self.disabled_plugins = set()
-
- # The .should_trace_cache attribute is a cache from file names to
- # coverage.FileDisposition objects, or None. When a file is first
- # considered for tracing, a FileDisposition is obtained from
- # Coverage.should_trace. Its .trace attribute indicates whether the
- # file should be traced or not. If it should be, a plugin with dynamic
- # file names can decide not to trace it based on the dynamic file name
- # being excluded by the inclusion rules, in which case the
- # FileDisposition will be replaced by None in the cache.
- if env.PYPY:
- import __pypy__ # pylint: disable=import-error
- # Alex Gaynor said:
- # should_trace_cache is a strictly growing key: once a key is in
- # it, it never changes. Further, the keys used to access it are
- # generally constant, given sufficient context. That is to say, at
- # any given point _trace() is called, pypy is able to know the key.
- # This is because the key is determined by the physical source code
- # line, and that's invariant with the call site.
- #
- # This property of a dict with immutable keys, combined with
- # call-site-constant keys is a match for PyPy's module dict,
- # which is optimized for such workloads.
- #
- # This gives a 20% benefit on the workload described at
- # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage
- self.should_trace_cache = __pypy__.newdict("module")
- else:
- self.should_trace_cache = {}
-
- # Our active Tracers.
- self.tracers = []
-
- self._clear_data()
-
- def _start_tracer(self):
- """Start a new Tracer object, and store it in self.tracers."""
- tracer = self._trace_class()
- tracer.data = self.data
- tracer.trace_arcs = self.branch
- tracer.should_trace = self.should_trace
- tracer.should_trace_cache = self.should_trace_cache
- tracer.warn = self.warn
-
- if hasattr(tracer, 'concur_id_func'):
- tracer.concur_id_func = self.concur_id_func
- elif self.concur_id_func:
- raise CoverageException(
- "Can't support concurrency=%s with %s, only threads are supported" % (
- self.concurrency, self.tracer_name(),
- )
- )
-
- if hasattr(tracer, 'file_tracers'):
- tracer.file_tracers = self.file_tracers
- if hasattr(tracer, 'threading'):
- tracer.threading = self.threading
- if hasattr(tracer, 'check_include'):
- tracer.check_include = self.check_include
- if hasattr(tracer, 'should_start_context'):
- tracer.should_start_context = self.should_start_context
- tracer.switch_context = self.switch_context
- if hasattr(tracer, 'disable_plugin'):
- tracer.disable_plugin = self.disable_plugin
-
- fn = tracer.start()
- self.tracers.append(tracer)
-
- return fn
-
- # The trace function has to be set individually on each thread before
- # execution begins. Ironically, the only support the threading module has
- # for running code before the thread main is the tracing function. So we
- # install this as a trace function, and the first time it's called, it does
- # the real trace installation.
-
- def _installation_trace(self, frame, event, arg):
- """Called on new threads, installs the real tracer."""
- # Remove ourselves as the trace function.
- sys.settrace(None)
- # Install the real tracer.
- fn = self._start_tracer()
- # Invoke the real trace function with the current event, to be sure
- # not to lose an event.
- if fn:
- fn = fn(frame, event, arg)
- # Return the new trace function to continue tracing in this scope.
- return fn
-
- def start(self):
- """Start collecting trace information."""
- if self._collectors:
- self._collectors[-1].pause()
-
- self.tracers = []
-
- # Check to see whether we had a fullcoverage tracer installed. If so,
- # get the stack frames it stashed away for us.
- traces0 = []
- fn0 = sys.gettrace()
- if fn0:
- tracer0 = getattr(fn0, '__self__', None)
- if tracer0:
- traces0 = getattr(tracer0, 'traces', [])
-
- try:
- # Install the tracer on this thread.
- fn = self._start_tracer()
- except:
- if self._collectors:
- self._collectors[-1].resume()
- raise
-
- # If _start_tracer succeeded, then we add ourselves to the global
- # stack of collectors.
- self._collectors.append(self)
-
- # Replay all the events from fullcoverage into the new trace function.
- for args in traces0:
- (frame, event, arg), lineno = args
- try:
- fn(frame, event, arg, lineno=lineno)
- except TypeError:
- raise Exception("fullcoverage must be run with the C trace function.")
-
- # Install our installation tracer in threading, to jump-start other
- # threads.
- if self.threading:
- self.threading.settrace(self._installation_trace)
-
- def stop(self):
- """Stop collecting trace information."""
- assert self._collectors
- if self._collectors[-1] is not self:
- print("self._collectors:")
- for c in self._collectors:
- print(" {!r}\n{}".format(c, c.origin))
- assert self._collectors[-1] is self, (
- "Expected current collector to be %r, but it's %r" % (self, self._collectors[-1])
- )
-
- self.pause()
-
- # Remove this Collector from the stack, and resume the one underneath
- # (if any).
- self._collectors.pop()
- if self._collectors:
- self._collectors[-1].resume()
-
- def pause(self):
- """Pause tracing, but be prepared to `resume`."""
- for tracer in self.tracers:
- tracer.stop()
- stats = tracer.get_stats()
- if stats:
- print("\nCoverage.py tracer stats:")
- for k in sorted(stats.keys()):
- print("%20s: %s" % (k, stats[k]))
- if self.threading:
- self.threading.settrace(None)
-
- def resume(self):
- """Resume tracing after a `pause`."""
- for tracer in self.tracers:
- tracer.start()
- if self.threading:
- self.threading.settrace(self._installation_trace)
- else:
- self._start_tracer()
-
- def _activity(self):
- """Has any activity been traced?
-
- Returns a boolean, True if any trace function was invoked.
-
- """
- return any(tracer.activity() for tracer in self.tracers)
-
- def switch_context(self, new_context):
- """Switch to a new dynamic context."""
- self.flush_data()
- if self.static_context:
- context = self.static_context
- if new_context:
- context += "|" + new_context
- else:
- context = new_context
- self.covdata.set_context(context)
-
- def disable_plugin(self, disposition):
- """Disable the plugin mentioned in `disposition`."""
- file_tracer = disposition.file_tracer
- plugin = file_tracer._coverage_plugin
- plugin_name = plugin._coverage_plugin_name
- self.warn("Disabling plug-in {!r} due to previous exception".format(plugin_name))
- plugin._coverage_enabled = False
- disposition.trace = False
-
- def cached_mapped_file(self, filename):
- """A locally cached version of file names mapped through file_mapper."""
- key = (type(filename), filename)
- try:
- return self.mapped_file_cache[key]
- except KeyError:
- return self.mapped_file_cache.setdefault(key, self.file_mapper(filename))
-
- def mapped_file_dict(self, d):
- """Return a dict like d, but with keys modified by file_mapper."""
- # The call to litems() ensures that the GIL protects the dictionary
- # iterator against concurrent modifications by tracers running
- # in other threads. We try three times in case of concurrent
- # access, hoping to get a clean copy.
- runtime_err = None
- for _ in range(3):
- try:
- items = litems(d)
- except RuntimeError as ex:
- runtime_err = ex
- else:
- break
- else:
- raise runtime_err
-
- if getattr(sys, 'is_standalone_binary', False):
- # filenames should stay relative to the arcadia root, because files may not exist
- return dict((k, v) for k, v in items if v)
-
- return dict((self.cached_mapped_file(k), v) for k, v in items if v)
-
- def plugin_was_disabled(self, plugin):
- """Record that `plugin` was disabled during the run."""
- self.disabled_plugins.add(plugin._coverage_plugin_name)
-
- def flush_data(self):
- """Save the collected data to our associated `CoverageData`.
-
- Data may have also been saved along the way. This forces the
- last of the data to be saved.
-
- Returns True if there was data to save, False if not.
- """
- if not self._activity():
- return False
-
- if self.branch:
- self.covdata.add_arcs(self.mapped_file_dict(self.data))
- else:
- self.covdata.add_lines(self.mapped_file_dict(self.data))
-
- file_tracers = {
- k: v for k, v in self.file_tracers.items()
- if v not in self.disabled_plugins
- }
- self.covdata.add_file_tracers(self.mapped_file_dict(file_tracers))
-
- self._clear_data()
- return True
diff --git a/contrib/python/coverage/py3/coverage/config.py b/contrib/python/coverage/py3/coverage/config.py
deleted file mode 100644
index ceb7201b65..0000000000
--- a/contrib/python/coverage/py3/coverage/config.py
+++ /dev/null
@@ -1,605 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Config file for coverage.py"""
-
-import collections
-import copy
-import os
-import os.path
-import re
-import sys
-
-from coverage import env
-from coverage.backward import configparser, iitems, string_class
-from coverage.misc import contract, CoverageException, isolate_module
-from coverage.misc import substitute_variables
-
-from coverage.tomlconfig import TomlConfigParser, TomlDecodeError
-
-os = isolate_module(os)
-
-
-class HandyConfigParser(configparser.RawConfigParser):
- """Our specialization of ConfigParser."""
-
- def __init__(self, our_file):
- """Create the HandyConfigParser.
-
- `our_file` is True if this config file is specifically for coverage,
- False if we are examining another config file (tox.ini, setup.cfg)
- for possible settings.
- """
-
- configparser.RawConfigParser.__init__(self)
- self.section_prefixes = ["coverage:"]
- if our_file:
- self.section_prefixes.append("")
-
- def read(self, filenames, encoding=None):
- """Read a file name as UTF-8 configuration data."""
- kwargs = {}
- if env.PYVERSION >= (3, 2):
- kwargs['encoding'] = encoding or "utf-8"
- return configparser.RawConfigParser.read(self, filenames, **kwargs)
-
- def has_option(self, section, option):
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- has = configparser.RawConfigParser.has_option(self, real_section, option)
- if has:
- return has
- return False
-
- def has_section(self, section):
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- has = configparser.RawConfigParser.has_section(self, real_section)
- if has:
- return real_section
- return False
-
- def options(self, section):
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- if configparser.RawConfigParser.has_section(self, real_section):
- return configparser.RawConfigParser.options(self, real_section)
- raise configparser.NoSectionError(section)
-
- def get_section(self, section):
- """Get the contents of a section, as a dictionary."""
- d = {}
- for opt in self.options(section):
- d[opt] = self.get(section, opt)
- return d
-
- def get(self, section, option, *args, **kwargs):
- """Get a value, replacing environment variables also.
-
- The arguments are the same as `RawConfigParser.get`, but in the found
- value, ``$WORD`` or ``${WORD}`` are replaced by the value of the
- environment variable ``WORD``.
-
- Returns the finished value.
-
- """
- for section_prefix in self.section_prefixes:
- real_section = section_prefix + section
- if configparser.RawConfigParser.has_option(self, real_section, option):
- break
- else:
- raise configparser.NoOptionError(option, section)
-
- v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
- v = substitute_variables(v, os.environ)
- return v
-
- def getlist(self, section, option):
- """Read a list of strings.
-
- The value of `section` and `option` is treated as a comma- and newline-
- separated list of strings. Each value is stripped of whitespace.
-
- Returns the list of strings.
-
- """
- value_list = self.get(section, option)
- values = []
- for value_line in value_list.split('\n'):
- for value in value_line.split(','):
- value = value.strip()
- if value:
- values.append(value)
- return values
-
- def getregexlist(self, section, option):
- """Read a list of full-line regexes.
-
- The value of `section` and `option` is treated as a newline-separated
- list of regexes. Each value is stripped of whitespace.
-
- Returns the list of strings.
-
- """
- line_list = self.get(section, option)
- value_list = []
- for value in line_list.splitlines():
- value = value.strip()
- try:
- re.compile(value)
- except re.error as e:
- raise CoverageException(
- "Invalid [%s].%s value %r: %s" % (section, option, value, e)
- )
- if value:
- value_list.append(value)
- return value_list
-
-
-# The default line exclusion regexes.
-DEFAULT_EXCLUDE = [
- r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)',
-]
-
-# The default partial branch regexes, to be modified by the user.
-DEFAULT_PARTIAL = [
- r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)',
-]
-
-# The default partial branch regexes, based on Python semantics.
-# These are any Python branching constructs that can't actually execute all
-# their branches.
-DEFAULT_PARTIAL_ALWAYS = [
- 'while (True|1|False|0):',
- 'if (True|1|False|0):',
-]
-
-
-class CoverageConfig(object):
- """Coverage.py configuration.
-
- The attributes of this class are the various settings that control the
- operation of coverage.py.
-
- """
- # pylint: disable=too-many-instance-attributes
-
- def __init__(self):
- """Initialize the configuration attributes to their defaults."""
- # Metadata about the config.
- # We tried to read these config files.
- self.attempted_config_files = []
- # We did read these config files, but maybe didn't find any content for us.
- self.config_files_read = []
- # The file that gave us our configuration.
- self.config_file = None
- self._config_contents = None
-
- # Defaults for [run] and [report]
- self._include = None
- self._omit = None
-
- # Defaults for [run]
- self.branch = False
- self.command_line = None
- self.concurrency = None
- self.context = None
- self.cover_pylib = False
- self.data_file = ".coverage"
- self.debug = []
- self.disable_warnings = []
- self.dynamic_context = None
- self.note = None
- self.parallel = False
- self.plugins = []
- self.relative_files = False
- self.run_include = None
- self.run_omit = None
- self.source = None
- self.source_pkgs = []
- self.timid = False
- self._crash = None
-
- # Defaults for [report]
- self.exclude_list = DEFAULT_EXCLUDE[:]
- self.fail_under = 0.0
- self.ignore_errors = False
- self.report_include = None
- self.report_omit = None
- self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
- self.partial_list = DEFAULT_PARTIAL[:]
- self.precision = 0
- self.report_contexts = None
- self.show_missing = False
- self.skip_covered = False
- self.skip_empty = False
- self.sort = None
-
- # Defaults for [html]
- self.extra_css = None
- self.html_dir = "htmlcov"
- self.html_skip_covered = None
- self.html_skip_empty = None
- self.html_title = "Coverage report"
- self.show_contexts = False
-
- # Defaults for [xml]
- self.xml_output = "coverage.xml"
- self.xml_package_depth = 99
-
- # Defaults for [json]
- self.json_output = "coverage.json"
- self.json_pretty_print = False
- self.json_show_contexts = False
-
- # Defaults for [paths]
- self.paths = collections.OrderedDict()
-
- # Options for plugins
- self.plugin_options = {}
- self.suppress_plugin_errors = True
-
- MUST_BE_LIST = [
- "debug", "concurrency", "plugins",
- "report_omit", "report_include",
- "run_omit", "run_include",
- ]
-
- def from_args(self, **kwargs):
- """Read config values from `kwargs`."""
- for k, v in iitems(kwargs):
- if v is not None:
- if k in self.MUST_BE_LIST and isinstance(v, string_class):
- v = [v]
- setattr(self, k, v)
-
- def from_resource(self, resource_name):
- assert getattr(sys, 'is_standalone_binary', False), 'You have used method by mistake in script, not binary'
- cp, self._config_contents = _load_config_from_resource(resource_name)
- return self._parse_config(cp, resource_name, True)
-
- @contract(filename=str)
- def from_file(self, filename, our_file):
- """Read configuration from a .rc file.
-
- `filename` is a file name to read.
-
- `our_file` is True if this config file is specifically for coverage,
- False if we are examining another config file (tox.ini, setup.cfg)
- for possible settings.
-
- Returns True or False, whether the file could be read, and it had some
- coverage.py settings in it.
-
- """
- _, ext = os.path.splitext(filename)
- if ext == '.toml':
- cp = TomlConfigParser(our_file)
- else:
- cp = HandyConfigParser(our_file)
-
- self.attempted_config_files.append(filename)
-
- try:
- files_read = cp.read(filename)
- except (configparser.Error, TomlDecodeError) as err:
- raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
- if not files_read:
- return False
-
- self.config_files_read.extend(map(os.path.abspath, files_read))
-
- return self._parse_config(cp, filename, our_file)
-
- def _parse_config(self, cp, filename, our_file):
- any_set = False
- try:
- for option_spec in self.CONFIG_FILE_OPTIONS:
- was_set = self._set_attr_from_config_option(cp, *option_spec)
- if was_set:
- any_set = True
- except ValueError as err:
- raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
-
- # Check that there are no unrecognized options.
- all_options = collections.defaultdict(set)
- for option_spec in self.CONFIG_FILE_OPTIONS:
- section, option = option_spec[1].split(":")
- all_options[section].add(option)
-
- for section, options in iitems(all_options):
- real_section = cp.has_section(section)
- if real_section:
- for unknown in set(cp.options(section)) - options:
- raise CoverageException(
- "Unrecognized option '[%s] %s=' in config file %s" % (
- real_section, unknown, filename
- )
- )
-
- # [paths] is special
- if cp.has_section('paths'):
- for option in cp.options('paths'):
- self.paths[option] = cp.getlist('paths', option)
- any_set = True
-
- # plugins can have options
- for plugin in self.plugins:
- if cp.has_section(plugin):
- self.plugin_options[plugin] = cp.get_section(plugin)
- any_set = True
-
- # Was this file used as a config file? If it's specifically our file,
- # then it was used. If we're piggybacking on someone else's file,
- # then it was only used if we found some settings in it.
- if our_file:
- used = True
- else:
- used = any_set
-
- if used:
- self.config_file = os.path.abspath(filename)
- if not getattr(sys, 'is_standalone_binary', False):
- with open(filename, "rb") as f:
- self._config_contents = f.read()
-
- return used
-
- def copy(self):
- """Return a copy of the configuration."""
- return copy.deepcopy(self)
-
- CONFIG_FILE_OPTIONS = [
- # These are *args for _set_attr_from_config_option:
- # (attr, where, type_="")
- #
- # attr is the attribute to set on the CoverageConfig object.
- # where is the section:name to read from the configuration file.
- # type_ is the optional type to apply, by using .getTYPE to read the
- # configuration value from the file.
-
- # [run]
- ('branch', 'run:branch', 'boolean'),
- ('command_line', 'run:command_line'),
- ('concurrency', 'run:concurrency', 'list'),
- ('context', 'run:context'),
- ('cover_pylib', 'run:cover_pylib', 'boolean'),
- ('data_file', 'run:data_file'),
- ('debug', 'run:debug', 'list'),
- ('disable_warnings', 'run:disable_warnings', 'list'),
- ('dynamic_context', 'run:dynamic_context'),
- ('note', 'run:note'),
- ('parallel', 'run:parallel', 'boolean'),
- ('plugins', 'run:plugins', 'list'),
- ('relative_files', 'run:relative_files', 'boolean'),
- ('run_include', 'run:include', 'list'),
- ('run_omit', 'run:omit', 'list'),
- ('source', 'run:source', 'list'),
- ('source_pkgs', 'run:source_pkgs', 'list'),
- ('timid', 'run:timid', 'boolean'),
- ('_crash', 'run:_crash'),
- ('suppress_plugin_errors', 'run:suppress_plugin_errors', 'boolean'),
-
- # [report]
- ('exclude_list', 'report:exclude_lines', 'regexlist'),
- ('fail_under', 'report:fail_under', 'float'),
- ('ignore_errors', 'report:ignore_errors', 'boolean'),
- ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
- ('partial_list', 'report:partial_branches', 'regexlist'),
- ('precision', 'report:precision', 'int'),
- ('report_contexts', 'report:contexts', 'list'),
- ('report_include', 'report:include', 'list'),
- ('report_omit', 'report:omit', 'list'),
- ('show_missing', 'report:show_missing', 'boolean'),
- ('skip_covered', 'report:skip_covered', 'boolean'),
- ('skip_empty', 'report:skip_empty', 'boolean'),
- ('sort', 'report:sort'),
-
- # [html]
- ('extra_css', 'html:extra_css'),
- ('html_dir', 'html:directory'),
- ('html_skip_covered', 'html:skip_covered', 'boolean'),
- ('html_skip_empty', 'html:skip_empty', 'boolean'),
- ('html_title', 'html:title'),
- ('show_contexts', 'html:show_contexts', 'boolean'),
-
- # [xml]
- ('xml_output', 'xml:output'),
- ('xml_package_depth', 'xml:package_depth', 'int'),
-
- # [json]
- ('json_output', 'json:output'),
- ('json_pretty_print', 'json:pretty_print', 'boolean'),
- ('json_show_contexts', 'json:show_contexts', 'boolean'),
- ]
-
- def _set_attr_from_config_option(self, cp, attr, where, type_=''):
- """Set an attribute on self if it exists in the ConfigParser.
-
- Returns True if the attribute was set.
-
- """
- section, option = where.split(":")
- if cp.has_option(section, option):
- method = getattr(cp, 'get' + type_)
- setattr(self, attr, method(section, option))
- return True
- return False
-
- def get_plugin_options(self, plugin):
- """Get a dictionary of options for the plugin named `plugin`."""
- return self.plugin_options.get(plugin, {})
-
- def set_option(self, option_name, value):
- """Set an option in the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with `"run:branch"`.
-
- `value` is the new value for the option.
-
- """
- # Special-cased options.
- if option_name == "paths":
- self.paths = value
- return
-
- # Check all the hard-coded options.
- for option_spec in self.CONFIG_FILE_OPTIONS:
- attr, where = option_spec[:2]
- if where == option_name:
- setattr(self, attr, value)
- return
-
- # See if it's a plugin option.
- plugin_name, _, key = option_name.partition(":")
- if key and plugin_name in self.plugins:
- self.plugin_options.setdefault(plugin_name, {})[key] = value
- return
-
- # If we get here, we didn't find the option.
- raise CoverageException("No such option: %r" % option_name)
-
- def get_option(self, option_name):
- """Get an option from the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with `"run:branch"`.
-
- Returns the value of the option.
-
- """
- # Special-cased options.
- if option_name == "paths":
- return self.paths
-
- # Check all the hard-coded options.
- for option_spec in self.CONFIG_FILE_OPTIONS:
- attr, where = option_spec[:2]
- if where == option_name:
- return getattr(self, attr)
-
- # See if it's a plugin option.
- plugin_name, _, key = option_name.partition(":")
- if key and plugin_name in self.plugins:
- return self.plugin_options.get(plugin_name, {}).get(key)
-
- # If we get here, we didn't find the option.
- raise CoverageException("No such option: %r" % option_name)
-
- def post_process_file(self, path):
- """Make final adjustments to a file path to make it usable."""
- return os.path.expanduser(path)
-
- def post_process(self):
- """Make final adjustments to settings to make them usable."""
- self.data_file = self.post_process_file(self.data_file)
- self.html_dir = self.post_process_file(self.html_dir)
- self.xml_output = self.post_process_file(self.xml_output)
- self.paths = collections.OrderedDict(
- (k, [self.post_process_file(f) for f in v])
- for k, v in self.paths.items()
- )
-
-
-def config_files_to_try(config_file):
- """What config files should we try to read?
-
- Returns a list of tuples:
- (filename, is_our_file, was_file_specified)
- """
-
- # Some API users were specifying ".coveragerc" to mean the same as
- # True, so make it so.
- if config_file == ".coveragerc":
- config_file = True
- specified_file = (config_file is not True)
- if not specified_file:
- # No file was specified. Check COVERAGE_RCFILE.
- config_file = os.environ.get('COVERAGE_RCFILE')
- if config_file:
- specified_file = True
- if not specified_file:
- # Still no file specified. Default to .coveragerc
- config_file = ".coveragerc"
- files_to_try = [
- (config_file, True, specified_file),
- ("setup.cfg", False, False),
- ("tox.ini", False, False),
- ("pyproject.toml", False, False),
- ]
- return files_to_try
-
-
-def read_coverage_config(config_file, **kwargs):
- """Read the coverage.py configuration.
-
- Arguments:
- config_file: a boolean or string, see the `Coverage` class for the
- tricky details.
- all others: keyword arguments from the `Coverage` class, used for
- setting values in the configuration.
-
- Returns:
- config:
- config is a CoverageConfig object read from the appropriate
- configuration file.
-
- """
- # Build the configuration from a number of sources:
- # 1) defaults:
- config = CoverageConfig()
-
- # 1.1 built-in config
- if getattr(sys, 'is_standalone_binary', False):
- config.from_resource("/coverage_plugins/coveragerc.txt")
-
- # 2) from a file:
- if config_file:
- files_to_try = config_files_to_try(config_file)
-
- for fname, our_file, specified_file in files_to_try:
- if getattr(sys, 'is_standalone_binary', False) and fname == "/coverage_plugins/coveragerc.txt":
- continue
- config_read = config.from_file(fname, our_file=our_file)
- if config_read:
- break
- if specified_file:
- raise CoverageException("Couldn't read '%s' as a config file" % fname)
-
- # $set_env.py: COVERAGE_DEBUG - Options for --debug.
- # 3) from environment variables:
- env_data_file = os.environ.get('COVERAGE_FILE')
- if env_data_file:
- config.data_file = env_data_file
- debugs = os.environ.get('COVERAGE_DEBUG')
- if debugs:
- config.debug.extend(d.strip() for d in debugs.split(","))
-
- # 4) from constructor arguments:
- config.from_args(**kwargs)
-
- # Once all the config has been collected, there's a little post-processing
- # to do.
- config.post_process()
-
- return config
-
-
-def _load_config_from_resource(resource_name):
- from io import StringIO
- from library.python import resource
-
- config_data = resource.find(resource_name)
- if config_data is None:
- raise IOError("No such resource: " + resource_name)
-
- config_data = config_data.decode('utf-8')
- cp = HandyConfigParser(True)
- try:
- cp.readfp(StringIO(config_data))
- except configparser.Error as err:
- raise CoverageException("Couldn't read config %s: %s" % (resource_name, err))
- return cp, config_data
diff --git a/contrib/python/coverage/py3/coverage/context.py b/contrib/python/coverage/py3/coverage/context.py
deleted file mode 100644
index ea13da21ed..0000000000
--- a/contrib/python/coverage/py3/coverage/context.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Determine contexts for coverage.py"""
-
-
-def combine_context_switchers(context_switchers):
- """Create a single context switcher from multiple switchers.
-
- `context_switchers` is a list of functions that take a frame as an
- argument and return a string to use as the new context label.
-
- Returns a function that composites `context_switchers` functions, or None
- if `context_switchers` is an empty list.
-
- When invoked, the combined switcher calls `context_switchers` one-by-one
- until a string is returned. The combined switcher returns None if all
- `context_switchers` return None.
- """
- if not context_switchers:
- return None
-
- if len(context_switchers) == 1:
- return context_switchers[0]
-
- def should_start_context(frame):
- """The combiner for multiple context switchers."""
- for switcher in context_switchers:
- new_context = switcher(frame)
- if new_context is not None:
- return new_context
- return None
-
- return should_start_context
-
-
-def should_start_context_test_function(frame):
- """Is this frame calling a test_* function?"""
- co_name = frame.f_code.co_name
- if co_name.startswith("test") or co_name == "runTest":
- return qualname_from_frame(frame)
- return None
-
-
-def qualname_from_frame(frame):
- """Get a qualified name for the code running in `frame`."""
- co = frame.f_code
- fname = co.co_name
- method = None
- if co.co_argcount and co.co_varnames[0] == "self":
- self = frame.f_locals["self"]
- method = getattr(self, fname, None)
-
- if method is None:
- func = frame.f_globals.get(fname)
- if func is None:
- return None
- return func.__module__ + '.' + fname
-
- func = getattr(method, '__func__', None)
- if func is None:
- cls = self.__class__
- return cls.__module__ + '.' + cls.__name__ + "." + fname
-
- if hasattr(func, '__qualname__'):
- qname = func.__module__ + '.' + func.__qualname__
- else:
- for cls in getattr(self.__class__, '__mro__', ()):
- f = cls.__dict__.get(fname, None)
- if f is None:
- continue
- if f is func:
- qname = cls.__module__ + '.' + cls.__name__ + "." + fname
- break
- else:
- # Support for old-style classes.
- def mro(bases):
- for base in bases:
- f = base.__dict__.get(fname, None)
- if f is func:
- return base.__module__ + '.' + base.__name__ + "." + fname
- for base in bases:
- qname = mro(base.__bases__)
- if qname is not None:
- return qname
- return None
- qname = mro([self.__class__])
- if qname is None:
- qname = func.__module__ + '.' + fname
-
- return qname
diff --git a/contrib/python/coverage/py3/coverage/control.py b/contrib/python/coverage/py3/coverage/control.py
deleted file mode 100644
index 605b50c26b..0000000000
--- a/contrib/python/coverage/py3/coverage/control.py
+++ /dev/null
@@ -1,1162 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Core control stuff for coverage.py."""
-
-import atexit
-import collections
-import contextlib
-import os
-import os.path
-import platform
-import sys
-import time
-import json
-
-from coverage import env
-from coverage.annotate import AnnotateReporter
-from coverage.backward import string_class, iitems
-from coverage.collector import Collector, CTracer
-from coverage.config import read_coverage_config
-from coverage.context import should_start_context_test_function, combine_context_switchers
-from coverage.data import CoverageData, combine_parallel_data
-from coverage.debug import DebugControl, short_stack, write_formatted_info
-from coverage.disposition import disposition_debug_msg
-from coverage.files import PathAliases, abs_file, canonical_filename, relative_filename, set_relative_directory
-from coverage.html import HtmlReporter
-from coverage.inorout import InOrOut
-from coverage.jsonreport import JsonReporter
-from coverage.misc import CoverageException, bool_or_none, join_regex
-from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module
-from coverage.plugin import FileReporter
-from coverage.plugin_support import Plugins
-from coverage.python import PythonFileReporter
-from coverage.report import render_report
-from coverage.results import Analysis, Numbers
-from coverage.summary import SummaryReporter
-from coverage.xmlreport import XmlReporter
-
-try:
- from coverage.multiproc import patch_multiprocessing
-except ImportError: # pragma: only jython
- # Jython has no multiprocessing module.
- patch_multiprocessing = None
-
-os = isolate_module(os)
-
-@contextlib.contextmanager
-def override_config(cov, **kwargs):
- """Temporarily tweak the configuration of `cov`.
-
- The arguments are applied to `cov.config` with the `from_args` method.
- At the end of the with-statement, the old configuration is restored.
- """
- original_config = cov.config
- cov.config = cov.config.copy()
- try:
- cov.config.from_args(**kwargs)
- yield
- finally:
- cov.config = original_config
-
-
-_DEFAULT_DATAFILE = DefaultValue("MISSING")
-
-class Coverage(object):
- """Programmatic access to coverage.py.
-
- To use::
-
- from coverage import Coverage
-
- cov = Coverage()
- cov.start()
- #.. call your code ..
- cov.stop()
- cov.html_report(directory='covhtml')
-
- Note: in keeping with Python custom, names starting with underscore are
- not part of the public API. They might stop working at any point. Please
- limit yourself to documented methods to avoid problems.
-
- """
-
- # The stack of started Coverage instances.
- _instances = []
-
- @classmethod
- def current(cls):
- """Get the latest started `Coverage` instance, if any.
-
- Returns: a `Coverage` instance, or None.
-
- .. versionadded:: 5.0
-
- """
- if cls._instances:
- return cls._instances[-1]
- else:
- return None
-
- def __init__(
- self, data_file=_DEFAULT_DATAFILE, data_suffix=None, cover_pylib=None,
- auto_data=False, timid=None, branch=None, config_file=True,
- source=None, source_pkgs=None, omit=None, include=None, debug=None,
- concurrency=None, check_preimported=False, context=None,
- ): # pylint: disable=too-many-arguments
- """
- Many of these arguments duplicate and override values that can be
- provided in a configuration file. Parameters that are missing here
- will use values from the config file.
-
- `data_file` is the base name of the data file to use. The config value
- defaults to ".coverage". None can be provided to prevent writing a data
- file. `data_suffix` is appended (with a dot) to `data_file` to create
- the final file name. If `data_suffix` is simply True, then a suffix is
- created with the machine and process identity included.
-
- `cover_pylib` is a boolean determining whether Python code installed
- with the Python interpreter is measured. This includes the Python
- standard library and any packages installed with the interpreter.
-
- If `auto_data` is true, then any existing data file will be read when
- coverage measurement starts, and data will be saved automatically when
- measurement stops.
-
- If `timid` is true, then a slower and simpler trace function will be
- used. This is important for some environments where manipulation of
- tracing functions breaks the faster trace function.
-
- If `branch` is true, then branch coverage will be measured in addition
- to the usual statement coverage.
-
- `config_file` determines what configuration file to read:
-
- * If it is ".coveragerc", it is interpreted as if it were True,
- for backward compatibility.
-
- * If it is a string, it is the name of the file to read. If the
- file can't be read, it is an error.
-
- * If it is True, then a few standard files names are tried
- (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for
- these files to not be found.
-
- * If it is False, then no configuration file is read.
-
- `source` is a list of file paths or package names. Only code located
- in the trees indicated by the file paths or package names will be
- measured.
-
- `source_pkgs` is a list of package names. It works the same as
- `source`, but can be used to name packages where the name can also be
- interpreted as a file path.
-
- `include` and `omit` are lists of file name patterns. Files that match
- `include` will be measured, files that match `omit` will not. Each
- will also accept a single string argument.
-
- `debug` is a list of strings indicating what debugging information is
- desired.
-
- `concurrency` is a string indicating the concurrency library being used
- in the measured code. Without this, coverage.py will get incorrect
- results if these libraries are in use. Valid strings are "greenlet",
- "eventlet", "gevent", "multiprocessing", or "thread" (the default).
- This can also be a list of these strings.
-
- If `check_preimported` is true, then when coverage is started, the
- already-imported files will be checked to see if they should be
- measured by coverage. Importing measured files before coverage is
- started can mean that code is missed.
-
- `context` is a string to use as the :ref:`static context
- <static_contexts>` label for collected data.
-
- .. versionadded:: 4.0
- The `concurrency` parameter.
-
- .. versionadded:: 4.2
- The `concurrency` parameter can now be a list of strings.
-
- .. versionadded:: 5.0
- The `check_preimported` and `context` parameters.
-
- .. versionadded:: 5.3
- The `source_pkgs` parameter.
-
- """
- # data_file=None means no disk file at all. data_file missing means
- # use the value from the config file.
- self._no_disk = data_file is None
- if data_file is _DEFAULT_DATAFILE:
- data_file = None
-
- # Build our configuration from a number of sources.
- self.config = read_coverage_config(
- config_file=config_file,
- data_file=data_file, cover_pylib=cover_pylib, timid=timid,
- branch=branch, parallel=bool_or_none(data_suffix),
- source=source, source_pkgs=source_pkgs, run_omit=omit, run_include=include, debug=debug,
- report_omit=omit, report_include=include,
- concurrency=concurrency, context=context,
- )
-
- # This is injectable by tests.
- self._debug_file = None
-
- self._auto_load = self._auto_save = auto_data
- self._data_suffix_specified = data_suffix
-
- # Is it ok for no data to be collected?
- self._warn_no_data = True
- self._warn_unimported_source = True
- self._warn_preimported_source = check_preimported
- self._no_warn_slugs = None
-
- # A record of all the warnings that have been issued.
- self._warnings = []
-
- # Other instance attributes, set later.
- self._data = self._collector = None
- self._plugins = None
- self._inorout = None
- self._data_suffix = self._run_suffix = None
- self._exclude_re = None
- self._debug = None
- self._file_mapper = None
-
- # State machine variables:
- # Have we initialized everything?
- self._inited = False
- self._inited_for_start = False
- # Have we started collecting and not stopped it?
- self._started = False
- # Should we write the debug output?
- self._should_write_debug = True
-
- # If we have sub-process measurement happening automatically, then we
- # want any explicit creation of a Coverage object to mean, this process
- # is already coverage-aware, so don't auto-measure it. By now, the
- # auto-creation of a Coverage object has already happened. But we can
- # find it and tell it not to save its data.
- if not env.METACOV:
- _prevent_sub_process_measurement()
-
- # Store constructor args to reproduce Coverage object in a subprocess created via multiprocessing.Process
- self._dumped_args = json.dumps(dict(
- data_file=data_file, data_suffix=data_suffix, cover_pylib=cover_pylib,
- auto_data=auto_data, timid=timid, branch=branch, config_file=config_file,
- source=source, omit=omit, include=include, debug=debug,
- concurrency=concurrency
- ))
-
- def _init(self):
- """Set all the initial state.
-
- This is called by the public methods to initialize state. This lets us
- construct a :class:`Coverage` object, then tweak its state before this
- function is called.
-
- """
- if self._inited:
- return
-
- self._inited = True
-
- # Create and configure the debugging controller. COVERAGE_DEBUG_FILE
- # is an environment variable, the name of a file to append debug logs
- # to.
- self._debug = DebugControl(self.config.debug, self._debug_file)
-
- if "multiprocessing" in (self.config.concurrency or ()):
- # Multi-processing uses parallel for the subprocesses, so also use
- # it for the main process.
- self.config.parallel = True
-
- # _exclude_re is a dict that maps exclusion list names to compiled regexes.
- self._exclude_re = {}
-
- set_relative_directory()
-
- if getattr(sys, 'is_standalone_binary', False):
- self._file_mapper = canonical_filename
- else:
- self._file_mapper = relative_filename if self.config.relative_files else abs_file
-
- # Load plugins
- self._plugins = Plugins.load_plugins(self.config.plugins, self.config, self._debug)
-
- # Run configuring plugins.
- for plugin in self._plugins.configurers:
- # We need an object with set_option and get_option. Either self or
- # self.config will do. Choosing randomly stops people from doing
- # other things with those objects, against the public API. Yes,
- # this is a bit childish. :)
- plugin.configure([self, self.config][int(time.time()) % 2])
-
- def _post_init(self):
- """Stuff to do after everything is initialized."""
- if self._should_write_debug:
- self._should_write_debug = False
- self._write_startup_debug()
-
- # '[run] _crash' will raise an exception if the value is close by in
- # the call stack, for testing error handling.
- if self.config._crash and self.config._crash in short_stack(limit=4):
- raise Exception("Crashing because called by {}".format(self.config._crash))
-
- def _write_startup_debug(self):
- """Write out debug info at startup if needed."""
- wrote_any = False
- with self._debug.without_callers():
- if self._debug.should('config'):
- config_info = sorted(self.config.__dict__.items())
- config_info = [(k, v) for k, v in config_info if not k.startswith('_')]
- write_formatted_info(self._debug, "config", config_info)
- wrote_any = True
-
- if self._debug.should('sys'):
- write_formatted_info(self._debug, "sys", self.sys_info())
- for plugin in self._plugins:
- header = "sys: " + plugin._coverage_plugin_name
- info = plugin.sys_info()
- write_formatted_info(self._debug, header, info)
- wrote_any = True
-
- if wrote_any:
- write_formatted_info(self._debug, "end", ())
-
- def _should_trace(self, filename, frame):
- """Decide whether to trace execution in `filename`.
-
- Calls `_should_trace_internal`, and returns the FileDisposition.
-
- """
- disp = self._inorout.should_trace(filename, frame)
- if self._debug.should('trace'):
- self._debug.write(disposition_debug_msg(disp))
- return disp
-
- def _check_include_omit_etc(self, filename, frame):
- """Check a file name against the include/omit/etc, rules, verbosely.
-
- Returns a boolean: True if the file should be traced, False if not.
-
- """
- reason = self._inorout.check_include_omit_etc(filename, frame)
- if self._debug.should('trace'):
- if not reason:
- msg = "Including %r" % (filename,)
- else:
- msg = "Not including %r: %s" % (filename, reason)
- self._debug.write(msg)
-
- return not reason
-
- def _warn(self, msg, slug=None, once=False):
- """Use `msg` as a warning.
-
- For warning suppression, use `slug` as the shorthand.
-
- If `once` is true, only show this warning once (determined by the
- slug.)
-
- """
- if self._no_warn_slugs is None:
- self._no_warn_slugs = list(self.config.disable_warnings)
-
- if slug in self._no_warn_slugs:
- # Don't issue the warning
- return
-
- self._warnings.append(msg)
- if slug:
- msg = "%s (%s)" % (msg, slug)
- if self._debug.should('pid'):
- msg = "[%d] %s" % (os.getpid(), msg)
- sys.stderr.write("Coverage.py warning: %s\n" % msg)
-
- if once:
- self._no_warn_slugs.append(slug)
-
- def get_option(self, option_name):
- """Get an option from the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with `"run:branch"`.
-
- Returns the value of the option. The type depends on the option
- selected.
-
- As a special case, an `option_name` of ``"paths"`` will return an
- OrderedDict with the entire ``[paths]`` section value.
-
- .. versionadded:: 4.0
-
- """
- return self.config.get_option(option_name)
-
- def set_option(self, option_name, value):
- """Set an option in the configuration.
-
- `option_name` is a colon-separated string indicating the section and
- option name. For example, the ``branch`` option in the ``[run]``
- section of the config file would be indicated with ``"run:branch"``.
-
- `value` is the new value for the option. This should be an
- appropriate Python value. For example, use True for booleans, not the
- string ``"True"``.
-
- As an example, calling::
-
- cov.set_option("run:branch", True)
-
- has the same effect as this configuration file::
-
- [run]
- branch = True
-
- As a special case, an `option_name` of ``"paths"`` will replace the
- entire ``[paths]`` section. The value should be an OrderedDict.
-
- .. versionadded:: 4.0
-
- """
- self.config.set_option(option_name, value)
-
- def load(self):
- """Load previously-collected coverage data from the data file."""
- self._init()
- if self._collector:
- self._collector.reset()
- should_skip = self.config.parallel and not os.path.exists(self.config.data_file)
- if not should_skip:
- self._init_data(suffix=None)
- self._post_init()
- if not should_skip:
- self._data.read()
-
- def _init_for_start(self):
- """Initialization for start()"""
- # Construct the collector.
- concurrency = self.config.concurrency or ()
- if "multiprocessing" in concurrency:
- if not patch_multiprocessing:
- raise CoverageException( # pragma: only jython
- "multiprocessing is not supported on this Python"
- )
- patch_multiprocessing(rcfile=self.config.config_file, coverage_args=self._dumped_args)
-
- dycon = self.config.dynamic_context
- if not dycon or dycon == "none":
- context_switchers = []
- elif dycon == "test_function":
- context_switchers = [should_start_context_test_function]
- else:
- raise CoverageException(
- "Don't understand dynamic_context setting: {!r}".format(dycon)
- )
-
- context_switchers.extend(
- plugin.dynamic_context for plugin in self._plugins.context_switchers
- )
-
- should_start_context = combine_context_switchers(context_switchers)
-
- self._collector = Collector(
- should_trace=self._should_trace,
- check_include=self._check_include_omit_etc,
- should_start_context=should_start_context,
- file_mapper=self._file_mapper,
- timid=self.config.timid,
- branch=self.config.branch,
- warn=self._warn,
- concurrency=concurrency,
- )
-
- suffix = self._data_suffix_specified
- if suffix or self.config.parallel:
- if not isinstance(suffix, string_class):
- # if data_suffix=True, use .machinename.pid.random
- suffix = True
- else:
- suffix = None
-
- self._init_data(suffix)
-
- self._collector.use_data(self._data, self.config.context)
-
- # Early warning if we aren't going to be able to support plugins.
- if self._plugins.file_tracers and not self._collector.supports_plugins:
- self._warn(
- "Plugin file tracers (%s) aren't supported with %s" % (
- ", ".join(
- plugin._coverage_plugin_name
- for plugin in self._plugins.file_tracers
- ),
- self._collector.tracer_name(),
- )
- )
- for plugin in self._plugins.file_tracers:
- plugin._coverage_enabled = False
-
- # Create the file classifying substructure.
- self._inorout = InOrOut(
- warn=self._warn,
- debug=(self._debug if self._debug.should('trace') else None),
- )
- self._inorout.configure(self.config)
- self._inorout.plugins = self._plugins
- self._inorout.disp_class = self._collector.file_disposition_class
-
- # It's useful to write debug info after initing for start.
- self._should_write_debug = True
-
- atexit.register(self._atexit)
-
- def _init_data(self, suffix):
- """Create a data file if we don't have one yet."""
- if self._data is None:
- # Create the data file. We do this at construction time so that the
- # data file will be written into the directory where the process
- # started rather than wherever the process eventually chdir'd to.
- ensure_dir_for_file(self.config.data_file)
- self._data = CoverageData(
- basename=self.config.data_file,
- suffix=suffix,
- warn=self._warn,
- debug=self._debug,
- no_disk=self._no_disk,
- )
-
- def start(self):
- """Start measuring code coverage.
-
- Coverage measurement only occurs in functions called after
- :meth:`start` is invoked. Statements in the same scope as
- :meth:`start` won't be measured.
-
- Once you invoke :meth:`start`, you must also call :meth:`stop`
- eventually, or your process might not shut down cleanly.
-
- """
- self._init()
- if not self._inited_for_start:
- self._inited_for_start = True
- self._init_for_start()
- self._post_init()
-
- # Issue warnings for possible problems.
- self._inorout.warn_conflicting_settings()
-
- # See if we think some code that would eventually be measured has
- # already been imported.
- if self._warn_preimported_source:
- self._inorout.warn_already_imported_files()
-
- if self._auto_load:
- self.load()
-
- self._collector.start()
- self._started = True
- self._instances.append(self)
-
- def stop(self):
- """Stop measuring code coverage."""
- if self._instances:
- if self._instances[-1] is self:
- self._instances.pop()
- if self._started:
- self._collector.stop()
- self._started = False
-
- def _atexit(self):
- """Clean up on process shutdown."""
- if self._debug.should("process"):
- self._debug.write("atexit: pid: {}, instance: {!r}".format(os.getpid(), self))
- if self._started:
- self.stop()
- if self._auto_save:
- self.save()
-
- def erase(self):
- """Erase previously collected coverage data.
-
- This removes the in-memory data collected in this session as well as
- discarding the data file.
-
- """
- self._init()
- self._post_init()
- if self._collector:
- self._collector.reset()
- self._init_data(suffix=None)
- self._data.erase(parallel=self.config.parallel)
- self._data = None
- self._inited_for_start = False
-
- def switch_context(self, new_context):
- """Switch to a new dynamic context.
-
- `new_context` is a string to use as the :ref:`dynamic context
- <dynamic_contexts>` label for collected data. If a :ref:`static
- context <static_contexts>` is in use, the static and dynamic context
- labels will be joined together with a pipe character.
-
- Coverage collection must be started already.
-
- .. versionadded:: 5.0
-
- """
- if not self._started: # pragma: part started
- raise CoverageException(
- "Cannot switch context, coverage is not started"
- )
-
- if self._collector.should_start_context:
- self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True)
-
- self._collector.switch_context(new_context)
-
- def clear_exclude(self, which='exclude'):
- """Clear the exclude list."""
- self._init()
- setattr(self.config, which + "_list", [])
- self._exclude_regex_stale()
-
- def exclude(self, regex, which='exclude'):
- """Exclude source lines from execution consideration.
-
- A number of lists of regular expressions are maintained. Each list
- selects lines that are treated differently during reporting.
-
- `which` determines which list is modified. The "exclude" list selects
- lines that are not considered executable at all. The "partial" list
- indicates lines with branches that are not taken.
-
- `regex` is a regular expression. The regex is added to the specified
- list. If any of the regexes in the list is found in a line, the line
- is marked for special treatment during reporting.
-
- """
- self._init()
- excl_list = getattr(self.config, which + "_list")
- excl_list.append(regex)
- self._exclude_regex_stale()
-
- def _exclude_regex_stale(self):
- """Drop all the compiled exclusion regexes, a list was modified."""
- self._exclude_re.clear()
-
- def _exclude_regex(self, which):
- """Return a compiled regex for the given exclusion list."""
- if which not in self._exclude_re:
- excl_list = getattr(self.config, which + "_list")
- self._exclude_re[which] = join_regex(excl_list)
- return self._exclude_re[which]
-
- def get_exclude_list(self, which='exclude'):
- """Return a list of excluded regex patterns.
-
- `which` indicates which list is desired. See :meth:`exclude` for the
- lists that are available, and their meaning.
-
- """
- self._init()
- return getattr(self.config, which + "_list")
-
- def save(self):
- """Save the collected coverage data to the data file."""
- data = self.get_data()
- data.write()
-
- def combine(self, data_paths=None, strict=False, keep=False):
- """Combine together a number of similarly-named coverage data files.
-
- All coverage data files whose name starts with `data_file` (from the
- coverage() constructor) will be read, and combined together into the
- current measurements.
-
- `data_paths` is a list of files or directories from which data should
- be combined. If no list is passed, then the data files from the
- directory indicated by the current data file (probably the current
- directory) will be combined.
-
- If `strict` is true, then it is an error to attempt to combine when
- there are no data files to combine.
-
- If `keep` is true, then original input data files won't be deleted.
-
- .. versionadded:: 4.0
- The `data_paths` parameter.
-
- .. versionadded:: 4.3
- The `strict` parameter.
-
- .. versionadded: 5.5
- The `keep` parameter.
- """
- self._init()
- self._init_data(suffix=None)
- self._post_init()
- self.get_data()
-
- aliases = None
- if self.config.paths:
- aliases = PathAliases()
- for paths in self.config.paths.values():
- result = paths[0]
- for pattern in paths[1:]:
- aliases.add(pattern, result)
-
- combine_parallel_data(
- self._data,
- aliases=aliases,
- data_paths=data_paths,
- strict=strict,
- keep=keep,
- )
-
- def get_data(self):
- """Get the collected data.
-
- Also warn about various problems collecting data.
-
- Returns a :class:`coverage.CoverageData`, the collected coverage data.
-
- .. versionadded:: 4.0
-
- """
- self._init()
- self._init_data(suffix=None)
- self._post_init()
-
- for plugin in self._plugins:
- if not plugin._coverage_enabled:
- self._collector.plugin_was_disabled(plugin)
-
- if self._collector and self._collector.flush_data():
- self._post_save_work()
-
- return self._data
-
- def _post_save_work(self):
- """After saving data, look for warnings, post-work, etc.
-
- Warn about things that should have happened but didn't.
- Look for unexecuted files.
-
- """
- # If there are still entries in the source_pkgs_unmatched list,
- # then we never encountered those packages.
- if self._warn_unimported_source:
- self._inorout.warn_unimported_source()
-
- # Find out if we got any data.
- if not self._data and self._warn_no_data:
- self._warn("No data was collected.", slug="no-data-collected")
-
- # Touch all the files that could have executed, so that we can
- # mark completely unexecuted files as 0% covered.
- if self._data is not None:
- file_paths = collections.defaultdict(list)
- for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files():
- file_path = self._file_mapper(file_path)
- file_paths[plugin_name].append(file_path)
- for plugin_name, paths in file_paths.items():
- self._data.touch_files(paths, plugin_name)
-
- if self.config.note:
- self._warn("The '[run] note' setting is no longer supported.")
-
- # Backward compatibility with version 1.
- def analysis(self, morf):
- """Like `analysis2` but doesn't return excluded line numbers."""
- f, s, _, m, mf = self.analysis2(morf)
- return f, s, m, mf
-
- def analysis2(self, morf):
- """Analyze a module.
-
- `morf` is a module or a file name. It will be analyzed to determine
- its coverage statistics. The return value is a 5-tuple:
-
- * The file name for the module.
- * A list of line numbers of executable statements.
- * A list of line numbers of excluded statements.
- * A list of line numbers of statements not run (missing from
- execution).
- * A readable formatted string of the missing line numbers.
-
- The analysis uses the source file itself and the current measured
- coverage data.
-
- """
- analysis = self._analyze(morf)
- return (
- analysis.filename,
- sorted(analysis.statements),
- sorted(analysis.excluded),
- sorted(analysis.missing),
- analysis.missing_formatted(),
- )
-
- def _analyze(self, it):
- """Analyze a single morf or code unit.
-
- Returns an `Analysis` object.
-
- """
- # All reporting comes through here, so do reporting initialization.
- self._init()
- Numbers.set_precision(self.config.precision)
- self._post_init()
-
- data = self.get_data()
- if not isinstance(it, FileReporter):
- it = self._get_file_reporter(it)
-
- return Analysis(data, it, self._file_mapper)
-
- def _get_file_reporter(self, morf):
- """Get a FileReporter for a module or file name."""
- plugin = None
- file_reporter = "python"
-
- if isinstance(morf, string_class):
- if getattr(sys, 'is_standalone_binary', False):
- # Leave morf in canonical format - relative to the arcadia root
- mapped_morf = morf
- else:
- mapped_morf = self._file_mapper(morf)
- plugin_name = self._data.file_tracer(mapped_morf)
- if plugin_name:
- plugin = self._plugins.get(plugin_name)
-
- if plugin:
- file_reporter = plugin.file_reporter(mapped_morf)
- if file_reporter is None:
- raise CoverageException(
- "Plugin %r did not provide a file reporter for %r." % (
- plugin._coverage_plugin_name, morf
- )
- )
-
- if file_reporter == "python":
- file_reporter = PythonFileReporter(morf, self)
-
- return file_reporter
-
- def _get_file_reporters(self, morfs=None):
- """Get a list of FileReporters for a list of modules or file names.
-
- For each module or file name in `morfs`, find a FileReporter. Return
- the list of FileReporters.
-
- If `morfs` is a single module or file name, this returns a list of one
- FileReporter. If `morfs` is empty or None, then the list of all files
- measured is used to find the FileReporters.
-
- """
- if not morfs:
- morfs = self._data.measured_files()
-
- # Be sure we have a collection.
- if not isinstance(morfs, (list, tuple, set)):
- morfs = [morfs]
-
- file_reporters = [self._get_file_reporter(morf) for morf in morfs]
- return file_reporters
-
- def report(
- self, morfs=None, show_missing=None, ignore_errors=None,
- file=None, omit=None, include=None, skip_covered=None,
- contexts=None, skip_empty=None, precision=None, sort=None
- ):
- """Write a textual summary report to `file`.
-
- Each module in `morfs` is listed, with counts of statements, executed
- statements, missing statements, and a list of lines missed.
-
- If `show_missing` is true, then details of which lines or branches are
- missing will be included in the report. If `ignore_errors` is true,
- then a failure while reporting a single file will not stop the entire
- report.
-
- `file` is a file-like object, suitable for writing.
-
- `include` is a list of file name patterns. Files that match will be
- included in the report. Files matching `omit` will not be included in
- the report.
-
- If `skip_covered` is true, don't report on files with 100% coverage.
-
- If `skip_empty` is true, don't report on empty files (those that have
- no statements).
-
- `contexts` is a list of regular expressions. Only data from
- :ref:`dynamic contexts <dynamic_contexts>` that match one of those
- expressions (using :func:`re.search <python:re.search>`) will be
- included in the report.
-
- `precision` is the number of digits to display after the decimal
- point for percentages.
-
- All of the arguments default to the settings read from the
- :ref:`configuration file <config>`.
-
- Returns a float, the total percentage covered.
-
- .. versionadded:: 4.0
- The `skip_covered` parameter.
-
- .. versionadded:: 5.0
- The `contexts` and `skip_empty` parameters.
-
- .. versionadded:: 5.2
- The `precision` parameter.
-
- """
- with override_config(
- self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- show_missing=show_missing, skip_covered=skip_covered,
- report_contexts=contexts, skip_empty=skip_empty, precision=precision,
- sort=sort
- ):
- reporter = SummaryReporter(self)
- return reporter.report(morfs, outfile=file)
-
- def annotate(
- self, morfs=None, directory=None, ignore_errors=None,
- omit=None, include=None, contexts=None,
- ):
- """Annotate a list of modules.
-
- Each module in `morfs` is annotated. The source is written to a new
- file, named with a ",cover" suffix, with each line prefixed with a
- marker to indicate the coverage of the line. Covered lines have ">",
- excluded lines have "-", and missing lines have "!".
-
- See :meth:`report` for other arguments.
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit,
- report_include=include, report_contexts=contexts,
- ):
- reporter = AnnotateReporter(self)
- reporter.report(morfs, directory=directory)
-
- def html_report(
- self, morfs=None, directory=None, ignore_errors=None,
- omit=None, include=None, extra_css=None, title=None,
- skip_covered=None, show_contexts=None, contexts=None,
- skip_empty=None, precision=None,
- ):
- """Generate an HTML report.
-
- The HTML is written to `directory`. The file "index.html" is the
- overview starting point, with links to more detailed pages for
- individual modules.
-
- `extra_css` is a path to a file of other CSS to apply on the page.
- It will be copied into the HTML directory.
-
- `title` is a text string (not HTML) to use as the title of the HTML
- report.
-
- See :meth:`report` for other arguments.
-
- Returns a float, the total percentage covered.
-
- .. note::
- The HTML report files are generated incrementally based on the
- source files and coverage results. If you modify the report files,
- the changes will not be considered. You should be careful about
- changing the files in the report folder.
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- html_dir=directory, extra_css=extra_css, html_title=title,
- html_skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
- html_skip_empty=skip_empty, precision=precision,
- ):
- reporter = HtmlReporter(self)
- return reporter.report(morfs)
-
- def xml_report(
- self, morfs=None, outfile=None, ignore_errors=None,
- omit=None, include=None, contexts=None, skip_empty=None,
- ):
- """Generate an XML report of coverage results.
-
- The report is compatible with Cobertura reports.
-
- Each module in `morfs` is included in the report. `outfile` is the
- path to write the file to, "-" will write to stdout.
-
- See :meth:`report` for other arguments.
-
- Returns a float, the total percentage covered.
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- xml_output=outfile, report_contexts=contexts, skip_empty=skip_empty,
- ):
- return render_report(self.config.xml_output, XmlReporter(self), morfs)
-
- def json_report(
- self, morfs=None, outfile=None, ignore_errors=None,
- omit=None, include=None, contexts=None, pretty_print=None,
- show_contexts=None
- ):
- """Generate a JSON report of coverage results.
-
- Each module in `morfs` is included in the report. `outfile` is the
- path to write the file to, "-" will write to stdout.
-
- See :meth:`report` for other arguments.
-
- Returns a float, the total percentage covered.
-
- .. versionadded:: 5.0
-
- """
- with override_config(self,
- ignore_errors=ignore_errors, report_omit=omit, report_include=include,
- json_output=outfile, report_contexts=contexts, json_pretty_print=pretty_print,
- json_show_contexts=show_contexts
- ):
- return render_report(self.config.json_output, JsonReporter(self), morfs)
-
- def sys_info(self):
- """Return a list of (key, value) pairs showing internal information."""
-
- import coverage as covmod
-
- self._init()
- self._post_init()
-
- def plugin_info(plugins):
- """Make an entry for the sys_info from a list of plug-ins."""
- entries = []
- for plugin in plugins:
- entry = plugin._coverage_plugin_name
- if not plugin._coverage_enabled:
- entry += " (disabled)"
- entries.append(entry)
- return entries
-
- info = [
- ('version', covmod.__version__),
- ('coverage', covmod.__file__),
- ('tracer', self._collector.tracer_name() if self._collector else "-none-"),
- ('CTracer', 'available' if CTracer else "unavailable"),
- ('plugins.file_tracers', plugin_info(self._plugins.file_tracers)),
- ('plugins.configurers', plugin_info(self._plugins.configurers)),
- ('plugins.context_switchers', plugin_info(self._plugins.context_switchers)),
- ('configs_attempted', self.config.attempted_config_files),
- ('configs_read', self.config.config_files_read),
- ('config_file', self.config.config_file),
- ('config_contents',
- repr(self.config._config_contents)
- if self.config._config_contents
- else '-none-'
- ),
- ('data_file', self._data.data_filename() if self._data is not None else "-none-"),
- ('python', sys.version.replace('\n', '')),
- ('platform', platform.platform()),
- ('implementation', platform.python_implementation()),
- ('executable', sys.executable),
- ('def_encoding', sys.getdefaultencoding()),
- ('fs_encoding', sys.getfilesystemencoding()),
- ('pid', os.getpid()),
- ('cwd', os.getcwd()),
- ('path', sys.path),
- ('environment', sorted(
- ("%s = %s" % (k, v))
- for k, v in iitems(os.environ)
- if any(slug in k for slug in ("COV", "PY"))
- )),
- ('command_line', " ".join(getattr(sys, 'argv', ['-none-']))),
- ]
-
- if self._inorout:
- info.extend(self._inorout.sys_info())
-
- info.extend(CoverageData.sys_info())
-
- return info
-
-
-# Mega debugging...
-# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage.
-if int(os.environ.get("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging
- from coverage.debug import decorate_methods, show_calls
-
- Coverage = decorate_methods(show_calls(show_args=True), butnot=['get_data'])(Coverage)
-
-
-def process_startup():
- """Call this at Python start-up to perhaps measure coverage.
-
- If the environment variable COVERAGE_PROCESS_START is defined, coverage
- measurement is started. The value of the variable is the config file
- to use.
-
- There are two ways to configure your Python installation to invoke this
- function when Python starts:
-
- #. Create or append to sitecustomize.py to add these lines::
-
- import coverage
- coverage.process_startup()
-
- #. Create a .pth file in your Python installation containing::
-
- import coverage; coverage.process_startup()
-
- Returns the :class:`Coverage` instance that was started, or None if it was
- not started by this call.
-
- """
- cps = os.environ.get("COVERAGE_PROCESS_START")
- if not cps:
- # No request for coverage, nothing to do.
- return None
-
- # This function can be called more than once in a process. This happens
- # because some virtualenv configurations make the same directory visible
- # twice in sys.path. This means that the .pth file will be found twice,
- # and executed twice, executing this function twice. We set a global
- # flag (an attribute on this function) to indicate that coverage.py has
- # already been started, so we can avoid doing it twice.
- #
- # https://github.com/nedbat/coveragepy/issues/340 has more details.
-
- if hasattr(process_startup, "coverage"):
- # We've annotated this function before, so we must have already
- # started coverage.py in this process. Nothing to do.
- return None
-
- cov = Coverage(config_file=cps)
- process_startup.coverage = cov
- cov._warn_no_data = False
- cov._warn_unimported_source = False
- cov._warn_preimported_source = False
- cov._auto_save = True
- cov.start()
-
- return cov
-
-
-def _prevent_sub_process_measurement():
- """Stop any subprocess auto-measurement from writing data."""
- auto_created_coverage = getattr(process_startup, "coverage", None)
- if auto_created_coverage is not None:
- auto_created_coverage._auto_save = False
diff --git a/contrib/python/coverage/py3/coverage/ctracer/datastack.c b/contrib/python/coverage/py3/coverage/ctracer/datastack.c
deleted file mode 100644
index a9cfcc2cf2..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/datastack.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#include "util.h"
-#include "datastack.h"
-
-#define STACK_DELTA 20
-
-int
-DataStack_init(Stats *pstats, DataStack *pdata_stack)
-{
- pdata_stack->depth = -1;
- pdata_stack->stack = NULL;
- pdata_stack->alloc = 0;
- return RET_OK;
-}
-
-void
-DataStack_dealloc(Stats *pstats, DataStack *pdata_stack)
-{
- int i;
-
- for (i = 0; i < pdata_stack->alloc; i++) {
- Py_XDECREF(pdata_stack->stack[i].file_data);
- }
- PyMem_Free(pdata_stack->stack);
-}
-
-int
-DataStack_grow(Stats *pstats, DataStack *pdata_stack)
-{
- pdata_stack->depth++;
- if (pdata_stack->depth >= pdata_stack->alloc) {
- /* We've outgrown our data_stack array: make it bigger. */
- int bigger = pdata_stack->alloc + STACK_DELTA;
- DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry));
- STATS( pstats->stack_reallocs++; )
- if (bigger_data_stack == NULL) {
- PyErr_NoMemory();
- pdata_stack->depth--;
- return RET_ERROR;
- }
- /* Zero the new entries. */
- memset(bigger_data_stack + pdata_stack->alloc, 0, STACK_DELTA * sizeof(DataStackEntry));
-
- pdata_stack->stack = bigger_data_stack;
- pdata_stack->alloc = bigger;
- }
- return RET_OK;
-}
diff --git a/contrib/python/coverage/py3/coverage/ctracer/datastack.h b/contrib/python/coverage/py3/coverage/ctracer/datastack.h
deleted file mode 100644
index 3b3078ba27..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/datastack.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_DATASTACK_H
-#define _COVERAGE_DATASTACK_H
-
-#include "util.h"
-#include "stats.h"
-
-/* An entry on the data stack. For each call frame, we need to record all
- * the information needed for CTracer_handle_line to operate as quickly as
- * possible.
- */
-typedef struct DataStackEntry {
- /* The current file_data dictionary. Owned. */
- PyObject * file_data;
-
- /* The disposition object for this frame. A borrowed instance of CFileDisposition. */
- PyObject * disposition;
-
- /* The FileTracer handling this frame, or None if it's Python. Borrowed. */
- PyObject * file_tracer;
-
- /* The line number of the last line recorded, for tracing arcs.
- -1 means there was no previous line, as when entering a code object.
- */
- int last_line;
-
- BOOL started_context;
-} DataStackEntry;
-
-/* A data stack is a dynamically allocated vector of DataStackEntry's. */
-typedef struct DataStack {
- int depth; /* The index of the last-used entry in stack. */
- int alloc; /* number of entries allocated at stack. */
- /* The file data at each level, or NULL if not recording. */
- DataStackEntry * stack;
-} DataStack;
-
-
-int DataStack_init(Stats * pstats, DataStack *pdata_stack);
-void DataStack_dealloc(Stats * pstats, DataStack *pdata_stack);
-int DataStack_grow(Stats * pstats, DataStack *pdata_stack);
-
-#endif /* _COVERAGE_DATASTACK_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/filedisp.c b/contrib/python/coverage/py3/coverage/ctracer/filedisp.c
deleted file mode 100644
index 47782ae090..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/filedisp.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#include "util.h"
-#include "filedisp.h"
-
-void
-CFileDisposition_dealloc(CFileDisposition *self)
-{
- Py_XDECREF(self->original_filename);
- Py_XDECREF(self->canonical_filename);
- Py_XDECREF(self->source_filename);
- Py_XDECREF(self->trace);
- Py_XDECREF(self->reason);
- Py_XDECREF(self->file_tracer);
- Py_XDECREF(self->has_dynamic_filename);
-}
-
-static PyMemberDef
-CFileDisposition_members[] = {
- { "original_filename", T_OBJECT, offsetof(CFileDisposition, original_filename), 0,
- PyDoc_STR("") },
-
- { "canonical_filename", T_OBJECT, offsetof(CFileDisposition, canonical_filename), 0,
- PyDoc_STR("") },
-
- { "source_filename", T_OBJECT, offsetof(CFileDisposition, source_filename), 0,
- PyDoc_STR("") },
-
- { "trace", T_OBJECT, offsetof(CFileDisposition, trace), 0,
- PyDoc_STR("") },
-
- { "reason", T_OBJECT, offsetof(CFileDisposition, reason), 0,
- PyDoc_STR("") },
-
- { "file_tracer", T_OBJECT, offsetof(CFileDisposition, file_tracer), 0,
- PyDoc_STR("") },
-
- { "has_dynamic_filename", T_OBJECT, offsetof(CFileDisposition, has_dynamic_filename), 0,
- PyDoc_STR("") },
-
- { NULL }
-};
-
-PyTypeObject
-CFileDispositionType = {
- MyType_HEAD_INIT
- "coverage.CFileDispositionType", /*tp_name*/
- sizeof(CFileDisposition), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)CFileDisposition_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "CFileDisposition objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- CFileDisposition_members, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
-};
diff --git a/contrib/python/coverage/py3/coverage/ctracer/filedisp.h b/contrib/python/coverage/py3/coverage/ctracer/filedisp.h
deleted file mode 100644
index 860f9a50b1..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/filedisp.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_FILEDISP_H
-#define _COVERAGE_FILEDISP_H
-
-#include "util.h"
-#include "structmember.h"
-
-typedef struct CFileDisposition {
- PyObject_HEAD
-
- PyObject * original_filename;
- PyObject * canonical_filename;
- PyObject * source_filename;
- PyObject * trace;
- PyObject * reason;
- PyObject * file_tracer;
- PyObject * has_dynamic_filename;
-} CFileDisposition;
-
-void CFileDisposition_dealloc(CFileDisposition *self);
-
-extern PyTypeObject CFileDispositionType;
-
-#endif /* _COVERAGE_FILEDISP_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/module.c b/contrib/python/coverage/py3/coverage/ctracer/module.c
deleted file mode 100644
index f308902b69..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/module.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#include "util.h"
-#include "tracer.h"
-#include "filedisp.h"
-
-/* Module definition */
-
-#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
-
-#if PY_MAJOR_VERSION >= 3
-
-static PyModuleDef
-moduledef = {
- PyModuleDef_HEAD_INIT,
- "coverage.tracer",
- MODULE_DOC,
- -1,
- NULL, /* methods */
- NULL,
- NULL, /* traverse */
- NULL, /* clear */
- NULL
-};
-
-
-PyObject *
-PyInit_tracer(void)
-{
- PyObject * mod = PyModule_Create(&moduledef);
- if (mod == NULL) {
- return NULL;
- }
-
- if (CTracer_intern_strings() < 0) {
- return NULL;
- }
-
- /* Initialize CTracer */
- CTracerType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CTracerType) < 0) {
- Py_DECREF(mod);
- return NULL;
- }
-
- Py_INCREF(&CTracerType);
- if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- return NULL;
- }
-
- /* Initialize CFileDisposition */
- CFileDispositionType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CFileDispositionType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- return NULL;
- }
-
- Py_INCREF(&CFileDispositionType);
- if (PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType) < 0) {
- Py_DECREF(mod);
- Py_DECREF(&CTracerType);
- Py_DECREF(&CFileDispositionType);
- return NULL;
- }
-
- return mod;
-}
-
-#else
-
-void
-inittracer(void)
-{
- PyObject * mod;
-
- mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
- if (mod == NULL) {
- return;
- }
-
- if (CTracer_intern_strings() < 0) {
- return;
- }
-
- /* Initialize CTracer */
- CTracerType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CTracerType) < 0) {
- return;
- }
-
- Py_INCREF(&CTracerType);
- PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
-
- /* Initialize CFileDisposition */
- CFileDispositionType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CFileDispositionType) < 0) {
- return;
- }
-
- Py_INCREF(&CFileDispositionType);
- PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType);
-}
-
-#endif /* Py3k */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/stats.h b/contrib/python/coverage/py3/coverage/ctracer/stats.h
deleted file mode 100644
index 05173369f7..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/stats.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_STATS_H
-#define _COVERAGE_STATS_H
-
-#include "util.h"
-
-#if COLLECT_STATS
-#define STATS(x) x
-#else
-#define STATS(x)
-#endif
-
-typedef struct Stats {
- unsigned int calls; /* Need at least one member, but the rest only if needed. */
-#if COLLECT_STATS
- unsigned int lines;
- unsigned int returns;
- unsigned int exceptions;
- unsigned int others;
- unsigned int files;
- unsigned int missed_returns;
- unsigned int stack_reallocs;
- unsigned int errors;
- unsigned int pycalls;
- unsigned int start_context_calls;
-#endif
-} Stats;
-
-#endif /* _COVERAGE_STATS_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/tracer.c b/contrib/python/coverage/py3/coverage/ctracer/tracer.c
deleted file mode 100644
index 3e776958cd..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/tracer.c
+++ /dev/null
@@ -1,1149 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-/* C-based Tracer for coverage.py. */
-
-#include "util.h"
-#include "datastack.h"
-#include "filedisp.h"
-#include "tracer.h"
-
-/* Python C API helpers. */
-
-static int
-pyint_as_int(PyObject * pyint, int *pint)
-{
- int the_int = MyInt_AsInt(pyint);
- if (the_int == -1 && PyErr_Occurred()) {
- return RET_ERROR;
- }
-
- *pint = the_int;
- return RET_OK;
-}
-
-
-/* Interned strings to speed GetAttr etc. */
-
-static PyObject *str_trace;
-static PyObject *str_file_tracer;
-static PyObject *str__coverage_enabled;
-static PyObject *str__coverage_plugin;
-static PyObject *str__coverage_plugin_name;
-static PyObject *str_dynamic_source_filename;
-static PyObject *str_line_number_range;
-
-int
-CTracer_intern_strings(void)
-{
- int ret = RET_ERROR;
-
-#define INTERN_STRING(v, s) \
- v = MyText_InternFromString(s); \
- if (v == NULL) { \
- goto error; \
- }
-
- INTERN_STRING(str_trace, "trace")
- INTERN_STRING(str_file_tracer, "file_tracer")
- INTERN_STRING(str__coverage_enabled, "_coverage_enabled")
- INTERN_STRING(str__coverage_plugin, "_coverage_plugin")
- INTERN_STRING(str__coverage_plugin_name, "_coverage_plugin_name")
- INTERN_STRING(str_dynamic_source_filename, "dynamic_source_filename")
- INTERN_STRING(str_line_number_range, "line_number_range")
-
- ret = RET_OK;
-
-error:
- return ret;
-}
-
-static void CTracer_disable_plugin(CTracer *self, PyObject * disposition);
-
-static int
-CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
-{
- int ret = RET_ERROR;
-
- if (DataStack_init(&self->stats, &self->data_stack) < 0) {
- goto error;
- }
-
- self->pdata_stack = &self->data_stack;
-
- self->context = Py_None;
- Py_INCREF(self->context);
-
- ret = RET_OK;
- goto ok;
-
-error:
- STATS( self->stats.errors++; )
-
-ok:
- return ret;
-}
-
-static void
-CTracer_dealloc(CTracer *self)
-{
- int i;
-
- if (self->started) {
- PyEval_SetTrace(NULL, NULL);
- }
-
- Py_XDECREF(self->should_trace);
- Py_XDECREF(self->check_include);
- Py_XDECREF(self->warn);
- Py_XDECREF(self->concur_id_func);
- Py_XDECREF(self->data);
- Py_XDECREF(self->file_tracers);
- Py_XDECREF(self->should_trace_cache);
- Py_XDECREF(self->should_start_context);
- Py_XDECREF(self->switch_context);
- Py_XDECREF(self->context);
- Py_XDECREF(self->disable_plugin);
-
- DataStack_dealloc(&self->stats, &self->data_stack);
- if (self->data_stacks) {
- for (i = 0; i < self->data_stacks_used; i++) {
- DataStack_dealloc(&self->stats, self->data_stacks + i);
- }
- PyMem_Free(self->data_stacks);
- }
-
- Py_XDECREF(self->data_stack_index);
-
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-#if TRACE_LOG
-static const char *
-indent(int n)
-{
- static const char * spaces =
- " "
- " "
- " "
- " "
- ;
- return spaces + strlen(spaces) - n*2;
-}
-
-static BOOL logging = FALSE;
-/* Set these constants to be a file substring and line number to start logging. */
-static const char * start_file = "tests/views";
-static int start_line = 27;
-
-static void
-showlog(int depth, int lineno, PyObject * filename, const char * msg)
-{
- if (logging) {
- printf("%s%3d ", indent(depth), depth);
- if (lineno) {
- printf("%4d", lineno);
- }
- else {
- printf(" ");
- }
- if (filename) {
- PyObject *ascii = MyText_AS_BYTES(filename);
- printf(" %s", MyBytes_AS_STRING(ascii));
- Py_DECREF(ascii);
- }
- if (msg) {
- printf(" %s", msg);
- }
- printf("\n");
- }
-}
-
-#define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
-#else
-#define SHOWLOG(a,b,c,d)
-#endif /* TRACE_LOG */
-
-#if WHAT_LOG
-static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
-#endif
-
-/* Record a pair of integers in self->pcur_entry->file_data. */
-static int
-CTracer_record_pair(CTracer *self, int l1, int l2)
-{
- int ret = RET_ERROR;
-
- PyObject * t = NULL;
-
- t = Py_BuildValue("(ii)", l1, l2);
- if (t == NULL) {
- goto error;
- }
-
- if (PyDict_SetItem(self->pcur_entry->file_data, t, Py_None) < 0) {
- goto error;
- }
-
- ret = RET_OK;
-
-error:
- Py_XDECREF(t);
-
- return ret;
-}
-
-/* Set self->pdata_stack to the proper data_stack to use. */
-static int
-CTracer_set_pdata_stack(CTracer *self)
-{
- int ret = RET_ERROR;
- PyObject * co_obj = NULL;
- PyObject * stack_index = NULL;
-
- if (self->concur_id_func != Py_None) {
- int the_index = 0;
-
- if (self->data_stack_index == NULL) {
- PyObject * weakref = NULL;
-
- weakref = PyImport_ImportModule("weakref");
- if (weakref == NULL) {
- goto error;
- }
- STATS( self->stats.pycalls++; )
- self->data_stack_index = PyObject_CallMethod(weakref, "WeakKeyDictionary", NULL);
- Py_XDECREF(weakref);
-
- if (self->data_stack_index == NULL) {
- goto error;
- }
- }
-
- STATS( self->stats.pycalls++; )
- co_obj = PyObject_CallObject(self->concur_id_func, NULL);
- if (co_obj == NULL) {
- goto error;
- }
- stack_index = PyObject_GetItem(self->data_stack_index, co_obj);
- if (stack_index == NULL) {
- /* PyObject_GetItem sets an exception if it didn't find the thing. */
- PyErr_Clear();
-
- /* A new concurrency object. Make a new data stack. */
- the_index = self->data_stacks_used;
- stack_index = MyInt_FromInt(the_index);
- if (stack_index == NULL) {
- goto error;
- }
- if (PyObject_SetItem(self->data_stack_index, co_obj, stack_index) < 0) {
- goto error;
- }
- self->data_stacks_used++;
- if (self->data_stacks_used >= self->data_stacks_alloc) {
- int bigger = self->data_stacks_alloc + 10;
- DataStack * bigger_stacks = PyMem_Realloc(self->data_stacks, bigger * sizeof(DataStack));
- if (bigger_stacks == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- self->data_stacks = bigger_stacks;
- self->data_stacks_alloc = bigger;
- }
- DataStack_init(&self->stats, &self->data_stacks[the_index]);
- }
- else {
- if (pyint_as_int(stack_index, &the_index) < 0) {
- goto error;
- }
- }
-
- self->pdata_stack = &self->data_stacks[the_index];
- }
- else {
- self->pdata_stack = &self->data_stack;
- }
-
- ret = RET_OK;
-
-error:
-
- Py_XDECREF(co_obj);
- Py_XDECREF(stack_index);
-
- return ret;
-}
-
-/*
- * Parts of the trace function.
- */
-
-static int
-CTracer_check_missing_return(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
-
- if (self->last_exc_back) {
- if (frame == self->last_exc_back) {
- /* Looks like someone forgot to send a return event. We'll clear
- the exception state and do the RETURN code here. Notice that the
- frame we have in hand here is not the correct frame for the RETURN,
- that frame is gone. Our handling for RETURN doesn't need the
- actual frame, but we do log it, so that will look a little off if
- you're looking at the detailed log.
-
- If someday we need to examine the frame when doing RETURN, then
- we'll need to keep more of the missed frame's state.
- */
- STATS( self->stats.missed_returns++; )
- if (CTracer_set_pdata_stack(self) < 0) {
- goto error;
- }
- if (self->pdata_stack->depth >= 0) {
- if (self->tracing_arcs && self->pcur_entry->file_data) {
- if (CTracer_record_pair(self, self->pcur_entry->last_line, -self->last_exc_firstlineno) < 0) {
- goto error;
- }
- }
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "missedreturn");
- self->pdata_stack->depth--;
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
- }
- }
- self->last_exc_back = NULL;
- }
-
- ret = RET_OK;
-
-error:
-
- return ret;
-}
-
-static int
-CTracer_handle_call(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
- int ret2;
-
- /* Owned references that we clean up at the very end of the function. */
- PyObject * disposition = NULL;
- PyObject * plugin = NULL;
- PyObject * plugin_name = NULL;
- PyObject * next_tracename = NULL;
-
- /* Borrowed references. */
- PyObject * filename = NULL;
- PyObject * disp_trace = NULL;
- PyObject * tracename = NULL;
- PyObject * file_tracer = NULL;
- PyObject * has_dynamic_filename = NULL;
-
- CFileDisposition * pdisp = NULL;
-
- STATS( self->stats.calls++; )
-
- /* Grow the stack. */
- if (CTracer_set_pdata_stack(self) < 0) {
- goto error;
- }
- if (DataStack_grow(&self->stats, self->pdata_stack) < 0) {
- goto error;
- }
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
-
- /* See if this frame begins a new context. */
- if (self->should_start_context != Py_None && self->context == Py_None) {
- PyObject * context;
- /* We're looking for our context, ask should_start_context if this is the start. */
- STATS( self->stats.start_context_calls++; )
- STATS( self->stats.pycalls++; )
- context = PyObject_CallFunctionObjArgs(self->should_start_context, frame, NULL);
- if (context == NULL) {
- goto error;
- }
- if (context != Py_None) {
- PyObject * val;
- Py_DECREF(self->context);
- self->context = context;
- self->pcur_entry->started_context = TRUE;
- STATS( self->stats.pycalls++; )
- val = PyObject_CallFunctionObjArgs(self->switch_context, context, NULL);
- if (val == NULL) {
- goto error;
- }
- Py_DECREF(val);
- }
- else {
- Py_DECREF(context);
- self->pcur_entry->started_context = FALSE;
- }
- }
- else {
- self->pcur_entry->started_context = FALSE;
- }
-
- /* Check if we should trace this line. */
- filename = MyFrame_GetCode(frame)->co_filename;
- disposition = PyDict_GetItem(self->should_trace_cache, filename);
- if (disposition == NULL) {
- if (PyErr_Occurred()) {
- goto error;
- }
- STATS( self->stats.files++; )
-
- /* We've never considered this file before. */
- /* Ask should_trace about it. */
- STATS( self->stats.pycalls++; )
- disposition = PyObject_CallFunctionObjArgs(self->should_trace, filename, frame, NULL);
- if (disposition == NULL) {
- /* An error occurred inside should_trace. */
- goto error;
- }
- if (PyDict_SetItem(self->should_trace_cache, filename, disposition) < 0) {
- goto error;
- }
- }
- else {
- Py_INCREF(disposition);
- }
-
- if (disposition == Py_None) {
- /* A later check_include returned false, so don't trace it. */
- disp_trace = Py_False;
- }
- else {
- /* The object we got is a CFileDisposition, use it efficiently. */
- pdisp = (CFileDisposition *) disposition;
- disp_trace = pdisp->trace;
- if (disp_trace == NULL) {
- goto error;
- }
- }
-
- if (disp_trace == Py_True) {
- /* If tracename is a string, then we're supposed to trace. */
- tracename = pdisp->source_filename;
- if (tracename == NULL) {
- goto error;
- }
- file_tracer = pdisp->file_tracer;
- if (file_tracer == NULL) {
- goto error;
- }
- if (file_tracer != Py_None) {
- plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin);
- if (plugin == NULL) {
- goto error;
- }
- plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name);
- if (plugin_name == NULL) {
- goto error;
- }
- }
- has_dynamic_filename = pdisp->has_dynamic_filename;
- if (has_dynamic_filename == NULL) {
- goto error;
- }
- if (has_dynamic_filename == Py_True) {
- STATS( self->stats.pycalls++; )
- next_tracename = PyObject_CallMethodObjArgs(
- file_tracer, str_dynamic_source_filename,
- tracename, frame, NULL
- );
- if (next_tracename == NULL) {
- /* An exception from the function. Alert the user with a
- * warning and a traceback.
- */
- CTracer_disable_plugin(self, disposition);
- /* Because we handled the error, goto ok. */
- goto ok;
- }
- tracename = next_tracename;
-
- if (tracename != Py_None) {
- /* Check the dynamic source filename against the include rules. */
- PyObject * included = NULL;
- int should_include;
- included = PyDict_GetItem(self->should_trace_cache, tracename);
- if (included == NULL) {
- PyObject * should_include_bool;
- if (PyErr_Occurred()) {
- goto error;
- }
- STATS( self->stats.files++; )
- STATS( self->stats.pycalls++; )
- should_include_bool = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL);
- if (should_include_bool == NULL) {
- goto error;
- }
- should_include = (should_include_bool == Py_True);
- Py_DECREF(should_include_bool);
- if (PyDict_SetItem(self->should_trace_cache, tracename, should_include ? disposition : Py_None) < 0) {
- goto error;
- }
- }
- else {
- should_include = (included != Py_None);
- }
- if (!should_include) {
- tracename = Py_None;
- }
- }
- }
- }
- else {
- tracename = Py_None;
- }
-
- if (tracename != Py_None) {
- PyObject * file_data = PyDict_GetItem(self->data, tracename);
-
- if (file_data == NULL) {
- if (PyErr_Occurred()) {
- goto error;
- }
- file_data = PyDict_New();
- if (file_data == NULL) {
- goto error;
- }
- ret2 = PyDict_SetItem(self->data, tracename, file_data);
- if (ret2 < 0) {
- goto error;
- }
-
- /* If the disposition mentions a plugin, record that. */
- if (file_tracer != Py_None) {
- ret2 = PyDict_SetItem(self->file_tracers, tracename, plugin_name);
- if (ret2 < 0) {
- goto error;
- }
- }
- }
- else {
- /* PyDict_GetItem gives a borrowed reference. Own it. */
- Py_INCREF(file_data);
- }
-
- Py_XDECREF(self->pcur_entry->file_data);
- self->pcur_entry->file_data = file_data;
- self->pcur_entry->file_tracer = file_tracer;
-
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "traced");
- }
- else {
- Py_XDECREF(self->pcur_entry->file_data);
- self->pcur_entry->file_data = NULL;
- self->pcur_entry->file_tracer = Py_None;
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "skipped");
- }
-
- self->pcur_entry->disposition = disposition;
-
- /* Make the frame right in case settrace(gettrace()) happens. */
- Py_INCREF(self);
- My_XSETREF(frame->f_trace, (PyObject*)self);
-
- /* A call event is really a "start frame" event, and can happen for
- * re-entering a generator also. f_lasti is -1 for a true call, and a
- * real byte offset for a generator re-entry.
- */
- if (MyFrame_GetLasti(frame) < 0) {
- self->pcur_entry->last_line = -MyFrame_GetCode(frame)->co_firstlineno;
- }
- else {
- self->pcur_entry->last_line = PyFrame_GetLineNumber(frame);
- }
-
-ok:
- ret = RET_OK;
-
-error:
- Py_XDECREF(next_tracename);
- Py_XDECREF(disposition);
- Py_XDECREF(plugin);
- Py_XDECREF(plugin_name);
-
- return ret;
-}
-
-
-static void
-CTracer_disable_plugin(CTracer *self, PyObject * disposition)
-{
- PyObject * ret;
- PyErr_Print();
-
- STATS( self->stats.pycalls++; )
- ret = PyObject_CallFunctionObjArgs(self->disable_plugin, disposition, NULL);
- if (ret == NULL) {
- goto error;
- }
- Py_DECREF(ret);
-
- return;
-
-error:
- /* This function doesn't return a status, so if an error happens, print it,
- * but don't interrupt the flow. */
- /* PySys_WriteStderr is nicer, but is not in the public API. */
- fprintf(stderr, "Error occurred while disabling plug-in:\n");
- PyErr_Print();
-}
-
-
-static int
-CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two)
-{
- int ret = RET_ERROR;
- int the_int;
- PyObject * pyint = NULL;
- int index;
-
- if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) {
- PyErr_SetString(
- PyExc_TypeError,
- "line_number_range must return 2-tuple"
- );
- goto error;
- }
-
- for (index = 0; index < 2; index++) {
- pyint = PyTuple_GetItem(pair, index);
- if (pyint == NULL) {
- goto error;
- }
- if (pyint_as_int(pyint, &the_int) < 0) {
- goto error;
- }
- *(index == 0 ? p_one : p_two) = the_int;
- }
-
- ret = RET_OK;
-
-error:
- return ret;
-}
-
-static int
-CTracer_handle_line(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
- int ret2;
-
- STATS( self->stats.lines++; )
- if (self->pdata_stack->depth >= 0) {
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "line");
- if (self->pcur_entry->file_data) {
- int lineno_from = -1;
- int lineno_to = -1;
-
- /* We're tracing in this frame: record something. */
- if (self->pcur_entry->file_tracer != Py_None) {
- PyObject * from_to = NULL;
- STATS( self->stats.pycalls++; )
- from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL);
- if (from_to == NULL) {
- CTracer_disable_plugin(self, self->pcur_entry->disposition);
- goto ok;
- }
- ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to);
- Py_DECREF(from_to);
- if (ret2 < 0) {
- CTracer_disable_plugin(self, self->pcur_entry->disposition);
- goto ok;
- }
- }
- else {
- lineno_from = lineno_to = PyFrame_GetLineNumber(frame);
- }
-
- if (lineno_from != -1) {
- for (; lineno_from <= lineno_to; lineno_from++) {
- if (self->tracing_arcs) {
- /* Tracing arcs: key is (last_line,this_line). */
- if (CTracer_record_pair(self, self->pcur_entry->last_line, lineno_from) < 0) {
- goto error;
- }
- }
- else {
- /* Tracing lines: key is simply this_line. */
- PyObject * this_line = MyInt_FromInt(lineno_from);
- if (this_line == NULL) {
- goto error;
- }
-
- ret2 = PyDict_SetItem(self->pcur_entry->file_data, this_line, Py_None);
- Py_DECREF(this_line);
- if (ret2 < 0) {
- goto error;
- }
- }
-
- self->pcur_entry->last_line = lineno_from;
- }
- }
- }
- }
-
-ok:
- ret = RET_OK;
-
-error:
-
- return ret;
-}
-
-static int
-CTracer_handle_return(CTracer *self, PyFrameObject *frame)
-{
- int ret = RET_ERROR;
-
- STATS( self->stats.returns++; )
- /* A near-copy of this code is above in the missing-return handler. */
- if (CTracer_set_pdata_stack(self) < 0) {
- goto error;
- }
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
-
- if (self->pdata_stack->depth >= 0) {
- if (self->tracing_arcs && self->pcur_entry->file_data) {
- /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read
- * the current bytecode to see what it is. In unusual circumstances
- * (Cython code), co_code can be the empty string, so range-check
- * f_lasti before reading the byte.
- */
- int bytecode = RETURN_VALUE;
- PyObject * pCode = MyCode_GetCode(MyFrame_GetCode(frame));
- int lasti = MyFrame_GetLasti(frame);
-
- if (lasti < MyBytes_GET_SIZE(pCode)) {
- bytecode = MyBytes_AS_STRING(pCode)[lasti];
- }
- if (bytecode != YIELD_VALUE) {
- int first = MyFrame_GetCode(frame)->co_firstlineno;
- if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) {
- goto error;
- }
- }
- }
-
- /* If this frame started a context, then returning from it ends the context. */
- if (self->pcur_entry->started_context) {
- PyObject * val;
- Py_DECREF(self->context);
- self->context = Py_None;
- Py_INCREF(self->context);
- STATS( self->stats.pycalls++; )
-
- val = PyObject_CallFunctionObjArgs(self->switch_context, self->context, NULL);
- if (val == NULL) {
- goto error;
- }
- Py_DECREF(val);
- }
-
- /* Pop the stack. */
- SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "return");
- self->pdata_stack->depth--;
- self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
- }
-
- ret = RET_OK;
-
-error:
-
- return ret;
-}
-
-static int
-CTracer_handle_exception(CTracer *self, PyFrameObject *frame)
-{
- /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
- without a return event. To detect that, we'll keep a copy of the
- parent frame for an exception event. If the next event is in that
- frame, then we must have returned without a return event. We can
- synthesize the missing event then.
-
- Python itself fixed this problem in 2.4. Pyexpat still has the bug.
- I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
- If it gets fixed, this code should still work properly. Maybe some day
- the bug will be fixed everywhere coverage.py is supported, and we can
- remove this missing-return detection.
-
- More about this fix: https://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
- */
- STATS( self->stats.exceptions++; )
- self->last_exc_back = frame->f_back;
- self->last_exc_firstlineno = MyFrame_GetCode(frame)->co_firstlineno;
-
- return RET_OK;
-}
-
-/*
- * The Trace Function
- */
-static int
-CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
-{
- int ret = RET_ERROR;
-
- #if DO_NOTHING
- return RET_OK;
- #endif
-
- if (!self->started) {
- /* If CTracer.stop() has been called from another thread, the tracer
- is still active in the current thread. Let's deactivate ourselves
- now. */
- PyEval_SetTrace(NULL, NULL);
- return RET_OK;
- }
-
- #if WHAT_LOG || TRACE_LOG
- PyObject * ascii = NULL;
- #endif
-
- #if WHAT_LOG
- if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) {
- ascii = MyText_AS_BYTES(MyFrame_GetCode(frame)->co_filename);
- printf("trace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
- Py_DECREF(ascii);
- }
- #endif
-
- #if TRACE_LOG
- ascii = MyText_AS_BYTES(MyFrame_GetCode(frame)->co_filename);
- if (strstr(MyBytes_AS_STRING(ascii), start_file) && PyFrame_GetLineNumber(frame) == start_line) {
- logging = TRUE;
- }
- Py_DECREF(ascii);
- #endif
-
- /* See below for details on missing-return detection. */
- if (CTracer_check_missing_return(self, frame) < 0) {
- goto error;
- }
-
- self->activity = TRUE;
-
- switch (what) {
- case PyTrace_CALL:
- if (CTracer_handle_call(self, frame) < 0) {
- goto error;
- }
- break;
-
- case PyTrace_RETURN:
- if (CTracer_handle_return(self, frame) < 0) {
- goto error;
- }
- break;
-
- case PyTrace_LINE:
- if (CTracer_handle_line(self, frame) < 0) {
- goto error;
- }
- break;
-
- case PyTrace_EXCEPTION:
- if (CTracer_handle_exception(self, frame) < 0) {
- goto error;
- }
- break;
-
- default:
- STATS( self->stats.others++; )
- break;
- }
-
- ret = RET_OK;
- goto cleanup;
-
-error:
- STATS( self->stats.errors++; )
-
-cleanup:
- return ret;
-}
-
-
-/*
- * Python has two ways to set the trace function: sys.settrace(fn), which
- * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
- * a C function and a Python object. The way these work together is that
- * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
- * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
- * simply returns the Python object used as the second argument to
- * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
- * means it must be callable to be used in sys.settrace().
- *
- * So we make ourself callable, equivalent to invoking our trace function.
- *
- * To help with the process of replaying stored frames, this function has an
- * optional keyword argument:
- *
- * def CTracer_call(frame, event, arg, lineno=0)
- *
- * If provided, the lineno argument is used as the line number, and the
- * frame's f_lineno member is ignored.
- */
-static PyObject *
-CTracer_call(CTracer *self, PyObject *args, PyObject *kwds)
-{
- PyFrameObject *frame;
- PyObject *what_str;
- PyObject *arg;
- int lineno = 0;
- int what;
- int orig_lineno;
- PyObject *ret = NULL;
- PyObject * ascii = NULL;
-
- #if DO_NOTHING
- CRASH
- #endif
-
- static char *what_names[] = {
- "call", "exception", "line", "return",
- "c_call", "c_exception", "c_return",
- NULL
- };
-
- static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
- &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
- goto done;
- }
-
- /* In Python, the what argument is a string, we need to find an int
- for the C function. */
- for (what = 0; what_names[what]; what++) {
- int should_break;
- ascii = MyText_AS_BYTES(what_str);
- should_break = !strcmp(MyBytes_AS_STRING(ascii), what_names[what]);
- Py_DECREF(ascii);
- if (should_break) {
- break;
- }
- }
-
- #if WHAT_LOG
- ascii = MyText_AS_BYTES(MyFrame_GetCode(frame)->co_filename);
- printf("pytrace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
- Py_DECREF(ascii);
- #endif
-
- /* Save off the frame's lineno, and use the forced one, if provided. */
- orig_lineno = frame->f_lineno;
- if (lineno > 0) {
- frame->f_lineno = lineno;
- }
-
- /* Invoke the C function, and return ourselves. */
- if (CTracer_trace(self, frame, what, arg) == RET_OK) {
- Py_INCREF(self);
- ret = (PyObject *)self;
- }
-
- /* Clean up. */
- frame->f_lineno = orig_lineno;
-
- /* For better speed, install ourselves the C way so that future calls go
- directly to CTracer_trace, without this intermediate function.
-
- Only do this if this is a CALL event, since new trace functions only
- take effect then. If we don't condition it on CALL, then we'll clobber
- the new trace function before it has a chance to get called. To
- understand why, there are three internal values to track: frame.f_trace,
- c_tracefunc, and c_traceobj. They are explained here:
- https://nedbatchelder.com/text/trace-function.html
-
- Without the conditional on PyTrace_CALL, this is what happens:
-
- def func(): # f_trace c_tracefunc c_traceobj
- # -------------- -------------- --------------
- # CTracer CTracer.trace CTracer
- sys.settrace(my_func)
- # CTracer trampoline my_func
- # Now Python calls trampoline(CTracer), which calls this function
- # which calls PyEval_SetTrace below, setting us as the tracer again:
- # CTracer CTracer.trace CTracer
- # and it's as if the settrace never happened.
- */
- if (what == PyTrace_CALL) {
- PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
- }
-
-done:
- return ret;
-}
-
-static PyObject *
-CTracer_start(CTracer *self, PyObject *args_unused)
-{
- PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
- self->started = TRUE;
- self->tracing_arcs = self->trace_arcs && PyObject_IsTrue(self->trace_arcs);
-
- /* start() returns a trace function usable with sys.settrace() */
- Py_INCREF(self);
- return (PyObject *)self;
-}
-
-static PyObject *
-CTracer_stop(CTracer *self, PyObject *args_unused)
-{
- if (self->started) {
- /* Set the started flag only. The actual call to
- PyEval_SetTrace(NULL, NULL) is delegated to the callback
- itself to ensure that it called from the right thread.
- */
- self->started = FALSE;
- }
-
- Py_RETURN_NONE;
-}
-
-static PyObject *
-CTracer_activity(CTracer *self, PyObject *args_unused)
-{
- if (self->activity) {
- Py_RETURN_TRUE;
- }
- else {
- Py_RETURN_FALSE;
- }
-}
-
-static PyObject *
-CTracer_reset_activity(CTracer *self, PyObject *args_unused)
-{
- self->activity = FALSE;
- Py_RETURN_NONE;
-}
-
-static PyObject *
-CTracer_get_stats(CTracer *self, PyObject *args_unused)
-{
-#if COLLECT_STATS
- return Py_BuildValue(
- "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI,sI,sI}",
- "calls", self->stats.calls,
- "lines", self->stats.lines,
- "returns", self->stats.returns,
- "exceptions", self->stats.exceptions,
- "others", self->stats.others,
- "files", self->stats.files,
- "missed_returns", self->stats.missed_returns,
- "stack_reallocs", self->stats.stack_reallocs,
- "stack_alloc", self->pdata_stack->alloc,
- "errors", self->stats.errors,
- "pycalls", self->stats.pycalls,
- "start_context_calls", self->stats.start_context_calls
- );
-#else
- Py_RETURN_NONE;
-#endif /* COLLECT_STATS */
-}
-
-static PyMemberDef
-CTracer_members[] = {
- { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0,
- PyDoc_STR("Function indicating whether to trace a file.") },
-
- { "check_include", T_OBJECT, offsetof(CTracer, check_include), 0,
- PyDoc_STR("Function indicating whether to include a file.") },
-
- { "warn", T_OBJECT, offsetof(CTracer, warn), 0,
- PyDoc_STR("Function for issuing warnings.") },
-
- { "concur_id_func", T_OBJECT, offsetof(CTracer, concur_id_func), 0,
- PyDoc_STR("Function for determining concurrency context") },
-
- { "data", T_OBJECT, offsetof(CTracer, data), 0,
- PyDoc_STR("The raw dictionary of trace data.") },
-
- { "file_tracers", T_OBJECT, offsetof(CTracer, file_tracers), 0,
- PyDoc_STR("Mapping from file name to plugin name.") },
-
- { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0,
- PyDoc_STR("Dictionary caching should_trace results.") },
-
- { "trace_arcs", T_OBJECT, offsetof(CTracer, trace_arcs), 0,
- PyDoc_STR("Should we trace arcs, or just lines?") },
-
- { "should_start_context", T_OBJECT, offsetof(CTracer, should_start_context), 0,
- PyDoc_STR("Function for starting contexts.") },
-
- { "switch_context", T_OBJECT, offsetof(CTracer, switch_context), 0,
- PyDoc_STR("Function for switching to a new context.") },
-
- { "disable_plugin", T_OBJECT, offsetof(CTracer, disable_plugin), 0,
- PyDoc_STR("Function for disabling a plugin.") },
-
- { NULL }
-};
-
-static PyMethodDef
-CTracer_methods[] = {
- { "start", (PyCFunction) CTracer_start, METH_VARARGS,
- PyDoc_STR("Start the tracer") },
-
- { "stop", (PyCFunction) CTracer_stop, METH_VARARGS,
- PyDoc_STR("Stop the tracer") },
-
- { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS,
- PyDoc_STR("Get statistics about the tracing") },
-
- { "activity", (PyCFunction) CTracer_activity, METH_VARARGS,
- PyDoc_STR("Has there been any activity?") },
-
- { "reset_activity", (PyCFunction) CTracer_reset_activity, METH_VARARGS,
- PyDoc_STR("Reset the activity flag") },
-
- { NULL }
-};
-
-PyTypeObject
-CTracerType = {
- MyType_HEAD_INIT
- "coverage.CTracer", /*tp_name*/
- sizeof(CTracer), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)CTracer_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- (ternaryfunc)CTracer_call, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "CTracer objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- CTracer_methods, /* tp_methods */
- CTracer_members, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)CTracer_init, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
-};
diff --git a/contrib/python/coverage/py3/coverage/ctracer/tracer.h b/contrib/python/coverage/py3/coverage/ctracer/tracer.h
deleted file mode 100644
index 8994a9e3d6..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/tracer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_TRACER_H
-#define _COVERAGE_TRACER_H
-
-#include "util.h"
-#include "structmember.h"
-#include "frameobject.h"
-#include "opcode.h"
-
-#include "datastack.h"
-
-/* The CTracer type. */
-
-typedef struct CTracer {
- PyObject_HEAD
-
- /* Python objects manipulated directly by the Collector class. */
- PyObject * should_trace;
- PyObject * check_include;
- PyObject * warn;
- PyObject * concur_id_func;
- PyObject * data;
- PyObject * file_tracers;
- PyObject * should_trace_cache;
- PyObject * trace_arcs;
- PyObject * should_start_context;
- PyObject * switch_context;
- PyObject * disable_plugin;
-
- /* Has the tracer been started? */
- BOOL started;
- /* Are we tracing arcs, or just lines? */
- BOOL tracing_arcs;
- /* Have we had any activity? */
- BOOL activity;
- /* The current dynamic context. */
- PyObject * context;
-
- /*
- The data stack is a stack of dictionaries. Each dictionary collects
- data for a single source file. The data stack parallels the call stack:
- each call pushes the new frame's file data onto the data stack, and each
- return pops file data off.
-
- The file data is a dictionary whose form depends on the tracing options.
- If tracing arcs, the keys are line number pairs. If not tracing arcs,
- the keys are line numbers. In both cases, the value is irrelevant
- (None).
- */
-
- DataStack data_stack; /* Used if we aren't doing concurrency. */
-
- PyObject * data_stack_index; /* Used if we are doing concurrency. */
- DataStack * data_stacks;
- int data_stacks_alloc;
- int data_stacks_used;
- DataStack * pdata_stack;
-
- /* The current file's data stack entry. */
- DataStackEntry * pcur_entry;
-
- /* The parent frame for the last exception event, to fix missing returns. */
- PyFrameObject * last_exc_back;
- int last_exc_firstlineno;
-
- Stats stats;
-} CTracer;
-
-int CTracer_intern_strings(void);
-
-extern PyTypeObject CTracerType;
-
-#endif /* _COVERAGE_TRACER_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/util.h b/contrib/python/coverage/py3/coverage/ctracer/util.h
deleted file mode 100644
index 973672db02..0000000000
--- a/contrib/python/coverage/py3/coverage/ctracer/util.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-#ifndef _COVERAGE_UTIL_H
-#define _COVERAGE_UTIL_H
-
-#include <Python.h>
-
-/* Compile-time debugging helpers */
-#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
-#undef TRACE_LOG /* Define to log our bookkeeping. */
-#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
-#undef DO_NOTHING /* Define this to make the tracer do nothing. */
-
-#if PY_VERSION_HEX >= 0x030B00A0
-// 3.11 moved f_lasti into an internal structure. This is totally the wrong way
-// to make this work, but it's all I've got until https://bugs.python.org/issue40421
-// is resolved.
-#include <internal/pycore_frame.h>
-#if PY_VERSION_HEX >= 0x030B00A7
-#define MyFrame_GetLasti(f) (PyFrame_GetLasti(f))
-#else
-#define MyFrame_GetLasti(f) ((f)->f_frame->f_lasti * 2)
-#endif
-#elif PY_VERSION_HEX >= 0x030A00A7
-// The f_lasti field changed meaning in 3.10.0a7. It had been bytes, but
-// now is instructions, so we need to adjust it to use it as a byte index.
-#define MyFrame_GetLasti(f) ((f)->f_lasti * 2)
-#else
-#define MyFrame_GetLasti(f) ((f)->f_lasti)
-#endif
-
-// Access f_code should be done through a helper starting in 3.9.
-#if PY_VERSION_HEX >= 0x03090000
-#define MyFrame_GetCode(f) (PyFrame_GetCode(f))
-#else
-#define MyFrame_GetCode(f) ((f)->f_code)
-#endif
-
-#if PY_VERSION_HEX >= 0x030B00B1
-#define MyCode_GetCode(co) (PyCode_GetCode(co))
-#define MyCode_FreeCode(code) Py_XDECREF(code)
-#elif PY_VERSION_HEX >= 0x030B00A7
-#define MyCode_GetCode(co) (PyObject_GetAttrString((PyObject *)(co), "co_code"))
-#define MyCode_FreeCode(code) Py_XDECREF(code)
-#else
-#define MyCode_GetCode(co) ((co)->co_code)
-#define MyCode_FreeCode(code)
-#endif
-
-/* Py 2.x and 3.x compatibility */
-
-#if PY_MAJOR_VERSION >= 3
-
-#define MyText_Type PyUnicode_Type
-#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
-#define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
-#define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o)
-#define MyText_AsString(o) PyUnicode_AsUTF8(o)
-#define MyText_FromFormat PyUnicode_FromFormat
-#define MyInt_FromInt(i) PyLong_FromLong((long)i)
-#define MyInt_AsInt(o) (int)PyLong_AsLong(o)
-#define MyText_InternFromString(s) PyUnicode_InternFromString(s)
-
-#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
-
-#else
-
-#define MyText_Type PyString_Type
-#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
-#define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o)
-#define MyBytes_AS_STRING(o) PyString_AS_STRING(o)
-#define MyText_AsString(o) PyString_AsString(o)
-#define MyText_FromFormat PyUnicode_FromFormat
-#define MyInt_FromInt(i) PyInt_FromLong((long)i)
-#define MyInt_AsInt(o) (int)PyInt_AsLong(o)
-#define MyText_InternFromString(s) PyString_InternFromString(s)
-
-#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
-
-#endif /* Py3k */
-
-// Undocumented, and not in all 2.7.x, so our own copy of it.
-#define My_XSETREF(op, op2) \
- do { \
- PyObject *_py_tmp = (PyObject *)(op); \
- (op) = (op2); \
- Py_XDECREF(_py_tmp); \
- } while (0)
-
-/* The values returned to indicate ok or error. */
-#define RET_OK 0
-#define RET_ERROR -1
-
-/* Nicer booleans */
-typedef int BOOL;
-#define FALSE 0
-#define TRUE 1
-
-/* Only for extreme machete-mode debugging! */
-#define CRASH { printf("*** CRASH! ***\n"); *((int*)1) = 1; }
-
-#endif /* _COVERAGE_UTIL_H */
diff --git a/contrib/python/coverage/py3/coverage/data.py b/contrib/python/coverage/py3/coverage/data.py
deleted file mode 100644
index 5dd1dfe3f0..0000000000
--- a/contrib/python/coverage/py3/coverage/data.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Coverage data for coverage.py.
-
-This file had the 4.x JSON data support, which is now gone. This file still
-has storage-agnostic helpers, and is kept to avoid changing too many imports.
-CoverageData is now defined in sqldata.py, and imported here to keep the
-imports working.
-
-"""
-
-import glob
-import os.path
-
-from coverage.misc import CoverageException, file_be_gone
-from coverage.sqldata import CoverageData
-
-
-def line_counts(data, fullpath=False):
- """Return a dict summarizing the line coverage data.
-
- Keys are based on the file names, and values are the number of executed
- lines. If `fullpath` is true, then the keys are the full pathnames of
- the files, otherwise they are the basenames of the files.
-
- Returns a dict mapping file names to counts of lines.
-
- """
- summ = {}
- if fullpath:
- filename_fn = lambda f: f
- else:
- filename_fn = os.path.basename
- for filename in data.measured_files():
- summ[filename_fn(filename)] = len(data.lines(filename))
- return summ
-
-
-def add_data_to_hash(data, filename, hasher):
- """Contribute `filename`'s data to the `hasher`.
-
- `hasher` is a `coverage.misc.Hasher` instance to be updated with
- the file's data. It should only get the results data, not the run
- data.
-
- """
- if data.has_arcs():
- hasher.update(sorted(data.arcs(filename) or []))
- else:
- hasher.update(sorted(data.lines(filename) or []))
- hasher.update(data.file_tracer(filename))
-
-
-def combine_parallel_data(data, aliases=None, data_paths=None, strict=False, keep=False):
- """Combine a number of data files together.
-
- Treat `data.filename` as a file prefix, and combine the data from all
- of the data files starting with that prefix plus a dot.
-
- If `aliases` is provided, it's a `PathAliases` object that is used to
- re-map paths to match the local machine's.
-
- If `data_paths` is provided, it is a list of directories or files to
- combine. Directories are searched for files that start with
- `data.filename` plus dot as a prefix, and those files are combined.
-
- If `data_paths` is not provided, then the directory portion of
- `data.filename` is used as the directory to search for data files.
-
- Unless `keep` is True every data file found and combined is then deleted from disk. If a file
- cannot be read, a warning will be issued, and the file will not be
- deleted.
-
- If `strict` is true, and no files are found to combine, an error is
- raised.
-
- """
- # Because of the os.path.abspath in the constructor, data_dir will
- # never be an empty string.
- data_dir, local = os.path.split(data.base_filename())
- localdot = local + '.*'
-
- data_paths = data_paths or [data_dir]
- files_to_combine = []
- for p in data_paths:
- if os.path.isfile(p):
- files_to_combine.append(os.path.abspath(p))
- elif os.path.isdir(p):
- pattern = os.path.join(os.path.abspath(p), localdot)
- files_to_combine.extend(glob.glob(pattern))
- else:
- raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,))
-
- if strict and not files_to_combine:
- raise CoverageException("No data to combine")
-
- files_combined = 0
- for f in files_to_combine:
- if f == data.data_filename():
- # Sometimes we are combining into a file which is one of the
- # parallel files. Skip that file.
- if data._debug.should('dataio'):
- data._debug.write("Skipping combining ourself: %r" % (f,))
- continue
- if data._debug.should('dataio'):
- data._debug.write("Combining data file %r" % (f,))
- try:
- new_data = CoverageData(f, debug=data._debug)
- new_data.read()
- except CoverageException as exc:
- if data._warn:
- # The CoverageException has the file name in it, so just
- # use the message as the warning.
- data._warn(str(exc))
- else:
- data.update(new_data, aliases=aliases)
- files_combined += 1
- if not keep:
- if data._debug.should('dataio'):
- data._debug.write("Deleting combined data file %r" % (f,))
- file_be_gone(f)
-
- if strict and not files_combined:
- raise CoverageException("No usable data files")
diff --git a/contrib/python/coverage/py3/coverage/debug.py b/contrib/python/coverage/py3/coverage/debug.py
deleted file mode 100644
index 194f16f50d..0000000000
--- a/contrib/python/coverage/py3/coverage/debug.py
+++ /dev/null
@@ -1,406 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Control of and utilities for debugging."""
-
-import contextlib
-import functools
-import inspect
-import itertools
-import os
-import pprint
-import sys
-try:
- import _thread
-except ImportError:
- import thread as _thread
-
-from coverage.backward import reprlib, StringIO
-from coverage.misc import isolate_module
-
-os = isolate_module(os)
-
-
-# When debugging, it can be helpful to force some options, especially when
-# debugging the configuration mechanisms you usually use to control debugging!
-# This is a list of forced debugging options.
-FORCED_DEBUG = []
-FORCED_DEBUG_FILE = None
-
-
-class DebugControl(object):
- """Control and output for debugging."""
-
- show_repr_attr = False # For SimpleReprMixin
-
- def __init__(self, options, output):
- """Configure the options and output file for debugging."""
- self.options = list(options) + FORCED_DEBUG
- self.suppress_callers = False
-
- filters = []
- if self.should('pid'):
- filters.append(add_pid_and_tid)
- self.output = DebugOutputFile.get_one(
- output,
- show_process=self.should('process'),
- filters=filters,
- )
- self.raw_output = self.output.outfile
-
- def __repr__(self):
- return "<DebugControl options=%r raw_output=%r>" % (self.options, self.raw_output)
-
- def should(self, option):
- """Decide whether to output debug information in category `option`."""
- if option == "callers" and self.suppress_callers:
- return False
- return (option in self.options)
-
- @contextlib.contextmanager
- def without_callers(self):
- """A context manager to prevent call stacks from being logged."""
- old = self.suppress_callers
- self.suppress_callers = True
- try:
- yield
- finally:
- self.suppress_callers = old
-
- def write(self, msg):
- """Write a line of debug output.
-
- `msg` is the line to write. A newline will be appended.
-
- """
- self.output.write(msg+"\n")
- if self.should('self'):
- caller_self = inspect.stack()[1][0].f_locals.get('self')
- if caller_self is not None:
- self.output.write("self: {!r}\n".format(caller_self))
- if self.should('callers'):
- dump_stack_frames(out=self.output, skip=1)
- self.output.flush()
-
-
-class DebugControlString(DebugControl):
- """A `DebugControl` that writes to a StringIO, for testing."""
- def __init__(self, options):
- super(DebugControlString, self).__init__(options, StringIO())
-
- def get_output(self):
- """Get the output text from the `DebugControl`."""
- return self.raw_output.getvalue()
-
-
-class NoDebugging(object):
- """A replacement for DebugControl that will never try to do anything."""
- def should(self, option): # pylint: disable=unused-argument
- """Should we write debug messages? Never."""
- return False
-
-
-def info_header(label):
- """Make a nice header string."""
- return "--{:-<60s}".format(" "+label+" ")
-
-
-def info_formatter(info):
- """Produce a sequence of formatted lines from info.
-
- `info` is a sequence of pairs (label, data). The produced lines are
- nicely formatted, ready to print.
-
- """
- info = list(info)
- if not info:
- return
- label_len = 30
- assert all(len(l) < label_len for l, _ in info)
- for label, data in info:
- if data == []:
- data = "-none-"
- if isinstance(data, (list, set, tuple)):
- prefix = "%*s:" % (label_len, label)
- for e in data:
- yield "%*s %s" % (label_len+1, prefix, e)
- prefix = ""
- else:
- yield "%*s: %s" % (label_len, label, data)
-
-
-def write_formatted_info(writer, header, info):
- """Write a sequence of (label,data) pairs nicely."""
- writer.write(info_header(header))
- for line in info_formatter(info):
- writer.write(" %s" % line)
-
-
-def short_stack(limit=None, skip=0):
- """Return a string summarizing the call stack.
-
- The string is multi-line, with one line per stack frame. Each line shows
- the function name, the file name, and the line number:
-
- ...
- start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
- import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
- import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
- ...
-
- `limit` is the number of frames to include, defaulting to all of them.
-
- `skip` is the number of frames to skip, so that debugging functions can
- call this and not be included in the result.
-
- """
- stack = inspect.stack()[limit:skip:-1]
- return "\n".join("%30s : %s:%d" % (t[3], t[1], t[2]) for t in stack)
-
-
-def dump_stack_frames(limit=None, out=None, skip=0):
- """Print a summary of the stack to stdout, or someplace else."""
- out = out or sys.stdout
- out.write(short_stack(limit=limit, skip=skip+1))
- out.write("\n")
-
-
-def clipped_repr(text, numchars=50):
- """`repr(text)`, but limited to `numchars`."""
- r = reprlib.Repr()
- r.maxstring = numchars
- return r.repr(text)
-
-
-def short_id(id64):
- """Given a 64-bit id, make a shorter 16-bit one."""
- id16 = 0
- for offset in range(0, 64, 16):
- id16 ^= id64 >> offset
- return id16 & 0xFFFF
-
-
-def add_pid_and_tid(text):
- """A filter to add pid and tid to debug messages."""
- # Thread ids are useful, but too long. Make a shorter one.
- tid = "{:04x}".format(short_id(_thread.get_ident()))
- text = "{:5d}.{}: {}".format(os.getpid(), tid, text)
- return text
-
-
-class SimpleReprMixin(object):
- """A mixin implementing a simple __repr__."""
- simple_repr_ignore = ['simple_repr_ignore', '$coverage.object_id']
-
- def __repr__(self):
- show_attrs = (
- (k, v) for k, v in self.__dict__.items()
- if getattr(v, "show_repr_attr", True)
- and not callable(v)
- and k not in self.simple_repr_ignore
- )
- return "<{klass} @0x{id:x} {attrs}>".format(
- klass=self.__class__.__name__,
- id=id(self),
- attrs=" ".join("{}={!r}".format(k, v) for k, v in show_attrs),
- )
-
-
-def simplify(v): # pragma: debugging
- """Turn things which are nearly dict/list/etc into dict/list/etc."""
- if isinstance(v, dict):
- return {k:simplify(vv) for k, vv in v.items()}
- elif isinstance(v, (list, tuple)):
- return type(v)(simplify(vv) for vv in v)
- elif hasattr(v, "__dict__"):
- return simplify({'.'+k: v for k, v in v.__dict__.items()})
- else:
- return v
-
-
-def pp(v): # pragma: debugging
- """Debug helper to pretty-print data, including SimpleNamespace objects."""
- # Might not be needed in 3.9+
- pprint.pprint(simplify(v))
-
-
-def filter_text(text, filters):
- """Run `text` through a series of filters.
-
- `filters` is a list of functions. Each takes a string and returns a
- string. Each is run in turn.
-
- Returns: the final string that results after all of the filters have
- run.
-
- """
- clean_text = text.rstrip()
- ending = text[len(clean_text):]
- text = clean_text
- for fn in filters:
- lines = []
- for line in text.splitlines():
- lines.extend(fn(line).splitlines())
- text = "\n".join(lines)
- return text + ending
-
-
-class CwdTracker(object): # pragma: debugging
- """A class to add cwd info to debug messages."""
- def __init__(self):
- self.cwd = None
-
- def filter(self, text):
- """Add a cwd message for each new cwd."""
- cwd = os.getcwd()
- if cwd != self.cwd:
- text = "cwd is now {!r}\n".format(cwd) + text
- self.cwd = cwd
- return text
-
-
-class DebugOutputFile(object): # pragma: debugging
- """A file-like object that includes pid and cwd information."""
- def __init__(self, outfile, show_process, filters):
- self.outfile = outfile
- self.show_process = show_process
- self.filters = list(filters)
-
- if self.show_process:
- self.filters.insert(0, CwdTracker().filter)
- self.write("New process: executable: %r\n" % (sys.executable,))
- self.write("New process: cmd: %r\n" % (getattr(sys, 'argv', None),))
- if hasattr(os, 'getppid'):
- self.write("New process: pid: %r, parent pid: %r\n" % (os.getpid(), os.getppid()))
-
- SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one'
-
- @classmethod
- def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False):
- """Get a DebugOutputFile.
-
- If `fileobj` is provided, then a new DebugOutputFile is made with it.
-
- If `fileobj` isn't provided, then a file is chosen
- (COVERAGE_DEBUG_FILE, or stderr), and a process-wide singleton
- DebugOutputFile is made.
-
- `show_process` controls whether the debug file adds process-level
- information, and filters is a list of other message filters to apply.
-
- `filters` are the text filters to apply to the stream to annotate with
- pids, etc.
-
- If `interim` is true, then a future `get_one` can replace this one.
-
- """
- if fileobj is not None:
- # Make DebugOutputFile around the fileobj passed.
- return cls(fileobj, show_process, filters)
-
- # Because of the way igor.py deletes and re-imports modules,
- # this class can be defined more than once. But we really want
- # a process-wide singleton. So stash it in sys.modules instead of
- # on a class attribute. Yes, this is aggressively gross.
- the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True))
- if the_one is None or is_interim:
- if fileobj is None:
- debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
- if debug_file_name:
- fileobj = open(debug_file_name, "a")
- else:
- fileobj = sys.stderr
- the_one = cls(fileobj, show_process, filters)
- sys.modules[cls.SYS_MOD_NAME] = (the_one, interim)
- return the_one
-
- def write(self, text):
- """Just like file.write, but filter through all our filters."""
- self.outfile.write(filter_text(text, self.filters))
- self.outfile.flush()
-
- def flush(self):
- """Flush our file."""
- self.outfile.flush()
-
-
-def log(msg, stack=False): # pragma: debugging
- """Write a log message as forcefully as possible."""
- out = DebugOutputFile.get_one(interim=True)
- out.write(msg+"\n")
- if stack:
- dump_stack_frames(out=out, skip=1)
-
-
-def decorate_methods(decorator, butnot=(), private=False): # pragma: debugging
- """A class decorator to apply a decorator to methods."""
- def _decorator(cls):
- for name, meth in inspect.getmembers(cls, inspect.isroutine):
- if name not in cls.__dict__:
- continue
- if name != "__init__":
- if not private and name.startswith("_"):
- continue
- if name in butnot:
- continue
- setattr(cls, name, decorator(meth))
- return cls
- return _decorator
-
-
-def break_in_pudb(func): # pragma: debugging
- """A function decorator to stop in the debugger for each call."""
- @functools.wraps(func)
- def _wrapper(*args, **kwargs):
- import pudb
- sys.stdout = sys.__stdout__
- pudb.set_trace()
- return func(*args, **kwargs)
- return _wrapper
-
-
-OBJ_IDS = itertools.count()
-CALLS = itertools.count()
-OBJ_ID_ATTR = "$coverage.object_id"
-
-def show_calls(show_args=True, show_stack=False, show_return=False): # pragma: debugging
- """A method decorator to debug-log each call to the function."""
- def _decorator(func):
- @functools.wraps(func)
- def _wrapper(self, *args, **kwargs):
- oid = getattr(self, OBJ_ID_ATTR, None)
- if oid is None:
- oid = "{:08d} {:04d}".format(os.getpid(), next(OBJ_IDS))
- setattr(self, OBJ_ID_ATTR, oid)
- extra = ""
- if show_args:
- eargs = ", ".join(map(repr, args))
- ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items())
- extra += "("
- extra += eargs
- if eargs and ekwargs:
- extra += ", "
- extra += ekwargs
- extra += ")"
- if show_stack:
- extra += " @ "
- extra += "; ".join(_clean_stack_line(l) for l in short_stack().splitlines())
- callid = next(CALLS)
- msg = "{} {:04d} {}{}\n".format(oid, callid, func.__name__, extra)
- DebugOutputFile.get_one(interim=True).write(msg)
- ret = func(self, *args, **kwargs)
- if show_return:
- msg = "{} {:04d} {} return {!r}\n".format(oid, callid, func.__name__, ret)
- DebugOutputFile.get_one(interim=True).write(msg)
- return ret
- return _wrapper
- return _decorator
-
-
-def _clean_stack_line(s): # pragma: debugging
- """Simplify some paths in a stack trace, for compactness."""
- s = s.strip()
- s = s.replace(os.path.dirname(__file__) + '/', '')
- s = s.replace(os.path.dirname(os.__file__) + '/', '')
- s = s.replace(sys.prefix + '/', '')
- return s
diff --git a/contrib/python/coverage/py3/coverage/disposition.py b/contrib/python/coverage/py3/coverage/disposition.py
deleted file mode 100644
index 9b9a997d8a..0000000000
--- a/contrib/python/coverage/py3/coverage/disposition.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Simple value objects for tracking what to do with files."""
-
-
-class FileDisposition(object):
- """A simple value type for recording what to do with a file."""
- pass
-
-
-# FileDisposition "methods": FileDisposition is a pure value object, so it can
-# be implemented in either C or Python. Acting on them is done with these
-# functions.
-
-def disposition_init(cls, original_filename):
- """Construct and initialize a new FileDisposition object."""
- disp = cls()
- disp.original_filename = original_filename
- disp.canonical_filename = original_filename
- disp.source_filename = None
- disp.trace = False
- disp.reason = ""
- disp.file_tracer = None
- disp.has_dynamic_filename = False
- return disp
-
-
-def disposition_debug_msg(disp):
- """Make a nice debug message of what the FileDisposition is doing."""
- if disp.trace:
- msg = "Tracing %r" % (disp.original_filename,)
- if disp.file_tracer:
- msg += ": will be traced by %r" % disp.file_tracer
- else:
- msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason)
- return msg
diff --git a/contrib/python/coverage/py3/coverage/env.py b/contrib/python/coverage/py3/coverage/env.py
deleted file mode 100644
index ea78a5be89..0000000000
--- a/contrib/python/coverage/py3/coverage/env.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Determine facts about the environment."""
-
-import os
-import platform
-import sys
-
-# Operating systems.
-WINDOWS = sys.platform == "win32"
-LINUX = sys.platform.startswith("linux")
-
-# Python implementations.
-CPYTHON = (platform.python_implementation() == "CPython")
-PYPY = (platform.python_implementation() == "PyPy")
-JYTHON = (platform.python_implementation() == "Jython")
-IRONPYTHON = (platform.python_implementation() == "IronPython")
-
-# Python versions. We amend version_info with one more value, a zero if an
-# official version, or 1 if built from source beyond an official version.
-PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),)
-PY2 = PYVERSION < (3, 0)
-PY3 = PYVERSION >= (3, 0)
-
-if PYPY:
- PYPYVERSION = sys.pypy_version_info
-
-PYPY2 = PYPY and PY2
-PYPY3 = PYPY and PY3
-
-# Python behavior.
-class PYBEHAVIOR(object):
- """Flags indicating this Python's behavior."""
-
- pep626 = CPYTHON and (PYVERSION > (3, 10, 0, 'alpha', 4))
-
- # Is "if __debug__" optimized away?
- if PYPY3:
- optimize_if_debug = True
- elif PYPY2:
- optimize_if_debug = False
- else:
- optimize_if_debug = not pep626
-
- # Is "if not __debug__" optimized away?
- optimize_if_not_debug = (not PYPY) and (PYVERSION >= (3, 7, 0, 'alpha', 4))
- if pep626:
- optimize_if_not_debug = False
- if PYPY3:
- optimize_if_not_debug = True
-
- # Is "if not __debug__" optimized away even better?
- optimize_if_not_debug2 = (not PYPY) and (PYVERSION >= (3, 8, 0, 'beta', 1))
- if pep626:
- optimize_if_not_debug2 = False
-
- # Do we have yield-from?
- yield_from = (PYVERSION >= (3, 3))
-
- # Do we have PEP 420 namespace packages?
- namespaces_pep420 = (PYVERSION >= (3, 3))
-
- # Do .pyc files have the source file size recorded in them?
- size_in_pyc = (PYVERSION >= (3, 3))
-
- # Do we have async and await syntax?
- async_syntax = (PYVERSION >= (3, 5))
-
- # PEP 448 defined additional unpacking generalizations
- unpackings_pep448 = (PYVERSION >= (3, 5))
-
- # Can co_lnotab have negative deltas?
- negative_lnotab = (PYVERSION >= (3, 6)) and not (PYPY and PYPYVERSION < (7, 2))
-
- # Do .pyc files conform to PEP 552? Hash-based pyc's.
- hashed_pyc_pep552 = (PYVERSION >= (3, 7, 0, 'alpha', 4))
-
- # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It
- # used to be an empty string (meaning the current directory). It changed
- # to be the actual path to the current directory, so that os.chdir wouldn't
- # affect the outcome.
- actual_syspath0_dash_m = CPYTHON and (PYVERSION >= (3, 7, 0, 'beta', 3))
-
- # 3.7 changed how functions with only docstrings are numbered.
- docstring_only_function = (not PYPY) and ((3, 7, 0, 'beta', 5) <= PYVERSION <= (3, 10))
-
- # When a break/continue/return statement in a try block jumps to a finally
- # block, does the finally block do the break/continue/return (pre-3.8), or
- # does the finally jump back to the break/continue/return (3.8) to do the
- # work?
- finally_jumps_back = ((3, 8) <= PYVERSION < (3, 10))
-
- # When a function is decorated, does the trace function get called for the
- # @-line and also the def-line (new behavior in 3.8)? Or just the @-line
- # (old behavior)?
- trace_decorated_def = (PYVERSION >= (3, 8))
-
- # Are while-true loops optimized into absolute jumps with no loop setup?
- nix_while_true = (PYVERSION >= (3, 8))
-
- # Python 3.9a1 made sys.argv[0] and other reported files absolute paths.
- report_absolute_files = (PYVERSION >= (3, 9))
-
- # Lines after break/continue/return/raise are no longer compiled into the
- # bytecode. They used to be marked as missing, now they aren't executable.
- omit_after_jump = pep626
-
- # PyPy has always omitted statements after return.
- omit_after_return = omit_after_jump or PYPY
-
- # Modules used to have firstlineno equal to the line number of the first
- # real line of code. Now they always start at 1.
- module_firstline_1 = pep626
-
- # Are "if 0:" lines (and similar) kept in the compiled code?
- keep_constant_test = pep626
-
-# Coverage.py specifics.
-
-# Are we using the C-implemented trace function?
-C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
-
-# Are we coverage-measuring ourselves?
-METACOV = os.getenv('COVERAGE_COVERAGE', '') != ''
-
-# Are we running our test suite?
-# Even when running tests, you can use COVERAGE_TESTING=0 to disable the
-# test-specific behavior like contracts.
-TESTING = os.getenv('COVERAGE_TESTING', '') == 'True'
diff --git a/contrib/python/coverage/py3/coverage/execfile.py b/contrib/python/coverage/py3/coverage/execfile.py
deleted file mode 100644
index 29409d517a..0000000000
--- a/contrib/python/coverage/py3/coverage/execfile.py
+++ /dev/null
@@ -1,362 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Execute files of Python code."""
-
-import inspect
-import marshal
-import os
-import struct
-import sys
-import types
-
-from coverage import env
-from coverage.backward import BUILTINS
-from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
-from coverage.files import canonical_filename, python_reported_file
-from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
-from coverage.phystokens import compile_unicode
-from coverage.python import get_python_source
-
-os = isolate_module(os)
-
-
-class DummyLoader(object):
- """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
-
- Currently only implements the .fullname attribute
- """
- def __init__(self, fullname, *_args):
- self.fullname = fullname
-
-
-if importlib_util_find_spec:
- def find_module(modulename):
- """Find the module named `modulename`.
-
- Returns the file path of the module, the name of the enclosing
- package, and the spec.
- """
- try:
- spec = importlib_util_find_spec(modulename)
- except ImportError as err:
- raise NoSource(str(err))
- if not spec:
- raise NoSource("No module named %r" % (modulename,))
- pathname = spec.origin
- packagename = spec.name
- if spec.submodule_search_locations:
- mod_main = modulename + ".__main__"
- spec = importlib_util_find_spec(mod_main)
- if not spec:
- raise NoSource(
- "No module named %s; "
- "%r is a package and cannot be directly executed"
- % (mod_main, modulename)
- )
- pathname = spec.origin
- packagename = spec.name
- packagename = packagename.rpartition(".")[0]
- return pathname, packagename, spec
-else:
- def find_module(modulename):
- """Find the module named `modulename`.
-
- Returns the file path of the module, the name of the enclosing
- package, and None (where a spec would have been).
- """
- openfile = None
- glo, loc = globals(), locals()
- try:
- # Search for the module - inside its parent package, if any - using
- # standard import mechanics.
- if '.' in modulename:
- packagename, name = modulename.rsplit('.', 1)
- package = __import__(packagename, glo, loc, ['__path__'])
- searchpath = package.__path__
- else:
- packagename, name = None, modulename
- searchpath = None # "top-level search" in imp.find_module()
- openfile, pathname, _ = imp.find_module(name, searchpath)
-
- # Complain if this is a magic non-file module.
- if openfile is None and pathname is None:
- raise NoSource(
- "module does not live in a file: %r" % modulename
- )
-
- # If `modulename` is actually a package, not a mere module, then we
- # pretend to be Python 2.7 and try running its __main__.py script.
- if openfile is None:
- packagename = modulename
- name = '__main__'
- package = __import__(packagename, glo, loc, ['__path__'])
- searchpath = package.__path__
- openfile, pathname, _ = imp.find_module(name, searchpath)
- except ImportError as err:
- raise NoSource(str(err))
- finally:
- if openfile:
- openfile.close()
-
- return pathname, packagename, None
-
-
-class PyRunner(object):
- """Multi-stage execution of Python code.
-
- This is meant to emulate real Python execution as closely as possible.
-
- """
- def __init__(self, args, as_module=False):
- self.args = args
- self.as_module = as_module
-
- self.arg0 = args[0]
- self.package = self.modulename = self.pathname = self.loader = self.spec = None
-
- def prepare(self):
- """Set sys.path properly.
-
- This needs to happen before any importing, and without importing anything.
- """
- if self.as_module:
- if env.PYBEHAVIOR.actual_syspath0_dash_m:
- path0 = os.getcwd()
- else:
- path0 = ""
- elif os.path.isdir(self.arg0):
- # Running a directory means running the __main__.py file in that
- # directory.
- path0 = self.arg0
- else:
- path0 = os.path.abspath(os.path.dirname(self.arg0))
-
- if os.path.isdir(sys.path[0]):
- # sys.path fakery. If we are being run as a command, then sys.path[0]
- # is the directory of the "coverage" script. If this is so, replace
- # sys.path[0] with the directory of the file we're running, or the
- # current directory when running modules. If it isn't so, then we
- # don't know what's going on, and just leave it alone.
- top_file = inspect.stack()[-1][0].f_code.co_filename
- sys_path_0_abs = os.path.abspath(sys.path[0])
- top_file_dir_abs = os.path.abspath(os.path.dirname(top_file))
- sys_path_0_abs = canonical_filename(sys_path_0_abs)
- top_file_dir_abs = canonical_filename(top_file_dir_abs)
- if sys_path_0_abs != top_file_dir_abs:
- path0 = None
-
- else:
- # sys.path[0] is a file. Is the next entry the directory containing
- # that file?
- if sys.path[1] == os.path.dirname(sys.path[0]):
- # Can it be right to always remove that?
- del sys.path[1]
-
- if path0 is not None:
- sys.path[0] = python_reported_file(path0)
-
- def _prepare2(self):
- """Do more preparation to run Python code.
-
- Includes finding the module to run and adjusting sys.argv[0].
- This method is allowed to import code.
-
- """
- if self.as_module:
- self.modulename = self.arg0
- pathname, self.package, self.spec = find_module(self.modulename)
- if self.spec is not None:
- self.modulename = self.spec.name
- self.loader = DummyLoader(self.modulename)
- self.pathname = os.path.abspath(pathname)
- self.args[0] = self.arg0 = self.pathname
- elif os.path.isdir(self.arg0):
- # Running a directory means running the __main__.py file in that
- # directory.
- for ext in [".py", ".pyc", ".pyo"]:
- try_filename = os.path.join(self.arg0, "__main__" + ext)
- if os.path.exists(try_filename):
- self.arg0 = try_filename
- break
- else:
- raise NoSource("Can't find '__main__' module in '%s'" % self.arg0)
-
- if env.PY2:
- self.arg0 = os.path.abspath(self.arg0)
-
- # Make a spec. I don't know if this is the right way to do it.
- try:
- import importlib.machinery
- except ImportError:
- pass
- else:
- try_filename = python_reported_file(try_filename)
- self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
- self.spec.has_location = True
- self.package = ""
- self.loader = DummyLoader("__main__")
- else:
- if env.PY3:
- self.loader = DummyLoader("__main__")
-
- self.arg0 = python_reported_file(self.arg0)
-
- def run(self):
- """Run the Python code!"""
-
- self._prepare2()
-
- # Create a module to serve as __main__
- main_mod = types.ModuleType('__main__')
-
- from_pyc = self.arg0.endswith((".pyc", ".pyo"))
- main_mod.__file__ = self.arg0
- if from_pyc:
- main_mod.__file__ = main_mod.__file__[:-1]
- if self.package is not None:
- main_mod.__package__ = self.package
- main_mod.__loader__ = self.loader
- if self.spec is not None:
- main_mod.__spec__ = self.spec
-
- main_mod.__builtins__ = BUILTINS
-
- sys.modules['__main__'] = main_mod
-
- # Set sys.argv properly.
- sys.argv = self.args
-
- try:
- # Make a code object somehow.
- if from_pyc:
- code = make_code_from_pyc(self.arg0)
- else:
- code = make_code_from_py(self.arg0)
- except CoverageException:
- raise
- except Exception as exc:
- msg = "Couldn't run '{filename}' as Python code: {exc.__class__.__name__}: {exc}"
- raise CoverageException(msg.format(filename=self.arg0, exc=exc))
-
- # Execute the code object.
- # Return to the original directory in case the test code exits in
- # a non-existent directory.
- cwd = os.getcwd()
- try:
- exec(code, main_mod.__dict__)
- except SystemExit: # pylint: disable=try-except-raise
- # The user called sys.exit(). Just pass it along to the upper
- # layers, where it will be handled.
- raise
- except Exception:
- # Something went wrong while executing the user code.
- # Get the exc_info, and pack them into an exception that we can
- # throw up to the outer loop. We peel one layer off the traceback
- # so that the coverage.py code doesn't appear in the final printed
- # traceback.
- typ, err, tb = sys.exc_info()
-
- # PyPy3 weirdness. If I don't access __context__, then somehow it
- # is non-None when the exception is reported at the upper layer,
- # and a nested exception is shown to the user. This getattr fixes
- # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
- getattr(err, '__context__', None)
-
- # Call the excepthook.
- try:
- if hasattr(err, "__traceback__"):
- err.__traceback__ = err.__traceback__.tb_next
- sys.excepthook(typ, err, tb.tb_next)
- except SystemExit: # pylint: disable=try-except-raise
- raise
- except Exception:
- # Getting the output right in the case of excepthook
- # shenanigans is kind of involved.
- sys.stderr.write("Error in sys.excepthook:\n")
- typ2, err2, tb2 = sys.exc_info()
- err2.__suppress_context__ = True
- if hasattr(err2, "__traceback__"):
- err2.__traceback__ = err2.__traceback__.tb_next
- sys.__excepthook__(typ2, err2, tb2.tb_next)
- sys.stderr.write("\nOriginal exception was:\n")
- raise ExceptionDuringRun(typ, err, tb.tb_next)
- else:
- sys.exit(1)
- finally:
- os.chdir(cwd)
-
-
-def run_python_module(args):
- """Run a Python module, as though with ``python -m name args...``.
-
- `args` is the argument array to present as sys.argv, including the first
- element naming the module being executed.
-
- This is a helper for tests, to encapsulate how to use PyRunner.
-
- """
- runner = PyRunner(args, as_module=True)
- runner.prepare()
- runner.run()
-
-
-def run_python_file(args):
- """Run a Python file as if it were the main program on the command line.
-
- `args` is the argument array to present as sys.argv, including the first
- element naming the file being executed. `package` is the name of the
- enclosing package, if any.
-
- This is a helper for tests, to encapsulate how to use PyRunner.
-
- """
- runner = PyRunner(args, as_module=False)
- runner.prepare()
- runner.run()
-
-
-def make_code_from_py(filename):
- """Get source from `filename` and make a code object of it."""
- # Open the source file.
- try:
- source = get_python_source(filename)
- except (IOError, NoSource):
- raise NoSource("No file to run: '%s'" % filename)
-
- code = compile_unicode(source, filename, "exec")
- return code
-
-
-def make_code_from_pyc(filename):
- """Get a code object from a .pyc file."""
- try:
- fpyc = open(filename, "rb")
- except IOError:
- raise NoCode("No file to run: '%s'" % filename)
-
- with fpyc:
- # First four bytes are a version-specific magic number. It has to
- # match or we won't run the file.
- magic = fpyc.read(4)
- if magic != PYC_MAGIC_NUMBER:
- raise NoCode("Bad magic number in .pyc file: {} != {}".format(magic, PYC_MAGIC_NUMBER))
-
- date_based = True
- if env.PYBEHAVIOR.hashed_pyc_pep552:
- flags = struct.unpack('<L', fpyc.read(4))[0]
- hash_based = flags & 0x01
- if hash_based:
- fpyc.read(8) # Skip the hash.
- date_based = False
- if date_based:
- # Skip the junk in the header that we don't need.
- fpyc.read(4) # Skip the moddate.
- if env.PYBEHAVIOR.size_in_pyc:
- # 3.3 added another long to the header (size), skip it.
- fpyc.read(4)
-
- # The rest of the file is the code object we want.
- code = marshal.load(fpyc)
-
- return code
diff --git a/contrib/python/coverage/py3/coverage/files.py b/contrib/python/coverage/py3/coverage/files.py
deleted file mode 100644
index 5133ad07f3..0000000000
--- a/contrib/python/coverage/py3/coverage/files.py
+++ /dev/null
@@ -1,441 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""File wrangling."""
-
-import hashlib
-import fnmatch
-import ntpath
-import os
-import os.path
-import posixpath
-import re
-import sys
-
-from coverage import env
-from coverage.backward import unicode_class
-from coverage.misc import contract, CoverageException, join_regex, isolate_module
-
-
-os = isolate_module(os)
-
-
-def set_relative_directory():
- """Set the directory that `relative_filename` will be relative to."""
- global RELATIVE_DIR, CANONICAL_FILENAME_CACHE
-
- # The absolute path to our current directory.
- RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep)
-
- # Cache of results of calling the canonical_filename() method, to
- # avoid duplicating work.
- CANONICAL_FILENAME_CACHE = {}
-
-
-def relative_directory():
- """Return the directory that `relative_filename` is relative to."""
- return RELATIVE_DIR
-
-
-@contract(returns='unicode')
-def relative_filename(filename):
- """Return the relative form of `filename`.
-
- The file name will be relative to the current directory when the
- `set_relative_directory` was called.
-
- """
- fnorm = os.path.normcase(filename)
- if fnorm.startswith(RELATIVE_DIR):
- filename = filename[len(RELATIVE_DIR):]
- return unicode_filename(filename)
-
-
-@contract(returns='unicode')
-def canonical_filename(filename):
- """Return a canonical file name for `filename`.
-
- An absolute path with no redundant components and normalized case.
-
- """
- if filename not in CANONICAL_FILENAME_CACHE:
- cf = filename
- if not os.path.isabs(filename):
- for path in [os.curdir] + sys.path:
- if path is None:
- continue
- f = os.path.join(path, filename)
- try:
- exists = os.path.exists(f)
- except UnicodeError:
- exists = False
- if exists:
- cf = f
- break
- cf = abs_file(cf)
- CANONICAL_FILENAME_CACHE[filename] = cf
- return CANONICAL_FILENAME_CACHE[filename]
-
-if getattr(sys, 'is_standalone_binary', False):
- # filename for py files in binary is already canonical,
- # it's relative to the arcadia root
- def canonical_filename(filename):
- # next assert does not needed in case when we load coverage from not arcadia source in arcadia binary
- # assert not filename.startswith("/"), filename
- return filename
-
-MAX_FLAT = 200
-
-@contract(filename='unicode', returns='unicode')
-def flat_rootname(filename):
- """A base for a flat file name to correspond to this file.
-
- Useful for writing files about the code where you want all the files in
- the same directory, but need to differentiate same-named files from
- different directories.
-
- For example, the file a/b/c.py will return 'a_b_c_py'
-
- """
- name = ntpath.splitdrive(filename)[1]
- name = re.sub(r"[\\/.:]", "_", name)
- if len(name) > MAX_FLAT:
- h = hashlib.sha1(name.encode('UTF-8')).hexdigest()
- name = name[-(MAX_FLAT-len(h)-1):] + '_' + h
- return name
-
-
-if env.WINDOWS:
-
- _ACTUAL_PATH_CACHE = {}
- _ACTUAL_PATH_LIST_CACHE = {}
-
- def actual_path(path):
- """Get the actual path of `path`, including the correct case."""
- if env.PY2 and isinstance(path, unicode_class):
- path = path.encode(sys.getfilesystemencoding())
- if path in _ACTUAL_PATH_CACHE:
- return _ACTUAL_PATH_CACHE[path]
-
- head, tail = os.path.split(path)
- if not tail:
- # This means head is the drive spec: normalize it.
- actpath = head.upper()
- elif not head:
- actpath = tail
- else:
- head = actual_path(head)
- if head in _ACTUAL_PATH_LIST_CACHE:
- files = _ACTUAL_PATH_LIST_CACHE[head]
- else:
- try:
- files = os.listdir(head)
- except Exception:
- # This will raise OSError, or this bizarre TypeError:
- # https://bugs.python.org/issue1776160
- files = []
- _ACTUAL_PATH_LIST_CACHE[head] = files
- normtail = os.path.normcase(tail)
- for f in files:
- if os.path.normcase(f) == normtail:
- tail = f
- break
- actpath = os.path.join(head, tail)
- _ACTUAL_PATH_CACHE[path] = actpath
- return actpath
-
-else:
- def actual_path(filename):
- """The actual path for non-Windows platforms."""
- return filename
-
-
-if env.PY2:
- @contract(returns='unicode')
- def unicode_filename(filename):
- """Return a Unicode version of `filename`."""
- if isinstance(filename, str):
- encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
- filename = filename.decode(encoding, "replace")
- return filename
-else:
- @contract(filename='unicode', returns='unicode')
- def unicode_filename(filename):
- """Return a Unicode version of `filename`."""
- return filename
-
-
-@contract(returns='unicode')
-def abs_file(path):
- """Return the absolute normalized form of `path`."""
- try:
- path = os.path.realpath(path)
- except UnicodeError:
- pass
- path = os.path.abspath(path)
- path = actual_path(path)
- path = unicode_filename(path)
- return path
-
-
-def python_reported_file(filename):
- """Return the string as Python would describe this file name."""
- if env.PYBEHAVIOR.report_absolute_files:
- filename = os.path.abspath(filename)
- return filename
-
-
-RELATIVE_DIR = None
-CANONICAL_FILENAME_CACHE = None
-set_relative_directory()
-
-
-def isabs_anywhere(filename):
- """Is `filename` an absolute path on any OS?"""
- return ntpath.isabs(filename) or posixpath.isabs(filename)
-
-
-def prep_patterns(patterns):
- """Prepare the file patterns for use in a `FnmatchMatcher`.
-
- If a pattern starts with a wildcard, it is used as a pattern
- as-is. If it does not start with a wildcard, then it is made
- absolute with the current directory.
-
- If `patterns` is None, an empty list is returned.
-
- """
- prepped = []
- for p in patterns or []:
- if p.startswith(("*", "?")):
- prepped.append(p)
- else:
- prepped.append(abs_file(p))
- return prepped
-
-
-class TreeMatcher(object):
- """A matcher for files in a tree.
-
- Construct with a list of paths, either files or directories. Paths match
- with the `match` method if they are one of the files, or if they are
- somewhere in a subtree rooted at one of the directories.
-
- """
- def __init__(self, paths):
- self.paths = list(paths)
-
- def __repr__(self):
- return "<TreeMatcher %r>" % self.paths
-
- def info(self):
- """A list of strings for displaying when dumping state."""
- return self.paths
-
- def match(self, fpath):
- """Does `fpath` indicate a file in one of our trees?"""
- for p in self.paths:
- if fpath.startswith(p):
- if fpath == p:
- # This is the same file!
- return True
- if fpath[len(p)] == os.sep:
- # This is a file in the directory
- return True
- return False
-
-
-class ModuleMatcher(object):
- """A matcher for modules in a tree."""
- def __init__(self, module_names):
- self.modules = list(module_names)
-
- def __repr__(self):
- return "<ModuleMatcher %r>" % (self.modules)
-
- def info(self):
- """A list of strings for displaying when dumping state."""
- return self.modules
-
- def match(self, module_name):
- """Does `module_name` indicate a module in one of our packages?"""
- if not module_name:
- return False
-
- for m in self.modules:
- if module_name.startswith(m):
- if module_name == m:
- return True
- if module_name[len(m)] == '.':
- # This is a module in the package
- return True
-
- return False
-
-
-class FnmatchMatcher(object):
- """A matcher for files by file name pattern."""
- def __init__(self, pats):
- self.pats = list(pats)
- self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS)
-
- def __repr__(self):
- return "<FnmatchMatcher %r>" % self.pats
-
- def info(self):
- """A list of strings for displaying when dumping state."""
- return self.pats
-
- def match(self, fpath):
- """Does `fpath` match one of our file name patterns?"""
- return self.re.match(fpath) is not None
-
-
-def sep(s):
- """Find the path separator used in this string, or os.sep if none."""
- sep_match = re.search(r"[\\/]", s)
- if sep_match:
- the_sep = sep_match.group(0)
- else:
- the_sep = os.sep
- return the_sep
-
-
-def fnmatches_to_regex(patterns, case_insensitive=False, partial=False):
- """Convert fnmatch patterns to a compiled regex that matches any of them.
-
- Slashes are always converted to match either slash or backslash, for
- Windows support, even when running elsewhere.
-
- If `partial` is true, then the pattern will match if the target string
- starts with the pattern. Otherwise, it must match the entire string.
-
- Returns: a compiled regex object. Use the .match method to compare target
- strings.
-
- """
- regexes = (fnmatch.translate(pattern) for pattern in patterns)
- # Python3.7 fnmatch translates "/" as "/". Before that, it translates as "\/",
- # so we have to deal with maybe a backslash.
- regexes = (re.sub(r"\\?/", r"[\\\\/]", regex) for regex in regexes)
-
- if partial:
- # fnmatch always adds a \Z to match the whole string, which we don't
- # want, so we remove the \Z. While removing it, we only replace \Z if
- # followed by paren (introducing flags), or at end, to keep from
- # destroying a literal \Z in the pattern.
- regexes = (re.sub(r'\\Z(\(\?|$)', r'\1', regex) for regex in regexes)
-
- flags = 0
- if case_insensitive:
- flags |= re.IGNORECASE
- compiled = re.compile(join_regex(regexes), flags=flags)
-
- return compiled
-
-
-class PathAliases(object):
- """A collection of aliases for paths.
-
- When combining data files from remote machines, often the paths to source
- code are different, for example, due to OS differences, or because of
- serialized checkouts on continuous integration machines.
-
- A `PathAliases` object tracks a list of pattern/result pairs, and can
- map a path through those aliases to produce a unified path.
-
- """
- def __init__(self):
- self.aliases = []
-
- def pprint(self): # pragma: debugging
- """Dump the important parts of the PathAliases, for debugging."""
- for regex, result in self.aliases:
- print("{!r} --> {!r}".format(regex.pattern, result))
-
- def add(self, pattern, result):
- """Add the `pattern`/`result` pair to the list of aliases.
-
- `pattern` is an `fnmatch`-style pattern. `result` is a simple
- string. When mapping paths, if a path starts with a match against
- `pattern`, then that match is replaced with `result`. This models
- isomorphic source trees being rooted at different places on two
- different machines.
-
- `pattern` can't end with a wildcard component, since that would
- match an entire tree, and not just its root.
-
- """
- pattern_sep = sep(pattern)
-
- if len(pattern) > 1:
- pattern = pattern.rstrip(r"\/")
-
- # The pattern can't end with a wildcard component.
- if pattern.endswith("*"):
- raise CoverageException("Pattern must not end with wildcards.")
-
- # The pattern is meant to match a filepath. Let's make it absolute
- # unless it already is, or is meant to match any prefix.
- if not pattern.startswith('*') and not isabs_anywhere(pattern +
- pattern_sep):
- pattern = abs_file(pattern)
- if not pattern.endswith(pattern_sep):
- pattern += pattern_sep
-
- # Make a regex from the pattern.
- regex = fnmatches_to_regex([pattern], case_insensitive=True, partial=True)
-
- # Normalize the result: it must end with a path separator.
- result_sep = sep(result)
- result = result.rstrip(r"\/") + result_sep
- self.aliases.append((regex, result))
-
- def map(self, path):
- """Map `path` through the aliases.
-
- `path` is checked against all of the patterns. The first pattern to
- match is used to replace the root of the path with the result root.
- Only one pattern is ever used. If no patterns match, `path` is
- returned unchanged.
-
- The separator style in the result is made to match that of the result
- in the alias.
-
- Returns the mapped path. If a mapping has happened, this is a
- canonical path. If no mapping has happened, it is the original value
- of `path` unchanged.
-
- """
- for regex, result in self.aliases:
- m = regex.match(path)
- if m:
- new = path.replace(m.group(0), result)
- new = new.replace(sep(path), sep(result))
- new = canonical_filename(new)
- return new
- return path
-
-
-def find_python_files(dirname):
- """Yield all of the importable Python files in `dirname`, recursively.
-
- To be importable, the files have to be in a directory with a __init__.py,
- except for `dirname` itself, which isn't required to have one. The
- assumption is that `dirname` was specified directly, so the user knows
- best, but sub-directories are checked for a __init__.py to be sure we only
- find the importable files.
-
- """
- for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)):
- if i > 0 and '__init__.py' not in filenames:
- # If a directory doesn't have __init__.py, then it isn't
- # importable and neither are its files
- del dirnames[:]
- continue
- for filename in filenames:
- # We're only interested in files that look like reasonable Python
- # files: Must end with .py or .pyw, and must not have certain funny
- # characters that probably mean they are editor junk.
- if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename):
- yield os.path.join(dirpath, filename)
diff --git a/contrib/python/coverage/py3/coverage/fullcoverage/encodings.py b/contrib/python/coverage/py3/coverage/fullcoverage/encodings.py
deleted file mode 100644
index aeb416e406..0000000000
--- a/contrib/python/coverage/py3/coverage/fullcoverage/encodings.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Imposter encodings module that installs a coverage-style tracer.
-
-This is NOT the encodings module; it is an imposter that sets up tracing
-instrumentation and then replaces itself with the real encodings module.
-
-If the directory that holds this file is placed first in the PYTHONPATH when
-using "coverage" to run Python's tests, then this file will become the very
-first module imported by the internals of Python 3. It installs a
-coverage.py-compatible trace function that can watch Standard Library modules
-execute from the very earliest stages of Python's own boot process. This fixes
-a problem with coverage.py - that it starts too late to trace the coverage of
-many of the most fundamental modules in the Standard Library.
-
-"""
-
-import sys
-
-class FullCoverageTracer(object):
- def __init__(self):
- # `traces` is a list of trace events. Frames are tricky: the same
- # frame object is used for a whole scope, with new line numbers
- # written into it. So in one scope, all the frame objects are the
- # same object, and will eventually all will point to the last line
- # executed. So we keep the line numbers alongside the frames.
- # The list looks like:
- #
- # traces = [
- # ((frame, event, arg), lineno), ...
- # ]
- #
- self.traces = []
-
- def fullcoverage_trace(self, *args):
- frame, event, arg = args
- self.traces.append((args, frame.f_lineno))
- return self.fullcoverage_trace
-
-sys.settrace(FullCoverageTracer().fullcoverage_trace)
-
-# In coverage/files.py is actual_filename(), which uses glob.glob. I don't
-# understand why, but that use of glob borks everything if fullcoverage is in
-# effect. So here we make an ugly hail-mary pass to switch off glob.glob over
-# there. This means when using fullcoverage, Windows path names will not be
-# their actual case.
-
-#sys.fullcoverage = True
-
-# Finally, remove our own directory from sys.path; remove ourselves from
-# sys.modules; and re-import "encodings", which will be the real package
-# this time. Note that the delete from sys.modules dictionary has to
-# happen last, since all of the symbols in this module will become None
-# at that exact moment, including "sys".
-
-parentdir = max(filter(__file__.startswith, sys.path), key=len)
-sys.path.remove(parentdir)
-del sys.modules['encodings']
-import encodings
diff --git a/contrib/python/coverage/py3/coverage/html.py b/contrib/python/coverage/py3/coverage/html.py
deleted file mode 100644
index 9d8e342716..0000000000
--- a/contrib/python/coverage/py3/coverage/html.py
+++ /dev/null
@@ -1,539 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""HTML reporting for coverage.py."""
-
-import datetime
-import json
-import os
-import re
-import shutil
-import sys
-
-import coverage
-from coverage import env
-from coverage.backward import iitems, SimpleNamespace, format_local_datetime
-from coverage.data import add_data_to_hash
-from coverage.files import flat_rootname
-from coverage.misc import CoverageException, ensure_dir, file_be_gone, Hasher, isolate_module
-from coverage.report import get_analysis_to_report
-from coverage.results import Numbers
-from coverage.templite import Templite
-
-os = isolate_module(os)
-
-
-# Static files are looked for in a list of places.
-STATIC_PATH = [
- # The place Debian puts system Javascript libraries.
- "/usr/share/javascript",
-
- # Our htmlfiles directory.
- os.path.join(os.path.dirname(__file__), "htmlfiles"),
-]
-
-
-def data_filename(fname, pkgdir=""):
- """Return the path to a data file of ours.
-
- The file is searched for on `STATIC_PATH`, and the first place it's found,
- is returned.
-
- Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
- is provided, at that sub-directory.
-
- """
- tried = []
- for static_dir in STATIC_PATH:
- static_filename = os.path.join(static_dir, fname)
- if os.path.exists(static_filename):
- return static_filename
- else:
- tried.append(static_filename)
- if pkgdir:
- static_filename = os.path.join(static_dir, pkgdir, fname)
- if os.path.exists(static_filename):
- return static_filename
- else:
- tried.append(static_filename)
- raise CoverageException(
- "Couldn't find static file %r from %r, tried: %r" % (fname, os.getcwd(), tried)
- )
-
-
-def get_htmlfiles_resource(name):
- import pkgutil
- return pkgutil.get_data(__package__, 'htmlfiles/' + name)
-
-
-def read_data(fname):
- """Return the contents of a data file of ours."""
- if getattr(sys, 'is_standalone_binary', False):
- res_buf = get_htmlfiles_resource(fname).decode()
- if res_buf is not None:
- return res_buf
-
- with open(data_filename(fname)) as data_file:
- return data_file.read()
-
-
-def write_html(fname, html):
- """Write `html` to `fname`, properly encoded."""
- html = re.sub(r"(\A\s+)|(\s+$)", "", html, flags=re.MULTILINE) + "\n"
- with open(fname, "wb") as fout:
- fout.write(html.encode('ascii', 'xmlcharrefreplace'))
-
-
-class HtmlDataGeneration(object):
- """Generate structured data to be turned into HTML reports."""
-
- EMPTY = "(empty)"
-
- def __init__(self, cov):
- self.coverage = cov
- self.config = self.coverage.config
- data = self.coverage.get_data()
- self.has_arcs = data.has_arcs()
- if self.config.show_contexts:
- if data.measured_contexts() == {""}:
- self.coverage._warn("No contexts were measured")
- data.set_query_contexts(self.config.report_contexts)
-
- def data_for_file(self, fr, analysis):
- """Produce the data needed for one file's report."""
- if self.has_arcs:
- missing_branch_arcs = analysis.missing_branch_arcs()
- arcs_executed = analysis.arcs_executed()
-
- if self.config.show_contexts:
- contexts_by_lineno = analysis.data.contexts_by_lineno(analysis.filename)
-
- lines = []
-
- for lineno, tokens in enumerate(fr.source_token_lines(), start=1):
- # Figure out how to mark this line.
- category = None
- short_annotations = []
- long_annotations = []
-
- if lineno in analysis.excluded:
- category = 'exc'
- elif lineno in analysis.missing:
- category = 'mis'
- elif self.has_arcs and lineno in missing_branch_arcs:
- category = 'par'
- for b in missing_branch_arcs[lineno]:
- if b < 0:
- short_annotations.append("exit")
- else:
- short_annotations.append(b)
- long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed))
- elif lineno in analysis.statements:
- category = 'run'
-
- contexts = contexts_label = None
- context_list = None
- if category and self.config.show_contexts:
- contexts = sorted(c or self.EMPTY for c in contexts_by_lineno[lineno])
- if contexts == [self.EMPTY]:
- contexts_label = self.EMPTY
- else:
- contexts_label = "{} ctx".format(len(contexts))
- context_list = contexts
-
- lines.append(SimpleNamespace(
- tokens=tokens,
- number=lineno,
- category=category,
- statement=(lineno in analysis.statements),
- contexts=contexts,
- contexts_label=contexts_label,
- context_list=context_list,
- short_annotations=short_annotations,
- long_annotations=long_annotations,
- ))
-
- file_data = SimpleNamespace(
- relative_filename=fr.relative_filename(),
- nums=analysis.numbers,
- lines=lines,
- )
-
- return file_data
-
-
-class HtmlReporter(object):
- """HTML reporting."""
-
- # These files will be copied from the htmlfiles directory to the output
- # directory.
- STATIC_FILES = [
- ("style.css", ""),
- ("jquery.min.js", "jquery"),
- ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"),
- ("jquery.hotkeys.js", "jquery-hotkeys"),
- ("jquery.isonscreen.js", "jquery-isonscreen"),
- ("jquery.tablesorter.min.js", "jquery-tablesorter"),
- ("coverage_html.js", ""),
- ("keybd_closed.png", ""),
- ("keybd_open.png", ""),
- ("favicon_32.png", ""),
- ]
-
- def __init__(self, cov):
- self.coverage = cov
- self.config = self.coverage.config
- self.directory = self.config.html_dir
-
- self.skip_covered = self.config.html_skip_covered
- if self.skip_covered is None:
- self.skip_covered = self.config.skip_covered
- self.skip_empty = self.config.html_skip_empty
- if self.skip_empty is None:
- self.skip_empty= self.config.skip_empty
-
- title = self.config.html_title
- if env.PY2:
- title = title.decode("utf8")
-
- if self.config.extra_css:
- self.extra_css = os.path.basename(self.config.extra_css)
- else:
- self.extra_css = None
-
- self.data = self.coverage.get_data()
- self.has_arcs = self.data.has_arcs()
-
- self.file_summaries = []
- self.all_files_nums = []
- self.incr = IncrementalChecker(self.directory)
- self.datagen = HtmlDataGeneration(self.coverage)
- self.totals = Numbers()
-
- self.template_globals = {
- # Functions available in the templates.
- 'escape': escape,
- 'pair': pair,
- 'len': len,
-
- # Constants for this report.
- '__url__': coverage.__url__,
- '__version__': coverage.__version__,
- 'title': title,
- 'time_stamp': format_local_datetime(datetime.datetime.now()),
- 'extra_css': self.extra_css,
- 'has_arcs': self.has_arcs,
- 'show_contexts': self.config.show_contexts,
-
- # Constants for all reports.
- # These css classes determine which lines are highlighted by default.
- 'category': {
- 'exc': 'exc show_exc',
- 'mis': 'mis show_mis',
- 'par': 'par run show_par',
- 'run': 'run',
- }
- }
- self.pyfile_html_source = read_data("pyfile.html")
- self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)
-
- def report(self, morfs):
- """Generate an HTML report for `morfs`.
-
- `morfs` is a list of modules or file names.
-
- """
- # Read the status data and check that this run used the same
- # global data as the last run.
- self.incr.read()
- self.incr.check_global_data(self.config, self.pyfile_html_source)
-
- # Process all the files.
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.html_file(fr, analysis)
-
- if not self.all_files_nums:
- raise CoverageException("No data to report.")
-
- self.totals = sum(self.all_files_nums)
-
- # Write the index file.
- self.index_file()
-
- self.make_local_static_report_files()
- return self.totals.n_statements and self.totals.pc_covered
-
- def make_local_static_report_files(self):
- """Make local instances of static files for HTML report."""
- # The files we provide must always be copied.
- for static, pkgdir in self.STATIC_FILES:
- if getattr(sys, 'is_standalone_binary', False):
- data = get_htmlfiles_resource(static)
- if data is None:
- raise IOError("No such resource: " + static)
-
- with open(os.path.join(self.directory, static), "wb") as afile:
- afile.write(data)
- else:
- shutil.copyfile(
- data_filename(static, pkgdir),
- os.path.join(self.directory, static)
- )
-
- # The user may have extra CSS they want copied.
- if self.extra_css:
- shutil.copyfile(
- self.config.extra_css,
- os.path.join(self.directory, self.extra_css)
- )
-
- def html_file(self, fr, analysis):
- """Generate an HTML file for one source file."""
- rootname = flat_rootname(fr.relative_filename())
- html_filename = rootname + ".html"
- ensure_dir(self.directory)
- html_path = os.path.join(self.directory, html_filename)
-
- # Get the numbers for this file.
- nums = analysis.numbers
- self.all_files_nums.append(nums)
-
- if self.skip_covered:
- # Don't report on 100% files.
- no_missing_lines = (nums.n_missing == 0)
- no_missing_branches = (nums.n_partial_branches == 0)
- if no_missing_lines and no_missing_branches:
- # If there's an existing file, remove it.
- file_be_gone(html_path)
- return
-
- if self.skip_empty:
- # Don't report on empty files.
- if nums.n_statements == 0:
- file_be_gone(html_path)
- return
-
- # Find out if the file on disk is already correct.
- if self.incr.can_skip_file(self.data, fr, rootname):
- self.file_summaries.append(self.incr.index_info(rootname))
- return
-
- # Write the HTML page for this file.
- file_data = self.datagen.data_for_file(fr, analysis)
- for ldata in file_data.lines:
- # Build the HTML for the line.
- html = []
- for tok_type, tok_text in ldata.tokens:
- if tok_type == "ws":
- html.append(escape(tok_text))
- else:
- tok_html = escape(tok_text) or '&nbsp;'
- html.append(
- u'<span class="{}">{}</span>'.format(tok_type, tok_html)
- )
- ldata.html = ''.join(html)
-
- if ldata.short_annotations:
- # 202F is NARROW NO-BREAK SPACE.
- # 219B is RIGHTWARDS ARROW WITH STROKE.
- ldata.annotate = u",&nbsp;&nbsp; ".join(
- u"{}&#x202F;&#x219B;&#x202F;{}".format(ldata.number, d)
- for d in ldata.short_annotations
- )
- else:
- ldata.annotate = None
-
- if ldata.long_annotations:
- longs = ldata.long_annotations
- if len(longs) == 1:
- ldata.annotate_long = longs[0]
- else:
- ldata.annotate_long = u"{:d} missed branches: {}".format(
- len(longs),
- u", ".join(
- u"{:d}) {}".format(num, ann_long)
- for num, ann_long in enumerate(longs, start=1)
- ),
- )
- else:
- ldata.annotate_long = None
-
- css_classes = []
- if ldata.category:
- css_classes.append(self.template_globals['category'][ldata.category])
- ldata.css_class = ' '.join(css_classes) or "pln"
-
- html = self.source_tmpl.render(file_data.__dict__)
- write_html(html_path, html)
-
- # Save this file's information for the index file.
- index_info = {
- 'nums': nums,
- 'html_filename': html_filename,
- 'relative_filename': fr.relative_filename(),
- }
- self.file_summaries.append(index_info)
- self.incr.set_index_info(rootname, index_info)
-
- def index_file(self):
- """Write the index.html file for this report."""
- index_tmpl = Templite(read_data("index.html"), self.template_globals)
-
- html = index_tmpl.render({
- 'files': self.file_summaries,
- 'totals': self.totals,
- })
-
- write_html(os.path.join(self.directory, "index.html"), html)
-
- # Write the latest hashes for next time.
- self.incr.write()
-
-
-class IncrementalChecker(object):
- """Logic and data to support incremental reporting."""
-
- STATUS_FILE = "status.json"
- STATUS_FORMAT = 2
-
- # pylint: disable=wrong-spelling-in-comment,useless-suppression
- # The data looks like:
- #
- # {
- # "format": 2,
- # "globals": "540ee119c15d52a68a53fe6f0897346d",
- # "version": "4.0a1",
- # "files": {
- # "cogapp___init__": {
- # "hash": "e45581a5b48f879f301c0f30bf77a50c",
- # "index": {
- # "html_filename": "cogapp___init__.html",
- # "relative_filename": "cogapp/__init__",
- # "nums": [ 1, 14, 0, 0, 0, 0, 0 ]
- # }
- # },
- # ...
- # "cogapp_whiteutils": {
- # "hash": "8504bb427fc488c4176809ded0277d51",
- # "index": {
- # "html_filename": "cogapp_whiteutils.html",
- # "relative_filename": "cogapp/whiteutils",
- # "nums": [ 1, 59, 0, 1, 28, 2, 2 ]
- # }
- # }
- # }
- # }
-
- def __init__(self, directory):
- self.directory = directory
- self.reset()
-
- def reset(self):
- """Initialize to empty. Causes all files to be reported."""
- self.globals = ''
- self.files = {}
-
- def read(self):
- """Read the information we stored last time."""
- usable = False
- try:
- status_file = os.path.join(self.directory, self.STATUS_FILE)
- with open(status_file) as fstatus:
- status = json.load(fstatus)
- except (IOError, ValueError):
- usable = False
- else:
- usable = True
- if status['format'] != self.STATUS_FORMAT:
- usable = False
- elif status['version'] != coverage.__version__:
- usable = False
-
- if usable:
- self.files = {}
- for filename, fileinfo in iitems(status['files']):
- fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums'])
- self.files[filename] = fileinfo
- self.globals = status['globals']
- else:
- self.reset()
-
- def write(self):
- """Write the current status."""
- status_file = os.path.join(self.directory, self.STATUS_FILE)
- files = {}
- for filename, fileinfo in iitems(self.files):
- fileinfo['index']['nums'] = fileinfo['index']['nums'].init_args()
- files[filename] = fileinfo
-
- status = {
- 'format': self.STATUS_FORMAT,
- 'version': coverage.__version__,
- 'globals': self.globals,
- 'files': files,
- }
- with open(status_file, "w") as fout:
- json.dump(status, fout, separators=(',', ':'))
-
- def check_global_data(self, *data):
- """Check the global data that can affect incremental reporting."""
- m = Hasher()
- for d in data:
- m.update(d)
- these_globals = m.hexdigest()
- if self.globals != these_globals:
- self.reset()
- self.globals = these_globals
-
- def can_skip_file(self, data, fr, rootname):
- """Can we skip reporting this file?
-
- `data` is a CoverageData object, `fr` is a `FileReporter`, and
- `rootname` is the name being used for the file.
- """
- m = Hasher()
- m.update(fr.source().encode('utf-8'))
- add_data_to_hash(data, fr.filename, m)
- this_hash = m.hexdigest()
-
- that_hash = self.file_hash(rootname)
-
- if this_hash == that_hash:
- # Nothing has changed to require the file to be reported again.
- return True
- else:
- self.set_file_hash(rootname, this_hash)
- return False
-
- def file_hash(self, fname):
- """Get the hash of `fname`'s contents."""
- return self.files.get(fname, {}).get('hash', '')
-
- def set_file_hash(self, fname, val):
- """Set the hash of `fname`'s contents."""
- self.files.setdefault(fname, {})['hash'] = val
-
- def index_info(self, fname):
- """Get the information for index.html for `fname`."""
- return self.files.get(fname, {}).get('index', {})
-
- def set_index_info(self, fname, info):
- """Set the information for index.html for `fname`."""
- self.files.setdefault(fname, {})['index'] = info
-
-
-# Helpers for templates and generating HTML
-
-def escape(t):
- """HTML-escape the text in `t`.
-
- This is only suitable for HTML text, not attributes.
-
- """
- # Convert HTML special chars into HTML entities.
- return t.replace("&", "&amp;").replace("<", "&lt;")
-
-
-def pair(ratio):
- """Format a pair of numbers so JavaScript can read them in an attribute."""
- return "%s %s" % ratio
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js b/contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js
deleted file mode 100644
index 27b49b36f9..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js
+++ /dev/null
@@ -1,616 +0,0 @@
-// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-// Coverage.py HTML report browser code.
-/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
-/*global coverage: true, document, window, $ */
-
-coverage = {};
-
-// Find all the elements with shortkey_* class, and use them to assign a shortcut key.
-coverage.assign_shortkeys = function () {
- $("*[class*='shortkey_']").each(function (i, e) {
- $.each($(e).attr("class").split(" "), function (i, c) {
- if (/^shortkey_/.test(c)) {
- $(document).bind('keydown', c.substr(9), function () {
- $(e).click();
- });
- }
- });
- });
-};
-
-// Create the events for the help panel.
-coverage.wire_up_help_panel = function () {
- $("#keyboard_icon").click(function () {
- // Show the help panel, and position it so the keyboard icon in the
- // panel is in the same place as the keyboard icon in the header.
- $(".help_panel").show();
- var koff = $("#keyboard_icon").offset();
- var poff = $("#panel_icon").position();
- $(".help_panel").offset({
- top: koff.top-poff.top,
- left: koff.left-poff.left
- });
- });
- $("#panel_icon").click(function () {
- $(".help_panel").hide();
- });
-};
-
-// Create the events for the filter box.
-coverage.wire_up_filter = function () {
- // Cache elements.
- var table = $("table.index");
- var table_rows = table.find("tbody tr");
- var table_row_names = table_rows.find("td.name a");
- var no_rows = $("#no_rows");
-
- // Create a duplicate table footer that we can modify with dynamic summed values.
- var table_footer = $("table.index tfoot tr");
- var table_dynamic_footer = table_footer.clone();
- table_dynamic_footer.attr('class', 'total_dynamic hidden');
- table_footer.after(table_dynamic_footer);
-
- // Observe filter keyevents.
- $("#filter").on("keyup change", $.debounce(150, function (event) {
- var filter_value = $(this).val();
-
- if (filter_value === "") {
- // Filter box is empty, remove all filtering.
- table_rows.removeClass("hidden");
-
- // Show standard footer, hide dynamic footer.
- table_footer.removeClass("hidden");
- table_dynamic_footer.addClass("hidden");
-
- // Hide placeholder, show table.
- if (no_rows.length > 0) {
- no_rows.hide();
- }
- table.show();
-
- }
- else {
- // Filter table items by value.
- var hidden = 0;
- var shown = 0;
-
- // Hide / show elements.
- $.each(table_row_names, function () {
- var element = $(this).parents("tr");
-
- if ($(this).text().indexOf(filter_value) === -1) {
- // hide
- element.addClass("hidden");
- hidden++;
- }
- else {
- // show
- element.removeClass("hidden");
- shown++;
- }
- });
-
- // Show placeholder if no rows will be displayed.
- if (no_rows.length > 0) {
- if (shown === 0) {
- // Show placeholder, hide table.
- no_rows.show();
- table.hide();
- }
- else {
- // Hide placeholder, show table.
- no_rows.hide();
- table.show();
- }
- }
-
- // Manage dynamic header:
- if (hidden > 0) {
- // Calculate new dynamic sum values based on visible rows.
- for (var column = 2; column < 20; column++) {
- // Calculate summed value.
- var cells = table_rows.find('td:nth-child(' + column + ')');
- if (!cells.length) {
- // No more columns...!
- break;
- }
-
- var sum = 0, numer = 0, denom = 0;
- $.each(cells.filter(':visible'), function () {
- var ratio = $(this).data("ratio");
- if (ratio) {
- var splitted = ratio.split(" ");
- numer += parseInt(splitted[0], 10);
- denom += parseInt(splitted[1], 10);
- }
- else {
- sum += parseInt(this.innerHTML, 10);
- }
- });
-
- // Get footer cell element.
- var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')');
-
- // Set value into dynamic footer cell element.
- if (cells[0].innerHTML.indexOf('%') > -1) {
- // Percentage columns use the numerator and denominator,
- // and adapt to the number of decimal places.
- var match = /\.([0-9]+)/.exec(cells[0].innerHTML);
- var places = 0;
- if (match) {
- places = match[1].length;
- }
- var pct = numer * 100 / denom;
- footer_cell.text(pct.toFixed(places) + '%');
- }
- else {
- footer_cell.text(sum);
- }
- }
-
- // Hide standard footer, show dynamic footer.
- table_footer.addClass("hidden");
- table_dynamic_footer.removeClass("hidden");
- }
- else {
- // Show standard footer, hide dynamic footer.
- table_footer.removeClass("hidden");
- table_dynamic_footer.addClass("hidden");
- }
- }
- }));
-
- // Trigger change event on setup, to force filter on page refresh
- // (filter value may still be present).
- $("#filter").trigger("change");
-};
-
-// Loaded on index.html
-coverage.index_ready = function ($) {
- // Look for a localStorage item containing previous sort settings:
- var sort_list = [];
- var storage_name = "COVERAGE_INDEX_SORT";
- var stored_list = undefined;
- try {
- stored_list = localStorage.getItem(storage_name);
- } catch(err) {}
-
- if (stored_list) {
- sort_list = JSON.parse('[[' + stored_list + ']]');
- }
-
- // Create a new widget which exists only to save and restore
- // the sort order:
- $.tablesorter.addWidget({
- id: "persistentSort",
-
- // Format is called by the widget before displaying:
- format: function (table) {
- if (table.config.sortList.length === 0 && sort_list.length > 0) {
- // This table hasn't been sorted before - we'll use
- // our stored settings:
- $(table).trigger('sorton', [sort_list]);
- }
- else {
- // This is not the first load - something has
- // already defined sorting so we'll just update
- // our stored value to match:
- sort_list = table.config.sortList;
- }
- }
- });
-
- // Configure our tablesorter to handle the variable number of
- // columns produced depending on report options:
- var headers = [];
- var col_count = $("table.index > thead > tr > th").length;
-
- headers[0] = { sorter: 'text' };
- for (i = 1; i < col_count-1; i++) {
- headers[i] = { sorter: 'digit' };
- }
- headers[col_count-1] = { sorter: 'percent' };
-
- // Enable the table sorter:
- $("table.index").tablesorter({
- widgets: ['persistentSort'],
- headers: headers
- });
-
- coverage.assign_shortkeys();
- coverage.wire_up_help_panel();
- coverage.wire_up_filter();
-
- // Watch for page unload events so we can save the final sort settings:
- $(window).on("unload", function () {
- try {
- localStorage.setItem(storage_name, sort_list.toString())
- } catch(err) {}
- });
-};
-
-// -- pyfile stuff --
-
-coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
-
-coverage.pyfile_ready = function ($) {
- // If we're directed to a particular line number, highlight the line.
- var frag = location.hash;
- if (frag.length > 2 && frag[1] === 't') {
- $(frag).addClass('highlight');
- coverage.set_sel(parseInt(frag.substr(2), 10));
- }
- else {
- coverage.set_sel(0);
- }
-
- $(document)
- .bind('keydown', 'j', coverage.to_next_chunk_nicely)
- .bind('keydown', 'k', coverage.to_prev_chunk_nicely)
- .bind('keydown', '0', coverage.to_top)
- .bind('keydown', '1', coverage.to_first_chunk)
- ;
-
- $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");});
- $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");});
- $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");});
- $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");});
-
- coverage.filters = undefined;
- try {
- coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
- } catch(err) {}
-
- if (coverage.filters) {
- coverage.filters = JSON.parse(coverage.filters);
- }
- else {
- coverage.filters = {run: false, exc: true, mis: true, par: true};
- }
-
- for (cls in coverage.filters) {
- coverage.set_line_visibilty(cls, coverage.filters[cls]);
- }
-
- coverage.assign_shortkeys();
- coverage.wire_up_help_panel();
-
- coverage.init_scroll_markers();
-
- // Rebuild scroll markers when the window height changes.
- $(window).resize(coverage.build_scroll_markers);
-};
-
-coverage.toggle_lines = function (btn, cls) {
- var onoff = !$(btn).hasClass("show_" + cls);
- coverage.set_line_visibilty(cls, onoff);
- coverage.build_scroll_markers();
- coverage.filters[cls] = onoff;
- try {
- localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
- } catch(err) {}
-};
-
-coverage.set_line_visibilty = function (cls, onoff) {
- var show = "show_" + cls;
- var btn = $(".button_toggle_" + cls);
- if (onoff) {
- $("#source ." + cls).addClass(show);
- btn.addClass(show);
- }
- else {
- $("#source ." + cls).removeClass(show);
- btn.removeClass(show);
- }
-};
-
-// Return the nth line div.
-coverage.line_elt = function (n) {
- return $("#t" + n);
-};
-
-// Return the nth line number div.
-coverage.num_elt = function (n) {
- return $("#n" + n);
-};
-
-// Set the selection. b and e are line numbers.
-coverage.set_sel = function (b, e) {
- // The first line selected.
- coverage.sel_begin = b;
- // The next line not selected.
- coverage.sel_end = (e === undefined) ? b+1 : e;
-};
-
-coverage.to_top = function () {
- coverage.set_sel(0, 1);
- coverage.scroll_window(0);
-};
-
-coverage.to_first_chunk = function () {
- coverage.set_sel(0, 1);
- coverage.to_next_chunk();
-};
-
-// Return a string indicating what kind of chunk this line belongs to,
-// or null if not a chunk.
-coverage.chunk_indicator = function (line_elt) {
- var klass = line_elt.attr('class');
- if (klass) {
- var m = klass.match(/\bshow_\w+\b/);
- if (m) {
- return m[0];
- }
- }
- return null;
-};
-
-coverage.to_next_chunk = function () {
- var c = coverage;
-
- // Find the start of the next colored chunk.
- var probe = c.sel_end;
- var chunk_indicator, probe_line;
- while (true) {
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- chunk_indicator = c.chunk_indicator(probe_line);
- if (chunk_indicator) {
- break;
- }
- probe++;
- }
-
- // There's a next chunk, `probe` points to it.
- var begin = probe;
-
- // Find the end of this chunk.
- var next_indicator = chunk_indicator;
- while (next_indicator === chunk_indicator) {
- probe++;
- probe_line = c.line_elt(probe);
- next_indicator = c.chunk_indicator(probe_line);
- }
- c.set_sel(begin, probe);
- c.show_selection();
-};
-
-coverage.to_prev_chunk = function () {
- var c = coverage;
-
- // Find the end of the prev colored chunk.
- var probe = c.sel_begin-1;
- var probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- var chunk_indicator = c.chunk_indicator(probe_line);
- while (probe > 0 && !chunk_indicator) {
- probe--;
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- chunk_indicator = c.chunk_indicator(probe_line);
- }
-
- // There's a prev chunk, `probe` points to its last line.
- var end = probe+1;
-
- // Find the beginning of this chunk.
- var prev_indicator = chunk_indicator;
- while (prev_indicator === chunk_indicator) {
- probe--;
- probe_line = c.line_elt(probe);
- prev_indicator = c.chunk_indicator(probe_line);
- }
- c.set_sel(probe+1, end);
- c.show_selection();
-};
-
-// Return the line number of the line nearest pixel position pos
-coverage.line_at_pos = function (pos) {
- var l1 = coverage.line_elt(1),
- l2 = coverage.line_elt(2),
- result;
- if (l1.length && l2.length) {
- var l1_top = l1.offset().top,
- line_height = l2.offset().top - l1_top,
- nlines = (pos - l1_top) / line_height;
- if (nlines < 1) {
- result = 1;
- }
- else {
- result = Math.ceil(nlines);
- }
- }
- else {
- result = 1;
- }
- return result;
-};
-
-// Returns 0, 1, or 2: how many of the two ends of the selection are on
-// the screen right now?
-coverage.selection_ends_on_screen = function () {
- if (coverage.sel_begin === 0) {
- return 0;
- }
-
- var top = coverage.line_elt(coverage.sel_begin);
- var next = coverage.line_elt(coverage.sel_end-1);
-
- return (
- (top.isOnScreen() ? 1 : 0) +
- (next.isOnScreen() ? 1 : 0)
- );
-};
-
-coverage.to_next_chunk_nicely = function () {
- coverage.finish_scrolling();
- if (coverage.selection_ends_on_screen() === 0) {
- // The selection is entirely off the screen: select the top line on
- // the screen.
- var win = $(window);
- coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop()));
- }
- coverage.to_next_chunk();
-};
-
-coverage.to_prev_chunk_nicely = function () {
- coverage.finish_scrolling();
- if (coverage.selection_ends_on_screen() === 0) {
- var win = $(window);
- coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height()));
- }
- coverage.to_prev_chunk();
-};
-
-// Select line number lineno, or if it is in a colored chunk, select the
-// entire chunk
-coverage.select_line_or_chunk = function (lineno) {
- var c = coverage;
- var probe_line = c.line_elt(lineno);
- if (probe_line.length === 0) {
- return;
- }
- var the_indicator = c.chunk_indicator(probe_line);
- if (the_indicator) {
- // The line is in a highlighted chunk.
- // Search backward for the first line.
- var probe = lineno;
- var indicator = the_indicator;
- while (probe > 0 && indicator === the_indicator) {
- probe--;
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- break;
- }
- indicator = c.chunk_indicator(probe_line);
- }
- var begin = probe + 1;
-
- // Search forward for the last line.
- probe = lineno;
- indicator = the_indicator;
- while (indicator === the_indicator) {
- probe++;
- probe_line = c.line_elt(probe);
- indicator = c.chunk_indicator(probe_line);
- }
-
- coverage.set_sel(begin, probe);
- }
- else {
- coverage.set_sel(lineno);
- }
-};
-
-coverage.show_selection = function () {
- var c = coverage;
-
- // Highlight the lines in the chunk
- $(".linenos .highlight").removeClass("highlight");
- for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
- c.num_elt(probe).addClass("highlight");
- }
-
- c.scroll_to_selection();
-};
-
-coverage.scroll_to_selection = function () {
- // Scroll the page if the chunk isn't fully visible.
- if (coverage.selection_ends_on_screen() < 2) {
- // Need to move the page. The html,body trick makes it scroll in all
- // browsers, got it from http://stackoverflow.com/questions/3042651
- var top = coverage.line_elt(coverage.sel_begin);
- var top_pos = parseInt(top.offset().top, 10);
- coverage.scroll_window(top_pos - 30);
- }
-};
-
-coverage.scroll_window = function (to_pos) {
- $("html,body").animate({scrollTop: to_pos}, 200);
-};
-
-coverage.finish_scrolling = function () {
- $("html,body").stop(true, true);
-};
-
-coverage.init_scroll_markers = function () {
- var c = coverage;
- // Init some variables
- c.lines_len = $('#source p').length;
- c.body_h = $('body').height();
- c.header_h = $('div#header').height();
-
- // Build html
- c.build_scroll_markers();
-};
-
-coverage.build_scroll_markers = function () {
- var c = coverage,
- min_line_height = 3,
- max_line_height = 10,
- visible_window_h = $(window).height();
-
- c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par');
- $('#scroll_marker').remove();
- // Don't build markers if the window has no scroll bar.
- if (c.body_h <= visible_window_h) {
- return;
- }
-
- $("body").append("<div id='scroll_marker'>&nbsp;</div>");
- var scroll_marker = $('#scroll_marker'),
- marker_scale = scroll_marker.height() / c.body_h,
- line_height = scroll_marker.height() / c.lines_len;
-
- // Line height must be between the extremes.
- if (line_height > min_line_height) {
- if (line_height > max_line_height) {
- line_height = max_line_height;
- }
- }
- else {
- line_height = min_line_height;
- }
-
- var previous_line = -99,
- last_mark,
- last_top,
- offsets = {};
-
- // Calculate line offsets outside loop to prevent relayouts
- c.lines_to_mark.each(function() {
- offsets[this.id] = $(this).offset().top;
- });
- c.lines_to_mark.each(function () {
- var id_name = $(this).attr('id'),
- line_top = Math.round(offsets[id_name] * marker_scale),
- line_number = parseInt(id_name.substring(1, id_name.length));
-
- if (line_number === previous_line + 1) {
- // If this solid missed block just make previous mark higher.
- last_mark.css({
- 'height': line_top + line_height - last_top
- });
- }
- else {
- // Add colored line in scroll_marker block.
- scroll_marker.append('<div id="m' + line_number + '" class="marker"></div>');
- last_mark = $('#m' + line_number);
- last_mark.css({
- 'height': line_height,
- 'top': line_top
- });
- last_top = line_top;
- }
-
- previous_line = line_number;
- });
-};
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.png b/contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.png
deleted file mode 100644
index 8649f0475d..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.png
+++ /dev/null
Binary files differ
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/index.html b/contrib/python/coverage/py3/coverage/htmlfiles/index.html
deleted file mode 100644
index 983db06125..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/index.html
+++ /dev/null
@@ -1,119 +0,0 @@
-{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
-{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
-
-<!DOCTYPE html>
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>{{ title|escape }}</title>
- <link rel="icon" sizes="32x32" href="favicon_32.png">
- <link rel="stylesheet" href="style.css" type="text/css">
- {% if extra_css %}
- <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
- {% endif %}
- <script type="text/javascript" src="jquery.min.js"></script>
- <script type="text/javascript" src="jquery.ba-throttle-debounce.min.js"></script>
- <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
- <script type="text/javascript" src="jquery.hotkeys.js"></script>
- <script type="text/javascript" src="coverage_html.js"></script>
- <script type="text/javascript">
- jQuery(document).ready(coverage.index_ready);
- </script>
-</head>
-<body class="indexfile">
-
-<div id="header">
- <div class="content">
- <h1>{{ title|escape }}:
- <span class="pc_cov">{{totals.pc_covered_str}}%</span>
- </h1>
-
- <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
-
- <form id="filter_container">
- <input id="filter" type="text" value="" placeholder="filter..." />
- </form>
- </div>
-</div>
-
-<div class="help_panel">
- <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
- <p class="legend">Hot-keys on this page</p>
- <div>
- <p class="keyhelp">
- <span class="key">n</span>
- <span class="key">s</span>
- <span class="key">m</span>
- <span class="key">x</span>
- {% if has_arcs %}
- <span class="key">b</span>
- <span class="key">p</span>
- {% endif %}
- <span class="key">c</span> &nbsp; change column sorting
- </p>
- </div>
-</div>
-
-<div id="index">
- <table class="index">
- <thead>
- {# The title="" attr doesn"t work in Safari. #}
- <tr class="tablehead" title="Click to sort">
- <th class="name left headerSortDown shortkey_n">Module</th>
- <th class="shortkey_s">statements</th>
- <th class="shortkey_m">missing</th>
- <th class="shortkey_x">excluded</th>
- {% if has_arcs %}
- <th class="shortkey_b">branches</th>
- <th class="shortkey_p">partial</th>
- {% endif %}
- <th class="right shortkey_c">coverage</th>
- </tr>
- </thead>
- {# HTML syntax requires thead, tfoot, tbody #}
- <tfoot>
- <tr class="total">
- <td class="name left">Total</td>
- <td>{{totals.n_statements}}</td>
- <td>{{totals.n_missing}}</td>
- <td>{{totals.n_excluded}}</td>
- {% if has_arcs %}
- <td>{{totals.n_branches}}</td>
- <td>{{totals.n_partial_branches}}</td>
- {% endif %}
- <td class="right" data-ratio="{{totals.ratio_covered|pair}}">{{totals.pc_covered_str}}%</td>
- </tr>
- </tfoot>
- <tbody>
- {% for file in files %}
- <tr class="file">
- <td class="name left"><a href="{{file.html_filename}}">{{file.relative_filename}}</a></td>
- <td>{{file.nums.n_statements}}</td>
- <td>{{file.nums.n_missing}}</td>
- <td>{{file.nums.n_excluded}}</td>
- {% if has_arcs %}
- <td>{{file.nums.n_branches}}</td>
- <td>{{file.nums.n_partial_branches}}</td>
- {% endif %}
- <td class="right" data-ratio="{{file.nums.ratio_covered|pair}}">{{file.nums.pc_covered_str}}%</td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
-
- <p id="no_rows">
- No items found using the specified filter.
- </p>
-</div>
-
-<div id="footer">
- <div class="content">
- <p>
- <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
- created at {{ time_stamp }}
- </p>
- </div>
-</div>
-
-</body>
-</html>
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
deleted file mode 100644
index 648fe5d3c2..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * jQuery throttle / debounce - v1.1 - 3/7/2010
- * http://benalman.com/projects/jquery-throttle-debounce-plugin/
- *
- * Copyright (c) 2010 "Cowboy" Ben Alman
- * Dual licensed under the MIT and GPL licenses.
- * http://benalman.com/about/license/
- */
-(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js
deleted file mode 100644
index 09b21e03c7..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * jQuery Hotkeys Plugin
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- *
- * Based upon the plugin by Tzury Bar Yochay:
- * http://github.com/tzuryby/hotkeys
- *
- * Original idea by:
- * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
-*/
-
-(function(jQuery){
-
- jQuery.hotkeys = {
- version: "0.8",
-
- specialKeys: {
- 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
- 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
- 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
- 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
- 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
- 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
- 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
- },
-
- shiftNums: {
- "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
- "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
- ".": ">", "/": "?", "\\": "|"
- }
- };
-
- function keyHandler( handleObj ) {
- // Only care when a possible input has been specified
- if ( typeof handleObj.data !== "string" ) {
- return;
- }
-
- var origHandler = handleObj.handler,
- keys = handleObj.data.toLowerCase().split(" ");
-
- handleObj.handler = function( event ) {
- // Don't fire in text-accepting inputs that we didn't directly bind to
- if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
- event.target.type === "text") ) {
- return;
- }
-
- // Keypress represents characters, not special keys
- var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
- character = String.fromCharCode( event.which ).toLowerCase(),
- key, modif = "", possible = {};
-
- // check combinations (alt|ctrl|shift+anything)
- if ( event.altKey && special !== "alt" ) {
- modif += "alt+";
- }
-
- if ( event.ctrlKey && special !== "ctrl" ) {
- modif += "ctrl+";
- }
-
- // TODO: Need to make sure this works consistently across platforms
- if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
- modif += "meta+";
- }
-
- if ( event.shiftKey && special !== "shift" ) {
- modif += "shift+";
- }
-
- if ( special ) {
- possible[ modif + special ] = true;
-
- } else {
- possible[ modif + character ] = true;
- possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
-
- // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
- if ( modif === "shift+" ) {
- possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
- }
- }
-
- for ( var i = 0, l = keys.length; i < l; i++ ) {
- if ( possible[ keys[i] ] ) {
- return origHandler.apply( this, arguments );
- }
- }
- };
- }
-
- jQuery.each([ "keydown", "keyup", "keypress" ], function() {
- jQuery.event.special[ this ] = { add: keyHandler };
- });
-
-})( jQuery );
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js
deleted file mode 100644
index 0182ebd213..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (c) 2010
- * @author Laurence Wheway
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
- *
- * @version 1.2.0
- */
-(function($) {
- jQuery.extend({
- isOnScreen: function(box, container) {
- //ensure numbers come in as intgers (not strings) and remove 'px' is it's there
- for(var i in box){box[i] = parseFloat(box[i])};
- for(var i in container){container[i] = parseFloat(container[i])};
-
- if(!container){
- container = {
- left: $(window).scrollLeft(),
- top: $(window).scrollTop(),
- width: $(window).width(),
- height: $(window).height()
- }
- }
-
- if( box.left+box.width-container.left > 0 &&
- box.left < container.width+container.left &&
- box.top+box.height-container.top > 0 &&
- box.top < container.height+container.top
- ) return true;
- return false;
- }
- })
-
-
- jQuery.fn.isOnScreen = function (container) {
- for(var i in container){container[i] = parseFloat(container[i])};
-
- if(!container){
- container = {
- left: $(window).scrollLeft(),
- top: $(window).scrollTop(),
- width: $(window).width(),
- height: $(window).height()
- }
- }
-
- if( $(this).offset().left+$(this).width()-container.left > 0 &&
- $(this).offset().left < container.width+container.left &&
- $(this).offset().top+$(this).height()-container.top > 0 &&
- $(this).offset().top < container.height+container.top
- ) return true;
- return false;
- }
-})(jQuery);
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js
deleted file mode 100644
index d1608e37ff..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
-if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
-},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m}); \ No newline at end of file
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js
deleted file mode 100644
index 64c7007129..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-
-(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,cells[i]);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,node){var l=parsers.length;for(var i=1;i<l;i++){if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)){return parsers[i];}}return parsers[0];}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=table.tBodies[0].rows[i],cols=[];cache.row.push($(c));for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));}cols.push(i);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){if(!node)return"";var t="";if(config.textExtraction=="simple"){if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){t=node.childNodes[0].innerHTML;}else{t=node.innerHTML;}}else{if(typeof(config.textExtraction)=="function"){t=config.textExtraction(node);}else{t=$(node).text();}}return t;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){rows.push(r[n[i][checkCell]]);if(!table.config.appender){var o=r[n[i][checkCell]];var l=o.length;for(var j=0;j<l;j++){tableBody[0].appendChild(o[j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false,tableHeadersRows=[];for(var i=0;i<table.tHead.rows.length;i++){tableHeadersRows[i]=0;};$tableHeaders=$("thead th",table);$tableHeaders.each(function(index){this.count=0;this.column=index;this.order=formatSortingOrder(table.config.sortInitialOrder);if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(!this.sortDisabled){$(this).addClass(table.config.cssHeader);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){i=(v.toLowerCase()=="desc")?1:0;}else{i=(v==(0||1))?v:0;}return i;}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(getCachedSortType(table.config.parsers,c)=="text")?((order==0)?"sortText":"sortTextDesc"):((order==0)?"sortNumeric":"sortNumericDesc");var e="e"+i;dynamicExp+="var "+e+" = "+s+"(a["+c+"],b["+c+"]); ";dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function sortText(a,b){return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){var DECIMAL='\\'+config.decimal;var exp='/(^[+]?0('+DECIMAL+'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)'+DECIMAL+'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*'+DECIMAL+'0+$)/';return RegExp(exp).test($.trim(s));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}$("tr:visible",table.tBodies[0]).filter(':even').removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]).end().filter(':odd').removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); \ No newline at end of file
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.png b/contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.png
deleted file mode 100644
index db114023f0..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.png
+++ /dev/null
Binary files differ
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.png b/contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.png
deleted file mode 100644
index db114023f0..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.png
+++ /dev/null
Binary files differ
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html b/contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html
deleted file mode 100644
index e15be066fb..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html
+++ /dev/null
@@ -1,113 +0,0 @@
-{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
-{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
-
-<!DOCTYPE html>
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #}
- {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #}
- <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
- <title>Coverage for {{relative_filename|escape}}: {{nums.pc_covered_str}}%</title>
- <link rel="icon" sizes="32x32" href="favicon_32.png">
- <link rel="stylesheet" href="style.css" type="text/css">
- {% if extra_css %}
- <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
- {% endif %}
- <script type="text/javascript" src="jquery.min.js"></script>
- <script type="text/javascript" src="jquery.hotkeys.js"></script>
- <script type="text/javascript" src="jquery.isonscreen.js"></script>
- <script type="text/javascript" src="coverage_html.js"></script>
- <script type="text/javascript">
- jQuery(document).ready(coverage.pyfile_ready);
- </script>
-</head>
-<body class="pyfile">
-
-<div id="header">
- <div class="content">
- <h1>Coverage for <b>{{relative_filename|escape}}</b> :
- <span class="pc_cov">{{nums.pc_covered_str}}%</span>
- </h1>
-
- <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
-
- <h2 class="stats">
- {{nums.n_statements}} statements &nbsp;
- <button type="button" class="{{category.run}} shortkey_r button_toggle_run" title="Toggle lines run">{{nums.n_executed}} run</button>
- <button type="button" class="{{category.mis}} shortkey_m button_toggle_mis" title="Toggle lines missing">{{nums.n_missing}} missing</button>
- <button type="button" class="{{category.exc}} shortkey_x button_toggle_exc" title="Toggle lines excluded">{{nums.n_excluded}} excluded</button>
-
- {% if has_arcs %}
- <button type="button" class="{{category.par}} shortkey_p button_toggle_par" title="Toggle lines partially run">{{nums.n_partial_branches}} partial</button>
- {% endif %}
- </h2>
- </div>
-</div>
-
-<div class="help_panel">
- <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
- <p class="legend">Hot-keys on this page</p>
- <div>
- <p class="keyhelp">
- <span class="key">r</span>
- <span class="key">m</span>
- <span class="key">x</span>
- <span class="key">p</span> &nbsp; toggle line displays
- </p>
- <p class="keyhelp">
- <span class="key">j</span>
- <span class="key">k</span> &nbsp; next/prev highlighted chunk
- </p>
- <p class="keyhelp">
- <span class="key">0</span> &nbsp; (zero) top of page
- </p>
- <p class="keyhelp">
- <span class="key">1</span> &nbsp; (one) first highlighted chunk
- </p>
- </div>
-</div>
-
-<div id="source">
- {% for line in lines -%}
- {% joined %}
- <p id="t{{line.number}}" class="{{line.css_class}}">
- <span class="n"><a href="#t{{line.number}}">{{line.number}}</a></span>
- <span class="t">{{line.html}}&nbsp;</span>
- {% if line.context_list %}
- <input type="checkbox" id="ctxs{{line.number}}" />
- {% endif %}
- {# Things that should float right in the line. #}
- <span class="r">
- {% if line.annotate %}
- <span class="annotate short">{{line.annotate}}</span>
- <span class="annotate long">{{line.annotate_long}}</span>
- {% endif %}
- {% if line.contexts %}
- <label for="ctxs{{line.number}}" class="ctx">{{ line.contexts_label }}</label>
- {% endif %}
- </span>
- {# Things that should appear below the line. #}
- {% if line.context_list %}
- <span class="ctxs">
- {% for context in line.context_list %}
- <span>{{context}}</span>
- {% endfor %}
- </span>
- {% endif %}
- </p>
- {% endjoined %}
- {% endfor %}
-</div>
-
-<div id="footer">
- <div class="content">
- <p>
- <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
- created at {{ time_stamp }}
- </p>
- </div>
-</div>
-
-</body>
-</html>
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/style.css b/contrib/python/coverage/py3/coverage/htmlfiles/style.css
deleted file mode 100644
index 36ee2a6e65..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/style.css
+++ /dev/null
@@ -1,291 +0,0 @@
-@charset "UTF-8";
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-/* Don't edit this .css file. Edit the .scss file instead! */
-html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
-
-body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; }
-
-@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } }
-
-@media (prefers-color-scheme: dark) { body { color: #eee; } }
-
-html > body { font-size: 16px; }
-
-a:active, a:focus { outline: 2px dashed #007acc; }
-
-p { font-size: .875em; line-height: 1.4em; }
-
-table { border-collapse: collapse; }
-
-td { vertical-align: top; }
-
-table tr.hidden { display: none !important; }
-
-p#no_rows { display: none; font-size: 1.2em; }
-
-a.nav { text-decoration: none; color: inherit; }
-
-a.nav:hover { text-decoration: underline; color: inherit; }
-
-#header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; }
-
-@media (prefers-color-scheme: dark) { #header { background: black; } }
-
-@media (prefers-color-scheme: dark) { #header { border-color: #333; } }
-
-.indexfile #footer { margin: 1rem 3.5rem; }
-
-.pyfile #footer { margin: 1rem 1rem; }
-
-#footer .content { padding: 0; color: #666; font-style: italic; }
-
-@media (prefers-color-scheme: dark) { #footer .content { color: #aaa; } }
-
-#index { margin: 1rem 0 0 3.5rem; }
-
-#header .content { padding: 1rem 3.5rem; }
-
-h1 { font-size: 1.25em; display: inline-block; }
-
-#filter_container { float: right; margin: 0 2em 0 0; }
-
-#filter_container input { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; }
-
-@media (prefers-color-scheme: dark) { #filter_container input { border-color: #444; } }
-
-@media (prefers-color-scheme: dark) { #filter_container input { background: #1e1e1e; } }
-
-@media (prefers-color-scheme: dark) { #filter_container input { color: #eee; } }
-
-#filter_container input:focus { border-color: #007acc; }
-
-h2.stats { margin-top: .5em; font-size: 1em; }
-
-.stats button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; color: inherit; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
-
-@media (prefers-color-scheme: dark) { .stats button { border-color: #444; } }
-
-.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
-
-.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
-
-.stats button.run { background: #eeffee; }
-
-@media (prefers-color-scheme: dark) { .stats button.run { background: #373d29; } }
-
-.stats button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.run.show_run { background: #373d29; } }
-
-.stats button.mis { background: #ffeeee; }
-
-@media (prefers-color-scheme: dark) { .stats button.mis { background: #4b1818; } }
-
-.stats button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.mis.show_mis { background: #4b1818; } }
-
-.stats button.exc { background: #f7f7f7; }
-
-@media (prefers-color-scheme: dark) { .stats button.exc { background: #333; } }
-
-.stats button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.exc.show_exc { background: #333; } }
-
-.stats button.par { background: #ffffd5; }
-
-@media (prefers-color-scheme: dark) { .stats button.par { background: #650; } }
-
-.stats button.par.show_par { background: #ffa; border: 2px solid #dddd00; margin: 0 .1em; }
-
-@media (prefers-color-scheme: dark) { .stats button.par.show_par { background: #650; } }
-
-.help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; }
-
-#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
-
-#keyboard_icon { float: right; margin: 5px; cursor: pointer; }
-
-.help_panel { padding: .5em; border: 1px solid #883; }
-
-.help_panel .legend { font-style: italic; margin-bottom: 1em; }
-
-.indexfile .help_panel { width: 20em; min-height: 4em; }
-
-.pyfile .help_panel { width: 16em; min-height: 8em; }
-
-#panel_icon { float: right; cursor: pointer; }
-
-.keyhelp { margin: .75em; }
-
-.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; }
-
-#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
-
-#source p { position: relative; white-space: pre; }
-
-#source p * { box-sizing: border-box; }
-
-#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; }
-
-@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
-
-#source p .n a { text-decoration: none; color: #999; }
-
-@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
-
-#source p .n a:hover { text-decoration: underline; color: #999; }
-
-@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
-
-#source p.highlight .n { background: #ffdd00; }
-
-#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
-
-@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
-
-#source p .t:hover { background: #f2f2f2; }
-
-@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } }
-
-#source p .t:hover ~ .r .annotate.long { display: block; }
-
-#source p .t .com { color: #008000; font-style: italic; line-height: 1px; }
-
-@media (prefers-color-scheme: dark) { #source p .t .com { color: #6A9955; } }
-
-#source p .t .key { font-weight: bold; line-height: 1px; }
-
-#source p .t .str { color: #0451A5; }
-
-@media (prefers-color-scheme: dark) { #source p .t .str { color: #9CDCFE; } }
-
-#source p.mis .t { border-left: 0.2em solid #ff0000; }
-
-#source p.mis.show_mis .t { background: #fdd; }
-
-@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } }
-
-#source p.mis.show_mis .t:hover { background: #f2d2d2; }
-
-@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } }
-
-#source p.run .t { border-left: 0.2em solid #00dd00; }
-
-#source p.run.show_run .t { background: #dfd; }
-
-@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } }
-
-#source p.run.show_run .t:hover { background: #d2f2d2; }
-
-@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } }
-
-#source p.exc .t { border-left: 0.2em solid #808080; }
-
-#source p.exc.show_exc .t { background: #eee; }
-
-@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } }
-
-#source p.exc.show_exc .t:hover { background: #e2e2e2; }
-
-@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } }
-
-#source p.par .t { border-left: 0.2em solid #dddd00; }
-
-#source p.par.show_par .t { background: #ffa; }
-
-@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } }
-
-#source p.par.show_par .t:hover { background: #f2f2a2; }
-
-@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } }
-
-#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
-
-#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; }
-
-@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } }
-
-#source p .annotate.short:hover ~ .long { display: block; }
-
-#source p .annotate.long { width: 30em; right: 2.5em; }
-
-#source p input { display: none; }
-
-#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
-
-#source p input ~ .r label.ctx::before { content: "▶ "; }
-
-#source p input ~ .r label.ctx:hover { background: #d5f7ff; color: #666; }
-
-@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } }
-
-@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } }
-
-#source p input:checked ~ .r label.ctx { background: #aef; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
-
-@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } }
-
-@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } }
-
-#source p input:checked ~ .r label.ctx::before { content: "▼ "; }
-
-#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
-
-#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
-
-@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } }
-
-#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #aef; border-radius: .25em; margin-right: 1.75em; }
-
-@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } }
-
-#source p .ctxs span { display: block; text-align: right; }
-
-#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; }
-
-#index table.index { margin-left: -.5em; }
-
-#index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; }
-
-@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } }
-
-#index td.name, #index th.name { text-align: left; width: auto; }
-
-#index th { font-style: italic; color: #333; cursor: pointer; }
-
-@media (prefers-color-scheme: dark) { #index th { color: #ddd; } }
-
-#index th:hover { background: #eee; }
-
-@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } }
-
-#index th.headerSortDown, #index th.headerSortUp { white-space: nowrap; background: #eee; }
-
-@media (prefers-color-scheme: dark) { #index th.headerSortDown, #index th.headerSortUp { background: #333; } }
-
-#index th.headerSortDown:after { content: " ↑"; }
-
-#index th.headerSortUp:after { content: " ↓"; }
-
-#index td.name a { text-decoration: none; color: inherit; }
-
-#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }
-
-#index tr.file:hover { background: #eee; }
-
-@media (prefers-color-scheme: dark) { #index tr.file:hover { background: #333; } }
-
-#index tr.file:hover td.name { text-decoration: underline; color: inherit; }
-
-#scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }
-
-@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } }
-
-@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } }
-
-#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; }
-
-@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } }
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/style.scss b/contrib/python/coverage/py3/coverage/htmlfiles/style.scss
deleted file mode 100644
index 158d1fb493..0000000000
--- a/contrib/python/coverage/py3/coverage/htmlfiles/style.scss
+++ /dev/null
@@ -1,660 +0,0 @@
-/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
-/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
-
-// CSS styles for coverage.py HTML reports.
-
-// When you edit this file, you need to run "make css" to get the CSS file
-// generated, and then check in both the .scss and the .css files.
-
-// When working on the file, this command is useful:
-// sass --watch --style=compact --sourcemap=none --no-cache coverage/htmlfiles/style.scss:htmlcov/style.css
-//
-// OR you can process sass purely in python with `pip install pysass`, then:
-// pysassc --style=compact coverage/htmlfiles/style.scss coverage/htmlfiles/style.css
-
-// Ignore this comment, it's for the CSS output file:
-/* Don't edit this .css file. Edit the .scss file instead! */
-
-// Dimensions
-$left-gutter: 3.5rem;
-
-
-//
-// Declare colors and variables
-//
-
-$font-normal: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
-$font-code: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
-
-$off-button-lighten: 50%;
-$hover-dark-amt: 95%;
-
-$focus-color: #007acc;
-
-$mis-color: #ff0000;
-$run-color: #00dd00;
-$exc-color: #808080;
-$par-color: #dddd00;
-
-$light-bg: #fff;
-$light-fg: #000;
-$light-gray1: #f8f8f8;
-$light-gray2: #eee;
-$light-gray3: #ccc;
-$light-gray4: #999;
-$light-gray5: #666;
-$light-gray6: #333;
-$light-pln-bg: $light-bg;
-$light-mis-bg: #fdd;
-$light-run-bg: #dfd;
-$light-exc-bg: $light-gray2;
-$light-par-bg: #ffa;
-$light-token-com: #008000;
-$light-token-str: #0451A5;
-$light-context-bg-color: #aef;
-
-$dark-bg: #1e1e1e;
-$dark-fg: #eee;
-$dark-gray1: #222;
-$dark-gray2: #333;
-$dark-gray3: #444;
-$dark-gray4: #777;
-$dark-gray5: #aaa;
-$dark-gray6: #ddd;
-$dark-pln-bg: $dark-bg;
-$dark-mis-bg: #4b1818;
-$dark-run-bg: #373d29;
-$dark-exc-bg: $dark-gray2;
-$dark-par-bg: #650;
-$dark-token-com: #6A9955;
-$dark-token-str: #9CDCFE;
-$dark-context-bg-color: #056;
-
-//
-// Mixins and utilities
-//
-@mixin background-dark($color) {
- @media (prefers-color-scheme: dark) {
- background: $color;
- }
-}
-@mixin color-dark($color) {
- @media (prefers-color-scheme: dark) {
- color: $color;
- }
-}
-@mixin border-color-dark($color) {
- @media (prefers-color-scheme: dark) {
- border-color: $color;
- }
-}
-
-// Add visual outline to navigable elements on focus improve accessibility.
-@mixin focus-border {
- &:active, &:focus {
- outline: 2px dashed $focus-color;
- }
-}
-
-// Page-wide styles
-html, body, h1, h2, h3, p, table, td, th {
- margin: 0;
- padding: 0;
- border: 0;
- font-weight: inherit;
- font-style: inherit;
- font-size: 100%;
- font-family: inherit;
- vertical-align: baseline;
-}
-
-// Set baseline grid to 16 pt.
-body {
- font-family: $font-normal;
- font-size: 1em;
- background: $light-bg;
- color: $light-fg;
- @include background-dark($dark-bg);
- @include color-dark($dark-fg);
-}
-
-html>body {
- font-size: 16px;
-}
-
-a {
- @include focus-border;
-}
-
-p {
- font-size: .875em;
- line-height: 1.4em;
-}
-
-table {
- border-collapse: collapse;
-}
-td {
- vertical-align: top;
-}
-table tr.hidden {
- display: none !important;
-}
-
-p#no_rows {
- display: none;
- font-size: 1.2em;
-}
-
-a.nav {
- text-decoration: none;
- color: inherit;
-
- &:hover {
- text-decoration: underline;
- color: inherit;
- }
-}
-
-// Page structure
-#header {
- background: $light-gray1;
- @include background-dark(black);
- width: 100%;
- border-bottom: 1px solid $light-gray2;
- @include border-color-dark($dark-gray2);
-}
-
-.indexfile #footer {
- margin: 1rem $left-gutter;
-}
-
-.pyfile #footer {
- margin: 1rem 1rem;
-}
-
-#footer .content {
- padding: 0;
- color: $light-gray5;
- @include color-dark($dark-gray5);
- font-style: italic;
-}
-
-#index {
- margin: 1rem 0 0 $left-gutter;
-}
-
-// Header styles
-#header .content {
- padding: 1rem $left-gutter;
-}
-
-h1 {
- font-size: 1.25em;
- display: inline-block;
-}
-
-#filter_container {
- float: right;
- margin: 0 2em 0 0;
-
- input {
- width: 10em;
- padding: 0.2em 0.5em;
- border: 2px solid $light-gray3;
- background: $light-bg;
- color: $light-fg;
- @include border-color-dark($dark-gray3);
- @include background-dark($dark-bg);
- @include color-dark($dark-fg);
- &:focus {
- border-color: $focus-color;
- }
- }
-}
-
-h2.stats {
- margin-top: .5em;
- font-size: 1em;
-}
-.stats button {
- font-family: inherit;
- font-size: inherit;
- border: 1px solid;
- border-radius: .2em;
- color: inherit;
- padding: .1em .5em;
- margin: 1px calc(.1em + 1px);
- cursor: pointer;
- border-color: $light-gray3;
- @include border-color-dark($dark-gray3);
- @include focus-border;
-
- @include focus-border;
-
- &.run {
- background: mix($light-run-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-run-bg);
- &.show_run {
- background: $light-run-bg;
- @include background-dark($dark-run-bg);
- border: 2px solid $run-color;
- margin: 0 .1em;
- }
- }
- &.mis {
- background: mix($light-mis-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-mis-bg);
- &.show_mis {
- background: $light-mis-bg;
- @include background-dark($dark-mis-bg);
- border: 2px solid $mis-color;
- margin: 0 .1em;
- }
- }
- &.exc {
- background: mix($light-exc-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-exc-bg);
- &.show_exc {
- background: $light-exc-bg;
- @include background-dark($dark-exc-bg);
- border: 2px solid $exc-color;
- margin: 0 .1em;
- }
- }
- &.par {
- background: mix($light-par-bg, $light-bg, $off-button-lighten);
- @include background-dark($dark-par-bg);
- &.show_par {
- background: $light-par-bg;
- @include background-dark($dark-par-bg);
- border: 2px solid $par-color;
- margin: 0 .1em;
- }
- }
-}
-
-// Yellow post-it things.
-%popup {
- display: none;
- position: absolute;
- z-index: 999;
- background: #ffffcc;
- border: 1px solid #888;
- border-radius: .2em;
- color: #333;
- padding: .25em .5em;
-}
-
-// Yellow post-it's in the text listings.
-%in-text-popup {
- @extend %popup;
- white-space: normal;
- float: right;
- top: 1.75em;
- right: 1em;
- height: auto;
-}
-
-// Help panel
-#keyboard_icon {
- float: right;
- margin: 5px;
- cursor: pointer;
-}
-
-.help_panel {
- @extend %popup;
- padding: .5em;
- border: 1px solid #883;
-
- .legend {
- font-style: italic;
- margin-bottom: 1em;
- }
-
- .indexfile & {
- width: 20em;
- min-height: 4em;
- }
-
- .pyfile & {
- width: 16em;
- min-height: 8em;
- }
-}
-
-#panel_icon {
- float: right;
- cursor: pointer;
-}
-
-.keyhelp {
- margin: .75em;
-
- .key {
- border: 1px solid black;
- border-color: #888 #333 #333 #888;
- padding: .1em .35em;
- font-family: $font-code;
- font-weight: bold;
- background: #eee;
- }
-}
-
-// Source file styles
-
-// The slim bar at the left edge of the source lines, colored by coverage.
-$border-indicator-width: .2em;
-
-#source {
- padding: 1em 0 1em $left-gutter;
- font-family: $font-code;
-
- p {
- // position relative makes position:absolute pop-ups appear in the right place.
- position: relative;
- white-space: pre;
-
- * {
- box-sizing: border-box;
- }
-
- .n {
- float: left;
- text-align: right;
- width: $left-gutter;
- box-sizing: border-box;
- margin-left: -$left-gutter;
- padding-right: 1em;
- color: $light-gray4;
- @include color-dark($dark-gray4);
-
- a {
- text-decoration: none;
- color: $light-gray4;
- @include color-dark($dark-gray4);
- &:hover {
- text-decoration: underline;
- color: $light-gray4;
- @include color-dark($dark-gray4);
- }
- }
- }
-
- &.highlight .n {
- background: #ffdd00;
- }
-
- .t {
- display: inline-block;
- width: 100%;
- box-sizing: border-box;
- margin-left: -.5em;
- padding-left: .5em - $border-indicator-width;
- border-left: $border-indicator-width solid $light-bg;
- @include border-color-dark($dark-bg);
-
- &:hover {
- background: mix($light-pln-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-pln-bg, $dark-fg, $hover-dark-amt));
-
- & ~ .r .annotate.long {
- display: block;
- }
- }
-
- // Syntax coloring
- .com {
- color: $light-token-com;
- @include color-dark($dark-token-com);
- font-style: italic;
- line-height: 1px;
- }
- .key {
- font-weight: bold;
- line-height: 1px;
- }
- .str {
- color: $light-token-str;
- @include color-dark($dark-token-str);
- }
- }
-
- &.mis {
- .t {
- border-left: $border-indicator-width solid $mis-color;
- }
-
- &.show_mis .t {
- background: $light-mis-bg;
- @include background-dark($dark-mis-bg);
-
- &:hover {
- background: mix($light-mis-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-mis-bg, $dark-fg, $hover-dark-amt));
- }
- }
- }
-
- &.run {
- .t {
- border-left: $border-indicator-width solid $run-color;
- }
-
- &.show_run .t {
- background: $light-run-bg;
- @include background-dark($dark-run-bg);
-
- &:hover {
- background: mix($light-run-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-run-bg, $dark-fg, $hover-dark-amt));
- }
- }
- }
-
- &.exc {
- .t {
- border-left: $border-indicator-width solid $exc-color;
- }
-
- &.show_exc .t {
- background: $light-exc-bg;
- @include background-dark($dark-exc-bg);
-
- &:hover {
- background: mix($light-exc-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-exc-bg, $dark-fg, $hover-dark-amt));
- }
- }
- }
-
- &.par {
- .t {
- border-left: $border-indicator-width solid $par-color;
- }
-
- &.show_par .t {
- background: $light-par-bg;
- @include background-dark($dark-par-bg);
-
- &:hover {
- background: mix($light-par-bg, $light-fg, $hover-dark-amt);
- @include background-dark(mix($dark-par-bg, $dark-fg, $hover-dark-amt));
- }
- }
-
- }
-
- .r {
- position: absolute;
- top: 0;
- right: 2.5em;
- font-family: $font-normal;
- }
-
- .annotate {
- font-family: $font-normal;
- color: $light-gray5;
- @include color-dark($dark-gray6);
- padding-right: .5em;
-
- &.short:hover ~ .long {
- display: block;
- }
-
- &.long {
- @extend %in-text-popup;
- width: 30em;
- right: 2.5em;
- }
- }
-
- input {
- display: none;
-
- & ~ .r label.ctx {
- cursor: pointer;
- border-radius: .25em;
- &::before {
- content: "▶ ";
- }
- &:hover {
- background: mix($light-context-bg-color, $light-bg, $off-button-lighten);
- @include background-dark(mix($dark-context-bg-color, $dark-bg, $off-button-lighten));
- color: $light-gray5;
- @include color-dark($dark-gray5);
- }
- }
-
- &:checked ~ .r label.ctx {
- background: $light-context-bg-color;
- @include background-dark($dark-context-bg-color);
- color: $light-gray5;
- @include color-dark($dark-gray5);
- border-radius: .75em .75em 0 0;
- padding: 0 .5em;
- margin: -.25em 0;
- &::before {
- content: "▼ ";
- }
- }
-
- &:checked ~ .ctxs {
- padding: .25em .5em;
- overflow-y: scroll;
- max-height: 10.5em;
- }
- }
-
- label.ctx {
- color: $light-gray4;
- @include color-dark($dark-gray4);
- display: inline-block;
- padding: 0 .5em;
- font-size: .8333em; // 10/12
- }
-
- .ctxs {
- display: block;
- max-height: 0;
- overflow-y: hidden;
- transition: all .2s;
- padding: 0 .5em;
- font-family: $font-normal;
- white-space: nowrap;
- background: $light-context-bg-color;
- @include background-dark($dark-context-bg-color);
- border-radius: .25em;
- margin-right: 1.75em;
- span {
- display: block;
- text-align: right;
- }
- }
- }
-}
-
-
-// index styles
-#index {
- font-family: $font-code;
- font-size: 0.875em;
-
- table.index {
- margin-left: -.5em;
- }
- td, th {
- text-align: right;
- width: 5em;
- padding: .25em .5em;
- border-bottom: 1px solid $light-gray2;
- @include border-color-dark($dark-gray2);
- &.name {
- text-align: left;
- width: auto;
- }
- }
- th {
- font-style: italic;
- color: $light-gray6;
- @include color-dark($dark-gray6);
- cursor: pointer;
- &:hover {
- background: $light-gray2;
- @include background-dark($dark-gray2);
- }
- &.headerSortDown, &.headerSortUp {
- white-space: nowrap;
- background: $light-gray2;
- @include background-dark($dark-gray2);
- }
- &.headerSortDown:after {
- content: " ↑";
- }
- &.headerSortUp:after {
- content: " ↓";
- }
- }
- td.name a {
- text-decoration: none;
- color: inherit;
- }
-
- tr.total td,
- tr.total_dynamic td {
- font-weight: bold;
- border-top: 1px solid #ccc;
- border-bottom: none;
- }
- tr.file:hover {
- background: $light-gray2;
- @include background-dark($dark-gray2);
- td.name {
- text-decoration: underline;
- color: inherit;
- }
- }
-}
-
-// scroll marker styles
-#scroll_marker {
- position: fixed;
- right: 0;
- top: 0;
- width: 16px;
- height: 100%;
- background: $light-bg;
- border-left: 1px solid $light-gray2;
- @include background-dark($dark-bg);
- @include border-color-dark($dark-gray2);
- will-change: transform; // for faster scrolling of fixed element in Chrome
-
- .marker {
- background: $light-gray3;
- @include background-dark($dark-gray3);
- position: absolute;
- min-height: 3px;
- width: 100%;
- }
-}
diff --git a/contrib/python/coverage/py3/coverage/inorout.py b/contrib/python/coverage/py3/coverage/inorout.py
deleted file mode 100644
index cbc80e8fb5..0000000000
--- a/contrib/python/coverage/py3/coverage/inorout.py
+++ /dev/null
@@ -1,513 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Determining whether files are being measured/reported or not."""
-
-# For finding the stdlib
-import atexit
-import inspect
-import itertools
-import os
-import platform
-import re
-import sys
-import traceback
-
-from coverage import env
-from coverage.backward import code_object
-from coverage.disposition import FileDisposition, disposition_init
-from coverage.files import TreeMatcher, FnmatchMatcher, ModuleMatcher
-from coverage.files import prep_patterns, find_python_files, canonical_filename
-from coverage.misc import CoverageException
-from coverage.python import source_for_file, source_for_morf
-
-
-# Pypy has some unusual stuff in the "stdlib". Consider those locations
-# when deciding where the stdlib is. These modules are not used for anything,
-# they are modules importable from the pypy lib directories, so that we can
-# find those directories.
-_structseq = _pypy_irc_topic = None
-if env.PYPY:
- try:
- import _structseq
- except ImportError:
- pass
-
- try:
- import _pypy_irc_topic
- except ImportError:
- pass
-
-
-def canonical_path(morf, directory=False):
- """Return the canonical path of the module or file `morf`.
-
- If the module is a package, then return its directory. If it is a
- module, then return its file, unless `directory` is True, in which
- case return its enclosing directory.
-
- """
- morf_path = canonical_filename(source_for_morf(morf))
- if morf_path.endswith("__init__.py") or directory:
- morf_path = os.path.split(morf_path)[0]
- return morf_path
-
-
-def name_for_module(filename, frame):
- """Get the name of the module for a filename and frame.
-
- For configurability's sake, we allow __main__ modules to be matched by
- their importable name.
-
- If loaded via runpy (aka -m), we can usually recover the "original"
- full dotted module name, otherwise, we resort to interpreting the
- file name to get the module's name. In the case that the module name
- can't be determined, None is returned.
-
- """
- module_globals = frame.f_globals if frame is not None else {}
- if module_globals is None: # pragma: only ironpython
- # IronPython doesn't provide globals: https://github.com/IronLanguages/main/issues/1296
- module_globals = {}
-
- dunder_name = module_globals.get('__name__', None)
-
- if isinstance(dunder_name, str) and dunder_name != '__main__':
- # This is the usual case: an imported module.
- return dunder_name
-
- loader = module_globals.get('__loader__', None)
- for attrname in ('fullname', 'name'): # attribute renamed in py3.2
- if hasattr(loader, attrname):
- fullname = getattr(loader, attrname)
- else:
- continue
-
- if isinstance(fullname, str) and fullname != '__main__':
- # Module loaded via: runpy -m
- return fullname
-
- # Script as first argument to Python command line.
- inspectedname = inspect.getmodulename(filename)
- if inspectedname is not None:
- return inspectedname
- else:
- return dunder_name
-
-
-def module_is_namespace(mod):
- """Is the module object `mod` a PEP420 namespace module?"""
- return hasattr(mod, '__path__') and getattr(mod, '__file__', None) is None
-
-
-def module_has_file(mod):
- """Does the module object `mod` have an existing __file__ ?"""
- mod__file__ = getattr(mod, '__file__', None)
- if mod__file__ is None:
- return False
- return os.path.exists(mod__file__)
-
-
-class InOrOut(object):
- """Machinery for determining what files to measure."""
-
- def __init__(self, warn, debug):
- self.warn = warn
- self.debug = debug
-
- # The matchers for should_trace.
- self.source_match = None
- self.source_pkgs_match = None
- self.pylib_paths = self.cover_paths = None
- self.pylib_match = self.cover_match = None
- self.include_match = self.omit_match = None
- self.plugins = []
- self.disp_class = FileDisposition
-
- # The source argument can be directories or package names.
- self.source = []
- self.source_pkgs = []
- self.source_pkgs_unmatched = []
- self.omit = self.include = None
-
- def configure(self, config):
- """Apply the configuration to get ready for decision-time."""
- self.config = config
- self.source_pkgs.extend(config.source_pkgs)
- for src in config.source or []:
- if os.path.isdir(src):
- self.source.append(canonical_filename(src))
- else:
- self.source_pkgs.append(src)
- self.source_pkgs_unmatched = self.source_pkgs[:]
-
- self.omit = prep_patterns(config.run_omit)
- if getattr(sys, 'is_standalone_binary', False):
- # don't trace contrib
- self.omit.append('contrib/python/*')
- self.omit.append('contrib/libs/protobuf/*')
- self.omit.append('library/python/pytest/*')
- self.include = prep_patterns(config.run_include)
-
- # The directories for files considered "installed with the interpreter".
- self.pylib_paths = set()
- if getattr(sys, 'is_standalone_binary', False):
- self.pylib_paths.add('contrib/tools/python')
- self.pylib_paths.add('contrib/tools/python3')
- if not self.pylib_paths and not config.cover_pylib:
- # Look at where some standard modules are located. That's the
- # indication for "installed with the interpreter". In some
- # environments (virtualenv, for example), these modules may be
- # spread across a few locations. Look at all the candidate modules
- # we've imported, and take all the different ones.
- for m in (atexit, inspect, os, platform, _pypy_irc_topic, re, _structseq, traceback):
- if m is not None and hasattr(m, "__file__"):
- self.pylib_paths.add(canonical_path(m, directory=True))
-
- if _structseq and not hasattr(_structseq, '__file__'):
- # PyPy 2.4 has no __file__ in the builtin modules, but the code
- # objects still have the file names. So dig into one to find
- # the path to exclude. The "filename" might be synthetic,
- # don't be fooled by those.
- structseq_file = code_object(_structseq.structseq_new).co_filename
- if not structseq_file.startswith("<"):
- self.pylib_paths.add(canonical_path(structseq_file))
-
- # To avoid tracing the coverage.py code itself, we skip anything
- # located where we are.
- if getattr(sys, 'is_standalone_binary', False):
- self.cover_paths = ["contrib/python/coverage"]
- else:
- self.cover_paths = [canonical_path(__file__, directory=True)]
- if env.TESTING:
- # Don't include our own test code.
- self.cover_paths.append(os.path.join(self.cover_paths[0], "tests"))
-
- # When testing, we use PyContracts, which should be considered
- # part of coverage.py, and it uses six. Exclude those directories
- # just as we exclude ourselves.
- import contracts
- import six
- for mod in [contracts, six]:
- self.cover_paths.append(canonical_path(mod))
-
- def debug(msg):
- if self.debug:
- self.debug.write(msg)
-
- # Create the matchers we need for should_trace
- if self.source or self.source_pkgs:
- against = []
- if self.source:
- self.source_match = TreeMatcher(self.source)
- against.append("trees {!r}".format(self.source_match))
- if self.source_pkgs:
- self.source_pkgs_match = ModuleMatcher(self.source_pkgs)
- against.append("modules {!r}".format(self.source_pkgs_match))
- debug("Source matching against " + " and ".join(against))
- else:
- if self.cover_paths:
- self.cover_match = TreeMatcher(self.cover_paths)
- debug("Coverage code matching: {!r}".format(self.cover_match))
- if self.pylib_paths:
- self.pylib_match = TreeMatcher(self.pylib_paths)
- debug("Python stdlib matching: {!r}".format(self.pylib_match))
- if self.include:
- self.include_match = FnmatchMatcher(self.include)
- debug("Include matching: {!r}".format(self.include_match))
- if self.omit:
- self.omit_match = FnmatchMatcher(self.omit)
- debug("Omit matching: {!r}".format(self.omit_match))
-
- def should_trace(self, filename, frame=None):
- """Decide whether to trace execution in `filename`, with a reason.
-
- This function is called from the trace function. As each new file name
- is encountered, this function determines whether it is traced or not.
-
- Returns a FileDisposition object.
-
- """
- original_filename = filename
- disp = disposition_init(self.disp_class, filename)
-
- def nope(disp, reason):
- """Simple helper to make it easy to return NO."""
- disp.trace = False
- disp.reason = reason
- return disp
-
- if frame is not None:
- # Compiled Python files have two file names: frame.f_code.co_filename is
- # the file name at the time the .pyc was compiled. The second name is
- # __file__, which is where the .pyc was actually loaded from. Since
- # .pyc files can be moved after compilation (for example, by being
- # installed), we look for __file__ in the frame and prefer it to the
- # co_filename value.
- dunder_file = frame.f_globals and frame.f_globals.get('__file__')
- if dunder_file:
- filename = source_for_file(dunder_file)
- if original_filename and not original_filename.startswith('<'):
- orig = os.path.basename(original_filename)
- if orig != os.path.basename(filename):
- # Files shouldn't be renamed when moved. This happens when
- # exec'ing code. If it seems like something is wrong with
- # the frame's file name, then just use the original.
- filename = original_filename
-
- if not filename:
- # Empty string is pretty useless.
- return nope(disp, "empty string isn't a file name")
-
- if filename.startswith('memory:'):
- return nope(disp, "memory isn't traceable")
-
- if filename.startswith('<'):
- # Lots of non-file execution is represented with artificial
- # file names like "<string>", "<doctest readme.txt[0]>", or
- # "<exec_function>". Don't ever trace these executions, since we
- # can't do anything with the data later anyway.
- return nope(disp, "not a real file name")
-
- # pyexpat does a dumb thing, calling the trace function explicitly from
- # C code with a C file name.
- if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename):
- return nope(disp, "pyexpat lies about itself")
-
- # Jython reports the .class file to the tracer, use the source file.
- if filename.endswith("$py.class"):
- filename = filename[:-9] + ".py"
-
- # XXX maybe we need to support both at the same time?
- # Don't trace modules imported from environment in standalone mode
- if getattr(sys, 'is_standalone_binary', False) and filename.startswith("/"):
- return nope(disp, "skip modules from environment")
-
- canonical = canonical_filename(filename)
- disp.canonical_filename = canonical
-
- # Try the plugins, see if they have an opinion about the file.
- plugin = None
- for plugin in self.plugins.file_tracers:
- if not plugin._coverage_enabled:
- continue
-
- try:
- file_tracer = plugin.file_tracer(canonical)
- if file_tracer is not None:
- file_tracer._coverage_plugin = plugin
- disp.trace = True
- disp.file_tracer = file_tracer
- if file_tracer.has_dynamic_source_filename():
- disp.has_dynamic_filename = True
- else:
- disp.source_filename = canonical_filename(
- file_tracer.source_filename()
- )
- break
- except Exception:
- if not self.config.suppress_plugin_errors:
- raise
- self.warn(
- "Disabling plug-in %r due to an exception:" % (plugin._coverage_plugin_name)
- )
- traceback.print_exc()
- plugin._coverage_enabled = False
- continue
- else:
- # No plugin wanted it: it's Python.
- disp.trace = True
- disp.source_filename = canonical
-
- if not disp.has_dynamic_filename:
- if not disp.source_filename:
- raise CoverageException(
- "Plugin %r didn't set source_filename for %r" %
- (plugin, disp.original_filename)
- )
- reason = self.check_include_omit_etc(disp.source_filename, frame)
- if reason:
- nope(disp, reason)
-
- return disp
-
- def check_include_omit_etc(self, filename, frame):
- """Check a file name against the include, omit, etc, rules.
-
- Returns a string or None. String means, don't trace, and is the reason
- why. None means no reason found to not trace.
-
- """
- modulename = name_for_module(filename, frame)
-
- # If the user specified source or include, then that's authoritative
- # about the outer bound of what to measure and we don't have to apply
- # any canned exclusions. If they didn't, then we have to exclude the
- # stdlib and coverage.py directories.
- if self.source_match or self.source_pkgs_match:
- extra = ""
- ok = False
- if self.source_pkgs_match:
- if self.source_pkgs_match.match(modulename):
- ok = True
- if modulename in self.source_pkgs_unmatched:
- self.source_pkgs_unmatched.remove(modulename)
- else:
- extra = "module {!r} ".format(modulename)
- if not ok and self.source_match:
- if self.source_match.match(filename):
- ok = True
- if not ok:
- return extra + "falls outside the --source spec"
- elif self.include_match:
- if not self.include_match.match(filename):
- return "falls outside the --include trees"
- else:
- # If we aren't supposed to trace installed code, then check if this
- # is near the Python standard library and skip it if so.
- if self.pylib_match and self.pylib_match.match(filename):
- return "is in the stdlib"
-
- # We exclude the coverage.py code itself, since a little of it
- # will be measured otherwise.
- if self.cover_match and self.cover_match.match(filename):
- return "is part of coverage.py"
-
- # Check the file against the omit pattern.
- if self.omit_match and self.omit_match.match(filename):
- return "is inside an --omit pattern"
-
- # No point tracing a file we can't later write to SQLite.
- try:
- filename.encode("utf8")
- except UnicodeEncodeError:
- return "non-encodable filename"
-
- # No reason found to skip this file.
- return None
-
- def warn_conflicting_settings(self):
- """Warn if there are settings that conflict."""
- if self.include:
- if self.source or self.source_pkgs:
- self.warn("--include is ignored because --source is set", slug="include-ignored")
-
- def warn_already_imported_files(self):
- """Warn if files have already been imported that we will be measuring."""
- if self.include or self.source or self.source_pkgs:
- warned = set()
- for mod in list(sys.modules.values()):
- filename = getattr(mod, "__file__", None)
- if filename is None:
- continue
- if filename in warned:
- continue
-
- disp = self.should_trace(filename)
- if disp.trace:
- msg = "Already imported a file that will be measured: {}".format(filename)
- self.warn(msg, slug="already-imported")
- warned.add(filename)
-
- def warn_unimported_source(self):
- """Warn about source packages that were of interest, but never traced."""
- for pkg in self.source_pkgs_unmatched:
- self._warn_about_unmeasured_code(pkg)
-
- def _warn_about_unmeasured_code(self, pkg):
- """Warn about a package or module that we never traced.
-
- `pkg` is a string, the name of the package or module.
-
- """
- mod = sys.modules.get(pkg)
- if mod is None:
- self.warn("Module %s was never imported." % pkg, slug="module-not-imported")
- return
-
- if module_is_namespace(mod):
- # A namespace package. It's OK for this not to have been traced,
- # since there is no code directly in it.
- return
-
- if not module_has_file(mod):
- self.warn("Module %s has no Python source." % pkg, slug="module-not-python")
- return
-
- # The module was in sys.modules, and seems like a module with code, but
- # we never measured it. I guess that means it was imported before
- # coverage even started.
- self.warn(
- "Module %s was previously imported, but not measured" % pkg,
- slug="module-not-measured",
- )
-
- def find_possibly_unexecuted_files(self):
- """Find files in the areas of interest that might be untraced.
-
- Yields pairs: file path, and responsible plug-in name.
- """
- for pkg in self.source_pkgs:
- if (not pkg in sys.modules or
- not module_has_file(sys.modules[pkg])):
- continue
- pkg_file = source_for_file(sys.modules[pkg].__file__)
- for ret in self._find_executable_files(canonical_path(pkg_file)):
- yield ret
-
- for src in self.source:
- for ret in self._find_executable_files(src):
- yield ret
-
- def _find_plugin_files(self, src_dir):
- """Get executable files from the plugins."""
- for plugin in self.plugins.file_tracers:
- for x_file in plugin.find_executable_files(src_dir):
- yield x_file, plugin._coverage_plugin_name
-
- def _find_executable_files(self, src_dir):
- """Find executable files in `src_dir`.
-
- Search for files in `src_dir` that can be executed because they
- are probably importable. Don't include ones that have been omitted
- by the configuration.
-
- Yield the file path, and the plugin name that handles the file.
-
- """
- py_files = ((py_file, None) for py_file in find_python_files(src_dir))
- plugin_files = self._find_plugin_files(src_dir)
-
- for file_path, plugin_name in itertools.chain(py_files, plugin_files):
- file_path = canonical_filename(file_path)
- if self.omit_match and self.omit_match.match(file_path):
- # Turns out this file was omitted, so don't pull it back
- # in as unexecuted.
- continue
- yield file_path, plugin_name
-
- def sys_info(self):
- """Our information for Coverage.sys_info.
-
- Returns a list of (key, value) pairs.
- """
- info = [
- ('cover_paths', self.cover_paths),
- ('pylib_paths', self.pylib_paths),
- ]
-
- matcher_names = [
- 'source_match', 'source_pkgs_match',
- 'include_match', 'omit_match',
- 'cover_match', 'pylib_match',
- ]
-
- for matcher_name in matcher_names:
- matcher = getattr(self, matcher_name)
- if matcher:
- matcher_info = matcher.info()
- else:
- matcher_info = '-none-'
- info.append((matcher_name, matcher_info))
-
- return info
diff --git a/contrib/python/coverage/py3/coverage/jsonreport.py b/contrib/python/coverage/py3/coverage/jsonreport.py
deleted file mode 100644
index 4287bc79a3..0000000000
--- a/contrib/python/coverage/py3/coverage/jsonreport.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# coding: utf-8
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Json reporting for coverage.py"""
-import datetime
-import json
-import sys
-
-from coverage import __version__
-from coverage.report import get_analysis_to_report
-from coverage.results import Numbers
-
-
-class JsonReporter(object):
- """A reporter for writing JSON coverage results."""
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
- self.total = Numbers()
- self.report_data = {}
-
- def report(self, morfs, outfile=None):
- """Generate a json report for `morfs`.
-
- `morfs` is a list of modules or file names.
-
- `outfile` is a file object to write the json to
-
- """
- outfile = outfile or sys.stdout
- coverage_data = self.coverage.get_data()
- coverage_data.set_query_contexts(self.config.report_contexts)
- self.report_data["meta"] = {
- "version": __version__,
- "timestamp": datetime.datetime.now().isoformat(),
- "branch_coverage": coverage_data.has_arcs(),
- "show_contexts": self.config.json_show_contexts,
- }
-
- measured_files = {}
- for file_reporter, analysis in get_analysis_to_report(self.coverage, morfs):
- measured_files[file_reporter.relative_filename()] = self.report_one_file(
- coverage_data,
- analysis
- )
-
- self.report_data["files"] = measured_files
-
- self.report_data["totals"] = {
- 'covered_lines': self.total.n_executed,
- 'num_statements': self.total.n_statements,
- 'percent_covered': self.total.pc_covered,
- 'missing_lines': self.total.n_missing,
- 'excluded_lines': self.total.n_excluded,
- }
-
- if coverage_data.has_arcs():
- self.report_data["totals"].update({
- 'num_branches': self.total.n_branches,
- 'num_partial_branches': self.total.n_partial_branches,
- 'covered_branches': self.total.n_executed_branches,
- 'missing_branches': self.total.n_missing_branches,
- })
-
- json.dump(
- self.report_data,
- outfile,
- indent=4 if self.config.json_pretty_print else None
- )
-
- return self.total.n_statements and self.total.pc_covered
-
- def report_one_file(self, coverage_data, analysis):
- """Extract the relevant report data for a single file"""
- nums = analysis.numbers
- self.total += nums
- summary = {
- 'covered_lines': nums.n_executed,
- 'num_statements': nums.n_statements,
- 'percent_covered': nums.pc_covered,
- 'missing_lines': nums.n_missing,
- 'excluded_lines': nums.n_excluded,
- }
- reported_file = {
- 'executed_lines': sorted(analysis.executed),
- 'summary': summary,
- 'missing_lines': sorted(analysis.missing),
- 'excluded_lines': sorted(analysis.excluded)
- }
- if self.config.json_show_contexts:
- reported_file['contexts'] = analysis.data.contexts_by_lineno(
- analysis.filename,
- )
- if coverage_data.has_arcs():
- reported_file['summary'].update({
- 'num_branches': nums.n_branches,
- 'num_partial_branches': nums.n_partial_branches,
- 'covered_branches': nums.n_executed_branches,
- 'missing_branches': nums.n_missing_branches,
- })
- return reported_file
diff --git a/contrib/python/coverage/py3/coverage/misc.py b/contrib/python/coverage/py3/coverage/misc.py
deleted file mode 100644
index 034e288eb9..0000000000
--- a/contrib/python/coverage/py3/coverage/misc.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Miscellaneous stuff for coverage.py."""
-
-import errno
-import hashlib
-import inspect
-import locale
-import os
-import os.path
-import random
-import re
-import socket
-import sys
-import types
-
-from coverage import env
-from coverage.backward import to_bytes, unicode_class
-
-ISOLATED_MODULES = {}
-
-
-def isolate_module(mod):
- """Copy a module so that we are isolated from aggressive mocking.
-
- If a test suite mocks os.path.exists (for example), and then we need to use
- it during the test, everything will get tangled up if we use their mock.
- Making a copy of the module when we import it will isolate coverage.py from
- those complications.
- """
- if mod not in ISOLATED_MODULES:
- new_mod = types.ModuleType(mod.__name__)
- ISOLATED_MODULES[mod] = new_mod
- for name in dir(mod):
- value = getattr(mod, name)
- if isinstance(value, types.ModuleType):
- value = isolate_module(value)
- setattr(new_mod, name, value)
- return ISOLATED_MODULES[mod]
-
-os = isolate_module(os)
-
-
-def dummy_decorator_with_args(*args_unused, **kwargs_unused):
- """Dummy no-op implementation of a decorator with arguments."""
- def _decorator(func):
- return func
- return _decorator
-
-
-# Environment COVERAGE_NO_CONTRACTS=1 can turn off contracts while debugging
-# tests to remove noise from stack traces.
-# $set_env.py: COVERAGE_NO_CONTRACTS - Disable PyContracts to simplify stack traces.
-USE_CONTRACTS = env.TESTING and not bool(int(os.environ.get("COVERAGE_NO_CONTRACTS", 0)))
-
-# Use PyContracts for assertion testing on parameters and returns, but only if
-# we are running our own test suite.
-if USE_CONTRACTS:
- from contracts import contract # pylint: disable=unused-import
- from contracts import new_contract as raw_new_contract
-
- def new_contract(*args, **kwargs):
- """A proxy for contracts.new_contract that doesn't mind happening twice."""
- try:
- raw_new_contract(*args, **kwargs)
- except ValueError:
- # During meta-coverage, this module is imported twice, and
- # PyContracts doesn't like redefining contracts. It's OK.
- pass
-
- # Define contract words that PyContract doesn't have.
- new_contract('bytes', lambda v: isinstance(v, bytes))
- if env.PY3:
- new_contract('unicode', lambda v: isinstance(v, unicode_class))
-
- def one_of(argnames):
- """Ensure that only one of the argnames is non-None."""
- def _decorator(func):
- argnameset = {name.strip() for name in argnames.split(",")}
- def _wrapper(*args, **kwargs):
- vals = [kwargs.get(name) for name in argnameset]
- assert sum(val is not None for val in vals) == 1
- return func(*args, **kwargs)
- return _wrapper
- return _decorator
-else: # pragma: not testing
- # We aren't using real PyContracts, so just define our decorators as
- # stunt-double no-ops.
- contract = dummy_decorator_with_args
- one_of = dummy_decorator_with_args
-
- def new_contract(*args_unused, **kwargs_unused):
- """Dummy no-op implementation of `new_contract`."""
- pass
-
-
-def nice_pair(pair):
- """Make a nice string representation of a pair of numbers.
-
- If the numbers are equal, just return the number, otherwise return the pair
- with a dash between them, indicating the range.
-
- """
- start, end = pair
- if start == end:
- return "%d" % start
- else:
- return "%d-%d" % (start, end)
-
-
-def expensive(fn):
- """A decorator to indicate that a method shouldn't be called more than once.
-
- Normally, this does nothing. During testing, this raises an exception if
- called more than once.
-
- """
- if env.TESTING:
- attr = "_once_" + fn.__name__
-
- def _wrapper(self):
- if hasattr(self, attr):
- raise AssertionError("Shouldn't have called %s more than once" % fn.__name__)
- setattr(self, attr, True)
- return fn(self)
- return _wrapper
- else:
- return fn # pragma: not testing
-
-
-def bool_or_none(b):
- """Return bool(b), but preserve None."""
- if b is None:
- return None
- else:
- return bool(b)
-
-
-def join_regex(regexes):
- """Combine a list of regexes into one that matches any of them."""
- return "|".join("(?:%s)" % r for r in regexes)
-
-
-def file_be_gone(path):
- """Remove a file, and don't get annoyed if it doesn't exist."""
- try:
- os.remove(path)
- except OSError as e:
- if e.errno != errno.ENOENT:
- raise
-
-
-def ensure_dir(directory):
- """Make sure the directory exists.
-
- If `directory` is None or empty, do nothing.
- """
- if directory and not os.path.isdir(directory):
- os.makedirs(directory)
-
-
-def ensure_dir_for_file(path):
- """Make sure the directory for the path exists."""
- ensure_dir(os.path.dirname(path))
-
-
-def output_encoding(outfile=None):
- """Determine the encoding to use for output written to `outfile` or stdout."""
- if outfile is None:
- outfile = sys.stdout
- encoding = (
- getattr(outfile, "encoding", None) or
- getattr(sys.__stdout__, "encoding", None) or
- locale.getpreferredencoding()
- )
- return encoding
-
-
-def filename_suffix(suffix):
- """Compute a filename suffix for a data file.
-
- If `suffix` is a string or None, simply return it. If `suffix` is True,
- then build a suffix incorporating the hostname, process id, and a random
- number.
-
- Returns a string or None.
-
- """
- if suffix is True:
- # If data_suffix was a simple true value, then make a suffix with
- # plenty of distinguishing information. We do this here in
- # `save()` at the last minute so that the pid will be correct even
- # if the process forks.
- dice = random.Random(os.urandom(8)).randint(0, 999999)
- suffix = "%s.%s.%06d" % (socket.gethostname(), os.getpid(), dice)
- return suffix
-
-
-class Hasher(object):
- """Hashes Python data into md5."""
- def __init__(self):
- self.md5 = hashlib.md5()
-
- def update(self, v):
- """Add `v` to the hash, recursively if needed."""
- self.md5.update(to_bytes(str(type(v))))
- if isinstance(v, unicode_class):
- self.md5.update(v.encode('utf8'))
- elif isinstance(v, bytes):
- self.md5.update(v)
- elif v is None:
- pass
- elif isinstance(v, (int, float)):
- self.md5.update(to_bytes(str(v)))
- elif isinstance(v, (tuple, list)):
- for e in v:
- self.update(e)
- elif isinstance(v, dict):
- keys = v.keys()
- for k in sorted(keys):
- self.update(k)
- self.update(v[k])
- else:
- for k in dir(v):
- if k.startswith('__'):
- continue
- a = getattr(v, k)
- if inspect.isroutine(a):
- continue
- self.update(k)
- self.update(a)
- self.md5.update(b'.')
-
- def hexdigest(self):
- """Retrieve the hex digest of the hash."""
- return self.md5.hexdigest()
-
-
-def _needs_to_implement(that, func_name):
- """Helper to raise NotImplementedError in interface stubs."""
- if hasattr(that, "_coverage_plugin_name"):
- thing = "Plugin"
- name = that._coverage_plugin_name
- else:
- thing = "Class"
- klass = that.__class__
- name = "{klass.__module__}.{klass.__name__}".format(klass=klass)
-
- raise NotImplementedError(
- "{thing} {name!r} needs to implement {func_name}()".format(
- thing=thing, name=name, func_name=func_name
- )
- )
-
-
-class DefaultValue(object):
- """A sentinel object to use for unusual default-value needs.
-
- Construct with a string that will be used as the repr, for display in help
- and Sphinx output.
-
- """
- def __init__(self, display_as):
- self.display_as = display_as
-
- def __repr__(self):
- return self.display_as
-
-
-def substitute_variables(text, variables):
- """Substitute ``${VAR}`` variables in `text` with their values.
-
- Variables in the text can take a number of shell-inspired forms::
-
- $VAR
- ${VAR}
- ${VAR?} strict: an error if VAR isn't defined.
- ${VAR-missing} defaulted: "missing" if VAR isn't defined.
- $$ just a dollar sign.
-
- `variables` is a dictionary of variable values.
-
- Returns the resulting text with values substituted.
-
- """
- dollar_pattern = r"""(?x) # Use extended regex syntax
- \$ # A dollar sign,
- (?: # then
- (?P<dollar>\$) | # a dollar sign, or
- (?P<word1>\w+) | # a plain word, or
- { # a {-wrapped
- (?P<word2>\w+) # word,
- (?:
- (?P<strict>\?) | # with a strict marker
- -(?P<defval>[^}]*) # or a default value
- )? # maybe.
- }
- )
- """
-
- def dollar_replace(match):
- """Called for each $replacement."""
- # Only one of the groups will have matched, just get its text.
- word = next(g for g in match.group('dollar', 'word1', 'word2') if g)
- if word == "$":
- return "$"
- elif word in variables:
- return variables[word]
- elif match.group('strict'):
- msg = "Variable {} is undefined: {!r}".format(word, text)
- raise CoverageException(msg)
- else:
- return match.group('defval')
-
- text = re.sub(dollar_pattern, dollar_replace, text)
- return text
-
-
-class BaseCoverageException(Exception):
- """The base of all Coverage exceptions."""
- pass
-
-
-class CoverageException(BaseCoverageException):
- """An exception raised by a coverage.py function."""
- pass
-
-
-class NoSource(CoverageException):
- """We couldn't find the source for a module."""
- pass
-
-
-class NoCode(NoSource):
- """We couldn't find any code at all."""
- pass
-
-
-class NotPython(CoverageException):
- """A source file turned out not to be parsable Python."""
- pass
-
-
-class ExceptionDuringRun(CoverageException):
- """An exception happened while running customer code.
-
- Construct it with three arguments, the values from `sys.exc_info`.
-
- """
- pass
-
-
-class StopEverything(BaseCoverageException):
- """An exception that means everything should stop.
-
- The CoverageTest class converts these to SkipTest, so that when running
- tests, raising this exception will automatically skip the test.
-
- """
- pass
diff --git a/contrib/python/coverage/py3/coverage/multiproc.py b/contrib/python/coverage/py3/coverage/multiproc.py
deleted file mode 100644
index 21ed2e2c95..0000000000
--- a/contrib/python/coverage/py3/coverage/multiproc.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Monkey-patching to add multiprocessing support for coverage.py"""
-
-import multiprocessing
-import multiprocessing.process
-import os
-import os.path
-import sys
-import traceback
-
-from coverage import env
-from coverage.misc import contract
-
-# An attribute that will be set on the module to indicate that it has been
-# monkey-patched.
-PATCHED_MARKER = "_coverage$patched"
-
-COVERAGE_CONFIGURATION_ENV = "_COVERAGE_CONFIGURATION_ENV"
-
-
-if env.PYVERSION >= (3, 4):
- OriginalProcess = multiprocessing.process.BaseProcess
-else:
- OriginalProcess = multiprocessing.Process
-
-original_bootstrap = OriginalProcess._bootstrap
-
-class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method
- """A replacement for multiprocess.Process that starts coverage."""
-
- def _bootstrap(self, *args, **kwargs):
- """Wrapper around _bootstrap to start coverage."""
- try:
- from coverage import Coverage # avoid circular import
- import json
- kwconf = json.loads(os.environ[COVERAGE_CONFIGURATION_ENV])
- cov = Coverage(**kwconf)
- cov._warn_preimported_source = False
- cov.start()
- debug = cov._debug
- if debug.should("multiproc"):
- debug.write("Calling multiprocessing bootstrap")
- except Exception:
- print("Exception during multiprocessing bootstrap init:")
- traceback.print_exc(file=sys.stdout)
- sys.stdout.flush()
- raise
- try:
- return original_bootstrap(self, *args, **kwargs)
- finally:
- if debug.should("multiproc"):
- debug.write("Finished multiprocessing bootstrap")
- cov.stop()
- cov.save()
- if debug.should("multiproc"):
- debug.write("Saved multiprocessing data")
-
-class Stowaway(object):
- """An object to pickle, so when it is unpickled, it can apply the monkey-patch."""
- def __init__(self, rcfile):
- self.rcfile = rcfile
-
- def __getstate__(self):
- return {'rcfile': self.rcfile}
-
- def __setstate__(self, state):
- patch_multiprocessing(state['rcfile'])
-
-
-@contract(rcfile=str)
-def patch_multiprocessing(rcfile, coverage_args):
- """Monkey-patch the multiprocessing module.
-
- This enables coverage measurement of processes started by multiprocessing.
- This involves aggressive monkey-patching.
-
- `rcfile` is the path to the rcfile being used.
-
- """
-
- if hasattr(multiprocessing, PATCHED_MARKER):
- return
-
- if env.PYVERSION >= (3, 4):
- OriginalProcess._bootstrap = ProcessWithCoverage._bootstrap
- else:
- multiprocessing.Process = ProcessWithCoverage
-
- # Set the value in ProcessWithCoverage that will be pickled into the child
- # process.
- os.environ["COVERAGE_RCFILE"] = os.path.abspath(rcfile)
-
- os.environ[COVERAGE_CONFIGURATION_ENV] = coverage_args
-
- # When spawning processes rather than forking them, we have no state in the
- # new process. We sneak in there with a Stowaway: we stuff one of our own
- # objects into the data that gets pickled and sent to the sub-process. When
- # the Stowaway is unpickled, it's __setstate__ method is called, which
- # re-applies the monkey-patch.
- # Windows only spawns, so this is needed to keep Windows working.
- try:
- from multiprocessing import spawn
- original_get_preparation_data = spawn.get_preparation_data
- except (ImportError, AttributeError):
- pass
- else:
- def get_preparation_data_with_stowaway(name):
- """Get the original preparation data, and also insert our stowaway."""
- d = original_get_preparation_data(name)
- d['stowaway'] = Stowaway(rcfile)
- return d
-
- spawn.get_preparation_data = get_preparation_data_with_stowaway
-
- setattr(multiprocessing, PATCHED_MARKER, True)
diff --git a/contrib/python/coverage/py3/coverage/numbits.py b/contrib/python/coverage/py3/coverage/numbits.py
deleted file mode 100644
index 6ca96fbcf7..0000000000
--- a/contrib/python/coverage/py3/coverage/numbits.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""
-Functions to manipulate packed binary representations of number sets.
-
-To save space, coverage stores sets of line numbers in SQLite using a packed
-binary representation called a numbits. A numbits is a set of positive
-integers.
-
-A numbits is stored as a blob in the database. The exact meaning of the bytes
-in the blobs should be considered an implementation detail that might change in
-the future. Use these functions to work with those binary blobs of data.
-
-"""
-import json
-
-from coverage import env
-from coverage.backward import byte_to_int, bytes_to_ints, binary_bytes, zip_longest
-from coverage.misc import contract, new_contract
-
-if env.PY3:
- def _to_blob(b):
- """Convert a bytestring into a type SQLite will accept for a blob."""
- return b
-
- new_contract('blob', lambda v: isinstance(v, bytes))
-else:
- def _to_blob(b):
- """Convert a bytestring into a type SQLite will accept for a blob."""
- return buffer(b) # pylint: disable=undefined-variable
-
- new_contract('blob', lambda v: isinstance(v, buffer)) # pylint: disable=undefined-variable
-
-
-@contract(nums='Iterable', returns='blob')
-def nums_to_numbits(nums):
- """Convert `nums` into a numbits.
-
- Arguments:
- nums: a reusable iterable of integers, the line numbers to store.
-
- Returns:
- A binary blob.
- """
- try:
- nbytes = max(nums) // 8 + 1
- except ValueError:
- # nums was empty.
- return _to_blob(b'')
- b = bytearray(nbytes)
- for num in nums:
- b[num//8] |= 1 << num % 8
- return _to_blob(bytes(b))
-
-
-@contract(numbits='blob', returns='list[int]')
-def numbits_to_nums(numbits):
- """Convert a numbits into a list of numbers.
-
- Arguments:
- numbits: a binary blob, the packed number set.
-
- Returns:
- A list of ints.
-
- When registered as a SQLite function by :func:`register_sqlite_functions`,
- this returns a string, a JSON-encoded list of ints.
-
- """
- nums = []
- for byte_i, byte in enumerate(bytes_to_ints(numbits)):
- for bit_i in range(8):
- if (byte & (1 << bit_i)):
- nums.append(byte_i * 8 + bit_i)
- return nums
-
-
-@contract(numbits1='blob', numbits2='blob', returns='blob')
-def numbits_union(numbits1, numbits2):
- """Compute the union of two numbits.
-
- Returns:
- A new numbits, the union of `numbits1` and `numbits2`.
- """
- byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
- return _to_blob(binary_bytes(b1 | b2 for b1, b2 in byte_pairs))
-
-
-@contract(numbits1='blob', numbits2='blob', returns='blob')
-def numbits_intersection(numbits1, numbits2):
- """Compute the intersection of two numbits.
-
- Returns:
- A new numbits, the intersection `numbits1` and `numbits2`.
- """
- byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
- intersection_bytes = binary_bytes(b1 & b2 for b1, b2 in byte_pairs)
- return _to_blob(intersection_bytes.rstrip(b'\0'))
-
-
-@contract(numbits1='blob', numbits2='blob', returns='bool')
-def numbits_any_intersection(numbits1, numbits2):
- """Is there any number that appears in both numbits?
-
- Determine whether two number sets have a non-empty intersection. This is
- faster than computing the intersection.
-
- Returns:
- A bool, True if there is any number in both `numbits1` and `numbits2`.
- """
- byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
- return any(b1 & b2 for b1, b2 in byte_pairs)
-
-
-@contract(num='int', numbits='blob', returns='bool')
-def num_in_numbits(num, numbits):
- """Does the integer `num` appear in `numbits`?
-
- Returns:
- A bool, True if `num` is a member of `numbits`.
- """
- nbyte, nbit = divmod(num, 8)
- if nbyte >= len(numbits):
- return False
- return bool(byte_to_int(numbits[nbyte]) & (1 << nbit))
-
-
-def register_sqlite_functions(connection):
- """
- Define numbits functions in a SQLite connection.
-
- This defines these functions for use in SQLite statements:
-
- * :func:`numbits_union`
- * :func:`numbits_intersection`
- * :func:`numbits_any_intersection`
- * :func:`num_in_numbits`
- * :func:`numbits_to_nums`
-
- `connection` is a :class:`sqlite3.Connection <python:sqlite3.Connection>`
- object. After creating the connection, pass it to this function to
- register the numbits functions. Then you can use numbits functions in your
- queries::
-
- import sqlite3
- from coverage.numbits import register_sqlite_functions
-
- conn = sqlite3.connect('example.db')
- register_sqlite_functions(conn)
- c = conn.cursor()
- # Kind of a nonsense query: find all the files and contexts that
- # executed line 47 in any file:
- c.execute(
- "select file_id, context_id from line_bits where num_in_numbits(?, numbits)",
- (47,)
- )
- """
- connection.create_function("numbits_union", 2, numbits_union)
- connection.create_function("numbits_intersection", 2, numbits_intersection)
- connection.create_function("numbits_any_intersection", 2, numbits_any_intersection)
- connection.create_function("num_in_numbits", 2, num_in_numbits)
- connection.create_function("numbits_to_nums", 1, lambda b: json.dumps(numbits_to_nums(b)))
diff --git a/contrib/python/coverage/py3/coverage/parser.py b/contrib/python/coverage/py3/coverage/parser.py
deleted file mode 100644
index 258f956039..0000000000
--- a/contrib/python/coverage/py3/coverage/parser.py
+++ /dev/null
@@ -1,1276 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Code parsing for coverage.py."""
-
-import ast
-import collections
-import os
-import re
-import token
-import tokenize
-
-from coverage import env
-from coverage.backward import range # pylint: disable=redefined-builtin
-from coverage.backward import bytes_to_ints, string_class
-from coverage.bytecode import code_objects
-from coverage.debug import short_stack
-from coverage.misc import contract, join_regex, new_contract, nice_pair, one_of
-from coverage.misc import NoSource, NotPython, StopEverything
-from coverage.phystokens import compile_unicode, generate_tokens, neuter_encoding_declaration
-
-
-class PythonParser(object):
- """Parse code to find executable lines, excluded lines, etc.
-
- This information is all based on static analysis: no code execution is
- involved.
-
- """
- @contract(text='unicode|None')
- def __init__(self, text=None, filename=None, exclude=None):
- """
- Source can be provided as `text`, the text itself, or `filename`, from
- which the text will be read. Excluded lines are those that match
- `exclude`, a regex.
-
- """
- assert text or filename, "PythonParser needs either text or filename"
- self.filename = filename or "<code>"
- self.text = text
- if not self.text:
- from coverage.python import get_python_source
- try:
- self.text = get_python_source(self.filename)
- except IOError as err:
- raise NoSource(
- "No source for code: '%s': %s" % (self.filename, err)
- )
-
- self.exclude = exclude
-
- # The text lines of the parsed code.
- self.lines = self.text.split('\n')
-
- # The normalized line numbers of the statements in the code. Exclusions
- # are taken into account, and statements are adjusted to their first
- # lines.
- self.statements = set()
-
- # The normalized line numbers of the excluded lines in the code,
- # adjusted to their first lines.
- self.excluded = set()
-
- # The raw_* attributes are only used in this class, and in
- # lab/parser.py to show how this class is working.
-
- # The line numbers that start statements, as reported by the line
- # number table in the bytecode.
- self.raw_statements = set()
-
- # The raw line numbers of excluded lines of code, as marked by pragmas.
- self.raw_excluded = set()
-
- # The line numbers of class definitions.
- self.raw_classdefs = set()
-
- # Function definitions (start, end, name)
- self._raw_funcdefs = set()
-
- # The line numbers of docstring lines.
- self.raw_docstrings = set()
-
- # Internal detail, used by lab/parser.py.
- self.show_tokens = False
-
- # A dict mapping line numbers to lexical statement starts for
- # multi-line statements.
- self._multiline = {}
-
- # Lazily-created ByteParser, arc data, and missing arc descriptions.
- self._byte_parser = None
- self._all_arcs = None
- self._missing_arc_fragments = None
-
- @property
- def byte_parser(self):
- """Create a ByteParser on demand."""
- if not self._byte_parser:
- self._byte_parser = ByteParser(self.text, filename=self.filename)
- return self._byte_parser
-
- @property
- def raw_funcdefs(self):
- return self._raw_funcdefs
-
- def lines_matching(self, *regexes):
- """Find the lines matching one of a list of regexes.
-
- Returns a set of line numbers, the lines that contain a match for one
- of the regexes in `regexes`. The entire line needn't match, just a
- part of it.
-
- """
- combined = join_regex(regexes)
- if env.PY2:
- combined = combined.decode("utf8")
- regex_c = re.compile(combined)
- matches = set()
- for i, ltext in enumerate(self.lines, start=1):
- if regex_c.search(ltext):
- matches.add(i)
- return matches
-
- def _raw_parse(self):
- """Parse the source to find the interesting facts about its lines.
-
- A handful of attributes are updated.
-
- """
- # Find lines which match an exclusion pattern.
- if self.exclude:
- self.raw_excluded = self.lines_matching(self.exclude)
-
- # Tokenize, to find excluded suites, to find docstrings, and to find
- # multi-line statements.
- indent = 0
- exclude_indent = 0
- excluding = False
- excluding_decorators = False
- prev_toktype = token.INDENT
- first_line = None
- empty = True
- first_on_line = True
-
- tokgen = generate_tokens(self.text)
- for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
- if self.show_tokens: # pragma: debugging
- print("%10s %5s %-20r %r" % (
- tokenize.tok_name.get(toktype, toktype),
- nice_pair((slineno, elineno)), ttext, ltext
- ))
- if toktype == token.INDENT:
- indent += 1
- elif toktype == token.DEDENT:
- indent -= 1
- elif toktype == token.NAME:
- if ttext == 'class':
- # Class definitions look like branches in the bytecode, so
- # we need to exclude them. The simplest way is to note the
- # lines with the 'class' keyword.
- self.raw_classdefs.add(slineno)
- elif toktype == token.OP:
- if ttext == ':':
- should_exclude = (elineno in self.raw_excluded) or excluding_decorators
- if not excluding and should_exclude:
- # Start excluding a suite. We trigger off of the colon
- # token so that the #pragma comment will be recognized on
- # the same line as the colon.
- self.raw_excluded.add(elineno)
- exclude_indent = indent
- excluding = True
- excluding_decorators = False
- elif ttext == '@' and first_on_line:
- # A decorator.
- if elineno in self.raw_excluded:
- excluding_decorators = True
- if excluding_decorators:
- self.raw_excluded.add(elineno)
- elif toktype == token.STRING and prev_toktype == token.INDENT:
- # Strings that are first on an indented line are docstrings.
- # (a trick from trace.py in the stdlib.) This works for
- # 99.9999% of cases. For the rest (!) see:
- # http://stackoverflow.com/questions/1769332/x/1769794#1769794
- self.raw_docstrings.update(range(slineno, elineno+1))
- elif toktype == token.NEWLINE:
- if first_line is not None and elineno != first_line:
- # We're at the end of a line, and we've ended on a
- # different line than the first line of the statement,
- # so record a multi-line range.
- for l in range(first_line, elineno+1):
- self._multiline[l] = first_line
- first_line = None
- first_on_line = True
-
- if ttext.strip() and toktype != tokenize.COMMENT:
- # A non-whitespace token.
- empty = False
- if first_line is None:
- # The token is not whitespace, and is the first in a
- # statement.
- first_line = slineno
- # Check whether to end an excluded suite.
- if excluding and indent <= exclude_indent:
- excluding = False
- if excluding:
- self.raw_excluded.add(elineno)
- first_on_line = False
-
- prev_toktype = toktype
-
- # Find the starts of the executable statements.
- if not empty:
- self.raw_statements.update(self.byte_parser._find_statements())
-
- # The first line of modules can lie and say 1 always, even if the first
- # line of code is later. If so, map 1 to the actual first line of the
- # module.
- if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
- self._multiline[1] = min(self.raw_statements)
-
- def first_line(self, line):
- """Return the first line number of the statement including `line`."""
- if line < 0:
- line = -self._multiline.get(-line, -line)
- else:
- line = self._multiline.get(line, line)
- return line
-
- def first_lines(self, lines):
- """Map the line numbers in `lines` to the correct first line of the
- statement.
-
- Returns a set of the first lines.
-
- """
- return {self.first_line(l) for l in lines}
-
- def translate_lines(self, lines):
- """Implement `FileReporter.translate_lines`."""
- return self.first_lines(lines)
-
- def translate_arcs(self, arcs):
- """Implement `FileReporter.translate_arcs`."""
- return [(self.first_line(a), self.first_line(b)) for (a, b) in arcs]
-
- def parse_source(self):
- """Parse source text to find executable lines, excluded lines, etc.
-
- Sets the .excluded and .statements attributes, normalized to the first
- line of multi-line statements.
-
- """
- try:
- self._raw_parse()
- except (tokenize.TokenError, IndentationError) as err:
- if hasattr(err, "lineno"):
- lineno = err.lineno # IndentationError
- else:
- lineno = err.args[1][0] # TokenError
- raise NotPython(
- u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
- self.filename, err.args[0], lineno
- )
- )
-
- self.excluded = self.first_lines(self.raw_excluded)
-
- ignore = self.excluded | self.raw_docstrings
- starts = self.raw_statements - ignore
- self.statements = self.first_lines(starts) - ignore
-
- def arcs(self):
- """Get information about the arcs available in the code.
-
- Returns a set of line number pairs. Line numbers have been normalized
- to the first line of multi-line statements.
-
- """
- if self._all_arcs is None:
- self._analyze_ast()
- return self._all_arcs
-
- def _analyze_ast(self):
- """Run the AstArcAnalyzer and save its results.
-
- `_all_arcs` is the set of arcs in the code.
-
- """
- aaa = AstArcAnalyzer(self.text, self.raw_statements, self._multiline)
- aaa.analyze()
-
- self._all_arcs = set()
- for l1, l2 in aaa.arcs:
- fl1 = self.first_line(l1)
- fl2 = self.first_line(l2)
- if fl1 != fl2:
- self._all_arcs.add((fl1, fl2))
-
- self._missing_arc_fragments = aaa.missing_arc_fragments
- self._raw_funcdefs = aaa.funcdefs
-
- def exit_counts(self):
- """Get a count of exits from that each line.
-
- Excluded lines are excluded.
-
- """
- exit_counts = collections.defaultdict(int)
- for l1, l2 in self.arcs():
- if l1 < 0:
- # Don't ever report -1 as a line number
- continue
- if l1 in self.excluded:
- # Don't report excluded lines as line numbers.
- continue
- if l2 in self.excluded:
- # Arcs to excluded lines shouldn't count.
- continue
- exit_counts[l1] += 1
-
- # Class definitions have one extra exit, so remove one for each:
- for l in self.raw_classdefs:
- # Ensure key is there: class definitions can include excluded lines.
- if l in exit_counts:
- exit_counts[l] -= 1
-
- return exit_counts
-
- def missing_arc_description(self, start, end, executed_arcs=None):
- """Provide an English sentence describing a missing arc."""
- if self._missing_arc_fragments is None:
- self._analyze_ast()
-
- actual_start = start
-
- if (
- executed_arcs and
- end < 0 and end == -start and
- (end, start) not in executed_arcs and
- (end, start) in self._missing_arc_fragments
- ):
- # It's a one-line callable, and we never even started it,
- # and we have a message about not starting it.
- start, end = end, start
-
- fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
-
- msgs = []
- for smsg, emsg in fragment_pairs:
- if emsg is None:
- if end < 0:
- # Hmm, maybe we have a one-line callable, let's check.
- if (-end, end) in self._missing_arc_fragments:
- return self.missing_arc_description(-end, end)
- emsg = "didn't jump to the function exit"
- else:
- emsg = "didn't jump to line {lineno}"
- emsg = emsg.format(lineno=end)
-
- msg = "line {start} {emsg}".format(start=actual_start, emsg=emsg)
- if smsg is not None:
- msg += ", because {smsg}".format(smsg=smsg.format(lineno=actual_start))
-
- msgs.append(msg)
-
- return " or ".join(msgs)
-
-
-class ByteParser(object):
- """Parse bytecode to understand the structure of code."""
-
- @contract(text='unicode')
- def __init__(self, text, code=None, filename=None):
- self.text = text
- if code:
- self.code = code
- else:
- try:
- self.code = compile_unicode(text, filename, "exec")
- except SyntaxError as synerr:
- raise NotPython(
- u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
- filename, synerr.msg, synerr.lineno
- )
- )
-
- # Alternative Python implementations don't always provide all the
- # attributes on code objects that we need to do the analysis.
- for attr in ['co_lnotab', 'co_firstlineno']:
- if not hasattr(self.code, attr):
- raise StopEverything( # pragma: only jython
- "This implementation of Python doesn't support code analysis.\n"
- "Run coverage.py under another Python for this command."
- )
-
- def child_parsers(self):
- """Iterate over all the code objects nested within this one.
-
- The iteration includes `self` as its first value.
-
- """
- return (ByteParser(self.text, code=c) for c in code_objects(self.code))
-
- def _line_numbers(self):
- """Yield the line numbers possible in this code object.
-
- Uses co_lnotab described in Python/compile.c to find the
- line numbers. Produces a sequence: l0, l1, ...
- """
- if hasattr(self.code, "co_lines"):
- for _, _, line in self.code.co_lines():
- if line is not None:
- yield line
- else:
- # Adapted from dis.py in the standard library.
- byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
- line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
-
- last_line_num = None
- line_num = self.code.co_firstlineno
- byte_num = 0
- for byte_incr, line_incr in zip(byte_increments, line_increments):
- if byte_incr:
- if line_num != last_line_num:
- yield line_num
- last_line_num = line_num
- byte_num += byte_incr
- if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
- line_incr -= 0x100
- line_num += line_incr
- if line_num != last_line_num:
- yield line_num
-
- def _find_statements(self):
- """Find the statements in `self.code`.
-
- Produce a sequence of line numbers that start statements. Recurses
- into all code objects reachable from `self.code`.
-
- """
- for bp in self.child_parsers():
- # Get all of the lineno information from this code.
- for l in bp._line_numbers():
- yield l
-
-
-#
-# AST analysis
-#
-
-class LoopBlock(object):
- """A block on the block stack representing a `for` or `while` loop."""
- @contract(start=int)
- def __init__(self, start):
- # The line number where the loop starts.
- self.start = start
- # A set of ArcStarts, the arcs from break statements exiting this loop.
- self.break_exits = set()
-
-
-class FunctionBlock(object):
- """A block on the block stack representing a function definition."""
- @contract(start=int, name=str)
- def __init__(self, start, name):
- # The line number where the function starts.
- self.start = start
- # The name of the function.
- self.name = name
-
-
-class TryBlock(object):
- """A block on the block stack representing a `try` block."""
- @contract(handler_start='int|None', final_start='int|None')
- def __init__(self, handler_start, final_start):
- # The line number of the first "except" handler, if any.
- self.handler_start = handler_start
- # The line number of the "finally:" clause, if any.
- self.final_start = final_start
-
- # The ArcStarts for breaks/continues/returns/raises inside the "try:"
- # that need to route through the "finally:" clause.
- self.break_from = set()
- self.continue_from = set()
- self.return_from = set()
- self.raise_from = set()
-
-
-class ArcStart(collections.namedtuple("Arc", "lineno, cause")):
- """The information needed to start an arc.
-
- `lineno` is the line number the arc starts from.
-
- `cause` is an English text fragment used as the `startmsg` for
- AstArcAnalyzer.missing_arc_fragments. It will be used to describe why an
- arc wasn't executed, so should fit well into a sentence of the form,
- "Line 17 didn't run because {cause}." The fragment can include "{lineno}"
- to have `lineno` interpolated into it.
-
- """
- def __new__(cls, lineno, cause=None):
- return super(ArcStart, cls).__new__(cls, lineno, cause)
-
-
-# Define contract words that PyContract doesn't have.
-# ArcStarts is for a list or set of ArcStart's.
-new_contract('ArcStarts', lambda seq: all(isinstance(x, ArcStart) for x in seq))
-
-
-# Turn on AST dumps with an environment variable.
-# $set_env.py: COVERAGE_AST_DUMP - Dump the AST nodes when parsing code.
-AST_DUMP = bool(int(os.environ.get("COVERAGE_AST_DUMP", 0)))
-
-class NodeList(object):
- """A synthetic fictitious node, containing a sequence of nodes.
-
- This is used when collapsing optimized if-statements, to represent the
- unconditional execution of one of the clauses.
-
- """
- def __init__(self, body):
- self.body = body
- self.lineno = body[0].lineno
-
-
-# TODO: some add_arcs methods here don't add arcs, they return them. Rename them.
-# TODO: the cause messages have too many commas.
-# TODO: Shouldn't the cause messages join with "and" instead of "or"?
-
-class AstArcAnalyzer(object):
- """Analyze source text with an AST to find executable code paths."""
-
- @contract(text='unicode', statements=set)
- def __init__(self, text, statements, multiline):
- self.root_node = ast.parse(neuter_encoding_declaration(text))
- # TODO: I think this is happening in too many places.
- self.statements = {multiline.get(l, l) for l in statements}
- self.multiline = multiline
-
- if AST_DUMP: # pragma: debugging
- # Dump the AST so that failing tests have helpful output.
- print("Statements: {}".format(self.statements))
- print("Multiline map: {}".format(self.multiline))
- ast_dump(self.root_node)
-
- self.arcs = set()
-
- # A map from arc pairs to a list of pairs of sentence fragments:
- # { (start, end): [(startmsg, endmsg), ...], }
- #
- # For an arc from line 17, they should be usable like:
- # "Line 17 {endmsg}, because {startmsg}"
- self.missing_arc_fragments = collections.defaultdict(list)
- self.block_stack = []
- self.funcdefs = set()
-
- # $set_env.py: COVERAGE_TRACK_ARCS - Trace every arc added while parsing code.
- self.debug = bool(int(os.environ.get("COVERAGE_TRACK_ARCS", 0)))
-
- def analyze(self):
- """Examine the AST tree from `root_node` to determine possible arcs.
-
- This sets the `arcs` attribute to be a set of (from, to) line number
- pairs.
-
- """
- for node in ast.walk(self.root_node):
- node_name = node.__class__.__name__
- code_object_handler = getattr(self, "_code_object__" + node_name, None)
- if code_object_handler is not None:
- code_object_handler(node)
-
- @contract(start=int, end=int)
- def add_arc(self, start, end, smsg=None, emsg=None):
- """Add an arc, including message fragments to use if it is missing."""
- if self.debug: # pragma: debugging
- print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg))
- print(short_stack(limit=6))
- self.arcs.add((start, end))
-
- if smsg is not None or emsg is not None:
- self.missing_arc_fragments[(start, end)].append((smsg, emsg))
-
- def nearest_blocks(self):
- """Yield the blocks in nearest-to-farthest order."""
- return reversed(self.block_stack)
-
- @contract(returns=int)
- def line_for_node(self, node):
- """What is the right line number to use for this node?
-
- This dispatches to _line__Node functions where needed.
-
- """
- node_name = node.__class__.__name__
- handler = getattr(self, "_line__" + node_name, None)
- if handler is not None:
- return handler(node)
- else:
- return node.lineno
-
- def _line_decorated(self, node):
- """Compute first line number for things that can be decorated (classes and functions)."""
- lineno = node.lineno
- if env.PYBEHAVIOR.trace_decorated_def:
- if node.decorator_list:
- lineno = node.decorator_list[0].lineno
- return lineno
-
- def _line__Assign(self, node):
- return self.line_for_node(node.value)
-
- _line__ClassDef = _line_decorated
-
- def _line__Dict(self, node):
- # Python 3.5 changed how dict literals are made.
- if env.PYVERSION >= (3, 5) and node.keys:
- if node.keys[0] is not None:
- return node.keys[0].lineno
- else:
- # Unpacked dict literals `{**{'a':1}}` have None as the key,
- # use the value in that case.
- return node.values[0].lineno
- else:
- return node.lineno
-
- _line__FunctionDef = _line_decorated
- _line__AsyncFunctionDef = _line_decorated
-
- def _line__List(self, node):
- if node.elts:
- return self.line_for_node(node.elts[0])
- else:
- return node.lineno
-
- def _line__Module(self, node):
- if env.PYBEHAVIOR.module_firstline_1:
- return 1
- elif node.body:
- return self.line_for_node(node.body[0])
- else:
- # Empty modules have no line number, they always start at 1.
- return 1
-
- # The node types that just flow to the next node with no complications.
- OK_TO_DEFAULT = {
- "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
- "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
- }
-
- @contract(returns='ArcStarts')
- def add_arcs(self, node):
- """Add the arcs for `node`.
-
- Return a set of ArcStarts, exits from this node to the next. Because a
- node represents an entire sub-tree (including its children), the exits
- from a node can be arbitrarily complex::
-
- if something(1):
- if other(2):
- doit(3)
- else:
- doit(5)
-
- There are two exits from line 1: they start at line 3 and line 5.
-
- """
- node_name = node.__class__.__name__
- handler = getattr(self, "_handle__" + node_name, None)
- if handler is not None:
- return handler(node)
- else:
- # No handler: either it's something that's ok to default (a simple
- # statement), or it's something we overlooked. Change this 0 to 1
- # to see if it's overlooked.
- if 0:
- if node_name not in self.OK_TO_DEFAULT:
- print("*** Unhandled: {}".format(node))
-
- # Default for simple statements: one exit from this node.
- return {ArcStart(self.line_for_node(node))}
-
- @one_of("from_start, prev_starts")
- @contract(returns='ArcStarts')
- def add_body_arcs(self, body, from_start=None, prev_starts=None):
- """Add arcs for the body of a compound statement.
-
- `body` is the body node. `from_start` is a single `ArcStart` that can
- be the previous line in flow before this body. `prev_starts` is a set
- of ArcStarts that can be the previous line. Only one of them should be
- given.
-
- Returns a set of ArcStarts, the exits from this body.
-
- """
- if prev_starts is None:
- prev_starts = {from_start}
- for body_node in body:
- lineno = self.line_for_node(body_node)
- first_line = self.multiline.get(lineno, lineno)
- if first_line not in self.statements:
- body_node = self.find_non_missing_node(body_node)
- if body_node is None:
- continue
- lineno = self.line_for_node(body_node)
- for prev_start in prev_starts:
- self.add_arc(prev_start.lineno, lineno, prev_start.cause)
- prev_starts = self.add_arcs(body_node)
- return prev_starts
-
- def find_non_missing_node(self, node):
- """Search `node` looking for a child that has not been optimized away.
-
- This might return the node you started with, or it will work recursively
- to find a child node in self.statements.
-
- Returns a node, or None if none of the node remains.
-
- """
- # This repeats work just done in add_body_arcs, but this duplication
- # means we can avoid a function call in the 99.9999% case of not
- # optimizing away statements.
- lineno = self.line_for_node(node)
- first_line = self.multiline.get(lineno, lineno)
- if first_line in self.statements:
- return node
-
- missing_fn = getattr(self, "_missing__" + node.__class__.__name__, None)
- if missing_fn:
- node = missing_fn(node)
- else:
- node = None
- return node
-
- # Missing nodes: _missing__*
- #
- # Entire statements can be optimized away by Python. They will appear in
- # the AST, but not the bytecode. These functions are called (by
- # find_non_missing_node) to find a node to use instead of the missing
- # node. They can return None if the node should truly be gone.
-
- def _missing__If(self, node):
- # If the if-node is missing, then one of its children might still be
- # here, but not both. So return the first of the two that isn't missing.
- # Use a NodeList to hold the clauses as a single node.
- non_missing = self.find_non_missing_node(NodeList(node.body))
- if non_missing:
- return non_missing
- if node.orelse:
- return self.find_non_missing_node(NodeList(node.orelse))
- return None
-
- def _missing__NodeList(self, node):
- # A NodeList might be a mixture of missing and present nodes. Find the
- # ones that are present.
- non_missing_children = []
- for child in node.body:
- child = self.find_non_missing_node(child)
- if child is not None:
- non_missing_children.append(child)
-
- # Return the simplest representation of the present children.
- if not non_missing_children:
- return None
- if len(non_missing_children) == 1:
- return non_missing_children[0]
- return NodeList(non_missing_children)
-
- def _missing__While(self, node):
- body_nodes = self.find_non_missing_node(NodeList(node.body))
- if not body_nodes:
- return None
- # Make a synthetic While-true node.
- new_while = ast.While()
- new_while.lineno = body_nodes.lineno
- new_while.test = ast.Name()
- new_while.test.lineno = body_nodes.lineno
- new_while.test.id = "True"
- new_while.body = body_nodes.body
- new_while.orelse = None
- return new_while
-
- def is_constant_expr(self, node):
- """Is this a compile-time constant?"""
- node_name = node.__class__.__name__
- if node_name in ["Constant", "NameConstant", "Num"]:
- return "Num"
- elif node_name == "Name":
- if node.id in ["True", "False", "None", "__debug__"]:
- return "Name"
- return None
-
- # In the fullness of time, these might be good tests to write:
- # while EXPR:
- # while False:
- # listcomps hidden deep in other expressions
- # listcomps hidden in lists: x = [[i for i in range(10)]]
- # nested function definitions
-
-
- # Exit processing: process_*_exits
- #
- # These functions process the four kinds of jump exits: break, continue,
- # raise, and return. To figure out where an exit goes, we have to look at
- # the block stack context. For example, a break will jump to the nearest
- # enclosing loop block, or the nearest enclosing finally block, whichever
- # is nearer.
-
- @contract(exits='ArcStarts')
- def process_break_exits(self, exits):
- """Add arcs due to jumps from `exits` being breaks."""
- for block in self.nearest_blocks():
- if isinstance(block, LoopBlock):
- block.break_exits.update(exits)
- break
- elif isinstance(block, TryBlock) and block.final_start is not None:
- block.break_from.update(exits)
- break
-
- @contract(exits='ArcStarts')
- def process_continue_exits(self, exits):
- """Add arcs due to jumps from `exits` being continues."""
- for block in self.nearest_blocks():
- if isinstance(block, LoopBlock):
- for xit in exits:
- self.add_arc(xit.lineno, block.start, xit.cause)
- break
- elif isinstance(block, TryBlock) and block.final_start is not None:
- block.continue_from.update(exits)
- break
-
- @contract(exits='ArcStarts')
- def process_raise_exits(self, exits):
- """Add arcs due to jumps from `exits` being raises."""
- for block in self.nearest_blocks():
- if isinstance(block, TryBlock):
- if block.handler_start is not None:
- for xit in exits:
- self.add_arc(xit.lineno, block.handler_start, xit.cause)
- break
- elif block.final_start is not None:
- block.raise_from.update(exits)
- break
- elif isinstance(block, FunctionBlock):
- for xit in exits:
- self.add_arc(
- xit.lineno, -block.start, xit.cause,
- "didn't except from function {!r}".format(block.name),
- )
- break
-
- @contract(exits='ArcStarts')
- def process_return_exits(self, exits):
- """Add arcs due to jumps from `exits` being returns."""
- for block in self.nearest_blocks():
- if isinstance(block, TryBlock) and block.final_start is not None:
- block.return_from.update(exits)
- break
- elif isinstance(block, FunctionBlock):
- for xit in exits:
- self.add_arc(
- xit.lineno, -block.start, xit.cause,
- "didn't return from function {!r}".format(block.name),
- )
- break
-
-
- # Handlers: _handle__*
- #
- # Each handler deals with a specific AST node type, dispatched from
- # add_arcs. Handlers return the set of exits from that node, and can
- # also call self.add_arc to record arcs they find. These functions mirror
- # the Python semantics of each syntactic construct. See the docstring
- # for add_arcs to understand the concept of exits from a node.
-
- @contract(returns='ArcStarts')
- def _handle__Break(self, node):
- here = self.line_for_node(node)
- break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed")
- self.process_break_exits([break_start])
- return set()
-
- @contract(returns='ArcStarts')
- def _handle_decorated(self, node):
- """Add arcs for things that can be decorated (classes and functions)."""
- main_line = last = node.lineno
- if node.decorator_list:
- if env.PYBEHAVIOR.trace_decorated_def:
- last = None
- for dec_node in node.decorator_list:
- dec_start = self.line_for_node(dec_node)
- if last is not None and dec_start != last:
- self.add_arc(last, dec_start)
- last = dec_start
- if env.PYBEHAVIOR.trace_decorated_def:
- self.add_arc(last, main_line)
- last = main_line
- # The definition line may have been missed, but we should have it
- # in `self.statements`. For some constructs, `line_for_node` is
- # not what we'd think of as the first line in the statement, so map
- # it to the first one.
- if node.body:
- body_start = self.line_for_node(node.body[0])
- body_start = self.multiline.get(body_start, body_start)
- for lineno in range(last+1, body_start):
- if lineno in self.statements:
- self.add_arc(last, lineno)
- last = lineno
- # The body is handled in collect_arcs.
- return {ArcStart(last)}
-
- _handle__ClassDef = _handle_decorated
-
- @contract(returns='ArcStarts')
- def _handle__Continue(self, node):
- here = self.line_for_node(node)
- continue_start = ArcStart(here, cause="the continue on line {lineno} wasn't executed")
- self.process_continue_exits([continue_start])
- return set()
-
- @contract(returns='ArcStarts')
- def _handle__For(self, node):
- start = self.line_for_node(node.iter)
- self.block_stack.append(LoopBlock(start=start))
- from_start = ArcStart(start, cause="the loop on line {lineno} never started")
- exits = self.add_body_arcs(node.body, from_start=from_start)
- # Any exit from the body will go back to the top of the loop.
- for xit in exits:
- self.add_arc(xit.lineno, start, xit.cause)
- my_block = self.block_stack.pop()
- exits = my_block.break_exits
- from_start = ArcStart(start, cause="the loop on line {lineno} didn't complete")
- if node.orelse:
- else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
- exits |= else_exits
- else:
- # No else clause: exit from the for line.
- exits.add(from_start)
- return exits
-
- _handle__AsyncFor = _handle__For
-
- _handle__FunctionDef = _handle_decorated
- _handle__AsyncFunctionDef = _handle_decorated
-
- @contract(returns='ArcStarts')
- def _handle__If(self, node):
- start = self.line_for_node(node.test)
- from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
- exits = self.add_body_arcs(node.body, from_start=from_start)
- from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
- exits |= self.add_body_arcs(node.orelse, from_start=from_start)
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__NodeList(self, node):
- start = self.line_for_node(node)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__Raise(self, node):
- here = self.line_for_node(node)
- raise_start = ArcStart(here, cause="the raise on line {lineno} wasn't executed")
- self.process_raise_exits([raise_start])
- # `raise` statement jumps away, no exits from here.
- return set()
-
- @contract(returns='ArcStarts')
- def _handle__Return(self, node):
- here = self.line_for_node(node)
- return_start = ArcStart(here, cause="the return on line {lineno} wasn't executed")
- self.process_return_exits([return_start])
- # `return` statement jumps away, no exits from here.
- return set()
-
- @contract(returns='ArcStarts')
- def _handle__Try(self, node):
- if node.handlers:
- handler_start = self.line_for_node(node.handlers[0])
- else:
- handler_start = None
-
- if node.finalbody:
- final_start = self.line_for_node(node.finalbody[0])
- else:
- final_start = None
-
- try_block = TryBlock(handler_start, final_start)
- self.block_stack.append(try_block)
-
- start = self.line_for_node(node)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
-
- # We're done with the `try` body, so this block no longer handles
- # exceptions. We keep the block so the `finally` clause can pick up
- # flows from the handlers and `else` clause.
- if node.finalbody:
- try_block.handler_start = None
- if node.handlers:
- # If there are `except` clauses, then raises in the try body
- # will already jump to them. Start this set over for raises in
- # `except` and `else`.
- try_block.raise_from = set()
- else:
- self.block_stack.pop()
-
- handler_exits = set()
-
- if node.handlers:
- last_handler_start = None
- for handler_node in node.handlers:
- handler_start = self.line_for_node(handler_node)
- if last_handler_start is not None:
- self.add_arc(last_handler_start, handler_start)
- last_handler_start = handler_start
- from_cause = "the exception caught by line {lineno} didn't happen"
- from_start = ArcStart(handler_start, cause=from_cause)
- handler_exits |= self.add_body_arcs(handler_node.body, from_start=from_start)
-
- if node.orelse:
- exits = self.add_body_arcs(node.orelse, prev_starts=exits)
-
- exits |= handler_exits
-
- if node.finalbody:
- self.block_stack.pop()
- final_from = ( # You can get to the `finally` clause from:
- exits | # the exits of the body or `else` clause,
- try_block.break_from | # or a `break`,
- try_block.continue_from | # or a `continue`,
- try_block.raise_from | # or a `raise`,
- try_block.return_from # or a `return`.
- )
-
- final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from)
-
- if try_block.break_from:
- if env.PYBEHAVIOR.finally_jumps_back:
- for break_line in try_block.break_from:
- lineno = break_line.lineno
- cause = break_line.cause.format(lineno=lineno)
- for final_exit in final_exits:
- self.add_arc(final_exit.lineno, lineno, cause)
- breaks = try_block.break_from
- else:
- breaks = self._combine_finally_starts(try_block.break_from, final_exits)
- self.process_break_exits(breaks)
-
- if try_block.continue_from:
- if env.PYBEHAVIOR.finally_jumps_back:
- for continue_line in try_block.continue_from:
- lineno = continue_line.lineno
- cause = continue_line.cause.format(lineno=lineno)
- for final_exit in final_exits:
- self.add_arc(final_exit.lineno, lineno, cause)
- continues = try_block.continue_from
- else:
- continues = self._combine_finally_starts(try_block.continue_from, final_exits)
- self.process_continue_exits(continues)
-
- if try_block.raise_from:
- self.process_raise_exits(
- self._combine_finally_starts(try_block.raise_from, final_exits)
- )
-
- if try_block.return_from:
- if env.PYBEHAVIOR.finally_jumps_back:
- for return_line in try_block.return_from:
- lineno = return_line.lineno
- cause = return_line.cause.format(lineno=lineno)
- for final_exit in final_exits:
- self.add_arc(final_exit.lineno, lineno, cause)
- returns = try_block.return_from
- else:
- returns = self._combine_finally_starts(try_block.return_from, final_exits)
- self.process_return_exits(returns)
-
- if exits:
- # The finally clause's exits are only exits for the try block
- # as a whole if the try block had some exits to begin with.
- exits = final_exits
-
- return exits
-
- @contract(starts='ArcStarts', exits='ArcStarts', returns='ArcStarts')
- def _combine_finally_starts(self, starts, exits):
- """Helper for building the cause of `finally` branches.
-
- "finally" clauses might not execute their exits, and the causes could
- be due to a failure to execute any of the exits in the try block. So
- we use the causes from `starts` as the causes for `exits`.
- """
- causes = []
- for start in sorted(starts):
- if start.cause is not None:
- causes.append(start.cause.format(lineno=start.lineno))
- cause = " or ".join(causes)
- exits = {ArcStart(xit.lineno, cause) for xit in exits}
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__TryExcept(self, node):
- # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
- # TryExcept, it means there was no finally, so fake it, and treat as
- # a general Try node.
- node.finalbody = []
- return self._handle__Try(node)
-
- @contract(returns='ArcStarts')
- def _handle__TryFinally(self, node):
- # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
- # TryFinally, see if there's a TryExcept nested inside. If so, merge
- # them. Otherwise, fake fields to complete a Try node.
- node.handlers = []
- node.orelse = []
-
- first = node.body[0]
- if first.__class__.__name__ == "TryExcept" and node.lineno == first.lineno:
- assert len(node.body) == 1
- node.body = first.body
- node.handlers = first.handlers
- node.orelse = first.orelse
-
- return self._handle__Try(node)
-
- @contract(returns='ArcStarts')
- def _handle__While(self, node):
- start = to_top = self.line_for_node(node.test)
- constant_test = self.is_constant_expr(node.test)
- top_is_body0 = False
- if constant_test and (env.PY3 or constant_test == "Num"):
- top_is_body0 = True
- if env.PYBEHAVIOR.keep_constant_test:
- top_is_body0 = False
- if top_is_body0:
- to_top = self.line_for_node(node.body[0])
- self.block_stack.append(LoopBlock(start=to_top))
- from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
- exits = self.add_body_arcs(node.body, from_start=from_start)
- for xit in exits:
- self.add_arc(xit.lineno, to_top, xit.cause)
- exits = set()
- my_block = self.block_stack.pop()
- exits.update(my_block.break_exits)
- from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
- if node.orelse:
- else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
- exits |= else_exits
- else:
- # No `else` clause: you can exit from the start.
- if not constant_test:
- exits.add(from_start)
- return exits
-
- @contract(returns='ArcStarts')
- def _handle__With(self, node):
- start = self.line_for_node(node)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
- return exits
-
- _handle__AsyncWith = _handle__With
-
- def _code_object__Module(self, node):
- start = self.line_for_node(node)
- if node.body:
- exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
- for xit in exits:
- self.add_arc(xit.lineno, -start, xit.cause, "didn't exit the module")
- else:
- # Empty module.
- self.add_arc(-start, start)
- self.add_arc(start, -start)
-
- def _process_function_def(self, start, node):
- self.funcdefs.add((start, node.body[-1].lineno, node.name))
-
- def _code_object__FunctionDef(self, node):
- start = self.line_for_node(node)
- self.block_stack.append(FunctionBlock(start=start, name=node.name))
- exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
- self.process_return_exits(exits)
- self._process_function_def(start, node)
- self.block_stack.pop()
-
- _code_object__AsyncFunctionDef = _code_object__FunctionDef
-
- def _code_object__ClassDef(self, node):
- start = self.line_for_node(node)
- self.add_arc(-start, start)
- exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
- for xit in exits:
- self.add_arc(
- xit.lineno, -start, xit.cause,
- "didn't exit the body of class {!r}".format(node.name),
- )
-
- def _make_oneline_code_method(noun): # pylint: disable=no-self-argument
- """A function to make methods for online callable _code_object__ methods."""
- def _code_object__oneline_callable(self, node):
- start = self.line_for_node(node)
- self.add_arc(-start, start, None, "didn't run the {} on line {}".format(noun, start))
- self.add_arc(
- start, -start, None,
- "didn't finish the {} on line {}".format(noun, start),
- )
- return _code_object__oneline_callable
-
- _code_object__Lambda = _make_oneline_code_method("lambda")
- _code_object__GeneratorExp = _make_oneline_code_method("generator expression")
- _code_object__DictComp = _make_oneline_code_method("dictionary comprehension")
- _code_object__SetComp = _make_oneline_code_method("set comprehension")
- if env.PY3:
- _code_object__ListComp = _make_oneline_code_method("list comprehension")
-
-
-if AST_DUMP: # pragma: debugging
- # Code only used when dumping the AST for debugging.
-
- SKIP_DUMP_FIELDS = ["ctx"]
-
- def _is_simple_value(value):
- """Is `value` simple enough to be displayed on a single line?"""
- return (
- value in [None, [], (), {}, set()] or
- isinstance(value, (string_class, int, float))
- )
-
- def ast_dump(node, depth=0):
- """Dump the AST for `node`.
-
- This recursively walks the AST, printing a readable version.
-
- """
- indent = " " * depth
- if not isinstance(node, ast.AST):
- print("{}<{} {!r}>".format(indent, node.__class__.__name__, node))
- return
-
- lineno = getattr(node, "lineno", None)
- if lineno is not None:
- linemark = " @ {}".format(node.lineno)
- else:
- linemark = ""
- head = "{}<{}{}".format(indent, node.__class__.__name__, linemark)
-
- named_fields = [
- (name, value)
- for name, value in ast.iter_fields(node)
- if name not in SKIP_DUMP_FIELDS
- ]
- if not named_fields:
- print("{}>".format(head))
- elif len(named_fields) == 1 and _is_simple_value(named_fields[0][1]):
- field_name, value = named_fields[0]
- print("{} {}: {!r}>".format(head, field_name, value))
- else:
- print(head)
- if 0:
- print("{}# mro: {}".format(
- indent, ", ".join(c.__name__ for c in node.__class__.__mro__[1:]),
- ))
- next_indent = indent + " "
- for field_name, value in named_fields:
- prefix = "{}{}:".format(next_indent, field_name)
- if _is_simple_value(value):
- print("{} {!r}".format(prefix, value))
- elif isinstance(value, list):
- print("{} [".format(prefix))
- for n in value:
- ast_dump(n, depth + 8)
- print("{}]".format(next_indent))
- else:
- print(prefix)
- ast_dump(value, depth + 8)
-
- print("{}>".format(indent))
diff --git a/contrib/python/coverage/py3/coverage/phystokens.py b/contrib/python/coverage/py3/coverage/phystokens.py
deleted file mode 100644
index 54378b3bc8..0000000000
--- a/contrib/python/coverage/py3/coverage/phystokens.py
+++ /dev/null
@@ -1,297 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Better tokenizing for coverage.py."""
-
-import codecs
-import keyword
-import re
-import sys
-import token
-import tokenize
-
-from coverage import env
-from coverage.backward import iternext, unicode_class
-from coverage.misc import contract
-
-
-def phys_tokens(toks):
- """Return all physical tokens, even line continuations.
-
- tokenize.generate_tokens() doesn't return a token for the backslash that
- continues lines. This wrapper provides those tokens so that we can
- re-create a faithful representation of the original source.
-
- Returns the same values as generate_tokens()
-
- """
- last_line = None
- last_lineno = -1
- last_ttext = None
- for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks:
- if last_lineno != elineno:
- if last_line and last_line.endswith("\\\n"):
- # We are at the beginning of a new line, and the last line
- # ended with a backslash. We probably have to inject a
- # backslash token into the stream. Unfortunately, there's more
- # to figure out. This code::
- #
- # usage = """\
- # HEY THERE
- # """
- #
- # triggers this condition, but the token text is::
- #
- # '"""\\\nHEY THERE\n"""'
- #
- # so we need to figure out if the backslash is already in the
- # string token or not.
- inject_backslash = True
- if last_ttext.endswith("\\"):
- inject_backslash = False
- elif ttype == token.STRING:
- if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\':
- # It's a multi-line string and the first line ends with
- # a backslash, so we don't need to inject another.
- inject_backslash = False
- if inject_backslash:
- # Figure out what column the backslash is in.
- ccol = len(last_line.split("\n")[-2]) - 1
- # Yield the token, with a fake token type.
- yield (
- 99999, "\\\n",
- (slineno, ccol), (slineno, ccol+2),
- last_line
- )
- last_line = ltext
- if ttype not in (tokenize.NEWLINE, tokenize.NL):
- last_ttext = ttext
- yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext
- last_lineno = elineno
-
-
-@contract(source='unicode')
-def source_token_lines(source):
- """Generate a series of lines, one for each line in `source`.
-
- Each line is a list of pairs, each pair is a token::
-
- [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
-
- Each pair has a token class, and the token text.
-
- If you concatenate all the token texts, and then join them with newlines,
- you should have your original `source` back, with two differences:
- trailing whitespace is not preserved, and a final line with no newline
- is indistinguishable from a final line with a newline.
-
- """
-
- ws_tokens = {token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL}
- line = []
- col = 0
-
- source = source.expandtabs(8).replace('\r\n', '\n')
- tokgen = generate_tokens(source)
-
- for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen):
- mark_start = True
- for part in re.split('(\n)', ttext):
- if part == '\n':
- yield line
- line = []
- col = 0
- mark_end = False
- elif part == '':
- mark_end = False
- elif ttype in ws_tokens:
- mark_end = False
- else:
- if mark_start and scol > col:
- line.append(("ws", u" " * (scol - col)))
- mark_start = False
- tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3]
- if ttype == token.NAME and keyword.iskeyword(ttext):
- tok_class = "key"
- line.append((tok_class, part))
- mark_end = True
- scol = 0
- if mark_end:
- col = ecol
-
- if line:
- yield line
-
-
-class CachedTokenizer(object):
- """A one-element cache around tokenize.generate_tokens.
-
- When reporting, coverage.py tokenizes files twice, once to find the
- structure of the file, and once to syntax-color it. Tokenizing is
- expensive, and easily cached.
-
- This is a one-element cache so that our twice-in-a-row tokenizing doesn't
- actually tokenize twice.
-
- """
- def __init__(self):
- self.last_text = None
- self.last_tokens = None
-
- @contract(text='unicode')
- def generate_tokens(self, text):
- """A stand-in for `tokenize.generate_tokens`."""
- if text != self.last_text:
- self.last_text = text
- readline = iternext(text.splitlines(True))
- self.last_tokens = list(tokenize.generate_tokens(readline))
- return self.last_tokens
-
-# Create our generate_tokens cache as a callable replacement function.
-generate_tokens = CachedTokenizer().generate_tokens
-
-
-COOKIE_RE = re.compile(r"^[ \t]*#.*coding[:=][ \t]*([-\w.]+)", flags=re.MULTILINE)
-
-@contract(source='bytes')
-def _source_encoding_py2(source):
- """Determine the encoding for `source`, according to PEP 263.
-
- `source` is a byte string, the text of the program.
-
- Returns a string, the name of the encoding.
-
- """
- assert isinstance(source, bytes)
-
- # Do this so the detect_encode code we copied will work.
- readline = iternext(source.splitlines(True))
-
- # This is mostly code adapted from Py3.2's tokenize module.
-
- def _get_normal_name(orig_enc):
- """Imitates get_normal_name in tokenizer.c."""
- # Only care about the first 12 characters.
- enc = orig_enc[:12].lower().replace("_", "-")
- if re.match(r"^utf-8($|-)", enc):
- return "utf-8"
- if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc):
- return "iso-8859-1"
- return orig_enc
-
- # From detect_encode():
- # It detects the encoding from the presence of a UTF-8 BOM or an encoding
- # cookie as specified in PEP-0263. If both a BOM and a cookie are present,
- # but disagree, a SyntaxError will be raised. If the encoding cookie is an
- # invalid charset, raise a SyntaxError. Note that if a UTF-8 BOM is found,
- # 'utf-8-sig' is returned.
-
- # If no encoding is specified, then the default will be returned.
- default = 'ascii'
-
- bom_found = False
- encoding = None
-
- def read_or_stop():
- """Get the next source line, or ''."""
- try:
- return readline()
- except StopIteration:
- return ''
-
- def find_cookie(line):
- """Find an encoding cookie in `line`."""
- try:
- line_string = line.decode('ascii')
- except UnicodeDecodeError:
- return None
-
- matches = COOKIE_RE.findall(line_string)
- if not matches:
- return None
- encoding = _get_normal_name(matches[0])
- try:
- codec = codecs.lookup(encoding)
- except LookupError:
- # This behavior mimics the Python interpreter
- raise SyntaxError("unknown encoding: " + encoding)
-
- if bom_found:
- # codecs in 2.3 were raw tuples of functions, assume the best.
- codec_name = getattr(codec, 'name', encoding)
- if codec_name != 'utf-8':
- # This behavior mimics the Python interpreter
- raise SyntaxError('encoding problem: utf-8')
- encoding += '-sig'
- return encoding
-
- first = read_or_stop()
- if first.startswith(codecs.BOM_UTF8):
- bom_found = True
- first = first[3:]
- default = 'utf-8-sig'
- if not first:
- return default
-
- encoding = find_cookie(first)
- if encoding:
- return encoding
-
- second = read_or_stop()
- if not second:
- return default
-
- encoding = find_cookie(second)
- if encoding:
- return encoding
-
- return default
-
-
-@contract(source='bytes')
-def _source_encoding_py3(source):
- """Determine the encoding for `source`, according to PEP 263.
-
- `source` is a byte string: the text of the program.
-
- Returns a string, the name of the encoding.
-
- """
- readline = iternext(source.splitlines(True))
- return tokenize.detect_encoding(readline)[0]
-
-
-if env.PY3:
- source_encoding = _source_encoding_py3
-else:
- source_encoding = _source_encoding_py2
-
-
-@contract(source='unicode')
-def compile_unicode(source, filename, mode):
- """Just like the `compile` builtin, but works on any Unicode string.
-
- Python 2's compile() builtin has a stupid restriction: if the source string
- is Unicode, then it may not have a encoding declaration in it. Why not?
- Who knows! It also decodes to utf8, and then tries to interpret those utf8
- bytes according to the encoding declaration. Why? Who knows!
-
- This function neuters the coding declaration, and compiles it.
-
- """
- source = neuter_encoding_declaration(source)
- if env.PY2 and isinstance(filename, unicode_class):
- filename = filename.encode(sys.getfilesystemencoding(), "replace")
- code = compile(source, filename, mode)
- return code
-
-
-@contract(source='unicode', returns='unicode')
-def neuter_encoding_declaration(source):
- """Return `source`, with any encoding declaration neutered."""
- if COOKIE_RE.search(source):
- source_lines = source.splitlines(True)
- for lineno in range(min(2, len(source_lines))):
- source_lines[lineno] = COOKIE_RE.sub("# (deleted declaration)", source_lines[lineno])
- source = "".join(source_lines)
- return source
diff --git a/contrib/python/coverage/py3/coverage/plugin.py b/contrib/python/coverage/py3/coverage/plugin.py
deleted file mode 100644
index 6997b489bb..0000000000
--- a/contrib/python/coverage/py3/coverage/plugin.py
+++ /dev/null
@@ -1,533 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""
-.. versionadded:: 4.0
-
-Plug-in interfaces for coverage.py.
-
-Coverage.py supports a few different kinds of plug-ins that change its
-behavior:
-
-* File tracers implement tracing of non-Python file types.
-
-* Configurers add custom configuration, using Python code to change the
- configuration.
-
-* Dynamic context switchers decide when the dynamic context has changed, for
- example, to record what test function produced the coverage.
-
-To write a coverage.py plug-in, create a module with a subclass of
-:class:`~coverage.CoveragePlugin`. You will override methods in your class to
-participate in various aspects of coverage.py's processing.
-Different types of plug-ins have to override different methods.
-
-Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info`
-to provide debugging information about their operation.
-
-Your module must also contain a ``coverage_init`` function that registers an
-instance of your plug-in class::
-
- import coverage
-
- class MyPlugin(coverage.CoveragePlugin):
- ...
-
- def coverage_init(reg, options):
- reg.add_file_tracer(MyPlugin())
-
-You use the `reg` parameter passed to your ``coverage_init`` function to
-register your plug-in object. The registration method you call depends on
-what kind of plug-in it is.
-
-If your plug-in takes options, the `options` parameter is a dictionary of your
-plug-in's options from the coverage.py configuration file. Use them however
-you want to configure your object before registering it.
-
-Coverage.py will store its own information on your plug-in object, using
-attributes whose names start with ``_coverage_``. Don't be startled.
-
-.. warning::
- Plug-ins are imported by coverage.py before it begins measuring code.
- If you write a plugin in your own project, it might import your product
- code before coverage.py can start measuring. This can result in your
- own code being reported as missing.
-
- One solution is to put your plugins in your project tree, but not in
- your importable Python package.
-
-
-.. _file_tracer_plugins:
-
-File Tracers
-============
-
-File tracers implement measurement support for non-Python files. File tracers
-implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim
-files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report
-on those files.
-
-In your ``coverage_init`` function, use the ``add_file_tracer`` method to
-register your file tracer.
-
-
-.. _configurer_plugins:
-
-Configurers
-===========
-
-.. versionadded:: 4.5
-
-Configurers modify the configuration of coverage.py during start-up.
-Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to
-change the configuration.
-
-In your ``coverage_init`` function, use the ``add_configurer`` method to
-register your configurer.
-
-
-.. _dynamic_context_plugins:
-
-Dynamic Context Switchers
-=========================
-
-.. versionadded:: 5.0
-
-Dynamic context switcher plugins implement the
-:meth:`~coverage.CoveragePlugin.dynamic_context` method to dynamically compute
-the context label for each measured frame.
-
-Computed context labels are useful when you want to group measured data without
-modifying the source code.
-
-For example, you could write a plugin that checks `frame.f_code` to inspect
-the currently executed method, and set the context label to a fully qualified
-method name if it's an instance method of `unittest.TestCase` and the method
-name starts with 'test'. Such a plugin would provide basic coverage grouping
-by test and could be used with test runners that have no built-in coveragepy
-support.
-
-In your ``coverage_init`` function, use the ``add_dynamic_context`` method to
-register your dynamic context switcher.
-
-"""
-
-from coverage import files
-from coverage.misc import contract, _needs_to_implement
-
-
-class CoveragePlugin(object):
- """Base class for coverage.py plug-ins."""
-
- def file_tracer(self, filename): # pylint: disable=unused-argument
- """Get a :class:`FileTracer` object for a file.
-
- Plug-in type: file tracer.
-
- Every Python source file is offered to your plug-in to give it a chance
- to take responsibility for tracing the file. If your plug-in can
- handle the file, it should return a :class:`FileTracer` object.
- Otherwise return None.
-
- There is no way to register your plug-in for particular files.
- Instead, this method is invoked for all files as they are executed,
- and the plug-in decides whether it can trace the file or not.
- Be prepared for `filename` to refer to all kinds of files that have
- nothing to do with your plug-in.
-
- The file name will be a Python file being executed. There are two
- broad categories of behavior for a plug-in, depending on the kind of
- files your plug-in supports:
-
- * Static file names: each of your original source files has been
- converted into a distinct Python file. Your plug-in is invoked with
- the Python file name, and it maps it back to its original source
- file.
-
- * Dynamic file names: all of your source files are executed by the same
- Python file. In this case, your plug-in implements
- :meth:`FileTracer.dynamic_source_filename` to provide the actual
- source file for each execution frame.
-
- `filename` is a string, the path to the file being considered. This is
- the absolute real path to the file. If you are comparing to other
- paths, be sure to take this into account.
-
- Returns a :class:`FileTracer` object to use to trace `filename`, or
- None if this plug-in cannot trace this file.
-
- """
- return None
-
- def file_reporter(self, filename): # pylint: disable=unused-argument
- """Get the :class:`FileReporter` class to use for a file.
-
- Plug-in type: file tracer.
-
- This will only be invoked if `filename` returns non-None from
- :meth:`file_tracer`. It's an error to return None from this method.
-
- Returns a :class:`FileReporter` object to use to report on `filename`,
- or the string `"python"` to have coverage.py treat the file as Python.
-
- """
- _needs_to_implement(self, "file_reporter")
-
- def dynamic_context(self, frame): # pylint: disable=unused-argument
- """Get the dynamically computed context label for `frame`.
-
- Plug-in type: dynamic context.
-
- This method is invoked for each frame when outside of a dynamic
- context, to see if a new dynamic context should be started. If it
- returns a string, a new context label is set for this and deeper
- frames. The dynamic context ends when this frame returns.
-
- Returns a string to start a new dynamic context, or None if no new
- context should be started.
-
- """
- return None
-
- def find_executable_files(self, src_dir): # pylint: disable=unused-argument
- """Yield all of the executable files in `src_dir`, recursively.
-
- Plug-in type: file tracer.
-
- Executability is a plug-in-specific property, but generally means files
- which would have been considered for coverage analysis, had they been
- included automatically.
-
- Returns or yields a sequence of strings, the paths to files that could
- have been executed, including files that had been executed.
-
- """
- return []
-
- def configure(self, config):
- """Modify the configuration of coverage.py.
-
- Plug-in type: configurer.
-
- This method is called during coverage.py start-up, to give your plug-in
- a chance to change the configuration. The `config` parameter is an
- object with :meth:`~coverage.Coverage.get_option` and
- :meth:`~coverage.Coverage.set_option` methods. Do not call any other
- methods on the `config` object.
-
- """
- pass
-
- def sys_info(self):
- """Get a list of information useful for debugging.
-
- Plug-in type: any.
-
- This method will be invoked for ``--debug=sys``. Your
- plug-in can return any information it wants to be displayed.
-
- Returns a list of pairs: `[(name, value), ...]`.
-
- """
- return []
-
-
-class FileTracer(object):
- """Support needed for files during the execution phase.
-
- File tracer plug-ins implement subclasses of FileTracer to return from
- their :meth:`~CoveragePlugin.file_tracer` method.
-
- You may construct this object from :meth:`CoveragePlugin.file_tracer` any
- way you like. A natural choice would be to pass the file name given to
- `file_tracer`.
-
- `FileTracer` objects should only be created in the
- :meth:`CoveragePlugin.file_tracer` method.
-
- See :ref:`howitworks` for details of the different coverage.py phases.
-
- """
-
- def source_filename(self):
- """The source file name for this file.
-
- This may be any file name you like. A key responsibility of a plug-in
- is to own the mapping from Python execution back to whatever source
- file name was originally the source of the code.
-
- See :meth:`CoveragePlugin.file_tracer` for details about static and
- dynamic file names.
-
- Returns the file name to credit with this execution.
-
- """
- _needs_to_implement(self, "source_filename")
-
- def has_dynamic_source_filename(self):
- """Does this FileTracer have dynamic source file names?
-
- FileTracers can provide dynamically determined file names by
- implementing :meth:`dynamic_source_filename`. Invoking that function
- is expensive. To determine whether to invoke it, coverage.py uses the
- result of this function to know if it needs to bother invoking
- :meth:`dynamic_source_filename`.
-
- See :meth:`CoveragePlugin.file_tracer` for details about static and
- dynamic file names.
-
- Returns True if :meth:`dynamic_source_filename` should be called to get
- dynamic source file names.
-
- """
- return False
-
- def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument
- """Get a dynamically computed source file name.
-
- Some plug-ins need to compute the source file name dynamically for each
- frame.
-
- This function will not be invoked if
- :meth:`has_dynamic_source_filename` returns False.
-
- Returns the source file name for this frame, or None if this frame
- shouldn't be measured.
-
- """
- return None
-
- def line_number_range(self, frame):
- """Get the range of source line numbers for a given a call frame.
-
- The call frame is examined, and the source line number in the original
- file is returned. The return value is a pair of numbers, the starting
- line number and the ending line number, both inclusive. For example,
- returning (5, 7) means that lines 5, 6, and 7 should be considered
- executed.
-
- This function might decide that the frame doesn't indicate any lines
- from the source file were executed. Return (-1, -1) in this case to
- tell coverage.py that no lines should be recorded for this frame.
-
- """
- lineno = frame.f_lineno
- return lineno, lineno
-
-
-class FileReporter(object):
- """Support needed for files during the analysis and reporting phases.
-
- File tracer plug-ins implement a subclass of `FileReporter`, and return
- instances from their :meth:`CoveragePlugin.file_reporter` method.
-
- There are many methods here, but only :meth:`lines` is required, to provide
- the set of executable lines in the file.
-
- See :ref:`howitworks` for details of the different coverage.py phases.
-
- """
-
- def __init__(self, filename):
- """Simple initialization of a `FileReporter`.
-
- The `filename` argument is the path to the file being reported. This
- will be available as the `.filename` attribute on the object. Other
- method implementations on this base class rely on this attribute.
-
- """
- self.filename = filename
-
- def __repr__(self):
- return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
-
- def relative_filename(self):
- """Get the relative file name for this file.
-
- This file path will be displayed in reports. The default
- implementation will supply the actual project-relative file path. You
- only need to supply this method if you have an unusual syntax for file
- paths.
-
- """
- return files.relative_filename(self.filename)
-
- @contract(returns='unicode')
- def source(self):
- """Get the source for the file.
-
- Returns a Unicode string.
-
- The base implementation simply reads the `self.filename` file and
- decodes it as UTF8. Override this method if your file isn't readable
- as a text file, or if you need other encoding support.
-
- """
- with open(self.filename, "rb") as f:
- return f.read().decode("utf8")
-
- def lines(self):
- """Get the executable lines in this file.
-
- Your plug-in must determine which lines in the file were possibly
- executable. This method returns a set of those line numbers.
-
- Returns a set of line numbers.
-
- """
- _needs_to_implement(self, "lines")
-
- def excluded_lines(self):
- """Get the excluded executable lines in this file.
-
- Your plug-in can use any method it likes to allow the user to exclude
- executable lines from consideration.
-
- Returns a set of line numbers.
-
- The base implementation returns the empty set.
-
- """
- return set()
-
- def translate_lines(self, lines):
- """Translate recorded lines into reported lines.
-
- Some file formats will want to report lines slightly differently than
- they are recorded. For example, Python records the last line of a
- multi-line statement, but reports are nicer if they mention the first
- line.
-
- Your plug-in can optionally define this method to perform these kinds
- of adjustment.
-
- `lines` is a sequence of integers, the recorded line numbers.
-
- Returns a set of integers, the adjusted line numbers.
-
- The base implementation returns the numbers unchanged.
-
- """
- return set(lines)
-
- def arcs(self):
- """Get the executable arcs in this file.
-
- To support branch coverage, your plug-in needs to be able to indicate
- possible execution paths, as a set of line number pairs. Each pair is
- a `(prev, next)` pair indicating that execution can transition from the
- `prev` line number to the `next` line number.
-
- Returns a set of pairs of line numbers. The default implementation
- returns an empty set.
-
- """
- return set()
-
- def no_branch_lines(self):
- """Get the lines excused from branch coverage in this file.
-
- Your plug-in can use any method it likes to allow the user to exclude
- lines from consideration of branch coverage.
-
- Returns a set of line numbers.
-
- The base implementation returns the empty set.
-
- """
- return set()
-
- def translate_arcs(self, arcs):
- """Translate recorded arcs into reported arcs.
-
- Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of
- line number pairs.
-
- Returns a set of line number pairs.
-
- The default implementation returns `arcs` unchanged.
-
- """
- return arcs
-
- def exit_counts(self):
- """Get a count of exits from that each line.
-
- To determine which lines are branches, coverage.py looks for lines that
- have more than one exit. This function creates a dict mapping each
- executable line number to a count of how many exits it has.
-
- To be honest, this feels wrong, and should be refactored. Let me know
- if you attempt to implement this method in your plug-in...
-
- """
- return {}
-
- def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument
- """Provide an English sentence describing a missing arc.
-
- The `start` and `end` arguments are the line numbers of the missing
- arc. Negative numbers indicate entering or exiting code objects.
-
- The `executed_arcs` argument is a set of line number pairs, the arcs
- that were executed in this file.
-
- By default, this simply returns the string "Line {start} didn't jump
- to {end}".
-
- """
- return "Line {start} didn't jump to line {end}".format(start=start, end=end)
-
- def source_token_lines(self):
- """Generate a series of tokenized lines, one for each line in `source`.
-
- These tokens are used for syntax-colored reports.
-
- Each line is a list of pairs, each pair is a token::
-
- [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
-
- Each pair has a token class, and the token text. The token classes
- are:
-
- * ``'com'``: a comment
- * ``'key'``: a keyword
- * ``'nam'``: a name, or identifier
- * ``'num'``: a number
- * ``'op'``: an operator
- * ``'str'``: a string literal
- * ``'ws'``: some white space
- * ``'txt'``: some other kind of text
-
- If you concatenate all the token texts, and then join them with
- newlines, you should have your original source back.
-
- The default implementation simply returns each line tagged as
- ``'txt'``.
-
- """
- for line in self.source().splitlines():
- yield [('txt', line)]
-
- # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
- # of them defined.
-
- def __eq__(self, other):
- return isinstance(other, FileReporter) and self.filename == other.filename
-
- def __ne__(self, other):
- return not (self == other)
-
- def __lt__(self, other):
- return self.filename < other.filename
-
- def __le__(self, other):
- return self.filename <= other.filename
-
- def __gt__(self, other):
- return self.filename > other.filename
-
- def __ge__(self, other):
- return self.filename >= other.filename
-
- __hash__ = None # This object doesn't need to be hashed.
diff --git a/contrib/python/coverage/py3/coverage/plugin_support.py b/contrib/python/coverage/py3/coverage/plugin_support.py
deleted file mode 100644
index 89c1c7658f..0000000000
--- a/contrib/python/coverage/py3/coverage/plugin_support.py
+++ /dev/null
@@ -1,281 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Support for plugins."""
-
-import os
-import os.path
-import sys
-
-from coverage.misc import CoverageException, isolate_module
-from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
-
-os = isolate_module(os)
-
-
-class Plugins(object):
- """The currently loaded collection of coverage.py plugins."""
-
- def __init__(self):
- self.order = []
- self.names = {}
- self.file_tracers = []
- self.configurers = []
- self.context_switchers = []
-
- self.current_module = None
- self.debug = None
-
- @classmethod
- def load_plugins(cls, modules, config, debug=None):
- """Load plugins from `modules`.
-
- Returns a Plugins object with the loaded and configured plugins.
-
- """
- plugins = cls()
- plugins.debug = debug
-
- for module in modules:
- plugins.current_module = module
- __import__(module)
- mod = sys.modules[module]
-
- coverage_init = getattr(mod, "coverage_init", None)
- if not coverage_init:
- raise CoverageException(
- "Plugin module %r didn't define a coverage_init function" % module
- )
-
- options = config.get_plugin_options(module)
- coverage_init(plugins, options)
-
- plugins.current_module = None
- return plugins
-
- def add_file_tracer(self, plugin):
- """Add a file tracer plugin.
-
- `plugin` is an instance of a third-party plugin class. It must
- implement the :meth:`CoveragePlugin.file_tracer` method.
-
- """
- self._add_plugin(plugin, self.file_tracers)
-
- def add_configurer(self, plugin):
- """Add a configuring plugin.
-
- `plugin` is an instance of a third-party plugin class. It must
- implement the :meth:`CoveragePlugin.configure` method.
-
- """
- self._add_plugin(plugin, self.configurers)
-
- def add_dynamic_context(self, plugin):
- """Add a dynamic context plugin.
-
- `plugin` is an instance of a third-party plugin class. It must
- implement the :meth:`CoveragePlugin.dynamic_context` method.
-
- """
- self._add_plugin(plugin, self.context_switchers)
-
- def add_noop(self, plugin):
- """Add a plugin that does nothing.
-
- This is only useful for testing the plugin support.
-
- """
- self._add_plugin(plugin, None)
-
- def _add_plugin(self, plugin, specialized):
- """Add a plugin object.
-
- `plugin` is a :class:`CoveragePlugin` instance to add. `specialized`
- is a list to append the plugin to.
-
- """
- plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__)
- if self.debug and self.debug.should('plugin'):
- self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin))
- labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug)
- plugin = DebugPluginWrapper(plugin, labelled)
-
- # pylint: disable=attribute-defined-outside-init
- plugin._coverage_plugin_name = plugin_name
- plugin._coverage_enabled = True
- self.order.append(plugin)
- self.names[plugin_name] = plugin
- if specialized is not None:
- specialized.append(plugin)
-
- def __nonzero__(self):
- return bool(self.order)
-
- __bool__ = __nonzero__
-
- def __iter__(self):
- return iter(self.order)
-
- def get(self, plugin_name):
- """Return a plugin by name."""
- return self.names[plugin_name]
-
-
-class LabelledDebug(object):
- """A Debug writer, but with labels for prepending to the messages."""
-
- def __init__(self, label, debug, prev_labels=()):
- self.labels = list(prev_labels) + [label]
- self.debug = debug
-
- def add_label(self, label):
- """Add a label to the writer, and return a new `LabelledDebug`."""
- return LabelledDebug(label, self.debug, self.labels)
-
- def message_prefix(self):
- """The prefix to use on messages, combining the labels."""
- prefixes = self.labels + ['']
- return ":\n".join(" "*i+label for i, label in enumerate(prefixes))
-
- def write(self, message):
- """Write `message`, but with the labels prepended."""
- self.debug.write("%s%s" % (self.message_prefix(), message))
-
-
-class DebugPluginWrapper(CoveragePlugin):
- """Wrap a plugin, and use debug to report on what it's doing."""
-
- def __init__(self, plugin, debug):
- super(DebugPluginWrapper, self).__init__()
- self.plugin = plugin
- self.debug = debug
-
- def file_tracer(self, filename):
- tracer = self.plugin.file_tracer(filename)
- self.debug.write("file_tracer(%r) --> %r" % (filename, tracer))
- if tracer:
- debug = self.debug.add_label("file %r" % (filename,))
- tracer = DebugFileTracerWrapper(tracer, debug)
- return tracer
-
- def file_reporter(self, filename):
- reporter = self.plugin.file_reporter(filename)
- self.debug.write("file_reporter(%r) --> %r" % (filename, reporter))
- if reporter:
- debug = self.debug.add_label("file %r" % (filename,))
- reporter = DebugFileReporterWrapper(filename, reporter, debug)
- return reporter
-
- def dynamic_context(self, frame):
- context = self.plugin.dynamic_context(frame)
- self.debug.write("dynamic_context(%r) --> %r" % (frame, context))
- return context
-
- def find_executable_files(self, src_dir):
- executable_files = self.plugin.find_executable_files(src_dir)
- self.debug.write("find_executable_files(%r) --> %r" % (src_dir, executable_files))
- return executable_files
-
- def configure(self, config):
- self.debug.write("configure(%r)" % (config,))
- self.plugin.configure(config)
-
- def sys_info(self):
- return self.plugin.sys_info()
-
-
-class DebugFileTracerWrapper(FileTracer):
- """A debugging `FileTracer`."""
-
- def __init__(self, tracer, debug):
- self.tracer = tracer
- self.debug = debug
-
- def _show_frame(self, frame):
- """A short string identifying a frame, for debug messages."""
- return "%s@%d" % (
- os.path.basename(frame.f_code.co_filename),
- frame.f_lineno,
- )
-
- def source_filename(self):
- sfilename = self.tracer.source_filename()
- self.debug.write("source_filename() --> %r" % (sfilename,))
- return sfilename
-
- def has_dynamic_source_filename(self):
- has = self.tracer.has_dynamic_source_filename()
- self.debug.write("has_dynamic_source_filename() --> %r" % (has,))
- return has
-
- def dynamic_source_filename(self, filename, frame):
- dyn = self.tracer.dynamic_source_filename(filename, frame)
- self.debug.write("dynamic_source_filename(%r, %s) --> %r" % (
- filename, self._show_frame(frame), dyn,
- ))
- return dyn
-
- def line_number_range(self, frame):
- pair = self.tracer.line_number_range(frame)
- self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair))
- return pair
-
-
-class DebugFileReporterWrapper(FileReporter):
- """A debugging `FileReporter`."""
-
- def __init__(self, filename, reporter, debug):
- super(DebugFileReporterWrapper, self).__init__(filename)
- self.reporter = reporter
- self.debug = debug
-
- def relative_filename(self):
- ret = self.reporter.relative_filename()
- self.debug.write("relative_filename() --> %r" % (ret,))
- return ret
-
- def lines(self):
- ret = self.reporter.lines()
- self.debug.write("lines() --> %r" % (ret,))
- return ret
-
- def excluded_lines(self):
- ret = self.reporter.excluded_lines()
- self.debug.write("excluded_lines() --> %r" % (ret,))
- return ret
-
- def translate_lines(self, lines):
- ret = self.reporter.translate_lines(lines)
- self.debug.write("translate_lines(%r) --> %r" % (lines, ret))
- return ret
-
- def translate_arcs(self, arcs):
- ret = self.reporter.translate_arcs(arcs)
- self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret))
- return ret
-
- def no_branch_lines(self):
- ret = self.reporter.no_branch_lines()
- self.debug.write("no_branch_lines() --> %r" % (ret,))
- return ret
-
- def exit_counts(self):
- ret = self.reporter.exit_counts()
- self.debug.write("exit_counts() --> %r" % (ret,))
- return ret
-
- def arcs(self):
- ret = self.reporter.arcs()
- self.debug.write("arcs() --> %r" % (ret,))
- return ret
-
- def source(self):
- ret = self.reporter.source()
- self.debug.write("source() --> %d chars" % (len(ret),))
- return ret
-
- def source_token_lines(self):
- ret = list(self.reporter.source_token_lines())
- self.debug.write("source_token_lines() --> %d tokens" % (len(ret),))
- return ret
diff --git a/contrib/python/coverage/py3/coverage/python.py b/contrib/python/coverage/py3/coverage/python.py
deleted file mode 100644
index 6ff19c34fe..0000000000
--- a/contrib/python/coverage/py3/coverage/python.py
+++ /dev/null
@@ -1,261 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Python source expertise for coverage.py"""
-
-import sys
-import os.path
-import types
-import zipimport
-
-from coverage import env, files
-from coverage.misc import contract, expensive, isolate_module, join_regex
-from coverage.misc import CoverageException, NoSource
-from coverage.parser import PythonParser
-from coverage.phystokens import source_token_lines, source_encoding
-from coverage.plugin import FileReporter
-
-os = isolate_module(os)
-
-
-@contract(returns='bytes')
-def read_python_source(filename):
- """Read the Python source text from `filename`.
-
- Returns bytes.
-
- """
- with open(filename, "rb") as f:
- source = f.read()
-
- if env.IRONPYTHON:
- # IronPython reads Unicode strings even for "rb" files.
- source = bytes(source)
-
- return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
-
-
-@contract(returns='unicode')
-def get_python_source(filename, force_fs=False):
- """Return the source code, as unicode."""
- if getattr(sys, 'is_standalone_binary', False) and not force_fs:
- import __res
-
- modname = __res.importer.file_source(filename)
- if modname:
- source = __res.find(modname)
- source = source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
- return source.decode('utf-8')
- else:
- # it's fake generated package
- return u''
- base, ext = os.path.splitext(filename)
- if ext == ".py" and env.WINDOWS:
- exts = [".py", ".pyw"]
- else:
- exts = [ext]
-
- for ext in exts:
- try_filename = base + ext
- if os.path.exists(try_filename):
- # A regular text file: open it.
- source = read_python_source(try_filename)
- break
-
- # Maybe it's in a zip file?
- source = get_zip_bytes(try_filename)
- if source is not None:
- break
- else:
- # Couldn't find source.
- exc_msg = "No source for code: '%s'.\n" % (filename,)
- exc_msg += "Aborting report output, consider using -i."
- raise NoSource(exc_msg)
-
- # Replace \f because of http://bugs.python.org/issue19035
- source = source.replace(b'\f', b' ')
- source = source.decode(source_encoding(source), "replace")
-
- # Python code should always end with a line with a newline.
- if source and source[-1] != '\n':
- source += '\n'
-
- return source
-
-
-@contract(returns='bytes|None')
-def get_zip_bytes(filename):
- """Get data from `filename` if it is a zip file path.
-
- Returns the bytestring data read from the zip file, or None if no zip file
- could be found or `filename` isn't in it. The data returned will be
- an empty string if the file is empty.
-
- """
- markers = ['.zip'+os.sep, '.egg'+os.sep, '.pex'+os.sep]
- for marker in markers:
- if marker in filename:
- parts = filename.split(marker)
- try:
- zi = zipimport.zipimporter(parts[0]+marker[:-1])
- except zipimport.ZipImportError:
- continue
- try:
- data = zi.get_data(parts[1])
- except IOError:
- continue
- return data
- return None
-
-
-def source_for_file(filename):
- """Return the source filename for `filename`.
-
- Given a file name being traced, return the best guess as to the source
- file to attribute it to.
-
- """
- if filename.endswith(".py"):
- # .py files are themselves source files.
- return filename
-
- elif filename.endswith((".pyc", ".pyo")):
- # Bytecode files probably have source files near them.
- py_filename = filename[:-1]
- if os.path.exists(py_filename):
- # Found a .py file, use that.
- return py_filename
- if env.WINDOWS:
- # On Windows, it could be a .pyw file.
- pyw_filename = py_filename + "w"
- if os.path.exists(pyw_filename):
- return pyw_filename
- # Didn't find source, but it's probably the .py file we want.
- return py_filename
-
- elif filename.endswith("$py.class"):
- # Jython is easy to guess.
- return filename[:-9] + ".py"
-
- # No idea, just use the file name as-is.
- return filename
-
-
-def source_for_morf(morf):
- """Get the source filename for the module-or-file `morf`."""
- if hasattr(morf, '__file__') and morf.__file__:
- filename = morf.__file__
- elif isinstance(morf, types.ModuleType):
- # A module should have had .__file__, otherwise we can't use it.
- # This could be a PEP-420 namespace package.
- raise CoverageException("Module {} has no file".format(morf))
- else:
- filename = morf
-
- filename = source_for_file(files.unicode_filename(filename))
- return filename
-
-
-class PythonFileReporter(FileReporter):
- """Report support for a Python file."""
-
- def __init__(self, morf, coverage=None):
- self.coverage = coverage
-
- filename = source_for_morf(morf)
-
- super(PythonFileReporter, self).__init__(files.canonical_filename(filename))
-
- if hasattr(morf, '__name__'):
- name = morf.__name__.replace(".", os.sep)
- if os.path.basename(filename).startswith('__init__.'):
- name += os.sep + "__init__"
- name += ".py"
- name = files.unicode_filename(name)
- else:
- name = files.relative_filename(filename)
- self.relname = name
-
- self._source = None
- self._parser = None
- self._excluded = None
-
- def __repr__(self):
- return "<PythonFileReporter {!r}>".format(self.filename)
-
- @contract(returns='unicode')
- def relative_filename(self):
- return self.relname
-
- @property
- def parser(self):
- """Lazily create a :class:`PythonParser`."""
- if self._parser is None:
- self._parser = PythonParser(
- filename=self.filename,
- exclude=self.coverage._exclude_regex('exclude'),
- )
- self._parser.parse_source()
- return self._parser
-
- def lines(self):
- """Return the line numbers of statements in the file."""
- return self.parser.statements
-
- def excluded_lines(self):
- """Return the line numbers of statements in the file."""
- return self.parser.excluded
-
- def translate_lines(self, lines):
- return self.parser.translate_lines(lines)
-
- def translate_arcs(self, arcs):
- return self.parser.translate_arcs(arcs)
-
- @expensive
- def no_branch_lines(self):
- no_branch = self.parser.lines_matching(
- join_regex(self.coverage.config.partial_list),
- join_regex(self.coverage.config.partial_always_list)
- )
- return no_branch
-
- @expensive
- def arcs(self):
- return self.parser.arcs()
-
- @expensive
- def exit_counts(self):
- return self.parser.exit_counts()
-
- def missing_arc_description(self, start, end, executed_arcs=None):
- return self.parser.missing_arc_description(start, end, executed_arcs)
-
- @contract(returns='unicode')
- def source(self):
- if self._source is None:
- self._source = get_python_source(self.filename)
- return self._source
-
- def should_be_python(self):
- """Does it seem like this file should contain Python?
-
- This is used to decide if a file reported as part of the execution of
- a program was really likely to have contained Python in the first
- place.
-
- """
- # Get the file extension.
- _, ext = os.path.splitext(self.filename)
-
- # Anything named *.py* should be Python.
- if ext.startswith('.py'):
- return True
- # A file with no extension should be Python.
- if not ext:
- return True
- # Everything else is probably not Python.
- return False
-
- def source_token_lines(self):
- return source_token_lines(self.source())
diff --git a/contrib/python/coverage/py3/coverage/pytracer.py b/contrib/python/coverage/py3/coverage/pytracer.py
deleted file mode 100644
index 7ab4d3ef92..0000000000
--- a/contrib/python/coverage/py3/coverage/pytracer.py
+++ /dev/null
@@ -1,274 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Raw data collector for coverage.py."""
-
-import atexit
-import dis
-import sys
-
-from coverage import env
-
-# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
-YIELD_VALUE = dis.opmap['YIELD_VALUE']
-if env.PY2:
- YIELD_VALUE = chr(YIELD_VALUE)
-
-# When running meta-coverage, this file can try to trace itself, which confuses
-# everything. Don't trace ourselves.
-
-THIS_FILE = __file__.rstrip("co")
-
-
-class PyTracer(object):
- """Python implementation of the raw data tracer."""
-
- # Because of poor implementations of trace-function-manipulating tools,
- # the Python trace function must be kept very simple. In particular, there
- # must be only one function ever set as the trace function, both through
- # sys.settrace, and as the return value from the trace function. Put
- # another way, the trace function must always return itself. It cannot
- # swap in other functions, or return None to avoid tracing a particular
- # frame.
- #
- # The trace manipulator that introduced this restriction is DecoratorTools,
- # which sets a trace function, and then later restores the pre-existing one
- # by calling sys.settrace with a function it found in the current frame.
- #
- # Systems that use DecoratorTools (or similar trace manipulations) must use
- # PyTracer to get accurate results. The command-line --timid argument is
- # used to force the use of this tracer.
-
- def __init__(self):
- # Attributes set from the collector:
- self.data = None
- self.trace_arcs = False
- self.should_trace = None
- self.should_trace_cache = None
- self.should_start_context = None
- self.warn = None
- # The threading module to use, if any.
- self.threading = None
-
- self.cur_file_dict = None
- self.last_line = 0 # int, but uninitialized.
- self.cur_file_name = None
- self.context = None
- self.started_context = False
-
- self.data_stack = []
- self.last_exc_back = None
- self.last_exc_firstlineno = 0
- self.thread = None
- self.stopped = False
- self._activity = False
-
- self.in_atexit = False
- # On exit, self.in_atexit = True
- atexit.register(setattr, self, 'in_atexit', True)
-
- def __repr__(self):
- return "<PyTracer at {}: {} lines in {} files>".format(
- id(self),
- sum(len(v) for v in self.data.values()),
- len(self.data),
- )
-
- def log(self, marker, *args):
- """For hard-core logging of what this tracer is doing."""
- with open("/tmp/debug_trace.txt", "a") as f:
- f.write("{} {}[{}]".format(
- marker,
- id(self),
- len(self.data_stack),
- ))
- if 0:
- f.write(".{:x}.{:x}".format(
- self.thread.ident,
- self.threading.currentThread().ident,
- ))
- f.write(" {}".format(" ".join(map(str, args))))
- if 0:
- f.write(" | ")
- stack = " / ".join(
- (fname or "???").rpartition("/")[-1]
- for _, fname, _, _ in self.data_stack
- )
- f.write(stack)
- f.write("\n")
-
- def _trace(self, frame, event, arg_unused):
- """The trace function passed to sys.settrace."""
-
- if THIS_FILE in frame.f_code.co_filename:
- return None
-
- #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event)
-
- if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable
- # The PyTrace.stop() method has been called, possibly by another
- # thread, let's deactivate ourselves now.
- if 0:
- self.log("---\nX", frame.f_code.co_filename, frame.f_lineno)
- f = frame
- while f:
- self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
- f = f.f_back
- sys.settrace(None)
- self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
- self.data_stack.pop()
- )
- return None
-
- if self.last_exc_back:
- if frame == self.last_exc_back:
- # Someone forgot a return event.
- if self.trace_arcs and self.cur_file_dict:
- pair = (self.last_line, -self.last_exc_firstlineno)
- self.cur_file_dict[pair] = None
- self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
- self.data_stack.pop()
- )
- self.last_exc_back = None
-
- # if event != 'call' and frame.f_code.co_filename != self.cur_file_name:
- # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno)
-
- if event == 'call':
- # Should we start a new context?
- if self.should_start_context and self.context is None:
- context_maybe = self.should_start_context(frame)
- if context_maybe is not None:
- self.context = context_maybe
- self.started_context = True
- self.switch_context(self.context)
- else:
- self.started_context = False
- else:
- self.started_context = False
-
- # Entering a new frame. Decide if we should trace
- # in this file.
- self._activity = True
- self.data_stack.append(
- (
- self.cur_file_dict,
- self.cur_file_name,
- self.last_line,
- self.started_context,
- )
- )
- filename = frame.f_code.co_filename
- self.cur_file_name = filename
- disp = self.should_trace_cache.get(filename)
- if disp is None:
- disp = self.should_trace(filename, frame)
- self.should_trace_cache[filename] = disp
-
- self.cur_file_dict = None
- if disp.trace:
- tracename = disp.source_filename
- if tracename not in self.data:
- self.data[tracename] = {}
- self.cur_file_dict = self.data[tracename]
- # The call event is really a "start frame" event, and happens for
- # function calls and re-entering generators. The f_lasti field is
- # -1 for calls, and a real offset for generators. Use <0 as the
- # line number for calls, and the real line number for generators.
- if getattr(frame, 'f_lasti', -1) < 0:
- self.last_line = -frame.f_code.co_firstlineno
- else:
- self.last_line = frame.f_lineno
- elif event == 'line':
- # Record an executed line.
- if self.cur_file_dict is not None:
- lineno = frame.f_lineno
-
- if self.trace_arcs:
- self.cur_file_dict[(self.last_line, lineno)] = None
- else:
- self.cur_file_dict[lineno] = None
- self.last_line = lineno
- elif event == 'return':
- if self.trace_arcs and self.cur_file_dict:
- # Record an arc leaving the function, but beware that a
- # "return" event might just mean yielding from a generator.
- # Jython seems to have an empty co_code, so just assume return.
- code = frame.f_code.co_code
- if (not code) or code[frame.f_lasti] != YIELD_VALUE:
- first = frame.f_code.co_firstlineno
- self.cur_file_dict[(self.last_line, -first)] = None
- # Leaving this function, pop the filename stack.
- self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
- self.data_stack.pop()
- )
- # Leaving a context?
- if self.started_context:
- self.context = None
- self.switch_context(None)
- elif event == 'exception':
- self.last_exc_back = frame.f_back
- self.last_exc_firstlineno = frame.f_code.co_firstlineno
- return self._trace
-
- def start(self):
- """Start this Tracer.
-
- Return a Python function suitable for use with sys.settrace().
-
- """
- self.stopped = False
- if self.threading:
- if self.thread is None:
- self.thread = self.threading.currentThread()
- else:
- if self.thread.ident != self.threading.currentThread().ident:
- # Re-starting from a different thread!? Don't set the trace
- # function, but we are marked as running again, so maybe it
- # will be ok?
- #self.log("~", "starting on different threads")
- return self._trace
-
- sys.settrace(self._trace)
- return self._trace
-
- def stop(self):
- """Stop this Tracer."""
- # Get the active tracer callback before setting the stop flag to be
- # able to detect if the tracer was changed prior to stopping it.
- tf = sys.gettrace()
-
- # Set the stop flag. The actual call to sys.settrace(None) will happen
- # in the self._trace callback itself to make sure to call it from the
- # right thread.
- self.stopped = True
-
- if self.threading and self.thread.ident != self.threading.currentThread().ident:
- # Called on a different thread than started us: we can't unhook
- # ourselves, but we've set the flag that we should stop, so we
- # won't do any more tracing.
- #self.log("~", "stopping on different threads")
- return
-
- if self.warn:
- # PyPy clears the trace function before running atexit functions,
- # so don't warn if we are in atexit on PyPy and the trace function
- # has changed to None.
- dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None)
- if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable
- self.warn(
- "Trace function changed, measurement is likely wrong: %r" % (tf,),
- slug="trace-changed",
- )
-
- def activity(self):
- """Has there been any activity?"""
- return self._activity
-
- def reset_activity(self):
- """Reset the activity() flag."""
- self._activity = False
-
- def get_stats(self):
- """Return a dictionary of statistics, or None."""
- return None
diff --git a/contrib/python/coverage/py3/coverage/report.py b/contrib/python/coverage/py3/coverage/report.py
deleted file mode 100644
index 64678ff95d..0000000000
--- a/contrib/python/coverage/py3/coverage/report.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Reporter foundation for coverage.py."""
-import sys
-
-from coverage import env
-from coverage.files import prep_patterns, FnmatchMatcher
-from coverage.misc import CoverageException, NoSource, NotPython, ensure_dir_for_file, file_be_gone
-
-
-def render_report(output_path, reporter, morfs):
- """Run the provided reporter ensuring any required setup and cleanup is done
-
- At a high level this method ensures the output file is ready to be written to. Then writes the
- report to it. Then closes the file and deletes any garbage created if necessary.
- """
- file_to_close = None
- delete_file = False
- if output_path:
- if output_path == '-':
- outfile = sys.stdout
- else:
- # Ensure that the output directory is created; done here
- # because this report pre-opens the output file.
- # HTMLReport does this using the Report plumbing because
- # its task is more complex, being multiple files.
- ensure_dir_for_file(output_path)
- open_kwargs = {}
- if env.PY3:
- open_kwargs['encoding'] = 'utf8'
- outfile = open(output_path, "w", **open_kwargs)
- file_to_close = outfile
- try:
- return reporter.report(morfs, outfile=outfile)
- except CoverageException:
- delete_file = True
- raise
- finally:
- if file_to_close:
- file_to_close.close()
- if delete_file:
- file_be_gone(output_path)
-
-
-def get_analysis_to_report(coverage, morfs):
- """Get the files to report on.
-
- For each morf in `morfs`, if it should be reported on (based on the omit
- and include configuration options), yield a pair, the `FileReporter` and
- `Analysis` for the morf.
-
- """
- file_reporters = coverage._get_file_reporters(morfs)
- config = coverage.config
-
- if config.report_include:
- matcher = FnmatchMatcher(prep_patterns(config.report_include))
- file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)]
-
- if config.report_omit:
- matcher = FnmatchMatcher(prep_patterns(config.report_omit))
- file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)]
-
- if not file_reporters:
- raise CoverageException("No data to report.")
-
- for fr in sorted(file_reporters):
- try:
- analysis = coverage._analyze(fr)
- except NoSource:
- if not config.ignore_errors:
- raise
- except NotPython:
- # Only report errors for .py files, and only if we didn't
- # explicitly suppress those errors.
- # NotPython is only raised by PythonFileReporter, which has a
- # should_be_python() method.
- if fr.should_be_python():
- if config.ignore_errors:
- msg = "Couldn't parse Python file '{}'".format(fr.filename)
- coverage._warn(msg, slug="couldnt-parse")
- else:
- raise
- else:
- yield (fr, analysis)
diff --git a/contrib/python/coverage/py3/coverage/results.py b/contrib/python/coverage/py3/coverage/results.py
deleted file mode 100644
index 4916864df3..0000000000
--- a/contrib/python/coverage/py3/coverage/results.py
+++ /dev/null
@@ -1,343 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Results of coverage measurement."""
-
-import collections
-
-from coverage.backward import iitems
-from coverage.debug import SimpleReprMixin
-from coverage.misc import contract, CoverageException, nice_pair
-
-
-class Analysis(object):
- """The results of analyzing a FileReporter."""
-
- def __init__(self, data, file_reporter, file_mapper):
- self.data = data
- self.file_reporter = file_reporter
- self.filename = file_mapper(self.file_reporter.filename)
- self.statements = self.file_reporter.lines()
- self.excluded = self.file_reporter.excluded_lines()
-
- # Identify missing statements.
- executed = self.data.lines(self.filename) or []
- executed = self.file_reporter.translate_lines(executed)
- self.executed = executed
- self.missing = self.statements - self.executed
-
- if self.data.has_arcs():
- self._arc_possibilities = sorted(self.file_reporter.arcs())
- self.exit_counts = self.file_reporter.exit_counts()
- self.no_branch = self.file_reporter.no_branch_lines()
- n_branches = self._total_branches()
- mba = self.missing_branch_arcs()
- n_partial_branches = sum(len(v) for k,v in iitems(mba) if k not in self.missing)
- n_missing_branches = sum(len(v) for k,v in iitems(mba))
- else:
- self._arc_possibilities = []
- self.exit_counts = {}
- self.no_branch = set()
- n_branches = n_partial_branches = n_missing_branches = 0
-
- self.numbers = Numbers(
- n_files=1,
- n_statements=len(self.statements),
- n_excluded=len(self.excluded),
- n_missing=len(self.missing),
- n_branches=n_branches,
- n_partial_branches=n_partial_branches,
- n_missing_branches=n_missing_branches,
- )
-
- def missing_formatted(self, branches=False):
- """The missing line numbers, formatted nicely.
-
- Returns a string like "1-2, 5-11, 13-14".
-
- If `branches` is true, includes the missing branch arcs also.
-
- """
- if branches and self.has_arcs():
- arcs = iitems(self.missing_branch_arcs())
- else:
- arcs = None
-
- return format_lines(self.statements, self.missing, arcs=arcs)
-
- def has_arcs(self):
- """Were arcs measured in this result?"""
- return self.data.has_arcs()
-
- @contract(returns='list(tuple(int, int))')
- def arc_possibilities(self):
- """Returns a sorted list of the arcs in the code."""
- return self._arc_possibilities
-
- @contract(returns='list(tuple(int, int))')
- def arcs_executed(self):
- """Returns a sorted list of the arcs actually executed in the code."""
- executed = self.data.arcs(self.filename) or []
- executed = self.file_reporter.translate_arcs(executed)
- return sorted(executed)
-
- @contract(returns='list(tuple(int, int))')
- def arcs_missing(self):
- """Returns a sorted list of the arcs in the code not executed."""
- possible = self.arc_possibilities()
- executed = self.arcs_executed()
- missing = (
- p for p in possible
- if p not in executed
- and p[0] not in self.no_branch
- )
- return sorted(missing)
-
- @contract(returns='list(tuple(int, int))')
- def arcs_unpredicted(self):
- """Returns a sorted list of the executed arcs missing from the code."""
- possible = self.arc_possibilities()
- executed = self.arcs_executed()
- # Exclude arcs here which connect a line to itself. They can occur
- # in executed data in some cases. This is where they can cause
- # trouble, and here is where it's the least burden to remove them.
- # Also, generators can somehow cause arcs from "enter" to "exit", so
- # make sure we have at least one positive value.
- unpredicted = (
- e for e in executed
- if e not in possible
- and e[0] != e[1]
- and (e[0] > 0 or e[1] > 0)
- )
- return sorted(unpredicted)
-
- def _branch_lines(self):
- """Returns a list of line numbers that have more than one exit."""
- return [l1 for l1,count in iitems(self.exit_counts) if count > 1]
-
- def _total_branches(self):
- """How many total branches are there?"""
- return sum(count for count in self.exit_counts.values() if count > 1)
-
- @contract(returns='dict(int: list(int))')
- def missing_branch_arcs(self):
- """Return arcs that weren't executed from branch lines.
-
- Returns {l1:[l2a,l2b,...], ...}
-
- """
- missing = self.arcs_missing()
- branch_lines = set(self._branch_lines())
- mba = collections.defaultdict(list)
- for l1, l2 in missing:
- if l1 in branch_lines:
- mba[l1].append(l2)
- return mba
-
- @contract(returns='dict(int: tuple(int, int))')
- def branch_stats(self):
- """Get stats about branches.
-
- Returns a dict mapping line numbers to a tuple:
- (total_exits, taken_exits).
- """
-
- missing_arcs = self.missing_branch_arcs()
- stats = {}
- for lnum in self._branch_lines():
- exits = self.exit_counts[lnum]
- missing = len(missing_arcs[lnum])
- stats[lnum] = (exits, exits - missing)
- return stats
-
-
-class Numbers(SimpleReprMixin):
- """The numerical results of measuring coverage.
-
- This holds the basic statistics from `Analysis`, and is used to roll
- up statistics across files.
-
- """
- # A global to determine the precision on coverage percentages, the number
- # of decimal places.
- _precision = 0
- _near0 = 1.0 # These will change when _precision is changed.
- _near100 = 99.0
-
- def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
- n_branches=0, n_partial_branches=0, n_missing_branches=0
- ):
- self.n_files = n_files
- self.n_statements = n_statements
- self.n_excluded = n_excluded
- self.n_missing = n_missing
- self.n_branches = n_branches
- self.n_partial_branches = n_partial_branches
- self.n_missing_branches = n_missing_branches
-
- def init_args(self):
- """Return a list for __init__(*args) to recreate this object."""
- return [
- self.n_files, self.n_statements, self.n_excluded, self.n_missing,
- self.n_branches, self.n_partial_branches, self.n_missing_branches,
- ]
-
- @classmethod
- def set_precision(cls, precision):
- """Set the number of decimal places used to report percentages."""
- assert 0 <= precision < 10
- cls._precision = precision
- cls._near0 = 1.0 / 10**precision
- cls._near100 = 100.0 - cls._near0
-
- @property
- def n_executed(self):
- """Returns the number of executed statements."""
- return self.n_statements - self.n_missing
-
- @property
- def n_executed_branches(self):
- """Returns the number of executed branches."""
- return self.n_branches - self.n_missing_branches
-
- @property
- def pc_covered(self):
- """Returns a single percentage value for coverage."""
- if self.n_statements > 0:
- numerator, denominator = self.ratio_covered
- pc_cov = (100.0 * numerator) / denominator
- else:
- pc_cov = 100.0
- return pc_cov
-
- @property
- def pc_covered_str(self):
- """Returns the percent covered, as a string, without a percent sign.
-
- Note that "0" is only returned when the value is truly zero, and "100"
- is only returned when the value is truly 100. Rounding can never
- result in either "0" or "100".
-
- """
- pc = self.pc_covered
- if 0 < pc < self._near0:
- pc = self._near0
- elif self._near100 < pc < 100:
- pc = self._near100
- else:
- pc = round(pc, self._precision)
- return "%.*f" % (self._precision, pc)
-
- @classmethod
- def pc_str_width(cls):
- """How many characters wide can pc_covered_str be?"""
- width = 3 # "100"
- if cls._precision > 0:
- width += 1 + cls._precision
- return width
-
- @property
- def ratio_covered(self):
- """Return a numerator and denominator for the coverage ratio."""
- numerator = self.n_executed + self.n_executed_branches
- denominator = self.n_statements + self.n_branches
- return numerator, denominator
-
- def __add__(self, other):
- nums = Numbers()
- nums.n_files = self.n_files + other.n_files
- nums.n_statements = self.n_statements + other.n_statements
- nums.n_excluded = self.n_excluded + other.n_excluded
- nums.n_missing = self.n_missing + other.n_missing
- nums.n_branches = self.n_branches + other.n_branches
- nums.n_partial_branches = (
- self.n_partial_branches + other.n_partial_branches
- )
- nums.n_missing_branches = (
- self.n_missing_branches + other.n_missing_branches
- )
- return nums
-
- def __radd__(self, other):
- # Implementing 0+Numbers allows us to sum() a list of Numbers.
- if other == 0:
- return self
- return NotImplemented # pragma: not covered (we never call it this way)
-
-
-def _line_ranges(statements, lines):
- """Produce a list of ranges for `format_lines`."""
- statements = sorted(statements)
- lines = sorted(lines)
-
- pairs = []
- start = None
- lidx = 0
- for stmt in statements:
- if lidx >= len(lines):
- break
- if stmt == lines[lidx]:
- lidx += 1
- if not start:
- start = stmt
- end = stmt
- elif start:
- pairs.append((start, end))
- start = None
- if start:
- pairs.append((start, end))
- return pairs
-
-
-def format_lines(statements, lines, arcs=None):
- """Nicely format a list of line numbers.
-
- Format a list of line numbers for printing by coalescing groups of lines as
- long as the lines represent consecutive statements. This will coalesce
- even if there are gaps between statements.
-
- For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and
- `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14".
-
- Both `lines` and `statements` can be any iterable. All of the elements of
- `lines` must be in `statements`, and all of the values must be positive
- integers.
-
- If `arcs` is provided, they are (start,[end,end,end]) pairs that will be
- included in the output as long as start isn't in `lines`.
-
- """
- line_items = [(pair[0], nice_pair(pair)) for pair in _line_ranges(statements, lines)]
- if arcs:
- line_exits = sorted(arcs)
- for line, exits in line_exits:
- for ex in sorted(exits):
- if line not in lines and ex not in lines:
- dest = (ex if ex > 0 else "exit")
- line_items.append((line, "%d->%s" % (line, dest)))
-
- ret = ', '.join(t[-1] for t in sorted(line_items))
- return ret
-
-
-@contract(total='number', fail_under='number', precision=int, returns=bool)
-def should_fail_under(total, fail_under, precision):
- """Determine if a total should fail due to fail-under.
-
- `total` is a float, the coverage measurement total. `fail_under` is the
- fail_under setting to compare with. `precision` is the number of digits
- to consider after the decimal point.
-
- Returns True if the total should fail.
-
- """
- # We can never achieve higher than 100% coverage, or less than zero.
- if not (0 <= fail_under <= 100.0):
- msg = "fail_under={} is invalid. Must be between 0 and 100.".format(fail_under)
- raise CoverageException(msg)
-
- # Special case for fail_under=100, it must really be 100.
- if fail_under == 100.0 and total != 100.0:
- return True
-
- return round(total, precision) < fail_under
diff --git a/contrib/python/coverage/py3/coverage/sqldata.py b/contrib/python/coverage/py3/coverage/sqldata.py
deleted file mode 100644
index a150fdfd0f..0000000000
--- a/contrib/python/coverage/py3/coverage/sqldata.py
+++ /dev/null
@@ -1,1123 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Sqlite coverage data."""
-
-# TODO: factor out dataop debugging to a wrapper class?
-# TODO: make sure all dataop debugging is in place somehow
-
-import collections
-import datetime
-import glob
-import itertools
-import os
-import re
-import sqlite3
-import sys
-import zlib
-
-from coverage import env
-from coverage.backward import get_thread_id, iitems, to_bytes, to_string
-from coverage.debug import NoDebugging, SimpleReprMixin, clipped_repr
-from coverage.files import PathAliases
-from coverage.misc import CoverageException, contract, file_be_gone, filename_suffix, isolate_module
-from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits
-from coverage.version import __version__
-
-os = isolate_module(os)
-
-# If you change the schema, increment the SCHEMA_VERSION, and update the
-# docs in docs/dbschema.rst also.
-
-SCHEMA_VERSION = 7
-
-# Schema versions:
-# 1: Released in 5.0a2
-# 2: Added contexts in 5.0a3.
-# 3: Replaced line table with line_map table.
-# 4: Changed line_map.bitmap to line_map.numbits.
-# 5: Added foreign key declarations.
-# 6: Key-value in meta.
-# 7: line_map -> line_bits
-
-SCHEMA = """\
-CREATE TABLE coverage_schema (
- -- One row, to record the version of the schema in this db.
- version integer
-);
-
-CREATE TABLE meta (
- -- Key-value pairs, to record metadata about the data
- key text,
- value text,
- unique (key)
- -- Keys:
- -- 'has_arcs' boolean -- Is this data recording branches?
- -- 'sys_argv' text -- The coverage command line that recorded the data.
- -- 'version' text -- The version of coverage.py that made the file.
- -- 'when' text -- Datetime when the file was created.
-);
-
-CREATE TABLE file (
- -- A row per file measured.
- id integer primary key,
- path text,
- unique (path)
-);
-
-CREATE TABLE context (
- -- A row per context measured.
- id integer primary key,
- context text,
- unique (context)
-);
-
-CREATE TABLE line_bits (
- -- If recording lines, a row per context per file executed.
- -- All of the line numbers for that file/context are in one numbits.
- file_id integer, -- foreign key to `file`.
- context_id integer, -- foreign key to `context`.
- numbits blob, -- see the numbits functions in coverage.numbits
- foreign key (file_id) references file (id),
- foreign key (context_id) references context (id),
- unique (file_id, context_id)
-);
-
-CREATE TABLE arc (
- -- If recording branches, a row per context per from/to line transition executed.
- file_id integer, -- foreign key to `file`.
- context_id integer, -- foreign key to `context`.
- fromno integer, -- line number jumped from.
- tono integer, -- line number jumped to.
- foreign key (file_id) references file (id),
- foreign key (context_id) references context (id),
- unique (file_id, context_id, fromno, tono)
-);
-
-CREATE TABLE tracer (
- -- A row per file indicating the tracer used for that file.
- file_id integer primary key,
- tracer text,
- foreign key (file_id) references file (id)
-);
-"""
-
-class CoverageData(SimpleReprMixin):
- """Manages collected coverage data, including file storage.
-
- This class is the public supported API to the data that coverage.py
- collects during program execution. It includes information about what code
- was executed. It does not include information from the analysis phase, to
- determine what lines could have been executed, or what lines were not
- executed.
-
- .. note::
-
- The data file is currently a SQLite database file, with a
- :ref:`documented schema <dbschema>`. The schema is subject to change
- though, so be careful about querying it directly. Use this API if you
- can to isolate yourself from changes.
-
- There are a number of kinds of data that can be collected:
-
- * **lines**: the line numbers of source lines that were executed.
- These are always available.
-
- * **arcs**: pairs of source and destination line numbers for transitions
- between source lines. These are only available if branch coverage was
- used.
-
- * **file tracer names**: the module names of the file tracer plugins that
- handled each file in the data.
-
- Lines, arcs, and file tracer names are stored for each source file. File
- names in this API are case-sensitive, even on platforms with
- case-insensitive file systems.
-
- A data file either stores lines, or arcs, but not both.
-
- A data file is associated with the data when the :class:`CoverageData`
- is created, using the parameters `basename`, `suffix`, and `no_disk`. The
- base name can be queried with :meth:`base_filename`, and the actual file
- name being used is available from :meth:`data_filename`.
-
- To read an existing coverage.py data file, use :meth:`read`. You can then
- access the line, arc, or file tracer data with :meth:`lines`, :meth:`arcs`,
- or :meth:`file_tracer`.
-
- The :meth:`has_arcs` method indicates whether arc data is available. You
- can get a set of the files in the data with :meth:`measured_files`. As
- with most Python containers, you can determine if there is any data at all
- by using this object as a boolean value.
-
- The contexts for each line in a file can be read with
- :meth:`contexts_by_lineno`.
-
- To limit querying to certain contexts, use :meth:`set_query_context` or
- :meth:`set_query_contexts`. These will narrow the focus of subsequent
- :meth:`lines`, :meth:`arcs`, and :meth:`contexts_by_lineno` calls. The set
- of all measured context names can be retrieved with
- :meth:`measured_contexts`.
-
- Most data files will be created by coverage.py itself, but you can use
- methods here to create data files if you like. The :meth:`add_lines`,
- :meth:`add_arcs`, and :meth:`add_file_tracers` methods add data, in ways
- that are convenient for coverage.py.
-
- To record data for contexts, use :meth:`set_context` to set a context to
- be used for subsequent :meth:`add_lines` and :meth:`add_arcs` calls.
-
- To add a source file without any measured data, use :meth:`touch_file`,
- or :meth:`touch_files` for a list of such files.
-
- Write the data to its file with :meth:`write`.
-
- You can clear the data in memory with :meth:`erase`. Two data collections
- can be combined by using :meth:`update` on one :class:`CoverageData`,
- passing it the other.
-
- Data in a :class:`CoverageData` can be serialized and deserialized with
- :meth:`dumps` and :meth:`loads`.
-
- """
-
- def __init__(self, basename=None, suffix=None, no_disk=False, warn=None, debug=None):
- """Create a :class:`CoverageData` object to hold coverage-measured data.
-
- Arguments:
- basename (str): the base name of the data file, defaulting to
- ".coverage".
- suffix (str or bool): has the same meaning as the `data_suffix`
- argument to :class:`coverage.Coverage`.
- no_disk (bool): if True, keep all data in memory, and don't
- write any disk file.
- warn: a warning callback function, accepting a warning message
- argument.
- debug: a `DebugControl` object (optional)
-
- """
- self._no_disk = no_disk
- self._basename = os.path.abspath(basename or ".coverage")
- self._suffix = suffix
- self._warn = warn
- self._debug = debug or NoDebugging()
-
- self._choose_filename()
- self._file_map = {}
- # Maps thread ids to SqliteDb objects.
- self._dbs = {}
- self._pid = os.getpid()
-
- # Are we in sync with the data file?
- self._have_used = False
-
- self._has_lines = False
- self._has_arcs = False
-
- self._current_context = None
- self._current_context_id = None
- self._query_context_ids = None
-
- def _choose_filename(self):
- """Set self._filename based on inited attributes."""
- if self._no_disk:
- self._filename = ":memory:"
- else:
- self._filename = self._basename
- suffix = filename_suffix(self._suffix)
- if suffix:
- self._filename += "." + suffix
-
- def _reset(self):
- """Reset our attributes."""
- if self._dbs:
- for db in self._dbs.values():
- db.close()
- self._dbs = {}
- self._file_map = {}
- self._have_used = False
- self._current_context_id = None
-
- def _create_db(self):
- """Create a db file that doesn't exist yet.
-
- Initializes the schema and certain metadata.
- """
- if self._debug.should('dataio'):
- self._debug.write("Creating data file {!r}".format(self._filename))
- self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
- with db:
- db.executescript(SCHEMA)
- db.execute("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
- db.executemany(
- "insert into meta (key, value) values (?, ?)",
- [
- ('sys_argv', str(getattr(sys, 'argv', None))),
- ('version', __version__),
- ('when', datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')),
- ]
- )
-
- def _open_db(self):
- """Open an existing db file, and read its metadata."""
- if self._debug.should('dataio'):
- self._debug.write("Opening data file {!r}".format(self._filename))
- self._dbs[get_thread_id()] = SqliteDb(self._filename, self._debug)
- self._read_db()
-
- def _read_db(self):
- """Read the metadata from a database so that we are ready to use it."""
- with self._dbs[get_thread_id()] as db:
- try:
- schema_version, = db.execute_one("select version from coverage_schema")
- except Exception as exc:
- raise CoverageException(
- "Data file {!r} doesn't seem to be a coverage data file: {}".format(
- self._filename, exc
- )
- )
- else:
- if schema_version != SCHEMA_VERSION:
- raise CoverageException(
- "Couldn't use data file {!r}: wrong schema: {} instead of {}".format(
- self._filename, schema_version, SCHEMA_VERSION
- )
- )
-
- for row in db.execute("select value from meta where key = 'has_arcs'"):
- self._has_arcs = bool(int(row[0]))
- self._has_lines = not self._has_arcs
-
- for path, file_id in db.execute("select path, id from file"):
- self._file_map[path] = file_id
-
- def _connect(self):
- """Get the SqliteDb object to use."""
- if get_thread_id() not in self._dbs:
- if os.path.exists(self._filename):
- self._open_db()
- else:
- self._create_db()
- return self._dbs[get_thread_id()]
-
- def __nonzero__(self):
- if (get_thread_id() not in self._dbs and not os.path.exists(self._filename)):
- return False
- try:
- with self._connect() as con:
- rows = con.execute("select * from file limit 1")
- return bool(list(rows))
- except CoverageException:
- return False
-
- __bool__ = __nonzero__
-
- @contract(returns='bytes')
- def dumps(self):
- """Serialize the current data to a byte string.
-
- The format of the serialized data is not documented. It is only
- suitable for use with :meth:`loads` in the same version of
- coverage.py.
-
- Returns:
- A byte string of serialized data.
-
- .. versionadded:: 5.0
-
- """
- if self._debug.should('dataio'):
- self._debug.write("Dumping data from data file {!r}".format(self._filename))
- with self._connect() as con:
- return b'z' + zlib.compress(to_bytes(con.dump()))
-
- @contract(data='bytes')
- def loads(self, data):
- """Deserialize data from :meth:`dumps`
-
- Use with a newly-created empty :class:`CoverageData` object. It's
- undefined what happens if the object already has data in it.
-
- Arguments:
- data: A byte string of serialized data produced by :meth:`dumps`.
-
- .. versionadded:: 5.0
-
- """
- if self._debug.should('dataio'):
- self._debug.write("Loading data into data file {!r}".format(self._filename))
- if data[:1] != b'z':
- raise CoverageException(
- "Unrecognized serialization: {!r} (head of {} bytes)".format(data[:40], len(data))
- )
- script = to_string(zlib.decompress(data[1:]))
- self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
- with db:
- db.executescript(script)
- self._read_db()
- self._have_used = True
-
- def _file_id(self, filename, add=False):
- """Get the file id for `filename`.
-
- If filename is not in the database yet, add it if `add` is True.
- If `add` is not True, return None.
- """
- if filename not in self._file_map:
- if add:
- with self._connect() as con:
- cur = con.execute("insert or replace into file (path) values (?)", (filename,))
- self._file_map[filename] = cur.lastrowid
- return self._file_map.get(filename)
-
- def _context_id(self, context):
- """Get the id for a context."""
- assert context is not None
- self._start_using()
- with self._connect() as con:
- row = con.execute_one("select id from context where context = ?", (context,))
- if row is not None:
- return row[0]
- else:
- return None
-
- def set_context(self, context):
- """Set the current context for future :meth:`add_lines` etc.
-
- `context` is a str, the name of the context to use for the next data
- additions. The context persists until the next :meth:`set_context`.
-
- .. versionadded:: 5.0
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Setting context: %r" % (context,))
- self._current_context = context
- self._current_context_id = None
-
- def _set_context_id(self):
- """Use the _current_context to set _current_context_id."""
- context = self._current_context or ""
- context_id = self._context_id(context)
- if context_id is not None:
- self._current_context_id = context_id
- else:
- with self._connect() as con:
- cur = con.execute("insert into context (context) values (?)", (context,))
- self._current_context_id = cur.lastrowid
-
- def base_filename(self):
- """The base filename for storing data.
-
- .. versionadded:: 5.0
-
- """
- return self._basename
-
- def data_filename(self):
- """Where is the data stored?
-
- .. versionadded:: 5.0
-
- """
- return self._filename
-
- def add_lines(self, line_data):
- """Add measured line data.
-
- `line_data` is a dictionary mapping file names to dictionaries::
-
- { filename: { lineno: None, ... }, ...}
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Adding lines: %d files, %d lines total" % (
- len(line_data), sum(len(lines) for lines in line_data.values())
- ))
- self._start_using()
- self._choose_lines_or_arcs(lines=True)
- if not line_data:
- return
- with self._connect() as con:
- self._set_context_id()
- for filename, linenos in iitems(line_data):
- linemap = nums_to_numbits(linenos)
- file_id = self._file_id(filename, add=True)
- query = "select numbits from line_bits where file_id = ? and context_id = ?"
- existing = list(con.execute(query, (file_id, self._current_context_id)))
- if existing:
- linemap = numbits_union(linemap, existing[0][0])
-
- con.execute(
- "insert or replace into line_bits "
- " (file_id, context_id, numbits) values (?, ?, ?)",
- (file_id, self._current_context_id, linemap),
- )
-
- def add_arcs(self, arc_data):
- """Add measured arc data.
-
- `arc_data` is a dictionary mapping file names to dictionaries::
-
- { filename: { (l1,l2): None, ... }, ...}
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Adding arcs: %d files, %d arcs total" % (
- len(arc_data), sum(len(arcs) for arcs in arc_data.values())
- ))
- self._start_using()
- self._choose_lines_or_arcs(arcs=True)
- if not arc_data:
- return
- with self._connect() as con:
- self._set_context_id()
- for filename, arcs in iitems(arc_data):
- file_id = self._file_id(filename, add=True)
- data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs]
- con.executemany(
- "insert or ignore into arc "
- "(file_id, context_id, fromno, tono) values (?, ?, ?, ?)",
- data,
- )
-
- def _choose_lines_or_arcs(self, lines=False, arcs=False):
- """Force the data file to choose between lines and arcs."""
- assert lines or arcs
- assert not (lines and arcs)
- if lines and self._has_arcs:
- raise CoverageException("Can't add line measurements to existing branch data")
- if arcs and self._has_lines:
- raise CoverageException("Can't add branch measurements to existing line data")
- if not self._has_arcs and not self._has_lines:
- self._has_lines = lines
- self._has_arcs = arcs
- with self._connect() as con:
- con.execute(
- "insert into meta (key, value) values (?, ?)",
- ('has_arcs', str(int(arcs)))
- )
-
- def add_file_tracers(self, file_tracers):
- """Add per-file plugin information.
-
- `file_tracers` is { filename: plugin_name, ... }
-
- """
- if self._debug.should('dataop'):
- self._debug.write("Adding file tracers: %d files" % (len(file_tracers),))
- if not file_tracers:
- return
- self._start_using()
- with self._connect() as con:
- for filename, plugin_name in iitems(file_tracers):
- file_id = self._file_id(filename)
- if file_id is None:
- raise CoverageException(
- "Can't add file tracer data for unmeasured file '%s'" % (filename,)
- )
-
- existing_plugin = self.file_tracer(filename)
- if existing_plugin:
- if existing_plugin != plugin_name:
- raise CoverageException(
- "Conflicting file tracer name for '%s': %r vs %r" % (
- filename, existing_plugin, plugin_name,
- )
- )
- elif plugin_name:
- con.execute(
- "insert into tracer (file_id, tracer) values (?, ?)",
- (file_id, plugin_name)
- )
-
- def touch_file(self, filename, plugin_name=""):
- """Ensure that `filename` appears in the data, empty if needed.
-
- `plugin_name` is the name of the plugin responsible for this file. It is used
- to associate the right filereporter, etc.
- """
- self.touch_files([filename], plugin_name)
-
- def touch_files(self, filenames, plugin_name=""):
- """Ensure that `filenames` appear in the data, empty if needed.
-
- `plugin_name` is the name of the plugin responsible for these files. It is used
- to associate the right filereporter, etc.
- """
- if self._debug.should('dataop'):
- self._debug.write("Touching %r" % (filenames,))
- self._start_using()
- with self._connect(): # Use this to get one transaction.
- if not self._has_arcs and not self._has_lines:
- raise CoverageException("Can't touch files in an empty CoverageData")
-
- for filename in filenames:
- self._file_id(filename, add=True)
- if plugin_name:
- # Set the tracer for this file
- self.add_file_tracers({filename: plugin_name})
-
- def update(self, other_data, aliases=None):
- """Update this data with data from several other :class:`CoverageData` instances.
-
- If `aliases` is provided, it's a `PathAliases` object that is used to
- re-map paths to match the local machine's.
- """
- if self._debug.should('dataop'):
- self._debug.write("Updating with data from %r" % (
- getattr(other_data, '_filename', '???'),
- ))
- if self._has_lines and other_data._has_arcs:
- raise CoverageException("Can't combine arc data with line data")
- if self._has_arcs and other_data._has_lines:
- raise CoverageException("Can't combine line data with arc data")
-
- aliases = aliases or PathAliases()
-
- # Force the database we're writing to to exist before we start nesting
- # contexts.
- self._start_using()
-
- # Collector for all arcs, lines and tracers
- other_data.read()
- with other_data._connect() as conn:
- # Get files data.
- cur = conn.execute('select path from file')
- files = {path: aliases.map(path) for (path,) in cur}
- cur.close()
-
- # Get contexts data.
- cur = conn.execute('select context from context')
- contexts = [context for (context,) in cur]
- cur.close()
-
- # Get arc data.
- cur = conn.execute(
- 'select file.path, context.context, arc.fromno, arc.tono '
- 'from arc '
- 'inner join file on file.id = arc.file_id '
- 'inner join context on context.id = arc.context_id'
- )
- arcs = [(files[path], context, fromno, tono) for (path, context, fromno, tono) in cur]
- cur.close()
-
- # Get line data.
- cur = conn.execute(
- 'select file.path, context.context, line_bits.numbits '
- 'from line_bits '
- 'inner join file on file.id = line_bits.file_id '
- 'inner join context on context.id = line_bits.context_id'
- )
- lines = {
- (files[path], context): numbits
- for (path, context, numbits) in cur
- }
- cur.close()
-
- # Get tracer data.
- cur = conn.execute(
- 'select file.path, tracer '
- 'from tracer '
- 'inner join file on file.id = tracer.file_id'
- )
- tracers = {files[path]: tracer for (path, tracer) in cur}
- cur.close()
-
- with self._connect() as conn:
- conn.con.isolation_level = 'IMMEDIATE'
-
- # Get all tracers in the DB. Files not in the tracers are assumed
- # to have an empty string tracer. Since Sqlite does not support
- # full outer joins, we have to make two queries to fill the
- # dictionary.
- this_tracers = {path: '' for path, in conn.execute('select path from file')}
- this_tracers.update({
- aliases.map(path): tracer
- for path, tracer in conn.execute(
- 'select file.path, tracer from tracer '
- 'inner join file on file.id = tracer.file_id'
- )
- })
-
- # Create all file and context rows in the DB.
- conn.executemany(
- 'insert or ignore into file (path) values (?)',
- ((file,) for file in files.values())
- )
- file_ids = {
- path: id
- for id, path in conn.execute('select id, path from file')
- }
- conn.executemany(
- 'insert or ignore into context (context) values (?)',
- ((context,) for context in contexts)
- )
- context_ids = {
- context: id
- for id, context in conn.execute('select id, context from context')
- }
-
- # Prepare tracers and fail, if a conflict is found.
- # tracer_paths is used to ensure consistency over the tracer data
- # and tracer_map tracks the tracers to be inserted.
- tracer_map = {}
- for path in files.values():
- this_tracer = this_tracers.get(path)
- other_tracer = tracers.get(path, '')
- # If there is no tracer, there is always the None tracer.
- if this_tracer is not None and this_tracer != other_tracer:
- raise CoverageException(
- "Conflicting file tracer name for '%s': %r vs %r" % (
- path, this_tracer, other_tracer
- )
- )
- tracer_map[path] = other_tracer
-
- # Prepare arc and line rows to be inserted by converting the file
- # and context strings with integer ids. Then use the efficient
- # `executemany()` to insert all rows at once.
- arc_rows = (
- (file_ids[file], context_ids[context], fromno, tono)
- for file, context, fromno, tono in arcs
- )
-
- # Get line data.
- cur = conn.execute(
- 'select file.path, context.context, line_bits.numbits '
- 'from line_bits '
- 'inner join file on file.id = line_bits.file_id '
- 'inner join context on context.id = line_bits.context_id'
- )
- for path, context, numbits in cur:
- key = (aliases.map(path), context)
- if key in lines:
- numbits = numbits_union(lines[key], numbits)
- lines[key] = numbits
- cur.close()
-
- if arcs:
- self._choose_lines_or_arcs(arcs=True)
-
- # Write the combined data.
- conn.executemany(
- 'insert or ignore into arc '
- '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)',
- arc_rows
- )
-
- if lines:
- self._choose_lines_or_arcs(lines=True)
- conn.execute("delete from line_bits")
- conn.executemany(
- "insert into line_bits "
- "(file_id, context_id, numbits) values (?, ?, ?)",
- [
- (file_ids[file], context_ids[context], numbits)
- for (file, context), numbits in lines.items()
- ]
- )
- conn.executemany(
- 'insert or ignore into tracer (file_id, tracer) values (?, ?)',
- ((file_ids[filename], tracer) for filename, tracer in tracer_map.items())
- )
-
- # Update all internal cache data.
- self._reset()
- self.read()
-
- def erase(self, parallel=False):
- """Erase the data in this object.
-
- If `parallel` is true, then also deletes data files created from the
- basename by parallel-mode.
-
- """
- self._reset()
- if self._no_disk:
- return
- if self._debug.should('dataio'):
- self._debug.write("Erasing data file {!r}".format(self._filename))
- file_be_gone(self._filename)
- if parallel:
- data_dir, local = os.path.split(self._filename)
- localdot = local + '.*'
- pattern = os.path.join(os.path.abspath(data_dir), localdot)
- for filename in glob.glob(pattern):
- if self._debug.should('dataio'):
- self._debug.write("Erasing parallel data file {!r}".format(filename))
- file_be_gone(filename)
-
- def read(self):
- """Start using an existing data file."""
- with self._connect(): # TODO: doesn't look right
- self._have_used = True
-
- def write(self):
- """Ensure the data is written to the data file."""
- pass
-
- def _start_using(self):
- """Call this before using the database at all."""
- if self._pid != os.getpid():
- # Looks like we forked! Have to start a new data file.
- self._reset()
- self._choose_filename()
- self._pid = os.getpid()
- if not self._have_used:
- self.erase()
- self._have_used = True
-
- def has_arcs(self):
- """Does the database have arcs (True) or lines (False)."""
- return bool(self._has_arcs)
-
- def measured_files(self):
- """A set of all files that had been measured."""
- return set(self._file_map)
-
- def measured_contexts(self):
- """A set of all contexts that have been measured.
-
- .. versionadded:: 5.0
-
- """
- self._start_using()
- with self._connect() as con:
- contexts = {row[0] for row in con.execute("select distinct(context) from context")}
- return contexts
-
- def file_tracer(self, filename):
- """Get the plugin name of the file tracer for a file.
-
- Returns the name of the plugin that handles this file. If the file was
- measured, but didn't use a plugin, then "" is returned. If the file
- was not measured, then None is returned.
-
- """
- self._start_using()
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return None
- row = con.execute_one("select tracer from tracer where file_id = ?", (file_id,))
- if row is not None:
- return row[0] or ""
- return "" # File was measured, but no tracer associated.
-
- def set_query_context(self, context):
- """Set a context for subsequent querying.
-
- The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
- calls will be limited to only one context. `context` is a string which
- must match a context exactly. If it does not, no exception is raised,
- but queries will return no data.
-
- .. versionadded:: 5.0
-
- """
- self._start_using()
- with self._connect() as con:
- cur = con.execute("select id from context where context = ?", (context,))
- self._query_context_ids = [row[0] for row in cur.fetchall()]
-
- def set_query_contexts(self, contexts):
- """Set a number of contexts for subsequent querying.
-
- The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
- calls will be limited to the specified contexts. `contexts` is a list
- of Python regular expressions. Contexts will be matched using
- :func:`re.search <python:re.search>`. Data will be included in query
- results if they are part of any of the contexts matched.
-
- .. versionadded:: 5.0
-
- """
- self._start_using()
- if contexts:
- with self._connect() as con:
- context_clause = ' or '.join(['context regexp ?'] * len(contexts))
- cur = con.execute("select id from context where " + context_clause, contexts)
- self._query_context_ids = [row[0] for row in cur.fetchall()]
- else:
- self._query_context_ids = None
-
- def lines(self, filename):
- """Get the list of lines executed for a file.
-
- If the file was not measured, returns None. A file might be measured,
- and have no lines executed, in which case an empty list is returned.
-
- If the file was executed, returns a list of integers, the line numbers
- executed in the file. The list is in no particular order.
-
- """
- self._start_using()
- if self.has_arcs():
- arcs = self.arcs(filename)
- if arcs is not None:
- all_lines = itertools.chain.from_iterable(arcs)
- return list({l for l in all_lines if l > 0})
-
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return None
- else:
- query = "select numbits from line_bits where file_id = ?"
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and context_id in (" + ids_array + ")"
- data += self._query_context_ids
- bitmaps = list(con.execute(query, data))
- nums = set()
- for row in bitmaps:
- nums.update(numbits_to_nums(row[0]))
- return list(nums)
-
- def arcs(self, filename):
- """Get the list of arcs executed for a file.
-
- If the file was not measured, returns None. A file might be measured,
- and have no arcs executed, in which case an empty list is returned.
-
- If the file was executed, returns a list of 2-tuples of integers. Each
- pair is a starting line number and an ending line number for a
- transition from one line to another. The list is in no particular
- order.
-
- Negative numbers have special meaning. If the starting line number is
- -N, it represents an entry to the code object that starts at line N.
- If the ending ling number is -N, it's an exit from the code object that
- starts at line N.
-
- """
- self._start_using()
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return None
- else:
- query = "select distinct fromno, tono from arc where file_id = ?"
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and context_id in (" + ids_array + ")"
- data += self._query_context_ids
- arcs = con.execute(query, data)
- return list(arcs)
-
- def contexts_by_lineno(self, filename):
- """Get the contexts for each line in a file.
-
- Returns:
- A dict mapping line numbers to a list of context names.
-
- .. versionadded:: 5.0
-
- """
- lineno_contexts_map = collections.defaultdict(list)
- self._start_using()
- with self._connect() as con:
- file_id = self._file_id(filename)
- if file_id is None:
- return lineno_contexts_map
- if self.has_arcs():
- query = (
- "select arc.fromno, arc.tono, context.context "
- "from arc, context "
- "where arc.file_id = ? and arc.context_id = context.id"
- )
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and arc.context_id in (" + ids_array + ")"
- data += self._query_context_ids
- for fromno, tono, context in con.execute(query, data):
- if context not in lineno_contexts_map[fromno]:
- lineno_contexts_map[fromno].append(context)
- if context not in lineno_contexts_map[tono]:
- lineno_contexts_map[tono].append(context)
- else:
- query = (
- "select l.numbits, c.context from line_bits l, context c "
- "where l.context_id = c.id "
- "and file_id = ?"
- )
- data = [file_id]
- if self._query_context_ids is not None:
- ids_array = ', '.join('?' * len(self._query_context_ids))
- query += " and l.context_id in (" + ids_array + ")"
- data += self._query_context_ids
- for numbits, context in con.execute(query, data):
- for lineno in numbits_to_nums(numbits):
- lineno_contexts_map[lineno].append(context)
- return lineno_contexts_map
-
- @classmethod
- def sys_info(cls):
- """Our information for `Coverage.sys_info`.
-
- Returns a list of (key, value) pairs.
-
- """
- with SqliteDb(":memory:", debug=NoDebugging()) as db:
- temp_store = [row[0] for row in db.execute("pragma temp_store")]
- compile_options = [row[0] for row in db.execute("pragma compile_options")]
-
- return [
- ('sqlite3_version', sqlite3.version),
- ('sqlite3_sqlite_version', sqlite3.sqlite_version),
- ('sqlite3_temp_store', temp_store),
- ('sqlite3_compile_options', compile_options),
- ]
-
-
-class SqliteDb(SimpleReprMixin):
- """A simple abstraction over a SQLite database.
-
- Use as a context manager, then you can use it like a
- :class:`python:sqlite3.Connection` object::
-
- with SqliteDb(filename, debug_control) as db:
- db.execute("insert into schema (version) values (?)", (SCHEMA_VERSION,))
-
- """
- def __init__(self, filename, debug):
- self.debug = debug if debug.should('sql') else None
- self.filename = filename
- self.nest = 0
- self.con = None
-
- def _connect(self):
- """Connect to the db and do universal initialization."""
- if self.con is not None:
- return
-
- # SQLite on Windows on py2 won't open a file if the filename argument
- # has non-ascii characters in it. Opening a relative file name avoids
- # a problem if the current directory has non-ascii.
- filename = self.filename
- if env.WINDOWS and env.PY2:
- try:
- filename = os.path.relpath(self.filename)
- except ValueError:
- # ValueError can be raised under Windows when os.getcwd() returns a
- # folder from a different drive than the drive of self.filename in
- # which case we keep the original value of self.filename unchanged,
- # hoping that we won't face the non-ascii directory problem.
- pass
-
- # It can happen that Python switches threads while the tracer writes
- # data. The second thread will also try to write to the data,
- # effectively causing a nested context. However, given the idempotent
- # nature of the tracer operations, sharing a connection among threads
- # is not a problem.
- if self.debug:
- self.debug.write("Connecting to {!r}".format(self.filename))
- self.con = sqlite3.connect(filename, check_same_thread=False)
- self.con.create_function('REGEXP', 2, _regexp)
-
- # This pragma makes writing faster. It disables rollbacks, but we never need them.
- # PyPy needs the .close() calls here, or sqlite gets twisted up:
- # https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on
- self.execute("pragma journal_mode=off").close()
- # This pragma makes writing faster.
- self.execute("pragma synchronous=off").close()
-
- def close(self):
- """If needed, close the connection."""
- if self.con is not None and self.filename != ":memory:":
- self.con.close()
- self.con = None
-
- def __enter__(self):
- if self.nest == 0:
- self._connect()
- self.con.__enter__()
- self.nest += 1
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.nest -= 1
- if self.nest == 0:
- try:
- self.con.__exit__(exc_type, exc_value, traceback)
- self.close()
- except Exception as exc:
- if self.debug:
- self.debug.write("EXCEPTION from __exit__: {}".format(exc))
- raise
-
- def execute(self, sql, parameters=()):
- """Same as :meth:`python:sqlite3.Connection.execute`."""
- if self.debug:
- tail = " with {!r}".format(parameters) if parameters else ""
- self.debug.write("Executing {!r}{}".format(sql, tail))
- try:
- try:
- return self.con.execute(sql, parameters)
- except Exception:
- # In some cases, an error might happen that isn't really an
- # error. Try again immediately.
- # https://github.com/nedbat/coveragepy/issues/1010
- return self.con.execute(sql, parameters)
- except sqlite3.Error as exc:
- msg = str(exc)
- try:
- # `execute` is the first thing we do with the database, so try
- # hard to provide useful hints if something goes wrong now.
- with open(self.filename, "rb") as bad_file:
- cov4_sig = b"!coverage.py: This is a private format"
- if bad_file.read(len(cov4_sig)) == cov4_sig:
- msg = (
- "Looks like a coverage 4.x data file. "
- "Are you mixing versions of coverage?"
- )
- except Exception:
- pass
- if self.debug:
- self.debug.write("EXCEPTION from execute: {}".format(msg))
- raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg))
-
- def execute_one(self, sql, parameters=()):
- """Execute a statement and return the one row that results.
-
- This is like execute(sql, parameters).fetchone(), except it is
- correct in reading the entire result set. This will raise an
- exception if more than one row results.
-
- Returns a row, or None if there were no rows.
- """
- rows = list(self.execute(sql, parameters))
- if len(rows) == 0:
- return None
- elif len(rows) == 1:
- return rows[0]
- else:
- raise CoverageException("Sql {!r} shouldn't return {} rows".format(sql, len(rows)))
-
- def executemany(self, sql, data):
- """Same as :meth:`python:sqlite3.Connection.executemany`."""
- if self.debug:
- data = list(data)
- self.debug.write("Executing many {!r} with {} rows".format(sql, len(data)))
- return self.con.executemany(sql, data)
-
- def executescript(self, script):
- """Same as :meth:`python:sqlite3.Connection.executescript`."""
- if self.debug:
- self.debug.write("Executing script with {} chars: {}".format(
- len(script), clipped_repr(script, 100),
- ))
- self.con.executescript(script)
-
- def dump(self):
- """Return a multi-line string, the SQL dump of the database."""
- return "\n".join(self.con.iterdump())
-
-
-def _regexp(text, pattern):
- """A regexp function for SQLite."""
- return re.search(text, pattern) is not None
diff --git a/contrib/python/coverage/py3/coverage/summary.py b/contrib/python/coverage/py3/coverage/summary.py
deleted file mode 100644
index 65f8047006..0000000000
--- a/contrib/python/coverage/py3/coverage/summary.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""Summary reporting"""
-
-import sys
-
-from coverage import env
-from coverage.report import get_analysis_to_report
-from coverage.results import Numbers
-from coverage.misc import CoverageException, output_encoding
-
-
-class SummaryReporter(object):
- """A reporter for writing the summary report."""
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
- self.branches = coverage.get_data().has_arcs()
- self.outfile = None
- self.fr_analysis = []
- self.skipped_count = 0
- self.empty_count = 0
- self.total = Numbers()
- self.fmt_err = u"%s %s: %s"
-
- def writeout(self, line):
- """Write a line to the output, adding a newline."""
- if env.PY2:
- line = line.encode(output_encoding())
- self.outfile.write(line.rstrip())
- self.outfile.write("\n")
-
- def report(self, morfs, outfile=None):
- """Writes a report summarizing coverage statistics per module.
-
- `outfile` is a file object to write the summary to. It must be opened
- for native strings (bytes on Python 2, Unicode on Python 3).
-
- """
- self.outfile = outfile or sys.stdout
-
- self.coverage.get_data().set_query_contexts(self.config.report_contexts)
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.report_one_file(fr, analysis)
-
- # Prepare the formatting strings, header, and column sorting.
- max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5])
- fmt_name = u"%%- %ds " % max_name
- fmt_skip_covered = u"\n%s file%s skipped due to complete coverage."
- fmt_skip_empty = u"\n%s empty file%s skipped."
-
- header = (fmt_name % "Name") + u" Stmts Miss"
- fmt_coverage = fmt_name + u"%6d %6d"
- if self.branches:
- header += u" Branch BrPart"
- fmt_coverage += u" %6d %6d"
- width100 = Numbers.pc_str_width()
- header += u"%*s" % (width100+4, "Cover")
- fmt_coverage += u"%%%ds%%%%" % (width100+3,)
- if self.config.show_missing:
- header += u" Missing"
- fmt_coverage += u" %s"
- rule = u"-" * len(header)
-
- column_order = dict(name=0, stmts=1, miss=2, cover=-1)
- if self.branches:
- column_order.update(dict(branch=3, brpart=4))
-
- # Write the header
- self.writeout(header)
- self.writeout(rule)
-
- # `lines` is a list of pairs, (line text, line values). The line text
- # is a string that will be printed, and line values is a tuple of
- # sortable values.
- lines = []
-
- for (fr, analysis) in self.fr_analysis:
- nums = analysis.numbers
-
- args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
- if self.branches:
- args += (nums.n_branches, nums.n_partial_branches)
- args += (nums.pc_covered_str,)
- if self.config.show_missing:
- args += (analysis.missing_formatted(branches=True),)
- text = fmt_coverage % args
- # Add numeric percent coverage so that sorting makes sense.
- args += (nums.pc_covered,)
- lines.append((text, args))
-
- # Sort the lines and write them out.
- if getattr(self.config, 'sort', None):
- sort_option = self.config.sort.lower()
- reverse = False
- if sort_option[0] == '-':
- reverse = True
- sort_option = sort_option[1:]
- elif sort_option[0] == '+':
- sort_option = sort_option[1:]
-
- position = column_order.get(sort_option)
- if position is None:
- raise CoverageException("Invalid sorting option: {!r}".format(self.config.sort))
- lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse)
-
- for line in lines:
- self.writeout(line[0])
-
- # Write a TOTAL line if we had at least one file.
- if self.total.n_files > 0:
- self.writeout(rule)
- args = ("TOTAL", self.total.n_statements, self.total.n_missing)
- if self.branches:
- args += (self.total.n_branches, self.total.n_partial_branches)
- args += (self.total.pc_covered_str,)
- if self.config.show_missing:
- args += ("",)
- self.writeout(fmt_coverage % args)
-
- # Write other final lines.
- if not self.total.n_files and not self.skipped_count:
- raise CoverageException("No data to report.")
-
- if self.config.skip_covered and self.skipped_count:
- self.writeout(
- fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '')
- )
- if self.config.skip_empty and self.empty_count:
- self.writeout(
- fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '')
- )
-
- return self.total.n_statements and self.total.pc_covered
-
- def report_one_file(self, fr, analysis):
- """Report on just one file, the callback from report()."""
- nums = analysis.numbers
- self.total += nums
-
- no_missing_lines = (nums.n_missing == 0)
- no_missing_branches = (nums.n_partial_branches == 0)
- if self.config.skip_covered and no_missing_lines and no_missing_branches:
- # Don't report on 100% files.
- self.skipped_count += 1
- elif self.config.skip_empty and nums.n_statements == 0:
- # Don't report on empty files.
- self.empty_count += 1
- else:
- self.fr_analysis.append((fr, analysis))
diff --git a/contrib/python/coverage/py3/coverage/templite.py b/contrib/python/coverage/py3/coverage/templite.py
deleted file mode 100644
index 7d4024e0af..0000000000
--- a/contrib/python/coverage/py3/coverage/templite.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""A simple Python template renderer, for a nano-subset of Django syntax.
-
-For a detailed discussion of this code, see this chapter from 500 Lines:
-http://aosabook.org/en/500L/a-template-engine.html
-
-"""
-
-# Coincidentally named the same as http://code.activestate.com/recipes/496702/
-
-import re
-
-from coverage import env
-
-
-class TempliteSyntaxError(ValueError):
- """Raised when a template has a syntax error."""
- pass
-
-
-class TempliteValueError(ValueError):
- """Raised when an expression won't evaluate in a template."""
- pass
-
-
-class CodeBuilder(object):
- """Build source code conveniently."""
-
- def __init__(self, indent=0):
- self.code = []
- self.indent_level = indent
-
- def __str__(self):
- return "".join(str(c) for c in self.code)
-
- def add_line(self, line):
- """Add a line of source to the code.
-
- Indentation and newline will be added for you, don't provide them.
-
- """
- self.code.extend([" " * self.indent_level, line, "\n"])
-
- def add_section(self):
- """Add a section, a sub-CodeBuilder."""
- section = CodeBuilder(self.indent_level)
- self.code.append(section)
- return section
-
- INDENT_STEP = 4 # PEP8 says so!
-
- def indent(self):
- """Increase the current indent for following lines."""
- self.indent_level += self.INDENT_STEP
-
- def dedent(self):
- """Decrease the current indent for following lines."""
- self.indent_level -= self.INDENT_STEP
-
- def get_globals(self):
- """Execute the code, and return a dict of globals it defines."""
- # A check that the caller really finished all the blocks they started.
- assert self.indent_level == 0
- # Get the Python source as a single string.
- python_source = str(self)
- # Execute the source, defining globals, and return them.
- global_namespace = {}
- exec(python_source, global_namespace)
- return global_namespace
-
-
-class Templite(object):
- """A simple template renderer, for a nano-subset of Django syntax.
-
- Supported constructs are extended variable access::
-
- {{var.modifier.modifier|filter|filter}}
-
- loops::
-
- {% for var in list %}...{% endfor %}
-
- and ifs::
-
- {% if var %}...{% endif %}
-
- Comments are within curly-hash markers::
-
- {# This will be ignored #}
-
- Lines between `{% joined %}` and `{% endjoined %}` will have lines stripped
- and joined. Be careful, this could join words together!
-
- Any of these constructs can have a hyphen at the end (`-}}`, `-%}`, `-#}`),
- which will collapse the whitespace following the tag.
-
- Construct a Templite with the template text, then use `render` against a
- dictionary context to create a finished string::
-
- templite = Templite('''
- <h1>Hello {{name|upper}}!</h1>
- {% for topic in topics %}
- <p>You are interested in {{topic}}.</p>
- {% endif %}
- ''',
- {'upper': str.upper},
- )
- text = templite.render({
- 'name': "Ned",
- 'topics': ['Python', 'Geometry', 'Juggling'],
- })
-
- """
- def __init__(self, text, *contexts):
- """Construct a Templite with the given `text`.
-
- `contexts` are dictionaries of values to use for future renderings.
- These are good for filters and global values.
-
- """
- self.context = {}
- for context in contexts:
- self.context.update(context)
-
- self.all_vars = set()
- self.loop_vars = set()
-
- # We construct a function in source form, then compile it and hold onto
- # it, and execute it to render the template.
- code = CodeBuilder()
-
- code.add_line("def render_function(context, do_dots):")
- code.indent()
- vars_code = code.add_section()
- code.add_line("result = []")
- code.add_line("append_result = result.append")
- code.add_line("extend_result = result.extend")
- if env.PY2:
- code.add_line("to_str = unicode")
- else:
- code.add_line("to_str = str")
-
- buffered = []
-
- def flush_output():
- """Force `buffered` to the code builder."""
- if len(buffered) == 1:
- code.add_line("append_result(%s)" % buffered[0])
- elif len(buffered) > 1:
- code.add_line("extend_result([%s])" % ", ".join(buffered))
- del buffered[:]
-
- ops_stack = []
-
- # Split the text to form a list of tokens.
- tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
-
- squash = in_joined = False
-
- for token in tokens:
- if token.startswith('{'):
- start, end = 2, -2
- squash = (token[-3] == '-')
- if squash:
- end = -3
-
- if token.startswith('{#'):
- # Comment: ignore it and move on.
- continue
- elif token.startswith('{{'):
- # An expression to evaluate.
- expr = self._expr_code(token[start:end].strip())
- buffered.append("to_str(%s)" % expr)
- else:
- # token.startswith('{%')
- # Action tag: split into words and parse further.
- flush_output()
-
- words = token[start:end].strip().split()
- if words[0] == 'if':
- # An if statement: evaluate the expression to determine if.
- if len(words) != 2:
- self._syntax_error("Don't understand if", token)
- ops_stack.append('if')
- code.add_line("if %s:" % self._expr_code(words[1]))
- code.indent()
- elif words[0] == 'for':
- # A loop: iterate over expression result.
- if len(words) != 4 or words[2] != 'in':
- self._syntax_error("Don't understand for", token)
- ops_stack.append('for')
- self._variable(words[1], self.loop_vars)
- code.add_line(
- "for c_%s in %s:" % (
- words[1],
- self._expr_code(words[3])
- )
- )
- code.indent()
- elif words[0] == 'joined':
- ops_stack.append('joined')
- in_joined = True
- elif words[0].startswith('end'):
- # Endsomething. Pop the ops stack.
- if len(words) != 1:
- self._syntax_error("Don't understand end", token)
- end_what = words[0][3:]
- if not ops_stack:
- self._syntax_error("Too many ends", token)
- start_what = ops_stack.pop()
- if start_what != end_what:
- self._syntax_error("Mismatched end tag", end_what)
- if end_what == 'joined':
- in_joined = False
- else:
- code.dedent()
- else:
- self._syntax_error("Don't understand tag", words[0])
- else:
- # Literal content. If it isn't empty, output it.
- if in_joined:
- token = re.sub(r"\s*\n\s*", "", token.strip())
- elif squash:
- token = token.lstrip()
- if token:
- buffered.append(repr(token))
-
- if ops_stack:
- self._syntax_error("Unmatched action tag", ops_stack[-1])
-
- flush_output()
-
- for var_name in self.all_vars - self.loop_vars:
- vars_code.add_line("c_%s = context[%r]" % (var_name, var_name))
-
- code.add_line('return "".join(result)')
- code.dedent()
- self._render_function = code.get_globals()['render_function']
-
- def _expr_code(self, expr):
- """Generate a Python expression for `expr`."""
- if "|" in expr:
- pipes = expr.split("|")
- code = self._expr_code(pipes[0])
- for func in pipes[1:]:
- self._variable(func, self.all_vars)
- code = "c_%s(%s)" % (func, code)
- elif "." in expr:
- dots = expr.split(".")
- code = self._expr_code(dots[0])
- args = ", ".join(repr(d) for d in dots[1:])
- code = "do_dots(%s, %s)" % (code, args)
- else:
- self._variable(expr, self.all_vars)
- code = "c_%s" % expr
- return code
-
- def _syntax_error(self, msg, thing):
- """Raise a syntax error using `msg`, and showing `thing`."""
- raise TempliteSyntaxError("%s: %r" % (msg, thing))
-
- def _variable(self, name, vars_set):
- """Track that `name` is used as a variable.
-
- Adds the name to `vars_set`, a set of variable names.
-
- Raises an syntax error if `name` is not a valid name.
-
- """
- if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name):
- self._syntax_error("Not a valid name", name)
- vars_set.add(name)
-
- def render(self, context=None):
- """Render this template by applying it to `context`.
-
- `context` is a dictionary of values to use in this rendering.
-
- """
- # Make the complete context we'll use.
- render_context = dict(self.context)
- if context:
- render_context.update(context)
- return self._render_function(render_context, self._do_dots)
-
- def _do_dots(self, value, *dots):
- """Evaluate dotted expressions at run-time."""
- for dot in dots:
- try:
- value = getattr(value, dot)
- except AttributeError:
- try:
- value = value[dot]
- except (TypeError, KeyError):
- raise TempliteValueError(
- "Couldn't evaluate %r.%s" % (value, dot)
- )
- if callable(value):
- value = value()
- return value
diff --git a/contrib/python/coverage/py3/coverage/tomlconfig.py b/contrib/python/coverage/py3/coverage/tomlconfig.py
deleted file mode 100644
index 3ad581571c..0000000000
--- a/contrib/python/coverage/py3/coverage/tomlconfig.py
+++ /dev/null
@@ -1,168 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""TOML configuration support for coverage.py"""
-
-import io
-import os
-import re
-
-from coverage import env
-from coverage.backward import configparser, path_types
-from coverage.misc import CoverageException, substitute_variables
-
-# TOML support is an install-time extra option.
-try:
- import toml
-except ImportError: # pragma: not covered
- toml = None
-
-
-class TomlDecodeError(Exception):
- """An exception class that exists even when toml isn't installed."""
- pass
-
-
-class TomlConfigParser:
- """TOML file reading with the interface of HandyConfigParser."""
-
- # This class has the same interface as config.HandyConfigParser, no
- # need for docstrings.
- # pylint: disable=missing-function-docstring
-
- def __init__(self, our_file):
- self.our_file = our_file
- self.data = None
-
- def read(self, filenames):
- # RawConfigParser takes a filename or list of filenames, but we only
- # ever call this with a single filename.
- assert isinstance(filenames, path_types)
- filename = filenames
- if env.PYVERSION >= (3, 6):
- filename = os.fspath(filename)
-
- try:
- with io.open(filename, encoding='utf-8') as fp:
- toml_text = fp.read()
- except IOError:
- return []
- if toml:
- toml_text = substitute_variables(toml_text, os.environ)
- try:
- self.data = toml.loads(toml_text)
- except toml.TomlDecodeError as err:
- raise TomlDecodeError(*err.args)
- return [filename]
- else:
- has_toml = re.search(r"^\[tool\.coverage\.", toml_text, flags=re.MULTILINE)
- if self.our_file or has_toml:
- # Looks like they meant to read TOML, but we can't read it.
- msg = "Can't read {!r} without TOML support. Install with [toml] extra"
- raise CoverageException(msg.format(filename))
- return []
-
- def _get_section(self, section):
- """Get a section from the data.
-
- Arguments:
- section (str): A section name, which can be dotted.
-
- Returns:
- name (str): the actual name of the section that was found, if any,
- or None.
- data (str): the dict of data in the section, or None if not found.
-
- """
- prefixes = ["tool.coverage."]
- if self.our_file:
- prefixes.append("")
- for prefix in prefixes:
- real_section = prefix + section
- parts = real_section.split(".")
- try:
- data = self.data[parts[0]]
- for part in parts[1:]:
- data = data[part]
- except KeyError:
- continue
- break
- else:
- return None, None
- return real_section, data
-
- def _get(self, section, option):
- """Like .get, but returns the real section name and the value."""
- name, data = self._get_section(section)
- if data is None:
- raise configparser.NoSectionError(section)
- try:
- return name, data[option]
- except KeyError:
- raise configparser.NoOptionError(option, name)
-
- def has_option(self, section, option):
- _, data = self._get_section(section)
- if data is None:
- return False
- return option in data
-
- def has_section(self, section):
- name, _ = self._get_section(section)
- return name
-
- def options(self, section):
- _, data = self._get_section(section)
- if data is None:
- raise configparser.NoSectionError(section)
- return list(data.keys())
-
- def get_section(self, section):
- _, data = self._get_section(section)
- return data
-
- def get(self, section, option):
- _, value = self._get(section, option)
- return value
-
- def _check_type(self, section, option, value, type_, type_desc):
- if not isinstance(value, type_):
- raise ValueError(
- 'Option {!r} in section {!r} is not {}: {!r}'
- .format(option, section, type_desc, value)
- )
-
- def getboolean(self, section, option):
- name, value = self._get(section, option)
- self._check_type(name, option, value, bool, "a boolean")
- return value
-
- def getlist(self, section, option):
- name, values = self._get(section, option)
- self._check_type(name, option, values, list, "a list")
- return values
-
- def getregexlist(self, section, option):
- name, values = self._get(section, option)
- self._check_type(name, option, values, list, "a list")
- for value in values:
- value = value.strip()
- try:
- re.compile(value)
- except re.error as e:
- raise CoverageException(
- "Invalid [%s].%s value %r: %s" % (name, option, value, e)
- )
- return values
-
- def getint(self, section, option):
- name, value = self._get(section, option)
- self._check_type(name, option, value, int, "an integer")
- return value
-
- def getfloat(self, section, option):
- name, value = self._get(section, option)
- if isinstance(value, int):
- value = float(value)
- self._check_type(name, option, value, float, "a float")
- return value
diff --git a/contrib/python/coverage/py3/coverage/version.py b/contrib/python/coverage/py3/coverage/version.py
deleted file mode 100644
index d141a11da3..0000000000
--- a/contrib/python/coverage/py3/coverage/version.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""The version and URL for coverage.py"""
-# This file is exec'ed in setup.py, don't import anything!
-
-# Same semantics as sys.version_info.
-version_info = (5, 5, 0, "final", 0)
-
-
-def _make_version(major, minor, micro, releaselevel, serial):
- """Create a readable version string from version_info tuple components."""
- assert releaselevel in ['alpha', 'beta', 'candidate', 'final']
- version = "%d.%d" % (major, minor)
- if micro:
- version += ".%d" % (micro,)
- if releaselevel != 'final':
- short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel]
- version += "%s%d" % (short, serial)
- return version
-
-
-def _make_url(major, minor, micro, releaselevel, serial):
- """Make the URL people should start at for this version of coverage.py."""
- url = "https://coverage.readthedocs.io"
- if releaselevel != 'final':
- # For pre-releases, use a version-specific URL.
- url += "/en/coverage-" + _make_version(major, minor, micro, releaselevel, serial)
- return url
-
-
-__version__ = _make_version(*version_info)
-__url__ = _make_url(*version_info)
diff --git a/contrib/python/coverage/py3/coverage/xmlreport.py b/contrib/python/coverage/py3/coverage/xmlreport.py
deleted file mode 100644
index 6d012ee692..0000000000
--- a/contrib/python/coverage/py3/coverage/xmlreport.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# coding: utf-8
-# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
-# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
-
-"""XML reporting for coverage.py"""
-
-import os
-import os.path
-import sys
-import time
-import xml.dom.minidom
-
-from coverage import env
-from coverage import __url__, __version__, files
-from coverage.backward import iitems
-from coverage.misc import isolate_module
-from coverage.report import get_analysis_to_report
-
-os = isolate_module(os)
-
-
-DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd'
-
-
-def rate(hit, num):
- """Return the fraction of `hit`/`num`, as a string."""
- if num == 0:
- return "1"
- else:
- return "%.4g" % (float(hit) / num)
-
-
-class XmlReporter(object):
- """A reporter for writing Cobertura-style XML coverage results."""
-
- def __init__(self, coverage):
- self.coverage = coverage
- self.config = self.coverage.config
-
- self.source_paths = set()
- if self.config.source:
- for src in self.config.source:
- if os.path.exists(src):
- if not self.config.relative_files:
- src = files.canonical_filename(src)
- self.source_paths.add(src)
- self.packages = {}
- self.xml_out = None
-
- def report(self, morfs, outfile=None):
- """Generate a Cobertura-compatible XML report for `morfs`.
-
- `morfs` is a list of modules or file names.
-
- `outfile` is a file object to write the XML to.
-
- """
- # Initial setup.
- outfile = outfile or sys.stdout
- has_arcs = self.coverage.get_data().has_arcs()
-
- # Create the DOM that will store the data.
- impl = xml.dom.minidom.getDOMImplementation()
- self.xml_out = impl.createDocument(None, "coverage", None)
-
- # Write header stuff.
- xcoverage = self.xml_out.documentElement
- xcoverage.setAttribute("version", __version__)
- xcoverage.setAttribute("timestamp", str(int(time.time()*1000)))
- xcoverage.appendChild(self.xml_out.createComment(
- " Generated by coverage.py: %s " % __url__
- ))
- xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL))
-
- # Call xml_file for each file in the data.
- for fr, analysis in get_analysis_to_report(self.coverage, morfs):
- self.xml_file(fr, analysis, has_arcs)
-
- xsources = self.xml_out.createElement("sources")
- xcoverage.appendChild(xsources)
-
- # Populate the XML DOM with the source info.
- for path in sorted(self.source_paths):
- xsource = self.xml_out.createElement("source")
- xsources.appendChild(xsource)
- txt = self.xml_out.createTextNode(path)
- xsource.appendChild(txt)
-
- lnum_tot, lhits_tot = 0, 0
- bnum_tot, bhits_tot = 0, 0
-
- xpackages = self.xml_out.createElement("packages")
- xcoverage.appendChild(xpackages)
-
- # Populate the XML DOM with the package info.
- for pkg_name, pkg_data in sorted(iitems(self.packages)):
- class_elts, lhits, lnum, bhits, bnum = pkg_data
- xpackage = self.xml_out.createElement("package")
- xpackages.appendChild(xpackage)
- xclasses = self.xml_out.createElement("classes")
- xpackage.appendChild(xclasses)
- for _, class_elt in sorted(iitems(class_elts)):
- xclasses.appendChild(class_elt)
- xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
- xpackage.setAttribute("line-rate", rate(lhits, lnum))
- if has_arcs:
- branch_rate = rate(bhits, bnum)
- else:
- branch_rate = "0"
- xpackage.setAttribute("branch-rate", branch_rate)
- xpackage.setAttribute("complexity", "0")
-
- lnum_tot += lnum
- lhits_tot += lhits
- bnum_tot += bnum
- bhits_tot += bhits
-
- xcoverage.setAttribute("lines-valid", str(lnum_tot))
- xcoverage.setAttribute("lines-covered", str(lhits_tot))
- xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot))
- if has_arcs:
- xcoverage.setAttribute("branches-valid", str(bnum_tot))
- xcoverage.setAttribute("branches-covered", str(bhits_tot))
- xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot))
- else:
- xcoverage.setAttribute("branches-covered", "0")
- xcoverage.setAttribute("branches-valid", "0")
- xcoverage.setAttribute("branch-rate", "0")
- xcoverage.setAttribute("complexity", "0")
-
- # Write the output file.
- outfile.write(serialize_xml(self.xml_out))
-
- # Return the total percentage.
- denom = lnum_tot + bnum_tot
- if denom == 0:
- pct = 0.0
- else:
- pct = 100.0 * (lhits_tot + bhits_tot) / denom
- return pct
-
- def xml_file(self, fr, analysis, has_arcs):
- """Add to the XML report for a single file."""
-
- if self.config.skip_empty:
- if analysis.numbers.n_statements == 0:
- return
-
- # Create the 'lines' and 'package' XML elements, which
- # are populated later. Note that a package == a directory.
- filename = fr.filename.replace("\\", "/")
- for source_path in self.source_paths:
- source_path = files.canonical_filename(source_path)
- if filename.startswith(source_path.replace("\\", "/") + "/"):
- rel_name = filename[len(source_path)+1:]
- break
- else:
- rel_name = fr.relative_filename()
- self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/"))
-
- dirname = os.path.dirname(rel_name) or u"."
- dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth])
- package_name = dirname.replace("/", ".")
-
- package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
-
- xclass = self.xml_out.createElement("class")
-
- xclass.appendChild(self.xml_out.createElement("methods"))
-
- xlines = self.xml_out.createElement("lines")
- xclass.appendChild(xlines)
-
- xclass.setAttribute("name", os.path.relpath(rel_name, dirname))
- xclass.setAttribute("filename", rel_name.replace("\\", "/"))
- xclass.setAttribute("complexity", "0")
-
- branch_stats = analysis.branch_stats()
- missing_branch_arcs = analysis.missing_branch_arcs()
-
- # For each statement, create an XML 'line' element.
- for line in sorted(analysis.statements):
- xline = self.xml_out.createElement("line")
- xline.setAttribute("number", str(line))
-
- # Q: can we get info about the number of times a statement is
- # executed? If so, that should be recorded here.
- xline.setAttribute("hits", str(int(line not in analysis.missing)))
-
- if has_arcs:
- if line in branch_stats:
- total, taken = branch_stats[line]
- xline.setAttribute("branch", "true")
- xline.setAttribute(
- "condition-coverage",
- "%d%% (%d/%d)" % (100*taken//total, taken, total)
- )
- if line in missing_branch_arcs:
- annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]]
- xline.setAttribute("missing-branches", ",".join(annlines))
- xlines.appendChild(xline)
-
- class_lines = len(analysis.statements)
- class_hits = class_lines - len(analysis.missing)
-
- if has_arcs:
- class_branches = sum(t for t, k in branch_stats.values())
- missing_branches = sum(t - k for t, k in branch_stats.values())
- class_br_hits = class_branches - missing_branches
- else:
- class_branches = 0.0
- class_br_hits = 0.0
-
- # Finalize the statistics that are collected in the XML DOM.
- xclass.setAttribute("line-rate", rate(class_hits, class_lines))
- if has_arcs:
- branch_rate = rate(class_br_hits, class_branches)
- else:
- branch_rate = "0"
- xclass.setAttribute("branch-rate", branch_rate)
-
- package[0][rel_name] = xclass
- package[1] += class_hits
- package[2] += class_lines
- package[3] += class_br_hits
- package[4] += class_branches
-
-
-def serialize_xml(dom):
- """Serialize a minidom node to XML."""
- out = dom.toprettyxml()
- if env.PY2:
- out = out.encode("utf8")
- return out
diff --git a/contrib/python/coverage/py3/ya.make b/contrib/python/coverage/py3/ya.make
deleted file mode 100644
index 76625b48eb..0000000000
--- a/contrib/python/coverage/py3/ya.make
+++ /dev/null
@@ -1,98 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(5.5)
-
-LICENSE(Apache-2.0)
-
-PEERDIR(
- contrib/python/coverage/plugins
- library/python/resource
-)
-
-NO_COMPILER_WARNINGS()
-
-NO_LINT()
-
-NO_CHECK_IMPORTS(
- coverage.fullcoverage.encodings
-)
-
-SRCS(
- coverage/ctracer/datastack.c
- coverage/ctracer/filedisp.c
- coverage/ctracer/module.c
- coverage/ctracer/tracer.c
-)
-
-PY_REGISTER(
- coverage.tracer
-)
-
-PY_SRCS(
- TOP_LEVEL
- coverage/__init__.py
- coverage/__main__.py
- coverage/annotate.py
- coverage/backward.py
- coverage/bytecode.py
- coverage/cmdline.py
- coverage/collector.py
- coverage/config.py
- coverage/context.py
- coverage/control.py
- coverage/data.py
- coverage/debug.py
- coverage/disposition.py
- coverage/env.py
- coverage/execfile.py
- coverage/files.py
- coverage/fullcoverage/encodings.py
- coverage/html.py
- coverage/inorout.py
- coverage/jsonreport.py
- coverage/misc.py
- coverage/multiproc.py
- coverage/numbits.py
- coverage/parser.py
- coverage/phystokens.py
- coverage/plugin.py
- coverage/plugin_support.py
- coverage/python.py
- coverage/pytracer.py
- coverage/report.py
- coverage/results.py
- coverage/sqldata.py
- coverage/summary.py
- coverage/templite.py
- coverage/tomlconfig.py
- coverage/version.py
- coverage/xmlreport.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/coverage/py3/
- .dist-info/METADATA
- .dist-info/entry_points.txt
- .dist-info/top_level.txt
- coverage/htmlfiles/coverage_html.js
- coverage/htmlfiles/favicon_32.png
- coverage/htmlfiles/index.html
- coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
- coverage/htmlfiles/jquery.hotkeys.js
- coverage/htmlfiles/jquery.isonscreen.js
- coverage/htmlfiles/jquery.min.js
- coverage/htmlfiles/jquery.tablesorter.min.js
- coverage/htmlfiles/keybd_closed.png
- coverage/htmlfiles/keybd_open.png
- coverage/htmlfiles/pyfile.html
- coverage/htmlfiles/style.css
- coverage/htmlfiles/style.scss
-)
-
-END()
-
-RECURSE(
- bin
-)
diff --git a/contrib/python/coverage/ya.make b/contrib/python/coverage/ya.make
deleted file mode 100644
index f7202723cd..0000000000
--- a/contrib/python/coverage/ya.make
+++ /dev/null
@@ -1,19 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/coverage/py2)
-ELSE()
- PEERDIR(contrib/python/coverage/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- plugins
- py2
- py3
-)
diff --git a/contrib/python/diff-match-patch/py2/.dist-info/METADATA b/contrib/python/diff-match-patch/py2/.dist-info/METADATA
deleted file mode 100644
index ea1c571881..0000000000
--- a/contrib/python/diff-match-patch/py2/.dist-info/METADATA
+++ /dev/null
@@ -1,112 +0,0 @@
-Metadata-Version: 2.1
-Name: diff-match-patch
-Version: 20200713
-Summary: Repackaging of Google's Diff Match and Patch libraries. Offers robust algorithms to perform the operations required for synchronizing plain text.
-Home-page: https://github.com/diff-match-patch-python/diff-match-patch
-Author: Neil Fraser
-Author-email: fraser@google.com
-Maintainer: John Reese
-Maintainer-email: john@noswap.com
-License: Apache
-Platform: UNKNOWN
-Classifier: Development Status :: 6 - Mature
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Text Processing
-Requires-Python: >=2.7
-Description-Content-Type: text/markdown
-
-# diff-match-patch
-
-Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
-
-[![build status](https://travis-ci.org/diff-match-patch-python/diff-match-patch.svg?branch=master)](https://travis-ci.org/diff-match-patch-python/diff-match-patch)
-[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
-[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
-
-## Install
-
-diff-match-patch is supported on Python 2.7 or Python 3.4 or newer.
-You can install it from PyPI:
-
-```shell
-python -m pip install diff-match-patch
-```
-
-## Usage
-
-Generating a patchset (analogous to unified diff) between two texts:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_make(text1, text2)
-diff = dmp.patch_toText(patches)
-```
-
-Applying a patchset to a text can then be done with:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_fromText(diff)
-new_text, _ = dmp.patch_apply(patches, text)
-```
-
-## Original README
-The Diff Match and Patch libraries offer robust algorithms to perform the
-operations required for synchronizing plain text.
-
-1. Diff:
- * Compare two blocks of plain text and efficiently return a list of differences.
- * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
-2. Match:
- * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
- * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
-3. Patch:
- * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
- * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
-
-Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
-
-### Reference
-
-* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
-* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
-* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
-* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
-* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
-
-### Languages
-Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
-
-* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
-* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
-* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
-* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
-* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
-* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
-* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
-* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
-
-A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
-
-### Algorithms
-This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
-
-This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
-
-[DMP]: https://github.com/google/diff-match-patch
-[API]: https://github.com/google/diff-match-patch/wiki/API
-
-
diff --git a/contrib/python/diff-match-patch/py2/.dist-info/top_level.txt b/contrib/python/diff-match-patch/py2/.dist-info/top_level.txt
deleted file mode 100644
index 63904d71d9..0000000000
--- a/contrib/python/diff-match-patch/py2/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-diff_match_patch
diff --git a/contrib/python/diff-match-patch/py2/AUTHORS b/contrib/python/diff-match-patch/py2/AUTHORS
deleted file mode 100644
index c82809e726..0000000000
--- a/contrib/python/diff-match-patch/py2/AUTHORS
+++ /dev/null
@@ -1,10 +0,0 @@
-# Below is a list of people and organizations that have contributed
-# to the Diff Match Patch project.
-
-Google Inc.
-
-Duncan Cross <duncan.cross@gmail.com> (Lua port)
-Jan Weiß <jan@geheimwerk.de> (Objective C port)
-Matthaeus G. Chajdas <anteru@developer.shelter13.net> (C# port)
-Mike Slemmer <mikeslemmer@gmail.com> (C++ port)
-
diff --git a/contrib/python/diff-match-patch/py2/LICENSE b/contrib/python/diff-match-patch/py2/LICENSE
deleted file mode 100644
index d645695673..0000000000
--- a/contrib/python/diff-match-patch/py2/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/contrib/python/diff-match-patch/py2/README.md b/contrib/python/diff-match-patch/py2/README.md
deleted file mode 100644
index 54f17337a7..0000000000
--- a/contrib/python/diff-match-patch/py2/README.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# diff-match-patch
-
-Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
-
-[![build status](https://travis-ci.org/diff-match-patch-python/diff-match-patch.svg?branch=master)](https://travis-ci.org/diff-match-patch-python/diff-match-patch)
-[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
-[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
-
-## Install
-
-diff-match-patch is supported on Python 2.7 or Python 3.4 or newer.
-You can install it from PyPI:
-
-```shell
-python -m pip install diff-match-patch
-```
-
-## Usage
-
-Generating a patchset (analogous to unified diff) between two texts:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_make(text1, text2)
-diff = dmp.patch_toText(patches)
-```
-
-Applying a patchset to a text can then be done with:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_fromText(diff)
-new_text, _ = dmp.patch_apply(patches, text)
-```
-
-## Original README
-The Diff Match and Patch libraries offer robust algorithms to perform the
-operations required for synchronizing plain text.
-
-1. Diff:
- * Compare two blocks of plain text and efficiently return a list of differences.
- * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
-2. Match:
- * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
- * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
-3. Patch:
- * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
- * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
-
-Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
-
-### Reference
-
-* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
-* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
-* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
-* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
-* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
-
-### Languages
-Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
-
-* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
-* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
-* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
-* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
-* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
-* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
-* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
-* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
-
-A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
-
-### Algorithms
-This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
-
-This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
-
-[DMP]: https://github.com/google/diff-match-patch
-[API]: https://github.com/google/diff-match-patch/wiki/API
diff --git a/contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py b/contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py
deleted file mode 100644
index 37e762a3b1..0000000000
--- a/contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import sys
-
-if sys.version_info >= (3, 0):
- from .diff_match_patch import __author__, __doc__, diff_match_patch, patch_obj
-else:
- from .diff_match_patch_py2 import __author__, __doc__, diff_match_patch, patch_obj
-
-__version__ = "20200713"
-__packager__ = "John Reese (john@noswap.com)"
diff --git a/contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py b/contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py
deleted file mode 100644
index 5bfe9688a2..0000000000
--- a/contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py
+++ /dev/null
@@ -1,2037 +0,0 @@
-#!/usr/bin/python2.4
-
-from __future__ import division
-
-"""Diff Match and Patch
-Copyright 2018 The diff-match-patch Authors.
-https://github.com/google/diff-match-patch
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-"""Functions for diff, match and patch.
-
-Computes the difference between two texts to create a patch.
-Applies the patch onto another text, allowing for errors.
-"""
-
-__author__ = "fraser@google.com (Neil Fraser)"
-
-import re
-import sys
-import time
-import urllib
-
-
-class diff_match_patch:
- """Class containing the diff, match and patch methods.
-
- Also contains the behaviour settings.
- """
-
- def __init__(self):
- """Inits a diff_match_patch object with default settings.
- Redefine these in your program to override the defaults.
- """
-
- # Number of seconds to map a diff before giving up (0 for infinity).
- self.Diff_Timeout = 1.0
- # Cost of an empty edit operation in terms of edit characters.
- self.Diff_EditCost = 4
- # At what point is no match declared (0.0 = perfection, 1.0 = very loose).
- self.Match_Threshold = 0.5
- # How far to search for a match (0 = exact location, 1000+ = broad match).
- # A match this many characters away from the expected location will add
- # 1.0 to the score (0.0 is a perfect match).
- self.Match_Distance = 1000
- # When deleting a large block of text (over ~64 characters), how close do
- # the contents have to be to match the expected contents. (0.0 = perfection,
- # 1.0 = very loose). Note that Match_Threshold controls how closely the
- # end points of a delete need to match.
- self.Patch_DeleteThreshold = 0.5
- # Chunk size for context length.
- self.Patch_Margin = 4
-
- # The number of bits in an int.
- # Python has no maximum, thus to disable patch splitting set to 0.
- # However to avoid long patches in certain pathological cases, use 32.
- # Multiple short patches (using native ints) are much faster than long ones.
- self.Match_MaxBits = 32
-
- # DIFF FUNCTIONS
-
- # The data structure representing a diff is an array of tuples:
- # [(DIFF_DELETE, "Hello"), (DIFF_INSERT, "Goodbye"), (DIFF_EQUAL, " world.")]
- # which means: delete "Hello", add "Goodbye" and keep " world."
- DIFF_DELETE = -1
- DIFF_INSERT = 1
- DIFF_EQUAL = 0
-
- def diff_main(self, text1, text2, checklines=True, deadline=None):
- """Find the differences between two texts. Simplifies the problem by
- stripping any common prefix or suffix off the texts before diffing.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- checklines: Optional speedup flag. If present and false, then don't run
- a line-level diff first to identify the changed areas.
- Defaults to true, which does a faster, slightly less optimal diff.
- deadline: Optional time when the diff should be complete by. Used
- internally for recursive calls. Users should set DiffTimeout instead.
-
- Returns:
- Array of changes.
- """
- # Set a deadline by which time the diff must be complete.
- if deadline == None:
- # Unlike in most languages, Python counts time in seconds.
- if self.Diff_Timeout <= 0:
- deadline = sys.maxint
- else:
- deadline = time.time() + self.Diff_Timeout
-
- # Check for null inputs.
- if text1 == None or text2 == None:
- raise ValueError("Null inputs. (diff_main)")
-
- # Check for equality (speedup).
- if text1 == text2:
- if text1:
- return [(self.DIFF_EQUAL, text1)]
- return []
-
- # Trim off common prefix (speedup).
- commonlength = self.diff_commonPrefix(text1, text2)
- commonprefix = text1[:commonlength]
- text1 = text1[commonlength:]
- text2 = text2[commonlength:]
-
- # Trim off common suffix (speedup).
- commonlength = self.diff_commonSuffix(text1, text2)
- if commonlength == 0:
- commonsuffix = ""
- else:
- commonsuffix = text1[-commonlength:]
- text1 = text1[:-commonlength]
- text2 = text2[:-commonlength]
-
- # Compute the diff on the middle block.
- diffs = self.diff_compute(text1, text2, checklines, deadline)
-
- # Restore the prefix and suffix.
- if commonprefix:
- diffs[:0] = [(self.DIFF_EQUAL, commonprefix)]
- if commonsuffix:
- diffs.append((self.DIFF_EQUAL, commonsuffix))
- self.diff_cleanupMerge(diffs)
- return diffs
-
- def diff_compute(self, text1, text2, checklines, deadline):
- """Find the differences between two texts. Assumes that the texts do not
- have any common prefix or suffix.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- checklines: Speedup flag. If false, then don't run a line-level diff
- first to identify the changed areas.
- If true, then run a faster, slightly less optimal diff.
- deadline: Time when the diff should be complete by.
-
- Returns:
- Array of changes.
- """
- if not text1:
- # Just add some text (speedup).
- return [(self.DIFF_INSERT, text2)]
-
- if not text2:
- # Just delete some text (speedup).
- return [(self.DIFF_DELETE, text1)]
-
- if len(text1) > len(text2):
- (longtext, shorttext) = (text1, text2)
- else:
- (shorttext, longtext) = (text1, text2)
- i = longtext.find(shorttext)
- if i != -1:
- # Shorter text is inside the longer text (speedup).
- diffs = [
- (self.DIFF_INSERT, longtext[:i]),
- (self.DIFF_EQUAL, shorttext),
- (self.DIFF_INSERT, longtext[i + len(shorttext) :]),
- ]
- # Swap insertions for deletions if diff is reversed.
- if len(text1) > len(text2):
- diffs[0] = (self.DIFF_DELETE, diffs[0][1])
- diffs[2] = (self.DIFF_DELETE, diffs[2][1])
- return diffs
-
- if len(shorttext) == 1:
- # Single character string.
- # After the previous speedup, the character can't be an equality.
- return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
-
- # Check to see if the problem can be split in two.
- hm = self.diff_halfMatch(text1, text2)
- if hm:
- # A half-match was found, sort out the return data.
- (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
- # Send both pairs off for separate processing.
- diffs_a = self.diff_main(text1_a, text2_a, checklines, deadline)
- diffs_b = self.diff_main(text1_b, text2_b, checklines, deadline)
- # Merge the results.
- return diffs_a + [(self.DIFF_EQUAL, mid_common)] + diffs_b
-
- if checklines and len(text1) > 100 and len(text2) > 100:
- return self.diff_lineMode(text1, text2, deadline)
-
- return self.diff_bisect(text1, text2, deadline)
-
- def diff_lineMode(self, text1, text2, deadline):
- """Do a quick line-level diff on both strings, then rediff the parts for
- greater accuracy.
- This speedup can produce non-minimal diffs.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- deadline: Time when the diff should be complete by.
-
- Returns:
- Array of changes.
- """
-
- # Scan the text on a line-by-line basis first.
- (text1, text2, linearray) = self.diff_linesToChars(text1, text2)
-
- diffs = self.diff_main(text1, text2, False, deadline)
-
- # Convert the diff back to original text.
- self.diff_charsToLines(diffs, linearray)
- # Eliminate freak matches (e.g. blank lines)
- self.diff_cleanupSemantic(diffs)
-
- # Rediff any replacement blocks, this time character-by-character.
- # Add a dummy entry at the end.
- diffs.append((self.DIFF_EQUAL, ""))
- pointer = 0
- count_delete = 0
- count_insert = 0
- text_delete = ""
- text_insert = ""
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_INSERT:
- count_insert += 1
- text_insert += diffs[pointer][1]
- elif diffs[pointer][0] == self.DIFF_DELETE:
- count_delete += 1
- text_delete += diffs[pointer][1]
- elif diffs[pointer][0] == self.DIFF_EQUAL:
- # Upon reaching an equality, check for prior redundancies.
- if count_delete >= 1 and count_insert >= 1:
- # Delete the offending records and add the merged ones.
- subDiff = self.diff_main(text_delete, text_insert, False, deadline)
- diffs[pointer - count_delete - count_insert : pointer] = subDiff
- pointer = pointer - count_delete - count_insert + len(subDiff)
- count_insert = 0
- count_delete = 0
- text_delete = ""
- text_insert = ""
-
- pointer += 1
-
- diffs.pop() # Remove the dummy entry at the end.
-
- return diffs
-
- def diff_bisect(self, text1, text2, deadline):
- """Find the 'middle snake' of a diff, split the problem in two
- and return the recursively constructed diff.
- See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- deadline: Time at which to bail if not yet complete.
-
- Returns:
- Array of diff tuples.
- """
-
- # Cache the text lengths to prevent multiple calls.
- text1_length = len(text1)
- text2_length = len(text2)
- max_d = (text1_length + text2_length + 1) // 2
- v_offset = max_d
- v_length = 2 * max_d
- v1 = [-1] * v_length
- v1[v_offset + 1] = 0
- v2 = v1[:]
- delta = text1_length - text2_length
- # If the total number of characters is odd, then the front path will
- # collide with the reverse path.
- front = delta % 2 != 0
- # Offsets for start and end of k loop.
- # Prevents mapping of space beyond the grid.
- k1start = 0
- k1end = 0
- k2start = 0
- k2end = 0
- for d in xrange(max_d):
- # Bail out if deadline is reached.
- if time.time() > deadline:
- break
-
- # Walk the front path one step.
- for k1 in xrange(-d + k1start, d + 1 - k1end, 2):
- k1_offset = v_offset + k1
- if k1 == -d or (k1 != d and v1[k1_offset - 1] < v1[k1_offset + 1]):
- x1 = v1[k1_offset + 1]
- else:
- x1 = v1[k1_offset - 1] + 1
- y1 = x1 - k1
- while (
- x1 < text1_length and y1 < text2_length and text1[x1] == text2[y1]
- ):
- x1 += 1
- y1 += 1
- v1[k1_offset] = x1
- if x1 > text1_length:
- # Ran off the right of the graph.
- k1end += 2
- elif y1 > text2_length:
- # Ran off the bottom of the graph.
- k1start += 2
- elif front:
- k2_offset = v_offset + delta - k1
- if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] != -1:
- # Mirror x2 onto top-left coordinate system.
- x2 = text1_length - v2[k2_offset]
- if x1 >= x2:
- # Overlap detected.
- return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
-
- # Walk the reverse path one step.
- for k2 in xrange(-d + k2start, d + 1 - k2end, 2):
- k2_offset = v_offset + k2
- if k2 == -d or (k2 != d and v2[k2_offset - 1] < v2[k2_offset + 1]):
- x2 = v2[k2_offset + 1]
- else:
- x2 = v2[k2_offset - 1] + 1
- y2 = x2 - k2
- while (
- x2 < text1_length
- and y2 < text2_length
- and text1[-x2 - 1] == text2[-y2 - 1]
- ):
- x2 += 1
- y2 += 1
- v2[k2_offset] = x2
- if x2 > text1_length:
- # Ran off the left of the graph.
- k2end += 2
- elif y2 > text2_length:
- # Ran off the top of the graph.
- k2start += 2
- elif not front:
- k1_offset = v_offset + delta - k2
- if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] != -1:
- x1 = v1[k1_offset]
- y1 = v_offset + x1 - k1_offset
- # Mirror x2 onto top-left coordinate system.
- x2 = text1_length - x2
- if x1 >= x2:
- # Overlap detected.
- return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
-
- # Diff took too long and hit the deadline or
- # number of diffs equals number of characters, no commonality at all.
- return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
-
- def diff_bisectSplit(self, text1, text2, x, y, deadline):
- """Given the location of the 'middle snake', split the diff in two parts
- and recurse.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- x: Index of split point in text1.
- y: Index of split point in text2.
- deadline: Time at which to bail if not yet complete.
-
- Returns:
- Array of diff tuples.
- """
- text1a = text1[:x]
- text2a = text2[:y]
- text1b = text1[x:]
- text2b = text2[y:]
-
- # Compute both diffs serially.
- diffs = self.diff_main(text1a, text2a, False, deadline)
- diffsb = self.diff_main(text1b, text2b, False, deadline)
-
- return diffs + diffsb
-
- def diff_linesToChars(self, text1, text2):
- """Split two texts into an array of strings. Reduce the texts to a string
- of hashes where each Unicode character represents one line.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- Three element tuple, containing the encoded text1, the encoded text2 and
- the array of unique strings. The zeroth element of the array of unique
- strings is intentionally blank.
- """
- lineArray = [] # e.g. lineArray[4] == "Hello\n"
- lineHash = {} # e.g. lineHash["Hello\n"] == 4
-
- # "\x00" is a valid character, but various debuggers don't like it.
- # So we'll insert a junk entry to avoid generating a null character.
- lineArray.append("")
-
- def diff_linesToCharsMunge(text):
- """Split a text into an array of strings. Reduce the texts to a string
- of hashes where each Unicode character represents one line.
- Modifies linearray and linehash through being a closure.
-
- Args:
- text: String to encode.
-
- Returns:
- Encoded string.
- """
- chars = []
- # Walk the text, pulling out a substring for each line.
- # text.split('\n') would would temporarily double our memory footprint.
- # Modifying text would create many large strings to garbage collect.
- lineStart = 0
- lineEnd = -1
- while lineEnd < len(text) - 1:
- lineEnd = text.find("\n", lineStart)
- if lineEnd == -1:
- lineEnd = len(text) - 1
- line = text[lineStart : lineEnd + 1]
-
- if line in lineHash:
- chars.append(unichr(lineHash[line]))
- else:
- if len(lineArray) == maxLines:
- # Bail out at 65535 because unichr(65536) throws.
- line = text[lineStart:]
- lineEnd = len(text)
- lineArray.append(line)
- lineHash[line] = len(lineArray) - 1
- chars.append(unichr(len(lineArray) - 1))
- lineStart = lineEnd + 1
- return "".join(chars)
-
- # Allocate 2/3rds of the space for text1, the rest for text2.
- maxLines = 40000
- chars1 = diff_linesToCharsMunge(text1)
- maxLines = 65535
- chars2 = diff_linesToCharsMunge(text2)
- return (chars1, chars2, lineArray)
-
- def diff_charsToLines(self, diffs, lineArray):
- """Rehydrate the text in a diff from a string of line hashes to real lines
- of text.
-
- Args:
- diffs: Array of diff tuples.
- lineArray: Array of unique strings.
- """
- for i in xrange(len(diffs)):
- text = []
- for char in diffs[i][1]:
- text.append(lineArray[ord(char)])
- diffs[i] = (diffs[i][0], "".join(text))
-
- def diff_commonPrefix(self, text1, text2):
- """Determine the common prefix of two strings.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- The number of characters common to the start of each string.
- """
- # Quick check for common null cases.
- if not text1 or not text2 or text1[0] != text2[0]:
- return 0
- # Binary search.
- # Performance analysis: https://neil.fraser.name/news/2007/10/09/
- pointermin = 0
- pointermax = min(len(text1), len(text2))
- pointermid = pointermax
- pointerstart = 0
- while pointermin < pointermid:
- if text1[pointerstart:pointermid] == text2[pointerstart:pointermid]:
- pointermin = pointermid
- pointerstart = pointermin
- else:
- pointermax = pointermid
- pointermid = (pointermax - pointermin) // 2 + pointermin
- return pointermid
-
- def diff_commonSuffix(self, text1, text2):
- """Determine the common suffix of two strings.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- The number of characters common to the end of each string.
- """
- # Quick check for common null cases.
- if not text1 or not text2 or text1[-1] != text2[-1]:
- return 0
- # Binary search.
- # Performance analysis: https://neil.fraser.name/news/2007/10/09/
- pointermin = 0
- pointermax = min(len(text1), len(text2))
- pointermid = pointermax
- pointerend = 0
- while pointermin < pointermid:
- if (
- text1[-pointermid : len(text1) - pointerend]
- == text2[-pointermid : len(text2) - pointerend]
- ):
- pointermin = pointermid
- pointerend = pointermin
- else:
- pointermax = pointermid
- pointermid = (pointermax - pointermin) // 2 + pointermin
- return pointermid
-
- def diff_commonOverlap(self, text1, text2):
- """Determine if the suffix of one string is the prefix of another.
-
- Args:
- text1 First string.
- text2 Second string.
-
- Returns:
- The number of characters common to the end of the first
- string and the start of the second string.
- """
- # Cache the text lengths to prevent multiple calls.
- text1_length = len(text1)
- text2_length = len(text2)
- # Eliminate the null case.
- if text1_length == 0 or text2_length == 0:
- return 0
- # Truncate the longer string.
- if text1_length > text2_length:
- text1 = text1[-text2_length:]
- elif text1_length < text2_length:
- text2 = text2[:text1_length]
- text_length = min(text1_length, text2_length)
- # Quick check for the worst case.
- if text1 == text2:
- return text_length
-
- # Start by looking for a single character match
- # and increase length until no match is found.
- # Performance analysis: https://neil.fraser.name/news/2010/11/04/
- best = 0
- length = 1
- while True:
- pattern = text1[-length:]
- found = text2.find(pattern)
- if found == -1:
- return best
- length += found
- if found == 0 or text1[-length:] == text2[:length]:
- best = length
- length += 1
-
- def diff_halfMatch(self, text1, text2):
- """Do the two texts share a substring which is at least half the length of
- the longer text?
- This speedup can produce non-minimal diffs.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- Five element Array, containing the prefix of text1, the suffix of text1,
- the prefix of text2, the suffix of text2 and the common middle. Or None
- if there was no match.
- """
- if self.Diff_Timeout <= 0:
- # Don't risk returning a non-optimal diff if we have unlimited time.
- return None
- if len(text1) > len(text2):
- (longtext, shorttext) = (text1, text2)
- else:
- (shorttext, longtext) = (text1, text2)
- if len(longtext) < 4 or len(shorttext) * 2 < len(longtext):
- return None # Pointless.
-
- def diff_halfMatchI(longtext, shorttext, i):
- """Does a substring of shorttext exist within longtext such that the
- substring is at least half the length of longtext?
- Closure, but does not reference any external variables.
-
- Args:
- longtext: Longer string.
- shorttext: Shorter string.
- i: Start index of quarter length substring within longtext.
-
- Returns:
- Five element Array, containing the prefix of longtext, the suffix of
- longtext, the prefix of shorttext, the suffix of shorttext and the
- common middle. Or None if there was no match.
- """
- seed = longtext[i : i + len(longtext) // 4]
- best_common = ""
- j = shorttext.find(seed)
- while j != -1:
- prefixLength = self.diff_commonPrefix(longtext[i:], shorttext[j:])
- suffixLength = self.diff_commonSuffix(longtext[:i], shorttext[:j])
- if len(best_common) < suffixLength + prefixLength:
- best_common = (
- shorttext[j - suffixLength : j]
- + shorttext[j : j + prefixLength]
- )
- best_longtext_a = longtext[: i - suffixLength]
- best_longtext_b = longtext[i + prefixLength :]
- best_shorttext_a = shorttext[: j - suffixLength]
- best_shorttext_b = shorttext[j + prefixLength :]
- j = shorttext.find(seed, j + 1)
-
- if len(best_common) * 2 >= len(longtext):
- return (
- best_longtext_a,
- best_longtext_b,
- best_shorttext_a,
- best_shorttext_b,
- best_common,
- )
- else:
- return None
-
- # First check if the second quarter is the seed for a half-match.
- hm1 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 3) // 4)
- # Check again based on the third quarter.
- hm2 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 1) // 2)
- if not hm1 and not hm2:
- return None
- elif not hm2:
- hm = hm1
- elif not hm1:
- hm = hm2
- else:
- # Both matched. Select the longest.
- if len(hm1[4]) > len(hm2[4]):
- hm = hm1
- else:
- hm = hm2
-
- # A half-match was found, sort out the return data.
- if len(text1) > len(text2):
- (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
- else:
- (text2_a, text2_b, text1_a, text1_b, mid_common) = hm
- return (text1_a, text1_b, text2_a, text2_b, mid_common)
-
- def diff_cleanupSemantic(self, diffs):
- """Reduce the number of edits by eliminating semantically trivial
- equalities.
-
- Args:
- diffs: Array of diff tuples.
- """
- changes = False
- equalities = [] # Stack of indices where equalities are found.
- lastEquality = None # Always equal to diffs[equalities[-1]][1]
- pointer = 0 # Index of current position.
- # Number of chars that changed prior to the equality.
- length_insertions1, length_deletions1 = 0, 0
- # Number of chars that changed after the equality.
- length_insertions2, length_deletions2 = 0, 0
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
- equalities.append(pointer)
- length_insertions1, length_insertions2 = length_insertions2, 0
- length_deletions1, length_deletions2 = length_deletions2, 0
- lastEquality = diffs[pointer][1]
- else: # An insertion or deletion.
- if diffs[pointer][0] == self.DIFF_INSERT:
- length_insertions2 += len(diffs[pointer][1])
- else:
- length_deletions2 += len(diffs[pointer][1])
- # Eliminate an equality that is smaller or equal to the edits on both
- # sides of it.
- if (
- lastEquality
- and (
- len(lastEquality) <= max(length_insertions1, length_deletions1)
- )
- and (
- len(lastEquality) <= max(length_insertions2, length_deletions2)
- )
- ):
- # Duplicate record.
- diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
- # Change second copy to insert.
- diffs[equalities[-1] + 1] = (
- self.DIFF_INSERT,
- diffs[equalities[-1] + 1][1],
- )
- # Throw away the equality we just deleted.
- equalities.pop()
- # Throw away the previous equality (it needs to be reevaluated).
- if len(equalities):
- equalities.pop()
- if len(equalities):
- pointer = equalities[-1]
- else:
- pointer = -1
- # Reset the counters.
- length_insertions1, length_deletions1 = 0, 0
- length_insertions2, length_deletions2 = 0, 0
- lastEquality = None
- changes = True
- pointer += 1
-
- # Normalize the diff.
- if changes:
- self.diff_cleanupMerge(diffs)
- self.diff_cleanupSemanticLossless(diffs)
-
- # Find any overlaps between deletions and insertions.
- # e.g: <del>abcxxx</del><ins>xxxdef</ins>
- # -> <del>abc</del>xxx<ins>def</ins>
- # e.g: <del>xxxabc</del><ins>defxxx</ins>
- # -> <ins>def</ins>xxx<del>abc</del>
- # Only extract an overlap if it is as big as the edit ahead or behind it.
- pointer = 1
- while pointer < len(diffs):
- if (
- diffs[pointer - 1][0] == self.DIFF_DELETE
- and diffs[pointer][0] == self.DIFF_INSERT
- ):
- deletion = diffs[pointer - 1][1]
- insertion = diffs[pointer][1]
- overlap_length1 = self.diff_commonOverlap(deletion, insertion)
- overlap_length2 = self.diff_commonOverlap(insertion, deletion)
- if overlap_length1 >= overlap_length2:
- if (
- overlap_length1 >= len(deletion) / 2.0
- or overlap_length1 >= len(insertion) / 2.0
- ):
- # Overlap found. Insert an equality and trim the surrounding edits.
- diffs.insert(
- pointer, (self.DIFF_EQUAL, insertion[:overlap_length1])
- )
- diffs[pointer - 1] = (
- self.DIFF_DELETE,
- deletion[: len(deletion) - overlap_length1],
- )
- diffs[pointer + 1] = (
- self.DIFF_INSERT,
- insertion[overlap_length1:],
- )
- pointer += 1
- else:
- if (
- overlap_length2 >= len(deletion) / 2.0
- or overlap_length2 >= len(insertion) / 2.0
- ):
- # Reverse overlap found.
- # Insert an equality and swap and trim the surrounding edits.
- diffs.insert(
- pointer, (self.DIFF_EQUAL, deletion[:overlap_length2])
- )
- diffs[pointer - 1] = (
- self.DIFF_INSERT,
- insertion[: len(insertion) - overlap_length2],
- )
- diffs[pointer + 1] = (
- self.DIFF_DELETE,
- deletion[overlap_length2:],
- )
- pointer += 1
- pointer += 1
- pointer += 1
-
- def diff_cleanupSemanticLossless(self, diffs):
- """Look for single edits surrounded on both sides by equalities
- which can be shifted sideways to align the edit to a word boundary.
- e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
-
- Args:
- diffs: Array of diff tuples.
- """
-
- def diff_cleanupSemanticScore(one, two):
- """Given two strings, compute a score representing whether the
- internal boundary falls on logical boundaries.
- Scores range from 6 (best) to 0 (worst).
- Closure, but does not reference any external variables.
-
- Args:
- one: First string.
- two: Second string.
-
- Returns:
- The score.
- """
- if not one or not two:
- # Edges are the best.
- return 6
-
- # Each port of this function behaves slightly differently due to
- # subtle differences in each language's definition of things like
- # 'whitespace'. Since this function's purpose is largely cosmetic,
- # the choice has been made to use each language's native features
- # rather than force total conformity.
- char1 = one[-1]
- char2 = two[0]
- nonAlphaNumeric1 = not char1.isalnum()
- nonAlphaNumeric2 = not char2.isalnum()
- whitespace1 = nonAlphaNumeric1 and char1.isspace()
- whitespace2 = nonAlphaNumeric2 and char2.isspace()
- lineBreak1 = whitespace1 and (char1 == "\r" or char1 == "\n")
- lineBreak2 = whitespace2 and (char2 == "\r" or char2 == "\n")
- blankLine1 = lineBreak1 and self.BLANKLINEEND.search(one)
- blankLine2 = lineBreak2 and self.BLANKLINESTART.match(two)
-
- if blankLine1 or blankLine2:
- # Five points for blank lines.
- return 5
- elif lineBreak1 or lineBreak2:
- # Four points for line breaks.
- return 4
- elif nonAlphaNumeric1 and not whitespace1 and whitespace2:
- # Three points for end of sentences.
- return 3
- elif whitespace1 or whitespace2:
- # Two points for whitespace.
- return 2
- elif nonAlphaNumeric1 or nonAlphaNumeric2:
- # One point for non-alphanumeric.
- return 1
- return 0
-
- pointer = 1
- # Intentionally ignore the first and last element (don't need checking).
- while pointer < len(diffs) - 1:
- if (
- diffs[pointer - 1][0] == self.DIFF_EQUAL
- and diffs[pointer + 1][0] == self.DIFF_EQUAL
- ):
- # This is a single edit surrounded by equalities.
- equality1 = diffs[pointer - 1][1]
- edit = diffs[pointer][1]
- equality2 = diffs[pointer + 1][1]
-
- # First, shift the edit as far left as possible.
- commonOffset = self.diff_commonSuffix(equality1, edit)
- if commonOffset:
- commonString = edit[-commonOffset:]
- equality1 = equality1[:-commonOffset]
- edit = commonString + edit[:-commonOffset]
- equality2 = commonString + equality2
-
- # Second, step character by character right, looking for the best fit.
- bestEquality1 = equality1
- bestEdit = edit
- bestEquality2 = equality2
- bestScore = diff_cleanupSemanticScore(
- equality1, edit
- ) + diff_cleanupSemanticScore(edit, equality2)
- while edit and equality2 and edit[0] == equality2[0]:
- equality1 += edit[0]
- edit = edit[1:] + equality2[0]
- equality2 = equality2[1:]
- score = diff_cleanupSemanticScore(
- equality1, edit
- ) + diff_cleanupSemanticScore(edit, equality2)
- # The >= encourages trailing rather than leading whitespace on edits.
- if score >= bestScore:
- bestScore = score
- bestEquality1 = equality1
- bestEdit = edit
- bestEquality2 = equality2
-
- if diffs[pointer - 1][1] != bestEquality1:
- # We have an improvement, save it back to the diff.
- if bestEquality1:
- diffs[pointer - 1] = (diffs[pointer - 1][0], bestEquality1)
- else:
- del diffs[pointer - 1]
- pointer -= 1
- diffs[pointer] = (diffs[pointer][0], bestEdit)
- if bestEquality2:
- diffs[pointer + 1] = (diffs[pointer + 1][0], bestEquality2)
- else:
- del diffs[pointer + 1]
- pointer -= 1
- pointer += 1
-
- # Define some regex patterns for matching boundaries.
- BLANKLINEEND = re.compile(r"\n\r?\n$")
- BLANKLINESTART = re.compile(r"^\r?\n\r?\n")
-
- def diff_cleanupEfficiency(self, diffs):
- """Reduce the number of edits by eliminating operationally trivial
- equalities.
-
- Args:
- diffs: Array of diff tuples.
- """
- changes = False
- equalities = [] # Stack of indices where equalities are found.
- lastEquality = None # Always equal to diffs[equalities[-1]][1]
- pointer = 0 # Index of current position.
- pre_ins = False # Is there an insertion operation before the last equality.
- pre_del = False # Is there a deletion operation before the last equality.
- post_ins = False # Is there an insertion operation after the last equality.
- post_del = False # Is there a deletion operation after the last equality.
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
- if len(diffs[pointer][1]) < self.Diff_EditCost and (
- post_ins or post_del
- ):
- # Candidate found.
- equalities.append(pointer)
- pre_ins = post_ins
- pre_del = post_del
- lastEquality = diffs[pointer][1]
- else:
- # Not a candidate, and can never become one.
- equalities = []
- lastEquality = None
-
- post_ins = post_del = False
- else: # An insertion or deletion.
- if diffs[pointer][0] == self.DIFF_DELETE:
- post_del = True
- else:
- post_ins = True
-
- # Five types to be split:
- # <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
- # <ins>A</ins>X<ins>C</ins><del>D</del>
- # <ins>A</ins><del>B</del>X<ins>C</ins>
- # <ins>A</del>X<ins>C</ins><del>D</del>
- # <ins>A</ins><del>B</del>X<del>C</del>
-
- if lastEquality and (
- (pre_ins and pre_del and post_ins and post_del)
- or (
- (len(lastEquality) < self.Diff_EditCost / 2)
- and (pre_ins + pre_del + post_ins + post_del) == 3
- )
- ):
- # Duplicate record.
- diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
- # Change second copy to insert.
- diffs[equalities[-1] + 1] = (
- self.DIFF_INSERT,
- diffs[equalities[-1] + 1][1],
- )
- equalities.pop() # Throw away the equality we just deleted.
- lastEquality = None
- if pre_ins and pre_del:
- # No changes made which could affect previous entry, keep going.
- post_ins = post_del = True
- equalities = []
- else:
- if len(equalities):
- equalities.pop() # Throw away the previous equality.
- if len(equalities):
- pointer = equalities[-1]
- else:
- pointer = -1
- post_ins = post_del = False
- changes = True
- pointer += 1
-
- if changes:
- self.diff_cleanupMerge(diffs)
-
- def diff_cleanupMerge(self, diffs):
- """Reorder and merge like edit sections. Merge equalities.
- Any edit section can move as long as it doesn't cross an equality.
-
- Args:
- diffs: Array of diff tuples.
- """
- diffs.append((self.DIFF_EQUAL, "")) # Add a dummy entry at the end.
- pointer = 0
- count_delete = 0
- count_insert = 0
- text_delete = ""
- text_insert = ""
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_INSERT:
- count_insert += 1
- text_insert += diffs[pointer][1]
- pointer += 1
- elif diffs[pointer][0] == self.DIFF_DELETE:
- count_delete += 1
- text_delete += diffs[pointer][1]
- pointer += 1
- elif diffs[pointer][0] == self.DIFF_EQUAL:
- # Upon reaching an equality, check for prior redundancies.
- if count_delete + count_insert > 1:
- if count_delete != 0 and count_insert != 0:
- # Factor out any common prefixies.
- commonlength = self.diff_commonPrefix(text_insert, text_delete)
- if commonlength != 0:
- x = pointer - count_delete - count_insert - 1
- if x >= 0 and diffs[x][0] == self.DIFF_EQUAL:
- diffs[x] = (
- diffs[x][0],
- diffs[x][1] + text_insert[:commonlength],
- )
- else:
- diffs.insert(
- 0, (self.DIFF_EQUAL, text_insert[:commonlength])
- )
- pointer += 1
- text_insert = text_insert[commonlength:]
- text_delete = text_delete[commonlength:]
- # Factor out any common suffixies.
- commonlength = self.diff_commonSuffix(text_insert, text_delete)
- if commonlength != 0:
- diffs[pointer] = (
- diffs[pointer][0],
- text_insert[-commonlength:] + diffs[pointer][1],
- )
- text_insert = text_insert[:-commonlength]
- text_delete = text_delete[:-commonlength]
- # Delete the offending records and add the merged ones.
- new_ops = []
- if len(text_delete) != 0:
- new_ops.append((self.DIFF_DELETE, text_delete))
- if len(text_insert) != 0:
- new_ops.append((self.DIFF_INSERT, text_insert))
- pointer -= count_delete + count_insert
- diffs[pointer : pointer + count_delete + count_insert] = new_ops
- pointer += len(new_ops) + 1
- elif pointer != 0 and diffs[pointer - 1][0] == self.DIFF_EQUAL:
- # Merge this equality with the previous one.
- diffs[pointer - 1] = (
- diffs[pointer - 1][0],
- diffs[pointer - 1][1] + diffs[pointer][1],
- )
- del diffs[pointer]
- else:
- pointer += 1
-
- count_insert = 0
- count_delete = 0
- text_delete = ""
- text_insert = ""
-
- if diffs[-1][1] == "":
- diffs.pop() # Remove the dummy entry at the end.
-
- # Second pass: look for single edits surrounded on both sides by equalities
- # which can be shifted sideways to eliminate an equality.
- # e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
- changes = False
- pointer = 1
- # Intentionally ignore the first and last element (don't need checking).
- while pointer < len(diffs) - 1:
- if (
- diffs[pointer - 1][0] == self.DIFF_EQUAL
- and diffs[pointer + 1][0] == self.DIFF_EQUAL
- ):
- # This is a single edit surrounded by equalities.
- if diffs[pointer][1].endswith(diffs[pointer - 1][1]):
- # Shift the edit over the previous equality.
- if diffs[pointer - 1][1] != "":
- diffs[pointer] = (
- diffs[pointer][0],
- diffs[pointer - 1][1]
- + diffs[pointer][1][: -len(diffs[pointer - 1][1])],
- )
- diffs[pointer + 1] = (
- diffs[pointer + 1][0],
- diffs[pointer - 1][1] + diffs[pointer + 1][1],
- )
- del diffs[pointer - 1]
- changes = True
- elif diffs[pointer][1].startswith(diffs[pointer + 1][1]):
- # Shift the edit over the next equality.
- diffs[pointer - 1] = (
- diffs[pointer - 1][0],
- diffs[pointer - 1][1] + diffs[pointer + 1][1],
- )
- diffs[pointer] = (
- diffs[pointer][0],
- diffs[pointer][1][len(diffs[pointer + 1][1]) :]
- + diffs[pointer + 1][1],
- )
- del diffs[pointer + 1]
- changes = True
- pointer += 1
-
- # If shifts were made, the diff needs reordering and another shift sweep.
- if changes:
- self.diff_cleanupMerge(diffs)
-
- def diff_xIndex(self, diffs, loc):
- """loc is a location in text1, compute and return the equivalent location
- in text2. e.g. "The cat" vs "The big cat", 1->1, 5->8
-
- Args:
- diffs: Array of diff tuples.
- loc: Location within text1.
-
- Returns:
- Location within text2.
- """
- chars1 = 0
- chars2 = 0
- last_chars1 = 0
- last_chars2 = 0
- for x in xrange(len(diffs)):
- (op, text) = diffs[x]
- if op != self.DIFF_INSERT: # Equality or deletion.
- chars1 += len(text)
- if op != self.DIFF_DELETE: # Equality or insertion.
- chars2 += len(text)
- if chars1 > loc: # Overshot the location.
- break
- last_chars1 = chars1
- last_chars2 = chars2
-
- if len(diffs) != x and diffs[x][0] == self.DIFF_DELETE:
- # The location was deleted.
- return last_chars2
- # Add the remaining len(character).
- return last_chars2 + (loc - last_chars1)
-
- def diff_prettyHtml(self, diffs):
- """Convert a diff array into a pretty HTML report.
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- HTML representation.
- """
- html = []
- for (op, data) in diffs:
- text = (
- data.replace("&", "&amp;")
- .replace("<", "&lt;")
- .replace(">", "&gt;")
- .replace("\n", "&para;<br>")
- )
- if op == self.DIFF_INSERT:
- html.append('<ins style="background:#e6ffe6;">%s</ins>' % text)
- elif op == self.DIFF_DELETE:
- html.append('<del style="background:#ffe6e6;">%s</del>' % text)
- elif op == self.DIFF_EQUAL:
- html.append("<span>%s</span>" % text)
- return "".join(html)
-
- def diff_text1(self, diffs):
- """Compute and return the source text (all equalities and deletions).
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Source text.
- """
- text = []
- for (op, data) in diffs:
- if op != self.DIFF_INSERT:
- text.append(data)
- return "".join(text)
-
- def diff_text2(self, diffs):
- """Compute and return the destination text (all equalities and insertions).
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Destination text.
- """
- text = []
- for (op, data) in diffs:
- if op != self.DIFF_DELETE:
- text.append(data)
- return "".join(text)
-
- def diff_levenshtein(self, diffs):
- """Compute the Levenshtein distance; the number of inserted, deleted or
- substituted characters.
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Number of changes.
- """
- levenshtein = 0
- insertions = 0
- deletions = 0
- for (op, data) in diffs:
- if op == self.DIFF_INSERT:
- insertions += len(data)
- elif op == self.DIFF_DELETE:
- deletions += len(data)
- elif op == self.DIFF_EQUAL:
- # A deletion and an insertion is one substitution.
- levenshtein += max(insertions, deletions)
- insertions = 0
- deletions = 0
- levenshtein += max(insertions, deletions)
- return levenshtein
-
- def diff_toDelta(self, diffs):
- """Crush the diff into an encoded string which describes the operations
- required to transform text1 into text2.
- E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
- Operations are tab-separated. Inserted text is escaped using %xx notation.
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Delta text.
- """
- text = []
- for (op, data) in diffs:
- if op == self.DIFF_INSERT:
- # High ascii will raise UnicodeDecodeError. Use Unicode instead.
- data = data.encode("utf-8")
- text.append("+" + urllib.quote(data, "!~*'();/?:@&=+$,# "))
- elif op == self.DIFF_DELETE:
- text.append("-%d" % len(data))
- elif op == self.DIFF_EQUAL:
- text.append("=%d" % len(data))
- return "\t".join(text)
-
- def diff_fromDelta(self, text1, delta):
- """Given the original text1, and an encoded string which describes the
- operations required to transform text1 into text2, compute the full diff.
-
- Args:
- text1: Source string for the diff.
- delta: Delta text.
-
- Returns:
- Array of diff tuples.
-
- Raises:
- ValueError: If invalid input.
- """
- if type(delta) == unicode:
- # Deltas should be composed of a subset of ascii chars, Unicode not
- # required. If this encode raises UnicodeEncodeError, delta is invalid.
- delta = delta.encode("ascii")
- diffs = []
- pointer = 0 # Cursor in text1
- tokens = delta.split("\t")
- for token in tokens:
- if token == "":
- # Blank tokens are ok (from a trailing \t).
- continue
- # Each token begins with a one character parameter which specifies the
- # operation of this token (delete, insert, equality).
- param = token[1:]
- if token[0] == "+":
- param = urllib.unquote(param).decode("utf-8")
- diffs.append((self.DIFF_INSERT, param))
- elif token[0] == "-" or token[0] == "=":
- try:
- n = int(param)
- except ValueError:
- raise ValueError("Invalid number in diff_fromDelta: " + param)
- if n < 0:
- raise ValueError("Negative number in diff_fromDelta: " + param)
- text = text1[pointer : pointer + n]
- pointer += n
- if token[0] == "=":
- diffs.append((self.DIFF_EQUAL, text))
- else:
- diffs.append((self.DIFF_DELETE, text))
- else:
- # Anything else is an error.
- raise ValueError(
- "Invalid diff operation in diff_fromDelta: " + token[0]
- )
- if pointer != len(text1):
- raise ValueError(
- "Delta length (%d) does not equal source text length (%d)."
- % (pointer, len(text1))
- )
- return diffs
-
- # MATCH FUNCTIONS
-
- def match_main(self, text, pattern, loc):
- """Locate the best instance of 'pattern' in 'text' near 'loc'.
-
- Args:
- text: The text to search.
- pattern: The pattern to search for.
- loc: The location to search around.
-
- Returns:
- Best match index or -1.
- """
- # Check for null inputs.
- if text == None or pattern == None:
- raise ValueError("Null inputs. (match_main)")
-
- loc = max(0, min(loc, len(text)))
- if text == pattern:
- # Shortcut (potentially not guaranteed by the algorithm)
- return 0
- elif not text:
- # Nothing to match.
- return -1
- elif text[loc : loc + len(pattern)] == pattern:
- # Perfect match at the perfect spot! (Includes case of null pattern)
- return loc
- else:
- # Do a fuzzy compare.
- match = self.match_bitap(text, pattern, loc)
- return match
-
- def match_bitap(self, text, pattern, loc):
- """Locate the best instance of 'pattern' in 'text' near 'loc' using the
- Bitap algorithm.
-
- Args:
- text: The text to search.
- pattern: The pattern to search for.
- loc: The location to search around.
-
- Returns:
- Best match index or -1.
- """
- # Python doesn't have a maxint limit, so ignore this check.
- # if self.Match_MaxBits != 0 and len(pattern) > self.Match_MaxBits:
- # raise ValueError("Pattern too long for this application.")
-
- # Initialise the alphabet.
- s = self.match_alphabet(pattern)
-
- def match_bitapScore(e, x):
- """Compute and return the score for a match with e errors and x location.
- Accesses loc and pattern through being a closure.
-
- Args:
- e: Number of errors in match.
- x: Location of match.
-
- Returns:
- Overall score for match (0.0 = good, 1.0 = bad).
- """
- accuracy = float(e) / len(pattern)
- proximity = abs(loc - x)
- if not self.Match_Distance:
- # Dodge divide by zero error.
- return proximity and 1.0 or accuracy
- return accuracy + (proximity / float(self.Match_Distance))
-
- # Highest score beyond which we give up.
- score_threshold = self.Match_Threshold
- # Is there a nearby exact match? (speedup)
- best_loc = text.find(pattern, loc)
- if best_loc != -1:
- score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
- # What about in the other direction? (speedup)
- best_loc = text.rfind(pattern, loc + len(pattern))
- if best_loc != -1:
- score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
-
- # Initialise the bit arrays.
- matchmask = 1 << (len(pattern) - 1)
- best_loc = -1
-
- bin_max = len(pattern) + len(text)
- # Empty initialization added to appease pychecker.
- last_rd = None
- for d in xrange(len(pattern)):
- # Scan for the best match each iteration allows for one more error.
- # Run a binary search to determine how far from 'loc' we can stray at
- # this error level.
- bin_min = 0
- bin_mid = bin_max
- while bin_min < bin_mid:
- if match_bitapScore(d, loc + bin_mid) <= score_threshold:
- bin_min = bin_mid
- else:
- bin_max = bin_mid
- bin_mid = (bin_max - bin_min) // 2 + bin_min
-
- # Use the result from this iteration as the maximum for the next.
- bin_max = bin_mid
- start = max(1, loc - bin_mid + 1)
- finish = min(loc + bin_mid, len(text)) + len(pattern)
-
- rd = [0] * (finish + 2)
- rd[finish + 1] = (1 << d) - 1
- for j in xrange(finish, start - 1, -1):
- if len(text) <= j - 1:
- # Out of range.
- charMatch = 0
- else:
- charMatch = s.get(text[j - 1], 0)
- if d == 0: # First pass: exact match.
- rd[j] = ((rd[j + 1] << 1) | 1) & charMatch
- else: # Subsequent passes: fuzzy match.
- rd[j] = (
- (((rd[j + 1] << 1) | 1) & charMatch)
- | (((last_rd[j + 1] | last_rd[j]) << 1) | 1)
- | last_rd[j + 1]
- )
- if rd[j] & matchmask:
- score = match_bitapScore(d, j - 1)
- # This match will almost certainly be better than any existing match.
- # But check anyway.
- if score <= score_threshold:
- # Told you so.
- score_threshold = score
- best_loc = j - 1
- if best_loc > loc:
- # When passing loc, don't exceed our current distance from loc.
- start = max(1, 2 * loc - best_loc)
- else:
- # Already passed loc, downhill from here on in.
- break
- # No hope for a (better) match at greater error levels.
- if match_bitapScore(d + 1, loc) > score_threshold:
- break
- last_rd = rd
- return best_loc
-
- def match_alphabet(self, pattern):
- """Initialise the alphabet for the Bitap algorithm.
-
- Args:
- pattern: The text to encode.
-
- Returns:
- Hash of character locations.
- """
- s = {}
- for char in pattern:
- s[char] = 0
- for i in xrange(len(pattern)):
- s[pattern[i]] |= 1 << (len(pattern) - i - 1)
- return s
-
- # PATCH FUNCTIONS
-
- def patch_addContext(self, patch, text):
- """Increase the context until it is unique,
- but don't let the pattern expand beyond Match_MaxBits.
-
- Args:
- patch: The patch to grow.
- text: Source text.
- """
- if len(text) == 0:
- return
- pattern = text[patch.start2 : patch.start2 + patch.length1]
- padding = 0
-
- # Look for the first and last matches of pattern in text. If two different
- # matches are found, increase the pattern length.
- while text.find(pattern) != text.rfind(pattern) and (
- self.Match_MaxBits == 0
- or len(pattern) < self.Match_MaxBits - self.Patch_Margin - self.Patch_Margin
- ):
- padding += self.Patch_Margin
- pattern = text[
- max(0, patch.start2 - padding) : patch.start2 + patch.length1 + padding
- ]
- # Add one chunk for good luck.
- padding += self.Patch_Margin
-
- # Add the prefix.
- prefix = text[max(0, patch.start2 - padding) : patch.start2]
- if prefix:
- patch.diffs[:0] = [(self.DIFF_EQUAL, prefix)]
- # Add the suffix.
- suffix = text[
- patch.start2 + patch.length1 : patch.start2 + patch.length1 + padding
- ]
- if suffix:
- patch.diffs.append((self.DIFF_EQUAL, suffix))
-
- # Roll back the start points.
- patch.start1 -= len(prefix)
- patch.start2 -= len(prefix)
- # Extend lengths.
- patch.length1 += len(prefix) + len(suffix)
- patch.length2 += len(prefix) + len(suffix)
-
- def patch_make(self, a, b=None, c=None):
- """Compute a list of patches to turn text1 into text2.
- Use diffs if provided, otherwise compute it ourselves.
- There are four ways to call this function, depending on what data is
- available to the caller:
- Method 1:
- a = text1, b = text2
- Method 2:
- a = diffs
- Method 3 (optimal):
- a = text1, b = diffs
- Method 4 (deprecated, use method 3):
- a = text1, b = text2, c = diffs
-
- Args:
- a: text1 (methods 1,3,4) or Array of diff tuples for text1 to
- text2 (method 2).
- b: text2 (methods 1,4) or Array of diff tuples for text1 to
- text2 (method 3) or undefined (method 2).
- c: Array of diff tuples for text1 to text2 (method 4) or
- undefined (methods 1,2,3).
-
- Returns:
- Array of Patch objects.
- """
- text1 = None
- diffs = None
- # Note that texts may arrive as 'str' or 'unicode'.
- if isinstance(a, basestring) and isinstance(b, basestring) and c is None:
- # Method 1: text1, text2
- # Compute diffs from text1 and text2.
- text1 = a
- diffs = self.diff_main(text1, b, True)
- if len(diffs) > 2:
- self.diff_cleanupSemantic(diffs)
- self.diff_cleanupEfficiency(diffs)
- elif isinstance(a, list) and b is None and c is None:
- # Method 2: diffs
- # Compute text1 from diffs.
- diffs = a
- text1 = self.diff_text1(diffs)
- elif isinstance(a, basestring) and isinstance(b, list) and c is None:
- # Method 3: text1, diffs
- text1 = a
- diffs = b
- elif (
- isinstance(a, basestring)
- and isinstance(b, basestring)
- and isinstance(c, list)
- ):
- # Method 4: text1, text2, diffs
- # text2 is not used.
- text1 = a
- diffs = c
- else:
- raise ValueError("Unknown call format to patch_make.")
-
- if not diffs:
- return [] # Get rid of the None case.
- patches = []
- patch = patch_obj()
- char_count1 = 0 # Number of characters into the text1 string.
- char_count2 = 0 # Number of characters into the text2 string.
- prepatch_text = text1 # Recreate the patches to determine context info.
- postpatch_text = text1
- for x in xrange(len(diffs)):
- (diff_type, diff_text) = diffs[x]
- if len(patch.diffs) == 0 and diff_type != self.DIFF_EQUAL:
- # A new patch starts here.
- patch.start1 = char_count1
- patch.start2 = char_count2
- if diff_type == self.DIFF_INSERT:
- # Insertion
- patch.diffs.append(diffs[x])
- patch.length2 += len(diff_text)
- postpatch_text = (
- postpatch_text[:char_count2]
- + diff_text
- + postpatch_text[char_count2:]
- )
- elif diff_type == self.DIFF_DELETE:
- # Deletion.
- patch.length1 += len(diff_text)
- patch.diffs.append(diffs[x])
- postpatch_text = (
- postpatch_text[:char_count2]
- + postpatch_text[char_count2 + len(diff_text) :]
- )
- elif (
- diff_type == self.DIFF_EQUAL
- and len(diff_text) <= 2 * self.Patch_Margin
- and len(patch.diffs) != 0
- and len(diffs) != x + 1
- ):
- # Small equality inside a patch.
- patch.diffs.append(diffs[x])
- patch.length1 += len(diff_text)
- patch.length2 += len(diff_text)
-
- if diff_type == self.DIFF_EQUAL and len(diff_text) >= 2 * self.Patch_Margin:
- # Time for a new patch.
- if len(patch.diffs) != 0:
- self.patch_addContext(patch, prepatch_text)
- patches.append(patch)
- patch = patch_obj()
- # Unlike Unidiff, our patch lists have a rolling context.
- # https://github.com/google/diff-match-patch/wiki/Unidiff
- # Update prepatch text & pos to reflect the application of the
- # just completed patch.
- prepatch_text = postpatch_text
- char_count1 = char_count2
-
- # Update the current character count.
- if diff_type != self.DIFF_INSERT:
- char_count1 += len(diff_text)
- if diff_type != self.DIFF_DELETE:
- char_count2 += len(diff_text)
-
- # Pick up the leftover patch if not empty.
- if len(patch.diffs) != 0:
- self.patch_addContext(patch, prepatch_text)
- patches.append(patch)
- return patches
-
- def patch_deepCopy(self, patches):
- """Given an array of patches, return another array that is identical.
-
- Args:
- patches: Array of Patch objects.
-
- Returns:
- Array of Patch objects.
- """
- patchesCopy = []
- for patch in patches:
- patchCopy = patch_obj()
- # No need to deep copy the tuples since they are immutable.
- patchCopy.diffs = patch.diffs[:]
- patchCopy.start1 = patch.start1
- patchCopy.start2 = patch.start2
- patchCopy.length1 = patch.length1
- patchCopy.length2 = patch.length2
- patchesCopy.append(patchCopy)
- return patchesCopy
-
- def patch_apply(self, patches, text):
- """Merge a set of patches onto the text. Return a patched text, as well
- as a list of true/false values indicating which patches were applied.
-
- Args:
- patches: Array of Patch objects.
- text: Old text.
-
- Returns:
- Two element Array, containing the new text and an array of boolean values.
- """
- if not patches:
- return (text, [])
-
- # Deep copy the patches so that no changes are made to originals.
- patches = self.patch_deepCopy(patches)
-
- nullPadding = self.patch_addPadding(patches)
- text = nullPadding + text + nullPadding
- self.patch_splitMax(patches)
-
- # delta keeps track of the offset between the expected and actual location
- # of the previous patch. If there are patches expected at positions 10 and
- # 20, but the first patch was found at 12, delta is 2 and the second patch
- # has an effective expected position of 22.
- delta = 0
- results = []
- for patch in patches:
- expected_loc = patch.start2 + delta
- text1 = self.diff_text1(patch.diffs)
- end_loc = -1
- if len(text1) > self.Match_MaxBits:
- # patch_splitMax will only provide an oversized pattern in the case of
- # a monster delete.
- start_loc = self.match_main(
- text, text1[: self.Match_MaxBits], expected_loc
- )
- if start_loc != -1:
- end_loc = self.match_main(
- text,
- text1[-self.Match_MaxBits :],
- expected_loc + len(text1) - self.Match_MaxBits,
- )
- if end_loc == -1 or start_loc >= end_loc:
- # Can't find valid trailing context. Drop this patch.
- start_loc = -1
- else:
- start_loc = self.match_main(text, text1, expected_loc)
- if start_loc == -1:
- # No match found. :(
- results.append(False)
- # Subtract the delta for this failed patch from subsequent patches.
- delta -= patch.length2 - patch.length1
- else:
- # Found a match. :)
- results.append(True)
- delta = start_loc - expected_loc
- if end_loc == -1:
- text2 = text[start_loc : start_loc + len(text1)]
- else:
- text2 = text[start_loc : end_loc + self.Match_MaxBits]
- if text1 == text2:
- # Perfect match, just shove the replacement text in.
- text = (
- text[:start_loc]
- + self.diff_text2(patch.diffs)
- + text[start_loc + len(text1) :]
- )
- else:
- # Imperfect match.
- # Run a diff to get a framework of equivalent indices.
- diffs = self.diff_main(text1, text2, False)
- if (
- len(text1) > self.Match_MaxBits
- and self.diff_levenshtein(diffs) / float(len(text1))
- > self.Patch_DeleteThreshold
- ):
- # The end points match, but the content is unacceptably bad.
- results[-1] = False
- else:
- self.diff_cleanupSemanticLossless(diffs)
- index1 = 0
- for (op, data) in patch.diffs:
- if op != self.DIFF_EQUAL:
- index2 = self.diff_xIndex(diffs, index1)
- if op == self.DIFF_INSERT: # Insertion
- text = (
- text[: start_loc + index2]
- + data
- + text[start_loc + index2 :]
- )
- elif op == self.DIFF_DELETE: # Deletion
- text = (
- text[: start_loc + index2]
- + text[
- start_loc
- + self.diff_xIndex(diffs, index1 + len(data)) :
- ]
- )
- if op != self.DIFF_DELETE:
- index1 += len(data)
- # Strip the padding off.
- text = text[len(nullPadding) : -len(nullPadding)]
- return (text, results)
-
- def patch_addPadding(self, patches):
- """Add some padding on text start and end so that edges can match
- something. Intended to be called only from within patch_apply.
-
- Args:
- patches: Array of Patch objects.
-
- Returns:
- The padding string added to each side.
- """
- paddingLength = self.Patch_Margin
- nullPadding = ""
- for x in xrange(1, paddingLength + 1):
- nullPadding += chr(x)
-
- # Bump all the patches forward.
- for patch in patches:
- patch.start1 += paddingLength
- patch.start2 += paddingLength
-
- # Add some padding on start of first diff.
- patch = patches[0]
- diffs = patch.diffs
- if not diffs or diffs[0][0] != self.DIFF_EQUAL:
- # Add nullPadding equality.
- diffs.insert(0, (self.DIFF_EQUAL, nullPadding))
- patch.start1 -= paddingLength # Should be 0.
- patch.start2 -= paddingLength # Should be 0.
- patch.length1 += paddingLength
- patch.length2 += paddingLength
- elif paddingLength > len(diffs[0][1]):
- # Grow first equality.
- extraLength = paddingLength - len(diffs[0][1])
- newText = nullPadding[len(diffs[0][1]) :] + diffs[0][1]
- diffs[0] = (diffs[0][0], newText)
- patch.start1 -= extraLength
- patch.start2 -= extraLength
- patch.length1 += extraLength
- patch.length2 += extraLength
-
- # Add some padding on end of last diff.
- patch = patches[-1]
- diffs = patch.diffs
- if not diffs or diffs[-1][0] != self.DIFF_EQUAL:
- # Add nullPadding equality.
- diffs.append((self.DIFF_EQUAL, nullPadding))
- patch.length1 += paddingLength
- patch.length2 += paddingLength
- elif paddingLength > len(diffs[-1][1]):
- # Grow last equality.
- extraLength = paddingLength - len(diffs[-1][1])
- newText = diffs[-1][1] + nullPadding[:extraLength]
- diffs[-1] = (diffs[-1][0], newText)
- patch.length1 += extraLength
- patch.length2 += extraLength
-
- return nullPadding
-
- def patch_splitMax(self, patches):
- """Look through the patches and break up any which are longer than the
- maximum limit of the match algorithm.
- Intended to be called only from within patch_apply.
-
- Args:
- patches: Array of Patch objects.
- """
- patch_size = self.Match_MaxBits
- if patch_size == 0:
- # Python has the option of not splitting strings due to its ability
- # to handle integers of arbitrary precision.
- return
- for x in xrange(len(patches)):
- if patches[x].length1 <= patch_size:
- continue
- bigpatch = patches[x]
- # Remove the big old patch.
- del patches[x]
- x -= 1
- start1 = bigpatch.start1
- start2 = bigpatch.start2
- precontext = ""
- while len(bigpatch.diffs) != 0:
- # Create one of several smaller patches.
- patch = patch_obj()
- empty = True
- patch.start1 = start1 - len(precontext)
- patch.start2 = start2 - len(precontext)
- if precontext:
- patch.length1 = patch.length2 = len(precontext)
- patch.diffs.append((self.DIFF_EQUAL, precontext))
-
- while (
- len(bigpatch.diffs) != 0
- and patch.length1 < patch_size - self.Patch_Margin
- ):
- (diff_type, diff_text) = bigpatch.diffs[0]
- if diff_type == self.DIFF_INSERT:
- # Insertions are harmless.
- patch.length2 += len(diff_text)
- start2 += len(diff_text)
- patch.diffs.append(bigpatch.diffs.pop(0))
- empty = False
- elif (
- diff_type == self.DIFF_DELETE
- and len(patch.diffs) == 1
- and patch.diffs[0][0] == self.DIFF_EQUAL
- and len(diff_text) > 2 * patch_size
- ):
- # This is a large deletion. Let it pass in one chunk.
- patch.length1 += len(diff_text)
- start1 += len(diff_text)
- empty = False
- patch.diffs.append((diff_type, diff_text))
- del bigpatch.diffs[0]
- else:
- # Deletion or equality. Only take as much as we can stomach.
- diff_text = diff_text[
- : patch_size - patch.length1 - self.Patch_Margin
- ]
- patch.length1 += len(diff_text)
- start1 += len(diff_text)
- if diff_type == self.DIFF_EQUAL:
- patch.length2 += len(diff_text)
- start2 += len(diff_text)
- else:
- empty = False
-
- patch.diffs.append((diff_type, diff_text))
- if diff_text == bigpatch.diffs[0][1]:
- del bigpatch.diffs[0]
- else:
- bigpatch.diffs[0] = (
- bigpatch.diffs[0][0],
- bigpatch.diffs[0][1][len(diff_text) :],
- )
-
- # Compute the head context for the next patch.
- precontext = self.diff_text2(patch.diffs)
- precontext = precontext[-self.Patch_Margin :]
- # Append the end context for this patch.
- postcontext = self.diff_text1(bigpatch.diffs)[: self.Patch_Margin]
- if postcontext:
- patch.length1 += len(postcontext)
- patch.length2 += len(postcontext)
- if len(patch.diffs) != 0 and patch.diffs[-1][0] == self.DIFF_EQUAL:
- patch.diffs[-1] = (
- self.DIFF_EQUAL,
- patch.diffs[-1][1] + postcontext,
- )
- else:
- patch.diffs.append((self.DIFF_EQUAL, postcontext))
-
- if not empty:
- x += 1
- patches.insert(x, patch)
-
- def patch_toText(self, patches):
- """Take a list of patches and return a textual representation.
-
- Args:
- patches: Array of Patch objects.
-
- Returns:
- Text representation of patches.
- """
- text = []
- for patch in patches:
- text.append(str(patch))
- return "".join(text)
-
- def patch_fromText(self, textline):
- """Parse a textual representation of patches and return a list of patch
- objects.
-
- Args:
- textline: Text representation of patches.
-
- Returns:
- Array of Patch objects.
-
- Raises:
- ValueError: If invalid input.
- """
- if type(textline) == unicode:
- # Patches should be composed of a subset of ascii chars, Unicode not
- # required. If this encode raises UnicodeEncodeError, patch is invalid.
- textline = textline.encode("ascii")
- patches = []
- if not textline:
- return patches
- text = textline.split("\n")
- while len(text) != 0:
- m = re.match("^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$", text[0])
- if not m:
- raise ValueError("Invalid patch string: " + text[0])
- patch = patch_obj()
- patches.append(patch)
- patch.start1 = int(m.group(1))
- if m.group(2) == "":
- patch.start1 -= 1
- patch.length1 = 1
- elif m.group(2) == "0":
- patch.length1 = 0
- else:
- patch.start1 -= 1
- patch.length1 = int(m.group(2))
-
- patch.start2 = int(m.group(3))
- if m.group(4) == "":
- patch.start2 -= 1
- patch.length2 = 1
- elif m.group(4) == "0":
- patch.length2 = 0
- else:
- patch.start2 -= 1
- patch.length2 = int(m.group(4))
-
- del text[0]
-
- while len(text) != 0:
- if text[0]:
- sign = text[0][0]
- else:
- sign = ""
- line = urllib.unquote(text[0][1:])
- line = line.decode("utf-8")
- if sign == "+":
- # Insertion.
- patch.diffs.append((self.DIFF_INSERT, line))
- elif sign == "-":
- # Deletion.
- patch.diffs.append((self.DIFF_DELETE, line))
- elif sign == " ":
- # Minor equality.
- patch.diffs.append((self.DIFF_EQUAL, line))
- elif sign == "@":
- # Start of next patch.
- break
- elif sign == "":
- # Blank line? Whatever.
- pass
- else:
- # WTF?
- raise ValueError("Invalid patch mode: '%s'\n%s" % (sign, line))
- del text[0]
- return patches
-
-
-class patch_obj:
- """Class representing one patch operation.
- """
-
- def __init__(self):
- """Initializes with an empty list of diffs.
- """
- self.diffs = []
- self.start1 = None
- self.start2 = None
- self.length1 = 0
- self.length2 = 0
-
- def __str__(self):
- """Emulate GNU diff's format.
- Header: @@ -382,8 +481,9 @@
- Indices are printed as 1-based, not 0-based.
-
- Returns:
- The GNU diff string.
- """
- if self.length1 == 0:
- coords1 = str(self.start1) + ",0"
- elif self.length1 == 1:
- coords1 = str(self.start1 + 1)
- else:
- coords1 = str(self.start1 + 1) + "," + str(self.length1)
- if self.length2 == 0:
- coords2 = str(self.start2) + ",0"
- elif self.length2 == 1:
- coords2 = str(self.start2 + 1)
- else:
- coords2 = str(self.start2 + 1) + "," + str(self.length2)
- text = ["@@ -", coords1, " +", coords2, " @@\n"]
- # Escape the body of the patch with %xx notation.
- for (op, data) in self.diffs:
- if op == diff_match_patch.DIFF_INSERT:
- text.append("+")
- elif op == diff_match_patch.DIFF_DELETE:
- text.append("-")
- elif op == diff_match_patch.DIFF_EQUAL:
- text.append(" ")
- # High ascii will raise UnicodeDecodeError. Use Unicode instead.
- data = data.encode("utf-8")
- text.append(urllib.quote(data, "!~*'();/?:@&=+$,# ") + "\n")
- return "".join(text)
diff --git a/contrib/python/diff-match-patch/py2/ya.make b/contrib/python/diff-match-patch/py2/ya.make
deleted file mode 100644
index 7dfc13ebe4..0000000000
--- a/contrib/python/diff-match-patch/py2/ya.make
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY2_LIBRARY()
-
-VERSION(20200713)
-
-LICENSE(Apache-2.0)
-
-NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- diff_match_patch/__init__.py
- diff_match_patch/diff_match_patch_py2.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/diff-match-patch/py2/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
-END()
-
-RECURSE_FOR_TESTS(
- tests
-)
diff --git a/contrib/python/diff-match-patch/py3/.dist-info/METADATA b/contrib/python/diff-match-patch/py3/.dist-info/METADATA
deleted file mode 100644
index eecf8db23f..0000000000
--- a/contrib/python/diff-match-patch/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,108 +0,0 @@
-Metadata-Version: 2.1
-Name: diff-match-patch
-Version: 20230430
-Summary: Diff Match and Patch
-Author-email: Neil Fraser <fraser@google.com>
-Maintainer-email: Amethyst Reese <amethyst@n7.gg>
-Requires-Python: >=3.7
-Description-Content-Type: text/markdown
-Classifier: Development Status :: 6 - Mature
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Text Processing
-Requires-Dist: attribution==1.6.2 ; extra == "dev"
-Requires-Dist: black==23.3.0 ; extra == "dev"
-Requires-Dist: flit==3.8.0 ; extra == "dev"
-Requires-Dist: mypy==1.2.0 ; extra == "dev"
-Requires-Dist: ufmt==2.1.0 ; extra == "dev"
-Requires-Dist: usort==1.0.6 ; extra == "dev"
-Project-URL: Changelog, https://github.com/diff-match-patch-python/diff-match-patch/blob/main/CHANGELOG.md
-Project-URL: Github, https://github.com/diff-match-patch-python/diff-match-patch
-Provides-Extra: dev
-
-# diff-match-patch
-
-Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
-
-[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
-[![changelog](https://img.shields.io/badge/change-log-blue)](https://github.com/diff-match-patch-python/diff-match-patch/blob/main/CHANGELOG.md)
-[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
-
-## Install
-
-diff-match-patch is supported on Python 3.7 or newer.
-You can install it from PyPI:
-
-```shell
-python -m pip install diff-match-patch
-```
-
-## Usage
-
-Generating a patchset (analogous to unified diff) between two texts:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_make(text1, text2)
-diff = dmp.patch_toText(patches)
-```
-
-Applying a patchset to a text can then be done with:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_fromText(diff)
-new_text, _ = dmp.patch_apply(patches, text)
-```
-
-## Original README
-The Diff Match and Patch libraries offer robust algorithms to perform the
-operations required for synchronizing plain text.
-
-1. Diff:
- * Compare two blocks of plain text and efficiently return a list of differences.
- * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
-2. Match:
- * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
- * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
-3. Patch:
- * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
- * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
-
-Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
-
-### Reference
-
-* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
-* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
-* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
-* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
-* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
-
-### Languages
-Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
-
-* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
-* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
-* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
-* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
-* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
-* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
-* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
-* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
-
-A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
-
-### Algorithms
-This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
-
-This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
-
-[DMP]: https://github.com/google/diff-match-patch
-[API]: https://github.com/google/diff-match-patch/wiki/API
-
diff --git a/contrib/python/diff-match-patch/py3/.dist-info/top_level.txt b/contrib/python/diff-match-patch/py3/.dist-info/top_level.txt
deleted file mode 100644
index 63904d71d9..0000000000
--- a/contrib/python/diff-match-patch/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-diff_match_patch
diff --git a/contrib/python/diff-match-patch/py3/AUTHORS b/contrib/python/diff-match-patch/py3/AUTHORS
deleted file mode 100644
index c82809e726..0000000000
--- a/contrib/python/diff-match-patch/py3/AUTHORS
+++ /dev/null
@@ -1,10 +0,0 @@
-# Below is a list of people and organizations that have contributed
-# to the Diff Match Patch project.
-
-Google Inc.
-
-Duncan Cross <duncan.cross@gmail.com> (Lua port)
-Jan Weiß <jan@geheimwerk.de> (Objective C port)
-Matthaeus G. Chajdas <anteru@developer.shelter13.net> (C# port)
-Mike Slemmer <mikeslemmer@gmail.com> (C++ port)
-
diff --git a/contrib/python/diff-match-patch/py3/LICENSE b/contrib/python/diff-match-patch/py3/LICENSE
deleted file mode 100644
index d645695673..0000000000
--- a/contrib/python/diff-match-patch/py3/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/contrib/python/diff-match-patch/py3/README.md b/contrib/python/diff-match-patch/py3/README.md
deleted file mode 100644
index bdcd2a46e7..0000000000
--- a/contrib/python/diff-match-patch/py3/README.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# diff-match-patch
-
-Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
-
-[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
-[![changelog](https://img.shields.io/badge/change-log-blue)](https://github.com/diff-match-patch-python/diff-match-patch/blob/main/CHANGELOG.md)
-[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
-
-## Install
-
-diff-match-patch is supported on Python 3.7 or newer.
-You can install it from PyPI:
-
-```shell
-python -m pip install diff-match-patch
-```
-
-## Usage
-
-Generating a patchset (analogous to unified diff) between two texts:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_make(text1, text2)
-diff = dmp.patch_toText(patches)
-```
-
-Applying a patchset to a text can then be done with:
-
-```python
-from diff_match_patch import diff_match_patch
-
-dmp = diff_match_patch()
-patches = dmp.patch_fromText(diff)
-new_text, _ = dmp.patch_apply(patches, text)
-```
-
-## Original README
-The Diff Match and Patch libraries offer robust algorithms to perform the
-operations required for synchronizing plain text.
-
-1. Diff:
- * Compare two blocks of plain text and efficiently return a list of differences.
- * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
-2. Match:
- * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
- * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
-3. Patch:
- * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
- * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
-
-Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
-
-### Reference
-
-* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
-* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
-* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
-* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
-* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
-
-### Languages
-Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
-
-* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
-* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
-* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
-* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
-* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
-* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
-* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
-* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
-
-A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
-
-### Algorithms
-This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
-
-This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
-
-[DMP]: https://github.com/google/diff-match-patch
-[API]: https://github.com/google/diff-match-patch/wiki/API
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py b/contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py
deleted file mode 100644
index 18ac58aadb..0000000000
--- a/contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-Repackaging of Google's Diff Match and Patch libraries.
-
-Offers robust algorithms to perform the operations required for synchronizing plain text.
-"""
-
-from .__version__ import __version__
-from .diff_match_patch import __author__, __doc__, diff_match_patch, patch_obj
-
-__packager__ = "Amethyst Reese (amy@noswap.com)"
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py b/contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py
deleted file mode 100644
index 1e429654a2..0000000000
--- a/contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-"""
-This file is automatically generated by attribution.
-
-Do not edit manually. Get more info at https://attribution.omnilib.dev
-"""
-
-__version__ = "20230430"
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py b/contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py
deleted file mode 100644
index 683f9487f2..0000000000
--- a/contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py
+++ /dev/null
@@ -1,2019 +0,0 @@
-#!/usr/bin/python3
-
-"""Diff Match and Patch
-Copyright 2018 The diff-match-patch Authors.
-https://github.com/google/diff-match-patch
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-"""Functions for diff, match and patch.
-
-Computes the difference between two texts to create a patch.
-Applies the patch onto another text, allowing for errors.
-"""
-
-__author__ = "fraser@google.com (Neil Fraser)"
-
-import re
-import sys
-import time
-import urllib.parse
-
-
-class diff_match_patch:
- """Class containing the diff, match and patch methods.
-
- Also contains the behaviour settings.
- """
-
- def __init__(self):
- """Inits a diff_match_patch object with default settings.
- Redefine these in your program to override the defaults.
- """
-
- # Number of seconds to map a diff before giving up (0 for infinity).
- self.Diff_Timeout = 1.0
- # Cost of an empty edit operation in terms of edit characters.
- self.Diff_EditCost = 4
- # At what point is no match declared (0.0 = perfection, 1.0 = very loose).
- self.Match_Threshold = 0.5
- # How far to search for a match (0 = exact location, 1000+ = broad match).
- # A match this many characters away from the expected location will add
- # 1.0 to the score (0.0 is a perfect match).
- self.Match_Distance = 1000
- # When deleting a large block of text (over ~64 characters), how close do
- # the contents have to be to match the expected contents. (0.0 = perfection,
- # 1.0 = very loose). Note that Match_Threshold controls how closely the
- # end points of a delete need to match.
- self.Patch_DeleteThreshold = 0.5
- # Chunk size for context length.
- self.Patch_Margin = 4
-
- # The number of bits in an int.
- # Python has no maximum, thus to disable patch splitting set to 0.
- # However to avoid long patches in certain pathological cases, use 32.
- # Multiple short patches (using native ints) are much faster than long ones.
- self.Match_MaxBits = 32
-
- # DIFF FUNCTIONS
-
- # The data structure representing a diff is an array of tuples:
- # [(DIFF_DELETE, "Hello"), (DIFF_INSERT, "Goodbye"), (DIFF_EQUAL, " world.")]
- # which means: delete "Hello", add "Goodbye" and keep " world."
- DIFF_DELETE = -1
- DIFF_INSERT = 1
- DIFF_EQUAL = 0
-
- def diff_main(self, text1, text2, checklines=True, deadline=None):
- """Find the differences between two texts. Simplifies the problem by
- stripping any common prefix or suffix off the texts before diffing.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- checklines: Optional speedup flag. If present and false, then don't run
- a line-level diff first to identify the changed areas.
- Defaults to true, which does a faster, slightly less optimal diff.
- deadline: Optional time when the diff should be complete by. Used
- internally for recursive calls. Users should set DiffTimeout instead.
-
- Returns:
- Array of changes.
- """
- # Set a deadline by which time the diff must be complete.
- if deadline == None:
- # Unlike in most languages, Python counts time in seconds.
- if self.Diff_Timeout <= 0:
- deadline = sys.maxsize
- else:
- deadline = time.time() + self.Diff_Timeout
-
- # Check for null inputs.
- if text1 == None or text2 == None:
- raise ValueError("Null inputs. (diff_main)")
-
- # Check for equality (speedup).
- if text1 == text2:
- if text1:
- return [(self.DIFF_EQUAL, text1)]
- return []
-
- # Trim off common prefix (speedup).
- commonlength = self.diff_commonPrefix(text1, text2)
- commonprefix = text1[:commonlength]
- text1 = text1[commonlength:]
- text2 = text2[commonlength:]
-
- # Trim off common suffix (speedup).
- commonlength = self.diff_commonSuffix(text1, text2)
- if commonlength == 0:
- commonsuffix = ""
- else:
- commonsuffix = text1[-commonlength:]
- text1 = text1[:-commonlength]
- text2 = text2[:-commonlength]
-
- # Compute the diff on the middle block.
- diffs = self.diff_compute(text1, text2, checklines, deadline)
-
- # Restore the prefix and suffix.
- if commonprefix:
- diffs[:0] = [(self.DIFF_EQUAL, commonprefix)]
- if commonsuffix:
- diffs.append((self.DIFF_EQUAL, commonsuffix))
- self.diff_cleanupMerge(diffs)
- return diffs
-
- def diff_compute(self, text1, text2, checklines, deadline):
- """Find the differences between two texts. Assumes that the texts do not
- have any common prefix or suffix.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- checklines: Speedup flag. If false, then don't run a line-level diff
- first to identify the changed areas.
- If true, then run a faster, slightly less optimal diff.
- deadline: Time when the diff should be complete by.
-
- Returns:
- Array of changes.
- """
- if not text1:
- # Just add some text (speedup).
- return [(self.DIFF_INSERT, text2)]
-
- if not text2:
- # Just delete some text (speedup).
- return [(self.DIFF_DELETE, text1)]
-
- if len(text1) > len(text2):
- (longtext, shorttext) = (text1, text2)
- else:
- (shorttext, longtext) = (text1, text2)
- i = longtext.find(shorttext)
- if i != -1:
- # Shorter text is inside the longer text (speedup).
- diffs = [
- (self.DIFF_INSERT, longtext[:i]),
- (self.DIFF_EQUAL, shorttext),
- (self.DIFF_INSERT, longtext[i + len(shorttext) :]),
- ]
- # Swap insertions for deletions if diff is reversed.
- if len(text1) > len(text2):
- diffs[0] = (self.DIFF_DELETE, diffs[0][1])
- diffs[2] = (self.DIFF_DELETE, diffs[2][1])
- return diffs
-
- if len(shorttext) == 1:
- # Single character string.
- # After the previous speedup, the character can't be an equality.
- return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
-
- # Check to see if the problem can be split in two.
- hm = self.diff_halfMatch(text1, text2)
- if hm:
- # A half-match was found, sort out the return data.
- (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
- # Send both pairs off for separate processing.
- diffs_a = self.diff_main(text1_a, text2_a, checklines, deadline)
- diffs_b = self.diff_main(text1_b, text2_b, checklines, deadline)
- # Merge the results.
- return diffs_a + [(self.DIFF_EQUAL, mid_common)] + diffs_b
-
- if checklines and len(text1) > 100 and len(text2) > 100:
- return self.diff_lineMode(text1, text2, deadline)
-
- return self.diff_bisect(text1, text2, deadline)
-
- def diff_lineMode(self, text1, text2, deadline):
- """Do a quick line-level diff on both strings, then rediff the parts for
- greater accuracy.
- This speedup can produce non-minimal diffs.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- deadline: Time when the diff should be complete by.
-
- Returns:
- Array of changes.
- """
-
- # Scan the text on a line-by-line basis first.
- (text1, text2, linearray) = self.diff_linesToChars(text1, text2)
-
- diffs = self.diff_main(text1, text2, False, deadline)
-
- # Convert the diff back to original text.
- self.diff_charsToLines(diffs, linearray)
- # Eliminate freak matches (e.g. blank lines)
- self.diff_cleanupSemantic(diffs)
-
- # Rediff any replacement blocks, this time character-by-character.
- # Add a dummy entry at the end.
- diffs.append((self.DIFF_EQUAL, ""))
- pointer = 0
- count_delete = 0
- count_insert = 0
- text_delete = ""
- text_insert = ""
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_INSERT:
- count_insert += 1
- text_insert += diffs[pointer][1]
- elif diffs[pointer][0] == self.DIFF_DELETE:
- count_delete += 1
- text_delete += diffs[pointer][1]
- elif diffs[pointer][0] == self.DIFF_EQUAL:
- # Upon reaching an equality, check for prior redundancies.
- if count_delete >= 1 and count_insert >= 1:
- # Delete the offending records and add the merged ones.
- subDiff = self.diff_main(text_delete, text_insert, False, deadline)
- diffs[pointer - count_delete - count_insert : pointer] = subDiff
- pointer = pointer - count_delete - count_insert + len(subDiff)
- count_insert = 0
- count_delete = 0
- text_delete = ""
- text_insert = ""
-
- pointer += 1
-
- diffs.pop() # Remove the dummy entry at the end.
-
- return diffs
-
- def diff_bisect(self, text1, text2, deadline):
- """Find the 'middle snake' of a diff, split the problem in two
- and return the recursively constructed diff.
- See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- deadline: Time at which to bail if not yet complete.
-
- Returns:
- Array of diff tuples.
- """
-
- # Cache the text lengths to prevent multiple calls.
- text1_length = len(text1)
- text2_length = len(text2)
- max_d = (text1_length + text2_length + 1) // 2
- v_offset = max_d
- v_length = 2 * max_d
- v1 = [-1] * v_length
- v1[v_offset + 1] = 0
- v2 = v1[:]
- delta = text1_length - text2_length
- # If the total number of characters is odd, then the front path will
- # collide with the reverse path.
- front = delta % 2 != 0
- # Offsets for start and end of k loop.
- # Prevents mapping of space beyond the grid.
- k1start = 0
- k1end = 0
- k2start = 0
- k2end = 0
- for d in range(max_d):
- # Bail out if deadline is reached.
- if time.time() > deadline:
- break
-
- # Walk the front path one step.
- for k1 in range(-d + k1start, d + 1 - k1end, 2):
- k1_offset = v_offset + k1
- if k1 == -d or (k1 != d and v1[k1_offset - 1] < v1[k1_offset + 1]):
- x1 = v1[k1_offset + 1]
- else:
- x1 = v1[k1_offset - 1] + 1
- y1 = x1 - k1
- while (
- x1 < text1_length and y1 < text2_length and text1[x1] == text2[y1]
- ):
- x1 += 1
- y1 += 1
- v1[k1_offset] = x1
- if x1 > text1_length:
- # Ran off the right of the graph.
- k1end += 2
- elif y1 > text2_length:
- # Ran off the bottom of the graph.
- k1start += 2
- elif front:
- k2_offset = v_offset + delta - k1
- if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] != -1:
- # Mirror x2 onto top-left coordinate system.
- x2 = text1_length - v2[k2_offset]
- if x1 >= x2:
- # Overlap detected.
- return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
-
- # Walk the reverse path one step.
- for k2 in range(-d + k2start, d + 1 - k2end, 2):
- k2_offset = v_offset + k2
- if k2 == -d or (k2 != d and v2[k2_offset - 1] < v2[k2_offset + 1]):
- x2 = v2[k2_offset + 1]
- else:
- x2 = v2[k2_offset - 1] + 1
- y2 = x2 - k2
- while (
- x2 < text1_length
- and y2 < text2_length
- and text1[-x2 - 1] == text2[-y2 - 1]
- ):
- x2 += 1
- y2 += 1
- v2[k2_offset] = x2
- if x2 > text1_length:
- # Ran off the left of the graph.
- k2end += 2
- elif y2 > text2_length:
- # Ran off the top of the graph.
- k2start += 2
- elif not front:
- k1_offset = v_offset + delta - k2
- if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] != -1:
- x1 = v1[k1_offset]
- y1 = v_offset + x1 - k1_offset
- # Mirror x2 onto top-left coordinate system.
- x2 = text1_length - x2
- if x1 >= x2:
- # Overlap detected.
- return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
-
- # Diff took too long and hit the deadline or
- # number of diffs equals number of characters, no commonality at all.
- return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
-
- def diff_bisectSplit(self, text1, text2, x, y, deadline):
- """Given the location of the 'middle snake', split the diff in two parts
- and recurse.
-
- Args:
- text1: Old string to be diffed.
- text2: New string to be diffed.
- x: Index of split point in text1.
- y: Index of split point in text2.
- deadline: Time at which to bail if not yet complete.
-
- Returns:
- Array of diff tuples.
- """
- text1a = text1[:x]
- text2a = text2[:y]
- text1b = text1[x:]
- text2b = text2[y:]
-
- # Compute both diffs serially.
- diffs = self.diff_main(text1a, text2a, False, deadline)
- diffsb = self.diff_main(text1b, text2b, False, deadline)
-
- return diffs + diffsb
-
- def diff_linesToChars(self, text1, text2):
- """Split two texts into an array of strings. Reduce the texts to a string
- of hashes where each Unicode character represents one line.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- Three element tuple, containing the encoded text1, the encoded text2 and
- the array of unique strings. The zeroth element of the array of unique
- strings is intentionally blank.
- """
- lineArray = [] # e.g. lineArray[4] == "Hello\n"
- lineHash = {} # e.g. lineHash["Hello\n"] == 4
-
- # "\x00" is a valid character, but various debuggers don't like it.
- # So we'll insert a junk entry to avoid generating a null character.
- lineArray.append("")
-
- def diff_linesToCharsMunge(text):
- """Split a text into an array of strings. Reduce the texts to a string
- of hashes where each Unicode character represents one line.
- Modifies linearray and linehash through being a closure.
-
- Args:
- text: String to encode.
-
- Returns:
- Encoded string.
- """
- chars = []
- # Walk the text, pulling out a substring for each line.
- # text.split('\n') would would temporarily double our memory footprint.
- # Modifying text would create many large strings to garbage collect.
- lineStart = 0
- lineEnd = -1
- while lineEnd < len(text) - 1:
- lineEnd = text.find("\n", lineStart)
- if lineEnd == -1:
- lineEnd = len(text) - 1
- line = text[lineStart : lineEnd + 1]
-
- if line in lineHash:
- chars.append(chr(lineHash[line]))
- else:
- if len(lineArray) == maxLines:
- # Bail out at 1114111 because chr(1114112) throws.
- line = text[lineStart:]
- lineEnd = len(text)
- lineArray.append(line)
- lineHash[line] = len(lineArray) - 1
- chars.append(chr(len(lineArray) - 1))
- lineStart = lineEnd + 1
- return "".join(chars)
-
- # Allocate 2/3rds of the space for text1, the rest for text2.
- maxLines = 666666
- chars1 = diff_linesToCharsMunge(text1)
- maxLines = 1114111
- chars2 = diff_linesToCharsMunge(text2)
- return (chars1, chars2, lineArray)
-
- def diff_charsToLines(self, diffs, lineArray):
- """Rehydrate the text in a diff from a string of line hashes to real lines
- of text.
-
- Args:
- diffs: Array of diff tuples.
- lineArray: Array of unique strings.
- """
- for i in range(len(diffs)):
- text = []
- for char in diffs[i][1]:
- text.append(lineArray[ord(char)])
- diffs[i] = (diffs[i][0], "".join(text))
-
- def diff_commonPrefix(self, text1, text2):
- """Determine the common prefix of two strings.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- The number of characters common to the start of each string.
- """
- # Quick check for common null cases.
- if not text1 or not text2 or text1[0] != text2[0]:
- return 0
- # Binary search.
- # Performance analysis: https://neil.fraser.name/news/2007/10/09/
- pointermin = 0
- pointermax = min(len(text1), len(text2))
- pointermid = pointermax
- pointerstart = 0
- while pointermin < pointermid:
- if text1[pointerstart:pointermid] == text2[pointerstart:pointermid]:
- pointermin = pointermid
- pointerstart = pointermin
- else:
- pointermax = pointermid
- pointermid = (pointermax - pointermin) // 2 + pointermin
- return pointermid
-
- def diff_commonSuffix(self, text1, text2):
- """Determine the common suffix of two strings.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- The number of characters common to the end of each string.
- """
- # Quick check for common null cases.
- if not text1 or not text2 or text1[-1] != text2[-1]:
- return 0
- # Binary search.
- # Performance analysis: https://neil.fraser.name/news/2007/10/09/
- pointermin = 0
- pointermax = min(len(text1), len(text2))
- pointermid = pointermax
- pointerend = 0
- while pointermin < pointermid:
- if (
- text1[-pointermid : len(text1) - pointerend]
- == text2[-pointermid : len(text2) - pointerend]
- ):
- pointermin = pointermid
- pointerend = pointermin
- else:
- pointermax = pointermid
- pointermid = (pointermax - pointermin) // 2 + pointermin
- return pointermid
-
- def diff_commonOverlap(self, text1, text2):
- """Determine if the suffix of one string is the prefix of another.
-
- Args:
- text1 First string.
- text2 Second string.
-
- Returns:
- The number of characters common to the end of the first
- string and the start of the second string.
- """
- # Cache the text lengths to prevent multiple calls.
- text1_length = len(text1)
- text2_length = len(text2)
- # Eliminate the null case.
- if text1_length == 0 or text2_length == 0:
- return 0
- # Truncate the longer string.
- if text1_length > text2_length:
- text1 = text1[-text2_length:]
- elif text1_length < text2_length:
- text2 = text2[:text1_length]
- text_length = min(text1_length, text2_length)
- # Quick check for the worst case.
- if text1 == text2:
- return text_length
-
- # Start by looking for a single character match
- # and increase length until no match is found.
- # Performance analysis: https://neil.fraser.name/news/2010/11/04/
- best = 0
- length = 1
- while True:
- pattern = text1[-length:]
- found = text2.find(pattern)
- if found == -1:
- return best
- length += found
- if found == 0 or text1[-length:] == text2[:length]:
- best = length
- length += 1
-
- def diff_halfMatch(self, text1, text2):
- """Do the two texts share a substring which is at least half the length of
- the longer text?
- This speedup can produce non-minimal diffs.
-
- Args:
- text1: First string.
- text2: Second string.
-
- Returns:
- Five element Array, containing the prefix of text1, the suffix of text1,
- the prefix of text2, the suffix of text2 and the common middle. Or None
- if there was no match.
- """
- if self.Diff_Timeout <= 0:
- # Don't risk returning a non-optimal diff if we have unlimited time.
- return None
- if len(text1) > len(text2):
- (longtext, shorttext) = (text1, text2)
- else:
- (shorttext, longtext) = (text1, text2)
- if len(longtext) < 4 or len(shorttext) * 2 < len(longtext):
- return None # Pointless.
-
- def diff_halfMatchI(longtext, shorttext, i):
- """Does a substring of shorttext exist within longtext such that the
- substring is at least half the length of longtext?
- Closure, but does not reference any external variables.
-
- Args:
- longtext: Longer string.
- shorttext: Shorter string.
- i: Start index of quarter length substring within longtext.
-
- Returns:
- Five element Array, containing the prefix of longtext, the suffix of
- longtext, the prefix of shorttext, the suffix of shorttext and the
- common middle. Or None if there was no match.
- """
- seed = longtext[i : i + len(longtext) // 4]
- best_common = ""
- j = shorttext.find(seed)
- while j != -1:
- prefixLength = self.diff_commonPrefix(longtext[i:], shorttext[j:])
- suffixLength = self.diff_commonSuffix(longtext[:i], shorttext[:j])
- if len(best_common) < suffixLength + prefixLength:
- best_common = (
- shorttext[j - suffixLength : j]
- + shorttext[j : j + prefixLength]
- )
- best_longtext_a = longtext[: i - suffixLength]
- best_longtext_b = longtext[i + prefixLength :]
- best_shorttext_a = shorttext[: j - suffixLength]
- best_shorttext_b = shorttext[j + prefixLength :]
- j = shorttext.find(seed, j + 1)
-
- if len(best_common) * 2 >= len(longtext):
- return (
- best_longtext_a,
- best_longtext_b,
- best_shorttext_a,
- best_shorttext_b,
- best_common,
- )
- else:
- return None
-
- # First check if the second quarter is the seed for a half-match.
- hm1 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 3) // 4)
- # Check again based on the third quarter.
- hm2 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 1) // 2)
- if not hm1 and not hm2:
- return None
- elif not hm2:
- hm = hm1
- elif not hm1:
- hm = hm2
- else:
- # Both matched. Select the longest.
- if len(hm1[4]) > len(hm2[4]):
- hm = hm1
- else:
- hm = hm2
-
- # A half-match was found, sort out the return data.
- if len(text1) > len(text2):
- (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
- else:
- (text2_a, text2_b, text1_a, text1_b, mid_common) = hm
- return (text1_a, text1_b, text2_a, text2_b, mid_common)
-
- def diff_cleanupSemantic(self, diffs):
- """Reduce the number of edits by eliminating semantically trivial
- equalities.
-
- Args:
- diffs: Array of diff tuples.
- """
- changes = False
- equalities = [] # Stack of indices where equalities are found.
- lastEquality = None # Always equal to diffs[equalities[-1]][1]
- pointer = 0 # Index of current position.
- # Number of chars that changed prior to the equality.
- length_insertions1, length_deletions1 = 0, 0
- # Number of chars that changed after the equality.
- length_insertions2, length_deletions2 = 0, 0
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
- equalities.append(pointer)
- length_insertions1, length_insertions2 = length_insertions2, 0
- length_deletions1, length_deletions2 = length_deletions2, 0
- lastEquality = diffs[pointer][1]
- else: # An insertion or deletion.
- if diffs[pointer][0] == self.DIFF_INSERT:
- length_insertions2 += len(diffs[pointer][1])
- else:
- length_deletions2 += len(diffs[pointer][1])
- # Eliminate an equality that is smaller or equal to the edits on both
- # sides of it.
- if (
- lastEquality
- and (
- len(lastEquality) <= max(length_insertions1, length_deletions1)
- )
- and (
- len(lastEquality) <= max(length_insertions2, length_deletions2)
- )
- ):
- # Duplicate record.
- diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
- # Change second copy to insert.
- diffs[equalities[-1] + 1] = (
- self.DIFF_INSERT,
- diffs[equalities[-1] + 1][1],
- )
- # Throw away the equality we just deleted.
- equalities.pop()
- # Throw away the previous equality (it needs to be reevaluated).
- if len(equalities):
- equalities.pop()
- if len(equalities):
- pointer = equalities[-1]
- else:
- pointer = -1
- # Reset the counters.
- length_insertions1, length_deletions1 = 0, 0
- length_insertions2, length_deletions2 = 0, 0
- lastEquality = None
- changes = True
- pointer += 1
-
- # Normalize the diff.
- if changes:
- self.diff_cleanupMerge(diffs)
- self.diff_cleanupSemanticLossless(diffs)
-
- # Find any overlaps between deletions and insertions.
- # e.g: <del>abcxxx</del><ins>xxxdef</ins>
- # -> <del>abc</del>xxx<ins>def</ins>
- # e.g: <del>xxxabc</del><ins>defxxx</ins>
- # -> <ins>def</ins>xxx<del>abc</del>
- # Only extract an overlap if it is as big as the edit ahead or behind it.
- pointer = 1
- while pointer < len(diffs):
- if (
- diffs[pointer - 1][0] == self.DIFF_DELETE
- and diffs[pointer][0] == self.DIFF_INSERT
- ):
- deletion = diffs[pointer - 1][1]
- insertion = diffs[pointer][1]
- overlap_length1 = self.diff_commonOverlap(deletion, insertion)
- overlap_length2 = self.diff_commonOverlap(insertion, deletion)
- if overlap_length1 >= overlap_length2:
- if (
- overlap_length1 >= len(deletion) / 2.0
- or overlap_length1 >= len(insertion) / 2.0
- ):
- # Overlap found. Insert an equality and trim the surrounding edits.
- diffs.insert(
- pointer, (self.DIFF_EQUAL, insertion[:overlap_length1])
- )
- diffs[pointer - 1] = (
- self.DIFF_DELETE,
- deletion[: len(deletion) - overlap_length1],
- )
- diffs[pointer + 1] = (
- self.DIFF_INSERT,
- insertion[overlap_length1:],
- )
- pointer += 1
- else:
- if (
- overlap_length2 >= len(deletion) / 2.0
- or overlap_length2 >= len(insertion) / 2.0
- ):
- # Reverse overlap found.
- # Insert an equality and swap and trim the surrounding edits.
- diffs.insert(
- pointer, (self.DIFF_EQUAL, deletion[:overlap_length2])
- )
- diffs[pointer - 1] = (
- self.DIFF_INSERT,
- insertion[: len(insertion) - overlap_length2],
- )
- diffs[pointer + 1] = (
- self.DIFF_DELETE,
- deletion[overlap_length2:],
- )
- pointer += 1
- pointer += 1
- pointer += 1
-
- def diff_cleanupSemanticLossless(self, diffs):
- """Look for single edits surrounded on both sides by equalities
- which can be shifted sideways to align the edit to a word boundary.
- e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
-
- Args:
- diffs: Array of diff tuples.
- """
-
- def diff_cleanupSemanticScore(one, two):
- """Given two strings, compute a score representing whether the
- internal boundary falls on logical boundaries.
- Scores range from 6 (best) to 0 (worst).
- Closure, but does not reference any external variables.
-
- Args:
- one: First string.
- two: Second string.
-
- Returns:
- The score.
- """
- if not one or not two:
- # Edges are the best.
- return 6
-
- # Each port of this function behaves slightly differently due to
- # subtle differences in each language's definition of things like
- # 'whitespace'. Since this function's purpose is largely cosmetic,
- # the choice has been made to use each language's native features
- # rather than force total conformity.
- char1 = one[-1]
- char2 = two[0]
- nonAlphaNumeric1 = not char1.isalnum()
- nonAlphaNumeric2 = not char2.isalnum()
- whitespace1 = nonAlphaNumeric1 and char1.isspace()
- whitespace2 = nonAlphaNumeric2 and char2.isspace()
- lineBreak1 = whitespace1 and (char1 == "\r" or char1 == "\n")
- lineBreak2 = whitespace2 and (char2 == "\r" or char2 == "\n")
- blankLine1 = lineBreak1 and self.BLANKLINEEND.search(one)
- blankLine2 = lineBreak2 and self.BLANKLINESTART.match(two)
-
- if blankLine1 or blankLine2:
- # Five points for blank lines.
- return 5
- elif lineBreak1 or lineBreak2:
- # Four points for line breaks.
- return 4
- elif nonAlphaNumeric1 and not whitespace1 and whitespace2:
- # Three points for end of sentences.
- return 3
- elif whitespace1 or whitespace2:
- # Two points for whitespace.
- return 2
- elif nonAlphaNumeric1 or nonAlphaNumeric2:
- # One point for non-alphanumeric.
- return 1
- return 0
-
- pointer = 1
- # Intentionally ignore the first and last element (don't need checking).
- while pointer < len(diffs) - 1:
- if (
- diffs[pointer - 1][0] == self.DIFF_EQUAL
- and diffs[pointer + 1][0] == self.DIFF_EQUAL
- ):
- # This is a single edit surrounded by equalities.
- equality1 = diffs[pointer - 1][1]
- edit = diffs[pointer][1]
- equality2 = diffs[pointer + 1][1]
-
- # First, shift the edit as far left as possible.
- commonOffset = self.diff_commonSuffix(equality1, edit)
- if commonOffset:
- commonString = edit[-commonOffset:]
- equality1 = equality1[:-commonOffset]
- edit = commonString + edit[:-commonOffset]
- equality2 = commonString + equality2
-
- # Second, step character by character right, looking for the best fit.
- bestEquality1 = equality1
- bestEdit = edit
- bestEquality2 = equality2
- bestScore = diff_cleanupSemanticScore(
- equality1, edit
- ) + diff_cleanupSemanticScore(edit, equality2)
- while edit and equality2 and edit[0] == equality2[0]:
- equality1 += edit[0]
- edit = edit[1:] + equality2[0]
- equality2 = equality2[1:]
- score = diff_cleanupSemanticScore(
- equality1, edit
- ) + diff_cleanupSemanticScore(edit, equality2)
- # The >= encourages trailing rather than leading whitespace on edits.
- if score >= bestScore:
- bestScore = score
- bestEquality1 = equality1
- bestEdit = edit
- bestEquality2 = equality2
-
- if diffs[pointer - 1][1] != bestEquality1:
- # We have an improvement, save it back to the diff.
- if bestEquality1:
- diffs[pointer - 1] = (diffs[pointer - 1][0], bestEquality1)
- else:
- del diffs[pointer - 1]
- pointer -= 1
- diffs[pointer] = (diffs[pointer][0], bestEdit)
- if bestEquality2:
- diffs[pointer + 1] = (diffs[pointer + 1][0], bestEquality2)
- else:
- del diffs[pointer + 1]
- pointer -= 1
- pointer += 1
-
- # Define some regex patterns for matching boundaries.
- BLANKLINEEND = re.compile(r"\n\r?\n$")
- BLANKLINESTART = re.compile(r"^\r?\n\r?\n")
-
- def diff_cleanupEfficiency(self, diffs):
- """Reduce the number of edits by eliminating operationally trivial
- equalities.
-
- Args:
- diffs: Array of diff tuples.
- """
- changes = False
- equalities = [] # Stack of indices where equalities are found.
- lastEquality = None # Always equal to diffs[equalities[-1]][1]
- pointer = 0 # Index of current position.
- pre_ins = False # Is there an insertion operation before the last equality.
- pre_del = False # Is there a deletion operation before the last equality.
- post_ins = False # Is there an insertion operation after the last equality.
- post_del = False # Is there a deletion operation after the last equality.
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
- if len(diffs[pointer][1]) < self.Diff_EditCost and (
- post_ins or post_del
- ):
- # Candidate found.
- equalities.append(pointer)
- pre_ins = post_ins
- pre_del = post_del
- lastEquality = diffs[pointer][1]
- else:
- # Not a candidate, and can never become one.
- equalities = []
- lastEquality = None
-
- post_ins = post_del = False
- else: # An insertion or deletion.
- if diffs[pointer][0] == self.DIFF_DELETE:
- post_del = True
- else:
- post_ins = True
-
- # Five types to be split:
- # <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
- # <ins>A</ins>X<ins>C</ins><del>D</del>
- # <ins>A</ins><del>B</del>X<ins>C</ins>
- # <ins>A</del>X<ins>C</ins><del>D</del>
- # <ins>A</ins><del>B</del>X<del>C</del>
-
- if lastEquality and (
- (pre_ins and pre_del and post_ins and post_del)
- or (
- (len(lastEquality) < self.Diff_EditCost / 2)
- and (pre_ins + pre_del + post_ins + post_del) == 3
- )
- ):
- # Duplicate record.
- diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
- # Change second copy to insert.
- diffs[equalities[-1] + 1] = (
- self.DIFF_INSERT,
- diffs[equalities[-1] + 1][1],
- )
- equalities.pop() # Throw away the equality we just deleted.
- lastEquality = None
- if pre_ins and pre_del:
- # No changes made which could affect previous entry, keep going.
- post_ins = post_del = True
- equalities = []
- else:
- if len(equalities):
- equalities.pop() # Throw away the previous equality.
- if len(equalities):
- pointer = equalities[-1]
- else:
- pointer = -1
- post_ins = post_del = False
- changes = True
- pointer += 1
-
- if changes:
- self.diff_cleanupMerge(diffs)
-
- def diff_cleanupMerge(self, diffs):
- """Reorder and merge like edit sections. Merge equalities.
- Any edit section can move as long as it doesn't cross an equality.
-
- Args:
- diffs: Array of diff tuples.
- """
- diffs.append((self.DIFF_EQUAL, "")) # Add a dummy entry at the end.
- pointer = 0
- count_delete = 0
- count_insert = 0
- text_delete = ""
- text_insert = ""
- while pointer < len(diffs):
- if diffs[pointer][0] == self.DIFF_INSERT:
- count_insert += 1
- text_insert += diffs[pointer][1]
- pointer += 1
- elif diffs[pointer][0] == self.DIFF_DELETE:
- count_delete += 1
- text_delete += diffs[pointer][1]
- pointer += 1
- elif diffs[pointer][0] == self.DIFF_EQUAL:
- # Upon reaching an equality, check for prior redundancies.
- if count_delete + count_insert > 1:
- if count_delete != 0 and count_insert != 0:
- # Factor out any common prefixies.
- commonlength = self.diff_commonPrefix(text_insert, text_delete)
- if commonlength != 0:
- x = pointer - count_delete - count_insert - 1
- if x >= 0 and diffs[x][0] == self.DIFF_EQUAL:
- diffs[x] = (
- diffs[x][0],
- diffs[x][1] + text_insert[:commonlength],
- )
- else:
- diffs.insert(
- 0, (self.DIFF_EQUAL, text_insert[:commonlength])
- )
- pointer += 1
- text_insert = text_insert[commonlength:]
- text_delete = text_delete[commonlength:]
- # Factor out any common suffixies.
- commonlength = self.diff_commonSuffix(text_insert, text_delete)
- if commonlength != 0:
- diffs[pointer] = (
- diffs[pointer][0],
- text_insert[-commonlength:] + diffs[pointer][1],
- )
- text_insert = text_insert[:-commonlength]
- text_delete = text_delete[:-commonlength]
- # Delete the offending records and add the merged ones.
- new_ops = []
- if len(text_delete) != 0:
- new_ops.append((self.DIFF_DELETE, text_delete))
- if len(text_insert) != 0:
- new_ops.append((self.DIFF_INSERT, text_insert))
- pointer -= count_delete + count_insert
- diffs[pointer : pointer + count_delete + count_insert] = new_ops
- pointer += len(new_ops) + 1
- elif pointer != 0 and diffs[pointer - 1][0] == self.DIFF_EQUAL:
- # Merge this equality with the previous one.
- diffs[pointer - 1] = (
- diffs[pointer - 1][0],
- diffs[pointer - 1][1] + diffs[pointer][1],
- )
- del diffs[pointer]
- else:
- pointer += 1
-
- count_insert = 0
- count_delete = 0
- text_delete = ""
- text_insert = ""
-
- if diffs[-1][1] == "":
- diffs.pop() # Remove the dummy entry at the end.
-
- # Second pass: look for single edits surrounded on both sides by equalities
- # which can be shifted sideways to eliminate an equality.
- # e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
- changes = False
- pointer = 1
- # Intentionally ignore the first and last element (don't need checking).
- while pointer < len(diffs) - 1:
- if (
- diffs[pointer - 1][0] == self.DIFF_EQUAL
- and diffs[pointer + 1][0] == self.DIFF_EQUAL
- ):
- # This is a single edit surrounded by equalities.
- if diffs[pointer][1].endswith(diffs[pointer - 1][1]):
- # Shift the edit over the previous equality.
- if diffs[pointer - 1][1] != "":
- diffs[pointer] = (
- diffs[pointer][0],
- diffs[pointer - 1][1]
- + diffs[pointer][1][: -len(diffs[pointer - 1][1])],
- )
- diffs[pointer + 1] = (
- diffs[pointer + 1][0],
- diffs[pointer - 1][1] + diffs[pointer + 1][1],
- )
- del diffs[pointer - 1]
- changes = True
- elif diffs[pointer][1].startswith(diffs[pointer + 1][1]):
- # Shift the edit over the next equality.
- diffs[pointer - 1] = (
- diffs[pointer - 1][0],
- diffs[pointer - 1][1] + diffs[pointer + 1][1],
- )
- diffs[pointer] = (
- diffs[pointer][0],
- diffs[pointer][1][len(diffs[pointer + 1][1]) :]
- + diffs[pointer + 1][1],
- )
- del diffs[pointer + 1]
- changes = True
- pointer += 1
-
- # If shifts were made, the diff needs reordering and another shift sweep.
- if changes:
- self.diff_cleanupMerge(diffs)
-
- def diff_xIndex(self, diffs, loc):
- """loc is a location in text1, compute and return the equivalent location
- in text2. e.g. "The cat" vs "The big cat", 1->1, 5->8
-
- Args:
- diffs: Array of diff tuples.
- loc: Location within text1.
-
- Returns:
- Location within text2.
- """
- chars1 = 0
- chars2 = 0
- last_chars1 = 0
- last_chars2 = 0
- for x in range(len(diffs)):
- (op, text) = diffs[x]
- if op != self.DIFF_INSERT: # Equality or deletion.
- chars1 += len(text)
- if op != self.DIFF_DELETE: # Equality or insertion.
- chars2 += len(text)
- if chars1 > loc: # Overshot the location.
- break
- last_chars1 = chars1
- last_chars2 = chars2
-
- if len(diffs) != x and diffs[x][0] == self.DIFF_DELETE:
- # The location was deleted.
- return last_chars2
- # Add the remaining len(character).
- return last_chars2 + (loc - last_chars1)
-
- def diff_prettyHtml(self, diffs):
- """Convert a diff array into a pretty HTML report.
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- HTML representation.
- """
- html = []
- for op, data in diffs:
- text = (
- data.replace("&", "&amp;")
- .replace("<", "&lt;")
- .replace(">", "&gt;")
- .replace("\n", "&para;<br>")
- )
- if op == self.DIFF_INSERT:
- html.append('<ins style="background:#e6ffe6;">%s</ins>' % text)
- elif op == self.DIFF_DELETE:
- html.append('<del style="background:#ffe6e6;">%s</del>' % text)
- elif op == self.DIFF_EQUAL:
- html.append("<span>%s</span>" % text)
- return "".join(html)
-
- def diff_text1(self, diffs):
- """Compute and return the source text (all equalities and deletions).
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Source text.
- """
- text = []
- for op, data in diffs:
- if op != self.DIFF_INSERT:
- text.append(data)
- return "".join(text)
-
- def diff_text2(self, diffs):
- """Compute and return the destination text (all equalities and insertions).
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Destination text.
- """
- text = []
- for op, data in diffs:
- if op != self.DIFF_DELETE:
- text.append(data)
- return "".join(text)
-
- def diff_levenshtein(self, diffs):
- """Compute the Levenshtein distance; the number of inserted, deleted or
- substituted characters.
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Number of changes.
- """
- levenshtein = 0
- insertions = 0
- deletions = 0
- for op, data in diffs:
- if op == self.DIFF_INSERT:
- insertions += len(data)
- elif op == self.DIFF_DELETE:
- deletions += len(data)
- elif op == self.DIFF_EQUAL:
- # A deletion and an insertion is one substitution.
- levenshtein += max(insertions, deletions)
- insertions = 0
- deletions = 0
- levenshtein += max(insertions, deletions)
- return levenshtein
-
- def diff_toDelta(self, diffs):
- """Crush the diff into an encoded string which describes the operations
- required to transform text1 into text2.
- E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
- Operations are tab-separated. Inserted text is escaped using %xx notation.
-
- Args:
- diffs: Array of diff tuples.
-
- Returns:
- Delta text.
- """
- text = []
- for op, data in diffs:
- if op == self.DIFF_INSERT:
- # High ascii will raise UnicodeDecodeError. Use Unicode instead.
- data = data.encode("utf-8")
- text.append("+" + urllib.parse.quote(data, "!~*'();/?:@&=+$,# "))
- elif op == self.DIFF_DELETE:
- text.append("-%d" % len(data))
- elif op == self.DIFF_EQUAL:
- text.append("=%d" % len(data))
- return "\t".join(text)
-
- def diff_fromDelta(self, text1, delta):
- """Given the original text1, and an encoded string which describes the
- operations required to transform text1 into text2, compute the full diff.
-
- Args:
- text1: Source string for the diff.
- delta: Delta text.
-
- Returns:
- Array of diff tuples.
-
- Raises:
- ValueError: If invalid input.
- """
- diffs = []
- pointer = 0 # Cursor in text1
- tokens = delta.split("\t")
- for token in tokens:
- if token == "":
- # Blank tokens are ok (from a trailing \t).
- continue
- # Each token begins with a one character parameter which specifies the
- # operation of this token (delete, insert, equality).
- param = token[1:]
- if token[0] == "+":
- param = urllib.parse.unquote(param)
- diffs.append((self.DIFF_INSERT, param))
- elif token[0] == "-" or token[0] == "=":
- try:
- n = int(param)
- except ValueError:
- raise ValueError("Invalid number in diff_fromDelta: " + param)
- if n < 0:
- raise ValueError("Negative number in diff_fromDelta: " + param)
- text = text1[pointer : pointer + n]
- pointer += n
- if token[0] == "=":
- diffs.append((self.DIFF_EQUAL, text))
- else:
- diffs.append((self.DIFF_DELETE, text))
- else:
- # Anything else is an error.
- raise ValueError(
- "Invalid diff operation in diff_fromDelta: " + token[0]
- )
- if pointer != len(text1):
- raise ValueError(
- "Delta length (%d) does not equal source text length (%d)."
- % (pointer, len(text1))
- )
- return diffs
-
- # MATCH FUNCTIONS
-
- def match_main(self, text, pattern, loc):
- """Locate the best instance of 'pattern' in 'text' near 'loc'.
-
- Args:
- text: The text to search.
- pattern: The pattern to search for.
- loc: The location to search around.
-
- Returns:
- Best match index or -1.
- """
- # Check for null inputs.
- if text == None or pattern == None:
- raise ValueError("Null inputs. (match_main)")
-
- loc = max(0, min(loc, len(text)))
- if text == pattern:
- # Shortcut (potentially not guaranteed by the algorithm)
- return 0
- elif not text:
- # Nothing to match.
- return -1
- elif text[loc : loc + len(pattern)] == pattern:
- # Perfect match at the perfect spot! (Includes case of null pattern)
- return loc
- else:
- # Do a fuzzy compare.
- match = self.match_bitap(text, pattern, loc)
- return match
-
- def match_bitap(self, text, pattern, loc):
- """Locate the best instance of 'pattern' in 'text' near 'loc' using the
- Bitap algorithm.
-
- Args:
- text: The text to search.
- pattern: The pattern to search for.
- loc: The location to search around.
-
- Returns:
- Best match index or -1.
- """
- # Python doesn't have a maxint limit, so ignore this check.
- # if self.Match_MaxBits != 0 and len(pattern) > self.Match_MaxBits:
- # raise ValueError("Pattern too long for this application.")
-
- # Initialise the alphabet.
- s = self.match_alphabet(pattern)
-
- def match_bitapScore(e, x):
- """Compute and return the score for a match with e errors and x location.
- Accesses loc and pattern through being a closure.
-
- Args:
- e: Number of errors in match.
- x: Location of match.
-
- Returns:
- Overall score for match (0.0 = good, 1.0 = bad).
- """
- accuracy = float(e) / len(pattern)
- proximity = abs(loc - x)
- if not self.Match_Distance:
- # Dodge divide by zero error.
- return proximity and 1.0 or accuracy
- return accuracy + (proximity / float(self.Match_Distance))
-
- # Highest score beyond which we give up.
- score_threshold = self.Match_Threshold
- # Is there a nearby exact match? (speedup)
- best_loc = text.find(pattern, loc)
- if best_loc != -1:
- score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
- # What about in the other direction? (speedup)
- best_loc = text.rfind(pattern, loc + len(pattern))
- if best_loc != -1:
- score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
-
- # Initialise the bit arrays.
- matchmask = 1 << (len(pattern) - 1)
- best_loc = -1
-
- bin_max = len(pattern) + len(text)
- # Empty initialization added to appease pychecker.
- last_rd = None
- for d in range(len(pattern)):
- # Scan for the best match each iteration allows for one more error.
- # Run a binary search to determine how far from 'loc' we can stray at
- # this error level.
- bin_min = 0
- bin_mid = bin_max
- while bin_min < bin_mid:
- if match_bitapScore(d, loc + bin_mid) <= score_threshold:
- bin_min = bin_mid
- else:
- bin_max = bin_mid
- bin_mid = (bin_max - bin_min) // 2 + bin_min
-
- # Use the result from this iteration as the maximum for the next.
- bin_max = bin_mid
- start = max(1, loc - bin_mid + 1)
- finish = min(loc + bin_mid, len(text)) + len(pattern)
-
- rd = [0] * (finish + 2)
- rd[finish + 1] = (1 << d) - 1
- for j in range(finish, start - 1, -1):
- if len(text) <= j - 1:
- # Out of range.
- charMatch = 0
- else:
- charMatch = s.get(text[j - 1], 0)
- if d == 0: # First pass: exact match.
- rd[j] = ((rd[j + 1] << 1) | 1) & charMatch
- else: # Subsequent passes: fuzzy match.
- rd[j] = (
- (((rd[j + 1] << 1) | 1) & charMatch)
- | (((last_rd[j + 1] | last_rd[j]) << 1) | 1)
- | last_rd[j + 1]
- )
- if rd[j] & matchmask:
- score = match_bitapScore(d, j - 1)
- # This match will almost certainly be better than any existing match.
- # But check anyway.
- if score <= score_threshold:
- # Told you so.
- score_threshold = score
- best_loc = j - 1
- if best_loc > loc:
- # When passing loc, don't exceed our current distance from loc.
- start = max(1, 2 * loc - best_loc)
- else:
- # Already passed loc, downhill from here on in.
- break
- # No hope for a (better) match at greater error levels.
- if match_bitapScore(d + 1, loc) > score_threshold:
- break
- last_rd = rd
- return best_loc
-
- def match_alphabet(self, pattern):
- """Initialise the alphabet for the Bitap algorithm.
-
- Args:
- pattern: The text to encode.
-
- Returns:
- Hash of character locations.
- """
- s = {}
- for char in pattern:
- s[char] = 0
- for i in range(len(pattern)):
- s[pattern[i]] |= 1 << (len(pattern) - i - 1)
- return s
-
- # PATCH FUNCTIONS
-
- def patch_addContext(self, patch, text):
- """Increase the context until it is unique,
- but don't let the pattern expand beyond Match_MaxBits.
-
- Args:
- patch: The patch to grow.
- text: Source text.
- """
- if len(text) == 0:
- return
- pattern = text[patch.start2 : patch.start2 + patch.length1]
- padding = 0
-
- # Look for the first and last matches of pattern in text. If two different
- # matches are found, increase the pattern length.
- while text.find(pattern) != text.rfind(pattern) and (
- self.Match_MaxBits == 0
- or len(pattern) < self.Match_MaxBits - self.Patch_Margin - self.Patch_Margin
- ):
- padding += self.Patch_Margin
- pattern = text[
- max(0, patch.start2 - padding) : patch.start2 + patch.length1 + padding
- ]
- # Add one chunk for good luck.
- padding += self.Patch_Margin
-
- # Add the prefix.
- prefix = text[max(0, patch.start2 - padding) : patch.start2]
- if prefix:
- patch.diffs[:0] = [(self.DIFF_EQUAL, prefix)]
- # Add the suffix.
- suffix = text[
- patch.start2 + patch.length1 : patch.start2 + patch.length1 + padding
- ]
- if suffix:
- patch.diffs.append((self.DIFF_EQUAL, suffix))
-
- # Roll back the start points.
- patch.start1 -= len(prefix)
- patch.start2 -= len(prefix)
- # Extend lengths.
- patch.length1 += len(prefix) + len(suffix)
- patch.length2 += len(prefix) + len(suffix)
-
- def patch_make(self, a, b=None, c=None):
- """Compute a list of patches to turn text1 into text2.
- Use diffs if provided, otherwise compute it ourselves.
- There are four ways to call this function, depending on what data is
- available to the caller:
- Method 1:
- a = text1, b = text2
- Method 2:
- a = diffs
- Method 3 (optimal):
- a = text1, b = diffs
- Method 4 (deprecated, use method 3):
- a = text1, b = text2, c = diffs
-
- Args:
- a: text1 (methods 1,3,4) or Array of diff tuples for text1 to
- text2 (method 2).
- b: text2 (methods 1,4) or Array of diff tuples for text1 to
- text2 (method 3) or undefined (method 2).
- c: Array of diff tuples for text1 to text2 (method 4) or
- undefined (methods 1,2,3).
-
- Returns:
- Array of Patch objects.
- """
- text1 = None
- diffs = None
- if isinstance(a, str) and isinstance(b, str) and c is None:
- # Method 1: text1, text2
- # Compute diffs from text1 and text2.
- text1 = a
- diffs = self.diff_main(text1, b, True)
- if len(diffs) > 2:
- self.diff_cleanupSemantic(diffs)
- self.diff_cleanupEfficiency(diffs)
- elif isinstance(a, list) and b is None and c is None:
- # Method 2: diffs
- # Compute text1 from diffs.
- diffs = a
- text1 = self.diff_text1(diffs)
- elif isinstance(a, str) and isinstance(b, list) and c is None:
- # Method 3: text1, diffs
- text1 = a
- diffs = b
- elif isinstance(a, str) and isinstance(b, str) and isinstance(c, list):
- # Method 4: text1, text2, diffs
- # text2 is not used.
- text1 = a
- diffs = c
- else:
- raise ValueError("Unknown call format to patch_make.")
-
- if not diffs:
- return [] # Get rid of the None case.
- patches = []
- patch = patch_obj()
- char_count1 = 0 # Number of characters into the text1 string.
- char_count2 = 0 # Number of characters into the text2 string.
- prepatch_text = text1 # Recreate the patches to determine context info.
- postpatch_text = text1
- for x in range(len(diffs)):
- (diff_type, diff_text) = diffs[x]
- if len(patch.diffs) == 0 and diff_type != self.DIFF_EQUAL:
- # A new patch starts here.
- patch.start1 = char_count1
- patch.start2 = char_count2
- if diff_type == self.DIFF_INSERT:
- # Insertion
- patch.diffs.append(diffs[x])
- patch.length2 += len(diff_text)
- postpatch_text = (
- postpatch_text[:char_count2]
- + diff_text
- + postpatch_text[char_count2:]
- )
- elif diff_type == self.DIFF_DELETE:
- # Deletion.
- patch.length1 += len(diff_text)
- patch.diffs.append(diffs[x])
- postpatch_text = (
- postpatch_text[:char_count2]
- + postpatch_text[char_count2 + len(diff_text) :]
- )
- elif (
- diff_type == self.DIFF_EQUAL
- and len(diff_text) <= 2 * self.Patch_Margin
- and len(patch.diffs) != 0
- and len(diffs) != x + 1
- ):
- # Small equality inside a patch.
- patch.diffs.append(diffs[x])
- patch.length1 += len(diff_text)
- patch.length2 += len(diff_text)
-
- if diff_type == self.DIFF_EQUAL and len(diff_text) >= 2 * self.Patch_Margin:
- # Time for a new patch.
- if len(patch.diffs) != 0:
- self.patch_addContext(patch, prepatch_text)
- patches.append(patch)
- patch = patch_obj()
- # Unlike Unidiff, our patch lists have a rolling context.
- # https://github.com/google/diff-match-patch/wiki/Unidiff
- # Update prepatch text & pos to reflect the application of the
- # just completed patch.
- prepatch_text = postpatch_text
- char_count1 = char_count2
-
- # Update the current character count.
- if diff_type != self.DIFF_INSERT:
- char_count1 += len(diff_text)
- if diff_type != self.DIFF_DELETE:
- char_count2 += len(diff_text)
-
- # Pick up the leftover patch if not empty.
- if len(patch.diffs) != 0:
- self.patch_addContext(patch, prepatch_text)
- patches.append(patch)
- return patches
-
- def patch_deepCopy(self, patches):
- """Given an array of patches, return another array that is identical.
-
- Args:
- patches: Array of Patch objects.
-
- Returns:
- Array of Patch objects.
- """
- patchesCopy = []
- for patch in patches:
- patchCopy = patch_obj()
- # No need to deep copy the tuples since they are immutable.
- patchCopy.diffs = patch.diffs[:]
- patchCopy.start1 = patch.start1
- patchCopy.start2 = patch.start2
- patchCopy.length1 = patch.length1
- patchCopy.length2 = patch.length2
- patchesCopy.append(patchCopy)
- return patchesCopy
-
- def patch_apply(self, patches, text):
- """Merge a set of patches onto the text. Return a patched text, as well
- as a list of true/false values indicating which patches were applied.
-
- Args:
- patches: Array of Patch objects.
- text: Old text.
-
- Returns:
- Two element Array, containing the new text and an array of boolean values.
- """
- if not patches:
- return (text, [])
-
- # Deep copy the patches so that no changes are made to originals.
- patches = self.patch_deepCopy(patches)
-
- nullPadding = self.patch_addPadding(patches)
- text = nullPadding + text + nullPadding
- self.patch_splitMax(patches)
-
- # delta keeps track of the offset between the expected and actual location
- # of the previous patch. If there are patches expected at positions 10 and
- # 20, but the first patch was found at 12, delta is 2 and the second patch
- # has an effective expected position of 22.
- delta = 0
- results = []
- for patch in patches:
- expected_loc = patch.start2 + delta
- text1 = self.diff_text1(patch.diffs)
- end_loc = -1
- if len(text1) > self.Match_MaxBits:
- # patch_splitMax will only provide an oversized pattern in the case of
- # a monster delete.
- start_loc = self.match_main(
- text, text1[: self.Match_MaxBits], expected_loc
- )
- if start_loc != -1:
- end_loc = self.match_main(
- text,
- text1[-self.Match_MaxBits :],
- expected_loc + len(text1) - self.Match_MaxBits,
- )
- if end_loc == -1 or start_loc >= end_loc:
- # Can't find valid trailing context. Drop this patch.
- start_loc = -1
- else:
- start_loc = self.match_main(text, text1, expected_loc)
- if start_loc == -1:
- # No match found. :(
- results.append(False)
- # Subtract the delta for this failed patch from subsequent patches.
- delta -= patch.length2 - patch.length1
- else:
- # Found a match. :)
- results.append(True)
- delta = start_loc - expected_loc
- if end_loc == -1:
- text2 = text[start_loc : start_loc + len(text1)]
- else:
- text2 = text[start_loc : end_loc + self.Match_MaxBits]
- if text1 == text2:
- # Perfect match, just shove the replacement text in.
- text = (
- text[:start_loc]
- + self.diff_text2(patch.diffs)
- + text[start_loc + len(text1) :]
- )
- else:
- # Imperfect match.
- # Run a diff to get a framework of equivalent indices.
- diffs = self.diff_main(text1, text2, False)
- if (
- len(text1) > self.Match_MaxBits
- and self.diff_levenshtein(diffs) / float(len(text1))
- > self.Patch_DeleteThreshold
- ):
- # The end points match, but the content is unacceptably bad.
- results[-1] = False
- else:
- self.diff_cleanupSemanticLossless(diffs)
- index1 = 0
- for op, data in patch.diffs:
- if op != self.DIFF_EQUAL:
- index2 = self.diff_xIndex(diffs, index1)
- if op == self.DIFF_INSERT: # Insertion
- text = (
- text[: start_loc + index2]
- + data
- + text[start_loc + index2 :]
- )
- elif op == self.DIFF_DELETE: # Deletion
- text = (
- text[: start_loc + index2]
- + text[
- start_loc
- + self.diff_xIndex(diffs, index1 + len(data)) :
- ]
- )
- if op != self.DIFF_DELETE:
- index1 += len(data)
- # Strip the padding off.
- text = text[len(nullPadding) : -len(nullPadding)]
- return (text, results)
-
- def patch_addPadding(self, patches):
- """Add some padding on text start and end so that edges can match
- something. Intended to be called only from within patch_apply.
-
- Args:
- patches: Array of Patch objects.
-
- Returns:
- The padding string added to each side.
- """
- paddingLength = self.Patch_Margin
- nullPadding = ""
- for x in range(1, paddingLength + 1):
- nullPadding += chr(x)
-
- # Bump all the patches forward.
- for patch in patches:
- patch.start1 += paddingLength
- patch.start2 += paddingLength
-
- # Add some padding on start of first diff.
- patch = patches[0]
- diffs = patch.diffs
- if not diffs or diffs[0][0] != self.DIFF_EQUAL:
- # Add nullPadding equality.
- diffs.insert(0, (self.DIFF_EQUAL, nullPadding))
- patch.start1 -= paddingLength # Should be 0.
- patch.start2 -= paddingLength # Should be 0.
- patch.length1 += paddingLength
- patch.length2 += paddingLength
- elif paddingLength > len(diffs[0][1]):
- # Grow first equality.
- extraLength = paddingLength - len(diffs[0][1])
- newText = nullPadding[len(diffs[0][1]) :] + diffs[0][1]
- diffs[0] = (diffs[0][0], newText)
- patch.start1 -= extraLength
- patch.start2 -= extraLength
- patch.length1 += extraLength
- patch.length2 += extraLength
-
- # Add some padding on end of last diff.
- patch = patches[-1]
- diffs = patch.diffs
- if not diffs or diffs[-1][0] != self.DIFF_EQUAL:
- # Add nullPadding equality.
- diffs.append((self.DIFF_EQUAL, nullPadding))
- patch.length1 += paddingLength
- patch.length2 += paddingLength
- elif paddingLength > len(diffs[-1][1]):
- # Grow last equality.
- extraLength = paddingLength - len(diffs[-1][1])
- newText = diffs[-1][1] + nullPadding[:extraLength]
- diffs[-1] = (diffs[-1][0], newText)
- patch.length1 += extraLength
- patch.length2 += extraLength
-
- return nullPadding
-
- def patch_splitMax(self, patches):
- """Look through the patches and break up any which are longer than the
- maximum limit of the match algorithm.
- Intended to be called only from within patch_apply.
-
- Args:
- patches: Array of Patch objects.
- """
- patch_size = self.Match_MaxBits
- if patch_size == 0:
- # Python has the option of not splitting strings due to its ability
- # to handle integers of arbitrary precision.
- return
- for x in range(len(patches)):
- if patches[x].length1 <= patch_size:
- continue
- bigpatch = patches[x]
- # Remove the big old patch.
- del patches[x]
- x -= 1
- start1 = bigpatch.start1
- start2 = bigpatch.start2
- precontext = ""
- while len(bigpatch.diffs) != 0:
- # Create one of several smaller patches.
- patch = patch_obj()
- empty = True
- patch.start1 = start1 - len(precontext)
- patch.start2 = start2 - len(precontext)
- if precontext:
- patch.length1 = patch.length2 = len(precontext)
- patch.diffs.append((self.DIFF_EQUAL, precontext))
-
- while (
- len(bigpatch.diffs) != 0
- and patch.length1 < patch_size - self.Patch_Margin
- ):
- (diff_type, diff_text) = bigpatch.diffs[0]
- if diff_type == self.DIFF_INSERT:
- # Insertions are harmless.
- patch.length2 += len(diff_text)
- start2 += len(diff_text)
- patch.diffs.append(bigpatch.diffs.pop(0))
- empty = False
- elif (
- diff_type == self.DIFF_DELETE
- and len(patch.diffs) == 1
- and patch.diffs[0][0] == self.DIFF_EQUAL
- and len(diff_text) > 2 * patch_size
- ):
- # This is a large deletion. Let it pass in one chunk.
- patch.length1 += len(diff_text)
- start1 += len(diff_text)
- empty = False
- patch.diffs.append((diff_type, diff_text))
- del bigpatch.diffs[0]
- else:
- # Deletion or equality. Only take as much as we can stomach.
- diff_text = diff_text[
- : patch_size - patch.length1 - self.Patch_Margin
- ]
- patch.length1 += len(diff_text)
- start1 += len(diff_text)
- if diff_type == self.DIFF_EQUAL:
- patch.length2 += len(diff_text)
- start2 += len(diff_text)
- else:
- empty = False
-
- patch.diffs.append((diff_type, diff_text))
- if diff_text == bigpatch.diffs[0][1]:
- del bigpatch.diffs[0]
- else:
- bigpatch.diffs[0] = (
- bigpatch.diffs[0][0],
- bigpatch.diffs[0][1][len(diff_text) :],
- )
-
- # Compute the head context for the next patch.
- precontext = self.diff_text2(patch.diffs)
- precontext = precontext[-self.Patch_Margin :]
- # Append the end context for this patch.
- postcontext = self.diff_text1(bigpatch.diffs)[: self.Patch_Margin]
- if postcontext:
- patch.length1 += len(postcontext)
- patch.length2 += len(postcontext)
- if len(patch.diffs) != 0 and patch.diffs[-1][0] == self.DIFF_EQUAL:
- patch.diffs[-1] = (
- self.DIFF_EQUAL,
- patch.diffs[-1][1] + postcontext,
- )
- else:
- patch.diffs.append((self.DIFF_EQUAL, postcontext))
-
- if not empty:
- x += 1
- patches.insert(x, patch)
-
- def patch_toText(self, patches):
- """Take a list of patches and return a textual representation.
-
- Args:
- patches: Array of Patch objects.
-
- Returns:
- Text representation of patches.
- """
- text = []
- for patch in patches:
- text.append(str(patch))
- return "".join(text)
-
- def patch_fromText(self, textline):
- """Parse a textual representation of patches and return a list of patch
- objects.
-
- Args:
- textline: Text representation of patches.
-
- Returns:
- Array of Patch objects.
-
- Raises:
- ValueError: If invalid input.
- """
- patches = []
- if not textline:
- return patches
- text = textline.split("\n")
- while len(text) != 0:
- m = re.match(r"^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$", text[0])
- if not m:
- raise ValueError("Invalid patch string: " + text[0])
- patch = patch_obj()
- patches.append(patch)
- patch.start1 = int(m.group(1))
- if m.group(2) == "":
- patch.start1 -= 1
- patch.length1 = 1
- elif m.group(2) == "0":
- patch.length1 = 0
- else:
- patch.start1 -= 1
- patch.length1 = int(m.group(2))
-
- patch.start2 = int(m.group(3))
- if m.group(4) == "":
- patch.start2 -= 1
- patch.length2 = 1
- elif m.group(4) == "0":
- patch.length2 = 0
- else:
- patch.start2 -= 1
- patch.length2 = int(m.group(4))
-
- del text[0]
-
- while len(text) != 0:
- if text[0]:
- sign = text[0][0]
- else:
- sign = ""
- line = urllib.parse.unquote(text[0][1:])
- if sign == "+":
- # Insertion.
- patch.diffs.append((self.DIFF_INSERT, line))
- elif sign == "-":
- # Deletion.
- patch.diffs.append((self.DIFF_DELETE, line))
- elif sign == " ":
- # Minor equality.
- patch.diffs.append((self.DIFF_EQUAL, line))
- elif sign == "@":
- # Start of next patch.
- break
- elif sign == "":
- # Blank line? Whatever.
- pass
- else:
- # WTF?
- raise ValueError("Invalid patch mode: '%s'\n%s" % (sign, line))
- del text[0]
- return patches
-
-
-class patch_obj:
- """Class representing one patch operation."""
-
- def __init__(self):
- """Initializes with an empty list of diffs."""
- self.diffs = []
- self.start1 = None
- self.start2 = None
- self.length1 = 0
- self.length2 = 0
-
- def __str__(self):
- """Emulate GNU diff's format.
- Header: @@ -382,8 +481,9 @@
- Indices are printed as 1-based, not 0-based.
-
- Returns:
- The GNU diff string.
- """
- if self.length1 == 0:
- coords1 = str(self.start1) + ",0"
- elif self.length1 == 1:
- coords1 = str(self.start1 + 1)
- else:
- coords1 = str(self.start1 + 1) + "," + str(self.length1)
- if self.length2 == 0:
- coords2 = str(self.start2) + ",0"
- elif self.length2 == 1:
- coords2 = str(self.start2 + 1)
- else:
- coords2 = str(self.start2 + 1) + "," + str(self.length2)
- text = ["@@ -", coords1, " +", coords2, " @@\n"]
- # Escape the body of the patch with %xx notation.
- for op, data in self.diffs:
- if op == diff_match_patch.DIFF_INSERT:
- text.append("+")
- elif op == diff_match_patch.DIFF_DELETE:
- text.append("-")
- elif op == diff_match_patch.DIFF_EQUAL:
- text.append(" ")
- # High ascii will raise UnicodeDecodeError. Use Unicode instead.
- data = data.encode("utf-8")
- text.append(urllib.parse.quote(data, "!~*'();/?:@&=+$,# ") + "\n")
- return "".join(text)
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt b/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt
deleted file mode 100644
index 54b438fd79..0000000000
--- a/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt
+++ /dev/null
@@ -1,230 +0,0 @@
-This is a '''list of newspapers published by [[Journal Register Company]]'''.
-
-The company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic "clusters":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>
-
-== Capital-Saratoga ==
-Three dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
-
-* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]
-* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]
-* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]
-* Weeklies:
-** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]
-** ''Rome Observer'' of [[Rome, New York]]
-** ''Life & Times of Utica'' of [[Utica, New York]]
-
-== Connecticut ==
-Five dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].
-
-* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]
-* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]
-* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]
-
-* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}
-** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]
-** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]
-** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]
-** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]
-** ''The Post'' of [[North Haven, Connecticut|North Haven]]
-** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]
-** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]
-** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]
-** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]
-* Housatonic Publications
-** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]
-** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]
-** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]
-** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]
-** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]
-** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]
-* Imprint Newspapers {{WS|imprintnewspapers.com}}
-** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]
-** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]
-** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]
-** ''Avon Post'' of [[Avon, Connecticut|Avon]]
-** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]
-** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]
-** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]
-* Minuteman Publications
-** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]
-** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]
-* Shoreline Newspapers weeklies:
-** ''Branford Review'' of [[Branford, Connecticut|Branford]]
-** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]
-** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]
-** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]
-** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]
-** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]
-** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]
-** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]
-** ''Shore View East'' of [[Madison, Connecticut|Madison]]
-** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]
-* Other weeklies:
-** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]
-** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]
-** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton
-
-== Michigan ==
-Four dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]
-* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]
-* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]
-* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]
-* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of [[Mount Pleasant, Michigan|Mount Pleasant]]
-* Heritage Newspapers {{WS|heritage.com}}
-** ''Belleville View''
-** ''Ile Camera''
-** ''Monroe Guardian''
-** ''Ypsilanti Courier''
-** ''News-Herald''
-** ''Press & Guide''
-** ''Chelsea Standard & Dexter Leader''
-** ''Manchester Enterprise''
-** ''Milan News-Leader''
-** ''Saline Reporter''
-* Independent Newspapers {{WS|sourcenewspapers.com}}
-** ''Advisor''
-** ''Source''
-* Morning Star {{WS|morningstarpublishing.com}}
-** ''Alma Reminder''
-** ''Alpena Star''
-** ''Antrim County News''
-** ''Carson City Reminder''
-** ''The Leader & Kalkaskian''
-** ''Ogemaw/Oscoda County Star''
-** ''Petoskey/Charlevoix Star''
-** ''Presque Isle Star''
-** ''Preview Community Weekly''
-** ''Roscommon County Star''
-** ''St. Johns Reminder''
-** ''Straits Area Star''
-** ''The (Edmore) Advertiser''
-* Voice Newspapers {{WS|voicenews.com}}
-** ''Armada Times''
-** ''Bay Voice''
-** ''Blue Water Voice''
-** ''Downriver Voice''
-** ''Macomb Township Voice''
-** ''North Macomb Voice''
-** ''Weekend Voice''
-** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}
-
-== Mid-Hudson ==
-One daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
-
-* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]
-
-== Ohio ==
-Two dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].
-
-* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]
-* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]
-
-== Philadelphia area ==
-Seven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].
-
-* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]
-* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos
-* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]
-* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]
-* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]
-* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]
-* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]
-
-* Weeklies
-** ''El Latino Expreso'' of [[Trenton, New Jersey]]
-** ''La Voz'' of [[Norristown, Pennsylvania]]
-** ''The Village News'' of [[Downingtown, Pennsylvania]]
-** ''The Times Record'' of [[Kennett Square, Pennsylvania]]
-** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]
-** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]
-** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]
-** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]
-** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]
-* Chesapeake Publishing {{WS|pa8newsgroup.com}}
-** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]
-** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]
-** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]
-** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]
-** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]
-** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]
-** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]
-** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]
-** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]
-** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]
-** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]
-** ''The Central Record'' of [[Medford, New Jersey]]
-** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]
-* Intercounty Newspapers {{WS|buckslocalnews.com}}
-** ''The Review'' of Roxborough, Pennsylvania
-** ''The Recorder'' of [[Conshohocken, Pennsylvania]]
-** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania
-** ''The Pennington Post'' of [[Pennington, New Jersey]]
-** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]
-** ''Yardley News'' of [[Yardley, Pennsylvania]]
-** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]
-** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]
-** ''Newtown Advance'' of [[Newtown, Pennsylvania]]
-** ''The Plain Dealer'' of [[Williamstown, New Jersey]]
-** ''News Report'' of [[Sewell, New Jersey]]
-** ''Record Breeze'' of [[Berlin, New Jersey]]
-** ''Newsweekly'' of [[Moorestown, New Jersey]]
-** ''Haddon Herald'' of [[Haddonfield, New Jersey]]
-** ''New Egypt Press'' of [[New Egypt, New Jersey]]
-** ''Community News'' of [[Pemberton, New Jersey]]
-** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]
-** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]
-* Montgomery Newspapers {{WS|montgomerynews.com}}
-** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]
-** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]
-** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]
-** ''Glenside News'' of [[Glenside, Pennsylvania]]
-** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]
-** ''Main Line Life'' of [[Ardmore, Pennsylvania]]
-** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]
-** ''North Penn Life'' of [[Lansdale, Pennsylvania]]
-** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]
-** ''Public Spirit'' of [[Hatboro, Pennsylvania]]
-** ''Souderton Independent'' of [[Souderton, Pennsylvania]]
-** ''Springfield Sun'' of [[Springfield, Pennsylvania]]
-** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]
-** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]
-** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]
-** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]
-* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}}
-** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]
-* Suburban Publications
-** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]
-** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]
-** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]
-* Press Newspapers {{WS|countypressonline.com}}
-** ''County Press'' of [[Newtown Square, Pennsylvania]]
-** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]
-** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)
-** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)
-** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)
-** ''Springfield Press'' of [[Springfield, Pennsylvania]]
-* Berks-Mont Newspapers {{WS|berksmontnews.com}}
-** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]
-** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]
-** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]
-** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]
-** ''The Free Press'' of [[Quakertown, Pennsylvania]]
-** ''The Saucon News'' of [[Quakertown, Pennsylvania]]
-** ''Westside Weekly'' of [[Reading, Pennsylvania]]
-
-* Magazines
-** ''Bucks Co. Town & Country Living''
-** ''Chester Co. Town & Country Living''
-** ''Montomgery Co. Town & Country Living''
-** ''Garden State Town & Country Living''
-** ''Montgomery Homes''
-** ''Philadelphia Golfer''
-** ''Parents Express''
-** ''Art Matters''
-
-{{JRC}}
-
-==References==
-<references />
-
-[[Category:Journal Register publications|*]]
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt b/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt
deleted file mode 100644
index 8f25a80fff..0000000000
--- a/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt
+++ /dev/null
@@ -1,188 +0,0 @@
-This is a '''list of newspapers published by [[Journal Register Company]]'''.
-
-The company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic "clusters":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>
-
-== Capital-Saratoga ==
-Three dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
-
-* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]
-* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]
-* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]
-* Weeklies:
-** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]
-** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]
-** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]
-** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]
-** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]
-** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]
-** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]
-
-== Connecticut ==
-Three dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].
-
-* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]
-* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]
-* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]
-
-* Housatonic Publications
-** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]
-** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]
-
-* Minuteman Publications
-** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]
-** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]
-
-* Shoreline Newspapers
-** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]
-** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]
-
-* Foothills Media Group {{WS|foothillsmediagroup.com}}
-** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]
-** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]
-** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]
-** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]
-** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]
-** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]
-** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]
-** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton
-
-* Other weeklies
-** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]
-** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]
-** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]
-
-* Magazines
-** ''The Connecticut Bride'' {{WS|connecticutmag.com}}
-** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}
-** ''Passport Magazine'' {{WS|passport-mag.com}}
-
-== Michigan ==
-Four dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]
-* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]
-* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]
-* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]
-* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of [[Mount Pleasant, Michigan|Mount Pleasant]]
-
-* Heritage Newspapers {{WS|heritage.com}}
-** ''Belleville View'' {{WS|bellevilleview.com}}
-** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}
-** ''Monroe Guardian'' {{WS|monreguardian.com}}
-** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}
-** ''News-Herald'' {{WS|thenewsherald.com}}
-** ''Press & Guide'' {{WS|pressandguide.com}}
-** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}
-** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}
-** ''Milan News-Leader'' {{WS|milannews.com}}
-** ''Saline Reporter'' {{WS|salinereporter.com}}
-* Independent Newspapers
-** ''Advisor'' {{WS|sourcenewspapers.com}}
-** ''Source'' {{WS|sourcenewspapers.com}}
-* Morning Star {{WS|morningstarpublishing.com}}
-** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}
-** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}
-** ''Alma Reminder''
-** ''Alpena Star''
-** ''Ogemaw/Oscoda County Star''
-** ''Presque Isle Star''
-** ''St. Johns Reminder''
-
-* Voice Newspapers {{WS|voicenews.com}}
-** ''Armada Times''
-** ''Bay Voice''
-** ''Blue Water Voice''
-** ''Downriver Voice''
-** ''Macomb Township Voice''
-** ''North Macomb Voice''
-** ''Weekend Voice''
-
-== Mid-Hudson ==
-One daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
-
-* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]
-* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]
-
-== Ohio ==
-Two dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].
-
-* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]
-* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]
-* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]
-
-== Philadelphia area ==
-Seven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].
-
-* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]
-* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]
-* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]
-* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]
-* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]
-* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]
-
-* Weeklies
-* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]
-** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]
-** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]
-** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]
-** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]
-
-* Chesapeake Publishing {{WS|southernchestercountyweeklies.com}}
-** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]
-** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]
-** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]
-** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]
-
-* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}}
-** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]
-** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]
-** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]
-** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]
-** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]
-** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]
-
-* Montgomery Newspapers {{WS|montgomerynews.com}}
-** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]
-** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]
-** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]
-** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]
-** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]
-** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]
-** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]
-** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]
-** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]
-** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]
-** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]
-** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]
-** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]
-** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]
-** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]
-
-* Main Line Media News {{WS|mainlinemedianews.com}}
-** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]
-** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]
-** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]
-
-* Delaware County News Network {{WS|delconewsnetwork.com}}
-** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]
-** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]
-** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]
-** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]
-** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]
-
-* Berks-Mont Newspapers {{WS|berksmontnews.com}}
-** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]
-** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]
-** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]
-** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]
-** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]
-
-* Magazines
-** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}}
-** ''Parents Express'' {{WS|parents-express.com}}
-** ''Real Men, Rednecks'' {{WS|realmenredneck.com}}
-
-{{JRC}}
-
-==References==
-<references />
-
-[[Category:Journal Register publications|*]]
diff --git a/contrib/python/diff-match-patch/py3/ya.make b/contrib/python/diff-match-patch/py3/ya.make
deleted file mode 100644
index 9a510f1683..0000000000
--- a/contrib/python/diff-match-patch/py3/ya.make
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(20230430)
-
-LICENSE(Apache-2.0)
-
-NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- diff_match_patch/__init__.py
- diff_match_patch/__version__.py
- diff_match_patch/diff_match_patch.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/diff-match-patch/py3/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
-END()
-
-RECURSE_FOR_TESTS(
- tests
-)
diff --git a/contrib/python/diff-match-patch/ya.make b/contrib/python/diff-match-patch/ya.make
deleted file mode 100644
index ca419d06b4..0000000000
--- a/contrib/python/diff-match-patch/ya.make
+++ /dev/null
@@ -1,18 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/diff-match-patch/py2)
-ELSE()
- PEERDIR(contrib/python/diff-match-patch/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- py2
- py3
-)
diff --git a/contrib/python/humanfriendly/py2/LICENSE.txt b/contrib/python/humanfriendly/py2/LICENSE.txt
deleted file mode 100644
index 96ece318ed..0000000000
--- a/contrib/python/humanfriendly/py2/LICENSE.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2021 Peter Odding
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/contrib/python/humanfriendly/py2/README.rst b/contrib/python/humanfriendly/py2/README.rst
deleted file mode 100644
index 80145d564c..0000000000
--- a/contrib/python/humanfriendly/py2/README.rst
+++ /dev/null
@@ -1,170 +0,0 @@
-humanfriendly: Human friendly input/output in Python
-====================================================
-
-.. image:: https://github.com/xolox/python-humanfriendly/actions/workflows/test.yml/badge.svg?branch=master
- :target: https://github.com/xolox/python-humanfriendly/actions
-
-.. image:: https://codecov.io/gh/xolox/python-humanfriendly/branch/master/graph/badge.svg?token=jYaj4T74TU
- :target: https://codecov.io/gh/xolox/python-humanfriendly
-
-The functions and classes in the `humanfriendly` package can be used to make
-text interfaces more user friendly. Some example features:
-
-- Parsing and formatting numbers, file sizes, pathnames and timespans in
- simple, human friendly formats.
-
-- Easy to use timers for long running operations, with human friendly
- formatting of the resulting timespans.
-
-- Prompting the user to select a choice from a list of options by typing the
- option's number or a unique substring of the option.
-
-- Terminal interaction including text styling (`ANSI escape sequences`_), user
- friendly rendering of usage messages and querying the terminal for its
- size.
-
-The `humanfriendly` package is currently tested on Python 2.7, 3.5+ and PyPy
-(2.7) on Linux and macOS. While the intention is to support Windows as well,
-you may encounter some rough edges.
-
-.. contents::
- :local:
-
-Getting started
----------------
-
-It's very simple to start using the `humanfriendly` package::
-
- >>> from humanfriendly import format_size, parse_size
- >>> from humanfriendly.prompts import prompt_for_input
- >>> user_input = prompt_for_input("Enter a readable file size: ")
-
- Enter a readable file size: 16G
-
- >>> num_bytes = parse_size(user_input)
- >>> print(num_bytes)
- 16000000000
- >>> print("You entered:", format_size(num_bytes))
- You entered: 16 GB
- >>> print("You entered:", format_size(num_bytes, binary=True))
- You entered: 14.9 GiB
-
-To get a demonstration of supported terminal text styles (based on
-`ANSI escape sequences`_) you can run the following command::
-
- $ humanfriendly --demo
-
-Command line
-------------
-
-.. A DRY solution to avoid duplication of the `humanfriendly --help' text:
-..
-.. [[[cog
-.. from humanfriendly.usage import inject_usage
-.. inject_usage('humanfriendly.cli')
-.. ]]]
-
-**Usage:** `humanfriendly [OPTIONS]`
-
-Human friendly input/output (text formatting) on the command
-line based on the Python package with the same name.
-
-**Supported options:**
-
-.. csv-table::
- :header: Option, Description
- :widths: 30, 70
-
-
- "``-c``, ``--run-command``","Execute an external command (given as the positional arguments) and render
- a spinner and timer while the command is running. The exit status of the
- command is propagated."
- ``--format-table``,"Read tabular data from standard input (each line is a row and each
- whitespace separated field is a column), format the data as a table and
- print the resulting table to standard output. See also the ``--delimiter``
- option."
- "``-d``, ``--delimiter=VALUE``","Change the delimiter used by ``--format-table`` to ``VALUE`` (a string). By default
- all whitespace is treated as a delimiter."
- "``-l``, ``--format-length=LENGTH``","Convert a length count (given as the integer or float ``LENGTH``) into a human
- readable string and print that string to standard output."
- "``-n``, ``--format-number=VALUE``","Format a number (given as the integer or floating point number ``VALUE``) with
- thousands separators and two decimal places (if needed) and print the
- formatted number to standard output."
- "``-s``, ``--format-size=BYTES``","Convert a byte count (given as the integer ``BYTES``) into a human readable
- string and print that string to standard output."
- "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to use binary multiples of bytes
- (base-2) instead of the default decimal multiples of bytes (base-10)."
- "``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given as the floating point number ``SECONDS``)
- into a human readable timespan and print that string to standard output."
- ``--parse-length=VALUE``,"Parse a human readable length (given as the string ``VALUE``) and print the
- number of metres to standard output."
- ``--parse-size=VALUE``,"Parse a human readable data size (given as the string ``VALUE``) and print the
- number of bytes to standard output."
- ``--demo``,"Demonstrate changing the style and color of the terminal font using ANSI
- escape sequences."
- "``-h``, ``--help``",Show this message and exit.
-
-.. [[[end]]]
-
-A note about size units
------------------------
-
-When I originally published the `humanfriendly` package I went with binary
-multiples of bytes (powers of two). It was pointed out several times that this
-was a poor choice (see issue `#4`_ and pull requests `#8`_ and `#9`_) and thus
-the new default became decimal multiples of bytes (powers of ten):
-
-+------+---------------+---------------+
-| Unit | Binary value | Decimal value |
-+------+---------------+---------------+
-| KB | 1024 | 1000 +
-+------+---------------+---------------+
-| MB | 1048576 | 1000000 |
-+------+---------------+---------------+
-| GB | 1073741824 | 1000000000 |
-+------+---------------+---------------+
-| TB | 1099511627776 | 1000000000000 |
-+------+---------------+---------------+
-| etc | | |
-+------+---------------+---------------+
-
-The option to use binary multiples of bytes remains by passing the keyword
-argument `binary=True` to the `format_size()`_ and `parse_size()`_ functions.
-
-Windows support
----------------
-
-Windows 10 gained native support for ANSI escape sequences which means commands
-like ``humanfriendly --demo`` should work out of the box (if your system is
-up-to-date enough). If this doesn't work then you can install the colorama_
-package, it will be used automatically once installed.
-
-Contact
--------
-
-The latest version of `humanfriendly` is available on PyPI_ and GitHub_. The
-documentation is hosted on `Read the Docs`_ and includes a changelog_. For bug
-reports please create an issue on GitHub_. If you have questions, suggestions,
-etc. feel free to send me an e-mail at `peter@peterodding.com`_.
-
-License
--------
-
-This software is licensed under the `MIT license`_.
-
-© 2021 Peter Odding.
-
-.. External references:
-.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
-.. _#8: https://github.com/xolox/python-humanfriendly/pull/8
-.. _#9: https://github.com/xolox/python-humanfriendly/pull/9
-.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
-.. _changelog: https://humanfriendly.readthedocs.io/en/latest/changelog.html
-.. _colorama: https://pypi.org/project/colorama
-.. _format_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.format_size
-.. _GitHub: https://github.com/xolox/python-humanfriendly
-.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
-.. _parse_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.parse_size
-.. _peter@peterodding.com: peter@peterodding.com
-.. _PyPI: https://pypi.org/project/humanfriendly
-.. _Read the Docs: https://humanfriendly.readthedocs.io
diff --git a/contrib/python/humanfriendly/py3/.dist-info/METADATA b/contrib/python/humanfriendly/py3/.dist-info/METADATA
deleted file mode 100644
index c36fa4cafe..0000000000
--- a/contrib/python/humanfriendly/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,216 +0,0 @@
-Metadata-Version: 2.1
-Name: humanfriendly
-Version: 10.0
-Summary: Human friendly output for text interfaces using Python
-Home-page: https://humanfriendly.readthedocs.io
-Author: Peter Odding
-Author-email: peter@peterodding.com
-License: MIT
-Platform: UNKNOWN
-Classifier: Development Status :: 6 - Mature
-Classifier: Environment :: Console
-Classifier: Framework :: Sphinx :: Extension
-Classifier: Intended Audience :: Developers
-Classifier: Intended Audience :: System Administrators
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Natural Language :: English
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Communications
-Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces
-Classifier: Topic :: Software Development
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: Software Development :: User Interfaces
-Classifier: Topic :: System :: Shells
-Classifier: Topic :: System :: System Shells
-Classifier: Topic :: System :: Systems Administration
-Classifier: Topic :: Terminals
-Classifier: Topic :: Text Processing :: General
-Classifier: Topic :: Text Processing :: Linguistic
-Classifier: Topic :: Utilities
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
-Requires-Dist: monotonic ; python_version == "2.7"
-Requires-Dist: pyreadline ; sys_platform == "win32" and python_version<"3.8"
-Requires-Dist: pyreadline3 ; sys_platform == "win32" and python_version>="3.8"
-
-humanfriendly: Human friendly input/output in Python
-====================================================
-
-.. image:: https://github.com/xolox/python-humanfriendly/actions/workflows/test.yml/badge.svg?branch=master
- :target: https://github.com/xolox/python-humanfriendly/actions
-
-.. image:: https://codecov.io/gh/xolox/python-humanfriendly/branch/master/graph/badge.svg?token=jYaj4T74TU
- :target: https://codecov.io/gh/xolox/python-humanfriendly
-
-The functions and classes in the `humanfriendly` package can be used to make
-text interfaces more user friendly. Some example features:
-
-- Parsing and formatting numbers, file sizes, pathnames and timespans in
- simple, human friendly formats.
-
-- Easy to use timers for long running operations, with human friendly
- formatting of the resulting timespans.
-
-- Prompting the user to select a choice from a list of options by typing the
- option's number or a unique substring of the option.
-
-- Terminal interaction including text styling (`ANSI escape sequences`_), user
- friendly rendering of usage messages and querying the terminal for its
- size.
-
-The `humanfriendly` package is currently tested on Python 2.7, 3.5+ and PyPy
-(2.7) on Linux and macOS. While the intention is to support Windows as well,
-you may encounter some rough edges.
-
-.. contents::
- :local:
-
-Getting started
----------------
-
-It's very simple to start using the `humanfriendly` package::
-
- >>> from humanfriendly import format_size, parse_size
- >>> from humanfriendly.prompts import prompt_for_input
- >>> user_input = prompt_for_input("Enter a readable file size: ")
-
- Enter a readable file size: 16G
-
- >>> num_bytes = parse_size(user_input)
- >>> print(num_bytes)
- 16000000000
- >>> print("You entered:", format_size(num_bytes))
- You entered: 16 GB
- >>> print("You entered:", format_size(num_bytes, binary=True))
- You entered: 14.9 GiB
-
-To get a demonstration of supported terminal text styles (based on
-`ANSI escape sequences`_) you can run the following command::
-
- $ humanfriendly --demo
-
-Command line
-------------
-
-.. A DRY solution to avoid duplication of the `humanfriendly --help' text:
-..
-.. [[[cog
-.. from humanfriendly.usage import inject_usage
-.. inject_usage('humanfriendly.cli')
-.. ]]]
-
-**Usage:** `humanfriendly [OPTIONS]`
-
-Human friendly input/output (text formatting) on the command
-line based on the Python package with the same name.
-
-**Supported options:**
-
-.. csv-table::
- :header: Option, Description
- :widths: 30, 70
-
-
- "``-c``, ``--run-command``","Execute an external command (given as the positional arguments) and render
- a spinner and timer while the command is running. The exit status of the
- command is propagated."
- ``--format-table``,"Read tabular data from standard input (each line is a row and each
- whitespace separated field is a column), format the data as a table and
- print the resulting table to standard output. See also the ``--delimiter``
- option."
- "``-d``, ``--delimiter=VALUE``","Change the delimiter used by ``--format-table`` to ``VALUE`` (a string). By default
- all whitespace is treated as a delimiter."
- "``-l``, ``--format-length=LENGTH``","Convert a length count (given as the integer or float ``LENGTH``) into a human
- readable string and print that string to standard output."
- "``-n``, ``--format-number=VALUE``","Format a number (given as the integer or floating point number ``VALUE``) with
- thousands separators and two decimal places (if needed) and print the
- formatted number to standard output."
- "``-s``, ``--format-size=BYTES``","Convert a byte count (given as the integer ``BYTES``) into a human readable
- string and print that string to standard output."
- "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to use binary multiples of bytes
- (base-2) instead of the default decimal multiples of bytes (base-10)."
- "``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given as the floating point number ``SECONDS``)
- into a human readable timespan and print that string to standard output."
- ``--parse-length=VALUE``,"Parse a human readable length (given as the string ``VALUE``) and print the
- number of metres to standard output."
- ``--parse-size=VALUE``,"Parse a human readable data size (given as the string ``VALUE``) and print the
- number of bytes to standard output."
- ``--demo``,"Demonstrate changing the style and color of the terminal font using ANSI
- escape sequences."
- "``-h``, ``--help``",Show this message and exit.
-
-.. [[[end]]]
-
-A note about size units
------------------------
-
-When I originally published the `humanfriendly` package I went with binary
-multiples of bytes (powers of two). It was pointed out several times that this
-was a poor choice (see issue `#4`_ and pull requests `#8`_ and `#9`_) and thus
-the new default became decimal multiples of bytes (powers of ten):
-
-+------+---------------+---------------+
-| Unit | Binary value | Decimal value |
-+------+---------------+---------------+
-| KB | 1024 | 1000 +
-+------+---------------+---------------+
-| MB | 1048576 | 1000000 |
-+------+---------------+---------------+
-| GB | 1073741824 | 1000000000 |
-+------+---------------+---------------+
-| TB | 1099511627776 | 1000000000000 |
-+------+---------------+---------------+
-| etc | | |
-+------+---------------+---------------+
-
-The option to use binary multiples of bytes remains by passing the keyword
-argument `binary=True` to the `format_size()`_ and `parse_size()`_ functions.
-
-Windows support
----------------
-
-Windows 10 gained native support for ANSI escape sequences which means commands
-like ``humanfriendly --demo`` should work out of the box (if your system is
-up-to-date enough). If this doesn't work then you can install the colorama_
-package, it will be used automatically once installed.
-
-Contact
--------
-
-The latest version of `humanfriendly` is available on PyPI_ and GitHub_. The
-documentation is hosted on `Read the Docs`_ and includes a changelog_. For bug
-reports please create an issue on GitHub_. If you have questions, suggestions,
-etc. feel free to send me an e-mail at `peter@peterodding.com`_.
-
-License
--------
-
-This software is licensed under the `MIT license`_.
-
-© 2021 Peter Odding.
-
-.. External references:
-.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
-.. _#8: https://github.com/xolox/python-humanfriendly/pull/8
-.. _#9: https://github.com/xolox/python-humanfriendly/pull/9
-.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
-.. _changelog: https://humanfriendly.readthedocs.io/en/latest/changelog.html
-.. _colorama: https://pypi.org/project/colorama
-.. _format_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.format_size
-.. _GitHub: https://github.com/xolox/python-humanfriendly
-.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
-.. _parse_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.parse_size
-.. _peter@peterodding.com: peter@peterodding.com
-.. _PyPI: https://pypi.org/project/humanfriendly
-.. _Read the Docs: https://humanfriendly.readthedocs.io
-
-
diff --git a/contrib/python/humanfriendly/py3/.dist-info/entry_points.txt b/contrib/python/humanfriendly/py3/.dist-info/entry_points.txt
deleted file mode 100644
index 2ce8fb8353..0000000000
--- a/contrib/python/humanfriendly/py3/.dist-info/entry_points.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-[console_scripts]
-humanfriendly = humanfriendly.cli:main
-
diff --git a/contrib/python/humanfriendly/py3/.dist-info/top_level.txt b/contrib/python/humanfriendly/py3/.dist-info/top_level.txt
deleted file mode 100644
index f5368c4974..0000000000
--- a/contrib/python/humanfriendly/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-humanfriendly
diff --git a/contrib/python/humanfriendly/py3/LICENSE.txt b/contrib/python/humanfriendly/py3/LICENSE.txt
deleted file mode 100644
index 96ece318ed..0000000000
--- a/contrib/python/humanfriendly/py3/LICENSE.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2021 Peter Odding
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/contrib/python/humanfriendly/py3/README.rst b/contrib/python/humanfriendly/py3/README.rst
deleted file mode 100644
index 80145d564c..0000000000
--- a/contrib/python/humanfriendly/py3/README.rst
+++ /dev/null
@@ -1,170 +0,0 @@
-humanfriendly: Human friendly input/output in Python
-====================================================
-
-.. image:: https://github.com/xolox/python-humanfriendly/actions/workflows/test.yml/badge.svg?branch=master
- :target: https://github.com/xolox/python-humanfriendly/actions
-
-.. image:: https://codecov.io/gh/xolox/python-humanfriendly/branch/master/graph/badge.svg?token=jYaj4T74TU
- :target: https://codecov.io/gh/xolox/python-humanfriendly
-
-The functions and classes in the `humanfriendly` package can be used to make
-text interfaces more user friendly. Some example features:
-
-- Parsing and formatting numbers, file sizes, pathnames and timespans in
- simple, human friendly formats.
-
-- Easy to use timers for long running operations, with human friendly
- formatting of the resulting timespans.
-
-- Prompting the user to select a choice from a list of options by typing the
- option's number or a unique substring of the option.
-
-- Terminal interaction including text styling (`ANSI escape sequences`_), user
- friendly rendering of usage messages and querying the terminal for its
- size.
-
-The `humanfriendly` package is currently tested on Python 2.7, 3.5+ and PyPy
-(2.7) on Linux and macOS. While the intention is to support Windows as well,
-you may encounter some rough edges.
-
-.. contents::
- :local:
-
-Getting started
----------------
-
-It's very simple to start using the `humanfriendly` package::
-
- >>> from humanfriendly import format_size, parse_size
- >>> from humanfriendly.prompts import prompt_for_input
- >>> user_input = prompt_for_input("Enter a readable file size: ")
-
- Enter a readable file size: 16G
-
- >>> num_bytes = parse_size(user_input)
- >>> print(num_bytes)
- 16000000000
- >>> print("You entered:", format_size(num_bytes))
- You entered: 16 GB
- >>> print("You entered:", format_size(num_bytes, binary=True))
- You entered: 14.9 GiB
-
-To get a demonstration of supported terminal text styles (based on
-`ANSI escape sequences`_) you can run the following command::
-
- $ humanfriendly --demo
-
-Command line
-------------
-
-.. A DRY solution to avoid duplication of the `humanfriendly --help' text:
-..
-.. [[[cog
-.. from humanfriendly.usage import inject_usage
-.. inject_usage('humanfriendly.cli')
-.. ]]]
-
-**Usage:** `humanfriendly [OPTIONS]`
-
-Human friendly input/output (text formatting) on the command
-line based on the Python package with the same name.
-
-**Supported options:**
-
-.. csv-table::
- :header: Option, Description
- :widths: 30, 70
-
-
- "``-c``, ``--run-command``","Execute an external command (given as the positional arguments) and render
- a spinner and timer while the command is running. The exit status of the
- command is propagated."
- ``--format-table``,"Read tabular data from standard input (each line is a row and each
- whitespace separated field is a column), format the data as a table and
- print the resulting table to standard output. See also the ``--delimiter``
- option."
- "``-d``, ``--delimiter=VALUE``","Change the delimiter used by ``--format-table`` to ``VALUE`` (a string). By default
- all whitespace is treated as a delimiter."
- "``-l``, ``--format-length=LENGTH``","Convert a length count (given as the integer or float ``LENGTH``) into a human
- readable string and print that string to standard output."
- "``-n``, ``--format-number=VALUE``","Format a number (given as the integer or floating point number ``VALUE``) with
- thousands separators and two decimal places (if needed) and print the
- formatted number to standard output."
- "``-s``, ``--format-size=BYTES``","Convert a byte count (given as the integer ``BYTES``) into a human readable
- string and print that string to standard output."
- "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to use binary multiples of bytes
- (base-2) instead of the default decimal multiples of bytes (base-10)."
- "``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given as the floating point number ``SECONDS``)
- into a human readable timespan and print that string to standard output."
- ``--parse-length=VALUE``,"Parse a human readable length (given as the string ``VALUE``) and print the
- number of metres to standard output."
- ``--parse-size=VALUE``,"Parse a human readable data size (given as the string ``VALUE``) and print the
- number of bytes to standard output."
- ``--demo``,"Demonstrate changing the style and color of the terminal font using ANSI
- escape sequences."
- "``-h``, ``--help``",Show this message and exit.
-
-.. [[[end]]]
-
-A note about size units
------------------------
-
-When I originally published the `humanfriendly` package I went with binary
-multiples of bytes (powers of two). It was pointed out several times that this
-was a poor choice (see issue `#4`_ and pull requests `#8`_ and `#9`_) and thus
-the new default became decimal multiples of bytes (powers of ten):
-
-+------+---------------+---------------+
-| Unit | Binary value | Decimal value |
-+------+---------------+---------------+
-| KB | 1024 | 1000 +
-+------+---------------+---------------+
-| MB | 1048576 | 1000000 |
-+------+---------------+---------------+
-| GB | 1073741824 | 1000000000 |
-+------+---------------+---------------+
-| TB | 1099511627776 | 1000000000000 |
-+------+---------------+---------------+
-| etc | | |
-+------+---------------+---------------+
-
-The option to use binary multiples of bytes remains by passing the keyword
-argument `binary=True` to the `format_size()`_ and `parse_size()`_ functions.
-
-Windows support
----------------
-
-Windows 10 gained native support for ANSI escape sequences which means commands
-like ``humanfriendly --demo`` should work out of the box (if your system is
-up-to-date enough). If this doesn't work then you can install the colorama_
-package, it will be used automatically once installed.
-
-Contact
--------
-
-The latest version of `humanfriendly` is available on PyPI_ and GitHub_. The
-documentation is hosted on `Read the Docs`_ and includes a changelog_. For bug
-reports please create an issue on GitHub_. If you have questions, suggestions,
-etc. feel free to send me an e-mail at `peter@peterodding.com`_.
-
-License
--------
-
-This software is licensed under the `MIT license`_.
-
-© 2021 Peter Odding.
-
-.. External references:
-.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
-.. _#8: https://github.com/xolox/python-humanfriendly/pull/8
-.. _#9: https://github.com/xolox/python-humanfriendly/pull/9
-.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
-.. _changelog: https://humanfriendly.readthedocs.io/en/latest/changelog.html
-.. _colorama: https://pypi.org/project/colorama
-.. _format_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.format_size
-.. _GitHub: https://github.com/xolox/python-humanfriendly
-.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
-.. _parse_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.parse_size
-.. _peter@peterodding.com: peter@peterodding.com
-.. _PyPI: https://pypi.org/project/humanfriendly
-.. _Read the Docs: https://humanfriendly.readthedocs.io
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/__init__.py b/contrib/python/humanfriendly/py3/humanfriendly/__init__.py
deleted file mode 100644
index 4c0a333861..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/__init__.py
+++ /dev/null
@@ -1,838 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: September 17, 2021
-# URL: https://humanfriendly.readthedocs.io
-
-"""The main module of the `humanfriendly` package."""
-
-# Standard library modules.
-import collections
-import datetime
-import decimal
-import numbers
-import os
-import os.path
-import re
-import time
-
-# Modules included in our package.
-from humanfriendly.compat import is_string, monotonic
-from humanfriendly.deprecation import define_aliases
-from humanfriendly.text import concatenate, format, pluralize, tokenize
-
-# Public identifiers that require documentation.
-__all__ = (
- 'CombinedUnit',
- 'InvalidDate',
- 'InvalidLength',
- 'InvalidSize',
- 'InvalidTimespan',
- 'SizeUnit',
- 'Timer',
- '__version__',
- 'coerce_boolean',
- 'coerce_pattern',
- 'coerce_seconds',
- 'disk_size_units',
- 'format_length',
- 'format_number',
- 'format_path',
- 'format_size',
- 'format_timespan',
- 'length_size_units',
- 'parse_date',
- 'parse_length',
- 'parse_path',
- 'parse_size',
- 'parse_timespan',
- 'round_number',
- 'time_units',
-)
-
-# Semi-standard module versioning.
-__version__ = '10.0'
-
-# Named tuples to define units of size.
-SizeUnit = collections.namedtuple('SizeUnit', 'divider, symbol, name')
-CombinedUnit = collections.namedtuple('CombinedUnit', 'decimal, binary')
-
-# Common disk size units in binary (base-2) and decimal (base-10) multiples.
-disk_size_units = (
- CombinedUnit(SizeUnit(1000**1, 'KB', 'kilobyte'), SizeUnit(1024**1, 'KiB', 'kibibyte')),
- CombinedUnit(SizeUnit(1000**2, 'MB', 'megabyte'), SizeUnit(1024**2, 'MiB', 'mebibyte')),
- CombinedUnit(SizeUnit(1000**3, 'GB', 'gigabyte'), SizeUnit(1024**3, 'GiB', 'gibibyte')),
- CombinedUnit(SizeUnit(1000**4, 'TB', 'terabyte'), SizeUnit(1024**4, 'TiB', 'tebibyte')),
- CombinedUnit(SizeUnit(1000**5, 'PB', 'petabyte'), SizeUnit(1024**5, 'PiB', 'pebibyte')),
- CombinedUnit(SizeUnit(1000**6, 'EB', 'exabyte'), SizeUnit(1024**6, 'EiB', 'exbibyte')),
- CombinedUnit(SizeUnit(1000**7, 'ZB', 'zettabyte'), SizeUnit(1024**7, 'ZiB', 'zebibyte')),
- CombinedUnit(SizeUnit(1000**8, 'YB', 'yottabyte'), SizeUnit(1024**8, 'YiB', 'yobibyte')),
-)
-
-# Common length size units, used for formatting and parsing.
-length_size_units = (dict(prefix='nm', divider=1e-09, singular='nm', plural='nm'),
- dict(prefix='mm', divider=1e-03, singular='mm', plural='mm'),
- dict(prefix='cm', divider=1e-02, singular='cm', plural='cm'),
- dict(prefix='m', divider=1, singular='metre', plural='metres'),
- dict(prefix='km', divider=1000, singular='km', plural='km'))
-
-# Common time units, used for formatting of time spans.
-time_units = (dict(divider=1e-9, singular='nanosecond', plural='nanoseconds', abbreviations=['ns']),
- dict(divider=1e-6, singular='microsecond', plural='microseconds', abbreviations=['us']),
- dict(divider=1e-3, singular='millisecond', plural='milliseconds', abbreviations=['ms']),
- dict(divider=1, singular='second', plural='seconds', abbreviations=['s', 'sec', 'secs']),
- dict(divider=60, singular='minute', plural='minutes', abbreviations=['m', 'min', 'mins']),
- dict(divider=60 * 60, singular='hour', plural='hours', abbreviations=['h']),
- dict(divider=60 * 60 * 24, singular='day', plural='days', abbreviations=['d']),
- dict(divider=60 * 60 * 24 * 7, singular='week', plural='weeks', abbreviations=['w']),
- dict(divider=60 * 60 * 24 * 7 * 52, singular='year', plural='years', abbreviations=['y']))
-
-
-def coerce_boolean(value):
- """
- Coerce any value to a boolean.
-
- :param value: Any Python value. If the value is a string:
-
- - The strings '1', 'yes', 'true' and 'on' are coerced to :data:`True`.
- - The strings '0', 'no', 'false' and 'off' are coerced to :data:`False`.
- - Other strings raise an exception.
-
- Other Python values are coerced using :class:`bool`.
- :returns: A proper boolean value.
- :raises: :exc:`exceptions.ValueError` when the value is a string but
- cannot be coerced with certainty.
- """
- if is_string(value):
- normalized = value.strip().lower()
- if normalized in ('1', 'yes', 'true', 'on'):
- return True
- elif normalized in ('0', 'no', 'false', 'off', ''):
- return False
- else:
- msg = "Failed to coerce string to boolean! (%r)"
- raise ValueError(format(msg, value))
- else:
- return bool(value)
-
-
-def coerce_pattern(value, flags=0):
- """
- Coerce strings to compiled regular expressions.
-
- :param value: A string containing a regular expression pattern
- or a compiled regular expression.
- :param flags: The flags used to compile the pattern (an integer).
- :returns: A compiled regular expression.
- :raises: :exc:`~exceptions.ValueError` when `value` isn't a string
- and also isn't a compiled regular expression.
- """
- if is_string(value):
- value = re.compile(value, flags)
- else:
- empty_pattern = re.compile('')
- pattern_type = type(empty_pattern)
- if not isinstance(value, pattern_type):
- msg = "Failed to coerce value to compiled regular expression! (%r)"
- raise ValueError(format(msg, value))
- return value
-
-
-def coerce_seconds(value):
- """
- Coerce a value to the number of seconds.
-
- :param value: An :class:`int`, :class:`float` or
- :class:`datetime.timedelta` object.
- :returns: An :class:`int` or :class:`float` value.
-
- When `value` is a :class:`datetime.timedelta` object the
- :meth:`~datetime.timedelta.total_seconds()` method is called.
- """
- if isinstance(value, datetime.timedelta):
- return value.total_seconds()
- if not isinstance(value, numbers.Number):
- msg = "Failed to coerce value to number of seconds! (%r)"
- raise ValueError(format(msg, value))
- return value
-
-
-def format_size(num_bytes, keep_width=False, binary=False):
- """
- Format a byte count as a human readable file size.
-
- :param num_bytes: The size to format in bytes (an integer).
- :param keep_width: :data:`True` if trailing zeros should not be stripped,
- :data:`False` if they can be stripped.
- :param binary: :data:`True` to use binary multiples of bytes (base-2),
- :data:`False` to use decimal multiples of bytes (base-10).
- :returns: The corresponding human readable file size (a string).
-
- This function knows how to format sizes in bytes, kilobytes, megabytes,
- gigabytes, terabytes and petabytes. Some examples:
-
- >>> from humanfriendly import format_size
- >>> format_size(0)
- '0 bytes'
- >>> format_size(1)
- '1 byte'
- >>> format_size(5)
- '5 bytes'
- > format_size(1000)
- '1 KB'
- > format_size(1024, binary=True)
- '1 KiB'
- >>> format_size(1000 ** 3 * 4)
- '4 GB'
- """
- for unit in reversed(disk_size_units):
- if num_bytes >= unit.binary.divider and binary:
- number = round_number(float(num_bytes) / unit.binary.divider, keep_width=keep_width)
- return pluralize(number, unit.binary.symbol, unit.binary.symbol)
- elif num_bytes >= unit.decimal.divider and not binary:
- number = round_number(float(num_bytes) / unit.decimal.divider, keep_width=keep_width)
- return pluralize(number, unit.decimal.symbol, unit.decimal.symbol)
- return pluralize(num_bytes, 'byte')
-
-
-def parse_size(size, binary=False):
- """
- Parse a human readable data size and return the number of bytes.
-
- :param size: The human readable file size to parse (a string).
- :param binary: :data:`True` to use binary multiples of bytes (base-2) for
- ambiguous unit symbols and names, :data:`False` to use
- decimal multiples of bytes (base-10).
- :returns: The corresponding size in bytes (an integer).
- :raises: :exc:`InvalidSize` when the input can't be parsed.
-
- This function knows how to parse sizes in bytes, kilobytes, megabytes,
- gigabytes, terabytes and petabytes. Some examples:
-
- >>> from humanfriendly import parse_size
- >>> parse_size('42')
- 42
- >>> parse_size('13b')
- 13
- >>> parse_size('5 bytes')
- 5
- >>> parse_size('1 KB')
- 1000
- >>> parse_size('1 kilobyte')
- 1000
- >>> parse_size('1 KiB')
- 1024
- >>> parse_size('1 KB', binary=True)
- 1024
- >>> parse_size('1.5 GB')
- 1500000000
- >>> parse_size('1.5 GB', binary=True)
- 1610612736
- """
- tokens = tokenize(size)
- if tokens and isinstance(tokens[0], numbers.Number):
- # Get the normalized unit (if any) from the tokenized input.
- normalized_unit = tokens[1].lower() if len(tokens) == 2 and is_string(tokens[1]) else ''
- # If the input contains only a number, it's assumed to be the number of
- # bytes. The second token can also explicitly reference the unit bytes.
- if len(tokens) == 1 or normalized_unit.startswith('b'):
- return int(tokens[0])
- # Otherwise we expect two tokens: A number and a unit.
- if normalized_unit:
- # Convert plural units to singular units, for details:
- # https://github.com/xolox/python-humanfriendly/issues/26
- normalized_unit = normalized_unit.rstrip('s')
- for unit in disk_size_units:
- # First we check for unambiguous symbols (KiB, MiB, GiB, etc)
- # and names (kibibyte, mebibyte, gibibyte, etc) because their
- # handling is always the same.
- if normalized_unit in (unit.binary.symbol.lower(), unit.binary.name.lower()):
- return int(tokens[0] * unit.binary.divider)
- # Now we will deal with ambiguous prefixes (K, M, G, etc),
- # symbols (KB, MB, GB, etc) and names (kilobyte, megabyte,
- # gigabyte, etc) according to the caller's preference.
- if (normalized_unit in (unit.decimal.symbol.lower(), unit.decimal.name.lower()) or
- normalized_unit.startswith(unit.decimal.symbol[0].lower())):
- return int(tokens[0] * (unit.binary.divider if binary else unit.decimal.divider))
- # We failed to parse the size specification.
- msg = "Failed to parse size! (input %r was tokenized as %r)"
- raise InvalidSize(format(msg, size, tokens))
-
-
-def format_length(num_metres, keep_width=False):
- """
- Format a metre count as a human readable length.
-
- :param num_metres: The length to format in metres (float / integer).
- :param keep_width: :data:`True` if trailing zeros should not be stripped,
- :data:`False` if they can be stripped.
- :returns: The corresponding human readable length (a string).
-
- This function supports ranges from nanometres to kilometres.
-
- Some examples:
-
- >>> from humanfriendly import format_length
- >>> format_length(0)
- '0 metres'
- >>> format_length(1)
- '1 metre'
- >>> format_length(5)
- '5 metres'
- >>> format_length(1000)
- '1 km'
- >>> format_length(0.004)
- '4 mm'
- """
- for unit in reversed(length_size_units):
- if num_metres >= unit['divider']:
- number = round_number(float(num_metres) / unit['divider'], keep_width=keep_width)
- return pluralize(number, unit['singular'], unit['plural'])
- return pluralize(num_metres, 'metre')
-
-
-def parse_length(length):
- """
- Parse a human readable length and return the number of metres.
-
- :param length: The human readable length to parse (a string).
- :returns: The corresponding length in metres (a float).
- :raises: :exc:`InvalidLength` when the input can't be parsed.
-
- Some examples:
-
- >>> from humanfriendly import parse_length
- >>> parse_length('42')
- 42
- >>> parse_length('1 km')
- 1000
- >>> parse_length('5mm')
- 0.005
- >>> parse_length('15.3cm')
- 0.153
- """
- tokens = tokenize(length)
- if tokens and isinstance(tokens[0], numbers.Number):
- # If the input contains only a number, it's assumed to be the number of metres.
- if len(tokens) == 1:
- return tokens[0]
- # Otherwise we expect to find two tokens: A number and a unit.
- if len(tokens) == 2 and is_string(tokens[1]):
- normalized_unit = tokens[1].lower()
- # Try to match the first letter of the unit.
- for unit in length_size_units:
- if normalized_unit.startswith(unit['prefix']):
- return tokens[0] * unit['divider']
- # We failed to parse the length specification.
- msg = "Failed to parse length! (input %r was tokenized as %r)"
- raise InvalidLength(format(msg, length, tokens))
-
-
-def format_number(number, num_decimals=2):
- """
- Format a number as a string including thousands separators.
-
- :param number: The number to format (a number like an :class:`int`,
- :class:`long` or :class:`float`).
- :param num_decimals: The number of decimals to render (2 by default). If no
- decimal places are required to represent the number
- they will be omitted regardless of this argument.
- :returns: The formatted number (a string).
-
- This function is intended to make it easier to recognize the order of size
- of the number being formatted.
-
- Here's an example:
-
- >>> from humanfriendly import format_number
- >>> print(format_number(6000000))
- 6,000,000
- > print(format_number(6000000000.42))
- 6,000,000,000.42
- > print(format_number(6000000000.42, num_decimals=0))
- 6,000,000,000
- """
- integer_part, _, decimal_part = str(float(number)).partition('.')
- negative_sign = integer_part.startswith('-')
- reversed_digits = ''.join(reversed(integer_part.lstrip('-')))
- parts = []
- while reversed_digits:
- parts.append(reversed_digits[:3])
- reversed_digits = reversed_digits[3:]
- formatted_number = ''.join(reversed(','.join(parts)))
- decimals_to_add = decimal_part[:num_decimals].rstrip('0')
- if decimals_to_add:
- formatted_number += '.' + decimals_to_add
- if negative_sign:
- formatted_number = '-' + formatted_number
- return formatted_number
-
-
-def round_number(count, keep_width=False):
- """
- Round a floating point number to two decimal places in a human friendly format.
-
- :param count: The number to format.
- :param keep_width: :data:`True` if trailing zeros should not be stripped,
- :data:`False` if they can be stripped.
- :returns: The formatted number as a string. If no decimal places are
- required to represent the number, they will be omitted.
-
- The main purpose of this function is to be used by functions like
- :func:`format_length()`, :func:`format_size()` and
- :func:`format_timespan()`.
-
- Here are some examples:
-
- >>> from humanfriendly import round_number
- >>> round_number(1)
- '1'
- >>> round_number(math.pi)
- '3.14'
- >>> round_number(5.001)
- '5'
- """
- text = '%.2f' % float(count)
- if not keep_width:
- text = re.sub('0+$', '', text)
- text = re.sub(r'\.$', '', text)
- return text
-
-
-def format_timespan(num_seconds, detailed=False, max_units=3):
- """
- Format a timespan in seconds as a human readable string.
-
- :param num_seconds: Any value accepted by :func:`coerce_seconds()`.
- :param detailed: If :data:`True` milliseconds are represented separately
- instead of being represented as fractional seconds
- (defaults to :data:`False`).
- :param max_units: The maximum number of units to show in the formatted time
- span (an integer, defaults to three).
- :returns: The formatted timespan as a string.
- :raise: See :func:`coerce_seconds()`.
-
- Some examples:
-
- >>> from humanfriendly import format_timespan
- >>> format_timespan(0)
- '0 seconds'
- >>> format_timespan(1)
- '1 second'
- >>> import math
- >>> format_timespan(math.pi)
- '3.14 seconds'
- >>> hour = 60 * 60
- >>> day = hour * 24
- >>> week = day * 7
- >>> format_timespan(week * 52 + day * 2 + hour * 3)
- '1 year, 2 days and 3 hours'
- """
- num_seconds = coerce_seconds(num_seconds)
- if num_seconds < 60 and not detailed:
- # Fast path.
- return pluralize(round_number(num_seconds), 'second')
- else:
- # Slow path.
- result = []
- num_seconds = decimal.Decimal(str(num_seconds))
- relevant_units = list(reversed(time_units[0 if detailed else 3:]))
- for unit in relevant_units:
- # Extract the unit count from the remaining time.
- divider = decimal.Decimal(str(unit['divider']))
- count = num_seconds / divider
- num_seconds %= divider
- # Round the unit count appropriately.
- if unit != relevant_units[-1]:
- # Integer rounding for all but the smallest unit.
- count = int(count)
- else:
- # Floating point rounding for the smallest unit.
- count = round_number(count)
- # Only include relevant units in the result.
- if count not in (0, '0'):
- result.append(pluralize(count, unit['singular'], unit['plural']))
- if len(result) == 1:
- # A single count/unit combination.
- return result[0]
- else:
- if not detailed:
- # Remove `insignificant' data from the formatted timespan.
- result = result[:max_units]
- # Format the timespan in a readable way.
- return concatenate(result)
-
-
-def parse_timespan(timespan):
- """
- Parse a "human friendly" timespan into the number of seconds.
-
- :param value: A string like ``5h`` (5 hours), ``10m`` (10 minutes) or
- ``42s`` (42 seconds).
- :returns: The number of seconds as a floating point number.
- :raises: :exc:`InvalidTimespan` when the input can't be parsed.
-
- Note that the :func:`parse_timespan()` function is not meant to be the
- "mirror image" of the :func:`format_timespan()` function. Instead it's
- meant to allow humans to easily and succinctly specify a timespan with a
- minimal amount of typing. It's very useful to accept easy to write time
- spans as e.g. command line arguments to programs.
-
- The time units (and abbreviations) supported by this function are:
-
- - ms, millisecond, milliseconds
- - s, sec, secs, second, seconds
- - m, min, mins, minute, minutes
- - h, hour, hours
- - d, day, days
- - w, week, weeks
- - y, year, years
-
- Some examples:
-
- >>> from humanfriendly import parse_timespan
- >>> parse_timespan('42')
- 42.0
- >>> parse_timespan('42s')
- 42.0
- >>> parse_timespan('1m')
- 60.0
- >>> parse_timespan('1h')
- 3600.0
- >>> parse_timespan('1d')
- 86400.0
- """
- tokens = tokenize(timespan)
- if tokens and isinstance(tokens[0], numbers.Number):
- # If the input contains only a number, it's assumed to be the number of seconds.
- if len(tokens) == 1:
- return float(tokens[0])
- # Otherwise we expect to find two tokens: A number and a unit.
- if len(tokens) == 2 and is_string(tokens[1]):
- normalized_unit = tokens[1].lower()
- for unit in time_units:
- if (normalized_unit == unit['singular'] or
- normalized_unit == unit['plural'] or
- normalized_unit in unit['abbreviations']):
- return float(tokens[0]) * unit['divider']
- # We failed to parse the timespan specification.
- msg = "Failed to parse timespan! (input %r was tokenized as %r)"
- raise InvalidTimespan(format(msg, timespan, tokens))
-
-
-def parse_date(datestring):
- """
- Parse a date/time string into a tuple of integers.
-
- :param datestring: The date/time string to parse.
- :returns: A tuple with the numbers ``(year, month, day, hour, minute,
- second)`` (all numbers are integers).
- :raises: :exc:`InvalidDate` when the date cannot be parsed.
-
- Supported date/time formats:
-
- - ``YYYY-MM-DD``
- - ``YYYY-MM-DD HH:MM:SS``
-
- .. note:: If you want to parse date/time strings with a fixed, known
- format and :func:`parse_date()` isn't useful to you, consider
- :func:`time.strptime()` or :meth:`datetime.datetime.strptime()`,
- both of which are included in the Python standard library.
- Alternatively for more complex tasks consider using the date/time
- parsing module in the dateutil_ package.
-
- Examples:
-
- >>> from humanfriendly import parse_date
- >>> parse_date('2013-06-17')
- (2013, 6, 17, 0, 0, 0)
- >>> parse_date('2013-06-17 02:47:42')
- (2013, 6, 17, 2, 47, 42)
-
- Here's how you convert the result to a number (`Unix time`_):
-
- >>> from humanfriendly import parse_date
- >>> from time import mktime
- >>> mktime(parse_date('2013-06-17 02:47:42') + (-1, -1, -1))
- 1371430062.0
-
- And here's how you convert it to a :class:`datetime.datetime` object:
-
- >>> from humanfriendly import parse_date
- >>> from datetime import datetime
- >>> datetime(*parse_date('2013-06-17 02:47:42'))
- datetime.datetime(2013, 6, 17, 2, 47, 42)
-
- Here's an example that combines :func:`format_timespan()` and
- :func:`parse_date()` to calculate a human friendly timespan since a
- given date:
-
- >>> from humanfriendly import format_timespan, parse_date
- >>> from time import mktime, time
- >>> unix_time = mktime(parse_date('2013-06-17 02:47:42') + (-1, -1, -1))
- >>> seconds_since_then = time() - unix_time
- >>> print(format_timespan(seconds_since_then))
- 1 year, 43 weeks and 1 day
-
- .. _dateutil: https://dateutil.readthedocs.io/en/latest/parser.html
- .. _Unix time: http://en.wikipedia.org/wiki/Unix_time
- """
- try:
- tokens = [t.strip() for t in datestring.split()]
- if len(tokens) >= 2:
- date_parts = list(map(int, tokens[0].split('-'))) + [1, 1]
- time_parts = list(map(int, tokens[1].split(':'))) + [0, 0, 0]
- return tuple(date_parts[0:3] + time_parts[0:3])
- else:
- year, month, day = (list(map(int, datestring.split('-'))) + [1, 1])[0:3]
- return (year, month, day, 0, 0, 0)
- except Exception:
- msg = "Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' but got: %r)"
- raise InvalidDate(format(msg, datestring))
-
-
-def format_path(pathname):
- """
- Shorten a pathname to make it more human friendly.
-
- :param pathname: An absolute pathname (a string).
- :returns: The pathname with the user's home directory abbreviated.
-
- Given an absolute pathname, this function abbreviates the user's home
- directory to ``~/`` in order to shorten the pathname without losing
- information. It is not an error if the pathname is not relative to the
- current user's home directory.
-
- Here's an example of its usage:
-
- >>> from os import environ
- >>> from os.path import join
- >>> vimrc = join(environ['HOME'], '.vimrc')
- >>> vimrc
- '/home/peter/.vimrc'
- >>> from humanfriendly import format_path
- >>> format_path(vimrc)
- '~/.vimrc'
- """
- pathname = os.path.abspath(pathname)
- home = os.environ.get('HOME')
- if home:
- home = os.path.abspath(home)
- if pathname.startswith(home):
- pathname = os.path.join('~', os.path.relpath(pathname, home))
- return pathname
-
-
-def parse_path(pathname):
- """
- Convert a human friendly pathname to an absolute pathname.
-
- Expands leading tildes using :func:`os.path.expanduser()` and
- environment variables using :func:`os.path.expandvars()` and makes the
- resulting pathname absolute using :func:`os.path.abspath()`.
-
- :param pathname: A human friendly pathname (a string).
- :returns: An absolute pathname (a string).
- """
- return os.path.abspath(os.path.expanduser(os.path.expandvars(pathname)))
-
-
-class Timer(object):
-
- """
- Easy to use timer to keep track of long during operations.
- """
-
- def __init__(self, start_time=None, resumable=False):
- """
- Remember the time when the :class:`Timer` was created.
-
- :param start_time: The start time (a float, defaults to the current time).
- :param resumable: Create a resumable timer (defaults to :data:`False`).
-
- When `start_time` is given :class:`Timer` uses :func:`time.time()` as a
- clock source, otherwise it uses :func:`humanfriendly.compat.monotonic()`.
- """
- if resumable:
- self.monotonic = True
- self.resumable = True
- self.start_time = 0.0
- self.total_time = 0.0
- elif start_time:
- self.monotonic = False
- self.resumable = False
- self.start_time = start_time
- else:
- self.monotonic = True
- self.resumable = False
- self.start_time = monotonic()
-
- def __enter__(self):
- """
- Start or resume counting elapsed time.
-
- :returns: The :class:`Timer` object.
- :raises: :exc:`~exceptions.ValueError` when the timer isn't resumable.
- """
- if not self.resumable:
- raise ValueError("Timer is not resumable!")
- self.start_time = monotonic()
- return self
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """
- Stop counting elapsed time.
-
- :raises: :exc:`~exceptions.ValueError` when the timer isn't resumable.
- """
- if not self.resumable:
- raise ValueError("Timer is not resumable!")
- if self.start_time:
- self.total_time += monotonic() - self.start_time
- self.start_time = 0.0
-
- def sleep(self, seconds):
- """
- Easy to use rate limiting of repeating actions.
-
- :param seconds: The number of seconds to sleep (an
- integer or floating point number).
-
- This method sleeps for the given number of seconds minus the
- :attr:`elapsed_time`. If the resulting duration is negative
- :func:`time.sleep()` will still be called, but the argument
- given to it will be the number 0 (negative numbers cause
- :func:`time.sleep()` to raise an exception).
-
- The use case for this is to initialize a :class:`Timer` inside
- the body of a :keyword:`for` or :keyword:`while` loop and call
- :func:`Timer.sleep()` at the end of the loop body to rate limit
- whatever it is that is being done inside the loop body.
-
- For posterity: Although the implementation of :func:`sleep()` only
- requires a single line of code I've added it to :mod:`humanfriendly`
- anyway because now that I've thought about how to tackle this once I
- never want to have to think about it again :-P (unless I find ways to
- improve this).
- """
- time.sleep(max(0, seconds - self.elapsed_time))
-
- @property
- def elapsed_time(self):
- """
- Get the number of seconds counted so far.
- """
- elapsed_time = 0
- if self.resumable:
- elapsed_time += self.total_time
- if self.start_time:
- current_time = monotonic() if self.monotonic else time.time()
- elapsed_time += current_time - self.start_time
- return elapsed_time
-
- @property
- def rounded(self):
- """Human readable timespan rounded to seconds (a string)."""
- return format_timespan(round(self.elapsed_time))
-
- def __str__(self):
- """Show the elapsed time since the :class:`Timer` was created."""
- return format_timespan(self.elapsed_time)
-
-
-class InvalidDate(Exception):
-
- """
- Raised when a string cannot be parsed into a date.
-
- For example:
-
- >>> from humanfriendly import parse_date
- >>> parse_date('2013-06-XY')
- Traceback (most recent call last):
- File "humanfriendly.py", line 206, in parse_date
- raise InvalidDate(format(msg, datestring))
- humanfriendly.InvalidDate: Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' but got: '2013-06-XY')
- """
-
-
-class InvalidSize(Exception):
-
- """
- Raised when a string cannot be parsed into a file size.
-
- For example:
-
- >>> from humanfriendly import parse_size
- >>> parse_size('5 Z')
- Traceback (most recent call last):
- File "humanfriendly/__init__.py", line 267, in parse_size
- raise InvalidSize(format(msg, size, tokens))
- humanfriendly.InvalidSize: Failed to parse size! (input '5 Z' was tokenized as [5, 'Z'])
- """
-
-
-class InvalidLength(Exception):
-
- """
- Raised when a string cannot be parsed into a length.
-
- For example:
-
- >>> from humanfriendly import parse_length
- >>> parse_length('5 Z')
- Traceback (most recent call last):
- File "humanfriendly/__init__.py", line 267, in parse_length
- raise InvalidLength(format(msg, length, tokens))
- humanfriendly.InvalidLength: Failed to parse length! (input '5 Z' was tokenized as [5, 'Z'])
- """
-
-
-class InvalidTimespan(Exception):
-
- """
- Raised when a string cannot be parsed into a timespan.
-
- For example:
-
- >>> from humanfriendly import parse_timespan
- >>> parse_timespan('1 age')
- Traceback (most recent call last):
- File "humanfriendly/__init__.py", line 419, in parse_timespan
- raise InvalidTimespan(format(msg, timespan, tokens))
- humanfriendly.InvalidTimespan: Failed to parse timespan! (input '1 age' was tokenized as [1, 'age'])
- """
-
-
-# Define aliases for backwards compatibility.
-define_aliases(
- module_name=__name__,
- # In humanfriendly 1.23 the format_table() function was added to render a
- # table using characters like dashes and vertical bars to emulate borders.
- # Since then support for other tables has been added and the name of
- # format_table() has changed.
- format_table='humanfriendly.tables.format_pretty_table',
- # In humanfriendly 1.30 the following text manipulation functions were
- # moved out into a separate module to enable their usage in other modules
- # of the humanfriendly package (without causing circular imports).
- compact='humanfriendly.text.compact',
- concatenate='humanfriendly.text.concatenate',
- dedent='humanfriendly.text.dedent',
- format='humanfriendly.text.format',
- is_empty_line='humanfriendly.text.is_empty_line',
- pluralize='humanfriendly.text.pluralize',
- tokenize='humanfriendly.text.tokenize',
- trim_empty_lines='humanfriendly.text.trim_empty_lines',
- # In humanfriendly 1.38 the prompt_for_choice() function was moved out into a
- # separate module because several variants of interactive prompts were added.
- prompt_for_choice='humanfriendly.prompts.prompt_for_choice',
- # In humanfriendly 8.0 the Spinner class and minimum_spinner_interval
- # variable were extracted to a new module and the erase_line_code,
- # hide_cursor_code and show_cursor_code variables were moved.
- AutomaticSpinner='humanfriendly.terminal.spinners.AutomaticSpinner',
- Spinner='humanfriendly.terminal.spinners.Spinner',
- erase_line_code='humanfriendly.terminal.ANSI_ERASE_LINE',
- hide_cursor_code='humanfriendly.terminal.ANSI_SHOW_CURSOR',
- minimum_spinner_interval='humanfriendly.terminal.spinners.MINIMUM_INTERVAL',
- show_cursor_code='humanfriendly.terminal.ANSI_HIDE_CURSOR',
-)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/case.py b/contrib/python/humanfriendly/py3/humanfriendly/case.py
deleted file mode 100644
index 4c71857e40..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/case.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: April 19, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Simple case insensitive dictionaries.
-
-The :class:`CaseInsensitiveDict` class is a dictionary whose string keys
-are case insensitive. It works by automatically coercing string keys to
-:class:`CaseInsensitiveKey` objects. Keys that are not strings are
-supported as well, just without case insensitivity.
-
-At its core this module works by normalizing strings to lowercase before
-comparing or hashing them. It doesn't support proper case folding nor
-does it support Unicode normalization, hence the word "simple".
-"""
-
-# Standard library modules.
-import collections
-
-try:
- # Python >= 3.3.
- from collections.abc import Iterable, Mapping
-except ImportError:
- # Python 2.7.
- from collections import Iterable, Mapping
-
-# Modules included in our package.
-from humanfriendly.compat import basestring, unicode
-
-# Public identifiers that require documentation.
-__all__ = ("CaseInsensitiveDict", "CaseInsensitiveKey")
-
-
-class CaseInsensitiveDict(collections.OrderedDict):
-
- """
- Simple case insensitive dictionary implementation (that remembers insertion order).
-
- This class works by overriding methods that deal with dictionary keys to
- coerce string keys to :class:`CaseInsensitiveKey` objects before calling
- down to the regular dictionary handling methods. While intended to be
- complete this class has not been extensively tested yet.
- """
-
- def __init__(self, other=None, **kw):
- """Initialize a :class:`CaseInsensitiveDict` object."""
- # Initialize our superclass.
- super(CaseInsensitiveDict, self).__init__()
- # Handle the initializer arguments.
- self.update(other, **kw)
-
- def coerce_key(self, key):
- """
- Coerce string keys to :class:`CaseInsensitiveKey` objects.
-
- :param key: The value to coerce (any type).
- :returns: If `key` is a string then a :class:`CaseInsensitiveKey`
- object is returned, otherwise the value of `key` is
- returned unmodified.
- """
- if isinstance(key, basestring):
- key = CaseInsensitiveKey(key)
- return key
-
- @classmethod
- def fromkeys(cls, iterable, value=None):
- """Create a case insensitive dictionary with keys from `iterable` and values set to `value`."""
- return cls((k, value) for k in iterable)
-
- def get(self, key, default=None):
- """Get the value of an existing item."""
- return super(CaseInsensitiveDict, self).get(self.coerce_key(key), default)
-
- def pop(self, key, default=None):
- """Remove an item from a case insensitive dictionary."""
- return super(CaseInsensitiveDict, self).pop(self.coerce_key(key), default)
-
- def setdefault(self, key, default=None):
- """Get the value of an existing item or add a new item."""
- return super(CaseInsensitiveDict, self).setdefault(self.coerce_key(key), default)
-
- def update(self, other=None, **kw):
- """Update a case insensitive dictionary with new items."""
- if isinstance(other, Mapping):
- # Copy the items from the given mapping.
- for key, value in other.items():
- self[key] = value
- elif isinstance(other, Iterable):
- # Copy the items from the given iterable.
- for key, value in other:
- self[key] = value
- elif other is not None:
- # Complain about unsupported values.
- msg = "'%s' object is not iterable"
- type_name = type(value).__name__
- raise TypeError(msg % type_name)
- # Copy the keyword arguments (if any).
- for key, value in kw.items():
- self[key] = value
-
- def __contains__(self, key):
- """Check if a case insensitive dictionary contains the given key."""
- return super(CaseInsensitiveDict, self).__contains__(self.coerce_key(key))
-
- def __delitem__(self, key):
- """Delete an item in a case insensitive dictionary."""
- return super(CaseInsensitiveDict, self).__delitem__(self.coerce_key(key))
-
- def __getitem__(self, key):
- """Get the value of an item in a case insensitive dictionary."""
- return super(CaseInsensitiveDict, self).__getitem__(self.coerce_key(key))
-
- def __setitem__(self, key, value):
- """Set the value of an item in a case insensitive dictionary."""
- return super(CaseInsensitiveDict, self).__setitem__(self.coerce_key(key), value)
-
-
-class CaseInsensitiveKey(unicode):
-
- """
- Simple case insensitive dictionary key implementation.
-
- The :class:`CaseInsensitiveKey` class provides an intentionally simple
- implementation of case insensitive strings to be used as dictionary keys.
-
- If you need features like Unicode normalization or proper case folding
- please consider using a more advanced implementation like the :pypi:`istr`
- package instead.
- """
-
- def __new__(cls, value):
- """Create a :class:`CaseInsensitiveKey` object."""
- # Delegate string object creation to our superclass.
- obj = unicode.__new__(cls, value)
- # Store the lowercased string and its hash value.
- normalized = obj.lower()
- obj._normalized = normalized
- obj._hash_value = hash(normalized)
- return obj
-
- def __hash__(self):
- """Get the hash value of the lowercased string."""
- return self._hash_value
-
- def __eq__(self, other):
- """Compare two strings as lowercase."""
- if isinstance(other, CaseInsensitiveKey):
- # Fast path (and the most common case): Comparison with same type.
- return self._normalized == other._normalized
- elif isinstance(other, unicode):
- # Slow path: Comparison with strings that need lowercasing.
- return self._normalized == other.lower()
- else:
- return NotImplemented
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/cli.py b/contrib/python/humanfriendly/py3/humanfriendly/cli.py
deleted file mode 100644
index eb81db172b..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/cli.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: March 1, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Usage: humanfriendly [OPTIONS]
-
-Human friendly input/output (text formatting) on the command
-line based on the Python package with the same name.
-
-Supported options:
-
- -c, --run-command
-
- Execute an external command (given as the positional arguments) and render
- a spinner and timer while the command is running. The exit status of the
- command is propagated.
-
- --format-table
-
- Read tabular data from standard input (each line is a row and each
- whitespace separated field is a column), format the data as a table and
- print the resulting table to standard output. See also the --delimiter
- option.
-
- -d, --delimiter=VALUE
-
- Change the delimiter used by --format-table to VALUE (a string). By default
- all whitespace is treated as a delimiter.
-
- -l, --format-length=LENGTH
-
- Convert a length count (given as the integer or float LENGTH) into a human
- readable string and print that string to standard output.
-
- -n, --format-number=VALUE
-
- Format a number (given as the integer or floating point number VALUE) with
- thousands separators and two decimal places (if needed) and print the
- formatted number to standard output.
-
- -s, --format-size=BYTES
-
- Convert a byte count (given as the integer BYTES) into a human readable
- string and print that string to standard output.
-
- -b, --binary
-
- Change the output of -s, --format-size to use binary multiples of bytes
- (base-2) instead of the default decimal multiples of bytes (base-10).
-
- -t, --format-timespan=SECONDS
-
- Convert a number of seconds (given as the floating point number SECONDS)
- into a human readable timespan and print that string to standard output.
-
- --parse-length=VALUE
-
- Parse a human readable length (given as the string VALUE) and print the
- number of metres to standard output.
-
- --parse-size=VALUE
-
- Parse a human readable data size (given as the string VALUE) and print the
- number of bytes to standard output.
-
- --demo
-
- Demonstrate changing the style and color of the terminal font using ANSI
- escape sequences.
-
- -h, --help
-
- Show this message and exit.
-"""
-
-# Standard library modules.
-import functools
-import getopt
-import pipes
-import subprocess
-import sys
-
-# Modules included in our package.
-from humanfriendly import (
- Timer,
- format_length,
- format_number,
- format_size,
- format_timespan,
- parse_length,
- parse_size,
-)
-from humanfriendly.tables import format_pretty_table, format_smart_table
-from humanfriendly.terminal import (
- ANSI_COLOR_CODES,
- ANSI_TEXT_STYLES,
- HIGHLIGHT_COLOR,
- ansi_strip,
- ansi_wrap,
- enable_ansi_support,
- find_terminal_size,
- output,
- usage,
- warning,
-)
-from humanfriendly.terminal.spinners import Spinner
-
-# Public identifiers that require documentation.
-__all__ = (
- 'demonstrate_256_colors',
- 'demonstrate_ansi_formatting',
- 'main',
- 'print_formatted_length',
- 'print_formatted_number',
- 'print_formatted_size',
- 'print_formatted_table',
- 'print_formatted_timespan',
- 'print_parsed_length',
- 'print_parsed_size',
- 'run_command',
-)
-
-
-def main():
- """Command line interface for the ``humanfriendly`` program."""
- enable_ansi_support()
- try:
- options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [
- 'run-command', 'format-table', 'delimiter=', 'format-length=',
- 'format-number=', 'format-size=', 'binary', 'format-timespan=',
- 'parse-length=', 'parse-size=', 'demo', 'help',
- ])
- except Exception as e:
- warning("Error: %s", e)
- sys.exit(1)
- actions = []
- delimiter = None
- should_format_table = False
- binary = any(o in ('-b', '--binary') for o, v in options)
- for option, value in options:
- if option in ('-d', '--delimiter'):
- delimiter = value
- elif option == '--parse-size':
- actions.append(functools.partial(print_parsed_size, value))
- elif option == '--parse-length':
- actions.append(functools.partial(print_parsed_length, value))
- elif option in ('-c', '--run-command'):
- actions.append(functools.partial(run_command, arguments))
- elif option in ('-l', '--format-length'):
- actions.append(functools.partial(print_formatted_length, value))
- elif option in ('-n', '--format-number'):
- actions.append(functools.partial(print_formatted_number, value))
- elif option in ('-s', '--format-size'):
- actions.append(functools.partial(print_formatted_size, value, binary))
- elif option == '--format-table':
- should_format_table = True
- elif option in ('-t', '--format-timespan'):
- actions.append(functools.partial(print_formatted_timespan, value))
- elif option == '--demo':
- actions.append(demonstrate_ansi_formatting)
- elif option in ('-h', '--help'):
- usage(__doc__)
- return
- if should_format_table:
- actions.append(functools.partial(print_formatted_table, delimiter))
- if not actions:
- usage(__doc__)
- return
- for partial in actions:
- partial()
-
-
-def run_command(command_line):
- """Run an external command and show a spinner while the command is running."""
- timer = Timer()
- spinner_label = "Waiting for command: %s" % " ".join(map(pipes.quote, command_line))
- with Spinner(label=spinner_label, timer=timer) as spinner:
- process = subprocess.Popen(command_line)
- while True:
- spinner.step()
- spinner.sleep()
- if process.poll() is not None:
- break
- sys.exit(process.returncode)
-
-
-def print_formatted_length(value):
- """Print a human readable length."""
- if '.' in value:
- output(format_length(float(value)))
- else:
- output(format_length(int(value)))
-
-
-def print_formatted_number(value):
- """Print large numbers in a human readable format."""
- output(format_number(float(value)))
-
-
-def print_formatted_size(value, binary):
- """Print a human readable size."""
- output(format_size(int(value), binary=binary))
-
-
-def print_formatted_table(delimiter):
- """Read tabular data from standard input and print a table."""
- data = []
- for line in sys.stdin:
- line = line.rstrip()
- data.append(line.split(delimiter))
- output(format_pretty_table(data))
-
-
-def print_formatted_timespan(value):
- """Print a human readable timespan."""
- output(format_timespan(float(value)))
-
-
-def print_parsed_length(value):
- """Parse a human readable length and print the number of metres."""
- output(parse_length(value))
-
-
-def print_parsed_size(value):
- """Parse a human readable data size and print the number of bytes."""
- output(parse_size(value))
-
-
-def demonstrate_ansi_formatting():
- """Demonstrate the use of ANSI escape sequences."""
- # First we demonstrate the supported text styles.
- output('%s', ansi_wrap('Text styles:', bold=True))
- styles = ['normal', 'bright']
- styles.extend(ANSI_TEXT_STYLES.keys())
- for style_name in sorted(styles):
- options = dict(color=HIGHLIGHT_COLOR)
- if style_name != 'normal':
- options[style_name] = True
- style_label = style_name.replace('_', ' ').capitalize()
- output(' - %s', ansi_wrap(style_label, **options))
- # Now we demonstrate named foreground and background colors.
- for color_type, color_label in (('color', 'Foreground colors'),
- ('background', 'Background colors')):
- intensities = [
- ('normal', dict()),
- ('bright', dict(bright=True)),
- ]
- if color_type != 'background':
- intensities.insert(0, ('faint', dict(faint=True)))
- output('\n%s' % ansi_wrap('%s:' % color_label, bold=True))
- output(format_smart_table([
- [color_name] + [
- ansi_wrap(
- 'XXXXXX' if color_type != 'background' else (' ' * 6),
- **dict(list(kw.items()) + [(color_type, color_name)])
- ) for label, kw in intensities
- ] for color_name in sorted(ANSI_COLOR_CODES.keys())
- ], column_names=['Color'] + [
- label.capitalize() for label, kw in intensities
- ]))
- # Demonstrate support for 256 colors as well.
- demonstrate_256_colors(0, 7, 'standard colors')
- demonstrate_256_colors(8, 15, 'high-intensity colors')
- demonstrate_256_colors(16, 231, '216 colors')
- demonstrate_256_colors(232, 255, 'gray scale colors')
-
-
-def demonstrate_256_colors(i, j, group=None):
- """Demonstrate 256 color mode support."""
- # Generate the label.
- label = '256 color mode'
- if group:
- label += ' (%s)' % group
- output('\n' + ansi_wrap('%s:' % label, bold=True))
- # Generate a simple rendering of the colors in the requested range and
- # check if it will fit on a single line (given the terminal's width).
- single_line = ''.join(' ' + ansi_wrap(str(n), color=n) for n in range(i, j + 1))
- lines, columns = find_terminal_size()
- if columns >= len(ansi_strip(single_line)):
- output(single_line)
- else:
- # Generate a more complex rendering of the colors that will nicely wrap
- # over multiple lines without using too many lines.
- width = len(str(j)) + 1
- colors_per_line = int(columns / width)
- colors = [ansi_wrap(str(n).rjust(width), color=n) for n in range(i, j + 1)]
- blocks = [colors[n:n + colors_per_line] for n in range(0, len(colors), colors_per_line)]
- output('\n'.join(''.join(b) for b in blocks))
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/compat.py b/contrib/python/humanfriendly/py3/humanfriendly/compat.py
deleted file mode 100644
index 24c9d1833a..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/compat.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: September 17, 2021
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Compatibility with Python 2 and 3.
-
-This module exposes aliases and functions that make it easier to write Python
-code that is compatible with Python 2 and Python 3.
-
-.. data:: basestring
-
- Alias for :func:`python2:basestring` (in Python 2) or :class:`python3:str`
- (in Python 3). See also :func:`is_string()`.
-
-.. data:: HTMLParser
-
- Alias for :class:`python2:HTMLParser.HTMLParser` (in Python 2) or
- :class:`python3:html.parser.HTMLParser` (in Python 3).
-
-.. data:: interactive_prompt
-
- Alias for :func:`python2:raw_input()` (in Python 2) or
- :func:`python3:input()` (in Python 3).
-
-.. data:: StringIO
-
- Alias for :class:`python2:StringIO.StringIO` (in Python 2) or
- :class:`python3:io.StringIO` (in Python 3).
-
-.. data:: unicode
-
- Alias for :func:`python2:unicode` (in Python 2) or :class:`python3:str` (in
- Python 3). See also :func:`coerce_string()`.
-
-.. data:: monotonic
-
- Alias for :func:`python3:time.monotonic()` (in Python 3.3 and higher) or
- `monotonic.monotonic()` (a `conditional dependency
- <https://pypi.org/project/monotonic/>`_ on older Python versions).
-"""
-
-__all__ = (
- 'HTMLParser',
- 'StringIO',
- 'basestring',
- 'coerce_string',
- 'interactive_prompt',
- 'is_string',
- 'is_unicode',
- 'monotonic',
- 'name2codepoint',
- 'on_macos',
- 'on_windows',
- 'unichr',
- 'unicode',
- 'which',
-)
-
-# Standard library modules.
-import sys
-
-# Differences between Python 2 and 3.
-try:
- # Python 2.
- unicode = unicode
- unichr = unichr
- basestring = basestring
- interactive_prompt = raw_input
- from distutils.spawn import find_executable as which
- from HTMLParser import HTMLParser
- from StringIO import StringIO
- from htmlentitydefs import name2codepoint
-except (ImportError, NameError):
- # Python 3.
- unicode = str
- unichr = chr
- basestring = str
- interactive_prompt = input
- from shutil import which
- from html.parser import HTMLParser
- from io import StringIO
- from html.entities import name2codepoint
-
-try:
- # Python 3.3 and higher.
- from time import monotonic
-except ImportError:
- # A replacement for older Python versions:
- # https://pypi.org/project/monotonic/
- try:
- from monotonic import monotonic
- except (ImportError, RuntimeError):
- # We fall back to the old behavior of using time.time() instead of
- # failing when {time,monotonic}.monotonic() are both missing.
- from time import time as monotonic
-
-
-def coerce_string(value):
- """
- Coerce any value to a Unicode string (:func:`python2:unicode` in Python 2 and :class:`python3:str` in Python 3).
-
- :param value: The value to coerce.
- :returns: The value coerced to a Unicode string.
- """
- return value if is_string(value) else unicode(value)
-
-
-def is_string(value):
- """
- Check if a value is a :func:`python2:basestring` (in Python 2) or :class:`python3:str` (in Python 3) object.
-
- :param value: The value to check.
- :returns: :data:`True` if the value is a string, :data:`False` otherwise.
- """
- return isinstance(value, basestring)
-
-
-def is_unicode(value):
- """
- Check if a value is a :func:`python2:unicode` (in Python 2) or :class:`python2:str` (in Python 3) object.
-
- :param value: The value to check.
- :returns: :data:`True` if the value is a Unicode string, :data:`False` otherwise.
- """
- return isinstance(value, unicode)
-
-
-def on_macos():
- """
- Check if we're running on Apple MacOS.
-
- :returns: :data:`True` if running MacOS, :data:`False` otherwise.
- """
- return sys.platform.startswith('darwin')
-
-
-def on_windows():
- """
- Check if we're running on the Microsoft Windows OS.
-
- :returns: :data:`True` if running Windows, :data:`False` otherwise.
- """
- return sys.platform.startswith('win')
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/decorators.py b/contrib/python/humanfriendly/py3/humanfriendly/decorators.py
deleted file mode 100644
index c90a59ea28..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/decorators.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: March 2, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""Simple function decorators to make Python programming easier."""
-
-# Standard library modules.
-import functools
-
-# Public identifiers that require documentation.
-__all__ = ('RESULTS_ATTRIBUTE', 'cached')
-
-RESULTS_ATTRIBUTE = 'cached_results'
-"""The name of the property used to cache the return values of functions (a string)."""
-
-
-def cached(function):
- """
- Rudimentary caching decorator for functions.
-
- :param function: The function whose return value should be cached.
- :returns: The decorated function.
-
- The given function will only be called once, the first time the wrapper
- function is called. The return value is cached by the wrapper function as
- an attribute of the given function and returned on each subsequent call.
-
- .. note:: Currently no function arguments are supported because only a
- single return value can be cached. Accepting any function
- arguments at all would imply that the cache is parametrized on
- function arguments, which is not currently the case.
- """
- @functools.wraps(function)
- def wrapper():
- try:
- return getattr(wrapper, RESULTS_ATTRIBUTE)
- except AttributeError:
- result = function()
- setattr(wrapper, RESULTS_ATTRIBUTE, result)
- return result
- return wrapper
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/deprecation.py b/contrib/python/humanfriendly/py3/humanfriendly/deprecation.py
deleted file mode 100644
index f2012bbd6f..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/deprecation.py
+++ /dev/null
@@ -1,251 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: March 2, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Support for deprecation warnings when importing names from old locations.
-
-When software evolves, things tend to move around. This is usually detrimental
-to backwards compatibility (in Python this primarily manifests itself as
-:exc:`~exceptions.ImportError` exceptions).
-
-While backwards compatibility is very important, it should not get in the way
-of progress. It would be great to have the agility to move things around
-without breaking backwards compatibility.
-
-This is where the :mod:`humanfriendly.deprecation` module comes in: It enables
-the definition of backwards compatible aliases that emit a deprecation warning
-when they are accessed.
-
-The way it works is that it wraps the original module in an :class:`DeprecationProxy`
-object that defines a :func:`~DeprecationProxy.__getattr__()` special method to
-override attribute access of the module.
-"""
-
-# Standard library modules.
-import collections
-import functools
-import importlib
-import inspect
-import sys
-import types
-import warnings
-
-# Modules included in our package.
-from humanfriendly.text import format
-
-# Registry of known aliases (used by humanfriendly.sphinx).
-REGISTRY = collections.defaultdict(dict)
-
-# Public identifiers that require documentation.
-__all__ = ("DeprecationProxy", "define_aliases", "deprecated_args", "get_aliases", "is_method")
-
-
-def define_aliases(module_name, **aliases):
- """
- Update a module with backwards compatible aliases.
-
- :param module_name: The ``__name__`` of the module (a string).
- :param aliases: Each keyword argument defines an alias. The values
- are expected to be "dotted paths" (strings).
-
- The behavior of this function depends on whether the Sphinx documentation
- generator is active, because the use of :class:`DeprecationProxy` to shadow the
- real module in :data:`sys.modules` has the unintended side effect of
- breaking autodoc support for ``:data:`` members (module variables).
-
- To avoid breaking Sphinx the proxy object is omitted and instead the
- aliased names are injected into the original module namespace, to make sure
- that imports can be satisfied when the documentation is being rendered.
-
- If you run into cyclic dependencies caused by :func:`define_aliases()` when
- running Sphinx, you can try moving the call to :func:`define_aliases()` to
- the bottom of the Python module you're working on.
- """
- module = sys.modules[module_name]
- proxy = DeprecationProxy(module, aliases)
- # Populate the registry of aliases.
- for name, target in aliases.items():
- REGISTRY[module.__name__][name] = target
- # Avoid confusing Sphinx.
- if "sphinx" in sys.modules:
- for name, target in aliases.items():
- setattr(module, name, proxy.resolve(target))
- else:
- # Install a proxy object to raise DeprecationWarning.
- sys.modules[module_name] = proxy
-
-
-def get_aliases(module_name):
- """
- Get the aliases defined by a module.
-
- :param module_name: The ``__name__`` of the module (a string).
- :returns: A dictionary with string keys and values:
-
- 1. Each key gives the name of an alias
- created for backwards compatibility.
-
- 2. Each value gives the dotted path of
- the proper location of the identifier.
-
- An empty dictionary is returned for modules that
- don't define any backwards compatible aliases.
- """
- return REGISTRY.get(module_name, {})
-
-
-def deprecated_args(*names):
- """
- Deprecate positional arguments without dropping backwards compatibility.
-
- :param names:
-
- The positional arguments to :func:`deprecated_args()` give the names of
- the positional arguments that the to-be-decorated function should warn
- about being deprecated and translate to keyword arguments.
-
- :returns: A decorator function specialized to `names`.
-
- The :func:`deprecated_args()` decorator function was created to make it
- easy to switch from positional arguments to keyword arguments [#]_ while
- preserving backwards compatibility [#]_ and informing call sites
- about the change.
-
- .. [#] Increased flexibility is the main reason why I find myself switching
- from positional arguments to (optional) keyword arguments as my code
- evolves to support more use cases.
-
- .. [#] In my experience positional argument order implicitly becomes part
- of API compatibility whether intended or not. While this makes sense
- for functions that over time adopt more and more optional arguments,
- at a certain point it becomes an inconvenience to code maintenance.
-
- Here's an example of how to use the decorator::
-
- @deprecated_args('text')
- def report_choice(**options):
- print(options['text'])
-
- When the decorated function is called with positional arguments
- a deprecation warning is given::
-
- >>> report_choice('this will give a deprecation warning')
- DeprecationWarning: report_choice has deprecated positional arguments, please switch to keyword arguments
- this will give a deprecation warning
-
- But when the function is called with keyword arguments no deprecation
- warning is emitted::
-
- >>> report_choice(text='this will not give a deprecation warning')
- this will not give a deprecation warning
- """
- def decorator(function):
- def translate(args, kw):
- # Raise TypeError when too many positional arguments are passed to the decorated function.
- if len(args) > len(names):
- raise TypeError(
- format(
- "{name} expected at most {limit} arguments, got {count}",
- name=function.__name__,
- limit=len(names),
- count=len(args),
- )
- )
- # Emit a deprecation warning when positional arguments are used.
- if args:
- warnings.warn(
- format(
- "{name} has deprecated positional arguments, please switch to keyword arguments",
- name=function.__name__,
- ),
- category=DeprecationWarning,
- stacklevel=3,
- )
- # Translate positional arguments to keyword arguments.
- for name, value in zip(names, args):
- kw[name] = value
- if is_method(function):
- @functools.wraps(function)
- def wrapper(*args, **kw):
- """Wrapper for instance methods."""
- args = list(args)
- self = args.pop(0)
- translate(args, kw)
- return function(self, **kw)
- else:
- @functools.wraps(function)
- def wrapper(*args, **kw):
- """Wrapper for module level functions."""
- translate(args, kw)
- return function(**kw)
- return wrapper
- return decorator
-
-
-def is_method(function):
- """Check if the expected usage of the given function is as an instance method."""
- try:
- # Python 3.3 and newer.
- signature = inspect.signature(function)
- return "self" in signature.parameters
- except AttributeError:
- # Python 3.2 and older.
- metadata = inspect.getargspec(function)
- return "self" in metadata.args
-
-
-class DeprecationProxy(types.ModuleType):
-
- """Emit deprecation warnings for imports that should be updated."""
-
- def __init__(self, module, aliases):
- """
- Initialize an :class:`DeprecationProxy` object.
-
- :param module: The original module object.
- :param aliases: A dictionary of aliases.
- """
- # Initialize our superclass.
- super(DeprecationProxy, self).__init__(name=module.__name__)
- # Store initializer arguments.
- self.module = module
- self.aliases = aliases
-
- def __getattr__(self, name):
- """
- Override module attribute lookup.
-
- :param name: The name to look up (a string).
- :returns: The attribute value.
- """
- # Check if the given name is an alias.
- target = self.aliases.get(name)
- if target is not None:
- # Emit the deprecation warning.
- warnings.warn(
- format("%s.%s was moved to %s, please update your imports", self.module.__name__, name, target),
- category=DeprecationWarning,
- stacklevel=2,
- )
- # Resolve the dotted path.
- return self.resolve(target)
- # Look up the name in the original module namespace.
- value = getattr(self.module, name, None)
- if value is not None:
- return value
- # Fall back to the default behavior.
- raise AttributeError(format("module '%s' has no attribute '%s'", self.module.__name__, name))
-
- def resolve(self, target):
- """
- Look up the target of an alias.
-
- :param target: The fully qualified dotted path (a string).
- :returns: The value of the given target.
- """
- module_name, _, member = target.rpartition(".")
- module = importlib.import_module(module_name)
- return getattr(module, member)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/prompts.py b/contrib/python/humanfriendly/py3/humanfriendly/prompts.py
deleted file mode 100644
index 07166b6709..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/prompts.py
+++ /dev/null
@@ -1,376 +0,0 @@
-# vim: fileencoding=utf-8
-
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: February 9, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Interactive terminal prompts.
-
-The :mod:`~humanfriendly.prompts` module enables interaction with the user
-(operator) by asking for confirmation (:func:`prompt_for_confirmation()`) and
-asking to choose from a list of options (:func:`prompt_for_choice()`). It works
-by rendering interactive prompts on the terminal.
-"""
-
-# Standard library modules.
-import logging
-import sys
-
-# Modules included in our package.
-from humanfriendly.compat import interactive_prompt
-from humanfriendly.terminal import (
- HIGHLIGHT_COLOR,
- ansi_strip,
- ansi_wrap,
- connected_to_terminal,
- terminal_supports_colors,
- warning,
-)
-from humanfriendly.text import format, concatenate
-
-# Public identifiers that require documentation.
-__all__ = (
- 'MAX_ATTEMPTS',
- 'TooManyInvalidReplies',
- 'logger',
- 'prepare_friendly_prompts',
- 'prepare_prompt_text',
- 'prompt_for_choice',
- 'prompt_for_confirmation',
- 'prompt_for_input',
- 'retry_limit',
-)
-
-MAX_ATTEMPTS = 10
-"""The number of times an interactive prompt is shown on invalid input (an integer)."""
-
-# Initialize a logger for this module.
-logger = logging.getLogger(__name__)
-
-
-def prompt_for_confirmation(question, default=None, padding=True):
- """
- Prompt the user for confirmation.
-
- :param question: The text that explains what the user is confirming (a string).
- :param default: The default value (a boolean) or :data:`None`.
- :param padding: Refer to the documentation of :func:`prompt_for_input()`.
- :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned.
- - If the user enters 'no' or 'n' then :data:`False` is returned.
- - If the user doesn't enter any text or standard input is not
- connected to a terminal (which makes it impossible to prompt
- the user) the value of the keyword argument ``default`` is
- returned (if that value is not :data:`None`).
- :raises: - Any exceptions raised by :func:`retry_limit()`.
- - Any exceptions raised by :func:`prompt_for_input()`.
-
- When `default` is :data:`False` and the user doesn't enter any text an
- error message is printed and the prompt is repeated:
-
- >>> prompt_for_confirmation("Are you sure?")
- <BLANKLINE>
- Are you sure? [y/n]
- <BLANKLINE>
- Error: Please enter 'yes' or 'no' (there's no default choice).
- <BLANKLINE>
- Are you sure? [y/n]
-
- The same thing happens when the user enters text that isn't recognized:
-
- >>> prompt_for_confirmation("Are you sure?")
- <BLANKLINE>
- Are you sure? [y/n] about what?
- <BLANKLINE>
- Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized).
- <BLANKLINE>
- Are you sure? [y/n]
- """
- # Generate the text for the prompt.
- prompt_text = prepare_prompt_text(question, bold=True)
- # Append the valid replies (and default reply) to the prompt text.
- hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]"
- prompt_text += " %s " % prepare_prompt_text(hint, color=HIGHLIGHT_COLOR)
- # Loop until a valid response is given.
- logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip())
- for attempt in retry_limit():
- reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
- if reply.lower() in ('y', 'yes'):
- logger.debug("Confirmation granted by reply (%r).", reply)
- return True
- elif reply.lower() in ('n', 'no'):
- logger.debug("Confirmation denied by reply (%r).", reply)
- return False
- elif (not reply) and default is not None:
- logger.debug("Default choice selected by empty reply (%r).",
- "granted" if default else "denied")
- return default
- else:
- details = ("the text '%s' is not recognized" % reply
- if reply else "there's no default choice")
- logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
- "invalid" if reply else "empty", details,
- attempt, MAX_ATTEMPTS)
- warning("{indent}Error: Please enter 'yes' or 'no' ({details}).",
- indent=' ' if padding else '', details=details)
-
-
-def prompt_for_choice(choices, default=None, padding=True):
- """
- Prompt the user to select a choice from a group of options.
-
- :param choices: A sequence of strings with available options.
- :param default: The default choice if the user simply presses Enter
- (expected to be a string, defaults to :data:`None`).
- :param padding: Refer to the documentation of
- :func:`~humanfriendly.prompts.prompt_for_input()`.
- :returns: The string corresponding to the user's choice.
- :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence.
- - Any exceptions raised by
- :func:`~humanfriendly.prompts.retry_limit()`.
- - Any exceptions raised by
- :func:`~humanfriendly.prompts.prompt_for_input()`.
-
- When no options are given an exception is raised:
-
- >>> prompt_for_choice([])
- Traceback (most recent call last):
- File "humanfriendly/prompts.py", line 148, in prompt_for_choice
- raise ValueError("Can't prompt for choice without any options!")
- ValueError: Can't prompt for choice without any options!
-
- If a single option is given the user isn't prompted:
-
- >>> prompt_for_choice(['only one choice'])
- 'only one choice'
-
- Here's what the actual prompt looks like by default:
-
- >>> prompt_for_choice(['first option', 'second option'])
- <BLANKLINE>
- 1. first option
- 2. second option
- <BLANKLINE>
- Enter your choice as a number or unique substring (Control-C aborts): second
- <BLANKLINE>
- 'second option'
-
- If you don't like the whitespace (empty lines and indentation):
-
- >>> prompt_for_choice(['first option', 'second option'], padding=False)
- 1. first option
- 2. second option
- Enter your choice as a number or unique substring (Control-C aborts): first
- 'first option'
- """
- indent = ' ' if padding else ''
- # Make sure we can use 'choices' more than once (i.e. not a generator).
- choices = list(choices)
- if len(choices) == 1:
- # If there's only one option there's no point in prompting the user.
- logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0])
- return choices[0]
- elif not choices:
- # We can't render a choice prompt without any options.
- raise ValueError("Can't prompt for choice without any options!")
- # Generate the prompt text.
- prompt_text = ('\n\n' if padding else '\n').join([
- # Present the available choices in a user friendly way.
- "\n".join([
- (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "")
- for i, choice in enumerate(choices, start=1)
- ]),
- # Instructions for the user.
- "Enter your choice as a number or unique substring (Control-C aborts): ",
- ])
- prompt_text = prepare_prompt_text(prompt_text, bold=True)
- # Loop until a valid choice is made.
- logger.debug("Requesting interactive choice on terminal (options are %s) ..",
- concatenate(map(repr, choices)))
- for attempt in retry_limit():
- reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
- if not reply and default is not None:
- logger.debug("Default choice selected by empty reply (%r).", default)
- return default
- elif reply.isdigit():
- index = int(reply) - 1
- if 0 <= index < len(choices):
- logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply)
- return choices[index]
- # Check for substring matches.
- matches = []
- for choice in choices:
- lower_reply = reply.lower()
- lower_choice = choice.lower()
- if lower_reply == lower_choice:
- # If we have an 'exact' match we return it immediately.
- logger.debug("Option (%r) selected by reply (exact match).", choice)
- return choice
- elif lower_reply in lower_choice and len(lower_reply) > 0:
- # Otherwise we gather substring matches.
- matches.append(choice)
- if len(matches) == 1:
- # If a single choice was matched we return it.
- logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply)
- return matches[0]
- else:
- # Give the user a hint about what went wrong.
- if matches:
- details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches))
- elif reply.isdigit():
- details = format("number %i is not a valid choice", int(reply))
- elif reply and not reply.isspace():
- details = format("text '%s' doesn't match any choices", reply)
- else:
- details = "there's no default choice"
- logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
- "invalid" if reply else "empty", details,
- attempt, MAX_ATTEMPTS)
- warning("%sError: Invalid input (%s).", indent, details)
-
-
-def prompt_for_input(question, default=None, padding=True, strip=True):
- """
- Prompt the user for input (free form text).
-
- :param question: An explanation of what is expected from the user (a string).
- :param default: The return value if the user doesn't enter any text or
- standard input is not connected to a terminal (which
- makes it impossible to prompt the user).
- :param padding: Render empty lines before and after the prompt to make it
- stand out from the surrounding text? (a boolean, defaults
- to :data:`True`)
- :param strip: Strip leading/trailing whitespace from the user's reply?
- :returns: The text entered by the user (a string) or the value of the
- `default` argument.
- :raises: - :exc:`~exceptions.KeyboardInterrupt` when the program is
- interrupted_ while the prompt is active, for example
- because the user presses Control-C_.
- - :exc:`~exceptions.EOFError` when reading from `standard input`_
- fails, for example because the user presses Control-D_ or
- because the standard input stream is redirected (only if
- `default` is :data:`None`).
-
- .. _Control-C: https://en.wikipedia.org/wiki/Control-C#In_command-line_environments
- .. _Control-D: https://en.wikipedia.org/wiki/End-of-transmission_character#Meaning_in_Unix
- .. _interrupted: https://en.wikipedia.org/wiki/Unix_signal#SIGINT
- .. _standard input: https://en.wikipedia.org/wiki/Standard_streams#Standard_input_.28stdin.29
- """
- prepare_friendly_prompts()
- reply = None
- try:
- # Prefix an empty line to the text and indent by one space?
- if padding:
- question = '\n' + question
- question = question.replace('\n', '\n ')
- # Render the prompt and wait for the user's reply.
- try:
- reply = interactive_prompt(question)
- finally:
- if reply is None:
- # If the user terminated the prompt using Control-C or
- # Control-D instead of pressing Enter no newline will be
- # rendered after the prompt's text. The result looks kind of
- # weird:
- #
- # $ python -c 'print(raw_input("Are you sure? "))'
- # Are you sure? ^CTraceback (most recent call last):
- # File "<string>", line 1, in <module>
- # KeyboardInterrupt
- #
- # We can avoid this by emitting a newline ourselves if an
- # exception was raised (signaled by `reply' being None).
- sys.stderr.write('\n')
- if padding:
- # If the caller requested (didn't opt out of) `padding' then we'll
- # emit a newline regardless of whether an exception is being
- # handled. This helps to make interactive prompts `stand out' from
- # a surrounding `wall of text' on the terminal.
- sys.stderr.write('\n')
- except BaseException as e:
- if isinstance(e, EOFError) and default is not None:
- # If standard input isn't connected to an interactive terminal
- # but the caller provided a default we'll return that.
- logger.debug("Got EOF from terminal, returning default value (%r) ..", default)
- return default
- else:
- # Otherwise we log that the prompt was interrupted but propagate
- # the exception to the caller.
- logger.warning("Interactive prompt was interrupted by exception!", exc_info=True)
- raise
- if default is not None and not reply:
- # If the reply is empty and `default' is None we don't want to return
- # None because it's nicer for callers to be able to assume that the
- # return value is always a string.
- return default
- else:
- return reply.strip()
-
-
-def prepare_prompt_text(prompt_text, **options):
- """
- Wrap a text to be rendered as an interactive prompt in ANSI escape sequences.
-
- :param prompt_text: The text to render on the prompt (a string).
- :param options: Any keyword arguments are passed on to :func:`.ansi_wrap()`.
- :returns: The resulting prompt text (a string).
-
- ANSI escape sequences are only used when the standard output stream is
- connected to a terminal. When the standard input stream is connected to a
- terminal any escape sequences are wrapped in "readline hints".
- """
- return (ansi_wrap(prompt_text, readline_hints=connected_to_terminal(sys.stdin), **options)
- if terminal_supports_colors(sys.stdout)
- else prompt_text)
-
-
-def prepare_friendly_prompts():
- u"""
- Make interactive prompts more user friendly.
-
- The prompts presented by :func:`python2:raw_input()` (in Python 2) and
- :func:`python3:input()` (in Python 3) are not very user friendly by
- default, for example the cursor keys (:kbd:`←`, :kbd:`↑`, :kbd:`→` and
- :kbd:`↓`) and the :kbd:`Home` and :kbd:`End` keys enter characters instead
- of performing the action you would expect them to. By simply importing the
- :mod:`readline` module these prompts become much friendlier (as mentioned
- in the Python standard library documentation).
-
- This function is called by the other functions in this module to enable
- user friendly prompts.
- """
- try:
- import readline # NOQA
- except ImportError:
- # might not be available on Windows if pyreadline isn't installed
- pass
-
-
-def retry_limit(limit=MAX_ATTEMPTS):
- """
- Allow the user to provide valid input up to `limit` times.
-
- :param limit: The maximum number of attempts (a number,
- defaults to :data:`MAX_ATTEMPTS`).
- :returns: A generator of numbers starting from one.
- :raises: :exc:`TooManyInvalidReplies` when an interactive prompt
- receives repeated invalid input (:data:`MAX_ATTEMPTS`).
-
- This function returns a generator for interactive prompts that want to
- repeat on invalid input without getting stuck in infinite loops.
- """
- for i in range(limit):
- yield i + 1
- msg = "Received too many invalid replies on interactive prompt, giving up! (tried %i times)"
- formatted_msg = msg % limit
- # Make sure the event is logged.
- logger.warning(formatted_msg)
- # Force the caller to decide what to do now.
- raise TooManyInvalidReplies(formatted_msg)
-
-
-class TooManyInvalidReplies(Exception):
-
- """Raised by interactive prompts when they've received too many invalid inputs."""
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/sphinx.py b/contrib/python/humanfriendly/py3/humanfriendly/sphinx.py
deleted file mode 100644
index cf5d1b3935..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/sphinx.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: June 11, 2021
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Customizations for and integration with the Sphinx_ documentation generator.
-
-The :mod:`humanfriendly.sphinx` module uses the `Sphinx extension API`_ to
-customize the process of generating Sphinx based Python documentation. To
-explore the functionality this module offers its best to start reading
-from the :func:`setup()` function.
-
-.. _Sphinx: http://www.sphinx-doc.org/
-.. _Sphinx extension API: http://sphinx-doc.org/extdev/appapi.html
-"""
-
-# Standard library modules.
-import logging
-import types
-
-# External dependencies (if Sphinx is installed docutils will be installed).
-import docutils.nodes
-import docutils.utils
-
-# Modules included in our package.
-from humanfriendly.deprecation import get_aliases
-from humanfriendly.text import compact, dedent, format
-from humanfriendly.usage import USAGE_MARKER, render_usage
-
-# Public identifiers that require documentation.
-__all__ = (
- "deprecation_note_callback",
- "enable_deprecation_notes",
- "enable_man_role",
- "enable_pypi_role",
- "enable_special_methods",
- "enable_usage_formatting",
- "logger",
- "man_role",
- "pypi_role",
- "setup",
- "special_methods_callback",
- "usage_message_callback",
-)
-
-# Initialize a logger for this module.
-logger = logging.getLogger(__name__)
-
-
-def deprecation_note_callback(app, what, name, obj, options, lines):
- """
- Automatically document aliases defined using :func:`~humanfriendly.deprecation.define_aliases()`.
-
- Refer to :func:`enable_deprecation_notes()` to enable the use of this
- function (you probably don't want to call :func:`deprecation_note_callback()`
- directly).
-
- This function implements a callback for ``autodoc-process-docstring`` that
- reformats module docstrings to append an overview of aliases defined by the
- module.
-
- The parameters expected by this function are those defined for Sphinx event
- callback functions (i.e. I'm not going to document them here :-).
- """
- if isinstance(obj, types.ModuleType) and lines:
- aliases = get_aliases(obj.__name__)
- if aliases:
- # Convert the existing docstring to a string and remove leading
- # indentation from that string, otherwise our generated content
- # would have to match the existing indentation in order not to
- # break docstring parsing (because indentation is significant
- # in the reStructuredText format).
- blocks = [dedent("\n".join(lines))]
- # Use an admonition to group the deprecated aliases together and
- # to distinguish them from the autodoc entries that follow.
- blocks.append(".. note:: Deprecated names")
- indent = " " * 3
- if len(aliases) == 1:
- explanation = """
- The following alias exists to preserve backwards compatibility,
- however a :exc:`~exceptions.DeprecationWarning` is triggered
- when it is accessed, because this alias will be removed
- in a future release.
- """
- else:
- explanation = """
- The following aliases exist to preserve backwards compatibility,
- however a :exc:`~exceptions.DeprecationWarning` is triggered
- when they are accessed, because these aliases will be
- removed in a future release.
- """
- blocks.append(indent + compact(explanation))
- for name, target in aliases.items():
- blocks.append(format("%s.. data:: %s", indent, name))
- blocks.append(format("%sAlias for :obj:`%s`.", indent * 2, target))
- update_lines(lines, "\n\n".join(blocks))
-
-
-def enable_deprecation_notes(app):
- """
- Enable documenting backwards compatibility aliases using the autodoc_ extension.
-
- :param app: The Sphinx application object.
-
- This function connects the :func:`deprecation_note_callback()` function to
- ``autodoc-process-docstring`` events.
-
- .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
- """
- app.connect("autodoc-process-docstring", deprecation_note_callback)
-
-
-def enable_man_role(app):
- """
- Enable the ``:man:`` role for linking to Debian Linux manual pages.
-
- :param app: The Sphinx application object.
-
- This function registers the :func:`man_role()` function to handle the
- ``:man:`` role.
- """
- app.add_role("man", man_role)
-
-
-def enable_pypi_role(app):
- """
- Enable the ``:pypi:`` role for linking to the Python Package Index.
-
- :param app: The Sphinx application object.
-
- This function registers the :func:`pypi_role()` function to handle the
- ``:pypi:`` role.
- """
- app.add_role("pypi", pypi_role)
-
-
-def enable_special_methods(app):
- """
- Enable documenting "special methods" using the autodoc_ extension.
-
- :param app: The Sphinx application object.
-
- This function connects the :func:`special_methods_callback()` function to
- ``autodoc-skip-member`` events.
-
- .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
- """
- app.connect("autodoc-skip-member", special_methods_callback)
-
-
-def enable_usage_formatting(app):
- """
- Reformat human friendly usage messages to reStructuredText_.
-
- :param app: The Sphinx application object (as given to ``setup()``).
-
- This function connects the :func:`usage_message_callback()` function to
- ``autodoc-process-docstring`` events.
-
- .. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
- """
- app.connect("autodoc-process-docstring", usage_message_callback)
-
-
-def man_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
- """
- Convert a Linux manual topic to a hyperlink.
-
- Using the ``:man:`` role is very simple, here's an example:
-
- .. code-block:: rst
-
- See the :man:`python` documentation.
-
- This results in the following:
-
- See the :man:`python` documentation.
-
- As the example shows you can use the role inline, embedded in sentences of
- text. In the generated documentation the ``:man:`` text is omitted and a
- hyperlink pointing to the Debian Linux manual pages is emitted.
- """
- man_url = "https://manpages.debian.org/%s" % text
- reference = docutils.nodes.reference(rawtext, docutils.utils.unescape(text), refuri=man_url, **options)
- return [reference], []
-
-
-def pypi_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
- """
- Generate hyperlinks to the Python Package Index.
-
- Using the ``:pypi:`` role is very simple, here's an example:
-
- .. code-block:: rst
-
- See the :pypi:`humanfriendly` package.
-
- This results in the following:
-
- See the :pypi:`humanfriendly` package.
-
- As the example shows you can use the role inline, embedded in sentences of
- text. In the generated documentation the ``:pypi:`` text is omitted and a
- hyperlink pointing to the Python Package Index is emitted.
- """
- pypi_url = "https://pypi.org/project/%s/" % text
- reference = docutils.nodes.reference(rawtext, docutils.utils.unescape(text), refuri=pypi_url, **options)
- return [reference], []
-
-
-def setup(app):
- """
- Enable all of the provided Sphinx_ customizations.
-
- :param app: The Sphinx application object.
-
- The :func:`setup()` function makes it easy to enable all of the Sphinx
- customizations provided by the :mod:`humanfriendly.sphinx` module with the
- least amount of code. All you need to do is to add the module name to the
- ``extensions`` variable in your ``conf.py`` file:
-
- .. code-block:: python
-
- # Sphinx extension module names.
- extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.doctest',
- 'sphinx.ext.intersphinx',
- 'humanfriendly.sphinx',
- ]
-
- When Sphinx sees the :mod:`humanfriendly.sphinx` name it will import the
- module and call its :func:`setup()` function. This function will then call
- the following:
-
- - :func:`enable_deprecation_notes()`
- - :func:`enable_man_role()`
- - :func:`enable_pypi_role()`
- - :func:`enable_special_methods()`
- - :func:`enable_usage_formatting()`
-
- Of course more functionality may be added at a later stage. If you don't
- like that idea you may be better of calling the individual functions from
- your own ``setup()`` function.
- """
- from humanfriendly import __version__
-
- enable_deprecation_notes(app)
- enable_man_role(app)
- enable_pypi_role(app)
- enable_special_methods(app)
- enable_usage_formatting(app)
-
- return dict(parallel_read_safe=True, parallel_write_safe=True, version=__version__)
-
-
-def special_methods_callback(app, what, name, obj, skip, options):
- """
- Enable documenting "special methods" using the autodoc_ extension.
-
- Refer to :func:`enable_special_methods()` to enable the use of this
- function (you probably don't want to call
- :func:`special_methods_callback()` directly).
-
- This function implements a callback for ``autodoc-skip-member`` events to
- include documented "special methods" (method names with two leading and two
- trailing underscores) in your documentation. The result is similar to the
- use of the ``special-members`` flag with one big difference: Special
- methods are included but other types of members are ignored. This means
- that attributes like ``__weakref__`` will always be ignored (this was my
- main annoyance with the ``special-members`` flag).
-
- The parameters expected by this function are those defined for Sphinx event
- callback functions (i.e. I'm not going to document them here :-).
- """
- if getattr(obj, "__doc__", None) and isinstance(obj, (types.FunctionType, types.MethodType)):
- return False
- else:
- return skip
-
-
-def update_lines(lines, text):
- """Private helper for ``autodoc-process-docstring`` callbacks."""
- while lines:
- lines.pop()
- lines.extend(text.splitlines())
-
-
-def usage_message_callback(app, what, name, obj, options, lines):
- """
- Reformat human friendly usage messages to reStructuredText_.
-
- Refer to :func:`enable_usage_formatting()` to enable the use of this
- function (you probably don't want to call :func:`usage_message_callback()`
- directly).
-
- This function implements a callback for ``autodoc-process-docstring`` that
- reformats module docstrings using :func:`.render_usage()` so that Sphinx
- doesn't mangle usage messages that were written to be human readable
- instead of machine readable. Only module docstrings whose first line starts
- with :data:`.USAGE_MARKER` are reformatted.
-
- The parameters expected by this function are those defined for Sphinx event
- callback functions (i.e. I'm not going to document them here :-).
- """
- # Make sure we only modify the docstrings of modules.
- if isinstance(obj, types.ModuleType) and lines:
- # Make sure we only modify docstrings containing a usage message.
- if lines[0].startswith(USAGE_MARKER):
- # Convert the usage message to reStructuredText.
- text = render_usage("\n".join(lines))
- # Fill up the buffer with our modified docstring.
- update_lines(lines, text)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/tables.py b/contrib/python/humanfriendly/py3/humanfriendly/tables.py
deleted file mode 100644
index 3a32ba3b3f..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/tables.py
+++ /dev/null
@@ -1,341 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: February 16, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Functions that render ASCII tables.
-
-Some generic notes about the table formatting functions in this module:
-
-- These functions were not written with performance in mind (*at all*) because
- they're intended to format tabular data to be presented on a terminal. If
- someone were to run into a performance problem using these functions, they'd
- be printing so much tabular data to the terminal that a human wouldn't be
- able to digest the tabular data anyway, so the point is moot :-).
-
-- These functions ignore ANSI escape sequences (at least the ones generated by
- the :mod:`~humanfriendly.terminal` module) in the calculation of columns
- widths. On reason for this is that column names are highlighted in color when
- connected to a terminal. It also means that you can use ANSI escape sequences
- to highlight certain column's values if you feel like it (for example to
- highlight deviations from the norm in an overview of calculated values).
-"""
-
-# Standard library modules.
-import collections
-import re
-
-# Modules included in our package.
-from humanfriendly.compat import coerce_string
-from humanfriendly.terminal import (
- ansi_strip,
- ansi_width,
- ansi_wrap,
- terminal_supports_colors,
- find_terminal_size,
- HIGHLIGHT_COLOR,
-)
-
-# Public identifiers that require documentation.
-__all__ = (
- 'format_pretty_table',
- 'format_robust_table',
- 'format_rst_table',
- 'format_smart_table',
-)
-
-# Compiled regular expression pattern to recognize table columns containing
-# numeric data (integer and/or floating point numbers). Used to right-align the
-# contents of such columns.
-#
-# Pre-emptive snarky comment: This pattern doesn't match every possible
-# floating point number notation!?!1!1
-#
-# Response: I know, that's intentional. The use of this regular expression
-# pattern has a very high DWIM level and weird floating point notations do not
-# fall under the DWIM umbrella :-).
-NUMERIC_DATA_PATTERN = re.compile(r'^\d+(\.\d+)?$')
-
-
-def format_smart_table(data, column_names):
- """
- Render tabular data using the most appropriate representation.
-
- :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
- containing the rows of the table, where each row is an
- iterable containing the columns of the table (strings).
- :param column_names: An iterable of column names (strings).
- :returns: The rendered table (a string).
-
- If you want an easy way to render tabular data on a terminal in a human
- friendly format then this function is for you! It works as follows:
-
- - If the input data doesn't contain any line breaks the function
- :func:`format_pretty_table()` is used to render a pretty table. If the
- resulting table fits in the terminal without wrapping the rendered pretty
- table is returned.
-
- - If the input data does contain line breaks or if a pretty table would
- wrap (given the width of the terminal) then the function
- :func:`format_robust_table()` is used to render a more robust table that
- can deal with data containing line breaks and long text.
- """
- # Normalize the input in case we fall back from a pretty table to a robust
- # table (in which case we'll definitely iterate the input more than once).
- data = [normalize_columns(r) for r in data]
- column_names = normalize_columns(column_names)
- # Make sure the input data doesn't contain any line breaks (because pretty
- # tables break horribly when a column's text contains a line break :-).
- if not any(any('\n' in c for c in r) for r in data):
- # Render a pretty table.
- pretty_table = format_pretty_table(data, column_names)
- # Check if the pretty table fits in the terminal.
- table_width = max(map(ansi_width, pretty_table.splitlines()))
- num_rows, num_columns = find_terminal_size()
- if table_width <= num_columns:
- # The pretty table fits in the terminal without wrapping!
- return pretty_table
- # Fall back to a robust table when a pretty table won't work.
- return format_robust_table(data, column_names)
-
-
-def format_pretty_table(data, column_names=None, horizontal_bar='-', vertical_bar='|'):
- """
- Render a table using characters like dashes and vertical bars to emulate borders.
-
- :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
- containing the rows of the table, where each row is an
- iterable containing the columns of the table (strings).
- :param column_names: An iterable of column names (strings).
- :param horizontal_bar: The character used to represent a horizontal bar (a
- string).
- :param vertical_bar: The character used to represent a vertical bar (a
- string).
- :returns: The rendered table (a string).
-
- Here's an example:
-
- >>> from humanfriendly.tables import format_pretty_table
- >>> column_names = ['Version', 'Uploaded on', 'Downloads']
- >>> humanfriendly_releases = [
- ... ['1.23', '2015-05-25', '218'],
- ... ['1.23.1', '2015-05-26', '1354'],
- ... ['1.24', '2015-05-26', '223'],
- ... ['1.25', '2015-05-26', '4319'],
- ... ['1.25.1', '2015-06-02', '197'],
- ... ]
- >>> print(format_pretty_table(humanfriendly_releases, column_names))
- -------------------------------------
- | Version | Uploaded on | Downloads |
- -------------------------------------
- | 1.23 | 2015-05-25 | 218 |
- | 1.23.1 | 2015-05-26 | 1354 |
- | 1.24 | 2015-05-26 | 223 |
- | 1.25 | 2015-05-26 | 4319 |
- | 1.25.1 | 2015-06-02 | 197 |
- -------------------------------------
-
- Notes about the resulting table:
-
- - If a column contains numeric data (integer and/or floating point
- numbers) in all rows (ignoring column names of course) then the content
- of that column is right-aligned, as can be seen in the example above. The
- idea here is to make it easier to compare the numbers in different
- columns to each other.
-
- - The column names are highlighted in color so they stand out a bit more
- (see also :data:`.HIGHLIGHT_COLOR`). The following screen shot shows what
- that looks like (my terminals are always set to white text on a black
- background):
-
- .. image:: images/pretty-table.png
- """
- # Normalize the input because we'll have to iterate it more than once.
- data = [normalize_columns(r, expandtabs=True) for r in data]
- if column_names is not None:
- column_names = normalize_columns(column_names)
- if column_names:
- if terminal_supports_colors():
- column_names = [highlight_column_name(n) for n in column_names]
- data.insert(0, column_names)
- # Calculate the maximum width of each column.
- widths = collections.defaultdict(int)
- numeric_data = collections.defaultdict(list)
- for row_index, row in enumerate(data):
- for column_index, column in enumerate(row):
- widths[column_index] = max(widths[column_index], ansi_width(column))
- if not (column_names and row_index == 0):
- numeric_data[column_index].append(bool(NUMERIC_DATA_PATTERN.match(ansi_strip(column))))
- # Create a horizontal bar of dashes as a delimiter.
- line_delimiter = horizontal_bar * (sum(widths.values()) + len(widths) * 3 + 1)
- # Start the table with a vertical bar.
- lines = [line_delimiter]
- # Format the rows and columns.
- for row_index, row in enumerate(data):
- line = [vertical_bar]
- for column_index, column in enumerate(row):
- padding = ' ' * (widths[column_index] - ansi_width(column))
- if all(numeric_data[column_index]):
- line.append(' ' + padding + column + ' ')
- else:
- line.append(' ' + column + padding + ' ')
- line.append(vertical_bar)
- lines.append(u''.join(line))
- if column_names and row_index == 0:
- lines.append(line_delimiter)
- # End the table with a vertical bar.
- lines.append(line_delimiter)
- # Join the lines, returning a single string.
- return u'\n'.join(lines)
-
-
-def format_robust_table(data, column_names):
- """
- Render tabular data with one column per line (allowing columns with line breaks).
-
- :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
- containing the rows of the table, where each row is an
- iterable containing the columns of the table (strings).
- :param column_names: An iterable of column names (strings).
- :returns: The rendered table (a string).
-
- Here's an example:
-
- >>> from humanfriendly.tables import format_robust_table
- >>> column_names = ['Version', 'Uploaded on', 'Downloads']
- >>> humanfriendly_releases = [
- ... ['1.23', '2015-05-25', '218'],
- ... ['1.23.1', '2015-05-26', '1354'],
- ... ['1.24', '2015-05-26', '223'],
- ... ['1.25', '2015-05-26', '4319'],
- ... ['1.25.1', '2015-06-02', '197'],
- ... ]
- >>> print(format_robust_table(humanfriendly_releases, column_names))
- -----------------------
- Version: 1.23
- Uploaded on: 2015-05-25
- Downloads: 218
- -----------------------
- Version: 1.23.1
- Uploaded on: 2015-05-26
- Downloads: 1354
- -----------------------
- Version: 1.24
- Uploaded on: 2015-05-26
- Downloads: 223
- -----------------------
- Version: 1.25
- Uploaded on: 2015-05-26
- Downloads: 4319
- -----------------------
- Version: 1.25.1
- Uploaded on: 2015-06-02
- Downloads: 197
- -----------------------
-
- The column names are highlighted in bold font and color so they stand out a
- bit more (see :data:`.HIGHLIGHT_COLOR`).
- """
- blocks = []
- column_names = ["%s:" % n for n in normalize_columns(column_names)]
- if terminal_supports_colors():
- column_names = [highlight_column_name(n) for n in column_names]
- # Convert each row into one or more `name: value' lines (one per column)
- # and group each `row of lines' into a block (i.e. rows become blocks).
- for row in data:
- lines = []
- for column_index, column_text in enumerate(normalize_columns(row)):
- stripped_column = column_text.strip()
- if '\n' not in stripped_column:
- # Columns without line breaks are formatted inline.
- lines.append("%s %s" % (column_names[column_index], stripped_column))
- else:
- # Columns with line breaks could very well contain indented
- # lines, so we'll put the column name on a separate line. This
- # way any indentation remains intact, and it's easier to
- # copy/paste the text.
- lines.append(column_names[column_index])
- lines.extend(column_text.rstrip().splitlines())
- blocks.append(lines)
- # Calculate the width of the row delimiter.
- num_rows, num_columns = find_terminal_size()
- longest_line = max(max(map(ansi_width, lines)) for lines in blocks)
- delimiter = u"\n%s\n" % ('-' * min(longest_line, num_columns))
- # Force a delimiter at the start and end of the table.
- blocks.insert(0, "")
- blocks.append("")
- # Embed the row delimiter between every two blocks.
- return delimiter.join(u"\n".join(b) for b in blocks).strip()
-
-
-def format_rst_table(data, column_names=None):
- """
- Render a table in reStructuredText_ format.
-
- :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
- containing the rows of the table, where each row is an
- iterable containing the columns of the table (strings).
- :param column_names: An iterable of column names (strings).
- :returns: The rendered table (a string).
-
- Here's an example:
-
- >>> from humanfriendly.tables import format_rst_table
- >>> column_names = ['Version', 'Uploaded on', 'Downloads']
- >>> humanfriendly_releases = [
- ... ['1.23', '2015-05-25', '218'],
- ... ['1.23.1', '2015-05-26', '1354'],
- ... ['1.24', '2015-05-26', '223'],
- ... ['1.25', '2015-05-26', '4319'],
- ... ['1.25.1', '2015-06-02', '197'],
- ... ]
- >>> print(format_rst_table(humanfriendly_releases, column_names))
- ======= =========== =========
- Version Uploaded on Downloads
- ======= =========== =========
- 1.23 2015-05-25 218
- 1.23.1 2015-05-26 1354
- 1.24 2015-05-26 223
- 1.25 2015-05-26 4319
- 1.25.1 2015-06-02 197
- ======= =========== =========
-
- .. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
- """
- data = [normalize_columns(r) for r in data]
- if column_names:
- data.insert(0, normalize_columns(column_names))
- # Calculate the maximum width of each column.
- widths = collections.defaultdict(int)
- for row in data:
- for index, column in enumerate(row):
- widths[index] = max(widths[index], len(column))
- # Pad the columns using whitespace.
- for row in data:
- for index, column in enumerate(row):
- if index < (len(row) - 1):
- row[index] = column.ljust(widths[index])
- # Add table markers.
- delimiter = ['=' * w for i, w in sorted(widths.items())]
- if column_names:
- data.insert(1, delimiter)
- data.insert(0, delimiter)
- data.append(delimiter)
- # Join the lines and columns together.
- return '\n'.join(' '.join(r) for r in data)
-
-
-def normalize_columns(row, expandtabs=False):
- results = []
- for value in row:
- text = coerce_string(value)
- if expandtabs:
- text = text.expandtabs()
- results.append(text)
- return results
-
-
-def highlight_column_name(name):
- return ansi_wrap(name, bold=True, color=HIGHLIGHT_COLOR)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py b/contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py
deleted file mode 100644
index ba9739ccb2..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py
+++ /dev/null
@@ -1,776 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: March 1, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Interaction with interactive text terminals.
-
-The :mod:`~humanfriendly.terminal` module makes it easy to interact with
-interactive text terminals and format text for rendering on such terminals. If
-the terms used in the documentation of this module don't make sense to you then
-please refer to the `Wikipedia article on ANSI escape sequences`_ for details
-about how ANSI escape sequences work.
-
-This module was originally developed for use on UNIX systems, but since then
-Windows 10 gained native support for ANSI escape sequences and this module was
-enhanced to recognize and support this. For details please refer to the
-:func:`enable_ansi_support()` function.
-
-.. _Wikipedia article on ANSI escape sequences: http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
-"""
-
-# Standard library modules.
-import codecs
-import numbers
-import os
-import platform
-import re
-import subprocess
-import sys
-
-# The `fcntl' module is platform specific so importing it may give an error. We
-# hide this implementation detail from callers by handling the import error and
-# setting a flag instead.
-try:
- import fcntl
- import termios
- import struct
- HAVE_IOCTL = True
-except ImportError:
- HAVE_IOCTL = False
-
-# Modules included in our package.
-from humanfriendly.compat import coerce_string, is_unicode, on_windows, which
-from humanfriendly.decorators import cached
-from humanfriendly.deprecation import define_aliases
-from humanfriendly.text import concatenate, format
-from humanfriendly.usage import format_usage
-
-# Public identifiers that require documentation.
-__all__ = (
- 'ANSI_COLOR_CODES',
- 'ANSI_CSI',
- 'ANSI_ERASE_LINE',
- 'ANSI_HIDE_CURSOR',
- 'ANSI_RESET',
- 'ANSI_SGR',
- 'ANSI_SHOW_CURSOR',
- 'ANSI_TEXT_STYLES',
- 'CLEAN_OUTPUT_PATTERN',
- 'DEFAULT_COLUMNS',
- 'DEFAULT_ENCODING',
- 'DEFAULT_LINES',
- 'HIGHLIGHT_COLOR',
- 'ansi_strip',
- 'ansi_style',
- 'ansi_width',
- 'ansi_wrap',
- 'auto_encode',
- 'clean_terminal_output',
- 'connected_to_terminal',
- 'enable_ansi_support',
- 'find_terminal_size',
- 'find_terminal_size_using_ioctl',
- 'find_terminal_size_using_stty',
- 'get_pager_command',
- 'have_windows_native_ansi_support',
- 'message',
- 'output',
- 'readline_strip',
- 'readline_wrap',
- 'show_pager',
- 'terminal_supports_colors',
- 'usage',
- 'warning',
-)
-
-ANSI_CSI = '\x1b['
-"""The ANSI "Control Sequence Introducer" (a string)."""
-
-ANSI_SGR = 'm'
-"""The ANSI "Select Graphic Rendition" sequence (a string)."""
-
-ANSI_ERASE_LINE = '%sK' % ANSI_CSI
-"""The ANSI escape sequence to erase the current line (a string)."""
-
-ANSI_RESET = '%s0%s' % (ANSI_CSI, ANSI_SGR)
-"""The ANSI escape sequence to reset styling (a string)."""
-
-ANSI_HIDE_CURSOR = '%s?25l' % ANSI_CSI
-"""The ANSI escape sequence to hide the text cursor (a string)."""
-
-ANSI_SHOW_CURSOR = '%s?25h' % ANSI_CSI
-"""The ANSI escape sequence to show the text cursor (a string)."""
-
-ANSI_COLOR_CODES = dict(black=0, red=1, green=2, yellow=3, blue=4, magenta=5, cyan=6, white=7)
-"""
-A dictionary with (name, number) pairs of `portable color codes`_. Used by
-:func:`ansi_style()` to generate ANSI escape sequences that change font color.
-
-.. _portable color codes: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
-"""
-
-ANSI_TEXT_STYLES = dict(bold=1, faint=2, italic=3, underline=4, inverse=7, strike_through=9)
-"""
-A dictionary with (name, number) pairs of text styles (effects). Used by
-:func:`ansi_style()` to generate ANSI escape sequences that change text
-styles. Only widely supported text styles are included here.
-"""
-
-CLEAN_OUTPUT_PATTERN = re.compile(u'(\r|\n|\b|%s)' % re.escape(ANSI_ERASE_LINE))
-"""
-A compiled regular expression used to separate significant characters from other text.
-
-This pattern is used by :func:`clean_terminal_output()` to split terminal
-output into regular text versus backspace, carriage return and line feed
-characters and ANSI 'erase line' escape sequences.
-"""
-
-DEFAULT_LINES = 25
-"""The default number of lines in a terminal (an integer)."""
-
-DEFAULT_COLUMNS = 80
-"""The default number of columns in a terminal (an integer)."""
-
-DEFAULT_ENCODING = 'UTF-8'
-"""The output encoding for Unicode strings."""
-
-HIGHLIGHT_COLOR = os.environ.get('HUMANFRIENDLY_HIGHLIGHT_COLOR', 'green')
-"""
-The color used to highlight important tokens in formatted text (e.g. the usage
-message of the ``humanfriendly`` program). If the environment variable
-``$HUMANFRIENDLY_HIGHLIGHT_COLOR`` is set it determines the value of
-:data:`HIGHLIGHT_COLOR`.
-"""
-
-
-def ansi_strip(text, readline_hints=True):
- """
- Strip ANSI escape sequences from the given string.
-
- :param text: The text from which ANSI escape sequences should be removed (a
- string).
- :param readline_hints: If :data:`True` then :func:`readline_strip()` is
- used to remove `readline hints`_ from the string.
- :returns: The text without ANSI escape sequences (a string).
- """
- pattern = '%s.*?%s' % (re.escape(ANSI_CSI), re.escape(ANSI_SGR))
- text = re.sub(pattern, '', text)
- if readline_hints:
- text = readline_strip(text)
- return text
-
-
-def ansi_style(**kw):
- """
- Generate ANSI escape sequences for the given color and/or style(s).
-
- :param color: The foreground color. Three types of values are supported:
-
- - The name of a color (one of the strings 'black', 'red',
- 'green', 'yellow', 'blue', 'magenta', 'cyan' or 'white').
- - An integer that refers to the 256 color mode palette.
- - A tuple or list with three integers representing an RGB
- (red, green, blue) value.
-
- The value :data:`None` (the default) means no escape
- sequence to switch color will be emitted.
- :param background: The background color (see the description
- of the `color` argument).
- :param bright: Use high intensity colors instead of default colors
- (a boolean, defaults to :data:`False`).
- :param readline_hints: If :data:`True` then :func:`readline_wrap()` is
- applied to the generated ANSI escape sequences (the
- default is :data:`False`).
- :param kw: Any additional keyword arguments are expected to match a key
- in the :data:`ANSI_TEXT_STYLES` dictionary. If the argument's
- value evaluates to :data:`True` the respective style will be
- enabled.
- :returns: The ANSI escape sequences to enable the requested text styles or
- an empty string if no styles were requested.
- :raises: :exc:`~exceptions.ValueError` when an invalid color name is given.
-
- Even though only eight named colors are supported, the use of `bright=True`
- and `faint=True` increases the number of available colors to around 24 (it
- may be slightly lower, for example because faint black is just black).
-
- **Support for 8-bit colors**
-
- In `release 4.7`_ support for 256 color mode was added. While this
- significantly increases the available colors it's not very human friendly
- in usage because you need to look up color codes in the `256 color mode
- palette <https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit>`_.
-
- You can use the ``humanfriendly --demo`` command to get a demonstration of
- the available colors, see also the screen shot below. Note that the small
- font size in the screen shot was so that the demonstration of 256 color
- mode support would fit into a single screen shot without scrolling :-)
- (I wasn't feeling very creative).
-
- .. image:: images/ansi-demo.png
-
- **Support for 24-bit colors**
-
- In `release 4.14`_ support for 24-bit colors was added by accepting a tuple
- or list with three integers representing the RGB (red, green, blue) value
- of a color. This is not included in the demo because rendering millions of
- colors was deemed unpractical ;-).
-
- .. _release 4.7: http://humanfriendly.readthedocs.io/en/latest/changelog.html#release-4-7-2018-01-14
- .. _release 4.14: http://humanfriendly.readthedocs.io/en/latest/changelog.html#release-4-14-2018-07-13
- """
- # Start with sequences that change text styles.
- sequences = [ANSI_TEXT_STYLES[k] for k, v in kw.items() if k in ANSI_TEXT_STYLES and v]
- # Append the color code (if any).
- for color_type in 'color', 'background':
- color_value = kw.get(color_type)
- if isinstance(color_value, (tuple, list)):
- if len(color_value) != 3:
- msg = "Invalid color value %r! (expected tuple or list with three numbers)"
- raise ValueError(msg % color_value)
- sequences.append(48 if color_type == 'background' else 38)
- sequences.append(2)
- sequences.extend(map(int, color_value))
- elif isinstance(color_value, numbers.Number):
- # Numeric values are assumed to be 256 color codes.
- sequences.extend((
- 39 if color_type == 'background' else 38,
- 5, int(color_value)
- ))
- elif color_value:
- # Other values are assumed to be strings containing one of the known color names.
- if color_value not in ANSI_COLOR_CODES:
- msg = "Invalid color value %r! (expected an integer or one of the strings %s)"
- raise ValueError(msg % (color_value, concatenate(map(repr, sorted(ANSI_COLOR_CODES)))))
- # Pick the right offset for foreground versus background
- # colors and regular intensity versus bright colors.
- offset = (
- (100 if kw.get('bright') else 40)
- if color_type == 'background'
- else (90 if kw.get('bright') else 30)
- )
- # Combine the offset and color code into a single integer.
- sequences.append(offset + ANSI_COLOR_CODES[color_value])
- if sequences:
- encoded = ANSI_CSI + ';'.join(map(str, sequences)) + ANSI_SGR
- return readline_wrap(encoded) if kw.get('readline_hints') else encoded
- else:
- return ''
-
-
-def ansi_width(text):
- """
- Calculate the effective width of the given text (ignoring ANSI escape sequences).
-
- :param text: The text whose width should be calculated (a string).
- :returns: The width of the text without ANSI escape sequences (an
- integer).
-
- This function uses :func:`ansi_strip()` to strip ANSI escape sequences from
- the given string and returns the length of the resulting string.
- """
- return len(ansi_strip(text))
-
-
-def ansi_wrap(text, **kw):
- """
- Wrap text in ANSI escape sequences for the given color and/or style(s).
-
- :param text: The text to wrap (a string).
- :param kw: Any keyword arguments are passed to :func:`ansi_style()`.
- :returns: The result of this function depends on the keyword arguments:
-
- - If :func:`ansi_style()` generates an ANSI escape sequence based
- on the keyword arguments, the given text is prefixed with the
- generated ANSI escape sequence and suffixed with
- :data:`ANSI_RESET`.
-
- - If :func:`ansi_style()` returns an empty string then the text
- given by the caller is returned unchanged.
- """
- start_sequence = ansi_style(**kw)
- if start_sequence:
- end_sequence = ANSI_RESET
- if kw.get('readline_hints'):
- end_sequence = readline_wrap(end_sequence)
- return start_sequence + text + end_sequence
- else:
- return text
-
-
-def auto_encode(stream, text, *args, **kw):
- """
- Reliably write Unicode strings to the terminal.
-
- :param stream: The file-like object to write to (a value like
- :data:`sys.stdout` or :data:`sys.stderr`).
- :param text: The text to write to the stream (a string).
- :param args: Refer to :func:`~humanfriendly.text.format()`.
- :param kw: Refer to :func:`~humanfriendly.text.format()`.
-
- Renders the text using :func:`~humanfriendly.text.format()` and writes it
- to the given stream. If an :exc:`~exceptions.UnicodeEncodeError` is
- encountered in doing so, the text is encoded using :data:`DEFAULT_ENCODING`
- and the write is retried. The reasoning behind this rather blunt approach
- is that it's preferable to get output on the command line in the wrong
- encoding then to have the Python program blow up with a
- :exc:`~exceptions.UnicodeEncodeError` exception.
- """
- text = format(text, *args, **kw)
- try:
- stream.write(text)
- except UnicodeEncodeError:
- stream.write(codecs.encode(text, DEFAULT_ENCODING))
-
-
-def clean_terminal_output(text):
- """
- Clean up the terminal output of a command.
-
- :param text: The raw text with special characters (a Unicode string).
- :returns: A list of Unicode strings (one for each line).
-
- This function emulates the effect of backspace (0x08), carriage return
- (0x0D) and line feed (0x0A) characters and the ANSI 'erase line' escape
- sequence on interactive terminals. It's intended to clean up command output
- that was originally meant to be rendered on an interactive terminal and
- that has been captured using e.g. the :man:`script` program [#]_ or the
- :mod:`pty` module [#]_.
-
- .. [#] My coloredlogs_ package supports the ``coloredlogs --to-html``
- command which uses :man:`script` to fool a subprocess into thinking
- that it's connected to an interactive terminal (in order to get it
- to emit ANSI escape sequences).
-
- .. [#] My capturer_ package uses the :mod:`pty` module to fool the current
- process and subprocesses into thinking they are connected to an
- interactive terminal (in order to get them to emit ANSI escape
- sequences).
-
- **Some caveats about the use of this function:**
-
- - Strictly speaking the effect of carriage returns cannot be emulated
- outside of an actual terminal due to the interaction between overlapping
- output, terminal widths and line wrapping. The goal of this function is
- to sanitize noise in terminal output while preserving useful output.
- Think of it as a useful and pragmatic but possibly lossy conversion.
-
- - The algorithm isn't smart enough to properly handle a pair of ANSI escape
- sequences that open before a carriage return and close after the last
- carriage return in a linefeed delimited string; the resulting string will
- contain only the closing end of the ANSI escape sequence pair. Tracking
- this kind of complexity requires a state machine and proper parsing.
-
- .. _capturer: https://pypi.org/project/capturer
- .. _coloredlogs: https://pypi.org/project/coloredlogs
- """
- cleaned_lines = []
- current_line = ''
- current_position = 0
- for token in CLEAN_OUTPUT_PATTERN.split(text):
- if token == '\r':
- # Seek back to the start of the current line.
- current_position = 0
- elif token == '\b':
- # Seek back one character in the current line.
- current_position = max(0, current_position - 1)
- else:
- if token == '\n':
- # Capture the current line.
- cleaned_lines.append(current_line)
- if token in ('\n', ANSI_ERASE_LINE):
- # Clear the current line.
- current_line = ''
- current_position = 0
- elif token:
- # Merge regular output into the current line.
- new_position = current_position + len(token)
- prefix = current_line[:current_position]
- suffix = current_line[new_position:]
- current_line = prefix + token + suffix
- current_position = new_position
- # Capture the last line (if any).
- cleaned_lines.append(current_line)
- # Remove any empty trailing lines.
- while cleaned_lines and not cleaned_lines[-1]:
- cleaned_lines.pop(-1)
- return cleaned_lines
-
-
-def connected_to_terminal(stream=None):
- """
- Check if a stream is connected to a terminal.
-
- :param stream: The stream to check (a file-like object,
- defaults to :data:`sys.stdout`).
- :returns: :data:`True` if the stream is connected to a terminal,
- :data:`False` otherwise.
-
- See also :func:`terminal_supports_colors()`.
- """
- stream = sys.stdout if stream is None else stream
- try:
- return stream.isatty()
- except Exception:
- return False
-
-
-@cached
-def enable_ansi_support():
- """
- Try to enable support for ANSI escape sequences (required on Windows).
-
- :returns: :data:`True` if ANSI is supported, :data:`False` otherwise.
-
- This functions checks for the following supported configurations, in the
- given order:
-
- 1. On Windows, if :func:`have_windows_native_ansi_support()` confirms
- native support for ANSI escape sequences :mod:`ctypes` will be used to
- enable this support.
-
- 2. On Windows, if the environment variable ``$ANSICON`` is set nothing is
- done because it is assumed that support for ANSI escape sequences has
- already been enabled via `ansicon <https://github.com/adoxa/ansicon>`_.
-
- 3. On Windows, an attempt is made to import and initialize the Python
- package :pypi:`colorama` instead (of course for this to work
- :pypi:`colorama` has to be installed).
-
- 4. On other platforms this function calls :func:`connected_to_terminal()`
- to determine whether ANSI escape sequences are supported (that is to
- say all platforms that are not Windows are assumed to support ANSI
- escape sequences natively, without weird contortions like above).
-
- This makes it possible to call :func:`enable_ansi_support()`
- unconditionally without checking the current platform.
-
- The :func:`~humanfriendly.decorators.cached` decorator is used to ensure
- that this function is only executed once, but its return value remains
- available on later calls.
- """
- if have_windows_native_ansi_support():
- import ctypes
- ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-11), 7)
- ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-12), 7)
- return True
- elif on_windows():
- if 'ANSICON' in os.environ:
- return True
- try:
- import colorama
- colorama.init()
- return True
- except ImportError:
- return False
- else:
- return connected_to_terminal()
-
-
-def find_terminal_size():
- """
- Determine the number of lines and columns visible in the terminal.
-
- :returns: A tuple of two integers with the line and column count.
-
- The result of this function is based on the first of the following three
- methods that works:
-
- 1. First :func:`find_terminal_size_using_ioctl()` is tried,
- 2. then :func:`find_terminal_size_using_stty()` is tried,
- 3. finally :data:`DEFAULT_LINES` and :data:`DEFAULT_COLUMNS` are returned.
-
- .. note:: The :func:`find_terminal_size()` function performs the steps
- above every time it is called, the result is not cached. This is
- because the size of a virtual terminal can change at any time and
- the result of :func:`find_terminal_size()` should be correct.
-
- `Pre-emptive snarky comment`_: It's possible to cache the result
- of this function and use :mod:`signal.SIGWINCH <signal>` to
- refresh the cached values!
-
- Response: As a library I don't consider it the role of the
- :mod:`humanfriendly.terminal` module to install a process wide
- signal handler ...
-
- .. _Pre-emptive snarky comment: http://blogs.msdn.com/b/oldnewthing/archive/2008/01/30/7315957.aspx
- """
- # The first method. Any of the standard streams may have been redirected
- # somewhere and there's no telling which, so we'll just try them all.
- for stream in sys.stdin, sys.stdout, sys.stderr:
- try:
- result = find_terminal_size_using_ioctl(stream)
- if min(result) >= 1:
- return result
- except Exception:
- pass
- # The second method.
- try:
- result = find_terminal_size_using_stty()
- if min(result) >= 1:
- return result
- except Exception:
- pass
- # Fall back to conservative defaults.
- return DEFAULT_LINES, DEFAULT_COLUMNS
-
-
-def find_terminal_size_using_ioctl(stream):
- """
- Find the terminal size using :func:`fcntl.ioctl()`.
-
- :param stream: A stream connected to the terminal (a file object with a
- ``fileno`` attribute).
- :returns: A tuple of two integers with the line and column count.
- :raises: This function can raise exceptions but I'm not going to document
- them here, you should be using :func:`find_terminal_size()`.
-
- Based on an `implementation found on StackOverflow <http://stackoverflow.com/a/3010495/788200>`_.
- """
- if not HAVE_IOCTL:
- raise NotImplementedError("It looks like the `fcntl' module is not available!")
- h, w, hp, wp = struct.unpack('HHHH', fcntl.ioctl(stream, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0)))
- return h, w
-
-
-def find_terminal_size_using_stty():
- """
- Find the terminal size using the external command ``stty size``.
-
- :param stream: A stream connected to the terminal (a file object).
- :returns: A tuple of two integers with the line and column count.
- :raises: This function can raise exceptions but I'm not going to document
- them here, you should be using :func:`find_terminal_size()`.
- """
- stty = subprocess.Popen(['stty', 'size'],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = stty.communicate()
- tokens = stdout.split()
- if len(tokens) != 2:
- raise Exception("Invalid output from `stty size'!")
- return tuple(map(int, tokens))
-
-
-def get_pager_command(text=None):
- """
- Get the command to show a text on the terminal using a pager.
-
- :param text: The text to print to the terminal (a string).
- :returns: A list of strings with the pager command and arguments.
-
- The use of a pager helps to avoid the wall of text effect where the user
- has to scroll up to see where the output began (not very user friendly).
-
- If the given text contains ANSI escape sequences the command ``less
- --RAW-CONTROL-CHARS`` is used, otherwise the environment variable
- ``$PAGER`` is used (if ``$PAGER`` isn't set :man:`less` is used).
-
- When the selected pager is :man:`less`, the following options are used to
- make the experience more user friendly:
-
- - ``--quit-if-one-screen`` causes :man:`less` to automatically exit if the
- entire text can be displayed on the first screen. This makes the use of a
- pager transparent for smaller texts (because the operator doesn't have to
- quit the pager).
-
- - ``--no-init`` prevents :man:`less` from clearing the screen when it
- exits. This ensures that the operator gets a chance to review the text
- (for example a usage message) after quitting the pager, while composing
- the next command.
- """
- # Compose the pager command.
- if text and ANSI_CSI in text:
- command_line = ['less', '--RAW-CONTROL-CHARS']
- else:
- command_line = [os.environ.get('PAGER', 'less')]
- # Pass some additional options to `less' (to make it more
- # user friendly) without breaking support for other pagers.
- if os.path.basename(command_line[0]) == 'less':
- command_line.append('--no-init')
- command_line.append('--quit-if-one-screen')
- return command_line
-
-
-@cached
-def have_windows_native_ansi_support():
- """
- Check if we're running on a Windows 10 release with native support for ANSI escape sequences.
-
- :returns: :data:`True` if so, :data:`False` otherwise.
-
- The :func:`~humanfriendly.decorators.cached` decorator is used as a minor
- performance optimization. Semantically this should have zero impact because
- the answer doesn't change in the lifetime of a computer process.
- """
- if on_windows():
- try:
- # I can't be 100% sure this will never break and I'm not in a
- # position to test it thoroughly either, so I decided that paying
- # the price of one additional try / except statement is worth the
- # additional peace of mind :-).
- components = tuple(int(c) for c in platform.version().split('.'))
- return components >= (10, 0, 14393)
- except Exception:
- pass
- return False
-
-
-def message(text, *args, **kw):
- """
- Print a formatted message to the standard error stream.
-
- For details about argument handling please refer to
- :func:`~humanfriendly.text.format()`.
-
- Renders the message using :func:`~humanfriendly.text.format()` and writes
- the resulting string (followed by a newline) to :data:`sys.stderr` using
- :func:`auto_encode()`.
- """
- auto_encode(sys.stderr, coerce_string(text) + '\n', *args, **kw)
-
-
-def output(text, *args, **kw):
- """
- Print a formatted message to the standard output stream.
-
- For details about argument handling please refer to
- :func:`~humanfriendly.text.format()`.
-
- Renders the message using :func:`~humanfriendly.text.format()` and writes
- the resulting string (followed by a newline) to :data:`sys.stdout` using
- :func:`auto_encode()`.
- """
- auto_encode(sys.stdout, coerce_string(text) + '\n', *args, **kw)
-
-
-def readline_strip(expr):
- """
- Remove `readline hints`_ from a string.
-
- :param text: The text to strip (a string).
- :returns: The stripped text.
- """
- return expr.replace('\001', '').replace('\002', '')
-
-
-def readline_wrap(expr):
- """
- Wrap an ANSI escape sequence in `readline hints`_.
-
- :param text: The text with the escape sequence to wrap (a string).
- :returns: The wrapped text.
-
- .. _readline hints: http://superuser.com/a/301355
- """
- return '\001' + expr + '\002'
-
-
-def show_pager(formatted_text, encoding=DEFAULT_ENCODING):
- """
- Print a large text to the terminal using a pager.
-
- :param formatted_text: The text to print to the terminal (a string).
- :param encoding: The name of the text encoding used to encode the formatted
- text if the formatted text is a Unicode string (a string,
- defaults to :data:`DEFAULT_ENCODING`).
-
- When :func:`connected_to_terminal()` returns :data:`True` a pager is used
- to show the text on the terminal, otherwise the text is printed directly
- without invoking a pager.
-
- The use of a pager helps to avoid the wall of text effect where the user
- has to scroll up to see where the output began (not very user friendly).
-
- Refer to :func:`get_pager_command()` for details about the command line
- that's used to invoke the pager.
- """
- if connected_to_terminal():
- # Make sure the selected pager command is available.
- command_line = get_pager_command(formatted_text)
- if which(command_line[0]):
- pager = subprocess.Popen(command_line, stdin=subprocess.PIPE)
- if is_unicode(formatted_text):
- formatted_text = formatted_text.encode(encoding)
- pager.communicate(input=formatted_text)
- return
- output(formatted_text)
-
-
-def terminal_supports_colors(stream=None):
- """
- Check if a stream is connected to a terminal that supports ANSI escape sequences.
-
- :param stream: The stream to check (a file-like object,
- defaults to :data:`sys.stdout`).
- :returns: :data:`True` if the terminal supports ANSI escape sequences,
- :data:`False` otherwise.
-
- This function was originally inspired by the implementation of
- `django.core.management.color.supports_color()
- <https://github.com/django/django/blob/master/django/core/management/color.py>`_
- but has since evolved significantly.
- """
- if on_windows():
- # On Windows support for ANSI escape sequences is not a given.
- have_ansicon = 'ANSICON' in os.environ
- have_colorama = 'colorama' in sys.modules
- have_native_support = have_windows_native_ansi_support()
- if not (have_ansicon or have_colorama or have_native_support):
- return False
- return connected_to_terminal(stream)
-
-
-def usage(usage_text):
- """
- Print a human friendly usage message to the terminal.
-
- :param text: The usage message to print (a string).
-
- This function does two things:
-
- 1. If :data:`sys.stdout` is connected to a terminal (see
- :func:`connected_to_terminal()`) then the usage message is formatted
- using :func:`.format_usage()`.
- 2. The usage message is shown using a pager (see :func:`show_pager()`).
- """
- if terminal_supports_colors(sys.stdout):
- usage_text = format_usage(usage_text)
- show_pager(usage_text)
-
-
-def warning(text, *args, **kw):
- """
- Show a warning message on the terminal.
-
- For details about argument handling please refer to
- :func:`~humanfriendly.text.format()`.
-
- Renders the message using :func:`~humanfriendly.text.format()` and writes
- the resulting string (followed by a newline) to :data:`sys.stderr` using
- :func:`auto_encode()`.
-
- If :data:`sys.stderr` is connected to a terminal that supports colors,
- :func:`ansi_wrap()` is used to color the message in a red font (to make
- the warning stand out from surrounding text).
- """
- text = coerce_string(text)
- if terminal_supports_colors(sys.stderr):
- text = ansi_wrap(text, color='red')
- auto_encode(sys.stderr, text + '\n', *args, **kw)
-
-
-# Define aliases for backwards compatibility.
-define_aliases(
- module_name=__name__,
- # In humanfriendly 1.31 the find_meta_variables() and format_usage()
- # functions were extracted to the new module humanfriendly.usage.
- find_meta_variables='humanfriendly.usage.find_meta_variables',
- format_usage='humanfriendly.usage.format_usage',
- # In humanfriendly 8.0 the html_to_ansi() function and HTMLConverter
- # class were extracted to the new module humanfriendly.terminal.html.
- html_to_ansi='humanfriendly.terminal.html.html_to_ansi',
- HTMLConverter='humanfriendly.terminal.html.HTMLConverter',
-)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py b/contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py
deleted file mode 100644
index 4214e09e70..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py
+++ /dev/null
@@ -1,423 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: February 29, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""Convert HTML with simple text formatting to text with ANSI escape sequences."""
-
-# Standard library modules.
-import re
-
-# Modules included in our package.
-from humanfriendly.compat import HTMLParser, StringIO, name2codepoint, unichr
-from humanfriendly.text import compact_empty_lines
-from humanfriendly.terminal import ANSI_COLOR_CODES, ANSI_RESET, ansi_style
-
-# Public identifiers that require documentation.
-__all__ = ('HTMLConverter', 'html_to_ansi')
-
-
-def html_to_ansi(data, callback=None):
- """
- Convert HTML with simple text formatting to text with ANSI escape sequences.
-
- :param data: The HTML to convert (a string).
- :param callback: Optional callback to pass to :class:`HTMLConverter`.
- :returns: Text with ANSI escape sequences (a string).
-
- Please refer to the documentation of the :class:`HTMLConverter` class for
- details about the conversion process (like which tags are supported) and an
- example with a screenshot.
- """
- converter = HTMLConverter(callback=callback)
- return converter(data)
-
-
-class HTMLConverter(HTMLParser):
-
- """
- Convert HTML with simple text formatting to text with ANSI escape sequences.
-
- The following text styles are supported:
-
- - Bold: ``<b>``, ``<strong>`` and ``<span style="font-weight: bold;">``
- - Italic: ``<i>``, ``<em>`` and ``<span style="font-style: italic;">``
- - Strike-through: ``<del>``, ``<s>`` and ``<span style="text-decoration: line-through;">``
- - Underline: ``<ins>``, ``<u>`` and ``<span style="text-decoration: underline">``
-
- Colors can be specified as follows:
-
- - Foreground color: ``<span style="color: #RRGGBB;">``
- - Background color: ``<span style="background-color: #RRGGBB;">``
-
- Here's a small demonstration:
-
- .. code-block:: python
-
- from humanfriendly.text import dedent
- from humanfriendly.terminal import html_to_ansi
-
- print(html_to_ansi(dedent('''
- <b>Hello world!</b>
- <i>Is this thing on?</i>
- I guess I can <u>underline</u> or <s>strike-through</s> text?
- And what about <span style="color: red">color</span>?
- ''')))
-
- rainbow_colors = [
- '#FF0000', '#E2571E', '#FF7F00', '#FFFF00', '#00FF00',
- '#96BF33', '#0000FF', '#4B0082', '#8B00FF', '#FFFFFF',
- ]
- html_rainbow = "".join('<span style="color: %s">o</span>' % c for c in rainbow_colors)
- print(html_to_ansi("Let's try a rainbow: %s" % html_rainbow))
-
- Here's what the results look like:
-
- .. image:: images/html-to-ansi.png
-
- Some more details:
-
- - Nested tags are supported, within reasonable limits.
-
- - Text in ``<code>`` and ``<pre>`` tags will be highlighted in a
- different color from the main text (currently this is yellow).
-
- - ``<a href="URL">TEXT</a>`` is converted to the format "TEXT (URL)" where
- the uppercase symbols are highlighted in light blue with an underline.
-
- - ``<div>``, ``<p>`` and ``<pre>`` tags are considered block level tags
- and are wrapped in vertical whitespace to prevent their content from
- "running into" surrounding text. This may cause runs of multiple empty
- lines to be emitted. As a *workaround* the :func:`__call__()` method
- will automatically call :func:`.compact_empty_lines()` on the generated
- output before returning it to the caller. Of course this won't work
- when `output` is set to something like :data:`sys.stdout`.
-
- - ``<br>`` is converted to a single plain text line break.
-
- Implementation notes:
-
- - A list of dictionaries with style information is used as a stack where
- new styling can be pushed and a pop will restore the previous styling.
- When new styling is pushed, it is merged with (but overrides) the current
- styling.
-
- - If you're going to be converting a lot of HTML it might be useful from
- a performance standpoint to re-use an existing :class:`HTMLConverter`
- object for unrelated HTML fragments, in this case take a look at the
- :func:`__call__()` method (it makes this use case very easy).
-
- .. versionadded:: 4.15
- :class:`humanfriendly.terminal.HTMLConverter` was added to the
- `humanfriendly` package during the initial development of my new
- `chat-archive <https://chat-archive.readthedocs.io/>`_ project, whose
- command line interface makes for a great demonstration of the
- flexibility that this feature provides (hint: check out how the search
- keyword highlighting combines with the regular highlighting).
- """
-
- BLOCK_TAGS = ('div', 'p', 'pre')
- """The names of tags that are padded with vertical whitespace."""
-
- def __init__(self, *args, **kw):
- """
- Initialize an :class:`HTMLConverter` object.
-
- :param callback: Optional keyword argument to specify a function that
- will be called to process text fragments before they
- are emitted on the output stream. Note that link text
- and preformatted text fragments are not processed by
- this callback.
- :param output: Optional keyword argument to redirect the output to the
- given file-like object. If this is not given a new
- :class:`~python3:io.StringIO` object is created.
- """
- # Hide our optional keyword arguments from the superclass.
- self.callback = kw.pop("callback", None)
- self.output = kw.pop("output", None)
- # Initialize the superclass.
- HTMLParser.__init__(self, *args, **kw)
-
- def __call__(self, data):
- """
- Reset the parser, convert some HTML and get the text with ANSI escape sequences.
-
- :param data: The HTML to convert to text (a string).
- :returns: The converted text (only in case `output` is
- a :class:`~python3:io.StringIO` object).
- """
- self.reset()
- self.feed(data)
- self.close()
- if isinstance(self.output, StringIO):
- return compact_empty_lines(self.output.getvalue())
-
- @property
- def current_style(self):
- """Get the current style from the top of the stack (a dictionary)."""
- return self.stack[-1] if self.stack else {}
-
- def close(self):
- """
- Close previously opened ANSI escape sequences.
-
- This method overrides the same method in the superclass to ensure that
- an :data:`.ANSI_RESET` code is emitted when parsing reaches the end of
- the input but a style is still active. This is intended to prevent
- malformed HTML from messing up terminal output.
- """
- if any(self.stack):
- self.output.write(ANSI_RESET)
- self.stack = []
- HTMLParser.close(self)
-
- def emit_style(self, style=None):
- """
- Emit an ANSI escape sequence for the given or current style to the output stream.
-
- :param style: A dictionary with arguments for :func:`.ansi_style()` or
- :data:`None`, in which case the style at the top of the
- stack is emitted.
- """
- # Clear the current text styles.
- self.output.write(ANSI_RESET)
- # Apply a new text style?
- style = self.current_style if style is None else style
- if style:
- self.output.write(ansi_style(**style))
-
- def handle_charref(self, value):
- """
- Process a decimal or hexadecimal numeric character reference.
-
- :param value: The decimal or hexadecimal value (a string).
- """
- self.output.write(unichr(int(value[1:], 16) if value.startswith('x') else int(value)))
-
- def handle_data(self, data):
- """
- Process textual data.
-
- :param data: The decoded text (a string).
- """
- if self.link_url:
- # Link text is captured literally so that we can reliably check
- # whether the text and the URL of the link are the same string.
- self.link_text = data
- elif self.callback and self.preformatted_text_level == 0:
- # Text that is not part of a link and not preformatted text is
- # passed to the user defined callback to allow for arbitrary
- # pre-processing.
- data = self.callback(data)
- # All text is emitted unmodified on the output stream.
- self.output.write(data)
-
- def handle_endtag(self, tag):
- """
- Process the end of an HTML tag.
-
- :param tag: The name of the tag (a string).
- """
- if tag in ('a', 'b', 'code', 'del', 'em', 'i', 'ins', 'pre', 's', 'strong', 'span', 'u'):
- old_style = self.current_style
- # The following conditional isn't necessary for well formed
- # HTML but prevents raising exceptions on malformed HTML.
- if self.stack:
- self.stack.pop(-1)
- new_style = self.current_style
- if tag == 'a':
- if self.urls_match(self.link_text, self.link_url):
- # Don't render the URL when it's part of the link text.
- self.emit_style(new_style)
- else:
- self.emit_style(new_style)
- self.output.write(' (')
- self.emit_style(old_style)
- self.output.write(self.render_url(self.link_url))
- self.emit_style(new_style)
- self.output.write(')')
- else:
- self.emit_style(new_style)
- if tag in ('code', 'pre'):
- self.preformatted_text_level -= 1
- if tag in self.BLOCK_TAGS:
- # Emit an empty line after block level tags.
- self.output.write('\n\n')
-
- def handle_entityref(self, name):
- """
- Process a named character reference.
-
- :param name: The name of the character reference (a string).
- """
- self.output.write(unichr(name2codepoint[name]))
-
- def handle_starttag(self, tag, attrs):
- """
- Process the start of an HTML tag.
-
- :param tag: The name of the tag (a string).
- :param attrs: A list of tuples with two strings each.
- """
- if tag in self.BLOCK_TAGS:
- # Emit an empty line before block level tags.
- self.output.write('\n\n')
- if tag == 'a':
- self.push_styles(color='blue', bright=True, underline=True)
- # Store the URL that the link points to for later use, so that we
- # can render the link text before the URL (with the reasoning that
- # this is the most intuitive way to present a link in a plain text
- # interface).
- self.link_url = next((v for n, v in attrs if n == 'href'), '')
- elif tag == 'b' or tag == 'strong':
- self.push_styles(bold=True)
- elif tag == 'br':
- self.output.write('\n')
- elif tag == 'code' or tag == 'pre':
- self.push_styles(color='yellow')
- self.preformatted_text_level += 1
- elif tag == 'del' or tag == 's':
- self.push_styles(strike_through=True)
- elif tag == 'em' or tag == 'i':
- self.push_styles(italic=True)
- elif tag == 'ins' or tag == 'u':
- self.push_styles(underline=True)
- elif tag == 'span':
- styles = {}
- css = next((v for n, v in attrs if n == 'style'), "")
- for rule in css.split(';'):
- name, _, value = rule.partition(':')
- name = name.strip()
- value = value.strip()
- if name == 'background-color':
- styles['background'] = self.parse_color(value)
- elif name == 'color':
- styles['color'] = self.parse_color(value)
- elif name == 'font-style' and value == 'italic':
- styles['italic'] = True
- elif name == 'font-weight' and value == 'bold':
- styles['bold'] = True
- elif name == 'text-decoration' and value == 'line-through':
- styles['strike_through'] = True
- elif name == 'text-decoration' and value == 'underline':
- styles['underline'] = True
- self.push_styles(**styles)
-
- def normalize_url(self, url):
- """
- Normalize a URL to enable string equality comparison.
-
- :param url: The URL to normalize (a string).
- :returns: The normalized URL (a string).
- """
- return re.sub('^mailto:', '', url)
-
- def parse_color(self, value):
- """
- Convert a CSS color to something that :func:`.ansi_style()` understands.
-
- :param value: A string like ``rgb(1,2,3)``, ``#AABBCC`` or ``yellow``.
- :returns: A color value supported by :func:`.ansi_style()` or :data:`None`.
- """
- # Parse an 'rgb(N,N,N)' expression.
- if value.startswith('rgb'):
- tokens = re.findall(r'\d+', value)
- if len(tokens) == 3:
- return tuple(map(int, tokens))
- # Parse an '#XXXXXX' expression.
- elif value.startswith('#'):
- value = value[1:]
- length = len(value)
- if length == 6:
- # Six hex digits (proper notation).
- return (
- int(value[:2], 16),
- int(value[2:4], 16),
- int(value[4:6], 16),
- )
- elif length == 3:
- # Three hex digits (shorthand).
- return (
- int(value[0], 16),
- int(value[1], 16),
- int(value[2], 16),
- )
- # Try to recognize a named color.
- value = value.lower()
- if value in ANSI_COLOR_CODES:
- return value
-
- def push_styles(self, **changes):
- """
- Push new style information onto the stack.
-
- :param changes: Any keyword arguments are passed on to :func:`.ansi_style()`.
-
- This method is a helper for :func:`handle_starttag()`
- that does the following:
-
- 1. Make a copy of the current styles (from the top of the stack),
- 2. Apply the given `changes` to the copy of the current styles,
- 3. Add the new styles to the stack,
- 4. Emit the appropriate ANSI escape sequence to the output stream.
- """
- prototype = self.current_style
- if prototype:
- new_style = dict(prototype)
- new_style.update(changes)
- else:
- new_style = changes
- self.stack.append(new_style)
- self.emit_style(new_style)
-
- def render_url(self, url):
- """
- Prepare a URL for rendering on the terminal.
-
- :param url: The URL to simplify (a string).
- :returns: The simplified URL (a string).
-
- This method pre-processes a URL before rendering on the terminal. The
- following modifications are made:
-
- - The ``mailto:`` prefix is stripped.
- - Spaces are converted to ``%20``.
- - A trailing parenthesis is converted to ``%29``.
- """
- url = re.sub('^mailto:', '', url)
- url = re.sub(' ', '%20', url)
- url = re.sub(r'\)$', '%29', url)
- return url
-
- def reset(self):
- """
- Reset the state of the HTML parser and ANSI converter.
-
- When `output` is a :class:`~python3:io.StringIO` object a new
- instance will be created (and the old one garbage collected).
- """
- # Reset the state of the superclass.
- HTMLParser.reset(self)
- # Reset our instance variables.
- self.link_text = None
- self.link_url = None
- self.preformatted_text_level = 0
- if self.output is None or isinstance(self.output, StringIO):
- # If the caller specified something like output=sys.stdout then it
- # doesn't make much sense to negate that choice here in reset().
- self.output = StringIO()
- self.stack = []
-
- def urls_match(self, a, b):
- """
- Compare two URLs for equality using :func:`normalize_url()`.
-
- :param a: A string containing a URL.
- :param b: A string containing a URL.
- :returns: :data:`True` if the URLs are the same, :data:`False` otherwise.
-
- This method is used by :func:`handle_endtag()` to omit the URL of a
- hyperlink (``<a href="...">``) when the link text is that same URL.
- """
- return self.normalize_url(a) == self.normalize_url(b)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py b/contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py
deleted file mode 100644
index e4dc55d302..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: March 1, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Support for spinners that represent progress on interactive terminals.
-
-The :class:`Spinner` class shows a "spinner" on the terminal to let the user
-know that something is happening during long running operations that would
-otherwise be silent (leaving the user to wonder what they're waiting for).
-Below are some visual examples that should illustrate the point.
-
-**Simple spinners:**
-
- Here's a screen capture that shows the simplest form of spinner:
-
- .. image:: images/spinner-basic.gif
- :alt: Animated screen capture of a simple spinner.
-
- The following code was used to create the spinner above:
-
- .. code-block:: python
-
- import itertools
- import time
- from humanfriendly import Spinner
-
- with Spinner(label="Downloading") as spinner:
- for i in itertools.count():
- # Do something useful here.
- time.sleep(0.1)
- # Advance the spinner.
- spinner.step()
-
-**Spinners that show elapsed time:**
-
- Here's a spinner that shows the elapsed time since it started:
-
- .. image:: images/spinner-with-timer.gif
- :alt: Animated screen capture of a spinner showing elapsed time.
-
- The following code was used to create the spinner above:
-
- .. code-block:: python
-
- import itertools
- import time
- from humanfriendly import Spinner, Timer
-
- with Spinner(label="Downloading", timer=Timer()) as spinner:
- for i in itertools.count():
- # Do something useful here.
- time.sleep(0.1)
- # Advance the spinner.
- spinner.step()
-
-**Spinners that show progress:**
-
- Here's a spinner that shows a progress percentage:
-
- .. image:: images/spinner-with-progress.gif
- :alt: Animated screen capture of spinner showing progress.
-
- The following code was used to create the spinner above:
-
- .. code-block:: python
-
- import itertools
- import random
- import time
- from humanfriendly import Spinner, Timer
-
- with Spinner(label="Downloading", total=100) as spinner:
- progress = 0
- while progress < 100:
- # Do something useful here.
- time.sleep(0.1)
- # Advance the spinner.
- spinner.step(progress)
- # Determine the new progress value.
- progress += random.random() * 5
-
-If you want to provide user feedback during a long running operation but it's
-not practical to periodically call the :func:`~Spinner.step()` method consider
-using :class:`AutomaticSpinner` instead.
-
-As you may already have noticed in the examples above, :class:`Spinner` objects
-can be used as context managers to automatically call :func:`Spinner.clear()`
-when the spinner ends.
-"""
-
-# Standard library modules.
-import multiprocessing
-import sys
-import time
-
-# Modules included in our package.
-from humanfriendly import Timer
-from humanfriendly.deprecation import deprecated_args
-from humanfriendly.terminal import ANSI_ERASE_LINE
-
-# Public identifiers that require documentation.
-__all__ = ("AutomaticSpinner", "GLYPHS", "MINIMUM_INTERVAL", "Spinner")
-
-GLYPHS = ["-", "\\", "|", "/"]
-"""A list of strings with characters that together form a crude animation :-)."""
-
-MINIMUM_INTERVAL = 0.2
-"""Spinners are redrawn with a frequency no higher than this number (a floating point number of seconds)."""
-
-
-class Spinner(object):
-
- """Show a spinner on the terminal as a simple means of feedback to the user."""
-
- @deprecated_args('label', 'total', 'stream', 'interactive', 'timer')
- def __init__(self, **options):
- """
- Initialize a :class:`Spinner` object.
-
- :param label:
-
- The label for the spinner (a string or :data:`None`, defaults to
- :data:`None`).
-
- :param total:
-
- The expected number of steps (an integer or :data:`None`). If this is
- provided the spinner will show a progress percentage.
-
- :param stream:
-
- The output stream to show the spinner on (a file-like object,
- defaults to :data:`sys.stderr`).
-
- :param interactive:
-
- :data:`True` to enable rendering of the spinner, :data:`False` to
- disable (defaults to the result of ``stream.isatty()``).
-
- :param timer:
-
- A :class:`.Timer` object (optional). If this is given the spinner
- will show the elapsed time according to the timer.
-
- :param interval:
-
- The spinner will be updated at most once every this many seconds
- (a floating point number, defaults to :data:`MINIMUM_INTERVAL`).
-
- :param glyphs:
-
- A list of strings with single characters that are drawn in the same
- place in succession to implement a simple animated effect (defaults
- to :data:`GLYPHS`).
- """
- # Store initializer arguments.
- self.interactive = options.get('interactive')
- self.interval = options.get('interval', MINIMUM_INTERVAL)
- self.label = options.get('label')
- self.states = options.get('glyphs', GLYPHS)
- self.stream = options.get('stream', sys.stderr)
- self.timer = options.get('timer')
- self.total = options.get('total')
- # Define instance variables.
- self.counter = 0
- self.last_update = 0
- # Try to automatically discover whether the stream is connected to
- # a terminal, but don't fail if no isatty() method is available.
- if self.interactive is None:
- try:
- self.interactive = self.stream.isatty()
- except Exception:
- self.interactive = False
-
- def step(self, progress=0, label=None):
- """
- Advance the spinner by one step and redraw it.
-
- :param progress: The number of the current step, relative to the total
- given to the :class:`Spinner` constructor (an integer,
- optional). If not provided the spinner will not show
- progress.
- :param label: The label to use while redrawing (a string, optional). If
- not provided the label given to the :class:`Spinner`
- constructor is used instead.
-
- This method advances the spinner by one step without starting a new
- line, causing an animated effect which is very simple but much nicer
- than waiting for a prompt which is completely silent for a long time.
-
- .. note:: This method uses time based rate limiting to avoid redrawing
- the spinner too frequently. If you know you're dealing with
- code that will call :func:`step()` at a high frequency,
- consider using :func:`sleep()` to avoid creating the
- equivalent of a busy loop that's rate limiting the spinner
- 99% of the time.
- """
- if self.interactive:
- time_now = time.time()
- if time_now - self.last_update >= self.interval:
- self.last_update = time_now
- state = self.states[self.counter % len(self.states)]
- label = label or self.label
- if not label:
- raise Exception("No label set for spinner!")
- elif self.total and progress:
- label = "%s: %.2f%%" % (label, progress / (self.total / 100.0))
- elif self.timer and self.timer.elapsed_time > 2:
- label = "%s (%s)" % (label, self.timer.rounded)
- self.stream.write("%s %s %s ..\r" % (ANSI_ERASE_LINE, state, label))
- self.counter += 1
-
- def sleep(self):
- """
- Sleep for a short period before redrawing the spinner.
-
- This method is useful when you know you're dealing with code that will
- call :func:`step()` at a high frequency. It will sleep for the interval
- with which the spinner is redrawn (less than a second). This avoids
- creating the equivalent of a busy loop that's rate limiting the
- spinner 99% of the time.
-
- This method doesn't redraw the spinner, you still have to call
- :func:`step()` in order to do that.
- """
- time.sleep(MINIMUM_INTERVAL)
-
- def clear(self):
- """
- Clear the spinner.
-
- The next line which is shown on the standard output or error stream
- after calling this method will overwrite the line that used to show the
- spinner.
- """
- if self.interactive:
- self.stream.write(ANSI_ERASE_LINE)
-
- def __enter__(self):
- """
- Enable the use of spinners as context managers.
-
- :returns: The :class:`Spinner` object.
- """
- return self
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Clear the spinner when leaving the context."""
- self.clear()
-
-
-class AutomaticSpinner(object):
-
- """
- Show a spinner on the terminal that automatically starts animating.
-
- This class shows a spinner on the terminal (just like :class:`Spinner`
- does) that automatically starts animating. This class should be used as a
- context manager using the :keyword:`with` statement. The animation
- continues for as long as the context is active.
-
- :class:`AutomaticSpinner` provides an alternative to :class:`Spinner`
- for situations where it is not practical for the caller to periodically
- call :func:`~Spinner.step()` to advance the animation, e.g. because
- you're performing a blocking call and don't fancy implementing threading or
- subprocess handling just to provide some user feedback.
-
- This works using the :mod:`multiprocessing` module by spawning a
- subprocess to render the spinner while the main process is busy doing
- something more useful. By using the :keyword:`with` statement you're
- guaranteed that the subprocess is properly terminated at the appropriate
- time.
- """
-
- def __init__(self, label, show_time=True):
- """
- Initialize an automatic spinner.
-
- :param label: The label for the spinner (a string).
- :param show_time: If this is :data:`True` (the default) then the spinner
- shows elapsed time.
- """
- self.label = label
- self.show_time = show_time
- self.shutdown_event = multiprocessing.Event()
- self.subprocess = multiprocessing.Process(target=self._target)
-
- def __enter__(self):
- """Enable the use of automatic spinners as context managers."""
- self.subprocess.start()
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Enable the use of automatic spinners as context managers."""
- self.shutdown_event.set()
- self.subprocess.join()
-
- def _target(self):
- try:
- timer = Timer() if self.show_time else None
- with Spinner(label=self.label, timer=timer) as spinner:
- while not self.shutdown_event.is_set():
- spinner.step()
- spinner.sleep()
- except KeyboardInterrupt:
- # Swallow Control-C signals without producing a nasty traceback that
- # won't make any sense to the average user.
- pass
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/testing.py b/contrib/python/humanfriendly/py3/humanfriendly/testing.py
deleted file mode 100644
index f6abddf074..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/testing.py
+++ /dev/null
@@ -1,669 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: March 6, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Utility classes and functions that make it easy to write :mod:`unittest` compatible test suites.
-
-Over the years I've developed the habit of writing test suites for Python
-projects using the :mod:`unittest` module. During those years I've come to know
-:pypi:`pytest` and in fact I use :pypi:`pytest` to run my test suites (due to
-its much better error reporting) but I've yet to publish a test suite that
-*requires* :pypi:`pytest`. I have several reasons for doing so:
-
-- It's nice to keep my test suites as simple and accessible as possible and
- not requiring a specific test runner is part of that attitude.
-
-- Whereas :mod:`unittest` is quite explicit, :pypi:`pytest` contains a lot of
- magic, which kind of contradicts the Python mantra "explicit is better than
- implicit" (IMHO).
-"""
-
-# Standard library module
-import functools
-import logging
-import os
-import pipes
-import shutil
-import sys
-import tempfile
-import time
-import unittest
-
-# Modules included in our package.
-from humanfriendly.compat import StringIO
-from humanfriendly.text import random_string
-
-# Initialize a logger for this module.
-logger = logging.getLogger(__name__)
-
-# A unique object reference used to detect missing attributes.
-NOTHING = object()
-
-# Public identifiers that require documentation.
-__all__ = (
- 'CallableTimedOut',
- 'CaptureBuffer',
- 'CaptureOutput',
- 'ContextManager',
- 'CustomSearchPath',
- 'MockedProgram',
- 'PatchedAttribute',
- 'PatchedItem',
- 'TemporaryDirectory',
- 'TestCase',
- 'configure_logging',
- 'make_dirs',
- 'retry',
- 'run_cli',
- 'skip_on_raise',
- 'touch',
-)
-
-
-def configure_logging(log_level=logging.DEBUG):
- """configure_logging(log_level=logging.DEBUG)
- Automatically configure logging to the terminal.
-
- :param log_level: The log verbosity (a number, defaults
- to :mod:`logging.DEBUG <logging>`).
-
- When :mod:`coloredlogs` is installed :func:`coloredlogs.install()` will be
- used to configure logging to the terminal. When this fails with an
- :exc:`~exceptions.ImportError` then :func:`logging.basicConfig()` is used
- as a fall back.
- """
- try:
- import coloredlogs
- coloredlogs.install(level=log_level)
- except ImportError:
- logging.basicConfig(
- level=log_level,
- format='%(asctime)s %(name)s[%(process)d] %(levelname)s %(message)s',
- datefmt='%Y-%m-%d %H:%M:%S')
-
-
-def make_dirs(pathname):
- """
- Create missing directories.
-
- :param pathname: The pathname of a directory (a string).
- """
- if not os.path.isdir(pathname):
- os.makedirs(pathname)
-
-
-def retry(func, timeout=60, exc_type=AssertionError):
- """retry(func, timeout=60, exc_type=AssertionError)
- Retry a function until assertions no longer fail.
-
- :param func: A callable. When the callable returns
- :data:`False` it will also be retried.
- :param timeout: The number of seconds after which to abort (a number,
- defaults to 60).
- :param exc_type: The type of exceptions to retry (defaults
- to :exc:`~exceptions.AssertionError`).
- :returns: The value returned by `func`.
- :raises: Once the timeout has expired :func:`retry()` will raise the
- previously retried assertion error. When `func` keeps returning
- :data:`False` until `timeout` expires :exc:`CallableTimedOut`
- will be raised.
-
- This function sleeps between retries to avoid claiming CPU cycles we don't
- need. It starts by sleeping for 0.1 second but adjusts this to one second
- as the number of retries grows.
- """
- pause = 0.1
- timeout += time.time()
- while True:
- try:
- result = func()
- if result is not False:
- return result
- except exc_type:
- if time.time() > timeout:
- raise
- else:
- if time.time() > timeout:
- raise CallableTimedOut()
- time.sleep(pause)
- if pause < 1:
- pause *= 2
-
-
-def run_cli(entry_point, *arguments, **options):
- """
- Test a command line entry point.
-
- :param entry_point: The function that implements the command line interface
- (a callable).
- :param arguments: Any positional arguments (strings) become the command
- line arguments (:data:`sys.argv` items 1-N).
- :param options: The following keyword arguments are supported:
-
- **capture**
- Whether to use :class:`CaptureOutput`. Defaults
- to :data:`True` but can be disabled by passing
- :data:`False` instead.
- **input**
- Refer to :class:`CaptureOutput`.
- **merged**
- Refer to :class:`CaptureOutput`.
- **program_name**
- Used to set :data:`sys.argv` item 0.
- :returns: A tuple with two values:
-
- 1. The return code (an integer).
- 2. The captured output (a string).
- """
- # Add the `program_name' option to the arguments.
- arguments = list(arguments)
- arguments.insert(0, options.pop('program_name', sys.executable))
- # Log the command line arguments (and the fact that we're about to call the
- # command line entry point function).
- logger.debug("Calling command line entry point with arguments: %s", arguments)
- # Prepare to capture the return code and output even if the command line
- # interface raises an exception (whether the exception type is SystemExit
- # or something else).
- returncode = 0
- stdout = None
- stderr = None
- try:
- # Temporarily override sys.argv.
- with PatchedAttribute(sys, 'argv', arguments):
- # Manipulate the standard input/output/error streams?
- options['enabled'] = options.pop('capture', True)
- with CaptureOutput(**options) as capturer:
- try:
- # Call the command line interface.
- entry_point()
- finally:
- # Get the output even if an exception is raised.
- stdout = capturer.stdout.getvalue()
- stderr = capturer.stderr.getvalue()
- # Reconfigure logging to the terminal because it is very
- # likely that the entry point function has changed the
- # configured log level.
- configure_logging()
- except BaseException as e:
- if isinstance(e, SystemExit):
- logger.debug("Intercepting return code %s from SystemExit exception.", e.code)
- returncode = e.code
- else:
- logger.warning("Defaulting return code to 1 due to raised exception.", exc_info=True)
- returncode = 1
- else:
- logger.debug("Command line entry point returned successfully!")
- # Always log the output captured on stdout/stderr, to make it easier to
- # diagnose test failures (but avoid duplicate logging when merged=True).
- is_merged = options.get('merged', False)
- merged_streams = [('merged streams', stdout)]
- separate_streams = [('stdout', stdout), ('stderr', stderr)]
- streams = merged_streams if is_merged else separate_streams
- for name, value in streams:
- if value:
- logger.debug("Output on %s:\n%s", name, value)
- else:
- logger.debug("No output on %s.", name)
- return returncode, stdout
-
-
-def skip_on_raise(*exc_types):
- """
- Decorate a test function to translation specific exception types to :exc:`unittest.SkipTest`.
-
- :param exc_types: One or more positional arguments give the exception
- types to be translated to :exc:`unittest.SkipTest`.
- :returns: A decorator function specialized to `exc_types`.
- """
- def decorator(function):
- @functools.wraps(function)
- def wrapper(*args, **kw):
- try:
- return function(*args, **kw)
- except exc_types as e:
- logger.debug("Translating exception to unittest.SkipTest ..", exc_info=True)
- raise unittest.SkipTest("skipping test because %s was raised" % type(e))
- return wrapper
- return decorator
-
-
-def touch(filename):
- """
- The equivalent of the UNIX :man:`touch` program in Python.
-
- :param filename: The pathname of the file to touch (a string).
-
- Note that missing directories are automatically created using
- :func:`make_dirs()`.
- """
- make_dirs(os.path.dirname(filename))
- with open(filename, 'a'):
- os.utime(filename, None)
-
-
-class CallableTimedOut(Exception):
-
- """Raised by :func:`retry()` when the timeout expires."""
-
-
-class ContextManager(object):
-
- """Base class to enable composition of context managers."""
-
- def __enter__(self):
- """Enable use as context managers."""
- return self
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Enable use as context managers."""
-
-
-class PatchedAttribute(ContextManager):
-
- """Context manager that temporary replaces an object attribute using :func:`setattr()`."""
-
- def __init__(self, obj, name, value):
- """
- Initialize a :class:`PatchedAttribute` object.
-
- :param obj: The object to patch.
- :param name: An attribute name.
- :param value: The value to set.
- """
- self.object_to_patch = obj
- self.attribute_to_patch = name
- self.patched_value = value
- self.original_value = NOTHING
-
- def __enter__(self):
- """
- Replace (patch) the attribute.
-
- :returns: The object whose attribute was patched.
- """
- # Enable composition of context managers.
- super(PatchedAttribute, self).__enter__()
- # Patch the object's attribute.
- self.original_value = getattr(self.object_to_patch, self.attribute_to_patch, NOTHING)
- setattr(self.object_to_patch, self.attribute_to_patch, self.patched_value)
- return self.object_to_patch
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Restore the attribute to its original value."""
- # Enable composition of context managers.
- super(PatchedAttribute, self).__exit__(exc_type, exc_value, traceback)
- # Restore the object's attribute.
- if self.original_value is NOTHING:
- delattr(self.object_to_patch, self.attribute_to_patch)
- else:
- setattr(self.object_to_patch, self.attribute_to_patch, self.original_value)
-
-
-class PatchedItem(ContextManager):
-
- """Context manager that temporary replaces an object item using :meth:`~object.__setitem__()`."""
-
- def __init__(self, obj, item, value):
- """
- Initialize a :class:`PatchedItem` object.
-
- :param obj: The object to patch.
- :param item: The item to patch.
- :param value: The value to set.
- """
- self.object_to_patch = obj
- self.item_to_patch = item
- self.patched_value = value
- self.original_value = NOTHING
-
- def __enter__(self):
- """
- Replace (patch) the item.
-
- :returns: The object whose item was patched.
- """
- # Enable composition of context managers.
- super(PatchedItem, self).__enter__()
- # Patch the object's item.
- try:
- self.original_value = self.object_to_patch[self.item_to_patch]
- except KeyError:
- self.original_value = NOTHING
- self.object_to_patch[self.item_to_patch] = self.patched_value
- return self.object_to_patch
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Restore the item to its original value."""
- # Enable composition of context managers.
- super(PatchedItem, self).__exit__(exc_type, exc_value, traceback)
- # Restore the object's item.
- if self.original_value is NOTHING:
- del self.object_to_patch[self.item_to_patch]
- else:
- self.object_to_patch[self.item_to_patch] = self.original_value
-
-
-class TemporaryDirectory(ContextManager):
-
- """
- Easy temporary directory creation & cleanup using the :keyword:`with` statement.
-
- Here's an example of how to use this:
-
- .. code-block:: python
-
- with TemporaryDirectory() as directory:
- # Do something useful here.
- assert os.path.isdir(directory)
- """
-
- def __init__(self, **options):
- """
- Initialize a :class:`TemporaryDirectory` object.
-
- :param options: Any keyword arguments are passed on to
- :func:`tempfile.mkdtemp()`.
- """
- self.mkdtemp_options = options
- self.temporary_directory = None
-
- def __enter__(self):
- """
- Create the temporary directory using :func:`tempfile.mkdtemp()`.
-
- :returns: The pathname of the directory (a string).
- """
- # Enable composition of context managers.
- super(TemporaryDirectory, self).__enter__()
- # Create the temporary directory.
- self.temporary_directory = tempfile.mkdtemp(**self.mkdtemp_options)
- return self.temporary_directory
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Cleanup the temporary directory using :func:`shutil.rmtree()`."""
- # Enable composition of context managers.
- super(TemporaryDirectory, self).__exit__(exc_type, exc_value, traceback)
- # Cleanup the temporary directory.
- if self.temporary_directory is not None:
- shutil.rmtree(self.temporary_directory)
- self.temporary_directory = None
-
-
-class MockedHomeDirectory(PatchedItem, TemporaryDirectory):
-
- """
- Context manager to temporarily change ``$HOME`` (the current user's profile directory).
-
- This class is a composition of the :class:`PatchedItem` and
- :class:`TemporaryDirectory` context managers.
- """
-
- def __init__(self):
- """Initialize a :class:`MockedHomeDirectory` object."""
- PatchedItem.__init__(self, os.environ, 'HOME', os.environ.get('HOME'))
- TemporaryDirectory.__init__(self)
-
- def __enter__(self):
- """
- Activate the custom ``$PATH``.
-
- :returns: The pathname of the directory that has
- been added to ``$PATH`` (a string).
- """
- # Get the temporary directory.
- directory = TemporaryDirectory.__enter__(self)
- # Override the value to patch now that we have
- # the pathname of the temporary directory.
- self.patched_value = directory
- # Temporary patch $HOME.
- PatchedItem.__enter__(self)
- # Pass the pathname of the temporary directory to the caller.
- return directory
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Deactivate the custom ``$HOME``."""
- super(MockedHomeDirectory, self).__exit__(exc_type, exc_value, traceback)
-
-
-class CustomSearchPath(PatchedItem, TemporaryDirectory):
-
- """
- Context manager to temporarily customize ``$PATH`` (the executable search path).
-
- This class is a composition of the :class:`PatchedItem` and
- :class:`TemporaryDirectory` context managers.
- """
-
- def __init__(self, isolated=False):
- """
- Initialize a :class:`CustomSearchPath` object.
-
- :param isolated: :data:`True` to clear the original search path,
- :data:`False` to add the temporary directory to the
- start of the search path.
- """
- # Initialize our own instance variables.
- self.isolated_search_path = isolated
- # Selectively initialize our superclasses.
- PatchedItem.__init__(self, os.environ, 'PATH', self.current_search_path)
- TemporaryDirectory.__init__(self)
-
- def __enter__(self):
- """
- Activate the custom ``$PATH``.
-
- :returns: The pathname of the directory that has
- been added to ``$PATH`` (a string).
- """
- # Get the temporary directory.
- directory = TemporaryDirectory.__enter__(self)
- # Override the value to patch now that we have
- # the pathname of the temporary directory.
- self.patched_value = (
- directory if self.isolated_search_path
- else os.pathsep.join([directory] + self.current_search_path.split(os.pathsep))
- )
- # Temporary patch the $PATH.
- PatchedItem.__enter__(self)
- # Pass the pathname of the temporary directory to the caller
- # because they may want to `install' custom executables.
- return directory
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Deactivate the custom ``$PATH``."""
- super(CustomSearchPath, self).__exit__(exc_type, exc_value, traceback)
-
- @property
- def current_search_path(self):
- """The value of ``$PATH`` or :data:`os.defpath` (a string)."""
- return os.environ.get('PATH', os.defpath)
-
-
-class MockedProgram(CustomSearchPath):
-
- """
- Context manager to mock the existence of a program (executable).
-
- This class extends the functionality of :class:`CustomSearchPath`.
- """
-
- def __init__(self, name, returncode=0, script=None):
- """
- Initialize a :class:`MockedProgram` object.
-
- :param name: The name of the program (a string).
- :param returncode: The return code that the program should emit (a
- number, defaults to zero).
- :param script: Shell script code to include in the mocked program (a
- string or :data:`None`). This can be used to mock a
- program that is expected to generate specific output.
- """
- # Initialize our own instance variables.
- self.program_name = name
- self.program_returncode = returncode
- self.program_script = script
- self.program_signal_file = None
- # Initialize our superclasses.
- super(MockedProgram, self).__init__()
-
- def __enter__(self):
- """
- Create the mock program.
-
- :returns: The pathname of the directory that has
- been added to ``$PATH`` (a string).
- """
- directory = super(MockedProgram, self).__enter__()
- self.program_signal_file = os.path.join(directory, 'program-was-run-%s' % random_string(10))
- pathname = os.path.join(directory, self.program_name)
- with open(pathname, 'w') as handle:
- handle.write('#!/bin/sh\n')
- handle.write('echo > %s\n' % pipes.quote(self.program_signal_file))
- if self.program_script:
- handle.write('%s\n' % self.program_script.strip())
- handle.write('exit %i\n' % self.program_returncode)
- os.chmod(pathname, 0o755)
- return directory
-
- def __exit__(self, *args, **kw):
- """
- Ensure that the mock program was run.
-
- :raises: :exc:`~exceptions.AssertionError` when
- the mock program hasn't been run.
- """
- try:
- assert self.program_signal_file and os.path.isfile(self.program_signal_file), \
- ("It looks like %r was never run!" % self.program_name)
- finally:
- return super(MockedProgram, self).__exit__(*args, **kw)
-
-
-class CaptureOutput(ContextManager):
-
- """
- Context manager that captures what's written to :data:`sys.stdout` and :data:`sys.stderr`.
-
- .. attribute:: stdin
-
- The :class:`~humanfriendly.compat.StringIO` object used to feed the standard input stream.
-
- .. attribute:: stdout
-
- The :class:`CaptureBuffer` object used to capture the standard output stream.
-
- .. attribute:: stderr
-
- The :class:`CaptureBuffer` object used to capture the standard error stream.
- """
-
- def __init__(self, merged=False, input='', enabled=True):
- """
- Initialize a :class:`CaptureOutput` object.
-
- :param merged: :data:`True` to merge the streams,
- :data:`False` to capture them separately.
- :param input: The data that reads from :data:`sys.stdin`
- should return (a string).
- :param enabled: :data:`True` to enable capturing (the default),
- :data:`False` otherwise. This makes it easy to
- unconditionally use :class:`CaptureOutput` in
- a :keyword:`with` block while preserving the
- choice to opt out of capturing output.
- """
- self.stdin = StringIO(input)
- self.stdout = CaptureBuffer()
- self.stderr = self.stdout if merged else CaptureBuffer()
- self.patched_attributes = []
- if enabled:
- self.patched_attributes.extend(
- PatchedAttribute(sys, name, getattr(self, name))
- for name in ('stdin', 'stdout', 'stderr')
- )
-
- def __enter__(self):
- """Start capturing what's written to :data:`sys.stdout` and :data:`sys.stderr`."""
- super(CaptureOutput, self).__enter__()
- for context in self.patched_attributes:
- context.__enter__()
- return self
-
- def __exit__(self, exc_type=None, exc_value=None, traceback=None):
- """Stop capturing what's written to :data:`sys.stdout` and :data:`sys.stderr`."""
- super(CaptureOutput, self).__exit__(exc_type, exc_value, traceback)
- for context in self.patched_attributes:
- context.__exit__(exc_type, exc_value, traceback)
-
- def get_lines(self):
- """Get the contents of :attr:`stdout` split into separate lines."""
- return self.get_text().splitlines()
-
- def get_text(self):
- """Get the contents of :attr:`stdout` as a Unicode string."""
- return self.stdout.get_text()
-
- def getvalue(self):
- """Get the text written to :data:`sys.stdout`."""
- return self.stdout.getvalue()
-
-
-class CaptureBuffer(StringIO):
-
- """
- Helper for :class:`CaptureOutput` to provide an easy to use API.
-
- The two methods defined by this subclass were specifically chosen to match
- the names of the methods provided by my :pypi:`capturer` package which
- serves a similar role as :class:`CaptureOutput` but knows how to simulate
- an interactive terminal (tty).
- """
-
- def get_lines(self):
- """Get the contents of the buffer split into separate lines."""
- return self.get_text().splitlines()
-
- def get_text(self):
- """Get the contents of the buffer as a Unicode string."""
- return self.getvalue()
-
-
-class TestCase(unittest.TestCase):
-
- """Subclass of :class:`unittest.TestCase` with automatic logging and other miscellaneous features."""
-
- def __init__(self, *args, **kw):
- """
- Initialize a :class:`TestCase` object.
-
- Any positional and/or keyword arguments are passed on to the
- initializer of the superclass.
- """
- super(TestCase, self).__init__(*args, **kw)
-
- def setUp(self, log_level=logging.DEBUG):
- """setUp(log_level=logging.DEBUG)
- Automatically configure logging to the terminal.
-
- :param log_level: Refer to :func:`configure_logging()`.
-
- The :func:`setUp()` method is automatically called by
- :class:`unittest.TestCase` before each test method starts.
- It does two things:
-
- - Logging to the terminal is configured using
- :func:`configure_logging()`.
-
- - Before the test method starts a newline is emitted, to separate the
- name of the test method (which will be printed to the terminal by
- :mod:`unittest` or :pypi:`pytest`) from the first line of logging
- output that the test method is likely going to generate.
- """
- # Configure logging to the terminal.
- configure_logging(log_level)
- # Separate the name of the test method (printed by the superclass
- # and/or py.test without a newline at the end) from the first line of
- # logging output that the test method is likely going to generate.
- sys.stderr.write("\n")
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/text.py b/contrib/python/humanfriendly/py3/humanfriendly/text.py
deleted file mode 100644
index a257a6a189..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/text.py
+++ /dev/null
@@ -1,449 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: December 1, 2020
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Simple text manipulation functions.
-
-The :mod:`~humanfriendly.text` module contains simple functions to manipulate text:
-
-- The :func:`concatenate()` and :func:`pluralize()` functions make it easy to
- generate human friendly output.
-
-- The :func:`format()`, :func:`compact()` and :func:`dedent()` functions
- provide a clean and simple to use syntax for composing large text fragments
- with interpolated variables.
-
-- The :func:`tokenize()` function parses simple user input.
-"""
-
-# Standard library modules.
-import numbers
-import random
-import re
-import string
-import textwrap
-
-# Public identifiers that require documentation.
-__all__ = (
- 'compact',
- 'compact_empty_lines',
- 'concatenate',
- 'dedent',
- 'format',
- 'generate_slug',
- 'is_empty_line',
- 'join_lines',
- 'pluralize',
- 'pluralize_raw',
- 'random_string',
- 'split',
- 'split_paragraphs',
- 'tokenize',
- 'trim_empty_lines',
-)
-
-
-def compact(text, *args, **kw):
- '''
- Compact whitespace in a string.
-
- Trims leading and trailing whitespace, replaces runs of whitespace
- characters with a single space and interpolates any arguments using
- :func:`format()`.
-
- :param text: The text to compact (a string).
- :param args: Any positional arguments are interpolated using :func:`format()`.
- :param kw: Any keyword arguments are interpolated using :func:`format()`.
- :returns: The compacted text (a string).
-
- Here's an example of how I like to use the :func:`compact()` function, this
- is an example from a random unrelated project I'm working on at the moment::
-
- raise PortDiscoveryError(compact("""
- Failed to discover port(s) that Apache is listening on!
- Maybe I'm parsing the wrong configuration file? ({filename})
- """, filename=self.ports_config))
-
- The combination of :func:`compact()` and Python's multi line strings allows
- me to write long text fragments with interpolated variables that are easy
- to write, easy to read and work well with Python's whitespace
- sensitivity.
- '''
- non_whitespace_tokens = text.split()
- compacted_text = ' '.join(non_whitespace_tokens)
- return format(compacted_text, *args, **kw)
-
-
-def compact_empty_lines(text):
- """
- Replace repeating empty lines with a single empty line (similar to ``cat -s``).
-
- :param text: The text in which to compact empty lines (a string).
- :returns: The text with empty lines compacted (a string).
- """
- i = 0
- lines = text.splitlines(True)
- while i < len(lines):
- if i > 0 and is_empty_line(lines[i - 1]) and is_empty_line(lines[i]):
- lines.pop(i)
- else:
- i += 1
- return ''.join(lines)
-
-
-def concatenate(items, conjunction='and', serial_comma=False):
- """
- Concatenate a list of items in a human friendly way.
-
- :param items:
-
- A sequence of strings.
-
- :param conjunction:
-
- The word to use before the last item (a string, defaults to "and").
-
- :param serial_comma:
-
- :data:`True` to use a `serial comma`_, :data:`False` otherwise
- (defaults to :data:`False`).
-
- :returns:
-
- A single string.
-
- >>> from humanfriendly.text import concatenate
- >>> concatenate(["eggs", "milk", "bread"])
- 'eggs, milk and bread'
-
- .. _serial comma: https://en.wikipedia.org/wiki/Serial_comma
- """
- items = list(items)
- if len(items) > 1:
- final_item = items.pop()
- formatted = ', '.join(items)
- if serial_comma:
- formatted += ','
- return ' '.join([formatted, conjunction, final_item])
- elif items:
- return items[0]
- else:
- return ''
-
-
-def dedent(text, *args, **kw):
- """
- Dedent a string (remove common leading whitespace from all lines).
-
- Removes common leading whitespace from all lines in the string using
- :func:`textwrap.dedent()`, removes leading and trailing empty lines using
- :func:`trim_empty_lines()` and interpolates any arguments using
- :func:`format()`.
-
- :param text: The text to dedent (a string).
- :param args: Any positional arguments are interpolated using :func:`format()`.
- :param kw: Any keyword arguments are interpolated using :func:`format()`.
- :returns: The dedented text (a string).
-
- The :func:`compact()` function's documentation contains an example of how I
- like to use the :func:`compact()` and :func:`dedent()` functions. The main
- difference is that I use :func:`compact()` for text that will be presented
- to the user (where whitespace is not so significant) and :func:`dedent()`
- for data file and code generation tasks (where newlines and indentation are
- very significant).
- """
- dedented_text = textwrap.dedent(text)
- trimmed_text = trim_empty_lines(dedented_text)
- return format(trimmed_text, *args, **kw)
-
-
-def format(text, *args, **kw):
- """
- Format a string using the string formatting operator and/or :meth:`str.format()`.
-
- :param text: The text to format (a string).
- :param args: Any positional arguments are interpolated into the text using
- the string formatting operator (``%``). If no positional
- arguments are given no interpolation is done.
- :param kw: Any keyword arguments are interpolated into the text using the
- :meth:`str.format()` function. If no keyword arguments are given
- no interpolation is done.
- :returns: The text with any positional and/or keyword arguments
- interpolated (a string).
-
- The implementation of this function is so trivial that it seems silly to
- even bother writing and documenting it. Justifying this requires some
- context :-).
-
- **Why format() instead of the string formatting operator?**
-
- For really simple string interpolation Python's string formatting operator
- is ideal, but it does have some strange quirks:
-
- - When you switch from interpolating a single value to interpolating
- multiple values you have to wrap them in tuple syntax. Because
- :func:`format()` takes a `variable number of arguments`_ it always
- receives a tuple (which saves me a context switch :-). Here's an
- example:
-
- >>> from humanfriendly.text import format
- >>> # The string formatting operator.
- >>> print('the magic number is %s' % 42)
- the magic number is 42
- >>> print('the magic numbers are %s and %s' % (12, 42))
- the magic numbers are 12 and 42
- >>> # The format() function.
- >>> print(format('the magic number is %s', 42))
- the magic number is 42
- >>> print(format('the magic numbers are %s and %s', 12, 42))
- the magic numbers are 12 and 42
-
- - When you interpolate a single value and someone accidentally passes in a
- tuple your code raises a :exc:`~exceptions.TypeError`. Because
- :func:`format()` takes a `variable number of arguments`_ it always
- receives a tuple so this can never happen. Here's an example:
-
- >>> # How expecting to interpolate a single value can fail.
- >>> value = (12, 42)
- >>> print('the magic value is %s' % value)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: not all arguments converted during string formatting
- >>> # The following line works as intended, no surprises here!
- >>> print(format('the magic value is %s', value))
- the magic value is (12, 42)
-
- **Why format() instead of the str.format() method?**
-
- When you're doing complex string interpolation the :meth:`str.format()`
- function results in more readable code, however I frequently find myself
- adding parentheses to force evaluation order. The :func:`format()` function
- avoids this because of the relative priority between the comma and dot
- operators. Here's an example:
-
- >>> "{adjective} example" + " " + "(can't think of anything less {adjective})".format(adjective='silly')
- "{adjective} example (can't think of anything less silly)"
- >>> ("{adjective} example" + " " + "(can't think of anything less {adjective})").format(adjective='silly')
- "silly example (can't think of anything less silly)"
- >>> format("{adjective} example" + " " + "(can't think of anything less {adjective})", adjective='silly')
- "silly example (can't think of anything less silly)"
-
- The :func:`compact()` and :func:`dedent()` functions are wrappers that
- combine :func:`format()` with whitespace manipulation to make it easy to
- write nice to read Python code.
-
- .. _variable number of arguments: https://docs.python.org/2/tutorial/controlflow.html#arbitrary-argument-lists
- """
- if args:
- text %= args
- if kw:
- text = text.format(**kw)
- return text
-
-
-def generate_slug(text, delimiter="-"):
- """
- Convert text to a normalized "slug" without whitespace.
-
- :param text: The original text, for example ``Some Random Text!``.
- :param delimiter: The delimiter used to separate words
- (defaults to the ``-`` character).
- :returns: The slug text, for example ``some-random-text``.
- :raises: :exc:`~exceptions.ValueError` when the provided
- text is nonempty but results in an empty slug.
- """
- slug = text.lower()
- escaped = delimiter.replace("\\", "\\\\")
- slug = re.sub("[^a-z0-9]+", escaped, slug)
- slug = slug.strip(delimiter)
- if text and not slug:
- msg = "The provided text %r results in an empty slug!"
- raise ValueError(format(msg, text))
- return slug
-
-
-def is_empty_line(text):
- """
- Check if a text is empty or contains only whitespace.
-
- :param text: The text to check for "emptiness" (a string).
- :returns: :data:`True` if the text is empty or contains only whitespace,
- :data:`False` otherwise.
- """
- return len(text) == 0 or text.isspace()
-
-
-def join_lines(text):
- """
- Remove "hard wrapping" from the paragraphs in a string.
-
- :param text: The text to reformat (a string).
- :returns: The text without hard wrapping (a string).
-
- This function works by removing line breaks when the last character before
- a line break and the first character after the line break are both
- non-whitespace characters. This means that common leading indentation will
- break :func:`join_lines()` (in that case you can use :func:`dedent()`
- before calling :func:`join_lines()`).
- """
- return re.sub(r'(\S)\n(\S)', r'\1 \2', text)
-
-
-def pluralize(count, singular, plural=None):
- """
- Combine a count with the singular or plural form of a word.
-
- :param count: The count (a number).
- :param singular: The singular form of the word (a string).
- :param plural: The plural form of the word (a string or :data:`None`).
- :returns: The count and singular or plural word concatenated (a string).
-
- See :func:`pluralize_raw()` for the logic underneath :func:`pluralize()`.
- """
- return '%s %s' % (count, pluralize_raw(count, singular, plural))
-
-
-def pluralize_raw(count, singular, plural=None):
- """
- Select the singular or plural form of a word based on a count.
-
- :param count: The count (a number).
- :param singular: The singular form of the word (a string).
- :param plural: The plural form of the word (a string or :data:`None`).
- :returns: The singular or plural form of the word (a string).
-
- When the given count is exactly 1.0 the singular form of the word is
- selected, in all other cases the plural form of the word is selected.
-
- If the plural form of the word is not provided it is obtained by
- concatenating the singular form of the word with the letter "s". Of course
- this will not always be correct, which is why you have the option to
- specify both forms.
- """
- if not plural:
- plural = singular + 's'
- return singular if float(count) == 1.0 else plural
-
-
-def random_string(length=(25, 100), characters=string.ascii_letters):
- """random_string(length=(25, 100), characters=string.ascii_letters)
- Generate a random string.
-
- :param length: The length of the string to be generated (a number or a
- tuple with two numbers). If this is a tuple then a random
- number between the two numbers given in the tuple is used.
- :param characters: The characters to be used (a string, defaults
- to :data:`string.ascii_letters`).
- :returns: A random string.
-
- The :func:`random_string()` function is very useful in test suites; by the
- time I included it in :mod:`humanfriendly.text` I had already included
- variants of this function in seven different test suites :-).
- """
- if not isinstance(length, numbers.Number):
- length = random.randint(length[0], length[1])
- return ''.join(random.choice(characters) for _ in range(length))
-
-
-def split(text, delimiter=','):
- """
- Split a comma-separated list of strings.
-
- :param text: The text to split (a string).
- :param delimiter: The delimiter to split on (a string).
- :returns: A list of zero or more nonempty strings.
-
- Here's the default behavior of Python's built in :meth:`str.split()`
- function:
-
- >>> 'foo,bar, baz,'.split(',')
- ['foo', 'bar', ' baz', '']
-
- In contrast here's the default behavior of the :func:`split()` function:
-
- >>> from humanfriendly.text import split
- >>> split('foo,bar, baz,')
- ['foo', 'bar', 'baz']
-
- Here is an example that parses a nested data structure (a mapping of
- logging level names to one or more styles per level) that's encoded in a
- string so it can be set as an environment variable:
-
- >>> from pprint import pprint
- >>> encoded_data = 'debug=green;warning=yellow;error=red;critical=red,bold'
- >>> parsed_data = dict((k, split(v, ',')) for k, v in (split(kv, '=') for kv in split(encoded_data, ';')))
- >>> pprint(parsed_data)
- {'debug': ['green'],
- 'warning': ['yellow'],
- 'error': ['red'],
- 'critical': ['red', 'bold']}
- """
- return [token.strip() for token in text.split(delimiter) if token and not token.isspace()]
-
-
-def split_paragraphs(text):
- """
- Split a string into paragraphs (one or more lines delimited by an empty line).
-
- :param text: The text to split into paragraphs (a string).
- :returns: A list of strings.
- """
- paragraphs = []
- for chunk in text.split('\n\n'):
- chunk = trim_empty_lines(chunk)
- if chunk and not chunk.isspace():
- paragraphs.append(chunk)
- return paragraphs
-
-
-def tokenize(text):
- """
- Tokenize a text into numbers and strings.
-
- :param text: The text to tokenize (a string).
- :returns: A list of strings and/or numbers.
-
- This function is used to implement robust tokenization of user input in
- functions like :func:`.parse_size()` and :func:`.parse_timespan()`. It
- automatically coerces integer and floating point numbers, ignores
- whitespace and knows how to separate numbers from strings even without
- whitespace. Some examples to make this more concrete:
-
- >>> from humanfriendly.text import tokenize
- >>> tokenize('42')
- [42]
- >>> tokenize('42MB')
- [42, 'MB']
- >>> tokenize('42.5MB')
- [42.5, 'MB']
- >>> tokenize('42.5 MB')
- [42.5, 'MB']
- """
- tokenized_input = []
- for token in re.split(r'(\d+(?:\.\d+)?)', text):
- token = token.strip()
- if re.match(r'\d+\.\d+', token):
- tokenized_input.append(float(token))
- elif token.isdigit():
- tokenized_input.append(int(token))
- elif token:
- tokenized_input.append(token)
- return tokenized_input
-
-
-def trim_empty_lines(text):
- """
- Trim leading and trailing empty lines from the given text.
-
- :param text: The text to trim (a string).
- :returns: The trimmed text (a string).
- """
- lines = text.splitlines(True)
- while lines and is_empty_line(lines[0]):
- lines.pop(0)
- while lines and is_empty_line(lines[-1]):
- lines.pop(-1)
- return ''.join(lines)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/usage.py b/contrib/python/humanfriendly/py3/humanfriendly/usage.py
deleted file mode 100644
index 81ba943ae0..0000000000
--- a/contrib/python/humanfriendly/py3/humanfriendly/usage.py
+++ /dev/null
@@ -1,351 +0,0 @@
-# Human friendly input/output in Python.
-#
-# Author: Peter Odding <peter@peterodding.com>
-# Last Change: June 11, 2021
-# URL: https://humanfriendly.readthedocs.io
-
-"""
-Parsing and reformatting of usage messages.
-
-The :mod:`~humanfriendly.usage` module parses and reformats usage messages:
-
-- The :func:`format_usage()` function takes a usage message and inserts ANSI
- escape sequences that highlight items of special significance like command
- line options, meta variables, etc. The resulting usage message is (intended
- to be) easier to read on a terminal.
-
-- The :func:`render_usage()` function takes a usage message and rewrites it to
- reStructuredText_ suitable for inclusion in the documentation of a Python
- package. This provides a DRY solution to keeping a single authoritative
- definition of the usage message while making it easily available in
- documentation. As a cherry on the cake it's not just a pre-formatted dump of
- the usage message but a nicely formatted reStructuredText_ fragment.
-
-- The remaining functions in this module support the two functions above.
-
-Usage messages in general are free format of course, however the functions in
-this module assume a certain structure from usage messages in order to
-successfully parse and reformat them, refer to :func:`parse_usage()` for
-details.
-
-.. _DRY: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
-.. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
-"""
-
-# Standard library modules.
-import csv
-import functools
-import logging
-import re
-
-# Standard library module or external dependency (see setup.py).
-from importlib import import_module
-
-# Modules included in our package.
-from humanfriendly.compat import StringIO
-from humanfriendly.text import dedent, split_paragraphs, trim_empty_lines
-
-# Public identifiers that require documentation.
-__all__ = (
- 'find_meta_variables',
- 'format_usage',
- 'import_module', # previously exported (backwards compatibility)
- 'inject_usage',
- 'parse_usage',
- 'render_usage',
- 'USAGE_MARKER',
-)
-
-USAGE_MARKER = "Usage:"
-"""The string that starts the first line of a usage message."""
-
-START_OF_OPTIONS_MARKER = "Supported options:"
-"""The string that marks the start of the documented command line options."""
-
-# Compiled regular expression used to tokenize usage messages.
-USAGE_PATTERN = re.compile(r'''
- # Make sure whatever we're matching isn't preceded by a non-whitespace
- # character.
- (?<!\S)
- (
- # A short command line option or a long command line option
- # (possibly including a meta variable for a value).
- (-\w|--\w+(-\w+)*(=\S+)?)
- # Or ...
- |
- # An environment variable.
- \$[A-Za-z_][A-Za-z0-9_]*
- # Or ...
- |
- # Might be a meta variable (usage() will figure it out).
- [A-Z][A-Z0-9_]+
- )
-''', re.VERBOSE)
-
-# Compiled regular expression used to recognize options.
-OPTION_PATTERN = re.compile(r'^(-\w|--\w+(-\w+)*(=\S+)?)$')
-
-# Initialize a logger for this module.
-logger = logging.getLogger(__name__)
-
-
-def format_usage(usage_text):
- """
- Highlight special items in a usage message.
-
- :param usage_text: The usage message to process (a string).
- :returns: The usage message with special items highlighted.
-
- This function highlights the following special items:
-
- - The initial line of the form "Usage: ..."
- - Short and long command line options
- - Environment variables
- - Meta variables (see :func:`find_meta_variables()`)
-
- All items are highlighted in the color defined by
- :data:`.HIGHLIGHT_COLOR`.
- """
- # Ugly workaround to avoid circular import errors due to interdependencies
- # between the humanfriendly.terminal and humanfriendly.usage modules.
- from humanfriendly.terminal import ansi_wrap, HIGHLIGHT_COLOR
- formatted_lines = []
- meta_variables = find_meta_variables(usage_text)
- for line in usage_text.strip().splitlines(True):
- if line.startswith(USAGE_MARKER):
- # Highlight the "Usage: ..." line in bold font and color.
- formatted_lines.append(ansi_wrap(line, color=HIGHLIGHT_COLOR))
- else:
- # Highlight options, meta variables and environment variables.
- formatted_lines.append(replace_special_tokens(
- line, meta_variables,
- lambda token: ansi_wrap(token, color=HIGHLIGHT_COLOR),
- ))
- return ''.join(formatted_lines)
-
-
-def find_meta_variables(usage_text):
- """
- Find the meta variables in the given usage message.
-
- :param usage_text: The usage message to parse (a string).
- :returns: A list of strings with any meta variables found in the usage
- message.
-
- When a command line option requires an argument, the convention is to
- format such options as ``--option=ARG``. The text ``ARG`` in this example
- is the meta variable.
- """
- meta_variables = set()
- for match in USAGE_PATTERN.finditer(usage_text):
- token = match.group(0)
- if token.startswith('-'):
- option, _, value = token.partition('=')
- if value:
- meta_variables.add(value)
- return list(meta_variables)
-
-
-def parse_usage(text):
- """
- Parse a usage message by inferring its structure (and making some assumptions :-).
-
- :param text: The usage message to parse (a string).
- :returns: A tuple of two lists:
-
- 1. A list of strings with the paragraphs of the usage message's
- "introduction" (the paragraphs before the documentation of the
- supported command line options).
-
- 2. A list of strings with pairs of command line options and their
- descriptions: Item zero is a line listing a supported command
- line option, item one is the description of that command line
- option, item two is a line listing another supported command
- line option, etc.
-
- Usage messages in general are free format of course, however
- :func:`parse_usage()` assume a certain structure from usage messages in
- order to successfully parse them:
-
- - The usage message starts with a line ``Usage: ...`` that shows a symbolic
- representation of the way the program is to be invoked.
-
- - After some free form text a line ``Supported options:`` (surrounded by
- empty lines) precedes the documentation of the supported command line
- options.
-
- - The command line options are documented as follows::
-
- -v, --verbose
-
- Make more noise.
-
- So all of the variants of the command line option are shown together on a
- separate line, followed by one or more paragraphs describing the option.
-
- - There are several other minor assumptions, but to be honest I'm not sure if
- anyone other than me is ever going to use this functionality, so for now I
- won't list every intricate detail :-).
-
- If you're curious anyway, refer to the usage message of the `humanfriendly`
- package (defined in the :mod:`humanfriendly.cli` module) and compare it with
- the usage message you see when you run ``humanfriendly --help`` and the
- generated usage message embedded in the readme.
-
- Feel free to request more detailed documentation if you're interested in
- using the :mod:`humanfriendly.usage` module outside of the little ecosystem
- of Python packages that I have been building over the past years.
- """
- introduction = []
- documented_options = []
- # Split the raw usage message into paragraphs.
- paragraphs = split_paragraphs(text)
- # Get the paragraphs that are part of the introduction.
- while paragraphs:
- # Check whether we've found the end of the introduction.
- end_of_intro = (paragraphs[0] == START_OF_OPTIONS_MARKER)
- # Append the current paragraph to the introduction.
- introduction.append(paragraphs.pop(0))
- # Stop after we've processed the complete introduction.
- if end_of_intro:
- break
- logger.debug("Parsed introduction: %s", introduction)
- # Parse the paragraphs that document command line options.
- while paragraphs:
- documented_options.append(dedent(paragraphs.pop(0)))
- description = []
- while paragraphs:
- # Check if the next paragraph starts the documentation of another
- # command line option. We split on a comma followed by a space so
- # that our parsing doesn't trip up when the label used for an
- # option's value contains commas.
- tokens = [t.strip() for t in re.split(r',\s', paragraphs[0]) if t and not t.isspace()]
- if all(OPTION_PATTERN.match(t) for t in tokens):
- break
- else:
- description.append(paragraphs.pop(0))
- # Join the description's paragraphs back together so we can remove
- # common leading indentation.
- documented_options.append(dedent('\n\n'.join(description)))
- logger.debug("Parsed options: %s", documented_options)
- return introduction, documented_options
-
-
-def render_usage(text):
- """
- Reformat a command line program's usage message to reStructuredText_.
-
- :param text: The plain text usage message (a string).
- :returns: The usage message rendered to reStructuredText_ (a string).
- """
- meta_variables = find_meta_variables(text)
- introduction, options = parse_usage(text)
- output = [render_paragraph(p, meta_variables) for p in introduction]
- if options:
- output.append('\n'.join([
- '.. csv-table::',
- ' :header: Option, Description',
- ' :widths: 30, 70',
- '',
- ]))
- csv_buffer = StringIO()
- csv_writer = csv.writer(csv_buffer)
- while options:
- variants = options.pop(0)
- description = options.pop(0)
- csv_writer.writerow([
- render_paragraph(variants, meta_variables),
- ('\n\n'.join(render_paragraph(p, meta_variables) for p in split_paragraphs(description))).rstrip(),
- ])
- csv_lines = csv_buffer.getvalue().splitlines()
- output.append('\n'.join(' %s' % line for line in csv_lines))
- logger.debug("Rendered output: %s", output)
- return '\n\n'.join(trim_empty_lines(o) for o in output)
-
-
-def inject_usage(module_name):
- """
- Use cog_ to inject a usage message into a reStructuredText_ file.
-
- :param module_name: The name of the module whose ``__doc__`` attribute is
- the source of the usage message (a string).
-
- This simple wrapper around :func:`render_usage()` makes it very easy to
- inject a reformatted usage message into your documentation using cog_. To
- use it you add a fragment like the following to your ``*.rst`` file::
-
- .. [[[cog
- .. from humanfriendly.usage import inject_usage
- .. inject_usage('humanfriendly.cli')
- .. ]]]
- .. [[[end]]]
-
- The lines in the fragment above are single line reStructuredText_ comments
- that are not copied to the output. Their purpose is to instruct cog_ where
- to inject the reformatted usage message. Once you've added these lines to
- your ``*.rst`` file, updating the rendered usage message becomes really
- simple thanks to cog_:
-
- .. code-block:: sh
-
- $ cog.py -r README.rst
-
- This will inject or replace the rendered usage message in your
- ``README.rst`` file with an up to date copy.
-
- .. _cog: http://nedbatchelder.com/code/cog/
- """
- import cog
- usage_text = import_module(module_name).__doc__
- cog.out("\n" + render_usage(usage_text) + "\n\n")
-
-
-def render_paragraph(paragraph, meta_variables):
- # Reformat the "Usage:" line to highlight "Usage:" in bold and show the
- # remainder of the line as pre-formatted text.
- if paragraph.startswith(USAGE_MARKER):
- tokens = paragraph.split()
- return "**%s** `%s`" % (tokens[0], ' '.join(tokens[1:]))
- # Reformat the "Supported options:" line to highlight it in bold.
- if paragraph == 'Supported options:':
- return "**%s**" % paragraph
- # Reformat shell transcripts into code blocks.
- if re.match(r'^\s*\$\s+\S', paragraph):
- # Split the paragraph into lines.
- lines = paragraph.splitlines()
- # Check if the paragraph is already indented.
- if not paragraph[0].isspace():
- # If the paragraph isn't already indented we'll indent it now.
- lines = [' %s' % line for line in lines]
- lines.insert(0, '.. code-block:: sh')
- lines.insert(1, '')
- return "\n".join(lines)
- # The following reformatting applies only to paragraphs which are not
- # indented. Yes this is a hack - for now we assume that indented paragraphs
- # are code blocks, even though this assumption can be wrong.
- if not paragraph[0].isspace():
- # Change UNIX style `quoting' so it doesn't trip up DocUtils.
- paragraph = re.sub("`(.+?)'", r'"\1"', paragraph)
- # Escape asterisks.
- paragraph = paragraph.replace('*', r'\*')
- # Reformat inline tokens.
- paragraph = replace_special_tokens(
- paragraph, meta_variables,
- lambda token: '``%s``' % token,
- )
- return paragraph
-
-
-def replace_special_tokens(text, meta_variables, replace_fn):
- return USAGE_PATTERN.sub(functools.partial(
- replace_tokens_callback,
- meta_variables=meta_variables,
- replace_fn=replace_fn
- ), text)
-
-
-def replace_tokens_callback(match, meta_variables, replace_fn):
- token = match.group(0)
- if not (re.match('^[A-Z][A-Z0-9_]+$', token) and token not in meta_variables):
- token = replace_fn(token)
- return token
diff --git a/contrib/python/humanfriendly/py3/ya.make b/contrib/python/humanfriendly/py3/ya.make
deleted file mode 100644
index e6814f8a46..0000000000
--- a/contrib/python/humanfriendly/py3/ya.make
+++ /dev/null
@@ -1,41 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(10.0)
-
-LICENSE(MIT)
-
-NO_LINT()
-
-NO_CHECK_IMPORTS(
- humanfriendly.sphinx
-)
-
-PY_SRCS(
- TOP_LEVEL
- humanfriendly/__init__.py
- humanfriendly/case.py
- humanfriendly/cli.py
- humanfriendly/compat.py
- humanfriendly/decorators.py
- humanfriendly/deprecation.py
- humanfriendly/prompts.py
- humanfriendly/sphinx.py
- humanfriendly/tables.py
- humanfriendly/terminal/__init__.py
- humanfriendly/terminal/html.py
- humanfriendly/terminal/spinners.py
- humanfriendly/testing.py
- humanfriendly/text.py
- humanfriendly/usage.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/humanfriendly/py3/
- .dist-info/METADATA
- .dist-info/entry_points.txt
- .dist-info/top_level.txt
-)
-
-END()
diff --git a/contrib/python/humanfriendly/ya.make b/contrib/python/humanfriendly/ya.make
deleted file mode 100644
index 24b7388aa5..0000000000
--- a/contrib/python/humanfriendly/ya.make
+++ /dev/null
@@ -1,18 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/humanfriendly/py2)
-ELSE()
- PEERDIR(contrib/python/humanfriendly/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- py2
- py3
-)
diff --git a/contrib/python/marisa-trie/agent.pxd b/contrib/python/marisa-trie/agent.pxd
deleted file mode 100644
index bf019673c2..0000000000
--- a/contrib/python/marisa-trie/agent.pxd
+++ /dev/null
@@ -1,22 +0,0 @@
-cimport query, key
-
-cdef extern from "<marisa/agent.h>" namespace "marisa" nogil:
- cdef cppclass Agent:
- Agent() except +
-
- query.Query &query()
- key.Key &key()
-
- void set_query(char *str)
- void set_query(char *ptr, int length)
- void set_query(int key_id)
-
- void set_key(char *str)
- void set_key(char *ptr, int length)
- void set_key(int id)
-
- void clear()
-
- void init_state()
-
- void swap(Agent &rhs)
diff --git a/contrib/python/marisa-trie/base.pxd b/contrib/python/marisa-trie/base.pxd
deleted file mode 100644
index c434e82122..0000000000
--- a/contrib/python/marisa-trie/base.pxd
+++ /dev/null
@@ -1,63 +0,0 @@
-cdef extern from "<marisa/base.h>":
-
- # A dictionary consists of 3 tries in default. Usually more tries make a
- # dictionary space-efficient but time-inefficient.
- ctypedef enum marisa_num_tries:
- MARISA_MIN_NUM_TRIES
- MARISA_MAX_NUM_TRIES
- MARISA_DEFAULT_NUM_TRIES
-
-
- # This library uses a cache technique to accelerate search functions. The
- # following enumerated type marisa_cache_level gives a list of available cache
- # size options. A larger cache enables faster search but takes a more space.
- ctypedef enum marisa_cache_level:
- MARISA_HUGE_CACHE
- MARISA_LARGE_CACHE
- MARISA_NORMAL_CACHE
- MARISA_SMALL_CACHE
- MARISA_TINY_CACHE
- MARISA_DEFAULT_CACHE
-
- # This library provides 2 kinds of TAIL implementations.
- ctypedef enum marisa_tail_mode:
- # MARISA_TEXT_TAIL merges last labels as zero-terminated strings. So, it is
- # available if and only if the last labels do not contain a NULL character.
- # If MARISA_TEXT_TAIL is specified and a NULL character exists in the last
- # labels, the setting is automatically switched to MARISA_BINARY_TAIL.
- MARISA_TEXT_TAIL
-
- # MARISA_BINARY_TAIL also merges last labels but as byte sequences. It uses
- # a bit vector to detect the end of a sequence, instead of NULL characters.
- # So, MARISA_BINARY_TAIL requires a larger space if the average length of
- # labels is greater than 8.
- MARISA_BINARY_TAIL
-
- MARISA_DEFAULT_TAIL
-
- # The arrangement of nodes affects the time cost of matching and the order of
- # predictive search.
- ctypedef enum marisa_node_order:
- # MARISA_LABEL_ORDER arranges nodes in ascending label order.
- # MARISA_LABEL_ORDER is useful if an application needs to predict keys in
- # label order.
- MARISA_LABEL_ORDER
-
- # MARISA_WEIGHT_ORDER arranges nodes in descending weight order.
- # MARISA_WEIGHT_ORDER is generally a better choice because it enables faster
- # matching.
- MARISA_WEIGHT_ORDER
- MARISA_DEFAULT_ORDER
-
- ctypedef enum marisa_config_mask:
- MARISA_NUM_TRIES_MASK
- MARISA_CACHE_LEVEL_MASK
- MARISA_TAIL_MODE_MASK
- MARISA_NODE_ORDER_MASK
- MARISA_CONFIG_MASK
-
-
-cdef extern from "<marisa/base.h>" namespace "marisa":
- ctypedef marisa_cache_level CacheLevel
- ctypedef marisa_tail_mode TailMode
- ctypedef marisa_node_order NodeOrder
diff --git a/contrib/python/marisa-trie/iostream.pxd b/contrib/python/marisa-trie/iostream.pxd
deleted file mode 100644
index 435ee85bb0..0000000000
--- a/contrib/python/marisa-trie/iostream.pxd
+++ /dev/null
@@ -1,7 +0,0 @@
-from std_iostream cimport istream, ostream
-from trie cimport Trie
-
-cdef extern from "<marisa/iostream.h>" namespace "marisa" nogil:
-
- istream &read(istream &stream, Trie *trie)
- ostream &write(ostream &stream, Trie &trie)
diff --git a/contrib/python/marisa-trie/key.pxd b/contrib/python/marisa-trie/key.pxd
deleted file mode 100644
index d99dee5e04..0000000000
--- a/contrib/python/marisa-trie/key.pxd
+++ /dev/null
@@ -1,22 +0,0 @@
-cdef extern from "<marisa/key.h>" namespace "marisa" nogil:
-
- cdef cppclass Key:
- Key()
- Key(Key &query)
-
- #Key &operator=(Key &query)
-
- char operator[](int i)
-
- void set_str(char *str)
- void set_str(char *ptr, int length)
- void set_id(int id)
- void set_weight(float weight)
-
- char *ptr()
- int length()
- int id()
- float weight()
-
- void clear()
- void swap(Key &rhs)
diff --git a/contrib/python/marisa-trie/keyset.pxd b/contrib/python/marisa-trie/keyset.pxd
deleted file mode 100644
index 1fb99a40c5..0000000000
--- a/contrib/python/marisa-trie/keyset.pxd
+++ /dev/null
@@ -1,30 +0,0 @@
-cimport key
-
-cdef extern from "<marisa/keyset.h>" namespace "marisa" nogil:
- cdef cppclass Keyset:
-
-# cdef enum constants:
-# BASE_BLOCK_SIZE = 4096
-# EXTRA_BLOCK_SIZE = 1024
-# KEY_BLOCK_SIZE = 256
-
- Keyset()
-
- void push_back(key.Key &key)
- void push_back(key.Key &key, char end_marker)
-
- void push_back(char *str)
- void push_back(char *ptr, int length)
- void push_back(char *ptr, int length, float weight)
-
- key.Key &operator[](int i)
-
- int num_keys()
- bint empty()
-
- int size()
- int total_length()
-
- void reset()
- void clear()
- void swap(Keyset &rhs)
diff --git a/contrib/python/marisa-trie/marisa/agent.cc b/contrib/python/marisa-trie/marisa/agent.cc
deleted file mode 100644
index 7f7f49f1bc..0000000000
--- a/contrib/python/marisa-trie/marisa/agent.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <new>
-
-#include "agent.h"
-#include "grimoire/trie.h"
-
-namespace marisa {
-
-Agent::Agent() : query_(), key_(), state_() {}
-
-Agent::~Agent() {}
-
-void Agent::set_query(const char *str) {
- MARISA_THROW_IF(str == NULL, MARISA_NULL_ERROR);
- if (state_.get() != NULL) {
- state_->reset();
- }
- query_.set_str(str);
-}
-
-void Agent::set_query(const char *ptr, std::size_t length) {
- MARISA_THROW_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- if (state_.get() != NULL) {
- state_->reset();
- }
- query_.set_str(ptr, length);
-}
-
-void Agent::set_query(std::size_t key_id) {
- if (state_.get() != NULL) {
- state_->reset();
- }
- query_.set_id(key_id);
-}
-
-void Agent::init_state() {
- MARISA_THROW_IF(state_.get() != NULL, MARISA_STATE_ERROR);
- state_.reset(new (std::nothrow) grimoire::State);
- MARISA_THROW_IF(state_.get() == NULL, MARISA_MEMORY_ERROR);
-}
-
-void Agent::clear() {
- Agent().swap(*this);
-}
-
-void Agent::swap(Agent &rhs) {
- query_.swap(rhs.query_);
- key_.swap(rhs.key_);
- state_.swap(rhs.state_);
-}
-
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/agent.h b/contrib/python/marisa-trie/marisa/agent.h
deleted file mode 100644
index 0f89f7df0f..0000000000
--- a/contrib/python/marisa-trie/marisa/agent.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#pragma once
-
-#ifndef MARISA_AGENT_H_
-#define MARISA_AGENT_H_
-
-#include "key.h"
-#include "query.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class State;
-
-} // namespace trie
-} // namespace grimoire
-
-class Agent {
- public:
- Agent();
- ~Agent();
-
- const Query &query() const {
- return query_;
- }
- const Key &key() const {
- return key_;
- }
-
- void set_query(const char *str);
- void set_query(const char *ptr, std::size_t length);
- void set_query(std::size_t key_id);
-
- const grimoire::trie::State &state() const {
- return *state_;
- }
- grimoire::trie::State &state() {
- return *state_;
- }
-
- void set_key(const char *str) {
- MARISA_DEBUG_IF(str == NULL, MARISA_NULL_ERROR);
- key_.set_str(str);
- }
- void set_key(const char *ptr, std::size_t length) {
- MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- key_.set_str(ptr, length);
- }
- void set_key(std::size_t id) {
- MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- key_.set_id(id);
- }
-
- bool has_state() const {
- return state_.get() != NULL;
- }
- void init_state();
-
- void clear();
- void swap(Agent &rhs);
-
- private:
- Query query_;
- Key key_;
- scoped_ptr<grimoire::trie::State> state_;
-
- // Disallows copy and assignment.
- Agent(const Agent &);
- Agent &operator=(const Agent &);
-};
-
-} // namespace marisa
-
-#endif // MARISA_AGENT_H_
diff --git a/contrib/python/marisa-trie/marisa/base.h b/contrib/python/marisa-trie/marisa/base.h
deleted file mode 100644
index 5c595dcd2b..0000000000
--- a/contrib/python/marisa-trie/marisa/base.h
+++ /dev/null
@@ -1,196 +0,0 @@
-#pragma once
-
-#ifndef MARISA_BASE_H_
-#define MARISA_BASE_H_
-
-// Old Visual C++ does not provide stdint.h.
-#ifndef _MSC_VER
- #include <stdint.h>
-#endif // _MSC_VER
-
-#ifdef __cplusplus
- #include <cstddef>
-#else // __cplusplus
- #include <stddef.h>
-#endif // __cplusplus
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-#ifdef _MSC_VER
-typedef unsigned __int8 marisa_uint8;
-typedef unsigned __int16 marisa_uint16;
-typedef unsigned __int32 marisa_uint32;
-typedef unsigned __int64 marisa_uint64;
-#else // _MSC_VER
-typedef uint8_t marisa_uint8;
-typedef uint16_t marisa_uint16;
-typedef uint32_t marisa_uint32;
-typedef uint64_t marisa_uint64;
-#endif // _MSC_VER
-
-#if defined(_WIN64) || defined(__amd64__) || defined(__x86_64__) || \
- defined(__ia64__) || defined(__ppc64__) || defined(__powerpc64__) || \
- defined(__sparc64__) || defined(__mips64__) || defined(__aarch64__) || \
- defined(__s390x__)
- #define MARISA_WORD_SIZE 64
-#else // defined(_WIN64), etc.
- #define MARISA_WORD_SIZE 32
-#endif // defined(_WIN64), etc.
-
-//#define MARISA_WORD_SIZE (sizeof(void *) * 8)
-
-#define MARISA_UINT8_MAX ((marisa_uint8)~(marisa_uint8)0)
-#define MARISA_UINT16_MAX ((marisa_uint16)~(marisa_uint16)0)
-#define MARISA_UINT32_MAX ((marisa_uint32)~(marisa_uint32)0)
-#define MARISA_UINT64_MAX ((marisa_uint64)~(marisa_uint64)0)
-#define MARISA_SIZE_MAX ((size_t)~(size_t)0)
-
-#define MARISA_INVALID_LINK_ID MARISA_UINT32_MAX
-#define MARISA_INVALID_KEY_ID MARISA_UINT32_MAX
-#define MARISA_INVALID_EXTRA (MARISA_UINT32_MAX >> 8)
-
-// Error codes are defined as members of marisa_error_code. This library throws
-// an exception with one of the error codes when an error occurs.
-typedef enum marisa_error_code_ {
- // MARISA_OK means that a requested operation has succeeded. In practice, an
- // exception never has MARISA_OK because it is not an error.
- MARISA_OK = 0,
-
- // MARISA_STATE_ERROR means that an object was not ready for a requested
- // operation. For example, an operation to modify a fixed vector throws an
- // exception with MARISA_STATE_ERROR.
- MARISA_STATE_ERROR = 1,
-
- // MARISA_NULL_ERROR means that an invalid NULL pointer has been given.
- MARISA_NULL_ERROR = 2,
-
- // MARISA_BOUND_ERROR means that an operation has tried to access an out of
- // range address.
- MARISA_BOUND_ERROR = 3,
-
- // MARISA_RANGE_ERROR means that an out of range value has appeared in
- // operation.
- MARISA_RANGE_ERROR = 4,
-
- // MARISA_CODE_ERROR means that an undefined code has appeared in operation.
- MARISA_CODE_ERROR = 5,
-
- // MARISA_RESET_ERROR means that a smart pointer has tried to reset itself.
- MARISA_RESET_ERROR = 6,
-
- // MARISA_SIZE_ERROR means that a size has exceeded a library limitation.
- MARISA_SIZE_ERROR = 7,
-
- // MARISA_MEMORY_ERROR means that a memory allocation has failed.
- MARISA_MEMORY_ERROR = 8,
-
- // MARISA_IO_ERROR means that an I/O operation has failed.
- MARISA_IO_ERROR = 9,
-
- // MARISA_FORMAT_ERROR means that input was in invalid format.
- MARISA_FORMAT_ERROR = 10,
-} marisa_error_code;
-
-// Min/max values, flags and masks for dictionary settings are defined below.
-// Please note that unspecified settings will be replaced with the default
-// settings. For example, 0 is equivalent to (MARISA_DEFAULT_NUM_TRIES |
-// MARISA_DEFAULT_TRIE | MARISA_DEFAULT_TAIL | MARISA_DEFAULT_ORDER).
-
-// A dictionary consists of 3 tries in default. Usually more tries make a
-// dictionary space-efficient but time-inefficient.
-typedef enum marisa_num_tries_ {
- MARISA_MIN_NUM_TRIES = 0x00001,
- MARISA_MAX_NUM_TRIES = 0x0007F,
- MARISA_DEFAULT_NUM_TRIES = 0x00003,
-} marisa_num_tries;
-
-// This library uses a cache technique to accelerate search functions. The
-// following enumerated type marisa_cache_level gives a list of available cache
-// size options. A larger cache enables faster search but takes a more space.
-typedef enum marisa_cache_level_ {
- MARISA_HUGE_CACHE = 0x00080,
- MARISA_LARGE_CACHE = 0x00100,
- MARISA_NORMAL_CACHE = 0x00200,
- MARISA_SMALL_CACHE = 0x00400,
- MARISA_TINY_CACHE = 0x00800,
- MARISA_DEFAULT_CACHE = MARISA_NORMAL_CACHE
-} marisa_cache_level;
-
-// This library provides 2 kinds of TAIL implementations.
-typedef enum marisa_tail_mode_ {
- // MARISA_TEXT_TAIL merges last labels as zero-terminated strings. So, it is
- // available if and only if the last labels do not contain a NULL character.
- // If MARISA_TEXT_TAIL is specified and a NULL character exists in the last
- // labels, the setting is automatically switched to MARISA_BINARY_TAIL.
- MARISA_TEXT_TAIL = 0x01000,
-
- // MARISA_BINARY_TAIL also merges last labels but as byte sequences. It uses
- // a bit vector to detect the end of a sequence, instead of NULL characters.
- // So, MARISA_BINARY_TAIL requires a larger space if the average length of
- // labels is greater than 8.
- MARISA_BINARY_TAIL = 0x02000,
-
- MARISA_DEFAULT_TAIL = MARISA_TEXT_TAIL,
-} marisa_tail_mode;
-
-// The arrangement of nodes affects the time cost of matching and the order of
-// predictive search.
-typedef enum marisa_node_order_ {
- // MARISA_LABEL_ORDER arranges nodes in ascending label order.
- // MARISA_LABEL_ORDER is useful if an application needs to predict keys in
- // label order.
- MARISA_LABEL_ORDER = 0x10000,
-
- // MARISA_WEIGHT_ORDER arranges nodes in descending weight order.
- // MARISA_WEIGHT_ORDER is generally a better choice because it enables faster
- // matching.
- MARISA_WEIGHT_ORDER = 0x20000,
-
- MARISA_DEFAULT_ORDER = MARISA_WEIGHT_ORDER,
-} marisa_node_order;
-
-typedef enum marisa_config_mask_ {
- MARISA_NUM_TRIES_MASK = 0x0007F,
- MARISA_CACHE_LEVEL_MASK = 0x00F80,
- MARISA_TAIL_MODE_MASK = 0x0F000,
- MARISA_NODE_ORDER_MASK = 0xF0000,
- MARISA_CONFIG_MASK = 0xFFFFF
-} marisa_config_mask;
-
-#ifdef __cplusplus
-} // extern "C"
-#endif // __cplusplus
-
-#ifdef __cplusplus
-namespace marisa {
-
-typedef ::marisa_uint8 UInt8;
-typedef ::marisa_uint16 UInt16;
-typedef ::marisa_uint32 UInt32;
-typedef ::marisa_uint64 UInt64;
-
-typedef ::marisa_error_code ErrorCode;
-
-typedef ::marisa_cache_level CacheLevel;
-typedef ::marisa_tail_mode TailMode;
-typedef ::marisa_node_order NodeOrder;
-
-template <typename T>
-inline void swap(T &lhs, T &rhs) {
- T temp = lhs;
- lhs = rhs;
- rhs = temp;
-}
-
-} // namespace marisa
-#endif // __cplusplus
-
-#ifdef __cplusplus
- #include "exception.h"
- #include "scoped-ptr.h"
- #include "scoped-array.h"
-#endif // __cplusplus
-
-#endif // MARISA_BASE_H_
diff --git a/contrib/python/marisa-trie/marisa/exception.h b/contrib/python/marisa-trie/marisa/exception.h
deleted file mode 100644
index 630936b23b..0000000000
--- a/contrib/python/marisa-trie/marisa/exception.h
+++ /dev/null
@@ -1,84 +0,0 @@
-#pragma once
-
-#ifndef MARISA_EXCEPTION_H_
-#define MARISA_EXCEPTION_H_
-
-#include <exception>
-
-#include "base.h"
-
-namespace marisa {
-
-// An exception object keeps a filename, a line number, an error code and an
-// error message. The message format is as follows:
-// "__FILE__:__LINE__: error_code: error_message"
-class Exception : public std::exception {
- public:
- Exception(const char *filename, int line,
- ErrorCode error_code, const char *error_message)
- : std::exception(), filename_(filename), line_(line),
- error_code_(error_code), error_message_(error_message) {}
- Exception(const Exception &ex)
- : std::exception(), filename_(ex.filename_), line_(ex.line_),
- error_code_(ex.error_code_), error_message_(ex.error_message_) {}
- virtual ~Exception() {}
-
- Exception &operator=(const Exception &rhs) {
- filename_ = rhs.filename_;
- line_ = rhs.line_;
- error_code_ = rhs.error_code_;
- error_message_ = rhs.error_message_;
- return *this;
- }
-
- const char *filename() const {
- return filename_;
- }
- int line() const {
- return line_;
- }
- ErrorCode error_code() const {
- return error_code_;
- }
- const char *error_message() const {
- return error_message_;
- }
-
- virtual const char *what() const noexcept {
- return error_message_;
- }
-
- private:
- const char *filename_;
- int line_;
- ErrorCode error_code_;
- const char *error_message_;
-};
-
-// These macros are used to convert a line number to a string constant.
-#define MARISA_INT_TO_STR(value) #value
-#define MARISA_LINE_TO_STR(line) MARISA_INT_TO_STR(line)
-#define MARISA_LINE_STR MARISA_LINE_TO_STR(__LINE__)
-
-// MARISA_THROW throws an exception with a filename, a line number, an error
-// code and an error message. The message format is as follows:
-// "__FILE__:__LINE__: error_code: error_message"
-#define MARISA_THROW(error_code, error_message) \
- (throw marisa::Exception(__FILE__, __LINE__, error_code, \
- __FILE__ ":" MARISA_LINE_STR ": " #error_code ": " error_message))
-
-// MARISA_THROW_IF throws an exception if `condition' is true.
-#define MARISA_THROW_IF(condition, error_code) \
- (void)((!(condition)) || (MARISA_THROW(error_code, #condition), 0))
-
-// MARISA_DEBUG_IF is ignored if _DEBUG is undefined. So, it is useful for
-// debugging time-critical codes.
-#ifdef _DEBUG
- #define MARISA_DEBUG_IF(cond, error_code) MARISA_THROW_IF(cond, error_code)
-#else
- #define MARISA_DEBUG_IF(cond, error_code)
-#endif
-
-} // namespace marisa
-
-#endif // MARISA_EXCEPTION_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/algorithm.h b/contrib/python/marisa-trie/marisa/grimoire/algorithm.h
deleted file mode 100644
index 71baec34ac..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/algorithm.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_ALGORITHM_H_
-#define MARISA_GRIMOIRE_ALGORITHM_H_
-
-#include "algorithm/sort.h"
-
-namespace marisa {
-namespace grimoire {
-
-class Algorithm {
- public:
- Algorithm() {}
-
- template <typename Iterator>
- std::size_t sort(Iterator begin, Iterator end) const {
- return algorithm::sort(begin, end);
- }
-
- private:
- Algorithm(const Algorithm &);
- Algorithm &operator=(const Algorithm &);
-};
-
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_ALGORITHM_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h b/contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h
deleted file mode 100644
index 9090336ce6..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h
+++ /dev/null
@@ -1,197 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_ALGORITHM_SORT_H_
-#define MARISA_GRIMOIRE_ALGORITHM_SORT_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace algorithm {
-namespace details {
-
-enum {
- MARISA_INSERTION_SORT_THRESHOLD = 10
-};
-
-template <typename T>
-int get_label(const T &unit, std::size_t depth) {
- MARISA_DEBUG_IF(depth > unit.length(), MARISA_BOUND_ERROR);
-
- return (depth < unit.length()) ? (int)(UInt8)unit[depth] : -1;
-}
-
-template <typename T>
-int median(const T &a, const T &b, const T &c, std::size_t depth) {
- const int x = get_label(a, depth);
- const int y = get_label(b, depth);
- const int z = get_label(c, depth);
- if (x < y) {
- if (y < z) {
- return y;
- } else if (x < z) {
- return z;
- }
- return x;
- } else if (x < z) {
- return x;
- } else if (y < z) {
- return z;
- }
- return y;
-}
-
-template <typename T>
-int compare(const T &lhs, const T &rhs, std::size_t depth) {
- for (std::size_t i = depth; i < lhs.length(); ++i) {
- if (i == rhs.length()) {
- return 1;
- }
- if (lhs[i] != rhs[i]) {
- return (UInt8)lhs[i] - (UInt8)rhs[i];
- }
- }
- if (lhs.length() == rhs.length()) {
- return 0;
- }
- return (lhs.length() < rhs.length()) ? -1 : 1;
-}
-
-template <typename Iterator>
-std::size_t insertion_sort(Iterator l, Iterator r, std::size_t depth) {
- MARISA_DEBUG_IF(l > r, MARISA_BOUND_ERROR);
-
- std::size_t count = 1;
- for (Iterator i = l + 1; i < r; ++i) {
- int result = 0;
- for (Iterator j = i; j > l; --j) {
- result = compare(*(j - 1), *j, depth);
- if (result <= 0) {
- break;
- }
- marisa::swap(*(j - 1), *j);
- }
- if (result != 0) {
- ++count;
- }
- }
- return count;
-}
-
-template <typename Iterator>
-std::size_t sort(Iterator l, Iterator r, std::size_t depth) {
- MARISA_DEBUG_IF(l > r, MARISA_BOUND_ERROR);
-
- std::size_t count = 0;
- while ((r - l) > MARISA_INSERTION_SORT_THRESHOLD) {
- Iterator pl = l;
- Iterator pr = r;
- Iterator pivot_l = l;
- Iterator pivot_r = r;
-
- const int pivot = median(*l, *(l + (r - l) / 2), *(r - 1), depth);
- for ( ; ; ) {
- while (pl < pr) {
- const int label = get_label(*pl, depth);
- if (label > pivot) {
- break;
- } else if (label == pivot) {
- marisa::swap(*pl, *pivot_l);
- ++pivot_l;
- }
- ++pl;
- }
- while (pl < pr) {
- const int label = get_label(*--pr, depth);
- if (label < pivot) {
- break;
- } else if (label == pivot) {
- marisa::swap(*pr, *--pivot_r);
- }
- }
- if (pl >= pr) {
- break;
- }
- marisa::swap(*pl, *pr);
- ++pl;
- }
- while (pivot_l > l) {
- marisa::swap(*--pivot_l, *--pl);
- }
- while (pivot_r < r) {
- marisa::swap(*pivot_r, *pr);
- ++pivot_r;
- ++pr;
- }
-
- if (((pl - l) > (pr - pl)) || ((r - pr) > (pr - pl))) {
- if ((pr - pl) == 1) {
- ++count;
- } else if ((pr - pl) > 1) {
- if (pivot == -1) {
- ++count;
- } else {
- count += sort(pl, pr, depth + 1);
- }
- }
-
- if ((pl - l) < (r - pr)) {
- if ((pl - l) == 1) {
- ++count;
- } else if ((pl - l) > 1) {
- count += sort(l, pl, depth);
- }
- l = pr;
- } else {
- if ((r - pr) == 1) {
- ++count;
- } else if ((r - pr) > 1) {
- count += sort(pr, r, depth);
- }
- r = pl;
- }
- } else {
- if ((pl - l) == 1) {
- ++count;
- } else if ((pl - l) > 1) {
- count += sort(l, pl, depth);
- }
-
- if ((r - pr) == 1) {
- ++count;
- } else if ((r - pr) > 1) {
- count += sort(pr, r, depth);
- }
-
- l = pl, r = pr;
- if ((pr - pl) == 1) {
- ++count;
- } else if ((pr - pl) > 1) {
- if (pivot == -1) {
- l = r;
- ++count;
- } else {
- ++depth;
- }
- }
- }
- }
-
- if ((r - l) > 1) {
- count += insertion_sort(l, r, depth);
- }
- return count;
-}
-
-} // namespace details
-
-template <typename Iterator>
-std::size_t sort(Iterator begin, Iterator end) {
- MARISA_DEBUG_IF(begin > end, MARISA_BOUND_ERROR);
- return details::sort(begin, end, 0);
-};
-
-} // namespace algorithm
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_ALGORITHM_SORT_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/intrin.h b/contrib/python/marisa-trie/marisa/grimoire/intrin.h
deleted file mode 100644
index 16843b353c..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/intrin.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_INTRIN_H_
-#define MARISA_GRIMOIRE_INTRIN_H_
-
-#include "../base.h"
-
-#if defined(__x86_64__) || defined(_M_X64)
- #define MARISA_X64
-#elif defined(__i386__) || defined(_M_IX86)
- #define MARISA_X86
-#else // defined(__i386__) || defined(_M_IX86)
- #ifdef MARISA_USE_POPCNT
- #undef MARISA_USE_POPCNT
- #endif // MARISA_USE_POPCNT
- #ifdef MARISA_USE_SSE4A
- #undef MARISA_USE_SSE4A
- #endif // MARISA_USE_SSE4A
- #ifdef MARISA_USE_SSE4
- #undef MARISA_USE_SSE4
- #endif // MARISA_USE_SSE4
- #ifdef MARISA_USE_SSE4_2
- #undef MARISA_USE_SSE4_2
- #endif // MARISA_USE_SSE4_2
- #ifdef MARISA_USE_SSE4_1
- #undef MARISA_USE_SSE4_1
- #endif // MARISA_USE_SSE4_1
- #ifdef MARISA_USE_SSSE3
- #undef MARISA_USE_SSSE3
- #endif // MARISA_USE_SSSE3
- #ifdef MARISA_USE_SSE3
- #undef MARISA_USE_SSE3
- #endif // MARISA_USE_SSE3
- #ifdef MARISA_USE_SSE2
- #undef MARISA_USE_SSE2
- #endif // MARISA_USE_SSE2
-#endif // defined(__i386__) || defined(_M_IX86)
-
-#ifdef MARISA_USE_POPCNT
- #ifndef MARISA_USE_SSE3
- #define MARISA_USE_SSE3
- #endif // MARISA_USE_SSE3
- #ifdef _MSC_VER
- #include <intrin.h>
- #else // _MSC_VER
- #include <popcntintrin.h>
- #endif // _MSC_VER
-#endif // MARISA_USE_POPCNT
-
-#ifdef MARISA_USE_SSE4A
- #ifndef MARISA_USE_SSE3
- #define MARISA_USE_SSE3
- #endif // MARISA_USE_SSE3
- #ifndef MARISA_USE_POPCNT
- #define MARISA_USE_POPCNT
- #endif // MARISA_USE_POPCNT
-#endif // MARISA_USE_SSE4A
-
-#ifdef MARISA_USE_SSE4
- #ifndef MARISA_USE_SSE4_2
- #define MARISA_USE_SSE4_2
- #endif // MARISA_USE_SSE4_2
-#endif // MARISA_USE_SSE4
-
-#ifdef MARISA_USE_SSE4_2
- #ifndef MARISA_USE_SSE4_1
- #define MARISA_USE_SSE4_1
- #endif // MARISA_USE_SSE4_1
- #ifndef MARISA_USE_POPCNT
- #define MARISA_USE_POPCNT
- #endif // MARISA_USE_POPCNT
-#endif // MARISA_USE_SSE4_2
-
-#ifdef MARISA_USE_SSE4_1
- #ifndef MARISA_USE_SSSE3
- #define MARISA_USE_SSSE3
- #endif // MARISA_USE_SSSE3
-#endif // MARISA_USE_SSE4_1
-
-#ifdef MARISA_USE_SSSE3
- #ifndef MARISA_USE_SSE3
- #define MARISA_USE_SSE3
- #endif // MARISA_USE_SSE3
- #ifdef MARISA_X64
- #define MARISA_X64_SSSE3
- #else // MARISA_X64
- #define MARISA_X86_SSSE3
- #endif // MAIRSA_X64
- #include <tmmintrin.h>
-#endif // MARISA_USE_SSSE3
-
-#ifdef MARISA_USE_SSE3
- #ifndef MARISA_USE_SSE2
- #define MARISA_USE_SSE2
- #endif // MARISA_USE_SSE2
-#endif // MARISA_USE_SSE3
-
-#ifdef MARISA_USE_SSE2
- #ifdef MARISA_X64
- #define MARISA_X64_SSE2
- #else // MARISA_X64
- #define MARISA_X86_SSE2
- #endif // MAIRSA_X64
- #include <emmintrin.h>
-#endif // MARISA_USE_SSE2
-
-#ifdef _MSC_VER
- #if MARISA_WORD_SIZE == 64
- #include <intrin.h>
- #pragma intrinsic(_BitScanForward64)
- #else // MARISA_WORD_SIZE == 64
- #include <intrin.h>
- #pragma intrinsic(_BitScanForward)
- #endif // MARISA_WORD_SIZE == 64
-#endif // _MSC_VER
-
-#endif // MARISA_GRIMOIRE_INTRIN_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io.h b/contrib/python/marisa-trie/marisa/grimoire/io.h
deleted file mode 100644
index 4de0110dbb..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_IO_H_
-#define MARISA_GRIMOIRE_IO_H_
-
-#include "io/mapper.h"
-#include "io/reader.h"
-#include "io/writer.h"
-
-namespace marisa {
-namespace grimoire {
-
-using io::Mapper;
-using io::Reader;
-using io::Writer;
-
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_IO_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc b/contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc
deleted file mode 100644
index 9ed6ffc755..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-#if (defined _WIN32) || (defined _WIN64)
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <windows.h>
-#else // (defined _WIN32) || (defined _WIN64)
- #include <sys/mman.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
-#endif // (defined _WIN32) || (defined _WIN64)
-
-#include "mapper.h"
-
-namespace marisa {
-namespace grimoire {
-namespace io {
-
-#if (defined _WIN32) || (defined _WIN64)
-Mapper::Mapper()
- : ptr_(NULL), origin_(NULL), avail_(0), size_(0),
- file_(NULL), map_(NULL) {}
-#else // (defined _WIN32) || (defined _WIN64)
-Mapper::Mapper()
- : ptr_(NULL), origin_(MAP_FAILED), avail_(0), size_(0), fd_(-1) {}
-#endif // (defined _WIN32) || (defined _WIN64)
-
-#if (defined _WIN32) || (defined _WIN64)
-Mapper::~Mapper() {
- if (origin_ != NULL) {
- ::UnmapViewOfFile(origin_);
- }
-
- if (map_ != NULL) {
- ::CloseHandle(map_);
- }
-
- if (file_ != NULL) {
- ::CloseHandle(file_);
- }
-}
-#else // (defined _WIN32) || (defined _WIN64)
-Mapper::~Mapper() {
- if (origin_ != MAP_FAILED) {
- ::munmap(origin_, size_);
- }
-
- if (fd_ != -1) {
- ::close(fd_);
- }
-}
-#endif // (defined _WIN32) || (defined _WIN64)
-
-void Mapper::open(const char *filename) {
- MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
-
- Mapper temp;
- temp.open_(filename);
- swap(temp);
-}
-
-void Mapper::open(const void *ptr, std::size_t size) {
- MARISA_THROW_IF((ptr == NULL) && (size != 0), MARISA_NULL_ERROR);
-
- Mapper temp;
- temp.open_(ptr, size);
- swap(temp);
-}
-
-void Mapper::seek(std::size_t size) {
- MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
- MARISA_THROW_IF(size > avail_, MARISA_IO_ERROR);
-
- map_data(size);
-}
-
-bool Mapper::is_open() const {
- return ptr_ != NULL;
-}
-
-void Mapper::clear() {
- Mapper().swap(*this);
-}
-
-void Mapper::swap(Mapper &rhs) {
- marisa::swap(ptr_, rhs.ptr_);
- marisa::swap(avail_, rhs.avail_);
- marisa::swap(origin_, rhs.origin_);
- marisa::swap(size_, rhs.size_);
-#if (defined _WIN32) || (defined _WIN64)
- marisa::swap(file_, rhs.file_);
- marisa::swap(map_, rhs.map_);
-#else // (defined _WIN32) || (defined _WIN64)
- marisa::swap(fd_, rhs.fd_);
-#endif // (defined _WIN32) || (defined _WIN64)
-}
-
-const void *Mapper::map_data(std::size_t size) {
- MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
- MARISA_THROW_IF(size > avail_, MARISA_IO_ERROR);
-
- const char * const data = static_cast<const char *>(ptr_);
- ptr_ = data + size;
- avail_ -= size;
- return data;
-}
-
-#if (defined _WIN32) || (defined _WIN64)
- #ifdef __MSVCRT_VERSION__
- #if __MSVCRT_VERSION__ >= 0x0601
- #define MARISA_HAS_STAT64
- #endif // __MSVCRT_VERSION__ >= 0x0601
- #endif // __MSVCRT_VERSION__
-void Mapper::open_(const char *filename) {
- #ifdef MARISA_HAS_STAT64
- struct __stat64 st;
- MARISA_THROW_IF(::_stat64(filename, &st) != 0, MARISA_IO_ERROR);
- #else // MARISA_HAS_STAT64
- struct _stat st;
- MARISA_THROW_IF(::_stat(filename, &st) != 0, MARISA_IO_ERROR);
- #endif // MARISA_HAS_STAT64
- MARISA_THROW_IF((UInt64)st.st_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- size_ = (std::size_t)st.st_size;
-
- file_ = ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- MARISA_THROW_IF(file_ == INVALID_HANDLE_VALUE, MARISA_IO_ERROR);
-
- map_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY, 0, 0, NULL);
- MARISA_THROW_IF(map_ == NULL, MARISA_IO_ERROR);
-
- origin_ = ::MapViewOfFile(map_, FILE_MAP_READ, 0, 0, 0);
- MARISA_THROW_IF(origin_ == NULL, MARISA_IO_ERROR);
-
- ptr_ = static_cast<const char *>(origin_);
- avail_ = size_;
-}
-#else // (defined _WIN32) || (defined _WIN64)
-void Mapper::open_(const char *filename) {
- struct stat st;
- MARISA_THROW_IF(::stat(filename, &st) != 0, MARISA_IO_ERROR);
- MARISA_THROW_IF((UInt64)st.st_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- size_ = (std::size_t)st.st_size;
-
- fd_ = ::open(filename, O_RDONLY);
- MARISA_THROW_IF(fd_ == -1, MARISA_IO_ERROR);
-
- origin_ = ::mmap(NULL, size_, PROT_READ, MAP_SHARED, fd_, 0);
- MARISA_THROW_IF(origin_ == MAP_FAILED, MARISA_IO_ERROR);
-
- ptr_ = static_cast<const char *>(origin_);
- avail_ = size_;
-}
-#endif // (defined _WIN32) || (defined _WIN64)
-
-void Mapper::open_(const void *ptr, std::size_t size) {
- ptr_ = ptr;
- avail_ = size;
-}
-
-} // namespace io
-} // namespace grimoire
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/mapper.h b/contrib/python/marisa-trie/marisa/grimoire/io/mapper.h
deleted file mode 100644
index e06072501d..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io/mapper.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_IO_MAPPER_H_
-#define MARISA_GRIMOIRE_IO_MAPPER_H_
-
-#include <cstdio>
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace io {
-
-class Mapper {
- public:
- Mapper();
- ~Mapper();
-
- void open(const char *filename);
- void open(const void *ptr, std::size_t size);
-
- template <typename T>
- void map(T *obj) {
- MARISA_THROW_IF(obj == NULL, MARISA_NULL_ERROR);
- *obj = *static_cast<const T *>(map_data(sizeof(T)));
- }
-
- template <typename T>
- void map(const T **objs, std::size_t num_objs) {
- MARISA_THROW_IF((objs == NULL) && (num_objs != 0), MARISA_NULL_ERROR);
- MARISA_THROW_IF(num_objs > (MARISA_SIZE_MAX / sizeof(T)),
- MARISA_SIZE_ERROR);
- *objs = static_cast<const T *>(map_data(sizeof(T) * num_objs));
- }
-
- void seek(std::size_t size);
-
- bool is_open() const;
-
- void clear();
- void swap(Mapper &rhs);
-
- private:
- const void *ptr_;
- void *origin_;
- std::size_t avail_;
- std::size_t size_;
-#if (defined _WIN32) || (defined _WIN64)
- void *file_;
- void *map_;
-#else // (defined _WIN32) || (defined _WIN64)
- int fd_;
-#endif // (defined _WIN32) || (defined _WIN64)
-
- void open_(const char *filename);
- void open_(const void *ptr, std::size_t size);
-
- const void *map_data(std::size_t size);
-
- // Disallows copy and assignment.
- Mapper(const Mapper &);
- Mapper &operator=(const Mapper &);
-};
-
-} // namespace io
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_IO_MAPPER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/reader.cc b/contrib/python/marisa-trie/marisa/grimoire/io/reader.cc
deleted file mode 100644
index cb22fcbd4a..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io/reader.cc
+++ /dev/null
@@ -1,147 +0,0 @@
-#include <stdio.h>
-
-#ifdef _WIN32
- #include <io.h>
-#else // _WIN32
- #include <unistd.h>
-#endif // _WIN32
-
-#include <limits>
-
-#include "reader.h"
-
-namespace marisa {
-namespace grimoire {
-namespace io {
-
-Reader::Reader()
- : file_(NULL), fd_(-1), stream_(NULL), needs_fclose_(false) {}
-
-Reader::~Reader() {
- if (needs_fclose_) {
- ::fclose(file_);
- }
-}
-
-void Reader::open(const char *filename) {
- MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
-
- Reader temp;
- temp.open_(filename);
- swap(temp);
-}
-
-void Reader::open(std::FILE *file) {
- MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
-
- Reader temp;
- temp.open_(file);
- swap(temp);
-}
-
-void Reader::open(int fd) {
- MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
-
- Reader temp;
- temp.open_(fd);
- swap(temp);
-}
-
-void Reader::open(std::istream &stream) {
- Reader temp;
- temp.open_(stream);
- swap(temp);
-}
-
-void Reader::clear() {
- Reader().swap(*this);
-}
-
-void Reader::swap(Reader &rhs) {
- marisa::swap(file_, rhs.file_);
- marisa::swap(fd_, rhs.fd_);
- marisa::swap(stream_, rhs.stream_);
- marisa::swap(needs_fclose_, rhs.needs_fclose_);
-}
-
-void Reader::seek(std::size_t size) {
- MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
- if (size == 0) {
- return;
- } else if (size <= 16) {
- char buf[16];
- read_data(buf, size);
- } else {
- char buf[1024];
- while (size != 0) {
- const std::size_t count = (size < sizeof(buf)) ? size : sizeof(buf);
- read_data(buf, count);
- size -= count;
- }
- }
-}
-
-bool Reader::is_open() const {
- return (file_ != NULL) || (fd_ != -1) || (stream_ != NULL);
-}
-
-void Reader::open_(const char *filename) {
- std::FILE *file = NULL;
-#ifdef _MSC_VER
- MARISA_THROW_IF(::fopen_s(&file, filename, "rb") != 0, MARISA_IO_ERROR);
-#else // _MSC_VER
- file = ::fopen(filename, "rb");
- MARISA_THROW_IF(file == NULL, MARISA_IO_ERROR);
-#endif // _MSC_VER
- file_ = file;
- needs_fclose_ = true;
-}
-
-void Reader::open_(std::FILE *file) {
- file_ = file;
-}
-
-void Reader::open_(int fd) {
- fd_ = fd;
-}
-
-void Reader::open_(std::istream &stream) {
- stream_ = &stream;
-}
-
-void Reader::read_data(void *buf, std::size_t size) {
- MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
- if (size == 0) {
- return;
- } else if (fd_ != -1) {
- while (size != 0) {
-#ifdef _WIN32
- static const std::size_t CHUNK_SIZE =
- std::numeric_limits<int>::max();
- const unsigned int count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
- const int size_read = ::_read(fd_, buf, count);
-#else // _WIN32
- static const std::size_t CHUNK_SIZE =
- std::numeric_limits< ::ssize_t>::max();
- const ::size_t count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
- const ::ssize_t size_read = ::read(fd_, buf, count);
-#endif // _WIN32
- MARISA_THROW_IF(size_read <= 0, MARISA_IO_ERROR);
- buf = static_cast<char *>(buf) + size_read;
- size -= size_read;
- }
- } else if (file_ != NULL) {
- MARISA_THROW_IF(::fread(buf, 1, size, file_) != size, MARISA_IO_ERROR);
- } else if (stream_ != NULL) {
- try {
- MARISA_THROW_IF(!stream_->read(static_cast<char *>(buf), size),
- MARISA_IO_ERROR);
- } catch (const std::ios_base::failure &) {
- MARISA_THROW(MARISA_IO_ERROR, "std::ios_base::failure");
- }
- }
-}
-
-} // namespace io
-} // namespace grimoire
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/reader.h b/contrib/python/marisa-trie/marisa/grimoire/io/reader.h
deleted file mode 100644
index fc1ba5eea7..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io/reader.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_IO_READER_H_
-#define MARISA_GRIMOIRE_IO_READER_H_
-
-#include <cstdio>
-#include <iostream>
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace io {
-
-class Reader {
- public:
- Reader();
- ~Reader();
-
- void open(const char *filename);
- void open(std::FILE *file);
- void open(int fd);
- void open(std::istream &stream);
-
- template <typename T>
- void read(T *obj) {
- MARISA_THROW_IF(obj == NULL, MARISA_NULL_ERROR);
- read_data(obj, sizeof(T));
- }
-
- template <typename T>
- void read(T *objs, std::size_t num_objs) {
- MARISA_THROW_IF((objs == NULL) && (num_objs != 0), MARISA_NULL_ERROR);
- MARISA_THROW_IF(num_objs > (MARISA_SIZE_MAX / sizeof(T)),
- MARISA_SIZE_ERROR);
- read_data(objs, sizeof(T) * num_objs);
- }
-
- void seek(std::size_t size);
-
- bool is_open() const;
-
- void clear();
- void swap(Reader &rhs);
-
- private:
- std::FILE *file_;
- int fd_;
- std::istream *stream_;
- bool needs_fclose_;
-
- void open_(const char *filename);
- void open_(std::FILE *file);
- void open_(int fd);
- void open_(std::istream &stream);
-
- void read_data(void *buf, std::size_t size);
-
- // Disallows copy and assignment.
- Reader(const Reader &);
- Reader &operator=(const Reader &);
-};
-
-} // namespace io
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_IO_READER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/writer.cc b/contrib/python/marisa-trie/marisa/grimoire/io/writer.cc
deleted file mode 100644
index 1f079d8ce6..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io/writer.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-#include <stdio.h>
-
-#ifdef _WIN32
- #include <io.h>
-#else // _WIN32
- #include <unistd.h>
-#endif // _WIN32
-
-#include <limits>
-
-#include "writer.h"
-
-namespace marisa {
-namespace grimoire {
-namespace io {
-
-Writer::Writer()
- : file_(NULL), fd_(-1), stream_(NULL), needs_fclose_(false) {}
-
-Writer::~Writer() {
- if (needs_fclose_) {
- ::fclose(file_);
- }
-}
-
-void Writer::open(const char *filename) {
- MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
-
- Writer temp;
- temp.open_(filename);
- swap(temp);
-}
-
-void Writer::open(std::FILE *file) {
- MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
-
- Writer temp;
- temp.open_(file);
- swap(temp);
-}
-
-void Writer::open(int fd) {
- MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
-
- Writer temp;
- temp.open_(fd);
- swap(temp);
-}
-
-void Writer::open(std::ostream &stream) {
- Writer temp;
- temp.open_(stream);
- swap(temp);
-}
-
-void Writer::clear() {
- Writer().swap(*this);
-}
-
-void Writer::swap(Writer &rhs) {
- marisa::swap(file_, rhs.file_);
- marisa::swap(fd_, rhs.fd_);
- marisa::swap(stream_, rhs.stream_);
- marisa::swap(needs_fclose_, rhs.needs_fclose_);
-}
-
-void Writer::seek(std::size_t size) {
- MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
- if (size == 0) {
- return;
- } else if (size <= 16) {
- const char buf[16] = {};
- write_data(buf, size);
- } else {
- const char buf[1024] = {};
- do {
- const std::size_t count = (size < sizeof(buf)) ? size : sizeof(buf);
- write_data(buf, count);
- size -= count;
- } while (size != 0);
- }
-}
-
-bool Writer::is_open() const {
- return (file_ != NULL) || (fd_ != -1) || (stream_ != NULL);
-}
-
-void Writer::open_(const char *filename) {
- std::FILE *file = NULL;
-#ifdef _MSC_VER
- MARISA_THROW_IF(::fopen_s(&file, filename, "wb") != 0, MARISA_IO_ERROR);
-#else // _MSC_VER
- file = ::fopen(filename, "wb");
- MARISA_THROW_IF(file == NULL, MARISA_IO_ERROR);
-#endif // _MSC_VER
- file_ = file;
- needs_fclose_ = true;
-}
-
-void Writer::open_(std::FILE *file) {
- file_ = file;
-}
-
-void Writer::open_(int fd) {
- fd_ = fd;
-}
-
-void Writer::open_(std::ostream &stream) {
- stream_ = &stream;
-}
-
-void Writer::write_data(const void *data, std::size_t size) {
- MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
- if (size == 0) {
- return;
- } else if (fd_ != -1) {
- while (size != 0) {
-#ifdef _WIN32
- static const std::size_t CHUNK_SIZE =
- std::numeric_limits<int>::max();
- const unsigned int count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
- const int size_written = ::_write(fd_, data, count);
-#else // _WIN32
- static const std::size_t CHUNK_SIZE =
- std::numeric_limits< ::ssize_t>::max();
- const ::size_t count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
- const ::ssize_t size_written = ::write(fd_, data, count);
-#endif // _WIN32
- MARISA_THROW_IF(size_written <= 0, MARISA_IO_ERROR);
- data = static_cast<const char *>(data) + size_written;
- size -= size_written;
- }
- } else if (file_ != NULL) {
- MARISA_THROW_IF(::fwrite(data, 1, size, file_) != size, MARISA_IO_ERROR);
- MARISA_THROW_IF(::fflush(file_) != 0, MARISA_IO_ERROR);
- } else if (stream_ != NULL) {
- try {
- MARISA_THROW_IF(!stream_->write(static_cast<const char *>(data), size),
- MARISA_IO_ERROR);
- } catch (const std::ios_base::failure &) {
- MARISA_THROW(MARISA_IO_ERROR, "std::ios_base::failure");
- }
- }
-}
-
-} // namespace io
-} // namespace grimoire
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/writer.h b/contrib/python/marisa-trie/marisa/grimoire/io/writer.h
deleted file mode 100644
index 1707b23de2..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/io/writer.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_IO_WRITER_H_
-#define MARISA_GRIMOIRE_IO_WRITER_H_
-
-#include <cstdio>
-#include <iostream>
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace io {
-
-class Writer {
- public:
- Writer();
- ~Writer();
-
- void open(const char *filename);
- void open(std::FILE *file);
- void open(int fd);
- void open(std::ostream &stream);
-
- template <typename T>
- void write(const T &obj) {
- write_data(&obj, sizeof(T));
- }
-
- template <typename T>
- void write(const T *objs, std::size_t num_objs) {
- MARISA_THROW_IF((objs == NULL) && (num_objs != 0), MARISA_NULL_ERROR);
- MARISA_THROW_IF(num_objs > (MARISA_SIZE_MAX / sizeof(T)),
- MARISA_SIZE_ERROR);
- write_data(objs, sizeof(T) * num_objs);
- }
-
- void seek(std::size_t size);
-
- bool is_open() const;
-
- void clear();
- void swap(Writer &rhs);
-
- private:
- std::FILE *file_;
- int fd_;
- std::ostream *stream_;
- bool needs_fclose_;
-
- void open_(const char *filename);
- void open_(std::FILE *file);
- void open_(int fd);
- void open_(std::ostream &stream);
-
- void write_data(const void *data, std::size_t size);
-
- // Disallows copy and assignment.
- Writer(const Writer &);
- Writer &operator=(const Writer &);
-};
-
-} // namespace io
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_IO_WRITER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie.h b/contrib/python/marisa-trie/marisa/grimoire/trie.h
deleted file mode 100644
index d23852a4fd..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_H_
-#define MARISA_GRIMOIRE_TRIE_H_
-
-#include "trie/state.h"
-#include "trie/louds-trie.h"
-
-namespace marisa {
-namespace grimoire {
-
-using trie::State;
-using trie::LoudsTrie;
-
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/cache.h b/contrib/python/marisa-trie/marisa/grimoire/trie/cache.h
deleted file mode 100644
index f9da360869..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/cache.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_CACHE_H_
-#define MARISA_GRIMOIRE_TRIE_CACHE_H_
-
-#include <cfloat>
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Cache {
- public:
- Cache() : parent_(0), child_(0), union_() {
- union_.weight = FLT_MIN;
- }
- Cache(const Cache &cache)
- : parent_(cache.parent_), child_(cache.child_), union_(cache.union_) {}
-
- Cache &operator=(const Cache &cache) {
- parent_ = cache.parent_;
- child_ = cache.child_;
- union_ = cache.union_;
- return *this;
- }
-
- void set_parent(std::size_t parent) {
- MARISA_DEBUG_IF(parent > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- parent_ = (UInt32)parent;
- }
- void set_child(std::size_t child) {
- MARISA_DEBUG_IF(child > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- child_ = (UInt32)child;
- }
- void set_base(UInt8 base) {
- union_.link = (union_.link & ~0xFFU) | base;
- }
- void set_extra(std::size_t extra) {
- MARISA_DEBUG_IF(extra > (MARISA_UINT32_MAX >> 8), MARISA_SIZE_ERROR);
- union_.link = (UInt32)((union_.link & 0xFFU) | (extra << 8));
- }
- void set_weight(float weight) {
- union_.weight = weight;
- }
-
- std::size_t parent() const {
- return parent_;
- }
- std::size_t child() const {
- return child_;
- }
- UInt8 base() const {
- return (UInt8)(union_.link & 0xFFU);
- }
- std::size_t extra() const {
- return union_.link >> 8;
- }
- char label() const {
- return (char)base();
- }
- std::size_t link() const {
- return union_.link;
- }
- float weight() const {
- return union_.weight;
- }
-
- private:
- UInt32 parent_;
- UInt32 child_;
- union Union {
- UInt32 link;
- float weight;
- } union_;
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_CACHE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/config.h b/contrib/python/marisa-trie/marisa/grimoire/trie/config.h
deleted file mode 100644
index 9b307de3e1..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/config.h
+++ /dev/null
@@ -1,156 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_CONFIG_H_
-#define MARISA_GRIMOIRE_TRIE_CONFIG_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Config {
- public:
- Config()
- : num_tries_(MARISA_DEFAULT_NUM_TRIES),
- cache_level_(MARISA_DEFAULT_CACHE),
- tail_mode_(MARISA_DEFAULT_TAIL),
- node_order_(MARISA_DEFAULT_ORDER) {}
-
- void parse(int config_flags) {
- Config temp;
- temp.parse_(config_flags);
- swap(temp);
- }
-
- int flags() const {
- return (int)num_tries_ | tail_mode_ | node_order_;
- }
-
- std::size_t num_tries() const {
- return num_tries_;
- }
- CacheLevel cache_level() const {
- return cache_level_;
- }
- TailMode tail_mode() const {
- return tail_mode_;
- }
- NodeOrder node_order() const {
- return node_order_;
- }
-
- void clear() {
- Config().swap(*this);
- }
- void swap(Config &rhs) {
- marisa::swap(num_tries_, rhs.num_tries_);
- marisa::swap(cache_level_, rhs.cache_level_);
- marisa::swap(tail_mode_, rhs.tail_mode_);
- marisa::swap(node_order_, rhs.node_order_);
- }
-
- private:
- std::size_t num_tries_;
- CacheLevel cache_level_;
- TailMode tail_mode_;
- NodeOrder node_order_;
-
- void parse_(int config_flags) {
- MARISA_THROW_IF((config_flags & ~MARISA_CONFIG_MASK) != 0,
- MARISA_CODE_ERROR);
-
- parse_num_tries(config_flags);
- parse_cache_level(config_flags);
- parse_tail_mode(config_flags);
- parse_node_order(config_flags);
- }
-
- void parse_num_tries(int config_flags) {
- const int num_tries = config_flags & MARISA_NUM_TRIES_MASK;
- if (num_tries != 0) {
- num_tries_ = num_tries;
- }
- }
-
- void parse_cache_level(int config_flags) {
- switch (config_flags & MARISA_CACHE_LEVEL_MASK) {
- case 0: {
- cache_level_ = MARISA_DEFAULT_CACHE;
- break;
- }
- case MARISA_HUGE_CACHE: {
- cache_level_ = MARISA_HUGE_CACHE;
- break;
- }
- case MARISA_LARGE_CACHE: {
- cache_level_ = MARISA_LARGE_CACHE;
- break;
- }
- case MARISA_NORMAL_CACHE: {
- cache_level_ = MARISA_NORMAL_CACHE;
- break;
- }
- case MARISA_SMALL_CACHE: {
- cache_level_ = MARISA_SMALL_CACHE;
- break;
- }
- case MARISA_TINY_CACHE: {
- cache_level_ = MARISA_TINY_CACHE;
- break;
- }
- default: {
- MARISA_THROW(MARISA_CODE_ERROR, "undefined cache level");
- }
- }
- }
-
- void parse_tail_mode(int config_flags) {
- switch (config_flags & MARISA_TAIL_MODE_MASK) {
- case 0: {
- tail_mode_ = MARISA_DEFAULT_TAIL;
- break;
- }
- case MARISA_TEXT_TAIL: {
- tail_mode_ = MARISA_TEXT_TAIL;
- break;
- }
- case MARISA_BINARY_TAIL: {
- tail_mode_ = MARISA_BINARY_TAIL;
- break;
- }
- default: {
- MARISA_THROW(MARISA_CODE_ERROR, "undefined tail mode");
- }
- }
- }
-
- void parse_node_order(int config_flags) {
- switch (config_flags & MARISA_NODE_ORDER_MASK) {
- case 0: {
- node_order_ = MARISA_DEFAULT_ORDER;
- break;
- }
- case MARISA_LABEL_ORDER: {
- node_order_ = MARISA_LABEL_ORDER;
- break;
- }
- case MARISA_WEIGHT_ORDER: {
- node_order_ = MARISA_WEIGHT_ORDER;
- break;
- }
- default: {
- MARISA_THROW(MARISA_CODE_ERROR, "undefined node order");
- }
- }
- }
-
- // Disallows copy and assignment.
- Config(const Config &);
- Config &operator=(const Config &);
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_CONFIG_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/entry.h b/contrib/python/marisa-trie/marisa/grimoire/trie/entry.h
deleted file mode 100644
index 834ab95e1e..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/entry.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_ENTRY_H_
-#define MARISA_GRIMOIRE_TRIE_ENTRY_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Entry {
- public:
- Entry()
- : ptr_(reinterpret_cast<const char *>(-1)), length_(0), id_(0) {}
- Entry(const Entry &entry)
- : ptr_(entry.ptr_), length_(entry.length_), id_(entry.id_) {}
-
- Entry &operator=(const Entry &entry) {
- ptr_ = entry.ptr_;
- length_ = entry.length_;
- id_ = entry.id_;
- return *this;
- }
-
- char operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
- return *(ptr_ - i);
- }
-
- void set_str(const char *ptr, std::size_t length) {
- MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- ptr_ = ptr + length - 1;
- length_ = (UInt32)length;
- }
- void set_id(std::size_t id) {
- MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- id_ = (UInt32)id;
- }
-
- const char *ptr() const {
- return ptr_ - length_ + 1;
- }
- std::size_t length() const {
- return length_;
- }
- std::size_t id() const {
- return id_;
- }
-
- class StringComparer {
- public:
- bool operator()(const Entry &lhs, const Entry &rhs) const {
- for (std::size_t i = 0; i < lhs.length(); ++i) {
- if (i == rhs.length()) {
- return true;
- }
- if (lhs[i] != rhs[i]) {
- return (UInt8)lhs[i] > (UInt8)rhs[i];
- }
- }
- return lhs.length() > rhs.length();
- }
- };
-
- class IDComparer {
- public:
- bool operator()(const Entry &lhs, const Entry &rhs) const {
- return lhs.id_ < rhs.id_;
- }
- };
-
- private:
- const char *ptr_;
- UInt32 length_;
- UInt32 id_;
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_ENTRY_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/header.h b/contrib/python/marisa-trie/marisa/grimoire/trie/header.h
deleted file mode 100644
index 04839f67e1..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/header.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_HEADER_H_
-#define MARISA_GRIMOIRE_TRIE_HEADER_H_
-
-#include "../io.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Header {
- public:
- enum {
- HEADER_SIZE = 16
- };
-
- Header() {}
-
- void map(Mapper &mapper) {
- const char *ptr;
- mapper.map(&ptr, HEADER_SIZE);
- MARISA_THROW_IF(!test_header(ptr), MARISA_FORMAT_ERROR);
- }
- void read(Reader &reader) {
- char buf[HEADER_SIZE];
- reader.read(buf, HEADER_SIZE);
- MARISA_THROW_IF(!test_header(buf), MARISA_FORMAT_ERROR);
- }
- void write(Writer &writer) const {
- writer.write(get_header(), HEADER_SIZE);
- }
-
- std::size_t io_size() const {
- return HEADER_SIZE;
- }
-
- private:
-
- static const char *get_header() {
- static const char buf[HEADER_SIZE] = "We love Marisa.";
- return buf;
- }
-
- static bool test_header(const char *ptr) {
- for (std::size_t i = 0; i < HEADER_SIZE; ++i) {
- if (ptr[i] != get_header()[i]) {
- return false;
- }
- }
- return true;
- }
-
- // Disallows copy and assignment.
- Header(const Header &);
- Header &operator=(const Header &);
-};
-
-} // namespace trie
-} // namespace marisa
-} // namespace grimoire
-
-#endif // MARISA_GRIMOIRE_TRIE_HEADER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/history.h b/contrib/python/marisa-trie/marisa/grimoire/trie/history.h
deleted file mode 100644
index 9a3d272260..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/history.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_STATE_HISTORY_H_
-#define MARISA_GRIMOIRE_TRIE_STATE_HISTORY_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class History {
- public:
- History()
- : node_id_(0), louds_pos_(0), key_pos_(0),
- link_id_(MARISA_INVALID_LINK_ID), key_id_(MARISA_INVALID_KEY_ID) {}
-
- void set_node_id(std::size_t node_id) {
- MARISA_DEBUG_IF(node_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- node_id_ = (UInt32)node_id;
- }
- void set_louds_pos(std::size_t louds_pos) {
- MARISA_DEBUG_IF(louds_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- louds_pos_ = (UInt32)louds_pos;
- }
- void set_key_pos(std::size_t key_pos) {
- MARISA_DEBUG_IF(key_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- key_pos_ = (UInt32)key_pos;
- }
- void set_link_id(std::size_t link_id) {
- MARISA_DEBUG_IF(link_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- link_id_ = (UInt32)link_id;
- }
- void set_key_id(std::size_t key_id) {
- MARISA_DEBUG_IF(key_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- key_id_ = (UInt32)key_id;
- }
-
- std::size_t node_id() const {
- return node_id_;
- }
- std::size_t louds_pos() const {
- return louds_pos_;
- }
- std::size_t key_pos() const {
- return key_pos_;
- }
- std::size_t link_id() const {
- return link_id_;
- }
- std::size_t key_id() const {
- return key_id_;
- }
-
- private:
- UInt32 node_id_;
- UInt32 louds_pos_;
- UInt32 key_pos_;
- UInt32 link_id_;
- UInt32 key_id_;
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_STATE_HISTORY_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/key.h b/contrib/python/marisa-trie/marisa/grimoire/trie/key.h
deleted file mode 100644
index c09ea86cf8..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/key.h
+++ /dev/null
@@ -1,227 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_KEY_H_
-#define MARISA_GRIMOIRE_TRIE_KEY_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Key {
- public:
- Key() : ptr_(NULL), length_(0), union_(), id_(0) {
- union_.terminal = 0;
- }
- Key(const Key &entry)
- : ptr_(entry.ptr_), length_(entry.length_),
- union_(entry.union_), id_(entry.id_) {}
-
- Key &operator=(const Key &entry) {
- ptr_ = entry.ptr_;
- length_ = entry.length_;
- union_ = entry.union_;
- id_ = entry.id_;
- return *this;
- }
-
- char operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
- return ptr_[i];
- }
-
- void substr(std::size_t pos, std::size_t length) {
- MARISA_DEBUG_IF(pos > length_, MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(length > length_, MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(pos > (length_ - length), MARISA_BOUND_ERROR);
- ptr_ += pos;
- length_ = (UInt32)length;
- }
-
- void set_str(const char *ptr, std::size_t length) {
- MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- ptr_ = ptr;
- length_ = (UInt32)length;
- }
- void set_weight(float weight) {
- union_.weight = weight;
- }
- void set_terminal(std::size_t terminal) {
- MARISA_DEBUG_IF(terminal > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- union_.terminal = (UInt32)terminal;
- }
- void set_id(std::size_t id) {
- MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- id_ = (UInt32)id;
- }
-
- const char *ptr() const {
- return ptr_;
- }
- std::size_t length() const {
- return length_;
- }
- float weight() const {
- return union_.weight;
- }
- std::size_t terminal() const {
- return union_.terminal;
- }
- std::size_t id() const {
- return id_;
- }
-
- private:
- const char *ptr_;
- UInt32 length_;
- union Union {
- float weight;
- UInt32 terminal;
- } union_;
- UInt32 id_;
-};
-
-inline bool operator==(const Key &lhs, const Key &rhs) {
- if (lhs.length() != rhs.length()) {
- return false;
- }
- for (std::size_t i = 0; i < lhs.length(); ++i) {
- if (lhs[i] != rhs[i]) {
- return false;
- }
- }
- return true;
-}
-
-inline bool operator!=(const Key &lhs, const Key &rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator<(const Key &lhs, const Key &rhs) {
- for (std::size_t i = 0; i < lhs.length(); ++i) {
- if (i == rhs.length()) {
- return false;
- }
- if (lhs[i] != rhs[i]) {
- return (UInt8)lhs[i] < (UInt8)rhs[i];
- }
- }
- return lhs.length() < rhs.length();
-}
-
-inline bool operator>(const Key &lhs, const Key &rhs) {
- return rhs < lhs;
-}
-
-class ReverseKey {
- public:
- ReverseKey() : ptr_(NULL), length_(0), union_(), id_(0) {
- union_.terminal = 0;
- }
- ReverseKey(const ReverseKey &entry)
- : ptr_(entry.ptr_), length_(entry.length_),
- union_(entry.union_), id_(entry.id_) {}
-
- ReverseKey &operator=(const ReverseKey &entry) {
- ptr_ = entry.ptr_;
- length_ = entry.length_;
- union_ = entry.union_;
- id_ = entry.id_;
- return *this;
- }
-
- char operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
- return *(ptr_ - i - 1);
- }
-
- void substr(std::size_t pos, std::size_t length) {
- MARISA_DEBUG_IF(pos > length_, MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(length > length_, MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(pos > (length_ - length), MARISA_BOUND_ERROR);
- ptr_ -= pos;
- length_ = (UInt32)length;
- }
-
- void set_str(const char *ptr, std::size_t length) {
- MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- ptr_ = ptr + length;
- length_ = (UInt32)length;
- }
- void set_weight(float weight) {
- union_.weight = weight;
- }
- void set_terminal(std::size_t terminal) {
- MARISA_DEBUG_IF(terminal > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- union_.terminal = (UInt32)terminal;
- }
- void set_id(std::size_t id) {
- MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- id_ = (UInt32)id;
- }
-
- const char *ptr() const {
- return ptr_ - length_;
- }
- std::size_t length() const {
- return length_;
- }
- float weight() const {
- return union_.weight;
- }
- std::size_t terminal() const {
- return union_.terminal;
- }
- std::size_t id() const {
- return id_;
- }
-
- private:
- const char *ptr_;
- UInt32 length_;
- union Union {
- float weight;
- UInt32 terminal;
- } union_;
- UInt32 id_;
-};
-
-inline bool operator==(const ReverseKey &lhs, const ReverseKey &rhs) {
- if (lhs.length() != rhs.length()) {
- return false;
- }
- for (std::size_t i = 0; i < lhs.length(); ++i) {
- if (lhs[i] != rhs[i]) {
- return false;
- }
- }
- return true;
-}
-
-inline bool operator!=(const ReverseKey &lhs, const ReverseKey &rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator<(const ReverseKey &lhs, const ReverseKey &rhs) {
- for (std::size_t i = 0; i < lhs.length(); ++i) {
- if (i == rhs.length()) {
- return false;
- }
- if (lhs[i] != rhs[i]) {
- return (UInt8)lhs[i] < (UInt8)rhs[i];
- }
- }
- return lhs.length() < rhs.length();
-}
-
-inline bool operator>(const ReverseKey &lhs, const ReverseKey &rhs) {
- return rhs < lhs;
-}
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_KEY_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc b/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc
deleted file mode 100644
index ed168539bc..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc
+++ /dev/null
@@ -1,877 +0,0 @@
-#include <algorithm>
-#include <functional>
-#include <queue>
-
-#include "../algorithm.h"
-#include "header.h"
-#include "range.h"
-#include "state.h"
-#include "louds-trie.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-LoudsTrie::LoudsTrie()
- : louds_(), terminal_flags_(), link_flags_(), bases_(), extras_(),
- tail_(), next_trie_(), cache_(), cache_mask_(0), num_l1_nodes_(0),
- config_(), mapper_() {}
-
-LoudsTrie::~LoudsTrie() {}
-
-void LoudsTrie::build(Keyset &keyset, int flags) {
- Config config;
- config.parse(flags);
-
- LoudsTrie temp;
- temp.build_(keyset, config);
- swap(temp);
-}
-
-void LoudsTrie::map(Mapper &mapper) {
- Header().map(mapper);
-
- LoudsTrie temp;
- temp.map_(mapper);
- temp.mapper_.swap(mapper);
- swap(temp);
-}
-
-void LoudsTrie::read(Reader &reader) {
- Header().read(reader);
-
- LoudsTrie temp;
- temp.read_(reader);
- swap(temp);
-}
-
-void LoudsTrie::write(Writer &writer) const {
- Header().write(writer);
-
- write_(writer);
-}
-
-bool LoudsTrie::lookup(Agent &agent) const {
- MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
-
- State &state = agent.state();
- state.lookup_init();
- while (state.query_pos() < agent.query().length()) {
- if (!find_child(agent)) {
- return false;
- }
- }
- if (!terminal_flags_[state.node_id()]) {
- return false;
- }
- agent.set_key(agent.query().ptr(), agent.query().length());
- agent.set_key(terminal_flags_.rank1(state.node_id()));
- return true;
-}
-
-void LoudsTrie::reverse_lookup(Agent &agent) const {
- MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
- MARISA_THROW_IF(agent.query().id() >= size(), MARISA_BOUND_ERROR);
-
- State &state = agent.state();
- state.reverse_lookup_init();
-
- state.set_node_id(terminal_flags_.select1(agent.query().id()));
- if (state.node_id() == 0) {
- agent.set_key(state.key_buf().begin(), state.key_buf().size());
- agent.set_key(agent.query().id());
- return;
- }
- for ( ; ; ) {
- if (link_flags_[state.node_id()]) {
- const std::size_t prev_key_pos = state.key_buf().size();
- restore(agent, get_link(state.node_id()));
- std::reverse(state.key_buf().begin() + prev_key_pos,
- state.key_buf().end());
- } else {
- state.key_buf().push_back((char)bases_[state.node_id()]);
- }
-
- if (state.node_id() <= num_l1_nodes_) {
- std::reverse(state.key_buf().begin(), state.key_buf().end());
- agent.set_key(state.key_buf().begin(), state.key_buf().size());
- agent.set_key(agent.query().id());
- return;
- }
- state.set_node_id(louds_.select1(state.node_id()) - state.node_id() - 1);
- }
-}
-
-bool LoudsTrie::common_prefix_search(Agent &agent) const {
- MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
-
- State &state = agent.state();
- if (state.status_code() == MARISA_END_OF_COMMON_PREFIX_SEARCH) {
- return false;
- }
-
- if (state.status_code() != MARISA_READY_TO_COMMON_PREFIX_SEARCH) {
- state.common_prefix_search_init();
- if (terminal_flags_[state.node_id()]) {
- agent.set_key(agent.query().ptr(), state.query_pos());
- agent.set_key(terminal_flags_.rank1(state.node_id()));
- return true;
- }
- }
-
- while (state.query_pos() < agent.query().length()) {
- if (!find_child(agent)) {
- state.set_status_code(MARISA_END_OF_COMMON_PREFIX_SEARCH);
- return false;
- } else if (terminal_flags_[state.node_id()]) {
- agent.set_key(agent.query().ptr(), state.query_pos());
- agent.set_key(terminal_flags_.rank1(state.node_id()));
- return true;
- }
- }
- state.set_status_code(MARISA_END_OF_COMMON_PREFIX_SEARCH);
- return false;
-}
-
-bool LoudsTrie::predictive_search(Agent &agent) const {
- MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
-
- State &state = agent.state();
- if (state.status_code() == MARISA_END_OF_PREDICTIVE_SEARCH) {
- return false;
- }
-
- if (state.status_code() != MARISA_READY_TO_PREDICTIVE_SEARCH) {
- state.predictive_search_init();
- while (state.query_pos() < agent.query().length()) {
- if (!predictive_find_child(agent)) {
- state.set_status_code(MARISA_END_OF_PREDICTIVE_SEARCH);
- return false;
- }
- }
-
- History history;
- history.set_node_id(state.node_id());
- history.set_key_pos(state.key_buf().size());
- state.history().push_back(history);
- state.set_history_pos(1);
-
- if (terminal_flags_[state.node_id()]) {
- agent.set_key(state.key_buf().begin(), state.key_buf().size());
- agent.set_key(terminal_flags_.rank1(state.node_id()));
- return true;
- }
- }
-
- for ( ; ; ) {
- if (state.history_pos() == state.history().size()) {
- const History &current = state.history().back();
- History next;
- next.set_louds_pos(louds_.select0(current.node_id()) + 1);
- next.set_node_id(next.louds_pos() - current.node_id() - 1);
- state.history().push_back(next);
- }
-
- History &next = state.history()[state.history_pos()];
- const bool link_flag = louds_[next.louds_pos()];
- next.set_louds_pos(next.louds_pos() + 1);
- if (link_flag) {
- state.set_history_pos(state.history_pos() + 1);
- if (link_flags_[next.node_id()]) {
- next.set_link_id(update_link_id(next.link_id(), next.node_id()));
- restore(agent, get_link(next.node_id(), next.link_id()));
- } else {
- state.key_buf().push_back((char)bases_[next.node_id()]);
- }
- next.set_key_pos(state.key_buf().size());
-
- if (terminal_flags_[next.node_id()]) {
- if (next.key_id() == MARISA_INVALID_KEY_ID) {
- next.set_key_id(terminal_flags_.rank1(next.node_id()));
- } else {
- next.set_key_id(next.key_id() + 1);
- }
- agent.set_key(state.key_buf().begin(), state.key_buf().size());
- agent.set_key(next.key_id());
- return true;
- }
- } else if (state.history_pos() != 1) {
- History &current = state.history()[state.history_pos() - 1];
- current.set_node_id(current.node_id() + 1);
- const History &prev =
- state.history()[state.history_pos() - 2];
- state.key_buf().resize(prev.key_pos());
- state.set_history_pos(state.history_pos() - 1);
- } else {
- state.set_status_code(MARISA_END_OF_PREDICTIVE_SEARCH);
- return false;
- }
- }
-}
-
-std::size_t LoudsTrie::total_size() const {
- return louds_.total_size() + terminal_flags_.total_size()
- + link_flags_.total_size() + bases_.total_size()
- + extras_.total_size() + tail_.total_size()
- + ((next_trie_.get() != NULL) ? next_trie_->total_size() : 0)
- + cache_.total_size();
-}
-
-std::size_t LoudsTrie::io_size() const {
- return Header().io_size() + louds_.io_size()
- + terminal_flags_.io_size() + link_flags_.io_size()
- + bases_.io_size() + extras_.io_size() + tail_.io_size()
- + ((next_trie_.get() != NULL) ?
- (next_trie_->io_size() - Header().io_size()) : 0)
- + cache_.io_size() + (sizeof(UInt32) * 2);
-}
-
-void LoudsTrie::clear() {
- LoudsTrie().swap(*this);
-}
-
-void LoudsTrie::swap(LoudsTrie &rhs) {
- louds_.swap(rhs.louds_);
- terminal_flags_.swap(rhs.terminal_flags_);
- link_flags_.swap(rhs.link_flags_);
- bases_.swap(rhs.bases_);
- extras_.swap(rhs.extras_);
- tail_.swap(rhs.tail_);
- next_trie_.swap(rhs.next_trie_);
- cache_.swap(rhs.cache_);
- marisa::swap(cache_mask_, rhs.cache_mask_);
- marisa::swap(num_l1_nodes_, rhs.num_l1_nodes_);
- config_.swap(rhs.config_);
- mapper_.swap(rhs.mapper_);
-}
-
-void LoudsTrie::build_(Keyset &keyset, const Config &config) {
- Vector<Key> keys;
- keys.resize(keyset.size());
- for (std::size_t i = 0; i < keyset.size(); ++i) {
- keys[i].set_str(keyset[i].ptr(), keyset[i].length());
- keys[i].set_weight(keyset[i].weight());
- }
-
- Vector<UInt32> terminals;
- build_trie(keys, &terminals, config, 1);
-
- typedef std::pair<UInt32, UInt32> TerminalIdPair;
-
- Vector<TerminalIdPair> pairs;
- pairs.resize(terminals.size());
- for (std::size_t i = 0; i < pairs.size(); ++i) {
- pairs[i].first = terminals[i];
- pairs[i].second = (UInt32)i;
- }
- terminals.clear();
- std::sort(pairs.begin(), pairs.end());
-
- std::size_t node_id = 0;
- for (std::size_t i = 0; i < pairs.size(); ++i) {
- while (node_id < pairs[i].first) {
- terminal_flags_.push_back(false);
- ++node_id;
- }
- if (node_id == pairs[i].first) {
- terminal_flags_.push_back(true);
- ++node_id;
- }
- }
- while (node_id < bases_.size()) {
- terminal_flags_.push_back(false);
- ++node_id;
- }
- terminal_flags_.push_back(false);
- terminal_flags_.build(false, true);
-
- for (std::size_t i = 0; i < keyset.size(); ++i) {
- keyset[pairs[i].second].set_id(terminal_flags_.rank1(pairs[i].first));
- }
-}
-
-template <typename T>
-void LoudsTrie::build_trie(Vector<T> &keys,
- Vector<UInt32> *terminals, const Config &config, std::size_t trie_id) {
- build_current_trie(keys, terminals, config, trie_id);
-
- Vector<UInt32> next_terminals;
- if (!keys.empty()) {
- build_next_trie(keys, &next_terminals, config, trie_id);
- }
-
- if (next_trie_.get() != NULL) {
- config_.parse(static_cast<int>((next_trie_->num_tries() + 1)) |
- next_trie_->tail_mode() | next_trie_->node_order());
- } else {
- config_.parse(1 | tail_.mode() | config.node_order() |
- config.cache_level());
- }
-
- link_flags_.build(false, false);
- std::size_t node_id = 0;
- for (std::size_t i = 0; i < next_terminals.size(); ++i) {
- while (!link_flags_[node_id]) {
- ++node_id;
- }
- bases_[node_id] = (UInt8)(next_terminals[i] % 256);
- next_terminals[i] /= 256;
- ++node_id;
- }
- extras_.build(next_terminals);
- fill_cache();
-}
-
-template <typename T>
-void LoudsTrie::build_current_trie(Vector<T> &keys,
- Vector<UInt32> *terminals, const Config &config,
- std::size_t trie_id) try {
- for (std::size_t i = 0; i < keys.size(); ++i) {
- keys[i].set_id(i);
- }
- const std::size_t num_keys = Algorithm().sort(keys.begin(), keys.end());
- reserve_cache(config, trie_id, num_keys);
-
- louds_.push_back(true);
- louds_.push_back(false);
- bases_.push_back('\0');
- link_flags_.push_back(false);
-
- Vector<T> next_keys;
- std::queue<Range> queue;
- Vector<WeightedRange> w_ranges;
-
- queue.push(make_range(0, keys.size(), 0));
- while (!queue.empty()) {
- const std::size_t node_id = link_flags_.size() - queue.size();
-
- Range range = queue.front();
- queue.pop();
-
- while ((range.begin() < range.end()) &&
- (keys[range.begin()].length() == range.key_pos())) {
- keys[range.begin()].set_terminal(node_id);
- range.set_begin(range.begin() + 1);
- }
-
- if (range.begin() == range.end()) {
- louds_.push_back(false);
- continue;
- }
-
- w_ranges.clear();
- double weight = keys[range.begin()].weight();
- for (std::size_t i = range.begin() + 1; i < range.end(); ++i) {
- if (keys[i - 1][range.key_pos()] != keys[i][range.key_pos()]) {
- w_ranges.push_back(make_weighted_range(
- range.begin(), i, range.key_pos(), (float)weight));
- range.set_begin(i);
- weight = 0.0;
- }
- weight += keys[i].weight();
- }
- w_ranges.push_back(make_weighted_range(
- range.begin(), range.end(), range.key_pos(), (float)weight));
- if (config.node_order() == MARISA_WEIGHT_ORDER) {
- std::stable_sort(w_ranges.begin(), w_ranges.end(),
- std::greater<WeightedRange>());
- }
-
- if (node_id == 0) {
- num_l1_nodes_ = w_ranges.size();
- }
-
- for (std::size_t i = 0; i < w_ranges.size(); ++i) {
- WeightedRange &w_range = w_ranges[i];
- std::size_t key_pos = w_range.key_pos() + 1;
- while (key_pos < keys[w_range.begin()].length()) {
- std::size_t j;
- for (j = w_range.begin() + 1; j < w_range.end(); ++j) {
- if (keys[j - 1][key_pos] != keys[j][key_pos]) {
- break;
- }
- }
- if (j < w_range.end()) {
- break;
- }
- ++key_pos;
- }
- cache<T>(node_id, bases_.size(), w_range.weight(),
- keys[w_range.begin()][w_range.key_pos()]);
-
- if (key_pos == w_range.key_pos() + 1) {
- bases_.push_back(keys[w_range.begin()][w_range.key_pos()]);
- link_flags_.push_back(false);
- } else {
- bases_.push_back('\0');
- link_flags_.push_back(true);
- T next_key;
- next_key.set_str(keys[w_range.begin()].ptr(),
- keys[w_range.begin()].length());
- next_key.substr(w_range.key_pos(), key_pos - w_range.key_pos());
- next_key.set_weight(w_range.weight());
- next_keys.push_back(next_key);
- }
- w_range.set_key_pos(key_pos);
- queue.push(w_range.range());
- louds_.push_back(true);
- }
- louds_.push_back(false);
- }
-
- louds_.push_back(false);
- louds_.build(trie_id == 1, true);
- bases_.shrink();
-
- build_terminals(keys, terminals);
- keys.swap(next_keys);
-} catch (const std::bad_alloc &) {
- MARISA_THROW(MARISA_MEMORY_ERROR, "std::bad_alloc");
-}
-
-template <>
-void LoudsTrie::build_next_trie(Vector<Key> &keys,
- Vector<UInt32> *terminals, const Config &config, std::size_t trie_id) {
- if (trie_id == config.num_tries()) {
- Vector<Entry> entries;
- entries.resize(keys.size());
- for (std::size_t i = 0; i < keys.size(); ++i) {
- entries[i].set_str(keys[i].ptr(), keys[i].length());
- }
- tail_.build(entries, terminals, config.tail_mode());
- return;
- }
- Vector<ReverseKey> reverse_keys;
- reverse_keys.resize(keys.size());
- for (std::size_t i = 0; i < keys.size(); ++i) {
- reverse_keys[i].set_str(keys[i].ptr(), keys[i].length());
- reverse_keys[i].set_weight(keys[i].weight());
- }
- keys.clear();
- next_trie_.reset(new (std::nothrow) LoudsTrie);
- MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
- next_trie_->build_trie(reverse_keys, terminals, config, trie_id + 1);
-}
-
-template <>
-void LoudsTrie::build_next_trie(Vector<ReverseKey> &keys,
- Vector<UInt32> *terminals, const Config &config, std::size_t trie_id) {
- if (trie_id == config.num_tries()) {
- Vector<Entry> entries;
- entries.resize(keys.size());
- for (std::size_t i = 0; i < keys.size(); ++i) {
- entries[i].set_str(keys[i].ptr(), keys[i].length());
- }
- tail_.build(entries, terminals, config.tail_mode());
- return;
- }
- next_trie_.reset(new (std::nothrow) LoudsTrie);
- MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
- next_trie_->build_trie(keys, terminals, config, trie_id + 1);
-}
-
-template <typename T>
-void LoudsTrie::build_terminals(const Vector<T> &keys,
- Vector<UInt32> *terminals) const {
- Vector<UInt32> temp;
- temp.resize(keys.size());
- for (std::size_t i = 0; i < keys.size(); ++i) {
- temp[keys[i].id()] = (UInt32)keys[i].terminal();
- }
- terminals->swap(temp);
-}
-
-template <>
-void LoudsTrie::cache<Key>(std::size_t parent, std::size_t child,
- float weight, char label) {
- MARISA_DEBUG_IF(parent >= child, MARISA_RANGE_ERROR);
-
- const std::size_t cache_id = get_cache_id(parent, label);
- if (weight > cache_[cache_id].weight()) {
- cache_[cache_id].set_parent(parent);
- cache_[cache_id].set_child(child);
- cache_[cache_id].set_weight(weight);
- }
-}
-
-void LoudsTrie::reserve_cache(const Config &config, std::size_t trie_id,
- std::size_t num_keys) {
- std::size_t cache_size = (trie_id == 1) ? 256 : 1;
- while (cache_size < (num_keys / config.cache_level())) {
- cache_size *= 2;
- }
- cache_.resize(cache_size);
- cache_mask_ = cache_size - 1;
-}
-
-template <>
-void LoudsTrie::cache<ReverseKey>(std::size_t parent, std::size_t child,
- float weight, char) {
- MARISA_DEBUG_IF(parent >= child, MARISA_RANGE_ERROR);
-
- const std::size_t cache_id = get_cache_id(child);
- if (weight > cache_[cache_id].weight()) {
- cache_[cache_id].set_parent(parent);
- cache_[cache_id].set_child(child);
- cache_[cache_id].set_weight(weight);
- }
-}
-
-void LoudsTrie::fill_cache() {
- for (std::size_t i = 0; i < cache_.size(); ++i) {
- const std::size_t node_id = cache_[i].child();
- if (node_id != 0) {
- cache_[i].set_base(bases_[node_id]);
- cache_[i].set_extra(!link_flags_[node_id] ?
- MARISA_INVALID_EXTRA : extras_[link_flags_.rank1(node_id)]);
- } else {
- cache_[i].set_parent(MARISA_UINT32_MAX);
- cache_[i].set_child(MARISA_UINT32_MAX);
- }
- }
-}
-
-void LoudsTrie::map_(Mapper &mapper) {
- louds_.map(mapper);
- terminal_flags_.map(mapper);
- link_flags_.map(mapper);
- bases_.map(mapper);
- extras_.map(mapper);
- tail_.map(mapper);
- if ((link_flags_.num_1s() != 0) && tail_.empty()) {
- next_trie_.reset(new (std::nothrow) LoudsTrie);
- MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
- next_trie_->map_(mapper);
- }
- cache_.map(mapper);
- cache_mask_ = cache_.size() - 1;
- {
- UInt32 temp_num_l1_nodes;
- mapper.map(&temp_num_l1_nodes);
- num_l1_nodes_ = temp_num_l1_nodes;
- }
- {
- UInt32 temp_config_flags;
- mapper.map(&temp_config_flags);
- config_.parse((int)temp_config_flags);
- }
-}
-
-void LoudsTrie::read_(Reader &reader) {
- louds_.read(reader);
- terminal_flags_.read(reader);
- link_flags_.read(reader);
- bases_.read(reader);
- extras_.read(reader);
- tail_.read(reader);
- if ((link_flags_.num_1s() != 0) && tail_.empty()) {
- next_trie_.reset(new (std::nothrow) LoudsTrie);
- MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
- next_trie_->read_(reader);
- }
- cache_.read(reader);
- cache_mask_ = cache_.size() - 1;
- {
- UInt32 temp_num_l1_nodes;
- reader.read(&temp_num_l1_nodes);
- num_l1_nodes_ = temp_num_l1_nodes;
- }
- {
- UInt32 temp_config_flags;
- reader.read(&temp_config_flags);
- config_.parse((int)temp_config_flags);
- }
-}
-
-void LoudsTrie::write_(Writer &writer) const {
- louds_.write(writer);
- terminal_flags_.write(writer);
- link_flags_.write(writer);
- bases_.write(writer);
- extras_.write(writer);
- tail_.write(writer);
- if (next_trie_.get() != NULL) {
- next_trie_->write_(writer);
- }
- cache_.write(writer);
- writer.write((UInt32)num_l1_nodes_);
- writer.write((UInt32)config_.flags());
-}
-
-bool LoudsTrie::find_child(Agent &agent) const {
- MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
- MARISA_BOUND_ERROR);
-
- State &state = agent.state();
- const std::size_t cache_id = get_cache_id(state.node_id(),
- agent.query()[state.query_pos()]);
- if (state.node_id() == cache_[cache_id].parent()) {
- if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
- if (!match(agent, cache_[cache_id].link())) {
- return false;
- }
- } else {
- state.set_query_pos(state.query_pos() + 1);
- }
- state.set_node_id(cache_[cache_id].child());
- return true;
- }
-
- std::size_t louds_pos = louds_.select0(state.node_id()) + 1;
- if (!louds_[louds_pos]) {
- return false;
- }
- state.set_node_id(louds_pos - state.node_id() - 1);
- std::size_t link_id = MARISA_INVALID_LINK_ID;
- do {
- if (link_flags_[state.node_id()]) {
- link_id = update_link_id(link_id, state.node_id());
- const std::size_t prev_query_pos = state.query_pos();
- if (match(agent, get_link(state.node_id(), link_id))) {
- return true;
- } else if (state.query_pos() != prev_query_pos) {
- return false;
- }
- } else if (bases_[state.node_id()] ==
- (UInt8)agent.query()[state.query_pos()]) {
- state.set_query_pos(state.query_pos() + 1);
- return true;
- }
- state.set_node_id(state.node_id() + 1);
- ++louds_pos;
- } while (louds_[louds_pos]);
- return false;
-}
-
-bool LoudsTrie::predictive_find_child(Agent &agent) const {
- MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
- MARISA_BOUND_ERROR);
-
- State &state = agent.state();
- const std::size_t cache_id = get_cache_id(state.node_id(),
- agent.query()[state.query_pos()]);
- if (state.node_id() == cache_[cache_id].parent()) {
- if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
- if (!prefix_match(agent, cache_[cache_id].link())) {
- return false;
- }
- } else {
- state.key_buf().push_back(cache_[cache_id].label());
- state.set_query_pos(state.query_pos() + 1);
- }
- state.set_node_id(cache_[cache_id].child());
- return true;
- }
-
- std::size_t louds_pos = louds_.select0(state.node_id()) + 1;
- if (!louds_[louds_pos]) {
- return false;
- }
- state.set_node_id(louds_pos - state.node_id() - 1);
- std::size_t link_id = MARISA_INVALID_LINK_ID;
- do {
- if (link_flags_[state.node_id()]) {
- link_id = update_link_id(link_id, state.node_id());
- const std::size_t prev_query_pos = state.query_pos();
- if (prefix_match(agent, get_link(state.node_id(), link_id))) {
- return true;
- } else if (state.query_pos() != prev_query_pos) {
- return false;
- }
- } else if (bases_[state.node_id()] ==
- (UInt8)agent.query()[state.query_pos()]) {
- state.key_buf().push_back((char)bases_[state.node_id()]);
- state.set_query_pos(state.query_pos() + 1);
- return true;
- }
- state.set_node_id(state.node_id() + 1);
- ++louds_pos;
- } while (louds_[louds_pos]);
- return false;
-}
-
-void LoudsTrie::restore(Agent &agent, std::size_t link) const {
- if (next_trie_.get() != NULL) {
- next_trie_->restore_(agent, link);
- } else {
- tail_.restore(agent, link);
- }
-}
-
-bool LoudsTrie::match(Agent &agent, std::size_t link) const {
- if (next_trie_.get() != NULL) {
- return next_trie_->match_(agent, link);
- } else {
- return tail_.match(agent, link);
- }
-}
-
-bool LoudsTrie::prefix_match(Agent &agent, std::size_t link) const {
- if (next_trie_.get() != NULL) {
- return next_trie_->prefix_match_(agent, link);
- } else {
- return tail_.prefix_match(agent, link);
- }
-}
-
-void LoudsTrie::restore_(Agent &agent, std::size_t node_id) const {
- MARISA_DEBUG_IF(node_id == 0, MARISA_RANGE_ERROR);
-
- State &state = agent.state();
- for ( ; ; ) {
- const std::size_t cache_id = get_cache_id(node_id);
- if (node_id == cache_[cache_id].child()) {
- if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
- restore(agent, cache_[cache_id].link());
- } else {
- state.key_buf().push_back(cache_[cache_id].label());
- }
-
- node_id = cache_[cache_id].parent();
- if (node_id == 0) {
- return;
- }
- continue;
- }
-
- if (link_flags_[node_id]) {
- restore(agent, get_link(node_id));
- } else {
- state.key_buf().push_back((char)bases_[node_id]);
- }
-
- if (node_id <= num_l1_nodes_) {
- return;
- }
- node_id = louds_.select1(node_id) - node_id - 1;
- }
-}
-
-bool LoudsTrie::match_(Agent &agent, std::size_t node_id) const {
- MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
- MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(node_id == 0, MARISA_RANGE_ERROR);
-
- State &state = agent.state();
- for ( ; ; ) {
- const std::size_t cache_id = get_cache_id(node_id);
- if (node_id == cache_[cache_id].child()) {
- if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
- if (!match(agent, cache_[cache_id].link())) {
- return false;
- }
- } else if (cache_[cache_id].label() ==
- agent.query()[state.query_pos()]) {
- state.set_query_pos(state.query_pos() + 1);
- } else {
- return false;
- }
-
- node_id = cache_[cache_id].parent();
- if (node_id == 0) {
- return true;
- } else if (state.query_pos() >= agent.query().length()) {
- return false;
- }
- continue;
- }
-
- if (link_flags_[node_id]) {
- if (next_trie_.get() != NULL) {
- if (!match(agent, get_link(node_id))) {
- return false;
- }
- } else if (!tail_.match(agent, get_link(node_id))) {
- return false;
- }
- } else if (bases_[node_id] == (UInt8)agent.query()[state.query_pos()]) {
- state.set_query_pos(state.query_pos() + 1);
- } else {
- return false;
- }
-
- if (node_id <= num_l1_nodes_) {
- return true;
- } else if (state.query_pos() >= agent.query().length()) {
- return false;
- }
- node_id = louds_.select1(node_id) - node_id - 1;
- }
-}
-
-bool LoudsTrie::prefix_match_(Agent &agent, std::size_t node_id) const {
- MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
- MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(node_id == 0, MARISA_RANGE_ERROR);
-
- State &state = agent.state();
- for ( ; ; ) {
- const std::size_t cache_id = get_cache_id(node_id);
- if (node_id == cache_[cache_id].child()) {
- if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
- if (!prefix_match(agent, cache_[cache_id].link())) {
- return false;
- }
- } else if (cache_[cache_id].label() ==
- agent.query()[state.query_pos()]) {
- state.key_buf().push_back(cache_[cache_id].label());
- state.set_query_pos(state.query_pos() + 1);
- } else {
- return false;
- }
-
- node_id = cache_[cache_id].parent();
- if (node_id == 0) {
- return true;
- }
- } else {
- if (link_flags_[node_id]) {
- if (!prefix_match(agent, get_link(node_id))) {
- return false;
- }
- } else if (bases_[node_id] == (UInt8)agent.query()[state.query_pos()]) {
- state.key_buf().push_back((char)bases_[node_id]);
- state.set_query_pos(state.query_pos() + 1);
- } else {
- return false;
- }
-
- if (node_id <= num_l1_nodes_) {
- return true;
- }
- node_id = louds_.select1(node_id) - node_id - 1;
- }
-
- if (state.query_pos() >= agent.query().length()) {
- restore_(agent, node_id);
- return true;
- }
- }
-}
-
-std::size_t LoudsTrie::get_cache_id(std::size_t node_id, char label) const {
- return (node_id ^ (node_id << 5) ^ (UInt8)label) & cache_mask_;
-}
-
-std::size_t LoudsTrie::get_cache_id(std::size_t node_id) const {
- return node_id & cache_mask_;
-}
-
-std::size_t LoudsTrie::get_link(std::size_t node_id) const {
- return bases_[node_id] | (extras_[link_flags_.rank1(node_id)] * 256);
-}
-
-std::size_t LoudsTrie::get_link(std::size_t node_id,
- std::size_t link_id) const {
- return bases_[node_id] | (extras_[link_id] * 256);
-}
-
-std::size_t LoudsTrie::update_link_id(std::size_t link_id,
- std::size_t node_id) const {
- return (link_id == MARISA_INVALID_LINK_ID) ?
- link_flags_.rank1(node_id) : (link_id + 1);
-}
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h b/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h
deleted file mode 100644
index 5a757ac8fc..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h
+++ /dev/null
@@ -1,135 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_LOUDS_TRIE_H_
-#define MARISA_GRIMOIRE_TRIE_LOUDS_TRIE_H_
-
-#include "../../keyset.h"
-#include "../../agent.h"
-#include "../vector.h"
-#include "config.h"
-#include "key.h"
-#include "tail.h"
-#include "cache.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class LoudsTrie {
- public:
- LoudsTrie();
- ~LoudsTrie();
-
- void build(Keyset &keyset, int flags);
-
- void map(Mapper &mapper);
- void read(Reader &reader);
- void write(Writer &writer) const;
-
- bool lookup(Agent &agent) const;
- void reverse_lookup(Agent &agent) const;
- bool common_prefix_search(Agent &agent) const;
- bool predictive_search(Agent &agent) const;
-
- std::size_t num_tries() const {
- return config_.num_tries();
- }
- std::size_t num_keys() const {
- return size();
- }
- std::size_t num_nodes() const {
- return (louds_.size() / 2) - 1;
- }
-
- CacheLevel cache_level() const {
- return config_.cache_level();
- }
- TailMode tail_mode() const {
- return config_.tail_mode();
- }
- NodeOrder node_order() const {
- return config_.node_order();
- }
-
- bool empty() const {
- return size() == 0;
- }
- std::size_t size() const {
- return terminal_flags_.num_1s();
- }
- std::size_t total_size() const;
- std::size_t io_size() const;
-
- void clear();
- void swap(LoudsTrie &rhs);
-
- private:
- BitVector louds_;
- BitVector terminal_flags_;
- BitVector link_flags_;
- Vector<UInt8> bases_;
- FlatVector extras_;
- Tail tail_;
- scoped_ptr<LoudsTrie> next_trie_;
- Vector<Cache> cache_;
- std::size_t cache_mask_;
- std::size_t num_l1_nodes_;
- Config config_;
- Mapper mapper_;
-
- void build_(Keyset &keyset, const Config &config);
-
- template <typename T>
- void build_trie(Vector<T> &keys,
- Vector<UInt32> *terminals, const Config &config, std::size_t trie_id);
- template <typename T>
- void build_current_trie(Vector<T> &keys,
- Vector<UInt32> *terminals, const Config &config, std::size_t trie_id);
- template <typename T>
- void build_next_trie(Vector<T> &keys,
- Vector<UInt32> *terminals, const Config &config, std::size_t trie_id);
- template <typename T>
- void build_terminals(const Vector<T> &keys,
- Vector<UInt32> *terminals) const;
-
- void reserve_cache(const Config &config, std::size_t trie_id,
- std::size_t num_keys);
- template <typename T>
- void cache(std::size_t parent, std::size_t child,
- float weight, char label);
- void fill_cache();
-
- void map_(Mapper &mapper);
- void read_(Reader &reader);
- void write_(Writer &writer) const;
-
- inline bool find_child(Agent &agent) const;
- inline bool predictive_find_child(Agent &agent) const;
-
- inline void restore(Agent &agent, std::size_t node_id) const;
- inline bool match(Agent &agent, std::size_t node_id) const;
- inline bool prefix_match(Agent &agent, std::size_t node_id) const;
-
- void restore_(Agent &agent, std::size_t node_id) const;
- bool match_(Agent &agent, std::size_t node_id) const;
- bool prefix_match_(Agent &agent, std::size_t node_id) const;
-
- inline std::size_t get_cache_id(std::size_t node_id, char label) const;
- inline std::size_t get_cache_id(std::size_t node_id) const;
-
- inline std::size_t get_link(std::size_t node_id) const;
- inline std::size_t get_link(std::size_t node_id,
- std::size_t link_id) const;
-
- inline std::size_t update_link_id(std::size_t link_id,
- std::size_t node_id) const;
-
- // Disallows copy and assignment.
- LoudsTrie(const LoudsTrie &);
- LoudsTrie &operator=(const LoudsTrie &);
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_LOUDS_TRIE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/range.h b/contrib/python/marisa-trie/marisa/grimoire/trie/range.h
deleted file mode 100644
index 4ca39a9c37..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/range.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_RANGE_H_
-#define MARISA_GRIMOIRE_TRIE_RANGE_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Range {
- public:
- Range() : begin_(0), end_(0), key_pos_(0) {}
-
- void set_begin(std::size_t begin) {
- MARISA_DEBUG_IF(begin > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- begin_ = static_cast<UInt32>(begin);
- }
- void set_end(std::size_t end) {
- MARISA_DEBUG_IF(end > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- end_ = static_cast<UInt32>(end);
- }
- void set_key_pos(std::size_t key_pos) {
- MARISA_DEBUG_IF(key_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- key_pos_ = static_cast<UInt32>(key_pos);
- }
-
- std::size_t begin() const {
- return begin_;
- }
- std::size_t end() const {
- return end_;
- }
- std::size_t key_pos() const {
- return key_pos_;
- }
-
- private:
- UInt32 begin_;
- UInt32 end_;
- UInt32 key_pos_;
-};
-
-inline Range make_range(std::size_t begin, std::size_t end,
- std::size_t key_pos) {
- Range range;
- range.set_begin(begin);
- range.set_end(end);
- range.set_key_pos(key_pos);
- return range;
-}
-
-class WeightedRange {
- public:
- WeightedRange() : range_(), weight_(0.0F) {}
-
- void set_range(const Range &range) {
- range_ = range;
- }
- void set_begin(std::size_t begin) {
- range_.set_begin(begin);
- }
- void set_end(std::size_t end) {
- range_.set_end(end);
- }
- void set_key_pos(std::size_t key_pos) {
- range_.set_key_pos(key_pos);
- }
- void set_weight(float weight) {
- weight_ = weight;
- }
-
- const Range &range() const {
- return range_;
- }
- std::size_t begin() const {
- return range_.begin();
- }
- std::size_t end() const {
- return range_.end();
- }
- std::size_t key_pos() const {
- return range_.key_pos();
- }
- float weight() const {
- return weight_;
- }
-
- private:
- Range range_;
- float weight_;
-};
-
-inline bool operator<(const WeightedRange &lhs, const WeightedRange &rhs) {
- return lhs.weight() < rhs.weight();
-}
-
-inline bool operator>(const WeightedRange &lhs, const WeightedRange &rhs) {
- return lhs.weight() > rhs.weight();
-}
-
-inline WeightedRange make_weighted_range(std::size_t begin, std::size_t end,
- std::size_t key_pos, float weight) {
- WeightedRange range;
- range.set_begin(begin);
- range.set_end(end);
- range.set_key_pos(key_pos);
- range.set_weight(weight);
- return range;
-}
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_RANGE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/state.h b/contrib/python/marisa-trie/marisa/grimoire/trie/state.h
deleted file mode 100644
index 219bf9e03a..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/state.h
+++ /dev/null
@@ -1,118 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_STATE_H_
-#define MARISA_GRIMOIRE_TRIE_STATE_H_
-
-#include "../vector.h"
-#include "history.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-// A search agent has its internal state and the status codes are defined
-// below.
-typedef enum StatusCode {
- MARISA_READY_TO_ALL,
- MARISA_READY_TO_COMMON_PREFIX_SEARCH,
- MARISA_READY_TO_PREDICTIVE_SEARCH,
- MARISA_END_OF_COMMON_PREFIX_SEARCH,
- MARISA_END_OF_PREDICTIVE_SEARCH,
-} StatusCode;
-
-class State {
- public:
- State()
- : key_buf_(), history_(), node_id_(0), query_pos_(0),
- history_pos_(0), status_code_(MARISA_READY_TO_ALL) {}
-
- void set_node_id(std::size_t node_id) {
- MARISA_DEBUG_IF(node_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- node_id_ = (UInt32)node_id;
- }
- void set_query_pos(std::size_t query_pos) {
- MARISA_DEBUG_IF(query_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- query_pos_ = (UInt32)query_pos;
- }
- void set_history_pos(std::size_t history_pos) {
- MARISA_DEBUG_IF(history_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- history_pos_ = (UInt32)history_pos;
- }
- void set_status_code(StatusCode status_code) {
- status_code_ = status_code;
- }
-
- std::size_t node_id() const {
- return node_id_;
- }
- std::size_t query_pos() const {
- return query_pos_;
- }
- std::size_t history_pos() const {
- return history_pos_;
- }
- StatusCode status_code() const {
- return status_code_;
- }
-
- const Vector<char> &key_buf() const {
- return key_buf_;
- }
- const Vector<History> &history() const {
- return history_;
- }
-
- Vector<char> &key_buf() {
- return key_buf_;
- }
- Vector<History> &history() {
- return history_;
- }
-
- void reset() {
- status_code_ = MARISA_READY_TO_ALL;
- }
-
- void lookup_init() {
- node_id_ = 0;
- query_pos_ = 0;
- status_code_ = MARISA_READY_TO_ALL;
- }
- void reverse_lookup_init() {
- key_buf_.resize(0);
- key_buf_.reserve(32);
- status_code_ = MARISA_READY_TO_ALL;
- }
- void common_prefix_search_init() {
- node_id_ = 0;
- query_pos_ = 0;
- status_code_ = MARISA_READY_TO_COMMON_PREFIX_SEARCH;
- }
- void predictive_search_init() {
- key_buf_.resize(0);
- key_buf_.reserve(64);
- history_.resize(0);
- history_.reserve(4);
- node_id_ = 0;
- query_pos_ = 0;
- history_pos_ = 0;
- status_code_ = MARISA_READY_TO_PREDICTIVE_SEARCH;
- }
-
- private:
- Vector<char> key_buf_;
- Vector<History> history_;
- UInt32 node_id_;
- UInt32 query_pos_;
- UInt32 history_pos_;
- StatusCode status_code_;
-
- // Disallows copy and assignment.
- State(const State &);
- State &operator=(const State &);
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_STATE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc b/contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc
deleted file mode 100644
index 6ec3652e1c..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-#include "../algorithm.h"
-#include "state.h"
-#include "tail.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-Tail::Tail() : buf_(), end_flags_() {}
-
-void Tail::build(Vector<Entry> &entries, Vector<UInt32> *offsets,
- TailMode mode) {
- MARISA_THROW_IF(offsets == NULL, MARISA_NULL_ERROR);
-
- switch (mode) {
- case MARISA_TEXT_TAIL: {
- for (std::size_t i = 0; i < entries.size(); ++i) {
- const char * const ptr = entries[i].ptr();
- const std::size_t length = entries[i].length();
- for (std::size_t j = 0; j < length; ++j) {
- if (ptr[j] == '\0') {
- mode = MARISA_BINARY_TAIL;
- break;
- }
- }
- if (mode == MARISA_BINARY_TAIL) {
- break;
- }
- }
- break;
- }
- case MARISA_BINARY_TAIL: {
- break;
- }
- default: {
- MARISA_THROW(MARISA_CODE_ERROR, "undefined tail mode");
- }
- }
-
- Tail temp;
- temp.build_(entries, offsets, mode);
- swap(temp);
-}
-
-void Tail::map(Mapper &mapper) {
- Tail temp;
- temp.map_(mapper);
- swap(temp);
-}
-
-void Tail::read(Reader &reader) {
- Tail temp;
- temp.read_(reader);
- swap(temp);
-}
-
-void Tail::write(Writer &writer) const {
- write_(writer);
-}
-
-void Tail::restore(Agent &agent, std::size_t offset) const {
- MARISA_DEBUG_IF(buf_.empty(), MARISA_STATE_ERROR);
-
- State &state = agent.state();
- if (end_flags_.empty()) {
- for (const char *ptr = &buf_[offset]; *ptr != '\0'; ++ptr) {
- state.key_buf().push_back(*ptr);
- }
- } else {
- do {
- state.key_buf().push_back(buf_[offset]);
- } while (!end_flags_[offset++]);
- }
-}
-
-bool Tail::match(Agent &agent, std::size_t offset) const {
- MARISA_DEBUG_IF(buf_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
- MARISA_BOUND_ERROR);
-
- State &state = agent.state();
- if (end_flags_.empty()) {
- const char * const ptr = &buf_[offset] - state.query_pos();
- do {
- if (ptr[state.query_pos()] != agent.query()[state.query_pos()]) {
- return false;
- }
- state.set_query_pos(state.query_pos() + 1);
- if (ptr[state.query_pos()] == '\0') {
- return true;
- }
- } while (state.query_pos() < agent.query().length());
- return false;
- } else {
- do {
- if (buf_[offset] != agent.query()[state.query_pos()]) {
- return false;
- }
- state.set_query_pos(state.query_pos() + 1);
- if (end_flags_[offset++]) {
- return true;
- }
- } while (state.query_pos() < agent.query().length());
- return false;
- }
-}
-
-bool Tail::prefix_match(Agent &agent, std::size_t offset) const {
- MARISA_DEBUG_IF(buf_.empty(), MARISA_STATE_ERROR);
-
- State &state = agent.state();
- if (end_flags_.empty()) {
- const char *ptr = &buf_[offset] - state.query_pos();
- do {
- if (ptr[state.query_pos()] != agent.query()[state.query_pos()]) {
- return false;
- }
- state.key_buf().push_back(ptr[state.query_pos()]);
- state.set_query_pos(state.query_pos() + 1);
- if (ptr[state.query_pos()] == '\0') {
- return true;
- }
- } while (state.query_pos() < agent.query().length());
- ptr += state.query_pos();
- do {
- state.key_buf().push_back(*ptr);
- } while (*++ptr != '\0');
- return true;
- } else {
- do {
- if (buf_[offset] != agent.query()[state.query_pos()]) {
- return false;
- }
- state.key_buf().push_back(buf_[offset]);
- state.set_query_pos(state.query_pos() + 1);
- if (end_flags_[offset++]) {
- return true;
- }
- } while (state.query_pos() < agent.query().length());
- do {
- state.key_buf().push_back(buf_[offset]);
- } while (!end_flags_[offset++]);
- return true;
- }
-}
-
-void Tail::clear() {
- Tail().swap(*this);
-}
-
-void Tail::swap(Tail &rhs) {
- buf_.swap(rhs.buf_);
- end_flags_.swap(rhs.end_flags_);
-}
-
-void Tail::build_(Vector<Entry> &entries, Vector<UInt32> *offsets,
- TailMode mode) {
- for (std::size_t i = 0; i < entries.size(); ++i) {
- entries[i].set_id(i);
- }
- Algorithm().sort(entries.begin(), entries.end());
-
- Vector<UInt32> temp_offsets;
- temp_offsets.resize(entries.size(), 0);
-
- const Entry dummy;
- const Entry *last = &dummy;
- for (std::size_t i = entries.size(); i > 0; --i) {
- const Entry &current = entries[i - 1];
- MARISA_THROW_IF(current.length() == 0, MARISA_RANGE_ERROR);
- std::size_t match = 0;
- while ((match < current.length()) && (match < last->length()) &&
- ((*last)[match] == current[match])) {
- ++match;
- }
- if ((match == current.length()) && (last->length() != 0)) {
- temp_offsets[current.id()] = (UInt32)(
- temp_offsets[last->id()] + (last->length() - match));
- } else {
- temp_offsets[current.id()] = (UInt32)buf_.size();
- for (std::size_t j = 1; j <= current.length(); ++j) {
- buf_.push_back(current[current.length() - j]);
- }
- if (mode == MARISA_TEXT_TAIL) {
- buf_.push_back('\0');
- } else {
- for (std::size_t j = 1; j < current.length(); ++j) {
- end_flags_.push_back(false);
- }
- end_flags_.push_back(true);
- }
- MARISA_THROW_IF(buf_.size() > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- }
- last = &current;
- }
- buf_.shrink();
-
- offsets->swap(temp_offsets);
-}
-
-void Tail::map_(Mapper &mapper) {
- buf_.map(mapper);
- end_flags_.map(mapper);
-}
-
-void Tail::read_(Reader &reader) {
- buf_.read(reader);
- end_flags_.read(reader);
-}
-
-void Tail::write_(Writer &writer) const {
- buf_.write(writer);
- end_flags_.write(writer);
-}
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/tail.h b/contrib/python/marisa-trie/marisa/grimoire/trie/tail.h
deleted file mode 100644
index 7e5ca1d3e7..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/trie/tail.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_TRIE_TAIL_H_
-#define MARISA_GRIMOIRE_TRIE_TAIL_H_
-
-#include "../../agent.h"
-#include "../vector.h"
-#include "entry.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class Tail {
- public:
- Tail();
-
- void build(Vector<Entry> &entries, Vector<UInt32> *offsets,
- TailMode mode);
-
- void map(Mapper &mapper);
- void read(Reader &reader);
- void write(Writer &writer) const;
-
- void restore(Agent &agent, std::size_t offset) const;
- bool match(Agent &agent, std::size_t offset) const;
- bool prefix_match(Agent &agent, std::size_t offset) const;
-
- const char &operator[](std::size_t offset) const {
- MARISA_DEBUG_IF(offset >= buf_.size(), MARISA_BOUND_ERROR);
- return buf_[offset];
- }
-
- TailMode mode() const {
- return end_flags_.empty() ? MARISA_TEXT_TAIL : MARISA_BINARY_TAIL;
- }
-
- bool empty() const {
- return buf_.empty();
- }
- std::size_t size() const {
- return buf_.size();
- }
- std::size_t total_size() const {
- return buf_.total_size() + end_flags_.total_size();
- }
- std::size_t io_size() const {
- return buf_.io_size() + end_flags_.io_size();
- }
-
- void clear();
- void swap(Tail &rhs);
-
- private:
- Vector<char> buf_;
- BitVector end_flags_;
-
- void build_(Vector<Entry> &entries, Vector<UInt32> *offsets,
- TailMode mode);
-
- void map_(Mapper &mapper);
- void read_(Reader &reader);
- void write_(Writer &writer) const;
-
- // Disallows copy and assignment.
- Tail(const Tail &);
- Tail &operator=(const Tail &);
-};
-
-} // namespace trie
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_TRIE_TAIL_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector.h
deleted file mode 100644
index d942a7f279..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_VECTOR_H_
-#define MARISA_GRIMOIRE_VECTOR_H_
-
-#include "vector/vector.h"
-#include "vector/flat-vector.h"
-#include "vector/bit-vector.h"
-
-namespace marisa {
-namespace grimoire {
-
-using vector::Vector;
-typedef vector::FlatVector FlatVector;
-typedef vector::BitVector BitVector;
-
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc b/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc
deleted file mode 100644
index a5abc69319..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc
+++ /dev/null
@@ -1,825 +0,0 @@
-#include "pop-count.h"
-#include "bit-vector.h"
-
-namespace marisa {
-namespace grimoire {
-namespace vector {
-namespace {
-
-const UInt8 SELECT_TABLE[8][256] = {
- {
- 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
- },
- {
- 7, 7, 7, 1, 7, 2, 2, 1, 7, 3, 3, 1, 3, 2, 2, 1,
- 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
- 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1,
- 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
- 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 7, 7, 7, 1, 7, 2, 2, 1, 7, 3, 3, 1, 3, 2, 2, 1,
- 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
- 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1,
- 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
- 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
- 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1
- },
- {
- 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 3, 7, 3, 3, 2,
- 7, 7, 7, 4, 7, 4, 4, 2, 7, 4, 4, 3, 4, 3, 3, 2,
- 7, 7, 7, 5, 7, 5, 5, 2, 7, 5, 5, 3, 5, 3, 3, 2,
- 7, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2,
- 7, 7, 7, 6, 7, 6, 6, 2, 7, 6, 6, 3, 6, 3, 3, 2,
- 7, 6, 6, 4, 6, 4, 4, 2, 6, 4, 4, 3, 4, 3, 3, 2,
- 7, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2,
- 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2,
- 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 3, 7, 3, 3, 2,
- 7, 7, 7, 4, 7, 4, 4, 2, 7, 4, 4, 3, 4, 3, 3, 2,
- 7, 7, 7, 5, 7, 5, 5, 2, 7, 5, 5, 3, 5, 3, 3, 2,
- 7, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2,
- 7, 7, 7, 6, 7, 6, 6, 2, 7, 6, 6, 3, 6, 3, 3, 2,
- 7, 6, 6, 4, 6, 4, 4, 2, 6, 4, 4, 3, 4, 3, 3, 2,
- 7, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2,
- 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2
- },
- {
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3,
- 7, 7, 7, 7, 7, 7, 7, 4, 7, 7, 7, 4, 7, 4, 4, 3,
- 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 3,
- 7, 7, 7, 5, 7, 5, 5, 4, 7, 5, 5, 4, 5, 4, 4, 3,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 3,
- 7, 7, 7, 6, 7, 6, 6, 4, 7, 6, 6, 4, 6, 4, 4, 3,
- 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 3,
- 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3,
- 7, 7, 7, 7, 7, 7, 7, 4, 7, 7, 7, 4, 7, 4, 4, 3,
- 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 3,
- 7, 7, 7, 5, 7, 5, 5, 4, 7, 5, 5, 4, 5, 4, 4, 3,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 3,
- 7, 7, 7, 6, 7, 6, 6, 4, 7, 6, 6, 4, 6, 4, 4, 3,
- 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 3,
- 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3
- },
- {
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5,
- 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 4,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 4,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5,
- 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5,
- 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 4,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 4,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5,
- 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4
- },
- {
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5
- },
- {
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6
- },
- {
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
- }
-};
-
-#if MARISA_WORD_SIZE == 64
-const UInt64 MASK_55 = 0x5555555555555555ULL;
-const UInt64 MASK_33 = 0x3333333333333333ULL;
-const UInt64 MASK_0F = 0x0F0F0F0F0F0F0F0FULL;
-const UInt64 MASK_01 = 0x0101010101010101ULL;
-const UInt64 MASK_80 = 0x8080808080808080ULL;
-
-std::size_t select_bit(std::size_t i, std::size_t bit_id, UInt64 unit) {
- UInt64 counts;
- {
- #if defined(MARISA_X64) && defined(MARISA_USE_SSSE3)
- __m128i lower_nibbles = _mm_cvtsi64_si128(unit & 0x0F0F0F0F0F0F0F0FULL);
- __m128i upper_nibbles = _mm_cvtsi64_si128(unit & 0xF0F0F0F0F0F0F0F0ULL);
- upper_nibbles = _mm_srli_epi32(upper_nibbles, 4);
-
- __m128i lower_counts =
- _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
- lower_counts = _mm_shuffle_epi8(lower_counts, lower_nibbles);
- __m128i upper_counts =
- _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
- upper_counts = _mm_shuffle_epi8(upper_counts, upper_nibbles);
-
- counts = _mm_cvtsi128_si64(_mm_add_epi8(lower_counts, upper_counts));
- #else // defined(MARISA_X64) && defined(MARISA_USE_SSSE3)
- counts = unit - ((unit >> 1) & MASK_55);
- counts = (counts & MASK_33) + ((counts >> 2) & MASK_33);
- counts = (counts + (counts >> 4)) & MASK_0F;
- #endif // defined(MARISA_X64) && defined(MARISA_USE_SSSE3)
- counts *= MASK_01;
- }
-
- #if defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
- UInt8 skip;
- {
- __m128i x = _mm_cvtsi64_si128((i + 1) * MASK_01);
- __m128i y = _mm_cvtsi64_si128(counts);
- x = _mm_cmpgt_epi8(x, y);
- skip = (UInt8)PopCount::count(_mm_cvtsi128_si64(x));
- }
- #else // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
- const UInt64 x = (counts | MASK_80) - ((i + 1) * MASK_01);
- #ifdef _MSC_VER
- unsigned long skip;
- ::_BitScanForward64(&skip, (x & MASK_80) >> 7);
- #else // _MSC_VER
- const int skip = ::__builtin_ctzll((x & MASK_80) >> 7);
- #endif // _MSC_VER
- #endif // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
-
- bit_id += skip;
- unit >>= skip;
- i -= ((counts << 8) >> skip) & 0xFF;
-
- return bit_id + SELECT_TABLE[i][unit & 0xFF];
-}
-#else // MARISA_WORD_SIZE == 64
- #ifdef MARISA_USE_SSE2
-const UInt8 POPCNT_TABLE[256] = {
- 0, 8, 8, 16, 8, 16, 16, 24, 8, 16, 16, 24, 16, 24, 24, 32,
- 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
- 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
- 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
- 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
- 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
- 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
- 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
- 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
- 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
- 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
- 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
- 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
- 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
- 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
- 32, 40, 40, 48, 40, 48, 48, 56, 40, 48, 48, 56, 48, 56, 56, 64
-};
-
-std::size_t select_bit(std::size_t i, std::size_t bit_id,
- UInt32 unit_lo, UInt32 unit_hi) {
- __m128i unit;
- {
- __m128i lower_dword = _mm_cvtsi32_si128(unit_lo);
- __m128i upper_dword = _mm_cvtsi32_si128(unit_hi);
- upper_dword = _mm_slli_si128(upper_dword, 4);
- unit = _mm_or_si128(lower_dword, upper_dword);
- }
-
- __m128i counts;
- {
- #ifdef MARISA_USE_SSSE3
- __m128i lower_nibbles = _mm_set1_epi8(0x0F);
- lower_nibbles = _mm_and_si128(lower_nibbles, unit);
- __m128i upper_nibbles = _mm_set1_epi8((UInt8)0xF0);
- upper_nibbles = _mm_and_si128(upper_nibbles, unit);
- upper_nibbles = _mm_srli_epi32(upper_nibbles, 4);
-
- __m128i lower_counts =
- _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
- lower_counts = _mm_shuffle_epi8(lower_counts, lower_nibbles);
- __m128i upper_counts =
- _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
- upper_counts = _mm_shuffle_epi8(upper_counts, upper_nibbles);
-
- counts = _mm_add_epi8(lower_counts, upper_counts);
- #else // MARISA_USE_SSSE3
- __m128i x = _mm_srli_epi32(unit, 1);
- x = _mm_and_si128(x, _mm_set1_epi8(0x55));
- x = _mm_sub_epi8(unit, x);
-
- __m128i y = _mm_srli_epi32(x, 2);
- y = _mm_and_si128(y, _mm_set1_epi8(0x33));
- x = _mm_and_si128(x, _mm_set1_epi8(0x33));
- x = _mm_add_epi8(x, y);
-
- y = _mm_srli_epi32(x, 4);
- x = _mm_add_epi8(x, y);
- counts = _mm_and_si128(x, _mm_set1_epi8(0x0F));
- #endif // MARISA_USE_SSSE3
- }
-
- __m128i accumulated_counts;
- {
- __m128i x = counts;
- x = _mm_slli_si128(x, 1);
- __m128i y = counts;
- y = _mm_add_epi32(y, x);
-
- x = y;
- y = _mm_slli_si128(y, 2);
- x = _mm_add_epi32(x, y);
-
- y = x;
- x = _mm_slli_si128(x, 4);
- y = _mm_add_epi32(y, x);
-
- accumulated_counts = _mm_set_epi32(0x7F7F7F7FU, 0x7F7F7F7FU, 0, 0);
- accumulated_counts = _mm_or_si128(accumulated_counts, y);
- }
-
- UInt8 skip;
- {
- __m128i x = _mm_set1_epi8((UInt8)(i + 1));
- x = _mm_cmpgt_epi8(x, accumulated_counts);
- skip = POPCNT_TABLE[_mm_movemask_epi8(x)];
- }
-
- UInt8 byte;
- {
- #ifdef _MSC_VER
- __declspec(align(16)) UInt8 unit_bytes[16];
- __declspec(align(16)) UInt8 accumulated_counts_bytes[16];
- #else // _MSC_VER
- UInt8 unit_bytes[16] __attribute__ ((aligned (16)));
- UInt8 accumulated_counts_bytes[16] __attribute__ ((aligned (16)));
- #endif // _MSC_VER
- accumulated_counts = _mm_slli_si128(accumulated_counts, 1);
- _mm_store_si128(reinterpret_cast<__m128i *>(unit_bytes), unit);
- _mm_store_si128(reinterpret_cast<__m128i *>(accumulated_counts_bytes),
- accumulated_counts);
-
- bit_id += skip;
- byte = unit_bytes[skip / 8];
- i -= accumulated_counts_bytes[skip / 8];
- }
-
- return bit_id + SELECT_TABLE[i][byte];
-}
- #endif // MARISA_USE_SSE2
-#endif // MARISA_WORD_SIZE == 64
-
-} // namespace
-
-#if MARISA_WORD_SIZE == 64
-
-std::size_t BitVector::rank1(std::size_t i) const {
- MARISA_DEBUG_IF(ranks_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i > size_, MARISA_BOUND_ERROR);
-
- const RankIndex &rank = ranks_[i / 512];
- std::size_t offset = rank.abs();
- switch ((i / 64) % 8) {
- case 1: {
- offset += rank.rel1();
- break;
- }
- case 2: {
- offset += rank.rel2();
- break;
- }
- case 3: {
- offset += rank.rel3();
- break;
- }
- case 4: {
- offset += rank.rel4();
- break;
- }
- case 5: {
- offset += rank.rel5();
- break;
- }
- case 6: {
- offset += rank.rel6();
- break;
- }
- case 7: {
- offset += rank.rel7();
- break;
- }
- }
- offset += PopCount::count(units_[i / 64] & ((1ULL << (i % 64)) - 1));
- return offset;
-}
-
-std::size_t BitVector::select0(std::size_t i) const {
- MARISA_DEBUG_IF(select0s_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i >= num_0s(), MARISA_BOUND_ERROR);
-
- const std::size_t select_id = i / 512;
- MARISA_DEBUG_IF((select_id + 1) >= select0s_.size(), MARISA_BOUND_ERROR);
- if ((i % 512) == 0) {
- return select0s_[select_id];
- }
- std::size_t begin = select0s_[select_id] / 512;
- std::size_t end = (select0s_[select_id + 1] + 511) / 512;
- if (begin + 10 >= end) {
- while (i >= ((begin + 1) * 512) - ranks_[begin + 1].abs()) {
- ++begin;
- }
- } else {
- while (begin + 1 < end) {
- const std::size_t middle = (begin + end) / 2;
- if (i < (middle * 512) - ranks_[middle].abs()) {
- end = middle;
- } else {
- begin = middle;
- }
- }
- }
- const std::size_t rank_id = begin;
- i -= (rank_id * 512) - ranks_[rank_id].abs();
-
- const RankIndex &rank = ranks_[rank_id];
- std::size_t unit_id = rank_id * 8;
- if (i < (256U - rank.rel4())) {
- if (i < (128U - rank.rel2())) {
- if (i >= (64U - rank.rel1())) {
- unit_id += 1;
- i -= 64 - rank.rel1();
- }
- } else if (i < (192U - rank.rel3())) {
- unit_id += 2;
- i -= 128 - rank.rel2();
- } else {
- unit_id += 3;
- i -= 192 - rank.rel3();
- }
- } else if (i < (384U - rank.rel6())) {
- if (i < (320U - rank.rel5())) {
- unit_id += 4;
- i -= 256 - rank.rel4();
- } else {
- unit_id += 5;
- i -= 320 - rank.rel5();
- }
- } else if (i < (448U - rank.rel7())) {
- unit_id += 6;
- i -= 384 - rank.rel6();
- } else {
- unit_id += 7;
- i -= 448 - rank.rel7();
- }
-
- return select_bit(i, unit_id * 64, ~units_[unit_id]);
-}
-
-std::size_t BitVector::select1(std::size_t i) const {
- MARISA_DEBUG_IF(select1s_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i >= num_1s(), MARISA_BOUND_ERROR);
-
- const std::size_t select_id = i / 512;
- MARISA_DEBUG_IF((select_id + 1) >= select1s_.size(), MARISA_BOUND_ERROR);
- if ((i % 512) == 0) {
- return select1s_[select_id];
- }
- std::size_t begin = select1s_[select_id] / 512;
- std::size_t end = (select1s_[select_id + 1] + 511) / 512;
- if (begin + 10 >= end) {
- while (i >= ranks_[begin + 1].abs()) {
- ++begin;
- }
- } else {
- while (begin + 1 < end) {
- const std::size_t middle = (begin + end) / 2;
- if (i < ranks_[middle].abs()) {
- end = middle;
- } else {
- begin = middle;
- }
- }
- }
- const std::size_t rank_id = begin;
- i -= ranks_[rank_id].abs();
-
- const RankIndex &rank = ranks_[rank_id];
- std::size_t unit_id = rank_id * 8;
- if (i < rank.rel4()) {
- if (i < rank.rel2()) {
- if (i >= rank.rel1()) {
- unit_id += 1;
- i -= rank.rel1();
- }
- } else if (i < rank.rel3()) {
- unit_id += 2;
- i -= rank.rel2();
- } else {
- unit_id += 3;
- i -= rank.rel3();
- }
- } else if (i < rank.rel6()) {
- if (i < rank.rel5()) {
- unit_id += 4;
- i -= rank.rel4();
- } else {
- unit_id += 5;
- i -= rank.rel5();
- }
- } else if (i < rank.rel7()) {
- unit_id += 6;
- i -= rank.rel6();
- } else {
- unit_id += 7;
- i -= rank.rel7();
- }
-
- return select_bit(i, unit_id * 64, units_[unit_id]);
-}
-
-#else // MARISA_WORD_SIZE == 64
-
-std::size_t BitVector::rank1(std::size_t i) const {
- MARISA_DEBUG_IF(ranks_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i > size_, MARISA_BOUND_ERROR);
-
- const RankIndex &rank = ranks_[i / 512];
- std::size_t offset = rank.abs();
- switch ((i / 64) % 8) {
- case 1: {
- offset += rank.rel1();
- break;
- }
- case 2: {
- offset += rank.rel2();
- break;
- }
- case 3: {
- offset += rank.rel3();
- break;
- }
- case 4: {
- offset += rank.rel4();
- break;
- }
- case 5: {
- offset += rank.rel5();
- break;
- }
- case 6: {
- offset += rank.rel6();
- break;
- }
- case 7: {
- offset += rank.rel7();
- break;
- }
- }
- if (((i / 32) & 1) == 1) {
- offset += PopCount::count(units_[(i / 32) - 1]);
- }
- offset += PopCount::count(units_[i / 32] & ((1U << (i % 32)) - 1));
- return offset;
-}
-
-std::size_t BitVector::select0(std::size_t i) const {
- MARISA_DEBUG_IF(select0s_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i >= num_0s(), MARISA_BOUND_ERROR);
-
- const std::size_t select_id = i / 512;
- MARISA_DEBUG_IF((select_id + 1) >= select0s_.size(), MARISA_BOUND_ERROR);
- if ((i % 512) == 0) {
- return select0s_[select_id];
- }
- std::size_t begin = select0s_[select_id] / 512;
- std::size_t end = (select0s_[select_id + 1] + 511) / 512;
- if (begin + 10 >= end) {
- while (i >= ((begin + 1) * 512) - ranks_[begin + 1].abs()) {
- ++begin;
- }
- } else {
- while (begin + 1 < end) {
- const std::size_t middle = (begin + end) / 2;
- if (i < (middle * 512) - ranks_[middle].abs()) {
- end = middle;
- } else {
- begin = middle;
- }
- }
- }
- const std::size_t rank_id = begin;
- i -= (rank_id * 512) - ranks_[rank_id].abs();
-
- const RankIndex &rank = ranks_[rank_id];
- std::size_t unit_id = rank_id * 16;
- if (i < (256U - rank.rel4())) {
- if (i < (128U - rank.rel2())) {
- if (i >= (64U - rank.rel1())) {
- unit_id += 2;
- i -= 64 - rank.rel1();
- }
- } else if (i < (192U - rank.rel3())) {
- unit_id += 4;
- i -= 128 - rank.rel2();
- } else {
- unit_id += 6;
- i -= 192 - rank.rel3();
- }
- } else if (i < (384U - rank.rel6())) {
- if (i < (320U - rank.rel5())) {
- unit_id += 8;
- i -= 256 - rank.rel4();
- } else {
- unit_id += 10;
- i -= 320 - rank.rel5();
- }
- } else if (i < (448U - rank.rel7())) {
- unit_id += 12;
- i -= 384 - rank.rel6();
- } else {
- unit_id += 14;
- i -= 448 - rank.rel7();
- }
-
-#ifdef MARISA_USE_SSE2
- return select_bit(i, unit_id * 32, ~units_[unit_id], ~units_[unit_id + 1]);
-#else // MARISA_USE_SSE2
- UInt32 unit = ~units_[unit_id];
- PopCount count(unit);
- if (i >= count.lo32()) {
- ++unit_id;
- i -= count.lo32();
- unit = ~units_[unit_id];
- count = PopCount(unit);
- }
-
- std::size_t bit_id = unit_id * 32;
- if (i < count.lo16()) {
- if (i >= count.lo8()) {
- bit_id += 8;
- unit >>= 8;
- i -= count.lo8();
- }
- } else if (i < count.lo24()) {
- bit_id += 16;
- unit >>= 16;
- i -= count.lo16();
- } else {
- bit_id += 24;
- unit >>= 24;
- i -= count.lo24();
- }
- return bit_id + SELECT_TABLE[i][unit & 0xFF];
-#endif // MARISA_USE_SSE2
-}
-
-std::size_t BitVector::select1(std::size_t i) const {
- MARISA_DEBUG_IF(select1s_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i >= num_1s(), MARISA_BOUND_ERROR);
-
- const std::size_t select_id = i / 512;
- MARISA_DEBUG_IF((select_id + 1) >= select1s_.size(), MARISA_BOUND_ERROR);
- if ((i % 512) == 0) {
- return select1s_[select_id];
- }
- std::size_t begin = select1s_[select_id] / 512;
- std::size_t end = (select1s_[select_id + 1] + 511) / 512;
- if (begin + 10 >= end) {
- while (i >= ranks_[begin + 1].abs()) {
- ++begin;
- }
- } else {
- while (begin + 1 < end) {
- const std::size_t middle = (begin + end) / 2;
- if (i < ranks_[middle].abs()) {
- end = middle;
- } else {
- begin = middle;
- }
- }
- }
- const std::size_t rank_id = begin;
- i -= ranks_[rank_id].abs();
-
- const RankIndex &rank = ranks_[rank_id];
- std::size_t unit_id = rank_id * 16;
- if (i < rank.rel4()) {
- if (i < rank.rel2()) {
- if (i >= rank.rel1()) {
- unit_id += 2;
- i -= rank.rel1();
- }
- } else if (i < rank.rel3()) {
- unit_id += 4;
- i -= rank.rel2();
- } else {
- unit_id += 6;
- i -= rank.rel3();
- }
- } else if (i < rank.rel6()) {
- if (i < rank.rel5()) {
- unit_id += 8;
- i -= rank.rel4();
- } else {
- unit_id += 10;
- i -= rank.rel5();
- }
- } else if (i < rank.rel7()) {
- unit_id += 12;
- i -= rank.rel6();
- } else {
- unit_id += 14;
- i -= rank.rel7();
- }
-
-#ifdef MARISA_USE_SSE2
- return select_bit(i, unit_id * 32, units_[unit_id], units_[unit_id + 1]);
-#else // MARISA_USE_SSE2
- UInt32 unit = units_[unit_id];
- PopCount count(unit);
- if (i >= count.lo32()) {
- ++unit_id;
- i -= count.lo32();
- unit = units_[unit_id];
- count = PopCount(unit);
- }
-
- std::size_t bit_id = unit_id * 32;
- if (i < count.lo16()) {
- if (i >= count.lo8()) {
- bit_id += 8;
- unit >>= 8;
- i -= count.lo8();
- }
- } else if (i < count.lo24()) {
- bit_id += 16;
- unit >>= 16;
- i -= count.lo16();
- } else {
- bit_id += 24;
- unit >>= 24;
- i -= count.lo24();
- }
- return bit_id + SELECT_TABLE[i][unit & 0xFF];
-#endif // MARISA_USE_SSE2
-}
-
-#endif // MARISA_WORD_SIZE == 64
-
-void BitVector::build_index(const BitVector &bv,
- bool enables_select0, bool enables_select1) {
- ranks_.resize((bv.size() / 512) + (((bv.size() % 512) != 0) ? 1 : 0) + 1);
-
- std::size_t num_0s = 0;
- std::size_t num_1s = 0;
-
- for (std::size_t i = 0; i < bv.size(); ++i) {
- if ((i % 64) == 0) {
- const std::size_t rank_id = i / 512;
- switch ((i / 64) % 8) {
- case 0: {
- ranks_[rank_id].set_abs(num_1s);
- break;
- }
- case 1: {
- ranks_[rank_id].set_rel1(num_1s - ranks_[rank_id].abs());
- break;
- }
- case 2: {
- ranks_[rank_id].set_rel2(num_1s - ranks_[rank_id].abs());
- break;
- }
- case 3: {
- ranks_[rank_id].set_rel3(num_1s - ranks_[rank_id].abs());
- break;
- }
- case 4: {
- ranks_[rank_id].set_rel4(num_1s - ranks_[rank_id].abs());
- break;
- }
- case 5: {
- ranks_[rank_id].set_rel5(num_1s - ranks_[rank_id].abs());
- break;
- }
- case 6: {
- ranks_[rank_id].set_rel6(num_1s - ranks_[rank_id].abs());
- break;
- }
- case 7: {
- ranks_[rank_id].set_rel7(num_1s - ranks_[rank_id].abs());
- break;
- }
- }
- }
-
- if (bv[i]) {
- if (enables_select1 && ((num_1s % 512) == 0)) {
- select1s_.push_back(static_cast<UInt32>(i));
- }
- ++num_1s;
- } else {
- if (enables_select0 && ((num_0s % 512) == 0)) {
- select0s_.push_back(static_cast<UInt32>(i));
- }
- ++num_0s;
- }
- }
-
- if ((bv.size() % 512) != 0) {
- const std::size_t rank_id = (bv.size() - 1) / 512;
- switch (((bv.size() - 1) / 64) % 8) {
- case 0: {
- ranks_[rank_id].set_rel1(num_1s - ranks_[rank_id].abs());
- }
- case 1: {
- ranks_[rank_id].set_rel2(num_1s - ranks_[rank_id].abs());
- }
- case 2: {
- ranks_[rank_id].set_rel3(num_1s - ranks_[rank_id].abs());
- }
- case 3: {
- ranks_[rank_id].set_rel4(num_1s - ranks_[rank_id].abs());
- }
- case 4: {
- ranks_[rank_id].set_rel5(num_1s - ranks_[rank_id].abs());
- }
- case 5: {
- ranks_[rank_id].set_rel6(num_1s - ranks_[rank_id].abs());
- }
- case 6: {
- ranks_[rank_id].set_rel7(num_1s - ranks_[rank_id].abs());
- break;
- }
- }
- }
-
- size_ = bv.size();
- num_1s_ = bv.num_1s();
-
- ranks_.back().set_abs(num_1s);
- if (enables_select0) {
- select0s_.push_back(static_cast<UInt32>(bv.size()));
- select0s_.shrink();
- }
- if (enables_select1) {
- select1s_.push_back(static_cast<UInt32>(bv.size()));
- select1s_.shrink();
- }
-}
-
-} // namespace vector
-} // namespace grimoire
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h
deleted file mode 100644
index 56f99ed699..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h
+++ /dev/null
@@ -1,180 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_VECTOR_BIT_VECTOR_H_
-#define MARISA_GRIMOIRE_VECTOR_BIT_VECTOR_H_
-
-#include "rank-index.h"
-#include "vector.h"
-
-namespace marisa {
-namespace grimoire {
-namespace vector {
-
-class BitVector {
- public:
-#if MARISA_WORD_SIZE == 64
- typedef UInt64 Unit;
-#else // MARISA_WORD_SIZE == 64
- typedef UInt32 Unit;
-#endif // MARISA_WORD_SIZE == 64
-
- BitVector()
- : units_(), size_(0), num_1s_(0), ranks_(), select0s_(), select1s_() {}
-
- void build(bool enables_select0, bool enables_select1) {
- BitVector temp;
- temp.build_index(*this, enables_select0, enables_select1);
- units_.shrink();
- temp.units_.swap(units_);
- swap(temp);
- }
-
- void map(Mapper &mapper) {
- BitVector temp;
- temp.map_(mapper);
- swap(temp);
- }
- void read(Reader &reader) {
- BitVector temp;
- temp.read_(reader);
- swap(temp);
- }
- void write(Writer &writer) const {
- write_(writer);
- }
-
- void disable_select0() {
- select0s_.clear();
- }
- void disable_select1() {
- select1s_.clear();
- }
-
- void push_back(bool bit) {
- MARISA_THROW_IF(size_ == MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- if (size_ == (MARISA_WORD_SIZE * units_.size())) {
- units_.resize(units_.size() + (64 / MARISA_WORD_SIZE), 0);
- }
- if (bit) {
- units_[size_ / MARISA_WORD_SIZE] |=
- (Unit)1 << (size_ % MARISA_WORD_SIZE);
- ++num_1s_;
- }
- ++size_;
- }
-
- bool operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
- return (units_[i / MARISA_WORD_SIZE]
- & ((Unit)1 << (i % MARISA_WORD_SIZE))) != 0;
- }
-
- std::size_t rank0(std::size_t i) const {
- MARISA_DEBUG_IF(ranks_.empty(), MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i > size_, MARISA_BOUND_ERROR);
- return i - rank1(i);
- }
- std::size_t rank1(std::size_t i) const;
-
- std::size_t select0(std::size_t i) const;
- std::size_t select1(std::size_t i) const;
-
- std::size_t num_0s() const {
- return size_ - num_1s_;
- }
- std::size_t num_1s() const {
- return num_1s_;
- }
-
- bool empty() const {
- return size_ == 0;
- }
- std::size_t size() const {
- return size_;
- }
- std::size_t total_size() const {
- return units_.total_size() + ranks_.total_size()
- + select0s_.total_size() + select1s_.total_size();
- }
- std::size_t io_size() const {
- return units_.io_size() + (sizeof(UInt32) * 2) + ranks_.io_size()
- + select0s_.io_size() + select1s_.io_size();
- }
-
- void clear() {
- BitVector().swap(*this);
- }
- void swap(BitVector &rhs) {
- units_.swap(rhs.units_);
- marisa::swap(size_, rhs.size_);
- marisa::swap(num_1s_, rhs.num_1s_);
- ranks_.swap(rhs.ranks_);
- select0s_.swap(rhs.select0s_);
- select1s_.swap(rhs.select1s_);
- }
-
- private:
- Vector<Unit> units_;
- std::size_t size_;
- std::size_t num_1s_;
- Vector<RankIndex> ranks_;
- Vector<UInt32> select0s_;
- Vector<UInt32> select1s_;
-
- void build_index(const BitVector &bv,
- bool enables_select0, bool enables_select1);
-
- void map_(Mapper &mapper) {
- units_.map(mapper);
- {
- UInt32 temp_size;
- mapper.map(&temp_size);
- size_ = temp_size;
- }
- {
- UInt32 temp_num_1s;
- mapper.map(&temp_num_1s);
- MARISA_THROW_IF(temp_num_1s > size_, MARISA_FORMAT_ERROR);
- num_1s_ = temp_num_1s;
- }
- ranks_.map(mapper);
- select0s_.map(mapper);
- select1s_.map(mapper);
- }
-
- void read_(Reader &reader) {
- units_.read(reader);
- {
- UInt32 temp_size;
- reader.read(&temp_size);
- size_ = temp_size;
- }
- {
- UInt32 temp_num_1s;
- reader.read(&temp_num_1s);
- MARISA_THROW_IF(temp_num_1s > size_, MARISA_FORMAT_ERROR);
- num_1s_ = temp_num_1s;
- }
- ranks_.read(reader);
- select0s_.read(reader);
- select1s_.read(reader);
- }
-
- void write_(Writer &writer) const {
- units_.write(writer);
- writer.write((UInt32)size_);
- writer.write((UInt32)num_1s_);
- ranks_.write(writer);
- select0s_.write(writer);
- select1s_.write(writer);
- }
-
- // Disallows copy and assignment.
- BitVector(const BitVector &);
- BitVector &operator=(const BitVector &);
-};
-
-} // namespace vector
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_VECTOR_BIT_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h
deleted file mode 100644
index 14b25d7d82..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h
+++ /dev/null
@@ -1,206 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_VECTOR_FLAT_VECTOR_H_
-#define MARISA_GRIMOIRE_VECTOR_FLAT_VECTOR_H_
-
-#include "vector.h"
-
-namespace marisa {
-namespace grimoire {
-namespace vector {
-
-class FlatVector {
- public:
-#if MARISA_WORD_SIZE == 64
- typedef UInt64 Unit;
-#else // MARISA_WORD_SIZE == 64
- typedef UInt32 Unit;
-#endif // MARISA_WORD_SIZE == 64
-
- FlatVector() : units_(), value_size_(0), mask_(0), size_(0) {}
-
- void build(const Vector<UInt32> &values) {
- FlatVector temp;
- temp.build_(values);
- swap(temp);
- }
-
- void map(Mapper &mapper) {
- FlatVector temp;
- temp.map_(mapper);
- swap(temp);
- }
- void read(Reader &reader) {
- FlatVector temp;
- temp.read_(reader);
- swap(temp);
- }
- void write(Writer &writer) const {
- write_(writer);
- }
-
- UInt32 operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
-
- const std::size_t pos = i * value_size_;
- const std::size_t unit_id = pos / MARISA_WORD_SIZE;
- const std::size_t unit_offset = pos % MARISA_WORD_SIZE;
-
- if ((unit_offset + value_size_) <= MARISA_WORD_SIZE) {
- return (UInt32)(units_[unit_id] >> unit_offset) & mask_;
- } else {
- return (UInt32)((units_[unit_id] >> unit_offset)
- | (units_[unit_id + 1] << (MARISA_WORD_SIZE - unit_offset))) & mask_;
- }
- }
-
- std::size_t value_size() const {
- return value_size_;
- }
- UInt32 mask() const {
- return mask_;
- }
-
- bool empty() const {
- return size_ == 0;
- }
- std::size_t size() const {
- return size_;
- }
- std::size_t total_size() const {
- return units_.total_size();
- }
- std::size_t io_size() const {
- return units_.io_size() + (sizeof(UInt32) * 2) + sizeof(UInt64);
- }
-
- void clear() {
- FlatVector().swap(*this);
- }
- void swap(FlatVector &rhs) {
- units_.swap(rhs.units_);
- marisa::swap(value_size_, rhs.value_size_);
- marisa::swap(mask_, rhs.mask_);
- marisa::swap(size_, rhs.size_);
- }
-
- private:
- Vector<Unit> units_;
- std::size_t value_size_;
- UInt32 mask_;
- std::size_t size_;
-
- void build_(const Vector<UInt32> &values) {
- UInt32 max_value = 0;
- for (std::size_t i = 0; i < values.size(); ++i) {
- if (values[i] > max_value) {
- max_value = values[i];
- }
- }
-
- std::size_t value_size = 0;
- while (max_value != 0) {
- ++value_size;
- max_value >>= 1;
- }
-
- std::size_t num_units = values.empty() ? 0 : (64 / MARISA_WORD_SIZE);
- if (value_size != 0) {
- num_units = (std::size_t)(
- (((UInt64)value_size * values.size()) + (MARISA_WORD_SIZE - 1))
- / MARISA_WORD_SIZE);
- num_units += num_units % (64 / MARISA_WORD_SIZE);
- }
-
- units_.resize(num_units);
- if (num_units > 0) {
- units_.back() = 0;
- }
-
- value_size_ = value_size;
- if (value_size != 0) {
- mask_ = MARISA_UINT32_MAX >> (32 - value_size);
- }
- size_ = values.size();
-
- for (std::size_t i = 0; i < values.size(); ++i) {
- set(i, values[i]);
- }
- }
-
- void map_(Mapper &mapper) {
- units_.map(mapper);
- {
- UInt32 temp_value_size;
- mapper.map(&temp_value_size);
- MARISA_THROW_IF(temp_value_size > 32, MARISA_FORMAT_ERROR);
- value_size_ = temp_value_size;
- }
- {
- UInt32 temp_mask;
- mapper.map(&temp_mask);
- mask_ = temp_mask;
- }
- {
- UInt64 temp_size;
- mapper.map(&temp_size);
- MARISA_THROW_IF(temp_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- size_ = (std::size_t)temp_size;
- }
- }
-
- void read_(Reader &reader) {
- units_.read(reader);
- {
- UInt32 temp_value_size;
- reader.read(&temp_value_size);
- MARISA_THROW_IF(temp_value_size > 32, MARISA_FORMAT_ERROR);
- value_size_ = temp_value_size;
- }
- {
- UInt32 temp_mask;
- reader.read(&temp_mask);
- mask_ = temp_mask;
- }
- {
- UInt64 temp_size;
- reader.read(&temp_size);
- MARISA_THROW_IF(temp_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- size_ = (std::size_t)temp_size;
- }
- }
-
- void write_(Writer &writer) const {
- units_.write(writer);
- writer.write((UInt32)value_size_);
- writer.write((UInt32)mask_);
- writer.write((UInt64)size_);
- }
-
- void set(std::size_t i, UInt32 value) {
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
- MARISA_DEBUG_IF(value > mask_, MARISA_RANGE_ERROR);
-
- const std::size_t pos = i * value_size_;
- const std::size_t unit_id = pos / MARISA_WORD_SIZE;
- const std::size_t unit_offset = pos % MARISA_WORD_SIZE;
-
- units_[unit_id] &= ~((Unit)mask_ << unit_offset);
- units_[unit_id] |= (Unit)(value & mask_) << unit_offset;
- if ((unit_offset + value_size_) > MARISA_WORD_SIZE) {
- units_[unit_id + 1] &=
- ~((Unit)mask_ >> (MARISA_WORD_SIZE - unit_offset));
- units_[unit_id + 1] |=
- (Unit)(value & mask_) >> (MARISA_WORD_SIZE - unit_offset);
- }
- }
-
- // Disallows copy and assignment.
- FlatVector(const FlatVector &);
- FlatVector &operator=(const FlatVector &);
-};
-
-} // namespace vector
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_VECTOR_FLAT_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h b/contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h
deleted file mode 100644
index 6d04cf831d..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_VECTOR_POP_COUNT_H_
-#define MARISA_GRIMOIRE_VECTOR_POP_COUNT_H_
-
-#include "../intrin.h"
-
-namespace marisa {
-namespace grimoire {
-namespace vector {
-
-#if MARISA_WORD_SIZE == 64
-
-class PopCount {
- public:
- explicit PopCount(UInt64 x) : value_() {
- x = (x & 0x5555555555555555ULL) + ((x & 0xAAAAAAAAAAAAAAAAULL) >> 1);
- x = (x & 0x3333333333333333ULL) + ((x & 0xCCCCCCCCCCCCCCCCULL) >> 2);
- x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x & 0xF0F0F0F0F0F0F0F0ULL) >> 4);
- x *= 0x0101010101010101ULL;
- value_ = x;
- }
-
- std::size_t lo8() const {
- return (std::size_t)(value_ & 0xFFU);
- }
- std::size_t lo16() const {
- return (std::size_t)((value_ >> 8) & 0xFFU);
- }
- std::size_t lo24() const {
- return (std::size_t)((value_ >> 16) & 0xFFU);
- }
- std::size_t lo32() const {
- return (std::size_t)((value_ >> 24) & 0xFFU);
- }
- std::size_t lo40() const {
- return (std::size_t)((value_ >> 32) & 0xFFU);
- }
- std::size_t lo48() const {
- return (std::size_t)((value_ >> 40) & 0xFFU);
- }
- std::size_t lo56() const {
- return (std::size_t)((value_ >> 48) & 0xFFU);
- }
- std::size_t lo64() const {
- return (std::size_t)((value_ >> 56) & 0xFFU);
- }
-
- static std::size_t count(UInt64 x) {
-#if defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
- #ifdef _MSC_VER
- return __popcnt64(x);
- #else // _MSC_VER
- return _mm_popcnt_u64(x);
- #endif // _MSC_VER
-#else // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
- return PopCount(x).lo64();
-#endif // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
- }
-
- private:
- UInt64 value_;
-};
-
-#else // MARISA_WORD_SIZE == 64
-
-class PopCount {
- public:
- explicit PopCount(UInt32 x) : value_() {
- x = (x & 0x55555555U) + ((x & 0xAAAAAAAAU) >> 1);
- x = (x & 0x33333333U) + ((x & 0xCCCCCCCCU) >> 2);
- x = (x & 0x0F0F0F0FU) + ((x & 0xF0F0F0F0U) >> 4);
- x *= 0x01010101U;
- value_ = x;
- }
-
- std::size_t lo8() const {
- return value_ & 0xFFU;
- }
- std::size_t lo16() const {
- return (value_ >> 8) & 0xFFU;
- }
- std::size_t lo24() const {
- return (value_ >> 16) & 0xFFU;
- }
- std::size_t lo32() const {
- return (value_ >> 24) & 0xFFU;
- }
-
- static std::size_t count(UInt32 x) {
-#ifdef MARISA_USE_POPCNT
- #ifdef _MSC_VER
- return __popcnt(x);
- #else // _MSC_VER
- return _mm_popcnt_u32(x);
- #endif // _MSC_VER
-#else // MARISA_USE_POPCNT
- return PopCount(x).lo32();
-#endif // MARISA_USE_POPCNT
- }
-
- private:
- UInt32 value_;
-};
-
-#endif // MARISA_WORD_SIZE == 64
-
-} // namespace vector
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_VECTOR_POP_COUNT_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h b/contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h
deleted file mode 100644
index 2403709954..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_VECTOR_RANK_INDEX_H_
-#define MARISA_GRIMOIRE_VECTOR_RANK_INDEX_H_
-
-#include "../../base.h"
-
-namespace marisa {
-namespace grimoire {
-namespace vector {
-
-class RankIndex {
- public:
- RankIndex() : abs_(0), rel_lo_(0), rel_hi_(0) {}
-
- void set_abs(std::size_t value) {
- MARISA_DEBUG_IF(value > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- abs_ = (UInt32)value;
- }
- void set_rel1(std::size_t value) {
- MARISA_DEBUG_IF(value > 64, MARISA_RANGE_ERROR);
- rel_lo_ = (UInt32)((rel_lo_ & ~0x7FU) | (value & 0x7FU));
- }
- void set_rel2(std::size_t value) {
- MARISA_DEBUG_IF(value > 128, MARISA_RANGE_ERROR);
- rel_lo_ = (UInt32)((rel_lo_ & ~(0xFFU << 7)) | ((value & 0xFFU) << 7));
- }
- void set_rel3(std::size_t value) {
- MARISA_DEBUG_IF(value > 192, MARISA_RANGE_ERROR);
- rel_lo_ = (UInt32)((rel_lo_ & ~(0xFFU << 15)) | ((value & 0xFFU) << 15));
- }
- void set_rel4(std::size_t value) {
- MARISA_DEBUG_IF(value > 256, MARISA_RANGE_ERROR);
- rel_lo_ = (UInt32)((rel_lo_ & ~(0x1FFU << 23)) | ((value & 0x1FFU) << 23));
- }
- void set_rel5(std::size_t value) {
- MARISA_DEBUG_IF(value > 320, MARISA_RANGE_ERROR);
- rel_hi_ = (UInt32)((rel_hi_ & ~0x1FFU) | (value & 0x1FFU));
- }
- void set_rel6(std::size_t value) {
- MARISA_DEBUG_IF(value > 384, MARISA_RANGE_ERROR);
- rel_hi_ = (UInt32)((rel_hi_ & ~(0x1FFU << 9)) | ((value & 0x1FFU) << 9));
- }
- void set_rel7(std::size_t value) {
- MARISA_DEBUG_IF(value > 448, MARISA_RANGE_ERROR);
- rel_hi_ = (UInt32)((rel_hi_ & ~(0x1FFU << 18)) | ((value & 0x1FFU) << 18));
- }
-
- std::size_t abs() const {
- return abs_;
- }
- std::size_t rel1() const {
- return rel_lo_ & 0x7FU;
- }
- std::size_t rel2() const {
- return (rel_lo_ >> 7) & 0xFFU;
- }
- std::size_t rel3() const {
- return (rel_lo_ >> 15) & 0xFFU;
- }
- std::size_t rel4() const {
- return (rel_lo_ >> 23) & 0x1FFU;
- }
- std::size_t rel5() const {
- return rel_hi_ & 0x1FFU;
- }
- std::size_t rel6() const {
- return (rel_hi_ >> 9) & 0x1FFU;
- }
- std::size_t rel7() const {
- return (rel_hi_ >> 18) & 0x1FFU;
- }
-
- private:
- UInt32 abs_;
- UInt32 rel_lo_;
- UInt32 rel_hi_;
-};
-
-} // namespace vector
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_VECTOR_RANK_INDEX_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector/vector.h
deleted file mode 100644
index 148cc8b491..0000000000
--- a/contrib/python/marisa-trie/marisa/grimoire/vector/vector.h
+++ /dev/null
@@ -1,257 +0,0 @@
-#pragma once
-#ifndef MARISA_GRIMOIRE_VECTOR_VECTOR_H_
-#define MARISA_GRIMOIRE_VECTOR_VECTOR_H_
-
-#include <new>
-
-#include "../io.h"
-
-namespace marisa {
-namespace grimoire {
-namespace vector {
-
-template <typename T>
-class Vector {
- public:
- Vector()
- : buf_(), objs_(NULL), const_objs_(NULL),
- size_(0), capacity_(0), fixed_(false) {}
- ~Vector() {
- if (objs_ != NULL) {
- for (std::size_t i = 0; i < size_; ++i) {
- objs_[i].~T();
- }
- }
- }
-
- void map(Mapper &mapper) {
- Vector temp;
- temp.map_(mapper);
- swap(temp);
- }
-
- void read(Reader &reader) {
- Vector temp;
- temp.read_(reader);
- swap(temp);
- }
-
- void write(Writer &writer) const {
- write_(writer);
- }
-
- void push_back(const T &x) {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(size_ == max_size(), MARISA_SIZE_ERROR);
- reserve(size_ + 1);
- new (&objs_[size_]) T(x);
- ++size_;
- }
-
- void pop_back() {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
- objs_[--size_].~T();
- }
-
- // resize() assumes that T's placement new does not throw an exception.
- void resize(std::size_t size) {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- reserve(size);
- for (std::size_t i = size_; i < size; ++i) {
- new (&objs_[i]) T;
- }
- for (std::size_t i = size; i < size_; ++i) {
- objs_[i].~T();
- }
- size_ = size;
- }
-
- // resize() assumes that T's placement new does not throw an exception.
- void resize(std::size_t size, const T &x) {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- reserve(size);
- for (std::size_t i = size_; i < size; ++i) {
- new (&objs_[i]) T(x);
- }
- for (std::size_t i = size; i < size_; ++i) {
- objs_[i].~T();
- }
- size_ = size;
- }
-
- void reserve(std::size_t capacity) {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- if (capacity <= capacity_) {
- return;
- }
- MARISA_DEBUG_IF(capacity > max_size(), MARISA_SIZE_ERROR);
- std::size_t new_capacity = capacity;
- if (capacity_ > (capacity / 2)) {
- if (capacity_ > (max_size() / 2)) {
- new_capacity = max_size();
- } else {
- new_capacity = capacity_ * 2;
- }
- }
- realloc(new_capacity);
- }
-
- void shrink() {
- MARISA_THROW_IF(fixed_, MARISA_STATE_ERROR);
- if (size_ != capacity_) {
- realloc(size_);
- }
- }
-
- void fix() {
- MARISA_THROW_IF(fixed_, MARISA_STATE_ERROR);
- fixed_ = true;
- }
-
- const T *begin() const {
- return const_objs_;
- }
- const T *end() const {
- return const_objs_ + size_;
- }
- const T &operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
- return const_objs_[i];
- }
- const T &front() const {
- MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
- return const_objs_[0];
- }
- const T &back() const {
- MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
- return const_objs_[size_ - 1];
- }
-
- T *begin() {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- return objs_;
- }
- T *end() {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- return objs_ + size_;
- }
- T &operator[](std::size_t i) {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
- return objs_[i];
- }
- T &front() {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
- return objs_[0];
- }
- T &back() {
- MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
- MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
- return objs_[size_ - 1];
- }
-
- std::size_t size() const {
- return size_;
- }
- std::size_t capacity() const {
- return capacity_;
- }
- bool fixed() const {
- return fixed_;
- }
-
- bool empty() const {
- return size_ == 0;
- }
- std::size_t total_size() const {
- return sizeof(T) * size_;
- }
- std::size_t io_size() const {
- return sizeof(UInt64) + ((total_size() + 7) & ~(std::size_t)0x07);
- }
-
- void clear() {
- Vector().swap(*this);
- }
- void swap(Vector &rhs) {
- buf_.swap(rhs.buf_);
- marisa::swap(objs_, rhs.objs_);
- marisa::swap(const_objs_, rhs.const_objs_);
- marisa::swap(size_, rhs.size_);
- marisa::swap(capacity_, rhs.capacity_);
- marisa::swap(fixed_, rhs.fixed_);
- }
-
- static std::size_t max_size() {
- return MARISA_SIZE_MAX / sizeof(T);
- }
-
- private:
- scoped_array<char> buf_;
- T *objs_;
- const T *const_objs_;
- std::size_t size_;
- std::size_t capacity_;
- bool fixed_;
-
- void map_(Mapper &mapper) {
- UInt64 total_size;
- mapper.map(&total_size);
- MARISA_THROW_IF(total_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- MARISA_THROW_IF((total_size % sizeof(T)) != 0, MARISA_FORMAT_ERROR);
- const std::size_t size = (std::size_t)(total_size / sizeof(T));
- mapper.map(&const_objs_, size);
- mapper.seek((std::size_t)((8 - (total_size % 8)) % 8));
- size_ = size;
- fix();
- }
- void read_(Reader &reader) {
- UInt64 total_size;
- reader.read(&total_size);
- MARISA_THROW_IF(total_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- MARISA_THROW_IF((total_size % sizeof(T)) != 0, MARISA_FORMAT_ERROR);
- const std::size_t size = (std::size_t)(total_size / sizeof(T));
- resize(size);
- reader.read(objs_, size);
- reader.seek((std::size_t)((8 - (total_size % 8)) % 8));
- }
- void write_(Writer &writer) const {
- writer.write((UInt64)total_size());
- writer.write(const_objs_, size_);
- writer.seek((8 - (total_size() % 8)) % 8);
- }
-
- // realloc() assumes that T's placement new does not throw an exception.
- void realloc(std::size_t new_capacity) {
- MARISA_DEBUG_IF(new_capacity > max_size(), MARISA_SIZE_ERROR);
-
- scoped_array<char> new_buf(
- new (std::nothrow) char[sizeof(T) * new_capacity]);
- MARISA_DEBUG_IF(new_buf.get() == NULL, MARISA_MEMORY_ERROR);
- T *new_objs = reinterpret_cast<T *>(new_buf.get());
-
- for (std::size_t i = 0; i < size_; ++i) {
- new (&new_objs[i]) T(objs_[i]);
- }
- for (std::size_t i = 0; i < size_; ++i) {
- objs_[i].~T();
- }
-
- buf_.swap(new_buf);
- objs_ = new_objs;
- const_objs_ = new_objs;
- capacity_ = new_capacity;
- }
-
- // Disallows copy and assignment.
- Vector(const Vector &);
- Vector &operator=(const Vector &);
-};
-
-} // namespace vector
-} // namespace grimoire
-} // namespace marisa
-
-#endif // MARISA_GRIMOIRE_VECTOR_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/iostream.h b/contrib/python/marisa-trie/marisa/iostream.h
deleted file mode 100644
index da5ec77a6c..0000000000
--- a/contrib/python/marisa-trie/marisa/iostream.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#ifndef MARISA_IOSTREAM_H_
-#define MARISA_IOSTREAM_H_
-
-#include <iosfwd>
-
-namespace marisa {
-
-class Trie;
-
-std::istream &read(std::istream &stream, Trie *trie);
-std::ostream &write(std::ostream &stream, const Trie &trie);
-
-std::istream &operator>>(std::istream &stream, Trie &trie);
-std::ostream &operator<<(std::ostream &stream, const Trie &trie);
-
-} // namespace marisa
-
-#endif // MARISA_IOSTREAM_H_
diff --git a/contrib/python/marisa-trie/marisa/key.h b/contrib/python/marisa-trie/marisa/key.h
deleted file mode 100644
index 48e03226c4..0000000000
--- a/contrib/python/marisa-trie/marisa/key.h
+++ /dev/null
@@ -1,86 +0,0 @@
-#pragma once
-#ifndef MARISA_KEY_H_
-#define MARISA_KEY_H_
-
-#include "base.h"
-
-namespace marisa {
-
-class Key {
- public:
- Key() : ptr_(NULL), length_(0), union_() {
- union_.id = 0;
- }
- Key(const Key &key)
- : ptr_(key.ptr_), length_(key.length_), union_(key.union_) {}
-
- Key &operator=(const Key &key) {
- ptr_ = key.ptr_;
- length_ = key.length_;
- union_ = key.union_;
- return *this;
- }
-
- char operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
- return ptr_[i];
- }
-
- void set_str(const char *str) {
- MARISA_DEBUG_IF(str == NULL, MARISA_NULL_ERROR);
- std::size_t length = 0;
- while (str[length] != '\0') {
- ++length;
- }
- MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- ptr_ = str;
- length_ = (UInt32)length;
- }
- void set_str(const char *ptr, std::size_t length) {
- MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- ptr_ = ptr;
- length_ = (UInt32)length;
- }
- void set_id(std::size_t id) {
- MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
- union_.id = (UInt32)id;
- }
- void set_weight(float weight) {
- union_.weight = weight;
- }
-
- const char *ptr() const {
- return ptr_;
- }
- std::size_t length() const {
- return length_;
- }
- std::size_t id() const {
- return union_.id;
- }
- float weight() const {
- return union_.weight;
- }
-
- void clear() {
- Key().swap(*this);
- }
- void swap(Key &rhs) {
- marisa::swap(ptr_, rhs.ptr_);
- marisa::swap(length_, rhs.length_);
- marisa::swap(union_.id, rhs.union_.id);
- }
-
- private:
- const char *ptr_;
- UInt32 length_;
- union Union {
- UInt32 id;
- float weight;
- } union_;
-};
-
-} // namespace marisa
-
-#endif // MARISA_KEY_H_
diff --git a/contrib/python/marisa-trie/marisa/keyset.cc b/contrib/python/marisa-trie/marisa/keyset.cc
deleted file mode 100644
index adb82b31fe..0000000000
--- a/contrib/python/marisa-trie/marisa/keyset.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-#include <new>
-
-#include "keyset.h"
-
-namespace marisa {
-
-Keyset::Keyset()
- : base_blocks_(), base_blocks_size_(0), base_blocks_capacity_(0),
- extra_blocks_(), extra_blocks_size_(0), extra_blocks_capacity_(0),
- key_blocks_(), key_blocks_size_(0), key_blocks_capacity_(0),
- ptr_(NULL), avail_(0), size_(0), total_length_(0) {}
-
-void Keyset::push_back(const Key &key) {
- MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
-
- char * const key_ptr = reserve(key.length());
- for (std::size_t i = 0; i < key.length(); ++i) {
- key_ptr[i] = key[i];
- }
-
- Key &new_key = key_blocks_[size_ / KEY_BLOCK_SIZE][size_ % KEY_BLOCK_SIZE];
- new_key.set_str(key_ptr, key.length());
- new_key.set_id(key.id());
- ++size_;
- total_length_ += new_key.length();
-}
-
-void Keyset::push_back(const Key &key, char end_marker) {
- MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
-
- if ((size_ / KEY_BLOCK_SIZE) == key_blocks_size_) {
- append_key_block();
- }
-
- char * const key_ptr = reserve(key.length() + 1);
- for (std::size_t i = 0; i < key.length(); ++i) {
- key_ptr[i] = key[i];
- }
- key_ptr[key.length()] = end_marker;
-
- Key &new_key = key_blocks_[size_ / KEY_BLOCK_SIZE][size_ % KEY_BLOCK_SIZE];
- new_key.set_str(key_ptr, key.length());
- new_key.set_id(key.id());
- ++size_;
- total_length_ += new_key.length();
-}
-
-void Keyset::push_back(const char *str) {
- MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- MARISA_THROW_IF(str == NULL, MARISA_NULL_ERROR);
-
- std::size_t length = 0;
- while (str[length] != '\0') {
- ++length;
- }
- push_back(str, length);
-}
-
-void Keyset::push_back(const char *ptr, std::size_t length, float weight) {
- MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
- MARISA_THROW_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- MARISA_THROW_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
-
- char * const key_ptr = reserve(length);
- for (std::size_t i = 0; i < length; ++i) {
- key_ptr[i] = ptr[i];
- }
-
- Key &key = key_blocks_[size_ / KEY_BLOCK_SIZE][size_ % KEY_BLOCK_SIZE];
- key.set_str(key_ptr, length);
- key.set_weight(weight);
- ++size_;
- total_length_ += length;
-}
-
-void Keyset::reset() {
- base_blocks_size_ = 0;
- extra_blocks_size_ = 0;
- ptr_ = NULL;
- avail_ = 0;
- size_ = 0;
- total_length_ = 0;
-}
-
-void Keyset::clear() {
- Keyset().swap(*this);
-}
-
-void Keyset::swap(Keyset &rhs) {
- base_blocks_.swap(rhs.base_blocks_);
- marisa::swap(base_blocks_size_, rhs.base_blocks_size_);
- marisa::swap(base_blocks_capacity_, rhs.base_blocks_capacity_);
- extra_blocks_.swap(rhs.extra_blocks_);
- marisa::swap(extra_blocks_size_, rhs.extra_blocks_size_);
- marisa::swap(extra_blocks_capacity_, rhs.extra_blocks_capacity_);
- key_blocks_.swap(rhs.key_blocks_);
- marisa::swap(key_blocks_size_, rhs.key_blocks_size_);
- marisa::swap(key_blocks_capacity_, rhs.key_blocks_capacity_);
- marisa::swap(ptr_, rhs.ptr_);
- marisa::swap(avail_, rhs.avail_);
- marisa::swap(size_, rhs.size_);
- marisa::swap(total_length_, rhs.total_length_);
-}
-
-char *Keyset::reserve(std::size_t size) {
- if ((size_ / KEY_BLOCK_SIZE) == key_blocks_size_) {
- append_key_block();
- }
-
- if (size > EXTRA_BLOCK_SIZE) {
- append_extra_block(size);
- return extra_blocks_[extra_blocks_size_ - 1].get();
- } else {
- if (size > avail_) {
- append_base_block();
- }
- ptr_ += size;
- avail_ -= size;
- return ptr_ - size;
- }
-}
-
-void Keyset::append_base_block() {
- if (base_blocks_size_ == base_blocks_capacity_) {
- const std::size_t new_capacity =
- (base_blocks_size_ != 0) ? (base_blocks_size_ * 2) : 1;
- scoped_array<scoped_array<char> > new_blocks(
- new (std::nothrow) scoped_array<char>[new_capacity]);
- MARISA_THROW_IF(new_blocks.get() == NULL, MARISA_MEMORY_ERROR);
- for (std::size_t i = 0; i < base_blocks_size_; ++i) {
- base_blocks_[i].swap(new_blocks[i]);
- }
- base_blocks_.swap(new_blocks);
- base_blocks_capacity_ = new_capacity;
- }
- if (base_blocks_[base_blocks_size_].get() == NULL) {
- scoped_array<char> new_block(new (std::nothrow) char[BASE_BLOCK_SIZE]);
- MARISA_THROW_IF(new_block.get() == NULL, MARISA_MEMORY_ERROR);
- base_blocks_[base_blocks_size_].swap(new_block);
- }
- ptr_ = base_blocks_[base_blocks_size_++].get();
- avail_ = BASE_BLOCK_SIZE;
-}
-
-void Keyset::append_extra_block(std::size_t size) {
- if (extra_blocks_size_ == extra_blocks_capacity_) {
- const std::size_t new_capacity =
- (extra_blocks_size_ != 0) ? (extra_blocks_size_ * 2) : 1;
- scoped_array<scoped_array<char> > new_blocks(
- new (std::nothrow) scoped_array<char>[new_capacity]);
- MARISA_THROW_IF(new_blocks.get() == NULL, MARISA_MEMORY_ERROR);
- for (std::size_t i = 0; i < extra_blocks_size_; ++i) {
- extra_blocks_[i].swap(new_blocks[i]);
- }
- extra_blocks_.swap(new_blocks);
- extra_blocks_capacity_ = new_capacity;
- }
- scoped_array<char> new_block(new (std::nothrow) char[size]);
- MARISA_THROW_IF(new_block.get() == NULL, MARISA_MEMORY_ERROR);
- extra_blocks_[extra_blocks_size_++].swap(new_block);
-}
-
-void Keyset::append_key_block() {
- if (key_blocks_size_ == key_blocks_capacity_) {
- const std::size_t new_capacity =
- (key_blocks_size_ != 0) ? (key_blocks_size_ * 2) : 1;
- scoped_array<scoped_array<Key> > new_blocks(
- new (std::nothrow) scoped_array<Key>[new_capacity]);
- MARISA_THROW_IF(new_blocks.get() == NULL, MARISA_MEMORY_ERROR);
- for (std::size_t i = 0; i < key_blocks_size_; ++i) {
- key_blocks_[i].swap(new_blocks[i]);
- }
- key_blocks_.swap(new_blocks);
- key_blocks_capacity_ = new_capacity;
- }
- scoped_array<Key> new_block(new (std::nothrow) Key[KEY_BLOCK_SIZE]);
- MARISA_THROW_IF(new_block.get() == NULL, MARISA_MEMORY_ERROR);
- key_blocks_[key_blocks_size_++].swap(new_block);
-}
-
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/keyset.h b/contrib/python/marisa-trie/marisa/keyset.h
deleted file mode 100644
index 86762dba47..0000000000
--- a/contrib/python/marisa-trie/marisa/keyset.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-#ifndef MARISA_KEYSET_H_
-#define MARISA_KEYSET_H_
-
-#include "key.h"
-
-namespace marisa {
-
-class Keyset {
- public:
- enum {
- BASE_BLOCK_SIZE = 4096,
- EXTRA_BLOCK_SIZE = 1024,
- KEY_BLOCK_SIZE = 256
- };
-
- Keyset();
-
- void push_back(const Key &key);
- void push_back(const Key &key, char end_marker);
-
- void push_back(const char *str);
- void push_back(const char *ptr, std::size_t length, float weight = 1.0);
-
- const Key &operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
- return key_blocks_[i / KEY_BLOCK_SIZE][i % KEY_BLOCK_SIZE];
- }
- Key &operator[](std::size_t i) {
- MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
- return key_blocks_[i / KEY_BLOCK_SIZE][i % KEY_BLOCK_SIZE];
- }
-
- std::size_t num_keys() const {
- return size_;
- }
-
- bool empty() const {
- return size_ == 0;
- }
- std::size_t size() const {
- return size_;
- }
- std::size_t total_length() const {
- return total_length_;
- }
-
- void reset();
-
- void clear();
- void swap(Keyset &rhs);
-
- private:
- scoped_array<scoped_array<char> > base_blocks_;
- std::size_t base_blocks_size_;
- std::size_t base_blocks_capacity_;
- scoped_array<scoped_array<char> > extra_blocks_;
- std::size_t extra_blocks_size_;
- std::size_t extra_blocks_capacity_;
- scoped_array<scoped_array<Key> > key_blocks_;
- std::size_t key_blocks_size_;
- std::size_t key_blocks_capacity_;
- char *ptr_;
- std::size_t avail_;
- std::size_t size_;
- std::size_t total_length_;
-
- char *reserve(std::size_t size);
-
- void append_base_block();
- void append_extra_block(std::size_t size);
- void append_key_block();
-
- // Disallows copy and assignment.
- Keyset(const Keyset &);
- Keyset &operator=(const Keyset &);
-};
-
-} // namespace marisa
-
-#endif // MARISA_KEYSET_H_
diff --git a/contrib/python/marisa-trie/marisa/query.h b/contrib/python/marisa-trie/marisa/query.h
deleted file mode 100644
index e08f8f72dc..0000000000
--- a/contrib/python/marisa-trie/marisa/query.h
+++ /dev/null
@@ -1,72 +0,0 @@
-#pragma once
-#ifndef MARISA_QUERY_H_
-#define MARISA_QUERY_H_
-
-#include "base.h"
-
-namespace marisa {
-
-class Query {
- public:
- Query() : ptr_(NULL), length_(0), id_(0) {}
- Query(const Query &query)
- : ptr_(query.ptr_), length_(query.length_), id_(query.id_) {}
-
- Query &operator=(const Query &query) {
- ptr_ = query.ptr_;
- length_ = query.length_;
- id_ = query.id_;
- return *this;
- }
-
- char operator[](std::size_t i) const {
- MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
- return ptr_[i];
- }
-
- void set_str(const char *str) {
- MARISA_DEBUG_IF(str == NULL, MARISA_NULL_ERROR);
- std::size_t length = 0;
- while (str[length] != '\0') {
- ++length;
- }
- ptr_ = str;
- length_ = length;
- }
- void set_str(const char *ptr, std::size_t length) {
- MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
- ptr_ = ptr;
- length_ = length;
- }
- void set_id(std::size_t id) {
- id_ = id;
- }
-
- const char *ptr() const {
- return ptr_;
- }
- std::size_t length() const {
- return length_;
- }
- std::size_t id() const {
- return id_;
- }
-
- void clear() {
- Query().swap(*this);
- }
- void swap(Query &rhs) {
- marisa::swap(ptr_, rhs.ptr_);
- marisa::swap(length_, rhs.length_);
- marisa::swap(id_, rhs.id_);
- }
-
- private:
- const char *ptr_;
- std::size_t length_;
- std::size_t id_;
-};
-
-} // namespace marisa
-
-#endif // MARISA_QUERY_H_
diff --git a/contrib/python/marisa-trie/marisa/scoped-array.h b/contrib/python/marisa-trie/marisa/scoped-array.h
deleted file mode 100644
index 210cb908a7..0000000000
--- a/contrib/python/marisa-trie/marisa/scoped-array.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-#ifndef MARISA_SCOPED_ARRAY_H_
-#define MARISA_SCOPED_ARRAY_H_
-
-#include "base.h"
-
-namespace marisa {
-
-template <typename T>
-class scoped_array {
- public:
- scoped_array() : array_(NULL) {}
- explicit scoped_array(T *array) : array_(array) {}
-
- ~scoped_array() {
- delete [] array_;
- }
-
- void reset(T *array = NULL) {
- MARISA_THROW_IF((array != NULL) && (array == array_), MARISA_RESET_ERROR);
- scoped_array(array).swap(*this);
- }
-
- T &operator[](std::size_t i) const {
- MARISA_DEBUG_IF(array_ == NULL, MARISA_STATE_ERROR);
- return array_[i];
- }
- T *get() const {
- return array_;
- }
-
- void clear() {
- scoped_array().swap(*this);
- }
- void swap(scoped_array &rhs) {
- marisa::swap(array_, rhs.array_);
- }
-
- private:
- T *array_;
-
- // Disallows copy and assignment.
- scoped_array(const scoped_array &);
- scoped_array &operator=(const scoped_array &);
-};
-
-} // namespace marisa
-
-#endif // MARISA_SCOPED_ARRAY_H_
diff --git a/contrib/python/marisa-trie/marisa/scoped-ptr.h b/contrib/python/marisa-trie/marisa/scoped-ptr.h
deleted file mode 100644
index 9a9c447353..0000000000
--- a/contrib/python/marisa-trie/marisa/scoped-ptr.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#pragma once
-#ifndef MARISA_SCOPED_PTR_H_
-#define MARISA_SCOPED_PTR_H_
-
-#include "base.h"
-
-namespace marisa {
-
-template <typename T>
-class scoped_ptr {
- public:
- scoped_ptr() : ptr_(NULL) {}
- explicit scoped_ptr(T *ptr) : ptr_(ptr) {}
-
- ~scoped_ptr() {
- delete ptr_;
- }
-
- void reset(T *ptr = NULL) {
- MARISA_THROW_IF((ptr != NULL) && (ptr == ptr_), MARISA_RESET_ERROR);
- scoped_ptr(ptr).swap(*this);
- }
-
- T &operator*() const {
- MARISA_DEBUG_IF(ptr_ == NULL, MARISA_STATE_ERROR);
- return *ptr_;
- }
- T *operator->() const {
- MARISA_DEBUG_IF(ptr_ == NULL, MARISA_STATE_ERROR);
- return ptr_;
- }
- T *get() const {
- return ptr_;
- }
-
- void clear() {
- scoped_ptr().swap(*this);
- }
- void swap(scoped_ptr &rhs) {
- marisa::swap(ptr_, rhs.ptr_);
- }
-
- private:
- T *ptr_;
-
- // Disallows copy and assignment.
- scoped_ptr(const scoped_ptr &);
- scoped_ptr &operator=(const scoped_ptr &);
-};
-
-} // namespace marisa
-
-#endif // MARISA_SCOPED_PTR_H_
diff --git a/contrib/python/marisa-trie/marisa/stdio.h b/contrib/python/marisa-trie/marisa/stdio.h
deleted file mode 100644
index 334ce56816..0000000000
--- a/contrib/python/marisa-trie/marisa/stdio.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-#ifndef MARISA_MYSTDIO_H_
-#define MARISA_MYSTDIO_H_
-
-#include <cstdio>
-
-namespace marisa {
-
-class Trie;
-
-void fread(std::FILE *file, Trie *trie);
-void fwrite(std::FILE *file, const Trie &trie);
-
-} // namespace marisa
-
-#endif // MARISA_MYSTDIO_H_
diff --git a/contrib/python/marisa-trie/marisa/trie.cc b/contrib/python/marisa-trie/marisa/trie.cc
deleted file mode 100644
index 5baaf9b288..0000000000
--- a/contrib/python/marisa-trie/marisa/trie.cc
+++ /dev/null
@@ -1,249 +0,0 @@
-#include "stdio.h"
-#include "iostream.h"
-#include "trie.h"
-#include "grimoire/trie.h"
-
-namespace marisa {
-
-Trie::Trie() : trie_() {}
-
-Trie::~Trie() {}
-
-void Trie::build(Keyset &keyset, int config_flags) {
- scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- temp->build(keyset, config_flags);
- trie_.swap(temp);
-}
-
-void Trie::mmap(const char *filename) {
- MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
-
- scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- grimoire::Mapper mapper;
- mapper.open(filename);
- temp->map(mapper);
- trie_.swap(temp);
-}
-
-void Trie::map(const void *ptr, std::size_t size) {
- MARISA_THROW_IF((ptr == NULL) && (size != 0), MARISA_NULL_ERROR);
-
- scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- grimoire::Mapper mapper;
- mapper.open(ptr, size);
- temp->map(mapper);
- trie_.swap(temp);
-}
-
-void Trie::load(const char *filename) {
- MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
-
- scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- grimoire::Reader reader;
- reader.open(filename);
- temp->read(reader);
- trie_.swap(temp);
-}
-
-void Trie::read(int fd) {
- MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
-
- scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- grimoire::Reader reader;
- reader.open(fd);
- temp->read(reader);
- trie_.swap(temp);
-}
-
-void Trie::save(const char *filename) const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
-
- grimoire::Writer writer;
- writer.open(filename);
- trie_->write(writer);
-}
-
-void Trie::write(int fd) const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
-
- grimoire::Writer writer;
- writer.open(fd);
- trie_->write(writer);
-}
-
-bool Trie::lookup(Agent &agent) const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- if (!agent.has_state()) {
- agent.init_state();
- }
- return trie_->lookup(agent);
-}
-
-void Trie::reverse_lookup(Agent &agent) const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- if (!agent.has_state()) {
- agent.init_state();
- }
- trie_->reverse_lookup(agent);
-}
-
-bool Trie::common_prefix_search(Agent &agent) const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- if (!agent.has_state()) {
- agent.init_state();
- }
- return trie_->common_prefix_search(agent);
-}
-
-bool Trie::predictive_search(Agent &agent) const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- if (!agent.has_state()) {
- agent.init_state();
- }
- return trie_->predictive_search(agent);
-}
-
-std::size_t Trie::num_tries() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->num_tries();
-}
-
-std::size_t Trie::num_keys() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->num_keys();
-}
-
-std::size_t Trie::num_nodes() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->num_nodes();
-}
-
-TailMode Trie::tail_mode() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->tail_mode();
-}
-
-NodeOrder Trie::node_order() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->node_order();
-}
-
-bool Trie::empty() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->empty();
-}
-
-std::size_t Trie::size() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->size();
-}
-
-std::size_t Trie::total_size() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->total_size();
-}
-
-std::size_t Trie::io_size() const {
- MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
- return trie_->io_size();
-}
-
-void Trie::clear() {
- Trie().swap(*this);
-}
-
-void Trie::swap(Trie &rhs) {
- trie_.swap(rhs.trie_);
-}
-
-} // namespace marisa
-
-#include <iostream>
-
-namespace marisa {
-
-class TrieIO {
- public:
- static void fread(std::FILE *file, Trie *trie) {
- MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
-
- scoped_ptr<grimoire::LoudsTrie> temp(
- new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- grimoire::Reader reader;
- reader.open(file);
- temp->read(reader);
- trie->trie_.swap(temp);
- }
- static void fwrite(std::FILE *file, const Trie &trie) {
- MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
- MARISA_THROW_IF(trie.trie_.get() == NULL, MARISA_STATE_ERROR);
- grimoire::Writer writer;
- writer.open(file);
- trie.trie_->write(writer);
- }
-
- static std::istream &read(std::istream &stream, Trie *trie) {
- MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
-
- scoped_ptr<grimoire::LoudsTrie> temp(
- new (std::nothrow) grimoire::LoudsTrie);
- MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
-
- grimoire::Reader reader;
- reader.open(stream);
- temp->read(reader);
- trie->trie_.swap(temp);
- return stream;
- }
- static std::ostream &write(std::ostream &stream, const Trie &trie) {
- MARISA_THROW_IF(trie.trie_.get() == NULL, MARISA_STATE_ERROR);
- grimoire::Writer writer;
- writer.open(stream);
- trie.trie_->write(writer);
- return stream;
- }
-};
-
-void fread(std::FILE *file, Trie *trie) {
- MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
- MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
- TrieIO::fread(file, trie);
-}
-
-void fwrite(std::FILE *file, const Trie &trie) {
- MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
- TrieIO::fwrite(file, trie);
-}
-
-std::istream &read(std::istream &stream, Trie *trie) {
- MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
- return TrieIO::read(stream, trie);
-}
-
-std::ostream &write(std::ostream &stream, const Trie &trie) {
- return TrieIO::write(stream, trie);
-}
-
-std::istream &operator>>(std::istream &stream, Trie &trie) {
- return read(stream, &trie);
-}
-
-std::ostream &operator<<(std::ostream &stream, const Trie &trie) {
- return write(stream, trie);
-}
-
-} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/trie.h b/contrib/python/marisa-trie/marisa/trie.h
deleted file mode 100644
index df85bd86ba..0000000000
--- a/contrib/python/marisa-trie/marisa/trie.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-#ifndef MARISA_TRIE_H_
-#define MARISA_TRIE_H_
-
-#include "keyset.h"
-#include "agent.h"
-
-namespace marisa {
-namespace grimoire {
-namespace trie {
-
-class LoudsTrie;
-
-} // namespace trie
-} // namespace grimoire
-
-class Trie {
- friend class TrieIO;
-
- public:
- Trie();
- ~Trie();
-
- void build(Keyset &keyset, int config_flags = 0);
-
- void mmap(const char *filename);
- void map(const void *ptr, std::size_t size);
-
- void load(const char *filename);
- void read(int fd);
-
- void save(const char *filename) const;
- void write(int fd) const;
-
- bool lookup(Agent &agent) const;
- void reverse_lookup(Agent &agent) const;
- bool common_prefix_search(Agent &agent) const;
- bool predictive_search(Agent &agent) const;
-
- std::size_t num_tries() const;
- std::size_t num_keys() const;
- std::size_t num_nodes() const;
-
- TailMode tail_mode() const;
- NodeOrder node_order() const;
-
- bool empty() const;
- std::size_t size() const;
- std::size_t total_size() const;
- std::size_t io_size() const;
-
- void clear();
- void swap(Trie &rhs);
-
- private:
- scoped_ptr<grimoire::trie::LoudsTrie> trie_;
-
- // Disallows copy and assignment.
- Trie(const Trie &);
- Trie &operator=(const Trie &);
-};
-
-} // namespace marisa
-
-#endif // MARISA_TRIE_H_
diff --git a/contrib/python/marisa-trie/marisa_trie.pyx b/contrib/python/marisa-trie/marisa_trie.pyx
deleted file mode 100644
index f9fe6f331b..0000000000
--- a/contrib/python/marisa-trie/marisa_trie.pyx
+++ /dev/null
@@ -1,763 +0,0 @@
-# cython: profile=False, embedsignature=True
-
-from __future__ import unicode_literals
-
-from std_iostream cimport stringstream, istream, ostream
-from libc.string cimport strncmp
-cimport keyset
-cimport key
-cimport agent
-cimport trie
-cimport iostream
-cimport base
-
-import itertools
-import struct
-import warnings
-
-try:
- from itertools import izip
-except ImportError:
- izip = zip
-
-
-DEFAULT_CACHE = base.MARISA_DEFAULT_CACHE
-HUGE_CACHE = base.MARISA_HUGE_CACHE
-LARGE_CACHE = base.MARISA_LARGE_CACHE
-NORMAL_CACHE = base.MARISA_NORMAL_CACHE
-SMALL_CACHE = base.MARISA_SMALL_CACHE
-TINY_CACHE = base.MARISA_TINY_CACHE
-
-MIN_NUM_TRIES = base.MARISA_MIN_NUM_TRIES
-MAX_NUM_TRIES = base.MARISA_MAX_NUM_TRIES
-DEFAULT_NUM_TRIES = base.MARISA_DEFAULT_NUM_TRIES
-
-# MARISA_TEXT_TAIL merges last labels as zero-terminated strings. So, it is
-# available if and only if the last labels do not contain a NULL character.
-# If MARISA_TEXT_TAIL is specified and a NULL character exists in the last
-# labels, the setting is automatically switched to MARISA_BINARY_TAIL.
-TEXT_TAIL = base.MARISA_TEXT_TAIL
-
-# MARISA_BINARY_TAIL also merges last labels but as byte sequences. It uses
-# a bit vector to detect the end of a sequence, instead of NULL characters.
-# So, MARISA_BINARY_TAIL requires a larger space if the average length of
-# labels is greater than 8.
-BINARY_TAIL = base.MARISA_BINARY_TAIL
-DEFAULT_TAIL = base.MARISA_DEFAULT_TAIL
-
-
-# MARISA_LABEL_ORDER arranges nodes in ascending label order.
-# MARISA_LABEL_ORDER is useful if an application needs to predict keys in
-# label order.
-LABEL_ORDER = base.MARISA_LABEL_ORDER
-
-# MARISA_WEIGHT_ORDER arranges nodes in descending weight order.
-# MARISA_WEIGHT_ORDER is generally a better choice because it enables faster
-# matching.
-WEIGHT_ORDER = base.MARISA_WEIGHT_ORDER
-DEFAULT_ORDER = base.MARISA_DEFAULT_ORDER
-
-
-cdef class _Trie:
- cdef trie.Trie* _trie
-
- cdef bytes _encode_key(self, key):
- return key
-
- cdef _get_key(self, agent.Agent& ag):
- return ag.key().ptr()[:ag.key().length()]
-
- def __init__(self, arg=None, num_tries=DEFAULT_NUM_TRIES, binary=False,
- cache_size=DEFAULT_CACHE, order=DEFAULT_ORDER, weights=None):
- """
- ``arg`` can be one of the following:
-
- * an iterable with bytes keys;
- * None (if you're going to load a trie later).
-
- Pass a ``weights`` iterable with expected lookup frequencies
- to optimize lookup and prefix search speed.
- """
-
- if self._trie:
- return
- self._trie = new trie.Trie()
-
- byte_keys = (self._encode_key(key) for key in (arg or []))
-
- self._build(
- byte_keys,
- weights,
- num_tries=num_tries,
- binary=binary,
- cache_size=cache_size,
- order=order
- )
-
- def __dealloc__(self):
- if self._trie:
- del self._trie
-
- def _config_flags(self, num_tries=DEFAULT_NUM_TRIES, binary=False,
- cache_size=DEFAULT_CACHE, order=DEFAULT_ORDER):
- if not MIN_NUM_TRIES <= num_tries <= MAX_NUM_TRIES:
- raise ValueError(
- "num_tries (which is %d) must be between between %d and %d" %
- (num_tries, MIN_NUM_TRIES, MAX_NUM_TRIES))
-
- binary_flag = BINARY_TAIL if binary else TEXT_TAIL
- return num_tries | binary_flag | cache_size | order
-
- def _build(self, byte_keys, weights=None, **options):
- if weights is None:
- weights = itertools.repeat(1.0)
-
- cdef char* data
- cdef float weight
- cdef keyset.Keyset *ks = new keyset.Keyset()
-
- try:
- for key, weight in izip(byte_keys, weights):
- ks.push_back(<char *>key, len(key), weight)
- self._trie.build(ks[0], self._config_flags(**options))
- finally:
- del ks
-
- def __richcmp__(self, other, int op):
- if op == 2: # ==
- if other is self:
- return True
- elif not isinstance(other, _Trie):
- return False
-
- return (<_Trie>self)._equals(other)
- elif op == 3: # !=
- return not (self == other)
-
- raise TypeError("unorderable types: {0} and {1}".format(
- self.__class__, other.__class__))
-
- cdef bint _equals(self, _Trie other) nogil:
- cdef int num_keys = self._trie.num_keys()
- cdef base.NodeOrder node_order = self._trie.node_order()
- if (other._trie.num_keys() != num_keys or
- other._trie.node_order() != node_order):
- return False
-
- cdef agent.Agent ag1, ag2
- ag1.set_query(b"")
- ag2.set_query(b"")
- cdef int i
- cdef key.Key key1, key2
- for i in range(num_keys):
- self._trie.predictive_search(ag1)
- other._trie.predictive_search(ag2)
- key1 = ag1.key()
- key2 = ag2.key()
- if (key1.length() != key2.length() or
- strncmp(key1.ptr(), key2.ptr(), key1.length()) != 0):
- return False
- return True
-
- def __iter__(self):
- return self.iterkeys()
-
- def __len__(self):
- return self._trie.num_keys()
-
- def __contains__(self, key):
- cdef bytes _key = self._encode_key(key)
- return self._contains(_key)
-
- cdef bint _contains(self, bytes key):
- cdef agent.Agent ag
- ag.set_query(key, len(key))
- return self._trie.lookup(ag)
-
- def read(self, f):
- """Read a trie from an open file.
-
- :param file f: a "real" on-disk file object. Passing a *file-like*
- object would result in an error.
-
- .. deprecated:: 0.7.3
-
- The method will be removed in version 0.8.0. Please use
- :meth:`load` instead.
- """
- warnings.warn("Trie.save is deprecated and will "
- "be removed in marisa_trie 0.8.0. Please use "
- "Trie.load instead.", DeprecationWarning)
- self._trie.read(f.fileno())
- return self
-
- def write(self, f):
- """Write a trie to an open file.
-
- :param file f: a "real" on-disk file object. Passing a *file-like*
- object would result in an error.
-
- .. deprecated:: 0.7.3
-
- The method will be removed in version 0.8.0. Please use
- :meth:`save` instead.
- """
- warnings.warn("Trie.write is deprecated and will "
- "be removed in marisa_trie 0.8.0. Please use "
- "Trie.save instead.", DeprecationWarning)
- self._trie.write(f.fileno())
-
- def save(self, path):
- """Save a trie to a specified path."""
- with open(path, 'w') as f:
- self._trie.write(f.fileno())
-
- def load(self, path):
- """Load a trie from a specified path."""
- with open(path, 'r') as f:
- self._trie.read(f.fileno())
- return self
-
- cpdef bytes tobytes(self) except +:
- """Return raw trie content as bytes."""
- cdef stringstream stream
- iostream.write((<ostream *> &stream)[0], self._trie[0])
- cdef bytes res = stream.str()
- return res
-
- cpdef frombytes(self, bytes data) except +:
- """Load a trie from raw bytes generated by :meth:`tobytes`."""
- cdef stringstream* stream = new stringstream(data)
- try:
- iostream.read((<istream *> stream)[0], self._trie)
- finally:
- del stream
- return self
-
- def __reduce__(self):
- return self.__class__, (), self.tobytes()
-
- __setstate__ = frombytes
-
- def mmap(self, path):
- """Memory map the content of a trie stored in a file.
-
- This allows to query trie without loading it fully in memory.
- """
- import sys
- str_path = path.encode(sys.getfilesystemencoding())
- cdef char* c_path = str_path
- self._trie.mmap(c_path)
- return self
-
- def iterkeys(self, prefix=None):
- """
- Return an iterator over trie keys starting with a given ``prefix``.
- """
- cdef agent.Agent ag
- cdef bytes b_prefix = b''
- if prefix is not None:
- b_prefix = self._encode_key(prefix)
- ag.set_query(b_prefix, len(b_prefix))
-
- while self._trie.predictive_search(ag):
- yield self._get_key(ag)
-
- cpdef list keys(self, prefix=None):
- """Return a list of trie keys starting with a given ``prefix``."""
- # non-generator inlined version of iterkeys()
- cdef list res = []
- cdef bytes b_prefix = b''
- if prefix is not None:
- b_prefix = self._encode_key(prefix)
- cdef agent.Agent ag
- ag.set_query(b_prefix, len(b_prefix))
-
- while self._trie.predictive_search(ag):
- res.append(self._get_key(ag))
-
- return res
-
- def has_keys_with_prefix(self, prefix=""):
- """
- Return ``True`` if any key in the trie begins with ``prefix``.
-
- .. deprecated:: 0.7.3
-
- The method will be removed in version 0.8.0. Please use
- :meth:`iterkeys` instead.
- """
- warnings.warn("Trie.has_keys_with_prefix is deprecated and will "
- "be removed in marisa_trie 0.8.0. Please use "
- "Trie.iterkeys instead.", DeprecationWarning)
-
- cdef agent.Agent ag
- cdef bytes b_prefix = self._encode_key(prefix)
- ag.set_query(b_prefix, len(b_prefix))
- return self._trie.predictive_search(ag)
-
-
-cdef class BinaryTrie(_Trie):
- """A trie mapping bytes keys to auto-generated unique IDs."""
-
- # key_id method is not in _Trie because it won't work for BytesTrie
- cpdef int key_id(self, bytes key) except -1:
- """Return an ID generated for a given ``key``.
-
- :raises KeyError: if key is not present in this trie.
- """
- cdef int res = self._key_id(key, len(key))
- if res == -1:
- raise KeyError(key)
- return res
-
- cdef int _key_id(self, char* key, int len):
- cdef bint res
- cdef agent.Agent ag
- ag.set_query(key, len)
- res = self._trie.lookup(ag)
- if not res:
- return -1
- return ag.key().id()
-
- cpdef restore_key(self, int index):
- """Return a key corresponding to a given ID."""
- cdef agent.Agent ag
- ag.set_query(index)
- try:
- self._trie.reverse_lookup(ag)
- except KeyError:
- raise KeyError(index)
- return self._get_key(ag)
-
- def __getitem__(self, bytes key):
- return self.key_id(key)
-
- def get(self, bytes key, default=None):
- """
- Return an ID for a given ``key`` or ``default`` if ``key`` is
- not present in this trie.
- """
- cdef int res
-
- res = self._key_id(key, len(key))
- if res == -1:
- return default
- return res
-
- def iter_prefixes(self, bytes key):
- """
- Return an iterator of all prefixes of a given key.
- """
- cdef agent.Agent ag
- ag.set_query(key, len(key))
-
- while self._trie.common_prefix_search(ag):
- yield self._get_key(ag)
-
- def prefixes(self, bytes key):
- """
- Return a list with all prefixes of a given key.
- """
- # this an inlined version of ``list(self.iter_prefixes(key))``
-
- cdef list res = []
- cdef agent.Agent ag
- ag.set_query(key, len(key))
-
- while self._trie.common_prefix_search(ag):
- res.append(self._get_key(ag))
- return res
-
- def items(self, bytes prefix=b""):
- # inlined for speed
- cdef list res = []
- cdef agent.Agent ag
- ag.set_query(prefix, len(prefix))
-
- while self._trie.predictive_search(ag):
- res.append((self._get_key(ag), ag.key().id()))
-
- return res
-
- def iteritems(self, bytes prefix=b""):
- """
- Return an iterator over items that have a prefix ``prefix``.
- """
- cdef agent.Agent ag
- ag.set_query(prefix, len(prefix))
-
- while self._trie.predictive_search(ag):
- yield self._get_key(ag), ag.key().id()
-
-
-cdef class _UnicodeKeyedTrie(_Trie):
- """
- MARISA-trie wrapper for unicode keys.
- """
- cdef bytes _encode_key(self, key):
- return key.encode('utf8')
-
- cdef _get_key(self, agent.Agent& ag):
- return <unicode>_Trie._get_key(self, ag).decode('utf8')
-
-
-cdef class Trie(_UnicodeKeyedTrie):
- """A trie mapping unicode keys to auto-generated unique IDs."""
-
- # key_id method is not in _Trie because it won't work for BytesTrie
- cpdef int key_id(self, unicode key) except -1:
- """Return an ID generated for a given ``key``.
-
- :raises KeyError: if key is not present in this trie.
- """
- cdef bytes _key = <bytes>key.encode('utf8')
- cdef int res = self._key_id(_key)
- if res == -1:
- raise KeyError(key)
- return res
-
- def __getitem__(self, unicode key):
- return self.key_id(key)
-
- def get(self, key, default=None):
- """
- Return an ID for a given ``key`` or ``default`` if ``key`` is
- not present in this trie.
- """
- cdef bytes b_key
- cdef int res
-
- if isinstance(key, unicode):
- b_key = <bytes>(<unicode>key).encode('utf8')
- else:
- b_key = key
-
- res = self._key_id(b_key)
- if res == -1:
- return default
- return res
-
- cpdef restore_key(self, int index):
- """Return a key corresponding to a given ID."""
- cdef agent.Agent ag
- ag.set_query(index)
- try:
- self._trie.reverse_lookup(ag)
- except KeyError:
- raise KeyError(index)
- return self._get_key(ag)
-
- cdef int _key_id(self, char* key):
- cdef bint res
- cdef agent.Agent ag
- ag.set_query(key)
- res = self._trie.lookup(ag)
- if not res:
- return -1
- return ag.key().id()
-
- def iter_prefixes(self, unicode key):
- """
- Return an iterator of all prefixes of a given key.
- """
- cdef bytes b_key = <bytes>key.encode('utf8')
- cdef agent.Agent ag
- ag.set_query(b_key)
-
- while self._trie.common_prefix_search(ag):
- yield self._get_key(ag)
-
- def prefixes(self, unicode key):
- """
- Return a list with all prefixes of a given key.
- """
- # this an inlined version of ``list(self.iter_prefixes(key))``
-
- cdef list res = []
- cdef bytes b_key = <bytes>key.encode('utf8')
- cdef agent.Agent ag
- ag.set_query(b_key)
-
- while self._trie.common_prefix_search(ag):
- res.append(self._get_key(ag))
- return res
-
- def iteritems(self, unicode prefix=""):
- """
- Return an iterator over items that have a prefix ``prefix``.
- """
- cdef bytes b_prefix = <bytes>prefix.encode('utf8')
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- yield self._get_key(ag), ag.key().id()
-
- def items(self, unicode prefix=""):
- # inlined for speed
- cdef list res = []
- cdef bytes b_prefix = <bytes>prefix.encode('utf8')
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- res.append((self._get_key(ag), ag.key().id()))
-
- return res
-
-
-# This symbol is not allowed in utf8 so it is safe to use
-# as a separator between utf8-encoded string and binary payload.
-# XXX: b'\xff' value changes sort order for BytesTrie and RecordTrie.
-# See https://github.com/kmike/DAWG docs for a description of a similar issue.
-cdef bytes _VALUE_SEPARATOR = b'\xff'
-
-
-cdef class BytesTrie(_UnicodeKeyedTrie):
- """A trie mapping unicode keys to lists of bytes objects.
-
- The mapping is implemented by appending binary values to UTF8-encoded
- and storing the result in MARISA-trie.
- """
- cdef bytes _b_value_separator
- cdef unsigned char _c_value_separator
-
- def __init__(self, arg=None, bytes value_separator=_VALUE_SEPARATOR,
- **options):
- """
- ``arg`` must be an iterable of tuples (unicode_key, bytes_payload).
- """
- super(BytesTrie, self).__init__()
-
- self._b_value_separator = value_separator
- self._c_value_separator = <unsigned char>ord(value_separator)
-
- byte_keys = (self._raw_key(d[0], d[1]) for d in (arg or []))
- self._build(byte_keys, **options)
-
- cpdef bytes _raw_key(self, unicode key, bytes payload):
- return key.encode('utf8') + self._b_value_separator + payload
-
- cdef bint _contains(self, bytes key):
- cdef agent.Agent ag
- cdef bytes _key = key + self._b_value_separator
- ag.set_query(_key)
- return self._trie.predictive_search(ag)
-
- cpdef list prefixes(self, unicode key):
- """
- Return a list with all prefixes of a given key.
- """
-
- # XXX: is there a char-walking API in libmarisa?
- # This implementation is suboptimal.
-
- cdef agent.Agent ag
- cdef list res = []
- cdef int key_len = len(key)
- cdef unicode prefix
- cdef bytes b_prefix
- cdef int ind = 1
-
- while ind <= key_len:
- prefix = key[:ind]
- b_prefix = <bytes>(prefix.encode('utf8') + self._b_value_separator)
- ag.set_query(b_prefix)
- if self._trie.predictive_search(ag):
- res.append(prefix)
-
- ind += 1
-
- return res
-
- def __getitem__(self, key):
- cdef list res = self.get(key)
- if res is None:
- raise KeyError(key)
- return res
-
- cpdef get(self, key, default=None):
- """
- Return a list of payloads (as byte objects) for a given key
- or ``default`` if the key is not found.
- """
- cdef list res
-
- if isinstance(key, unicode):
- res = self.get_value(<unicode>key)
- else:
- res = self.b_get_value(key)
-
- if not res:
- return default
- return res
-
- cpdef list get_value(self, unicode key):
- """
- Return a list of payloads (as byte objects) for a given unicode key.
- """
- cdef bytes b_key = <bytes>key.encode('utf8')
- return self.b_get_value(b_key)
-
- cpdef list b_get_value(self, bytes key):
- """
- Return a list of payloads (as byte objects) for a given utf8-encoded key.
- """
- cdef list res = []
- cdef bytes value
- cdef bytes b_prefix = key + self._b_value_separator
- cdef int prefix_len = len(b_prefix)
-
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- value = ag.key().ptr()[prefix_len:ag.key().length()]
- res.append(value)
-
- return res
-
- cpdef list items(self, unicode prefix=""):
- # copied from iteritems for speed
- cdef bytes b_prefix = <bytes>prefix.encode('utf8')
- cdef bytes value
- cdef unicode key
- cdef unsigned char* raw_key
- cdef list res = []
- cdef int i, value_len
-
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- raw_key = <unsigned char*>ag.key().ptr()
-
- for i in range(0, ag.key().length()):
- if raw_key[i] == self._c_value_separator:
- break
-
- key = raw_key[:i].decode('utf8')
- value = raw_key[i+1:ag.key().length()]
-
- res.append(
- (key, value)
- )
- return res
-
- def iteritems(self, unicode prefix=""):
- cdef bytes b_prefix = <bytes>prefix.encode('utf8')
- cdef bytes value
- cdef unicode key
- cdef unsigned char* raw_key
- cdef int i, value_len
-
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- raw_key = <unsigned char*>ag.key().ptr()
-
- for i in range(0, ag.key().length()):
- if raw_key[i] == self._c_value_separator:
- break
-
- key = raw_key[:i].decode('utf8')
- value = raw_key[i+1:ag.key().length()]
-
- yield key, value
-
- cpdef list keys(self, prefix=""):
- # copied from iterkeys for speed
- cdef bytes b_prefix = <bytes>prefix.encode('utf8')
- cdef unicode key
- cdef unsigned char* raw_key
- cdef list res = []
- cdef int i
-
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- raw_key = <unsigned char*>ag.key().ptr()
-
- for i in range(0, ag.key().length()):
- if raw_key[i] == self._c_value_separator:
- key = raw_key[:i].decode('utf8')
- res.append(key)
- break
- return res
-
- def iterkeys(self, unicode prefix=""):
- cdef bytes b_prefix = <bytes>prefix.encode('utf8')
- cdef unicode key
- cdef unsigned char* raw_key
- cdef int i
-
- cdef agent.Agent ag
- ag.set_query(b_prefix)
-
- while self._trie.predictive_search(ag):
- raw_key = <unsigned char*>ag.key().ptr()
-
- for i in range(0, ag.key().length()):
- if raw_key[i] == self._c_value_separator:
- yield raw_key[:i].decode('utf8')
- break
-
-
-cdef class _UnpackTrie(BytesTrie):
-
- def __init__(self, arg=None, **options):
- keys = ((d[0], self._pack(d[1])) for d in (arg or []))
- super(_UnpackTrie, self).__init__(keys, **options)
-
- cdef _unpack(self, bytes value):
- return value
-
- cdef bytes _pack(self, value):
- return value
-
- cpdef list b_get_value(self, bytes key):
- cdef list values = BytesTrie.b_get_value(self, key)
- return [self._unpack(val) for val in values]
-
- cpdef list items(self, unicode prefix=""):
- cdef list items = BytesTrie.items(self, prefix)
- return [(key, self._unpack(val)) for (key, val) in items]
-
- def iteritems(self, unicode prefix=""):
- return ((key, self._unpack(val)) for key, val in BytesTrie.iteritems(self, prefix))
-
-
-cdef class RecordTrie(_UnpackTrie):
- """A trie mapping unicode keys to lists of data tuples.
-
- The data is packed using :mod:`struct` module, therefore all
- tuples must be of the same format. See :mod:`struct` documentation
- for available format strings.
-
- The mapping is implemented by appending binary values to UTF8-encoded
- and storing the result in MARISA-trie.
- """
- cdef _struct
- cdef _fmt
-
- def __init__(self, fmt, arg=None, **options):
- """
- ``arg`` must be an iterable of tuples (unicode_key, data_tuple).
- Data tuples will be converted to bytes with
- ``struct.pack(fmt, *data_tuple)``.
- """
- self._fmt = fmt
- self._struct = struct.Struct(str(fmt))
- super(RecordTrie, self).__init__(arg, **options)
-
- cdef _unpack(self, bytes value):
- return self._struct.unpack(value)
-
- cdef bytes _pack(self, value):
- return self._struct.pack(*value)
-
- def __reduce__(self):
- return self.__class__, (self._fmt, ), self.tobytes()
diff --git a/contrib/python/marisa-trie/query.pxd b/contrib/python/marisa-trie/query.pxd
deleted file mode 100644
index a650bb8965..0000000000
--- a/contrib/python/marisa-trie/query.pxd
+++ /dev/null
@@ -1,20 +0,0 @@
-cdef extern from "<marisa/query.h>" namespace "marisa" nogil:
-
- cdef cppclass Query:
- Query()
- Query(Query &query)
-
- #Query &operator=(Query &query)
-
- char operator[](int i)
-
- void set_str(char *str)
- void set_str(char *ptr, int length)
- void set_id(int id)
-
- char *ptr()
- int length()
- int id()
-
- void clear()
- void swap(Query &rhs)
diff --git a/contrib/python/marisa-trie/std_iostream.pxd b/contrib/python/marisa-trie/std_iostream.pxd
deleted file mode 100644
index bf7d0e89aa..0000000000
--- a/contrib/python/marisa-trie/std_iostream.pxd
+++ /dev/null
@@ -1,18 +0,0 @@
-from libcpp.string cimport string
-
-cdef extern from "<istream>" namespace "std" nogil:
- cdef cppclass istream:
- istream() except +
- istream& read (char* s, int n) except +
-
- cdef cppclass ostream:
- ostream() except +
- ostream& write (char* s, int n) except +
-
-cdef extern from "<sstream>" namespace "std" nogil:
-
- cdef cppclass stringstream:
- stringstream()
- stringstream(string s)
- string str ()
-
diff --git a/contrib/python/marisa-trie/trie.pxd b/contrib/python/marisa-trie/trie.pxd
deleted file mode 100644
index f525caf8ad..0000000000
--- a/contrib/python/marisa-trie/trie.pxd
+++ /dev/null
@@ -1,41 +0,0 @@
-cimport agent
-cimport base
-cimport keyset
-
-
-cdef extern from "<marisa/trie.h>" namespace "marisa" nogil:
-
- cdef cppclass Trie:
- Trie()
-
- void build(keyset.Keyset &keyset, int config_flags) except +
- void build(keyset.Keyset &keyset) except +
-
- void mmap(char *filename) except +
- void map(void *ptr, int size) except +
-
- void load(char *filename) except +
- void read(int fd) except +
-
- void save(char *filename) except +
- void write(int fd) except +
-
- bint lookup(agent.Agent &agent) except +
- void reverse_lookup(agent.Agent &agent) except +KeyError
- bint common_prefix_search(agent.Agent &agent) except +
- bint predictive_search(agent.Agent &agent) except +
-
- int num_tries() except +
- int num_keys() except +
- int num_nodes() except +
-
- base.TailMode tail_mode()
- base.NodeOrder node_order()
-
- bint empty() except +
- int size() except +
- int total_size() except +
- int io_size() except +
-
- void clear() except +
- void swap(Trie &rhs) except +
diff --git a/contrib/python/marisa-trie/ya.make b/contrib/python/marisa-trie/ya.make
deleted file mode 100644
index 490eef9afa..0000000000
--- a/contrib/python/marisa-trie/ya.make
+++ /dev/null
@@ -1,33 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(MIT)
-
-VERSION(0.7.5)
-
-NO_COMPILER_WARNINGS()
-
-ADDINCL(
- contrib/python/marisa-trie
-)
-
-SRCS(
- marisa/agent.cc
- marisa/keyset.cc
- marisa/trie.cc
-
- marisa/grimoire/io/mapper.cc
- marisa/grimoire/io/reader.cc
- marisa/grimoire/io/writer.cc
- marisa/grimoire/trie/louds-trie.cc
- marisa/grimoire/trie/tail.cc
- marisa/grimoire/vector/bit-vector.cc
-)
-
-PY_SRCS(
- TOP_LEVEL
- marisa_trie.pyx
-)
-
-NO_LINT()
-
-END()
diff --git a/contrib/python/path.py/py2/LICENSE b/contrib/python/path.py/py2/LICENSE
deleted file mode 100644
index 5e795a61f3..0000000000
--- a/contrib/python/path.py/py2/LICENSE
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright Jason R. Coombs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/contrib/python/path.py/py2/README.rst b/contrib/python/path.py/py2/README.rst
deleted file mode 100644
index 424d3e77c8..0000000000
--- a/contrib/python/path.py/py2/README.rst
+++ /dev/null
@@ -1,134 +0,0 @@
-.. image:: https://img.shields.io/pypi/v/path.py.svg
- :target: https://pypi.org/project/path.py
-
-.. image:: https://img.shields.io/pypi/pyversions/path.py.svg
-
-.. image:: https://img.shields.io/travis/jaraco/path.py/master.svg
- :target: https://travis-ci.org/jaraco/path.py
-
-.. image:: https://img.shields.io/appveyor/ci/jaraco/path-py/master.svg
- :target: https://ci.appveyor.com/project/jaraco/path-py/branch/master
-
-.. image:: https://readthedocs.org/projects/pathpy/badge/?version=latest
- :target: https://pathpy.readthedocs.io/en/latest/?badge=latest
-
-``path.py`` implements path objects as first-class entities, allowing
-common operations on files to be invoked on those path objects directly. For
-example:
-
-.. code-block:: python
-
- from path import Path
- d = Path('/home/guido/bin')
- for f in d.files('*.py'):
- f.chmod(0o755)
-
- # Globbing
- for f in d.files('*.py'):
- f.chmod(0o755)
-
- # Changing the working directory:
- with Path("somewhere"):
- # cwd in now `somewhere`
- ...
-
- # Concatenate paths with /
- foo_txt = Path("bar") / "foo.txt"
-
-``path.py`` is `hosted at Github <https://github.com/jaraco/path.py>`_.
-
-Find `the documentation here <https://pathpy.readthedocs.io>`_.
-
-Guides and Testimonials
-=======================
-
-Yasoob wrote the Python 101 `Writing a Cleanup Script
-<http://freepythontips.wordpress.com/2014/01/23/python-101-writing-a-cleanup-script/>`_
-based on ``path.py``.
-
-Installing
-==========
-
-Path.py may be installed using ``setuptools``, ``distribute``, or ``pip``::
-
- pip install path.py
-
-The latest release is always updated to the `Python Package Index
-<http://pypi.python.org/pypi/path.py>`_.
-
-You may also always download the source distribution (zip/tarball), extract
-it, and run ``python setup.py`` to install it.
-
-Advantages
-==========
-
-Python 3.4 introduced
-`pathlib <https://docs.python.org/3/library/pathlib.html>`_,
-which shares many characteristics with ``path.py``. In particular,
-it provides an object encapsulation for representing filesystem paths.
-One may have imagined ``pathlib`` would supersede ``path.py``.
-
-But the implementation and the usage quickly diverge, and ``path.py``
-has several advantages over ``pathlib``:
-
-- ``path.py`` implements ``Path`` objects as a subclass of
- ``str`` (unicode on Python 2), and as a result these ``Path``
- objects may be passed directly to other APIs that expect simple
- text representations of paths, whereas with ``pathlib``, one
- must first cast values to strings before passing them to
- APIs unaware of ``pathlib``. This shortcoming was `addressed
- by PEP 519 <https://www.python.org/dev/peps/pep-0519/>`_,
- in Python 3.6.
-- ``path.py`` goes beyond exposing basic functionality of a path
- and exposes commonly-used behaviors on a path, providing
- methods like ``rmtree`` (from shlib) and ``remove_p`` (remove
- a file if it exists).
-- As a PyPI-hosted package, ``path.py`` is free to iterate
- faster than a stdlib package. Contributions are welcome
- and encouraged.
-- ``path.py`` provides a uniform abstraction over its Path object,
- freeing the implementer to subclass it readily. One cannot
- subclass a ``pathlib.Path`` to add functionality, but must
- subclass ``Path``, ``PosixPath``, and ``WindowsPath``, even
- if one only wishes to add a ``__dict__`` to the subclass
- instances. ``path.py`` instead allows the ``Path.module``
- object to be overridden by subclasses, defaulting to the
- ``os.path``. Even advanced uses of ``path.Path`` that
- subclass the model do not need to be concerned with
- OS-specific nuances.
-
-Alternatives
-============
-
-In addition to
-`pathlib <https://docs.python.org/3/library/pathlib.html>`_, the
-`pylib project <https://pypi.org/project/py/>`_ implements a
-`LocalPath <https://github.com/pytest-dev/py/blob/72601dc8bbb5e11298bf9775bb23b0a395deb09b/py/_path/local.py#L106>`_
-class, which shares some behaviors and interfaces with ``path.py``.
-
-Development
-===========
-
-To install a development version, use the Github links to clone or
-download a snapshot of the latest code. Alternatively, if you have git
-installed, you may be able to use ``pip`` to install directly from
-the repository::
-
- pip install git+https://github.com/jaraco/path.py.git
-
-Testing
-=======
-
-Tests are continuously run by Travis-CI: |BuildStatus|_
-
-.. |BuildStatus| image:: https://secure.travis-ci.org/jaraco/path.py.png
-.. _BuildStatus: http://travis-ci.org/jaraco/path.py
-
-To run the tests, refer to the ``.travis.yml`` file for the steps run on the
-Travis-CI hosts.
-
-Releasing
-=========
-
-Tagged releases are automatically published to PyPI by Travis-CI, assuming
-the tests pass.
diff --git a/contrib/python/path.py/py3/.dist-info/METADATA b/contrib/python/path.py/py3/.dist-info/METADATA
deleted file mode 100644
index 68873c1391..0000000000
--- a/contrib/python/path.py/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,36 +0,0 @@
-Metadata-Version: 2.1
-Name: path.py
-Version: 12.5.0
-Summary: A module wrapper for os.path
-Home-page: https://github.com/jaraco/path
-Author: Jason Orendorff
-Author-email: jason.orendorff@gmail.com
-Maintainer: Jason R. Coombs
-Maintainer-email: jaraco@jaraco.com
-License: UNKNOWN
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Operating System :: OS Independent
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.5
-Requires-Dist: path
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=3.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (!=3.7.3,>=3.5) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=1.2.3) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-black-multipy ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: appdirs ; extra == 'testing'
-Requires-Dist: packaging ; extra == 'testing'
-Requires-Dist: pygments ; extra == 'testing'
-
-``path.py`` has been renamed to `path <https://pypi.org/project/path>`_.
-
-
diff --git a/contrib/python/path.py/py3/.dist-info/top_level.txt b/contrib/python/path.py/py3/.dist-info/top_level.txt
deleted file mode 100644
index e7a8fd4d0a..0000000000
--- a/contrib/python/path.py/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-path
diff --git a/contrib/python/path.py/py3/LICENSE b/contrib/python/path.py/py3/LICENSE
deleted file mode 100644
index 5e795a61f3..0000000000
--- a/contrib/python/path.py/py3/LICENSE
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright Jason R. Coombs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/contrib/python/path.py/py3/README.rst b/contrib/python/path.py/py3/README.rst
deleted file mode 100644
index 4bc121481c..0000000000
--- a/contrib/python/path.py/py3/README.rst
+++ /dev/null
@@ -1 +0,0 @@
-``path.py`` has been renamed to `path <https://pypi.org/project/path>`_.
diff --git a/contrib/python/path.py/py3/ya.make b/contrib/python/path.py/py3/ya.make
deleted file mode 100644
index 241d32971e..0000000000
--- a/contrib/python/path.py/py3/ya.make
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(12.5.0)
-
-LICENSE(MIT)
-
-PEERDIR(
- contrib/python/path
-)
-
-NO_LINT()
-
-RESOURCE_FILES(
- PREFIX contrib/python/path.py/py3/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
-END()
diff --git a/contrib/python/path.py/ya.make b/contrib/python/path.py/ya.make
deleted file mode 100644
index d0c1aca518..0000000000
--- a/contrib/python/path.py/ya.make
+++ /dev/null
@@ -1,18 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/path.py/py2)
-ELSE()
- PEERDIR(contrib/python/path.py/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- py2
- py3
-)
diff --git a/contrib/python/path/.dist-info/METADATA b/contrib/python/path/.dist-info/METADATA
deleted file mode 100644
index 7cddb38723..0000000000
--- a/contrib/python/path/.dist-info/METADATA
+++ /dev/null
@@ -1,201 +0,0 @@
-Metadata-Version: 2.1
-Name: path
-Version: 16.7.1
-Summary: A module wrapper for os.path
-Home-page: https://github.com/jaraco/path
-Author: Jason Orendorff
-Author-email: jason.orendorff@gmail.com
-Maintainer: Jason R. Coombs
-Maintainer-email: jaraco@jaraco.com
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Operating System :: OS Independent
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.8
-License-File: LICENSE
-Provides-Extra: docs
-Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=9.3) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Requires-Dist: furo ; extra == 'docs'
-Requires-Dist: sphinx-lint ; extra == 'docs'
-Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=2.2) ; extra == 'testing'
-Requires-Dist: pytest-ruff ; extra == 'testing'
-Requires-Dist: appdirs ; extra == 'testing'
-Requires-Dist: packaging ; extra == 'testing'
-Requires-Dist: pygments ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pywin32 ; (platform_system == "Windows" and python_version < "3.12") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/path.svg
- :target: https://pypi.org/project/path
-
-.. image:: https://img.shields.io/pypi/pyversions/path.svg
-
-.. image:: https://github.com/jaraco/path/workflows/tests/badge.svg
- :target: https://github.com/jaraco/path/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
- :target: https://github.com/astral-sh/ruff
- :alt: Ruff
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
- :alt: Code style: Black
-
-.. image:: https://readthedocs.org/projects/path/badge/?version=latest
- :target: https://path.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2023-informational
- :target: https://blog.jaraco.com/skeleton
-
-.. image:: https://tidelift.com/badges/package/pypi/path
- :target: https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=readme
-
-
-``path`` (aka path pie, formerly ``path.py``) implements path
-objects as first-class entities, allowing common operations on
-files to be invoked on those path objects directly. For example:
-
-.. code-block:: python
-
- from path import Path
-
- d = Path("/home/guido/bin")
- for f in d.files("*.py"):
- f.chmod(0o755)
-
- # Globbing
- for f in d.files("*.py"):
- f.chmod("u+rwx")
-
- # Changing the working directory:
- with Path("somewhere"):
- # cwd in now `somewhere`
- ...
-
- # Concatenate paths with /
- foo_txt = Path("bar") / "foo.txt"
-
-Path pie is `hosted at Github <https://github.com/jaraco/path>`_.
-
-Find `the documentation here <https://path.readthedocs.io>`_.
-
-Guides and Testimonials
-=======================
-
-Yasoob wrote the Python 101 `Writing a Cleanup Script
-<http://freepythontips.wordpress.com/2014/01/23/python-101-writing-a-cleanup-script/>`_
-based on ``path``.
-
-Advantages
-==========
-
-Python 3.4 introduced
-`pathlib <https://docs.python.org/3/library/pathlib.html>`_,
-which shares many characteristics with ``path``. In particular,
-it provides an object encapsulation for representing filesystem paths.
-One may have imagined ``pathlib`` would supersede ``path``.
-
-But the implementation and the usage quickly diverge, and ``path``
-has several advantages over ``pathlib``:
-
-- ``path`` implements ``Path`` objects as a subclass of
- ``str``, and as a result these ``Path``
- objects may be passed directly to other APIs that expect simple
- text representations of paths, whereas with ``pathlib``, one
- must first cast values to strings before passing them to
- APIs unaware of ``pathlib``. This shortcoming was `addressed
- by PEP 519 <https://www.python.org/dev/peps/pep-0519/>`_,
- in Python 3.6.
-- ``path`` goes beyond exposing basic functionality of a path
- and exposes commonly-used behaviors on a path, providing
- methods like ``rmtree`` (from shlib) and ``remove_p`` (remove
- a file if it exists).
-- As a PyPI-hosted package, ``path`` is free to iterate
- faster than a stdlib package. Contributions are welcome
- and encouraged.
-- ``path`` provides a uniform abstraction over its Path object,
- freeing the implementer to subclass it readily. One cannot
- subclass a ``pathlib.Path`` to add functionality, but must
- subclass ``Path``, ``PosixPath``, and ``WindowsPath``, even
- if one only wishes to add a ``__dict__`` to the subclass
- instances. ``path`` instead allows the ``Path.module``
- object to be overridden by subclasses, defaulting to the
- ``os.path``. Even advanced uses of ``path.Path`` that
- subclass the model do not need to be concerned with
- OS-specific nuances.
-
-This path project has the explicit aim to provide compatibility
-with ``pathlib`` objects where possible, such that a ``path.Path``
-object is a drop-in replacement for ``pathlib.Path*`` objects.
-This project welcomes contributions to improve that compatibility
-where it's lacking.
-
-Alternatives
-============
-
-In addition to
-`pathlib <https://docs.python.org/3/library/pathlib.html>`_, the
-`pylib project <https://pypi.org/project/py/>`_ implements a
-`LocalPath <https://github.com/pytest-dev/py/blob/72601dc8bbb5e11298bf9775bb23b0a395deb09b/py/_path/local.py#L106>`_
-class, which shares some behaviors and interfaces with ``path``.
-
-Development
-===========
-
-To install a development version, use the Github links to clone or
-download a snapshot of the latest code. Alternatively, if you have git
-installed, you may be able to use ``pip`` to install directly from
-the repository::
-
- pip install git+https://github.com/jaraco/path.git
-
-Testing
-=======
-
-Tests are invoked with `tox <https://pypi.org/project/tox>`_. After
-having installed tox, simply invoke ``tox`` in a checkout of the repo
-to invoke the tests.
-
-Tests are also run in continuous integration. See the badges above
-for links to the CI runs.
-
-Releasing
-=========
-
-Tagged releases are automatically published to PyPI by Azure
-Pipelines, assuming the tests pass.
-
-Origins
-=======
-
-The ``path.py`` project was initially released in 2003 by Jason Orendorff
-and has been continuously developed and supported by several maintainers
-over the years.
-
-For Enterprise
-==============
-
-Available as part of the Tidelift Subscription.
-
-This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
-
-`Learn more <https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=referral&utm_campaign=github>`_.
-
-Security Contact
-================
-
-To report a security vulnerability, please use the
-`Tidelift security contact <https://tidelift.com/security>`_.
-Tidelift will coordinate the fix and disclosure.
diff --git a/contrib/python/path/.dist-info/top_level.txt b/contrib/python/path/.dist-info/top_level.txt
deleted file mode 100644
index e7a8fd4d0a..0000000000
--- a/contrib/python/path/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-path
diff --git a/contrib/python/path/LICENSE b/contrib/python/path/LICENSE
deleted file mode 100644
index 1bb5a44356..0000000000
--- a/contrib/python/path/LICENSE
+++ /dev/null
@@ -1,17 +0,0 @@
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
diff --git a/contrib/python/path/README.rst b/contrib/python/path/README.rst
deleted file mode 100644
index 69aa8737d6..0000000000
--- a/contrib/python/path/README.rst
+++ /dev/null
@@ -1,163 +0,0 @@
-.. image:: https://img.shields.io/pypi/v/path.svg
- :target: https://pypi.org/project/path
-
-.. image:: https://img.shields.io/pypi/pyversions/path.svg
-
-.. image:: https://github.com/jaraco/path/workflows/tests/badge.svg
- :target: https://github.com/jaraco/path/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
- :target: https://github.com/astral-sh/ruff
- :alt: Ruff
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
- :alt: Code style: Black
-
-.. image:: https://readthedocs.org/projects/path/badge/?version=latest
- :target: https://path.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2023-informational
- :target: https://blog.jaraco.com/skeleton
-
-.. image:: https://tidelift.com/badges/package/pypi/path
- :target: https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=readme
-
-
-``path`` (aka path pie, formerly ``path.py``) implements path
-objects as first-class entities, allowing common operations on
-files to be invoked on those path objects directly. For example:
-
-.. code-block:: python
-
- from path import Path
-
- d = Path("/home/guido/bin")
- for f in d.files("*.py"):
- f.chmod(0o755)
-
- # Globbing
- for f in d.files("*.py"):
- f.chmod("u+rwx")
-
- # Changing the working directory:
- with Path("somewhere"):
- # cwd in now `somewhere`
- ...
-
- # Concatenate paths with /
- foo_txt = Path("bar") / "foo.txt"
-
-Path pie is `hosted at Github <https://github.com/jaraco/path>`_.
-
-Find `the documentation here <https://path.readthedocs.io>`_.
-
-Guides and Testimonials
-=======================
-
-Yasoob wrote the Python 101 `Writing a Cleanup Script
-<http://freepythontips.wordpress.com/2014/01/23/python-101-writing-a-cleanup-script/>`_
-based on ``path``.
-
-Advantages
-==========
-
-Python 3.4 introduced
-`pathlib <https://docs.python.org/3/library/pathlib.html>`_,
-which shares many characteristics with ``path``. In particular,
-it provides an object encapsulation for representing filesystem paths.
-One may have imagined ``pathlib`` would supersede ``path``.
-
-But the implementation and the usage quickly diverge, and ``path``
-has several advantages over ``pathlib``:
-
-- ``path`` implements ``Path`` objects as a subclass of
- ``str``, and as a result these ``Path``
- objects may be passed directly to other APIs that expect simple
- text representations of paths, whereas with ``pathlib``, one
- must first cast values to strings before passing them to
- APIs unaware of ``pathlib``. This shortcoming was `addressed
- by PEP 519 <https://www.python.org/dev/peps/pep-0519/>`_,
- in Python 3.6.
-- ``path`` goes beyond exposing basic functionality of a path
- and exposes commonly-used behaviors on a path, providing
- methods like ``rmtree`` (from shlib) and ``remove_p`` (remove
- a file if it exists).
-- As a PyPI-hosted package, ``path`` is free to iterate
- faster than a stdlib package. Contributions are welcome
- and encouraged.
-- ``path`` provides a uniform abstraction over its Path object,
- freeing the implementer to subclass it readily. One cannot
- subclass a ``pathlib.Path`` to add functionality, but must
- subclass ``Path``, ``PosixPath``, and ``WindowsPath``, even
- if one only wishes to add a ``__dict__`` to the subclass
- instances. ``path`` instead allows the ``Path.module``
- object to be overridden by subclasses, defaulting to the
- ``os.path``. Even advanced uses of ``path.Path`` that
- subclass the model do not need to be concerned with
- OS-specific nuances.
-
-This path project has the explicit aim to provide compatibility
-with ``pathlib`` objects where possible, such that a ``path.Path``
-object is a drop-in replacement for ``pathlib.Path*`` objects.
-This project welcomes contributions to improve that compatibility
-where it's lacking.
-
-Alternatives
-============
-
-In addition to
-`pathlib <https://docs.python.org/3/library/pathlib.html>`_, the
-`pylib project <https://pypi.org/project/py/>`_ implements a
-`LocalPath <https://github.com/pytest-dev/py/blob/72601dc8bbb5e11298bf9775bb23b0a395deb09b/py/_path/local.py#L106>`_
-class, which shares some behaviors and interfaces with ``path``.
-
-Development
-===========
-
-To install a development version, use the Github links to clone or
-download a snapshot of the latest code. Alternatively, if you have git
-installed, you may be able to use ``pip`` to install directly from
-the repository::
-
- pip install git+https://github.com/jaraco/path.git
-
-Testing
-=======
-
-Tests are invoked with `tox <https://pypi.org/project/tox>`_. After
-having installed tox, simply invoke ``tox`` in a checkout of the repo
-to invoke the tests.
-
-Tests are also run in continuous integration. See the badges above
-for links to the CI runs.
-
-Releasing
-=========
-
-Tagged releases are automatically published to PyPI by Azure
-Pipelines, assuming the tests pass.
-
-Origins
-=======
-
-The ``path.py`` project was initially released in 2003 by Jason Orendorff
-and has been continuously developed and supported by several maintainers
-over the years.
-
-For Enterprise
-==============
-
-Available as part of the Tidelift Subscription.
-
-This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
-
-`Learn more <https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=referral&utm_campaign=github>`_.
-
-Security Contact
-================
-
-To report a security vulnerability, please use the
-`Tidelift security contact <https://tidelift.com/security>`_.
-Tidelift will coordinate the fix and disclosure.
diff --git a/contrib/python/path/path/__init__.py b/contrib/python/path/path/__init__.py
deleted file mode 100644
index eebdc3a0b8..0000000000
--- a/contrib/python/path/path/__init__.py
+++ /dev/null
@@ -1,1665 +0,0 @@
-"""
-Path Pie
-
-Implements ``path.Path`` - An object representing a
-path to a file or directory.
-
-Example::
-
- from path import Path
- d = Path('/home/guido/bin')
-
- # Globbing
- for f in d.files('*.py'):
- f.chmod(0o755)
-
- # Changing the working directory:
- with Path("somewhere"):
- # cwd in now `somewhere`
- ...
-
- # Concatenate paths with /
- foo_txt = Path("bar") / "foo.txt"
-"""
-
-import sys
-import warnings
-import os
-import fnmatch
-import glob
-import shutil
-import hashlib
-import errno
-import tempfile
-import functools
-import re
-import contextlib
-import importlib
-import itertools
-import datetime
-from numbers import Number
-from typing import Union
-
-with contextlib.suppress(ImportError):
- import win32security
-
-with contextlib.suppress(ImportError):
- import pwd
-
-with contextlib.suppress(ImportError):
- import grp
-
-from . import matchers
-from . import masks
-from . import classes
-
-
-__all__ = ['Path', 'TempDir']
-
-
-LINESEPS = ['\r\n', '\r', '\n']
-U_LINESEPS = LINESEPS + ['\u0085', '\u2028', '\u2029']
-B_NEWLINE = re.compile('|'.join(LINESEPS).encode())
-U_NEWLINE = re.compile('|'.join(U_LINESEPS))
-B_NL_END = re.compile(B_NEWLINE.pattern + b'$')
-U_NL_END = re.compile(U_NEWLINE.pattern + '$')
-
-_default_linesep = object()
-
-
-def _make_timestamp_ns(value: Union[Number, datetime.datetime]) -> Number:
- timestamp_s = value if isinstance(value, Number) else value.timestamp()
- return int(timestamp_s * 10**9)
-
-
-class TreeWalkWarning(Warning):
- pass
-
-
-class Traversal:
- """
- Wrap a walk result to customize the traversal.
-
- `follow` is a function that takes an item and returns
- True if that item should be followed and False otherwise.
-
- For example, to avoid traversing into directories that
- begin with `.`:
-
- >>> traverse = Traversal(lambda dir: not dir.startswith('.'))
- >>> items = list(traverse(Path('.').walk()))
-
- Directories beginning with `.` will appear in the results, but
- their children will not.
-
- >>> dot_dir = next(item for item in items if item.isdir() and item.startswith('.'))
- >>> any(item.parent == dot_dir for item in items)
- False
- """
-
- def __init__(self, follow):
- self.follow = follow
-
- def __call__(self, walker):
- traverse = None
- while True:
- try:
- item = walker.send(traverse)
- except StopIteration:
- return
- yield item
-
- traverse = functools.partial(self.follow, item)
-
-
-def _strip_newlines(lines):
- r"""
- >>> list(_strip_newlines(['Hello World\r\n', 'foo']))
- ['Hello World', 'foo']
- """
- return (U_NL_END.sub('', line) for line in lines)
-
-
-class Path(str):
- """
- Represents a filesystem path.
-
- For documentation on individual methods, consult their
- counterparts in :mod:`os.path`.
-
- Some methods are additionally included from :mod:`shutil`.
- The functions are linked directly into the class namespace
- such that they will be bound to the Path instance. For example,
- ``Path(src).copy(target)`` is equivalent to
- ``shutil.copy(src, target)``. Therefore, when referencing
- the docs for these methods, assume `src` references `self`,
- the Path instance.
- """
-
- module = os.path
- """ The path module to use for path operations.
-
- .. seealso:: :mod:`os.path`
- """
-
- def __init__(self, other=''):
- if other is None:
- raise TypeError("Invalid initial value for path: None")
- with contextlib.suppress(AttributeError):
- self._validate()
-
- @classmethod
- @functools.lru_cache
- def using_module(cls, module):
- subclass_name = cls.__name__ + '_' + module.__name__
- bases = (cls,)
- ns = {'module': module}
- return type(subclass_name, bases, ns)
-
- @classes.ClassProperty
- @classmethod
- def _next_class(cls):
- """
- What class should be used to construct new instances from this class
- """
- return cls
-
- # --- Special Python methods.
-
- def __repr__(self):
- return '{}({})'.format(type(self).__name__, super().__repr__())
-
- # Adding a Path and a string yields a Path.
- def __add__(self, more):
- return self._next_class(super().__add__(more))
-
- def __radd__(self, other):
- return self._next_class(other.__add__(self))
-
- # The / operator joins Paths.
- def __div__(self, rel):
- """fp.__div__(rel) == fp / rel == fp.joinpath(rel)
-
- Join two path components, adding a separator character if
- needed.
-
- .. seealso:: :func:`os.path.join`
- """
- return self._next_class(self.module.join(self, rel))
-
- # Make the / operator work even when true division is enabled.
- __truediv__ = __div__
-
- # The / operator joins Paths the other way around
- def __rdiv__(self, rel):
- """fp.__rdiv__(rel) == rel / fp
-
- Join two path components, adding a separator character if
- needed.
-
- .. seealso:: :func:`os.path.join`
- """
- return self._next_class(self.module.join(rel, self))
-
- # Make the / operator work even when true division is enabled.
- __rtruediv__ = __rdiv__
-
- def __enter__(self):
- self._old_dir = self.getcwd()
- os.chdir(self)
- return self
-
- def __exit__(self, *_):
- os.chdir(self._old_dir)
-
- @classmethod
- def getcwd(cls):
- """Return the current working directory as a path object.
-
- .. seealso:: :func:`os.getcwd`
- """
- return cls(os.getcwd())
-
- #
- # --- Operations on Path strings.
-
- def abspath(self):
- """.. seealso:: :func:`os.path.abspath`"""
- return self._next_class(self.module.abspath(self))
-
- def normcase(self):
- """.. seealso:: :func:`os.path.normcase`"""
- return self._next_class(self.module.normcase(self))
-
- def normpath(self):
- """.. seealso:: :func:`os.path.normpath`"""
- return self._next_class(self.module.normpath(self))
-
- def realpath(self):
- """.. seealso:: :func:`os.path.realpath`"""
- return self._next_class(self.module.realpath(self))
-
- def expanduser(self):
- """.. seealso:: :func:`os.path.expanduser`"""
- return self._next_class(self.module.expanduser(self))
-
- def expandvars(self):
- """.. seealso:: :func:`os.path.expandvars`"""
- return self._next_class(self.module.expandvars(self))
-
- def dirname(self):
- """.. seealso:: :attr:`parent`, :func:`os.path.dirname`"""
- return self._next_class(self.module.dirname(self))
-
- def basename(self):
- """.. seealso:: :attr:`name`, :func:`os.path.basename`"""
- return self._next_class(self.module.basename(self))
-
- def expand(self):
- """Clean up a filename by calling :meth:`expandvars()`,
- :meth:`expanduser()`, and :meth:`normpath()` on it.
-
- This is commonly everything needed to clean up a filename
- read from a configuration file, for example.
- """
- return self.expandvars().expanduser().normpath()
-
- @property
- def stem(self):
- """The same as :meth:`name`, but with one file extension stripped off.
-
- >>> Path('/home/guido/python.tar.gz').stem
- 'python.tar'
- """
- base, ext = self.module.splitext(self.name)
- return base
-
- @property
- def ext(self):
- """The file extension, for example ``'.py'``."""
- f, ext = self.module.splitext(self)
- return ext
-
- def with_suffix(self, suffix):
- """Return a new path with the file suffix changed (or added, if none)
-
- >>> Path('/home/guido/python.tar.gz').with_suffix(".foo")
- Path('/home/guido/python.tar.foo')
-
- >>> Path('python').with_suffix('.zip')
- Path('python.zip')
-
- >>> Path('filename.ext').with_suffix('zip')
- Traceback (most recent call last):
- ...
- ValueError: Invalid suffix 'zip'
- """
- if not suffix.startswith('.'):
- raise ValueError(f"Invalid suffix {suffix!r}")
-
- return self.stripext() + suffix
-
- @property
- def drive(self):
- """The drive specifier, for example ``'C:'``.
-
- This is always empty on systems that don't use drive specifiers.
- """
- drive, r = self.module.splitdrive(self)
- return self._next_class(drive)
-
- parent = property(
- dirname,
- None,
- None,
- """ This path's parent directory, as a new Path object.
-
- For example,
- ``Path('/usr/local/lib/libpython.so').parent ==
- Path('/usr/local/lib')``
-
- .. seealso:: :meth:`dirname`, :func:`os.path.dirname`
- """,
- )
-
- name = property(
- basename,
- None,
- None,
- """ The name of this file or directory without the full path.
-
- For example,
- ``Path('/usr/local/lib/libpython.so').name == 'libpython.so'``
-
- .. seealso:: :meth:`basename`, :func:`os.path.basename`
- """,
- )
-
- def splitpath(self):
- """Return two-tuple of ``.parent``, ``.name``.
-
- .. seealso:: :attr:`parent`, :attr:`name`, :func:`os.path.split`
- """
- parent, child = self.module.split(self)
- return self._next_class(parent), child
-
- def splitdrive(self):
- """Return two-tuple of ``.drive`` and rest without drive.
-
- Split the drive specifier from this path. If there is
- no drive specifier, :samp:`{p.drive}` is empty, so the return value
- is simply ``(Path(''), p)``. This is always the case on Unix.
-
- .. seealso:: :func:`os.path.splitdrive`
- """
- drive, rel = self.module.splitdrive(self)
- return self._next_class(drive), self._next_class(rel)
-
- def splitext(self):
- """Return two-tuple of ``.stripext()`` and ``.ext``.
-
- Split the filename extension from this path and return
- the two parts. Either part may be empty.
-
- The extension is everything from ``'.'`` to the end of the
- last path segment. This has the property that if
- ``(a, b) == p.splitext()``, then ``a + b == p``.
-
- .. seealso:: :func:`os.path.splitext`
- """
- filename, ext = self.module.splitext(self)
- return self._next_class(filename), ext
-
- def stripext(self):
- """Remove one file extension from the path.
-
- For example, ``Path('/home/guido/python.tar.gz').stripext()``
- returns ``Path('/home/guido/python.tar')``.
- """
- return self.splitext()[0]
-
- @classes.multimethod
- def joinpath(cls, first, *others):
- """
- Join first to zero or more :class:`Path` components,
- adding a separator character (:samp:`{first}.module.sep`)
- if needed. Returns a new instance of
- :samp:`{first}._next_class`.
-
- .. seealso:: :func:`os.path.join`
- """
- return cls._next_class(cls.module.join(first, *others))
-
- def splitall(self):
- r"""Return a list of the path components in this path.
-
- The first item in the list will be a Path. Its value will be
- either :data:`os.curdir`, :data:`os.pardir`, empty, or the root
- directory of this path (for example, ``'/'`` or ``'C:\\'``). The
- other items in the list will be strings.
-
- ``Path.joinpath(*result)`` will yield the original path.
-
- >>> Path('/foo/bar/baz').splitall()
- [Path('/'), 'foo', 'bar', 'baz']
- """
- return list(self._parts())
-
- def parts(self):
- """
- >>> Path('/foo/bar/baz').parts()
- (Path('/'), 'foo', 'bar', 'baz')
- """
- return tuple(self._parts())
-
- def _parts(self):
- return reversed(tuple(self._parts_iter()))
-
- def _parts_iter(self):
- loc = self
- while loc != os.curdir and loc != os.pardir:
- prev = loc
- loc, child = prev.splitpath()
- if loc == prev:
- break
- yield child
- yield loc
-
- def relpath(self, start='.'):
- """Return this path as a relative path,
- based from `start`, which defaults to the current working directory.
- """
- cwd = self._next_class(start)
- return cwd.relpathto(self)
-
- def relpathto(self, dest):
- """Return a relative path from `self` to `dest`.
-
- If there is no relative path from `self` to `dest`, for example if
- they reside on different drives in Windows, then this returns
- ``dest.abspath()``.
- """
- origin = self.abspath()
- dest = self._next_class(dest).abspath()
-
- orig_list = origin.normcase().splitall()
- # Don't normcase dest! We want to preserve the case.
- dest_list = dest.splitall()
-
- if orig_list[0] != self.module.normcase(dest_list[0]):
- # Can't get here from there.
- return dest
-
- # Find the location where the two paths start to differ.
- i = 0
- for start_seg, dest_seg in zip(orig_list, dest_list):
- if start_seg != self.module.normcase(dest_seg):
- break
- i += 1
-
- # Now i is the point where the two paths diverge.
- # Need a certain number of "os.pardir"s to work up
- # from the origin to the point of divergence.
- segments = [os.pardir] * (len(orig_list) - i)
- # Need to add the diverging part of dest_list.
- segments += dest_list[i:]
- if len(segments) == 0:
- # If they happen to be identical, use os.curdir.
- relpath = os.curdir
- else:
- relpath = self.module.join(*segments)
- return self._next_class(relpath)
-
- # --- Listing, searching, walking, and matching
-
- def listdir(self, match=None):
- """List of items in this directory.
-
- Use :meth:`files` or :meth:`dirs` instead if you want a listing
- of just files or just subdirectories.
-
- The elements of the list are Path objects.
-
- With the optional `match` argument, a callable,
- only return items whose names match the given pattern.
-
- .. seealso:: :meth:`files`, :meth:`dirs`
- """
- match = matchers.load(match)
- return list(filter(match, (self / child for child in os.listdir(self))))
-
- def dirs(self, *args, **kwargs):
- """List of this directory's subdirectories.
-
- The elements of the list are Path objects.
- This does not walk recursively into subdirectories
- (but see :meth:`walkdirs`).
-
- Accepts parameters to :meth:`listdir`.
- """
- return [p for p in self.listdir(*args, **kwargs) if p.isdir()]
-
- def files(self, *args, **kwargs):
- """List of the files in self.
-
- The elements of the list are Path objects.
- This does not walk into subdirectories (see :meth:`walkfiles`).
-
- Accepts parameters to :meth:`listdir`.
- """
-
- return [p for p in self.listdir(*args, **kwargs) if p.isfile()]
-
- def walk(self, match=None, errors='strict'):
- """Iterator over files and subdirs, recursively.
-
- The iterator yields Path objects naming each child item of
- this directory and its descendants. This requires that
- ``D.isdir()``.
-
- This performs a depth-first traversal of the directory tree.
- Each directory is returned just before all its children.
-
- The `errors=` keyword argument controls behavior when an
- error occurs. The default is ``'strict'``, which causes an
- exception. Other allowed values are ``'warn'`` (which
- reports the error via :func:`warnings.warn()`), and ``'ignore'``.
- `errors` may also be an arbitrary callable taking a msg parameter.
- """
-
- errors = Handlers._resolve(errors)
- match = matchers.load(match)
-
- try:
- childList = self.listdir()
- except Exception as exc:
- errors(f"Unable to list directory '{self}': {exc}")
- return
-
- for child in childList:
- traverse = None
- if match(child):
- traverse = yield child
- traverse = traverse or child.isdir
- try:
- do_traverse = traverse()
- except Exception as exc:
- errors(f"Unable to access '{child}': {exc}")
- continue
-
- if do_traverse:
- yield from child.walk(errors=errors, match=match)
-
- def walkdirs(self, *args, **kwargs):
- """Iterator over subdirs, recursively."""
- return (item for item in self.walk(*args, **kwargs) if item.isdir())
-
- def walkfiles(self, *args, **kwargs):
- """Iterator over files, recursively."""
- return (item for item in self.walk(*args, **kwargs) if item.isfile())
-
- def fnmatch(self, pattern, normcase=None):
- """Return ``True`` if `self.name` matches the given `pattern`.
-
- `pattern` - A filename pattern with wildcards,
- for example ``'*.py'``. If the pattern contains a `normcase`
- attribute, it is applied to the name and path prior to comparison.
-
- `normcase` - (optional) A function used to normalize the pattern and
- filename before matching. Defaults to normcase from
- ``self.module``, :func:`os.path.normcase`.
-
- .. seealso:: :func:`fnmatch.fnmatch`
- """
- default_normcase = getattr(pattern, 'normcase', self.module.normcase)
- normcase = normcase or default_normcase
- name = normcase(self.name)
- pattern = normcase(pattern)
- return fnmatch.fnmatchcase(name, pattern)
-
- def glob(self, pattern):
- """Return a list of Path objects that match the pattern.
-
- `pattern` - a path relative to this directory, with wildcards.
-
- For example, ``Path('/users').glob('*/bin/*')`` returns a list
- of all the files users have in their :file:`bin` directories.
-
- .. seealso:: :func:`glob.glob`
-
- .. note:: Glob is **not** recursive, even when using ``**``.
- To do recursive globbing see :func:`walk`,
- :func:`walkdirs` or :func:`walkfiles`.
- """
- cls = self._next_class
- return [cls(s) for s in glob.glob(self / pattern)]
-
- def iglob(self, pattern):
- """Return an iterator of Path objects that match the pattern.
-
- `pattern` - a path relative to this directory, with wildcards.
-
- For example, ``Path('/users').iglob('*/bin/*')`` returns an
- iterator of all the files users have in their :file:`bin`
- directories.
-
- .. seealso:: :func:`glob.iglob`
-
- .. note:: Glob is **not** recursive, even when using ``**``.
- To do recursive globbing see :func:`walk`,
- :func:`walkdirs` or :func:`walkfiles`.
- """
- cls = self._next_class
- return (cls(s) for s in glob.iglob(self / pattern))
-
- #
- # --- Reading or writing an entire file at once.
-
- def open(self, *args, **kwargs):
- """Open this file and return a corresponding file object.
-
- Keyword arguments work as in :func:`io.open`. If the file cannot be
- opened, an :class:`OSError` is raised.
- """
- return open(self, *args, **kwargs)
-
- def bytes(self):
- """Open this file, read all bytes, return them as a string."""
- with self.open('rb') as f:
- return f.read()
-
- def chunks(self, size, *args, **kwargs):
- """Returns a generator yielding chunks of the file, so it can
- be read piece by piece with a simple for loop.
-
- Any argument you pass after `size` will be passed to :meth:`open`.
-
- :example:
-
- >>> hash = hashlib.md5()
- >>> for chunk in Path("NEWS.rst").chunks(8192, mode='rb'):
- ... hash.update(chunk)
-
- This will read the file by chunks of 8192 bytes.
- """
- with self.open(*args, **kwargs) as f:
- yield from iter(lambda: f.read(size) or None, None)
-
- def write_bytes(self, bytes, append=False):
- """Open this file and write the given bytes to it.
-
- Default behavior is to overwrite any existing file.
- Call ``p.write_bytes(bytes, append=True)`` to append instead.
- """
- with self.open('ab' if append else 'wb') as f:
- f.write(bytes)
-
- def read_text(self, encoding=None, errors=None):
- r"""Open this file, read it in, return the content as a string.
-
- Optional parameters are passed to :meth:`open`.
-
- .. seealso:: :meth:`lines`
- """
- with self.open(encoding=encoding, errors=errors) as f:
- return f.read()
-
- def read_bytes(self):
- r"""Return the contents of this file as bytes."""
- with self.open(mode='rb') as f:
- return f.read()
-
- def text(self, encoding=None, errors='strict'):
- r"""Legacy function to read text.
-
- Converts all newline sequences to ``\n``.
- """
- warnings.warn(
- ".text is deprecated; use read_text",
- DeprecationWarning,
- stacklevel=2,
- )
- return U_NEWLINE.sub('\n', self.read_text(encoding, errors))
-
- def write_text(
- self, text, encoding=None, errors='strict', linesep=os.linesep, append=False
- ):
- r"""Write the given text to this file.
-
- The default behavior is to overwrite any existing file;
- to append instead, use the `append=True` keyword argument.
-
- There are two differences between :meth:`write_text` and
- :meth:`write_bytes`: newline handling and Unicode handling.
- See below.
-
- Parameters:
-
- `text` - str/bytes - The text to be written.
-
- `encoding` - str - The text encoding used.
-
- `errors` - str - How to handle Unicode encoding errors.
- Default is ``'strict'``. See ``help(unicode.encode)`` for the
- options. Ignored if `text` isn't a Unicode string.
-
- `linesep` - keyword argument - str/unicode - The sequence of
- characters to be used to mark end-of-line. The default is
- :data:`os.linesep`. Specify ``None`` to
- use newlines unmodified.
-
- `append` - keyword argument - bool - Specifies what to do if
- the file already exists (``True``: append to the end of it;
- ``False``: overwrite it). The default is ``False``.
-
-
- --- Newline handling.
-
- ``write_text()`` converts all standard end-of-line sequences
- (``'\n'``, ``'\r'``, and ``'\r\n'``) to your platform's default
- end-of-line sequence (see :data:`os.linesep`; on Windows, for example,
- the end-of-line marker is ``'\r\n'``).
-
- To override the platform's default, pass the `linesep=`
- keyword argument. To preserve the newlines as-is, pass
- ``linesep=None``.
-
- This handling applies to Unicode text and bytes, except
- with Unicode, additional non-ASCII newlines are recognized:
- ``\x85``, ``\r\x85``, and ``\u2028``.
-
- --- Unicode
-
- If `text` isn't Unicode, then apart from newline handling, the
- bytes are written verbatim to the file. The `encoding` and
- `errors` arguments are not used and must be omitted.
-
- If `text` is Unicode, it is first converted to :func:`bytes` using the
- specified `encoding` (or the default encoding if `encoding`
- isn't specified). The `errors` argument applies only to this
- conversion.
- """
- if isinstance(text, str):
- if linesep is not None:
- text = U_NEWLINE.sub(linesep, text)
- bytes = text.encode(encoding or sys.getdefaultencoding(), errors)
- else:
- warnings.warn(
- "Writing bytes in write_text is deprecated",
- DeprecationWarning,
- stacklevel=1,
- )
- assert encoding is None
- if linesep is not None:
- text = B_NEWLINE.sub(linesep.encode(), text)
- bytes = text
- self.write_bytes(bytes, append=append)
-
- def lines(self, encoding=None, errors=None, retain=True):
- r"""Open this file, read all lines, return them in a list.
-
- Optional arguments:
- `encoding` - The Unicode encoding (or character set) of
- the file. The default is ``None``, meaning use
- ``locale.getpreferredencoding()``.
- `errors` - How to handle Unicode errors; see
- `open <https://docs.python.org/3/library/functions.html#open>`_
- for the options. Default is ``None`` meaning "strict".
- `retain` - If ``True`` (default), retain newline characters,
- but translate all newline
- characters to ``\n``. If ``False``, newline characters are
- omitted.
-
- .. seealso:: :meth:`text`
- """
- text = U_NEWLINE.sub('\n', self.read_text(encoding, errors))
- return text.splitlines(retain)
-
- def write_lines(
- self,
- lines,
- encoding=None,
- errors='strict',
- linesep=_default_linesep,
- append=False,
- ):
- r"""Write the given lines of text to this file.
-
- By default this overwrites any existing file at this path.
-
- This puts a platform-specific newline sequence on every line.
- See `linesep` below.
-
- `lines` - A list of strings.
-
- `encoding` - A Unicode encoding to use. This applies only if
- `lines` contains any Unicode strings.
-
- `errors` - How to handle errors in Unicode encoding. This
- also applies only to Unicode strings.
-
- linesep - (deprecated) The desired line-ending. This line-ending is
- applied to every line. If a line already has any
- standard line ending (``'\r'``, ``'\n'``, ``'\r\n'``,
- ``u'\x85'``, ``u'\r\x85'``, ``u'\u2028'``), that will
- be stripped off and this will be used instead. The
- default is os.linesep, which is platform-dependent
- (``'\r\n'`` on Windows, ``'\n'`` on Unix, etc.).
- Specify ``None`` to write the lines as-is, like
- ``.writelines`` on a file object.
-
- Use the keyword argument ``append=True`` to append lines to the
- file. The default is to overwrite the file.
- """
- mode = 'a' if append else 'w'
- with self.open(mode, encoding=encoding, errors=errors, newline='') as f:
- f.writelines(self._replace_linesep(lines, linesep))
-
- @staticmethod
- def _replace_linesep(lines, linesep):
- if linesep != _default_linesep:
- warnings.warn("linesep is deprecated", DeprecationWarning, stacklevel=3)
- else:
- linesep = os.linesep
- if linesep is None:
- return lines
-
- return (line + linesep for line in _strip_newlines(lines))
-
- def read_md5(self):
- """Calculate the md5 hash for this file.
-
- This reads through the entire file.
-
- .. seealso:: :meth:`read_hash`
- """
- return self.read_hash('md5')
-
- def _hash(self, hash_name):
- """Returns a hash object for the file at the current path.
-
- `hash_name` should be a hash algo name (such as ``'md5'``
- or ``'sha1'``) that's available in the :mod:`hashlib` module.
- """
- m = hashlib.new(hash_name)
- for chunk in self.chunks(8192, mode="rb"):
- m.update(chunk)
- return m
-
- def read_hash(self, hash_name):
- """Calculate given hash for this file.
-
- List of supported hashes can be obtained from :mod:`hashlib` package.
- This reads the entire file.
-
- .. seealso:: :meth:`hashlib.hash.digest`
- """
- return self._hash(hash_name).digest()
-
- def read_hexhash(self, hash_name):
- """Calculate given hash for this file, returning hexdigest.
-
- List of supported hashes can be obtained from :mod:`hashlib` package.
- This reads the entire file.
-
- .. seealso:: :meth:`hashlib.hash.hexdigest`
- """
- return self._hash(hash_name).hexdigest()
-
- # --- Methods for querying the filesystem.
- # N.B. On some platforms, the os.path functions may be implemented in C
- # (e.g. isdir on Windows, Python 3.2.2), and compiled functions don't get
- # bound. Playing it safe and wrapping them all in method calls.
-
- def isabs(self):
- """
- >>> Path('.').isabs()
- False
-
- .. seealso:: :func:`os.path.isabs`
- """
- return self.module.isabs(self)
-
- def exists(self):
- """.. seealso:: :func:`os.path.exists`"""
- return self.module.exists(self)
-
- def isdir(self):
- """.. seealso:: :func:`os.path.isdir`"""
- return self.module.isdir(self)
-
- def isfile(self):
- """.. seealso:: :func:`os.path.isfile`"""
- return self.module.isfile(self)
-
- def islink(self):
- """.. seealso:: :func:`os.path.islink`"""
- return self.module.islink(self)
-
- def ismount(self):
- """
- >>> Path('.').ismount()
- False
-
- .. seealso:: :func:`os.path.ismount`
- """
- return self.module.ismount(self)
-
- def samefile(self, other):
- """.. seealso:: :func:`os.path.samefile`"""
- return self.module.samefile(self, other)
-
- def getatime(self):
- """.. seealso:: :attr:`atime`, :func:`os.path.getatime`"""
- return self.module.getatime(self)
-
- def set_atime(self, value):
- mtime_ns = self.stat().st_atime_ns
- self.utime(ns=(_make_timestamp_ns(value), mtime_ns))
-
- atime = property(
- getatime,
- set_atime,
- None,
- """
- Last access time of the file.
-
- >>> Path('.').atime > 0
- True
-
- Allows setting:
-
- >>> some_file = Path(getfixture('tmp_path')).joinpath('file.txt').touch()
- >>> MST = datetime.timezone(datetime.timedelta(hours=-7))
- >>> some_file.atime = datetime.datetime(1976, 5, 7, 10, tzinfo=MST)
- >>> some_file.atime
- 200336400.0
-
- .. seealso:: :meth:`getatime`, :func:`os.path.getatime`
- """,
- )
-
- def getmtime(self):
- """.. seealso:: :attr:`mtime`, :func:`os.path.getmtime`"""
- return self.module.getmtime(self)
-
- def set_mtime(self, value):
- atime_ns = self.stat().st_atime_ns
- self.utime(ns=(atime_ns, _make_timestamp_ns(value)))
-
- mtime = property(
- getmtime,
- set_mtime,
- None,
- """
- Last modified time of the file.
-
- Allows setting:
-
- >>> some_file = Path(getfixture('tmp_path')).joinpath('file.txt').touch()
- >>> MST = datetime.timezone(datetime.timedelta(hours=-7))
- >>> some_file.mtime = datetime.datetime(1976, 5, 7, 10, tzinfo=MST)
- >>> some_file.mtime
- 200336400.0
-
- .. seealso:: :meth:`getmtime`, :func:`os.path.getmtime`
- """,
- )
-
- def getctime(self):
- """.. seealso:: :attr:`ctime`, :func:`os.path.getctime`"""
- return self.module.getctime(self)
-
- ctime = property(
- getctime,
- None,
- None,
- """ Creation time of the file.
-
- .. seealso:: :meth:`getctime`, :func:`os.path.getctime`
- """,
- )
-
- def getsize(self):
- """.. seealso:: :attr:`size`, :func:`os.path.getsize`"""
- return self.module.getsize(self)
-
- size = property(
- getsize,
- None,
- None,
- """ Size of the file, in bytes.
-
- .. seealso:: :meth:`getsize`, :func:`os.path.getsize`
- """,
- )
-
- @property
- def permissions(self) -> masks.Permissions:
- """
- Permissions.
-
- >>> perms = Path('.').permissions
- >>> isinstance(perms, int)
- True
- >>> set(perms.symbolic) <= set('rwx-')
- True
- >>> perms.symbolic
- 'r...'
- """
- return masks.Permissions(self.stat().st_mode)
-
- def access(self, *args, **kwargs):
- """
- Return does the real user have access to this path.
-
- >>> Path('.').access(os.F_OK)
- True
-
- .. seealso:: :func:`os.access`
- """
- return os.access(self, *args, **kwargs)
-
- def stat(self):
- """
- Perform a ``stat()`` system call on this path.
-
- >>> Path('.').stat()
- os.stat_result(...)
-
- .. seealso:: :meth:`lstat`, :func:`os.stat`
- """
- return os.stat(self)
-
- def lstat(self):
- """
- Like :meth:`stat`, but do not follow symbolic links.
-
- >>> Path('.').lstat() == Path('.').stat()
- True
-
- .. seealso:: :meth:`stat`, :func:`os.lstat`
- """
- return os.lstat(self)
-
- def __get_owner_windows(self): # pragma: nocover
- r"""
- Return the name of the owner of this file or directory. Follow
- symbolic links.
-
- Return a name of the form ``DOMAIN\User Name``; may be a group.
-
- .. seealso:: :attr:`owner`
- """
- desc = win32security.GetFileSecurity(
- self, win32security.OWNER_SECURITY_INFORMATION
- )
- sid = desc.GetSecurityDescriptorOwner()
- account, domain, typecode = win32security.LookupAccountSid(None, sid)
- return domain + '\\' + account
-
- def __get_owner_unix(self): # pragma: nocover
- """
- Return the name of the owner of this file or directory. Follow
- symbolic links.
-
- .. seealso:: :attr:`owner`
- """
- st = self.stat()
- return pwd.getpwuid(st.st_uid).pw_name
-
- def __get_owner_not_implemented(self): # pragma: nocover
- raise NotImplementedError("Ownership not available on this platform.")
-
- get_owner = (
- __get_owner_windows
- if 'win32security' in globals()
- else __get_owner_unix
- if 'pwd' in globals()
- else __get_owner_not_implemented
- )
-
- owner = property(
- get_owner,
- None,
- None,
- """ Name of the owner of this file or directory.
-
- .. seealso:: :meth:`get_owner`""",
- )
-
- if hasattr(os, 'statvfs'):
-
- def statvfs(self):
- """Perform a ``statvfs()`` system call on this path.
-
- .. seealso:: :func:`os.statvfs`
- """
- return os.statvfs(self)
-
- if hasattr(os, 'pathconf'):
-
- def pathconf(self, name):
- """.. seealso:: :func:`os.pathconf`"""
- return os.pathconf(self, name)
-
- #
- # --- Modifying operations on files and directories
-
- def utime(self, *args, **kwargs):
- """Set the access and modified times of this file.
-
- .. seealso:: :func:`os.utime`
- """
- os.utime(self, *args, **kwargs)
- return self
-
- def chmod(self, mode):
- """
- Set the mode. May be the new mode (os.chmod behavior) or a `symbolic
- mode <http://en.wikipedia.org/wiki/Chmod#Symbolic_modes>`_.
-
- >>> a_file = Path(getfixture('tmp_path')).joinpath('afile.txt').touch()
- >>> a_file.chmod(0o700)
- Path(...
- >>> a_file.chmod('u+x')
- Path(...
-
- .. seealso:: :func:`os.chmod`
- """
- if isinstance(mode, str):
- mask = masks.compound(mode)
- mode = mask(self.stat().st_mode)
- os.chmod(self, mode)
- return self
-
- if hasattr(os, 'chown'):
-
- def chown(self, uid=-1, gid=-1):
- """
- Change the owner and group by names or numbers.
-
- .. seealso:: :func:`os.chown`
- """
-
- def resolve_uid(uid):
- return uid if isinstance(uid, int) else pwd.getpwnam(uid).pw_uid
-
- def resolve_gid(gid):
- return gid if isinstance(gid, int) else grp.getgrnam(gid).gr_gid
-
- os.chown(self, resolve_uid(uid), resolve_gid(gid))
- return self
-
- def rename(self, new):
- """.. seealso:: :func:`os.rename`"""
- os.rename(self, new)
- return self._next_class(new)
-
- def renames(self, new):
- """.. seealso:: :func:`os.renames`"""
- os.renames(self, new)
- return self._next_class(new)
-
- #
- # --- Create/delete operations on directories
-
- def mkdir(self, mode=0o777):
- """.. seealso:: :func:`os.mkdir`"""
- os.mkdir(self, mode)
- return self
-
- def mkdir_p(self, mode=0o777):
- """Like :meth:`mkdir`, but does not raise an exception if the
- directory already exists."""
- with contextlib.suppress(FileExistsError):
- self.mkdir(mode)
- return self
-
- def makedirs(self, mode=0o777):
- """.. seealso:: :func:`os.makedirs`"""
- os.makedirs(self, mode)
- return self
-
- def makedirs_p(self, mode=0o777):
- """Like :meth:`makedirs`, but does not raise an exception if the
- directory already exists."""
- with contextlib.suppress(FileExistsError):
- self.makedirs(mode)
- return self
-
- def rmdir(self):
- """.. seealso:: :func:`os.rmdir`"""
- os.rmdir(self)
- return self
-
- def rmdir_p(self):
- """Like :meth:`rmdir`, but does not raise an exception if the
- directory is not empty or does not exist."""
- suppressed = FileNotFoundError, FileExistsError, DirectoryNotEmpty
- with contextlib.suppress(suppressed):
- with DirectoryNotEmpty.translate():
- self.rmdir()
- return self
-
- def removedirs(self):
- """.. seealso:: :func:`os.removedirs`"""
- os.removedirs(self)
- return self
-
- def removedirs_p(self):
- """Like :meth:`removedirs`, but does not raise an exception if the
- directory is not empty or does not exist."""
- with contextlib.suppress(FileExistsError, DirectoryNotEmpty):
- with DirectoryNotEmpty.translate():
- self.removedirs()
- return self
-
- # --- Modifying operations on files
-
- def touch(self):
- """Set the access/modified times of this file to the current time.
- Create the file if it does not exist.
- """
- os.close(os.open(self, os.O_WRONLY | os.O_CREAT, 0o666))
- os.utime(self, None)
- return self
-
- def remove(self):
- """.. seealso:: :func:`os.remove`"""
- os.remove(self)
- return self
-
- def remove_p(self):
- """Like :meth:`remove`, but does not raise an exception if the
- file does not exist."""
- with contextlib.suppress(FileNotFoundError):
- self.unlink()
- return self
-
- unlink = remove
- unlink_p = remove_p
-
- # --- Links
-
- def link(self, newpath):
- """Create a hard link at `newpath`, pointing to this file.
-
- .. seealso:: :func:`os.link`
- """
- os.link(self, newpath)
- return self._next_class(newpath)
-
- def symlink(self, newlink=None):
- """Create a symbolic link at `newlink`, pointing here.
-
- If newlink is not supplied, the symbolic link will assume
- the name self.basename(), creating the link in the cwd.
-
- .. seealso:: :func:`os.symlink`
- """
- if newlink is None:
- newlink = self.basename()
- os.symlink(self, newlink)
- return self._next_class(newlink)
-
- def readlink(self):
- """Return the path to which this symbolic link points.
-
- The result may be an absolute or a relative path.
-
- .. seealso:: :meth:`readlinkabs`, :func:`os.readlink`
- """
- return self._next_class(os.readlink(self))
-
- def readlinkabs(self):
- """Return the path to which this symbolic link points.
-
- The result is always an absolute path.
-
- .. seealso:: :meth:`readlink`, :func:`os.readlink`
- """
- p = self.readlink()
- return p if p.isabs() else (self.parent / p).abspath()
-
- # High-level functions from shutil
- # These functions will be bound to the instance such that
- # Path(name).copy(target) will invoke shutil.copy(name, target)
-
- copyfile = shutil.copyfile
- copymode = shutil.copymode
- copystat = shutil.copystat
- copy = shutil.copy
- copy2 = shutil.copy2
- copytree = shutil.copytree
- if hasattr(shutil, 'move'):
- move = shutil.move
- rmtree = shutil.rmtree
-
- def rmtree_p(self):
- """Like :meth:`rmtree`, but does not raise an exception if the
- directory does not exist."""
- with contextlib.suppress(FileNotFoundError):
- self.rmtree()
- return self
-
- def chdir(self):
- """.. seealso:: :func:`os.chdir`"""
- os.chdir(self)
-
- cd = chdir
-
- def merge_tree(
- self,
- dst,
- symlinks=False,
- *,
- copy_function=shutil.copy2,
- ignore=lambda dir, contents: [],
- ):
- """
- Copy entire contents of self to dst, overwriting existing
- contents in dst with those in self.
-
- Pass ``symlinks=True`` to copy symbolic links as links.
-
- Accepts a ``copy_function``, similar to copytree.
-
- To avoid overwriting newer files, supply a copy function
- wrapped in ``only_newer``. For example::
-
- src.merge_tree(dst, copy_function=only_newer(shutil.copy2))
- """
- dst = self._next_class(dst)
- dst.makedirs_p()
-
- sources = self.listdir()
- _ignored = ignore(self, [item.name for item in sources])
-
- def ignored(item):
- return item.name in _ignored
-
- for source in itertools.filterfalse(ignored, sources):
- dest = dst / source.name
- if symlinks and source.islink():
- target = source.readlink()
- target.symlink(dest)
- elif source.isdir():
- source.merge_tree(
- dest,
- symlinks=symlinks,
- copy_function=copy_function,
- ignore=ignore,
- )
- else:
- copy_function(source, dest)
-
- self.copystat(dst)
-
- #
- # --- Special stuff from os
-
- if hasattr(os, 'chroot'):
-
- def chroot(self): # pragma: nocover
- """.. seealso:: :func:`os.chroot`"""
- os.chroot(self)
-
- if hasattr(os, 'startfile'):
-
- def startfile(self, *args, **kwargs): # pragma: nocover
- """.. seealso:: :func:`os.startfile`"""
- os.startfile(self, *args, **kwargs)
- return self
-
- # in-place re-writing, courtesy of Martijn Pieters
- # http://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/
- @contextlib.contextmanager
- def in_place(
- self,
- mode='r',
- buffering=-1,
- encoding=None,
- errors=None,
- newline=None,
- backup_extension=None,
- ):
- """
- A context in which a file may be re-written in-place with
- new content.
-
- Yields a tuple of :samp:`({readable}, {writable})` file
- objects, where `writable` replaces `readable`.
-
- If an exception occurs, the old file is restored, removing the
- written data.
-
- Mode *must not* use ``'w'``, ``'a'``, or ``'+'``; only
- read-only-modes are allowed. A :exc:`ValueError` is raised
- on invalid modes.
-
- For example, to add line numbers to a file::
-
- p = Path(filename)
- assert p.isfile()
- with p.in_place() as (reader, writer):
- for number, line in enumerate(reader, 1):
- writer.write('{0:3}: '.format(number)))
- writer.write(line)
-
- Thereafter, the file at `filename` will have line numbers in it.
- """
- if set(mode).intersection('wa+'):
- raise ValueError('Only read-only file modes can be used')
-
- # move existing file to backup, create new file with same permissions
- # borrowed extensively from the fileinput module
- backup_fn = self + (backup_extension or os.extsep + 'bak')
- backup_fn.remove_p()
- self.rename(backup_fn)
- readable = open(
- backup_fn,
- mode,
- buffering=buffering,
- encoding=encoding,
- errors=errors,
- newline=newline,
- )
- try:
- perm = os.fstat(readable.fileno()).st_mode
- except OSError:
- writable = self.open(
- 'w' + mode.replace('r', ''),
- buffering=buffering,
- encoding=encoding,
- errors=errors,
- newline=newline,
- )
- else:
- os_mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
- os_mode |= getattr(os, 'O_BINARY', 0)
- fd = os.open(self, os_mode, perm)
- writable = open(
- fd,
- "w" + mode.replace('r', ''),
- buffering=buffering,
- encoding=encoding,
- errors=errors,
- newline=newline,
- )
- with contextlib.suppress(OSError, AttributeError):
- self.chmod(perm)
- try:
- yield readable, writable
- except Exception:
- # move backup back
- readable.close()
- writable.close()
- self.remove_p()
- backup_fn.rename(self)
- raise
- else:
- readable.close()
- writable.close()
- finally:
- backup_fn.remove_p()
-
- @classes.ClassProperty
- @classmethod
- def special(cls):
- """
- Return a SpecialResolver object suitable referencing a suitable
- directory for the relevant platform for the given
- type of content.
-
- For example, to get a user config directory, invoke:
-
- dir = Path.special().user.config
-
- Uses the `appdirs
- <https://pypi.python.org/pypi/appdirs/1.4.0>`_ to resolve
- the paths in a platform-friendly way.
-
- To create a config directory for 'My App', consider:
-
- dir = Path.special("My App").user.config.makedirs_p()
-
- If the ``appdirs`` module is not installed, invocation
- of special will raise an ImportError.
- """
- return functools.partial(SpecialResolver, cls)
-
-
-class DirectoryNotEmpty(OSError):
- @staticmethod
- @contextlib.contextmanager
- def translate():
- try:
- yield
- except OSError as exc:
- if exc.errno == errno.ENOTEMPTY:
- raise DirectoryNotEmpty(*exc.args) from exc
- raise
-
-
-def only_newer(copy_func):
- """
- Wrap a copy function (like shutil.copy2) to return
- the dst if it's newer than the source.
- """
-
- @functools.wraps(copy_func)
- def wrapper(src, dst, *args, **kwargs):
- is_newer_dst = dst.exists() and dst.getmtime() >= src.getmtime()
- if is_newer_dst:
- return dst
- return copy_func(src, dst, *args, **kwargs)
-
- return wrapper
-
-
-class ExtantPath(Path):
- """
- >>> ExtantPath('.')
- ExtantPath('.')
- >>> ExtantPath('does-not-exist')
- Traceback (most recent call last):
- OSError: does-not-exist does not exist.
- """
-
- def _validate(self):
- if not self.exists():
- raise OSError(f"{self} does not exist.")
-
-
-class ExtantFile(Path):
- """
- >>> ExtantFile('.')
- Traceback (most recent call last):
- FileNotFoundError: . does not exist as a file.
- >>> ExtantFile('does-not-exist')
- Traceback (most recent call last):
- FileNotFoundError: does-not-exist does not exist as a file.
- """
-
- def _validate(self):
- if not self.isfile():
- raise FileNotFoundError(f"{self} does not exist as a file.")
-
-
-class SpecialResolver:
- class ResolverScope:
- def __init__(self, paths, scope):
- self.paths = paths
- self.scope = scope
-
- def __getattr__(self, class_):
- return self.paths.get_dir(self.scope, class_)
-
- def __init__(self, path_class, *args, **kwargs):
- appdirs = importlib.import_module('appdirs')
-
- vars(self).update(
- path_class=path_class, wrapper=appdirs.AppDirs(*args, **kwargs)
- )
-
- def __getattr__(self, scope):
- return self.ResolverScope(self, scope)
-
- def get_dir(self, scope, class_):
- """
- Return the callable function from appdirs, but with the
- result wrapped in self.path_class
- """
- prop_name = f'{scope}_{class_}_dir'
- value = getattr(self.wrapper, prop_name)
- MultiPath = Multi.for_class(self.path_class)
- return MultiPath.detect(value)
-
-
-class Multi:
- """
- A mix-in for a Path which may contain multiple Path separated by pathsep.
- """
-
- @classmethod
- def for_class(cls, path_cls):
- name = 'Multi' + path_cls.__name__
- return type(name, (cls, path_cls), {})
-
- @classmethod
- def detect(cls, input):
- if os.pathsep not in input:
- cls = cls._next_class
- return cls(input)
-
- def __iter__(self):
- return iter(map(self._next_class, self.split(os.pathsep)))
-
- @classes.ClassProperty
- @classmethod
- def _next_class(cls):
- """
- Multi-subclasses should use the parent class
- """
- return next(class_ for class_ in cls.__mro__ if not issubclass(class_, Multi))
-
-
-class TempDir(Path):
- """
- A temporary directory via :func:`tempfile.mkdtemp`, and
- constructed with the same parameters that you can use
- as a context manager.
-
- For example:
-
- >>> with TempDir() as d:
- ... d.isdir() and isinstance(d, Path)
- True
-
- The directory is deleted automatically.
-
- >>> d.isdir()
- False
-
- .. seealso:: :func:`tempfile.mkdtemp`
- """
-
- @classes.ClassProperty
- @classmethod
- def _next_class(cls):
- return Path
-
- def __new__(cls, *args, **kwargs):
- dirname = tempfile.mkdtemp(*args, **kwargs)
- return super().__new__(cls, dirname)
-
- def __init__(self, *args, **kwargs):
- pass
-
- def __enter__(self):
- # TempDir should return a Path version of itself and not itself
- # so that a second context manager does not create a second
- # temporary directory, but rather changes CWD to the location
- # of the temporary directory.
- return self._next_class(self)
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.rmtree()
-
-
-class Handlers:
- def strict(msg):
- raise
-
- def warn(msg):
- warnings.warn(msg, TreeWalkWarning)
-
- def ignore(msg):
- pass
-
- @classmethod
- def _resolve(cls, param):
- if not callable(param) and param not in vars(Handlers):
- raise ValueError("invalid errors parameter")
- return vars(cls).get(param, param)
diff --git a/contrib/python/path/path/classes.py b/contrib/python/path/path/classes.py
deleted file mode 100644
index b6101d0a7e..0000000000
--- a/contrib/python/path/path/classes.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import functools
-
-
-class ClassProperty(property):
- def __get__(self, cls, owner):
- return self.fget.__get__(None, owner)()
-
-
-class multimethod:
- """
- Acts like a classmethod when invoked from the class and like an
- instancemethod when invoked from the instance.
- """
-
- def __init__(self, func):
- self.func = func
-
- def __get__(self, instance, owner):
- """
- If called on an instance, pass the instance as the first
- argument.
- """
- return (
- functools.partial(self.func, owner)
- if instance is None
- else functools.partial(self.func, owner, instance)
- )
diff --git a/contrib/python/path/path/masks.py b/contrib/python/path/path/masks.py
deleted file mode 100644
index e7037e9603..0000000000
--- a/contrib/python/path/path/masks.py
+++ /dev/null
@@ -1,159 +0,0 @@
-import re
-import functools
-import operator
-import itertools
-
-
-# from jaraco.functools
-def compose(*funcs):
- compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs)) # noqa
- return functools.reduce(compose_two, funcs)
-
-
-# from jaraco.structures.binary
-def gen_bit_values(number):
- """
- Return a zero or one for each bit of a numeric value up to the most
- significant 1 bit, beginning with the least significant bit.
-
- >>> list(gen_bit_values(16))
- [0, 0, 0, 0, 1]
- """
- digits = bin(number)[2:]
- return map(int, reversed(digits))
-
-
-# from more_itertools
-def padded(iterable, fillvalue=None, n=None, next_multiple=False):
- """Yield the elements from *iterable*, followed by *fillvalue*, such that
- at least *n* items are emitted.
-
- >>> list(padded([1, 2, 3], '?', 5))
- [1, 2, 3, '?', '?']
-
- If *next_multiple* is ``True``, *fillvalue* will be emitted until the
- number of items emitted is a multiple of *n*::
-
- >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True))
- [1, 2, 3, 4, None, None]
-
- If *n* is ``None``, *fillvalue* will be emitted indefinitely.
-
- """
- it = iter(iterable)
- if n is None:
- yield from itertools.chain(it, itertools.repeat(fillvalue))
- elif n < 1:
- raise ValueError('n must be at least 1')
- else:
- item_count = 0
- for item in it:
- yield item
- item_count += 1
-
- remaining = (n - item_count) % n if next_multiple else n - item_count
- for _ in range(remaining):
- yield fillvalue
-
-
-def compound(mode):
- """
- Support multiple, comma-separated Unix chmod symbolic modes.
-
- >>> oct(compound('a=r,u+w')(0))
- '0o644'
- """
- return compose(*map(simple, reversed(mode.split(','))))
-
-
-def simple(mode):
- """
- Convert a Unix chmod symbolic mode like ``'ugo+rwx'`` to a function
- suitable for applying to a mask to affect that change.
-
- >>> mask = simple('ugo+rwx')
- >>> mask(0o554) == 0o777
- True
-
- >>> simple('go-x')(0o777) == 0o766
- True
-
- >>> simple('o-x')(0o445) == 0o444
- True
-
- >>> simple('a+x')(0) == 0o111
- True
-
- >>> simple('a=rw')(0o057) == 0o666
- True
-
- >>> simple('u=x')(0o666) == 0o166
- True
-
- >>> simple('g=')(0o157) == 0o107
- True
-
- >>> simple('gobbledeegook')
- Traceback (most recent call last):
- ValueError: ('Unrecognized symbolic mode', 'gobbledeegook')
- """
- # parse the symbolic mode
- parsed = re.match('(?P<who>[ugoa]+)(?P<op>[-+=])(?P<what>[rwx]*)$', mode)
- if not parsed:
- raise ValueError("Unrecognized symbolic mode", mode)
-
- # generate a mask representing the specified permission
- spec_map = dict(r=4, w=2, x=1)
- specs = (spec_map[perm] for perm in parsed.group('what'))
- spec = functools.reduce(operator.or_, specs, 0)
-
- # now apply spec to each subject in who
- shift_map = dict(u=6, g=3, o=0)
- who = parsed.group('who').replace('a', 'ugo')
- masks = (spec << shift_map[subj] for subj in who)
- mask = functools.reduce(operator.or_, masks)
-
- op = parsed.group('op')
-
- # if op is -, invert the mask
- if op == '-':
- mask ^= 0o777
-
- # if op is =, retain extant values for unreferenced subjects
- if op == '=':
- masks = (0o7 << shift_map[subj] for subj in who)
- retain = functools.reduce(operator.or_, masks) ^ 0o777
-
- op_map = {
- '+': operator.or_,
- '-': operator.and_,
- '=': lambda mask, target: target & retain ^ mask,
- }
- return functools.partial(op_map[op], mask)
-
-
-class Permissions(int):
- """
- >>> perms = Permissions(0o764)
- >>> oct(perms)
- '0o764'
- >>> perms.symbolic
- 'rwxrw-r--'
- >>> str(perms)
- 'rwxrw-r--'
- >>> str(Permissions(0o222))
- '-w--w--w-'
- """
-
- @property
- def symbolic(self):
- return ''.join(
- ['-', val][bit] for val, bit in zip(itertools.cycle('rwx'), self.bits)
- )
-
- @property
- def bits(self):
- return reversed(tuple(padded(gen_bit_values(self), 0, n=9)))
-
- def __str__(self):
- return self.symbolic
diff --git a/contrib/python/path/path/matchers.py b/contrib/python/path/path/matchers.py
deleted file mode 100644
index 63ca218a80..0000000000
--- a/contrib/python/path/path/matchers.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import ntpath
-import fnmatch
-
-
-def load(param):
- """
- If the supplied parameter is a string, assume it's a simple
- pattern.
- """
- return (
- Pattern(param)
- if isinstance(param, str)
- else param
- if param is not None
- else Null()
- )
-
-
-class Base:
- pass
-
-
-class Null(Base):
- def __call__(self, path):
- return True
-
-
-class Pattern(Base):
- def __init__(self, pattern):
- self.pattern = pattern
-
- def get_pattern(self, normcase):
- try:
- return self._pattern
- except AttributeError:
- pass
- self._pattern = normcase(self.pattern)
- return self._pattern
-
- def __call__(self, path):
- normcase = getattr(self, 'normcase', path.module.normcase)
- pattern = self.get_pattern(normcase)
- return fnmatch.fnmatchcase(normcase(path.name), pattern)
-
-
-class CaseInsensitive(Pattern):
- """
- A Pattern with a ``'normcase'`` property, suitable for passing to
- :meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`,
- :meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive.
-
- For example, to get all files ending in .py, .Py, .pY, or .PY in the
- current directory::
-
- from path import Path, matchers
- Path('.').files(matchers.CaseInsensitive('*.py'))
- """
-
- normcase = staticmethod(ntpath.normcase)
diff --git a/contrib/python/path/path/py.typed b/contrib/python/path/path/py.typed
deleted file mode 100644
index e69de29bb2..0000000000
--- a/contrib/python/path/path/py.typed
+++ /dev/null
diff --git a/contrib/python/path/ya.make b/contrib/python/path/ya.make
deleted file mode 100644
index aa9cb07250..0000000000
--- a/contrib/python/path/ya.make
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(16.7.1)
-
-LICENSE(MIT)
-
-NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- path/__init__.py
- path/__init__.pyi
- path/classes.py
- path/classes.pyi
- path/masks.py
- path/masks.pyi
- path/matchers.py
- path/matchers.pyi
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/path/
- .dist-info/METADATA
- .dist-info/top_level.txt
- path/py.typed
-)
-
-END()
diff --git a/contrib/python/pygtrie/py2/LICENSE b/contrib/python/pygtrie/py2/LICENSE
deleted file mode 100644
index d645695673..0000000000
--- a/contrib/python/pygtrie/py2/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/contrib/python/pygtrie/py2/README.rst b/contrib/python/pygtrie/py2/README.rst
deleted file mode 100644
index 41ca83db94..0000000000
--- a/contrib/python/pygtrie/py2/README.rst
+++ /dev/null
@@ -1,66 +0,0 @@
-pygtrie
-=======
-
-.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=latest
- :target: http://pygtrie.readthedocs.io/en/latest/
- :alt: Documentation build status (latest)
-
-.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=stable
- :target: http://pygtrie.readthedocs.io/en/stable/
- :alt: Documentation build status (stable)
-
-.. image:: https://api.travis-ci.com/mina86/pygtrie.svg
- :target: https://travis-ci.com/mina86/pygtrie
- :alt: Continuous integration status
-
-pygtrie is a pure Python implementation of a trie data structure
-compatible with Python 2.x and Python 3.x.
-
-`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known
-as radix or prefix tree, is a tree associating keys to values where
-all the descendants of a node have a common prefix (associated with
-that node).
-
-The trie module contains ``Trie``, ``CharTrie`` and ``StringTrie``
-classes each implementing a mutable mapping interface, i.e. ``dict``
-interface. As such, in most circumstances, ``Trie`` could be used as
-a drop-in replacement for a ``dict``, but the prefix nature of the
-data structure is trie’s real strength.
-
-The module also contains ``PrefixSet`` class which uses a trie to
-store a set of prefixes such that a key is contained in the set if it
-or its prefix is stored in the set.
-
-Features
---------
-
-- A full mutable mapping implementation.
-
-- Supports iterating over as well as deleting a subtrie.
-
-- Supports prefix checking as well as shortest and longest prefix
- look-up.
-
-- Extensible for any kind of user-defined keys.
-
-- A PrefixSet supports “all keys starting with given prefix” logic.
-
-- Can store any value including None.
-
-Installation
-------------
-
-To install pygtrie, simply run::
-
- pip install pygtrie
-
-or by adding line such as::
-
- pygtrie == 2.*
-
-to project’s `requirements file
-<https://pip.pypa.io/en/latest/user_guide/#requirements-files>`_.
-Alternatively, if installation from source is desired, it can be
-achieved by executing::
-
- python setup.py install
diff --git a/contrib/python/pygtrie/py3/.dist-info/METADATA b/contrib/python/pygtrie/py3/.dist-info/METADATA
deleted file mode 100644
index bb99559336..0000000000
--- a/contrib/python/pygtrie/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,220 +0,0 @@
-Metadata-Version: 2.1
-Name: pygtrie
-Version: 2.5.0
-Summary: A pure Python trie data structure implementation.
-Home-page: https://github.com/mina86/pygtrie
-Author: Michal Nazarewicz
-Author-email: mina86@mina86.com
-License: Apache-2.0
-Download-URL: https://github.com/mina86/pygtrie/tarball/v2.5.0
-Keywords: trie,prefix tree,data structure
-Platform: Platform Independent
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-License-File: LICENSE
-
-pygtrie
-=======
-
-pygtrie is a pure Python implementation of a trie data structure
-compatible with Python 2.x and Python 3.x.
-
-`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known
-as radix or prefix tree, is a tree associating keys to values where
-all the descendants of a node have a common prefix (associated with
-that node).
-
-The trie module contains ``Trie``, ``CharTrie`` and ``StringTrie``
-classes each implementing a mutable mapping interface, i.e. ``dict``
-interface. As such, in most circumstances, ``Trie`` could be used as
-a drop-in replacement for a ``dict``, but the prefix nature of the
-data structure is trie’s real strength.
-
-The module also contains ``PrefixSet`` class which uses a trie to
-store a set of prefixes such that a key is contained in the set if it
-or its prefix is stored in the set.
-
-Features
---------
-
-- A full mutable mapping implementation.
-
-- Supports iterating over as well as deleting a subtrie.
-
-- Supports prefix checking as well as shortest and longest prefix
- look-up.
-
-- Extensible for any kind of user-defined keys.
-
-- A PrefixSet supports “all keys starting with given prefix” logic.
-
-- Can store any value including None.
-
-Installation
-------------
-
-To install pygtrie, simply run::
-
- pip install pygtrie
-
-or by adding line such as::
-
- pygtrie == 2.*
-
-to project’s `requirements file
-<https://pip.pypa.io/en/latest/user_guide/#requirements-files>`_.
-Alternatively, if installation from source is desired, it can be
-achieved by executing::
-
- python setup.py install
-
-Version History
----------------
-
-2.5: 2022/07/16
-
-- Add ``pygtrie.Trie.merge`` method which merges structures of two
- tries.
-
-- Add ``pygtrie.Trie.strictly_equals`` method which compares two
- tries with stricter rules than regular equality operator. It’s not
- sufficient that keys and values are the same but the structure of
- the tries must be the same as well. For example:
-
- >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
- >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
- >>> t0 == t1
- True
- >>> t0.strictly_equals(t1)
- False
-
-- Fix ``pygtrie.Trie.__eq__`` implementation such that key values
- are taken into consideration rather than just looking at trie
- structure. To see what this means it’s best to look at a few
- examples. Firstly:
-
- >>> t0 = StringTrie({'foo/bar': 42}, separator='/')
- >>> t1 = StringTrie({'foo.bar': 42}, separator='.')
- >>> t0 == t1
- False
-
- This used to be true since the two tries have the same node
- structure. However, as far as Mapping interface is concerned, they
- use different keys, i.e. ```set(t0) != set(t1)``. Secondly:
-
- >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
- >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
- >>> t0 == t1
- True
-
- This used to be false since the two tries have different node
- structures (the first one splits key into ``('foo', 'bar.baz')``
- while the second into ``('foo/bar', 'baz')``). However, their keys
- are the same, i.e. ```set(t0) == set(t1)``. And lastly:
-
- >>> t0 = Trie({'foo': 42})
- >>> t1 = CharTrie({'foo': 42})
- >>> t0 == t1
- False
-
- This used to be true since the two tries have the same node
- structure. However, the two classes return key as different values.
- ``pygtrie.Trie`` returns keys as tuples while
- ``pygtrie.CharTrie`` returns them as strings.
-
-2.4.2: 2021/01/03
-
-- Remove use of ‘super’ in ``setup.py`` to fix compatibility with
- Python 2.7. This changes build code only; no changes to the library
- itself.
-
-2.4.1: 2020/11/20
-
-- Remove dependency on ``packaging`` module from ``setup.py`` to fix
- installation on systems without that package. This changes build
- code only; no changes to the library itself. [Thanks to Eric
- McLachlan for reporting]
-
-2.4.0: 2020/11/19 [pulled back from PyPi]
-
-- Change ``children`` argument of the ``node_factory`` passed to
- ``pygtrie.Trie.traverse`` from a generator to an iterator with
- a custom bool conversion. This allows checking whether node has
- children without having to iterate over them (``bool(children)``)
-
- To test whether this feature is available, one can check whether
- `Trie.traverse.uses_bool_convertible_children` property is true,
- e.g.: ``getattr(pygtrie.Trie.traverse,
- 'uses_bool_convertible_children', False)``.
-
- [Thanks to Pallab Pain for suggesting the feature]
-
-2.3.3: 2020/04/04
-
-- Fix to ‘``AttributeError``: ``_NoChildren`` object has no
- attribute ``sorted_items``’ failure when iterating over a trie with
- sorting enabled. [Thanks to Pallab Pain for reporting]
-
-- Add ``value`` property setter to step objects returned by
- ``pygtrie.Trie.walk_towards`` et al. This deprecates the
- ``set`` method.
-
-- The module now exports `pygtrie.__version__` making it possible to
- determine version of the library at run-time.
-
-2.3.2: 2019/07/18
-
-- Trivial metadata fix
-
-2.3.1: 2019/07/18 [pulled back from PyPi]
-
-- Fix to ``pygtrie.PrefixSet`` initialisation incorrectly storing
- elements even if their prefixes are also added to the set.
-
- For example, ``PrefixSet(('foo', 'foobar'))`` incorrectly resulted
- in a two-element set even though the interface dictates that only
- ``foo`` is kept (recall that if ``foo`` is member of the set,
- ``foobar`` is as well). [Thanks to Tal Maimon for reporting]
-
-- Fix to ``pygtrie.Trie.copy`` method not preserving
- enable-sorting flag and, in case of ``pygtrie.StringTrie``,
- ``separator`` property.
-
-- Add support for the ``copy`` module so ``copy.copy`` can now be
- used with trie objects.
-
-- Leafs and nodes with just one child use more memory-optimised
- representation which reduces overall memory usage of a trie
- structure.
-
-- Minor performance improvement for adding new elements to
- a ``pygtrie.PrefixSet``.
-
-- Improvements to string representation of objects which now includes
- type and, for ``pygtrie.StringTrie`` object, value of separator
- property.
-
-2.3: 2018/08/10
-
-- New ``pygtrie.Trie.walk_towards`` method allows walking a path
- towards a node with given key accessing each step of the path.
- Compared to `pygtrie.Trie.walk_prefixes` method, steps for nodes
- without assigned values are returned.
-
-- Fix to ``pygtrie.PrefixSet.copy`` not preserving type of backing
- trie.
-
-- ``pygtrie.StringTrie`` now checks and explicitly rejects empty
- separators. Previously empty separator would be accepted but lead
- to confusing errors later on. [Thanks to Waren Long]
-
-- Various documentation improvements, Python 2/3 compatibility and
- test coverage (python-coverage reports 100%).
-
diff --git a/contrib/python/pygtrie/py3/.dist-info/top_level.txt b/contrib/python/pygtrie/py3/.dist-info/top_level.txt
deleted file mode 100644
index 5b98eaa2e7..0000000000
--- a/contrib/python/pygtrie/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-pygtrie
diff --git a/contrib/python/pygtrie/py3/LICENSE b/contrib/python/pygtrie/py3/LICENSE
deleted file mode 100644
index d645695673..0000000000
--- a/contrib/python/pygtrie/py3/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/contrib/python/pygtrie/py3/README.rst b/contrib/python/pygtrie/py3/README.rst
deleted file mode 100644
index 41ca83db94..0000000000
--- a/contrib/python/pygtrie/py3/README.rst
+++ /dev/null
@@ -1,66 +0,0 @@
-pygtrie
-=======
-
-.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=latest
- :target: http://pygtrie.readthedocs.io/en/latest/
- :alt: Documentation build status (latest)
-
-.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=stable
- :target: http://pygtrie.readthedocs.io/en/stable/
- :alt: Documentation build status (stable)
-
-.. image:: https://api.travis-ci.com/mina86/pygtrie.svg
- :target: https://travis-ci.com/mina86/pygtrie
- :alt: Continuous integration status
-
-pygtrie is a pure Python implementation of a trie data structure
-compatible with Python 2.x and Python 3.x.
-
-`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known
-as radix or prefix tree, is a tree associating keys to values where
-all the descendants of a node have a common prefix (associated with
-that node).
-
-The trie module contains ``Trie``, ``CharTrie`` and ``StringTrie``
-classes each implementing a mutable mapping interface, i.e. ``dict``
-interface. As such, in most circumstances, ``Trie`` could be used as
-a drop-in replacement for a ``dict``, but the prefix nature of the
-data structure is trie’s real strength.
-
-The module also contains ``PrefixSet`` class which uses a trie to
-store a set of prefixes such that a key is contained in the set if it
-or its prefix is stored in the set.
-
-Features
---------
-
-- A full mutable mapping implementation.
-
-- Supports iterating over as well as deleting a subtrie.
-
-- Supports prefix checking as well as shortest and longest prefix
- look-up.
-
-- Extensible for any kind of user-defined keys.
-
-- A PrefixSet supports “all keys starting with given prefix” logic.
-
-- Can store any value including None.
-
-Installation
-------------
-
-To install pygtrie, simply run::
-
- pip install pygtrie
-
-or by adding line such as::
-
- pygtrie == 2.*
-
-to project’s `requirements file
-<https://pip.pypa.io/en/latest/user_guide/#requirements-files>`_.
-Alternatively, if installation from source is desired, it can be
-achieved by executing::
-
- python setup.py install
diff --git a/contrib/python/pygtrie/py3/pygtrie.py b/contrib/python/pygtrie/py3/pygtrie.py
deleted file mode 100644
index 5e91ef770c..0000000000
--- a/contrib/python/pygtrie/py3/pygtrie.py
+++ /dev/null
@@ -1,1939 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Pure Python implementation of a trie data structure compatible with Python
-2.x and Python 3.x.
-
-`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known as radix
-or prefix tree, is a tree associating keys to values where all the descendants
-of a node have a common prefix (associated with that node).
-
-The trie module contains :class:`pygtrie.Trie`, :class:`pygtrie.CharTrie` and
-:class:`pygtrie.StringTrie` classes each implementing a mutable mapping
-interface, i.e. :class:`dict` interface. As such, in most circumstances,
-:class:`pygtrie.Trie` could be used as a drop-in replacement for
-a :class:`dict`, but the prefix nature of the data structure is trie’s real
-strength.
-
-The module also contains :class:`pygtrie.PrefixSet` class which uses a trie to
-store a set of prefixes such that a key is contained in the set if it or its
-prefix is stored in the set.
-
-Features
---------
-
-- A full mutable mapping implementation.
-
-- Supports iterating over as well as deleting of a branch of a trie
- (i.e. subtrie)
-
-- Supports prefix checking as well as shortest and longest prefix
- look-up.
-
-- Extensible for any kind of user-defined keys.
-
-- A PrefixSet supports “all keys starting with given prefix” logic.
-
-- Can store any value including None.
-
-For a few simple examples see ``example.py`` file.
-"""
-
-from __future__ import absolute_import, division, print_function
-
-__author__ = 'Michal Nazarewicz <mina86@mina86.com>'
-__copyright__ = ('Copyright 2014-2017 Google LLC',
- 'Copyright 2018-2020 Michal Nazarewicz <mina86@mina86.com>')
-__version__ = '2.5.0'
-
-
-import copy as _copy
-try:
- import collections.abc as _abc
-except ImportError: # Python 2 compatibility
- import collections as _abc
-
-
-class ShortKeyError(KeyError):
- """Raised when given key is a prefix of an existing longer key
- but does not have a value associated with itself."""
-
-
-class _NoChildren(object):
- """Collection representing lack of any children.
-
- Also acts as an empty iterable and an empty iterator. This isn’t the
- cleanest designs but it makes various things more concise and avoids object
- allocations in a few places.
-
- Don’t create objects of this type directly; instead use _EMPTY singleton.
- """
- __slots__ = ()
-
- def __bool__(self):
- return False
- __nonzero__ = __bool__
- def __len__(self):
- return 0
- def __iter__(self):
- return self
- iteritems = sorted_items = __iter__
- def __next__(self):
- raise StopIteration()
- next = __next__
-
- def get(self, _step):
- return None
-
- def add(self, parent, step):
- node = _Node()
- parent.children = _OneChild(step, node)
- return node
-
- require = add
-
- def copy(self, _make_copy, _queue):
- return self
-
- def __deepcopy__(self, memo):
- return self
-
- # delete is not implemented on purpose since it should never be called on
- # a node with no children.
-
-
-_EMPTY = _NoChildren()
-
-
-class _OneChild(object):
- """Children collection representing a single child."""
-
- __slots__ = ('step', 'node')
-
- def __init__(self, step, node):
- self.step = step
- self.node = node
-
- def __bool__(self):
- return True
- __nonzero__ = __bool__
- def __len__(self):
- return 1
-
- def sorted_items(self):
- return [(self.step, self.node)]
-
- def iteritems(self):
- return iter(((self.step, self.node),))
-
- def get(self, step):
- return self.node if step == self.step else None
-
- def add(self, parent, step):
- node = _Node()
- parent.children = _Children((self.step, self.node), (step, node))
- return node
-
- def require(self, parent, step):
- return self.node if self.step == step else self.add(parent, step)
-
- def merge(self, other, queue):
- """Moves children from other into this object."""
- if type(other) == _OneChild and other.step == self.step:
- queue.append((self.node, other.node))
- return self
- else:
- children = _Children((self.step, self.node))
- children.merge(other, queue)
- return children
-
- def delete(self, parent, _step):
- parent.children = _EMPTY
-
- def copy(self, make_copy, queue):
- cpy = _OneChild(make_copy(self.step), self.node.shallow_copy(make_copy))
- queue.append((cpy.node,))
- return cpy
-
-
-class _Children(dict):
- """Children collection representing more than one child."""
-
- __slots__ = ()
-
- def __init__(self, *items):
- super(_Children, self).__init__(items)
-
- if hasattr(dict, 'iteritems'): # Python 2 compatibility
- def sorted_items(self):
- items = self.items()
- items.sort()
- return items
- else:
- def sorted_items(self):
- return sorted(self.items())
-
- def iteritems(self):
- return iter(self.items())
-
- def add(self, _parent, step):
- self[step] = node = _Node()
- return node
-
- def require(self, _parent, step):
- return self.setdefault(step, _Node())
-
- def merge(self, other, queue):
- """Moves children from other into this object."""
- for step, other_node in other.iteritems():
- node = self.setdefault(step, other_node)
- if node is not other_node:
- queue.append((node, other_node))
- return self
-
- def delete(self, parent, step):
- del self[step]
- if len(self) == 1:
- parent.children = _OneChild(*self.popitem())
-
- def copy(self, make_copy, queue):
- cpy = _Children()
- cpy.update((make_copy(step), node.shallow_copy(make_copy))
- for step, node in self.items())
- queue.append(cpy.values())
- return cpy
-
-
-class _Node(object):
- """A single node of a trie.
-
- Stores value associated with the node and dictionary of children.
- """
- __slots__ = ('children', 'value')
-
- def __init__(self):
- self.children = _EMPTY
- self.value = _EMPTY
-
- def merge(self, other, overwrite):
- """Move children from other node into this one.
-
- Args:
- other: Other node to move children and value from.
- overwrite: Whether to overwrite existing node values.
- """
- queue = [(self, other)]
- while queue:
- lhs, rhs = queue.pop()
- if lhs.value is _EMPTY or (overwrite and rhs.value is not _EMPTY):
- lhs.value = rhs.value
- if lhs.children is _EMPTY:
- lhs.children = rhs.children
- elif rhs.children is not _EMPTY:
- lhs.children = lhs.children.merge(rhs.children, queue)
- rhs.children = _EMPTY
-
- def iterate(self, path, shallow, iteritems):
- """Yields all the nodes with values associated to them in the trie.
-
- Args:
- path: Path leading to this node. Used to construct the key when
- returning value of this node and as a prefix for children.
- shallow: Perform a shallow traversal, i.e. do not yield nodes if
- their prefix has been yielded.
- iteritems: A callable taking ``node.children`` as sole argument and
- returning an iterable of children as ``(step, node)`` pair. The
- callable would typically call ``iteritems`` or ``sorted_items``
- method on the argument depending on whether sorted output is
- desired.
-
- Yields:
- ``(path, value)`` tuples.
- """
- # Use iterative function with stack on the heap so we don't hit Python's
- # recursion depth limits.
- node = self
- stack = []
- while True:
- if node.value is not _EMPTY:
- yield path, node.value
-
- if (not shallow or node.value is _EMPTY) and node.children:
- stack.append(iter(iteritems(node.children)))
- path.append(None)
-
- while True:
- try:
- step, node = next(stack[-1])
- path[-1] = step
- break
- except StopIteration:
- stack.pop()
- path.pop()
- except IndexError:
- return
-
- def traverse(self, node_factory, path_conv, path, iteritems):
- """Traverses the node and returns another type of node from factory.
-
- Args:
- node_factory: Callable to construct return value.
- path_conv: Callable to convert node path to a key.
- path: Current path for this node.
- iteritems: A callable taking ``node.children`` as sole argument and
- returning an iterable of children as ``(step, node)`` pair. The
- callable would typically call ``iteritems`` or ``sorted_items``
- method on the argument depending on whether sorted output is
- desired.
-
- Returns:
- An object constructed by calling node_factory(path_conv, path,
- children, value=...), where children are constructed by node_factory
- from the children of this node. There doesn't need to be 1:1
- correspondence between original nodes in the trie and constructed
- nodes (see make_test_node_and_compress in test.py).
- """
- children = self.children and (
- node.traverse(node_factory, path_conv, path + [step], iteritems)
- for step, node in iteritems(self.children))
-
- value_maybe = ()
- if self.value is not _EMPTY:
- value_maybe = (self.value,)
-
- return node_factory(path_conv, tuple(path), children, *value_maybe)
-
- def equals(self, other):
- """Returns whether this and other node are recursively equal."""
- # Like iterate, we don't recurse so this works on deep tries.
- a, b = self, other
- stack = []
- while True:
- if a.value != b.value or len(a.children) != len(b.children):
- return False
- if len(a.children) == 1:
- # We know a.children and b.children are both _OneChild objects
- # but pylint doesn’t recognise that: pylint: disable=no-member
- if a.children.step != b.children.step:
- return False
- a = a.children.node
- b = b.children.node
- continue
- if a.children:
- stack.append((a.children.iteritems(), b.children))
-
- while True:
- try:
- key, a = next(stack[-1][0])
- b = stack[-1][1][key]
- break
- except StopIteration:
- stack.pop()
- except IndexError:
- return True
- except KeyError:
- return False
-
- __bool__ = __nonzero__ = __hash__ = None
-
- def shallow_copy(self, make_copy):
- """Returns a copy of the node which shares the children property."""
- cpy = _Node()
- cpy.children = self.children
- cpy.value = make_copy(self.value)
- return cpy
-
- def copy(self, make_copy):
- """Returns a copy of the node structure."""
- cpy = self.shallow_copy(make_copy)
- queue = [(cpy,)]
- while queue:
- for node in queue.pop():
- node.children = node.children.copy(make_copy, queue)
- return cpy
-
- def __getstate__(self):
- """Get state used for pickling.
-
- The state is encoded as a list of simple commands which consist of an
- integer and some command-dependent number of arguments. The commands
- modify what the current node is by navigating the trie up and down and
- setting node values. Possible commands are:
-
- * [n, step0, step1, ..., stepn-1, value], for n >= 0, specifies step
- needed to reach the next current node as well as its new value. There
- is no way to create a child node without setting its (or its
- descendant's) value.
-
- * [-n], for -n < 0, specifies to go up n steps in the trie.
-
- When encoded as a state, the commands are flattened into a single list.
-
- For example::
-
- [ 0, 'Root',
- 2, 'Foo', 'Bar', 'Root/Foo/Bar Node',
- -1,
- 1, 'Baz', 'Root/Foo/Baz Node',
- -2,
- 1, 'Qux', 'Root/Qux Node' ]
-
- Creates the following hierarchy::
-
- -* value: Root
- +-- Foo --* no value
- | +-- Bar -- * value: Root/Foo/Bar Node
- | +-- Baz -- * value: Root/Foo/Baz Node
- +-- Qux -- * value: Root/Qux Node
-
- Returns:
- A pickable state which can be passed to :func:`_Node.__setstate__`
- to reconstruct the node and its full hierarchy.
- """
- # Like iterate, we don't recurse so pickling works on deep tries.
- state = [] if self.value is _EMPTY else [0]
- last_cmd = 0
- node = self
- stack = []
- while True:
- if node.value is not _EMPTY:
- last_cmd = 0
- state.append(node.value)
- stack.append(node.children.iteritems())
-
- while True:
- step, node = next(stack[-1], (None, None))
- if node is not None:
- break
-
- if last_cmd < 0:
- state[-1] -= 1
- else:
- last_cmd = -1
- state.append(-1)
- stack.pop()
- if not stack:
- state.pop() # Final -n command is not necessary
- return state
-
- if last_cmd > 0:
- last_cmd += 1
- state[-last_cmd] += 1
- else:
- last_cmd = 1
- state.append(1)
- state.append(step)
-
- def __setstate__(self, state):
- """Unpickles node. See :func:`_Node.__getstate__`."""
- self.__init__()
- state = iter(state)
- stack = [self]
- for cmd in state:
- if cmd < 0:
- del stack[cmd:]
- else:
- while cmd > 0:
- parent = stack[-1]
- stack.append(parent.children.add(parent, next(state)))
- cmd -= 1
- stack[-1].value = next(state)
-
-
-class Trie(_abc.MutableMapping):
- """A trie implementation with dict interface plus some extensions.
-
- Keys used with the :class:`pygtrie.Trie` class must be iterable which each
- component being a hashable objects. In other words, for a given key,
- ``dict.fromkeys(key)`` must be valid expression.
-
- In particular, strings work well as trie keys, however when getting them
- back (for example via :func:`Trie.iterkeys` method), instead of strings,
- tuples of characters are produced. For that reason,
- :class:`pygtrie.CharTrie` or :class:`pygtrie.StringTrie` classes may be
- preferred when using string keys.
- """
-
- def __init__(self, *args, **kwargs):
- """Initialises the trie.
-
- Arguments are interpreted the same way :func:`Trie.update` interprets
- them.
- """
- self._root = _Node()
- self._iteritems = self._ITERITEMS_CALLBACKS[0]
- self.update(*args, **kwargs)
-
- _ITERITEMS_CALLBACKS = (lambda x: x.iteritems(), lambda x: x.sorted_items())
-
- def enable_sorting(self, enable=True):
- """Enables sorting of child nodes when iterating and traversing.
-
- Normally, child nodes are not sorted when iterating or traversing over
- the trie (just like dict elements are not sorted). This method allows
- sorting to be enabled (which was the behaviour prior to pygtrie 2.0
- release).
-
- For Trie class, enabling sorting of children is identical to simply
- sorting the list of items since Trie returns keys as tuples. However,
- for other implementations such as StringTrie the two may behave subtly
- different. For example, sorting items might produce::
-
- root/foo-bar
- root/foo/baz
-
- even though foo comes before foo-bar.
-
- Args:
- enable: Whether to enable sorting of child nodes.
- """
- self._iteritems = self._ITERITEMS_CALLBACKS[bool(enable)]
-
- def __getstate__(self):
- # encode self._iteritems as self._sorted when pickling
- state = self.__dict__.copy()
- callback = state.pop('_iteritems', None)
- state['_sorted'] = callback is self._ITERITEMS_CALLBACKS[1]
- return state
-
- def __setstate__(self, state):
- # translate self._sorted back to _iteritems when unpickling
- self.__dict__ = state
- self.enable_sorting(state.pop('_sorted'))
-
- def clear(self):
- """Removes all the values from the trie."""
- self._root = _Node()
-
- def update(self, *args, **kwargs): # pylint: disable=arguments-differ
- """Updates stored values. Works like :meth:`dict.update`."""
- if len(args) > 1:
- raise ValueError('update() takes at most one positional argument, '
- '%d given.' % len(args))
- # We have this here instead of just letting MutableMapping.update()
- # handle things because it will iterate over keys and for each key
- # retrieve the value. With Trie, this may be expensive since the path
- # to the node would have to be walked twice. Instead, we have our own
- # implementation where iteritems() is used avoiding the unnecessary
- # value look-up.
- if args and isinstance(args[0], Trie):
- for key, value in args[0].items():
- self[key] = value
- args = ()
- super(Trie, self).update(*args, **kwargs)
-
- def merge(self, other, overwrite=False):
- """Moves nodes from other trie into this one.
-
- The merging happens at trie structure level and as such is different
- than iterating over items of one trie and setting them in the other
- trie.
-
- The merging may happen between different types of tries resulting in
- different (key, value) pairs in the destination trie compared to the
- source. For example, merging two :class:`pygtrie.StringTrie` objects
- each using different separators will work as if the other trie had
- separator of this trie. Similarly, a :class:`pygtrie.CharTrie` may be
- merged into a :class:`pygtrie.StringTrie` but when keys are read those
- will be joined by the separator. For example:
-
- >>> import pygtrie
- >>> st = pygtrie.StringTrie(separator='.')
- >>> st.merge(pygtrie.StringTrie({'foo/bar': 42}))
- >>> list(st.items())
- [('foo.bar', 42)]
- >>> st.merge(pygtrie.CharTrie({'baz': 24}))
- >>> sorted(st.items())
- [('b.a.z', 24), ('foo.bar', 42)]
-
- Not all tries can be merged into other tries. For example,
- a :class:`pygtrie.StringTrie` may not be merged into
- a :class:`pygtrie.CharTrie` because the latter imposes a requirement for
- each component in the key to be exactly one character while in the
- former components may be arbitrary length.
-
- Note that the other trie is cleared and any references or iterators over
- it are invalidated. To preserve other’s value it needs to be copied
- first.
-
- Args:
- other: Other trie to move nodes from.
- overwrite: Whether to overwrite existing values in this trie.
- """
- if isinstance(self, type(other)):
- self._merge_impl(self, other, overwrite=overwrite)
- else:
- other._merge_impl(self, other, overwrite=overwrite) # pylint: disable=protected-access
- other.clear()
-
- @classmethod
- def _merge_impl(cls, dst, src, overwrite):
- # pylint: disable=protected-access
- dst._root.merge(src._root, overwrite=overwrite)
-
- def copy(self, __make_copy=lambda x: x):
- """Returns a shallow copy of the object."""
- # pylint: disable=protected-access
- cpy = self.__class__()
- cpy.__dict__ = self.__dict__.copy()
- cpy._root = self._root.copy(__make_copy)
- return cpy
-
- def __copy__(self):
- return self.copy()
-
- def __deepcopy__(self, memo):
- return self.copy(lambda x: _copy.deepcopy(x, memo))
-
- @classmethod
- def fromkeys(cls, keys, value=None):
- """Creates a new trie with given keys set.
-
- This is roughly equivalent to calling the constructor with a ``(key,
- value) for key in keys`` generator.
-
- Args:
- keys: An iterable of keys that should be set in the new trie.
- value: Value to associate with given keys.
-
- Returns:
- A new trie where each key from ``keys`` has been set to the given
- value.
- """
- trie = cls()
- for key in keys:
- trie[key] = value
- return trie
-
- def _get_node(self, key):
- """Returns node for given key. Creates it if requested.
-
- Args:
- key: A key to look for.
-
- Returns:
- ``(node, trace)`` tuple where ``node`` is the node for given key and
- ``trace`` is a list specifying path to reach the node including all
- the encountered nodes. Each element of trace is a ``(step, node)``
- tuple where ``step`` is a step from parent node to given node and
- ``node`` is node on the path. The first element of the path is
- always ``(None, self._root)``.
-
- Raises:
- KeyError: If there is no node for the key.
- """
- node = self._root
- trace = [(None, node)]
- for step in self.__path_from_key(key):
- # pylint thinks node.children is always _NoChildren and thus that
- # we’re assigning None here; pylint: disable=assignment-from-none
- node = node.children.get(step)
- if node is None:
- raise KeyError(key)
- trace.append((step, node))
- return node, trace
-
- def _set_node(self, key, value, only_if_missing=False):
- """Sets value for a given key.
-
- Args:
- key: Key to set value of.
- value: Value to set to.
- only_if_missing: If true, value won't be changed if the key is
- already associated with a value.
-
- Returns:
- The node.
- """
- node = self._root
- for step in self.__path_from_key(key):
- node = node.children.require(node, step)
- if node.value is _EMPTY or not only_if_missing:
- node.value = value
- return node
-
- def _set_node_if_no_prefix(self, key):
- """Sets given key to True but only if none of its prefixes are present.
-
- If value is set, removes all ancestors of the node.
-
- This is a method for exclusive use by PrefixSet.
-
- Args:
- key: Key to set value of.
- """
- steps = iter(self.__path_from_key(key))
- node = self._root
- try:
- while node.value is _EMPTY:
- node = node.children.require(node, next(steps))
- except StopIteration:
- node.value = True
- node.children = _EMPTY
-
- def __iter__(self):
- return self.iterkeys()
-
- # pylint: disable=arguments-differ
-
- def iteritems(self, prefix=_EMPTY, shallow=False):
- """Yields all nodes with associated values with given prefix.
-
- Only nodes with values are output. For example::
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo'] = 'Foo'
- >>> t['foo/bar/baz'] = 'Baz'
- >>> t['qux'] = 'Qux'
- >>> sorted(t.items())
- [('foo', 'Foo'), ('foo/bar/baz', 'Baz'), ('qux', 'Qux')]
-
- Items are generated in topological order (i.e. parents before child
- nodes) but the order of siblings is unspecified. At an expense of
- efficiency, :func:`Trie.enable_sorting` method can turn deterministic
- ordering of siblings.
-
- With ``prefix`` argument, only items with specified prefix are generated
- (i.e. only given subtrie is traversed) as demonstrated by::
-
- >>> t.items(prefix='foo')
- [('foo', 'Foo'), ('foo/bar/baz', 'Baz')]
-
- With ``shallow`` argument, if a node has value associated with it, it's
- children are not traversed even if they exist which can be seen in::
-
- >>> sorted(t.items(shallow=True))
- [('foo', 'Foo'), ('qux', 'Qux')]
-
- Args:
- prefix: Prefix to limit iteration to.
- shallow: Perform a shallow traversal, i.e. do not yield items if
- their prefix has been yielded.
-
- Yields:
- ``(key, value)`` tuples.
-
- Raises:
- KeyError: If ``prefix`` does not match any node.
- """
- node, _ = self._get_node(prefix)
- for path, value in node.iterate(list(self.__path_from_key(prefix)),
- shallow, self._iteritems):
- yield (self._key_from_path(path), value)
-
- def iterkeys(self, prefix=_EMPTY, shallow=False):
- """Yields all keys having associated values with given prefix.
-
- This is equivalent to taking first element of tuples generated by
- :func:`Trie.iteritems` which see for more detailed documentation.
-
- Args:
- prefix: Prefix to limit iteration to.
- shallow: Perform a shallow traversal, i.e. do not yield keys if
- their prefix has been yielded.
-
- Yields:
- All the keys (with given prefix) with associated values in the trie.
-
- Raises:
- KeyError: If ``prefix`` does not match any node.
- """
- for key, _ in self.iteritems(prefix=prefix, shallow=shallow):
- yield key
-
- def itervalues(self, prefix=_EMPTY, shallow=False):
- """Yields all values associated with keys with given prefix.
-
- This is equivalent to taking second element of tuples generated by
- :func:`Trie.iteritems` which see for more detailed documentation.
-
- Args:
- prefix: Prefix to limit iteration to.
- shallow: Perform a shallow traversal, i.e. do not yield values if
- their prefix has been yielded.
-
- Yields:
- All the values associated with keys (with given prefix) in the trie.
-
- Raises:
- KeyError: If ``prefix`` does not match any node.
- """
- node, _ = self._get_node(prefix)
- for _, value in node.iterate(list(self.__path_from_key(prefix)),
- shallow, self._iteritems):
- yield value
-
- def items(self, prefix=_EMPTY, shallow=False):
- """Returns a list of ``(key, value)`` pairs in given subtrie.
-
- This is equivalent to constructing a list from generator returned by
- :func:`Trie.iteritems` which see for more detailed documentation.
- """
- return list(self.iteritems(prefix=prefix, shallow=shallow))
-
- def keys(self, prefix=_EMPTY, shallow=False):
- """Returns a list of all the keys, with given prefix, in the trie.
-
- This is equivalent to constructing a list from generator returned by
- :func:`Trie.iterkeys` which see for more detailed documentation.
- """
- return list(self.iterkeys(prefix=prefix, shallow=shallow))
-
- def values(self, prefix=_EMPTY, shallow=False):
- """Returns a list of values in given subtrie.
-
- This is equivalent to constructing a list from generator returned by
- :func:`Trie.itervalues` which see for more detailed documentation.
- """
- return list(self.itervalues(prefix=prefix, shallow=shallow))
-
- def __len__(self):
- """Returns number of values in a trie.
-
- Note that this method is expensive as it iterates over the whole trie.
- """
- return sum(1 for _ in self.itervalues())
-
- def __bool__(self):
- return self._root.value is not _EMPTY or bool(self._root.children)
-
- __nonzero__ = __bool__
- __hash__ = None
-
- HAS_VALUE = 1
- HAS_SUBTRIE = 2
-
- def has_node(self, key):
- """Returns whether given node is in the trie.
-
- Return value is a bitwise or of ``HAS_VALUE`` and ``HAS_SUBTRIE``
- constants indicating node has a value associated with it and that it is
- a prefix of another existing key respectively. Both of those are
- independent of each other and all of the four combinations are possible.
- For example::
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo/bar'] = 'Bar'
- >>> t['foo/bar/baz'] = 'Baz'
- >>> t.has_node('qux') == 0
- True
- >>> t.has_node('foo/bar/baz') == pygtrie.Trie.HAS_VALUE
- True
- >>> t.has_node('foo') == pygtrie.Trie.HAS_SUBTRIE
- True
- >>> t.has_node('foo/bar') == (pygtrie.Trie.HAS_VALUE |
- ... pygtrie.Trie.HAS_SUBTRIE)
- True
-
- There are two higher level methods built on top of this one which give
- easier interface for the information. :func:`Trie.has_key` returns
- whether node has a value associated with it and :func:`Trie.has_subtrie`
- checks whether node is a prefix. Continuing previous example::
-
- >>> t.has_key('qux'), t.has_subtrie('qux')
- (False, False)
- >>> t.has_key('foo/bar/baz'), t.has_subtrie('foo/bar/baz')
- (True, False)
- >>> t.has_key('foo'), t.has_subtrie('foo')
- (False, True)
- >>> t.has_key('foo/bar'), t.has_subtrie('foo/bar')
- (True, True)
-
- Args:
- key: A key to look for.
-
- Returns:
- Non-zero if node exists and if it does a bit-field denoting whether
- it has a value associated with it and whether it has a subtrie.
- """
- try:
- node, _ = self._get_node(key)
- except KeyError:
- return 0
- return ((self.HAS_VALUE * (node.value is not _EMPTY)) |
- (self.HAS_SUBTRIE * bool(node.children)))
-
- def has_key(self, key):
- """Indicates whether given key has value associated with it.
-
- See :func:`Trie.has_node` for more detailed documentation.
- """
- return bool(self.has_node(key) & self.HAS_VALUE)
-
- def has_subtrie(self, key):
- """Returns whether given key is a prefix of another key in the trie.
-
- See :func:`Trie.has_node` for more detailed documentation.
- """
- return bool(self.has_node(key) & self.HAS_SUBTRIE)
-
- @staticmethod
- def _slice_maybe(key_or_slice):
- """Checks whether argument is a slice or a plain key.
-
- Args:
- key_or_slice: A key or a slice to test.
-
- Returns:
- ``(key, is_slice)`` tuple. ``is_slice`` indicates whether
- ``key_or_slice`` is a slice and ``key`` is either ``key_or_slice``
- itself (if it's not a slice) or slice's start position.
-
- Raises:
- TypeError: If ``key_or_slice`` is a slice whose stop or step are not
- ``None`` In other words, only ``[key:]`` slices are valid.
- """
- if isinstance(key_or_slice, slice):
- if key_or_slice.stop is not None or key_or_slice.step is not None:
- raise TypeError(key_or_slice)
- return key_or_slice.start, True
- return key_or_slice, False
-
- def __getitem__(self, key_or_slice):
- """Returns value associated with given key or raises KeyError.
-
- When argument is a single key, value for that key is returned (or
- :class:`KeyError` exception is thrown if the node does not exist or has
- no value associated with it).
-
- When argument is a slice, it must be one with only `start` set in which
- case the access is identical to :func:`Trie.itervalues` invocation with
- prefix argument.
-
- Example:
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo/bar'] = 'Bar'
- >>> t['foo/baz'] = 'Baz'
- >>> t['qux'] = 'Qux'
- >>> t['foo/bar']
- 'Bar'
- >>> sorted(t['foo':])
- ['Bar', 'Baz']
- >>> t['foo'] # doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ...
- ShortKeyError: 'foo'
-
- Args:
- key_or_slice: A key or a slice to look for.
-
- Returns:
- If a single key is passed, a value associated with given key. If
- a slice is passed, a generator of values in specified subtrie.
-
- Raises:
- ShortKeyError: If the key has no value associated with it but is
- a prefix of some key with a value. Note that
- :class:`ShortKeyError` is subclass of :class:`KeyError`.
- KeyError: If key has no value associated with it nor is a prefix of
- an existing key.
- TypeError: If ``key_or_slice`` is a slice but it's stop or step are
- not ``None``.
- """
- if self._slice_maybe(key_or_slice)[1]:
- return self.itervalues(key_or_slice.start)
- node, _ = self._get_node(key_or_slice)
- if node.value is _EMPTY:
- raise ShortKeyError(key_or_slice)
- return node.value
-
- def __setitem__(self, key_or_slice, value):
- """Sets value associated with given key.
-
- If `key_or_slice` is a key, simply associate it with given value. If it
- is a slice (which must have `start` set only), it in addition clears any
- subtrie that might have been attached to particular key. For example::
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo/bar'] = 'Bar'
- >>> t['foo/baz'] = 'Baz'
- >>> sorted(t.keys())
- ['foo/bar', 'foo/baz']
- >>> t['foo':] = 'Foo'
- >>> t.keys()
- ['foo']
-
- Args:
- key_or_slice: A key to look for or a slice. If it is a slice, the
- whole subtrie (if present) will be replaced by a single node
- with given value set.
- value: Value to set.
-
- Raises:
- TypeError: If key is a slice whose stop or step are not None.
- """
- key, is_slice = self._slice_maybe(key_or_slice)
- node = self._set_node(key, value)
- if is_slice:
- node.children = _EMPTY
-
- def setdefault(self, key, default=None):
- """Sets value of a given node if not set already. Also returns it.
-
- In contrast to :func:`Trie.__setitem__`, this method does not accept
- slice as a key.
- """
- return self._set_node(key, default, only_if_missing=True).value
-
- @staticmethod
- def _pop_value(trace):
- """Removes value from given node and removes any empty nodes.
-
- Args:
- trace: Trace to the node to cleanup as returned by
- :func:`Trie._get_node`. The last element of the trace denotes
- the node to get value of.
-
- Returns:
- Value which was held in the node at the end of specified trace.
- This may be _EMPTY if the node didn’t have a value in the first
- place.
- """
- i = len(trace) - 1 # len(path) >= 1 since root is always there
- step, node = trace[i]
- value, node.value = node.value, _EMPTY
- while i and node.value is _EMPTY and not node.children:
- i -= 1
- parent_step, parent = trace[i]
- parent.children.delete(parent, step)
- step, node = parent_step, parent
- return value
-
- def pop(self, key, default=_EMPTY):
- """Deletes value associated with given key and returns it.
-
- Args:
- key: A key to look for.
- default: If specified, value that will be returned if given key has
- no value associated with it. If not specified, method will
- throw KeyError in such cases.
-
- Returns:
- Removed value, if key had value associated with it, or ``default``
- (if given).
-
- Raises:
- ShortKeyError: If ``default`` has not been specified and the key has
- no value associated with it but is a prefix of some key with
- a value. Note that :class:`ShortKeyError` is subclass of
- :class:`KeyError`.
- KeyError: If default has not been specified and key has no value
- associated with it nor is a prefix of an existing key.
- """
- try:
- _, trace = self._get_node(key)
- except KeyError:
- if default is not _EMPTY:
- return default
- raise
- value = self._pop_value(trace)
- if value is not _EMPTY:
- return value
- if default is not _EMPTY:
- return default
- raise ShortKeyError()
-
- def popitem(self):
- """Deletes an arbitrary value from the trie and returns it.
-
- There is no guarantee as to which item is deleted and returned. Neither
- in respect to its lexicographical nor topological order.
-
- Returns:
- ``(key, value)`` tuple indicating deleted key.
-
- Raises:
- KeyError: If the trie is empty.
- """
- if not self:
- raise KeyError()
- node = self._root
- trace = [(None, node)]
- while node.value is _EMPTY:
- step, node = next(node.children.iteritems())
- trace.append((step, node))
- key = self._key_from_path((step for step, _ in trace[1:]))
- return key, self._pop_value(trace)
-
- def __delitem__(self, key_or_slice):
- """Deletes value associated with given key or raises KeyError.
-
- If argument is a key, value associated with it is deleted. If the key
- is also a prefix, its descendents are not affected. On the other hand,
- if the argument is a slice (in which case it must have only start set),
- the whole subtrie is removed. For example::
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo'] = 'Foo'
- >>> t['foo/bar'] = 'Bar'
- >>> t['foo/bar/baz'] = 'Baz'
- >>> del t['foo/bar']
- >>> t.keys()
- ['foo', 'foo/bar/baz']
- >>> del t['foo':]
- >>> t.keys()
- []
-
- Args:
- key_or_slice: A key to look for or a slice. If key is a slice, the
- whole subtrie will be removed.
-
- Raises:
- ShortKeyError: If the key has no value associated with it but is
- a prefix of some key with a value. This is not thrown if
- key_or_slice is a slice -- in such cases, the whole subtrie is
- removed. Note that :class:`ShortKeyError` is subclass of
- :class:`KeyError`.
- KeyError: If key has no value associated with it nor is a prefix of
- an existing key.
- TypeError: If key is a slice whose stop or step are not ``None``.
- """
- key, is_slice = self._slice_maybe(key_or_slice)
- node, trace = self._get_node(key)
- if is_slice:
- node.children = _EMPTY
- elif node.value is _EMPTY:
- raise ShortKeyError(key)
- self._pop_value(trace)
-
- class _NoneStep(object):
- """Representation of a non-existent step towards non-existent node."""
-
- __slots__ = ()
-
- def __bool__(self):
- return False
- __nonzero__ = __bool__
-
- def get(self, default=None):
- return default
-
- is_set = has_subtrie = property(__bool__)
- key = value = property(lambda self: None)
-
- def __getitem__(self, index):
- """Makes object appear like a (key, value) tuple.
-
- This is deprecated and for backwards-compatibility only. Prefer
- using ``key`` and ``value`` properties directly.
-
- Args:
- index: Element index to return. Zero for key, one for value.
-
- Returns:
- ``self.key`` if index is ``0``, ``self.value`` if it's ``1``.
- Otherwise raises an IndexError exception.
-
- Raises:
- IndexError: if index is not 0 or 1.
- KeyError: if index is 1 but node has no value assigned.
- """
- if index == 0:
- return self.key
- if index == 1:
- return self.value
- raise IndexError('index out of range')
-
- def __repr__(self):
- return '(None Step)'
-
- class _Step(_NoneStep):
- """Representation of a single step on a path towards particular node."""
-
- __slots__ = ('_trie', '_path', '_pos', '_node', '__key')
-
- def __init__(self, trie, path, pos, node):
- self._trie = trie
- self._path = path
- self._pos = pos
- self._node = node
-
- def __bool__(self):
- return True
- __nonzero__ = __bool__
-
- @property
- def is_set(self):
- """Returns whether the node has value assigned to it."""
- return self._node.value is not _EMPTY
-
- @property
- def has_subtrie(self):
- """Returns whether the node has any children."""
- return bool(self._node.children)
-
- def get(self, default=None):
- """Returns node's value or the default if value is not assigned."""
- v = self._node.value
- return default if v is _EMPTY else v
-
- def set(self, value):
- """Deprecated. Use ``step.value = value`` instead."""
- self._node.value = value
-
- def setdefault(self, value):
- """Assigns value to the node if one is not set then returns it."""
- if self._node.value is _EMPTY:
- self._node.value = value
- return self._node.value
-
- def __repr__(self):
- return '(%r: %r)' % (self.key, self.value)
-
- @property
- def key(self):
- """Returns key of the node."""
- if not hasattr(self, '_Step__key'):
- # pylint:disable=protected-access,attribute-defined-outside-init
- self.__key = self._trie._key_from_path(self._path[:self._pos])
- return self.__key
-
- @property
- def value(self):
- """Returns node's value or raises KeyError."""
- v = self._node.value
- if v is _EMPTY:
- raise ShortKeyError(self.key)
- return v
-
- @value.setter
- def value(self, value):
- self._node.value = value
-
- _NONE_STEP = _NoneStep()
-
- def walk_towards(self, key):
- """Yields nodes on the path to given node.
-
- Args:
- key: Key of the node to look for.
-
- Yields:
- :class:`pygtrie.Trie._Step` objects which can be used to extract or
- set node's value as well as get node's key.
-
- When representing nodes with assigned values, the objects can be
- treated as ``(k, value)`` pairs denoting keys with associated values
- encountered on the way towards the specified key. This is
- deprecated, prefer using ``key`` and ``value`` properties or ``get``
- method of the object.
-
- Raises:
- KeyError: If node with given key does not exist. It's all right if
- they value is not assigned to the node provided it has a child
- node. Because the method is a generator, the exception is
- raised only once a missing node is encountered.
- """
- node = self._root
- path = self.__path_from_key(key)
- pos = 0
- while True:
- yield self._Step(self, path, pos, node)
- if pos == len(path):
- break
- # pylint thinks node.children is always _NoChildren and thus that
- # we’re assigning None here; pylint: disable=assignment-from-none
- node = node.children.get(path[pos])
- if node is None:
- raise KeyError(key)
- pos += 1
-
- def prefixes(self, key):
- """Walks towards the node specified by key and yields all found items.
-
- Example:
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo'] = 'Foo'
- >>> t['foo/bar/baz'] = 'Baz'
- >>> list(t.prefixes('foo/bar/baz/qux'))
- [('foo': 'Foo'), ('foo/bar/baz': 'Baz')]
- >>> list(t.prefixes('does/not/exist'))
- []
-
- Args:
- key: Key to look for.
-
- Yields:
- :class:`pygtrie.Trie._Step` objects which can be used to extract or
- set node's value as well as get node's key.
-
- The objects can be treated as ``(k, value)`` pairs denoting keys
- with associated values encountered on the way towards the specified
- key. This is deprecated, prefer using ``key`` and ``value``
- properties of the object.
- """
- try:
- for step in self.walk_towards(key):
- if step.is_set:
- yield step
- except KeyError:
- pass
-
- def shortest_prefix(self, key):
- """Finds the shortest prefix of a key with a value.
-
- This is roughly equivalent to taking the first object yielded by
- :func:`Trie.prefixes` with additional handling for situations when no
- prefixes are found.
-
- Example:
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo'] = 'Foo'
- >>> t['foo/bar/baz'] = 'Baz'
- >>> t.shortest_prefix('foo/bar/baz/qux')
- ('foo': 'Foo')
- >>> t.shortest_prefix('foo/bar/baz/qux').key
- 'foo'
- >>> t.shortest_prefix('foo/bar/baz/qux').value
- 'Foo'
- >>> t.shortest_prefix('does/not/exist')
- (None Step)
- >>> bool(t.shortest_prefix('does/not/exist'))
- False
-
- Args:
- key: Key to look for.
-
- Returns:
- :class:`pygtrie.Trie._Step` object (which can be used to extract or
- set node's value as well as get node's key), or
- a :class:`pygtrie.Trie._NoneStep` object (which is falsy value
- simulating a _Step with ``None`` key and value) if no prefix is
- found.
-
- The object can be treated as ``(key, value)`` pair denoting key with
- associated value of the prefix. This is deprecated, prefer using
- ``key`` and ``value`` properties of the object.
- """
- return next(self.prefixes(key), self._NONE_STEP)
-
- def longest_prefix(self, key):
- """Finds the longest prefix of a key with a value.
-
- This is roughly equivalent to taking the last object yielded by
- :func:`Trie.prefixes` with additional handling for situations when no
- prefixes are found.
-
- Example:
-
- >>> import pygtrie
- >>> t = pygtrie.StringTrie()
- >>> t['foo'] = 'Foo'
- >>> t['foo/bar/baz'] = 'Baz'
- >>> t.longest_prefix('foo/bar/baz/qux')
- ('foo/bar/baz': 'Baz')
- >>> t.longest_prefix('foo/bar/baz/qux').key
- 'foo/bar/baz'
- >>> t.longest_prefix('foo/bar/baz/qux').value
- 'Baz'
- >>> t.longest_prefix('does/not/exist')
- (None Step)
- >>> bool(t.longest_prefix('does/not/exist'))
- False
-
- Args:
- key: Key to look for.
-
- Returns:
- :class:`pygtrie.Trie._Step` object (which can be used to extract or
- set node's value as well as get node's key), or
- a :class:`pygtrie.Trie._NoneStep` object (which is falsy value
- simulating a _Step with ``None`` key and value) if no prefix is
- found.
-
- The object can be treated as ``(key, value)`` pair denoting key with
- associated value of the prefix. This is deprecated, prefer using
- ``key`` and ``value`` properties of the object.
- """
- ret = self._NONE_STEP
- for ret in self.prefixes(key):
- pass
- return ret
-
- def strictly_equals(self, other):
- """Checks whether tries are equal with the same structure.
-
- This is stricter comparison than the one performed by equality operator.
- It not only requires for keys and values to be equal but also for the
- two tries to be of the same type and have the same structure.
-
- For example, for two :class:`pygtrie.StringTrie` objects to be equal,
- they need to have the same structure as well as the same separator as
- seen below:
-
- >>> import pygtrie
- >>> t0 = StringTrie({'foo/bar': 42}, separator='/')
- >>> t1 = StringTrie({'foo.bar': 42}, separator='.')
- >>> t0.strictly_equals(t1)
- False
-
- >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
- >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
- >>> t0 == t1
- True
- >>> t0.strictly_equals(t1)
- False
-
- Args:
- other: Other trie to compare to.
-
- Returns:
- Whether the two tries are the same type and have the same structure.
- """
- if self is other:
- return True
- if type(self) != type(other):
- return False
- result = self._eq_impl(other)
- if result is NotImplemented:
- return False
- else:
- return result
-
- def __eq__(self, other):
- """Compares this trie’s mapping with another mapping.
-
- Note that this method doesn’t take trie’s structure into consideration.
- What matters is whether keys and values in both mappings are the same.
- This may lead to unexpected results, for example:
-
- >>> import pygtrie
- >>> t0 = StringTrie({'foo/bar': 42}, separator='/')
- >>> t1 = StringTrie({'foo.bar': 42}, separator='.')
- >>> t0 == t1
- False
-
- >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
- >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
- >>> t0 == t1
- True
-
- >>> t0 = Trie({'foo': 42})
- >>> t1 = CharTrie({'foo': 42})
- >>> t0 == t1
- False
-
- This behaviour is required to maintain consistency with Mapping
- interface and its __eq__ method. For example, this implementation
- maintains transitivity of the comparison:
-
- >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
- >>> d = {'foo/bar.baz': 42}
- >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
- >>> t0 == d
- True
- >>> d == t1
- True
- >>> t0 == t1
- True
-
- >>> t0 = Trie({'foo': 42})
- >>> d = {'foo': 42}
- >>> t1 = CharTrie({'foo': 42})
- >>> t0 == d
- False
- >>> d == t1
- True
- >>> t0 == t1
- False
-
- Args:
- other: Other object to compare to.
-
- Returns:
- ``NotImplemented`` if this method does not know how to perform the
- comparison or a ``bool`` denoting whether the two objects are equal
- or not.
- """
- if self is other:
- return True
- if type(other) == type(self):
- result = self._eq_impl(other)
- if result is not NotImplemented:
- return result
- return super(Trie, self).__eq__(other)
-
- def _eq_impl(self, other):
- return self._root.equals(other._root) # pylint: disable=protected-access
-
- def __ne__(self, other):
- return not self == other
-
- def _str_items(self, fmt='%s: %s'):
- return ', '.join(fmt % item for item in self.iteritems())
-
- def __str__(self):
- return '%s(%s)' % (type(self).__name__, self._str_items())
-
- def __repr__(self):
- return '%s([%s])' % (type(self).__name__, self._str_items('(%r, %r)'))
-
- def __path_from_key(self, key):
- """Converts a user visible key object to internal path representation.
-
- Args:
- key: User supplied key or ``_EMPTY``.
-
- Returns:
- An empty tuple if ``key`` was ``_EMPTY``, otherwise whatever
- :func:`Trie._path_from_key` returns.
-
- Raises:
- TypeError: If ``key`` is of invalid type.
- """
- return () if key is _EMPTY else self._path_from_key(key)
-
- def _path_from_key(self, key):
- """Converts a user visible key object to internal path representation.
-
- The default implementation simply returns key.
-
- Args:
- key: User supplied key.
-
- Returns:
- A path, which is an iterable of steps. Each step must be hashable.
-
- Raises:
- TypeError: If key is of invalid type.
- """
- return key
-
- def _key_from_path(self, path):
- """Converts an internal path into a user visible key object.
-
- The default implementation creates a tuple from the path.
-
- Args:
- path: Internal path representation.
- Returns:
- A user visible key object.
- """
- return tuple(path)
-
- def traverse(self, node_factory, prefix=_EMPTY):
- """Traverses the tree using node_factory object.
-
- node_factory is a callable which accepts (path_conv, path, children,
- value=...) arguments, where path_conv is a lambda converting path
- representation to key, path is the path to this node, children is an
- iterable of children nodes constructed by node_factory, optional value
- is the value associated with the path.
-
- node_factory's children argument is an iterator which has a few
- consequences:
-
- * To traverse into node's children, the object must be iterated over.
- This can by accomplished by a simple ``children = list(children)``
- statement.
- * Ignoring the argument allows node_factory to stop the traversal from
- going into the children of the node. In other words, whole subtries
- can be removed from traversal if node_factory chooses so.
- * If children is stored as is (i.e. as a iterator) when it is iterated
- over later on it may see an inconsistent state of the trie if it has
- changed between invocation of this method and the iteration.
-
- However, to allow constant-time determination whether the node has
- children or not, the iterator implements bool conversion such that
- ``has_children = bool(children)`` will tell whether node has children
- without iterating over them. (Note that ``bool(children)`` will
- continue returning ``True`` even if the iterator has been iterated
- over).
-
- :func:`Trie.traverse` has two advantages over :func:`Trie.iteritems` and
- similar methods:
-
- 1. it allows subtries to be skipped completely when going through the
- list of nodes based on the property of the parent node; and
-
- 2. it represents structure of the trie directly making it easy to
- convert structure into a different representation.
-
- For example, the below snippet prints all files in current directory
- counting how many HTML files were found but ignores hidden files and
- directories (i.e. those whose names start with a dot)::
-
- import os
- import pygtrie
-
- t = pygtrie.StringTrie(separator=os.sep)
-
- # Construct a trie with all files in current directory and all
- # of its sub-directories. Files get set a True value.
- # Directories are represented implicitly by being prefixes of
- # files.
- for root, _, files in os.walk('.'):
- for name in files: t[os.path.join(root, name)] = True
-
- def traverse_callback(path_conv, path, children, is_file=False):
- if path and path[-1] != '.' and path[-1][0] == '.':
- # Ignore hidden directory (but accept root node and '.')
- return 0
- elif is_file:
- print path_conv(path)
- return int(path[-1].endswith('.html'))
- else:
- # Otherwise, it's a directory. Traverse into children.
- return sum(children)
-
- print t.traverse(traverse_callback)
-
- As documented, ignoring the children argument causes subtrie to be
- omitted and not walked into.
-
- In the next example, the trie is converted to a tree representation
- where child nodes include a pointer to their parent. As before, hidden
- files and directories are ignored::
-
- import os
- import pygtrie
-
- t = pygtrie.StringTrie(separator=os.sep)
- for root, _, files in os.walk('.'):
- for name in files: t[os.path.join(root, name)] = True
-
- class File(object):
- def __init__(self, name):
- self.name = name
- self.parent = None
-
- class Directory(File):
- def __init__(self, name, children):
- super(Directory, self).__init__(name)
- self._children = children
- for child in children:
- child.parent = self
-
- def traverse_callback(path_conv, path, children, is_file=False):
- if not path or path[-1] == '.' or path[-1][0] != '.':
- if is_file:
- return File(path[-1])
- children = filter(None, children)
- return Directory(path[-1] if path else '', children)
-
- root = t.traverse(traverse_callback)
-
- Note: Unlike iterators, when used on a deep trie, traverse method is
- prone to rising a RuntimeError exception when Python's maximum recursion
- depth is reached. This can be addressed by not iterating over children
- inside of the node_factory. For example, the below code converts a trie
- into an undirected graph using adjacency list representation::
-
- def undirected_graph_from_trie(t):
- '''Converts trie into a graph and returns its nodes.'''
-
- Node = collections.namedtuple('Node', 'path neighbours')
-
- class Builder(object):
- def __init__(self, path_conv, path, children, _=None):
- self.node = Node(path_conv(path), [])
- self.children = children
- self.parent = None
-
- def build(self, queue):
- for builder in self.children:
- builder.parent = self.node
- queue.append(builder)
- if self.parent:
- self.parent.neighbours.append(self.node)
- self.node.neighbours.append(self.parent)
- return self.node
-
- nodes = [t.traverse(Builder)]
- i = 0
- while i < len(nodes):
- nodes[i] = nodes[i].build(nodes)
- i += 1
- return nodes
-
- Args:
- node_factory: Makes opaque objects from the keys and values of the
- trie.
- prefix: Prefix for node to start traversal, by default starts at
- root.
-
- Returns:
- Node object constructed by node_factory corresponding to the root
- node.
- """
- node, _ = self._get_node(prefix)
- return node.traverse(node_factory, self._key_from_path,
- list(self.__path_from_key(prefix)),
- self._iteritems)
-
- traverse.uses_bool_convertible_children = True
-
-class CharTrie(Trie):
- """A variant of a :class:`pygtrie.Trie` which accepts strings as keys.
-
- The only difference between :class:`pygtrie.CharTrie` and
- :class:`pygtrie.Trie` is that when :class:`pygtrie.CharTrie` returns keys
- back to the client (for instance when :func:`Trie.keys` method is called),
- those keys are returned as strings.
-
- Common example where this class can be used is a dictionary of words in
- a natural language. For example::
-
- >>> import pygtrie
- >>> t = pygtrie.CharTrie()
- >>> t['wombat'] = True
- >>> t['woman'] = True
- >>> t['man'] = True
- >>> t['manhole'] = True
- >>> t.has_subtrie('wo')
- True
- >>> t.has_key('man')
- True
- >>> t.has_subtrie('man')
- True
- >>> t.has_subtrie('manhole')
- False
- """
-
- def _key_from_path(self, path):
- return ''.join(path)
-
-
-class StringTrie(Trie):
- """:class:`pygtrie.Trie` variant accepting strings with a separator as keys.
-
- The trie accepts strings as keys which are split into components using
- a separator specified during initialisation (forward slash, i.e. ``/``, by
- default).
-
- Common example where this class can be used is when keys are paths. For
- example, it could map from a path to a request handler::
-
- import pygtrie
-
- def handle_root(): pass
- def handle_admin(): pass
- def handle_admin_images(): pass
-
- handlers = pygtrie.StringTrie()
- handlers[''] = handle_root
- handlers['/admin'] = handle_admin
- handlers['/admin/images'] = handle_admin_images
-
- request_path = '/admin/images/foo'
-
- handler = handlers.longest_prefix(request_path)
- """
-
- def __init__(self, *args, **kwargs): # pylint: disable=differing-param-doc
- """Initialises the trie.
-
- Except for a ``separator`` named argument, all other arguments are
- interpreted the same way :func:`Trie.update` interprets them.
-
- Args:
- *args: Passed to super class initialiser.
- **kwargs: Passed to super class initialiser.
- separator: A separator to use when splitting keys into paths used by
- the trie. "/" is used if this argument is not specified. This
- named argument is not specified on the function's prototype
- because of Python's limitations.
-
- Raises:
- TypeError: If ``separator`` is not a string.
- ValueError: If ``separator`` is empty.
- """
- separator = kwargs.pop('separator', '/')
- if not isinstance(separator, getattr(__builtins__, 'basestring', str)):
- raise TypeError('separator must be a string')
- if not separator:
- raise ValueError('separator can not be empty')
- self._separator = separator
- super(StringTrie, self).__init__(*args, **kwargs)
-
- @classmethod
- def fromkeys(cls, keys, value=None, separator='/'): # pylint: disable=arguments-differ
- trie = cls(separator=separator)
- for key in keys:
- trie[key] = value
- return trie
-
- @classmethod
- def _merge_impl(cls, dst, src, overwrite):
- if not isinstance(dst, StringTrie):
- raise TypeError('%s cannot be merged into a %s' % (
- type(src).__name__, type(dst).__name__))
- super(StringTrie, cls)._merge_impl(dst, src, overwrite=overwrite)
-
- def __str__(self):
- if not self:
- return '%s(separator=%s)' % (type(self).__name__, self._separator)
- return '%s(%s, separator=%s)' % (
- type(self).__name__, self._str_items(), self._separator)
-
- def __repr__(self):
- return '%s([%s], separator=%r)' % (
- type(self).__name__, self._str_items('(%r, %r)'), self._separator)
-
- def _eq_impl(self, other):
- # If separators differ, fall back to slow generic comparison. This is
- # because we want StringTrie(foo/bar.baz: 42, separator=/) compare equal
- # to StringTrie(foo/bar.baz: 42, separator=.) even though they have
- # different trie structure.
- if self._separator != other._separator: # pylint: disable=protected-access
- return NotImplemented
- return super(StringTrie, self)._eq_impl(other)
-
- def _path_from_key(self, key):
- return key.split(self._separator)
-
- def _key_from_path(self, path):
- return self._separator.join(path)
-
-
-class PrefixSet(_abc.MutableSet):
- """A set of prefixes.
-
- :class:`pygtrie.PrefixSet` works similar to a normal set except it is said
- to contain a key if the key or it's prefix is stored in the set. For
- instance, if "foo" is added to the set, the set contains "foo" as well as
- "foobar".
-
- The set supports addition of elements but does *not* support removal of
- elements. This is because there's no obvious consistent and intuitive
- behaviour for element deletion.
- """
-
- def __init__(self, iterable=(), factory=Trie, **kwargs):
- """Initialises the prefix set.
-
- Args:
- iterable: A sequence of keys to add to the set.
- factory: A function used to create a trie used by the
- :class:`pygtrie.PrefixSet`.
- kwargs: Additional keyword arguments passed to the factory function.
- """
- super(PrefixSet, self).__init__()
- self._trie = factory(**kwargs)
- for key in iterable:
- self.add(key)
-
- def copy(self):
- """Returns a shallow copy of the object."""
- return self.__copy__()
-
- def __copy__(self):
- # pylint: disable=protected-access
- cpy = self.__class__()
- cpy.__dict__ = self.__dict__.copy()
- cpy._trie = self._trie.__copy__()
- return cpy
-
- def __deepcopy__(self, memo):
- # pylint: disable=protected-access
- cpy = self.__class__()
- cpy.__dict__ = self.__dict__.copy()
- cpy._trie = self._trie.__deepcopy__(memo)
- return cpy
-
- def clear(self):
- """Removes all keys from the set."""
- self._trie.clear()
-
- def __contains__(self, key):
- """Checks whether set contains key or its prefix."""
- return bool(self._trie.shortest_prefix(key)[1])
-
- def __iter__(self):
- """Return iterator over all prefixes in the set.
-
- See :func:`PrefixSet.iter` method for more info.
- """
- return self._trie.iterkeys()
-
- def iter(self, prefix=_EMPTY):
- """Iterates over all keys in the set optionally starting with a prefix.
-
- Since a key does not have to be explicitly added to the set to be an
- element of the set, this method does not iterate over all possible keys
- that the set contains, but only over the shortest set of prefixes of all
- the keys the set contains.
-
- For example, if "foo" has been added to the set, the set contains also
- "foobar", but this method will *not* iterate over "foobar".
-
- If ``prefix`` argument is given, method will iterate over keys with
- given prefix only. The keys yielded from the function if prefix is
- given does not have to be a subset (in mathematical sense) of the keys
- yielded when there is not prefix. This happens, if the set contains
- a prefix of the given prefix.
-
- For example, if only "foo" has been added to the set, iter method called
- with no arguments will yield "foo" only. However, when called with
- "foobar" argument, it will yield "foobar" only.
- """
- if prefix is _EMPTY:
- return iter(self)
- if self._trie.has_node(prefix):
- return self._trie.iterkeys(prefix=prefix)
- if prefix in self:
- # Make sure the type of returned keys is consistent.
- # pylint: disable=protected-access
- return (
- self._trie._key_from_path(self._trie._path_from_key(prefix)),)
- return ()
-
- def __len__(self):
- """Returns number of keys stored in the set.
-
- Since a key does not have to be explicitly added to the set to be an
- element of the set, this method does not count over all possible keys
- that the set contains (since that would be infinity), but only over the
- shortest set of prefixes of all the keys the set contains.
-
- For example, if "foo" has been added to the set, the set contains also
- "foobar", but this method will *not* count "foobar".
-
- """
- return len(self._trie)
-
- def add(self, value):
- """Adds given value to the set.
-
- If the set already contains prefix of the value being added, this
- operation has no effect. If the value being added is a prefix of some
- existing values in the set, those values are deleted and replaced by
- a single entry for the value being added.
-
- For example, if the set contains value "foo" adding a value "foobar"
- does not change anything. On the other hand, if the set contains values
- "foobar" and "foobaz", adding a value "foo" will replace those two
- values with a single value "foo".
-
- This makes a difference when iterating over the values or counting
- number of values. Counter intuitively, adding of a value can *decrease*
- size of the set.
-
- Args:
- value: Value to add.
- """
- # We're friends with Trie; pylint: disable=protected-access
- self._trie._set_node_if_no_prefix(value)
-
- def discard(self, value):
- """Raises NotImplementedError."""
- raise NotImplementedError(
- 'Removing values from PrefixSet is not implemented.')
-
- def remove(self, value):
- """Raises NotImplementedError."""
- raise NotImplementedError(
- 'Removing values from PrefixSet is not implemented.')
-
- def pop(self):
- """Raises NotImplementedError."""
- raise NotImplementedError(
- 'Removing values from PrefixSet is not implemented.')
diff --git a/contrib/python/pygtrie/py3/ya.make b/contrib/python/pygtrie/py3/ya.make
deleted file mode 100644
index ecb0cffd62..0000000000
--- a/contrib/python/pygtrie/py3/ya.make
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(2.5.0)
-
-LICENSE(Apache-2.0)
-
-NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- pygtrie.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/pygtrie/py3/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
-END()
-
-RECURSE_FOR_TESTS(
- tests
-)
diff --git a/contrib/python/pygtrie/ya.make b/contrib/python/pygtrie/ya.make
deleted file mode 100644
index 03fb697fac..0000000000
--- a/contrib/python/pygtrie/ya.make
+++ /dev/null
@@ -1,18 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/pygtrie/py2)
-ELSE()
- PEERDIR(contrib/python/pygtrie/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- py2
- py3
-)
diff --git a/contrib/python/pyre2/py2/AUTHORS b/contrib/python/pyre2/py2/AUTHORS
deleted file mode 100644
index 0f1a37f2b4..0000000000
--- a/contrib/python/pyre2/py2/AUTHORS
+++ /dev/null
@@ -1,12 +0,0 @@
-All contributors own the copyright to their own contributions, but agree
-to release each of their contributions under the BSD license included
-in this software.
-
-Michael Axiak <mike@axiak.net>
-
-Contributors
-============
-
-Alec Berryman <alec@thened.net>
-Israel Tsadok <itsadok@gmail.com>
-Alex Willmer <alex@moreati.org.uk>
diff --git a/contrib/python/pyre2/py2/LICENSE b/contrib/python/pyre2/py2/LICENSE
deleted file mode 100644
index 803fbbcd9f..0000000000
--- a/contrib/python/pyre2/py2/LICENSE
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 2010, Michael Axiak <mike@axiak.net>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/python/pyre2/py2/README.rst b/contrib/python/pyre2/py2/README.rst
deleted file mode 100644
index 3f46ff6eaf..0000000000
--- a/contrib/python/pyre2/py2/README.rst
+++ /dev/null
@@ -1,250 +0,0 @@
-===============================================================
- pyre2: Python RE2 wrapper for linear-time regular expressions
-===============================================================
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Build/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Build
- :alt: Build CI Status
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Release/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Release
- :alt: Release CI Status
-
-.. image:: https://img.shields.io/github/v/tag/andreasvc/pyre2?color=green&include_prereleases&label=latest%20release
- :target: https://github.com/andreasvc/pyre2/releases
- :alt: GitHub tag (latest SemVer, including pre-release)
-
-.. image:: https://badge.fury.io/py/pyre2.svg
- :target: https://badge.fury.io/py/pyre2
- :alt: Pypi version
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Conda/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Conda
- :alt: Conda CI Status
-
-.. image:: https://img.shields.io/github/license/andreasvc/pyre2
- :target: https://github.com/andreasvc/pyre2/blob/master/LICENSE
- :alt: License
-
-.. image:: https://img.shields.io/badge/python-3.6+-blue.svg
- :target: https://www.python.org/downloads/
- :alt: Python version
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/version.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: version
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/platforms.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: platforms
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/downloads.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: downloads
-
-
-.. contents:: Table of Contents
- :depth: 2
- :backlinks: top
-
-
-Summary
-=======
-
-pyre2 is a Python extension that wraps
-`Google's RE2 regular expression library <https://github.com/google/re2>`_.
-The RE2 engine compiles (strictly) regular expressions to
-deterministic finite automata, which guarantees linear-time behavior.
-
-Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
-to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
-For best performance, work with UTF-8 encoded bytes strings.
-
-Installation
-============
-
-Normal usage for Linux/Mac/Windows::
-
- $ pip install pyre2
-
-Compiling from source
----------------------
-
-Requirements for building the C++ extension from the repo source:
-
-* A build environment with ``gcc`` or ``clang`` (e.g. ``sudo apt-get install build-essential``)
-* Build tools and libraries: RE2, pybind11, and cmake installed in the build
- environment.
-
- + On Ubuntu/Debian: ``sudo apt-get install build-essential cmake ninja-build python3-dev cython3 pybind11-dev libre2-dev``
- + On Gentoo, install dev-util/cmake, dev-python/pybind11, and dev-libs/re2
- + For a venv you can install the pybind11, cmake, and cython packages from PyPI
-
-On MacOS, use the ``brew`` package manager::
-
- $ brew install -s re2 pybind11
-
-On Windows use the ``vcpkg`` package manager::
-
- $ vcpkg install re2:x64-windows pybind11:x64-windows
-
-You can pass some cmake environment variables to alter the build type or
-pass a toolchain file (the latter is required on Windows) or specify the
-cmake generator. For example::
-
- $ CMAKE_GENERATOR="Unix Makefiles" CMAKE_TOOLCHAIN_FILE=clang_toolchain.cmake tox -e deploy
-
-For development, get the source::
-
- $ git clone git://github.com/andreasvc/pyre2.git
- $ cd pyre2
- $ make install
-
-
-Platform-agnostic building with conda
--------------------------------------
-
-An alternative to the above is provided via the `conda`_ recipe (use the
-`miniconda installer`_ if you don't have ``conda`` installed already).
-
-
-.. _conda: https://anaconda.org/conda-forge/pyre2
-.. _miniconda installer: https://docs.conda.io/en/latest/miniconda.html
-
-
-Backwards Compatibility
-=======================
-
-The stated goal of this module is to be a drop-in replacement for ``re``, i.e.::
-
- try:
- import re2 as re
- except ImportError:
- import re
-
-That being said, there are features of the ``re`` module that this module may
-never have; these will be handled through fallback to the original ``re`` module:
-
-* lookahead assertions ``(?!...)``
-* backreferences (``\\n`` in search pattern)
-* \W and \S not supported inside character classes
-
-On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
-Syntax reference: https://github.com/google/re2/wiki/Syntax
-
-However, there are times when you may want to be notified of a failover. The
-function ``set_fallback_notification`` determines the behavior in these cases::
-
- try:
- import re2 as re
- except ImportError:
- import re
- else:
- re.set_fallback_notification(re.FALLBACK_WARNING)
-
-``set_fallback_notification`` takes three values:
-``re.FALLBACK_QUIETLY`` (default), ``re.FALLBACK_WARNING`` (raise a warning),
-and ``re.FALLBACK_EXCEPTION`` (raise an exception).
-
-Documentation
-=============
-
-Consult the docstrings in the source code or interactively
-through ipython or ``pydoc re2`` etc.
-
-Unicode Support
-===============
-
-Python ``bytes`` and ``unicode`` strings are fully supported, but note that
-``RE2`` works with UTF-8 encoded strings under the hood, which means that
-``unicode`` strings need to be encoded and decoded back and forth.
-There are two important factors:
-
-* whether a ``unicode`` pattern and search string is used (will be encoded to UTF-8 internally)
-* the ``UNICODE`` flag: whether operators such as ``\w`` recognize Unicode characters.
-
-To avoid the overhead of encoding and decoding to UTF-8, it is possible to pass
-UTF-8 encoded bytes strings directly but still treat them as ``unicode``::
-
- In [18]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
- Out[18]: ['M', '\xc3\xb6', 't', 'l', 'e', 'y', 'C', 'r', '\xc3\xbc', 'e']
- In [19]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'))
- Out[19]: ['M', 't', 'l', 'e', 'y', 'C', 'r', 'e']
-
-However, note that the indices in ``Match`` objects will refer to the bytes string.
-The indices of the match in the ``unicode`` string could be computed by
-decoding/encoding, but this is done automatically and more efficiently if you
-pass the ``unicode`` string::
-
- >>> re2.search(u'ü'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
- <re2.Match object; span=(10, 12), match='\xc3\xbc'>
- >>> re2.search(u'ü', u'Mötley Crüe', flags=re2.UNICODE)
- <re2.Match object; span=(9, 10), match=u'\xfc'>
-
-Finally, if you want to match bytes without regard for Unicode characters,
-pass bytes strings and leave out the ``UNICODE`` flag (this will cause Latin 1
-encoding to be used with ``RE2`` under the hood)::
-
- >>> re2.findall(br'.', b'\x80\x81\x82')
- ['\x80', '\x81', '\x82']
-
-Performance
-===========
-
-Performance is of course the point of this module, so it better perform well.
-Regular expressions vary widely in complexity, and the salient feature of ``RE2`` is
-that it behaves well asymptotically. This being said, for very simple substitutions,
-I've found that occasionally python's regular ``re`` module is actually slightly faster.
-However, when the ``re`` module gets slow, it gets *really* slow, while this module
-buzzes along.
-
-In the below example, I'm running the data against 8MB of text from the colossal Wikipedia
-XML file. I'm running them multiple times, being careful to use the ``timeit`` module.
-To see more details, please see the `performance script <http://github.com/andreasvc/pyre2/tree/master/tests/performance.py>`_.
-
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Test |Description |# total runs|``re`` time(s)|``re2`` time(s)|% ``re`` time|``regex`` time(s)|% ``regex`` time|
-+=================+===========================================================================+============+==============+===============+=============+=================+================+
-|Findall URI|Email|Find list of '([a-zA-Z][a-zA-Z0-9]*)://([^ /]+)(/[^ ]*)?|([^ @]+)@([^ @]+)'|2 |6.262 |0.131 |2.08% |5.119 |2.55% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Replace WikiLinks|This test replaces links of the form [[Obama|Barack_Obama]] to Obama. |100 |4.374 |0.815 |18.63% |1.176 |69.33% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Remove WikiLinks |This test splits the data by the <page> tag. |100 |4.153 |0.225 |5.43% |0.537 |42.01% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-
-Feel free to add more speed tests to the bottom of the script and send a pull request my way!
-
-Current Status
-==============
-
-The tests show the following differences with Python's ``re`` module:
-
-* The ``$`` operator in Python's ``re`` matches twice if the string ends
- with ``\n``. This can be simulated using ``\n?$``, except when doing
- substitutions.
-* The ``pyre2`` module and Python's ``re`` may behave differently with nested groups.
- See ``tests/test_emptygroups.txt`` for the examples.
-
-Please report any further issues with ``pyre2``.
-
-Tests
-=====
-
-If you would like to help, one thing that would be very useful
-is writing comprehensive tests for this. It's actually really easy:
-
-* Come up with regular expression problems using the regular python 're' module.
-* Write a session in python traceback format `Example <http://github.com/andreasvc/pyre2/blob/master/tests/test_search.txt>`_.
-* Replace your ``import re`` with ``import re2 as re``.
-* Save it with as ``test_<name>.txt`` in the tests directory. You can comment on it however you like and indent the code with 4 spaces.
-
-
-Credits
-=======
-This code builds on the following projects (in chronological order):
-
-- Google's RE2 regular expression library: https://github.com/google/re2
-- Facebook's pyre2 github repository: http://github.com/facebook/pyre2/
-- Mike Axiak's Cython version of this: http://github.com/axiak/pyre2/ (seems not actively maintained)
-- This fork adds Python 3 support and other improvements.
-
diff --git a/contrib/python/pyre2/py2/tests/test_charliterals.txt b/contrib/python/pyre2/py2/tests/test_charliterals.txt
deleted file mode 100644
index 2eaea128a3..0000000000
--- a/contrib/python/pyre2/py2/tests/test_charliterals.txt
+++ /dev/null
@@ -1,47 +0,0 @@
- >>> import re2 as re
- >>> import warnings
- >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
-
-character literals:
-
- >>> i = 126
- >>> re.compile(r"\%03o" % i)
- re2.compile('\\176')
- >>> re.compile(r"\%03o" % i)._dump_pattern()
- '\\176'
- >>> re.match(r"\%03o" % i, chr(i)) is None
- False
- >>> re.match(r"\%03o0" % i, chr(i) + "0") is None
- False
- >>> re.match(r"\%03o8" % i, chr(i) + "8") is None
- False
- >>> re.match(r"\x%02x" % i, chr(i)) is None
- False
- >>> re.match(r"\x%02x0" % i, chr(i) + "0") is None
- False
- >>> re.match(r"\x%02xz" % i, chr(i) + "z") is None
- False
- >>> re.match("\911", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
- Traceback (most recent call last):
- ...
- re.error: invalid escape sequence: \9
-
-character class literals:
-
- >>> re.match(r"[\%03o]" % i, chr(i)) is None
- False
- >>> re.match(r"[\%03o0]" % i, chr(i) + "0") is None
- False
- >>> re.match(r"[\%03o8]" % i, chr(i) + "8") is None
- False
- >>> re.match(r"[\x%02x]" % i, chr(i)) is None
- False
- >>> re.match(r"[\x%02x0]" % i, chr(i) + "0") is None
- False
- >>> re.match(r"[\x%02xz]" % i, chr(i) + "z") is None
- False
- >>> re.match("[\911]", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
- Traceback (most recent call last):
- ...
- re.error: invalid escape sequence: \9
-
diff --git a/contrib/python/pyre2/py2/tests/test_count.txt b/contrib/python/pyre2/py2/tests/test_count.txt
deleted file mode 100644
index ce3525adc5..0000000000
--- a/contrib/python/pyre2/py2/tests/test_count.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-count tests
-===========
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
-
- >>> re2.count(r"\w+ly", "He was carefully disguised but captured quickly by police.")
- 2
-
-Groups should not affect count():
-
- >>> re2.count(r"(\w+)=(\d+)", "foo=1,foo=2")
- 2
- >>> re2.count(r"(\w)\w", "fx")
- 1
-
-Zero matches:
-
- >>> re2.count("(f)", "gggg")
- 0
-
-A pattern matching an empty string:
-
- >>> re2.count(".*", "foo")
- 2
-
- >>> re2.count("", "foo")
- 4
-
-contains tests
-==============
-
- >>> re2.contains('a', 'bbabb')
- True
- >>> re2.contains('a', 'bbbbb')
- False
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_emptygroups.txt b/contrib/python/pyre2/py2/tests/test_emptygroups.txt
deleted file mode 100644
index 424c8ba25e..0000000000
--- a/contrib/python/pyre2/py2/tests/test_emptygroups.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Empty/unused groups
-===================
-
- >>> import re
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-Unused vs. empty group:
-
- >>> re.search( '(foo)?((.*).)(bar)?', 'a').groups()
- (None, 'a', '', None)
- >>> re2.search('(foo)?((.*).)(bar)?', 'a').groups()
- (None, 'a', '', None)
-
- >>> re.search(r'((.*)?.)', 'a').groups()
- ('a', '')
- >>> re2.search(r'((.*)?.)', 'a').groups()
- ('a', '')
- >>> re.search(r'((.*)+.)', 'a').groups()
- ('a', '')
- >>> re2.search(r'((.*)+.)', 'a').groups()
- ('a', '')
-
-The following show different behavior for re and re2:
-
- >>> re.search(r'((.*)*.)', 'a').groups()
- ('a', '')
- >>> re2.search(r'((.*)*.)', 'a').groups()
- ('a', None)
-
- >>> re.search(r'((.*)*.)', 'Hello').groups()
- ('Hello', '')
- >>> re2.search(r'((.*)*.)', 'Hello').groups()
- ('Hello', 'Hell')
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_findall.txt b/contrib/python/pyre2/py2/tests/test_findall.txt
deleted file mode 100644
index c753b936df..0000000000
--- a/contrib/python/pyre2/py2/tests/test_findall.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-findall tests
-=============
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
-
- >>> re2.findall(r"\w+ly", "He was carefully disguised but captured quickly by police.")
- ['carefully', 'quickly']
-
-This one makes sure all groups are found:
-
- >>> re2.findall(r"(\w+)=(\d+)", "foo=1,foo=2")
- [('foo', '1'), ('foo', '2')]
-
-When there's only one matched group, it should not be returned in a tuple:
-
- >>> re2.findall(r"(\w)\w", "fx")
- ['f']
-
-Zero matches is an empty list:
-
- >>> re2.findall("(f)", "gggg")
- []
-
-If pattern matches an empty string, do it only once at the end:
-
- >>> re2.findall(".*", "foo")
- ['foo', '']
-
- >>> re2.findall("", "foo")
- ['', '', '', '']
-
-
- >>> import re
- >>> re.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
- ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
- >>> re2.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
- ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_finditer.txt b/contrib/python/pyre2/py2/tests/test_finditer.txt
deleted file mode 100644
index 3d60d199c7..0000000000
--- a/contrib/python/pyre2/py2/tests/test_finditer.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-Simple tests for the ``finditer`` function.
-===========================================
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
- >>> with open('tests/cnn_homepage.dat') as tmp:
- ... data = tmp.read()
- >>> len(list(re2.finditer(r'\w+', data)))
- 14230
-
- >>> [m.group(1) for m in re2.finditer(r'\n#hdr-editions(.*?)\n', data)]
- [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
-
- >>> [m.group(1) for m in re2.finditer(r'^#hdr-editions(.*?)$',
- ... data, re2.M)]
- [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
-
- >>> for a in re2.finditer(r'\b', 'foo bar zed'): print(a)
- <re2.Match object; span=(0, 0), match=''>
- <re2.Match object; span=(3, 3), match=''>
- <re2.Match object; span=(4, 4), match=''>
- <re2.Match object; span=(7, 7), match=''>
- <re2.Match object; span=(8, 8), match=''>
- <re2.Match object; span=(11, 11), match=''>
-
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_match_expand.txt b/contrib/python/pyre2/py2/tests/test_match_expand.txt
deleted file mode 100644
index b3d5652c76..0000000000
--- a/contrib/python/pyre2/py2/tests/test_match_expand.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Match Expand Tests
-==================
-
-Match objects have an .expand() method which allows them to
-expand templates as if the .sub() method was called on the pattern.
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> m = re2.match("(\\w+) (\\w+)\\W+(?P<title>\\w+)", "Isaac Newton, physicist")
- >>> m.expand("\\2, \\1")
- 'Newton, Isaac'
- >>> m.expand("\\1 \\g<title>")
- 'Isaac physicist'
- >>> m.expand("\\2, \\1 \\2")
- 'Newton, Isaac Newton'
- >>> m.expand("\\3")
- 'physicist'
- >>> m.expand("\\1 \\g<foo>") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
- Traceback (most recent call last):
- ...
- IndexError: no such group 'foo'; available groups: ['title']
- >>> m.expand("\\0")
- '\x00'
- >>> m.expand("\01")
- '\x01'
- >>> m.expand('\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D')
- '\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D'
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_mmap.txt b/contrib/python/pyre2/py2/tests/test_mmap.txt
deleted file mode 100644
index 12ffa97498..0000000000
--- a/contrib/python/pyre2/py2/tests/test_mmap.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-
-Testing re2 on buffer object
-============================
-
- >>> import re2
- >>> import mmap
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
- >>> tmp = open("tests/cnn_homepage.dat", "rb+")
- >>> data = mmap.mmap(tmp.fileno(), 0)
-
- >>> len(list(re2.finditer(b'\\w+', data)))
- 14230
-
- >>> data.close()
- >>> tmp.close()
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_namedgroups.txt b/contrib/python/pyre2/py2/tests/test_namedgroups.txt
deleted file mode 100644
index 70f561a39f..0000000000
--- a/contrib/python/pyre2/py2/tests/test_namedgroups.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-Testing some aspects of named groups
-=================================================
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
- >>> m = re2.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
- >>> m.start("first_name")
- 0
- >>> m.start("last_name")
- 8
-
- >>> m.span("last_name")
- (8, 16)
- >>> m.regs
- ((0, 16), (0, 7), (8, 16))
-
- >>> m = re2.match(u"(?P<first_name>\\w+) (?P<last_name>\\w+)", u"Malcolm Reynolds")
- >>> m.start(u"first_name")
- 0
- >>> m.start(u"last_name")
- 8
-
- >>> m.span(u"last_name")
- (8, 16)
- >>> m.regs
- ((0, 16), (0, 7), (8, 16))
-
-Compare patterns with and without unicode
-
- >>> pattern = re2.compile(br"(?P<first_name>\w+) (?P<last_name>\w+)")
- >>> print(pattern._dump_pattern().decode('utf8'))
- (?P<first_name>\w+) (?P<last_name>\w+)
- >>> pattern = re2.compile(u"(?P<first_name>\\w+) (?P<last_name>\\w+)",
- ... re2.UNICODE)
- >>> print(pattern._dump_pattern())
- (?P<first_name>[_\p{L}\p{Nd}]+) (?P<last_name>[_\p{L}\p{Nd}]+)
-
-Make sure positions are converted properly for unicode
-
- >>> m = pattern.match(
- ... u'\u05d9\u05e9\u05e8\u05d0\u05dc \u05e6\u05d3\u05d5\u05e7')
- >>> m.start(u"first_name")
- 0
- >>> m.start(u"last_name")
- 6
- >>> m.end(u"last_name")
- 10
- >>> m.regs
- ((0, 10), (0, 5), (6, 10))
- >>> m.span(2)
- (6, 10)
- >>> m.span(u"last_name")
- (6, 10)
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_pattern.txt b/contrib/python/pyre2/py2/tests/test_pattern.txt
deleted file mode 100644
index aab47359a2..0000000000
--- a/contrib/python/pyre2/py2/tests/test_pattern.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-pattern tests
-=============
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-We should be able to get back what we put in.
-
- >>> re2.compile("(foo|b[a]r?)").pattern
- '(foo|b[a]r?)'
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_search.txt b/contrib/python/pyre2/py2/tests/test_search.txt
deleted file mode 100644
index 9c1e18f08c..0000000000
--- a/contrib/python/pyre2/py2/tests/test_search.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-These are simple tests of the ``search`` function
-=================================================
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> import warnings
- >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
-
- >>> re2.search("((?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])", "hello 28.224.2.1 test").group()
- '28.224.2.1'
-
- >>> re2.search("(\d{3})\D?(\d{3})\D?(\d{4})", "800-555-1212").groups()
- ('800', '555', '1212')
-
- >>> input = 'a' * 999
- >>> len(re2.search('(?:a{1000})?a{999}', input).group())
- 999
-
- >>> with open('tests/cnn_homepage.dat') as tmp:
- ... data = tmp.read()
- >>> re2.search(r'\n#hdr-editions(.*?)\n', data).groups()
- (' a { text-decoration:none; }',)
-
-Verify some sanity checks
-
- >>> re2.compile(r'x').search('x', 2000)
- >>> re2.compile(r'x').search('x', 1, -300)
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_split.txt b/contrib/python/pyre2/py2/tests/test_split.txt
deleted file mode 100644
index a3e44bc605..0000000000
--- a/contrib/python/pyre2/py2/tests/test_split.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Split tests
-===========
-
-This one tests to make sure that unicode / utf8 data is parsed correctly.
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> a = u'\u6211\u5f88\u597d, \u4f60\u5462?'
-
- >>> re2.split(u' ', a) == [u'\u6211\u5f88\u597d,', u'\u4f60\u5462?']
- True
- >>> re2.split(b' ', a.encode('utf8')) == [
- ... b'\xe6\x88\x91\xe5\xbe\x88\xe5\xa5\xbd,',
- ... b'\xe4\xbd\xa0\xe5\x91\xa2?']
- True
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_sub.txt b/contrib/python/pyre2/py2/tests/test_sub.txt
deleted file mode 100644
index b41dd30d28..0000000000
--- a/contrib/python/pyre2/py2/tests/test_sub.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Tests of substitution
-=====================
-
-This first test is just looking to replace things between parentheses
-with an empty string.
-
-
- >>> import hashlib
- >>> import gzip
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> import warnings
- >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
-
- >>> with gzip.open('tests/wikipages.xml.gz', 'rb') as tmp:
- ... data = tmp.read()
- >>> print(hashlib.md5(re2.sub(b'\(.*?\)', b'', data)).hexdigest())
- b7a469f55ab76cd5887c81dbb0cfe6d3
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
-
-Issue #26 re2.sub replacements with a match of "(.*)" hangs forever
-
- >>> re2.sub('(.*)', r'\1;replacement', 'original')
- 'original;replacement;replacement'
-
- >>> re2.sub('(.*)', lambda x: x.group() + ';replacement', 'original')
- 'original;replacement;replacement'
-
- >>> re2.subn("b*", lambda x: "X", "xyz", 4)
- ('XxXyXzX', 4)
diff --git a/contrib/python/pyre2/py2/tests/test_unicode.txt b/contrib/python/pyre2/py2/tests/test_unicode.txt
deleted file mode 100644
index 71d497b80d..0000000000
--- a/contrib/python/pyre2/py2/tests/test_unicode.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-Here are some tests to make sure that utf-8 works
-=================================================
-
- >>> import sys
- >>> import re2 as re
- >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
- >>> a = u'\u6211\u5f88\u597d'
- >>> c = re.compile(a[0])
- >>> c.search(a).group() == u'\u6211'
- True
-
-Test unicode stickyness
-
- >>> re.sub(u'x', u'y', u'x') == u'y'
- True
- >>> re.sub(r'x', 'y', 'x') == 'y'
- True
- >>> re.findall('.', 'x') == ['x']
- True
- >>> re.findall(u'.', u'x') == [u'x']
- True
- >>> re.split(',', '1,2,3') == ['1', '2', '3']
- True
- >>> re.split(u',', u'1,2,3') == [u'1', u'2', u'3']
- True
- >>> re.search('(\\d)', '1').group(1) == '1'
- True
- >>> re.search(u'(\\d)', u'1').group(1) == u'1'
- True
-
-Test unicode character groups
-
- >>> re.search(u'\\d', u'\u0661', re.UNICODE).group(0) == u'\u0661'
- True
- >>> int(re.search(u'\\d', u'\u0661', re.UNICODE).group(0)) == 1
- True
- >>> (re.search(u'\\w', u'\u0401') is None) == (sys.version_info[0] == 2)
- True
- >>> re.search(u'\\w', u'\u0401', re.UNICODE).group(0) == u'\u0401'
- True
- >>> re.search(u'\\s', u'\u1680', re.UNICODE).group(0) == u'\u1680'
- True
- >>> re.findall(r'[\s\d\w]', 'hey 123', re.UNICODE) == ['h', 'e', 'y', ' ', '1', '2', '3']
- True
- >>> re.search(u'\\D', u'\u0661x', re.UNICODE).group(0) == u'x'
- True
- >>> re.search(u'\\W', u'\u0401!', re.UNICODE).group(0) == u'!'
- True
- >>> re.search(u'\\S', u'\u1680x', re.UNICODE).group(0) == u'x'
- True
- >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
- >>> re.search(u'[\\W]', u'\u0401!', re.UNICODE).group(0) == u'!'
- True
- >>> re.search(u'[\\S]', u'\u1680x', re.UNICODE).group(0) == u'x'
- True
- >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
-
-
-Positions are translated transparently between unicode and UTF-8
-
- >>> re.search(u' (.)', u'\U0001d200xxx\u1234 x').span(1)
- (6, 7)
- >>> re.search(b' (.)', u'\U0001d200xxx\u1234 x'.encode('utf-8')).span(1)
- (11, 12)
- >>> re.compile(u'x').findall(u'\u1234x', 1, 2) == [u'x']
- True
- >>> data = u'\U0001d200xxx\u1234 x'
- >>> re.search(u' (.)', data).string == data
- True
-
- >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/.dist-info/METADATA b/contrib/python/pyre2/py3/.dist-info/METADATA
deleted file mode 100644
index 6f4f966e33..0000000000
--- a/contrib/python/pyre2/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,275 +0,0 @@
-Metadata-Version: 2.1
-Name: pyre2
-Version: 0.3.6
-Summary: Python wrapper for Google\'s RE2 using Cython
-Home-page: https://github.com/andreasvc/pyre2
-Author: Andreas van Cranenburgh
-Author-email: andreas@unstable.nl
-Maintainer: Steve Arnold
-Maintainer-email: nerdboy@gentoo.org
-License: BSD
-Platform: UNKNOWN
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Programming Language :: Cython
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Intended Audience :: Developers
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.6
-Description-Content-Type: text/x-rst; charset=UTF-8
-Provides-Extra: perf
-Requires-Dist: regex ; extra == 'perf'
-Provides-Extra: test
-Requires-Dist: pytest ; extra == 'test'
-
-===============================================================
- pyre2: Python RE2 wrapper for linear-time regular expressions
-===============================================================
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Build/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Build
- :alt: Build CI Status
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Release/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Release
- :alt: Release CI Status
-
-.. image:: https://img.shields.io/github/v/tag/andreasvc/pyre2?color=green&include_prereleases&label=latest%20release
- :target: https://github.com/andreasvc/pyre2/releases
- :alt: GitHub tag (latest SemVer, including pre-release)
-
-.. image:: https://badge.fury.io/py/pyre2.svg
- :target: https://badge.fury.io/py/pyre2
- :alt: Pypi version
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Conda/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Conda
- :alt: Conda CI Status
-
-.. image:: https://img.shields.io/github/license/andreasvc/pyre2
- :target: https://github.com/andreasvc/pyre2/blob/master/LICENSE
- :alt: License
-
-.. image:: https://img.shields.io/badge/python-3.6+-blue.svg
- :target: https://www.python.org/downloads/
- :alt: Python version
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/version.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: version
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/platforms.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: platforms
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/downloads.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: downloads
-
-
-.. contents:: Table of Contents
- :depth: 2
- :backlinks: top
-
-
-Summary
-=======
-
-pyre2 is a Python extension that wraps
-`Google's RE2 regular expression library <https://github.com/google/re2>`_.
-The RE2 engine compiles (strictly) regular expressions to
-deterministic finite automata, which guarantees linear-time behavior.
-
-Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
-to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
-For best performance, work with UTF-8 encoded bytes strings.
-
-Installation
-============
-
-Normal usage for Linux/Mac/Windows::
-
- $ pip install pyre2
-
-Compiling from source
----------------------
-
-Requirements for building the C++ extension from the repo source:
-
-* A build environment with ``gcc`` or ``clang`` (e.g. ``sudo apt-get install build-essential``)
-* Build tools and libraries: RE2, pybind11, and cmake installed in the build
- environment.
-
- + On Ubuntu/Debian: ``sudo apt-get install build-essential cmake ninja-build python3-dev cython3 pybind11-dev libre2-dev``
- + On Gentoo, install dev-util/cmake, dev-python/pybind11, and dev-libs/re2
- + For a venv you can install the pybind11, cmake, and cython packages from PyPI
-
-On MacOS, use the ``brew`` package manager::
-
- $ brew install -s re2 pybind11
-
-On Windows use the ``vcpkg`` package manager::
-
- $ vcpkg install re2:x64-windows pybind11:x64-windows
-
-You can pass some cmake environment variables to alter the build type or
-pass a toolchain file (the latter is required on Windows) or specify the
-cmake generator. For example::
-
- $ CMAKE_GENERATOR="Unix Makefiles" CMAKE_TOOLCHAIN_FILE=clang_toolchain.cmake tox -e deploy
-
-For development, get the source::
-
- $ git clone git://github.com/andreasvc/pyre2.git
- $ cd pyre2
- $ make install
-
-
-Platform-agnostic building with conda
--------------------------------------
-
-An alternative to the above is provided via the `conda`_ recipe (use the
-`miniconda installer`_ if you don't have ``conda`` installed already).
-
-
-.. _conda: https://anaconda.org/conda-forge/pyre2
-.. _miniconda installer: https://docs.conda.io/en/latest/miniconda.html
-
-
-Backwards Compatibility
-=======================
-
-The stated goal of this module is to be a drop-in replacement for ``re``, i.e.::
-
- try:
- import re2 as re
- except ImportError:
- import re
-
-That being said, there are features of the ``re`` module that this module may
-never have; these will be handled through fallback to the original ``re`` module:
-
-* lookahead assertions ``(?!...)``
-* backreferences (``\\n`` in search pattern)
-* \W and \S not supported inside character classes
-
-On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
-Syntax reference: https://github.com/google/re2/wiki/Syntax
-
-However, there are times when you may want to be notified of a failover. The
-function ``set_fallback_notification`` determines the behavior in these cases::
-
- try:
- import re2 as re
- except ImportError:
- import re
- else:
- re.set_fallback_notification(re.FALLBACK_WARNING)
-
-``set_fallback_notification`` takes three values:
-``re.FALLBACK_QUIETLY`` (default), ``re.FALLBACK_WARNING`` (raise a warning),
-and ``re.FALLBACK_EXCEPTION`` (raise an exception).
-
-Documentation
-=============
-
-Consult the docstrings in the source code or interactively
-through ipython or ``pydoc re2`` etc.
-
-Unicode Support
-===============
-
-Python ``bytes`` and ``unicode`` strings are fully supported, but note that
-``RE2`` works with UTF-8 encoded strings under the hood, which means that
-``unicode`` strings need to be encoded and decoded back and forth.
-There are two important factors:
-
-* whether a ``unicode`` pattern and search string is used (will be encoded to UTF-8 internally)
-* the ``UNICODE`` flag: whether operators such as ``\w`` recognize Unicode characters.
-
-To avoid the overhead of encoding and decoding to UTF-8, it is possible to pass
-UTF-8 encoded bytes strings directly but still treat them as ``unicode``::
-
- In [18]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
- Out[18]: ['M', '\xc3\xb6', 't', 'l', 'e', 'y', 'C', 'r', '\xc3\xbc', 'e']
- In [19]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'))
- Out[19]: ['M', 't', 'l', 'e', 'y', 'C', 'r', 'e']
-
-However, note that the indices in ``Match`` objects will refer to the bytes string.
-The indices of the match in the ``unicode`` string could be computed by
-decoding/encoding, but this is done automatically and more efficiently if you
-pass the ``unicode`` string::
-
- >>> re2.search(u'ü'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
- <re2.Match object; span=(10, 12), match='\xc3\xbc'>
- >>> re2.search(u'ü', u'Mötley Crüe', flags=re2.UNICODE)
- <re2.Match object; span=(9, 10), match=u'\xfc'>
-
-Finally, if you want to match bytes without regard for Unicode characters,
-pass bytes strings and leave out the ``UNICODE`` flag (this will cause Latin 1
-encoding to be used with ``RE2`` under the hood)::
-
- >>> re2.findall(br'.', b'\x80\x81\x82')
- ['\x80', '\x81', '\x82']
-
-Performance
-===========
-
-Performance is of course the point of this module, so it better perform well.
-Regular expressions vary widely in complexity, and the salient feature of ``RE2`` is
-that it behaves well asymptotically. This being said, for very simple substitutions,
-I've found that occasionally python's regular ``re`` module is actually slightly faster.
-However, when the ``re`` module gets slow, it gets *really* slow, while this module
-buzzes along.
-
-In the below example, I'm running the data against 8MB of text from the colossal Wikipedia
-XML file. I'm running them multiple times, being careful to use the ``timeit`` module.
-To see more details, please see the `performance script <http://github.com/andreasvc/pyre2/tree/master/tests/performance.py>`_.
-
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Test |Description |# total runs|``re`` time(s)|``re2`` time(s)|% ``re`` time|``regex`` time(s)|% ``regex`` time|
-+=================+===========================================================================+============+==============+===============+=============+=================+================+
-|Findall URI|Email|Find list of '([a-zA-Z][a-zA-Z0-9]*)://([^ /]+)(/[^ ]*)?|([^ @]+)@([^ @]+)'|2 |6.262 |0.131 |2.08% |5.119 |2.55% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Replace WikiLinks|This test replaces links of the form [[Obama|Barack_Obama]] to Obama. |100 |4.374 |0.815 |18.63% |1.176 |69.33% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Remove WikiLinks |This test splits the data by the <page> tag. |100 |4.153 |0.225 |5.43% |0.537 |42.01% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-
-Feel free to add more speed tests to the bottom of the script and send a pull request my way!
-
-Current Status
-==============
-
-The tests show the following differences with Python's ``re`` module:
-
-* The ``$`` operator in Python's ``re`` matches twice if the string ends
- with ``\n``. This can be simulated using ``\n?$``, except when doing
- substitutions.
-* The ``pyre2`` module and Python's ``re`` may behave differently with nested groups.
- See ``tests/test_emptygroups.txt`` for the examples.
-
-Please report any further issues with ``pyre2``.
-
-Tests
-=====
-
-If you would like to help, one thing that would be very useful
-is writing comprehensive tests for this. It's actually really easy:
-
-* Come up with regular expression problems using the regular python 're' module.
-* Write a session in python traceback format `Example <http://github.com/andreasvc/pyre2/blob/master/tests/test_search.txt>`_.
-* Replace your ``import re`` with ``import re2 as re``.
-* Save it with as ``test_<name>.txt`` in the tests directory. You can comment on it however you like and indent the code with 4 spaces.
-
-
-Credits
-=======
-This code builds on the following projects (in chronological order):
-
-- Google's RE2 regular expression library: https://github.com/google/re2
-- Facebook's pyre2 github repository: http://github.com/facebook/pyre2/
-- Mike Axiak's Cython version of this: http://github.com/axiak/pyre2/ (seems not actively maintained)
-- This fork adds Python 3 support and other improvements.
-
-
-
diff --git a/contrib/python/pyre2/py3/.dist-info/top_level.txt b/contrib/python/pyre2/py3/.dist-info/top_level.txt
deleted file mode 100644
index 94e9d8fad0..0000000000
--- a/contrib/python/pyre2/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-re2
diff --git a/contrib/python/pyre2/py3/AUTHORS b/contrib/python/pyre2/py3/AUTHORS
deleted file mode 100644
index 0f1a37f2b4..0000000000
--- a/contrib/python/pyre2/py3/AUTHORS
+++ /dev/null
@@ -1,12 +0,0 @@
-All contributors own the copyright to their own contributions, but agree
-to release each of their contributions under the BSD license included
-in this software.
-
-Michael Axiak <mike@axiak.net>
-
-Contributors
-============
-
-Alec Berryman <alec@thened.net>
-Israel Tsadok <itsadok@gmail.com>
-Alex Willmer <alex@moreati.org.uk>
diff --git a/contrib/python/pyre2/py3/LICENSE b/contrib/python/pyre2/py3/LICENSE
deleted file mode 100644
index 803fbbcd9f..0000000000
--- a/contrib/python/pyre2/py3/LICENSE
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 2010, Michael Axiak <mike@axiak.net>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/python/pyre2/py3/README.rst b/contrib/python/pyre2/py3/README.rst
deleted file mode 100644
index 3f46ff6eaf..0000000000
--- a/contrib/python/pyre2/py3/README.rst
+++ /dev/null
@@ -1,250 +0,0 @@
-===============================================================
- pyre2: Python RE2 wrapper for linear-time regular expressions
-===============================================================
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Build/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Build
- :alt: Build CI Status
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Release/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Release
- :alt: Release CI Status
-
-.. image:: https://img.shields.io/github/v/tag/andreasvc/pyre2?color=green&include_prereleases&label=latest%20release
- :target: https://github.com/andreasvc/pyre2/releases
- :alt: GitHub tag (latest SemVer, including pre-release)
-
-.. image:: https://badge.fury.io/py/pyre2.svg
- :target: https://badge.fury.io/py/pyre2
- :alt: Pypi version
-
-.. image:: https://github.com/andreasvc/pyre2/workflows/Conda/badge.svg
- :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Conda
- :alt: Conda CI Status
-
-.. image:: https://img.shields.io/github/license/andreasvc/pyre2
- :target: https://github.com/andreasvc/pyre2/blob/master/LICENSE
- :alt: License
-
-.. image:: https://img.shields.io/badge/python-3.6+-blue.svg
- :target: https://www.python.org/downloads/
- :alt: Python version
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/version.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: version
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/platforms.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: platforms
-
-.. image:: https://anaconda.org/conda-forge/pyre2/badges/downloads.svg
- :target: https://anaconda.org/conda-forge/pyre2
- :alt: downloads
-
-
-.. contents:: Table of Contents
- :depth: 2
- :backlinks: top
-
-
-Summary
-=======
-
-pyre2 is a Python extension that wraps
-`Google's RE2 regular expression library <https://github.com/google/re2>`_.
-The RE2 engine compiles (strictly) regular expressions to
-deterministic finite automata, which guarantees linear-time behavior.
-
-Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
-to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
-For best performance, work with UTF-8 encoded bytes strings.
-
-Installation
-============
-
-Normal usage for Linux/Mac/Windows::
-
- $ pip install pyre2
-
-Compiling from source
----------------------
-
-Requirements for building the C++ extension from the repo source:
-
-* A build environment with ``gcc`` or ``clang`` (e.g. ``sudo apt-get install build-essential``)
-* Build tools and libraries: RE2, pybind11, and cmake installed in the build
- environment.
-
- + On Ubuntu/Debian: ``sudo apt-get install build-essential cmake ninja-build python3-dev cython3 pybind11-dev libre2-dev``
- + On Gentoo, install dev-util/cmake, dev-python/pybind11, and dev-libs/re2
- + For a venv you can install the pybind11, cmake, and cython packages from PyPI
-
-On MacOS, use the ``brew`` package manager::
-
- $ brew install -s re2 pybind11
-
-On Windows use the ``vcpkg`` package manager::
-
- $ vcpkg install re2:x64-windows pybind11:x64-windows
-
-You can pass some cmake environment variables to alter the build type or
-pass a toolchain file (the latter is required on Windows) or specify the
-cmake generator. For example::
-
- $ CMAKE_GENERATOR="Unix Makefiles" CMAKE_TOOLCHAIN_FILE=clang_toolchain.cmake tox -e deploy
-
-For development, get the source::
-
- $ git clone git://github.com/andreasvc/pyre2.git
- $ cd pyre2
- $ make install
-
-
-Platform-agnostic building with conda
--------------------------------------
-
-An alternative to the above is provided via the `conda`_ recipe (use the
-`miniconda installer`_ if you don't have ``conda`` installed already).
-
-
-.. _conda: https://anaconda.org/conda-forge/pyre2
-.. _miniconda installer: https://docs.conda.io/en/latest/miniconda.html
-
-
-Backwards Compatibility
-=======================
-
-The stated goal of this module is to be a drop-in replacement for ``re``, i.e.::
-
- try:
- import re2 as re
- except ImportError:
- import re
-
-That being said, there are features of the ``re`` module that this module may
-never have; these will be handled through fallback to the original ``re`` module:
-
-* lookahead assertions ``(?!...)``
-* backreferences (``\\n`` in search pattern)
-* \W and \S not supported inside character classes
-
-On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
-Syntax reference: https://github.com/google/re2/wiki/Syntax
-
-However, there are times when you may want to be notified of a failover. The
-function ``set_fallback_notification`` determines the behavior in these cases::
-
- try:
- import re2 as re
- except ImportError:
- import re
- else:
- re.set_fallback_notification(re.FALLBACK_WARNING)
-
-``set_fallback_notification`` takes three values:
-``re.FALLBACK_QUIETLY`` (default), ``re.FALLBACK_WARNING`` (raise a warning),
-and ``re.FALLBACK_EXCEPTION`` (raise an exception).
-
-Documentation
-=============
-
-Consult the docstrings in the source code or interactively
-through ipython or ``pydoc re2`` etc.
-
-Unicode Support
-===============
-
-Python ``bytes`` and ``unicode`` strings are fully supported, but note that
-``RE2`` works with UTF-8 encoded strings under the hood, which means that
-``unicode`` strings need to be encoded and decoded back and forth.
-There are two important factors:
-
-* whether a ``unicode`` pattern and search string is used (will be encoded to UTF-8 internally)
-* the ``UNICODE`` flag: whether operators such as ``\w`` recognize Unicode characters.
-
-To avoid the overhead of encoding and decoding to UTF-8, it is possible to pass
-UTF-8 encoded bytes strings directly but still treat them as ``unicode``::
-
- In [18]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
- Out[18]: ['M', '\xc3\xb6', 't', 'l', 'e', 'y', 'C', 'r', '\xc3\xbc', 'e']
- In [19]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'))
- Out[19]: ['M', 't', 'l', 'e', 'y', 'C', 'r', 'e']
-
-However, note that the indices in ``Match`` objects will refer to the bytes string.
-The indices of the match in the ``unicode`` string could be computed by
-decoding/encoding, but this is done automatically and more efficiently if you
-pass the ``unicode`` string::
-
- >>> re2.search(u'ü'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
- <re2.Match object; span=(10, 12), match='\xc3\xbc'>
- >>> re2.search(u'ü', u'Mötley Crüe', flags=re2.UNICODE)
- <re2.Match object; span=(9, 10), match=u'\xfc'>
-
-Finally, if you want to match bytes without regard for Unicode characters,
-pass bytes strings and leave out the ``UNICODE`` flag (this will cause Latin 1
-encoding to be used with ``RE2`` under the hood)::
-
- >>> re2.findall(br'.', b'\x80\x81\x82')
- ['\x80', '\x81', '\x82']
-
-Performance
-===========
-
-Performance is of course the point of this module, so it better perform well.
-Regular expressions vary widely in complexity, and the salient feature of ``RE2`` is
-that it behaves well asymptotically. This being said, for very simple substitutions,
-I've found that occasionally python's regular ``re`` module is actually slightly faster.
-However, when the ``re`` module gets slow, it gets *really* slow, while this module
-buzzes along.
-
-In the below example, I'm running the data against 8MB of text from the colossal Wikipedia
-XML file. I'm running them multiple times, being careful to use the ``timeit`` module.
-To see more details, please see the `performance script <http://github.com/andreasvc/pyre2/tree/master/tests/performance.py>`_.
-
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Test |Description |# total runs|``re`` time(s)|``re2`` time(s)|% ``re`` time|``regex`` time(s)|% ``regex`` time|
-+=================+===========================================================================+============+==============+===============+=============+=================+================+
-|Findall URI|Email|Find list of '([a-zA-Z][a-zA-Z0-9]*)://([^ /]+)(/[^ ]*)?|([^ @]+)@([^ @]+)'|2 |6.262 |0.131 |2.08% |5.119 |2.55% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Replace WikiLinks|This test replaces links of the form [[Obama|Barack_Obama]] to Obama. |100 |4.374 |0.815 |18.63% |1.176 |69.33% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-|Remove WikiLinks |This test splits the data by the <page> tag. |100 |4.153 |0.225 |5.43% |0.537 |42.01% |
-+-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
-
-Feel free to add more speed tests to the bottom of the script and send a pull request my way!
-
-Current Status
-==============
-
-The tests show the following differences with Python's ``re`` module:
-
-* The ``$`` operator in Python's ``re`` matches twice if the string ends
- with ``\n``. This can be simulated using ``\n?$``, except when doing
- substitutions.
-* The ``pyre2`` module and Python's ``re`` may behave differently with nested groups.
- See ``tests/test_emptygroups.txt`` for the examples.
-
-Please report any further issues with ``pyre2``.
-
-Tests
-=====
-
-If you would like to help, one thing that would be very useful
-is writing comprehensive tests for this. It's actually really easy:
-
-* Come up with regular expression problems using the regular python 're' module.
-* Write a session in python traceback format `Example <http://github.com/andreasvc/pyre2/blob/master/tests/test_search.txt>`_.
-* Replace your ``import re`` with ``import re2 as re``.
-* Save it with as ``test_<name>.txt`` in the tests directory. You can comment on it however you like and indent the code with 4 spaces.
-
-
-Credits
-=======
-This code builds on the following projects (in chronological order):
-
-- Google's RE2 regular expression library: https://github.com/google/re2
-- Facebook's pyre2 github repository: http://github.com/facebook/pyre2/
-- Mike Axiak's Cython version of this: http://github.com/axiak/pyre2/ (seems not actively maintained)
-- This fork adds Python 3 support and other improvements.
-
diff --git a/contrib/python/pyre2/py3/src/_re2macros.h b/contrib/python/pyre2/py3/src/_re2macros.h
deleted file mode 100644
index b9ac82af6b..0000000000
--- a/contrib/python/pyre2/py3/src/_re2macros.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __RE2MACROS_H
-#define __RE2MACROS_H
-
-#include <stdio.h>
-#include "re2/stringpiece.h"
-
-static inline re2::StringPiece * new_StringPiece_array(int n)
-{
- re2::StringPiece * sp = new re2::StringPiece[n];
- return sp;
-}
-
-#endif
diff --git a/contrib/python/pyre2/py3/src/compile.pxi b/contrib/python/pyre2/py3/src/compile.pxi
deleted file mode 100644
index 887a2778cd..0000000000
--- a/contrib/python/pyre2/py3/src/compile.pxi
+++ /dev/null
@@ -1,234 +0,0 @@
-
-def compile(pattern, int flags=0, int max_mem=8388608):
- cachekey = (type(pattern), pattern, flags)
- if cachekey in _cache:
- return _cache[cachekey]
- p = _compile(pattern, flags, max_mem)
-
- if len(_cache) >= _MAXCACHE:
- _cache.popitem()
- _cache[cachekey] = p
- return p
-
-
-def _compile(object pattern, int flags=0, int max_mem=8388608):
- """Compile a regular expression pattern, returning a pattern object."""
- def fallback(pattern, flags, error_msg):
- """Raise error, warn, or simply return fallback from re module."""
- if current_notification == FALLBACK_EXCEPTION:
- raise RegexError(error_msg)
- elif current_notification == FALLBACK_WARNING:
- warnings.warn("WARNING: Using re module. Reason: %s" % error_msg)
- try:
- result = PythonRePattern(pattern, flags)
- except re.error as err:
- raise RegexError(*err.args)
- return result
-
- cdef StringPiece * s
- cdef Options opts
- cdef int error_code
- cdef int encoded = 0
- cdef object original_pattern
-
- if isinstance(pattern, (Pattern, SREPattern)):
- if flags:
- raise ValueError(
- 'Cannot process flags argument with a compiled pattern')
- return pattern
-
- original_pattern = pattern
- if flags & _L:
- return fallback(original_pattern, flags, "re.LOCALE not supported")
- pattern = unicode_to_bytes(pattern, &encoded, -1)
- newflags = flags
- if not PY2:
- if not encoded and flags & _U: # re.UNICODE
- pass # can use UNICODE with bytes pattern, but assumes valid UTF-8
- # raise ValueError("can't use UNICODE flag with a bytes pattern")
- elif encoded and not (flags & ASCII): # re.ASCII (not in Python 2)
- newflags = flags | _U # re.UNICODE
- elif encoded and flags & ASCII:
- newflags = flags & ~_U # re.UNICODE
- try:
- pattern = _prepare_pattern(pattern, newflags)
- except BackreferencesException:
- return fallback(original_pattern, flags, "Backreferences not supported")
- except CharClassProblemException:
- return fallback(original_pattern, flags,
- "\W and \S not supported inside character classes")
-
- # Set the options given the flags above.
- if flags & _I:
- opts.set_case_sensitive(0);
-
- opts.set_max_mem(max_mem)
- opts.set_log_errors(0)
- if flags & _U or encoded:
- opts.set_encoding(EncodingUTF8)
- else: # re.UNICODE flag not passed, and pattern is bytes,
- # so allow matching of arbitrary byte sequences.
- opts.set_encoding(EncodingLatin1)
-
- s = new StringPiece(<char *><bytes>pattern, len(pattern))
-
- cdef RE2 *re_pattern
- with nogil:
- re_pattern = new RE2(s[0], opts)
-
- if not re_pattern.ok():
- # Something went wrong with the compilation.
- del s
- error_msg = cpp_to_unicode(re_pattern.error())
- error_code = re_pattern.error_code()
- del re_pattern
- if current_notification == FALLBACK_EXCEPTION:
- # Raise an exception regardless of the type of error.
- raise RegexError(error_msg)
- elif error_code not in (ErrorBadPerlOp, ErrorRepeatSize,
- # ErrorBadEscape,
- ErrorPatternTooLarge):
- # Raise an error because these will not be fixed by using the
- # ``re`` module.
- raise RegexError(error_msg)
- elif current_notification == FALLBACK_WARNING:
- warnings.warn("WARNING: Using re module. Reason: %s" % error_msg)
- return PythonRePattern(original_pattern, flags)
-
- cdef Pattern pypattern = Pattern()
- cdef map[cpp_string, int] named_groups = re_pattern.NamedCapturingGroups()
- pypattern.pattern = original_pattern
- pypattern.re_pattern = re_pattern
- pypattern.groups = re_pattern.NumberOfCapturingGroups()
- pypattern.encoded = encoded
- pypattern.flags = flags
- pypattern.groupindex = {}
- for it in named_groups:
- pypattern.groupindex[cpp_to_unicode(it.first)] = it.second
-
- if flags & DEBUG:
- print(repr(pypattern._dump_pattern()))
- del s
- return pypattern
-
-
-def _prepare_pattern(bytes pattern, int flags):
- """Translate pattern to RE2 syntax."""
- cdef bytearray result = bytearray()
- cdef unsigned char * cstring = pattern
- cdef unsigned char this, that
- cdef int size = len(pattern)
- cdef int n = 0
-
- if flags & (_S | _M):
- result.extend(b'(?')
- if flags & _S:
- result.extend(b's')
- if flags & _M:
- result.extend(b'm')
- result.extend(b')')
- while n < size:
- this = cstring[n]
- if flags & _X:
- if this in b' \t\n\r\f\v':
- n += 1
- continue
- elif this == b'#':
- while True:
- n += 1
- if n >= size:
- break
- this = cstring[n]
- if this == b'\n':
- break
- n += 1
- continue
-
- if this != b'[' and this != b'\\':
- result.append(this)
- n += 1
- continue
- elif this == b'[':
- result.append(this)
- while True:
- n += 1
- if n >= size:
- raise RegexError("unexpected end of regular expression")
- this = cstring[n]
- if this == b']':
- result.append(this)
- break
- elif this == b'\\':
- n += 1
- that = cstring[n]
- if that == b'b':
- result.extend(br'\010')
- elif flags & _U:
- if that == b'd':
- result.extend(br'\p{Nd}')
- elif that == b'w':
- result.extend(br'_\p{L}\p{Nd}')
- elif that == b's':
- result.extend(br'\s\p{Z}')
- elif that == b'D':
- result.extend(br'\P{Nd}')
- elif that == b'W':
- # Since \w and \s are made out of several character
- # groups, I don't see a way to convert their
- # complements into a group without rewriting the
- # whole expression, which seems too complicated.
- raise CharClassProblemException()
- elif that == b'S':
- raise CharClassProblemException()
- else:
- result.append(this)
- result.append(that)
- else:
- result.append(this)
- result.append(that)
- else:
- result.append(this)
- elif this == b'\\':
- n += 1
- that = cstring[n]
- if b'8' <= that <= b'9':
- raise BackreferencesException()
- elif isoct(that):
- if (n + 2 < size and isoct(cstring[n + 1])
- and isoct(cstring[n + 2])):
- # all clear, this is an octal escape
- result.extend(cstring[n - 1:n + 3])
- n += 2
- else:
- raise BackreferencesException()
- elif that == b'x':
- if (n + 2 < size and ishex(cstring[n + 1])
- and ishex(cstring[n + 2])):
- # hex escape
- result.extend(cstring[n - 1:n + 3])
- n += 2
- else:
- raise BackreferencesException()
- elif that == b'Z':
- result.extend(b'\\z')
- elif flags & _U:
- if that == b'd':
- result.extend(br'\p{Nd}')
- elif that == b'w':
- result.extend(br'[_\p{L}\p{Nd}]')
- elif that == b's':
- result.extend(br'[\s\p{Z}]')
- elif that == b'D':
- result.extend(br'[^\p{Nd}]')
- elif that == b'W':
- result.extend(br'[^_\p{L}\p{Nd}]')
- elif that == b'S':
- result.extend(br'[^\s\p{Z}]')
- else:
- result.append(this)
- result.append(that)
- else:
- result.append(this)
- result.append(that)
- n += 1
- return bytes(result)
diff --git a/contrib/python/pyre2/py3/src/includes.pxi b/contrib/python/pyre2/py3/src/includes.pxi
deleted file mode 100644
index 8c35b6d4b2..0000000000
--- a/contrib/python/pyre2/py3/src/includes.pxi
+++ /dev/null
@@ -1,109 +0,0 @@
-cimport cpython.unicode
-from libcpp.map cimport map
-from libcpp.string cimport string as cpp_string
-from cython.operator cimport postincrement, dereference
-from cpython.buffer cimport Py_buffer, PyBUF_SIMPLE, PyObject_CheckBuffer, \
- PyObject_GetBuffer, PyBuffer_Release
-from cpython.version cimport PY_MAJOR_VERSION
-
-
-cdef extern from *:
- cdef void emit_if_narrow_unicode "#if !defined(Py_UNICODE_WIDE) && PY_VERSION_HEX < 0x03030000 //" ()
- cdef void emit_endif "#endif //" ()
-
-
-cdef extern from "Python.h":
- int PyObject_CheckReadBuffer(object)
- int PyObject_AsReadBuffer(object, const void **, Py_ssize_t *)
-
-
-cdef extern from "re2/stringpiece.h" namespace "re2":
- cdef cppclass StringPiece:
- StringPiece()
- StringPiece(const char *)
- StringPiece(const char *, int)
- const char * data()
- int copy(char * buf, size_t n, size_t pos)
- int length()
-
-
-cdef extern from "re2/re2.h" namespace "re2":
- cdef enum Anchor:
- UNANCHORED "RE2::UNANCHORED"
- ANCHOR_START "RE2::ANCHOR_START"
- ANCHOR_BOTH "RE2::ANCHOR_BOTH"
-
- ctypedef Anchor re2_Anchor "RE2::Anchor"
-
- cdef enum ErrorCode:
- NoError "RE2::NoError"
- ErrorInternal "RE2::ErrorInternal"
- # Parse errors
- ErrorBadEscape "RE2::ErrorBadEscape" # bad escape sequence
- ErrorBadCharClass "RE2::ErrorBadCharClass" # bad character class
- ErrorBadCharRange "RE2::ErrorBadCharRange" # bad character class range
- ErrorMissingBracket "RE2::ErrorMissingBracket" # missing closing ]
- ErrorMissingParen "RE2::ErrorMissingParen" # missing closing )
- ErrorTrailingBackslash "RE2::ErrorTrailingBackslash" # trailing \ at end of regexp
- ErrorRepeatArgument "RE2::ErrorRepeatArgument" # repeat argument missing, e.g. "*"
- ErrorRepeatSize "RE2::ErrorRepeatSize" # bad repetition argument
- ErrorRepeatOp "RE2::ErrorRepeatOp" # bad repetition operator
- ErrorBadPerlOp "RE2::ErrorBadPerlOp" # bad perl operator
- ErrorBadUTF8 "RE2::ErrorBadUTF8" # invalid UTF-8 in regexp
- ErrorBadNamedCapture "RE2::ErrorBadNamedCapture" # bad named capture group
- ErrorPatternTooLarge "RE2::ErrorPatternTooLarge" # pattern too large (compile failed)
-
- cdef enum Encoding:
- EncodingUTF8 "RE2::Options::EncodingUTF8"
- EncodingLatin1 "RE2::Options::EncodingLatin1"
-
- ctypedef Encoding re2_Encoding "RE2::Options::Encoding"
-
- cdef cppclass Options "RE2::Options":
- Options()
- void set_posix_syntax(int b)
- void set_longest_match(int b)
- void set_log_errors(int b)
- void set_max_mem(int m)
- void set_literal(int b)
- void set_never_nl(int b)
- void set_case_sensitive(int b)
- void set_perl_classes(int b)
- void set_word_boundary(int b)
- void set_one_line(int b)
- int case_sensitive()
- void set_encoding(re2_Encoding encoding)
-
- cdef cppclass RE2:
- RE2(const StringPiece pattern, Options option) nogil
- RE2(const StringPiece pattern) nogil
- int Match(const StringPiece text, int startpos, int endpos,
- Anchor anchor, StringPiece * match, int nmatch) nogil
- int Replace(cpp_string *str, const RE2 pattern,
- const StringPiece rewrite) nogil
- int GlobalReplace(cpp_string *str, const RE2 pattern,
- const StringPiece rewrite) nogil
- int NumberOfCapturingGroups()
- int ok()
- const cpp_string pattern()
- cpp_string error()
- ErrorCode error_code()
- const map[cpp_string, int]& NamedCapturingGroups()
-
- # hack for static methods
- cdef int Replace "RE2::Replace"(
- cpp_string *str, const RE2 pattern,
- const StringPiece rewrite) nogil
- cdef int GlobalReplace "RE2::GlobalReplace"(
- cpp_string *str,
- const RE2 pattern,
- const StringPiece rewrite) nogil
-
-
-cdef extern from "_re2macros.h":
- StringPiece * new_StringPiece_array(int) nogil
-
-
-cdef extern from *:
- # StringPiece * new_StringPiece_array "new re2::StringPiece[n]" (int) nogil
- void delete_StringPiece_array "delete[]" (StringPiece *) nogil
diff --git a/contrib/python/pyre2/py3/src/match.pxi b/contrib/python/pyre2/py3/src/match.pxi
deleted file mode 100644
index 3eaae74b47..0000000000
--- a/contrib/python/pyre2/py3/src/match.pxi
+++ /dev/null
@@ -1,280 +0,0 @@
-cdef class Match:
- cdef readonly Pattern re
- cdef readonly object string
- cdef readonly int pos
- cdef readonly int endpos
- cdef readonly tuple regs
-
- cdef StringPiece * matches
- cdef int encoded
- cdef int nmatches
- cdef int _lastindex
- cdef tuple _groups
- cdef dict _named_groups
-
- property lastindex:
- def __get__(self):
- return None if self._lastindex < 1 else self._lastindex
-
- property lastgroup:
- def __get__(self):
- if self._lastindex < 1:
- return None
- for name, n in self.re.groupindex.items():
- if n == self._lastindex:
- return name
- return None
-
- def __init__(self, Pattern pattern_object, int num_groups):
- self._lastindex = -1
- self._groups = None
- self.pos = 0
- self.endpos = -1
- self.matches = new_StringPiece_array(num_groups + 1)
- self.nmatches = num_groups
- self.re = pattern_object
-
- cdef _init_groups(self):
- cdef list groups = []
- cdef int i
- cdef const char * last_end = NULL
- cdef const char * cur_end = NULL
-
- for i in range(self.nmatches):
- if self.matches[i].data() == NULL:
- groups.append(None)
- else:
- if i > 0:
- cur_end = self.matches[i].data() + self.matches[i].length()
-
- if last_end == NULL:
- last_end = cur_end
- self._lastindex = i
- else:
- # The rules for last group are a bit complicated:
- # if two groups end at the same point, the earlier one
- # is considered last, so we don't switch our selection
- # unless the end point has moved.
- if cur_end > last_end:
- last_end = cur_end
- self._lastindex = i
- groups.append(
- self.matches[i].data()[:self.matches[i].length()])
- self._groups = tuple(groups)
-
- cdef bytes _group(self, object groupnum):
- cdef int idx
- if isinstance(groupnum, int):
- idx = groupnum
- if idx > self.nmatches - 1:
- raise IndexError("no such group %d; available groups: %r"
- % (idx, list(range(self.nmatches))))
- return self._groups[idx]
- groupdict = self._groupdict()
- if groupnum not in groupdict:
- raise IndexError("no such group %r; available groups: %r"
- % (groupnum, list(groupdict)))
- return groupdict[groupnum]
-
- cdef dict _groupdict(self):
- if self._named_groups is None:
- self._named_groups = {name: self._groups[n]
- for name, n in self.re.groupindex.items()}
- return self._named_groups
-
- def groups(self, default=None):
- if self.encoded:
- return tuple([default if g is None else g.decode('utf8')
- for g in self._groups[1:]])
- return tuple([default if g is None else g
- for g in self._groups[1:]])
-
- def group(self, *args):
- if len(args) == 0:
- groupnum = 0
- elif len(args) == 1:
- groupnum = args[0]
- else: # len(args) > 1:
- return tuple([self.group(i) for i in args])
- if self.encoded:
- result = self._group(groupnum)
- return None if result is None else result.decode('utf8')
- return self._group(groupnum)
-
- def groupdict(self):
- result = self._groupdict()
- if self.encoded:
- return {a: None if b is None else b.decode('utf8')
- for a, b in result.items()}
- return result
-
- def expand(self, object template):
- """Expand a template with groups."""
- cdef bytearray result = bytearray()
- if isinstance(template, unicode):
- if not PY2 and not self.encoded:
- raise ValueError(
- 'cannot expand unicode template on bytes pattern')
- templ = template.encode('utf8')
- else:
- if not PY2 and self.encoded:
- raise ValueError(
- 'cannot expand bytes template on unicode pattern')
- templ = bytes(template)
- self._expand(templ, result)
- return result.decode('utf8') if self.encoded else bytes(result)
-
- cdef _expand(self, bytes templ, bytearray result):
- """Expand template by appending to an existing bytearray.
- Everything remains UTF-8 encoded."""
- cdef char * cstring
- cdef int n = 0, prev = 0, size
-
- # NB: cstring is used to get single characters, to avoid difference in
- # Python 2/3 behavior of bytes objects.
- cstring = templ
- size = len(templ)
- while True:
- prev = n
- n = templ.find(b'\\', prev)
- if n == -1:
- result.extend(templ[prev:])
- break
- result.extend(templ[prev:n])
- n += 1
- if (n + 2 < size and cstring[n] == b'x'
- and ishex(cstring[n + 1]) and ishex(cstring[n + 2])):
- # hex char reference \x1f
- result.append(int(templ[n + 1:n + 3], base=16) & 255)
- n += 3
- elif (n + 2 < size and isoct(cstring[n]) and isoct(cstring[n + 1])
- and isoct(cstring[n + 2])):
- # octal char reference \123
- result.append(int(templ[n:n + 3], base=8) & 255)
- n += 3
- elif cstring[n] == b'0':
- if n + 1 < size and isoct(cstring[n + 1]):
- # 2 character octal: \01
- result.append(int(templ[n:n + 2], base=8))
- n += 2
- else: # nul-terminator literal \0
- result.append(b'\0')
- n += 1
- elif b'0' <= cstring[n] <= b'9': # numeric group reference
- if n + 1 < size and isdigit(cstring[n + 1]):
- # 2 digit group ref \12
- groupno = int(templ[n:n + 2])
- n += 2
- else:
- # 1 digit group ref \1
- groupno = int(templ[n:n + 1])
- n += 1
- if groupno <= self.re.groups:
- groupval = self._group(groupno)
- if groupval is not None:
- result.extend(groupval)
- else:
- raise RegexError('invalid group reference.')
- elif cstring[n] == b'g': # named group reference
- n += 1
- if n >= size or cstring[n] != b'<':
- raise RegexError('missing group name')
- n += 1
- start = n
- while cstring[n] != b'>':
- if not isident(cstring[n]):
- raise RegexError('bad character in group name')
- n += 1
- if n >= size:
- raise RegexError('unterminated group name')
- if templ[start:n].isdigit():
- name = int(templ[start:n])
- elif isdigit(cstring[start]):
- raise RegexError('bad character in group name')
- else:
- name = templ[start:n]
- if self.encoded:
- name = name.decode('utf8')
- groupval = self._group(name)
- if groupval is not None:
- result.extend(groupval)
- n += 1
- else:
- if cstring[n] == b'n':
- result.append(b'\n')
- elif cstring[n] == b'r':
- result.append(b'\r')
- elif cstring[n] == b't':
- result.append(b'\t')
- elif cstring[n] == b'v':
- result.append(b'\v')
- elif cstring[n] == b'f':
- result.append(b'\f')
- elif cstring[n] == b'a':
- result.append(b'\a')
- elif cstring[n] == b'b':
- result.append(b'\b')
- elif cstring[n] == b'\\':
- result.append(b'\\')
- else: # copy verbatim
- result.append(b'\\')
- result.append(cstring[n])
- n += 1
- return bytes(result)
-
- def start(self, group=0):
- return self.span(group)[0]
-
- def end(self, group=0):
- return self.span(group)[1]
-
- def span(self, group=0):
- if isinstance(group, int):
- if group > len(self.regs):
- raise IndexError("no such group %d; available groups: %r"
- % (group, list(range(len(self.regs)))))
- return self.regs[group]
- else:
- self._groupdict()
- if group not in self.re.groupindex:
- raise IndexError("no such group %r; available groups: %r"
- % (group, list(self.re.groupindex)))
- return self.regs[self.re.groupindex[group]]
-
- cdef _make_spans(self, char * cstring, int size, int * cpos, int * upos):
- cdef int start, end
- cdef StringPiece * piece
-
- spans = []
- for i in range(self.nmatches):
- if self.matches[i].data() == NULL:
- spans.append((-1, -1))
- else:
- piece = &self.matches[i]
- if piece.data() == NULL:
- return (-1, -1)
- start = piece.data() - cstring
- end = start + piece.length()
- spans.append((start, end))
-
- if self.encoded == 2:
- spans = self._convert_spans(spans, cstring, size, cpos, upos)
-
- self.regs = tuple(spans)
-
- cdef list _convert_spans(self, spans,
- char * cstring, int size, int * cpos, int * upos):
- cdef map[int, int] positions
- cdef int x, y
- for x, y in spans:
- positions[x] = x
- positions[y] = y
- unicodeindices(positions, cstring, size, cpos, upos)
- return [(positions[x], positions[y]) for x, y in spans]
-
- def __dealloc__(self):
- delete_StringPiece_array(self.matches)
-
- def __repr__(self):
- return '<re2.Match object; span=%r, match=%r>' % (
- self.span(), self.group())
diff --git a/contrib/python/pyre2/py3/src/pattern.pxi b/contrib/python/pyre2/py3/src/pattern.pxi
deleted file mode 100644
index b8439d2007..0000000000
--- a/contrib/python/pyre2/py3/src/pattern.pxi
+++ /dev/null
@@ -1,650 +0,0 @@
-cdef class Pattern:
- cdef readonly object pattern # original pattern in Python format
- cdef readonly int flags
- cdef readonly int groups # number of groups
- cdef readonly dict groupindex # name => group number
- cdef object __weakref__
-
- cdef bint encoded # True if this was originally a Unicode pattern
- cdef RE2 * re_pattern
-
- def search(self, object string, int pos=0, int endpos=-1):
- """Scan through string looking for a match, and return a corresponding
- Match instance. Return None if no position in the string matches."""
- return self._search(string, pos, endpos, UNANCHORED)
-
- def match(self, object string, int pos=0, int endpos=-1):
- """Matches zero or more characters at the beginning of the string."""
- return self._search(string, pos, endpos, ANCHOR_START)
-
- def fullmatch(self, object string, int pos=0, int endpos=-1):
- """"fullmatch(string[, pos[, endpos]]) --> Match object or None."
-
- Matches the entire string."""
- return self._search(string, pos, endpos, ANCHOR_BOTH)
-
- cdef _search(self, object string, int pos, int endpos,
- re2_Anchor anchoring):
- """Scan through string looking for a match, and return a corresponding
- Match instance. Return None if no position in the string matches."""
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int retval
- cdef int encoded = 0
- cdef StringPiece * sp
- cdef Match m = Match(self, self.groups + 1)
- cdef int cpos = 0, upos = pos
-
- if 0 <= endpos <= pos:
- return None
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- try:
- if encoded == 2 and (pos or endpos != -1):
- utf8indices(cstring, size, &pos, &endpos)
- cpos = pos
- if pos > size:
- return None
- if 0 <= endpos < size:
- size = endpos
-
- sp = new StringPiece(cstring, size)
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- anchoring,
- m.matches,
- self.groups + 1)
- del sp
- if retval == 0:
- return None
-
- m.encoded = encoded
- m.nmatches = self.groups + 1
- m.string = string
- m.pos = pos
- if endpos == -1:
- m.endpos = size
- else:
- m.endpos = endpos
- m._make_spans(cstring, size, &cpos, &upos)
- m._init_groups()
- finally:
- release_cstring(&buf)
- return m
-
- def contains(self, object string, int pos=0, int endpos=-1):
- """"contains(string[, pos[, endpos]]) --> bool."
-
- Scan through string looking for a match, and return True or False."""
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int retval
- cdef int encoded = 0
- cdef StringPiece * sp
-
- if 0 <= endpos <= pos:
- return False
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- try:
- if encoded == 2 and (pos or endpos != -1):
- utf8indices(cstring, size, &pos, &endpos)
- if pos > size:
- return False
- if 0 <= endpos < size:
- size = endpos
-
- sp = new StringPiece(cstring, size)
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- UNANCHORED,
- NULL,
- 0)
- del sp
- finally:
- release_cstring(&buf)
- return retval != 0
-
- def count(self, object string, int pos=0, int endpos=-1):
- """Return number of non-overlapping matches of pattern in string."""
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int retval
- cdef int encoded = 0
- cdef int result = 0
- cdef StringPiece * sp = NULL
- cdef StringPiece * matches = NULL
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- try:
- if encoded == 2 and (pos or endpos != -1):
- utf8indices(cstring, size, &pos, &endpos)
- if pos > size:
- return 0
- if 0 <= endpos < size:
- size = endpos
-
- sp = new StringPiece(cstring, size)
- matches = new_StringPiece_array(1)
- try:
- while True:
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- UNANCHORED,
- matches,
- 1)
- if retval == 0:
- break
- result += 1
- if pos == size:
- break
- # offset the pos to move to the next point
- pos = matches[0].data() - cstring + (
- matches[0].length() or 1)
- finally:
- del sp
- delete_StringPiece_array(matches)
- finally:
- release_cstring(&buf)
- return result
-
- def findall(self, object string, int pos=0, int endpos=-1):
- """Return all non-overlapping matches of pattern in string as a list
- of strings."""
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int encoded = 0
- cdef int retval
- cdef list resultlist = []
- cdef StringPiece * sp = NULL
- cdef StringPiece * matches = NULL
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- try:
- if encoded == 2 and (pos or endpos != -1):
- utf8indices(cstring, size, &pos, &endpos)
- if pos > size:
- return []
- if 0 <= endpos < size:
- size = endpos
-
- sp = new StringPiece(cstring, size)
- matches = new_StringPiece_array(self.groups + 1)
-
- while True:
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- UNANCHORED,
- matches,
- self.groups + 1)
- if retval == 0:
- break
- if self.groups > 1:
- if encoded:
- resultlist.append(tuple([
- '' if matches[i].data() is NULL else
- matches[i].data()[:matches[i].length()
- ].decode('utf8')
- for i in range(1, self.groups + 1)]))
- else:
- resultlist.append(tuple([
- b'' if matches[i].data() is NULL
- else matches[i].data()[:matches[i].length()]
- for i in range(1, self.groups + 1)]))
- else: # 0 or 1 group; return list of strings
- if encoded:
- resultlist.append(matches[self.groups].data()[
- :matches[self.groups].length()].decode('utf8'))
- else:
- resultlist.append(matches[self.groups].data()[
- :matches[self.groups].length()])
- if pos == size:
- break
- # offset the pos to move to the next point
- pos = matches[0].data() - cstring + (matches[0].length() or 1)
- finally:
- del sp
- delete_StringPiece_array(matches)
- release_cstring(&buf)
- return resultlist
-
- def finditer(self, object string, int pos=0, int endpos=-1):
- """Yield all non-overlapping matches of pattern in string as Match
- objects."""
- result = iter(self._finditer(string, pos, endpos))
- next(result) # dummy value to raise error before start of generator
- return result
-
- def _finditer(self, object string, int pos=0, int endpos=-1):
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int retval
- cdef StringPiece * sp = NULL
- cdef Match m
- cdef int encoded = 0
- cdef int cpos = 0, upos = pos
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- try:
- if encoded == 2 and (pos or endpos != -1):
- utf8indices(cstring, size, &pos, &endpos)
- cpos = pos
- if pos > size:
- return
- if 0 <= endpos < size:
- size = endpos
-
- sp = new StringPiece(cstring, size)
-
- yield
- while True:
- m = Match(self, self.groups + 1)
- m.string = string
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- UNANCHORED,
- m.matches,
- self.groups + 1)
- if retval == 0:
- break
- m.encoded = encoded
- m.nmatches = self.groups + 1
- m.pos = pos
- if endpos == -1:
- m.endpos = size
- else:
- m.endpos = endpos
- m._make_spans(cstring, size, &cpos, &upos)
- m._init_groups()
- yield m
- if pos == size:
- break
- # offset the pos to move to the next point
- pos = m.matches[0].data() - cstring + (
- m.matches[0].length() or 1)
- finally:
- del sp
- release_cstring(&buf)
-
- def split(self, string, int maxsplit=0):
- """split(string[, maxsplit = 0]) --> list
-
- Split a string by the occurrences of the pattern."""
- cdef char * cstring
- cdef Py_ssize_t size
- cdef int retval
- cdef int pos = 0
- cdef int lookahead = 0
- cdef int num_split = 0
- cdef StringPiece * sp
- cdef StringPiece * matches
- cdef list resultlist = []
- cdef int encoded = 0
- cdef Py_buffer buf
-
- if maxsplit < 0:
- maxsplit = 0
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- matches = new_StringPiece_array(self.groups + 1)
- sp = new StringPiece(cstring, size)
- try:
-
- while True:
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos + lookahead,
- size,
- UNANCHORED,
- matches,
- self.groups + 1)
- if retval == 0:
- break
-
- match_start = matches[0].data() - cstring
- match_end = match_start + matches[0].length()
-
- # If an empty match, just look ahead until you find something
- if match_start == match_end:
- if pos + lookahead == size:
- break
- lookahead += 1
- continue
-
- if encoded:
- resultlist.append(
- char_to_unicode(&sp.data()[pos], match_start - pos))
- else:
- resultlist.append(sp.data()[pos:match_start])
- if self.groups > 0:
- for group in range(self.groups):
- if matches[group + 1].data() == NULL:
- resultlist.append(None)
- else:
- if encoded:
- resultlist.append(char_to_unicode(
- matches[group + 1].data(),
- matches[group + 1].length()))
- else:
- resultlist.append(matches[group + 1].data()[:
- matches[group + 1].length()])
-
- # offset the pos to move to the next point
- pos = match_end
- lookahead = 0
-
- num_split += 1
- if maxsplit and num_split >= maxsplit:
- break
-
- if encoded:
- resultlist.append(
- char_to_unicode(&sp.data()[pos], sp.length() - pos))
- else:
- resultlist.append(sp.data()[pos:])
- finally:
- del sp
- delete_StringPiece_array(matches)
- release_cstring(&buf)
- return resultlist
-
- def sub(self, repl, string, int count=0):
- """sub(repl, string[, count = 0]) --> newstring
-
- Return the string obtained by replacing the leftmost non-overlapping
- occurrences of pattern in string by the replacement repl."""
- cdef int num_repl = 0
- return self._subn(repl, string, count, &num_repl)
-
- def subn(self, repl, string, int count=0):
- """subn(repl, string[, count = 0]) --> (newstring, number of subs)
-
- Return the tuple (new_string, number_of_subs_made) found by replacing
- the leftmost non-overlapping occurrences of pattern with the
- replacement repl."""
- cdef int num_repl = 0
- result = self._subn(repl, string, count, &num_repl)
- return result, num_repl
-
- cdef _subn(self, repl, string, int count, int *num_repl):
- cdef bytes repl_b
- cdef char * cstring
- cdef object result
- cdef Py_ssize_t size
- cdef StringPiece * sp = NULL
- cdef cpp_string * input_str = NULL
- cdef int string_encoded = 0
- cdef int repl_encoded = 0
-
- if callable(repl):
- # This is a callback, so use the custom function
- return self._subn_callback(repl, string, count, num_repl)
-
- repl_b = unicode_to_bytes(repl, &repl_encoded, self.encoded)
- if not repl_encoded and not isinstance(repl, bytes):
- repl_b = bytes(repl) # coerce buffer to bytes object
-
- if count > 1 or (b'\\' if PY2 else <char>b'\\') in repl_b:
- # Limit on number of substitutions or replacement string contains
- # escape sequences; handle with Match.expand() implementation.
- # RE2 does support simple numeric group references \1, \2,
- # but the number of differences with Python behavior is
- # non-trivial.
- return self._subn_expand(repl_b, string, count, num_repl)
- try:
- cstring = repl_b
- size = len(repl_b)
- sp = new StringPiece(cstring, size)
-
- bytestr = unicode_to_bytes(string, &string_encoded, self.encoded)
- if not string_encoded and not isinstance(bytestr, bytes):
- bytestr = bytes(bytestr) # coerce buffer to bytes object
- input_str = new cpp_string(<char *>bytestr, len(bytestr))
- # NB: RE2 treats unmatched groups in repl as empty string;
- # Python raises an error.
- with nogil:
- if count == 0:
- num_repl[0] = GlobalReplace(
- input_str, self.re_pattern[0], sp[0])
- elif count == 1:
- num_repl[0] = Replace(
- input_str, self.re_pattern[0], sp[0])
-
- if string_encoded or (repl_encoded and num_repl[0] > 0):
- result = cpp_to_unicode(input_str[0])
- else:
- result = cpp_to_bytes(input_str[0])
- finally:
- del input_str, sp
- return result
-
- cdef _subn_callback(self, callback, string, int count, int * num_repl):
- # This function is probably the hardest to implement correctly.
- # This is my first attempt, but if anybody has a better solution,
- # please help out.
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int retval
- cdef int prevendpos = -1
- cdef int endpos = 0
- cdef int pos = 0
- cdef int encoded = 0
- cdef StringPiece * sp
- cdef Match m
- cdef bytearray result = bytearray()
- cdef int cpos = 0, upos = 0
-
- if count < 0:
- count = 0
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- sp = new StringPiece(cstring, size)
- try:
- while True:
- m = Match(self, self.groups + 1)
- m.string = string
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- UNANCHORED,
- m.matches,
- self.groups + 1)
- if retval == 0:
- break
-
- endpos = m.matches[0].data() - cstring
- if endpos == prevendpos:
- endpos += 1
- if endpos > size:
- break
- prevendpos = endpos
- result.extend(sp.data()[pos:endpos])
- pos = endpos + m.matches[0].length()
-
- m.encoded = encoded
- m.nmatches = self.groups + 1
- m._make_spans(cstring, size, &cpos, &upos)
- m._init_groups()
- tmp = callback(m)
- if tmp:
- result.extend(tmp.encode('utf8') if encoded else tmp)
- else:
- result.extend(b'')
-
- num_repl[0] += 1
- if count and num_repl[0] >= count:
- break
- result.extend(sp.data()[pos:])
- finally:
- del sp
- release_cstring(&buf)
- return result.decode('utf8') if encoded else bytes(result)
-
- cdef _subn_expand(self, bytes repl, string, int count, int * num_repl):
- """Perform ``count`` substitutions with replacement string and
- Match.expand."""
- cdef char * cstring
- cdef Py_ssize_t size
- cdef Py_buffer buf
- cdef int retval
- cdef int prevendpos = -1
- cdef int endpos = 0
- cdef int pos = 0
- cdef int encoded = 0
- cdef StringPiece * sp
- cdef Match m
- cdef bytearray result = bytearray()
-
- if count < 0:
- count = 0
-
- bytestr = unicode_to_bytes(string, &encoded, self.encoded)
- if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
- raise TypeError('expected string or buffer')
- sp = new StringPiece(cstring, size)
- try:
- while True:
- m = Match(self, self.groups + 1)
- m.string = string
- with nogil:
- retval = self.re_pattern.Match(
- sp[0],
- pos,
- size,
- UNANCHORED,
- m.matches,
- self.groups + 1)
- if retval == 0:
- break
-
- endpos = m.matches[0].data() - cstring
- if endpos == prevendpos:
- endpos += 1
- if endpos > size:
- break
- prevendpos = endpos
- result.extend(sp.data()[pos:endpos])
- pos = endpos + m.matches[0].length()
-
- m.encoded = encoded
- m.nmatches = self.groups + 1
- m._init_groups()
- m._expand(repl, result)
-
- num_repl[0] += 1
- if count and num_repl[0] >= count:
- break
- result.extend(sp.data()[pos:])
- finally:
- del sp
- release_cstring(&buf)
- return result.decode('utf8') if encoded else bytes(result)
-
- def scanner(self, arg):
- return re.compile(self.pattern).scanner(arg)
- # raise NotImplementedError
-
- def _dump_pattern(self):
- cdef cpp_string s = self.re_pattern.pattern()
- if self.encoded:
- return cpp_to_bytes(s).decode('utf8')
- return cpp_to_bytes(s)
-
- def __repr__(self):
- if self.flags == 0:
- return 're2.compile(%r)' % self.pattern
- return 're2.compile(%r, %r)' % (self.pattern, self.flags)
-
- def __reduce__(self):
- return (compile, (self.pattern, self.flags))
-
- def __dealloc__(self):
- del self.re_pattern
-
-
-class PythonRePattern:
- """A wrapper for re.Pattern to support the extra methods defined by re2
- (contains, count)."""
- def __init__(self, pattern, flags=None):
- self._pattern = re.compile(pattern, flags)
- self.pattern = pattern
- self.flags = flags
- self.groupindex = self._pattern.groupindex
- self.groups = self._pattern.groups
-
- def contains(self, string):
- return bool(self._pattern.search(string))
-
- def count(self, string, pos=0, endpos=9223372036854775807):
- return len(self._pattern.findall(string, pos, endpos))
-
- def findall(self, string, pos=0, endpos=9223372036854775807):
- return self._pattern.findall(string, pos, endpos)
-
- def finditer(self, string, pos=0, endpos=9223372036854775807):
- return self._pattern.finditer(string, pos, endpos)
-
- def fullmatch(self, string, pos=0, endpos=9223372036854775807):
- return self._pattern.fullmatch(string, pos, endpos)
-
- def match(self, string, pos=0, endpos=9223372036854775807):
- return self._pattern.match(string, pos, endpos)
-
- def scanner(self, string, pos=0, endpos=9223372036854775807):
- return self._pattern.scanner(string, pos, endpos)
-
- def search(self, string, pos=0, endpos=9223372036854775807):
- return self._pattern.search(string, pos, endpos)
-
- def split(self, string, maxsplit=0):
- return self._pattern.split(string, maxsplit)
-
- def sub(self, repl, string, count=0):
- return self._pattern.sub(repl, string, count)
-
- def subn(self, repl, string, count=0):
- return self._pattern.subn(repl, string, count)
-
- def __repr__(self):
- return repr(self._pattern)
-
- def __reduce__(self):
- return (self, (self.pattern, self.flags))
diff --git a/contrib/python/pyre2/py3/src/re2.pyx b/contrib/python/pyre2/py3/src/re2.pyx
deleted file mode 100644
index c48101426f..0000000000
--- a/contrib/python/pyre2/py3/src/re2.pyx
+++ /dev/null
@@ -1,458 +0,0 @@
-# cython: infer_types(False)
-r"""Regular expressions using Google's RE2 engine.
-
-Compared to Python's ``re``, the RE2 engine compiles regular expressions to
-deterministic finite automata, which guarantees linear-time behavior.
-
-Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
-to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
-For best performance, work with UTF-8 encoded bytes strings.
-
-Regular expressions that are not compatible with RE2 are processed with
-fallback to ``re``. Examples of features not supported by RE2:
-
- - lookahead assertions ``(?!...)``
- - backreferences (``\\n`` in search pattern)
- - \W and \S not supported inside character classes
-
-On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
-Syntax reference: https://github.com/google/re2/wiki/Syntax
-
-What follows is a reference for the regular expression syntax supported by this
-module (i.e., without requiring fallback to `re`).
-
-Regular expressions can contain both special and ordinary characters.
-Most ordinary characters, like "A", "a", or "0", are the simplest
-regular expressions; they simply match themselves.
-
-The special characters are::
-
- "." Matches any character except a newline.
- "^" Matches the start of the string.
- "$" Matches the end of the string or just before the newline at
- the end of the string.
- "*" Matches 0 or more (greedy) repetitions of the preceding RE.
- Greedy means that it will match as many repetitions as possible.
- "+" Matches 1 or more (greedy) repetitions of the preceding RE.
- "?" Matches 0 or 1 (greedy) of the preceding RE.
- *?,+?,?? Non-greedy versions of the previous three special characters.
- {m,n} Matches from m to n repetitions of the preceding RE.
- {m,n}? Non-greedy version of the above.
- "\\" Either escapes special characters or signals a special sequence.
- [] Indicates a set of characters.
- A "^" as the first character indicates a complementing set.
- "|" A|B, creates an RE that will match either A or B.
- (...) Matches the RE inside the parentheses.
- The contents can be retrieved or matched later in the string.
- (?:...) Non-grouping version of regular parentheses.
- (?imsux) Set the I, M, S, U, or X flag for the RE (see below).
-
-The special sequences consist of "\\" and a character from the list
-below. If the ordinary character is not on the list, then the
-resulting RE will match the second character::
-
- \A Matches only at the start of the string.
- \Z Matches only at the end of the string.
- \b Matches the empty string, but only at the start or end of a word.
- \B Matches the empty string, but not at the start or end of a word.
- \d Matches any decimal digit.
- \D Matches any non-digit character.
- \s Matches any whitespace character.
- \S Matches any non-whitespace character.
- \w Matches any alphanumeric character.
- \W Matches the complement of \w.
- \\ Matches a literal backslash.
- \pN Unicode character class (one-letter name)
- \p{Greek} Unicode character class
- \PN negated Unicode character class (one-letter name)
- \P{Greek} negated Unicode character class
-
-This module exports the following functions::
-
- count Count all occurrences of a pattern in a string.
- match Match a regular expression pattern to the beginning of a string.
- fullmatch Match a regular expression pattern to all of a string.
- search Search a string for a pattern and return Match object.
- contains Same as search, but only return bool.
- sub Substitute occurrences of a pattern found in a string.
- subn Same as sub, but also return the number of substitutions made.
- split Split a string by the occurrences of a pattern.
- findall Find all occurrences of a pattern in a string.
- finditer Return an iterator yielding a match object for each match.
- compile Compile a pattern into a RegexObject.
- purge Clear the regular expression cache.
- escape Backslash all non-alphanumerics in a string.
-
-Some of the functions in this module takes flags as optional parameters::
-
- A ASCII Make \w, \W, \b, \B, \d, \D match the corresponding ASCII
- character categories (rather than the whole Unicode
- categories, which is the default).
- I IGNORECASE Perform case-insensitive matching.
- M MULTILINE "^" matches the beginning of lines (after a newline)
- as well as the string.
- "$" matches the end of lines (before a newline) as well
- as the end of the string.
- S DOTALL "." matches any character at all, including the newline.
- X VERBOSE Ignore whitespace and comments for nicer looking RE's.
- U UNICODE Enable Unicode character classes and make \w, \W, \b, \B,
- Unicode-aware (default for unicode patterns).
-
-This module also defines an exception 'RegexError' (also available under the
-alias 'error').
-
-"""
-
-include "includes.pxi"
-
-import re
-import sys
-import warnings
-from re import error as RegexError
-
-error = re.error
-
-# Import re flags to be compatible.
-I, M, S, U, X, L = re.I, re.M, re.S, re.U, re.X, re.L
-IGNORECASE = re.IGNORECASE
-MULTILINE = re.MULTILINE
-DOTALL = re.DOTALL
-UNICODE = re.UNICODE
-VERBOSE = re.VERBOSE
-LOCALE = re.LOCALE
-DEBUG = re.DEBUG
-ASCII = 256 # Python 3
-
-FALLBACK_QUIETLY = 0
-FALLBACK_WARNING = 1
-FALLBACK_EXCEPTION = 2
-
-VERSION = (0, 2, 23)
-VERSION_HEX = 0x000217
-
-cdef int _I = I, _M = M, _S = S, _U = U, _X = X, _L = L
-cdef int current_notification = FALLBACK_QUIETLY
-cdef bint PY2 = PY_MAJOR_VERSION == 2
-
-# Type of compiled re object from Python stdlib
-SREPattern = type(re.compile(''))
-
-_cache = {}
-_cache_repl = {}
-
-_MAXCACHE = 100
-
-
-include "compile.pxi"
-include "pattern.pxi"
-include "match.pxi"
-
-
-def purge():
- """Clear the regular expression caches."""
- _cache.clear()
- _cache_repl.clear()
-
-
-def search(pattern, string, int flags=0):
- """Scan through string looking for a match to the pattern, returning
- a ``Match`` object or none if no match was found."""
- return compile(pattern, flags).search(string)
-
-
-def match(pattern, string, int flags=0):
- """Try to apply the pattern at the start of the string, returning
- a ``Match`` object, or ``None`` if no match was found."""
- return compile(pattern, flags).match(string)
-
-
-def fullmatch(pattern, string, int flags=0):
- """Try to apply the pattern to the entire string, returning
- a ``Match`` object, or ``None`` if no match was found."""
- return compile(pattern, flags).fullmatch(string)
-
-
-def contains(pattern, string, int flags=0):
- """Scan through string looking for a match to the pattern, returning
- True or False."""
- return compile(pattern, flags).contains(string)
-
-
-def finditer(pattern, string, int flags=0):
- """Yield all non-overlapping matches in the string.
-
- For each match, the iterator returns a ``Match`` object.
- Empty matches are included in the result."""
- return compile(pattern, flags).finditer(string)
-
-
-def findall(pattern, string, int flags=0):
- """Return a list of all non-overlapping matches in the string.
-
- Each match is represented as a string or a tuple (when there are two ore
- more groups). Empty matches are included in the result."""
- return compile(pattern, flags).findall(string)
-
-
-def count(pattern, string, int flags=0):
- """Return number of non-overlapping matches in the string.
-
- Empty matches are included in the count."""
- return compile(pattern, flags).count(string)
-
-
-def split(pattern, string, int maxsplit=0, int flags=0):
- """Split the source string by the occurrences of the pattern,
- returning a list containing the resulting substrings."""
- return compile(pattern, flags).split(string, maxsplit)
-
-
-def sub(pattern, repl, string, int count=0, int flags=0):
- """Return the string obtained by replacing the leftmost
- non-overlapping occurrences of the pattern in string by the
- replacement ``repl``. ``repl`` can be either a string or a callable;
- if a string, backslash escapes in it are processed. If it is
- a callable, it's passed the ``Match`` object and must return
- a replacement string to be used."""
- return compile(pattern, flags).sub(repl, string, count)
-
-
-def subn(pattern, repl, string, int count=0, int flags=0):
- """Return a 2-tuple containing ``(new_string, number)``.
- new_string is the string obtained by replacing the leftmost
- non-overlapping occurrences of the pattern in the source
- string by the replacement ``repl``. ``number`` is the number of
- substitutions that were made. ``repl`` can be either a string or a
- callable; if a string, backslash escapes in it are processed.
- If it is a callable, it's passed the ``Match`` object and must
- return a replacement string to be used."""
- return compile(pattern, flags).subn(repl, string, count)
-
-
-def escape(pattern):
- """Escape all non-alphanumeric characters in pattern."""
- cdef bint uni = isinstance(pattern, unicode)
- cdef list s
- if PY2 or uni:
- s = list(pattern)
- else:
- s = [bytes([c]) for c in pattern]
- for i in range(len(pattern)):
- # c = pattern[i]
- c = s[i]
- if ord(c) < 0x80 and not c.isalnum():
- if uni:
- if c == u'\000':
- s[i] = u'\\000'
- else:
- s[i] = u"\\" + c
- else:
- if c == b'\000':
- s[i] = b'\\000'
- else:
- s[i] = b'\\' + c
- return u''.join(s) if uni else b''.join(s)
-
-
-class BackreferencesException(Exception):
- """Search pattern contains backreferences."""
- pass
-
-
-class CharClassProblemException(Exception):
- """Search pattern contains unsupported character class."""
- pass
-
-
-def set_fallback_notification(level):
- """Set the fallback notification to a level; one of:
- FALLBACK_QUIETLY
- FALLBACK_WARNING
- FALLBACK_EXCEPTION
- """
- global current_notification
- level = int(level)
- if level < 0 or level > 2:
- raise ValueError("This function expects a valid notification level.")
- current_notification = level
-
-
-cdef bint ishex(unsigned char c):
- """Test whether ``c`` is in ``[0-9a-fA-F]``"""
- return (b'0' <= c <= b'9' or b'a' <= c <= b'f' or b'A' <= c <= b'F')
-
-
-cdef bint isoct(unsigned char c):
- """Test whether ``c`` is in ``[0-7]``"""
- return b'0' <= c <= b'7'
-
-
-cdef bint isdigit(unsigned char c):
- """Test whether ``c`` is in ``[0-9]``"""
- return b'0' <= c <= b'9'
-
-
-cdef bint isident(unsigned char c):
- """Test whether ``c`` is in ``[a-zA-Z0-9_]``"""
- return (b'a' <= c <= b'z' or b'A' <= c <= b'Z'
- or b'0' <= c <= b'9' or c == b'_')
-
-
-cdef inline bytes cpp_to_bytes(cpp_string input):
- """Convert from a std::string object to a python string."""
- # By taking the slice we go to the right size,
- # despite spurious or missing null characters.
- return input.data()[:input.length()]
-
-
-cdef inline unicode cpp_to_unicode(cpp_string input):
- """Convert a std::string object to a unicode string."""
- return cpython.unicode.PyUnicode_DecodeUTF8(
- input.data(), input.length(), 'strict')
-
-
-cdef inline unicode char_to_unicode(const char * input, int length):
- """Convert a C string to a unicode string."""
- return cpython.unicode.PyUnicode_DecodeUTF8(input, length, 'strict')
-
-
-cdef inline unicode_to_bytes(object pystring, int * encoded,
- int checkotherencoding):
- """Convert a unicode string to a utf8 bytes object, if necessary.
-
- If pystring is a bytes string or a buffer, return unchanged.
- If checkotherencoding is 0 or 1 and using Python 3, raise an error
- if its truth value is not equal to that of encoded.
- encoded is set to 1 if encoded string can be treated as ASCII,
- and 2 if it contains multibyte unicode characters."""
- if cpython.unicode.PyUnicode_Check(pystring):
- origlen = len(pystring)
- pystring = pystring.encode('utf8')
- encoded[0] = 1 if origlen == len(pystring) else 2
- else:
- encoded[0] = 0
- if not PY2 and checkotherencoding > 0 and not encoded[0]:
- raise TypeError("can't use a string pattern on a bytes-like object")
- elif not PY2 and checkotherencoding == 0 and encoded[0]:
- raise TypeError("can't use a bytes pattern on a string-like object")
- return pystring
-
-
-cdef inline int pystring_to_cstring(
- object pystring, char ** cstring, Py_ssize_t * size,
- Py_buffer * buf):
- """Get a pointer from bytes/buffer object ``pystring``.
-
- On success, return 0, and set ``cstring``, ``size``, and ``buf``."""
- cdef int result = -1
- cstring[0] = NULL
- size[0] = 0
- if PyObject_CheckBuffer(pystring) == 1: # new-style Buffer interface
- result = PyObject_GetBuffer(pystring, buf, PyBUF_SIMPLE)
- if result == 0:
- cstring[0] = <char *>buf.buf
- size[0] = buf.len
- return result
-
-
-cdef inline void release_cstring(Py_buffer *buf):
- """Release buffer if necessary."""
- if not PY2:
- PyBuffer_Release(buf)
-
-
-cdef utf8indices(char * cstring, int size, int *pos, int *endpos):
- """Convert unicode indices ``pos`` and ``endpos`` to UTF-8 indices.
-
- If the indices are out of range, leave them unchanged."""
- cdef unsigned char * data = <unsigned char *>cstring
- cdef int newpos = pos[0], newendpos = -1
- cdef int cpos = 0, upos = 0
- while cpos < size:
- if data[cpos] < 0x80:
- cpos += 1
- upos += 1
- elif data[cpos] < 0xe0:
- cpos += 2
- upos += 1
- elif data[cpos] < 0xf0:
- cpos += 3
- upos += 1
- else:
- cpos += 4
- upos += 1
- # wide unicode chars get 2 unichars when Python <3.3 is compiled
- # with --enable-unicode=ucs2
- emit_if_narrow_unicode()
- upos += 1
- emit_endif()
-
- if upos == pos[0]:
- newpos = cpos
- if endpos[0] == -1:
- break
- elif upos == endpos[0]:
- newendpos = cpos
- break
- pos[0] = newpos
- endpos[0] = newendpos
-
-
-cdef void unicodeindices(map[int, int] &positions,
- char * cstring, int size, int * cpos, int * upos):
- """Convert UTF-8 byte indices to unicode indices."""
- cdef unsigned char * s = <unsigned char *>cstring
- cdef map[int, int].iterator it = positions.begin()
-
- if dereference(it).first == -1:
- dereference(it).second = -1
- postincrement(it)
- if it == positions.end():
- return
- if dereference(it).first == cpos[0]:
- dereference(it).second = upos[0]
- postincrement(it)
- if it == positions.end():
- return
-
- while cpos[0] < size:
- if s[cpos[0]] < 0x80:
- cpos[0] += 1
- upos[0] += 1
- elif s[cpos[0]] < 0xe0:
- cpos[0] += 2
- upos[0] += 1
- elif s[cpos[0]] < 0xf0:
- cpos[0] += 3
- upos[0] += 1
- else:
- cpos[0] += 4
- upos[0] += 1
- # wide unicode chars get 2 unichars when Python <3.3 is compiled
- # with --enable-unicode=ucs2
- emit_if_narrow_unicode()
- upos[0] += 1
- emit_endif()
-
- if dereference(it).first == cpos[0]:
- dereference(it).second = upos[0]
- postincrement(it)
- if it == positions.end():
- break
-
-
-__all__ = [
- # exceptions
- 'BackreferencesException', 'CharClassProblemException',
- 'RegexError', 'error',
- # constants
- 'FALLBACK_EXCEPTION', 'FALLBACK_QUIETLY', 'FALLBACK_WARNING', 'DEBUG',
- 'S', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE',
- 'U', 'UNICODE', 'X', 'VERBOSE', 'VERSION', 'VERSION_HEX',
- # classes
- 'Match', 'Pattern', 'SREPattern',
- # functions
- 'compile', 'count', 'escape', 'findall', 'finditer', 'fullmatch',
- 'match', 'purge', 'search', 'split', 'sub', 'subn',
- 'set_fallback_notification',
- ]
diff --git a/contrib/python/pyre2/py3/tests/test_charliterals.txt b/contrib/python/pyre2/py3/tests/test_charliterals.txt
deleted file mode 100644
index 2eaea128a3..0000000000
--- a/contrib/python/pyre2/py3/tests/test_charliterals.txt
+++ /dev/null
@@ -1,47 +0,0 @@
- >>> import re2 as re
- >>> import warnings
- >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
-
-character literals:
-
- >>> i = 126
- >>> re.compile(r"\%03o" % i)
- re2.compile('\\176')
- >>> re.compile(r"\%03o" % i)._dump_pattern()
- '\\176'
- >>> re.match(r"\%03o" % i, chr(i)) is None
- False
- >>> re.match(r"\%03o0" % i, chr(i) + "0") is None
- False
- >>> re.match(r"\%03o8" % i, chr(i) + "8") is None
- False
- >>> re.match(r"\x%02x" % i, chr(i)) is None
- False
- >>> re.match(r"\x%02x0" % i, chr(i) + "0") is None
- False
- >>> re.match(r"\x%02xz" % i, chr(i) + "z") is None
- False
- >>> re.match("\911", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
- Traceback (most recent call last):
- ...
- re.error: invalid escape sequence: \9
-
-character class literals:
-
- >>> re.match(r"[\%03o]" % i, chr(i)) is None
- False
- >>> re.match(r"[\%03o0]" % i, chr(i) + "0") is None
- False
- >>> re.match(r"[\%03o8]" % i, chr(i) + "8") is None
- False
- >>> re.match(r"[\x%02x]" % i, chr(i)) is None
- False
- >>> re.match(r"[\x%02x0]" % i, chr(i) + "0") is None
- False
- >>> re.match(r"[\x%02xz]" % i, chr(i) + "z") is None
- False
- >>> re.match("[\911]", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
- Traceback (most recent call last):
- ...
- re.error: invalid escape sequence: \9
-
diff --git a/contrib/python/pyre2/py3/tests/test_count.txt b/contrib/python/pyre2/py3/tests/test_count.txt
deleted file mode 100644
index ce3525adc5..0000000000
--- a/contrib/python/pyre2/py3/tests/test_count.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-count tests
-===========
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
-
- >>> re2.count(r"\w+ly", "He was carefully disguised but captured quickly by police.")
- 2
-
-Groups should not affect count():
-
- >>> re2.count(r"(\w+)=(\d+)", "foo=1,foo=2")
- 2
- >>> re2.count(r"(\w)\w", "fx")
- 1
-
-Zero matches:
-
- >>> re2.count("(f)", "gggg")
- 0
-
-A pattern matching an empty string:
-
- >>> re2.count(".*", "foo")
- 2
-
- >>> re2.count("", "foo")
- 4
-
-contains tests
-==============
-
- >>> re2.contains('a', 'bbabb')
- True
- >>> re2.contains('a', 'bbbbb')
- False
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_emptygroups.txt b/contrib/python/pyre2/py3/tests/test_emptygroups.txt
deleted file mode 100644
index 424c8ba25e..0000000000
--- a/contrib/python/pyre2/py3/tests/test_emptygroups.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Empty/unused groups
-===================
-
- >>> import re
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-Unused vs. empty group:
-
- >>> re.search( '(foo)?((.*).)(bar)?', 'a').groups()
- (None, 'a', '', None)
- >>> re2.search('(foo)?((.*).)(bar)?', 'a').groups()
- (None, 'a', '', None)
-
- >>> re.search(r'((.*)?.)', 'a').groups()
- ('a', '')
- >>> re2.search(r'((.*)?.)', 'a').groups()
- ('a', '')
- >>> re.search(r'((.*)+.)', 'a').groups()
- ('a', '')
- >>> re2.search(r'((.*)+.)', 'a').groups()
- ('a', '')
-
-The following show different behavior for re and re2:
-
- >>> re.search(r'((.*)*.)', 'a').groups()
- ('a', '')
- >>> re2.search(r'((.*)*.)', 'a').groups()
- ('a', None)
-
- >>> re.search(r'((.*)*.)', 'Hello').groups()
- ('Hello', '')
- >>> re2.search(r'((.*)*.)', 'Hello').groups()
- ('Hello', 'Hell')
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_findall.txt b/contrib/python/pyre2/py3/tests/test_findall.txt
deleted file mode 100644
index c753b936df..0000000000
--- a/contrib/python/pyre2/py3/tests/test_findall.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-findall tests
-=============
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
-
- >>> re2.findall(r"\w+ly", "He was carefully disguised but captured quickly by police.")
- ['carefully', 'quickly']
-
-This one makes sure all groups are found:
-
- >>> re2.findall(r"(\w+)=(\d+)", "foo=1,foo=2")
- [('foo', '1'), ('foo', '2')]
-
-When there's only one matched group, it should not be returned in a tuple:
-
- >>> re2.findall(r"(\w)\w", "fx")
- ['f']
-
-Zero matches is an empty list:
-
- >>> re2.findall("(f)", "gggg")
- []
-
-If pattern matches an empty string, do it only once at the end:
-
- >>> re2.findall(".*", "foo")
- ['foo', '']
-
- >>> re2.findall("", "foo")
- ['', '', '', '']
-
-
- >>> import re
- >>> re.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
- ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
- >>> re2.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
- ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_finditer.txt b/contrib/python/pyre2/py3/tests/test_finditer.txt
deleted file mode 100644
index 3d60d199c7..0000000000
--- a/contrib/python/pyre2/py3/tests/test_finditer.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-Simple tests for the ``finditer`` function.
-===========================================
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
- >>> with open('tests/cnn_homepage.dat') as tmp:
- ... data = tmp.read()
- >>> len(list(re2.finditer(r'\w+', data)))
- 14230
-
- >>> [m.group(1) for m in re2.finditer(r'\n#hdr-editions(.*?)\n', data)]
- [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
-
- >>> [m.group(1) for m in re2.finditer(r'^#hdr-editions(.*?)$',
- ... data, re2.M)]
- [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
-
- >>> for a in re2.finditer(r'\b', 'foo bar zed'): print(a)
- <re2.Match object; span=(0, 0), match=''>
- <re2.Match object; span=(3, 3), match=''>
- <re2.Match object; span=(4, 4), match=''>
- <re2.Match object; span=(7, 7), match=''>
- <re2.Match object; span=(8, 8), match=''>
- <re2.Match object; span=(11, 11), match=''>
-
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_match_expand.txt b/contrib/python/pyre2/py3/tests/test_match_expand.txt
deleted file mode 100644
index b3d5652c76..0000000000
--- a/contrib/python/pyre2/py3/tests/test_match_expand.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Match Expand Tests
-==================
-
-Match objects have an .expand() method which allows them to
-expand templates as if the .sub() method was called on the pattern.
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> m = re2.match("(\\w+) (\\w+)\\W+(?P<title>\\w+)", "Isaac Newton, physicist")
- >>> m.expand("\\2, \\1")
- 'Newton, Isaac'
- >>> m.expand("\\1 \\g<title>")
- 'Isaac physicist'
- >>> m.expand("\\2, \\1 \\2")
- 'Newton, Isaac Newton'
- >>> m.expand("\\3")
- 'physicist'
- >>> m.expand("\\1 \\g<foo>") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
- Traceback (most recent call last):
- ...
- IndexError: no such group 'foo'; available groups: ['title']
- >>> m.expand("\\0")
- '\x00'
- >>> m.expand("\01")
- '\x01'
- >>> m.expand('\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D')
- '\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D'
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_mmap.txt b/contrib/python/pyre2/py3/tests/test_mmap.txt
deleted file mode 100644
index 12ffa97498..0000000000
--- a/contrib/python/pyre2/py3/tests/test_mmap.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-
-Testing re2 on buffer object
-============================
-
- >>> import re2
- >>> import mmap
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
- >>> tmp = open("tests/cnn_homepage.dat", "rb+")
- >>> data = mmap.mmap(tmp.fileno(), 0)
-
- >>> len(list(re2.finditer(b'\\w+', data)))
- 14230
-
- >>> data.close()
- >>> tmp.close()
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_namedgroups.txt b/contrib/python/pyre2/py3/tests/test_namedgroups.txt
deleted file mode 100644
index 70f561a39f..0000000000
--- a/contrib/python/pyre2/py3/tests/test_namedgroups.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-Testing some aspects of named groups
-=================================================
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
- >>> m = re2.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
- >>> m.start("first_name")
- 0
- >>> m.start("last_name")
- 8
-
- >>> m.span("last_name")
- (8, 16)
- >>> m.regs
- ((0, 16), (0, 7), (8, 16))
-
- >>> m = re2.match(u"(?P<first_name>\\w+) (?P<last_name>\\w+)", u"Malcolm Reynolds")
- >>> m.start(u"first_name")
- 0
- >>> m.start(u"last_name")
- 8
-
- >>> m.span(u"last_name")
- (8, 16)
- >>> m.regs
- ((0, 16), (0, 7), (8, 16))
-
-Compare patterns with and without unicode
-
- >>> pattern = re2.compile(br"(?P<first_name>\w+) (?P<last_name>\w+)")
- >>> print(pattern._dump_pattern().decode('utf8'))
- (?P<first_name>\w+) (?P<last_name>\w+)
- >>> pattern = re2.compile(u"(?P<first_name>\\w+) (?P<last_name>\\w+)",
- ... re2.UNICODE)
- >>> print(pattern._dump_pattern())
- (?P<first_name>[_\p{L}\p{Nd}]+) (?P<last_name>[_\p{L}\p{Nd}]+)
-
-Make sure positions are converted properly for unicode
-
- >>> m = pattern.match(
- ... u'\u05d9\u05e9\u05e8\u05d0\u05dc \u05e6\u05d3\u05d5\u05e7')
- >>> m.start(u"first_name")
- 0
- >>> m.start(u"last_name")
- 6
- >>> m.end(u"last_name")
- 10
- >>> m.regs
- ((0, 10), (0, 5), (6, 10))
- >>> m.span(2)
- (6, 10)
- >>> m.span(u"last_name")
- (6, 10)
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_pattern.txt b/contrib/python/pyre2/py3/tests/test_pattern.txt
deleted file mode 100644
index aab47359a2..0000000000
--- a/contrib/python/pyre2/py3/tests/test_pattern.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-pattern tests
-=============
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
-
-We should be able to get back what we put in.
-
- >>> re2.compile("(foo|b[a]r?)").pattern
- '(foo|b[a]r?)'
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_search.txt b/contrib/python/pyre2/py3/tests/test_search.txt
deleted file mode 100644
index 9c1e18f08c..0000000000
--- a/contrib/python/pyre2/py3/tests/test_search.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-These are simple tests of the ``search`` function
-=================================================
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> import warnings
- >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
-
- >>> re2.search("((?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])", "hello 28.224.2.1 test").group()
- '28.224.2.1'
-
- >>> re2.search("(\d{3})\D?(\d{3})\D?(\d{4})", "800-555-1212").groups()
- ('800', '555', '1212')
-
- >>> input = 'a' * 999
- >>> len(re2.search('(?:a{1000})?a{999}', input).group())
- 999
-
- >>> with open('tests/cnn_homepage.dat') as tmp:
- ... data = tmp.read()
- >>> re2.search(r'\n#hdr-editions(.*?)\n', data).groups()
- (' a { text-decoration:none; }',)
-
-Verify some sanity checks
-
- >>> re2.compile(r'x').search('x', 2000)
- >>> re2.compile(r'x').search('x', 1, -300)
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_split.txt b/contrib/python/pyre2/py3/tests/test_split.txt
deleted file mode 100644
index a3e44bc605..0000000000
--- a/contrib/python/pyre2/py3/tests/test_split.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Split tests
-===========
-
-This one tests to make sure that unicode / utf8 data is parsed correctly.
-
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> a = u'\u6211\u5f88\u597d, \u4f60\u5462?'
-
- >>> re2.split(u' ', a) == [u'\u6211\u5f88\u597d,', u'\u4f60\u5462?']
- True
- >>> re2.split(b' ', a.encode('utf8')) == [
- ... b'\xe6\x88\x91\xe5\xbe\x88\xe5\xa5\xbd,',
- ... b'\xe4\xbd\xa0\xe5\x91\xa2?']
- True
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_sub.txt b/contrib/python/pyre2/py3/tests/test_sub.txt
deleted file mode 100644
index b41dd30d28..0000000000
--- a/contrib/python/pyre2/py3/tests/test_sub.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Tests of substitution
-=====================
-
-This first test is just looking to replace things between parentheses
-with an empty string.
-
-
- >>> import hashlib
- >>> import gzip
- >>> import re2
- >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
- >>> import warnings
- >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
-
- >>> with gzip.open('tests/wikipages.xml.gz', 'rb') as tmp:
- ... data = tmp.read()
- >>> print(hashlib.md5(re2.sub(b'\(.*?\)', b'', data)).hexdigest())
- b7a469f55ab76cd5887c81dbb0cfe6d3
-
- >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
-
-Issue #26 re2.sub replacements with a match of "(.*)" hangs forever
-
- >>> re2.sub('(.*)', r'\1;replacement', 'original')
- 'original;replacement;replacement'
-
- >>> re2.sub('(.*)', lambda x: x.group() + ';replacement', 'original')
- 'original;replacement;replacement'
-
- >>> re2.subn("b*", lambda x: "X", "xyz", 4)
- ('XxXyXzX', 4)
diff --git a/contrib/python/pyre2/py3/tests/test_unicode.txt b/contrib/python/pyre2/py3/tests/test_unicode.txt
deleted file mode 100644
index 71d497b80d..0000000000
--- a/contrib/python/pyre2/py3/tests/test_unicode.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-Here are some tests to make sure that utf-8 works
-=================================================
-
- >>> import sys
- >>> import re2 as re
- >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
- >>> a = u'\u6211\u5f88\u597d'
- >>> c = re.compile(a[0])
- >>> c.search(a).group() == u'\u6211'
- True
-
-Test unicode stickyness
-
- >>> re.sub(u'x', u'y', u'x') == u'y'
- True
- >>> re.sub(r'x', 'y', 'x') == 'y'
- True
- >>> re.findall('.', 'x') == ['x']
- True
- >>> re.findall(u'.', u'x') == [u'x']
- True
- >>> re.split(',', '1,2,3') == ['1', '2', '3']
- True
- >>> re.split(u',', u'1,2,3') == [u'1', u'2', u'3']
- True
- >>> re.search('(\\d)', '1').group(1) == '1'
- True
- >>> re.search(u'(\\d)', u'1').group(1) == u'1'
- True
-
-Test unicode character groups
-
- >>> re.search(u'\\d', u'\u0661', re.UNICODE).group(0) == u'\u0661'
- True
- >>> int(re.search(u'\\d', u'\u0661', re.UNICODE).group(0)) == 1
- True
- >>> (re.search(u'\\w', u'\u0401') is None) == (sys.version_info[0] == 2)
- True
- >>> re.search(u'\\w', u'\u0401', re.UNICODE).group(0) == u'\u0401'
- True
- >>> re.search(u'\\s', u'\u1680', re.UNICODE).group(0) == u'\u1680'
- True
- >>> re.findall(r'[\s\d\w]', 'hey 123', re.UNICODE) == ['h', 'e', 'y', ' ', '1', '2', '3']
- True
- >>> re.search(u'\\D', u'\u0661x', re.UNICODE).group(0) == u'x'
- True
- >>> re.search(u'\\W', u'\u0401!', re.UNICODE).group(0) == u'!'
- True
- >>> re.search(u'\\S', u'\u1680x', re.UNICODE).group(0) == u'x'
- True
- >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
- >>> re.search(u'[\\W]', u'\u0401!', re.UNICODE).group(0) == u'!'
- True
- >>> re.search(u'[\\S]', u'\u1680x', re.UNICODE).group(0) == u'x'
- True
- >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
-
-
-Positions are translated transparently between unicode and UTF-8
-
- >>> re.search(u' (.)', u'\U0001d200xxx\u1234 x').span(1)
- (6, 7)
- >>> re.search(b' (.)', u'\U0001d200xxx\u1234 x'.encode('utf-8')).span(1)
- (11, 12)
- >>> re.compile(u'x').findall(u'\u1234x', 1, 2) == [u'x']
- True
- >>> data = u'\U0001d200xxx\u1234 x'
- >>> re.search(u' (.)', data).string == data
- True
-
- >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/ya.make b/contrib/python/pyre2/py3/ya.make
deleted file mode 100644
index 920808ff57..0000000000
--- a/contrib/python/pyre2/py3/ya.make
+++ /dev/null
@@ -1,39 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(0.3.6)
-
-LICENSE(BSD-3-Clause)
-
-PEERDIR(
- contrib/libs/re2
-)
-
-ADDINCL(
- contrib/python/pyre2/py3/src
-)
-
-NO_COMPILER_WARNINGS()
-
-NO_LINT()
-
-SRCDIR(contrib/python/pyre2/py3/src)
-
-PY_SRCS(
- TOP_LEVEL
- CYTHON_CPP
- re2.pyx
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/pyre2/py3/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
-END()
-
-RECURSE_FOR_TESTS(
- tests
-)
diff --git a/contrib/python/pyre2/ya.make b/contrib/python/pyre2/ya.make
deleted file mode 100644
index 52c15dd0c8..0000000000
--- a/contrib/python/pyre2/ya.make
+++ /dev/null
@@ -1,18 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/pyre2/py2)
-ELSE()
- PEERDIR(contrib/python/pyre2/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- py2
- py3
-)
diff --git a/contrib/python/python-magic/py2/LICENSE b/contrib/python/python-magic/py2/LICENSE
deleted file mode 100644
index b8ca4b96dd..0000000000
--- a/contrib/python/python-magic/py2/LICENSE
+++ /dev/null
@@ -1,58 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2001-2014 Adam Hupp
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-====
-
-Portions of this package (magic/compat.py and test/libmagic_test.py)
-are distributed under the following copyright notice:
-
-
-$File: LEGAL.NOTICE,v 1.15 2006/05/03 18:48:33 christos Exp $
-Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
-Software written by Ian F. Darwin and others;
-maintained 1994- Christos Zoulas.
-
-This software is not subject to any export provision of the United States
-Department of Commerce, and may be exported to any country or planet.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice immediately at the beginning of the file, without modification,
- this list of conditions, and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
diff --git a/contrib/python/python-magic/py2/README.md b/contrib/python/python-magic/py2/README.md
deleted file mode 100644
index 9eb70e8a30..0000000000
--- a/contrib/python/python-magic/py2/README.md
+++ /dev/null
@@ -1,144 +0,0 @@
-# python-magic
-[![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
-[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-python-magic is a Python interface to the libmagic file type
-identification library. libmagic identifies file types by checking
-their headers according to a predefined list of file types. This
-functionality is exposed to the command line by the Unix command
-`file`.
-
-## Usage
-
-```python
->>> import magic
->>> magic.from_file("testdata/test.pdf")
-'PDF document, version 1.2'
-# recommend using at least the first 2048 bytes, as less can produce incorrect identification
->>> magic.from_buffer(open("testdata/test.pdf", "rb").read(2048))
-'PDF document, version 1.2'
->>> magic.from_file("testdata/test.pdf", mime=True)
-'application/pdf'
-```
-
-There is also a `Magic` class that provides more direct control,
-including overriding the magic database file and turning on character
-encoding detection. This is not recommended for general use. In
-particular, it's not safe for sharing across multiple threads and
-will fail throw if this is attempted.
-
-```python
->>> f = magic.Magic(uncompress=True)
->>> f.from_file('testdata/test.gz')
-'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
-21:32:52 2008, from Unix)'
-```
-
-You can also combine the flag options:
-
-```python
->>> f = magic.Magic(mime=True, uncompress=True)
->>> f.from_file('testdata/test.gz')
-'text/plain'
-```
-
-## Installation
-
-The current stable version of python-magic is available on PyPI and
-can be installed by running `pip install python-magic`.
-
-Other sources:
-
-- PyPI: http://pypi.python.org/pypi/python-magic/
-- GitHub: https://github.com/ahupp/python-magic
-
-This module is a simple wrapper around the libmagic C library, and
-that must be installed as well:
-
-### Debian/Ubuntu
-
-```
-sudo apt-get install libmagic1
-```
-
-### Windows
-
-You'll need DLLs for libmagic. @julian-r maintains a pypi package with the DLLs, you can fetch it with:
-
-```
-pip install python-magic-bin
-```
-
-### OSX
-
-- When using Homebrew: `brew install libmagic`
-- When using macports: `port install file`
-
-### Troubleshooting
-
-- 'MagicException: could not find any magic files!': some
- installations of libmagic do not correctly point to their magic
- database file. Try specifying the path to the file explicitly in the
- constructor: `magic.Magic(magic_file="path_to_magic_file")`.
-
-- 'WindowsError: [Error 193] %1 is not a valid Win32 application':
- Attempting to run the 32-bit libmagic DLL in a 64-bit build of
- python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64.
- Newer version can be found here: https://github.com/nscaife/file-windows.
-
-- 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
- Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
-
-
-## Bug Reports
-
-python-magic is a thin layer over the libmagic C library.
-Historically, most bugs that have been reported against python-magic
-are actually bugs in libmagic; libmagic bugs can be reported on their
-tracker here: https://bugs.astron.com/my_view_page.php. If you're not
-sure where the bug lies feel free to file an issue on GitHub and I can
-triage it.
-
-## Running the tests
-
-To run the tests across a variety of linux distributions (depends on Docker):
-
-```
-./test_docker.sh
-```
-
-To run tests locally across all available python versions:
-
-```
-./test/run.py
-```
-
-To run against a specific python version:
-
-```
-LC_ALL=en_US.UTF-8 python3 test/test.py
-```
-
-## libmagic python API compatibility
-
-The python bindings shipped with libmagic use a module name that conflicts with this package. To work around this, python-magic includes a compatibility layer for the libmagic API. See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
-
-## Versioning
-
-Minor version bumps should be backwards compatible. Major bumps are not.
-
-## Author
-
-Written by Adam Hupp in 2001 for a project that never got off the
-ground. It originally used SWIG for the C library bindings, but
-switched to ctypes once that was part of the python standard library.
-
-You can contact me via my [website](http://hupp.org/adam) or
-[GitHub](http://github.com/ahupp).
-
-## License
-
-python-magic is distributed under the MIT license. See the included
-LICENSE file for details.
-
-I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer (Facebook).
diff --git a/contrib/python/python-magic/py3/.dist-info/METADATA b/contrib/python/python-magic/py3/.dist-info/METADATA
deleted file mode 100644
index 65b11c5555..0000000000
--- a/contrib/python/python-magic/py3/.dist-info/METADATA
+++ /dev/null
@@ -1,171 +0,0 @@
-Metadata-Version: 2.1
-Name: python-magic
-Version: 0.4.27
-Summary: File type identification using libmagic
-Home-page: http://github.com/ahupp/python-magic
-Author: Adam Hupp
-Author-email: adam@hupp.org
-License: MIT
-Keywords: mime magic file
-Platform: UNKNOWN
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
-Description-Content-Type: text/markdown
-License-File: LICENSE
-
-# python-magic
-[![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
-[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-python-magic is a Python interface to the libmagic file type
-identification library. libmagic identifies file types by checking
-their headers according to a predefined list of file types. This
-functionality is exposed to the command line by the Unix command
-`file`.
-
-## Usage
-
-```python
->>> import magic
->>> magic.from_file("testdata/test.pdf")
-'PDF document, version 1.2'
-# recommend using at least the first 2048 bytes, as less can produce incorrect identification
->>> magic.from_buffer(open("testdata/test.pdf", "rb").read(2048))
-'PDF document, version 1.2'
->>> magic.from_file("testdata/test.pdf", mime=True)
-'application/pdf'
-```
-
-There is also a `Magic` class that provides more direct control,
-including overriding the magic database file and turning on character
-encoding detection. This is not recommended for general use. In
-particular, it's not safe for sharing across multiple threads and
-will fail throw if this is attempted.
-
-```python
->>> f = magic.Magic(uncompress=True)
->>> f.from_file('testdata/test.gz')
-'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
-21:32:52 2008, from Unix)'
-```
-
-You can also combine the flag options:
-
-```python
->>> f = magic.Magic(mime=True, uncompress=True)
->>> f.from_file('testdata/test.gz')
-'text/plain'
-```
-
-## Installation
-
-The current stable version of python-magic is available on PyPI and
-can be installed by running `pip install python-magic`.
-
-Other sources:
-
-- PyPI: http://pypi.python.org/pypi/python-magic/
-- GitHub: https://github.com/ahupp/python-magic
-
-This module is a simple wrapper around the libmagic C library, and
-that must be installed as well:
-
-### Debian/Ubuntu
-
-```
-sudo apt-get install libmagic1
-```
-
-### Windows
-
-You'll need DLLs for libmagic. @julian-r maintains a pypi package with the DLLs, you can fetch it with:
-
-```
-pip install python-magic-bin
-```
-
-### OSX
-
-- When using Homebrew: `brew install libmagic`
-- When using macports: `port install file`
-
-### Troubleshooting
-
-- 'MagicException: could not find any magic files!': some
- installations of libmagic do not correctly point to their magic
- database file. Try specifying the path to the file explicitly in the
- constructor: `magic.Magic(magic_file="path_to_magic_file")`.
-
-- 'WindowsError: [Error 193] %1 is not a valid Win32 application':
- Attempting to run the 32-bit libmagic DLL in a 64-bit build of
- python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64.
- Newer version can be found here: https://github.com/nscaife/file-windows.
-
-- 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
- Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
-
-
-## Bug Reports
-
-python-magic is a thin layer over the libmagic C library.
-Historically, most bugs that have been reported against python-magic
-are actually bugs in libmagic; libmagic bugs can be reported on their
-tracker here: https://bugs.astron.com/my_view_page.php. If you're not
-sure where the bug lies feel free to file an issue on GitHub and I can
-triage it.
-
-## Running the tests
-
-To run the tests across a variety of linux distributions (depends on Docker):
-
-```
-./test_docker.sh
-```
-
-To run tests locally across all available python versions:
-
-```
-./test/run.py
-```
-
-To run against a specific python version:
-
-```
-LC_ALL=en_US.UTF-8 python3 test/test.py
-```
-
-## libmagic python API compatibility
-
-The python bindings shipped with libmagic use a module name that conflicts with this package. To work around this, python-magic includes a compatibility layer for the libmagic API. See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
-
-## Versioning
-
-Minor version bumps should be backwards compatible. Major bumps are not.
-
-## Author
-
-Written by Adam Hupp in 2001 for a project that never got off the
-ground. It originally used SWIG for the C library bindings, but
-switched to ctypes once that was part of the python standard library.
-
-You can contact me via my [website](http://hupp.org/adam) or
-[GitHub](http://github.com/ahupp).
-
-## License
-
-python-magic is distributed under the MIT license. See the included
-LICENSE file for details.
-
-I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer (Facebook).
-
-
diff --git a/contrib/python/python-magic/py3/.dist-info/top_level.txt b/contrib/python/python-magic/py3/.dist-info/top_level.txt
deleted file mode 100644
index 71c947b02f..0000000000
--- a/contrib/python/python-magic/py3/.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-magic
diff --git a/contrib/python/python-magic/py3/LICENSE b/contrib/python/python-magic/py3/LICENSE
deleted file mode 100644
index b8ca4b96dd..0000000000
--- a/contrib/python/python-magic/py3/LICENSE
+++ /dev/null
@@ -1,58 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2001-2014 Adam Hupp
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-====
-
-Portions of this package (magic/compat.py and test/libmagic_test.py)
-are distributed under the following copyright notice:
-
-
-$File: LEGAL.NOTICE,v 1.15 2006/05/03 18:48:33 christos Exp $
-Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
-Software written by Ian F. Darwin and others;
-maintained 1994- Christos Zoulas.
-
-This software is not subject to any export provision of the United States
-Department of Commerce, and may be exported to any country or planet.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice immediately at the beginning of the file, without modification,
- this list of conditions, and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
diff --git a/contrib/python/python-magic/py3/README.md b/contrib/python/python-magic/py3/README.md
deleted file mode 100644
index 9eb70e8a30..0000000000
--- a/contrib/python/python-magic/py3/README.md
+++ /dev/null
@@ -1,144 +0,0 @@
-# python-magic
-[![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
-[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-python-magic is a Python interface to the libmagic file type
-identification library. libmagic identifies file types by checking
-their headers according to a predefined list of file types. This
-functionality is exposed to the command line by the Unix command
-`file`.
-
-## Usage
-
-```python
->>> import magic
->>> magic.from_file("testdata/test.pdf")
-'PDF document, version 1.2'
-# recommend using at least the first 2048 bytes, as less can produce incorrect identification
->>> magic.from_buffer(open("testdata/test.pdf", "rb").read(2048))
-'PDF document, version 1.2'
->>> magic.from_file("testdata/test.pdf", mime=True)
-'application/pdf'
-```
-
-There is also a `Magic` class that provides more direct control,
-including overriding the magic database file and turning on character
-encoding detection. This is not recommended for general use. In
-particular, it's not safe for sharing across multiple threads and
-will fail throw if this is attempted.
-
-```python
->>> f = magic.Magic(uncompress=True)
->>> f.from_file('testdata/test.gz')
-'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
-21:32:52 2008, from Unix)'
-```
-
-You can also combine the flag options:
-
-```python
->>> f = magic.Magic(mime=True, uncompress=True)
->>> f.from_file('testdata/test.gz')
-'text/plain'
-```
-
-## Installation
-
-The current stable version of python-magic is available on PyPI and
-can be installed by running `pip install python-magic`.
-
-Other sources:
-
-- PyPI: http://pypi.python.org/pypi/python-magic/
-- GitHub: https://github.com/ahupp/python-magic
-
-This module is a simple wrapper around the libmagic C library, and
-that must be installed as well:
-
-### Debian/Ubuntu
-
-```
-sudo apt-get install libmagic1
-```
-
-### Windows
-
-You'll need DLLs for libmagic. @julian-r maintains a pypi package with the DLLs, you can fetch it with:
-
-```
-pip install python-magic-bin
-```
-
-### OSX
-
-- When using Homebrew: `brew install libmagic`
-- When using macports: `port install file`
-
-### Troubleshooting
-
-- 'MagicException: could not find any magic files!': some
- installations of libmagic do not correctly point to their magic
- database file. Try specifying the path to the file explicitly in the
- constructor: `magic.Magic(magic_file="path_to_magic_file")`.
-
-- 'WindowsError: [Error 193] %1 is not a valid Win32 application':
- Attempting to run the 32-bit libmagic DLL in a 64-bit build of
- python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64.
- Newer version can be found here: https://github.com/nscaife/file-windows.
-
-- 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
- Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
-
-
-## Bug Reports
-
-python-magic is a thin layer over the libmagic C library.
-Historically, most bugs that have been reported against python-magic
-are actually bugs in libmagic; libmagic bugs can be reported on their
-tracker here: https://bugs.astron.com/my_view_page.php. If you're not
-sure where the bug lies feel free to file an issue on GitHub and I can
-triage it.
-
-## Running the tests
-
-To run the tests across a variety of linux distributions (depends on Docker):
-
-```
-./test_docker.sh
-```
-
-To run tests locally across all available python versions:
-
-```
-./test/run.py
-```
-
-To run against a specific python version:
-
-```
-LC_ALL=en_US.UTF-8 python3 test/test.py
-```
-
-## libmagic python API compatibility
-
-The python bindings shipped with libmagic use a module name that conflicts with this package. To work around this, python-magic includes a compatibility layer for the libmagic API. See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
-
-## Versioning
-
-Minor version bumps should be backwards compatible. Major bumps are not.
-
-## Author
-
-Written by Adam Hupp in 2001 for a project that never got off the
-ground. It originally used SWIG for the C library bindings, but
-switched to ctypes once that was part of the python standard library.
-
-You can contact me via my [website](http://hupp.org/adam) or
-[GitHub](http://github.com/ahupp).
-
-## License
-
-python-magic is distributed under the MIT license. See the included
-LICENSE file for details.
-
-I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer (Facebook).
diff --git a/contrib/python/python-magic/py3/magic/__init__.py b/contrib/python/python-magic/py3/magic/__init__.py
deleted file mode 100644
index bab7c7b122..0000000000
--- a/contrib/python/python-magic/py3/magic/__init__.py
+++ /dev/null
@@ -1,469 +0,0 @@
-"""
-magic is a wrapper around the libmagic file identification library.
-
-See README for more information.
-
-Usage:
-
->>> import magic
->>> magic.from_file("testdata/test.pdf")
-'PDF document, version 1.2'
->>> magic.from_file("testdata/test.pdf", mime=True)
-'application/pdf'
->>> magic.from_buffer(open("testdata/test.pdf").read(1024))
-'PDF document, version 1.2'
->>>
-
-"""
-
-import sys
-import glob
-import ctypes
-import ctypes.util
-import threading
-import logging
-
-from ctypes import c_char_p, c_int, c_size_t, c_void_p, byref, POINTER
-
-# avoid shadowing the real open with the version from compat.py
-_real_open = open
-
-
-class MagicException(Exception):
- def __init__(self, message):
- super(Exception, self).__init__(message)
- self.message = message
-
-
-class Magic:
- """
- Magic is a wrapper around the libmagic C library.
- """
-
- def __init__(self, mime=False, magic_file=None, mime_encoding=False,
- keep_going=False, uncompress=False, raw=False, extension=False):
- """
- Create a new libmagic wrapper.
-
- mime - if True, mimetypes are returned instead of textual descriptions
- mime_encoding - if True, codec is returned
- magic_file - use a mime database other than the system default
- keep_going - don't stop at the first match, keep going
- uncompress - Try to look inside compressed files.
- raw - Do not try to decode "non-printable" chars.
- extension - Print a slash-separated list of valid extensions for the file type found.
- """
- self.flags = MAGIC_NONE
- if mime:
- self.flags |= MAGIC_MIME_TYPE
- if mime_encoding:
- self.flags |= MAGIC_MIME_ENCODING
- if keep_going:
- self.flags |= MAGIC_CONTINUE
- if uncompress:
- self.flags |= MAGIC_COMPRESS
- if raw:
- self.flags |= MAGIC_RAW
- if extension:
- self.flags |= MAGIC_EXTENSION
-
- self.cookie = magic_open(self.flags)
- self.lock = threading.Lock()
-
- magic_load(self.cookie, magic_file)
-
- # MAGIC_EXTENSION was added in 523 or 524, so bail if
- # it doesn't appear to be available
- if extension and (not _has_version or version() < 524):
- raise NotImplementedError('MAGIC_EXTENSION is not supported in this version of libmagic')
-
- # For https://github.com/ahupp/python-magic/issues/190
- # libmagic has fixed internal limits that some files exceed, causing
- # an error. We can avoid this (at least for the sample file given)
- # by bumping the limit up. It's not clear if this is a general solution
- # or whether other internal limits should be increased, but given
- # the lack of other reports I'll assume this is rare.
- if _has_param:
- try:
- self.setparam(MAGIC_PARAM_NAME_MAX, 64)
- except MagicException as e:
- # some versions of libmagic fail this call,
- # so rather than fail hard just use default behavior
- pass
-
- def from_buffer(self, buf):
- """
- Identify the contents of `buf`
- """
- with self.lock:
- try:
- # if we're on python3, convert buf to bytes
- # otherwise this string is passed as wchar*
- # which is not what libmagic expects
- # NEXTBREAK: only take bytes
- if type(buf) == str and str != bytes:
- buf = buf.encode('utf-8', errors='replace')
- return maybe_decode(magic_buffer(self.cookie, buf))
- except MagicException as e:
- return self._handle509Bug(e)
-
- def from_file(self, filename):
- # raise FileNotFoundException or IOError if the file does not exist
- with _real_open(filename):
- pass
-
- with self.lock:
- try:
- return maybe_decode(magic_file(self.cookie, filename))
- except MagicException as e:
- return self._handle509Bug(e)
-
- def from_descriptor(self, fd):
- with self.lock:
- try:
- return maybe_decode(magic_descriptor(self.cookie, fd))
- except MagicException as e:
- return self._handle509Bug(e)
-
- def _handle509Bug(self, e):
- # libmagic 5.09 has a bug where it might fail to identify the
- # mimetype of a file and returns null from magic_file (and
- # likely _buffer), but also does not return an error message.
- if e.message is None and (self.flags & MAGIC_MIME_TYPE):
- return "application/octet-stream"
- else:
- raise e
-
- def setparam(self, param, val):
- return magic_setparam(self.cookie, param, val)
-
- def getparam(self, param):
- return magic_getparam(self.cookie, param)
-
- def __del__(self):
- # no _thread_check here because there can be no other
- # references to this object at this point.
-
- # during shutdown magic_close may have been cleared already so
- # make sure it exists before using it.
-
- # the self.cookie check should be unnecessary and was an
- # incorrect fix for a threading problem, however I'm leaving
- # it in because it's harmless and I'm slightly afraid to
- # remove it.
- if hasattr(self, 'cookie') and self.cookie and magic_close:
- magic_close(self.cookie)
- self.cookie = None
-
-
-_instances = {}
-
-
-def _get_magic_type(mime):
- i = _instances.get(mime)
- if i is None:
- i = _instances[mime] = Magic(mime=mime)
- return i
-
-
-def from_file(filename, mime=False):
- """"
- Accepts a filename and returns the detected filetype. Return
- value is the mimetype if mime=True, otherwise a human readable
- name.
-
- >>> magic.from_file("testdata/test.pdf", mime=True)
- 'application/pdf'
- """
- m = _get_magic_type(mime)
- return m.from_file(filename)
-
-
-def from_buffer(buffer, mime=False):
- """
- Accepts a binary string and returns the detected filetype. Return
- value is the mimetype if mime=True, otherwise a human readable
- name.
-
- >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
- 'PDF document, version 1.2'
- """
- m = _get_magic_type(mime)
- return m.from_buffer(buffer)
-
-
-def from_descriptor(fd, mime=False):
- """
- Accepts a file descriptor and returns the detected filetype. Return
- value is the mimetype if mime=True, otherwise a human readable
- name.
-
- >>> f = open("testdata/test.pdf")
- >>> magic.from_descriptor(f.fileno())
- 'PDF document, version 1.2'
- """
- m = _get_magic_type(mime)
- return m.from_descriptor(fd)
-
-from . import loader
-libmagic = loader.load_lib()
-
-magic_t = ctypes.c_void_p
-
-
-def errorcheck_null(result, func, args):
- if result is None:
- err = magic_error(args[0])
- raise MagicException(err)
- else:
- return result
-
-
-def errorcheck_negative_one(result, func, args):
- if result == -1:
- err = magic_error(args[0])
- raise MagicException(err)
- else:
- return result
-
-
-# return str on python3. Don't want to unconditionally
-# decode because that results in unicode on python2
-def maybe_decode(s):
- # NEXTBREAK: remove
- if str == bytes:
- return s
- else:
- # backslashreplace here because sometimes libmagic will return metadata in the charset
- # of the file, which is unknown to us (e.g the title of a Word doc)
- return s.decode('utf-8', 'backslashreplace')
-
-
-try:
- from os import PathLike
- def unpath(filename):
- if isinstance(filename, PathLike):
- return filename.__fspath__()
- else:
- return filename
-except ImportError:
- def unpath(filename):
- return filename
-
-def coerce_filename(filename):
- if filename is None:
- return None
-
- filename = unpath(filename)
-
- # ctypes will implicitly convert unicode strings to bytes with
- # .encode('ascii'). If you use the filesystem encoding
- # then you'll get inconsistent behavior (crashes) depending on the user's
- # LANG environment variable
- # NEXTBREAK: remove
- is_unicode = (sys.version_info[0] <= 2 and
- isinstance(filename, unicode)) or \
- (sys.version_info[0] >= 3 and
- isinstance(filename, str))
- if is_unicode:
- return filename.encode('utf-8', 'surrogateescape')
- else:
- return filename
-
-
-magic_open = libmagic.magic_open
-magic_open.restype = magic_t
-magic_open.argtypes = [c_int]
-
-magic_close = libmagic.magic_close
-magic_close.restype = None
-magic_close.argtypes = [magic_t]
-
-magic_error = libmagic.magic_error
-magic_error.restype = c_char_p
-magic_error.argtypes = [magic_t]
-
-magic_errno = libmagic.magic_errno
-magic_errno.restype = c_int
-magic_errno.argtypes = [magic_t]
-
-_magic_file = libmagic.magic_file
-_magic_file.restype = c_char_p
-_magic_file.argtypes = [magic_t, c_char_p]
-_magic_file.errcheck = errorcheck_null
-
-
-def magic_file(cookie, filename):
- return _magic_file(cookie, coerce_filename(filename))
-
-
-_magic_buffer = libmagic.magic_buffer
-_magic_buffer.restype = c_char_p
-_magic_buffer.argtypes = [magic_t, c_void_p, c_size_t]
-_magic_buffer.errcheck = errorcheck_null
-
-
-def magic_buffer(cookie, buf):
- return _magic_buffer(cookie, buf, len(buf))
-
-
-magic_descriptor = libmagic.magic_descriptor
-magic_descriptor.restype = c_char_p
-magic_descriptor.argtypes = [magic_t, c_int]
-magic_descriptor.errcheck = errorcheck_null
-
-_magic_descriptor = libmagic.magic_descriptor
-_magic_descriptor.restype = c_char_p
-_magic_descriptor.argtypes = [magic_t, c_int]
-_magic_descriptor.errcheck = errorcheck_null
-
-
-def magic_descriptor(cookie, fd):
- return _magic_descriptor(cookie, fd)
-
-
-_magic_load = libmagic.magic_load
-_magic_load.restype = c_int
-_magic_load.argtypes = [magic_t, c_char_p]
-_magic_load.errcheck = errorcheck_negative_one
-
-
-def magic_load(cookie, filename):
- return _magic_load(cookie, coerce_filename(filename))
-
-
-magic_setflags = libmagic.magic_setflags
-magic_setflags.restype = c_int
-magic_setflags.argtypes = [magic_t, c_int]
-
-magic_check = libmagic.magic_check
-magic_check.restype = c_int
-magic_check.argtypes = [magic_t, c_char_p]
-
-magic_compile = libmagic.magic_compile
-magic_compile.restype = c_int
-magic_compile.argtypes = [magic_t, c_char_p]
-
-_has_param = False
-if hasattr(libmagic, 'magic_setparam') and hasattr(libmagic, 'magic_getparam'):
- _has_param = True
- _magic_setparam = libmagic.magic_setparam
- _magic_setparam.restype = c_int
- _magic_setparam.argtypes = [magic_t, c_int, POINTER(c_size_t)]
- _magic_setparam.errcheck = errorcheck_negative_one
-
- _magic_getparam = libmagic.magic_getparam
- _magic_getparam.restype = c_int
- _magic_getparam.argtypes = [magic_t, c_int, POINTER(c_size_t)]
- _magic_getparam.errcheck = errorcheck_negative_one
-
-
-def magic_setparam(cookie, param, val):
- if not _has_param:
- raise NotImplementedError("magic_setparam not implemented")
- v = c_size_t(val)
- return _magic_setparam(cookie, param, byref(v))
-
-
-def magic_getparam(cookie, param):
- if not _has_param:
- raise NotImplementedError("magic_getparam not implemented")
- val = c_size_t()
- _magic_getparam(cookie, param, byref(val))
- return val.value
-
-
-_has_version = False
-if hasattr(libmagic, "magic_version"):
- _has_version = True
- magic_version = libmagic.magic_version
- magic_version.restype = c_int
- magic_version.argtypes = []
-
-
-def version():
- if not _has_version:
- raise NotImplementedError("magic_version not implemented")
- return magic_version()
-
-
-MAGIC_NONE = 0x000000 # No flags
-MAGIC_DEBUG = 0x000001 # Turn on debugging
-MAGIC_SYMLINK = 0x000002 # Follow symlinks
-MAGIC_COMPRESS = 0x000004 # Check inside compressed files
-MAGIC_DEVICES = 0x000008 # Look at the contents of devices
-MAGIC_MIME_TYPE = 0x000010 # Return a mime string
-MAGIC_MIME_ENCODING = 0x000400 # Return the MIME encoding
-# TODO: should be
-# MAGIC_MIME = MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING
-MAGIC_MIME = 0x000010 # Return a mime string
-MAGIC_EXTENSION = 0x1000000 # Return a /-separated list of extensions
-
-MAGIC_CONTINUE = 0x000020 # Return all matches
-MAGIC_CHECK = 0x000040 # Print warnings to stderr
-MAGIC_PRESERVE_ATIME = 0x000080 # Restore access time on exit
-MAGIC_RAW = 0x000100 # Don't translate unprintable chars
-MAGIC_ERROR = 0x000200 # Handle ENOENT etc as real errors
-
-MAGIC_NO_CHECK_COMPRESS = 0x001000 # Don't check for compressed files
-MAGIC_NO_CHECK_TAR = 0x002000 # Don't check for tar files
-MAGIC_NO_CHECK_SOFT = 0x004000 # Don't check magic entries
-MAGIC_NO_CHECK_APPTYPE = 0x008000 # Don't check application type
-MAGIC_NO_CHECK_ELF = 0x010000 # Don't check for elf details
-MAGIC_NO_CHECK_ASCII = 0x020000 # Don't check for ascii files
-MAGIC_NO_CHECK_TROFF = 0x040000 # Don't check ascii/troff
-MAGIC_NO_CHECK_FORTRAN = 0x080000 # Don't check ascii/fortran
-MAGIC_NO_CHECK_TOKENS = 0x100000 # Don't check ascii/tokens
-
-MAGIC_PARAM_INDIR_MAX = 0 # Recursion limit for indirect magic
-MAGIC_PARAM_NAME_MAX = 1 # Use count limit for name/use magic
-MAGIC_PARAM_ELF_PHNUM_MAX = 2 # Max ELF notes processed
-MAGIC_PARAM_ELF_SHNUM_MAX = 3 # Max ELF program sections processed
-MAGIC_PARAM_ELF_NOTES_MAX = 4 # # Max ELF sections processed
-MAGIC_PARAM_REGEX_MAX = 5 # Length limit for regex searches
-MAGIC_PARAM_BYTES_MAX = 6 # Max number of bytes to read from file
-
-
-# This package name conflicts with the one provided by upstream
-# libmagic. This is a common source of confusion for users. To
-# resolve, We ship a copy of that module, and expose it's functions
-# wrapped in deprecation warnings.
-def _add_compat(to_module):
- import warnings, re
- from magic import compat
-
- def deprecation_wrapper(fn):
- def _(*args, **kwargs):
- warnings.warn(
- "Using compatibility mode with libmagic's python binding. "
- "See https://github.com/ahupp/python-magic/blob/master/COMPAT.md for details.",
- PendingDeprecationWarning)
-
- return fn(*args, **kwargs)
-
- return _
-
- fn = ['detect_from_filename',
- 'detect_from_content',
- 'detect_from_fobj',
- 'open']
- for fname in fn:
- to_module[fname] = deprecation_wrapper(compat.__dict__[fname])
-
- # copy constants over, ensuring there's no conflicts
- is_const_re = re.compile("^[A-Z_]+$")
- allowed_inconsistent = set(['MAGIC_MIME'])
- for name, value in compat.__dict__.items():
- if is_const_re.match(name):
- if name in to_module:
- if name in allowed_inconsistent:
- continue
- if to_module[name] != value:
- raise Exception("inconsistent value for " + name)
- else:
- continue
- else:
- to_module[name] = value
-
-
-_add_compat(globals())
diff --git a/contrib/python/python-magic/py3/magic/compat.py b/contrib/python/python-magic/py3/magic/compat.py
deleted file mode 100644
index 07fad45a8b..0000000000
--- a/contrib/python/python-magic/py3/magic/compat.py
+++ /dev/null
@@ -1,287 +0,0 @@
-# coding: utf-8
-
-'''
-Python bindings for libmagic
-'''
-
-import ctypes
-
-from collections import namedtuple
-
-from ctypes import *
-from ctypes.util import find_library
-
-
-from . import loader
-
-_libraries = {}
-_libraries['magic'] = loader.load_lib()
-
-# Flag constants for open and setflags
-MAGIC_NONE = NONE = 0
-MAGIC_DEBUG = DEBUG = 1
-MAGIC_SYMLINK = SYMLINK = 2
-MAGIC_COMPRESS = COMPRESS = 4
-MAGIC_DEVICES = DEVICES = 8
-MAGIC_MIME_TYPE = MIME_TYPE = 16
-MAGIC_CONTINUE = CONTINUE = 32
-MAGIC_CHECK = CHECK = 64
-MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128
-MAGIC_RAW = RAW = 256
-MAGIC_ERROR = ERROR = 512
-MAGIC_MIME_ENCODING = MIME_ENCODING = 1024
-MAGIC_MIME = MIME = 1040 # MIME_TYPE + MIME_ENCODING
-MAGIC_APPLE = APPLE = 2048
-
-MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096
-MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192
-MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384
-MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768
-MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536
-MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072
-MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144
-MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576
-MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152
-
-MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824
-
-FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
-
-
-class magic_set(Structure):
- pass
-
-
-magic_set._fields_ = []
-magic_t = POINTER(magic_set)
-
-_open = _libraries['magic'].magic_open
-_open.restype = magic_t
-_open.argtypes = [c_int]
-
-_close = _libraries['magic'].magic_close
-_close.restype = None
-_close.argtypes = [magic_t]
-
-_file = _libraries['magic'].magic_file
-_file.restype = c_char_p
-_file.argtypes = [magic_t, c_char_p]
-
-_descriptor = _libraries['magic'].magic_descriptor
-_descriptor.restype = c_char_p
-_descriptor.argtypes = [magic_t, c_int]
-
-_buffer = _libraries['magic'].magic_buffer
-_buffer.restype = c_char_p
-_buffer.argtypes = [magic_t, c_void_p, c_size_t]
-
-_error = _libraries['magic'].magic_error
-_error.restype = c_char_p
-_error.argtypes = [magic_t]
-
-_setflags = _libraries['magic'].magic_setflags
-_setflags.restype = c_int
-_setflags.argtypes = [magic_t, c_int]
-
-_load = _libraries['magic'].magic_load
-_load.restype = c_int
-_load.argtypes = [magic_t, c_char_p]
-
-_compile = _libraries['magic'].magic_compile
-_compile.restype = c_int
-_compile.argtypes = [magic_t, c_char_p]
-
-_check = _libraries['magic'].magic_check
-_check.restype = c_int
-_check.argtypes = [magic_t, c_char_p]
-
-_list = _libraries['magic'].magic_list
-_list.restype = c_int
-_list.argtypes = [magic_t, c_char_p]
-
-_errno = _libraries['magic'].magic_errno
-_errno.restype = c_int
-_errno.argtypes = [magic_t]
-
-
-class Magic(object):
- def __init__(self, ms):
- self._magic_t = ms
-
- def close(self):
- """
- Closes the magic database and deallocates any resources used.
- """
- _close(self._magic_t)
-
- @staticmethod
- def __tostr(s):
- if s is None:
- return None
- if isinstance(s, str):
- return s
- try: # keep Python 2 compatibility
- return str(s, 'utf-8')
- except TypeError:
- return str(s)
-
- @staticmethod
- def __tobytes(b):
- if b is None:
- return None
- if isinstance(b, bytes):
- return b
- try: # keep Python 2 compatibility
- return bytes(b, 'utf-8')
- except TypeError:
- return bytes(b)
-
- def file(self, filename):
- """
- Returns a textual description of the contents of the argument passed
- as a filename or None if an error occurred and the MAGIC_ERROR flag
- is set. A call to errno() will return the numeric error code.
- """
- return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename)))
-
- def descriptor(self, fd):
- """
- Returns a textual description of the contents of the argument passed
- as a file descriptor or None if an error occurred and the MAGIC_ERROR
- flag is set. A call to errno() will return the numeric error code.
- """
- return Magic.__tostr(_descriptor(self._magic_t, fd))
-
- def buffer(self, buf):
- """
- Returns a textual description of the contents of the argument passed
- as a buffer or None if an error occurred and the MAGIC_ERROR flag
- is set. A call to errno() will return the numeric error code.
- """
- return Magic.__tostr(_buffer(self._magic_t, buf, len(buf)))
-
- def error(self):
- """
- Returns a textual explanation of the last error or None
- if there was no error.
- """
- return Magic.__tostr(_error(self._magic_t))
-
- def setflags(self, flags):
- """
- Set flags on the magic object which determine how magic checking
- behaves; a bitwise OR of the flags described in libmagic(3), but
- without the MAGIC_ prefix.
-
- Returns -1 on systems that don't support utime(2) or utimes(2)
- when PRESERVE_ATIME is set.
- """
- return _setflags(self._magic_t, flags)
-
- def load(self, filename=None):
- """
- Must be called to load entries in the colon separated list of database
- files passed as argument or the default database file if no argument
- before any magic queries can be performed.
-
- Returns 0 on success and -1 on failure.
- """
- return _load(self._magic_t, Magic.__tobytes(filename))
-
- def compile(self, dbs):
- """
- Compile entries in the colon separated list of database files
- passed as argument or the default database file if no argument.
- The compiled files created are named from the basename(1) of each file
- argument with ".mgc" appended to it.
-
- Returns 0 on success and -1 on failure.
- """
- return _compile(self._magic_t, Magic.__tobytes(dbs))
-
- def check(self, dbs):
- """
- Check the validity of entries in the colon separated list of
- database files passed as argument or the default database file
- if no argument.
-
- Returns 0 on success and -1 on failure.
- """
- return _check(self._magic_t, Magic.__tobytes(dbs))
-
- def list(self, dbs):
- """
- Check the validity of entries in the colon separated list of
- database files passed as argument or the default database file
- if no argument.
-
- Returns 0 on success and -1 on failure.
- """
- return _list(self._magic_t, Magic.__tobytes(dbs))
-
- def errno(self):
- """
- Returns a numeric error code. If return value is 0, an internal
- magic error occurred. If return value is non-zero, the value is
- an OS error code. Use the errno module or os.strerror() can be used
- to provide detailed error information.
- """
- return _errno(self._magic_t)
-
-
-def open(flags):
- """
- Returns a magic object on success and None on failure.
- Flags argument as for setflags.
- """
- return Magic(_open(flags))
-
-
-# Objects used by `detect_from_` functions
-mime_magic = Magic(_open(MAGIC_MIME))
-mime_magic.load()
-none_magic = Magic(_open(MAGIC_NONE))
-none_magic.load()
-
-
-def _create_filemagic(mime_detected, type_detected):
- splat = mime_detected.split('; ')
- mime_type = splat[0]
- if len(splat) == 2:
- mime_encoding = splat[1]
- else:
- mime_encoding = ''
-
- return FileMagic(name=type_detected, mime_type=mime_type,
- encoding=mime_encoding.replace('charset=', ''))
-
-
-def detect_from_filename(filename):
- '''Detect mime type, encoding and file type from a filename
-
- Returns a `FileMagic` namedtuple.
- '''
-
- return _create_filemagic(mime_magic.file(filename),
- none_magic.file(filename))
-
-
-def detect_from_fobj(fobj):
- '''Detect mime type, encoding and file type from file-like object
-
- Returns a `FileMagic` namedtuple.
- '''
-
- file_descriptor = fobj.fileno()
- return _create_filemagic(mime_magic.descriptor(file_descriptor),
- none_magic.descriptor(file_descriptor))
-
-
-def detect_from_content(byte_content):
- '''Detect mime type, encoding and file type from bytes
-
- Returns a `FileMagic` namedtuple.
- '''
-
- return _create_filemagic(mime_magic.buffer(byte_content),
- none_magic.buffer(byte_content))
diff --git a/contrib/python/python-magic/py3/magic/loader.py b/contrib/python/python-magic/py3/magic/loader.py
deleted file mode 100644
index 931f16193e..0000000000
--- a/contrib/python/python-magic/py3/magic/loader.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from ctypes.util import find_library
-import ctypes
-import sys
-import glob
-import os.path
-
-def _lib_candidates():
-
- yield find_library('magic')
-
- if sys.platform == 'darwin':
-
- paths = [
- '/opt/local/lib',
- '/usr/local/lib',
- '/opt/homebrew/lib',
- ] + glob.glob('/usr/local/Cellar/libmagic/*/lib')
-
- for i in paths:
- yield os.path.join(i, 'libmagic.dylib')
-
- elif sys.platform in ('win32', 'cygwin'):
-
- prefixes = ['libmagic', 'magic1', 'cygmagic-1', 'libmagic-1', 'msys-magic-1']
-
- for i in prefixes:
- # find_library searches in %PATH% but not the current directory,
- # so look for both
- yield './%s.dll' % (i,)
- yield find_library(i)
-
- elif sys.platform == 'linux':
- # This is necessary because alpine is bad
- yield 'libmagic.so.1'
-
-
-def load_lib():
-
- for lib in _lib_candidates():
- # find_library returns None when lib not found
- if lib is None:
- continue
- try:
- return ctypes.CDLL(lib)
- except OSError:
- pass
- else:
- # It is better to raise an ImportError since we are importing magic module
- raise ImportError('failed to find libmagic. Check your installation')
-
diff --git a/contrib/python/python-magic/py3/magic/py.typed b/contrib/python/python-magic/py3/magic/py.typed
deleted file mode 100644
index e69de29bb2..0000000000
--- a/contrib/python/python-magic/py3/magic/py.typed
+++ /dev/null
diff --git a/contrib/python/python-magic/py3/ya.make b/contrib/python/python-magic/py3/ya.make
deleted file mode 100644
index f1aba820d5..0000000000
--- a/contrib/python/python-magic/py3/ya.make
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by devtools/yamaker (pypi).
-
-PY3_LIBRARY()
-
-VERSION(0.4.27)
-
-LICENSE(MIT)
-
-PEERDIR(
- contrib/libs/libmagic
- library/python/symbols/libmagic
-)
-
-NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- magic/__init__.py
- magic/__init__.pyi
- magic/compat.py
- magic/loader.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/python-magic/py3/
- .dist-info/METADATA
- .dist-info/top_level.txt
- magic/py.typed
-)
-
-END()
diff --git a/contrib/python/python-magic/ya.make b/contrib/python/python-magic/ya.make
deleted file mode 100644
index 6ed9425039..0000000000
--- a/contrib/python/python-magic/ya.make
+++ /dev/null
@@ -1,18 +0,0 @@
-PY23_LIBRARY()
-
-LICENSE(Service-Py23-Proxy)
-
-IF (PYTHON2)
- PEERDIR(contrib/python/python-magic/py2)
-ELSE()
- PEERDIR(contrib/python/python-magic/py3)
-ENDIF()
-
-NO_LINT()
-
-END()
-
-RECURSE(
- py2
- py3
-)