From 30f5c7589e6d19c387a7bea780a5fb34cf1b1e40 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Wed, 14 Jan 2026 17:03:44 -0500 Subject: [PATCH 1/4] Updated all tests/test helper files from CPython version 3.13.11 New files: * cjkencodings/ * crashers/ * leakers/ * regrtestdata/ * test_gdb/ * test_interpreters/ * test_module/ * test_tkinter/ * test_ttk/ * tkinterdata/ * tokenizedata/ * wheeldata/ * audit-tests.py * bisect_cmd.py * clinic.test.c * cov.py * curses_tests.py * dis_module.py * empty.vbs * levenshtein_examples.json * memory_watchdog.py * multibytecodec_support.py * profilee.py * pstats.pck * reperf.py * .ruff.toml * ssltests.py * test_apple.py * test_asdl_parser.py * test_clinic.py * test_codecencodings_cn.py * test_codecencodings_hk.py * test_codecencodings_iso2022.py * test_codecencodings_jp.py * test_codecencodings_kr.py * test_codecencodings_tw.py * test_codecmaps_cn.py * test_codecmaps_hk.py * test_codecmaps_jp.py * test_codecmaps_kr.py * test_codecmaps_tw.py * test_coroutines.py * test_curses.py * test_dataclasses * test_dbm_gnu.py * test_dbm_ndbm.py * test_embed.py * _test_embed_set_config.py * _test_embed_structseq.py * test_except_star.py * test_fileutils.py * test_flufl.py * test_fork1.py * test_frame.py * test_frozen.py * test_generator_stop.py * test_idle.py * test__interpchannels.py * test_interpreters * test__interpreters.py * test_lltrace.py * test__locale.py * _test_monitoring_shutdown.py * test_osx_env.py * test_pep646_syntax.py * test_perfmaps.py * test_perf_profiler.py * test_readline.py * test_sax.py * test_select.py * test_source_encoding.py * test_stable_abi_ctypes.py * test_startfile.py * test_strptime.py * test_struct.py * test_tcl.py * test_termios.py * test_threadsignals.py * test_ttk_textonly.py * test_turtle.py * test_type_annotations.py * test_type_cache.py * test_unparse.py * test_winapi.py * test_winsound.py * test_zipimport_support.py * win_console_handler.py Modified files: * test_zoneinfo/ * test_except_star.py * test_struct.py * test_winapi.py --- Lib/test/.ruff.toml | 30 + Lib/test/_test_embed_set_config.py | 291 + Lib/test/_test_embed_structseq.py | 59 + Lib/test/_test_monitoring_shutdown.py | 30 + Lib/test/audit-tests.py | 595 + Lib/test/bisect_cmd.py | 185 + Lib/test/cjkencodings/big5-utf8.txt | 9 + Lib/test/cjkencodings/big5.txt | 9 + Lib/test/cjkencodings/big5hkscs-utf8.txt | 2 + Lib/test/cjkencodings/big5hkscs.txt | 2 + Lib/test/cjkencodings/cp949-utf8.txt | 9 + Lib/test/cjkencodings/cp949.txt | 9 + Lib/test/cjkencodings/euc_jisx0213-utf8.txt | 8 + Lib/test/cjkencodings/euc_jisx0213.txt | 8 + Lib/test/cjkencodings/euc_jp-utf8.txt | 7 + Lib/test/cjkencodings/euc_jp.txt | 7 + Lib/test/cjkencodings/euc_kr-utf8.txt | 7 + Lib/test/cjkencodings/euc_kr.txt | 7 + Lib/test/cjkencodings/gb18030-utf8.txt | 15 + Lib/test/cjkencodings/gb18030.txt | 15 + Lib/test/cjkencodings/gb2312-utf8.txt | 6 + Lib/test/cjkencodings/gb2312.txt | 6 + Lib/test/cjkencodings/gbk-utf8.txt | 14 + Lib/test/cjkencodings/gbk.txt | 14 + Lib/test/cjkencodings/hz-utf8.txt | 2 + Lib/test/cjkencodings/hz.txt | 2 + Lib/test/cjkencodings/iso2022_jp-utf8.txt | 7 + Lib/test/cjkencodings/iso2022_jp.txt | 7 + Lib/test/cjkencodings/iso2022_kr-utf8.txt | 7 + Lib/test/cjkencodings/iso2022_kr.txt | 7 + Lib/test/cjkencodings/johab-utf8.txt | 9 + Lib/test/cjkencodings/johab.txt | 9 + Lib/test/cjkencodings/shift_jis-utf8.txt | 7 + Lib/test/cjkencodings/shift_jis.txt | 7 + Lib/test/cjkencodings/shift_jisx0213-utf8.txt | 8 + Lib/test/cjkencodings/shift_jisx0213.txt | 8 + Lib/test/clinic.test.c | 5796 ++ Lib/test/cov.py | 48 + Lib/test/crashers/README | 17 + Lib/test/crashers/bogus_code_obj.py | 19 + Lib/test/crashers/gc_inspection.py | 32 + Lib/test/crashers/infinite_loop_re.py | 16 + Lib/test/crashers/mutation_inside_cyclegc.py | 31 + Lib/test/crashers/recursive_call.py | 15 + Lib/test/crashers/trace_at_recursion_limit.py | 27 + Lib/test/crashers/underlying_dict.py | 20 + Lib/test/curses_tests.py | 46 + Lib/test/dis_module.py | 5 + Lib/test/empty.vbs | 1 + Lib/test/leakers/README.txt | 32 + Lib/test/leakers/__init__.py | 0 Lib/test/leakers/test_ctypes.py | 15 + Lib/test/leakers/test_selftype.py | 13 + Lib/test/levenshtein_examples.json | 50002 ++++++++++++++++ Lib/test/memory_watchdog.py | 21 + Lib/test/multibytecodec_support.py | 400 + Lib/test/profilee.py | 115 + Lib/test/pstats.pck | Bin 0 -> 66607 bytes .../import_from_tests/test_regrtest_a.py | 11 + .../test_regrtest_b/__init__.py | 9 + .../import_from_tests/test_regrtest_b/util.py | 0 .../import_from_tests/test_regrtest_c.py | 11 + Lib/test/reperf.py | 23 + Lib/test/ssltests.py | 37 + Lib/test/test__interpchannels.py | 1810 + Lib/test/test__interpreters.py | 1202 + Lib/test/test__locale.py | 310 + Lib/test/test_apple.py | 155 + Lib/test/test_asdl_parser.py | 131 + Lib/test/test_clinic.py | 4299 ++ Lib/test/test_codecencodings_cn.py | 96 + Lib/test/test_codecencodings_hk.py | 22 + Lib/test/test_codecencodings_iso2022.py | 87 + Lib/test/test_codecencodings_jp.py | 126 + Lib/test/test_codecencodings_kr.py | 69 + Lib/test/test_codecencodings_tw.py | 22 + Lib/test/test_codecmaps_cn.py | 26 + Lib/test/test_codecmaps_hk.py | 15 + Lib/test/test_codecmaps_jp.py | 60 + Lib/test/test_codecmaps_kr.py | 37 + Lib/test/test_codecmaps_tw.py | 27 + Lib/test/test_coroutines.py | 2483 + Lib/test/test_curses.py | 1432 + Lib/test/test_dataclasses/__init__.py | 2 +- Lib/test/test_dbm_gnu.py | 222 + Lib/test/test_dbm_ndbm.py | 165 + Lib/test/test_embed.py | 1995 + Lib/test/test_except_star.py | 2 +- Lib/test/test_fileutils.py | 30 + Lib/test/test_flufl.py | 65 + Lib/test/test_fork1.py | 102 + Lib/test/test_frame.py | 781 + Lib/test/test_free_threading/__init__.py | 11 + Lib/test/test_free_threading/test_bisect.py | 79 + Lib/test/test_free_threading/test_code.py | 30 + Lib/test/test_free_threading/test_dict.py | 201 + .../test_func_annotations.py | 67 + Lib/test/test_free_threading/test_gc.py | 61 + Lib/test/test_free_threading/test_list.py | 76 + .../test_free_threading/test_monitoring.py | 295 + Lib/test/test_free_threading/test_set.py | 41 + Lib/test/test_free_threading/test_slots.py | 43 + Lib/test/test_free_threading/test_str.py | 75 + Lib/test/test_free_threading/test_tokenize.py | 57 + Lib/test/test_free_threading/test_type.py | 157 + Lib/test/test_frozen.py | 56 + Lib/test/test_gdb/__init__.py | 32 + Lib/test/test_gdb/gdb_sample.py | 12 + Lib/test/test_gdb/test_backtrace.py | 135 + Lib/test/test_gdb/test_cfunction.py | 85 + Lib/test/test_gdb/test_cfunction_full.py | 36 + Lib/test/test_gdb/test_misc.py | 188 + Lib/test/test_gdb/test_pretty_print.py | 438 + Lib/test/test_gdb/util.py | 287 + Lib/test/test_generator_stop.py | 34 + Lib/test/test_idle.py | 25 + Lib/test/test_interpreters/__init__.py | 9 + Lib/test/test_interpreters/__main__.py | 4 + Lib/test/test_interpreters/test_api.py | 1672 + Lib/test/test_interpreters/test_channels.py | 600 + Lib/test/test_interpreters/test_lifecycle.py | 190 + Lib/test/test_interpreters/test_queues.py | 707 + Lib/test/test_interpreters/test_stress.py | 89 + Lib/test/test_interpreters/utils.py | 690 + Lib/test/test_lltrace.py | 111 + Lib/test/test_module/__init__.py | 41 +- Lib/test/test_module/final_a.py | 19 + Lib/test/test_module/final_b.py | 19 + Lib/test/test_osx_env.py | 34 + Lib/test/test_pep646_syntax.py | 329 + Lib/test/test_perf_profiler.py | 567 + Lib/test/test_perfmaps.py | 23 + Lib/test/test_readline.py | 415 + Lib/test/test_sax.py | 1561 + Lib/test/test_select.py | 104 + Lib/test/test_source_encoding.py | 429 + Lib/test/test_stable_abi_ctypes.py | 985 + Lib/test/test_startfile.py | 45 + Lib/test/test_strptime.py | 900 + Lib/test/test_struct.py | 2 +- Lib/test/test_tcl.py | 806 + Lib/test/test_termios.py | 276 + Lib/test/test_threadsignals.py | 237 + Lib/test/test_tkinter/README | 14 + Lib/test/test_tkinter/__init__.py | 24 + Lib/test/test_tkinter/__main__.py | 4 + Lib/test/test_tkinter/support.py | 134 + Lib/test/test_tkinter/test_colorchooser.py | 68 + Lib/test/test_tkinter/test_font.py | 163 + .../test_tkinter/test_geometry_managers.py | 908 + Lib/test/test_tkinter/test_images.py | 653 + Lib/test/test_tkinter/test_loadtk.py | 46 + Lib/test/test_tkinter/test_messagebox.py | 36 + Lib/test/test_tkinter/test_misc.py | 1334 + Lib/test/test_tkinter/test_simpledialog.py | 35 + Lib/test/test_tkinter/test_text.py | 132 + Lib/test/test_tkinter/test_variables.py | 344 + Lib/test/test_tkinter/test_widgets.py | 1514 + Lib/test/test_tkinter/widget_tests.py | 583 + Lib/test/test_ttk/__init__.py | 39 + Lib/test/test_ttk/__main__.py | 4 + Lib/test/test_ttk/test_extensions.py | 330 + Lib/test/test_ttk/test_style.py | 445 + Lib/test/test_ttk/test_widgets.py | 1972 + Lib/test/test_ttk_textonly.py | 481 + Lib/test/test_turtle.py | 546 + Lib/test/test_type_annotations.py | 214 + Lib/test/test_type_cache.py | 254 + Lib/test/test_unparse.py | 848 + Lib/test/test_winapi.py | 2 +- Lib/test/test_winsound.py | 166 + Lib/test/test_zipimport_support.py | 244 + .../test_zoneinfo/data/update_test_data.py | 122 + .../test_zoneinfo/data/zoneinfo_data.json | 378 +- Lib/test/test_zoneinfo/test_zoneinfo.py | 77 +- .../test_zoneinfo/test_zoneinfo_property.py | 16 +- Lib/test/tkinterdata/python.gif | Bin 0 -> 405 bytes Lib/test/tkinterdata/python.pgm | Bin 0 -> 269 bytes Lib/test/tkinterdata/python.png | Bin 0 -> 1020 bytes Lib/test/tkinterdata/python.ppm | Bin 0 -> 781 bytes Lib/test/tkinterdata/python.xbm | 6 + Lib/test/tokenizedata/__init__.py | 0 Lib/test/tokenizedata/bad_coding.py | 1 + Lib/test/tokenizedata/bad_coding2.py | 2 + Lib/test/tokenizedata/badsyntax_3131.py | 2 + Lib/test/tokenizedata/badsyntax_pep3120.py | 1 + Lib/test/tokenizedata/coding20731.py | 4 + ...-latin1-coding-cookie-and-utf8-bom-sig.txt | 13 + ...no-coding-cookie-and-utf8-bom-sig-only.txt | 11 + ...utf8-coding-cookie-and-no-utf8-bom-sig.txt | 13 + ...ts-utf8-coding-cookie-and-utf8-bom-sig.txt | 12 + Lib/test/tokenizedata/tokenize_tests.txt | 189 + .../setuptools-79.0.1-py3-none-any.whl | Bin 0 -> 1256281 bytes Lib/test/win_console_handler.py | 49 + 194 files changed, 99570 insertions(+), 207 deletions(-) create mode 100644 Lib/test/.ruff.toml create mode 100644 Lib/test/_test_embed_set_config.py create mode 100644 Lib/test/_test_embed_structseq.py create mode 100644 Lib/test/_test_monitoring_shutdown.py create mode 100644 Lib/test/audit-tests.py create mode 100755 Lib/test/bisect_cmd.py create mode 100644 Lib/test/cjkencodings/big5-utf8.txt create mode 100644 Lib/test/cjkencodings/big5.txt create mode 100644 Lib/test/cjkencodings/big5hkscs-utf8.txt create mode 100644 Lib/test/cjkencodings/big5hkscs.txt create mode 100644 Lib/test/cjkencodings/cp949-utf8.txt create mode 100644 Lib/test/cjkencodings/cp949.txt create mode 100644 Lib/test/cjkencodings/euc_jisx0213-utf8.txt create mode 100644 Lib/test/cjkencodings/euc_jisx0213.txt create mode 100644 Lib/test/cjkencodings/euc_jp-utf8.txt create mode 100644 Lib/test/cjkencodings/euc_jp.txt create mode 100644 Lib/test/cjkencodings/euc_kr-utf8.txt create mode 100644 Lib/test/cjkencodings/euc_kr.txt create mode 100644 Lib/test/cjkencodings/gb18030-utf8.txt create mode 100644 Lib/test/cjkencodings/gb18030.txt create mode 100644 Lib/test/cjkencodings/gb2312-utf8.txt create mode 100644 Lib/test/cjkencodings/gb2312.txt create mode 100644 Lib/test/cjkencodings/gbk-utf8.txt create mode 100644 Lib/test/cjkencodings/gbk.txt create mode 100644 Lib/test/cjkencodings/hz-utf8.txt create mode 100644 Lib/test/cjkencodings/hz.txt create mode 100644 Lib/test/cjkencodings/iso2022_jp-utf8.txt create mode 100644 Lib/test/cjkencodings/iso2022_jp.txt create mode 100644 Lib/test/cjkencodings/iso2022_kr-utf8.txt create mode 100644 Lib/test/cjkencodings/iso2022_kr.txt create mode 100644 Lib/test/cjkencodings/johab-utf8.txt create mode 100644 Lib/test/cjkencodings/johab.txt create mode 100644 Lib/test/cjkencodings/shift_jis-utf8.txt create mode 100644 Lib/test/cjkencodings/shift_jis.txt create mode 100644 Lib/test/cjkencodings/shift_jisx0213-utf8.txt create mode 100644 Lib/test/cjkencodings/shift_jisx0213.txt create mode 100644 Lib/test/clinic.test.c create mode 100644 Lib/test/cov.py create mode 100644 Lib/test/crashers/README create mode 100644 Lib/test/crashers/bogus_code_obj.py create mode 100644 Lib/test/crashers/gc_inspection.py create mode 100644 Lib/test/crashers/infinite_loop_re.py create mode 100644 Lib/test/crashers/mutation_inside_cyclegc.py create mode 100755 Lib/test/crashers/recursive_call.py create mode 100644 Lib/test/crashers/trace_at_recursion_limit.py create mode 100644 Lib/test/crashers/underlying_dict.py create mode 100755 Lib/test/curses_tests.py create mode 100644 Lib/test/dis_module.py create mode 100644 Lib/test/empty.vbs create mode 100644 Lib/test/leakers/README.txt create mode 100644 Lib/test/leakers/__init__.py create mode 100644 Lib/test/leakers/test_ctypes.py create mode 100644 Lib/test/leakers/test_selftype.py create mode 100644 Lib/test/levenshtein_examples.json create mode 100644 Lib/test/memory_watchdog.py create mode 100644 Lib/test/multibytecodec_support.py create mode 100644 Lib/test/profilee.py create mode 100644 Lib/test/pstats.pck create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_b/util.py create mode 100644 Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py create mode 100644 Lib/test/reperf.py create mode 100644 Lib/test/ssltests.py create mode 100644 Lib/test/test__interpchannels.py create mode 100644 Lib/test/test__interpreters.py create mode 100644 Lib/test/test__locale.py create mode 100644 Lib/test/test_apple.py create mode 100644 Lib/test/test_asdl_parser.py create mode 100644 Lib/test/test_clinic.py create mode 100644 Lib/test/test_codecencodings_cn.py create mode 100644 Lib/test/test_codecencodings_hk.py create mode 100644 Lib/test/test_codecencodings_iso2022.py create mode 100644 Lib/test/test_codecencodings_jp.py create mode 100644 Lib/test/test_codecencodings_kr.py create mode 100644 Lib/test/test_codecencodings_tw.py create mode 100644 Lib/test/test_codecmaps_cn.py create mode 100644 Lib/test/test_codecmaps_hk.py create mode 100644 Lib/test/test_codecmaps_jp.py create mode 100644 Lib/test/test_codecmaps_kr.py create mode 100644 Lib/test/test_codecmaps_tw.py create mode 100644 Lib/test/test_coroutines.py create mode 100644 Lib/test/test_curses.py create mode 100644 Lib/test/test_dbm_gnu.py create mode 100644 Lib/test/test_dbm_ndbm.py create mode 100644 Lib/test/test_embed.py create mode 100644 Lib/test/test_fileutils.py create mode 100644 Lib/test/test_flufl.py create mode 100644 Lib/test/test_fork1.py create mode 100644 Lib/test/test_frame.py create mode 100644 Lib/test/test_free_threading/__init__.py create mode 100644 Lib/test/test_free_threading/test_bisect.py create mode 100644 Lib/test/test_free_threading/test_code.py create mode 100644 Lib/test/test_free_threading/test_dict.py create mode 100644 Lib/test/test_free_threading/test_func_annotations.py create mode 100644 Lib/test/test_free_threading/test_gc.py create mode 100644 Lib/test/test_free_threading/test_list.py create mode 100644 Lib/test/test_free_threading/test_monitoring.py create mode 100644 Lib/test/test_free_threading/test_set.py create mode 100644 Lib/test/test_free_threading/test_slots.py create mode 100644 Lib/test/test_free_threading/test_str.py create mode 100644 Lib/test/test_free_threading/test_tokenize.py create mode 100644 Lib/test/test_free_threading/test_type.py create mode 100644 Lib/test/test_frozen.py create mode 100644 Lib/test/test_gdb/__init__.py create mode 100644 Lib/test/test_gdb/gdb_sample.py create mode 100644 Lib/test/test_gdb/test_backtrace.py create mode 100644 Lib/test/test_gdb/test_cfunction.py create mode 100644 Lib/test/test_gdb/test_cfunction_full.py create mode 100644 Lib/test/test_gdb/test_misc.py create mode 100644 Lib/test/test_gdb/test_pretty_print.py create mode 100644 Lib/test/test_gdb/util.py create mode 100644 Lib/test/test_generator_stop.py create mode 100644 Lib/test/test_idle.py create mode 100644 Lib/test/test_interpreters/__init__.py create mode 100644 Lib/test/test_interpreters/__main__.py create mode 100644 Lib/test/test_interpreters/test_api.py create mode 100644 Lib/test/test_interpreters/test_channels.py create mode 100644 Lib/test/test_interpreters/test_lifecycle.py create mode 100644 Lib/test/test_interpreters/test_queues.py create mode 100644 Lib/test/test_interpreters/test_stress.py create mode 100644 Lib/test/test_interpreters/utils.py create mode 100644 Lib/test/test_lltrace.py create mode 100644 Lib/test/test_module/final_a.py create mode 100644 Lib/test/test_module/final_b.py create mode 100644 Lib/test/test_osx_env.py create mode 100644 Lib/test/test_pep646_syntax.py create mode 100644 Lib/test/test_perf_profiler.py create mode 100644 Lib/test/test_perfmaps.py create mode 100644 Lib/test/test_readline.py create mode 100644 Lib/test/test_sax.py create mode 100644 Lib/test/test_select.py create mode 100644 Lib/test/test_source_encoding.py create mode 100644 Lib/test/test_stable_abi_ctypes.py create mode 100644 Lib/test/test_startfile.py create mode 100644 Lib/test/test_strptime.py create mode 100644 Lib/test/test_tcl.py create mode 100644 Lib/test/test_termios.py create mode 100644 Lib/test/test_threadsignals.py create mode 100644 Lib/test/test_tkinter/README create mode 100644 Lib/test/test_tkinter/__init__.py create mode 100644 Lib/test/test_tkinter/__main__.py create mode 100644 Lib/test/test_tkinter/support.py create mode 100644 Lib/test/test_tkinter/test_colorchooser.py create mode 100644 Lib/test/test_tkinter/test_font.py create mode 100644 Lib/test/test_tkinter/test_geometry_managers.py create mode 100644 Lib/test/test_tkinter/test_images.py create mode 100644 Lib/test/test_tkinter/test_loadtk.py create mode 100644 Lib/test/test_tkinter/test_messagebox.py create mode 100644 Lib/test/test_tkinter/test_misc.py create mode 100644 Lib/test/test_tkinter/test_simpledialog.py create mode 100644 Lib/test/test_tkinter/test_text.py create mode 100644 Lib/test/test_tkinter/test_variables.py create mode 100644 Lib/test/test_tkinter/test_widgets.py create mode 100644 Lib/test/test_tkinter/widget_tests.py create mode 100644 Lib/test/test_ttk/__init__.py create mode 100644 Lib/test/test_ttk/__main__.py create mode 100644 Lib/test/test_ttk/test_extensions.py create mode 100644 Lib/test/test_ttk/test_style.py create mode 100644 Lib/test/test_ttk/test_widgets.py create mode 100644 Lib/test/test_ttk_textonly.py create mode 100644 Lib/test/test_turtle.py create mode 100644 Lib/test/test_type_annotations.py create mode 100644 Lib/test/test_type_cache.py create mode 100644 Lib/test/test_unparse.py create mode 100644 Lib/test/test_winsound.py create mode 100644 Lib/test/test_zipimport_support.py create mode 100644 Lib/test/tkinterdata/python.gif create mode 100644 Lib/test/tkinterdata/python.pgm create mode 100644 Lib/test/tkinterdata/python.png create mode 100644 Lib/test/tkinterdata/python.ppm create mode 100644 Lib/test/tkinterdata/python.xbm create mode 100644 Lib/test/tokenizedata/__init__.py create mode 100644 Lib/test/tokenizedata/bad_coding.py create mode 100644 Lib/test/tokenizedata/bad_coding2.py create mode 100644 Lib/test/tokenizedata/badsyntax_3131.py create mode 100644 Lib/test/tokenizedata/badsyntax_pep3120.py create mode 100644 Lib/test/tokenizedata/coding20731.py create mode 100644 Lib/test/tokenizedata/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt create mode 100644 Lib/test/tokenizedata/tokenize_tests-no-coding-cookie-and-utf8-bom-sig-only.txt create mode 100644 Lib/test/tokenizedata/tokenize_tests-utf8-coding-cookie-and-no-utf8-bom-sig.txt create mode 100644 Lib/test/tokenizedata/tokenize_tests-utf8-coding-cookie-and-utf8-bom-sig.txt create mode 100644 Lib/test/tokenizedata/tokenize_tests.txt create mode 100644 Lib/test/wheeldata/setuptools-79.0.1-py3-none-any.whl create mode 100644 Lib/test/win_console_handler.py diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml new file mode 100644 index 00000000000..f800dc03dce --- /dev/null +++ b/Lib/test/.ruff.toml @@ -0,0 +1,30 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +target-version = "py312" + +extend-exclude = [ + # Excluded (run with the other AC files in its own separate ruff job in pre-commit) + "test_clinic.py", + # Excluded (these aren't actually executed, they're just "data files") + "tokenizedata/*.py", + # Non UTF-8 files + "encoded_modules/module_iso_8859_1.py", + "encoded_modules/module_koi8_r.py", + # New grammar constructions may not yet be recognized by Ruff, + # and tests re-use the same names as only the grammar is being checked. + "test_grammar.py", +] + +[per-file-target-version] +# Type parameter defaults +"test_type_params.py" = "py313" + +# Template string literals +"test_annotationlib.py" = "py314" +"test_string/test_templatelib.py" = "py314" +"test_tstring.py" = "py314" + +[lint] +select = [ + "F811", # Redefinition of unused variable (useful for finding test methods with the same name) +] diff --git a/Lib/test/_test_embed_set_config.py b/Lib/test/_test_embed_set_config.py new file mode 100644 index 00000000000..5ff521892cb --- /dev/null +++ b/Lib/test/_test_embed_set_config.py @@ -0,0 +1,291 @@ +# bpo-42260: Test _PyInterpreterState_GetConfigCopy() +# and _PyInterpreterState_SetConfig(). +# +# Test run in a subprocess since set_config(get_config()) +# does reset sys attributes to their state of the Python startup +# (before the site module is run). + +import _testinternalcapi +import os +import sys +import unittest +from test import support +from test.support import MS_WINDOWS + + +MAX_HASH_SEED = 4294967295 + + +BOOL_OPTIONS = [ + 'isolated', + 'use_environment', + 'dev_mode', + 'install_signal_handlers', + 'use_hash_seed', + 'faulthandler', + 'import_time', + 'code_debug_ranges', + 'show_ref_count', + 'dump_refs', + 'malloc_stats', + 'parse_argv', + 'site_import', + 'warn_default_encoding', + 'inspect', + 'interactive', + 'parser_debug', + 'write_bytecode', + 'quiet', + 'user_site_directory', + 'configure_c_stdio', + 'buffered_stdio', + 'use_frozen_modules', + 'safe_path', + 'pathconfig_warnings', + 'module_search_paths_set', + 'skip_source_first_line', + '_install_importlib', + '_init_main', + '_is_python_build', +] +if MS_WINDOWS: + BOOL_OPTIONS.append('legacy_windows_stdio') + + +class SetConfigTests(unittest.TestCase): + def setUp(self): + self.old_config = _testinternalcapi.get_config() + self.sys_copy = dict(sys.__dict__) + + def tearDown(self): + _testinternalcapi.reset_path_config() + _testinternalcapi.set_config(self.old_config) + sys.__dict__.clear() + sys.__dict__.update(self.sys_copy) + + def set_config(self, **kwargs): + _testinternalcapi.set_config(self.old_config | kwargs) + + def check(self, **kwargs): + self.set_config(**kwargs) + for key, value in kwargs.items(): + self.assertEqual(getattr(sys, key), value, + (key, value)) + + def test_set_invalid(self): + invalid_uint = -1 + NULL = None + invalid_wstr = NULL + # PyWideStringList strings must be non-NULL + invalid_wstrlist = ["abc", NULL, "def"] + + type_tests = [] + value_tests = [ + # enum + ('_config_init', 0), + ('_config_init', 4), + # unsigned long + ("hash_seed", -1), + ("hash_seed", MAX_HASH_SEED + 1), + ] + + # int (unsigned) + int_options = [ + '_config_init', + 'bytes_warning', + 'optimization_level', + 'tracemalloc', + 'verbose', + ] + int_options.extend(BOOL_OPTIONS) + for key in int_options: + value_tests.append((key, invalid_uint)) + type_tests.append((key, "abc")) + type_tests.append((key, 2.0)) + + # wchar_t* + for key in ( + 'filesystem_encoding', + 'filesystem_errors', + 'stdio_encoding', + 'stdio_errors', + 'check_hash_pycs_mode', + 'program_name', + 'platlibdir', + # optional wstr: + # 'pythonpath_env' + # 'home' + # 'pycache_prefix' + # 'run_command' + # 'run_module' + # 'run_filename' + # 'executable' + # 'prefix' + # 'exec_prefix' + # 'base_executable' + # 'base_prefix' + # 'base_exec_prefix' + ): + value_tests.append((key, invalid_wstr)) + type_tests.append((key, b'bytes')) + type_tests.append((key, 123)) + + # PyWideStringList + for key in ( + 'orig_argv', + 'argv', + 'xoptions', + 'warnoptions', + 'module_search_paths', + ): + value_tests.append((key, invalid_wstrlist)) + type_tests.append((key, 123)) + type_tests.append((key, "abc")) + type_tests.append((key, [123])) + type_tests.append((key, [b"bytes"])) + + + if MS_WINDOWS: + value_tests.append(('legacy_windows_stdio', invalid_uint)) + + for exc_type, tests in ( + (ValueError, value_tests), + (TypeError, type_tests), + ): + for key, value in tests: + config = self.old_config | {key: value} + with self.subTest(key=key, value=value, exc_type=exc_type): + with self.assertRaises(exc_type): + _testinternalcapi.set_config(config) + + def test_flags(self): + bool_options = set(BOOL_OPTIONS) + for sys_attr, key, value in ( + ("debug", "parser_debug", 1), + ("inspect", "inspect", 2), + ("interactive", "interactive", 3), + ("optimize", "optimization_level", 4), + ("verbose", "verbose", 1), + ("bytes_warning", "bytes_warning", 10), + ("quiet", "quiet", 11), + ("isolated", "isolated", 12), + ): + with self.subTest(sys=sys_attr, key=key, value=value): + self.set_config(**{key: value, 'parse_argv': 0}) + if key in bool_options: + self.assertEqual(getattr(sys.flags, sys_attr), int(bool(value))) + else: + self.assertEqual(getattr(sys.flags, sys_attr), value) + + self.set_config(write_bytecode=0) + self.assertEqual(sys.flags.dont_write_bytecode, True) + self.assertEqual(sys.dont_write_bytecode, True) + + self.set_config(write_bytecode=1) + self.assertEqual(sys.flags.dont_write_bytecode, False) + self.assertEqual(sys.dont_write_bytecode, False) + + self.set_config(user_site_directory=0, isolated=0) + self.assertEqual(sys.flags.no_user_site, 1) + self.set_config(user_site_directory=1, isolated=0) + self.assertEqual(sys.flags.no_user_site, 0) + + self.set_config(site_import=0) + self.assertEqual(sys.flags.no_site, 1) + self.set_config(site_import=1) + self.assertEqual(sys.flags.no_site, 0) + + self.set_config(dev_mode=0) + self.assertEqual(sys.flags.dev_mode, False) + self.set_config(dev_mode=1) + self.assertEqual(sys.flags.dev_mode, True) + + self.set_config(use_environment=0, isolated=0) + self.assertEqual(sys.flags.ignore_environment, 1) + self.set_config(use_environment=1, isolated=0) + self.assertEqual(sys.flags.ignore_environment, 0) + + self.set_config(use_hash_seed=1, hash_seed=0) + self.assertEqual(sys.flags.hash_randomization, 0) + self.set_config(use_hash_seed=0, hash_seed=0) + self.assertEqual(sys.flags.hash_randomization, 1) + self.set_config(use_hash_seed=1, hash_seed=123) + self.assertEqual(sys.flags.hash_randomization, 1) + + if support.Py_GIL_DISABLED: + self.set_config(enable_gil=-1) + self.assertEqual(sys.flags.gil, None) + self.set_config(enable_gil=0) + self.assertEqual(sys.flags.gil, 0) + self.set_config(enable_gil=1) + self.assertEqual(sys.flags.gil, 1) + else: + # Builds without Py_GIL_DISABLED don't have + # PyConfig.enable_gil. sys.flags.gil is always defined to 1, for + # consistency. + self.assertEqual(sys.flags.gil, 1) + + def test_options(self): + self.check(warnoptions=[]) + self.check(warnoptions=["default", "ignore"]) + + self.set_config(xoptions=[]) + self.assertEqual(sys._xoptions, {}) + self.set_config(xoptions=["dev", "tracemalloc=5"]) + self.assertEqual(sys._xoptions, {"dev": True, "tracemalloc": "5"}) + + def test_pathconfig(self): + self.check( + executable='executable', + prefix="prefix", + base_prefix="base_prefix", + exec_prefix="exec_prefix", + base_exec_prefix="base_exec_prefix", + platlibdir="platlibdir") + + self.set_config(base_executable="base_executable") + self.assertEqual(sys._base_executable, "base_executable") + + # When base_xxx is NULL, value is copied from xxxx + self.set_config( + executable='executable', + prefix="prefix", + exec_prefix="exec_prefix", + base_executable=None, + base_prefix=None, + base_exec_prefix=None) + self.assertEqual(sys._base_executable, "executable") + self.assertEqual(sys.base_prefix, "prefix") + self.assertEqual(sys.base_exec_prefix, "exec_prefix") + + def test_path(self): + self.set_config(module_search_paths_set=1, + module_search_paths=['a', 'b', 'c']) + self.assertEqual(sys.path, ['a', 'b', 'c']) + + # sys.path is reset if module_search_paths_set=0 + self.set_config(module_search_paths_set=0, + module_search_paths=['new_path']) + self.assertNotEqual(sys.path, ['a', 'b', 'c']) + self.assertNotEqual(sys.path, ['new_path']) + + def test_argv(self): + self.set_config(parse_argv=0, + argv=['python_program', 'args'], + orig_argv=['orig', 'orig_args']) + self.assertEqual(sys.argv, ['python_program', 'args']) + self.assertEqual(sys.orig_argv, ['orig', 'orig_args']) + + self.set_config(parse_argv=0, + argv=[], + orig_argv=[]) + self.assertEqual(sys.argv, ['']) + self.assertEqual(sys.orig_argv, []) + + def test_pycache_prefix(self): + self.check(pycache_prefix=None) + self.check(pycache_prefix="pycache_prefix") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/_test_embed_structseq.py b/Lib/test/_test_embed_structseq.py new file mode 100644 index 00000000000..834daa4df55 --- /dev/null +++ b/Lib/test/_test_embed_structseq.py @@ -0,0 +1,59 @@ +import sys +import types + +# Note: This test file can't import `unittest` since the runtime can't +# currently guarantee that it will not leak memory. Doing so will mark +# the test as passing but with reference leaks. This can safely import +# the `unittest` library once there's a strict guarantee of no leaks +# during runtime shutdown. + +# bpo-46417: Test that structseq types used by the sys module are still +# valid when Py_Finalize()/Py_Initialize() are called multiple times. +class TestStructSeq: + # test PyTypeObject members + def _check_structseq(self, obj_type): + # ob_refcnt + assert sys.getrefcount(obj_type) > 1 + # tp_base + assert issubclass(obj_type, tuple) + # tp_bases + assert obj_type.__bases__ == (tuple,) + # tp_dict + assert isinstance(obj_type.__dict__, types.MappingProxyType) + # tp_mro + assert obj_type.__mro__ == (obj_type, tuple, object) + # tp_name + assert isinstance(type.__name__, str) + # tp_subclasses + assert obj_type.__subclasses__() == [] + + def test_sys_attrs(self): + for attr_name in ( + 'flags', # FlagsType + 'float_info', # FloatInfoType + 'hash_info', # Hash_InfoType + 'int_info', # Int_InfoType + 'thread_info', # ThreadInfoType + 'version_info', # VersionInfoType + ): + attr = getattr(sys, attr_name) + self._check_structseq(type(attr)) + + def test_sys_funcs(self): + func_names = ['get_asyncgen_hooks'] # AsyncGenHooksType + if hasattr(sys, 'getwindowsversion'): + func_names.append('getwindowsversion') # WindowsVersionType + for func_name in func_names: + func = getattr(sys, func_name) + obj = func() + self._check_structseq(type(obj)) + + +try: + tests = TestStructSeq() + tests.test_sys_attrs() + tests.test_sys_funcs() +except SystemExit as exc: + if exc.args[0] != 0: + raise +print("Tests passed") diff --git a/Lib/test/_test_monitoring_shutdown.py b/Lib/test/_test_monitoring_shutdown.py new file mode 100644 index 00000000000..3d0fbec029d --- /dev/null +++ b/Lib/test/_test_monitoring_shutdown.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +# gh-115832: An object destructor running during the final GC of interpreter +# shutdown triggered an infinite loop in the instrumentation code. + +import sys + +class CallableCycle: + def __init__(self): + self._cycle = self + + def __del__(self): + pass + + def __call__(self, code, instruction_offset): + pass + +def tracefunc(frame, event, arg): + pass + +def main(): + tool_id = sys.monitoring.PROFILER_ID + event_id = sys.monitoring.events.PY_START + + sys.monitoring.use_tool_id(tool_id, "test profiler") + sys.monitoring.set_events(tool_id, event_id) + sys.monitoring.register_callback(tool_id, event_id, CallableCycle()) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py new file mode 100644 index 00000000000..92c41f4a79a --- /dev/null +++ b/Lib/test/audit-tests.py @@ -0,0 +1,595 @@ +"""This script contains the actual auditing tests. + +It should not be imported directly, but should be run by the test_audit +module with arguments identifying each test. + +""" + +import contextlib +import os +import sys + + +class TestHook: + """Used in standard hook tests to collect any logged events. + + Should be used in a with block to ensure that it has no impact + after the test completes. + """ + + def __init__(self, raise_on_events=None, exc_type=RuntimeError): + self.raise_on_events = raise_on_events or () + self.exc_type = exc_type + self.seen = [] + self.closed = False + + def __enter__(self, *a): + sys.addaudithook(self) + return self + + def __exit__(self, *a): + self.close() + + def close(self): + self.closed = True + + @property + def seen_events(self): + return [i[0] for i in self.seen] + + def __call__(self, event, args): + if self.closed: + return + self.seen.append((event, args)) + if event in self.raise_on_events: + raise self.exc_type("saw event " + event) + + +# Simple helpers, since we are not in unittest here +def assertEqual(x, y): + if x != y: + raise AssertionError(f"{x!r} should equal {y!r}") + + +def assertIn(el, series): + if el not in series: + raise AssertionError(f"{el!r} should be in {series!r}") + + +def assertNotIn(el, series): + if el in series: + raise AssertionError(f"{el!r} should not be in {series!r}") + + +def assertSequenceEqual(x, y): + if len(x) != len(y): + raise AssertionError(f"{x!r} should equal {y!r}") + if any(ix != iy for ix, iy in zip(x, y)): + raise AssertionError(f"{x!r} should equal {y!r}") + + +@contextlib.contextmanager +def assertRaises(ex_type): + try: + yield + assert False, f"expected {ex_type}" + except BaseException as ex: + if isinstance(ex, AssertionError): + raise + assert type(ex) is ex_type, f"{ex} should be {ex_type}" + + +def test_basic(): + with TestHook() as hook: + sys.audit("test_event", 1, 2, 3) + assertEqual(hook.seen[0][0], "test_event") + assertEqual(hook.seen[0][1], (1, 2, 3)) + + +def test_block_add_hook(): + # Raising an exception should prevent a new hook from being added, + # but will not propagate out. + with TestHook(raise_on_events="sys.addaudithook") as hook1: + with TestHook() as hook2: + sys.audit("test_event") + assertIn("test_event", hook1.seen_events) + assertNotIn("test_event", hook2.seen_events) + + +def test_block_add_hook_baseexception(): + # Raising BaseException will propagate out when adding a hook + with assertRaises(BaseException): + with TestHook( + raise_on_events="sys.addaudithook", exc_type=BaseException + ) as hook1: + # Adding this next hook should raise BaseException + with TestHook() as hook2: + pass + + +def test_marshal(): + import marshal + o = ("a", "b", "c", 1, 2, 3) + payload = marshal.dumps(o) + + with TestHook() as hook: + assertEqual(o, marshal.loads(marshal.dumps(o))) + + try: + with open("test-marshal.bin", "wb") as f: + marshal.dump(o, f) + with open("test-marshal.bin", "rb") as f: + assertEqual(o, marshal.load(f)) + finally: + os.unlink("test-marshal.bin") + + actual = [(a[0], a[1]) for e, a in hook.seen if e == "marshal.dumps"] + assertSequenceEqual(actual, [(o, marshal.version)] * 2) + + actual = [a[0] for e, a in hook.seen if e == "marshal.loads"] + assertSequenceEqual(actual, [payload]) + + actual = [e for e, a in hook.seen if e == "marshal.load"] + assertSequenceEqual(actual, ["marshal.load"]) + + +def test_pickle(): + import pickle + + class PicklePrint: + def __reduce_ex__(self, p): + return str, ("Pwned!",) + + payload_1 = pickle.dumps(PicklePrint()) + payload_2 = pickle.dumps(("a", "b", "c", 1, 2, 3)) + + # Before we add the hook, ensure our malicious pickle loads + assertEqual("Pwned!", pickle.loads(payload_1)) + + with TestHook(raise_on_events="pickle.find_class") as hook: + with assertRaises(RuntimeError): + # With the hook enabled, loading globals is not allowed + pickle.loads(payload_1) + # pickles with no globals are okay + pickle.loads(payload_2) + + +def test_monkeypatch(): + class A: + pass + + class B: + pass + + class C(A): + pass + + a = A() + + with TestHook() as hook: + # Catch name changes + C.__name__ = "X" + # Catch type changes + C.__bases__ = (B,) + # Ensure bypassing __setattr__ is still caught + type.__dict__["__bases__"].__set__(C, (B,)) + # Catch attribute replacement + C.__init__ = B.__init__ + # Catch attribute addition + C.new_attr = 123 + # Catch class changes + a.__class__ = B + + actual = [(a[0], a[1]) for e, a in hook.seen if e == "object.__setattr__"] + assertSequenceEqual( + [(C, "__name__"), (C, "__bases__"), (C, "__bases__"), (a, "__class__")], actual + ) + + +def test_open(testfn): + # SSLContext.load_dh_params uses _Py_fopen_obj rather than normal open() + try: + import ssl + + load_dh_params = ssl.create_default_context().load_dh_params + except ImportError: + load_dh_params = None + + # Try a range of "open" functions. + # All of them should fail + with TestHook(raise_on_events={"open"}) as hook: + for fn, *args in [ + (open, testfn, "r"), + (open, sys.executable, "rb"), + (open, 3, "wb"), + (open, testfn, "w", -1, None, None, None, False, lambda *a: 1), + (load_dh_params, testfn), + ]: + if not fn: + continue + with assertRaises(RuntimeError): + try: + fn(*args) + except NotImplementedError: + if fn == load_dh_params: + # Not callable in some builds + load_dh_params = None + raise RuntimeError + else: + raise + + actual_mode = [(a[0], a[1]) for e, a in hook.seen if e == "open" and a[1]] + actual_flag = [(a[0], a[2]) for e, a in hook.seen if e == "open" and not a[1]] + assertSequenceEqual( + [ + i + for i in [ + (testfn, "r"), + (sys.executable, "r"), + (3, "w"), + (testfn, "w"), + (testfn, "rb") if load_dh_params else None, + ] + if i is not None + ], + actual_mode, + ) + assertSequenceEqual([], actual_flag) + + +def test_cantrace(): + traced = [] + + def trace(frame, event, *args): + if frame.f_code == TestHook.__call__.__code__: + traced.append(event) + + old = sys.settrace(trace) + try: + with TestHook() as hook: + # No traced call + eval("1") + + # No traced call + hook.__cantrace__ = False + eval("2") + + # One traced call + hook.__cantrace__ = True + eval("3") + + # Two traced calls (writing to private member, eval) + hook.__cantrace__ = 1 + eval("4") + + # One traced call (writing to private member) + hook.__cantrace__ = 0 + finally: + sys.settrace(old) + + assertSequenceEqual(["call"] * 4, traced) + + +def test_mmap(): + import mmap + + with TestHook() as hook: + mmap.mmap(-1, 8) + assertEqual(hook.seen[0][1][:2], (-1, 8)) + + +def test_excepthook(): + def excepthook(exc_type, exc_value, exc_tb): + if exc_type is not RuntimeError: + sys.__excepthook__(exc_type, exc_value, exc_tb) + + def hook(event, args): + if event == "sys.excepthook": + if not isinstance(args[2], args[1]): + raise TypeError(f"Expected isinstance({args[2]!r}, " f"{args[1]!r})") + if args[0] != excepthook: + raise ValueError(f"Expected {args[0]} == {excepthook}") + print(event, repr(args[2])) + + sys.addaudithook(hook) + sys.excepthook = excepthook + raise RuntimeError("fatal-error") + + +def test_unraisablehook(): + from _testcapi import err_formatunraisable + + def unraisablehook(hookargs): + pass + + def hook(event, args): + if event == "sys.unraisablehook": + if args[0] != unraisablehook: + raise ValueError(f"Expected {args[0]} == {unraisablehook}") + print(event, repr(args[1].exc_value), args[1].err_msg) + + sys.addaudithook(hook) + sys.unraisablehook = unraisablehook + err_formatunraisable(RuntimeError("nonfatal-error"), + "Exception ignored for audit hook test") + + +def test_winreg(): + from winreg import OpenKey, EnumKey, CloseKey, HKEY_LOCAL_MACHINE + + def hook(event, args): + if not event.startswith("winreg."): + return + print(event, *args) + + sys.addaudithook(hook) + + k = OpenKey(HKEY_LOCAL_MACHINE, "Software") + EnumKey(k, 0) + try: + EnumKey(k, 10000) + except OSError: + pass + else: + raise RuntimeError("Expected EnumKey(HKLM, 10000) to fail") + + kv = k.Detach() + CloseKey(kv) + + +def test_socket(): + import socket + + def hook(event, args): + if event.startswith("socket."): + print(event, *args) + + sys.addaudithook(hook) + + socket.gethostname() + + # Don't care if this fails, we just want the audit message + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + # Don't care if this fails, we just want the audit message + sock.bind(('127.0.0.1', 8080)) + except Exception: + pass + finally: + sock.close() + + +def test_gc(): + import gc + + def hook(event, args): + if event.startswith("gc."): + print(event, *args) + + sys.addaudithook(hook) + + gc.get_objects(generation=1) + + x = object() + y = [x] + + gc.get_referrers(x) + gc.get_referents(y) + + +def test_http_client(): + import http.client + + def hook(event, args): + if event.startswith("http.client."): + print(event, *args[1:]) + + sys.addaudithook(hook) + + conn = http.client.HTTPConnection('www.python.org') + try: + conn.request('GET', '/') + except OSError: + print('http.client.send', '[cannot send]') + finally: + conn.close() + + +def test_sqlite3(): + import sqlite3 + + def hook(event, *args): + if event.startswith("sqlite3."): + print(event, *args) + + sys.addaudithook(hook) + cx1 = sqlite3.connect(":memory:") + cx2 = sqlite3.Connection(":memory:") + + # Configured without --enable-loadable-sqlite-extensions + try: + if hasattr(sqlite3.Connection, "enable_load_extension"): + cx1.enable_load_extension(False) + try: + cx1.load_extension("test") + except sqlite3.OperationalError: + pass + else: + raise RuntimeError("Expected sqlite3.load_extension to fail") + finally: + cx1.close() + cx2.close() + +def test_sys_getframe(): + import sys + + def hook(event, args): + if event.startswith("sys."): + print(event, args[0].f_code.co_name) + + sys.addaudithook(hook) + sys._getframe() + + +def test_sys_getframemodulename(): + import sys + + def hook(event, args): + if event.startswith("sys."): + print(event, *args) + + sys.addaudithook(hook) + sys._getframemodulename() + + +def test_threading(): + import _thread + + def hook(event, args): + if event.startswith(("_thread.", "cpython.PyThreadState", "test.")): + print(event, args) + + sys.addaudithook(hook) + + lock = _thread.allocate_lock() + lock.acquire() + + class test_func: + def __repr__(self): return "" + def __call__(self): + sys.audit("test.test_func") + lock.release() + + i = _thread.start_new_thread(test_func(), ()) + lock.acquire() + + handle = _thread.start_joinable_thread(test_func()) + handle.join() + + +def test_threading_abort(): + # Ensures that aborting PyThreadState_New raises the correct exception + import _thread + + class ThreadNewAbortError(Exception): + pass + + def hook(event, args): + if event == "cpython.PyThreadState_New": + raise ThreadNewAbortError() + + sys.addaudithook(hook) + + try: + _thread.start_new_thread(lambda: None, ()) + except ThreadNewAbortError: + # Other exceptions are raised and the test will fail + pass + + +def test_wmi_exec_query(): + import _wmi + + def hook(event, args): + if event.startswith("_wmi."): + print(event, args[0]) + + sys.addaudithook(hook) + try: + _wmi.exec_query("SELECT * FROM Win32_OperatingSystem") + except WindowsError as e: + # gh-112278: WMI may be slow response when first called, but we still + # get the audit event, so just ignore the timeout + if e.winerror != 258: + raise + +def test_syslog(): + import syslog + + def hook(event, args): + if event.startswith("syslog."): + print(event, *args) + + sys.addaudithook(hook) + syslog.openlog('python') + syslog.syslog('test') + syslog.setlogmask(syslog.LOG_DEBUG) + syslog.closelog() + # implicit open + syslog.syslog('test2') + # open with default ident + syslog.openlog(logoption=syslog.LOG_NDELAY, facility=syslog.LOG_LOCAL0) + sys.argv = None + syslog.openlog() + syslog.closelog() + + +def test_not_in_gc(): + import gc + + hook = lambda *a: None + sys.addaudithook(hook) + + for o in gc.get_objects(): + if isinstance(o, list): + assert hook not in o + + +def test_time(mode): + import time + + def hook(event, args): + if event.startswith("time."): + if mode == 'print': + print(event, *args) + elif mode == 'fail': + raise AssertionError('hook failed') + sys.addaudithook(hook) + + time.sleep(0) + time.sleep(0.0625) # 1/16, a small exact float + try: + time.sleep(-1) + except ValueError: + pass + +def test_sys_monitoring_register_callback(): + import sys + + def hook(event, args): + if event.startswith("sys.monitoring"): + print(event, args) + + sys.addaudithook(hook) + sys.monitoring.register_callback(1, 1, None) + + +def test_winapi_createnamedpipe(pipe_name): + import _winapi + + def hook(event, args): + if event == "_winapi.CreateNamedPipe": + print(event, args) + + sys.addaudithook(hook) + _winapi.CreateNamedPipe(pipe_name, _winapi.PIPE_ACCESS_DUPLEX, 8, 2, 0, 0, 0, 0) + + +def test_assert_unicode(): + import sys + sys.addaudithook(lambda *args: None) + try: + sys.audit(9) + except TypeError: + pass + else: + raise RuntimeError("Expected sys.audit(9) to fail.") + + +if __name__ == "__main__": + from test.support import suppress_msvcrt_asserts + + suppress_msvcrt_asserts() + + test = sys.argv[1] + globals()[test](*sys.argv[2:]) diff --git a/Lib/test/bisect_cmd.py b/Lib/test/bisect_cmd.py new file mode 100755 index 00000000000..aee2e8ac120 --- /dev/null +++ b/Lib/test/bisect_cmd.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +""" +Command line tool to bisect failing CPython tests. + +Find the test_os test method which alters the environment: + + ./python -m test.bisect_cmd --fail-env-changed test_os + +Find a reference leak in "test_os", write the list of failing tests into the +"bisect" file: + + ./python -m test.bisect_cmd -o bisect -R 3:3 test_os + +Load an existing list of tests from a file using -i option: + + ./python -m test --list-cases -m FileTests test_os > tests + ./python -m test.bisect_cmd -i tests test_os +""" + +import argparse +import datetime +import os.path +import math +import random +import subprocess +import sys +import tempfile +import time + + +def write_tests(filename, tests): + with open(filename, "w") as fp: + for name in tests: + print(name, file=fp) + fp.flush() + + +def write_output(filename, tests): + if not filename: + return + print("Writing %s tests into %s" % (len(tests), filename)) + write_tests(filename, tests) + return filename + + +def format_shell_args(args): + return ' '.join(args) + + +def python_cmd(): + cmd = [sys.executable] + cmd.extend(subprocess._args_from_interpreter_flags()) + cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.extend(('-X', 'faulthandler')) + return cmd + + +def list_cases(args): + cmd = python_cmd() + cmd.extend(['-m', 'test', '--list-cases']) + cmd.extend(args.test_args) + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + universal_newlines=True) + exitcode = proc.returncode + if exitcode: + cmd = format_shell_args(cmd) + print("Failed to list tests: %s failed with exit code %s" + % (cmd, exitcode)) + sys.exit(exitcode) + tests = proc.stdout.splitlines() + return tests + + +def run_tests(args, tests, huntrleaks=None): + tmp = tempfile.mktemp() + try: + write_tests(tmp, tests) + + cmd = python_cmd() + cmd.extend(['-u', '-m', 'test', '--matchfile', tmp]) + cmd.extend(args.test_args) + print("+ %s" % format_shell_args(cmd)) + + sys.stdout.flush() + sys.stderr.flush() + + proc = subprocess.run(cmd) + return proc.returncode + finally: + if os.path.exists(tmp): + os.unlink(tmp) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--input', + help='Test names produced by --list-tests written ' + 'into a file. If not set, run --list-tests') + parser.add_argument('-o', '--output', + help='Result of the bisection') + parser.add_argument('-n', '--max-tests', type=int, default=1, + help='Maximum number of tests to stop the bisection ' + '(default: 1)') + parser.add_argument('-N', '--max-iter', type=int, default=100, + help='Maximum number of bisection iterations ' + '(default: 100)') + # FIXME: document that following arguments are test arguments + + args, test_args = parser.parse_known_args() + args.test_args = test_args + return args + + +def main(): + args = parse_args() + for opt in ('-w', '--rerun', '--verbose2'): + if opt in args.test_args: + print(f"WARNING: {opt} option should not be used to bisect!") + print() + + if args.input: + with open(args.input) as fp: + tests = [line.strip() for line in fp] + else: + tests = list_cases(args) + + print("Start bisection with %s tests" % len(tests)) + print("Test arguments: %s" % format_shell_args(args.test_args)) + print("Bisection will stop when getting %s or less tests " + "(-n/--max-tests option), or after %s iterations " + "(-N/--max-iter option)" + % (args.max_tests, args.max_iter)) + output = write_output(args.output, tests) + print() + + start_time = time.monotonic() + iteration = 1 + try: + while len(tests) > args.max_tests and iteration <= args.max_iter: + ntest = len(tests) + ntest = max(ntest // 2, 1) + subtests = random.sample(tests, ntest) + + print(f"[+] Iteration {iteration}/{args.max_iter}: " + f"run {len(subtests)} tests/{len(tests)}") + print() + + exitcode = run_tests(args, subtests) + + print("ran %s tests/%s" % (ntest, len(tests))) + print("exit", exitcode) + if exitcode: + print("Tests failed: continuing with this subtest") + tests = subtests + output = write_output(args.output, tests) + else: + print("Tests succeeded: skipping this subtest, trying a new subset") + print() + iteration += 1 + except KeyboardInterrupt: + print() + print("Bisection interrupted!") + print() + + print("Tests (%s):" % len(tests)) + for test in tests: + print("* %s" % test) + print() + + if output: + print("Output written into %s" % output) + + dt = math.ceil(time.monotonic() - start_time) + if len(tests) <= args.max_tests: + print("Bisection completed in %s iterations and %s" + % (iteration, datetime.timedelta(seconds=dt))) + else: + print("Bisection failed after %s iterations and %s" + % (iteration, datetime.timedelta(seconds=dt))) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/Lib/test/cjkencodings/big5-utf8.txt b/Lib/test/cjkencodings/big5-utf8.txt new file mode 100644 index 00000000000..a0a534a964d --- /dev/null +++ b/Lib/test/cjkencodings/big5-utf8.txt @@ -0,0 +1,9 @@ +如何在 Python 中使用既有的 C library? + 在資訊科技快速發展的今天, 開發及測試軟體的速度是不容忽視的 +課題. 為加快開發及測試的速度, 我們便常希望能利用一些已開發好的 +library, 並有一個 fast prototyping 的 programming language 可 +供使用. 目前有許許多多的 library 是以 C 寫成, 而 Python 是一個 +fast prototyping 的 programming language. 故我們希望能將既有的 +C library 拿到 Python 的環境中測試及整合. 其中最主要也是我們所 +要討論的問題就是: + diff --git a/Lib/test/cjkencodings/big5.txt b/Lib/test/cjkencodings/big5.txt new file mode 100644 index 00000000000..f4424959e91 --- /dev/null +++ b/Lib/test/cjkencodings/big5.txt @@ -0,0 +1,9 @@ +pb Python ϥάJ C library? +@bTާֳtoi, }oδճn骺t׬Oe +D. [ֶ}oδժt, ڭ̫K`ƱQΤ@Ǥw}on +library, æ@ fast prototyping programming language i +Ѩϥ. ثe\\hh library OH C g, Python O@ +fast prototyping programming language. Gڭ̧ƱNJ +C library Python ҤդξX. 䤤̥Dn]Oڭ̩ +nQתDNO: + diff --git a/Lib/test/cjkencodings/big5hkscs-utf8.txt b/Lib/test/cjkencodings/big5hkscs-utf8.txt new file mode 100644 index 00000000000..f744ce9ae08 --- /dev/null +++ b/Lib/test/cjkencodings/big5hkscs-utf8.txt @@ -0,0 +1,2 @@ +𠄌Ě鵮罓洆 +ÊÊ̄ê êê̄ diff --git a/Lib/test/cjkencodings/big5hkscs.txt b/Lib/test/cjkencodings/big5hkscs.txt new file mode 100644 index 00000000000..81c42b3503d --- /dev/null +++ b/Lib/test/cjkencodings/big5hkscs.txt @@ -0,0 +1,2 @@ +E\sڍ +fb diff --git a/Lib/test/cjkencodings/cp949-utf8.txt b/Lib/test/cjkencodings/cp949-utf8.txt new file mode 100644 index 00000000000..5655e385176 --- /dev/null +++ b/Lib/test/cjkencodings/cp949-utf8.txt @@ -0,0 +1,9 @@ +똠방각하 펲시콜라 + +㉯㉯납!! 因九月패믤릔궈 ⓡⓖ훀¿¿¿ 긍뒙 ⓔ뎨 ㉯. . +亞영ⓔ능횹 . . . . 서울뤄 뎐학乙 家훀 ! ! !ㅠ.ㅠ +흐흐흐 ㄱㄱㄱ☆ㅠ_ㅠ 어릨 탸콰긐 뎌응 칑九들乙 ㉯드긐 +설릌 家훀 . . . . 굴애쉌 ⓔ궈 ⓡ릘㉱긐 因仁川女中까즼 +와쒀훀 ! ! 亞영ⓔ 家능궈 ☆上관 없능궈능 亞능뒈훀 글애듴 +ⓡ려듀九 싀풔숴훀 어릨 因仁川女中싁⑨들앜!! ㉯㉯납♡ ⌒⌒* + diff --git a/Lib/test/cjkencodings/cp949.txt b/Lib/test/cjkencodings/cp949.txt new file mode 100644 index 00000000000..16549aa5e49 --- /dev/null +++ b/Lib/test/cjkencodings/cp949.txt @@ -0,0 +1,9 @@ +c氢 ݶ + +!! Вp ިR ѵ . . +䬿Ѵ . . . . ʫR ! ! !. + ٤_  O h O +j ʫR . . . . ֚f ѱ ސtƒO  +;R ! ! 䬿 ʫɱ ߾ ɱŴ 䬴ɵR ۾֊ +޷ ǴR  Ĩ!! ҡ* + diff --git a/Lib/test/cjkencodings/euc_jisx0213-utf8.txt b/Lib/test/cjkencodings/euc_jisx0213-utf8.txt new file mode 100644 index 00000000000..9a56a2e18bd --- /dev/null +++ b/Lib/test/cjkencodings/euc_jisx0213-utf8.txt @@ -0,0 +1,8 @@ +Python の開発は、1990 年ごろから開始されています。 +開発者の Guido van Rossum は教育用のプログラミング言語「ABC」の開発に参加していましたが、ABC は実用上の目的にはあまり適していませんでした。 +このため、Guido はより実用的なプログラミング言語の開発を開始し、英国 BBS 放送のコメディ番組「モンティ パイソン」のファンである Guido はこの言語を「Python」と名づけました。 +このような背景から生まれた Python の言語設計は、「シンプル」で「習得が容易」という目標に重点が置かれています。 +多くのスクリプト系言語ではユーザの目先の利便性を優先して色々な機能を言語要素として取り入れる場合が多いのですが、Python ではそういった小細工が追加されることはあまりありません。 +言語自体の機能は最小限に押さえ、必要な機能は拡張モジュールとして追加する、というのが Python のポリシーです。 + +ノか゚ ト゚ トキ喝塀 𡚴𪎌 麀齁𩛰 diff --git a/Lib/test/cjkencodings/euc_jisx0213.txt b/Lib/test/cjkencodings/euc_jisx0213.txt new file mode 100644 index 00000000000..51e9268ca98 --- /dev/null +++ b/Lib/test/cjkencodings/euc_jisx0213.txt @@ -0,0 +1,8 @@ +Python γȯϡ1990 ǯ鳫ϤƤޤ +ȯԤ Guido van Rossum ϶ѤΥץߥ󥰸ABCפγȯ˻äƤޤABC ϼѾŪˤϤޤŬƤޤǤ +ΤᡢGuido ϤŪʥץߥ󥰸γȯ򳫻Ϥѹ BBS Υǥȡ֥ƥ ѥפΥեǤ Guido ϤθPythonפ̾Ťޤ +Τ褦طʤޤ줿 Python θ߷פϡ֥ץפǡֽưספȤɸ˽֤Ƥޤ +¿ΥץȷϸǤϥ桼ͥ褷ƿʵǽǤȤƼ礬¿ΤǤPython ǤϤäٹɲä뤳ȤϤޤꤢޤ +켫ΤεǽϺǾ¤˲ɬפʵǽϳĥ⥸塼Ȥɲä롢ȤΤ Python ΥݥꥷǤ + +Τ ȥ ԏ diff --git a/Lib/test/cjkencodings/euc_jp-utf8.txt b/Lib/test/cjkencodings/euc_jp-utf8.txt new file mode 100644 index 00000000000..7763250ebbe --- /dev/null +++ b/Lib/test/cjkencodings/euc_jp-utf8.txt @@ -0,0 +1,7 @@ +Python の開発は、1990 年ごろから開始されています。 +開発者の Guido van Rossum は教育用のプログラミング言語「ABC」の開発に参加していましたが、ABC は実用上の目的にはあまり適していませんでした。 +このため、Guido はより実用的なプログラミング言語の開発を開始し、英国 BBS 放送のコメディ番組「モンティ パイソン」のファンである Guido はこの言語を「Python」と名づけました。 +このような背景から生まれた Python の言語設計は、「シンプル」で「習得が容易」という目標に重点が置かれています。 +多くのスクリプト系言語ではユーザの目先の利便性を優先して色々な機能を言語要素として取り入れる場合が多いのですが、Python ではそういった小細工が追加されることはあまりありません。 +言語自体の機能は最小限に押さえ、必要な機能は拡張モジュールとして追加する、というのが Python のポリシーです。 + diff --git a/Lib/test/cjkencodings/euc_jp.txt b/Lib/test/cjkencodings/euc_jp.txt new file mode 100644 index 00000000000..9da6b5d83da --- /dev/null +++ b/Lib/test/cjkencodings/euc_jp.txt @@ -0,0 +1,7 @@ +Python γȯϡ1990 ǯ鳫ϤƤޤ +ȯԤ Guido van Rossum ϶ѤΥץߥ󥰸ABCפγȯ˻äƤޤABC ϼѾŪˤϤޤŬƤޤǤ +ΤᡢGuido ϤŪʥץߥ󥰸γȯ򳫻Ϥѹ BBS Υǥȡ֥ƥ ѥפΥեǤ Guido ϤθPythonפ̾Ťޤ +Τ褦طʤޤ줿 Python θ߷פϡ֥ץפǡֽưספȤɸ˽֤Ƥޤ +¿ΥץȷϸǤϥ桼ͥ褷ƿʵǽǤȤƼ礬¿ΤǤPython ǤϤäٹɲä뤳ȤϤޤꤢޤ +켫ΤεǽϺǾ¤˲ɬפʵǽϳĥ⥸塼Ȥɲä롢ȤΤ Python ΥݥꥷǤ + diff --git a/Lib/test/cjkencodings/euc_kr-utf8.txt b/Lib/test/cjkencodings/euc_kr-utf8.txt new file mode 100644 index 00000000000..16c37412b6a --- /dev/null +++ b/Lib/test/cjkencodings/euc_kr-utf8.txt @@ -0,0 +1,7 @@ +◎ 파이썬(Python)은 배우기 쉽고, 강력한 프로그래밍 언어입니다. 파이썬은 +효율적인 고수준 데이터 구조와 간단하지만 효율적인 객체지향프로그래밍을 +지원합니다. 파이썬의 우아(優雅)한 문법과 동적 타이핑, 그리고 인터프리팅 +환경은 파이썬을 스크립팅과 여러 분야에서와 대부분의 플랫폼에서의 빠른 +애플리케이션 개발을 할 수 있는 이상적인 언어로 만들어줍니다. + +☆첫가끝: 날아라 쓔쓔쓩~ 닁큼! 뜽금없이 전홥니다. 뷁. 그런거 읎다. diff --git a/Lib/test/cjkencodings/euc_kr.txt b/Lib/test/cjkencodings/euc_kr.txt new file mode 100644 index 00000000000..f68dd350289 --- /dev/null +++ b/Lib/test/cjkencodings/euc_kr.txt @@ -0,0 +1,7 @@ + ̽(Python) , α׷ Դϴ. ̽ +ȿ ȿ üα׷ +մϴ. ̽ () Ÿ, ׸ +ȯ ̽ ũð о߿ κ ÷ +ø̼ ִ ̻ ݴϴ. + +ù: ƶ ԤФԤԤФԾ~ ԤҤŭ! ԤѤݾ ԤȤϴ. ԤΤ. ׷ ԤѤ. diff --git a/Lib/test/cjkencodings/gb18030-utf8.txt b/Lib/test/cjkencodings/gb18030-utf8.txt new file mode 100644 index 00000000000..2060d2593eb --- /dev/null +++ b/Lib/test/cjkencodings/gb18030-utf8.txt @@ -0,0 +1,15 @@ +Python(派森)语言是一种功能强大而完善的通用型计算机程序设计语言, +已经具有十多年的发展历史,成熟且稳定。这种语言具有非常简捷而清晰 +的语法特点,适合完成各种高层任务,几乎可以在所有的操作系统中 +运行。这种语言简单而强大,适合各种人士学习使用。目前,基于这 +种语言的相关技术正在飞速的发展,用户数量急剧扩大,相关的资源非常多。 +如何在 Python 中使用既有的 C library? + 在資訊科技快速發展的今天, 開發及測試軟體的速度是不容忽視的 +課題. 為加快開發及測試的速度, 我們便常希望能利用一些已開發好的 +library, 並有一個 fast prototyping 的 programming language 可 +供使用. 目前有許許多多的 library 是以 C 寫成, 而 Python 是一個 +fast prototyping 的 programming language. 故我們希望能將既有的 +C library 拿到 Python 的環境中測試及整合. 其中最主要也是我們所 +要討論的問題就是: +파이썬은 강력한 기능을 지닌 범용 컴퓨터 프로그래밍 언어다. + diff --git a/Lib/test/cjkencodings/gb18030.txt b/Lib/test/cjkencodings/gb18030.txt new file mode 100644 index 00000000000..5d1f6dca232 --- /dev/null +++ b/Lib/test/cjkencodings/gb18030.txt @@ -0,0 +1,15 @@ +PythonɭһֹǿƵͨͼԣ +ѾʮķչʷȶԾзdzݶ +﷨ص㣬ʺɸָ߲񣬼еIJϵͳ +СԼ򵥶ǿʺϸʿѧϰʹáĿǰ +ԵؼڷٵķչûصԴdzࡣ + Python ʹüе C library? +YӍƼٰlչĽ, _lyԇܛwٶDzݺҕ +n}. ӿ_lyԇٶ, ҂㳣ϣһЩ_lõ +library, Kһ fast prototyping programming language +ʹ. ĿǰSS library C , Python һ +fast prototyping programming language. ҂ϣ܌е +C library õ Python ĭhМyԇ. ҪҲ҂ +ҪӑՓĆ}: +51332131 760463 858635 3195 0930 435755 5509899304 292599. + diff --git a/Lib/test/cjkencodings/gb2312-utf8.txt b/Lib/test/cjkencodings/gb2312-utf8.txt new file mode 100644 index 00000000000..efb7d8f95cd --- /dev/null +++ b/Lib/test/cjkencodings/gb2312-utf8.txt @@ -0,0 +1,6 @@ +Python(派森)语言是一种功能强大而完善的通用型计算机程序设计语言, +已经具有十多年的发展历史,成熟且稳定。这种语言具有非常简捷而清晰 +的语法特点,适合完成各种高层任务,几乎可以在所有的操作系统中 +运行。这种语言简单而强大,适合各种人士学习使用。目前,基于这 +种语言的相关技术正在飞速的发展,用户数量急剧扩大,相关的资源非常多。 + diff --git a/Lib/test/cjkencodings/gb2312.txt b/Lib/test/cjkencodings/gb2312.txt new file mode 100644 index 00000000000..1536ac10b9e --- /dev/null +++ b/Lib/test/cjkencodings/gb2312.txt @@ -0,0 +1,6 @@ +PythonɭһֹǿƵͨͼԣ +ѾʮķչʷȶԾзdzݶ +﷨ص㣬ʺɸָ߲񣬼еIJϵͳ +СԼ򵥶ǿʺϸʿѧϰʹáĿǰ +ԵؼڷٵķչûصԴdzࡣ + diff --git a/Lib/test/cjkencodings/gbk-utf8.txt b/Lib/test/cjkencodings/gbk-utf8.txt new file mode 100644 index 00000000000..75bbd31ec5a --- /dev/null +++ b/Lib/test/cjkencodings/gbk-utf8.txt @@ -0,0 +1,14 @@ +Python(派森)语言是一种功能强大而完善的通用型计算机程序设计语言, +已经具有十多年的发展历史,成熟且稳定。这种语言具有非常简捷而清晰 +的语法特点,适合完成各种高层任务,几乎可以在所有的操作系统中 +运行。这种语言简单而强大,适合各种人士学习使用。目前,基于这 +种语言的相关技术正在飞速的发展,用户数量急剧扩大,相关的资源非常多。 +如何在 Python 中使用既有的 C library? + 在資訊科技快速發展的今天, 開發及測試軟體的速度是不容忽視的 +課題. 為加快開發及測試的速度, 我們便常希望能利用一些已開發好的 +library, 並有一個 fast prototyping 的 programming language 可 +供使用. 目前有許許多多的 library 是以 C 寫成, 而 Python 是一個 +fast prototyping 的 programming language. 故我們希望能將既有的 +C library 拿到 Python 的環境中測試及整合. 其中最主要也是我們所 +要討論的問題就是: + diff --git a/Lib/test/cjkencodings/gbk.txt b/Lib/test/cjkencodings/gbk.txt new file mode 100644 index 00000000000..8788f8a2dc4 --- /dev/null +++ b/Lib/test/cjkencodings/gbk.txt @@ -0,0 +1,14 @@ +PythonɭһֹǿƵͨͼԣ +ѾʮķչʷȶԾзdzݶ +﷨ص㣬ʺɸָ߲񣬼еIJϵͳ +СԼ򵥶ǿʺϸʿѧϰʹáĿǰ +ԵؼڷٵķչûصԴdzࡣ + Python ʹüе C library? +YӍƼٰlչĽ, _lyԇܛwٶDzݺҕ +n}. ӿ_lyԇٶ, ҂㳣ϣһЩ_lõ +library, Kһ fast prototyping programming language +ʹ. ĿǰSS library C , Python һ +fast prototyping programming language. ҂ϣ܌е +C library õ Python ĭhМyԇ. ҪҲ҂ +ҪӑՓĆ}: + diff --git a/Lib/test/cjkencodings/hz-utf8.txt b/Lib/test/cjkencodings/hz-utf8.txt new file mode 100644 index 00000000000..7c11735c1f1 --- /dev/null +++ b/Lib/test/cjkencodings/hz-utf8.txt @@ -0,0 +1,2 @@ +This sentence is in ASCII. +The next sentence is in GB.己所不欲,勿施於人。Bye. diff --git a/Lib/test/cjkencodings/hz.txt b/Lib/test/cjkencodings/hz.txt new file mode 100644 index 00000000000..f882d463447 --- /dev/null +++ b/Lib/test/cjkencodings/hz.txt @@ -0,0 +1,2 @@ +This sentence is in ASCII. +The next sentence is in GB.~{<:Ky2;S{#,NpJ)l6HK!#~}Bye. diff --git a/Lib/test/cjkencodings/iso2022_jp-utf8.txt b/Lib/test/cjkencodings/iso2022_jp-utf8.txt new file mode 100644 index 00000000000..7763250ebbe --- /dev/null +++ b/Lib/test/cjkencodings/iso2022_jp-utf8.txt @@ -0,0 +1,7 @@ +Python の開発は、1990 年ごろから開始されています。 +開発者の Guido van Rossum は教育用のプログラミング言語「ABC」の開発に参加していましたが、ABC は実用上の目的にはあまり適していませんでした。 +このため、Guido はより実用的なプログラミング言語の開発を開始し、英国 BBS 放送のコメディ番組「モンティ パイソン」のファンである Guido はこの言語を「Python」と名づけました。 +このような背景から生まれた Python の言語設計は、「シンプル」で「習得が容易」という目標に重点が置かれています。 +多くのスクリプト系言語ではユーザの目先の利便性を優先して色々な機能を言語要素として取り入れる場合が多いのですが、Python ではそういった小細工が追加されることはあまりありません。 +言語自体の機能は最小限に押さえ、必要な機能は拡張モジュールとして追加する、というのが Python のポリシーです。 + diff --git a/Lib/test/cjkencodings/iso2022_jp.txt b/Lib/test/cjkencodings/iso2022_jp.txt new file mode 100644 index 00000000000..fc398d64ad2 --- /dev/null +++ b/Lib/test/cjkencodings/iso2022_jp.txt @@ -0,0 +1,7 @@ +Python $B$N3+H/$O!"(B1990 $BG/$4$m$+$i3+;O$5$l$F$$$^$9!#(B +$B3+H/e$NL\E*$K$O$"$^$jE,$7$F$$$^$;$s$G$7$?!#(B +$B$3$N$?$a!"(BGuido $B$O$h$j$E$1$^$7$?!#(B +$B$3$N$h$&$JGX7J$+$i@8$^$l$?(B Python $B$N8@8l@_7W$O!"!V%7%s%W%k!W$G!V=,F@$,MF0W!W$H$$$&L\I8$K=EE@$,CV$+$l$F$$$^$9!#(B +$BB?$/$N%9%/%j%W%H7O8@8l$G$O%f!<%6$NL\@h$NMxJX@-$rM%@h$7$F?'!9$J5!G=$r8@8lMWAG$H$7$Fl9g$,B?$$$N$G$9$,!"(BPython $B$G$O$=$&$$$C$?>.:Y9)$,DI2C$5$l$k$3$H$O$"$^$j$"$j$^$;$s!#(B +$B8@8l<+BN$N5!G=$O:G>.8B$K2!$5$(!"I,MW$J5!G=$O3HD%%b%8%e!<%k$H$7$FDI2C$9$k!"$H$$$&$N$,(B Python $B$N%]%j%7!<$G$9!#(B + diff --git a/Lib/test/cjkencodings/iso2022_kr-utf8.txt b/Lib/test/cjkencodings/iso2022_kr-utf8.txt new file mode 100644 index 00000000000..d5c9d6eeeb2 --- /dev/null +++ b/Lib/test/cjkencodings/iso2022_kr-utf8.txt @@ -0,0 +1,7 @@ +◎ 파이썬(Python)은 배우기 쉽고, 강력한 프로그래밍 언어입니다. 파이썬은 +효율적인 고수준 데이터 구조와 간단하지만 효율적인 객체지향프로그래밍을 +지원합니다. 파이썬의 우아(優雅)한 문법과 동적 타이핑, 그리고 인터프리팅 +환경은 파이썬을 스크립팅과 여러 분야에서와 대부분의 플랫폼에서의 빠른 +애플리케이션 개발을 할 수 있는 이상적인 언어로 만들어줍니다. + +☆첫가끝: 날아라 쓩~ 큼! 금없이 전니다. 그런거 다. diff --git a/Lib/test/cjkencodings/iso2022_kr.txt b/Lib/test/cjkencodings/iso2022_kr.txt new file mode 100644 index 00000000000..2cece21c5dd --- /dev/null +++ b/Lib/test/cjkencodings/iso2022_kr.txt @@ -0,0 +1,7 @@ +$)C!] FD@L=c(Python)@: 9h?l1b =10m, 0-7BGQ GA7N1W7!9V >p>n@T4O4Y. FD@L=c@: +H?@2@{@N 0mF(iPd:)GQ 9.9}0z 5?@{ E8@LGN, 1W8.0m @NEMGA8.FC +H/0f@: FD@L=c@; =:E)83FC0z ?)7/ :P>_?!<-?M 4k:N:P@G GC7'F{?!<-@G :|8% +>VGC8.DI@Lp>n7N 885i>nA]4O4Y. + +!YC90!3!: 3/>F6s >1~ E-! 1]>x@L @|4O4Y. 1W710E 4Y. diff --git a/Lib/test/cjkencodings/johab-utf8.txt b/Lib/test/cjkencodings/johab-utf8.txt new file mode 100644 index 00000000000..5655e385176 --- /dev/null +++ b/Lib/test/cjkencodings/johab-utf8.txt @@ -0,0 +1,9 @@ +똠방각하 펲시콜라 + +㉯㉯납!! 因九月패믤릔궈 ⓡⓖ훀¿¿¿ 긍뒙 ⓔ뎨 ㉯. . +亞영ⓔ능횹 . . . . 서울뤄 뎐학乙 家훀 ! ! !ㅠ.ㅠ +흐흐흐 ㄱㄱㄱ☆ㅠ_ㅠ 어릨 탸콰긐 뎌응 칑九들乙 ㉯드긐 +설릌 家훀 . . . . 굴애쉌 ⓔ궈 ⓡ릘㉱긐 因仁川女中까즼 +와쒀훀 ! ! 亞영ⓔ 家능궈 ☆上관 없능궈능 亞능뒈훀 글애듴 +ⓡ려듀九 싀풔숴훀 어릨 因仁川女中싁⑨들앜!! ㉯㉯납♡ ⌒⌒* + diff --git a/Lib/test/cjkencodings/johab.txt b/Lib/test/cjkencodings/johab.txt new file mode 100644 index 00000000000..067781b785a --- /dev/null +++ b/Lib/test/cjkencodings/johab.txt @@ -0,0 +1,9 @@ +wba \ũa + +s!! gÚ zٯٯٯ w ѕ . . +, /)\n" +"--\n" +"\n"); + +#define TEST_OBJECTS_CONVERTER_METHODDEF \ + {"test_objects_converter", _PyCFunction_CAST(test_objects_converter), METH_FASTCALL, test_objects_converter__doc__}, + +static PyObject * +test_objects_converter_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +test_objects_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b = NULL; + + if (!_PyArg_CheckPositional("test_objects_converter", nargs, 1, 2)) { + goto exit; + } + a = args[0]; + if (nargs < 2) { + goto skip_optional; + } + b = args[1]; +skip_optional: + return_value = test_objects_converter_impl(module, a, b); + +exit: + return return_value; +} + +static PyObject * +test_objects_converter_impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=fc26328b79d46bb7 input=4cbb3d9edd2a36f3]*/ + + +/*[clinic input] +test_object_converter_subclass_of + + a: object(subclass_of="&PyLong_Type") + b: object(subclass_of="&PyTuple_Type") + c: object(subclass_of="&PyList_Type") + d: object(subclass_of="&PySet_Type") + e: object(subclass_of="&PyFrozenSet_Type") + f: object(subclass_of="&PyDict_Type") + g: object(subclass_of="&PyUnicode_Type") + h: object(subclass_of="&PyBytes_Type") + i: object(subclass_of="&PyByteArray_Type") + j: object(subclass_of="&MyType") + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_object_converter_subclass_of__doc__, +"test_object_converter_subclass_of($module, a, b, c, d, e, f, g, h, i,\n" +" j, /)\n" +"--\n" +"\n"); + +#define TEST_OBJECT_CONVERTER_SUBCLASS_OF_METHODDEF \ + {"test_object_converter_subclass_of", _PyCFunction_CAST(test_object_converter_subclass_of), METH_FASTCALL, test_object_converter_subclass_of__doc__}, + +static PyObject * +test_object_converter_subclass_of_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d, + PyObject *e, PyObject *f, PyObject *g, + PyObject *h, PyObject *i, PyObject *j); + +static PyObject * +test_object_converter_subclass_of(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + PyObject *e; + PyObject *f; + PyObject *g; + PyObject *h; + PyObject *i; + PyObject *j; + + if (!_PyArg_CheckPositional("test_object_converter_subclass_of", nargs, 10, 10)) { + goto exit; + } + if (!PyLong_Check(args[0])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 1", "int", args[0]); + goto exit; + } + a = args[0]; + if (!PyTuple_Check(args[1])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 2", "tuple", args[1]); + goto exit; + } + b = args[1]; + if (!PyList_Check(args[2])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 3", "list", args[2]); + goto exit; + } + c = args[2]; + if (!PySet_Check(args[3])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 4", "set", args[3]); + goto exit; + } + d = args[3]; + if (!PyFrozenSet_Check(args[4])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 5", "frozenset", args[4]); + goto exit; + } + e = args[4]; + if (!PyDict_Check(args[5])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 6", "dict", args[5]); + goto exit; + } + f = args[5]; + if (!PyUnicode_Check(args[6])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 7", "str", args[6]); + goto exit; + } + g = args[6]; + if (!PyBytes_Check(args[7])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 8", "bytes", args[7]); + goto exit; + } + h = args[7]; + if (!PyByteArray_Check(args[8])) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 9", "bytearray", args[8]); + goto exit; + } + i = args[8]; + if (!PyObject_TypeCheck(args[9], &MyType)) { + _PyArg_BadArgument("test_object_converter_subclass_of", "argument 10", (&MyType)->tp_name, args[9]); + goto exit; + } + j = args[9]; + return_value = test_object_converter_subclass_of_impl(module, a, b, c, d, e, f, g, h, i, j); + +exit: + return return_value; +} + +static PyObject * +test_object_converter_subclass_of_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d, + PyObject *e, PyObject *f, PyObject *g, + PyObject *h, PyObject *i, PyObject *j) +/*[clinic end generated code: output=e4b07c9a54479a40 input=31b06b772d5f983e]*/ + + +/*[clinic input] +test_PyBytesObject_converter + + a: PyBytesObject + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_PyBytesObject_converter__doc__, +"test_PyBytesObject_converter($module, a, /)\n" +"--\n" +"\n"); + +#define TEST_PYBYTESOBJECT_CONVERTER_METHODDEF \ + {"test_PyBytesObject_converter", (PyCFunction)test_PyBytesObject_converter, METH_O, test_PyBytesObject_converter__doc__}, + +static PyObject * +test_PyBytesObject_converter_impl(PyObject *module, PyBytesObject *a); + +static PyObject * +test_PyBytesObject_converter(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyBytesObject *a; + + if (!PyBytes_Check(arg)) { + _PyArg_BadArgument("test_PyBytesObject_converter", "argument", "bytes", arg); + goto exit; + } + a = (PyBytesObject *)arg; + return_value = test_PyBytesObject_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_PyBytesObject_converter_impl(PyObject *module, PyBytesObject *a) +/*[clinic end generated code: output=7539d628e6fceace input=12b10c7cb5750400]*/ + + +/*[clinic input] +test_PyByteArrayObject_converter + + a: PyByteArrayObject + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_PyByteArrayObject_converter__doc__, +"test_PyByteArrayObject_converter($module, a, /)\n" +"--\n" +"\n"); + +#define TEST_PYBYTEARRAYOBJECT_CONVERTER_METHODDEF \ + {"test_PyByteArrayObject_converter", (PyCFunction)test_PyByteArrayObject_converter, METH_O, test_PyByteArrayObject_converter__doc__}, + +static PyObject * +test_PyByteArrayObject_converter_impl(PyObject *module, PyByteArrayObject *a); + +static PyObject * +test_PyByteArrayObject_converter(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyByteArrayObject *a; + + if (!PyByteArray_Check(arg)) { + _PyArg_BadArgument("test_PyByteArrayObject_converter", "argument", "bytearray", arg); + goto exit; + } + a = (PyByteArrayObject *)arg; + return_value = test_PyByteArrayObject_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_PyByteArrayObject_converter_impl(PyObject *module, PyByteArrayObject *a) +/*[clinic end generated code: output=1245af9f5b3e355e input=5a657da535d194ae]*/ + + +/*[clinic input] +test_unicode_converter + + a: unicode + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_unicode_converter__doc__, +"test_unicode_converter($module, a, /)\n" +"--\n" +"\n"); + +#define TEST_UNICODE_CONVERTER_METHODDEF \ + {"test_unicode_converter", (PyCFunction)test_unicode_converter, METH_O, test_unicode_converter__doc__}, + +static PyObject * +test_unicode_converter_impl(PyObject *module, PyObject *a); + +static PyObject * +test_unicode_converter(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_unicode_converter", "argument", "str", arg); + goto exit; + } + a = arg; + return_value = test_unicode_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_unicode_converter_impl(PyObject *module, PyObject *a) +/*[clinic end generated code: output=8c1625cc272c1f3d input=aa33612df92aa9c5]*/ + + +/*[clinic input] +test_bool_converter + + a: bool = True + b: bool(accept={object}) = True + c: bool(accept={int}) = True + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_bool_converter__doc__, +"test_bool_converter($module, a=True, b=True, c=True, /)\n" +"--\n" +"\n"); + +#define TEST_BOOL_CONVERTER_METHODDEF \ + {"test_bool_converter", _PyCFunction_CAST(test_bool_converter), METH_FASTCALL, test_bool_converter__doc__}, + +static PyObject * +test_bool_converter_impl(PyObject *module, int a, int b, int c); + +static PyObject * +test_bool_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int a = 1; + int b = 1; + int c = 1; + + if (!_PyArg_CheckPositional("test_bool_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + a = PyObject_IsTrue(args[0]); + if (a < 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + b = PyObject_IsTrue(args[1]); + if (b < 0) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + c = PyLong_AsInt(args[2]); + if (c == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = test_bool_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_bool_converter_impl(PyObject *module, int a, int b, int c) +/*[clinic end generated code: output=3190e46490de0644 input=939854fa9f248c60]*/ + + +/*[clinic input] +test_char_converter + + a: char = b'A' + b: char = b'\a' + c: char = b'\b' + d: char = b'\t' + e: char = b'\n' + f: char = b'\v' + g: char = b'\f' + h: char = b'\r' + i: char = b'"' + j: char = b"'" + k: char = b'?' + l: char = b'\\' + m: char = b'\000' + n: char = b'\377' + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_char_converter__doc__, +"test_char_converter($module, a=b\'A\', b=b\'\\x07\', c=b\'\\x08\', d=b\'\\t\',\n" +" e=b\'\\n\', f=b\'\\x0b\', g=b\'\\x0c\', h=b\'\\r\', i=b\'\"\',\n" +" j=b\"\'\", k=b\'?\', l=b\'\\\\\', m=b\'\\x00\', n=b\'\\xff\', /)\n" +"--\n" +"\n"); + +#define TEST_CHAR_CONVERTER_METHODDEF \ + {"test_char_converter", _PyCFunction_CAST(test_char_converter), METH_FASTCALL, test_char_converter__doc__}, + +static PyObject * +test_char_converter_impl(PyObject *module, char a, char b, char c, char d, + char e, char f, char g, char h, char i, char j, + char k, char l, char m, char n); + +static PyObject * +test_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + char a = 'A'; + char b = '\x07'; + char c = '\x08'; + char d = '\t'; + char e = '\n'; + char f = '\x0b'; + char g = '\x0c'; + char h = '\r'; + char i = '"'; + char j = '\''; + char k = '?'; + char l = '\\'; + char m = '\x00'; + char n = '\xff'; + + if (!_PyArg_CheckPositional("test_char_converter", nargs, 0, 14)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (PyBytes_Check(args[0]) && PyBytes_GET_SIZE(args[0]) == 1) { + a = PyBytes_AS_STRING(args[0])[0]; + } + else if (PyByteArray_Check(args[0]) && PyByteArray_GET_SIZE(args[0]) == 1) { + a = PyByteArray_AS_STRING(args[0])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 1", "a byte string of length 1", args[0]); + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (PyBytes_Check(args[1]) && PyBytes_GET_SIZE(args[1]) == 1) { + b = PyBytes_AS_STRING(args[1])[0]; + } + else if (PyByteArray_Check(args[1]) && PyByteArray_GET_SIZE(args[1]) == 1) { + b = PyByteArray_AS_STRING(args[1])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 2", "a byte string of length 1", args[1]); + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + if (PyBytes_Check(args[2]) && PyBytes_GET_SIZE(args[2]) == 1) { + c = PyBytes_AS_STRING(args[2])[0]; + } + else if (PyByteArray_Check(args[2]) && PyByteArray_GET_SIZE(args[2]) == 1) { + c = PyByteArray_AS_STRING(args[2])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 3", "a byte string of length 1", args[2]); + goto exit; + } + if (nargs < 4) { + goto skip_optional; + } + if (PyBytes_Check(args[3]) && PyBytes_GET_SIZE(args[3]) == 1) { + d = PyBytes_AS_STRING(args[3])[0]; + } + else if (PyByteArray_Check(args[3]) && PyByteArray_GET_SIZE(args[3]) == 1) { + d = PyByteArray_AS_STRING(args[3])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 4", "a byte string of length 1", args[3]); + goto exit; + } + if (nargs < 5) { + goto skip_optional; + } + if (PyBytes_Check(args[4]) && PyBytes_GET_SIZE(args[4]) == 1) { + e = PyBytes_AS_STRING(args[4])[0]; + } + else if (PyByteArray_Check(args[4]) && PyByteArray_GET_SIZE(args[4]) == 1) { + e = PyByteArray_AS_STRING(args[4])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 5", "a byte string of length 1", args[4]); + goto exit; + } + if (nargs < 6) { + goto skip_optional; + } + if (PyBytes_Check(args[5]) && PyBytes_GET_SIZE(args[5]) == 1) { + f = PyBytes_AS_STRING(args[5])[0]; + } + else if (PyByteArray_Check(args[5]) && PyByteArray_GET_SIZE(args[5]) == 1) { + f = PyByteArray_AS_STRING(args[5])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 6", "a byte string of length 1", args[5]); + goto exit; + } + if (nargs < 7) { + goto skip_optional; + } + if (PyBytes_Check(args[6]) && PyBytes_GET_SIZE(args[6]) == 1) { + g = PyBytes_AS_STRING(args[6])[0]; + } + else if (PyByteArray_Check(args[6]) && PyByteArray_GET_SIZE(args[6]) == 1) { + g = PyByteArray_AS_STRING(args[6])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 7", "a byte string of length 1", args[6]); + goto exit; + } + if (nargs < 8) { + goto skip_optional; + } + if (PyBytes_Check(args[7]) && PyBytes_GET_SIZE(args[7]) == 1) { + h = PyBytes_AS_STRING(args[7])[0]; + } + else if (PyByteArray_Check(args[7]) && PyByteArray_GET_SIZE(args[7]) == 1) { + h = PyByteArray_AS_STRING(args[7])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 8", "a byte string of length 1", args[7]); + goto exit; + } + if (nargs < 9) { + goto skip_optional; + } + if (PyBytes_Check(args[8]) && PyBytes_GET_SIZE(args[8]) == 1) { + i = PyBytes_AS_STRING(args[8])[0]; + } + else if (PyByteArray_Check(args[8]) && PyByteArray_GET_SIZE(args[8]) == 1) { + i = PyByteArray_AS_STRING(args[8])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 9", "a byte string of length 1", args[8]); + goto exit; + } + if (nargs < 10) { + goto skip_optional; + } + if (PyBytes_Check(args[9]) && PyBytes_GET_SIZE(args[9]) == 1) { + j = PyBytes_AS_STRING(args[9])[0]; + } + else if (PyByteArray_Check(args[9]) && PyByteArray_GET_SIZE(args[9]) == 1) { + j = PyByteArray_AS_STRING(args[9])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 10", "a byte string of length 1", args[9]); + goto exit; + } + if (nargs < 11) { + goto skip_optional; + } + if (PyBytes_Check(args[10]) && PyBytes_GET_SIZE(args[10]) == 1) { + k = PyBytes_AS_STRING(args[10])[0]; + } + else if (PyByteArray_Check(args[10]) && PyByteArray_GET_SIZE(args[10]) == 1) { + k = PyByteArray_AS_STRING(args[10])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 11", "a byte string of length 1", args[10]); + goto exit; + } + if (nargs < 12) { + goto skip_optional; + } + if (PyBytes_Check(args[11]) && PyBytes_GET_SIZE(args[11]) == 1) { + l = PyBytes_AS_STRING(args[11])[0]; + } + else if (PyByteArray_Check(args[11]) && PyByteArray_GET_SIZE(args[11]) == 1) { + l = PyByteArray_AS_STRING(args[11])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 12", "a byte string of length 1", args[11]); + goto exit; + } + if (nargs < 13) { + goto skip_optional; + } + if (PyBytes_Check(args[12]) && PyBytes_GET_SIZE(args[12]) == 1) { + m = PyBytes_AS_STRING(args[12])[0]; + } + else if (PyByteArray_Check(args[12]) && PyByteArray_GET_SIZE(args[12]) == 1) { + m = PyByteArray_AS_STRING(args[12])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 13", "a byte string of length 1", args[12]); + goto exit; + } + if (nargs < 14) { + goto skip_optional; + } + if (PyBytes_Check(args[13]) && PyBytes_GET_SIZE(args[13]) == 1) { + n = PyBytes_AS_STRING(args[13])[0]; + } + else if (PyByteArray_Check(args[13]) && PyByteArray_GET_SIZE(args[13]) == 1) { + n = PyByteArray_AS_STRING(args[13])[0]; + } + else { + _PyArg_BadArgument("test_char_converter", "argument 14", "a byte string of length 1", args[13]); + goto exit; + } +skip_optional: + return_value = test_char_converter_impl(module, a, b, c, d, e, f, g, h, i, j, k, l, m, n); + +exit: + return return_value; +} + +static PyObject * +test_char_converter_impl(PyObject *module, char a, char b, char c, char d, + char e, char f, char g, char h, char i, char j, + char k, char l, char m, char n) +/*[clinic end generated code: output=98589f02422fe6b1 input=e42330417a44feac]*/ + + +/*[clinic input] +test_unsigned_char_converter + + a: unsigned_char = 12 + b: unsigned_char(bitwise=False) = 34 + c: unsigned_char(bitwise=True) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_unsigned_char_converter__doc__, +"test_unsigned_char_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_UNSIGNED_CHAR_CONVERTER_METHODDEF \ + {"test_unsigned_char_converter", _PyCFunction_CAST(test_unsigned_char_converter), METH_FASTCALL, test_unsigned_char_converter__doc__}, + +static PyObject * +test_unsigned_char_converter_impl(PyObject *module, unsigned char a, + unsigned char b, unsigned char c); + +static PyObject * +test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned char a = 12; + unsigned char b = 34; + unsigned char c = 56; + + if (!_PyArg_CheckPositional("test_unsigned_char_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + { + long ival = PyLong_AsLong(args[0]); + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + else if (ival < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is less than minimum"); + goto exit; + } + else if (ival > UCHAR_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is greater than maximum"); + goto exit; + } + else { + a = (unsigned char) ival; + } + } + if (nargs < 2) { + goto skip_optional; + } + { + long ival = PyLong_AsLong(args[1]); + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + else if (ival < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is less than minimum"); + goto exit; + } + else if (ival > UCHAR_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is greater than maximum"); + goto exit; + } + else { + b = (unsigned char) ival; + } + } + if (nargs < 3) { + goto skip_optional; + } + { + unsigned long ival = PyLong_AsUnsignedLongMask(args[2]); + if (ival == (unsigned long)-1 && PyErr_Occurred()) { + goto exit; + } + else { + c = (unsigned char) ival; + } + } +skip_optional: + return_value = test_unsigned_char_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_unsigned_char_converter_impl(PyObject *module, unsigned char a, + unsigned char b, unsigned char c) +/*[clinic end generated code: output=45920dbedc22eb55 input=021414060993e289]*/ + + +/*[clinic input] +test_short_converter + + a: short = 12 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_short_converter__doc__, +"test_short_converter($module, a=12, /)\n" +"--\n" +"\n"); + +#define TEST_SHORT_CONVERTER_METHODDEF \ + {"test_short_converter", _PyCFunction_CAST(test_short_converter), METH_FASTCALL, test_short_converter__doc__}, + +static PyObject * +test_short_converter_impl(PyObject *module, short a); + +static PyObject * +test_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + short a = 12; + + if (!_PyArg_CheckPositional("test_short_converter", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + { + long ival = PyLong_AsLong(args[0]); + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + else if (ival < SHRT_MIN) { + PyErr_SetString(PyExc_OverflowError, + "signed short integer is less than minimum"); + goto exit; + } + else if (ival > SHRT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "signed short integer is greater than maximum"); + goto exit; + } + else { + a = (short) ival; + } + } +skip_optional: + return_value = test_short_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_short_converter_impl(PyObject *module, short a) +/*[clinic end generated code: output=a580945bd6963d45 input=6a8a7a509a498ff4]*/ + + +/*[clinic input] +test_unsigned_short_converter + + a: unsigned_short = 12 + b: unsigned_short(bitwise=False) = 34 + c: unsigned_short(bitwise=True) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_unsigned_short_converter__doc__, +"test_unsigned_short_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_UNSIGNED_SHORT_CONVERTER_METHODDEF \ + {"test_unsigned_short_converter", _PyCFunction_CAST(test_unsigned_short_converter), METH_FASTCALL, test_unsigned_short_converter__doc__}, + +static PyObject * +test_unsigned_short_converter_impl(PyObject *module, unsigned short a, + unsigned short b, unsigned short c); + +static PyObject * +test_unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned short a = 12; + unsigned short b = 34; + unsigned short c = 56; + + if (!_PyArg_CheckPositional("test_unsigned_short_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_PyLong_UnsignedShort_Converter(args[0], &a)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (!_PyLong_UnsignedShort_Converter(args[1], &b)) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]); + if (c == (unsigned short)-1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = test_unsigned_short_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_unsigned_short_converter_impl(PyObject *module, unsigned short a, + unsigned short b, unsigned short c) +/*[clinic end generated code: output=e6e990df729114fc input=cdfd8eff3d9176b4]*/ + + +/*[clinic input] +test_int_converter + + a: int = 12 + b: int(accept={int}) = 34 + c: int(accept={str}) = 45 + d: int(type='myenum') = 67 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_int_converter__doc__, +"test_int_converter($module, a=12, b=34, c=45, d=67, /)\n" +"--\n" +"\n"); + +#define TEST_INT_CONVERTER_METHODDEF \ + {"test_int_converter", _PyCFunction_CAST(test_int_converter), METH_FASTCALL, test_int_converter__doc__}, + +static PyObject * +test_int_converter_impl(PyObject *module, int a, int b, int c, myenum d); + +static PyObject * +test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int a = 12; + int b = 34; + int c = 45; + myenum d = 67; + + if (!_PyArg_CheckPositional("test_int_converter", nargs, 0, 4)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + a = PyLong_AsInt(args[0]); + if (a == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + b = PyLong_AsInt(args[1]); + if (b == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + if (!PyUnicode_Check(args[2])) { + _PyArg_BadArgument("test_int_converter", "argument 3", "a unicode character", args[2]); + goto exit; + } + if (PyUnicode_GET_LENGTH(args[2]) != 1) { + _PyArg_BadArgument("test_int_converter", "argument 3", "a unicode character", args[2]); + goto exit; + } + c = PyUnicode_READ_CHAR(args[2], 0); + if (nargs < 4) { + goto skip_optional; + } + d = PyLong_AsInt(args[3]); + if (d == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = test_int_converter_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_int_converter_impl(PyObject *module, int a, int b, int c, myenum d) +/*[clinic end generated code: output=5aed87a7589eefb2 input=d20541fc1ca0553e]*/ + + +/*[clinic input] +test_unsigned_int_converter + + a: unsigned_int = 12 + b: unsigned_int(bitwise=False) = 34 + c: unsigned_int(bitwise=True) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_unsigned_int_converter__doc__, +"test_unsigned_int_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_UNSIGNED_INT_CONVERTER_METHODDEF \ + {"test_unsigned_int_converter", _PyCFunction_CAST(test_unsigned_int_converter), METH_FASTCALL, test_unsigned_int_converter__doc__}, + +static PyObject * +test_unsigned_int_converter_impl(PyObject *module, unsigned int a, + unsigned int b, unsigned int c); + +static PyObject * +test_unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned int a = 12; + unsigned int b = 34; + unsigned int c = 56; + + if (!_PyArg_CheckPositional("test_unsigned_int_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_PyLong_UnsignedInt_Converter(args[0], &a)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (!_PyLong_UnsignedInt_Converter(args[1], &b)) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]); + if (c == (unsigned int)-1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = test_unsigned_int_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_unsigned_int_converter_impl(PyObject *module, unsigned int a, + unsigned int b, unsigned int c) +/*[clinic end generated code: output=f9cdbe410ccc98a3 input=5533534828b62fc0]*/ + + +/*[clinic input] +test_long_converter + + a: long = 12 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_long_converter__doc__, +"test_long_converter($module, a=12, /)\n" +"--\n" +"\n"); + +#define TEST_LONG_CONVERTER_METHODDEF \ + {"test_long_converter", _PyCFunction_CAST(test_long_converter), METH_FASTCALL, test_long_converter__doc__}, + +static PyObject * +test_long_converter_impl(PyObject *module, long a); + +static PyObject * +test_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + long a = 12; + + if (!_PyArg_CheckPositional("test_long_converter", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + a = PyLong_AsLong(args[0]); + if (a == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = test_long_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_long_converter_impl(PyObject *module, long a) +/*[clinic end generated code: output=02b3a83495c1d236 input=d2179e3c9cdcde89]*/ + + +/*[clinic input] +test_unsigned_long_converter + + a: unsigned_long = 12 + b: unsigned_long(bitwise=False) = 34 + c: unsigned_long(bitwise=True) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_unsigned_long_converter__doc__, +"test_unsigned_long_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_UNSIGNED_LONG_CONVERTER_METHODDEF \ + {"test_unsigned_long_converter", _PyCFunction_CAST(test_unsigned_long_converter), METH_FASTCALL, test_unsigned_long_converter__doc__}, + +static PyObject * +test_unsigned_long_converter_impl(PyObject *module, unsigned long a, + unsigned long b, unsigned long c); + +static PyObject * +test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned long a = 12; + unsigned long b = 34; + unsigned long c = 56; + + if (!_PyArg_CheckPositional("test_unsigned_long_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_PyLong_UnsignedLong_Converter(args[0], &a)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (!_PyLong_UnsignedLong_Converter(args[1], &b)) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + if (!PyLong_Check(args[2])) { + _PyArg_BadArgument("test_unsigned_long_converter", "argument 3", "int", args[2]); + goto exit; + } + c = PyLong_AsUnsignedLongMask(args[2]); +skip_optional: + return_value = test_unsigned_long_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_unsigned_long_converter_impl(PyObject *module, unsigned long a, + unsigned long b, unsigned long c) +/*[clinic end generated code: output=540bb0ba2894e1fe input=f450d94cae1ef73b]*/ + + +/*[clinic input] +test_long_long_converter + + a: long_long = 12 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_long_long_converter__doc__, +"test_long_long_converter($module, a=12, /)\n" +"--\n" +"\n"); + +#define TEST_LONG_LONG_CONVERTER_METHODDEF \ + {"test_long_long_converter", _PyCFunction_CAST(test_long_long_converter), METH_FASTCALL, test_long_long_converter__doc__}, + +static PyObject * +test_long_long_converter_impl(PyObject *module, long long a); + +static PyObject * +test_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + long long a = 12; + + if (!_PyArg_CheckPositional("test_long_long_converter", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + a = PyLong_AsLongLong(args[0]); + if (a == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = test_long_long_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_long_long_converter_impl(PyObject *module, long long a) +/*[clinic end generated code: output=f9d4ed79ad2db857 input=d5fc81577ff4dd02]*/ + + +/*[clinic input] +test_unsigned_long_long_converter + + a: unsigned_long_long = 12 + b: unsigned_long_long(bitwise=False) = 34 + c: unsigned_long_long(bitwise=True) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_unsigned_long_long_converter__doc__, +"test_unsigned_long_long_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_UNSIGNED_LONG_LONG_CONVERTER_METHODDEF \ + {"test_unsigned_long_long_converter", _PyCFunction_CAST(test_unsigned_long_long_converter), METH_FASTCALL, test_unsigned_long_long_converter__doc__}, + +static PyObject * +test_unsigned_long_long_converter_impl(PyObject *module, + unsigned long long a, + unsigned long long b, + unsigned long long c); + +static PyObject * +test_unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned long long a = 12; + unsigned long long b = 34; + unsigned long long c = 56; + + if (!_PyArg_CheckPositional("test_unsigned_long_long_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_PyLong_UnsignedLongLong_Converter(args[0], &a)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (!_PyLong_UnsignedLongLong_Converter(args[1], &b)) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + if (!PyLong_Check(args[2])) { + _PyArg_BadArgument("test_unsigned_long_long_converter", "argument 3", "int", args[2]); + goto exit; + } + c = PyLong_AsUnsignedLongLongMask(args[2]); +skip_optional: + return_value = test_unsigned_long_long_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_unsigned_long_long_converter_impl(PyObject *module, + unsigned long long a, + unsigned long long b, + unsigned long long c) +/*[clinic end generated code: output=3d69994f618b46bb input=a15115dc41866ff4]*/ + + +/*[clinic input] +test_Py_ssize_t_converter + + a: Py_ssize_t = 12 + b: Py_ssize_t(accept={int}) = 34 + c: Py_ssize_t(accept={int, NoneType}) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_Py_ssize_t_converter__doc__, +"test_Py_ssize_t_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_PY_SSIZE_T_CONVERTER_METHODDEF \ + {"test_Py_ssize_t_converter", _PyCFunction_CAST(test_Py_ssize_t_converter), METH_FASTCALL, test_Py_ssize_t_converter__doc__}, + +static PyObject * +test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, + Py_ssize_t c); + +static PyObject * +test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t a = 12; + Py_ssize_t b = 34; + Py_ssize_t c = 56; + + if (!_PyArg_CheckPositional("test_Py_ssize_t_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + a = ival; + } + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + b = ival; + } + if (nargs < 3) { + goto skip_optional; + } + if (!_Py_convert_optional_to_ssize_t(args[2], &c)) { + goto exit; + } +skip_optional: + return_value = test_Py_ssize_t_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, + Py_ssize_t c) +/*[clinic end generated code: output=48214bc3d01f4dd7 input=3855f184bb3f299d]*/ + + +/*[clinic input] +test_slice_index_converter + + a: slice_index = 12 + b: slice_index(accept={int}) = 34 + c: slice_index(accept={int, NoneType}) = 56 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_slice_index_converter__doc__, +"test_slice_index_converter($module, a=12, b=34, c=56, /)\n" +"--\n" +"\n"); + +#define TEST_SLICE_INDEX_CONVERTER_METHODDEF \ + {"test_slice_index_converter", _PyCFunction_CAST(test_slice_index_converter), METH_FASTCALL, test_slice_index_converter__doc__}, + +static PyObject * +test_slice_index_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, + Py_ssize_t c); + +static PyObject * +test_slice_index_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t a = 12; + Py_ssize_t b = 34; + Py_ssize_t c = 56; + + if (!_PyArg_CheckPositional("test_slice_index_converter", nargs, 0, 3)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_PyEval_SliceIndex(args[0], &a)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (!_PyEval_SliceIndexNotNone(args[1], &b)) { + goto exit; + } + if (nargs < 3) { + goto skip_optional; + } + if (!_PyEval_SliceIndex(args[2], &c)) { + goto exit; + } +skip_optional: + return_value = test_slice_index_converter_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_slice_index_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, + Py_ssize_t c) +/*[clinic end generated code: output=67506ed999361212 input=edeadb0ee126f531]*/ + + +/*[clinic input] +test_size_t_converter + + a: size_t = 12 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_size_t_converter__doc__, +"test_size_t_converter($module, a=12, /)\n" +"--\n" +"\n"); + +#define TEST_SIZE_T_CONVERTER_METHODDEF \ + {"test_size_t_converter", _PyCFunction_CAST(test_size_t_converter), METH_FASTCALL, test_size_t_converter__doc__}, + +static PyObject * +test_size_t_converter_impl(PyObject *module, size_t a); + +static PyObject * +test_size_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + size_t a = 12; + + if (!_PyArg_CheckPositional("test_size_t_converter", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_PyLong_Size_t_Converter(args[0], &a)) { + goto exit; + } +skip_optional: + return_value = test_size_t_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_size_t_converter_impl(PyObject *module, size_t a) +/*[clinic end generated code: output=1653ecb5cbf775aa input=52e93a0fed0f1fb3]*/ + + +/*[clinic input] +test_float_converter + + a: float = 12.5 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_float_converter__doc__, +"test_float_converter($module, a=12.5, /)\n" +"--\n" +"\n"); + +#define TEST_FLOAT_CONVERTER_METHODDEF \ + {"test_float_converter", _PyCFunction_CAST(test_float_converter), METH_FASTCALL, test_float_converter__doc__}, + +static PyObject * +test_float_converter_impl(PyObject *module, float a); + +static PyObject * +test_float_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + float a = 12.5; + + if (!_PyArg_CheckPositional("test_float_converter", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (PyFloat_CheckExact(args[0])) { + a = (float) (PyFloat_AS_DOUBLE(args[0])); + } + else + { + a = (float) PyFloat_AsDouble(args[0]); + if (a == -1.0 && PyErr_Occurred()) { + goto exit; + } + } +skip_optional: + return_value = test_float_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_float_converter_impl(PyObject *module, float a) +/*[clinic end generated code: output=36ad006990a8a91e input=259c0d98eca35034]*/ + + +/*[clinic input] +test_double_converter + + a: double = 12.5 + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_double_converter__doc__, +"test_double_converter($module, a=12.5, /)\n" +"--\n" +"\n"); + +#define TEST_DOUBLE_CONVERTER_METHODDEF \ + {"test_double_converter", _PyCFunction_CAST(test_double_converter), METH_FASTCALL, test_double_converter__doc__}, + +static PyObject * +test_double_converter_impl(PyObject *module, double a); + +static PyObject * +test_double_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + double a = 12.5; + + if (!_PyArg_CheckPositional("test_double_converter", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (PyFloat_CheckExact(args[0])) { + a = PyFloat_AS_DOUBLE(args[0]); + } + else + { + a = PyFloat_AsDouble(args[0]); + if (a == -1.0 && PyErr_Occurred()) { + goto exit; + } + } +skip_optional: + return_value = test_double_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_double_converter_impl(PyObject *module, double a) +/*[clinic end generated code: output=7435925592bac795 input=c6a9945706a41c27]*/ + + +/*[clinic input] +test_Py_complex_converter + + a: Py_complex + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_Py_complex_converter__doc__, +"test_Py_complex_converter($module, a, /)\n" +"--\n" +"\n"); + +#define TEST_PY_COMPLEX_CONVERTER_METHODDEF \ + {"test_Py_complex_converter", (PyCFunction)test_Py_complex_converter, METH_O, test_Py_complex_converter__doc__}, + +static PyObject * +test_Py_complex_converter_impl(PyObject *module, Py_complex a); + +static PyObject * +test_Py_complex_converter(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_complex a; + + a = PyComplex_AsCComplex(arg); + if (PyErr_Occurred()) { + goto exit; + } + return_value = test_Py_complex_converter_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +test_Py_complex_converter_impl(PyObject *module, Py_complex a) +/*[clinic end generated code: output=c2ecbec2144ca540 input=070f216a515beb79]*/ + + +/*[clinic input] +test_str_converter + + a: str = NULL + b: str = "ab" + c: str(accept={str}) = "cd" + d: str(accept={robuffer}) = "cef" + e: str(accept={str, NoneType}) = "gh" + f: str(accept={robuffer}, zeroes=True) = "ij" + g: str(accept={robuffer, str}, zeroes=True) = "kl" + h: str(accept={robuffer, str, NoneType}, zeroes=True) = "mn" + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_str_converter__doc__, +"test_str_converter($module, a=, b=\'ab\', c=\'cd\',\n" +" d=\'cef\', e=\'gh\', f=\'ij\', g=\'kl\', h=\'mn\', /)\n" +"--\n" +"\n"); + +#define TEST_STR_CONVERTER_METHODDEF \ + {"test_str_converter", _PyCFunction_CAST(test_str_converter), METH_FASTCALL, test_str_converter__doc__}, + +static PyObject * +test_str_converter_impl(PyObject *module, const char *a, const char *b, + const char *c, const char *d, const char *e, + const char *f, Py_ssize_t f_length, const char *g, + Py_ssize_t g_length, const char *h, + Py_ssize_t h_length); + +static PyObject * +test_str_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + const char *a = NULL; + const char *b = "ab"; + const char *c = "cd"; + const char *d = "cef"; + const char *e = "gh"; + const char *f = "ij"; + Py_ssize_t f_length; + const char *g = "kl"; + Py_ssize_t g_length; + const char *h = "mn"; + Py_ssize_t h_length; + + if (!_PyArg_ParseStack(args, nargs, "|sssyzy#s#z#:test_str_converter", + &a, &b, &c, &d, &e, &f, &f_length, &g, &g_length, &h, &h_length)) { + goto exit; + } + return_value = test_str_converter_impl(module, a, b, c, d, e, f, f_length, g, g_length, h, h_length); + +exit: + return return_value; +} + +static PyObject * +test_str_converter_impl(PyObject *module, const char *a, const char *b, + const char *c, const char *d, const char *e, + const char *f, Py_ssize_t f_length, const char *g, + Py_ssize_t g_length, const char *h, + Py_ssize_t h_length) +/*[clinic end generated code: output=82cb06d5237ef062 input=8afe9da8185cd38c]*/ + + +/*[clinic input] +test_str_converter_encoding + + a: str(encoding="idna") + b: str(encoding="idna", accept={str}) + c: str(encoding="idna", accept={bytes, bytearray, str}) + d: str(encoding="idna", zeroes=True) + e: str(encoding="idna", accept={bytes, bytearray, str}, zeroes=True) + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_str_converter_encoding__doc__, +"test_str_converter_encoding($module, a, b, c, d, e, /)\n" +"--\n" +"\n"); + +#define TEST_STR_CONVERTER_ENCODING_METHODDEF \ + {"test_str_converter_encoding", _PyCFunction_CAST(test_str_converter_encoding), METH_FASTCALL, test_str_converter_encoding__doc__}, + +static PyObject * +test_str_converter_encoding_impl(PyObject *module, char *a, char *b, char *c, + char *d, Py_ssize_t d_length, char *e, + Py_ssize_t e_length); + +static PyObject * +test_str_converter_encoding(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + char *a = NULL; + char *b = NULL; + char *c = NULL; + char *d = NULL; + Py_ssize_t d_length; + char *e = NULL; + Py_ssize_t e_length; + + if (!_PyArg_ParseStack(args, nargs, "esesetes#et#:test_str_converter_encoding", + "idna", &a, "idna", &b, "idna", &c, "idna", &d, &d_length, "idna", &e, &e_length)) { + goto exit; + } + return_value = test_str_converter_encoding_impl(module, a, b, c, d, d_length, e, e_length); + /* Post parse cleanup for a */ + PyMem_FREE(a); + /* Post parse cleanup for b */ + PyMem_FREE(b); + /* Post parse cleanup for c */ + PyMem_FREE(c); + /* Post parse cleanup for d */ + PyMem_FREE(d); + /* Post parse cleanup for e */ + PyMem_FREE(e); + +exit: + return return_value; +} + +static PyObject * +test_str_converter_encoding_impl(PyObject *module, char *a, char *b, char *c, + char *d, Py_ssize_t d_length, char *e, + Py_ssize_t e_length) +/*[clinic end generated code: output=999c1deecfa15b0a input=eb4c38e1f898f402]*/ + + +/*[clinic input] +test_Py_UNICODE_converter + + a: Py_UNICODE + b: Py_UNICODE(accept={str}) + c: Py_UNICODE(accept={str, NoneType}) + d: Py_UNICODE(zeroes=True) + e: Py_UNICODE(accept={str, NoneType}, zeroes=True) + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_Py_UNICODE_converter__doc__, +"test_Py_UNICODE_converter($module, a, b, c, d, e, /)\n" +"--\n" +"\n"); + +#define TEST_PY_UNICODE_CONVERTER_METHODDEF \ + {"test_Py_UNICODE_converter", _PyCFunction_CAST(test_Py_UNICODE_converter), METH_FASTCALL, test_Py_UNICODE_converter__doc__}, + +static PyObject * +test_Py_UNICODE_converter_impl(PyObject *module, const wchar_t *a, + const wchar_t *b, const wchar_t *c, + const wchar_t *d, Py_ssize_t d_length, + const wchar_t *e, Py_ssize_t e_length); + +static PyObject * +test_Py_UNICODE_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + const wchar_t *a = NULL; + const wchar_t *b = NULL; + const wchar_t *c = NULL; + const wchar_t *d = NULL; + Py_ssize_t d_length; + const wchar_t *e = NULL; + Py_ssize_t e_length; + + if (!_PyArg_ParseStack(args, nargs, "O&O&O&u#Z#:test_Py_UNICODE_converter", + _PyUnicode_WideCharString_Converter, &a, _PyUnicode_WideCharString_Converter, &b, _PyUnicode_WideCharString_Opt_Converter, &c, &d, &d_length, &e, &e_length)) { + goto exit; + } + return_value = test_Py_UNICODE_converter_impl(module, a, b, c, d, d_length, e, e_length); + +exit: + /* Cleanup for a */ + PyMem_Free((void *)a); + /* Cleanup for b */ + PyMem_Free((void *)b); + /* Cleanup for c */ + PyMem_Free((void *)c); + + return return_value; +} + +static PyObject * +test_Py_UNICODE_converter_impl(PyObject *module, const wchar_t *a, + const wchar_t *b, const wchar_t *c, + const wchar_t *d, Py_ssize_t d_length, + const wchar_t *e, Py_ssize_t e_length) +/*[clinic end generated code: output=529af9cda2a20349 input=064a3b68ad7f04b0]*/ + + +/*[clinic input] +test_Py_buffer_converter + + a: Py_buffer + b: Py_buffer(accept={buffer}) + c: Py_buffer(accept={str, buffer}) + d: Py_buffer(accept={str, buffer, NoneType}) + e: Py_buffer(accept={rwbuffer}) + / + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_Py_buffer_converter__doc__, +"test_Py_buffer_converter($module, a, b, c, d, e, /)\n" +"--\n" +"\n"); + +#define TEST_PY_BUFFER_CONVERTER_METHODDEF \ + {"test_Py_buffer_converter", _PyCFunction_CAST(test_Py_buffer_converter), METH_FASTCALL, test_Py_buffer_converter__doc__}, + +static PyObject * +test_Py_buffer_converter_impl(PyObject *module, Py_buffer *a, Py_buffer *b, + Py_buffer *c, Py_buffer *d, Py_buffer *e); + +static PyObject * +test_Py_buffer_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer a = {NULL, NULL}; + Py_buffer b = {NULL, NULL}; + Py_buffer c = {NULL, NULL}; + Py_buffer d = {NULL, NULL}; + Py_buffer e = {NULL, NULL}; + + if (!_PyArg_ParseStack(args, nargs, "y*y*s*z*w*:test_Py_buffer_converter", + &a, &b, &c, &d, &e)) { + goto exit; + } + return_value = test_Py_buffer_converter_impl(module, &a, &b, &c, &d, &e); + +exit: + /* Cleanup for a */ + if (a.obj) { + PyBuffer_Release(&a); + } + /* Cleanup for b */ + if (b.obj) { + PyBuffer_Release(&b); + } + /* Cleanup for c */ + if (c.obj) { + PyBuffer_Release(&c); + } + /* Cleanup for d */ + if (d.obj) { + PyBuffer_Release(&d); + } + /* Cleanup for e */ + if (e.obj) { + PyBuffer_Release(&e); + } + + return return_value; +} + +static PyObject * +test_Py_buffer_converter_impl(PyObject *module, Py_buffer *a, Py_buffer *b, + Py_buffer *c, Py_buffer *d, Py_buffer *e) +/*[clinic end generated code: output=a153b71b4f45f952 input=6a9da0f56f9525fd]*/ + + +/*[clinic input] +test_keywords + + a: object + b: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_keywords__doc__, +"test_keywords($module, /, a, b)\n" +"--\n" +"\n"); + +#define TEST_KEYWORDS_METHODDEF \ + {"test_keywords", _PyCFunction_CAST(test_keywords), METH_FASTCALL|METH_KEYWORDS, test_keywords__doc__}, + +static PyObject * +test_keywords_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +test_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_keywords", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *a; + PyObject *b; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + return_value = test_keywords_impl(module, a, b); + +exit: + return return_value; +} + +static PyObject * +test_keywords_impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=13ba007e1c842a37 input=0d3484844749c05b]*/ + + +/*[clinic input] +test_keywords_kwonly + + a: object + * + b: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_keywords_kwonly__doc__, +"test_keywords_kwonly($module, /, a, *, b)\n" +"--\n" +"\n"); + +#define TEST_KEYWORDS_KWONLY_METHODDEF \ + {"test_keywords_kwonly", _PyCFunction_CAST(test_keywords_kwonly), METH_FASTCALL|METH_KEYWORDS, test_keywords_kwonly__doc__}, + +static PyObject * +test_keywords_kwonly_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +test_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_keywords_kwonly", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *a; + PyObject *b; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + return_value = test_keywords_kwonly_impl(module, a, b); + +exit: + return return_value; +} + +static PyObject * +test_keywords_kwonly_impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=789799a6d2d6eb4d input=384adc78bfa0bff7]*/ + + +/*[clinic input] +test_keywords_opt + + a: object + b: object = None + c: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_keywords_opt__doc__, +"test_keywords_opt($module, /, a, b=None, c=None)\n" +"--\n" +"\n"); + +#define TEST_KEYWORDS_OPT_METHODDEF \ + {"test_keywords_opt", _PyCFunction_CAST(test_keywords_opt), METH_FASTCALL|METH_KEYWORDS, test_keywords_opt__doc__}, + +static PyObject * +test_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c); + +static PyObject * +test_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_keywords_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + b = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + c = args[2]; +skip_optional_pos: + return_value = test_keywords_opt_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c) +/*[clinic end generated code: output=42430dd8ea5afde6 input=eda7964f784f4607]*/ + + +/*[clinic input] +test_keywords_opt_kwonly + + a: object + b: object = None + * + c: object = None + d: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_keywords_opt_kwonly__doc__, +"test_keywords_opt_kwonly($module, /, a, b=None, *, c=None, d=None)\n" +"--\n" +"\n"); + +#define TEST_KEYWORDS_OPT_KWONLY_METHODDEF \ + {"test_keywords_opt_kwonly", _PyCFunction_CAST(test_keywords_opt_kwonly), METH_FASTCALL|METH_KEYWORDS, test_keywords_opt_kwonly__doc__}, + +static PyObject * +test_keywords_opt_kwonly_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d); + +static PyObject * +test_keywords_opt_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_keywords_opt_kwonly", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + PyObject *d = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + b = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + d = args[3]; +skip_optional_kwonly: + return_value = test_keywords_opt_kwonly_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_keywords_opt_kwonly_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d) +/*[clinic end generated code: output=f312c35c380d2bf9 input=209387a4815e5082]*/ + + +/*[clinic input] +test_keywords_kwonly_opt + + a: object + * + b: object = None + c: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_keywords_kwonly_opt__doc__, +"test_keywords_kwonly_opt($module, /, a, *, b=None, c=None)\n" +"--\n" +"\n"); + +#define TEST_KEYWORDS_KWONLY_OPT_METHODDEF \ + {"test_keywords_kwonly_opt", _PyCFunction_CAST(test_keywords_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_keywords_kwonly_opt__doc__}, + +static PyObject * +test_keywords_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c); + +static PyObject * +test_keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_keywords_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + b = args[1]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + c = args[2]; +skip_optional_kwonly: + return_value = test_keywords_kwonly_opt_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_keywords_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c) +/*[clinic end generated code: output=3937da2a8233ebe0 input=18393cc64fa000f4]*/ + + +/*[clinic input] +test_posonly_keywords + + a: object + / + b: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords__doc__, +"test_posonly_keywords($module, a, /, b)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_METHODDEF \ + {"test_posonly_keywords", _PyCFunction_CAST(test_posonly_keywords), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords__doc__}, + +static PyObject * +test_posonly_keywords_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +test_posonly_keywords(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *a; + PyObject *b; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + return_value = test_posonly_keywords_impl(module, a, b); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=6b4f6dd5f4db3877 input=1767b0ebdf06060e]*/ + + +/*[clinic input] +test_posonly_kwonly + + a: object + / + * + c: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_kwonly__doc__, +"test_posonly_kwonly($module, a, /, *, c)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KWONLY_METHODDEF \ + {"test_posonly_kwonly", _PyCFunction_CAST(test_posonly_kwonly), METH_FASTCALL|METH_KEYWORDS, test_posonly_kwonly__doc__}, + +static PyObject * +test_posonly_kwonly_impl(PyObject *module, PyObject *a, PyObject *c); + +static PyObject * +test_posonly_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_kwonly", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *a; + PyObject *c; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + c = args[1]; + return_value = test_posonly_kwonly_impl(module, a, c); + +exit: + return return_value; +} + +static PyObject * +test_posonly_kwonly_impl(PyObject *module, PyObject *a, PyObject *c) +/*[clinic end generated code: output=8bef2a8198e70b26 input=9042f2818f664839]*/ + + +/*[clinic input] +test_posonly_keywords_kwonly + + a: object + / + b: object + * + c: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_kwonly__doc__, +"test_posonly_keywords_kwonly($module, a, /, b, *, c)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_KWONLY_METHODDEF \ + {"test_posonly_keywords_kwonly", _PyCFunction_CAST(test_posonly_keywords_kwonly), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_kwonly__doc__}, + +static PyObject * +test_posonly_keywords_kwonly_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c); + +static PyObject * +test_posonly_keywords_kwonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_kwonly", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyObject *a; + PyObject *b; + PyObject *c; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + c = args[2]; + return_value = test_posonly_keywords_kwonly_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_kwonly_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c) +/*[clinic end generated code: output=a44b8ae8300955e1 input=29546ebdca492fea]*/ + + +/*[clinic input] +test_posonly_keywords_opt + + a: object + / + b: object + c: object = None + d: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_opt__doc__, +"test_posonly_keywords_opt($module, a, /, b, c=None, d=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_OPT_METHODDEF \ + {"test_posonly_keywords_opt", _PyCFunction_CAST(test_posonly_keywords_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_opt__doc__}, + +static PyObject * +test_posonly_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d); + +static PyObject * +test_posonly_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *a; + PyObject *b; + PyObject *c = Py_None; + PyObject *d = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 4, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + d = args[3]; +skip_optional_pos: + return_value = test_posonly_keywords_opt_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d) +/*[clinic end generated code: output=cae6647c9e8e0238 input=cdf5a9625e554e9b]*/ + + +/*[clinic input] +test_posonly_keywords_opt2 + + a: object + / + b: object = None + c: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_opt2__doc__, +"test_posonly_keywords_opt2($module, a, /, b=None, c=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_OPT2_METHODDEF \ + {"test_posonly_keywords_opt2", _PyCFunction_CAST(test_posonly_keywords_opt2), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_opt2__doc__}, + +static PyObject * +test_posonly_keywords_opt2_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c); + +static PyObject * +test_posonly_keywords_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_opt2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + b = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + c = args[2]; +skip_optional_pos: + return_value = test_posonly_keywords_opt2_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_opt2_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c) +/*[clinic end generated code: output=6526fd08aafa2149 input=1581299d21d16f14]*/ + + +/*[clinic input] +test_posonly_opt_keywords_opt + + a: object + b: object = None + / + c: object = None + d: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_opt_keywords_opt__doc__, +"test_posonly_opt_keywords_opt($module, a, b=None, /, c=None, d=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_OPT_KEYWORDS_OPT_METHODDEF \ + {"test_posonly_opt_keywords_opt", _PyCFunction_CAST(test_posonly_opt_keywords_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_opt_keywords_opt__doc__}, + +static PyObject * +test_posonly_opt_keywords_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d); + +static PyObject * +test_posonly_opt_keywords_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_opt_keywords_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + PyObject *d = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (nargs < 2) { + goto skip_optional_posonly; + } + noptargs--; + b = args[1]; +skip_optional_posonly: + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + d = args[3]; +skip_optional_pos: + return_value = test_posonly_opt_keywords_opt_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_posonly_opt_keywords_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d) +/*[clinic end generated code: output=b8d01e98443738c2 input=408798ec3d42949f]*/ + + +/*[clinic input] +test_posonly_kwonly_opt + + a: object + / + * + b: object + c: object = None + d: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_kwonly_opt__doc__, +"test_posonly_kwonly_opt($module, a, /, *, b, c=None, d=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KWONLY_OPT_METHODDEF \ + {"test_posonly_kwonly_opt", _PyCFunction_CAST(test_posonly_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_kwonly_opt__doc__}, + +static PyObject * +test_posonly_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d); + +static PyObject * +test_posonly_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *a; + PyObject *b; + PyObject *c = Py_None; + PyObject *d = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + d = args[3]; +skip_optional_kwonly: + return_value = test_posonly_kwonly_opt_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_posonly_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d) +/*[clinic end generated code: output=81d71c288f13d4dc input=8d8e5643bbbc2309]*/ + + +/*[clinic input] +test_posonly_kwonly_opt2 + + a: object + / + * + b: object = None + c: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_kwonly_opt2__doc__, +"test_posonly_kwonly_opt2($module, a, /, *, b=None, c=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KWONLY_OPT2_METHODDEF \ + {"test_posonly_kwonly_opt2", _PyCFunction_CAST(test_posonly_kwonly_opt2), METH_FASTCALL|METH_KEYWORDS, test_posonly_kwonly_opt2__doc__}, + +static PyObject * +test_posonly_kwonly_opt2_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c); + +static PyObject * +test_posonly_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_kwonly_opt2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + b = args[1]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + c = args[2]; +skip_optional_kwonly: + return_value = test_posonly_kwonly_opt2_impl(module, a, b, c); + +exit: + return return_value; +} + +static PyObject * +test_posonly_kwonly_opt2_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c) +/*[clinic end generated code: output=a717d2a1a3310289 input=f7e5eed94f75fff0]*/ + + +/*[clinic input] +test_posonly_opt_kwonly_opt + + a: object + b: object = None + / + * + c: object = None + d: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_opt_kwonly_opt__doc__, +"test_posonly_opt_kwonly_opt($module, a, b=None, /, *, c=None, d=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_OPT_KWONLY_OPT_METHODDEF \ + {"test_posonly_opt_kwonly_opt", _PyCFunction_CAST(test_posonly_opt_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_opt_kwonly_opt__doc__}, + +static PyObject * +test_posonly_opt_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d); + +static PyObject * +test_posonly_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_opt_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + PyObject *d = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (nargs < 2) { + goto skip_optional_posonly; + } + noptargs--; + b = args[1]; +skip_optional_posonly: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + d = args[3]; +skip_optional_kwonly: + return_value = test_posonly_opt_kwonly_opt_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_posonly_opt_kwonly_opt_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *c, PyObject *d) +/*[clinic end generated code: output=0f50b4b8d45cf2de input=1e557dc979d120fd]*/ + + +/*[clinic input] +test_posonly_keywords_kwonly_opt + + a: object + / + b: object + * + c: object + d: object = None + e: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_kwonly_opt__doc__, +"test_posonly_keywords_kwonly_opt($module, a, /, b, *, c, d=None, e=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_KWONLY_OPT_METHODDEF \ + {"test_posonly_keywords_kwonly_opt", _PyCFunction_CAST(test_posonly_keywords_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_kwonly_opt__doc__}, + +static PyObject * +test_posonly_keywords_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d, + PyObject *e); + +static PyObject * +test_posonly_keywords_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), _Py_LATIN1_CHR('e'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", "d", "e", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 3; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d = Py_None; + PyObject *e = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + c = args[2]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[3]) { + d = args[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + e = args[4]; +skip_optional_kwonly: + return_value = test_posonly_keywords_kwonly_opt_impl(module, a, b, c, d, e); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d, + PyObject *e) +/*[clinic end generated code: output=8dac8d2a4e6105fa input=c3884a4f956fdc89]*/ + + +/*[clinic input] +test_posonly_keywords_kwonly_opt2 + + a: object + / + b: object + * + c: object = None + d: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_kwonly_opt2__doc__, +"test_posonly_keywords_kwonly_opt2($module, a, /, b, *, c=None, d=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_KWONLY_OPT2_METHODDEF \ + {"test_posonly_keywords_kwonly_opt2", _PyCFunction_CAST(test_posonly_keywords_kwonly_opt2), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_kwonly_opt2__doc__}, + +static PyObject * +test_posonly_keywords_kwonly_opt2_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d); + +static PyObject * +test_posonly_keywords_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_kwonly_opt2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *a; + PyObject *b; + PyObject *c = Py_None; + PyObject *d = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + d = args[3]; +skip_optional_kwonly: + return_value = test_posonly_keywords_kwonly_opt2_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_kwonly_opt2_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, PyObject *d) +/*[clinic end generated code: output=5a96d521e6414f5d input=68d01d7c0f6dafb0]*/ + + +/*[clinic input] +test_posonly_keywords_opt_kwonly_opt + + a: object + / + b: object + c: object = None + * + d: object = None + e: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_opt_kwonly_opt__doc__, +"test_posonly_keywords_opt_kwonly_opt($module, a, /, b, c=None, *,\n" +" d=None, e=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_OPT_KWONLY_OPT_METHODDEF \ + {"test_posonly_keywords_opt_kwonly_opt", _PyCFunction_CAST(test_posonly_keywords_opt_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_opt_kwonly_opt__doc__}, + +static PyObject * +test_posonly_keywords_opt_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e); + +static PyObject * +test_posonly_keywords_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), _Py_LATIN1_CHR('e'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", "d", "e", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_opt_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *a; + PyObject *b; + PyObject *c = Py_None; + PyObject *d = Py_None; + PyObject *e = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[3]) { + d = args[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + e = args[4]; +skip_optional_kwonly: + return_value = test_posonly_keywords_opt_kwonly_opt_impl(module, a, b, c, d, e); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_opt_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e) +/*[clinic end generated code: output=d5a474dcd5dc3e9f input=d0883d45876f186c]*/ + + +/*[clinic input] +test_posonly_keywords_opt2_kwonly_opt + + a: object + / + b: object = None + c: object = None + * + d: object = None + e: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_keywords_opt2_kwonly_opt__doc__, +"test_posonly_keywords_opt2_kwonly_opt($module, a, /, b=None, c=None, *,\n" +" d=None, e=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_KEYWORDS_OPT2_KWONLY_OPT_METHODDEF \ + {"test_posonly_keywords_opt2_kwonly_opt", _PyCFunction_CAST(test_posonly_keywords_opt2_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_keywords_opt2_kwonly_opt__doc__}, + +static PyObject * +test_posonly_keywords_opt2_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e); + +static PyObject * +test_posonly_keywords_opt2_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), _Py_LATIN1_CHR('e'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "b", "c", "d", "e", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_keywords_opt2_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + PyObject *d = Py_None; + PyObject *e = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + b = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[3]) { + d = args[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + e = args[4]; +skip_optional_kwonly: + return_value = test_posonly_keywords_opt2_kwonly_opt_impl(module, a, b, c, d, e); + +exit: + return return_value; +} + +static PyObject * +test_posonly_keywords_opt2_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e) +/*[clinic end generated code: output=ac239c5ee8a74408 input=c95e2e1ec93035ad]*/ + + +/*[clinic input] +test_posonly_opt_keywords_opt_kwonly_opt + + a: object + b: object = None + / + c: object = None + d: object = None + * + e: object = None + f: object = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_posonly_opt_keywords_opt_kwonly_opt__doc__, +"test_posonly_opt_keywords_opt_kwonly_opt($module, a, b=None, /, c=None,\n" +" d=None, *, e=None, f=None)\n" +"--\n" +"\n"); + +#define TEST_POSONLY_OPT_KEYWORDS_OPT_KWONLY_OPT_METHODDEF \ + {"test_posonly_opt_keywords_opt_kwonly_opt", _PyCFunction_CAST(test_posonly_opt_keywords_opt_kwonly_opt), METH_FASTCALL|METH_KEYWORDS, test_posonly_opt_keywords_opt_kwonly_opt__doc__}, + +static PyObject * +test_posonly_opt_keywords_opt_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e, + PyObject *f); + +static PyObject * +test_posonly_opt_keywords_opt_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('c'), _Py_LATIN1_CHR('d'), _Py_LATIN1_CHR('e'), _Py_LATIN1_CHR('f'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "c", "d", "e", "f", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_posonly_opt_keywords_opt_kwonly_opt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *b = Py_None; + PyObject *c = Py_None; + PyObject *d = Py_None; + PyObject *e = Py_None; + PyObject *f = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + if (nargs < 2) { + goto skip_optional_posonly; + } + noptargs--; + b = args[1]; +skip_optional_posonly: + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + c = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + d = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[4]) { + e = args[4]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + f = args[5]; +skip_optional_kwonly: + return_value = test_posonly_opt_keywords_opt_kwonly_opt_impl(module, a, b, c, d, e, f); + +exit: + return return_value; +} + +static PyObject * +test_posonly_opt_keywords_opt_kwonly_opt_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e, + PyObject *f) +/*[clinic end generated code: output=638bbd0005639342 input=9914857713c5bbf8]*/ + +/*[clinic input] +test_keyword_only_parameter + + + * + co_lnotab: PyBytesObject(c_default="(PyBytesObject *)self->co_lnotab") = None + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_keyword_only_parameter__doc__, +"test_keyword_only_parameter($module, /, *, co_lnotab=None)\n" +"--\n" +"\n"); + +#define TEST_KEYWORD_ONLY_PARAMETER_METHODDEF \ + {"test_keyword_only_parameter", _PyCFunction_CAST(test_keyword_only_parameter), METH_FASTCALL|METH_KEYWORDS, test_keyword_only_parameter__doc__}, + +static PyObject * +test_keyword_only_parameter_impl(PyObject *module, PyBytesObject *co_lnotab); + +static PyObject * +test_keyword_only_parameter(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(co_lnotab), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"co_lnotab", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_keyword_only_parameter", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyBytesObject *co_lnotab = (PyBytesObject *)self->co_lnotab; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (!PyBytes_Check(args[0])) { + _PyArg_BadArgument("test_keyword_only_parameter", "argument 'co_lnotab'", "bytes", args[0]); + goto exit; + } + co_lnotab = (PyBytesObject *)args[0]; +skip_optional_kwonly: + return_value = test_keyword_only_parameter_impl(module, co_lnotab); + +exit: + return return_value; +} + +static PyObject * +test_keyword_only_parameter_impl(PyObject *module, PyBytesObject *co_lnotab) +/*[clinic end generated code: output=b12fe2e515a62603 input=303df5046c7e37a3]*/ + + +/*[clinic input] +output push +output preset buffer +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5bff3376ee0df0b5]*/ + +#ifdef CONDITION_A +/*[clinic input] +test_preprocessor_guarded_condition_a +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_condition_a_impl(PyObject *module) +/*[clinic end generated code: output=ad012af18085add6 input=8edb8706a98cda7e]*/ +#elif CONDITION_B +/*[clinic input] +test_preprocessor_guarded_elif_condition_b +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_elif_condition_b_impl(PyObject *module) +/*[clinic end generated code: output=615f2dee82b138d1 input=53777cebbf7fee32]*/ +#else +/*[clinic input] +test_preprocessor_guarded_else +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_else_impl(PyObject *module) +/*[clinic end generated code: output=13af7670aac51b12 input=6657ab31d74c29fc]*/ +#endif + +#ifndef CONDITION_C +/*[clinic input] +test_preprocessor_guarded_ifndef_condition_c +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_ifndef_condition_c_impl(PyObject *module) +/*[clinic end generated code: output=ed422e8c895bb0a5 input=e9b50491cea2b668]*/ +#else +/*[clinic input] +test_preprocessor_guarded_ifndef_not_condition_c +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_ifndef_not_condition_c_impl(PyObject *module) +/*[clinic end generated code: output=de6f4c6a67f8c536 input=da74e30e01c6f2c5]*/ +#endif + +#if \ +CONDITION_D +/*[clinic input] +test_preprocessor_guarded_if_with_continuation +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_if_with_continuation_impl(PyObject *module) +/*[clinic end generated code: output=3d0712ca9e2d15b9 input=4a956fd91be30284]*/ +#endif + +#if CONDITION_E || CONDITION_F +#warning "different type of CPP directive" +/*[clinic input] +test_preprocessor_guarded_if_e_or_f +Makes sure cpp.Monitor handles other directives than preprocessor conditionals. +[clinic start generated code]*/ + +static PyObject * +test_preprocessor_guarded_if_e_or_f_impl(PyObject *module) +/*[clinic end generated code: output=e49d24ff64ad88bc input=57b9c37f938bc4f1]*/ +#endif + +/*[clinic input] +dump buffer +output pop +[clinic start generated code]*/ + +#if defined(CONDITION_A) + +PyDoc_STRVAR(test_preprocessor_guarded_condition_a__doc__, +"test_preprocessor_guarded_condition_a($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF \ + {"test_preprocessor_guarded_condition_a", (PyCFunction)test_preprocessor_guarded_condition_a, METH_NOARGS, test_preprocessor_guarded_condition_a__doc__}, + +static PyObject * +test_preprocessor_guarded_condition_a(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_condition_a_impl(module); +} + +#endif /* defined(CONDITION_A) */ + +#if !defined(CONDITION_A) && (CONDITION_B) + +PyDoc_STRVAR(test_preprocessor_guarded_elif_condition_b__doc__, +"test_preprocessor_guarded_elif_condition_b($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_ELIF_CONDITION_B_METHODDEF \ + {"test_preprocessor_guarded_elif_condition_b", (PyCFunction)test_preprocessor_guarded_elif_condition_b, METH_NOARGS, test_preprocessor_guarded_elif_condition_b__doc__}, + +static PyObject * +test_preprocessor_guarded_elif_condition_b(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_elif_condition_b_impl(module); +} + +#endif /* !defined(CONDITION_A) && (CONDITION_B) */ + +#if !defined(CONDITION_A) && !(CONDITION_B) + +PyDoc_STRVAR(test_preprocessor_guarded_else__doc__, +"test_preprocessor_guarded_else($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF \ + {"test_preprocessor_guarded_else", (PyCFunction)test_preprocessor_guarded_else, METH_NOARGS, test_preprocessor_guarded_else__doc__}, + +static PyObject * +test_preprocessor_guarded_else(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_else_impl(module); +} + +#endif /* !defined(CONDITION_A) && !(CONDITION_B) */ + +#if !defined(CONDITION_C) + +PyDoc_STRVAR(test_preprocessor_guarded_ifndef_condition_c__doc__, +"test_preprocessor_guarded_ifndef_condition_c($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF \ + {"test_preprocessor_guarded_ifndef_condition_c", (PyCFunction)test_preprocessor_guarded_ifndef_condition_c, METH_NOARGS, test_preprocessor_guarded_ifndef_condition_c__doc__}, + +static PyObject * +test_preprocessor_guarded_ifndef_condition_c(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_ifndef_condition_c_impl(module); +} + +#endif /* !defined(CONDITION_C) */ + +#if defined(CONDITION_C) + +PyDoc_STRVAR(test_preprocessor_guarded_ifndef_not_condition_c__doc__, +"test_preprocessor_guarded_ifndef_not_condition_c($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF \ + {"test_preprocessor_guarded_ifndef_not_condition_c", (PyCFunction)test_preprocessor_guarded_ifndef_not_condition_c, METH_NOARGS, test_preprocessor_guarded_ifndef_not_condition_c__doc__}, + +static PyObject * +test_preprocessor_guarded_ifndef_not_condition_c(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_ifndef_not_condition_c_impl(module); +} + +#endif /* defined(CONDITION_C) */ + +#if (CONDITION_D) + +PyDoc_STRVAR(test_preprocessor_guarded_if_with_continuation__doc__, +"test_preprocessor_guarded_if_with_continuation($module, /)\n" +"--\n" +"\n"); + +#define TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF \ + {"test_preprocessor_guarded_if_with_continuation", (PyCFunction)test_preprocessor_guarded_if_with_continuation, METH_NOARGS, test_preprocessor_guarded_if_with_continuation__doc__}, + +static PyObject * +test_preprocessor_guarded_if_with_continuation(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_if_with_continuation_impl(module); +} + +#endif /* (CONDITION_D) */ + +#if (CONDITION_E || CONDITION_F) + +PyDoc_STRVAR(test_preprocessor_guarded_if_e_or_f__doc__, +"test_preprocessor_guarded_if_e_or_f($module, /)\n" +"--\n" +"\n" +"Makes sure cpp.Monitor handles other directives than preprocessor conditionals."); + +#define TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF \ + {"test_preprocessor_guarded_if_e_or_f", (PyCFunction)test_preprocessor_guarded_if_e_or_f, METH_NOARGS, test_preprocessor_guarded_if_e_or_f__doc__}, + +static PyObject * +test_preprocessor_guarded_if_e_or_f(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return test_preprocessor_guarded_if_e_or_f_impl(module); +} + +#endif /* (CONDITION_E || CONDITION_F) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_CONDITION_A_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_ELIF_CONDITION_B_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_ELIF_CONDITION_B_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_ELIF_CONDITION_B_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IFNDEF_CONDITION_C_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IFNDEF_NOT_CONDITION_C_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IF_WITH_CONTINUATION_METHODDEF) */ + +#ifndef TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF + #define TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF +#endif /* !defined(TEST_PREPROCESSOR_GUARDED_IF_E_OR_F_METHODDEF) */ +/*[clinic end generated code: output=fcfae7cac7a99e62 input=3fc80c9989d2f2e1]*/ + +/*[clinic input] +test_vararg_and_posonly + + + a: object + / + *args: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_and_posonly__doc__, +"test_vararg_and_posonly($module, a, /, *args)\n" +"--\n" +"\n"); + +#define TEST_VARARG_AND_POSONLY_METHODDEF \ + {"test_vararg_and_posonly", _PyCFunction_CAST(test_vararg_and_posonly), METH_FASTCALL, test_vararg_and_posonly__doc__}, + +static PyObject * +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); + +static PyObject * +test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { + goto exit; + } + a = args[0]; + __clinic_args = PyTuple_New(nargs - 1); + if (!__clinic_args) { + goto exit; + } + for (Py_ssize_t i = 0; i < nargs - 1; ++i) { + PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[1 + i])); + } + return_value = test_vararg_and_posonly_impl(module, a, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static PyObject * +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) +/*[clinic end generated code: output=79b75dc07decc8d6 input=9cfa748bbff09877]*/ + +/*[clinic input] +test_vararg + + + a: object + *args: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg__doc__, +"test_vararg($module, /, a, *args)\n" +"--\n" +"\n"); + +#define TEST_VARARG_METHODDEF \ + {"test_vararg", _PyCFunction_CAST(test_vararg), METH_FASTCALL|METH_KEYWORDS, test_vararg__doc__}, + +static PyObject * +test_vararg_impl(PyObject *module, PyObject *a, PyObject *args); + +static PyObject * +test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_vararg", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *a; + PyObject *__clinic_args = NULL; + + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + __clinic_args = args[1]; + return_value = test_vararg_impl(module, a, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static PyObject * +test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) +/*[clinic end generated code: output=1411e464f358a7ba input=81d33815ad1bae6e]*/ + +/*[clinic input] +test_vararg_with_default + + + a: object + *args: object + b: bool = False + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_with_default__doc__, +"test_vararg_with_default($module, /, a, *args, b=False)\n" +"--\n" +"\n"); + +#define TEST_VARARG_WITH_DEFAULT_METHODDEF \ + {"test_vararg_with_default", _PyCFunction_CAST(test_vararg_with_default), METH_FASTCALL|METH_KEYWORDS, test_vararg_with_default__doc__}, + +static PyObject * +test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, + int b); + +static PyObject * +test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_vararg_with_default", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *a; + PyObject *__clinic_args = NULL; + int b = 0; + + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + __clinic_args = args[1]; + if (!noptargs) { + goto skip_optional_kwonly; + } + b = PyObject_IsTrue(args[2]); + if (b < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = test_vararg_with_default_impl(module, a, __clinic_args, b); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static PyObject * +test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, + int b) +/*[clinic end generated code: output=f09d4b917063ca41 input=6e110b54acd9b22d]*/ + +/*[clinic input] +test_vararg_with_only_defaults + + + *args: object + b: bool = False + c: object = ' ' + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_with_only_defaults__doc__, +"test_vararg_with_only_defaults($module, /, *args, b=False, c=\' \')\n" +"--\n" +"\n"); + +#define TEST_VARARG_WITH_ONLY_DEFAULTS_METHODDEF \ + {"test_vararg_with_only_defaults", _PyCFunction_CAST(test_vararg_with_only_defaults), METH_FASTCALL|METH_KEYWORDS, test_vararg_with_only_defaults__doc__}, + +static PyObject * +test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, + PyObject *c); + +static PyObject * +test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('b'), _Py_LATIN1_CHR('c'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"b", "c", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_vararg_with_only_defaults", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *__clinic_args = NULL; + int b = 0; + PyObject *c = " "; + + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + if (!args) { + goto exit; + } + __clinic_args = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + b = PyObject_IsTrue(args[1]); + if (b < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + c = args[2]; +skip_optional_kwonly: + return_value = test_vararg_with_only_defaults_impl(module, __clinic_args, b, c); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static PyObject * +test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, + PyObject *c) +/*[clinic end generated code: output=cc6590b8805d5433 input=fa56a709a035666e]*/ + +/*[clinic input] +test_paramname_module + + module as mod: object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_paramname_module__doc__, +"test_paramname_module($module, /, module)\n" +"--\n" +"\n"); + +#define TEST_PARAMNAME_MODULE_METHODDEF \ + {"test_paramname_module", _PyCFunction_CAST(test_paramname_module), METH_FASTCALL|METH_KEYWORDS, test_paramname_module__doc__}, + +static PyObject * +test_paramname_module_impl(PyObject *module, PyObject *mod); + +static PyObject * +test_paramname_module(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(module), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"module", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_paramname_module", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *mod; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + mod = args[0]; + return_value = test_paramname_module_impl(module, mod); + +exit: + return return_value; +} + +static PyObject * +test_paramname_module_impl(PyObject *module, PyObject *mod) +/*[clinic end generated code: output=4a2a849ecbcc8b53 input=afefe259667f13ba]*/ + +/*[clinic input] +mangle1 + + args: object + kwnames: object + return_value: object + _keywords: object + _parser: object + argsbuf: object + fastargs: object + nargs: object + noptargs: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(mangle1__doc__, +"mangle1($module, /, args, kwnames, return_value, _keywords, _parser,\n" +" argsbuf, fastargs, nargs, noptargs)\n" +"--\n" +"\n"); + +#define MANGLE1_METHODDEF \ + {"mangle1", _PyCFunction_CAST(mangle1), METH_FASTCALL|METH_KEYWORDS, mangle1__doc__}, + +static PyObject * +mangle1_impl(PyObject *module, PyObject *args, PyObject *kwnames, + PyObject *return_value, PyObject *_keywords, PyObject *_parser, + PyObject *argsbuf, PyObject *fastargs, PyObject *nargs, + PyObject *noptargs); + +static PyObject * +mangle1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 9 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(args), &_Py_ID(kwnames), &_Py_ID(return_value), &_Py_ID(_keywords), &_Py_ID(_parser), &_Py_ID(argsbuf), &_Py_ID(fastargs), &_Py_ID(nargs), &_Py_ID(noptargs), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"args", "kwnames", "return_value", "_keywords", "_parser", "argsbuf", "fastargs", "nargs", "noptargs", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "mangle1", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[9]; + PyObject *__clinic_args; + PyObject *__clinic_kwnames; + PyObject *__clinic_return_value; + PyObject *__clinic__keywords; + PyObject *__clinic__parser; + PyObject *__clinic_argsbuf; + PyObject *__clinic_fastargs; + PyObject *__clinic_nargs; + PyObject *__clinic_noptargs; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 9, 9, 0, argsbuf); + if (!args) { + goto exit; + } + __clinic_args = args[0]; + __clinic_kwnames = args[1]; + __clinic_return_value = args[2]; + __clinic__keywords = args[3]; + __clinic__parser = args[4]; + __clinic_argsbuf = args[5]; + __clinic_fastargs = args[6]; + __clinic_nargs = args[7]; + __clinic_noptargs = args[8]; + return_value = mangle1_impl(module, __clinic_args, __clinic_kwnames, __clinic_return_value, __clinic__keywords, __clinic__parser, __clinic_argsbuf, __clinic_fastargs, __clinic_nargs, __clinic_noptargs); + +exit: + return return_value; +} + +static PyObject * +mangle1_impl(PyObject *module, PyObject *args, PyObject *kwnames, + PyObject *return_value, PyObject *_keywords, PyObject *_parser, + PyObject *argsbuf, PyObject *fastargs, PyObject *nargs, + PyObject *noptargs) +/*[clinic end generated code: output=083e5076be9987c3 input=a3ed51bdedf8a3c7]*/ + +/*[clinic input] +mangle2 + + args: object + kwargs: object + return_value: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(mangle2__doc__, +"mangle2($module, /, args, kwargs, return_value)\n" +"--\n" +"\n"); + +#define MANGLE2_METHODDEF \ + {"mangle2", _PyCFunction_CAST(mangle2), METH_FASTCALL|METH_KEYWORDS, mangle2__doc__}, + +static PyObject * +mangle2_impl(PyObject *module, PyObject *args, PyObject *kwargs, + PyObject *return_value); + +static PyObject * +mangle2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(args), &_Py_ID(kwargs), &_Py_ID(return_value), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"args", "kwargs", "return_value", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "mangle2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyObject *__clinic_args; + PyObject *__clinic_kwargs; + PyObject *__clinic_return_value; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + if (!args) { + goto exit; + } + __clinic_args = args[0]; + __clinic_kwargs = args[1]; + __clinic_return_value = args[2]; + return_value = mangle2_impl(module, __clinic_args, __clinic_kwargs, __clinic_return_value); + +exit: + return return_value; +} + +static PyObject * +mangle2_impl(PyObject *module, PyObject *args, PyObject *kwargs, + PyObject *return_value) +/*[clinic end generated code: output=2ebb62aaefe7590a input=391766fee51bad7a]*/ + + +/*[clinic input] +Test.cls_with_param + cls: defining_class + / + a: int +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_cls_with_param__doc__, +"cls_with_param($self, /, a)\n" +"--\n" +"\n"); + +#define TEST_CLS_WITH_PARAM_METHODDEF \ + {"cls_with_param", _PyCFunction_CAST(Test_cls_with_param), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test_cls_with_param__doc__}, + +static PyObject * +Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a); + +static PyObject * +Test_cls_with_param(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "cls_with_param", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int a; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + a = PyLong_AsInt(args[0]); + if (a == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = Test_cls_with_param_impl(self, cls, a); + +exit: + return return_value; +} + +static PyObject * +Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a) +/*[clinic end generated code: output=a3a968137b0f320a input=af158077bd237ef9]*/ + + +/*[clinic input] +Test.__init__ +Empty init method. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test()\n" +"--\n" +"\n" +"Empty init method."); + +static int +Test___init___impl(TestObj *self); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = TestType; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoPositional("Test", args)) { + goto exit; + } + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + return_value = Test___init___impl((TestObj *)self); + +exit: + return return_value; +} + +static int +Test___init___impl(TestObj *self) +/*[clinic end generated code: output=f6a35c85bc5b408f input=4ea79fee54d0c3ff]*/ + + +/*[clinic input] +@classmethod +Test.__new__ +Empty new method. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test__doc__, +"Test()\n" +"--\n" +"\n" +"Empty new method."); + +static PyObject * +Test_impl(PyTypeObject *type); + +static PyObject * +Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = TestType; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoPositional("Test", args)) { + goto exit; + } + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + return_value = Test_impl(type); + +exit: + return return_value; +} + +static PyObject * +Test_impl(PyTypeObject *type) +/*[clinic end generated code: output=68a117adc057940f input=6fe98a19f097907f]*/ + + +/*[clinic input] +Test.cls_no_params + cls: defining_class + / +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_cls_no_params__doc__, +"cls_no_params($self, /)\n" +"--\n" +"\n"); + +#define TEST_CLS_NO_PARAMS_METHODDEF \ + {"cls_no_params", _PyCFunction_CAST(Test_cls_no_params), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test_cls_no_params__doc__}, + +static PyObject * +Test_cls_no_params_impl(TestObj *self, PyTypeObject *cls); + +static PyObject * +Test_cls_no_params(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "cls_no_params() takes no arguments"); + return NULL; + } + return Test_cls_no_params_impl(self, cls); +} + +static PyObject * +Test_cls_no_params_impl(TestObj *self, PyTypeObject *cls) +/*[clinic end generated code: output=4d68b4652c144af3 input=e7e2e4e344e96a11]*/ + + +/*[clinic input] +Test.metho_not_default_return_converter -> int + a: object + / +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_metho_not_default_return_converter__doc__, +"metho_not_default_return_converter($self, a, /)\n" +"--\n" +"\n"); + +#define TEST_METHO_NOT_DEFAULT_RETURN_CONVERTER_METHODDEF \ + {"metho_not_default_return_converter", (PyCFunction)Test_metho_not_default_return_converter, METH_O, Test_metho_not_default_return_converter__doc__}, + +static int +Test_metho_not_default_return_converter_impl(TestObj *self, PyObject *a); + +static PyObject * +Test_metho_not_default_return_converter(TestObj *self, PyObject *a) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = Test_metho_not_default_return_converter_impl(self, a); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong((long)_return_value); + +exit: + return return_value; +} + +static int +Test_metho_not_default_return_converter_impl(TestObj *self, PyObject *a) +/*[clinic end generated code: output=3350de11bd538007 input=428657129b521177]*/ + + +/*[clinic input] +Test.an_metho_arg_named_arg + arg: int + Name should be mangled to 'arg_' in generated output. + / +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_an_metho_arg_named_arg__doc__, +"an_metho_arg_named_arg($self, arg, /)\n" +"--\n" +"\n" +"\n" +"\n" +" arg\n" +" Name should be mangled to \'arg_\' in generated output."); + +#define TEST_AN_METHO_ARG_NAMED_ARG_METHODDEF \ + {"an_metho_arg_named_arg", (PyCFunction)Test_an_metho_arg_named_arg, METH_O, Test_an_metho_arg_named_arg__doc__}, + +static PyObject * +Test_an_metho_arg_named_arg_impl(TestObj *self, int arg); + +static PyObject * +Test_an_metho_arg_named_arg(TestObj *self, PyObject *arg_) +{ + PyObject *return_value = NULL; + int arg; + + arg = PyLong_AsInt(arg_); + if (arg == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = Test_an_metho_arg_named_arg_impl(self, arg); + +exit: + return return_value; +} + +static PyObject * +Test_an_metho_arg_named_arg_impl(TestObj *self, int arg) +/*[clinic end generated code: output=9f04de4a62287e28 input=2a53a57cf5624f95]*/ + + +/*[clinic input] +Test.__init__ + *args: object + +Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test(*args)\n" +"--\n" +"\n" +"Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); + +static int +Test___init___impl(TestObj *self, PyObject *args); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = TestType; + PyObject *__clinic_args = NULL; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = PyTuple_GetSlice(0, -1); + return_value = Test___init___impl((TestObj *)self, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static int +Test___init___impl(TestObj *self, PyObject *args) +/*[clinic end generated code: output=0ed1009fe0dcf98d input=2a8bd0033c9ac772]*/ + + +/*[clinic input] +@classmethod +Test.__new__ + *args: object + +Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test__doc__, +"Test(*args)\n" +"--\n" +"\n" +"Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); + +static PyObject * +Test_impl(PyTypeObject *type, PyObject *args); + +static PyObject * +Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyTypeObject *base_tp = TestType; + PyObject *__clinic_args = NULL; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = PyTuple_GetSlice(0, -1); + return_value = Test_impl(type, __clinic_args); + +exit: + Py_XDECREF(__clinic_args); + return return_value; +} + +static PyObject * +Test_impl(PyTypeObject *type, PyObject *args) +/*[clinic end generated code: output=8b219f6633e2a2e9 input=70ad829df3dd9b84]*/ + + +/*[clinic input] +Test.__init__ + a: object +Init method with positional or keyword arguments. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test(a)\n" +"--\n" +"\n" +"Init method with positional or keyword arguments."); + +static int +Test___init___impl(TestObj *self, PyObject *a); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "Test", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *a; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + a = fastargs[0]; + return_value = Test___init___impl((TestObj *)self, a); + +exit: + return return_value; +} + +static int +Test___init___impl(TestObj *self, PyObject *a) +/*[clinic end generated code: output=0e1239b9bc247bc1 input=a8f9222a6ab35c59]*/ + + +/*[clinic input] +@classmethod +Test.class_method +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_class_method__doc__, +"class_method($type, /)\n" +"--\n" +"\n"); + +#define TEST_CLASS_METHOD_METHODDEF \ + {"class_method", (PyCFunction)Test_class_method, METH_NOARGS|METH_CLASS, Test_class_method__doc__}, + +static PyObject * +Test_class_method_impl(PyTypeObject *type); + +static PyObject * +Test_class_method(PyTypeObject *type, PyObject *Py_UNUSED(ignored)) +{ + return Test_class_method_impl(type); +} + +static PyObject * +Test_class_method_impl(PyTypeObject *type) +/*[clinic end generated code: output=47fb7ecca1abcaaa input=43bc4a0494547b80]*/ + + +/*[clinic input] +@staticmethod +Test.static_method +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_static_method__doc__, +"static_method()\n" +"--\n" +"\n"); + +#define TEST_STATIC_METHOD_METHODDEF \ + {"static_method", (PyCFunction)Test_static_method, METH_NOARGS|METH_STATIC, Test_static_method__doc__}, + +static PyObject * +Test_static_method_impl(); + +static PyObject * +Test_static_method(void *null, PyObject *Py_UNUSED(ignored)) +{ + return Test_static_method_impl(); +} + +static PyObject * +Test_static_method_impl() +/*[clinic end generated code: output=82524a63025cf7ab input=dae892fac55ae72b]*/ + + +/*[clinic input] +@coexist +Test.meth_coexist +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_meth_coexist__doc__, +"meth_coexist($self, /)\n" +"--\n" +"\n"); + +#define TEST_METH_COEXIST_METHODDEF \ + {"meth_coexist", (PyCFunction)Test_meth_coexist, METH_NOARGS|METH_COEXIST, Test_meth_coexist__doc__}, + +static PyObject * +Test_meth_coexist_impl(TestObj *self); + +static PyObject * +Test_meth_coexist(TestObj *self, PyObject *Py_UNUSED(ignored)) +{ + return Test_meth_coexist_impl(self); +} + +static PyObject * +Test_meth_coexist_impl(TestObj *self) +/*[clinic end generated code: output=808a293d0cd27439 input=2a1d75b5e6fec6dd]*/ + +/*[clinic input] +@getter +Test.property +[clinic start generated code]*/ + +#if !defined(Test_property_DOCSTR) +# define Test_property_DOCSTR NULL +#endif +#if defined(TEST_PROPERTY_GETSETDEF) +# undef TEST_PROPERTY_GETSETDEF +# define TEST_PROPERTY_GETSETDEF {"property", (getter)Test_property_get, (setter)Test_property_set, Test_property_DOCSTR}, +#else +# define TEST_PROPERTY_GETSETDEF {"property", (getter)Test_property_get, NULL, Test_property_DOCSTR}, +#endif + +static PyObject * +Test_property_get_impl(TestObj *self); + +static PyObject * +Test_property_get(TestObj *self, void *Py_UNUSED(context)) +{ + return Test_property_get_impl(self); +} + +static PyObject * +Test_property_get_impl(TestObj *self) +/*[clinic end generated code: output=7cadd0f539805266 input=2d92b3449fbc7d2b]*/ + +/*[clinic input] +@setter +Test.property +[clinic start generated code]*/ + +#if !defined(Test_property_DOCSTR) +# define Test_property_DOCSTR NULL +#endif +#if defined(TEST_PROPERTY_GETSETDEF) +# undef TEST_PROPERTY_GETSETDEF +# define TEST_PROPERTY_GETSETDEF {"property", (getter)Test_property_get, (setter)Test_property_set, Test_property_DOCSTR}, +#else +# define TEST_PROPERTY_GETSETDEF {"property", NULL, (setter)Test_property_set, NULL}, +#endif + +static int +Test_property_set_impl(TestObj *self, PyObject *value); + +static int +Test_property_set(TestObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + return_value = Test_property_set_impl(self, value); + + return return_value; +} + +static int +Test_property_set_impl(TestObj *self, PyObject *value) +/*[clinic end generated code: output=e4342fe9bb1d7817 input=3bc3f46a23c83a88]*/ + +/*[clinic input] +@setter +Test.setter_first_with_docstr +[clinic start generated code]*/ + +#if !defined(Test_setter_first_with_docstr_DOCSTR) +# define Test_setter_first_with_docstr_DOCSTR NULL +#endif +#if defined(TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF) +# undef TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", (getter)Test_setter_first_with_docstr_get, (setter)Test_setter_first_with_docstr_set, Test_setter_first_with_docstr_DOCSTR}, +#else +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", NULL, (setter)Test_setter_first_with_docstr_set, NULL}, +#endif + +static int +Test_setter_first_with_docstr_set_impl(TestObj *self, PyObject *value); + +static int +Test_setter_first_with_docstr_set(TestObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + return_value = Test_setter_first_with_docstr_set_impl(self, value); + + return return_value; +} + +static int +Test_setter_first_with_docstr_set_impl(TestObj *self, PyObject *value) +/*[clinic end generated code: output=e4d76b558a4061db input=31a045ce11bbe961]*/ + +/*[clinic input] +@getter +Test.setter_first_with_docstr + +my silly docstring +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_setter_first_with_docstr__doc__, +"my silly docstring"); +#if defined(Test_setter_first_with_docstr_DOCSTR) +# undef Test_setter_first_with_docstr_DOCSTR +#endif +#define Test_setter_first_with_docstr_DOCSTR Test_setter_first_with_docstr__doc__ + +#if !defined(Test_setter_first_with_docstr_DOCSTR) +# define Test_setter_first_with_docstr_DOCSTR NULL +#endif +#if defined(TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF) +# undef TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", (getter)Test_setter_first_with_docstr_get, (setter)Test_setter_first_with_docstr_set, Test_setter_first_with_docstr_DOCSTR}, +#else +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", (getter)Test_setter_first_with_docstr_get, NULL, Test_setter_first_with_docstr_DOCSTR}, +#endif + +static PyObject * +Test_setter_first_with_docstr_get_impl(TestObj *self); + +static PyObject * +Test_setter_first_with_docstr_get(TestObj *self, void *Py_UNUSED(context)) +{ + return Test_setter_first_with_docstr_get_impl(self); +} + +static PyObject * +Test_setter_first_with_docstr_get_impl(TestObj *self) +/*[clinic end generated code: output=749a30266f9fb443 input=10af4e43b3cb34dc]*/ + +/*[clinic input] +output push +output preset buffer +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5bff3376ee0df0b5]*/ + +/*[clinic input] +buffer_clear + a: int +We'll call 'destination buffer clear' after this. + +Argument Clinic's buffer preset puts most generated code into the +'buffer' destination, except from 'impl_definition', which is put into +the 'block' destination, so we should expect everything but +'impl_definition' to be cleared. +[clinic start generated code]*/ + +static PyObject * +buffer_clear_impl(PyObject *module, int a) +/*[clinic end generated code: output=f14bba74677e1846 input=a4c308a6fdab043c]*/ + +/*[clinic input] +destination buffer clear +output pop +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f20d06adb8252084]*/ + + +/*[clinic input] +output push +destination test1 new buffer +output everything suppress +output docstring_definition test1 +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5a77c454970992fc]*/ + +/*[clinic input] +new_dest + a: int +Only this docstring should be outputted to test1. +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=da5af421ed8996ed]*/ + +/*[clinic input] +dump test1 +output pop +[clinic start generated code]*/ + +PyDoc_STRVAR(new_dest__doc__, +"new_dest($module, /, a)\n" +"--\n" +"\n" +"Only this docstring should be outputted to test1."); +/*[clinic end generated code: output=9cac703f51d90e84 input=090db8df4945576d]*/ + + +/*[clinic input] +mangled_c_keyword_identifier + i as int: int +The 'int' param should be mangled as 'int_value' +[clinic start generated code]*/ + +PyDoc_STRVAR(mangled_c_keyword_identifier__doc__, +"mangled_c_keyword_identifier($module, /, i)\n" +"--\n" +"\n" +"The \'int\' param should be mangled as \'int_value\'"); + +#define MANGLED_C_KEYWORD_IDENTIFIER_METHODDEF \ + {"mangled_c_keyword_identifier", _PyCFunction_CAST(mangled_c_keyword_identifier), METH_FASTCALL|METH_KEYWORDS, mangled_c_keyword_identifier__doc__}, + +static PyObject * +mangled_c_keyword_identifier_impl(PyObject *module, int int_value); + +static PyObject * +mangled_c_keyword_identifier(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('i'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"i", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "mangled_c_keyword_identifier", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int int_value; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + int_value = PyLong_AsInt(args[0]); + if (int_value == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = mangled_c_keyword_identifier_impl(module, int_value); + +exit: + return return_value; +} + +static PyObject * +mangled_c_keyword_identifier_impl(PyObject *module, int int_value) +/*[clinic end generated code: output=01a8088b57632916 input=060876448ab567a2]*/ + + +/*[clinic input] +bool_return -> bool +[clinic start generated code]*/ + +PyDoc_STRVAR(bool_return__doc__, +"bool_return($module, /)\n" +"--\n" +"\n"); + +#define BOOL_RETURN_METHODDEF \ + {"bool_return", (PyCFunction)bool_return, METH_NOARGS, bool_return__doc__}, + +static int +bool_return_impl(PyObject *module); + +static PyObject * +bool_return(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = bool_return_impl(module); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + +static int +bool_return_impl(PyObject *module) +/*[clinic end generated code: output=3a65f07830e48e98 input=93ba95d39ee98f39]*/ + + +/*[clinic input] +double_return -> double +[clinic start generated code]*/ + +PyDoc_STRVAR(double_return__doc__, +"double_return($module, /)\n" +"--\n" +"\n"); + +#define DOUBLE_RETURN_METHODDEF \ + {"double_return", (PyCFunction)double_return, METH_NOARGS, double_return__doc__}, + +static double +double_return_impl(PyObject *module); + +static PyObject * +double_return(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + double _return_value; + + _return_value = double_return_impl(module); + if ((_return_value == -1.0) && PyErr_Occurred()) { + goto exit; + } + return_value = PyFloat_FromDouble(_return_value); + +exit: + return return_value; +} + +static double +double_return_impl(PyObject *module) +/*[clinic end generated code: output=076dc72595d3f66d input=da11b6255e4cbfd7]*/ + + +/*[clinic input] +Test.__init__ + a: object + [ + b: object + ] + / +Should generate two PyArg_ParseTuple calls. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test___init____doc__, +"Test(a, [b])\n" +"Should generate two PyArg_ParseTuple calls."); + +static int +Test___init___impl(TestObj *self, PyObject *a, int group_right_1, + PyObject *b); + +static int +Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = TestType; + PyObject *a; + int group_right_1 = 0; + PyObject *b = NULL; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("Test", kwargs)) { + goto exit; + } + switch (PyTuple_GET_SIZE(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O:__init__", &a)) { + goto exit; + } + break; + case 2: + if (!PyArg_ParseTuple(args, "OO:__init__", &a, &b)) { + goto exit; + } + group_right_1 = 1; + break; + default: + PyErr_SetString(PyExc_TypeError, "Test.__init__ requires 1 to 2 arguments"); + goto exit; + } + return_value = Test___init___impl((TestObj *)self, a, group_right_1, b); + +exit: + return return_value; +} + +static int +Test___init___impl(TestObj *self, PyObject *a, int group_right_1, + PyObject *b) +/*[clinic end generated code: output=2bbb8ea60e8f57a6 input=10f5d0f1e8e466ef]*/ + + +/*[clinic input] +Test._pyarg_parsestackandkeywords + cls: defining_class + key: str(accept={str, robuffer}, zeroes=True) + / +Check that _PyArg_ParseStackAndKeywords() is generated. +[clinic start generated code]*/ + +PyDoc_STRVAR(Test__pyarg_parsestackandkeywords__doc__, +"_pyarg_parsestackandkeywords($self, key, /)\n" +"--\n" +"\n" +"Check that _PyArg_ParseStackAndKeywords() is generated."); + +#define TEST__PYARG_PARSESTACKANDKEYWORDS_METHODDEF \ + {"_pyarg_parsestackandkeywords", _PyCFunction_CAST(Test__pyarg_parsestackandkeywords), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test__pyarg_parsestackandkeywords__doc__}, + +static PyObject * +Test__pyarg_parsestackandkeywords_impl(TestObj *self, PyTypeObject *cls, + const char *key, + Py_ssize_t key_length); + +static PyObject * +Test__pyarg_parsestackandkeywords(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .format = "s#:_pyarg_parsestackandkeywords", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + const char *key; + Py_ssize_t key_length; + + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + &key, &key_length)) { + goto exit; + } + return_value = Test__pyarg_parsestackandkeywords_impl(self, cls, key, key_length); + +exit: + return return_value; +} + +static PyObject * +Test__pyarg_parsestackandkeywords_impl(TestObj *self, PyTypeObject *cls, + const char *key, + Py_ssize_t key_length) +/*[clinic end generated code: output=4fda8a7f2547137c input=fc72ef4b4cfafabc]*/ + + +/*[clinic input] +fn_with_default_binop_expr + arg: object(c_default='CONST_A + CONST_B') = a+b +[clinic start generated code]*/ + +PyDoc_STRVAR(fn_with_default_binop_expr__doc__, +"fn_with_default_binop_expr($module, /, arg=a+b)\n" +"--\n" +"\n"); + +#define FN_WITH_DEFAULT_BINOP_EXPR_METHODDEF \ + {"fn_with_default_binop_expr", _PyCFunction_CAST(fn_with_default_binop_expr), METH_FASTCALL|METH_KEYWORDS, fn_with_default_binop_expr__doc__}, + +static PyObject * +fn_with_default_binop_expr_impl(PyObject *module, PyObject *arg); + +static PyObject * +fn_with_default_binop_expr(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(arg), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"arg", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "fn_with_default_binop_expr", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *arg = CONST_A + CONST_B; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + arg = args[0]; +skip_optional_pos: + return_value = fn_with_default_binop_expr_impl(module, arg); + +exit: + return return_value; +} + +static PyObject * +fn_with_default_binop_expr_impl(PyObject *module, PyObject *arg) +/*[clinic end generated code: output=018672772e4092ff input=1b55c8ae68d89453]*/ + + +/*[python input] +class Custom_converter(CConverter): + type = "str" + default = "Hello!" + converter = "c_converter_func" +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=d612708f0efb8e3c]*/ + +/*[clinic input] +docstr_fallback_to_converter_default + a: Custom +Check docstring default value fallback. + +Verify that the docstring formatter fetches the default +value from the converter if no 'py_default' is found. +The signature should have the default a='Hello!', +as given by the Custom converter. +[clinic start generated code]*/ + +PyDoc_STRVAR(docstr_fallback_to_converter_default__doc__, +"docstr_fallback_to_converter_default($module, /, a=\'Hello!\')\n" +"--\n" +"\n" +"Check docstring default value fallback.\n" +"\n" +"Verify that the docstring formatter fetches the default\n" +"value from the converter if no \'py_default\' is found.\n" +"The signature should have the default a=\'Hello!\',\n" +"as given by the Custom converter."); + +#define DOCSTR_FALLBACK_TO_CONVERTER_DEFAULT_METHODDEF \ + {"docstr_fallback_to_converter_default", _PyCFunction_CAST(docstr_fallback_to_converter_default), METH_FASTCALL|METH_KEYWORDS, docstr_fallback_to_converter_default__doc__}, + +static PyObject * +docstr_fallback_to_converter_default_impl(PyObject *module, str a); + +static PyObject * +docstr_fallback_to_converter_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { _Py_LATIN1_CHR('a'), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "docstr_fallback_to_converter_default", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + str a; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!c_converter_func(args[0], &a)) { + goto exit; + } + return_value = docstr_fallback_to_converter_default_impl(module, a); + +exit: + return return_value; +} + +static PyObject * +docstr_fallback_to_converter_default_impl(PyObject *module, str a) +/*[clinic end generated code: output=3fff7b702f0065cb input=0cbe6a4d24bc2274]*/ + + +/*[clinic input] +@critical_section +test_critical_section +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section__doc__, +"test_critical_section($module, /)\n" +"--\n" +"\n"); + +#define TEST_CRITICAL_SECTION_METHODDEF \ + {"test_critical_section", (PyCFunction)test_critical_section, METH_NOARGS, test_critical_section__doc__}, + +static PyObject * +test_critical_section_impl(PyObject *module); + +static PyObject * +test_critical_section(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(module); + return_value = test_critical_section_impl(module); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +static PyObject * +test_critical_section_impl(PyObject *module) +/*[clinic end generated code: output=9d5a87bb28aa3f0c input=8c58956d6ff00f80]*/ + + +/*[clinic input] +@critical_section +test_critical_section_meth_o + a: object(subclass_of="&PyUnicode_Type") + / +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_meth_o__doc__, +"test_critical_section_meth_o($module, a, /)\n" +"--\n" +"\n"); + +#define TEST_CRITICAL_SECTION_METH_O_METHODDEF \ + {"test_critical_section_meth_o", (PyCFunction)test_critical_section_meth_o, METH_O, test_critical_section_meth_o__doc__}, + +static PyObject * +test_critical_section_meth_o_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_meth_o(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_meth_o", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(module); + return_value = test_critical_section_meth_o_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_meth_o_impl(PyObject *module, PyObject *a) +/*[clinic end generated code: output=7a9d7420802d1202 input=376533f51eceb6c3]*/ + +/*[clinic input] +@critical_section a +test_critical_section_object + a: object(subclass_of="&PyUnicode_Type") + / +test_critical_section_object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_object__doc__, +"test_critical_section_object($module, a, /)\n" +"--\n" +"\n" +"test_critical_section_object"); + +#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \ + {"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__}, + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_object(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_object", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(a); + return_value = test_critical_section_object_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a) +/*[clinic end generated code: output=ec06df92232b0fb5 input=6f67f91b523c875f]*/ + +PyDoc_STRVAR(test_critical_section_object__doc__, +"test_critical_section_object($module, a, /)\n" +"--\n" +"\n" +"test_critical_section_object"); + +#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \ + {"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__}, + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_object(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_object", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(a); + return_value = test_critical_section_object_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +/*[clinic input] +@critical_section a b +test_critical_section_object2 + a: object(subclass_of="&PyUnicode_Type") + b: object(subclass_of="&PyUnicode_Type") + / +test_critical_section_object2 +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_object2__doc__, +"test_critical_section_object2($module, a, b, /)\n" +"--\n" +"\n" +"test_critical_section_object2"); + +#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \ + {"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__}, + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b); + +static PyObject * +test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]); + goto exit; + } + a = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]); + goto exit; + } + b = args[1]; + Py_BEGIN_CRITICAL_SECTION2(a, b); + return_value = test_critical_section_object2_impl(module, a, b); + Py_END_CRITICAL_SECTION2(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b) +/*[clinic end generated code: output=d73a1657c18df17a input=638824e41419a466]*/ + +PyDoc_STRVAR(test_critical_section_object2__doc__, +"test_critical_section_object2($module, a, b, /)\n" +"--\n" +"\n" +"test_critical_section_object2"); + +#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \ + {"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__}, + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b); + +static PyObject * +test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]); + goto exit; + } + a = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]); + goto exit; + } + b = args[1]; + Py_BEGIN_CRITICAL_SECTION2(a, b); + return_value = test_critical_section_object2_impl(module, a, b); + Py_END_CRITICAL_SECTION2(); + +exit: + return return_value; +} diff --git a/Lib/test/cov.py b/Lib/test/cov.py new file mode 100644 index 00000000000..e4699c7afe1 --- /dev/null +++ b/Lib/test/cov.py @@ -0,0 +1,48 @@ +"""A minimal hook for gathering line coverage of the standard library. + +Designed to be used with -Xpresite= which means: +* it installs itself on import +* it's not imported as `__main__` so can't use the ifmain idiom +* it can't import anything besides `sys` to avoid tainting gathered coverage +* filenames are not normalized + +To get gathered coverage back, look for 'test.cov' in `sys.modules` +instead of importing directly. That way you can determine if the module +was already in use. + +If you need to disable the hook, call the `disable()` function. +""" + +import sys + +mon = sys.monitoring + +FileName = str +LineNo = int +Location = tuple[FileName, LineNo] + +coverage: set[Location] = set() + + +# `types` and `typing` aren't imported to avoid invalid coverage +def add_line( + code: "types.CodeType", + lineno: int, +) -> "typing.Literal[sys.monitoring.DISABLE]": + coverage.add((code.co_filename, lineno)) + return mon.DISABLE + + +def enable(): + mon.use_tool_id(mon.COVERAGE_ID, "regrtest coverage") + mon.register_callback(mon.COVERAGE_ID, mon.events.LINE, add_line) + mon.set_events(mon.COVERAGE_ID, mon.events.LINE) + + +def disable(): + mon.set_events(mon.COVERAGE_ID, 0) + mon.register_callback(mon.COVERAGE_ID, mon.events.LINE, None) + mon.free_tool_id(mon.COVERAGE_ID) + + +enable() diff --git a/Lib/test/crashers/README b/Lib/test/crashers/README new file mode 100644 index 00000000000..7111946b93b --- /dev/null +++ b/Lib/test/crashers/README @@ -0,0 +1,17 @@ +This directory only contains tests for outstanding bugs that cause the +interpreter to segfault. Ideally this directory should always be empty, but +sometimes it may not be easy to fix the underlying cause and the bug is deemed +too obscure to invest the effort. + +Each test should fail when run from the command line: + + ./python Lib/test/crashers/weakref_in_del.py + +Put as much info into a docstring or comments to help determine the cause of the +failure, as well as an issue number or link if it exists. +Particularly note if the cause is system or environment dependent and +what the variables are. + +Once the crash is fixed, the test case should be moved into an appropriate test +(even if it was originally from the test suite). This ensures the regression +doesn't happen again. And if it does, it should be easier to track down. diff --git a/Lib/test/crashers/bogus_code_obj.py b/Lib/test/crashers/bogus_code_obj.py new file mode 100644 index 00000000000..e71b3582cf2 --- /dev/null +++ b/Lib/test/crashers/bogus_code_obj.py @@ -0,0 +1,19 @@ +""" +Broken bytecode objects can easily crash the interpreter. + +This is not going to be fixed. It is generally agreed that there is no +point in writing a bytecode verifier and putting it in CPython just for +this. Moreover, a verifier is bound to accept only a subset of all safe +bytecodes, so it could lead to unnecessary breakage. + +For security purposes, "restricted" interpreters are not going to let +the user build or load random bytecodes anyway. Otherwise, this is a +"won't fix" case. + +""" + +import types + +co = types.CodeType(0, 0, 0, 0, 0, 0, b'\x04\x00\x71\x00', + (), (), (), '', '', 1, b'') +exec(co) diff --git a/Lib/test/crashers/gc_inspection.py b/Lib/test/crashers/gc_inspection.py new file mode 100644 index 00000000000..ae85f97a74a --- /dev/null +++ b/Lib/test/crashers/gc_inspection.py @@ -0,0 +1,32 @@ +""" +gc.get_referrers() can be used to see objects before they are fully built. + +Note that this is only an example. There are many ways to crash Python +by using gc.get_referrers(), as well as many extension modules (even +when they are using perfectly documented patterns to build objects). + +Identifying and removing all places that expose to the GC a +partially-built object is a long-term project. A patch was proposed on +SF specifically for this example but I consider fixing just this single +example a bit pointless (#1517042). + +A fix would include a whole-scale code review, possibly with an API +change to decouple object creation and GC registration, and according +fixes to the documentation for extension module writers. It's unlikely +to happen, though. So this is currently classified as +"gc.get_referrers() is dangerous, use only for debugging". +""" + +import gc + + +def g(): + marker = object() + yield marker + # now the marker is in the tuple being constructed + [tup] = [x for x in gc.get_referrers(marker) if type(x) is tuple] + print(tup) + print(tup[1]) + + +tuple(g()) diff --git a/Lib/test/crashers/infinite_loop_re.py b/Lib/test/crashers/infinite_loop_re.py new file mode 100644 index 00000000000..c84f28d601f --- /dev/null +++ b/Lib/test/crashers/infinite_loop_re.py @@ -0,0 +1,16 @@ + +# This was taken from https://bugs.python.org/issue1541697 +# It's not technically a crasher. It may not even truly be infinite, +# however, I haven't waited a long time to see the result. It takes +# 100% of CPU while running this and should be fixed. + +import re +starttag = re.compile(r'<[a-zA-Z][-_.:a-zA-Z0-9]*\s*(' + r'\s*([a-zA-Z_][-:.a-zA-Z_0-9]*)(\s*=\s*' + r'(\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~@]' + r'[][\-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*(?=[\s>/<])))?' + r')*\s*/?\s*(?=[<>])') + +if __name__ == '__main__': + foo = '>> from test.leakers.test_gestalt import leak +[24275 refs] +>>> leak() +[28936 refs] +>>> leak() +[28938 refs] +>>> leak() +[28940 refs] +>>> + +Once the leak is fixed, the test case should be moved into an appropriate +test (even if it was originally from the test suite). This ensures the +regression doesn't happen again. And if it does, it should be easier +to track down. diff --git a/Lib/test/leakers/__init__.py b/Lib/test/leakers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Lib/test/leakers/test_ctypes.py b/Lib/test/leakers/test_ctypes.py new file mode 100644 index 00000000000..ec09ac36996 --- /dev/null +++ b/Lib/test/leakers/test_ctypes.py @@ -0,0 +1,15 @@ + +# Taken from Lib/test/test_ctypes/test_keeprefs.py, PointerToStructure.test(). + +from ctypes import Structure, c_int, POINTER +import gc + +def leak_inner(): + class POINT(Structure): + _fields_ = [("x", c_int)] + class RECT(Structure): + _fields_ = [("a", POINTER(POINT))] + +def leak(): + leak_inner() + gc.collect() diff --git a/Lib/test/leakers/test_selftype.py b/Lib/test/leakers/test_selftype.py new file mode 100644 index 00000000000..12f29345d2c --- /dev/null +++ b/Lib/test/leakers/test_selftype.py @@ -0,0 +1,13 @@ +# Reference cycles involving only the ob_type field are rather uncommon +# but possible. Inspired by SF bug 1469629. + +import gc + +def leak(): + class T(type): + pass + class U(type, metaclass=T): + pass + U.__class__ = U + del U + gc.collect(); gc.collect(); gc.collect() diff --git a/Lib/test/levenshtein_examples.json b/Lib/test/levenshtein_examples.json new file mode 100644 index 00000000000..a32672cfdba --- /dev/null +++ b/Lib/test/levenshtein_examples.json @@ -0,0 +1,50002 @@ +[ + [ + "", + "", + 0 + ], + [ + "", + "AabBbb", + 12 + ], + [ + "", + "AbaC", + 8 + ], + [ + "", + "B", + 2 + ], + [ + "", + "Ba", + 4 + ], + [ + "", + "CBaACCCa", + 16 + ], + [ + "", + "CCbBC", + 10 + ], + [ + "", + "CcbCbaaAB", + 18 + ], + [ + "", + "bAa", + 6 + ], + [ + "", + "bAaCABb", + 14 + ], + [ + "A", + "A", + 0 + ], + [ + "A", + "AA", + 2 + ], + [ + "A", + "AABCAabcC", + 16 + ], + [ + "A", + "AACB", + 6 + ], + [ + "A", + "AACC", + 6 + ], + [ + "A", + "ABAaBbc", + 12 + ], + [ + "A", + "ABCcC", + 8 + ], + [ + "A", + "ABa", + 4 + ], + [ + "A", + "ABbBabcaa", + 16 + ], + [ + "A", + "ABbbCBcA", + 14 + ], + [ + "A", + "ACCBcBbBb", + 16 + ], + [ + "A", + "ACa", + 4 + ], + [ + "A", + "ACb", + 4 + ], + [ + "A", + "ACcbbaAB", + 14 + ], + [ + "A", + "AaAaccAb", + 14 + ], + [ + "A", + "AaabbCC", + 12 + ], + [ + "A", + "AabB", + 6 + ], + [ + "A", + "AbBa", + 6 + ], + [ + "A", + "AbBaA", + 8 + ], + [ + "A", + "AbCBBCaC", + 14 + ], + [ + "A", + "AbCb", + 6 + ], + [ + "A", + "AbcB", + 6 + ], + [ + "A", + "B", + 2 + ], + [ + "A", + "BA", + 2 + ], + [ + "A", + "BABbCc", + 10 + ], + [ + "A", + "BABca", + 8 + ], + [ + "A", + "BB", + 4 + ], + [ + "A", + "BBaBBaa", + 13 + ], + [ + "A", + "BBbbaAA", + 12 + ], + [ + "A", + "BCCAAc", + 10 + ], + [ + "A", + "BCa", + 5 + ], + [ + "A", + "BCcaC", + 9 + ], + [ + "A", + "BCccaa", + 11 + ], + [ + "A", + "BaacCbC", + 13 + ], + [ + "A", + "BabCC", + 9 + ], + [ + "A", + "Bac", + 5 + ], + [ + "A", + "BbCbb", + 10 + ], + [ + "A", + "BbaBAC", + 10 + ], + [ + "A", + "Bbc", + 6 + ], + [ + "A", + "BcB", + 6 + ], + [ + "A", + "BcCAbCBA", + 14 + ], + [ + "A", + "BcaaCbCB", + 15 + ], + [ + "A", + "C", + 2 + ], + [ + "A", + "CA", + 2 + ], + [ + "A", + "CABAacB", + 12 + ], + [ + "A", + "CABBABBc", + 14 + ], + [ + "A", + "CAaaBc", + 10 + ], + [ + "A", + "CBAA", + 6 + ], + [ + "A", + "CBCcBaBAB", + 16 + ], + [ + "A", + "CBcbc", + 10 + ], + [ + "A", + "CC", + 4 + ], + [ + "A", + "CCA", + 4 + ], + [ + "A", + "CCB", + 6 + ], + [ + "A", + "CCBaACcC", + 14 + ], + [ + "A", + "CCBcAAacb", + 16 + ], + [ + "A", + "CaCCaA", + 10 + ], + [ + "A", + "CbAB", + 6 + ], + [ + "A", + "CbBaAACC", + 14 + ], + [ + "A", + "CbCCbB", + 12 + ], + [ + "A", + "CbacBb", + 11 + ], + [ + "A", + "CbbABc", + 10 + ], + [ + "A", + "CcC", + 6 + ], + [ + "A", + "CcCacAAAC", + 16 + ], + [ + "A", + "CccCbaCcb", + 17 + ], + [ + "A", + "a", + 1 + ], + [ + "A", + "aABABBa", + 12 + ], + [ + "A", + "aABBA", + 8 + ], + [ + "A", + "aABCaBCa", + 14 + ], + [ + "A", + "aACBabAC", + 14 + ], + [ + "A", + "aBCb", + 7 + ], + [ + "A", + "aBaB", + 7 + ], + [ + "A", + "aBbBb", + 9 + ], + [ + "A", + "aC", + 3 + ], + [ + "A", + "aCA", + 4 + ], + [ + "A", + "aaBBb", + 9 + ], + [ + "A", + "aaC", + 5 + ], + [ + "A", + "aaCb", + 7 + ], + [ + "A", + "aaCbABbb", + 14 + ], + [ + "A", + "aaCcbb", + 11 + ], + [ + "A", + "aaaaCcAB", + 14 + ], + [ + "A", + "aaabb", + 9 + ], + [ + "A", + "aaacCabCC", + 17 + ], + [ + "A", + "aacBccCAC", + 16 + ], + [ + "A", + "aacC", + 7 + ], + [ + "A", + "abCAb", + 8 + ], + [ + "A", + "abbBcaccc", + 17 + ], + [ + "A", + "acCAB", + 8 + ], + [ + "A", + "acacBcAA", + 14 + ], + [ + "A", + "b", + 2 + ], + [ + "A", + "bABACaB", + 12 + ], + [ + "A", + "bAbA", + 6 + ], + [ + "A", + "bAbaBc", + 10 + ], + [ + "A", + "bBAa", + 6 + ], + [ + "A", + "bBa", + 5 + ], + [ + "A", + "bBaCAab", + 12 + ], + [ + "A", + "bBaCaCBbB", + 17 + ], + [ + "A", + "bBbAbaBa", + 14 + ], + [ + "A", + "bBbacabAb", + 16 + ], + [ + "A", + "bCCbA", + 8 + ], + [ + "A", + "bCCbB", + 10 + ], + [ + "A", + "bCbbACc", + 12 + ], + [ + "A", + "bCbbc", + 10 + ], + [ + "A", + "baACBCB", + 12 + ], + [ + "A", + "babABb", + 10 + ], + [ + "A", + "bacB", + 7 + ], + [ + "A", + "bacacA", + 10 + ], + [ + "A", + "bb", + 4 + ], + [ + "A", + "bbCa", + 7 + ], + [ + "A", + "bbCbaa", + 11 + ], + [ + "A", + "bbbAcb", + 10 + ], + [ + "A", + "bc", + 4 + ], + [ + "A", + "bcAAcCAb", + 14 + ], + [ + "A", + "bcC", + 6 + ], + [ + "A", + "bcbbaab", + 13 + ], + [ + "A", + "c", + 2 + ], + [ + "A", + "cAB", + 4 + ], + [ + "A", + "cACAc", + 8 + ], + [ + "A", + "cAaBccaC", + 14 + ], + [ + "A", + "cAcAaCAc", + 14 + ], + [ + "A", + "cBAaC", + 8 + ], + [ + "A", + "cBAac", + 8 + ], + [ + "A", + "cBCAABbc", + 14 + ], + [ + "A", + "cBaCbab", + 13 + ], + [ + "A", + "cBacA", + 8 + ], + [ + "A", + "cBb", + 6 + ], + [ + "A", + "cC", + 4 + ], + [ + "A", + "cCBABaC", + 12 + ], + [ + "A", + "cCCCa", + 9 + ], + [ + "A", + "cCa", + 5 + ], + [ + "A", + "cCaAaCaCB", + 16 + ], + [ + "A", + "cCabbbCa", + 15 + ], + [ + "A", + "caacCacAb", + 16 + ], + [ + "A", + "cabbC", + 9 + ], + [ + "A", + "cb", + 4 + ], + [ + "A", + "cbC", + 6 + ], + [ + "A", + "cbCAaAaAB", + 16 + ], + [ + "A", + "cbbaaAbbB", + 16 + ], + [ + "A", + "cc", + 4 + ], + [ + "A", + "ccCcCBaB", + 15 + ], + [ + "A", + "ccabbC", + 11 + ], + [ + "AA", + "AA", + 0 + ], + [ + "AA", + "ACAA", + 4 + ], + [ + "AA", + "BAc", + 4 + ], + [ + "AA", + "BC", + 4 + ], + [ + "AA", + "BaccaAA", + 10 + ], + [ + "AA", + "Bba", + 5 + ], + [ + "AA", + "BbcbcccAB", + 16 + ], + [ + "AA", + "CAb", + 4 + ], + [ + "AA", + "Cc", + 4 + ], + [ + "AA", + "CcBACCa", + 11 + ], + [ + "AA", + "aCBa", + 6 + ], + [ + "AA", + "aaBA", + 5 + ], + [ + "AA", + "aaBac", + 8 + ], + [ + "AA", + "bAAabBA", + 10 + ], + [ + "AA", + "bABbacbb", + 13 + ], + [ + "AA", + "bBbBbc", + 12 + ], + [ + "AA", + "bC", + 4 + ], + [ + "AA", + "bCabb", + 9 + ], + [ + "AA", + "baAB", + 5 + ], + [ + "AA", + "bbBaBcCBB", + 17 + ], + [ + "AA", + "bbBbAaaB", + 13 + ], + [ + "AA", + "c", + 4 + ], + [ + "AA", + "cACaCbab", + 13 + ], + [ + "AA", + "cBbCbBcA", + 14 + ], + [ + "AA", + "cBba", + 7 + ], + [ + "AA", + "cCbBBCca", + 15 + ], + [ + "AA", + "cacaca", + 10 + ], + [ + "AA", + "ccbccAa", + 11 + ], + [ + "AAA", + "B", + 6 + ], + [ + "AAA", + "C", + 6 + ], + [ + "AAA", + "aBBbcAaBa", + 14 + ], + [ + "AAA", + "aacbbBbbc", + 16 + ], + [ + "AAA", + "abAb", + 5 + ], + [ + "AAA", + "cccaAbBcB", + 15 + ], + [ + "AAAAABA", + "CaBcAaa", + 10 + ], + [ + "AAABACbC", + "BBBC", + 11 + ], + [ + "AAABAbaaA", + "CBbbacb", + 12 + ], + [ + "AAABa", + "AA", + 6 + ], + [ + "AAABc", + "cbBabcaBA", + 14 + ], + [ + "AAAC", + "acBcCCcC", + 13 + ], + [ + "AAACAbAaa", + "BCc", + 16 + ], + [ + "AAACBACc", + "CaCabAc", + 10 + ], + [ + "AAACCbbcA", + "bc", + 14 + ], + [ + "AAACa", + "A", + 8 + ], + [ + "AAACbCaa", + "acABAAa", + 9 + ], + [ + "AAACbaaa", + "BBa", + 13 + ], + [ + "AAAa", + "bBaaCc", + 9 + ], + [ + "AAAaAB", + "BCB", + 10 + ], + [ + "AAAaAcC", + "CAbBcBBC", + 12 + ], + [ + "AAAaCAAb", + "aBbAbaaA", + 12 + ], + [ + "AAAaCaA", + "cBcABaAb", + 11 + ], + [ + "AAAaaaaA", + "ABBBcaA", + 10 + ], + [ + "AAAb", + "AcC", + 6 + ], + [ + "AAAb", + "B", + 7 + ], + [ + "AAAbAB", + "cCBaAbcc", + 11 + ], + [ + "AAAbBcbc", + "C", + 15 + ], + [ + "AAAbaAABb", + "cCcAaca", + 15 + ], + [ + "AAAbbaA", + "Cbcc", + 12 + ], + [ + "AAAbbc", + "ABcaCCbca", + 11 + ], + [ + "AAAbcAcBA", + "bBaACAC", + 13 + ], + [ + "AAAbcbCa", + "cabCCbCba", + 10 + ], + [ + "AAAcAcCC", + "cbBCAAB", + 13 + ], + [ + "AAAcBAbCB", + "cbbABC", + 12 + ], + [ + "AAB", + "CB", + 4 + ], + [ + "AAB", + "accAC", + 7 + ], + [ + "AAB", + "bCCbaBbAc", + 15 + ], + [ + "AAB", + "bbb", + 5 + ], + [ + "AAB", + "bcCBcC", + 10 + ], + [ + "AABA", + "AAc", + 4 + ], + [ + "AABA", + "cBCAbA", + 7 + ], + [ + "AABABCcCc", + "abAC", + 12 + ], + [ + "AABAaB", + "BAB", + 6 + ], + [ + "AABAbAa", + "BcaB", + 11 + ], + [ + "AABAcb", + "cca", + 10 + ], + [ + "AABBA", + "AbAaCBb", + 8 + ], + [ + "AABBAACcC", + "A", + 16 + ], + [ + "AABBBcb", + "B", + 12 + ], + [ + "AABBCaB", + "bAbBA", + 8 + ], + [ + "AABBCabC", + "BCC", + 10 + ], + [ + "AABBCcB", + "cABBBAACc", + 10 + ], + [ + "AABBabAA", + "A", + 14 + ], + [ + "AABBac", + "BACCCB", + 10 + ], + [ + "AABBc", + "CAaBaA", + 7 + ], + [ + "AABCb", + "CcaCab", + 8 + ], + [ + "AABCbccb", + "B", + 14 + ], + [ + "AABCcA", + "BabC", + 8 + ], + [ + "AABCcB", + "BAB", + 8 + ], + [ + "AABCca", + "AbBBC", + 7 + ], + [ + "AABCcbBB", + "Bab", + 12 + ], + [ + "AABa", + "ACCcBA", + 7 + ], + [ + "AABa", + "BCcbaAa", + 11 + ], + [ + "AABa", + "C", + 8 + ], + [ + "AABaAc", + "Ccaa", + 9 + ], + [ + "AABaC", + "aA", + 7 + ], + [ + "AABaCccb", + "AABaAAbc", + 8 + ], + [ + "AABabcc", + "ccaABC", + 11 + ], + [ + "AABacB", + "cCbaa", + 9 + ], + [ + "AABacBac", + "AcAa", + 10 + ], + [ + "AABaccb", + "CcbBbbc", + 12 + ], + [ + "AABbBa", + "bBAcbaaB", + 10 + ], + [ + "AABbCc", + "AccbaB", + 8 + ], + [ + "AABba", + "cBbAc", + 7 + ], + [ + "AABbaBa", + "BBaAbb", + 10 + ], + [ + "AABc", + "BccAAA", + 10 + ], + [ + "AABcABaaA", + "cbB", + 14 + ], + [ + "AABcAC", + "acabAC", + 7 + ], + [ + "AABcBbcb", + "baAACAbAB", + 11 + ], + [ + "AABcCB", + "ca", + 10 + ], + [ + "AABcaBC", + "AaAaab", + 8 + ], + [ + "AAC", + "CBabAaB", + 11 + ], + [ + "AAC", + "aBA", + 5 + ], + [ + "AAC", + "aCBCBbac", + 13 + ], + [ + "AAC", + "aCa", + 5 + ], + [ + "AAC", + "baaBAba", + 11 + ], + [ + "AAC", + "bbAbc", + 7 + ], + [ + "AACA", + "aaCB", + 4 + ], + [ + "AACA", + "ab", + 7 + ], + [ + "AACA", + "cc", + 7 + ], + [ + "AACAba", + "CBAAaAB", + 9 + ], + [ + "AACAba", + "b", + 10 + ], + [ + "AACAbbc", + "AbaCABCcB", + 8 + ], + [ + "AACBACbcB", + "b", + 16 + ], + [ + "AACBC", + "Abbc", + 6 + ], + [ + "AACBC", + "aaCcAb", + 8 + ], + [ + "AACBCA", + "A", + 10 + ], + [ + "AACBCcbaa", + "cbBca", + 12 + ], + [ + "AACBaB", + "AAcABBA", + 7 + ], + [ + "AACBc", + "aABCaACA", + 10 + ], + [ + "AACC", + "bB", + 8 + ], + [ + "AACCA", + "b", + 10 + ], + [ + "AACCAC", + "Ccab", + 8 + ], + [ + "AACCBBAac", + "abbabb", + 14 + ], + [ + "AACCCCbc", + "BBCCaB", + 11 + ], + [ + "AACCCc", + "bcCbaBACA", + 14 + ], + [ + "AACCaBcA", + "BaC", + 13 + ], + [ + "AACCaca", + "ccbaBBaB", + 13 + ], + [ + "AACCc", + "b", + 10 + ], + [ + "AACa", + "BAC", + 4 + ], + [ + "AACaAaBA", + "AbcbB", + 11 + ], + [ + "AACaac", + "C", + 10 + ], + [ + "AACacB", + "CcbCBaBb", + 11 + ], + [ + "AACb", + "AC", + 4 + ], + [ + "AACb", + "ac", + 6 + ], + [ + "AACbAC", + "BBCCBCa", + 10 + ], + [ + "AACbC", + "bb", + 8 + ], + [ + "AACbbcaa", + "bbaaBCBb", + 14 + ], + [ + "AACbc", + "AbcbbBcCB", + 11 + ], + [ + "AACbc", + "aCCB", + 6 + ], + [ + "AACbcAaAb", + "BABcaBBAA", + 12 + ], + [ + "AACbcb", + "cabBc", + 8 + ], + [ + "AACc", + "BbCC", + 5 + ], + [ + "AACc", + "bbb", + 8 + ], + [ + "AACcAABAC", + "CBAcBaABa", + 11 + ], + [ + "AACcAb", + "CBCbCACcb", + 11 + ], + [ + "AACcCAAc", + "AbAACbCC", + 11 + ], + [ + "AACcabbC", + "cAAccbAB", + 9 + ], + [ + "AACcbA", + "BCCCcaCc", + 12 + ], + [ + "AAa", + "ACCC", + 6 + ], + [ + "AAa", + "BcAC", + 6 + ], + [ + "AAa", + "CACcaAAAb", + 13 + ], + [ + "AAa", + "CAaCA", + 6 + ], + [ + "AAa", + "aCcBACb", + 11 + ], + [ + "AAa", + "abACAA", + 7 + ], + [ + "AAa", + "b", + 6 + ], + [ + "AAa", + "cAAaBA", + 6 + ], + [ + "AAa", + "cBaB", + 6 + ], + [ + "AAaA", + "ca", + 6 + ], + [ + "AAaACcb", + "ABCcb", + 6 + ], + [ + "AAaB", + "aCAC", + 6 + ], + [ + "AAaB", + "cbAb", + 6 + ], + [ + "AAaBA", + "bBAc", + 8 + ], + [ + "AAaBB", + "ABCCaCBb", + 9 + ], + [ + "AAaBa", + "AcB", + 6 + ], + [ + "AAaBaB", + "aCcb", + 9 + ], + [ + "AAaBacBaC", + "CacABc", + 12 + ], + [ + "AAaBbAb", + "cC", + 14 + ], + [ + "AAaBbcaa", + "caB", + 12 + ], + [ + "AAaC", + "aCbBcBCB", + 13 + ], + [ + "AAaCACAB", + "cBa", + 14 + ], + [ + "AAaCCaCaA", + "baACABCCA", + 10 + ], + [ + "AAaCCcbb", + "aa", + 13 + ], + [ + "AAaCaccb", + "AAA", + 11 + ], + [ + "AAaCbAAAc", + "babcbBccb", + 14 + ], + [ + "AAaCcB", + "aA", + 9 + ], + [ + "AAaa", + "Abcbb", + 8 + ], + [ + "AAaaA", + "ACaCAcaa", + 9 + ], + [ + "AAaaB", + "cAaaAB", + 4 + ], + [ + "AAaaBbaC", + "bbCaCBc", + 12 + ], + [ + "AAaaCBabC", + "bBb", + 14 + ], + [ + "AAaacacca", + "C", + 17 + ], + [ + "AAaaccbA", + "CB", + 14 + ], + [ + "AAabAbac", + "cbABacbbc", + 11 + ], + [ + "AAabBBaA", + "caCcAAB", + 13 + ], + [ + "AAabCA", + "CbACAA", + 9 + ], + [ + "AAabCbCA", + "ACb", + 10 + ], + [ + "AAabb", + "B", + 9 + ], + [ + "AAabc", + "cBc", + 7 + ], + [ + "AAacA", + "CBBcB", + 8 + ], + [ + "AAacA", + "Cc", + 8 + ], + [ + "AAacCBCBc", + "bAaacCc", + 9 + ], + [ + "AAacCaBa", + "a", + 14 + ], + [ + "AAacCbCCC", + "BaB", + 15 + ], + [ + "AAaccABAA", + "bBCb", + 16 + ], + [ + "AAb", + "AbB", + 3 + ], + [ + "AAb", + "B", + 5 + ], + [ + "AAb", + "aCBbBBa", + 11 + ], + [ + "AAb", + "bAAc", + 4 + ], + [ + "AAb", + "bABA", + 5 + ], + [ + "AAb", + "bBCbBbaCA", + 16 + ], + [ + "AAb", + "bbBBaB", + 10 + ], + [ + "AAb", + "bbCcAb", + 8 + ], + [ + "AAbA", + "Bbb", + 6 + ], + [ + "AAbAAaBCC", + "b", + 16 + ], + [ + "AAbABB", + "Ca", + 11 + ], + [ + "AAbAaBBcB", + "BABaCB", + 10 + ], + [ + "AAbBBb", + "a", + 11 + ], + [ + "AAbBCCb", + "bA", + 12 + ], + [ + "AAbBbBcC", + "C", + 14 + ], + [ + "AAbBbbBcc", + "aCA", + 16 + ], + [ + "AAbC", + "cBbBCBBb", + 12 + ], + [ + "AAbCbBcC", + "BaABcCaCc", + 11 + ], + [ + "AAbCcCbC", + "a", + 15 + ], + [ + "AAba", + "cBCabCC", + 11 + ], + [ + "AAba", + "cBcBCcaBc", + 15 + ], + [ + "AAbaCabC", + "Aa", + 12 + ], + [ + "AAbabb", + "cbBAca", + 10 + ], + [ + "AAbb", + "AcbaaAB", + 9 + ], + [ + "AAbb", + "bccb", + 6 + ], + [ + "AAbbAcAB", + "cbacaBBBA", + 14 + ], + [ + "AAbbBAaAC", + "bcbaabc", + 11 + ], + [ + "AAbbbB", + "bCa", + 10 + ], + [ + "AAbcBbA", + "aaccCbB", + 8 + ], + [ + "AAbcaBBBA", + "ABb", + 13 + ], + [ + "AAbcacaB", + "cAAAbBb", + 12 + ], + [ + "AAbcbacC", + "abbbAaCCa", + 10 + ], + [ + "AAc", + "ACAc", + 2 + ], + [ + "AAc", + "AbB", + 4 + ], + [ + "AAc", + "BAabA", + 7 + ], + [ + "AAc", + "aAbc", + 3 + ], + [ + "AAc", + "aaaaacAA", + 12 + ], + [ + "AAc", + "aab", + 4 + ], + [ + "AAc", + "c", + 4 + ], + [ + "AAcA", + "AAbAAA", + 6 + ], + [ + "AAcABCA", + "bBBAbab", + 11 + ], + [ + "AAcAC", + "cCAAa", + 8 + ], + [ + "AAcAac", + "cAcA", + 6 + ], + [ + "AAcAbBcB", + "AAAAB", + 8 + ], + [ + "AAcAccAb", + "Ccac", + 11 + ], + [ + "AAcBBBcCB", + "A", + 16 + ], + [ + "AAcBbA", + "Aa", + 9 + ], + [ + "AAcC", + "bCbcaCCa", + 12 + ], + [ + "AAcCabaB", + "cACAC", + 11 + ], + [ + "AAcaACCBA", + "abcBCca", + 11 + ], + [ + "AAcaCBAcB", + "cCaBCcC", + 11 + ], + [ + "AAcab", + "A", + 8 + ], + [ + "AAcab", + "AcaabCccc", + 12 + ], + [ + "AAcb", + "aCBCcCBcC", + 14 + ], + [ + "AAcbAccc", + "CcccbA", + 12 + ], + [ + "AAcbCCAcB", + "BbbCaACc", + 11 + ], + [ + "AAcbCca", + "a", + 12 + ], + [ + "AAcbc", + "caCcb", + 7 + ], + [ + "AAcc", + "CcaBbcC", + 10 + ], + [ + "AAccB", + "cCca", + 7 + ], + [ + "AAccb", + "aA", + 7 + ], + [ + "AAccccbAA", + "caAaa", + 14 + ], + [ + "AB", + "A", + 2 + ], + [ + "AB", + "AACca", + 8 + ], + [ + "AB", + "ABcAc", + 6 + ], + [ + "AB", + "ACCACCbC", + 13 + ], + [ + "AB", + "BAA", + 4 + ], + [ + "AB", + "BAbac", + 7 + ], + [ + "AB", + "BC", + 4 + ], + [ + "AB", + "BbaCAc", + 10 + ], + [ + "AB", + "Bbac", + 7 + ], + [ + "AB", + "BcA", + 6 + ], + [ + "AB", + "CAAacB", + 8 + ], + [ + "AB", + "CC", + 4 + ], + [ + "AB", + "CCaabBcb", + 13 + ], + [ + "AB", + "CCcB", + 6 + ], + [ + "AB", + "CbA", + 5 + ], + [ + "AB", + "aCAC", + 6 + ], + [ + "AB", + "bBA", + 4 + ], + [ + "AB", + "bCbB", + 6 + ], + [ + "AB", + "bCccbbb", + 13 + ], + [ + "AB", + "bbb", + 5 + ], + [ + "AB", + "bcAbAAA", + 11 + ], + [ + "AB", + "bcbC", + 7 + ], + [ + "AB", + "c", + 4 + ], + [ + "AB", + "cCabccbCB", + 15 + ], + [ + "AB", + "caCBB", + 7 + ], + [ + "AB", + "caaac", + 9 + ], + [ + "AB", + "cababCa", + 12 + ], + [ + "ABA", + "A", + 4 + ], + [ + "ABA", + "ACBcBa", + 7 + ], + [ + "ABA", + "AcAb", + 4 + ], + [ + "ABA", + "abbBaaC", + 10 + ], + [ + "ABA", + "c", + 6 + ], + [ + "ABA", + "caAaCCbc", + 13 + ], + [ + "ABAAB", + "cbB", + 7 + ], + [ + "ABAABacbC", + "Ba", + 14 + ], + [ + "ABAAaCAa", + "cABb", + 14 + ], + [ + "ABAAaaBb", + "BCACaB", + 8 + ], + [ + "ABAAabb", + "bAcABc", + 9 + ], + [ + "ABAAabbCC", + "cbcBc", + 15 + ], + [ + "ABAAbBCb", + "aCACb", + 9 + ], + [ + "ABAAc", + "AbCc", + 5 + ], + [ + "ABAAcC", + "A", + 10 + ], + [ + "ABABBCaBb", + "aAcBcc", + 12 + ], + [ + "ABABCAB", + "cbBBCaA", + 8 + ], + [ + "ABABaaCc", + "BBBBBAB", + 11 + ], + [ + "ABABbCCab", + "aAC", + 13 + ], + [ + "ABAC", + "abaAC", + 4 + ], + [ + "ABACAccCC", + "BBCBcbCAc", + 11 + ], + [ + "ABACCAACA", + "aAacA", + 11 + ], + [ + "ABACaa", + "BA", + 8 + ], + [ + "ABACb", + "b", + 8 + ], + [ + "ABACbaCcC", + "cCBBbCbC", + 12 + ], + [ + "ABAa", + "A", + 6 + ], + [ + "ABAaAbbc", + "ccaCBA", + 13 + ], + [ + "ABAaB", + "bb", + 8 + ], + [ + "ABAaCCAB", + "acba", + 12 + ], + [ + "ABAaaBCBB", + "BaCC", + 12 + ], + [ + "ABAacbAbA", + "ABAb", + 10 + ], + [ + "ABAb", + "AcA", + 4 + ], + [ + "ABAb", + "cCab", + 5 + ], + [ + "ABAbACCB", + "cBCB", + 10 + ], + [ + "ABAbBC", + "BBAacabC", + 9 + ], + [ + "ABAbCCb", + "BAA", + 10 + ], + [ + "ABAbaCBB", + "aaCbbbBB", + 9 + ], + [ + "ABAbaCcC", + "bac", + 10 + ], + [ + "ABAc", + "CBACCbBA", + 11 + ], + [ + "ABAcAcB", + "Ac", + 10 + ], + [ + "ABAca", + "aCcCaAbC", + 12 + ], + [ + "ABAcaBc", + "bCaabABAb", + 14 + ], + [ + "ABAcaac", + "BcA", + 9 + ], + [ + "ABAcabA", + "aa", + 11 + ], + [ + "ABAcb", + "cCa", + 9 + ], + [ + "ABB", + "acaccCcA", + 15 + ], + [ + "ABB", + "bAaAAC", + 10 + ], + [ + "ABB", + "bBbAA", + 7 + ], + [ + "ABB", + "bac", + 6 + ], + [ + "ABBAAaCc", + "CaCaCAaCc", + 9 + ], + [ + "ABBAAbb", + "acBbCba", + 9 + ], + [ + "ABBAAc", + "bBccabAcB", + 11 + ], + [ + "ABBAbbAA", + "abAaaa", + 10 + ], + [ + "ABBAcBaa", + "Ab", + 13 + ], + [ + "ABBAcbAc", + "abBccbA", + 6 + ], + [ + "ABBBAa", + "ccBbcBa", + 9 + ], + [ + "ABBBAbAcc", + "abCaCAbcc", + 10 + ], + [ + "ABBBB", + "cAaCBCbCb", + 12 + ], + [ + "ABBBCaB", + "BaAbBabBa", + 12 + ], + [ + "ABBCC", + "aBBbaBAab", + 13 + ], + [ + "ABBCCc", + "aAcAbB", + 11 + ], + [ + "ABBCCcab", + "AcBcbcbAc", + 10 + ], + [ + "ABBa", + "bA", + 6 + ], + [ + "ABBaACA", + "cbBcAB", + 9 + ], + [ + "ABBaACCcb", + "bbCCcCb", + 10 + ], + [ + "ABBaa", + "bcabCCC", + 12 + ], + [ + "ABBabAbAC", + "AbcbbAcCc", + 10 + ], + [ + "ABBaba", + "BCcbAaBCA", + 13 + ], + [ + "ABBacb", + "CAbcBAbBa", + 11 + ], + [ + "ABBb", + "Aabc", + 5 + ], + [ + "ABBb", + "aAcAb", + 6 + ], + [ + "ABBbBAbCA", + "CCBACAAb", + 14 + ], + [ + "ABBbCbb", + "bccbcAc", + 11 + ], + [ + "ABBbb", + "aAabbCbCC", + 11 + ], + [ + "ABBcBBC", + "bCaB", + 10 + ], + [ + "ABBcCCC", + "bABbaCCC", + 5 + ], + [ + "ABBcb", + "aCc", + 7 + ], + [ + "ABBcbcC", + "AACBbAC", + 8 + ], + [ + "ABBccBcAa", + "ABa", + 12 + ], + [ + "ABBccCBA", + "CCCACa", + 12 + ], + [ + "ABBccc", + "B", + 10 + ], + [ + "ABC", + "BAaACA", + 8 + ], + [ + "ABC", + "BCc", + 4 + ], + [ + "ABC", + "CAa", + 6 + ], + [ + "ABC", + "CBca", + 5 + ], + [ + "ABC", + "CbCbbCB", + 11 + ], + [ + "ABC", + "bacacB", + 10 + ], + [ + "ABC", + "c", + 5 + ], + [ + "ABCAB", + "bAaAc", + 8 + ], + [ + "ABCAC", + "BaCBaC", + 7 + ], + [ + "ABCAa", + "aABbb", + 8 + ], + [ + "ABCAbAacA", + "cbCAa", + 11 + ], + [ + "ABCBC", + "AB", + 6 + ], + [ + "ABCBa", + "CcAACBC", + 8 + ], + [ + "ABCBa", + "caBbccC", + 10 + ], + [ + "ABCCAa", + "A", + 10 + ], + [ + "ABCCC", + "CC", + 6 + ], + [ + "ABCCbcBc", + "AbCBcBB", + 6 + ], + [ + "ABCCcCBB", + "bbccAbBBc", + 11 + ], + [ + "ABCCccaB", + "aCcACCCa", + 10 + ], + [ + "ABCaBa", + "BBb", + 8 + ], + [ + "ABCaBa", + "bbAcAA", + 10 + ], + [ + "ABCaC", + "bcCcab", + 8 + ], + [ + "ABCaa", + "AbCcaAb", + 6 + ], + [ + "ABCab", + "b", + 8 + ], + [ + "ABCabB", + "AAc", + 9 + ], + [ + "ABCb", + "CBBBC", + 7 + ], + [ + "ABCb", + "aABCcBC", + 7 + ], + [ + "ABCbAA", + "Ba", + 9 + ], + [ + "ABCbBBbc", + "cabbbCbcB", + 11 + ], + [ + "ABCbC", + "cc", + 8 + ], + [ + "ABCbCaCCc", + "aABB", + 15 + ], + [ + "ABCbaaCbb", + "CBbBBABBB", + 12 + ], + [ + "ABCbaaac", + "aaCaCCCca", + 13 + ], + [ + "ABCbbA", + "BCaAa", + 7 + ], + [ + "ABCbcC", + "ccbBBa", + 11 + ], + [ + "ABCc", + "Aa", + 6 + ], + [ + "ABCcAabbB", + "cCCaBBc", + 11 + ], + [ + "ABCcBcbC", + "Bcbcc", + 8 + ], + [ + "ABCcb", + "CacBAb", + 9 + ], + [ + "ABCccaAcC", + "BaAcBbC", + 12 + ], + [ + "ABa", + "AaabCa", + 7 + ], + [ + "ABa", + "CaA", + 5 + ], + [ + "ABaABc", + "caC", + 9 + ], + [ + "ABaAC", + "Caca", + 8 + ], + [ + "ABaAb", + "bAaC", + 7 + ], + [ + "ABaB", + "c", + 8 + ], + [ + "ABaBa", + "aacCAc", + 10 + ], + [ + "ABaBb", + "acbaBCcb", + 8 + ], + [ + "ABaBccB", + "BacbBBBcb", + 11 + ], + [ + "ABaCAb", + "aba", + 8 + ], + [ + "ABaCCC", + "aCAaacbc", + 10 + ], + [ + "ABaCc", + "bB", + 8 + ], + [ + "ABaa", + "CCcCC", + 10 + ], + [ + "ABabAbb", + "BCAcB", + 9 + ], + [ + "ABabBC", + "acc", + 9 + ], + [ + "ABabBaAc", + "AbBCbaBBB", + 12 + ], + [ + "ABababAC", + "aabaaBAA", + 8 + ], + [ + "ABabb", + "bBb", + 6 + ], + [ + "ABabcBacC", + "aC", + 14 + ], + [ + "ABabcBcc", + "BAcB", + 9 + ], + [ + "ABacCac", + "bbaCBcbcB", + 11 + ], + [ + "ABacbAAab", + "bC", + 16 + ], + [ + "ABacbbaCC", + "CbCbcbBAA", + 13 + ], + [ + "ABb", + "AaAcB", + 7 + ], + [ + "ABb", + "B", + 4 + ], + [ + "ABb", + "BB", + 3 + ], + [ + "ABb", + "BBaCCAAA", + 14 + ], + [ + "ABb", + "bBBaA", + 7 + ], + [ + "ABb", + "baCaaBaa", + 13 + ], + [ + "ABbA", + "CaACAABa", + 12 + ], + [ + "ABbACAC", + "BAcBBaB", + 12 + ], + [ + "ABbACAaAa", + "Cca", + 14 + ], + [ + "ABbAaa", + "cbacaa", + 7 + ], + [ + "ABbAaaB", + "BBc", + 11 + ], + [ + "ABbAacbAC", + "bAbAaacaa", + 11 + ], + [ + "ABbAbA", + "aaAaB", + 9 + ], + [ + "ABbBAb", + "a", + 11 + ], + [ + "ABbBcCbcA", + "acaAacBAB", + 15 + ], + [ + "ABbCC", + "caACcc", + 9 + ], + [ + "ABbCaAbb", + "b", + 14 + ], + [ + "ABbCb", + "aABCBBca", + 10 + ], + [ + "ABbCbA", + "A", + 10 + ], + [ + "ABbCbA", + "AbCbB", + 4 + ], + [ + "ABbCbbBa", + "B", + 14 + ], + [ + "ABbCcB", + "AaCccab", + 8 + ], + [ + "ABbaACBCc", + "baBCbCbcB", + 11 + ], + [ + "ABbaabcCC", + "aaaacAAAA", + 15 + ], + [ + "ABbacAAA", + "CAAccb", + 13 + ], + [ + "ABbb", + "cAaC", + 8 + ], + [ + "ABbbA", + "BCcbb", + 8 + ], + [ + "ABbbC", + "ABBc", + 4 + ], + [ + "ABbbbB", + "BCabaCc", + 12 + ], + [ + "ABbc", + "bBcAAac", + 10 + ], + [ + "ABbcc", + "AbBABaaa", + 11 + ], + [ + "ABbccBA", + "cACaCcCC", + 11 + ], + [ + "ABc", + "a", + 5 + ], + [ + "ABc", + "aA", + 5 + ], + [ + "ABc", + "b", + 5 + ], + [ + "ABcA", + "CB", + 6 + ], + [ + "ABcAa", + "BcbAC", + 6 + ], + [ + "ABcAaB", + "ab", + 9 + ], + [ + "ABcAbAa", + "AcAABbb", + 9 + ], + [ + "ABcAc", + "c", + 8 + ], + [ + "ABcAcCAc", + "cAcBcCaC", + 8 + ], + [ + "ABcBBcCCB", + "BaAcBbC", + 12 + ], + [ + "ABcBbAb", + "aAbACcAca", + 13 + ], + [ + "ABcBcCCbB", + "cbAC", + 13 + ], + [ + "ABcC", + "Cabc", + 6 + ], + [ + "ABcC", + "c", + 6 + ], + [ + "ABcCAaCc", + "C", + 14 + ], + [ + "ABcCAbCba", + "CcBa", + 12 + ], + [ + "ABcCBb", + "CAb", + 8 + ], + [ + "ABcCCa", + "bCaca", + 7 + ], + [ + "ABcCCcBa", + "Cbc", + 12 + ], + [ + "ABcCbbBbC", + "b", + 16 + ], + [ + "ABcaBC", + "Bcc", + 7 + ], + [ + "ABcaBb", + "CB", + 9 + ], + [ + "ABcb", + "a", + 7 + ], + [ + "ABcbAcc", + "AbBa", + 9 + ], + [ + "ABcbBC", + "cCcAa", + 10 + ], + [ + "ABcbCacCb", + "ba", + 14 + ], + [ + "ABcc", + "bAAcbCa", + 9 + ], + [ + "ABccA", + "CA", + 7 + ], + [ + "ABcccb", + "CB", + 10 + ], + [ + "AC", + "A", + 2 + ], + [ + "AC", + "AAb", + 4 + ], + [ + "AC", + "AB", + 2 + ], + [ + "AC", + "ABBc", + 5 + ], + [ + "AC", + "ABaaaBc", + 11 + ], + [ + "AC", + "ACBaAbb", + 10 + ], + [ + "AC", + "AaBcBC", + 8 + ], + [ + "AC", + "AaCCCB", + 8 + ], + [ + "AC", + "AabB", + 6 + ], + [ + "AC", + "AacAaBc", + 11 + ], + [ + "AC", + "AbBCacbA", + 12 + ], + [ + "AC", + "AbbbcbACb", + 14 + ], + [ + "AC", + "B", + 4 + ], + [ + "AC", + "BBbA", + 8 + ], + [ + "AC", + "BCaa", + 6 + ], + [ + "AC", + "BbCaaA", + 10 + ], + [ + "AC", + "BcAab", + 8 + ], + [ + "AC", + "C", + 2 + ], + [ + "AC", + "CAcb", + 5 + ], + [ + "AC", + "Ca", + 4 + ], + [ + "AC", + "CabAB", + 8 + ], + [ + "AC", + "aABaABAB", + 14 + ], + [ + "AC", + "aAacbbcAC", + 14 + ], + [ + "AC", + "aBCaCBba", + 13 + ], + [ + "AC", + "aa", + 3 + ], + [ + "AC", + "aaaBBabAb", + 16 + ], + [ + "AC", + "baccAbcB", + 13 + ], + [ + "AC", + "c", + 3 + ], + [ + "AC", + "cACCaBA", + 10 + ], + [ + "AC", + "cCaaAacaA", + 15 + ], + [ + "AC", + "cbcbbcC", + 12 + ], + [ + "ACA", + "Aa", + 3 + ], + [ + "ACA", + "C", + 4 + ], + [ + "ACA", + "CbaaBc", + 11 + ], + [ + "ACA", + "aAABAAcb", + 12 + ], + [ + "ACA", + "cACCbaAaC", + 12 + ], + [ + "ACAA", + "AcaAAb", + 5 + ], + [ + "ACAABBaC", + "A", + 14 + ], + [ + "ACAABcA", + "bCBACacC", + 10 + ], + [ + "ACAAC", + "bB", + 10 + ], + [ + "ACAAC", + "cBaaCabaC", + 11 + ], + [ + "ACAAa", + "CAa", + 4 + ], + [ + "ACAAaCaA", + "AAacb", + 9 + ], + [ + "ACAAbcaAa", + "bba", + 14 + ], + [ + "ACABA", + "bAB", + 6 + ], + [ + "ACABA", + "c", + 9 + ], + [ + "ACABABA", + "CcC", + 12 + ], + [ + "ACABACaB", + "bcBAaAB", + 8 + ], + [ + "ACABbCaBc", + "cA", + 15 + ], + [ + "ACAC", + "aaBAA", + 7 + ], + [ + "ACACB", + "aaCcbaBC", + 11 + ], + [ + "ACACBB", + "cCb", + 8 + ], + [ + "ACACBbccA", + "BABbAc", + 10 + ], + [ + "ACACC", + "BaAA", + 8 + ], + [ + "ACACa", + "Bccc", + 8 + ], + [ + "ACACbB", + "aCaacAaC", + 11 + ], + [ + "ACACbab", + "cC", + 11 + ], + [ + "ACAaA", + "Aaabbc", + 9 + ], + [ + "ACAaB", + "caBBcB", + 9 + ], + [ + "ACAaBA", + "CAbCC", + 8 + ], + [ + "ACAaCBccb", + "c", + 16 + ], + [ + "ACAabbCAc", + "BB", + 16 + ], + [ + "ACAb", + "AaaCC", + 7 + ], + [ + "ACAbBCaCA", + "A", + 16 + ], + [ + "ACAbbaaBA", + "aacacABbc", + 15 + ], + [ + "ACAbcB", + "bcBCB", + 7 + ], + [ + "ACAc", + "CCbcaabA", + 12 + ], + [ + "ACAcBCc", + "AcaAbcb", + 8 + ], + [ + "ACAcaabA", + "CACbBC", + 10 + ], + [ + "ACB", + "ACbbaBb", + 8 + ], + [ + "ACB", + "aCbbc", + 6 + ], + [ + "ACB", + "baBacBAB", + 12 + ], + [ + "ACB", + "cabbCcBA", + 11 + ], + [ + "ACBA", + "CbCCCaCAc", + 14 + ], + [ + "ACBAC", + "CaABbbcBA", + 13 + ], + [ + "ACBACBB", + "cAABCbBC", + 9 + ], + [ + "ACBACCba", + "ACBCcCb", + 5 + ], + [ + "ACBAbCBc", + "bC", + 12 + ], + [ + "ACBAba", + "aAaCaC", + 10 + ], + [ + "ACBAcb", + "aCACc", + 6 + ], + [ + "ACBB", + "bAbb", + 6 + ], + [ + "ACBBAB", + "BbBCb", + 8 + ], + [ + "ACBBABBa", + "bbabA", + 11 + ], + [ + "ACBBBaA", + "bccbA", + 10 + ], + [ + "ACBBBaCA", + "bCCCB", + 12 + ], + [ + "ACBBcAccc", + "ab", + 16 + ], + [ + "ACBCCC", + "a", + 11 + ], + [ + "ACBCbaa", + "ACCAbCCb", + 10 + ], + [ + "ACBaA", + "BcBcBcA", + 9 + ], + [ + "ACBaBa", + "AaAa", + 6 + ], + [ + "ACBabaa", + "abcAbbb", + 10 + ], + [ + "ACBabbab", + "CAa", + 11 + ], + [ + "ACBac", + "BcbcCb", + 9 + ], + [ + "ACBb", + "bcCAca", + 10 + ], + [ + "ACBbCaaBB", + "Cc", + 15 + ], + [ + "ACBba", + "CcaAbbc", + 10 + ], + [ + "ACBbaAbAC", + "aCabbAC", + 7 + ], + [ + "ACBbcAcB", + "cb", + 13 + ], + [ + "ACBcAB", + "cb", + 9 + ], + [ + "ACBcaaAAb", + "ACBA", + 10 + ], + [ + "ACC", + "BcccCaAba", + 15 + ], + [ + "ACC", + "Ccb", + 5 + ], + [ + "ACC", + "aB", + 5 + ], + [ + "ACC", + "bBcABBbAC", + 14 + ], + [ + "ACC", + "cABA", + 6 + ], + [ + "ACC", + "caACC", + 4 + ], + [ + "ACCA", + "BaaBc", + 9 + ], + [ + "ACCA", + "BacAabAAa", + 14 + ], + [ + "ACCACABB", + "ABAAbBaBa", + 11 + ], + [ + "ACCAbB", + "CcbAacC", + 11 + ], + [ + "ACCAccAB", + "ACbCC", + 10 + ], + [ + "ACCBAAC", + "Ac", + 11 + ], + [ + "ACCBAaBC", + "a", + 14 + ], + [ + "ACCBB", + "BcCccabbA", + 13 + ], + [ + "ACCBBcac", + "CCB", + 10 + ], + [ + "ACCBCBC", + "AcaaCacba", + 11 + ], + [ + "ACCBac", + "aCACaCc", + 7 + ], + [ + "ACCBb", + "BaCac", + 8 + ], + [ + "ACCBbcbAC", + "AAaCaCBCC", + 12 + ], + [ + "ACCBcCBB", + "aBBABb", + 10 + ], + [ + "ACCBcaaCa", + "CBb", + 14 + ], + [ + "ACCBcc", + "bbAa", + 11 + ], + [ + "ACCCAaCcC", + "CBAc", + 12 + ], + [ + "ACCCBac", + "CcCBACBB", + 9 + ], + [ + "ACCCC", + "CACCACc", + 5 + ], + [ + "ACCCb", + "CbBaCcb", + 9 + ], + [ + "ACCCbaCAc", + "abACCC", + 11 + ], + [ + "ACCaAACa", + "C", + 14 + ], + [ + "ACCaACAa", + "a", + 14 + ], + [ + "ACCaB", + "A", + 8 + ], + [ + "ACCaCA", + "a", + 10 + ], + [ + "ACCaaAc", + "A", + 12 + ], + [ + "ACCac", + "a", + 8 + ], + [ + "ACCbA", + "aCCBa", + 3 + ], + [ + "ACCbB", + "BaAa", + 10 + ], + [ + "ACCbC", + "CcbaCC", + 7 + ], + [ + "ACCbCba", + "a", + 12 + ], + [ + "ACCbaBbb", + "aA", + 14 + ], + [ + "ACCbba", + "aaCBCbccB", + 11 + ], + [ + "ACCbbbC", + "BCbABAbaA", + 13 + ], + [ + "ACCbcBa", + "aCBaBcbC", + 9 + ], + [ + "ACCbcbccB", + "caAAbaCcb", + 13 + ], + [ + "ACCcAccCc", + "AccAb", + 11 + ], + [ + "ACCcBcB", + "AcBcAAca", + 9 + ], + [ + "ACCcC", + "a", + 9 + ], + [ + "ACCcaC", + "cCA", + 8 + ], + [ + "ACCcacC", + "BBaaAcAab", + 14 + ], + [ + "ACa", + "AcbCAbaB", + 10 + ], + [ + "ACa", + "Bca", + 3 + ], + [ + "ACa", + "ba", + 4 + ], + [ + "ACa", + "baAAaba", + 10 + ], + [ + "ACaA", + "A", + 6 + ], + [ + "ACaA", + "ABCAba", + 6 + ], + [ + "ACaA", + "acbCc", + 8 + ], + [ + "ACaA", + "cc", + 7 + ], + [ + "ACaAC", + "cABCBAc", + 7 + ], + [ + "ACaAcABa", + "cCAACC", + 10 + ], + [ + "ACaB", + "ABAbccB", + 9 + ], + [ + "ACaB", + "bAAAaAbAA", + 13 + ], + [ + "ACaBBBCB", + "AaAC", + 10 + ], + [ + "ACaBBaAa", + "AcaBcBbcB", + 9 + ], + [ + "ACaBCB", + "cc", + 10 + ], + [ + "ACaBCBb", + "b", + 12 + ], + [ + "ACaBacCc", + "bb", + 15 + ], + [ + "ACaBcb", + "BcCc", + 9 + ], + [ + "ACaCACc", + "c", + 12 + ], + [ + "ACaCBccBc", + "CcABCcBcb", + 9 + ], + [ + "ACaCCBA", + "AAcacC", + 8 + ], + [ + "ACaa", + "BAAc", + 7 + ], + [ + "ACaaBAbc", + "ACAccba", + 9 + ], + [ + "ACaabb", + "BCAb", + 7 + ], + [ + "ACaacBa", + "cBCBAc", + 11 + ], + [ + "ACabABBA", + "CAcCbB", + 10 + ], + [ + "ACabC", + "B", + 9 + ], + [ + "ACabac", + "BACcbCcbB", + 10 + ], + [ + "ACabbcA", + "aCacc", + 7 + ], + [ + "ACaccaa", + "AA", + 11 + ], + [ + "ACb", + "A", + 4 + ], + [ + "ACb", + "cBAab", + 6 + ], + [ + "ACb", + "ccbccbB", + 11 + ], + [ + "ACbA", + "AaBacA", + 7 + ], + [ + "ACbA", + "BaCCbA", + 5 + ], + [ + "ACbA", + "abACa", + 7 + ], + [ + "ACbACcbaB", + "ccc", + 14 + ], + [ + "ACbAbaCBC", + "acbaba", + 9 + ], + [ + "ACbB", + "aCACBAbb", + 9 + ], + [ + "ACbBA", + "CACB", + 6 + ], + [ + "ACbBCC", + "bcbCaAB", + 11 + ], + [ + "ACbBa", + "B", + 8 + ], + [ + "ACbBaCab", + "cbB", + 11 + ], + [ + "ACbCCbBb", + "aaca", + 14 + ], + [ + "ACbCaA", + "bAbC", + 8 + ], + [ + "ACbCb", + "c", + 9 + ], + [ + "ACbCc", + "ABc", + 5 + ], + [ + "ACbCcbA", + "accAAAaCc", + 15 + ], + [ + "ACba", + "acABAcbC", + 11 + ], + [ + "ACba", + "bCabba", + 6 + ], + [ + "ACbaAaA", + "BaaAbcC", + 12 + ], + [ + "ACbaAbB", + "AaBbbbb", + 8 + ], + [ + "ACbaAc", + "ABaBbCBcB", + 12 + ], + [ + "ACbaCAC", + "bBcaC", + 8 + ], + [ + "ACbaCaB", + "cbaA", + 8 + ], + [ + "ACbaaaB", + "CAcA", + 10 + ], + [ + "ACbabABB", + "BCBABBAc", + 11 + ], + [ + "ACbabcac", + "ABBA", + 11 + ], + [ + "ACbbBB", + "cAbbaC", + 8 + ], + [ + "ACbbBb", + "abAAbCab", + 10 + ], + [ + "ACbbCc", + "AccCbBAAB", + 11 + ], + [ + "ACbbca", + "cbABC", + 9 + ], + [ + "ACbbccB", + "b", + 12 + ], + [ + "ACbcBBc", + "CBacBbCbA", + 11 + ], + [ + "ACbcCba", + "abBBc", + 10 + ], + [ + "ACbcCccA", + "BBaA", + 13 + ], + [ + "ACc", + "AABbabcbb", + 14 + ], + [ + "ACc", + "ABbCcC", + 6 + ], + [ + "ACc", + "ABcBaB", + 8 + ], + [ + "ACc", + "a", + 5 + ], + [ + "ACc", + "b", + 6 + ], + [ + "ACc", + "baCBbbCbc", + 13 + ], + [ + "ACc", + "bcAcC", + 6 + ], + [ + "ACc", + "c", + 4 + ], + [ + "ACcA", + "CbcBBcbb", + 13 + ], + [ + "ACcA", + "aBBA", + 5 + ], + [ + "ACcAAb", + "ccAa", + 6 + ], + [ + "ACcABac", + "cA", + 10 + ], + [ + "ACcAc", + "baC", + 8 + ], + [ + "ACcBCBbbB", + "aA", + 17 + ], + [ + "ACcBa", + "ABBbBAbca", + 12 + ], + [ + "ACcBa", + "abbaca", + 9 + ], + [ + "ACcBba", + "baaAcAcA", + 12 + ], + [ + "ACcBbaa", + "CbCcCCBAC", + 12 + ], + [ + "ACcC", + "AcCABAC", + 8 + ], + [ + "ACcCACb", + "CABB", + 9 + ], + [ + "ACcCC", + "ccabBBC", + 11 + ], + [ + "ACcCa", + "Bcb", + 8 + ], + [ + "ACcCacAa", + "CbCba", + 10 + ], + [ + "ACcCc", + "bbBCC", + 7 + ], + [ + "ACca", + "cBBCBcbC", + 12 + ], + [ + "ACcaCAb", + "aaA", + 9 + ], + [ + "ACcaaAb", + "CaCAc", + 8 + ], + [ + "ACcabc", + "AAACb", + 8 + ], + [ + "ACcac", + "abBCBCc", + 9 + ], + [ + "ACcbB", + "caBaa", + 10 + ], + [ + "ACcbC", + "aCA", + 7 + ], + [ + "ACcbaBb", + "aCACCCAaA", + 12 + ], + [ + "ACcbbC", + "cacb", + 8 + ], + [ + "ACcbcCBB", + "A", + 14 + ], + [ + "ACcc", + "Cca", + 4 + ], + [ + "ACcca", + "accBbB", + 8 + ], + [ + "ACccabBC", + "Bb", + 14 + ], + [ + "ACccb", + "C", + 8 + ], + [ + "ACccbCccb", + "BbCacaA", + 14 + ], + [ + "ACccca", + "A", + 10 + ], + [ + "Aa", + "A", + 2 + ], + [ + "Aa", + "AAcac", + 6 + ], + [ + "Aa", + "ABBa", + 4 + ], + [ + "Aa", + "AbAabCcB", + 12 + ], + [ + "Aa", + "AbCCCCbcC", + 16 + ], + [ + "Aa", + "AbccAA", + 9 + ], + [ + "Aa", + "B", + 4 + ], + [ + "Aa", + "BA", + 3 + ], + [ + "Aa", + "BB", + 4 + ], + [ + "Aa", + "BBaccBAc", + 14 + ], + [ + "Aa", + "BCAaBb", + 8 + ], + [ + "Aa", + "BaBBAc", + 10 + ], + [ + "Aa", + "BaBcCaBc", + 13 + ], + [ + "Aa", + "BaCc", + 6 + ], + [ + "Aa", + "BabcCcAb", + 14 + ], + [ + "Aa", + "BbBB", + 8 + ], + [ + "Aa", + "C", + 4 + ], + [ + "Aa", + "CAabcaaAB", + 14 + ], + [ + "Aa", + "CAbbCbCB", + 14 + ], + [ + "Aa", + "CBBaBccb", + 14 + ], + [ + "Aa", + "CBCAccba", + 12 + ], + [ + "Aa", + "a", + 2 + ], + [ + "Aa", + "aAc", + 4 + ], + [ + "Aa", + "aacb", + 5 + ], + [ + "Aa", + "acAAacaB", + 12 + ], + [ + "Aa", + "acaA", + 5 + ], + [ + "Aa", + "acbc", + 7 + ], + [ + "Aa", + "b", + 4 + ], + [ + "Aa", + "bBA", + 5 + ], + [ + "Aa", + "bBABbc", + 10 + ], + [ + "Aa", + "baABcc", + 10 + ], + [ + "Aa", + "babbc", + 8 + ], + [ + "Aa", + "bbabBCac", + 13 + ], + [ + "Aa", + "cAAB", + 5 + ], + [ + "Aa", + "cC", + 4 + ], + [ + "Aa", + "caCAB", + 8 + ], + [ + "Aa", + "ccabB", + 8 + ], + [ + "AaA", + "Acbc", + 6 + ], + [ + "AaA", + "BBAcB", + 8 + ], + [ + "AaA", + "BaAbb", + 6 + ], + [ + "AaA", + "CAcBB", + 8 + ], + [ + "AaA", + "Cc", + 6 + ], + [ + "AaA", + "a", + 4 + ], + [ + "AaA", + "bAcaab", + 7 + ], + [ + "AaA", + "bCbCac", + 10 + ], + [ + "AaA", + "bCcaBAa", + 10 + ], + [ + "AaA", + "cAbaCAbB", + 10 + ], + [ + "AaAA", + "bcacBb", + 10 + ], + [ + "AaAABca", + "aACbAbaa", + 9 + ], + [ + "AaAAbA", + "CAbA", + 6 + ], + [ + "AaAAbBCcc", + "bbcAb", + 14 + ], + [ + "AaAAcbcbB", + "aAcCCBba", + 11 + ], + [ + "AaABbA", + "BBaACcACA", + 12 + ], + [ + "AaABc", + "cCcaBB", + 9 + ], + [ + "AaABcBAbb", + "BABAbaB", + 11 + ], + [ + "AaACCAb", + "C", + 12 + ], + [ + "AaACCaac", + "aabBbAbCA", + 13 + ], + [ + "AaACa", + "BBb", + 10 + ], + [ + "AaAaABB", + "CABcCCcCA", + 16 + ], + [ + "AaAaBbCc", + "B", + 14 + ], + [ + "AaAaa", + "cba", + 8 + ], + [ + "AaAabb", + "CACAA", + 9 + ], + [ + "AaAaccCB", + "bAaacBbaA", + 12 + ], + [ + "AaAb", + "cABAcb", + 6 + ], + [ + "AaAbAaAb", + "a", + 14 + ], + [ + "AaAbAbbB", + "aA", + 12 + ], + [ + "AaAbBba", + "CCBac", + 12 + ], + [ + "AaAbb", + "BbaB", + 8 + ], + [ + "AaAbbc", + "cAAcCC", + 8 + ], + [ + "AaAbc", + "a", + 8 + ], + [ + "AaAc", + "aAaCA", + 6 + ], + [ + "AaAcA", + "BCAAAba", + 8 + ], + [ + "AaAccaBbc", + "baAcAACaa", + 11 + ], + [ + "AaB", + "AbbAbb", + 8 + ], + [ + "AaB", + "Bca", + 6 + ], + [ + "AaB", + "bbaAb", + 7 + ], + [ + "AaB", + "bcABBBCc", + 12 + ], + [ + "AaB", + "caAb", + 5 + ], + [ + "AaBA", + "cBAB", + 6 + ], + [ + "AaBACC", + "BCAAbB", + 10 + ], + [ + "AaBACaBA", + "cAc", + 13 + ], + [ + "AaBAa", + "c", + 10 + ], + [ + "AaBAc", + "CcACba", + 10 + ], + [ + "AaBAcBcaa", + "cb", + 15 + ], + [ + "AaBBAC", + "ACAca", + 9 + ], + [ + "AaBBAcbcc", + "CbCcBb", + 14 + ], + [ + "AaBBbcA", + "A", + 12 + ], + [ + "AaBCcbCB", + "CCcAb", + 11 + ], + [ + "AaBa", + "abA", + 4 + ], + [ + "AaBa", + "bbbCabA", + 10 + ], + [ + "AaBaaaCBa", + "A", + 16 + ], + [ + "AaBabBCb", + "aBac", + 9 + ], + [ + "AaBbB", + "bBaa", + 8 + ], + [ + "AaBba", + "ab", + 6 + ], + [ + "AaBbba", + "caAbBAa", + 7 + ], + [ + "AaBbbaac", + "Cbcca", + 12 + ], + [ + "AaBbbbA", + "AabcBAa", + 7 + ], + [ + "AaBc", + "bAaaBaCCB", + 11 + ], + [ + "AaBcA", + "aCbCbB", + 9 + ], + [ + "AaBcAbBC", + "CBCACaBBA", + 12 + ], + [ + "AaBccbA", + "BBcaaccb", + 10 + ], + [ + "AaC", + "AACcabcAa", + 13 + ], + [ + "AaC", + "ABAaabA", + 10 + ], + [ + "AaC", + "Acc", + 3 + ], + [ + "AaC", + "aCCB", + 5 + ], + [ + "AaC", + "bbbbbbAA", + 15 + ], + [ + "AaCA", + "CCc", + 6 + ], + [ + "AaCAAAcac", + "ABB", + 16 + ], + [ + "AaCAAbaa", + "CCbABc", + 11 + ], + [ + "AaCACCa", + "BCcBACc", + 10 + ], + [ + "AaCACCbcc", + "CAacc", + 10 + ], + [ + "AaCAcAcB", + "bbA", + 14 + ], + [ + "AaCB", + "BbBC", + 8 + ], + [ + "AaCB", + "bCCbBAA", + 10 + ], + [ + "AaCBa", + "ACAbC", + 7 + ], + [ + "AaCBaC", + "Ba", + 8 + ], + [ + "AaCBaaAAC", + "CaBc", + 13 + ], + [ + "AaCBbBBa", + "CBbAcCBb", + 12 + ], + [ + "AaCCAAC", + "ACaBb", + 9 + ], + [ + "AaCCAbcBB", + "BCaab", + 13 + ], + [ + "AaCCCB", + "AaaAA", + 8 + ], + [ + "AaCCCBB", + "bB", + 11 + ], + [ + "AaCCcbB", + "BCcaCcB", + 9 + ], + [ + "AaCCcc", + "cbc", + 9 + ], + [ + "AaCaAAa", + "ccccBbcaC", + 15 + ], + [ + "AaCaCccb", + "aCB", + 11 + ], + [ + "AaCaaaA", + "a", + 12 + ], + [ + "AaCaac", + "CBabAA", + 10 + ], + [ + "AaCac", + "AbaBC", + 7 + ], + [ + "AaCbbbBaB", + "ccC", + 16 + ], + [ + "AaCbcCb", + "ccAAaB", + 12 + ], + [ + "AaCc", + "a", + 6 + ], + [ + "AaCcABAa", + "B", + 14 + ], + [ + "AaCcAbC", + "Bb", + 12 + ], + [ + "AaCcBBcc", + "cBb", + 11 + ], + [ + "AaCcaCBaA", + "ABca", + 12 + ], + [ + "AaCcab", + "bBcB", + 9 + ], + [ + "AaCcacA", + "BBBAcbC", + 13 + ], + [ + "AaCcbBbCa", + "BBBAaabCb", + 14 + ], + [ + "AaCcc", + "abcBAb", + 10 + ], + [ + "AaCccBBCb", + "cB", + 14 + ], + [ + "AaCccbC", + "baaaaAb", + 11 + ], + [ + "Aaa", + "BaAA", + 5 + ], + [ + "Aaa", + "CbabcBBB", + 14 + ], + [ + "Aaa", + "aAa", + 2 + ], + [ + "Aaa", + "aBB", + 5 + ], + [ + "Aaa", + "bAAbBaBcc", + 13 + ], + [ + "Aaa", + "ca", + 4 + ], + [ + "AaaA", + "aBabbB", + 9 + ], + [ + "AaaA", + "baCb", + 6 + ], + [ + "AaaAAC", + "a", + 10 + ], + [ + "AaaAAaC", + "b", + 14 + ], + [ + "AaaACAcbA", + "CBAcAC", + 12 + ], + [ + "AaaACa", + "BABcccC", + 11 + ], + [ + "AaaAbBAab", + "bacccCc", + 16 + ], + [ + "AaaAcBCB", + "Bbbb", + 14 + ], + [ + "AaaAcb", + "AcabAA", + 8 + ], + [ + "AaaB", + "aaCAC", + 6 + ], + [ + "AaaBABa", + "bBaC", + 11 + ], + [ + "AaaBCaCC", + "c", + 15 + ], + [ + "AaaBa", + "CBCBc", + 8 + ], + [ + "AaaC", + "aaAc", + 3 + ], + [ + "AaaC", + "b", + 8 + ], + [ + "AaaC", + "caaBCAABc", + 12 + ], + [ + "AaaCB", + "ABcbBCCA", + 12 + ], + [ + "AaaCcbcc", + "B", + 15 + ], + [ + "Aaaa", + "abCBCBa", + 11 + ], + [ + "AaaaA", + "CccAB", + 9 + ], + [ + "AaaaBcb", + "BCabBBBC", + 11 + ], + [ + "AaaaCCC", + "BBcaacaCB", + 11 + ], + [ + "AaaacbC", + "CcAcBACcb", + 13 + ], + [ + "AaabA", + "cB", + 9 + ], + [ + "AaabACBca", + "bAc", + 12 + ], + [ + "AaabCAC", + "bCaacCbB", + 10 + ], + [ + "Aaaba", + "BbccCaa", + 12 + ], + [ + "AaabaBba", + "aBaB", + 9 + ], + [ + "Aaababb", + "BcbBCcc", + 13 + ], + [ + "AaabcBb", + "abab", + 8 + ], + [ + "Aaac", + "BAbaab", + 6 + ], + [ + "AaacACBc", + "ACbbCaABA", + 12 + ], + [ + "AaaccCBcb", + "CBabaCACb", + 11 + ], + [ + "AaacccCc", + "c", + 14 + ], + [ + "Aab", + "BcBaaCBC", + 12 + ], + [ + "Aab", + "CBBaACcBA", + 15 + ], + [ + "Aab", + "a", + 4 + ], + [ + "AabA", + "acCbacA", + 9 + ], + [ + "AabAAcBBB", + "aa", + 15 + ], + [ + "AabAbBbcB", + "AcCcA", + 14 + ], + [ + "AabAccB", + "AcabBCb", + 8 + ], + [ + "AabBABc", + "AAabbCcCc", + 9 + ], + [ + "AabBCAaC", + "aBcCbc", + 10 + ], + [ + "AabBCC", + "A", + 10 + ], + [ + "AabBaBcBB", + "cbcc", + 14 + ], + [ + "AabBacAaa", + "aAc", + 13 + ], + [ + "AabBba", + "bcabb", + 8 + ], + [ + "AabBbc", + "CcAaA", + 12 + ], + [ + "AabBcBca", + "Ca", + 13 + ], + [ + "AabBcbc", + "ac", + 10 + ], + [ + "AabCbA", + "B", + 11 + ], + [ + "AabCbC", + "ccBbc", + 8 + ], + [ + "Aaba", + "BBaa", + 6 + ], + [ + "AabaACCC", + "aaBBcccCa", + 10 + ], + [ + "AabaCCccc", + "BaB", + 15 + ], + [ + "AabbA", + "AB", + 7 + ], + [ + "AabbABCBb", + "aBB", + 12 + ], + [ + "AabbB", + "AA", + 7 + ], + [ + "AabbB", + "bCCC", + 10 + ], + [ + "AabbbbbC", + "bBbBaB", + 10 + ], + [ + "AabbcCACc", + "AAa", + 14 + ], + [ + "Aabc", + "bBCbc", + 6 + ], + [ + "Aabc", + "bbA", + 6 + ], + [ + "AabcAAbCc", + "Aa", + 14 + ], + [ + "AabcBBb", + "bAbAcCBB", + 8 + ], + [ + "AabcBb", + "bb", + 8 + ], + [ + "AabcC", + "BbbBcbbC", + 10 + ], + [ + "AabcbCa", + "CB", + 12 + ], + [ + "Aac", + "Bccc", + 6 + ], + [ + "Aac", + "CBABCbaA", + 12 + ], + [ + "Aac", + "Cb", + 6 + ], + [ + "Aac", + "a", + 4 + ], + [ + "Aac", + "aBbCCaaa", + 13 + ], + [ + "Aac", + "baBAcA", + 8 + ], + [ + "Aac", + "c", + 4 + ], + [ + "Aac", + "caAABac", + 8 + ], + [ + "AacA", + "ACcBA", + 4 + ], + [ + "AacA", + "BaaAccBcA", + 11 + ], + [ + "AacAABbaa", + "a", + 16 + ], + [ + "AacABAB", + "CCc", + 12 + ], + [ + "AacAcbbC", + "cbCa", + 12 + ], + [ + "AacBBCc", + "BcBAA", + 10 + ], + [ + "AacBaA", + "AAa", + 7 + ], + [ + "AacBbaCBB", + "CcbC", + 12 + ], + [ + "AacBbcccc", + "CbACCaBab", + 18 + ], + [ + "AacC", + "BcBcAbb", + 12 + ], + [ + "AacCaBcaB", + "CAAb", + 13 + ], + [ + "AacCacC", + "CaABC", + 9 + ], + [ + "AacCcCcAC", + "CCBBabBA", + 16 + ], + [ + "AacCcc", + "Aab", + 8 + ], + [ + "AacaaCc", + "bAaA", + 10 + ], + [ + "AacacB", + "aaBBB", + 7 + ], + [ + "AacacBC", + "CBBcAcba", + 10 + ], + [ + "AacacBb", + "AAabbcaC", + 10 + ], + [ + "Aacb", + "c", + 6 + ], + [ + "AacbAAA", + "BbcBaCCCb", + 14 + ], + [ + "Aacbaa", + "aCBbbCaAc", + 12 + ], + [ + "Aacc", + "ACAA", + 6 + ], + [ + "Aacc", + "CBAcB", + 7 + ], + [ + "AaccAB", + "baAcAcbcc", + 11 + ], + [ + "AaccABc", + "bB", + 12 + ], + [ + "AaccAaAb", + "acb", + 10 + ], + [ + "AaccB", + "ac", + 6 + ], + [ + "AaccBcc", + "BC", + 11 + ], + [ + "AaccC", + "bAbacBAa", + 10 + ], + [ + "AaccaAc", + "Bc", + 12 + ], + [ + "AaccaBa", + "B", + 12 + ], + [ + "AaccaCcBb", + "bABCca", + 13 + ], + [ + "Aaccac", + "c", + 10 + ], + [ + "AacccAa", + "AC", + 11 + ], + [ + "AacccAcC", + "ab", + 14 + ], + [ + "AacccBBcC", + "Bb", + 15 + ], + [ + "Ab", + "A", + 2 + ], + [ + "Ab", + "AA", + 2 + ], + [ + "Ab", + "AaabCAAA", + 12 + ], + [ + "Ab", + "AbACAaCAA", + 14 + ], + [ + "Ab", + "AbCCAAACc", + 14 + ], + [ + "Ab", + "AbCbAa", + 8 + ], + [ + "Ab", + "Ac", + 2 + ], + [ + "Ab", + "AcaABbc", + 10 + ], + [ + "Ab", + "BAcCa", + 8 + ], + [ + "Ab", + "BBc", + 5 + ], + [ + "Ab", + "BbcAcCCaa", + 16 + ], + [ + "Ab", + "CABa", + 5 + ], + [ + "Ab", + "CAaB", + 5 + ], + [ + "Ab", + "CCBcAbbc", + 12 + ], + [ + "Ab", + "CaBc", + 6 + ], + [ + "Ab", + "CbBCAbaBc", + 14 + ], + [ + "Ab", + "aCAccA", + 10 + ], + [ + "Ab", + "aab", + 3 + ], + [ + "Ab", + "acaCBAa", + 12 + ], + [ + "Ab", + "bA", + 4 + ], + [ + "Ab", + "bCBc", + 7 + ], + [ + "Ab", + "c", + 4 + ], + [ + "Ab", + "cBBB", + 7 + ], + [ + "Ab", + "cC", + 4 + ], + [ + "Ab", + "cCAaBbbBC", + 14 + ], + [ + "Ab", + "cCaAcc", + 10 + ], + [ + "Ab", + "caA", + 5 + ], + [ + "Ab", + "caCacCcBC", + 16 + ], + [ + "Ab", + "cbcacAC", + 12 + ], + [ + "Ab", + "ccAbB", + 6 + ], + [ + "Ab", + "ccb", + 4 + ], + [ + "AbA", + "BCBAaC", + 9 + ], + [ + "AbA", + "BbBAaA", + 8 + ], + [ + "AbA", + "CcBa", + 6 + ], + [ + "AbA", + "aAC", + 5 + ], + [ + "AbA", + "b", + 4 + ], + [ + "AbA", + "caABB", + 7 + ], + [ + "AbA", + "ccc", + 6 + ], + [ + "AbAA", + "CBba", + 6 + ], + [ + "AbAAC", + "BAAC", + 3 + ], + [ + "AbAAC", + "bbab", + 7 + ], + [ + "AbAACaAb", + "C", + 14 + ], + [ + "AbAACbbBB", + "BCbaa", + 13 + ], + [ + "AbAAbC", + "caCaAAAbA", + 11 + ], + [ + "AbAAcbBa", + "BabBBCa", + 11 + ], + [ + "AbAB", + "Ab", + 4 + ], + [ + "AbAB", + "Bcb", + 6 + ], + [ + "AbAB", + "ac", + 7 + ], + [ + "AbABCa", + "cC", + 10 + ], + [ + "AbABa", + "BAaaCB", + 9 + ], + [ + "AbACABc", + "aBCACA", + 8 + ], + [ + "AbACBCA", + "aBB", + 10 + ], + [ + "AbACaa", + "AcAba", + 6 + ], + [ + "AbACbA", + "A", + 10 + ], + [ + "AbACbAA", + "aACA", + 7 + ], + [ + "AbACccccb", + "cCbbCBA", + 15 + ], + [ + "AbAaABB", + "BcaaA", + 9 + ], + [ + "AbAaAb", + "CB", + 11 + ], + [ + "AbAaC", + "AacbbACba", + 11 + ], + [ + "AbAaCAab", + "CabaacCaA", + 9 + ], + [ + "AbAaaa", + "AaC", + 8 + ], + [ + "AbAacaaac", + "abACABAc", + 8 + ], + [ + "AbAb", + "bbccBB", + 9 + ], + [ + "AbAbCba", + "BCbA", + 8 + ], + [ + "AbAbabac", + "bc", + 12 + ], + [ + "AbAbbBaab", + "bbabb", + 10 + ], + [ + "AbAc", + "BcbBCbC", + 11 + ], + [ + "AbAcACAa", + "cBcBBA", + 11 + ], + [ + "AbAcB", + "c", + 8 + ], + [ + "AbAcbCbca", + "AcBaaB", + 13 + ], + [ + "AbAccAbBc", + "aACcAACbA", + 11 + ], + [ + "AbAccBCa", + "BACCAaA", + 10 + ], + [ + "AbB", + "AABC", + 4 + ], + [ + "AbB", + "AbCbCCc", + 9 + ], + [ + "AbB", + "BcaAbAAAb", + 13 + ], + [ + "AbB", + "bBBA", + 5 + ], + [ + "AbB", + "cAaaaB", + 8 + ], + [ + "AbB", + "cbbABB", + 7 + ], + [ + "AbBAcCAC", + "CAAbcCC", + 9 + ], + [ + "AbBB", + "AAAAC", + 8 + ], + [ + "AbBBabc", + "Bcb", + 10 + ], + [ + "AbBBbCaA", + "AaCccAAA", + 11 + ], + [ + "AbBBcaAaa", + "bcbBbcCb", + 13 + ], + [ + "AbBCAacC", + "c", + 14 + ], + [ + "AbBCAcaa", + "aa", + 12 + ], + [ + "AbBCBbA", + "abbcBb", + 5 + ], + [ + "AbBaABC", + "AabAAbc", + 6 + ], + [ + "AbBaBB", + "AAaAca", + 9 + ], + [ + "AbBaCC", + "CAb", + 10 + ], + [ + "AbBaaAb", + "ACcBcca", + 11 + ], + [ + "AbBabBa", + "bccBAbb", + 10 + ], + [ + "AbBb", + "A", + 6 + ], + [ + "AbBb", + "BbaAB", + 7 + ], + [ + "AbBbA", + "babCBABbb", + 11 + ], + [ + "AbBbAABc", + "AbAa", + 9 + ], + [ + "AbBbCb", + "bcAACCcac", + 15 + ], + [ + "AbBbb", + "aCAC", + 9 + ], + [ + "AbBbbacbC", + "CcbccAb", + 14 + ], + [ + "AbBbcb", + "Ab", + 8 + ], + [ + "AbBbcbabC", + "Ba", + 14 + ], + [ + "AbBcBaaCc", + "BbcAB", + 13 + ], + [ + "AbBcaaCCA", + "aba", + 13 + ], + [ + "AbBcb", + "BAaAcb", + 6 + ], + [ + "AbBcc", + "AaccBc", + 6 + ], + [ + "AbBcccB", + "cB", + 10 + ], + [ + "AbC", + "BccBcC", + 9 + ], + [ + "AbC", + "C", + 4 + ], + [ + "AbCAA", + "CCcCcacBa", + 14 + ], + [ + "AbCABa", + "ACBC", + 6 + ], + [ + "AbCACa", + "BcaBacAA", + 12 + ], + [ + "AbCACcBbB", + "C", + 16 + ], + [ + "AbCAc", + "BbCbcBCAb", + 12 + ], + [ + "AbCB", + "Caa", + 8 + ], + [ + "AbCB", + "acbAc", + 7 + ], + [ + "AbCB", + "caaaba", + 10 + ], + [ + "AbCBbB", + "BacABACaB", + 13 + ], + [ + "AbCBbbC", + "cACb", + 10 + ], + [ + "AbCCcAbc", + "abB", + 12 + ], + [ + "AbCCca", + "C", + 10 + ], + [ + "AbCCcacba", + "aaAaabcb", + 13 + ], + [ + "AbCa", + "CcAaCAaA", + 10 + ], + [ + "AbCa", + "aBcccBBa", + 11 + ], + [ + "AbCaA", + "bBbAB", + 8 + ], + [ + "AbCaAAbab", + "BCbCcb", + 13 + ], + [ + "AbCaC", + "AbABC", + 4 + ], + [ + "AbCaabBCB", + "Cb", + 14 + ], + [ + "AbCabbc", + "BcCaB", + 9 + ], + [ + "AbCacAacb", + "aaAAAAC", + 12 + ], + [ + "AbCb", + "A", + 6 + ], + [ + "AbCb", + "BbaBcaCb", + 10 + ], + [ + "AbCbC", + "Cc", + 7 + ], + [ + "AbCbaB", + "aCac", + 7 + ], + [ + "AbCc", + "bAaCac", + 6 + ], + [ + "AbCcBBACC", + "cACC", + 10 + ], + [ + "AbCcbaBCA", + "cccCAbbCA", + 11 + ], + [ + "Aba", + "ACA", + 3 + ], + [ + "Aba", + "C", + 6 + ], + [ + "Aba", + "aB", + 4 + ], + [ + "Aba", + "bccccA", + 11 + ], + [ + "Aba", + "cabbcC", + 9 + ], + [ + "AbaAACBCb", + "CACabC", + 13 + ], + [ + "AbaAabca", + "CBCBCA", + 12 + ], + [ + "AbaAb", + "cb", + 8 + ], + [ + "AbaB", + "aCaB", + 3 + ], + [ + "AbaBAaCbC", + "cbbacCbA", + 10 + ], + [ + "AbaBAbaB", + "cbaC", + 12 + ], + [ + "AbaBBcbAA", + "bCaaC", + 14 + ], + [ + "AbaBCa", + "B", + 10 + ], + [ + "AbaBCbaB", + "acBcb", + 10 + ], + [ + "AbaBaca", + "acAAABBB", + 13 + ], + [ + "AbaBcCba", + "accBCABAB", + 12 + ], + [ + "AbaC", + "BAAb", + 7 + ], + [ + "AbaC", + "aA", + 6 + ], + [ + "AbaCb", + "B", + 9 + ], + [ + "AbaCbBc", + "ACBAaCAcA", + 11 + ], + [ + "Abaa", + "aAcBb", + 8 + ], + [ + "Abaa", + "cbbAaabCc", + 12 + ], + [ + "AbaaAbab", + "CCaaBc", + 11 + ], + [ + "AbaaAc", + "bAba", + 8 + ], + [ + "AbaaabCBb", + "aa", + 14 + ], + [ + "Abab", + "bbbBb", + 6 + ], + [ + "Abab", + "cab", + 4 + ], + [ + "AbabBBb", + "CaAAc", + 12 + ], + [ + "AbabCC", + "CACcAabA", + 12 + ], + [ + "AbabcCbb", + "AbcCcbB", + 7 + ], + [ + "AbacAa", + "bCAAabcCa", + 10 + ], + [ + "AbacB", + "CBaCBAcBa", + 11 + ], + [ + "AbacBCc", + "B", + 12 + ], + [ + "AbacBbBCC", + "bC", + 14 + ], + [ + "AbacaaCB", + "B", + 14 + ], + [ + "AbacaaaBC", + "AacAa", + 9 + ], + [ + "Abacb", + "AcccbAb", + 8 + ], + [ + "Abb", + "Aba", + 2 + ], + [ + "Abb", + "Ac", + 4 + ], + [ + "Abb", + "BCcba", + 8 + ], + [ + "AbbA", + "BCb", + 6 + ], + [ + "AbbABab", + "baacCBb", + 11 + ], + [ + "AbbACbab", + "ccAcA", + 12 + ], + [ + "AbbAcA", + "CabbA", + 7 + ], + [ + "AbbB", + "b", + 6 + ], + [ + "AbbBbCbAC", + "bCCaBA", + 13 + ], + [ + "AbbBbb", + "BBcAbaAbB", + 11 + ], + [ + "AbbBcaaBa", + "aAAAaCAba", + 13 + ], + [ + "AbbCACaB", + "CbBBAbcB", + 9 + ], + [ + "AbbCB", + "AAC", + 6 + ], + [ + "AbbCBbCA", + "aAa", + 14 + ], + [ + "AbbCCA", + "bBBcbaAcC", + 13 + ], + [ + "AbbCCaBCB", + "AabbCB", + 9 + ], + [ + "AbbCCcAAb", + "ABCbbBCC", + 13 + ], + [ + "AbbCaCCa", + "B", + 15 + ], + [ + "AbbCbBC", + "Cc", + 11 + ], + [ + "AbbCcA", + "BaaCcBac", + 11 + ], + [ + "AbbaBacBC", + "BaCaCBcC", + 10 + ], + [ + "Abbaa", + "aCbBbc", + 8 + ], + [ + "Abbb", + "bBab", + 5 + ], + [ + "Abbbba", + "BbbaA", + 6 + ], + [ + "AbbcBbA", + "cCbCcaC", + 11 + ], + [ + "Abc", + "CBAaABCCc", + 13 + ], + [ + "Abc", + "Cab", + 5 + ], + [ + "Abc", + "ba", + 4 + ], + [ + "Abc", + "cCBCa", + 8 + ], + [ + "AbcAAAa", + "CBcBBAabc", + 11 + ], + [ + "AbcABaa", + "cCcBCb", + 10 + ], + [ + "AbcAcA", + "B", + 11 + ], + [ + "AbcB", + "AbaCBBaA", + 9 + ], + [ + "AbcBAA", + "BAAb", + 8 + ], + [ + "AbcBBaBB", + "CCBACAA", + 13 + ], + [ + "AbcBCcAA", + "abbABCcCa", + 8 + ], + [ + "AbcCACAbB", + "aBcCb", + 10 + ], + [ + "AbcCAacab", + "a", + 16 + ], + [ + "AbcCa", + "cBAaab", + 9 + ], + [ + "AbcCbbc", + "ACccaBbAB", + 10 + ], + [ + "AbcaA", + "cac", + 6 + ], + [ + "AbcaBABac", + "aA", + 14 + ], + [ + "AbcaBbBcA", + "CBbA", + 11 + ], + [ + "AbcaCAAAB", + "ac", + 15 + ], + [ + "AbcacabA", + "ABbaAC", + 10 + ], + [ + "Abcb", + "AbCCcbb", + 6 + ], + [ + "AbcbA", + "b", + 8 + ], + [ + "AbcbABc", + "bCBCabbBb", + 12 + ], + [ + "AbcbB", + "A", + 8 + ], + [ + "AbcbbC", + "bABbBAa", + 10 + ], + [ + "Abcc", + "bAABaA", + 9 + ], + [ + "Abcc", + "cAABAAa", + 11 + ], + [ + "AbccB", + "cA", + 8 + ], + [ + "Abccc", + "bba", + 8 + ], + [ + "Ac", + "ACbAa", + 7 + ], + [ + "Ac", + "AbB", + 4 + ], + [ + "Ac", + "BCB", + 5 + ], + [ + "Ac", + "BCacCBaBb", + 15 + ], + [ + "Ac", + "BaaCB", + 8 + ], + [ + "Ac", + "BacBbABCa", + 15 + ], + [ + "Ac", + "Bacc", + 5 + ], + [ + "Ac", + "BbaBaACcC", + 14 + ], + [ + "Ac", + "BccaAa", + 10 + ], + [ + "Ac", + "CA", + 4 + ], + [ + "Ac", + "CABacA", + 8 + ], + [ + "Ac", + "CABbbCA", + 11 + ], + [ + "Ac", + "CAa", + 4 + ], + [ + "Ac", + "CBCaAcBaB", + 14 + ], + [ + "Ac", + "CCA", + 5 + ], + [ + "Ac", + "CCcbBBc", + 12 + ], + [ + "Ac", + "CbBBBBbBA", + 18 + ], + [ + "Ac", + "Cbba", + 8 + ], + [ + "Ac", + "aCaa", + 6 + ], + [ + "Ac", + "aCbca", + 7 + ], + [ + "Ac", + "aaBC", + 6 + ], + [ + "Ac", + "aaCAa", + 8 + ], + [ + "Ac", + "aaaA", + 7 + ], + [ + "Ac", + "aabca", + 7 + ], + [ + "Ac", + "abcAAaB", + 11 + ], + [ + "Ac", + "acaBBbaAC", + 15 + ], + [ + "Ac", + "bAaa", + 6 + ], + [ + "Ac", + "bCCCCC", + 11 + ], + [ + "Ac", + "baAcB", + 6 + ], + [ + "Ac", + "bc", + 2 + ], + [ + "Ac", + "cB", + 4 + ], + [ + "Ac", + "cBaaaAAAb", + 16 + ], + [ + "Ac", + "cC", + 3 + ], + [ + "Ac", + "cCAcbCA", + 10 + ], + [ + "Ac", + "cCcCAAA", + 12 + ], + [ + "Ac", + "cbAAcc", + 8 + ], + [ + "AcA", + "ABAACCbc", + 12 + ], + [ + "AcA", + "BBBb", + 8 + ], + [ + "AcA", + "CcabCab", + 11 + ], + [ + "AcA", + "acA", + 1 + ], + [ + "AcA", + "bAbCc", + 7 + ], + [ + "AcA", + "bccBB", + 8 + ], + [ + "AcA", + "cCCA", + 5 + ], + [ + "AcAAAb", + "Acba", + 7 + ], + [ + "AcAABAc", + "bCaa", + 11 + ], + [ + "AcAABBc", + "c", + 12 + ], + [ + "AcAACA", + "cCb", + 8 + ], + [ + "AcAACBb", + "AbaA", + 9 + ], + [ + "AcAAbb", + "abBbA", + 9 + ], + [ + "AcAAcBaB", + "caB", + 10 + ], + [ + "AcABCCba", + "Ca", + 12 + ], + [ + "AcABa", + "aAbBBCA", + 9 + ], + [ + "AcAC", + "c", + 6 + ], + [ + "AcACB", + "aBBCbBabC", + 13 + ], + [ + "AcACCBA", + "c", + 12 + ], + [ + "AcACCaACA", + "aCAAAcABC", + 12 + ], + [ + "AcACaA", + "Ac", + 8 + ], + [ + "AcACcC", + "bABBAcC", + 8 + ], + [ + "AcACccA", + "CaacccB", + 8 + ], + [ + "AcAa", + "b", + 8 + ], + [ + "AcAaaCBA", + "abB", + 12 + ], + [ + "AcAac", + "BBCCC", + 9 + ], + [ + "AcAb", + "cCBBcBCAc", + 14 + ], + [ + "AcAbA", + "cCbbBcb", + 11 + ], + [ + "AcAbBabb", + "BBCBa", + 12 + ], + [ + "AcAbBcABC", + "baCcCcb", + 15 + ], + [ + "AcAbCB", + "ccCCaC", + 10 + ], + [ + "AcAba", + "CcBcBcC", + 11 + ], + [ + "AcAbb", + "bAAbc", + 6 + ], + [ + "AcAcB", + "aAcBAbC", + 8 + ], + [ + "AcAcCB", + "bc", + 10 + ], + [ + "AcAcCCCbb", + "BAaBC", + 14 + ], + [ + "AcAcbacA", + "cb", + 12 + ], + [ + "AcB", + "AbCbCa", + 8 + ], + [ + "AcB", + "CCABca", + 8 + ], + [ + "AcB", + "aBA", + 5 + ], + [ + "AcB", + "b", + 5 + ], + [ + "AcB", + "bCB", + 3 + ], + [ + "AcB", + "cBBaB", + 8 + ], + [ + "AcB", + "caBcC", + 7 + ], + [ + "AcB", + "caCCCaA", + 12 + ], + [ + "AcBAAa", + "ac", + 9 + ], + [ + "AcBAbbAA", + "ACCB", + 12 + ], + [ + "AcBB", + "AAAccBcA", + 10 + ], + [ + "AcBB", + "BaccCCACc", + 15 + ], + [ + "AcBB", + "bBCA", + 8 + ], + [ + "AcBB", + "cCAaaBaBb", + 12 + ], + [ + "AcBBAC", + "AaCA", + 8 + ], + [ + "AcBBACA", + "aAA", + 9 + ], + [ + "AcBBACCaC", + "abBcCAC", + 8 + ], + [ + "AcBBaB", + "aC", + 10 + ], + [ + "AcBBbbaBc", + "CC", + 16 + ], + [ + "AcBBbcb", + "ABBAAc", + 8 + ], + [ + "AcBBcabc", + "a", + 14 + ], + [ + "AcBC", + "ACabcCbBB", + 12 + ], + [ + "AcBC", + "AacAcBCcA", + 10 + ], + [ + "AcBC", + "bCbcAbbA", + 13 + ], + [ + "AcBCAcb", + "AB", + 10 + ], + [ + "AcBCCaAbc", + "ca", + 14 + ], + [ + "AcBCCb", + "aCcCAccc", + 11 + ], + [ + "AcBCaccAA", + "cBAcc", + 9 + ], + [ + "AcBCb", + "aBbaBac", + 11 + ], + [ + "AcBCcbCb", + "AAABCa", + 11 + ], + [ + "AcBCcbbaB", + "bBbC", + 14 + ], + [ + "AcBa", + "BBbCBAA", + 10 + ], + [ + "AcBaCCbCa", + "bbBBb", + 14 + ], + [ + "AcBaCCc", + "aaCcca", + 8 + ], + [ + "AcBaacCb", + "BBbbCbcC", + 13 + ], + [ + "AcBbAb", + "BabcbBbc", + 11 + ], + [ + "AcBbAc", + "cbAbC", + 7 + ], + [ + "AcBbaac", + "bA", + 11 + ], + [ + "AcBbbbB", + "ACaa", + 11 + ], + [ + "AcBc", + "abAcbb", + 7 + ], + [ + "AcBcAcA", + "aaCBAaAAa", + 11 + ], + [ + "AcBcBBB", + "a", + 13 + ], + [ + "AcBcCba", + "BCb", + 8 + ], + [ + "AcBca", + "Ab", + 7 + ], + [ + "AcBcbbA", + "ABb", + 8 + ], + [ + "AcBcbbA", + "BB", + 11 + ], + [ + "AcBcbcA", + "B", + 12 + ], + [ + "AcBccbbbC", + "CbBAAbB", + 13 + ], + [ + "AcBcccC", + "b", + 13 + ], + [ + "AcC", + "ACAbc", + 6 + ], + [ + "AcC", + "bBBA", + 8 + ], + [ + "AcC", + "bbCBACbcA", + 14 + ], + [ + "AcC", + "c", + 4 + ], + [ + "AcC", + "cBBa", + 8 + ], + [ + "AcCACC", + "bbAcCCcc", + 8 + ], + [ + "AcCACaCA", + "CBcCCB", + 11 + ], + [ + "AcCAb", + "bCBAACBC", + 12 + ], + [ + "AcCAbA", + "Bac", + 11 + ], + [ + "AcCAbcaBb", + "Cb", + 14 + ], + [ + "AcCAcCCCA", + "AcCC", + 10 + ], + [ + "AcCBA", + "a", + 9 + ], + [ + "AcCBBaBb", + "AC", + 12 + ], + [ + "AcCBba", + "CaaBCAa", + 10 + ], + [ + "AcCCAA", + "BcA", + 8 + ], + [ + "AcCCBcc", + "bbBACCa", + 13 + ], + [ + "AcCCCbb", + "AcaAc", + 9 + ], + [ + "AcCCb", + "bb", + 8 + ], + [ + "AcCCba", + "BC", + 10 + ], + [ + "AcCCcBACB", + "cb", + 15 + ], + [ + "AcCCccCB", + "baaCaAaB", + 12 + ], + [ + "AcCa", + "AAaCcC", + 8 + ], + [ + "AcCaAcccA", + "AaaaBaCBB", + 13 + ], + [ + "AcCaBCACC", + "a", + 16 + ], + [ + "AcCaC", + "CAcC", + 6 + ], + [ + "AcCaa", + "Ccab", + 6 + ], + [ + "AcCabb", + "BbCBa", + 9 + ], + [ + "AcCb", + "A", + 6 + ], + [ + "AcCbA", + "BaCcaC", + 9 + ], + [ + "AcCbA", + "aA", + 7 + ], + [ + "AcCbBb", + "CCAcb", + 7 + ], + [ + "AcCba", + "cBBbCbCc", + 12 + ], + [ + "AcCbac", + "cCBa", + 5 + ], + [ + "AcCbbbc", + "BbBbABCBc", + 12 + ], + [ + "AcCbc", + "ABBaaAc", + 10 + ], + [ + "AcCc", + "BBBAA", + 10 + ], + [ + "AcCc", + "CABBBCaCb", + 13 + ], + [ + "AcCc", + "acCbACCcc", + 11 + ], + [ + "AcCcA", + "AbCbabBc", + 11 + ], + [ + "AcCcABb", + "cCBaAc", + 9 + ], + [ + "AcCcBB", + "AAaCAaA", + 10 + ], + [ + "AcCcabBCB", + "BcA", + 15 + ], + [ + "AcCcb", + "bC", + 8 + ], + [ + "AcCccABB", + "bbCbbCB", + 12 + ], + [ + "AcCccbBBb", + "ACABAa", + 12 + ], + [ + "Aca", + "BaAA", + 6 + ], + [ + "Aca", + "CCBCCB", + 11 + ], + [ + "Aca", + "cA", + 3 + ], + [ + "AcaAAA", + "CcbbaB", + 9 + ], + [ + "AcaABAaCb", + "CcCACC", + 12 + ], + [ + "AcaABCa", + "bacAcA", + 9 + ], + [ + "AcaAcBC", + "ccbAaa", + 10 + ], + [ + "AcaBACCb", + "ACacBaBAb", + 8 + ], + [ + "AcaBAabCb", + "ACACAbaCB", + 9 + ], + [ + "AcaBC", + "ABacC", + 4 + ], + [ + "AcaBCa", + "AbBC", + 6 + ], + [ + "AcaBacc", + "CBB", + 11 + ], + [ + "AcaBcba", + "AbAbCBcC", + 10 + ], + [ + "AcaC", + "Aca", + 2 + ], + [ + "AcaCB", + "C", + 8 + ], + [ + "AcaCBB", + "cbacccC", + 11 + ], + [ + "AcaCBb", + "CCcaBC", + 8 + ], + [ + "AcaCa", + "bCA", + 7 + ], + [ + "AcaCaC", + "BB", + 12 + ], + [ + "AcaCbcC", + "aBBCccaC", + 9 + ], + [ + "AcaCc", + "CaaCBBb", + 10 + ], + [ + "AcaCc", + "aBaCb", + 5 + ], + [ + "AcaCcAc", + "acA", + 8 + ], + [ + "Acaa", + "BACcCA", + 7 + ], + [ + "AcaaA", + "bacbCca", + 10 + ], + [ + "Acaaa", + "ACccca", + 6 + ], + [ + "AcaaaaaBA", + "b", + 17 + ], + [ + "AcaaabbAC", + "AACbc", + 12 + ], + [ + "AcaabBaAc", + "aCAbBBCaA", + 10 + ], + [ + "AcaacaAAB", + "CA", + 15 + ], + [ + "AcaacbaC", + "A", + 14 + ], + [ + "Acab", + "AB", + 5 + ], + [ + "AcabA", + "BCcca", + 8 + ], + [ + "AcabBAcB", + "B", + 14 + ], + [ + "AcabCaab", + "cCCaBB", + 9 + ], + [ + "AcabcC", + "AC", + 8 + ], + [ + "Acabca", + "cCCC", + 9 + ], + [ + "AcacaAbbb", + "abB", + 13 + ], + [ + "AcacaBC", + "bbaCB", + 9 + ], + [ + "AcacaCCCc", + "aBbbcACA", + 14 + ], + [ + "AcaccB", + "aaBc", + 7 + ], + [ + "Acb", + "BcbA", + 4 + ], + [ + "Acb", + "BccCaB", + 9 + ], + [ + "Acb", + "aC", + 4 + ], + [ + "Acb", + "bAccAa", + 8 + ], + [ + "Acb", + "ccAcbaB", + 8 + ], + [ + "AcbA", + "cA", + 4 + ], + [ + "AcbAAcaA", + "Cba", + 11 + ], + [ + "AcbAB", + "cbBbAa", + 8 + ], + [ + "AcbACBAaA", + "cc", + 15 + ], + [ + "AcbB", + "C", + 7 + ], + [ + "AcbBa", + "aAABbACcB", + 13 + ], + [ + "AcbBaCb", + "bCcacBbA", + 12 + ], + [ + "AcbBaba", + "CbbCBaB", + 9 + ], + [ + "AcbBb", + "abBbA", + 5 + ], + [ + "AcbC", + "bbCaA", + 8 + ], + [ + "AcbCAC", + "CcACABBb", + 10 + ], + [ + "AcbCAc", + "aaBCaAbAa", + 12 + ], + [ + "AcbCBcc", + "aA", + 13 + ], + [ + "AcbCaCb", + "ccAABC", + 10 + ], + [ + "AcbCcBCa", + "bbbcAcB", + 11 + ], + [ + "AcbCcbCa", + "CcaABBcCa", + 11 + ], + [ + "Acba", + "aACbbBcc", + 11 + ], + [ + "AcbaCAA", + "bAaBbaca", + 10 + ], + [ + "Acbab", + "acBc", + 6 + ], + [ + "AcbbAcCa", + "cCb", + 12 + ], + [ + "AcbbC", + "B", + 9 + ], + [ + "Acbbb", + "cCbCAACA", + 13 + ], + [ + "AcbbcAB", + "cca", + 9 + ], + [ + "Acbc", + "AbCaCB", + 8 + ], + [ + "Acbc", + "Bac", + 6 + ], + [ + "AcbcBB", + "ABBBB", + 5 + ], + [ + "AcbcBcA", + "AaaBaba", + 11 + ], + [ + "AcbcCbcbA", + "ac", + 15 + ], + [ + "Acc", + "BbBCABBb", + 14 + ], + [ + "Acc", + "aAbBbBca", + 12 + ], + [ + "Acc", + "aa", + 5 + ], + [ + "Acc", + "aaBcC", + 6 + ], + [ + "Acc", + "bCbAaCab", + 13 + ], + [ + "AccA", + "BCBA", + 5 + ], + [ + "AccA", + "aC", + 6 + ], + [ + "AccAB", + "Bcb", + 7 + ], + [ + "AccABcbBC", + "AAcBcAcc", + 9 + ], + [ + "AccACBbA", + "c", + 14 + ], + [ + "AccAbcC", + "cbbB", + 10 + ], + [ + "AccBbab", + "bcAaaaA", + 10 + ], + [ + "AccC", + "BBbca", + 8 + ], + [ + "AccCBBACa", + "b", + 17 + ], + [ + "AccCCCcaa", + "aabC", + 15 + ], + [ + "AccCcAA", + "CCbAaABCA", + 13 + ], + [ + "Acca", + "C", + 7 + ], + [ + "Acca", + "aB", + 7 + ], + [ + "AccaCABA", + "CBBBc", + 13 + ], + [ + "AccaaAbbC", + "BaAB", + 13 + ], + [ + "Accb", + "baBaaCaCa", + 15 + ], + [ + "Accbbbb", + "bA", + 12 + ], + [ + "Accbcc", + "aaAAcb", + 9 + ], + [ + "Accc", + "ABbAB", + 8 + ], + [ + "AcccAC", + "BAAbCacBb", + 13 + ], + [ + "AcccAbbB", + "BaBCa", + 14 + ], + [ + "AcccBAAbC", + "CCbCb", + 13 + ], + [ + "AcccCACac", + "BAbccaC", + 11 + ], + [ + "AcccCabab", + "bBAAaBaA", + 13 + ], + [ + "AcccCb", + "BACacbcC", + 9 + ], + [ + "AcccaCb", + "CBBCcaCB", + 8 + ], + [ + "Acccac", + "acCaAcaCb", + 9 + ], + [ + "B", + "A", + 2 + ], + [ + "B", + "AAABB", + 8 + ], + [ + "B", + "AAAcc", + 10 + ], + [ + "B", + "AABBcC", + 10 + ], + [ + "B", + "AABCBb", + 10 + ], + [ + "B", + "AAC", + 6 + ], + [ + "B", + "AACA", + 8 + ], + [ + "B", + "AACaaaa", + 14 + ], + [ + "B", + "AAcaBa", + 10 + ], + [ + "B", + "AAcacbab", + 15 + ], + [ + "B", + "ABBc", + 6 + ], + [ + "B", + "ABCBcBCB", + 14 + ], + [ + "B", + "ABaAba", + 10 + ], + [ + "B", + "ABaB", + 6 + ], + [ + "B", + "ABaBba", + 10 + ], + [ + "B", + "ABaaCcBa", + 14 + ], + [ + "B", + "ABc", + 4 + ], + [ + "B", + "ACB", + 4 + ], + [ + "B", + "ACCCCCBc", + 14 + ], + [ + "B", + "ACbbC", + 9 + ], + [ + "B", + "ACbbCA", + 11 + ], + [ + "B", + "AaA", + 6 + ], + [ + "B", + "AaACbbaaB", + 16 + ], + [ + "B", + "AaAaCCbaa", + 17 + ], + [ + "B", + "AaBaBcba", + 14 + ], + [ + "B", + "AabcAaa", + 13 + ], + [ + "B", + "AacCacAb", + 15 + ], + [ + "B", + "AaccbBacB", + 16 + ], + [ + "B", + "Ab", + 3 + ], + [ + "B", + "AbBc", + 6 + ], + [ + "B", + "AbaBCcaBa", + 16 + ], + [ + "B", + "Abc", + 5 + ], + [ + "B", + "AcA", + 6 + ], + [ + "B", + "AcBA", + 6 + ], + [ + "B", + "AcBaBbCc", + 14 + ], + [ + "B", + "AcCc", + 8 + ], + [ + "B", + "AcaC", + 8 + ], + [ + "B", + "AccBacc", + 12 + ], + [ + "B", + "B", + 0 + ], + [ + "B", + "BAA", + 4 + ], + [ + "B", + "BBAbCc", + 10 + ], + [ + "B", + "BBAccA", + 10 + ], + [ + "B", + "BBBCb", + 8 + ], + [ + "B", + "BBa", + 4 + ], + [ + "B", + "BBaAcacab", + 16 + ], + [ + "B", + "BBc", + 4 + ], + [ + "B", + "BC", + 2 + ], + [ + "B", + "BCC", + 4 + ], + [ + "B", + "BCCbaBA", + 12 + ], + [ + "B", + "BCbCb", + 8 + ], + [ + "B", + "BCcC", + 6 + ], + [ + "B", + "BCcCccC", + 12 + ], + [ + "B", + "BCcb", + 6 + ], + [ + "B", + "BCcc", + 6 + ], + [ + "B", + "Ba", + 2 + ], + [ + "B", + "BaBCAaCAa", + 16 + ], + [ + "B", + "Baca", + 6 + ], + [ + "B", + "BacbcaCaC", + 16 + ], + [ + "B", + "BbcCCbAbb", + 16 + ], + [ + "B", + "BcACbAAC", + 14 + ], + [ + "B", + "BcACbbbc", + 14 + ], + [ + "B", + "BcabCBbab", + 16 + ], + [ + "B", + "C", + 2 + ], + [ + "B", + "CAAb", + 7 + ], + [ + "B", + "CAAba", + 9 + ], + [ + "B", + "CACcA", + 10 + ], + [ + "B", + "CB", + 2 + ], + [ + "B", + "CBCcAcbb", + 14 + ], + [ + "B", + "CBc", + 4 + ], + [ + "B", + "CBcA", + 6 + ], + [ + "B", + "CBcCC", + 8 + ], + [ + "B", + "CCaaCaac", + 16 + ], + [ + "B", + "CCcBBCb", + 12 + ], + [ + "B", + "Ca", + 4 + ], + [ + "B", + "CaCC", + 8 + ], + [ + "B", + "Caa", + 6 + ], + [ + "B", + "Cab", + 5 + ], + [ + "B", + "Cacbc", + 9 + ], + [ + "B", + "Cb", + 3 + ], + [ + "B", + "Cbc", + 5 + ], + [ + "B", + "CbcaAaACC", + 17 + ], + [ + "B", + "CbccCbca", + 15 + ], + [ + "B", + "Cc", + 4 + ], + [ + "B", + "CcAacCAA", + 16 + ], + [ + "B", + "CcBb", + 6 + ], + [ + "B", + "CcbBb", + 8 + ], + [ + "B", + "Ccc", + 6 + ], + [ + "B", + "a", + 2 + ], + [ + "B", + "aAA", + 6 + ], + [ + "B", + "aAACAb", + 11 + ], + [ + "B", + "aABCB", + 8 + ], + [ + "B", + "aACaCCBbA", + 16 + ], + [ + "B", + "aB", + 2 + ], + [ + "B", + "aBBbc", + 8 + ], + [ + "B", + "aBabc", + 8 + ], + [ + "B", + "aBcbCA", + 10 + ], + [ + "B", + "aBccCCBC", + 14 + ], + [ + "B", + "aC", + 4 + ], + [ + "B", + "aCBBAAc", + 12 + ], + [ + "B", + "aCBcA", + 8 + ], + [ + "B", + "aCCaAabB", + 14 + ], + [ + "B", + "aCCcbbac", + 15 + ], + [ + "B", + "aCa", + 6 + ], + [ + "B", + "aCaBABACa", + 16 + ], + [ + "B", + "aCcbCA", + 11 + ], + [ + "B", + "aaBBac", + 10 + ], + [ + "B", + "aaBCC", + 8 + ], + [ + "B", + "aab", + 5 + ], + [ + "B", + "aabBcbBa", + 14 + ], + [ + "B", + "aacbAB", + 10 + ], + [ + "B", + "abA", + 5 + ], + [ + "B", + "abAAA", + 9 + ], + [ + "B", + "abBBCBBcb", + 16 + ], + [ + "B", + "abC", + 5 + ], + [ + "B", + "abCcacAC", + 15 + ], + [ + "B", + "ac", + 4 + ], + [ + "B", + "acAbAB", + 10 + ], + [ + "B", + "acBcBba", + 12 + ], + [ + "B", + "b", + 1 + ], + [ + "B", + "bA", + 3 + ], + [ + "B", + "bAAbcbb", + 13 + ], + [ + "B", + "bABAacc", + 12 + ], + [ + "B", + "bACA", + 7 + ], + [ + "B", + "bAaAbBbcb", + 16 + ], + [ + "B", + "bAc", + 5 + ], + [ + "B", + "bB", + 2 + ], + [ + "B", + "bC", + 3 + ], + [ + "B", + "bCAbccaCA", + 17 + ], + [ + "B", + "bCCA", + 7 + ], + [ + "B", + "bCabCB", + 10 + ], + [ + "B", + "bCabcCa", + 13 + ], + [ + "B", + "ba", + 3 + ], + [ + "B", + "baba", + 7 + ], + [ + "B", + "babaa", + 9 + ], + [ + "B", + "bb", + 3 + ], + [ + "B", + "bbc", + 5 + ], + [ + "B", + "bbcbCCCb", + 15 + ], + [ + "B", + "bc", + 3 + ], + [ + "B", + "bcCBCacb", + 14 + ], + [ + "B", + "bcaAcbbB", + 14 + ], + [ + "B", + "c", + 2 + ], + [ + "B", + "cABAAb", + 10 + ], + [ + "B", + "cAaaACc", + 14 + ], + [ + "B", + "cAcCA", + 10 + ], + [ + "B", + "cAccC", + 10 + ], + [ + "B", + "cB", + 2 + ], + [ + "B", + "cBBBa", + 8 + ], + [ + "B", + "cBaCCba", + 12 + ], + [ + "B", + "cBbbaAaBC", + 16 + ], + [ + "B", + "cBcCaA", + 10 + ], + [ + "B", + "cC", + 4 + ], + [ + "B", + "cCABB", + 8 + ], + [ + "B", + "cCBAacA", + 12 + ], + [ + "B", + "cCbBBAccB", + 16 + ], + [ + "B", + "cCbcaBaca", + 16 + ], + [ + "B", + "cCcBaC", + 10 + ], + [ + "B", + "cCcbcaCcc", + 17 + ], + [ + "B", + "cabBca", + 10 + ], + [ + "B", + "cabccaAAB", + 16 + ], + [ + "B", + "cb", + 3 + ], + [ + "B", + "cbAACcCCb", + 17 + ], + [ + "B", + "cbAC", + 7 + ], + [ + "B", + "cbBbCC", + 10 + ], + [ + "B", + "cbC", + 5 + ], + [ + "B", + "cbCaba", + 11 + ], + [ + "B", + "cbCbBBaa", + 14 + ], + [ + "B", + "cbaaAAA", + 13 + ], + [ + "B", + "cbaaCAb", + 13 + ], + [ + "B", + "cbbCaccA", + 15 + ], + [ + "B", + "cbbb", + 7 + ], + [ + "B", + "cbcCa", + 9 + ], + [ + "B", + "ccABaB", + 10 + ], + [ + "B", + "ccAc", + 8 + ], + [ + "B", + "ccCbCbc", + 13 + ], + [ + "B", + "ccabaC", + 11 + ], + [ + "B", + "ccb", + 5 + ], + [ + "BA", + "A", + 2 + ], + [ + "BA", + "ABaabAbAc", + 14 + ], + [ + "BA", + "B", + 2 + ], + [ + "BA", + "BBABbCAaB", + 14 + ], + [ + "BA", + "BBcaC", + 7 + ], + [ + "BA", + "BBcacAbcb", + 14 + ], + [ + "BA", + "BCbCbbcAB", + 14 + ], + [ + "BA", + "CACCAaac", + 14 + ], + [ + "BA", + "CAaaacb", + 12 + ], + [ + "BA", + "CBC", + 4 + ], + [ + "BA", + "aACbbcba", + 14 + ], + [ + "BA", + "ab", + 4 + ], + [ + "BA", + "abCAa", + 7 + ], + [ + "BA", + "b", + 3 + ], + [ + "BA", + "bABaAca", + 10 + ], + [ + "BA", + "baCCBbC", + 12 + ], + [ + "BA", + "bbBcB", + 8 + ], + [ + "BA", + "bbbCC", + 9 + ], + [ + "BA", + "bbbaBA", + 8 + ], + [ + "BA", + "bbbbCBb", + 12 + ], + [ + "BA", + "cBABaC", + 8 + ], + [ + "BA", + "cBBbbA", + 8 + ], + [ + "BA", + "cC", + 4 + ], + [ + "BA", + "ccB", + 6 + ], + [ + "BA", + "ccCa", + 7 + ], + [ + "BAA", + "A", + 4 + ], + [ + "BAA", + "abA", + 4 + ], + [ + "BAA", + "acCAc", + 8 + ], + [ + "BAA", + "bB", + 5 + ], + [ + "BAA", + "bbcACCCAB", + 13 + ], + [ + "BAA", + "bcBC", + 7 + ], + [ + "BAA", + "cBaCaBc", + 10 + ], + [ + "BAAA", + "B", + 6 + ], + [ + "BAAA", + "BBCacaC", + 10 + ], + [ + "BAAA", + "bBAc", + 5 + ], + [ + "BAAAAAA", + "CbcbAc", + 12 + ], + [ + "BAAAACb", + "cbcACabBc", + 13 + ], + [ + "BAAAC", + "cbCBcbCC", + 12 + ], + [ + "BAAAaCCAB", + "bcCB", + 12 + ], + [ + "BAAAabC", + "CCaaaAcc", + 11 + ], + [ + "BAAAbcBa", + "aCcAAAcc", + 12 + ], + [ + "BAABC", + "bccab", + 9 + ], + [ + "BAABaC", + "Cbb", + 11 + ], + [ + "BAABabAb", + "ACC", + 14 + ], + [ + "BAAC", + "bB", + 7 + ], + [ + "BAAC", + "ccaBab", + 10 + ], + [ + "BAAC", + "ccbC", + 6 + ], + [ + "BAACAA", + "a", + 11 + ], + [ + "BAACaC", + "Cac", + 7 + ], + [ + "BAAaA", + "a", + 8 + ], + [ + "BAAaACaC", + "bb", + 15 + ], + [ + "BAAaB", + "ccAcbA", + 9 + ], + [ + "BAAaa", + "b", + 9 + ], + [ + "BAAaa", + "cacCaBc", + 11 + ], + [ + "BAAaaac", + "c", + 12 + ], + [ + "BAAaabB", + "aaAAA", + 9 + ], + [ + "BAAaac", + "BCaCcCAB", + 12 + ], + [ + "BAAacA", + "ccBAcCAc", + 11 + ], + [ + "BAAb", + "AA", + 4 + ], + [ + "BAAbAC", + "BbAccCba", + 10 + ], + [ + "BAAbCbbC", + "CCCBBc", + 11 + ], + [ + "BAAbacbC", + "aBAabc", + 9 + ], + [ + "BAAbbACC", + "CCABb", + 11 + ], + [ + "BAAcCBa", + "ACcA", + 9 + ], + [ + "BAAcaBBCa", + "AccB", + 12 + ], + [ + "BAAcac", + "Bb", + 10 + ], + [ + "BAAcc", + "CACbabA", + 11 + ], + [ + "BAB", + "AcbBaaBa", + 11 + ], + [ + "BAB", + "BB", + 2 + ], + [ + "BAB", + "Caa", + 5 + ], + [ + "BAB", + "cc", + 6 + ], + [ + "BABA", + "abbccc", + 11 + ], + [ + "BABA", + "caCca", + 8 + ], + [ + "BABB", + "bBCCBcAcb", + 13 + ], + [ + "BABBAAc", + "caAaa", + 10 + ], + [ + "BABBbCA", + "bAca", + 9 + ], + [ + "BABBc", + "BCCcAcCC", + 11 + ], + [ + "BABCB", + "AB", + 6 + ], + [ + "BABCcBbA", + "CBb", + 10 + ], + [ + "BABa", + "abbbaccAa", + 14 + ], + [ + "BABabAB", + "b", + 12 + ], + [ + "BABacA", + "abcBA", + 8 + ], + [ + "BABb", + "cC", + 8 + ], + [ + "BABbC", + "BaA", + 7 + ], + [ + "BABbb", + "Ccc", + 10 + ], + [ + "BABc", + "aB", + 5 + ], + [ + "BABcACCaC", + "CbbBaCbaC", + 10 + ], + [ + "BABcBA", + "CA", + 9 + ], + [ + "BABcBCaBc", + "CBAaBCC", + 11 + ], + [ + "BABcBcBa", + "bCBACcc", + 11 + ], + [ + "BABcaBB", + "bAaabb", + 7 + ], + [ + "BABcb", + "CcCAaC", + 11 + ], + [ + "BABcbcaa", + "BbbCcAAab", + 11 + ], + [ + "BABccba", + "cCbbbA", + 10 + ], + [ + "BAC", + "BB", + 4 + ], + [ + "BAC", + "BBacAbC", + 8 + ], + [ + "BAC", + "BaacBaC", + 9 + ], + [ + "BAC", + "BbbBaB", + 9 + ], + [ + "BAC", + "CAAaa", + 8 + ], + [ + "BAC", + "CBBbBccbc", + 15 + ], + [ + "BAC", + "b", + 5 + ], + [ + "BAC", + "babAAcA", + 10 + ], + [ + "BACACcCcc", + "cbB", + 16 + ], + [ + "BACB", + "AaCABa", + 7 + ], + [ + "BACBAACba", + "BaA", + 13 + ], + [ + "BACBCAC", + "A", + 12 + ], + [ + "BACBaB", + "Bcbc", + 8 + ], + [ + "BACBbCbB", + "ACaAAaCb", + 12 + ], + [ + "BACC", + "ab", + 7 + ], + [ + "BACCBcbaa", + "ab", + 15 + ], + [ + "BACCCAaCA", + "bCCcBb", + 12 + ], + [ + "BACCbABA", + "AAAAcAB", + 10 + ], + [ + "BACCbAac", + "AcBCaacB", + 10 + ], + [ + "BACCc", + "BbAAcA", + 7 + ], + [ + "BACCc", + "aBbbBBCa", + 12 + ], + [ + "BACaC", + "CB", + 8 + ], + [ + "BACab", + "bCA", + 6 + ], + [ + "BACb", + "AcBC", + 6 + ], + [ + "BACbAbCbC", + "BbC", + 12 + ], + [ + "BACbbaaAc", + "ACabC", + 11 + ], + [ + "BACcBbcCb", + "AcbB", + 11 + ], + [ + "BACcbBbc", + "AC", + 12 + ], + [ + "BACccabb", + "bAcAAa", + 10 + ], + [ + "BACcccA", + "aacC", + 10 + ], + [ + "BAa", + "AC", + 4 + ], + [ + "BAa", + "AbCBcA", + 9 + ], + [ + "BAa", + "CabbAabb", + 11 + ], + [ + "BAa", + "cBaAa", + 4 + ], + [ + "BAaA", + "BCB", + 6 + ], + [ + "BAaABbB", + "ABabABcbC", + 9 + ], + [ + "BAaAbCc", + "acb", + 10 + ], + [ + "BAaAcCb", + "AbbC", + 10 + ], + [ + "BAaAcbB", + "Aa", + 10 + ], + [ + "BAaBBaAb", + "bBaBcaAAa", + 9 + ], + [ + "BAaBaB", + "A", + 10 + ], + [ + "BAaBbCc", + "cacBaAcaB", + 13 + ], + [ + "BAaBbabc", + "A", + 14 + ], + [ + "BAaBcCaaa", + "ab", + 15 + ], + [ + "BAaBcCb", + "aCAA", + 11 + ], + [ + "BAaCB", + "BBb", + 7 + ], + [ + "BAaCB", + "aABACA", + 7 + ], + [ + "BAaCaA", + "acB", + 9 + ], + [ + "BAaCabCCa", + "b", + 16 + ], + [ + "BAaCbAC", + "bA", + 10 + ], + [ + "BAaCbB", + "aCcbbAAA", + 13 + ], + [ + "BAaCcc", + "bc", + 9 + ], + [ + "BAaaACaA", + "Ccbcaa", + 12 + ], + [ + "BAaaCCb", + "bCBC", + 11 + ], + [ + "BAab", + "c", + 8 + ], + [ + "BAab", + "ccaba", + 6 + ], + [ + "BAabAbBB", + "ABaBaaBa", + 10 + ], + [ + "BAabCA", + "Cb", + 10 + ], + [ + "BAabCCA", + "CCbBcb", + 11 + ], + [ + "BAac", + "bBbcaca", + 8 + ], + [ + "BAacBb", + "cABAca", + 9 + ], + [ + "BAacbBbCb", + "bBAca", + 13 + ], + [ + "BAacbaBC", + "ABCbcaCAc", + 12 + ], + [ + "BAaccAba", + "BAbBbCA", + 11 + ], + [ + "BAb", + "Aa", + 4 + ], + [ + "BAb", + "AbaccbCbC", + 14 + ], + [ + "BAb", + "BaCa", + 5 + ], + [ + "BAb", + "CCaBA", + 8 + ], + [ + "BAb", + "CbCb", + 5 + ], + [ + "BAb", + "caAAC", + 8 + ], + [ + "BAb", + "ccCAAAABC", + 15 + ], + [ + "BAbABaBbC", + "CAAcAb", + 11 + ], + [ + "BAbACcA", + "aBaaCcc", + 8 + ], + [ + "BAbAa", + "ABbCbabac", + 11 + ], + [ + "BAbAa", + "c", + 10 + ], + [ + "BAbAc", + "cBc", + 7 + ], + [ + "BAbAcABa", + "ac", + 13 + ], + [ + "BAbAcbB", + "aCAcBB", + 6 + ], + [ + "BAbBA", + "BcaCBcaBa", + 11 + ], + [ + "BAbBAB", + "ABBCCb", + 8 + ], + [ + "BAbBBaB", + "abaA", + 9 + ], + [ + "BAbBCc", + "aaAcAcAAC", + 14 + ], + [ + "BAbBc", + "ABcAAABb", + 10 + ], + [ + "BAbCCbBC", + "bc", + 13 + ], + [ + "BAbCbBacB", + "acBca", + 12 + ], + [ + "BAbCc", + "BBbcBAab", + 11 + ], + [ + "BAbCcB", + "CbacAaaa", + 14 + ], + [ + "BAbCcBB", + "ccaBBcACC", + 14 + ], + [ + "BAba", + "Bc", + 6 + ], + [ + "BAbaBCAB", + "bbCbBa", + 11 + ], + [ + "BAbaBbBc", + "baAC", + 11 + ], + [ + "BAbaCc", + "C", + 10 + ], + [ + "BAbabca", + "cBabaCb", + 8 + ], + [ + "BAbacCaa", + "CCa", + 11 + ], + [ + "BAbb", + "baBaBaB", + 9 + ], + [ + "BAbbBAA", + "ACbcccc", + 12 + ], + [ + "BAbc", + "B", + 6 + ], + [ + "BAbc", + "bABCB", + 5 + ], + [ + "BAbc", + "bcBAb", + 6 + ], + [ + "BAbcAcA", + "ccA", + 8 + ], + [ + "BAbcaBab", + "CaACACbcB", + 13 + ], + [ + "BAbccAcB", + "BA", + 12 + ], + [ + "BAbccaBA", + "CCCcaa", + 10 + ], + [ + "BAc", + "AAAccAa", + 10 + ], + [ + "BAc", + "Bbc", + 2 + ], + [ + "BAc", + "ba", + 4 + ], + [ + "BAcA", + "Ac", + 4 + ], + [ + "BAcAA", + "acbCAA", + 7 + ], + [ + "BAcAACc", + "accA", + 9 + ], + [ + "BAcAAb", + "AbC", + 10 + ], + [ + "BAcAB", + "aAacCcbac", + 13 + ], + [ + "BAcACCb", + "cccBC", + 10 + ], + [ + "BAcACcBA", + "BCAbccA", + 7 + ], + [ + "BAcAcC", + "aAcBb", + 8 + ], + [ + "BAcBa", + "CCAbBCc", + 10 + ], + [ + "BAcBcba", + "aaa", + 11 + ], + [ + "BAcCBCbaA", + "bCb", + 13 + ], + [ + "BAcCC", + "ACCaC", + 5 + ], + [ + "BAcCCBAbA", + "b", + 16 + ], + [ + "BAcCaB", + "cCBBCaa", + 10 + ], + [ + "BAcCbC", + "CAB", + 9 + ], + [ + "BAcCbaC", + "CCBBaCabB", + 14 + ], + [ + "BAcCbc", + "Cb", + 8 + ], + [ + "BAcCcAACa", + "aacaCaA", + 11 + ], + [ + "BAcaBBBc", + "a", + 14 + ], + [ + "BAcaBc", + "CAbaa", + 8 + ], + [ + "BAcaC", + "cAcaCA", + 4 + ], + [ + "BAcaab", + "ACbA", + 8 + ], + [ + "BAcab", + "BaCbca", + 7 + ], + [ + "BAcbA", + "AbA", + 4 + ], + [ + "BAcbA", + "bACBCbbCA", + 10 + ], + [ + "BAcbAcCB", + "ac", + 13 + ], + [ + "BAcbBCC", + "bBaba", + 11 + ], + [ + "BAcbCC", + "c", + 10 + ], + [ + "BAcbCc", + "B", + 10 + ], + [ + "BAcbabcB", + "aAa", + 12 + ], + [ + "BAcbbba", + "Cb", + 11 + ], + [ + "BAcbcABbA", + "baABcBCa", + 10 + ], + [ + "BAccAAA", + "AaaC", + 10 + ], + [ + "BAccAAca", + "BBCbbCcab", + 11 + ], + [ + "BAccAbCB", + "Cba", + 13 + ], + [ + "BAccCAb", + "cCBcCcac", + 11 + ], + [ + "BAccCAbC", + "BCabCBA", + 11 + ], + [ + "BAccCCcA", + "CbbBaCB", + 14 + ], + [ + "BAccCcCB", + "bcCAB", + 9 + ], + [ + "BAcca", + "cBcCBBcbC", + 13 + ], + [ + "BAccaAaC", + "cbBcAaA", + 10 + ], + [ + "BAcccCbb", + "Bb", + 12 + ], + [ + "BB", + "A", + 4 + ], + [ + "BB", + "AACBAacBB", + 14 + ], + [ + "BB", + "ABaA", + 6 + ], + [ + "BB", + "ACbbCcc", + 12 + ], + [ + "BB", + "AbAABC", + 9 + ], + [ + "BB", + "AbbbBBa", + 10 + ], + [ + "BB", + "BC", + 2 + ], + [ + "BB", + "Ba", + 2 + ], + [ + "BB", + "BbcAaACB", + 12 + ], + [ + "BB", + "CAcaACbBb", + 15 + ], + [ + "BB", + "CBabcc", + 9 + ], + [ + "BB", + "CaCBb", + 7 + ], + [ + "BB", + "CaaAAaAAc", + 18 + ], + [ + "BB", + "Cab", + 5 + ], + [ + "BB", + "Cb", + 3 + ], + [ + "BB", + "CbAAC", + 9 + ], + [ + "BB", + "CbcbCcBc", + 13 + ], + [ + "BB", + "Ccccc", + 10 + ], + [ + "BB", + "a", + 4 + ], + [ + "BB", + "aAAbcc", + 11 + ], + [ + "BB", + "aACBABa", + 10 + ], + [ + "BB", + "aACbBCaac", + 15 + ], + [ + "BB", + "aAcCCb", + 11 + ], + [ + "BB", + "aCACcBC", + 12 + ], + [ + "BB", + "aCa", + 6 + ], + [ + "BB", + "aa", + 4 + ], + [ + "BB", + "bAABCCabC", + 15 + ], + [ + "BB", + "bABBCb", + 8 + ], + [ + "BB", + "bBacaAb", + 11 + ], + [ + "BB", + "bc", + 3 + ], + [ + "BB", + "cBBcCBaAa", + 14 + ], + [ + "BB", + "cCaAaBc", + 12 + ], + [ + "BB", + "cCb", + 5 + ], + [ + "BB", + "cCc", + 6 + ], + [ + "BB", + "caBcCA", + 10 + ], + [ + "BB", + "cac", + 6 + ], + [ + "BB", + "cbb", + 4 + ], + [ + "BB", + "ccaC", + 8 + ], + [ + "BBA", + "BB", + 2 + ], + [ + "BBA", + "BCCaaAb", + 10 + ], + [ + "BBA", + "Bb", + 3 + ], + [ + "BBA", + "Bba", + 2 + ], + [ + "BBA", + "CBbBab", + 7 + ], + [ + "BBA", + "aaBbAc", + 7 + ], + [ + "BBA", + "ab", + 5 + ], + [ + "BBA", + "bAAaBCbA", + 11 + ], + [ + "BBA", + "cbcBbC", + 9 + ], + [ + "BBAACb", + "BBaBbb", + 5 + ], + [ + "BBAAb", + "abaBbac", + 10 + ], + [ + "BBAAbbCc", + "a", + 15 + ], + [ + "BBAAcAb", + "cCBCaBCA", + 12 + ], + [ + "BBAAcbba", + "aCACbcCc", + 13 + ], + [ + "BBABA", + "bbccbbb", + 11 + ], + [ + "BBABACCCB", + "c", + 17 + ], + [ + "BBABBAca", + "aCCAAcCA", + 13 + ], + [ + "BBABBaBA", + "CaAaaa", + 11 + ], + [ + "BBABBaaBB", + "CccbccAc", + 16 + ], + [ + "BBABa", + "b", + 9 + ], + [ + "BBABcC", + "aCAA", + 10 + ], + [ + "BBABccb", + "C", + 13 + ], + [ + "BBAC", + "bBCAcBaC", + 9 + ], + [ + "BBACB", + "bAb", + 6 + ], + [ + "BBACC", + "CBccc", + 6 + ], + [ + "BBACC", + "aCBBBB", + 10 + ], + [ + "BBACaBC", + "C", + 12 + ], + [ + "BBACcCbBa", + "CCCcCC", + 12 + ], + [ + "BBAbA", + "AbCaCcB", + 12 + ], + [ + "BBAbCcacC", + "aBaacBcb", + 11 + ], + [ + "BBAba", + "BCbBcB", + 9 + ], + [ + "BBAbcB", + "BBc", + 6 + ], + [ + "BBAbcBBaA", + "bCcAB", + 13 + ], + [ + "BBAbcC", + "BacA", + 7 + ], + [ + "BBAcA", + "bBcaAacbb", + 11 + ], + [ + "BBAcC", + "cC", + 6 + ], + [ + "BBB", + "BaBbB", + 4 + ], + [ + "BBB", + "Bbcbc", + 6 + ], + [ + "BBB", + "CC", + 6 + ], + [ + "BBB", + "CCCAbc", + 11 + ], + [ + "BBB", + "CcbcBc", + 9 + ], + [ + "BBB", + "aaAbb", + 8 + ], + [ + "BBBAAA", + "AA", + 8 + ], + [ + "BBBAAAcB", + "BAcAcCCA", + 11 + ], + [ + "BBBACc", + "AaBCBb", + 10 + ], + [ + "BBBAbAaAB", + "aAcBABA", + 13 + ], + [ + "BBBAccBbC", + "ccbCbAb", + 14 + ], + [ + "BBBB", + "baABcbb", + 9 + ], + [ + "BBBBCCacA", + "bbCCc", + 10 + ], + [ + "BBBBcCA", + "bBAabcB", + 10 + ], + [ + "BBBBccCCC", + "C", + 16 + ], + [ + "BBBCABbb", + "cAAaAB", + 12 + ], + [ + "BBBCab", + "abBACbBc", + 10 + ], + [ + "BBBa", + "CCabbCcaB", + 14 + ], + [ + "BBBa", + "cAa", + 6 + ], + [ + "BBBaCA", + "AAbbaaACB", + 12 + ], + [ + "BBBaaBb", + "cBcaac", + 8 + ], + [ + "BBBab", + "bAa", + 7 + ], + [ + "BBBac", + "aaCcCBcAB", + 15 + ], + [ + "BBBb", + "B", + 6 + ], + [ + "BBBbCCb", + "BbBaCc", + 6 + ], + [ + "BBBba", + "BCaC", + 8 + ], + [ + "BBBbcBcCA", + "BbBB", + 11 + ], + [ + "BBBbccc", + "A", + 14 + ], + [ + "BBBc", + "cCAba", + 9 + ], + [ + "BBBcAB", + "AbCAAbc", + 10 + ], + [ + "BBBcBAa", + "AbBCCa", + 8 + ], + [ + "BBBcBabbB", + "aCC", + 16 + ], + [ + "BBBcbBA", + "AbACCCbCb", + 14 + ], + [ + "BBBcc", + "CCbbabCb", + 12 + ], + [ + "BBBccBaaB", + "BcCB", + 11 + ], + [ + "BBC", + "CaAAAaCbB", + 16 + ], + [ + "BBC", + "Cbcbcc", + 9 + ], + [ + "BBC", + "abBBacA", + 9 + ], + [ + "BBC", + "b", + 5 + ], + [ + "BBC", + "bCBAA", + 7 + ], + [ + "BBC", + "bcCaAbb", + 11 + ], + [ + "BBC", + "cba", + 5 + ], + [ + "BBCA", + "BAcCC", + 6 + ], + [ + "BBCAbbbc", + "BAa", + 12 + ], + [ + "BBCAcb", + "Ca", + 9 + ], + [ + "BBCBb", + "aac", + 9 + ], + [ + "BBCBbc", + "CaaBB", + 9 + ], + [ + "BBCBcaBb", + "ccCcCbBC", + 11 + ], + [ + "BBCBcb", + "CBcCBcacC", + 10 + ], + [ + "BBCC", + "C", + 6 + ], + [ + "BBCCBC", + "aCbbA", + 9 + ], + [ + "BBCCBab", + "cBCaaC", + 8 + ], + [ + "BBCCCbaCB", + "AAA", + 17 + ], + [ + "BBCCaBbB", + "BaBAcCbC", + 11 + ], + [ + "BBCCccBCB", + "bCaaCA", + 13 + ], + [ + "BBCa", + "aB", + 6 + ], + [ + "BBCaa", + "AccACcAAC", + 14 + ], + [ + "BBCabCCBB", + "BcAcCaCC", + 12 + ], + [ + "BBCbB", + "BBb", + 4 + ], + [ + "BBCbB", + "CB", + 6 + ], + [ + "BBCbB", + "caABAb", + 10 + ], + [ + "BBCbc", + "BAAbcaaBc", + 11 + ], + [ + "BBCc", + "BabAb", + 7 + ], + [ + "BBCc", + "cbabab", + 10 + ], + [ + "BBCcbACcB", + "Ab", + 15 + ], + [ + "BBCccCAba", + "aCcCCBAb", + 9 + ], + [ + "BBa", + "AcBcBAb", + 9 + ], + [ + "BBa", + "Ccb", + 6 + ], + [ + "BBa", + "aCCcbCAc", + 14 + ], + [ + "BBa", + "bBaCBa", + 6 + ], + [ + "BBa", + "cbAcBBaA", + 10 + ], + [ + "BBaA", + "bAAaBAAAB", + 12 + ], + [ + "BBaAAccbb", + "CABB", + 14 + ], + [ + "BBaACBaB", + "cbcC", + 13 + ], + [ + "BBaAab", + "BCB", + 9 + ], + [ + "BBaAcAAcb", + "cbCBBCAcb", + 11 + ], + [ + "BBaAcB", + "ACbAcb", + 7 + ], + [ + "BBaBbcB", + "C", + 13 + ], + [ + "BBaCB", + "cC", + 8 + ], + [ + "BBaCCA", + "ABAcc", + 7 + ], + [ + "BBaCccb", + "Cbcbb", + 9 + ], + [ + "BBaa", + "ba", + 5 + ], + [ + "BBaaCCBA", + "AbCbC", + 12 + ], + [ + "BBaaCca", + "aaBBcbAB", + 13 + ], + [ + "BBaaa", + "B", + 8 + ], + [ + "BBaab", + "bbAcbB", + 7 + ], + [ + "BBaabcA", + "BACBAC", + 10 + ], + [ + "BBaacbcaB", + "Abacab", + 10 + ], + [ + "BBaacc", + "C", + 11 + ], + [ + "BBabBa", + "BbBBcAACB", + 13 + ], + [ + "BBabcBab", + "abB", + 10 + ], + [ + "BBabcbb", + "BbbCbA", + 6 + ], + [ + "BBac", + "cBAB", + 5 + ], + [ + "BBacAcaAa", + "Bba", + 13 + ], + [ + "BBacCaBBa", + "aa", + 14 + ], + [ + "BBacCc", + "bcaBA", + 9 + ], + [ + "BBacaABc", + "Cbbaac", + 10 + ], + [ + "BBacb", + "AbbaCBCb", + 9 + ], + [ + "BBacb", + "abCACCBBA", + 14 + ], + [ + "BBb", + "BAcaAaca", + 14 + ], + [ + "BBb", + "BBa", + 2 + ], + [ + "BBb", + "CAcBCAAc", + 14 + ], + [ + "BBb", + "aBACcCCC", + 14 + ], + [ + "BBb", + "acbCc", + 8 + ], + [ + "BBbA", + "ca", + 7 + ], + [ + "BBbAACb", + "ACCbb", + 10 + ], + [ + "BBbABAaAB", + "cAbaCba", + 13 + ], + [ + "BBbABbac", + "cAB", + 12 + ], + [ + "BBbAabbCA", + "A", + 16 + ], + [ + "BBbAbcB", + "AcAAcbABa", + 12 + ], + [ + "BBbAcaAB", + "cBB", + 12 + ], + [ + "BBbAcc", + "bBAb", + 7 + ], + [ + "BBbB", + "ccABABc", + 10 + ], + [ + "BBbBB", + "a", + 10 + ], + [ + "BBbBBbba", + "aACbb", + 12 + ], + [ + "BBbBBcacc", + "AcAa", + 15 + ], + [ + "BBbBCbBc", + "BbABC", + 9 + ], + [ + "BBbBaa", + "bbBbbbaAb", + 9 + ], + [ + "BBbCCCCbc", + "ccaAB", + 15 + ], + [ + "BBbCCcbAc", + "bABcac", + 11 + ], + [ + "BBbCbcb", + "Ab", + 12 + ], + [ + "BBba", + "a", + 6 + ], + [ + "BBbaBc", + "ac", + 8 + ], + [ + "BBbab", + "AcAB", + 8 + ], + [ + "BBbabb", + "CCbAa", + 9 + ], + [ + "BBbabbab", + "bCBBbaA", + 9 + ], + [ + "BBbb", + "BA", + 6 + ], + [ + "BBbbACc", + "A", + 12 + ], + [ + "BBbbCCBB", + "AaCcBAAA", + 15 + ], + [ + "BBbcABBbC", + "AaC", + 14 + ], + [ + "BBbcB", + "ACbA", + 8 + ], + [ + "BBbcCac", + "cbb", + 11 + ], + [ + "BBbccCCBB", + "BbaBcb", + 12 + ], + [ + "BBc", + "ABCbAA", + 9 + ], + [ + "BBc", + "BbbABAACA", + 13 + ], + [ + "BBc", + "BcccAbBaC", + 13 + ], + [ + "BBc", + "aBaCccA", + 10 + ], + [ + "BBc", + "aC", + 5 + ], + [ + "BBc", + "aCacBbCCa", + 14 + ], + [ + "BBcAAA", + "Acbc", + 10 + ], + [ + "BBcAAac", + "acCcAbc", + 9 + ], + [ + "BBcABAA", + "BbBaABaC", + 7 + ], + [ + "BBcB", + "AAacBC", + 8 + ], + [ + "BBcB", + "aAaabB", + 10 + ], + [ + "BBcB", + "acb", + 5 + ], + [ + "BBcB", + "bAcAAbac", + 12 + ], + [ + "BBcBACa", + "CBACA", + 6 + ], + [ + "BBcBCB", + "c", + 10 + ], + [ + "BBcBbCBA", + "cBaaAca", + 12 + ], + [ + "BBcBbcA", + "bBbca", + 6 + ], + [ + "BBcC", + "AcAcAcCc", + 12 + ], + [ + "BBcCBCac", + "ababc", + 12 + ], + [ + "BBcCC", + "ccB", + 7 + ], + [ + "BBcCCabAA", + "BCC", + 12 + ], + [ + "BBcCCcab", + "bCcc", + 10 + ], + [ + "BBcCa", + "BBAA", + 5 + ], + [ + "BBcCcb", + "BCb", + 6 + ], + [ + "BBca", + "b", + 7 + ], + [ + "BBcaA", + "AaCCAbCCC", + 15 + ], + [ + "BBcaA", + "b", + 9 + ], + [ + "BBcaACbB", + "AbCb", + 11 + ], + [ + "BBcaC", + "CCccCCCC", + 12 + ], + [ + "BBcaa", + "aBaa", + 4 + ], + [ + "BBcaa", + "bAAAC", + 8 + ], + [ + "BBcaa", + "ccCCcABA", + 12 + ], + [ + "BBcaaCBB", + "BbCcbC", + 10 + ], + [ + "BBcab", + "aaBc", + 8 + ], + [ + "BBcbA", + "BcBabbA", + 6 + ], + [ + "BBcbaA", + "caBbCcaa", + 9 + ], + [ + "BBcbbaB", + "ccCbbAccB", + 10 + ], + [ + "BBcbbabC", + "BB", + 12 + ], + [ + "BBcc", + "AcbAAB", + 11 + ], + [ + "BBcc", + "CCcBccBbC", + 12 + ], + [ + "BBccAb", + "aCACbAC", + 11 + ], + [ + "BBccB", + "BcB", + 4 + ], + [ + "BBccc", + "BBAbCAcbB", + 11 + ], + [ + "BBcccCAAB", + "ABABBAca", + 14 + ], + [ + "BC", + "A", + 4 + ], + [ + "BC", + "AAcba", + 9 + ], + [ + "BC", + "AC", + 2 + ], + [ + "BC", + "ACBACCA", + 10 + ], + [ + "BC", + "AaA", + 6 + ], + [ + "BC", + "AaCABABAb", + 16 + ], + [ + "BC", + "Ac", + 3 + ], + [ + "BC", + "AcCCcc", + 10 + ], + [ + "BC", + "B", + 2 + ], + [ + "BC", + "BAbC", + 4 + ], + [ + "BC", + "BC", + 0 + ], + [ + "BC", + "BCCCC", + 6 + ], + [ + "BC", + "BbAaaBAbb", + 16 + ], + [ + "BC", + "BbB", + 4 + ], + [ + "BC", + "BcB", + 3 + ], + [ + "BC", + "C", + 2 + ], + [ + "BC", + "CA", + 4 + ], + [ + "BC", + "CaaaCAcaC", + 16 + ], + [ + "BC", + "CbCC", + 5 + ], + [ + "BC", + "CcC", + 4 + ], + [ + "BC", + "CcCCCab", + 12 + ], + [ + "BC", + "CcaAbbbcb", + 16 + ], + [ + "BC", + "CccAb", + 9 + ], + [ + "BC", + "aAA", + 6 + ], + [ + "BC", + "aAbbcbac", + 14 + ], + [ + "BC", + "aCbAaAbBB", + 16 + ], + [ + "BC", + "abbba", + 9 + ], + [ + "BC", + "acAACAC", + 12 + ], + [ + "BC", + "acbCA", + 7 + ], + [ + "BC", + "acccbAAB", + 15 + ], + [ + "BC", + "bAABa", + 8 + ], + [ + "BC", + "bBBAcBbCC", + 14 + ], + [ + "BC", + "bC", + 1 + ], + [ + "BC", + "bCaCaCaa", + 13 + ], + [ + "BC", + "bCbaCa", + 9 + ], + [ + "BC", + "bCbaaaAAC", + 15 + ], + [ + "BC", + "babCAbca", + 13 + ], + [ + "BC", + "babbBBaB", + 14 + ], + [ + "BC", + "bbCA", + 5 + ], + [ + "BC", + "c", + 3 + ], + [ + "BC", + "cAAB", + 8 + ], + [ + "BC", + "cABCBCAB", + 12 + ], + [ + "BC", + "cBaACA", + 8 + ], + [ + "BC", + "cBbaAcB", + 11 + ], + [ + "BC", + "cCCBb", + 8 + ], + [ + "BC", + "cb", + 4 + ], + [ + "BC", + "cbBBB", + 8 + ], + [ + "BC", + "cba", + 5 + ], + [ + "BC", + "cbaB", + 7 + ], + [ + "BC", + "cbaCbBAC", + 12 + ], + [ + "BC", + "cbbCabA", + 11 + ], + [ + "BC", + "ccAACb", + 10 + ], + [ + "BC", + "ccccbb", + 11 + ], + [ + "BCA", + "A", + 4 + ], + [ + "BCA", + "AbcCbCaA", + 11 + ], + [ + "BCA", + "abccbBCC", + 12 + ], + [ + "BCAABABCB", + "abBbBaB", + 11 + ], + [ + "BCAABc", + "bBACcBa", + 9 + ], + [ + "BCAAbBc", + "AaABC", + 8 + ], + [ + "BCAAbbbc", + "A", + 14 + ], + [ + "BCAAcbb", + "CAaCAcBbC", + 9 + ], + [ + "BCAAccAa", + "Aaaa", + 10 + ], + [ + "BCABBA", + "AbBba", + 8 + ], + [ + "BCABBBAa", + "bCAbBA", + 6 + ], + [ + "BCABa", + "b", + 9 + ], + [ + "BCABcB", + "bcAAbCb", + 7 + ], + [ + "BCABcCCc", + "CACcbBAc", + 10 + ], + [ + "BCABccC", + "B", + 12 + ], + [ + "BCABccaB", + "cAaCbBCc", + 13 + ], + [ + "BCAC", + "CAcCB", + 6 + ], + [ + "BCACBaACB", + "aaCaC", + 11 + ], + [ + "BCACBaba", + "BbAaAbc", + 9 + ], + [ + "BCACBb", + "aCbCA", + 8 + ], + [ + "BCACb", + "CAABCa", + 8 + ], + [ + "BCACcABBB", + "C", + 16 + ], + [ + "BCACcCAC", + "aBcAaaB", + 12 + ], + [ + "BCACcb", + "AC", + 8 + ], + [ + "BCAa", + "A", + 6 + ], + [ + "BCAa", + "Cc", + 6 + ], + [ + "BCAacBCB", + "CaCC", + 9 + ], + [ + "BCAbBAbAc", + "aBcbB", + 13 + ], + [ + "BCAbBaCB", + "caCba", + 11 + ], + [ + "BCAbCB", + "ccBcBCC", + 10 + ], + [ + "BCAbaCC", + "aB", + 12 + ], + [ + "BCAbbAcBA", + "A", + 16 + ], + [ + "BCAbbBA", + "BBAAAbac", + 10 + ], + [ + "BCAbc", + "Cb", + 6 + ], + [ + "BCAbcaBCC", + "ccBacBbA", + 13 + ], + [ + "BCAbccbb", + "bAcaCCbAB", + 12 + ], + [ + "BCAc", + "BaBb", + 6 + ], + [ + "BCAcC", + "Ccc", + 5 + ], + [ + "BCAcCabcc", + "CBC", + 14 + ], + [ + "BCAcabbBc", + "bcbC", + 12 + ], + [ + "BCB", + "caa", + 6 + ], + [ + "BCB", + "ccB", + 3 + ], + [ + "BCBA", + "B", + 6 + ], + [ + "BCBA", + "bAB", + 5 + ], + [ + "BCBAACcc", + "aBB", + 14 + ], + [ + "BCBACbAbA", + "ccABAa", + 11 + ], + [ + "BCBACbCb", + "cBA", + 11 + ], + [ + "BCBAaBcA", + "baC", + 12 + ], + [ + "BCBAabB", + "Bcb", + 9 + ], + [ + "BCBAb", + "CccbcCcBb", + 13 + ], + [ + "BCBAcbbC", + "AccaABA", + 13 + ], + [ + "BCBB", + "A", + 8 + ], + [ + "BCBBA", + "ACAC", + 8 + ], + [ + "BCBBACa", + "AbbAA", + 9 + ], + [ + "BCBBbCbC", + "Ccb", + 11 + ], + [ + "BCBBbab", + "cCAccAbb", + 11 + ], + [ + "BCBBcCa", + "BCCCBbA", + 9 + ], + [ + "BCBBcaCBA", + "CbBCCBacA", + 10 + ], + [ + "BCBBccB", + "B", + 12 + ], + [ + "BCBC", + "CbaBa", + 7 + ], + [ + "BCBC", + "cabAcCaCc", + 13 + ], + [ + "BCBCAaB", + "CAcb", + 9 + ], + [ + "BCBCBaAA", + "b", + 15 + ], + [ + "BCBCBbbc", + "CBBA", + 10 + ], + [ + "BCBCbbBC", + "CACcaACaB", + 14 + ], + [ + "BCBCc", + "aBAA", + 8 + ], + [ + "BCBaACb", + "aCB", + 9 + ], + [ + "BCBaAcb", + "AaC", + 11 + ], + [ + "BCBaC", + "BC", + 6 + ], + [ + "BCBaCCabc", + "aAcbC", + 12 + ], + [ + "BCBb", + "AaaBabaAa", + 14 + ], + [ + "BCBbAABB", + "A", + 14 + ], + [ + "BCBbBCaA", + "B", + 14 + ], + [ + "BCBbc", + "AbBba", + 6 + ], + [ + "BCBbcbBC", + "ca", + 14 + ], + [ + "BCBcB", + "bbC", + 7 + ], + [ + "BCBcaACa", + "bbCaabC", + 10 + ], + [ + "BCBcabB", + "aaBaaaAac", + 14 + ], + [ + "BCC", + "AabbC", + 7 + ], + [ + "BCC", + "AbbBBa", + 10 + ], + [ + "BCC", + "CCBcABb", + 11 + ], + [ + "BCCA", + "bcbbc", + 8 + ], + [ + "BCCA", + "cBBbacAc", + 11 + ], + [ + "BCCAAbbC", + "ccCB", + 12 + ], + [ + "BCCABc", + "c", + 10 + ], + [ + "BCCAaba", + "CAbB", + 8 + ], + [ + "BCCB", + "cBca", + 7 + ], + [ + "BCCBCC", + "ACCBaa", + 6 + ], + [ + "BCCBbcAAA", + "aaaCCB", + 16 + ], + [ + "BCCCAbA", + "CabCcbc", + 10 + ], + [ + "BCCCCAB", + "BC", + 10 + ], + [ + "BCCCCBAB", + "cABCcc", + 13 + ], + [ + "BCCCb", + "cCCAc", + 6 + ], + [ + "BCCaAcbB", + "Cabccaa", + 12 + ], + [ + "BCCaB", + "cAaAcAa", + 12 + ], + [ + "BCCaBABCc", + "a", + 16 + ], + [ + "BCCaCBCAA", + "cBbCaccaA", + 9 + ], + [ + "BCCac", + "CbC", + 7 + ], + [ + "BCCbAc", + "AcCAaBcCc", + 12 + ], + [ + "BCCbCbaB", + "caa", + 13 + ], + [ + "BCCbaBBbC", + "baBAB", + 11 + ], + [ + "BCCbb", + "aAAcc", + 10 + ], + [ + "BCCbbb", + "BCaB", + 7 + ], + [ + "BCCbc", + "BBaAbbBCC", + 13 + ], + [ + "BCCbc", + "caBaAbbC", + 11 + ], + [ + "BCCcaAccC", + "bacbC", + 11 + ], + [ + "BCCcbAcBa", + "CA", + 14 + ], + [ + "BCCcbaBc", + "CcCcCAba", + 9 + ], + [ + "BCCccbaC", + "C", + 14 + ], + [ + "BCa", + "CBbbAac", + 10 + ], + [ + "BCa", + "aAbCCb", + 9 + ], + [ + "BCa", + "bbcAB", + 7 + ], + [ + "BCa", + "c", + 5 + ], + [ + "BCaABBaAB", + "acCcBAC", + 13 + ], + [ + "BCaAa", + "AC", + 8 + ], + [ + "BCaAaCac", + "aaAB", + 11 + ], + [ + "BCaAba", + "a", + 10 + ], + [ + "BCaAcAbC", + "B", + 14 + ], + [ + "BCaBAC", + "bCAcCAaB", + 10 + ], + [ + "BCaBcCB", + "BaAA", + 10 + ], + [ + "BCaCAc", + "CBAaBc", + 8 + ], + [ + "BCaCCCcc", + "ccbaBCaC", + 12 + ], + [ + "BCaCbbbc", + "aaAAa", + 14 + ], + [ + "BCaa", + "bc", + 6 + ], + [ + "BCaaAbaB", + "ccCA", + 13 + ], + [ + "BCaaab", + "cAbBBa", + 11 + ], + [ + "BCaaabC", + "Bbbcb", + 10 + ], + [ + "BCaabcbA", + "BabcCAb", + 8 + ], + [ + "BCabaB", + "BaBAbcB", + 7 + ], + [ + "BCabacC", + "a", + 12 + ], + [ + "BCabbbABB", + "BBCcABb", + 10 + ], + [ + "BCabbcCCC", + "ABcCAcbc", + 13 + ], + [ + "BCabcaAAA", + "Baa", + 12 + ], + [ + "BCacBBAAB", + "aBC", + 14 + ], + [ + "BCacBBbB", + "CBCB", + 10 + ], + [ + "BCacCB", + "bB", + 9 + ], + [ + "BCacCba", + "BaBbaAAba", + 10 + ], + [ + "BCb", + "BBa", + 4 + ], + [ + "BCb", + "bBCCa", + 6 + ], + [ + "BCb", + "bCAAAAC", + 11 + ], + [ + "BCbA", + "Bccc", + 5 + ], + [ + "BCbAAC", + "aA", + 9 + ], + [ + "BCbAcbcCB", + "cbBcba", + 11 + ], + [ + "BCbBCacaC", + "AACB", + 16 + ], + [ + "BCbBa", + "cbbBC", + 6 + ], + [ + "BCbBaa", + "aCAAbACc", + 12 + ], + [ + "BCbBca", + "bBaB", + 8 + ], + [ + "BCbCA", + "cB", + 8 + ], + [ + "BCbCccA", + "aaABcCAB", + 11 + ], + [ + "BCbaAABaa", + "AA", + 14 + ], + [ + "BCbaBAb", + "cAAAcbbaA", + 15 + ], + [ + "BCbaCbBAc", + "CBCcabcAb", + 10 + ], + [ + "BCbaaCCaa", + "cCbac", + 11 + ], + [ + "BCbaabbBC", + "abcCBbB", + 11 + ], + [ + "BCbaccCC", + "bbBc", + 11 + ], + [ + "BCbbc", + "AcCbBaACc", + 11 + ], + [ + "BCbbcbbB", + "AbbCca", + 11 + ], + [ + "BCbc", + "cbcCCcBAA", + 13 + ], + [ + "BCbcBbaC", + "babCBCACb", + 9 + ], + [ + "BCbcaACBB", + "aab", + 14 + ], + [ + "BCbccccB", + "Ca", + 14 + ], + [ + "BCc", + "AAaCBcAbc", + 13 + ], + [ + "BCc", + "AcBbBbcba", + 14 + ], + [ + "BCc", + "CAcB", + 6 + ], + [ + "BCc", + "bABBC", + 7 + ], + [ + "BCcA", + "Aac", + 6 + ], + [ + "BCcA", + "bbbCc", + 7 + ], + [ + "BCcACaB", + "CBcbcaBAA", + 11 + ], + [ + "BCcACaCca", + "cca", + 12 + ], + [ + "BCcAcb", + "bbcacbb", + 6 + ], + [ + "BCcBBAccb", + "cBB", + 12 + ], + [ + "BCcBBacB", + "B", + 14 + ], + [ + "BCcBBc", + "ca", + 10 + ], + [ + "BCcBac", + "Cb", + 9 + ], + [ + "BCcBbBAb", + "BBcBBcA", + 7 + ], + [ + "BCcBbBCc", + "A", + 16 + ], + [ + "BCcBc", + "a", + 10 + ], + [ + "BCcCB", + "CABB", + 6 + ], + [ + "BCcCC", + "baB", + 9 + ], + [ + "BCcCcaaB", + "CAaa", + 10 + ], + [ + "BCca", + "aBcCcA", + 5 + ], + [ + "BCcaAc", + "aaAAA", + 9 + ], + [ + "BCcaBbAb", + "A", + 14 + ], + [ + "BCcab", + "CA", + 7 + ], + [ + "BCcacCbcc", + "CBcabaBCC", + 11 + ], + [ + "BCcb", + "CCbBACCcc", + 12 + ], + [ + "BCcb", + "Ca", + 6 + ], + [ + "BCcb", + "bC", + 5 + ], + [ + "BCcbAc", + "B", + 10 + ], + [ + "BCcbAcBAA", + "AacbBc", + 12 + ], + [ + "BCcbBac", + "Aaa", + 12 + ], + [ + "BCcbaC", + "cB", + 9 + ], + [ + "BCcbc", + "BBa", + 7 + ], + [ + "BCccAac", + "ABB", + 12 + ], + [ + "BCccBb", + "Cc", + 8 + ], + [ + "BCccCC", + "BBA", + 10 + ], + [ + "Ba", + "AAACAbbb", + 15 + ], + [ + "Ba", + "AAaBBbBaa", + 14 + ], + [ + "Ba", + "ABBaAabBa", + 14 + ], + [ + "Ba", + "AaccaB", + 10 + ], + [ + "Ba", + "AcABCBc", + 12 + ], + [ + "Ba", + "AcB", + 6 + ], + [ + "Ba", + "AcaAcb", + 10 + ], + [ + "Ba", + "B", + 2 + ], + [ + "Ba", + "BAcBc", + 7 + ], + [ + "Ba", + "Ba", + 0 + ], + [ + "Ba", + "BcAACc", + 9 + ], + [ + "Ba", + "BcCa", + 4 + ], + [ + "Ba", + "BcbBb", + 8 + ], + [ + "Ba", + "C", + 4 + ], + [ + "Ba", + "CABbaBcb", + 12 + ], + [ + "Ba", + "CBBAAB", + 9 + ], + [ + "Ba", + "CC", + 4 + ], + [ + "Ba", + "CCCcABBC", + 14 + ], + [ + "Ba", + "CbCbCa", + 9 + ], + [ + "Ba", + "Cc", + 4 + ], + [ + "Ba", + "a", + 2 + ], + [ + "Ba", + "aA", + 3 + ], + [ + "Ba", + "aB", + 4 + ], + [ + "Ba", + "aCB", + 6 + ], + [ + "Ba", + "aCbCa", + 7 + ], + [ + "Ba", + "acBbb", + 8 + ], + [ + "Ba", + "b", + 3 + ], + [ + "Ba", + "bcBCAbcCB", + 15 + ], + [ + "Ba", + "c", + 4 + ], + [ + "Ba", + "cAaAABbCB", + 16 + ], + [ + "Ba", + "cBBa", + 4 + ], + [ + "Ba", + "cbBaCAb", + 10 + ], + [ + "Ba", + "ccBcACA", + 11 + ], + [ + "Ba", + "ccC", + 6 + ], + [ + "BaA", + "CACaA", + 6 + ], + [ + "BaA", + "CBAbbaca", + 11 + ], + [ + "BaA", + "aaBB", + 6 + ], + [ + "BaA", + "bA", + 3 + ], + [ + "BaA", + "baACAbcA", + 11 + ], + [ + "BaA", + "cbc", + 6 + ], + [ + "BaA", + "ccBAAB", + 7 + ], + [ + "BaAAA", + "BB", + 8 + ], + [ + "BaAAa", + "ca", + 8 + ], + [ + "BaAAaCBaA", + "ACCC", + 14 + ], + [ + "BaAAcCA", + "abCCBA", + 9 + ], + [ + "BaAAcc", + "aaAAbac", + 6 + ], + [ + "BaAAccaCA", + "CcABcBCc", + 12 + ], + [ + "BaAB", + "BBAB", + 2 + ], + [ + "BaAB", + "ac", + 6 + ], + [ + "BaAB", + "bBB", + 5 + ], + [ + "BaABAACBA", + "bAb", + 14 + ], + [ + "BaABCaACb", + "bCbabAaBb", + 13 + ], + [ + "BaABa", + "abC", + 7 + ], + [ + "BaABcbab", + "cAaca", + 10 + ], + [ + "BaACAcCc", + "BC", + 12 + ], + [ + "BaACB", + "bcAa", + 7 + ], + [ + "BaACBAbAC", + "Bac", + 13 + ], + [ + "BaACaCbC", + "AACACCC", + 6 + ], + [ + "BaAa", + "BBCC", + 6 + ], + [ + "BaAa", + "BcbccbCC", + 14 + ], + [ + "BaAa", + "aAbB", + 6 + ], + [ + "BaAaAaAB", + "Ccab", + 13 + ], + [ + "BaAaaAAb", + "ABBCBAcB", + 13 + ], + [ + "BaAacA", + "bACCCb", + 9 + ], + [ + "BaAb", + "AbCBcCba", + 12 + ], + [ + "BaAbCBa", + "CCCcAb", + 13 + ], + [ + "BaAbab", + "aAbaBCc", + 7 + ], + [ + "BaAbcCCB", + "CBcA", + 13 + ], + [ + "BaAcAB", + "BabCCCaC", + 10 + ], + [ + "BaAcAa", + "abaCCCBAA", + 11 + ], + [ + "BaAcAbbC", + "ABaBBAC", + 10 + ], + [ + "BaAcB", + "CBAACbCa", + 9 + ], + [ + "BaAcCbb", + "Abb", + 8 + ], + [ + "BaAcCbca", + "BcCbCAAcB", + 12 + ], + [ + "BaAcbcb", + "c", + 12 + ], + [ + "BaB", + "CACAb", + 8 + ], + [ + "BaB", + "aCACaB", + 8 + ], + [ + "BaB", + "bc", + 5 + ], + [ + "BaB", + "cBBB", + 4 + ], + [ + "BaB", + "cCacaaB", + 10 + ], + [ + "BaBA", + "cABACbbA", + 10 + ], + [ + "BaBAA", + "bbAacbA", + 9 + ], + [ + "BaBAB", + "acAaABBA", + 11 + ], + [ + "BaBABabC", + "C", + 14 + ], + [ + "BaBACA", + "ACAcbbccb", + 15 + ], + [ + "BaBAc", + "BaBABaaC", + 7 + ], + [ + "BaBB", + "aBBCaA", + 8 + ], + [ + "BaBBaABA", + "AaabaABb", + 7 + ], + [ + "BaBBb", + "BC", + 8 + ], + [ + "BaBBc", + "C", + 9 + ], + [ + "BaBBcAaAc", + "C", + 17 + ], + [ + "BaBBcbcb", + "bbabb", + 10 + ], + [ + "BaBCAbab", + "A", + 14 + ], + [ + "BaBCBC", + "BA", + 9 + ], + [ + "BaBCaCac", + "cACc", + 10 + ], + [ + "BaBCc", + "cBcCbAab", + 13 + ], + [ + "BaBa", + "bBBABAabb", + 11 + ], + [ + "BaBa", + "bCAbaAbCb", + 13 + ], + [ + "BaBaBB", + "C", + 12 + ], + [ + "BaBb", + "CC", + 8 + ], + [ + "BaBb", + "bbAbCcbBc", + 13 + ], + [ + "BaBbABA", + "ababcCAc", + 11 + ], + [ + "BaBbBb", + "cCAAb", + 10 + ], + [ + "BaBbC", + "BBACac", + 8 + ], + [ + "BaBbabbCb", + "b", + 16 + ], + [ + "BaBc", + "CB", + 6 + ], + [ + "BaBcACac", + "AbCaBA", + 11 + ], + [ + "BaBcB", + "AaabaaBAa", + 13 + ], + [ + "BaBcBbbAa", + "BCACcab", + 13 + ], + [ + "BaBcCA", + "CbCACA", + 8 + ], + [ + "BaBcCA", + "bCa", + 8 + ], + [ + "BaBcaCCbB", + "BCC", + 12 + ], + [ + "BaBcacC", + "ACB", + 12 + ], + [ + "BaC", + "ABAbc", + 6 + ], + [ + "BaC", + "ABbb", + 6 + ], + [ + "BaC", + "ACa", + 5 + ], + [ + "BaC", + "Aaba", + 6 + ], + [ + "BaC", + "BACccaaBA", + 13 + ], + [ + "BaC", + "BC", + 2 + ], + [ + "BaC", + "Bccb", + 5 + ], + [ + "BaC", + "a", + 4 + ], + [ + "BaC", + "bCbbBcB", + 12 + ], + [ + "BaC", + "baBcbC", + 7 + ], + [ + "BaC", + "cBAabACAa", + 12 + ], + [ + "BaC", + "cBaBAaCc", + 10 + ], + [ + "BaCAA", + "cbbcBBbb", + 14 + ], + [ + "BaCAAbbB", + "BAcB", + 10 + ], + [ + "BaCAB", + "cC", + 8 + ], + [ + "BaCABCB", + "bB", + 11 + ], + [ + "BaCABccaB", + "CAacBCCA", + 12 + ], + [ + "BaCAaAc", + "bCa", + 9 + ], + [ + "BaCB", + "B", + 6 + ], + [ + "BaCBa", + "BbAbaBC", + 9 + ], + [ + "BaCBaCcCC", + "aCCAc", + 11 + ], + [ + "BaCBbCB", + "acB", + 9 + ], + [ + "BaCBc", + "ABCB", + 6 + ], + [ + "BaCBcBC", + "cc", + 11 + ], + [ + "BaCCAcAb", + "Bab", + 10 + ], + [ + "BaCCC", + "CcBCabAAa", + 14 + ], + [ + "BaCCCAcb", + "bbcAaA", + 12 + ], + [ + "BaCCCbb", + "bCCaaCb", + 9 + ], + [ + "BaCCbAA", + "acCBcBB", + 10 + ], + [ + "BaCCc", + "Ca", + 8 + ], + [ + "BaCCcCcc", + "baAbcaCc", + 8 + ], + [ + "BaCa", + "bbAba", + 6 + ], + [ + "BaCaAAaca", + "CCbCb", + 15 + ], + [ + "BaCaCCABa", + "bCaC", + 11 + ], + [ + "BaCb", + "bbaA", + 7 + ], + [ + "BaCb", + "cABC", + 7 + ], + [ + "BaCbAB", + "BAcbaAC", + 6 + ], + [ + "BaCbBA", + "bcAacBaa", + 10 + ], + [ + "BaCbBCaa", + "acBCAAcA", + 11 + ], + [ + "BaCbBcbB", + "bc", + 12 + ], + [ + "BaCbbbBaa", + "CcCAcCCC", + 16 + ], + [ + "BaCc", + "Bc", + 4 + ], + [ + "BaCc", + "CCBcbAAA", + 13 + ], + [ + "BaCcBbAb", + "BcaAaBa", + 11 + ], + [ + "BaCcCcab", + "abBAcBA", + 12 + ], + [ + "BaCcCcbAc", + "BacCAaaaA", + 11 + ], + [ + "BaCcaAabB", + "BBc", + 14 + ], + [ + "BaCcaBAa", + "AaCb", + 11 + ], + [ + "BaCccaaaA", + "cBacBCA", + 12 + ], + [ + "Baa", + "A", + 5 + ], + [ + "Baa", + "AcccabBa", + 12 + ], + [ + "Baa", + "BaaBCABcB", + 12 + ], + [ + "Baa", + "CAAa", + 5 + ], + [ + "Baa", + "CBBcBcC", + 12 + ], + [ + "Baa", + "CCABBacBb", + 14 + ], + [ + "Baa", + "CbABbAcbb", + 15 + ], + [ + "Baa", + "CbCaAbc", + 10 + ], + [ + "Baa", + "bBaBbabAc", + 12 + ], + [ + "Baa", + "bBccbbaAC", + 13 + ], + [ + "Baa", + "bcAbaAACA", + 14 + ], + [ + "BaaA", + "CaBBc", + 8 + ], + [ + "BaaA", + "baBabC", + 7 + ], + [ + "BaaABa", + "bCbcaa", + 9 + ], + [ + "BaaACcAAc", + "bccAaa", + 11 + ], + [ + "BaaAaCc", + "cB", + 13 + ], + [ + "BaaB", + "C", + 8 + ], + [ + "BaaB", + "cAa", + 5 + ], + [ + "BaaBAbAb", + "baccCC", + 13 + ], + [ + "BaaBBcA", + "ccC", + 12 + ], + [ + "BaaC", + "bCb", + 7 + ], + [ + "BaaC", + "cacAcCBcC", + 13 + ], + [ + "BaaCACAaB", + "cAaCabab", + 9 + ], + [ + "BaaCBCBBc", + "ac", + 14 + ], + [ + "BaaCbAC", + "AbCAbca", + 11 + ], + [ + "BaaCcCa", + "cBACb", + 11 + ], + [ + "BaaaBcCab", + "BaBbbbAaC", + 11 + ], + [ + "BaaaC", + "accb", + 8 + ], + [ + "Baaaa", + "B", + 8 + ], + [ + "Baaaa", + "CaA", + 7 + ], + [ + "BaaababB", + "bCAcBCB", + 11 + ], + [ + "BaaacbA", + "aBCba", + 8 + ], + [ + "Baab", + "cCAAcBa", + 11 + ], + [ + "Baab", + "ccaAACcAC", + 15 + ], + [ + "BaabA", + "b", + 8 + ], + [ + "BaabAA", + "A", + 10 + ], + [ + "BaabBAcC", + "aaBCaab", + 10 + ], + [ + "BaabBcc", + "cCCAaCbc", + 12 + ], + [ + "BaabCBCC", + "CcCaB", + 14 + ], + [ + "BaabCa", + "cAaAB", + 9 + ], + [ + "BaabCb", + "CA", + 10 + ], + [ + "Baabacaa", + "bAcbccB", + 10 + ], + [ + "Baabb", + "bBbAbbA", + 7 + ], + [ + "BaabbAaA", + "BBA", + 11 + ], + [ + "BaabcAb", + "aBaC", + 10 + ], + [ + "Baac", + "AaCacBCA", + 10 + ], + [ + "Baac", + "CcBaBAb", + 9 + ], + [ + "Baac", + "a", + 6 + ], + [ + "Baac", + "aaAB", + 5 + ], + [ + "Baac", + "cCcBC", + 9 + ], + [ + "Baac", + "ccCb", + 8 + ], + [ + "BaacCABbA", + "cA", + 14 + ], + [ + "Bab", + "BaCABaCa", + 11 + ], + [ + "Bab", + "C", + 6 + ], + [ + "Bab", + "a", + 4 + ], + [ + "Bab", + "acabAacac", + 14 + ], + [ + "Bab", + "bbCaccBaB", + 13 + ], + [ + "BabA", + "a", + 6 + ], + [ + "BabA", + "cbAc", + 6 + ], + [ + "BabAAccaB", + "abb", + 13 + ], + [ + "BabACA", + "b", + 10 + ], + [ + "BabAaC", + "baCCBcC", + 9 + ], + [ + "BabAb", + "Aa", + 8 + ], + [ + "BabAbACA", + "CBAaCaaa", + 12 + ], + [ + "BabAbCA", + "CccAab", + 12 + ], + [ + "BabBBAaCa", + "AcCAccAB", + 15 + ], + [ + "BabBa", + "ABbACcCcb", + 15 + ], + [ + "BabBcA", + "ACC", + 10 + ], + [ + "BabBcBcA", + "acbCABcB", + 10 + ], + [ + "BabC", + "aABb", + 6 + ], + [ + "BabCaBca", + "AA", + 14 + ], + [ + "Baba", + "BaBCbCBB", + 10 + ], + [ + "BabaBaAA", + "AaB", + 11 + ], + [ + "BabacAbAb", + "cCaA", + 14 + ], + [ + "Babb", + "CcAa", + 8 + ], + [ + "Babb", + "aaACcbc", + 10 + ], + [ + "BabbA", + "CaCb", + 6 + ], + [ + "BabbAaaBA", + "BbCcACC", + 13 + ], + [ + "BabbAccb", + "cacBB", + 11 + ], + [ + "BabbBcBA", + "cAabCbba", + 11 + ], + [ + "BabbCBb", + "aCCcBcB", + 10 + ], + [ + "BabbCCbCA", + "AbAcBa", + 12 + ], + [ + "BabbcBAAa", + "aCacA", + 12 + ], + [ + "BabcBAbcc", + "CabA", + 12 + ], + [ + "BabcCbCA", + "cbC", + 10 + ], + [ + "BabcaC", + "ABB", + 10 + ], + [ + "Babcac", + "bcccAbBC", + 11 + ], + [ + "Bac", + "BBcaBcC", + 8 + ], + [ + "Bac", + "CbcAbAbB", + 14 + ], + [ + "Bac", + "bacBAccb", + 11 + ], + [ + "Bac", + "bbACab", + 9 + ], + [ + "BacAa", + "bBCb", + 8 + ], + [ + "BacAaaCaB", + "ccaaAb", + 10 + ], + [ + "BacAb", + "aBBaBa", + 9 + ], + [ + "BacAbA", + "caAaBbCAb", + 11 + ], + [ + "BacBBa", + "AbBBAAA", + 10 + ], + [ + "BacBCAB", + "cCBaBCc", + 10 + ], + [ + "BacBab", + "CbBABcB", + 10 + ], + [ + "BacCAaBAB", + "ccAb", + 12 + ], + [ + "BacCBBCc", + "B", + 14 + ], + [ + "BacCCaA", + "bcaBA", + 9 + ], + [ + "BacCCcbB", + "cCbaaCBB", + 12 + ], + [ + "BacCa", + "cCBBB", + 10 + ], + [ + "BacCab", + "bA", + 10 + ], + [ + "BacCabcAc", + "CCbBC", + 12 + ], + [ + "BacCccBa", + "ABCBA", + 10 + ], + [ + "BacaB", + "bbac", + 7 + ], + [ + "BacaC", + "AAC", + 6 + ], + [ + "BacaCaBB", + "bB", + 13 + ], + [ + "BacaCbA", + "caBCAa", + 9 + ], + [ + "BacbB", + "CcaACc", + 10 + ], + [ + "BacbC", + "AAabcbbCB", + 10 + ], + [ + "BacbCbBca", + "cCcCC", + 13 + ], + [ + "BacbbCaAa", + "aCACaC", + 11 + ], + [ + "Bacc", + "b", + 7 + ], + [ + "BaccACa", + "B", + 12 + ], + [ + "BaccAaacb", + "A", + 16 + ], + [ + "BaccBcCBB", + "acb", + 13 + ], + [ + "BaccCB", + "BaC", + 6 + ], + [ + "Bacca", + "Ac", + 7 + ], + [ + "BaccaCC", + "CcbA", + 11 + ], + [ + "Baccb", + "aCc", + 5 + ], + [ + "Baccba", + "cbaaBcbc", + 9 + ], + [ + "Bb", + "AAABBB", + 9 + ], + [ + "Bb", + "AAccaCAaa", + 18 + ], + [ + "Bb", + "AaA", + 6 + ], + [ + "Bb", + "AaAAc", + 10 + ], + [ + "Bb", + "B", + 2 + ], + [ + "Bb", + "BACBbABA", + 12 + ], + [ + "Bb", + "BBaBb", + 6 + ], + [ + "Bb", + "BacCBA", + 9 + ], + [ + "Bb", + "BbCBCABAC", + 14 + ], + [ + "Bb", + "Bba", + 2 + ], + [ + "Bb", + "BcBAACA", + 11 + ], + [ + "Bb", + "CC", + 4 + ], + [ + "Bb", + "CcaaBa", + 10 + ], + [ + "Bb", + "a", + 4 + ], + [ + "Bb", + "aAbbb", + 7 + ], + [ + "Bb", + "aBa", + 4 + ], + [ + "Bb", + "aBbcCbAAC", + 14 + ], + [ + "Bb", + "aCCAB", + 9 + ], + [ + "Bb", + "aCccacCB", + 15 + ], + [ + "Bb", + "aacA", + 8 + ], + [ + "Bb", + "abBaBbAa", + 12 + ], + [ + "Bb", + "b", + 2 + ], + [ + "Bb", + "bAcCbc", + 9 + ], + [ + "Bb", + "bBaCBc", + 9 + ], + [ + "Bb", + "bBbBCccc", + 12 + ], + [ + "Bb", + "bCCc", + 7 + ], + [ + "Bb", + "bCaBcC", + 10 + ], + [ + "Bb", + "bCcbbc", + 9 + ], + [ + "Bb", + "baB", + 4 + ], + [ + "Bb", + "bc", + 3 + ], + [ + "Bb", + "bcCaacCC", + 15 + ], + [ + "Bb", + "bcaBccAcb", + 14 + ], + [ + "Bb", + "cab", + 4 + ], + [ + "Bb", + "cbAACCBb", + 12 + ], + [ + "Bb", + "cbabCcbb", + 13 + ], + [ + "Bb", + "cc", + 4 + ], + [ + "BbA", + "BCaAcABab", + 14 + ], + [ + "BbA", + "C", + 6 + ], + [ + "BbA", + "aacCbAc", + 10 + ], + [ + "BbA", + "cbCcbaAAA", + 13 + ], + [ + "BbAAaA", + "AbbBccC", + 11 + ], + [ + "BbAAaA", + "bBc", + 10 + ], + [ + "BbABB", + "BBAACAa", + 9 + ], + [ + "BbABBbAa", + "acaCCbbcB", + 14 + ], + [ + "BbABC", + "c", + 9 + ], + [ + "BbABa", + "accBbBcBb", + 12 + ], + [ + "BbABaacaA", + "b", + 16 + ], + [ + "BbAC", + "CaCCBcAa", + 12 + ], + [ + "BbAC", + "caAcaab", + 11 + ], + [ + "BbACAbcB", + "aaaAB", + 11 + ], + [ + "BbAa", + "ACcA", + 7 + ], + [ + "BbAa", + "cCA", + 6 + ], + [ + "BbAaAaaa", + "cBbbcCB", + 14 + ], + [ + "BbAaCab", + "cA", + 12 + ], + [ + "BbAaa", + "Bbbcacaa", + 7 + ], + [ + "BbAac", + "b", + 8 + ], + [ + "BbAb", + "CccCbabaC", + 13 + ], + [ + "BbAbC", + "CABA", + 7 + ], + [ + "BbAbC", + "ccA", + 8 + ], + [ + "BbAbCab", + "bcabABC", + 10 + ], + [ + "BbAbCcBab", + "CAaAbcbAC", + 12 + ], + [ + "BbAbCcaAC", + "ABbcCa", + 11 + ], + [ + "BbAbbcbb", + "B", + 14 + ], + [ + "BbAbc", + "aABB", + 7 + ], + [ + "BbAcaaaa", + "bBccaac", + 8 + ], + [ + "BbAcbca", + "acCCcbBCB", + 13 + ], + [ + "BbB", + "ACbcc", + 8 + ], + [ + "BbB", + "Acaca", + 10 + ], + [ + "BbB", + "bBcb", + 5 + ], + [ + "BbBA", + "CA", + 6 + ], + [ + "BbBABCcA", + "aaC", + 13 + ], + [ + "BbBAaBa", + "b", + 12 + ], + [ + "BbBAb", + "aaCCcb", + 10 + ], + [ + "BbBAbABAc", + "cBbbBCcB", + 12 + ], + [ + "BbBBCbca", + "cbBcCbC", + 7 + ], + [ + "BbBBa", + "cC", + 10 + ], + [ + "BbBBaBBba", + "bAC", + 15 + ], + [ + "BbBBabAb", + "cbB", + 12 + ], + [ + "BbBC", + "cbbbcaA", + 9 + ], + [ + "BbBCAbb", + "baCCbBbb", + 9 + ], + [ + "BbBCbAb", + "BbCcBbbac", + 9 + ], + [ + "BbBa", + "aCaaaA", + 10 + ], + [ + "BbBa", + "caCcb", + 10 + ], + [ + "BbBaC", + "BA", + 7 + ], + [ + "BbBaCbCcB", + "baAbCbaCc", + 11 + ], + [ + "BbBaaCBbc", + "bcAAAbC", + 11 + ], + [ + "BbBacBCca", + "c", + 16 + ], + [ + "BbBbA", + "BbaaA", + 4 + ], + [ + "BbBbBAb", + "CbcCbAaCc", + 13 + ], + [ + "BbBbBa", + "A", + 11 + ], + [ + "BbBbBbC", + "cBbacbBa", + 10 + ], + [ + "BbBbbbBAb", + "AabCcBa", + 13 + ], + [ + "BbBcBc", + "bcaaC", + 9 + ], + [ + "BbBcaAC", + "cB", + 12 + ], + [ + "BbBcabAa", + "BaBAaACc", + 10 + ], + [ + "BbBcbbC", + "bCBAabbaa", + 11 + ], + [ + "BbC", + "A", + 6 + ], + [ + "BbC", + "Cc", + 5 + ], + [ + "BbC", + "aAAAc", + 9 + ], + [ + "BbC", + "aAAbca", + 9 + ], + [ + "BbC", + "bAaBabC", + 8 + ], + [ + "BbC", + "bBCcCa", + 8 + ], + [ + "BbC", + "baCACBbc", + 11 + ], + [ + "BbC", + "bcBccBcBa", + 14 + ], + [ + "BbCAACcac", + "AcCbB", + 14 + ], + [ + "BbCAACcb", + "b", + 14 + ], + [ + "BbCAaCaC", + "BBc", + 12 + ], + [ + "BbCAbc", + "aaa", + 11 + ], + [ + "BbCAcCaCC", + "CCcB", + 13 + ], + [ + "BbCAcaab", + "BCbACaABc", + 9 + ], + [ + "BbCBCB", + "bAAccbCc", + 11 + ], + [ + "BbCBCbAc", + "CcACA", + 11 + ], + [ + "BbCCAAab", + "ccCCC", + 12 + ], + [ + "BbCCABABa", + "Acaab", + 14 + ], + [ + "BbCCBBb", + "aacCb", + 9 + ], + [ + "BbCCCBCC", + "BCA", + 12 + ], + [ + "BbCCa", + "aaaCAc", + 9 + ], + [ + "BbCCcCcCc", + "AaAAA", + 18 + ], + [ + "BbCCcbc", + "BaaBCc", + 9 + ], + [ + "BbCa", + "aCC", + 6 + ], + [ + "BbCaB", + "A", + 9 + ], + [ + "BbCaBAA", + "AbAABc", + 9 + ], + [ + "BbCaCbACB", + "AAA", + 15 + ], + [ + "BbCaCcC", + "aAAcA", + 11 + ], + [ + "BbCaCcaC", + "A", + 15 + ], + [ + "BbCbC", + "bAACAAcAC", + 13 + ], + [ + "BbCbCCb", + "AbBAA", + 11 + ], + [ + "BbCbaAB", + "C", + 12 + ], + [ + "BbCbaB", + "aAcC", + 11 + ], + [ + "BbCbaBB", + "Cabb", + 8 + ], + [ + "BbCbbBc", + "b", + 12 + ], + [ + "BbCbbb", + "cCcAcBAcA", + 16 + ], + [ + "BbCbcc", + "AAACbaaAa", + 14 + ], + [ + "BbCc", + "A", + 8 + ], + [ + "BbCc", + "aac", + 6 + ], + [ + "BbCcCACB", + "cB", + 12 + ], + [ + "BbCcaBBC", + "CB", + 12 + ], + [ + "BbCccBCB", + "cCcbBb", + 9 + ], + [ + "Bba", + "ABBBcACb", + 12 + ], + [ + "Bba", + "AbAcAbCcA", + 14 + ], + [ + "Bba", + "CaBCAaaaB", + 14 + ], + [ + "Bba", + "CacBBCBA", + 12 + ], + [ + "Bba", + "aBAcbCC", + 10 + ], + [ + "Bba", + "aCCcAaAc", + 14 + ], + [ + "Bba", + "abcA", + 5 + ], + [ + "Bba", + "bcCa", + 5 + ], + [ + "BbaAB", + "BbB", + 4 + ], + [ + "BbaABacc", + "bccbc", + 11 + ], + [ + "BbaAbBCAA", + "caAcCbc", + 12 + ], + [ + "BbaAcAb", + "bacACCbC", + 9 + ], + [ + "BbaB", + "aBbAaCb", + 7 + ], + [ + "BbaB", + "bBbBAbBC", + 9 + ], + [ + "BbaBCB", + "bcA", + 9 + ], + [ + "BbaBcbacc", + "CCBAC", + 14 + ], + [ + "BbaC", + "C", + 6 + ], + [ + "BbaCBC", + "C", + 10 + ], + [ + "BbaCCCb", + "Cca", + 11 + ], + [ + "BbaCa", + "AcCC", + 8 + ], + [ + "BbaCa", + "ccbA", + 9 + ], + [ + "BbaCcBba", + "ACb", + 11 + ], + [ + "BbaCcC", + "cbCCAAbcc", + 11 + ], + [ + "Bbaa", + "abbaAA", + 6 + ], + [ + "BbaaAbAA", + "Acc", + 14 + ], + [ + "BbaaBAb", + "CBbaCbaBB", + 9 + ], + [ + "BbaacA", + "ccAbCabAa", + 12 + ], + [ + "BbaacBcbC", + "cabbCaC", + 12 + ], + [ + "Bbab", + "BCBb", + 4 + ], + [ + "BbabA", + "Cbc", + 8 + ], + [ + "BbabBc", + "AcA", + 11 + ], + [ + "BbabC", + "ab", + 6 + ], + [ + "BbabCcBA", + "AabACBc", + 9 + ], + [ + "Bbaba", + "AAa", + 7 + ], + [ + "BbabbBb", + "b", + 12 + ], + [ + "Bbac", + "bbb", + 5 + ], + [ + "BbacB", + "CBcBB", + 7 + ], + [ + "BbacCc", + "Cb", + 10 + ], + [ + "Bbacaa", + "AaAaac", + 8 + ], + [ + "Bbb", + "ACCCCc", + 12 + ], + [ + "Bbb", + "BCABAa", + 9 + ], + [ + "Bbb", + "BabbbBcCA", + 12 + ], + [ + "Bbb", + "acaCCaa", + 14 + ], + [ + "Bbb", + "bAAcBa", + 10 + ], + [ + "Bbb", + "ccacA", + 10 + ], + [ + "BbbA", + "ACccA", + 8 + ], + [ + "BbbAAB", + "baBAb", + 7 + ], + [ + "BbbABccc", + "AbaABca", + 8 + ], + [ + "BbbAC", + "bcAaBBBca", + 14 + ], + [ + "BbbAaAba", + "a", + 14 + ], + [ + "BbbB", + "Bcc", + 6 + ], + [ + "BbbBCCA", + "cBBb", + 11 + ], + [ + "BbbBCba", + "BA", + 11 + ], + [ + "BbbBcaa", + "b", + 12 + ], + [ + "BbbCAaA", + "cbbBaBaCA", + 9 + ], + [ + "BbbCAb", + "AACC", + 10 + ], + [ + "BbbCAc", + "cbaB", + 9 + ], + [ + "BbbCBcac", + "bAbaBCba", + 10 + ], + [ + "BbbCCB", + "C", + 10 + ], + [ + "BbbCCBAc", + "cbbBcCCAC", + 8 + ], + [ + "Bbba", + "AAaAB", + 9 + ], + [ + "Bbba", + "abAc", + 6 + ], + [ + "BbbaAC", + "aBABcAAaB", + 12 + ], + [ + "BbbaC", + "AbBaaAaA", + 11 + ], + [ + "BbbaabCCA", + "CbcAb", + 13 + ], + [ + "BbbacCbb", + "AaccA", + 11 + ], + [ + "BbbbCAaA", + "cACCaCcAC", + 15 + ], + [ + "BbbbbAc", + "AbbcCb", + 10 + ], + [ + "BbbbcA", + "AaCcBabb", + 14 + ], + [ + "BbbbcbbA", + "bAA", + 12 + ], + [ + "Bbbc", + "CbccaAc", + 10 + ], + [ + "BbbcABb", + "bbbcc", + 7 + ], + [ + "BbbcB", + "bbacbbc", + 8 + ], + [ + "BbbcbAcbB", + "aCaCbaAc", + 13 + ], + [ + "Bbbcbcab", + "caCaAAbc", + 14 + ], + [ + "BbcA", + "CBaA", + 5 + ], + [ + "BbcA", + "CBcAB", + 5 + ], + [ + "BbcABcB", + "AcA", + 10 + ], + [ + "BbcACB", + "BaaCCBBC", + 10 + ], + [ + "BbcACCcB", + "bcbbaaBba", + 14 + ], + [ + "BbcAbCa", + "CbbaCba", + 9 + ], + [ + "BbcAcaB", + "Aca", + 8 + ], + [ + "BbcAccCab", + "abCCaA", + 11 + ], + [ + "BbcB", + "aACCccCaA", + 16 + ], + [ + "BbcBbB", + "baacAca", + 11 + ], + [ + "BbcBbbaC", + "Ab", + 14 + ], + [ + "BbcC", + "AccAcaBaC", + 14 + ], + [ + "BbcCB", + "bBca", + 6 + ], + [ + "BbcCBaB", + "AcCcaCbb", + 11 + ], + [ + "BbcCaab", + "cACaC", + 10 + ], + [ + "BbcaBbCCb", + "acBBcc", + 11 + ], + [ + "BbcaCcaa", + "AbcccaBB", + 9 + ], + [ + "BbcaaACb", + "A", + 14 + ], + [ + "BbcbACAa", + "a", + 14 + ], + [ + "BbcbBCCA", + "AcaCB", + 12 + ], + [ + "BbcbCcCCc", + "a", + 18 + ], + [ + "Bbcbaa", + "a", + 10 + ], + [ + "BbcbbbBab", + "aaBcBcCB", + 14 + ], + [ + "BbcbccB", + "bAACb", + 10 + ], + [ + "Bbcc", + "caAACBBb", + 15 + ], + [ + "BbccBcb", + "BCAbccb", + 8 + ], + [ + "BbccC", + "CaaAC", + 8 + ], + [ + "Bbcca", + "bBcb", + 6 + ], + [ + "BbccccCC", + "aAbcc", + 12 + ], + [ + "Bc", + "A", + 4 + ], + [ + "Bc", + "AB", + 4 + ], + [ + "Bc", + "ABAccAcC", + 12 + ], + [ + "Bc", + "ABBbBBbb", + 14 + ], + [ + "Bc", + "AaA", + 6 + ], + [ + "Bc", + "AaaBaCcCc", + 14 + ], + [ + "Bc", + "AabBBC", + 9 + ], + [ + "Bc", + "Aac", + 4 + ], + [ + "Bc", + "Aba", + 5 + ], + [ + "Bc", + "Abccbabbc", + 15 + ], + [ + "Bc", + "AcAbBC", + 9 + ], + [ + "Bc", + "B", + 2 + ], + [ + "Bc", + "BACaACBC", + 13 + ], + [ + "Bc", + "BBcBCbCB", + 12 + ], + [ + "Bc", + "BcBAAAaA", + 12 + ], + [ + "Bc", + "BcaaABCcc", + 14 + ], + [ + "Bc", + "C", + 3 + ], + [ + "Bc", + "CBCccCbaA", + 14 + ], + [ + "Bc", + "CCaBABaBA", + 16 + ], + [ + "Bc", + "CaBCBbBbc", + 14 + ], + [ + "Bc", + "CbaBCbb", + 11 + ], + [ + "Bc", + "a", + 4 + ], + [ + "Bc", + "aC", + 3 + ], + [ + "Bc", + "abAbBBcc", + 12 + ], + [ + "Bc", + "abaCAABA", + 14 + ], + [ + "Bc", + "abaabAbc", + 13 + ], + [ + "Bc", + "abcbBBB", + 11 + ], + [ + "Bc", + "ac", + 2 + ], + [ + "Bc", + "acCCcb", + 10 + ], + [ + "Bc", + "acCacaaAa", + 16 + ], + [ + "Bc", + "ba", + 3 + ], + [ + "Bc", + "cC", + 3 + ], + [ + "Bc", + "cb", + 4 + ], + [ + "Bc", + "cbACCabca", + 15 + ], + [ + "BcA", + "AC", + 5 + ], + [ + "BcA", + "BAaB", + 5 + ], + [ + "BcA", + "BBc", + 4 + ], + [ + "BcA", + "CBababB", + 11 + ], + [ + "BcA", + "CcB", + 4 + ], + [ + "BcA", + "CcCb", + 6 + ], + [ + "BcA", + "a", + 5 + ], + [ + "BcA", + "bAcab", + 6 + ], + [ + "BcA", + "bBAbb", + 7 + ], + [ + "BcA", + "bbbc", + 7 + ], + [ + "BcA", + "c", + 4 + ], + [ + "BcAABaB", + "bACcAc", + 10 + ], + [ + "BcAAaA", + "BcCAbaBb", + 8 + ], + [ + "BcAB", + "aBCCcACc", + 10 + ], + [ + "BcABAc", + "BaAa", + 7 + ], + [ + "BcABaAaA", + "aCCaCcb", + 13 + ], + [ + "BcABaa", + "CaaBBaa", + 7 + ], + [ + "BcAC", + "BCAbcbCAa", + 11 + ], + [ + "BcACAA", + "aCb", + 9 + ], + [ + "BcACB", + "bCcC", + 6 + ], + [ + "BcACBaBCa", + "BCcBAbACc", + 10 + ], + [ + "BcACC", + "aa", + 9 + ], + [ + "BcAa", + "Cb", + 7 + ], + [ + "BcAaA", + "CBBc", + 9 + ], + [ + "BcAaA", + "abBbBBcB", + 14 + ], + [ + "BcAaACA", + "cbCABAaCb", + 10 + ], + [ + "BcAaCCBB", + "cBbCaBbA", + 11 + ], + [ + "BcAaCbB", + "ACB", + 8 + ], + [ + "BcAaaaC", + "cC", + 10 + ], + [ + "BcAb", + "BB", + 5 + ], + [ + "BcAb", + "CC", + 7 + ], + [ + "BcAbAbCac", + "bab", + 13 + ], + [ + "BcAbB", + "B", + 8 + ], + [ + "BcAbabBA", + "acb", + 12 + ], + [ + "BcAbabBBB", + "BA", + 14 + ], + [ + "BcAbabC", + "BBbaCAB", + 10 + ], + [ + "BcAbbcC", + "AaAcbC", + 8 + ], + [ + "BcAbc", + "BAC", + 5 + ], + [ + "BcAbc", + "BbbcbBabB", + 11 + ], + [ + "BcAbcAAC", + "A", + 14 + ], + [ + "BcAc", + "bC", + 6 + ], + [ + "BcAcAaACb", + "aAbBBB", + 14 + ], + [ + "BcAcB", + "caaAbbccB", + 12 + ], + [ + "BcAcaccb", + "cbAbAb", + 11 + ], + [ + "BcAcba", + "aA", + 10 + ], + [ + "BcAcc", + "BCaCBcAcC", + 9 + ], + [ + "BcB", + "AcCCAC", + 10 + ], + [ + "BcB", + "BBAcaAaB", + 10 + ], + [ + "BcB", + "BabAcacCA", + 14 + ], + [ + "BcB", + "aaBcAb", + 7 + ], + [ + "BcB", + "abcC", + 5 + ], + [ + "BcB", + "bcBBCbab", + 11 + ], + [ + "BcB", + "cAAc", + 8 + ], + [ + "BcB", + "ca", + 4 + ], + [ + "BcB", + "caBaaAAb", + 13 + ], + [ + "BcBA", + "CAabC", + 9 + ], + [ + "BcBA", + "baaC", + 7 + ], + [ + "BcBAAc", + "cBBaaabBB", + 14 + ], + [ + "BcBAB", + "aCaAaBb", + 9 + ], + [ + "BcBABCB", + "aaB", + 11 + ], + [ + "BcBAccAc", + "c", + 14 + ], + [ + "BcBBBA", + "ACc", + 11 + ], + [ + "BcBBC", + "cCABb", + 7 + ], + [ + "BcBBbb", + "bbBAa", + 8 + ], + [ + "BcBBbcAaA", + "CA", + 15 + ], + [ + "BcBC", + "c", + 6 + ], + [ + "BcBCA", + "cAaCC", + 8 + ], + [ + "BcBCAAAcc", + "abc", + 15 + ], + [ + "BcBCBBC", + "BbC", + 9 + ], + [ + "BcBCbAc", + "cBBCaC", + 8 + ], + [ + "BcBCcbccc", + "BcBC", + 10 + ], + [ + "BcBCccAa", + "acb", + 13 + ], + [ + "BcBa", + "aCB", + 5 + ], + [ + "BcBaAcAAc", + "bccBBBcBc", + 11 + ], + [ + "BcBaC", + "acBbcABAa", + 11 + ], + [ + "BcBab", + "CbCACC", + 10 + ], + [ + "BcBb", + "acAABAA", + 10 + ], + [ + "BcBbA", + "Ccba", + 5 + ], + [ + "BcBbAbABB", + "a", + 17 + ], + [ + "BcBbbCAa", + "baACcBCcC", + 14 + ], + [ + "BcBbbCa", + "AcCBAa", + 9 + ], + [ + "BcBbc", + "CcABb", + 6 + ], + [ + "BcBc", + "cBBcc", + 6 + ], + [ + "BcBcAbbc", + "babC", + 11 + ], + [ + "BcBcBc", + "cAAc", + 8 + ], + [ + "BcC", + "ABabcbC", + 8 + ], + [ + "BcC", + "Ac", + 4 + ], + [ + "BcC", + "C", + 4 + ], + [ + "BcC", + "acbcaAaa", + 13 + ], + [ + "BcC", + "bAbB", + 7 + ], + [ + "BcCA", + "ccaBaaAac", + 14 + ], + [ + "BcCAABBB", + "BCabBB", + 6 + ], + [ + "BcCAaAC", + "acCaB", + 8 + ], + [ + "BcCAaBc", + "Bcab", + 7 + ], + [ + "BcCAbB", + "ACaccCB", + 10 + ], + [ + "BcCAbaACc", + "aCAAcCBC", + 12 + ], + [ + "BcCAbcbA", + "bccAbCC", + 7 + ], + [ + "BcCAcA", + "BbB", + 10 + ], + [ + "BcCBABA", + "accb", + 10 + ], + [ + "BcCBAaC", + "b", + 13 + ], + [ + "BcCBbabcc", + "cCb", + 12 + ], + [ + "BcCBc", + "ba", + 9 + ], + [ + "BcCBcaAcc", + "B", + 16 + ], + [ + "BcCBcaCbC", + "CA", + 15 + ], + [ + "BcCCAA", + "caaA", + 7 + ], + [ + "BcCCBaaa", + "AaaCBb", + 12 + ], + [ + "BcCCCAA", + "b", + 13 + ], + [ + "BcCCaaaA", + "BccaAabBa", + 9 + ], + [ + "BcCCbCac", + "AcAa", + 12 + ], + [ + "BcCa", + "AB", + 8 + ], + [ + "BcCa", + "cB", + 6 + ], + [ + "BcCaACC", + "cbACaA", + 9 + ], + [ + "BcCaACCc", + "aaaCb", + 11 + ], + [ + "BcCaBbCAC", + "AbaAc", + 12 + ], + [ + "BcCaBc", + "CCAAB", + 8 + ], + [ + "BcCaCacc", + "CaCbbbc", + 10 + ], + [ + "BcCaaC", + "AcC", + 8 + ], + [ + "BcCb", + "aBacB", + 6 + ], + [ + "BcCbAcCb", + "CAA", + 12 + ], + [ + "BcCbCCCa", + "CBCC", + 9 + ], + [ + "BcCbaBabc", + "abcbB", + 13 + ], + [ + "BcCbbb", + "ccBCb", + 6 + ], + [ + "BcCc", + "bBb", + 7 + ], + [ + "BcCcBBb", + "aCCC", + 10 + ], + [ + "BcCcBcAb", + "aacbBbC", + 13 + ], + [ + "BcCca", + "CCcCaaB", + 8 + ], + [ + "Bca", + "AcbCBab", + 10 + ], + [ + "Bca", + "Bb", + 4 + ], + [ + "Bca", + "Bcc", + 2 + ], + [ + "Bca", + "CAAbA", + 9 + ], + [ + "Bca", + "CcBc", + 6 + ], + [ + "Bca", + "aAAccCBB", + 14 + ], + [ + "Bca", + "aCbbBBcCb", + 14 + ], + [ + "Bca", + "ac", + 4 + ], + [ + "Bca", + "c", + 4 + ], + [ + "BcaAAbACa", + "cAaacBB", + 13 + ], + [ + "BcaAaC", + "B", + 10 + ], + [ + "BcaB", + "A", + 7 + ], + [ + "BcaB", + "ABC", + 7 + ], + [ + "BcaB", + "AcAacA", + 8 + ], + [ + "BcaB", + "CAba", + 7 + ], + [ + "BcaB", + "cc", + 6 + ], + [ + "BcaBACC", + "aabAcbb", + 10 + ], + [ + "BcaBACbA", + "cB", + 12 + ], + [ + "BcaBAc", + "CAcBaCC", + 10 + ], + [ + "BcaBb", + "bABA", + 6 + ], + [ + "BcaBcab", + "aAaA", + 10 + ], + [ + "BcaC", + "abbAb", + 8 + ], + [ + "BcaC", + "ba", + 5 + ], + [ + "BcaC", + "ccaCBCB", + 8 + ], + [ + "BcaCBAbA", + "BA", + 12 + ], + [ + "BcaCC", + "CAbBbB", + 12 + ], + [ + "BcaCCbb", + "bbb", + 9 + ], + [ + "BcaCc", + "AaCABAACA", + 13 + ], + [ + "Bcaa", + "C", + 7 + ], + [ + "Bcaa", + "CbBABaa", + 8 + ], + [ + "Bcaa", + "cbbBcc", + 10 + ], + [ + "BcaaAc", + "AcaCCb", + 8 + ], + [ + "BcaaBBa", + "bBBCAAb", + 12 + ], + [ + "BcaaBcB", + "cCBbbAAcA", + 13 + ], + [ + "Bcaaba", + "ACbcA", + 10 + ], + [ + "Bcaabc", + "Bba", + 8 + ], + [ + "BcaacbcC", + "CBBBbCCA", + 12 + ], + [ + "Bcab", + "AcABabB", + 8 + ], + [ + "Bcab", + "abA", + 6 + ], + [ + "BcabB", + "BcbCc", + 6 + ], + [ + "BcabBB", + "BbAbbbA", + 7 + ], + [ + "BcabCA", + "b", + 10 + ], + [ + "BcabCac", + "BbAAaca", + 9 + ], + [ + "Bcaba", + "aCcbA", + 6 + ], + [ + "BcabaABA", + "CAbB", + 10 + ], + [ + "Bcac", + "CcCAbaaa", + 12 + ], + [ + "Bcac", + "ccc", + 4 + ], + [ + "BcacAC", + "abBc", + 9 + ], + [ + "BcacAaca", + "acbAbc", + 10 + ], + [ + "BcacCB", + "cbabacC", + 9 + ], + [ + "BcacaBC", + "ACCac", + 9 + ], + [ + "BcacabbA", + "Bcb", + 10 + ], + [ + "Bcacc", + "b", + 9 + ], + [ + "Bcb", + "AaAcacaB", + 13 + ], + [ + "Bcb", + "Cc", + 4 + ], + [ + "Bcb", + "aBBC", + 6 + ], + [ + "BcbAACc", + "aBBcA", + 11 + ], + [ + "BcbAAb", + "ab", + 9 + ], + [ + "BcbAC", + "AcC", + 6 + ], + [ + "BcbAaaacc", + "cc", + 14 + ], + [ + "BcbB", + "BbcbBbBC", + 8 + ], + [ + "BcbB", + "aB", + 6 + ], + [ + "BcbBcB", + "bCbb", + 7 + ], + [ + "BcbCA", + "bCc", + 6 + ], + [ + "BcbCAAba", + "CCaa", + 10 + ], + [ + "BcbCBCA", + "BcBa", + 7 + ], + [ + "BcbCC", + "b", + 8 + ], + [ + "BcbCCcc", + "Abb", + 12 + ], + [ + "BcbCabC", + "A", + 13 + ], + [ + "BcbCcB", + "cbaccCa", + 9 + ], + [ + "Bcba", + "ACa", + 5 + ], + [ + "Bcbb", + "baaAa", + 9 + ], + [ + "BcbbA", + "BBb", + 5 + ], + [ + "BcbbC", + "ccaBAABCC", + 12 + ], + [ + "BcbcA", + "bcBcAAcB", + 8 + ], + [ + "BcbcB", + "Ccc", + 6 + ], + [ + "Bcc", + "Ab", + 6 + ], + [ + "Bcc", + "BCAbacBc", + 10 + ], + [ + "Bcc", + "aacb", + 6 + ], + [ + "Bcc", + "baAa", + 7 + ], + [ + "BccABAbcA", + "Aba", + 13 + ], + [ + "BccACA", + "aa", + 10 + ], + [ + "BccAcAbbC", + "caC", + 13 + ], + [ + "BccBA", + "BACCAC", + 7 + ], + [ + "BccBAAbb", + "aac", + 14 + ], + [ + "BccBCbAA", + "BBCCC", + 10 + ], + [ + "BccBaAaa", + "AACAbbbbB", + 16 + ], + [ + "BccBac", + "CCabACa", + 10 + ], + [ + "BccC", + "ac", + 6 + ], + [ + "BccC", + "bCA", + 6 + ], + [ + "BccCa", + "caaBBaaB", + 14 + ], + [ + "BccCab", + "Cbcbac", + 8 + ], + [ + "BccCacBC", + "BBbA", + 13 + ], + [ + "BccCcA", + "BcCBCAB", + 6 + ], + [ + "Bcca", + "AAb", + 8 + ], + [ + "BccaA", + "cCC", + 7 + ], + [ + "BccaA", + "ccc", + 6 + ], + [ + "BccaBbB", + "AA", + 13 + ], + [ + "Bccac", + "CcbaCCa", + 9 + ], + [ + "Bccb", + "AcbC", + 6 + ], + [ + "BccbB", + "bbccA", + 7 + ], + [ + "BccbBCAA", + "AA", + 12 + ], + [ + "BccbBaCbb", + "CbcBBCAA", + 11 + ], + [ + "BccbacAAc", + "CAac", + 12 + ], + [ + "BccbbACa", + "acCAbCAc", + 10 + ], + [ + "BcccAC", + "cccBbBaac", + 12 + ], + [ + "BcccBB", + "bABCc", + 10 + ], + [ + "BcccBBb", + "Cbacbaa", + 11 + ], + [ + "C", + "A", + 2 + ], + [ + "C", + "AAA", + 6 + ], + [ + "C", + "AAAb", + 8 + ], + [ + "C", + "AAB", + 6 + ], + [ + "C", + "AAacba", + 11 + ], + [ + "C", + "AAbb", + 8 + ], + [ + "C", + "ABA", + 6 + ], + [ + "C", + "ABAB", + 8 + ], + [ + "C", + "ABAcBAabA", + 17 + ], + [ + "C", + "ABCB", + 6 + ], + [ + "C", + "ABCbbbBAC", + 16 + ], + [ + "C", + "ABabc", + 9 + ], + [ + "C", + "ACCCcAcAa", + 16 + ], + [ + "C", + "ACc", + 4 + ], + [ + "C", + "ACcbaABc", + 14 + ], + [ + "C", + "AaAAa", + 10 + ], + [ + "C", + "AaAa", + 8 + ], + [ + "C", + "AaAbcaBb", + 15 + ], + [ + "C", + "AabbbC", + 10 + ], + [ + "C", + "AacCCABaB", + 16 + ], + [ + "C", + "AacaaaB", + 13 + ], + [ + "C", + "Ab", + 4 + ], + [ + "C", + "AbBbBACab", + 16 + ], + [ + "C", + "AbBcCcBAb", + 16 + ], + [ + "C", + "AbC", + 4 + ], + [ + "C", + "AbCCaCbc", + 14 + ], + [ + "C", + "Abb", + 6 + ], + [ + "C", + "AbbCcBb", + 12 + ], + [ + "C", + "Acbc", + 7 + ], + [ + "C", + "B", + 2 + ], + [ + "C", + "BAAbAbCa", + 14 + ], + [ + "C", + "BAAc", + 7 + ], + [ + "C", + "BACcBbAcB", + 16 + ], + [ + "C", + "BBBaAcB", + 13 + ], + [ + "C", + "BBacaaA", + 13 + ], + [ + "C", + "BC", + 2 + ], + [ + "C", + "BCCbA", + 8 + ], + [ + "C", + "BCba", + 6 + ], + [ + "C", + "Ba", + 4 + ], + [ + "C", + "BaA", + 6 + ], + [ + "C", + "BaBCCBcb", + 14 + ], + [ + "C", + "BaBcbbCBA", + 16 + ], + [ + "C", + "BaaC", + 6 + ], + [ + "C", + "BabbcCBa", + 14 + ], + [ + "C", + "BacccCB", + 12 + ], + [ + "C", + "BbABc", + 9 + ], + [ + "C", + "BbAaAAC", + 12 + ], + [ + "C", + "BbBBcc", + 11 + ], + [ + "C", + "BbCBCB", + 10 + ], + [ + "C", + "BbcCCbAC", + 14 + ], + [ + "C", + "BcB", + 5 + ], + [ + "C", + "BcBBBbAaa", + 17 + ], + [ + "C", + "BcCAcacAB", + 16 + ], + [ + "C", + "BcCB", + 6 + ], + [ + "C", + "BcacCaB", + 12 + ], + [ + "C", + "BccAaa", + 11 + ], + [ + "C", + "BccaAa", + 11 + ], + [ + "C", + "C", + 0 + ], + [ + "C", + "CA", + 2 + ], + [ + "C", + "CAAbCa", + 10 + ], + [ + "C", + "CABBB", + 8 + ], + [ + "C", + "CAaAa", + 8 + ], + [ + "C", + "CAbAABb", + 12 + ], + [ + "C", + "CBBCaABbB", + 16 + ], + [ + "C", + "CBaacB", + 10 + ], + [ + "C", + "CBc", + 4 + ], + [ + "C", + "CC", + 2 + ], + [ + "C", + "CCA", + 4 + ], + [ + "C", + "CCBbccA", + 12 + ], + [ + "C", + "CCc", + 4 + ], + [ + "C", + "CCcbbCBCc", + 16 + ], + [ + "C", + "CCcc", + 6 + ], + [ + "C", + "CCccCaCa", + 14 + ], + [ + "C", + "Ca", + 2 + ], + [ + "C", + "CaA", + 4 + ], + [ + "C", + "Caab", + 6 + ], + [ + "C", + "Cacaacba", + 14 + ], + [ + "C", + "Cb", + 2 + ], + [ + "C", + "CbaCBcAb", + 14 + ], + [ + "C", + "CbbBCbC", + 12 + ], + [ + "C", + "Cc", + 2 + ], + [ + "C", + "CcAcCA", + 10 + ], + [ + "C", + "CcBBCbBB", + 14 + ], + [ + "C", + "CcCBBbB", + 12 + ], + [ + "C", + "a", + 2 + ], + [ + "C", + "aA", + 4 + ], + [ + "C", + "aAABBb", + 12 + ], + [ + "C", + "aACBaca", + 12 + ], + [ + "C", + "aAaCB", + 8 + ], + [ + "C", + "aAaaBcaB", + 15 + ], + [ + "C", + "aAabb", + 10 + ], + [ + "C", + "aAccAABB", + 15 + ], + [ + "C", + "aBAaa", + 10 + ], + [ + "C", + "aBbab", + 10 + ], + [ + "C", + "aCBccCbBc", + 16 + ], + [ + "C", + "aCCBcbcBc", + 16 + ], + [ + "C", + "aCCC", + 6 + ], + [ + "C", + "aCcB", + 6 + ], + [ + "C", + "aCcccA", + 10 + ], + [ + "C", + "aa", + 4 + ], + [ + "C", + "aaBA", + 8 + ], + [ + "C", + "aaBAbA", + 12 + ], + [ + "C", + "aaBabBB", + 14 + ], + [ + "C", + "aaCAC", + 8 + ], + [ + "C", + "aaCBb", + 8 + ], + [ + "C", + "aaaBcBBc", + 15 + ], + [ + "C", + "aaab", + 8 + ], + [ + "C", + "aab", + 6 + ], + [ + "C", + "aacCCabb", + 14 + ], + [ + "C", + "abBbAcaCc", + 16 + ], + [ + "C", + "abbbAaaC", + 14 + ], + [ + "C", + "abcBBB", + 11 + ], + [ + "C", + "ac", + 3 + ], + [ + "C", + "b", + 2 + ], + [ + "C", + "bAAAccb", + 13 + ], + [ + "C", + "bAAaCBaCa", + 16 + ], + [ + "C", + "bAaABACCb", + 16 + ], + [ + "C", + "bAbAcCAc", + 14 + ], + [ + "C", + "bBABA", + 10 + ], + [ + "C", + "bBB", + 6 + ], + [ + "C", + "bBacA", + 9 + ], + [ + "C", + "bBcbbCabb", + 16 + ], + [ + "C", + "bC", + 2 + ], + [ + "C", + "bCcAcACba", + 16 + ], + [ + "C", + "ba", + 4 + ], + [ + "C", + "baAA", + 8 + ], + [ + "C", + "baAba", + 10 + ], + [ + "C", + "baCABab", + 12 + ], + [ + "C", + "baaA", + 8 + ], + [ + "C", + "baaB", + 8 + ], + [ + "C", + "baaCbCb", + 12 + ], + [ + "C", + "bbABBCA", + 12 + ], + [ + "C", + "bbABcCb", + 12 + ], + [ + "C", + "bbCab", + 8 + ], + [ + "C", + "bbacCaabC", + 16 + ], + [ + "C", + "bbbcaAb", + 13 + ], + [ + "C", + "bc", + 3 + ], + [ + "C", + "bcAca", + 9 + ], + [ + "C", + "bcBAABAAC", + 16 + ], + [ + "C", + "bcBcA", + 9 + ], + [ + "C", + "bcC", + 4 + ], + [ + "C", + "bcCAb", + 8 + ], + [ + "C", + "bcaAb", + 9 + ], + [ + "C", + "bcacBAC", + 12 + ], + [ + "C", + "bcb", + 5 + ], + [ + "C", + "bcbBba", + 11 + ], + [ + "C", + "bcbacaAA", + 15 + ], + [ + "C", + "bccC", + 6 + ], + [ + "C", + "bccccCA", + 12 + ], + [ + "C", + "c", + 1 + ], + [ + "C", + "cAc", + 5 + ], + [ + "C", + "cAcBCbCb", + 14 + ], + [ + "C", + "cAcaABCA", + 14 + ], + [ + "C", + "cBAA", + 7 + ], + [ + "C", + "cBBB", + 7 + ], + [ + "C", + "cBc", + 5 + ], + [ + "C", + "cBccbAA", + 13 + ], + [ + "C", + "cC", + 2 + ], + [ + "C", + "cCBAAB", + 10 + ], + [ + "C", + "cCaACAAC", + 14 + ], + [ + "C", + "cCbA", + 6 + ], + [ + "C", + "cCbbABa", + 12 + ], + [ + "C", + "cCcabCbBb", + 16 + ], + [ + "C", + "ca", + 3 + ], + [ + "C", + "caCACCCaB", + 16 + ], + [ + "C", + "cabCa", + 8 + ], + [ + "C", + "cacACaBB", + 14 + ], + [ + "C", + "cacBaACc", + 14 + ], + [ + "C", + "cbACabc", + 12 + ], + [ + "C", + "cbCCB", + 8 + ], + [ + "C", + "cbcC", + 6 + ], + [ + "C", + "cc", + 3 + ], + [ + "C", + "ccBAbBBC", + 14 + ], + [ + "C", + "ccBaC", + 8 + ], + [ + "C", + "ccaCbbCbb", + 16 + ], + [ + "C", + "ccabC", + 8 + ], + [ + "C", + "ccbBbCCc", + 14 + ], + [ + "CA", + "A", + 2 + ], + [ + "CA", + "AAa", + 4 + ], + [ + "CA", + "Aaacbb", + 11 + ], + [ + "CA", + "AcC", + 5 + ], + [ + "CA", + "BBaB", + 7 + ], + [ + "CA", + "BcAcbBC", + 11 + ], + [ + "CA", + "C", + 2 + ], + [ + "CA", + "CBCAa", + 6 + ], + [ + "CA", + "CacbbBaa", + 13 + ], + [ + "CA", + "CcBcBAB", + 10 + ], + [ + "CA", + "CcbCaBB", + 11 + ], + [ + "CA", + "a", + 3 + ], + [ + "CA", + "aAABAaC", + 12 + ], + [ + "CA", + "aABBa", + 8 + ], + [ + "CA", + "abbCC", + 8 + ], + [ + "CA", + "acbb", + 7 + ], + [ + "CA", + "bAbAAcbA", + 13 + ], + [ + "CA", + "bAcBcACC", + 13 + ], + [ + "CA", + "bCBaBBaB", + 13 + ], + [ + "CA", + "bbbB", + 8 + ], + [ + "CA", + "cAaaaA", + 9 + ], + [ + "CA", + "caCBCcaaA", + 14 + ], + [ + "CAA", + "CCbc", + 6 + ], + [ + "CAA", + "CbCcba", + 9 + ], + [ + "CAA", + "bb", + 6 + ], + [ + "CAA", + "c", + 5 + ], + [ + "CAA", + "cACAaa", + 7 + ], + [ + "CAA", + "cAaabbacA", + 13 + ], + [ + "CAAA", + "bb", + 8 + ], + [ + "CAAAAAcCB", + "bC", + 16 + ], + [ + "CAAAB", + "ba", + 9 + ], + [ + "CAAABaBAc", + "CaaC", + 12 + ], + [ + "CAAACa", + "AAcbAAc", + 9 + ], + [ + "CAAACaAC", + "aABBcbcBa", + 15 + ], + [ + "CAAAaBAa", + "BCBc", + 14 + ], + [ + "CAAAba", + "CacBaB", + 8 + ], + [ + "CAAAbb", + "cCaCb", + 8 + ], + [ + "CAAB", + "CbcCbBBA", + 12 + ], + [ + "CAABAAcBC", + "bccb", + 14 + ], + [ + "CAABAAcbB", + "ABBa", + 13 + ], + [ + "CAABCaaBa", + "aBCACcBca", + 12 + ], + [ + "CAABCcAAa", + "ccCB", + 15 + ], + [ + "CAABbaA", + "AbBaAac", + 10 + ], + [ + "CAAC", + "cacABc", + 7 + ], + [ + "CAACAAC", + "Ab", + 12 + ], + [ + "CAACAbca", + "Bb", + 14 + ], + [ + "CAACCAc", + "CAAB", + 8 + ], + [ + "CAACb", + "CA", + 6 + ], + [ + "CAAa", + "cBCC", + 7 + ], + [ + "CAAaBaAC", + "AaccBBB", + 13 + ], + [ + "CAAaaA", + "CaAaACca", + 7 + ], + [ + "CAAab", + "b", + 8 + ], + [ + "CAAabac", + "Accbbc", + 8 + ], + [ + "CAAbCbCcC", + "B", + 17 + ], + [ + "CAAbb", + "caC", + 8 + ], + [ + "CAAbbAbb", + "BC", + 15 + ], + [ + "CAAcBB", + "aCBCbAAb", + 13 + ], + [ + "CAAccAcC", + "abA", + 13 + ], + [ + "CAB", + "ABabCcc", + 12 + ], + [ + "CAB", + "ABccbC", + 10 + ], + [ + "CAB", + "CABba", + 4 + ], + [ + "CAB", + "CcbB", + 4 + ], + [ + "CAB", + "aababa", + 10 + ], + [ + "CAB", + "bC", + 6 + ], + [ + "CAB", + "cAbbABAac", + 13 + ], + [ + "CABA", + "CaabCCc", + 10 + ], + [ + "CABAAa", + "abC", + 10 + ], + [ + "CABACacA", + "cAbaaABC", + 10 + ], + [ + "CABACcA", + "B", + 12 + ], + [ + "CABAa", + "aac", + 8 + ], + [ + "CABAcBa", + "b", + 13 + ], + [ + "CABBAa", + "bbBcabcC", + 13 + ], + [ + "CABBBAa", + "B", + 12 + ], + [ + "CABBBCaa", + "ACb", + 12 + ], + [ + "CABBBb", + "cacaAACa", + 14 + ], + [ + "CABC", + "cCbacA", + 9 + ], + [ + "CABCAcaC", + "B", + 14 + ], + [ + "CABa", + "B", + 6 + ], + [ + "CABaCc", + "BCaaaAAcb", + 11 + ], + [ + "CABab", + "baB", + 6 + ], + [ + "CABacBA", + "BaACb", + 10 + ], + [ + "CABb", + "bCcBaCba", + 10 + ], + [ + "CABbABcBA", + "aBaAccabA", + 10 + ], + [ + "CABbbC", + "CbACCAcc", + 11 + ], + [ + "CABcAaAb", + "bb", + 13 + ], + [ + "CABcaa", + "C", + 10 + ], + [ + "CABcaa", + "aCaBABAab", + 10 + ], + [ + "CABcabCAa", + "AcbaBcCA", + 11 + ], + [ + "CABcbCAc", + "ABAcc", + 9 + ], + [ + "CABcbbA", + "BBbcA", + 8 + ], + [ + "CABcbcacA", + "cb", + 14 + ], + [ + "CAC", + "aCAAaABb", + 12 + ], + [ + "CAC", + "abCCACaBc", + 12 + ], + [ + "CAC", + "bbcbB", + 9 + ], + [ + "CAC", + "caBAa", + 7 + ], + [ + "CAC", + "ccBaBcb", + 11 + ], + [ + "CACAACccb", + "bc", + 16 + ], + [ + "CACABBA", + "cCACAcCcC", + 10 + ], + [ + "CACACAcBB", + "aBAACCc", + 12 + ], + [ + "CACAabc", + "cbAaBbbbB", + 13 + ], + [ + "CACAcaAC", + "ACcACc", + 8 + ], + [ + "CACAcbC", + "Cc", + 10 + ], + [ + "CACB", + "Cbc", + 5 + ], + [ + "CACBb", + "C", + 8 + ], + [ + "CACC", + "bCa", + 6 + ], + [ + "CACCAAB", + "bACabAC", + 8 + ], + [ + "CACCAb", + "abBBB", + 10 + ], + [ + "CACCCBA", + "cabaac", + 12 + ], + [ + "CACCc", + "ba", + 9 + ], + [ + "CACaAcc", + "acaBbCbb", + 13 + ], + [ + "CACaCBBc", + "a", + 14 + ], + [ + "CACaabcBb", + "aCabba", + 10 + ], + [ + "CACacaABb", + "b", + 16 + ], + [ + "CACb", + "ABbb", + 6 + ], + [ + "CACb", + "AabbaCa", + 11 + ], + [ + "CACbA", + "bCbbC", + 8 + ], + [ + "CACbaCB", + "caacaaC", + 9 + ], + [ + "CACbaaCa", + "bBacacB", + 12 + ], + [ + "CACbbBBc", + "Bc", + 12 + ], + [ + "CACbc", + "Cc", + 6 + ], + [ + "CACbcAcac", + "cCAcA", + 10 + ], + [ + "CACbcba", + "caABaCb", + 10 + ], + [ + "CACcBabCc", + "baAbbaBB", + 13 + ], + [ + "CACcCacC", + "ABcC", + 10 + ], + [ + "CACccCAC", + "B", + 16 + ], + [ + "CAa", + "Ab", + 4 + ], + [ + "CAa", + "CabcCc", + 9 + ], + [ + "CAa", + "aaCAbaBc", + 10 + ], + [ + "CAa", + "bABABaCA", + 12 + ], + [ + "CAa", + "bBAaB", + 6 + ], + [ + "CAa", + "bca", + 4 + ], + [ + "CAa", + "cbC", + 5 + ], + [ + "CAaAAaA", + "abAbbcbaC", + 14 + ], + [ + "CAaAAbaaB", + "CAcBB", + 11 + ], + [ + "CAaAcb", + "bBCBB", + 11 + ], + [ + "CAaBAA", + "AacAcBABB", + 11 + ], + [ + "CAaBB", + "Cc", + 8 + ], + [ + "CAaBBACa", + "CAaacBccC", + 9 + ], + [ + "CAaBC", + "b", + 9 + ], + [ + "CAaBbbaBA", + "BBB", + 13 + ], + [ + "CAaC", + "AaBCacAba", + 14 + ], + [ + "CAaC", + "BcccCbBb", + 13 + ], + [ + "CAaCBbcab", + "CbC", + 13 + ], + [ + "CAaCc", + "AA", + 7 + ], + [ + "CAaCcabB", + "cBABCb", + 11 + ], + [ + "CAaCccCc", + "A", + 14 + ], + [ + "CAaa", + "bcABBaAb", + 10 + ], + [ + "CAaa", + "cbabABAC", + 12 + ], + [ + "CAaaB", + "b", + 9 + ], + [ + "CAaabBcC", + "a", + 14 + ], + [ + "CAaabbCac", + "bAC", + 14 + ], + [ + "CAaabcaB", + "cabCacbaA", + 12 + ], + [ + "CAabABbAa", + "bcbaCa", + 13 + ], + [ + "CAabac", + "baaABbB", + 10 + ], + [ + "CAabb", + "b", + 8 + ], + [ + "CAabb", + "cACACBaBB", + 10 + ], + [ + "CAac", + "aaB", + 5 + ], + [ + "CAac", + "cccBB", + 9 + ], + [ + "CAacAB", + "a", + 10 + ], + [ + "CAaccC", + "cacbc", + 6 + ], + [ + "CAb", + "ACCCAcaA", + 12 + ], + [ + "CAb", + "Aca", + 6 + ], + [ + "CAb", + "BCcBBAA", + 10 + ], + [ + "CAb", + "C", + 4 + ], + [ + "CAb", + "CCAcbAB", + 8 + ], + [ + "CAb", + "CcCbCBC", + 10 + ], + [ + "CAb", + "acAc", + 5 + ], + [ + "CAb", + "bab", + 3 + ], + [ + "CAb", + "cA", + 3 + ], + [ + "CAbAAaAA", + "abc", + 13 + ], + [ + "CAbAc", + "ABBCCB", + 10 + ], + [ + "CAbB", + "acC", + 7 + ], + [ + "CAbBBBB", + "BCcaBbabB", + 10 + ], + [ + "CAbBCCBAC", + "baA", + 14 + ], + [ + "CAbBab", + "cbABcBc", + 10 + ], + [ + "CAbBc", + "bBcAcCA", + 11 + ], + [ + "CAbBc", + "bCAcbba", + 7 + ], + [ + "CAbC", + "AaAc", + 6 + ], + [ + "CAbC", + "cBAC", + 5 + ], + [ + "CAbCBaBb", + "cAbcBa", + 6 + ], + [ + "CAbCCC", + "c", + 11 + ], + [ + "CAbCabbA", + "BbAABACba", + 12 + ], + [ + "CAbCcc", + "babBA", + 9 + ], + [ + "CAba", + "abbB", + 6 + ], + [ + "CAbaAbBBC", + "CbBBAbB", + 9 + ], + [ + "CAbaAcCCC", + "AcbB", + 14 + ], + [ + "CAbaBb", + "A", + 10 + ], + [ + "CAbaa", + "cc", + 9 + ], + [ + "CAbacbba", + "cBCccabA", + 10 + ], + [ + "CAbbAA", + "BAAaCbC", + 12 + ], + [ + "CAbbAAcC", + "CBBACbBCB", + 12 + ], + [ + "CAbbBCcB", + "Abca", + 10 + ], + [ + "CAbbBbC", + "ABcaca", + 11 + ], + [ + "CAbbbAC", + "bcB", + 11 + ], + [ + "CAbbcCABA", + "CbB", + 12 + ], + [ + "CAbc", + "B", + 7 + ], + [ + "CAbcA", + "CAcaccACb", + 10 + ], + [ + "CAbcACCB", + "BB", + 13 + ], + [ + "CAbcBAa", + "bccBACB", + 10 + ], + [ + "CAbcCca", + "cAc", + 9 + ], + [ + "CAbcaBbca", + "ccaacBaaa", + 12 + ], + [ + "CAc", + "AAcacbcC", + 12 + ], + [ + "CAc", + "AbbAbbcc", + 12 + ], + [ + "CAc", + "BAB", + 4 + ], + [ + "CAc", + "ab", + 5 + ], + [ + "CAc", + "b", + 6 + ], + [ + "CAc", + "bbaaac", + 9 + ], + [ + "CAcAaCcaC", + "AAa", + 12 + ], + [ + "CAcAbaaA", + "ccBcBc", + 12 + ], + [ + "CAcAcC", + "BAbCb", + 9 + ], + [ + "CAcAca", + "CCbb", + 9 + ], + [ + "CAcAcbbAc", + "Cca", + 13 + ], + [ + "CAcB", + "cBcaaCcB", + 10 + ], + [ + "CAcBbCccb", + "BBAbAbAB", + 15 + ], + [ + "CAcBbbAaA", + "aACbbCbaa", + 9 + ], + [ + "CAcBbcbCa", + "bcBc", + 12 + ], + [ + "CAcBcBC", + "abCaCb", + 11 + ], + [ + "CAcC", + "CBcB", + 4 + ], + [ + "CAcCACCA", + "cAAa", + 10 + ], + [ + "CAcCaCBCC", + "aac", + 14 + ], + [ + "CAca", + "AaBACbAab", + 13 + ], + [ + "CAcaABcab", + "aCCbC", + 14 + ], + [ + "CAcaaa", + "bbccA", + 9 + ], + [ + "CAcaacBB", + "c", + 14 + ], + [ + "CAcb", + "B", + 7 + ], + [ + "CAcb", + "aBCBAb", + 8 + ], + [ + "CAcbBA", + "bBAaba", + 9 + ], + [ + "CAcbBB", + "CCcCBA", + 6 + ], + [ + "CAcbBacb", + "AbbbB", + 10 + ], + [ + "CAcbCCaaC", + "bccB", + 14 + ], + [ + "CAcbCbc", + "bAcCA", + 8 + ], + [ + "CAcbbabC", + "abAccCaac", + 11 + ], + [ + "CAcbcA", + "BBca", + 8 + ], + [ + "CAcbcB", + "Ab", + 8 + ], + [ + "CAcc", + "b", + 8 + ], + [ + "CAccAaAcc", + "bccbAB", + 12 + ], + [ + "CAccBaAC", + "abbAAaB", + 13 + ], + [ + "CAccBaaCc", + "BCAb", + 15 + ], + [ + "CAccBbA", + "bcBbBcBc", + 13 + ], + [ + "CAccac", + "aAc", + 8 + ], + [ + "CB", + "A", + 4 + ], + [ + "CB", + "ABac", + 6 + ], + [ + "CB", + "AC", + 4 + ], + [ + "CB", + "ACbbcbb", + 11 + ], + [ + "CB", + "ACcCCBAC", + 12 + ], + [ + "CB", + "AaAAcBc", + 11 + ], + [ + "CB", + "AaBCab", + 9 + ], + [ + "CB", + "BCc", + 4 + ], + [ + "CB", + "BaCaB", + 6 + ], + [ + "CB", + "BccbcabCB", + 14 + ], + [ + "CB", + "C", + 2 + ], + [ + "CB", + "CAAAb", + 7 + ], + [ + "CB", + "CCba", + 5 + ], + [ + "CB", + "CaAcA", + 8 + ], + [ + "CB", + "CcCaAccb", + 13 + ], + [ + "CB", + "a", + 4 + ], + [ + "CB", + "aAa", + 6 + ], + [ + "CB", + "aBCBaBbC", + 12 + ], + [ + "CB", + "aCbcAc", + 9 + ], + [ + "CB", + "ac", + 4 + ], + [ + "CB", + "bAa", + 6 + ], + [ + "CB", + "bBAAbcb", + 12 + ], + [ + "CB", + "bCB", + 2 + ], + [ + "CB", + "bCaCCcaCA", + 16 + ], + [ + "CB", + "ba", + 4 + ], + [ + "CB", + "baBCcCc", + 12 + ], + [ + "CB", + "bbcbCB", + 8 + ], + [ + "CB", + "bcaab", + 8 + ], + [ + "CB", + "c", + 3 + ], + [ + "CB", + "cACca", + 8 + ], + [ + "CB", + "cBCbAB", + 8 + ], + [ + "CB", + "cCB", + 2 + ], + [ + "CB", + "cbb", + 4 + ], + [ + "CB", + "cbbBbACCA", + 15 + ], + [ + "CBA", + "ba", + 4 + ], + [ + "CBA", + "cbcBb", + 7 + ], + [ + "CBAA", + "ABBaBcB", + 11 + ], + [ + "CBAA", + "Ba", + 5 + ], + [ + "CBAA", + "bcc", + 7 + ], + [ + "CBAABa", + "BAaBA", + 4 + ], + [ + "CBAAC", + "CBAac", + 2 + ], + [ + "CBAAa", + "cccbAb", + 9 + ], + [ + "CBAAaac", + "bcAAb", + 10 + ], + [ + "CBAAbCc", + "bbaABBB", + 9 + ], + [ + "CBAAbbBC", + "CbBAaAc", + 10 + ], + [ + "CBAAc", + "AAA", + 6 + ], + [ + "CBAAc", + "bCbBbAAaC", + 9 + ], + [ + "CBAB", + "A", + 6 + ], + [ + "CBABAbcBA", + "b", + 16 + ], + [ + "CBABAcBc", + "cAb", + 12 + ], + [ + "CBABCacb", + "aCCbbBABA", + 14 + ], + [ + "CBABaa", + "cBbcCcc", + 11 + ], + [ + "CBABbbCa", + "BbAaA", + 11 + ], + [ + "CBABbcCCC", + "cCAC", + 12 + ], + [ + "CBABc", + "cCc", + 7 + ], + [ + "CBABccBbc", + "caAaaACbb", + 13 + ], + [ + "CBACAA", + "CCc", + 8 + ], + [ + "CBACAcC", + "aCbbc", + 10 + ], + [ + "CBACa", + "BACaBBaB", + 10 + ], + [ + "CBACbcB", + "a", + 13 + ], + [ + "CBACc", + "baCCCA", + 9 + ], + [ + "CBACcAccA", + "aAb", + 15 + ], + [ + "CBAaAC", + "ccbC", + 9 + ], + [ + "CBAaBaC", + "bCccccCb", + 14 + ], + [ + "CBAaCB", + "acabB", + 8 + ], + [ + "CBAaCa", + "Cabb", + 8 + ], + [ + "CBAaCc", + "cBBBBBaC", + 11 + ], + [ + "CBAabb", + "bbCabaBa", + 10 + ], + [ + "CBAacCa", + "Bccbacb", + 11 + ], + [ + "CBAb", + "cCAcbc", + 7 + ], + [ + "CBAbCcaB", + "aC", + 13 + ], + [ + "CBAba", + "CbbabA", + 5 + ], + [ + "CBAbabb", + "BABa", + 7 + ], + [ + "CBAbba", + "ba", + 8 + ], + [ + "CBAcAAbcb", + "BBBBBbABA", + 15 + ], + [ + "CBAcC", + "acbabBA", + 11 + ], + [ + "CBAccB", + "cb", + 9 + ], + [ + "CBAccab", + "cbB", + 11 + ], + [ + "CBB", + "Aa", + 6 + ], + [ + "CBB", + "BbAaC", + 9 + ], + [ + "CBB", + "CB", + 2 + ], + [ + "CBB", + "CCA", + 4 + ], + [ + "CBB", + "CcbCb", + 6 + ], + [ + "CBB", + "baBB", + 4 + ], + [ + "CBB", + "cAcBBccA", + 11 + ], + [ + "CBB", + "cBccaB", + 7 + ], + [ + "CBBABB", + "BbBb", + 6 + ], + [ + "CBBABC", + "AABcaac", + 10 + ], + [ + "CBBAbACb", + "CCBCbAbcB", + 8 + ], + [ + "CBBBA", + "bB", + 7 + ], + [ + "CBBBBaAC", + "ca", + 13 + ], + [ + "CBBBBbbcC", + "BaAbCaB", + 14 + ], + [ + "CBBBb", + "bCbaBBAb", + 7 + ], + [ + "CBBBbCbcA", + "ABCCcABc", + 13 + ], + [ + "CBBC", + "ACaca", + 8 + ], + [ + "CBBC", + "cB", + 5 + ], + [ + "CBBCAAB", + "b", + 13 + ], + [ + "CBBCAb", + "CBA", + 6 + ], + [ + "CBBCCAB", + "AbCa", + 10 + ], + [ + "CBBCCa", + "c", + 11 + ], + [ + "CBBCabAa", + "Cca", + 11 + ], + [ + "CBBaCc", + "CbbA", + 7 + ], + [ + "CBBab", + "acBCcA", + 9 + ], + [ + "CBBabb", + "BBCcBb", + 7 + ], + [ + "CBBacBacB", + "caCCccBc", + 12 + ], + [ + "CBBbAb", + "Bbab", + 5 + ], + [ + "CBBba", + "bcCcb", + 9 + ], + [ + "CBBbaAcbc", + "ABcABaAAc", + 11 + ], + [ + "CBBbbbabc", + "baAaBC", + 12 + ], + [ + "CBBbcaaC", + "bABABABB", + 13 + ], + [ + "CBBc", + "abCABc", + 6 + ], + [ + "CBBcACB", + "abBbC", + 9 + ], + [ + "CBBcBcc", + "A", + 14 + ], + [ + "CBBcbA", + "ccbBAca", + 9 + ], + [ + "CBBcbaBc", + "C", + 14 + ], + [ + "CBBcbaC", + "bBcAAbaaC", + 9 + ], + [ + "CBBccBCCB", + "bacCC", + 11 + ], + [ + "CBBccc", + "aBabc", + 8 + ], + [ + "CBC", + "ACB", + 4 + ], + [ + "CBC", + "CAbCabB", + 9 + ], + [ + "CBC", + "aCaAaAC", + 10 + ], + [ + "CBCAA", + "C", + 8 + ], + [ + "CBCAABa", + "BBcCbBa", + 7 + ], + [ + "CBCAACB", + "bBBcAAB", + 7 + ], + [ + "CBCACAa", + "ABbA", + 10 + ], + [ + "CBCAaCA", + "BbaAbac", + 10 + ], + [ + "CBCAacCc", + "cCb", + 12 + ], + [ + "CBCB", + "aAAaA", + 10 + ], + [ + "CBCBBA", + "acc", + 11 + ], + [ + "CBCBBCAA", + "ABC", + 12 + ], + [ + "CBCBC", + "aBccbCB", + 8 + ], + [ + "CBCBCC", + "BAbc", + 8 + ], + [ + "CBCC", + "CbaaA", + 7 + ], + [ + "CBCCBC", + "aACAA", + 10 + ], + [ + "CBCCBCcCb", + "Ba", + 16 + ], + [ + "CBCCCB", + "cacbAbbaA", + 15 + ], + [ + "CBCCCbaB", + "cacAbc", + 12 + ], + [ + "CBCCacb", + "a", + 12 + ], + [ + "CBCCbcc", + "BbbBcbCBc", + 11 + ], + [ + "CBCCcCbA", + "CBaAa", + 11 + ], + [ + "CBCa", + "aaAbbabA", + 13 + ], + [ + "CBCa", + "b", + 7 + ], + [ + "CBCaA", + "ab", + 8 + ], + [ + "CBCaAaBaa", + "acBbAa", + 12 + ], + [ + "CBCaB", + "caa", + 7 + ], + [ + "CBCacBBBB", + "BcabACCb", + 12 + ], + [ + "CBCb", + "AabACCa", + 11 + ], + [ + "CBCb", + "aABc", + 7 + ], + [ + "CBCbBa", + "abaCaB", + 9 + ], + [ + "CBCbbc", + "CcBCBCcbA", + 9 + ], + [ + "CBCbccaA", + "AbCbbCba", + 9 + ], + [ + "CBCc", + "bbcCBBAcb", + 12 + ], + [ + "CBCcB", + "BAa", + 8 + ], + [ + "CBCcBbb", + "ccaCBbA", + 8 + ], + [ + "CBCcccacB", + "BA", + 15 + ], + [ + "CBa", + "ACaba", + 5 + ], + [ + "CBa", + "CAaB", + 4 + ], + [ + "CBa", + "CBBCccc", + 10 + ], + [ + "CBa", + "CacAccB", + 11 + ], + [ + "CBa", + "c", + 5 + ], + [ + "CBaAAa", + "caCcaBAA", + 9 + ], + [ + "CBaAC", + "CBb", + 6 + ], + [ + "CBaAbaaAA", + "CbbcC", + 13 + ], + [ + "CBaB", + "aabBAbbC", + 12 + ], + [ + "CBaBBAa", + "AaAcA", + 10 + ], + [ + "CBaBCABC", + "AAA", + 13 + ], + [ + "CBaBaCcB", + "cBaAbBAAa", + 12 + ], + [ + "CBaBaa", + "AAB", + 9 + ], + [ + "CBaCAbC", + "BbCaCC", + 7 + ], + [ + "CBaCBba", + "bcABB", + 10 + ], + [ + "CBaCCAB", + "cBcBAcA", + 10 + ], + [ + "CBaCabccB", + "Bba", + 14 + ], + [ + "CBaaA", + "AacbaC", + 10 + ], + [ + "CBaaAcac", + "aacbcbACc", + 13 + ], + [ + "CBaaB", + "CbaAcB", + 4 + ], + [ + "CBaaBc", + "Ba", + 8 + ], + [ + "CBaaCaCa", + "c", + 15 + ], + [ + "CBaaaBB", + "B", + 12 + ], + [ + "CBaab", + "BaabBcba", + 10 + ], + [ + "CBaab", + "bBB", + 7 + ], + [ + "CBaabAB", + "CBCC", + 10 + ], + [ + "CBaacACCC", + "a", + 16 + ], + [ + "CBab", + "CB", + 4 + ], + [ + "CBab", + "aBcc", + 6 + ], + [ + "CBabACCaA", + "A", + 16 + ], + [ + "CBabCbbaC", + "AcbAc", + 12 + ], + [ + "CBacA", + "acBCCaCB", + 10 + ], + [ + "CBacaCBCB", + "aB", + 14 + ], + [ + "CBacbacB", + "CaBa", + 9 + ], + [ + "CBb", + "Cbb", + 1 + ], + [ + "CBb", + "a", + 6 + ], + [ + "CBb", + "bBbcc", + 6 + ], + [ + "CBb", + "bCaaaA", + 10 + ], + [ + "CBbA", + "bAaCbBC", + 10 + ], + [ + "CBbAAa", + "aabB", + 10 + ], + [ + "CBbAB", + "A", + 8 + ], + [ + "CBbAB", + "bbb", + 6 + ], + [ + "CBbACca", + "CbCAcB", + 7 + ], + [ + "CBbACcbC", + "bCcbBAb", + 12 + ], + [ + "CBbBB", + "C", + 8 + ], + [ + "CBbBB", + "CBCBcCBa", + 8 + ], + [ + "CBbBBCaBB", + "AbAca", + 13 + ], + [ + "CBbCA", + "aca", + 8 + ], + [ + "CBbCCB", + "BAbC", + 8 + ], + [ + "CBbCCCa", + "Ca", + 10 + ], + [ + "CBbCCb", + "CACbBBb", + 8 + ], + [ + "CBbCbbBc", + "ABaA", + 14 + ], + [ + "CBbaAcAAc", + "ccAACB", + 12 + ], + [ + "CBbaAca", + "bB", + 12 + ], + [ + "CBbaB", + "B", + 8 + ], + [ + "CBbaBBCaC", + "baaCcA", + 12 + ], + [ + "CBbaC", + "c", + 9 + ], + [ + "CBbaCa", + "cbbcAa", + 6 + ], + [ + "CBbaCbB", + "AcCcACbA", + 10 + ], + [ + "CBbabC", + "abB", + 8 + ], + [ + "CBbb", + "C", + 6 + ], + [ + "CBbb", + "a", + 8 + ], + [ + "CBbbbaB", + "B", + 12 + ], + [ + "CBbc", + "BaAABc", + 9 + ], + [ + "CBbcBAAB", + "cAbCAA", + 8 + ], + [ + "CBbcbb", + "cBCaC", + 8 + ], + [ + "CBc", + "Bcab", + 6 + ], + [ + "CBc", + "CBAAacc", + 8 + ], + [ + "CBc", + "abaCa", + 8 + ], + [ + "CBc", + "cAabccaaA", + 14 + ], + [ + "CBcA", + "CCA", + 3 + ], + [ + "CBcAAaccC", + "abBcAbaBC", + 10 + ], + [ + "CBcAaCCCb", + "aB", + 15 + ], + [ + "CBcAbAA", + "BabCcAAc", + 11 + ], + [ + "CBcAcB", + "CbbAAA", + 7 + ], + [ + "CBcB", + "a", + 8 + ], + [ + "CBcB", + "bcCBCCcbb", + 11 + ], + [ + "CBcBA", + "cBbCb", + 7 + ], + [ + "CBcBBABcc", + "bbBcabA", + 13 + ], + [ + "CBcBC", + "cAbCAAbcA", + 13 + ], + [ + "CBcBaaACB", + "cCc", + 14 + ], + [ + "CBcBb", + "c", + 8 + ], + [ + "CBcC", + "bCBBbBa", + 10 + ], + [ + "CBcC", + "c", + 6 + ], + [ + "CBcCC", + "aB", + 8 + ], + [ + "CBca", + "cC", + 6 + ], + [ + "CBcaAa", + "A", + 10 + ], + [ + "CBcaAba", + "AaAAaaCaA", + 13 + ], + [ + "CBcaBCc", + "AcBcb", + 9 + ], + [ + "CBcabbbba", + "bCCBc", + 15 + ], + [ + "CBcacCbCC", + "bCAaab", + 13 + ], + [ + "CBcb", + "Acb", + 4 + ], + [ + "CBcb", + "CbcABCA", + 8 + ], + [ + "CBcb", + "cBAac", + 7 + ], + [ + "CBcbCbc", + "cBBaBc", + 7 + ], + [ + "CBcbCcaBc", + "BAACAaC", + 11 + ], + [ + "CBcba", + "abCA", + 7 + ], + [ + "CBcbab", + "ABBabBC", + 9 + ], + [ + "CBcbbaC", + "ABcCa", + 8 + ], + [ + "CBcbcAAA", + "aaAbb", + 14 + ], + [ + "CBcbcbC", + "c", + 12 + ], + [ + "CBccaC", + "CAAa", + 8 + ], + [ + "CBccaCaaa", + "ccbBabcBB", + 15 + ], + [ + "CBccac", + "aB", + 10 + ], + [ + "CBccbaAbb", + "bBaacaC", + 14 + ], + [ + "CBccbbc", + "ACaAaCB", + 13 + ], + [ + "CC", + "AA", + 4 + ], + [ + "CC", + "AAaacAc", + 12 + ], + [ + "CC", + "ACACbCABB", + 14 + ], + [ + "CC", + "ACBaCAb", + 10 + ], + [ + "CC", + "ACaaB", + 8 + ], + [ + "CC", + "ACcCAaabb", + 14 + ], + [ + "CC", + "AacCBBc", + 11 + ], + [ + "CC", + "BaAACABCC", + 14 + ], + [ + "CC", + "BaBcaCbb", + 13 + ], + [ + "CC", + "Bc", + 3 + ], + [ + "CC", + "CBC", + 2 + ], + [ + "CC", + "CCAAAbab", + 12 + ], + [ + "CC", + "CCc", + 2 + ], + [ + "CC", + "Cb", + 2 + ], + [ + "CC", + "CbbBb", + 8 + ], + [ + "CC", + "CcAc", + 5 + ], + [ + "CC", + "a", + 4 + ], + [ + "CC", + "aACcC", + 6 + ], + [ + "CC", + "aBC", + 4 + ], + [ + "CC", + "aBbc", + 7 + ], + [ + "CC", + "aa", + 4 + ], + [ + "CC", + "bA", + 4 + ], + [ + "CC", + "c", + 3 + ], + [ + "CC", + "cBBB", + 7 + ], + [ + "CC", + "cBcCbBcab", + 15 + ], + [ + "CC", + "cCabBBC", + 10 + ], + [ + "CC", + "cCbBB", + 7 + ], + [ + "CCA", + "A", + 4 + ], + [ + "CCA", + "BaaabCA", + 10 + ], + [ + "CCA", + "CBaCacAB", + 10 + ], + [ + "CCA", + "aba", + 5 + ], + [ + "CCA", + "bb", + 6 + ], + [ + "CCAA", + "ABBCA", + 8 + ], + [ + "CCAA", + "BcCbBA", + 7 + ], + [ + "CCAA", + "babAabAA", + 12 + ], + [ + "CCAA", + "cAcC", + 7 + ], + [ + "CCAABbAAc", + "CA", + 14 + ], + [ + "CCAABc", + "BacACaaB", + 11 + ], + [ + "CCAACAbaa", + "BA", + 16 + ], + [ + "CCAAaB", + "CaCCcCa", + 10 + ], + [ + "CCAAbabbc", + "cc", + 15 + ], + [ + "CCAAcCCcA", + "ABcACaBc", + 13 + ], + [ + "CCAAcb", + "C", + 10 + ], + [ + "CCAB", + "bbc", + 8 + ], + [ + "CCABAAB", + "BCABbccB", + 8 + ], + [ + "CCABBCcbB", + "CabB", + 11 + ], + [ + "CCABCCAaa", + "CBCc", + 11 + ], + [ + "CCABb", + "Bcc", + 9 + ], + [ + "CCABcCBAa", + "bbBa", + 13 + ], + [ + "CCABcaC", + "caCc", + 10 + ], + [ + "CCABcbBaC", + "cAAaAAaC", + 11 + ], + [ + "CCAC", + "aBAccBA", + 11 + ], + [ + "CCACb", + "BABcBc", + 10 + ], + [ + "CCACbACc", + "Aabbbac", + 11 + ], + [ + "CCACcaCB", + "ABbCACbB", + 11 + ], + [ + "CCAa", + "aACCbaaac", + 11 + ], + [ + "CCAb", + "ACCBba", + 6 + ], + [ + "CCAbBcABA", + "cAaCBc", + 12 + ], + [ + "CCAbBcb", + "baCB", + 10 + ], + [ + "CCAbCAb", + "BaCCaa", + 10 + ], + [ + "CCAbaC", + "BBbcaa", + 10 + ], + [ + "CCAbaCBa", + "bcccA", + 12 + ], + [ + "CCAcAB", + "ccAAbCa", + 9 + ], + [ + "CCAcACA", + "acBA", + 9 + ], + [ + "CCAcBAa", + "aCacBaBAA", + 8 + ], + [ + "CCAcC", + "BbcBbC", + 9 + ], + [ + "CCAcCB", + "cBcaBBBA", + 11 + ], + [ + "CCAcaC", + "CcaB", + 6 + ], + [ + "CCAcaaca", + "cC", + 13 + ], + [ + "CCAcacccc", + "cAcabcB", + 9 + ], + [ + "CCAcbCb", + "ACCa", + 9 + ], + [ + "CCAcbbCBc", + "CcAbBCa", + 8 + ], + [ + "CCB", + "ACbaAACcB", + 12 + ], + [ + "CCB", + "Cc", + 3 + ], + [ + "CCB", + "abaAB", + 8 + ], + [ + "CCB", + "baCac", + 8 + ], + [ + "CCBA", + "Aaaaa", + 9 + ], + [ + "CCBAA", + "a", + 9 + ], + [ + "CCBABcc", + "BCABb", + 8 + ], + [ + "CCBAC", + "bCCcaCAc", + 9 + ], + [ + "CCBAbAC", + "bCA", + 10 + ], + [ + "CCBAc", + "BC", + 7 + ], + [ + "CCBAcaacb", + "aCCAA", + 13 + ], + [ + "CCBBCBccb", + "BBAa", + 14 + ], + [ + "CCBBbB", + "CbcAACa", + 11 + ], + [ + "CCBBccC", + "c", + 12 + ], + [ + "CCBC", + "CBCaABac", + 9 + ], + [ + "CCBCAaAA", + "CaC", + 12 + ], + [ + "CCBCBCcc", + "bcCccbAbb", + 13 + ], + [ + "CCBCb", + "c", + 9 + ], + [ + "CCBaAaB", + "bACAC", + 11 + ], + [ + "CCBaAaB", + "cbcCBca", + 11 + ], + [ + "CCBaAcba", + "BaaaCAA", + 11 + ], + [ + "CCBaBbbBC", + "a", + 16 + ], + [ + "CCBaCcAa", + "ccBbACCb", + 10 + ], + [ + "CCBbAAAB", + "CCcaBabc", + 11 + ], + [ + "CCBbACc", + "bbbaacBb", + 12 + ], + [ + "CCBbB", + "cBBBACC", + 10 + ], + [ + "CCBbBC", + "bAcCCab", + 12 + ], + [ + "CCBbabCB", + "bCcaB", + 10 + ], + [ + "CCBbca", + "bc", + 8 + ], + [ + "CCBcB", + "ACbbCa", + 8 + ], + [ + "CCBca", + "BacCaaAA", + 12 + ], + [ + "CCBcbbC", + "cB", + 11 + ], + [ + "CCC", + "CcaB", + 5 + ], + [ + "CCC", + "baaaC", + 8 + ], + [ + "CCCAABC", + "CC", + 10 + ], + [ + "CCCAbbBCb", + "cBBaaBab", + 12 + ], + [ + "CCCAc", + "BaccCaB", + 9 + ], + [ + "CCCBAbcc", + "accAb", + 10 + ], + [ + "CCCBBcab", + "ACCA", + 11 + ], + [ + "CCCBCaAcC", + "aaCcAbAa", + 14 + ], + [ + "CCCBbbbC", + "aCb", + 12 + ], + [ + "CCCBcBBaa", + "aBACAbAA", + 14 + ], + [ + "CCCBccaB", + "ccBcc", + 8 + ], + [ + "CCCC", + "bBcAaAbaC", + 15 + ], + [ + "CCCCA", + "BBACa", + 7 + ], + [ + "CCCCA", + "bAcAbbC", + 13 + ], + [ + "CCCCA", + "cccAC", + 7 + ], + [ + "CCCCAbaa", + "CaBaccBB", + 14 + ], + [ + "CCCCBC", + "ABbBc", + 9 + ], + [ + "CCCCCa", + "BCACA", + 7 + ], + [ + "CCCCaBCA", + "Bacbcab", + 13 + ], + [ + "CCCCbaC", + "b", + 12 + ], + [ + "CCCCbbba", + "BAbcAbcBb", + 14 + ], + [ + "CCCCcA", + "bbCCbbc", + 10 + ], + [ + "CCCCcBAC", + "acACBcC", + 9 + ], + [ + "CCCa", + "bAb", + 8 + ], + [ + "CCCaABacb", + "bacBBcAA", + 14 + ], + [ + "CCCaCcA", + "bAcAC", + 10 + ], + [ + "CCCaCcbBA", + "bAbc", + 15 + ], + [ + "CCCaaCaBA", + "AbAbBCb", + 15 + ], + [ + "CCCabaaab", + "cAbca", + 12 + ], + [ + "CCCacacb", + "CAcCBcA", + 10 + ], + [ + "CCCb", + "C", + 6 + ], + [ + "CCCbBBB", + "A", + 14 + ], + [ + "CCCba", + "BbAcaC", + 10 + ], + [ + "CCCbbAA", + "a", + 13 + ], + [ + "CCCbbaA", + "cCB", + 10 + ], + [ + "CCCbbcc", + "BCA", + 12 + ], + [ + "CCCc", + "CBbBBaBBa", + 16 + ], + [ + "CCCcBBba", + "CcBbcAa", + 9 + ], + [ + "CCCcBaA", + "CCCBAAAcA", + 9 + ], + [ + "CCa", + "Ca", + 2 + ], + [ + "CCa", + "CbabbAAcA", + 14 + ], + [ + "CCa", + "bbbCBA", + 9 + ], + [ + "CCa", + "cCAcbAbCC", + 14 + ], + [ + "CCaA", + "c", + 7 + ], + [ + "CCaAA", + "BBACcB", + 11 + ], + [ + "CCaAAb", + "cBa", + 9 + ], + [ + "CCaABbBaA", + "CcACab", + 11 + ], + [ + "CCaAcb", + "ABbBCaCa", + 13 + ], + [ + "CCaBAcbAC", + "CBbc", + 11 + ], + [ + "CCaBBA", + "bAbBB", + 8 + ], + [ + "CCaCAAbA", + "AA", + 12 + ], + [ + "CCaCACBcb", + "caC", + 13 + ], + [ + "CCaCAaA", + "BCAaCaa", + 7 + ], + [ + "CCaCB", + "CAAA", + 7 + ], + [ + "CCaCBcAC", + "bcCa", + 12 + ], + [ + "CCaCCC", + "CACcBa", + 8 + ], + [ + "CCaCaCc", + "BcCba", + 11 + ], + [ + "CCaCaCcBA", + "a", + 16 + ], + [ + "CCaCabaAa", + "CcccAacAC", + 11 + ], + [ + "CCaCb", + "bAcaCbCAb", + 11 + ], + [ + "CCaCbaaaC", + "CaCBCBBc", + 10 + ], + [ + "CCaaBbbA", + "BAAc", + 14 + ], + [ + "CCaaCbCab", + "aBbB", + 13 + ], + [ + "CCaaaaccC", + "AABcca", + 12 + ], + [ + "CCaacBa", + "a", + 12 + ], + [ + "CCabA", + "acbCAAbb", + 10 + ], + [ + "CCabA", + "bbaacCA", + 10 + ], + [ + "CCabCAac", + "ABABA", + 12 + ], + [ + "CCabcCbc", + "BCbc", + 9 + ], + [ + "CCac", + "a", + 6 + ], + [ + "CCacAac", + "bbBbaC", + 11 + ], + [ + "CCacAbCcb", + "bCbBa", + 14 + ], + [ + "CCacBca", + "C", + 12 + ], + [ + "CCacCb", + "cBAC", + 8 + ], + [ + "CCacab", + "Aa", + 9 + ], + [ + "CCacbb", + "CAb", + 7 + ], + [ + "CCacbcB", + "Cca", + 9 + ], + [ + "CCb", + "BAbBAaB", + 12 + ], + [ + "CCb", + "CAAAbca", + 10 + ], + [ + "CCb", + "bAc", + 6 + ], + [ + "CCb", + "bcabBaB", + 11 + ], + [ + "CCb", + "cbCcaAaac", + 15 + ], + [ + "CCbA", + "AABAC", + 7 + ], + [ + "CCbA", + "BbBCabCaa", + 13 + ], + [ + "CCbAAa", + "BcABbC", + 11 + ], + [ + "CCbABCC", + "a", + 13 + ], + [ + "CCbACCBa", + "BbbaBbC", + 12 + ], + [ + "CCbAaaA", + "aB", + 12 + ], + [ + "CCbAbC", + "cAacBCa", + 10 + ], + [ + "CCbAbbBB", + "abcaA", + 13 + ], + [ + "CCbB", + "Aa", + 8 + ], + [ + "CCbB", + "cbaBCBBA", + 10 + ], + [ + "CCbBBB", + "CACCacCBc", + 12 + ], + [ + "CCbBCC", + "BbCAbcAA", + 12 + ], + [ + "CCbBbaBa", + "BcbccCAc", + 13 + ], + [ + "CCbCBCbB", + "cCbbB", + 7 + ], + [ + "CCbCbA", + "ABACcA", + 8 + ], + [ + "CCbCbcacC", + "bBC", + 13 + ], + [ + "CCba", + "aBaAcAaCC", + 15 + ], + [ + "CCba", + "caCAcc", + 9 + ], + [ + "CCba", + "cbaCcba", + 7 + ], + [ + "CCbaB", + "Ba", + 7 + ], + [ + "CCbaBB", + "CCaccA", + 8 + ], + [ + "CCbaCC", + "aBbcaa", + 10 + ], + [ + "CCbaaBcAB", + "a", + 16 + ], + [ + "CCbaba", + "ACbaBAAb", + 8 + ], + [ + "CCbb", + "aBbacACBa", + 14 + ], + [ + "CCbbACcA", + "CBabaAA", + 9 + ], + [ + "CCbbAabCa", + "cAABb", + 13 + ], + [ + "CCbbBabb", + "aBBcc", + 13 + ], + [ + "CCbbaBC", + "cBC", + 9 + ], + [ + "CCbbca", + "bAC", + 9 + ], + [ + "CCbbcaCBA", + "CCaAaBBAB", + 10 + ], + [ + "CCbcAac", + "a", + 12 + ], + [ + "CCbcBC", + "aabbAc", + 9 + ], + [ + "CCbcBca", + "B", + 12 + ], + [ + "CCbcCAc", + "Bb", + 12 + ], + [ + "CCbcb", + "cBaC", + 8 + ], + [ + "CCbcc", + "aBaccBcB", + 11 + ], + [ + "CCc", + "A", + 6 + ], + [ + "CCc", + "Ac", + 4 + ], + [ + "CCc", + "Bc", + 4 + ], + [ + "CCc", + "Cacbb", + 6 + ], + [ + "CCc", + "bCcbAbbAB", + 14 + ], + [ + "CCcAC", + "CABCA", + 7 + ], + [ + "CCcACBCC", + "B", + 14 + ], + [ + "CCcACc", + "cACCCA", + 8 + ], + [ + "CCcAaaba", + "CCCBcABca", + 9 + ], + [ + "CCcAc", + "abaaaCCc", + 13 + ], + [ + "CCcB", + "aCAaBc", + 8 + ], + [ + "CCcBAB", + "CacAbbcbB", + 11 + ], + [ + "CCcBAcC", + "bB", + 12 + ], + [ + "CCcBbAb", + "aBbC", + 10 + ], + [ + "CCcBcaB", + "cBbb", + 9 + ], + [ + "CCcCaaAC", + "CAAbccC", + 12 + ], + [ + "CCcCb", + "B", + 9 + ], + [ + "CCcCbacba", + "AAa", + 15 + ], + [ + "CCcCcABB", + "cAcBabacb", + 13 + ], + [ + "CCcCcccAB", + "BAaACCCAB", + 11 + ], + [ + "CCca", + "Acc", + 5 + ], + [ + "CCca", + "a", + 6 + ], + [ + "CCcaBCabB", + "cAabc", + 11 + ], + [ + "CCcaC", + "bbCACAA", + 10 + ], + [ + "CCcaCca", + "CabacBb", + 9 + ], + [ + "CCcaaBaB", + "ccaAabb", + 9 + ], + [ + "CCcaaabbc", + "BCaCC", + 13 + ], + [ + "CCcabA", + "bCCAAcaBC", + 9 + ], + [ + "CCcacbB", + "Babb", + 9 + ], + [ + "CCcb", + "C", + 6 + ], + [ + "CCcbB", + "abB", + 6 + ], + [ + "CCcbBBC", + "aAAaCCCc", + 14 + ], + [ + "CCcbC", + "ACcBBBBc", + 10 + ], + [ + "CCcbC", + "a", + 10 + ], + [ + "CCcbCACab", + "Accba", + 11 + ], + [ + "CCcbcaaaA", + "ccaaAbaaB", + 12 + ], + [ + "CCccAAB", + "CCaaa", + 8 + ], + [ + "CCccB", + "AACBAbC", + 11 + ], + [ + "CCcca", + "baAaCccaa", + 10 + ], + [ + "CCccaAA", + "b", + 14 + ], + [ + "CCccbb", + "babC", + 10 + ], + [ + "Ca", + "ABCCb", + 8 + ], + [ + "Ca", + "AaCcCBaab", + 14 + ], + [ + "Ca", + "Aac", + 4 + ], + [ + "Ca", + "BAAABca", + 11 + ], + [ + "Ca", + "BACaCCb", + 10 + ], + [ + "Ca", + "BAc", + 5 + ], + [ + "Ca", + "BBAA", + 7 + ], + [ + "Ca", + "BCB", + 4 + ], + [ + "Ca", + "BCBbBaAC", + 12 + ], + [ + "Ca", + "BaCCAcBAa", + 14 + ], + [ + "Ca", + "BabB", + 6 + ], + [ + "Ca", + "BababbBb", + 14 + ], + [ + "Ca", + "BbCB", + 6 + ], + [ + "Ca", + "Bbb", + 6 + ], + [ + "Ca", + "CAbc", + 5 + ], + [ + "Ca", + "CBAbac", + 8 + ], + [ + "Ca", + "CBB", + 4 + ], + [ + "Ca", + "CBaA", + 4 + ], + [ + "Ca", + "CCAB", + 5 + ], + [ + "Ca", + "CCaCBaaA", + 12 + ], + [ + "Ca", + "CCbaAA", + 8 + ], + [ + "Ca", + "CCbb", + 6 + ], + [ + "Ca", + "Cb", + 2 + ], + [ + "Ca", + "aACCba", + 8 + ], + [ + "Ca", + "aB", + 4 + ], + [ + "Ca", + "aBBA", + 7 + ], + [ + "Ca", + "aac", + 4 + ], + [ + "Ca", + "acBacBcAA", + 15 + ], + [ + "Ca", + "b", + 4 + ], + [ + "Ca", + "bAbBbbAa", + 14 + ], + [ + "Ca", + "bBCA", + 5 + ], + [ + "Ca", + "bbBAAAa", + 12 + ], + [ + "Ca", + "c", + 3 + ], + [ + "Ca", + "cCBCBbAAa", + 14 + ], + [ + "Ca", + "caBabaBc", + 13 + ], + [ + "Ca", + "ccCaCCC", + 10 + ], + [ + "CaA", + "BcBcAb", + 9 + ], + [ + "CaA", + "CCBBA", + 6 + ], + [ + "CaA", + "aBa", + 5 + ], + [ + "CaA", + "aBcbcb", + 11 + ], + [ + "CaA", + "bBcCBcbcc", + 16 + ], + [ + "CaA", + "bCABC", + 7 + ], + [ + "CaA", + "bCcBaAaA", + 10 + ], + [ + "CaA", + "c", + 5 + ], + [ + "CaAA", + "bacCbbA", + 10 + ], + [ + "CaAABbc", + "c", + 12 + ], + [ + "CaAAC", + "c", + 9 + ], + [ + "CaAACB", + "bB", + 10 + ], + [ + "CaAAa", + "AcCbA", + 9 + ], + [ + "CaAAbcCa", + "a", + 14 + ], + [ + "CaAB", + "BCaCaCB", + 7 + ], + [ + "CaAB", + "Ba", + 6 + ], + [ + "CaAB", + "bCb", + 7 + ], + [ + "CaABBc", + "b", + 11 + ], + [ + "CaABCBA", + "bBb", + 11 + ], + [ + "CaABa", + "B", + 8 + ], + [ + "CaABbACB", + "BA", + 12 + ], + [ + "CaABbC", + "AAacAc", + 9 + ], + [ + "CaAC", + "BBCbCcaaC", + 11 + ], + [ + "CaAC", + "aCAABab", + 9 + ], + [ + "CaAC", + "acCBbcbA", + 13 + ], + [ + "CaACB", + "B", + 8 + ], + [ + "CaACbacC", + "BAbabAAB", + 12 + ], + [ + "CaACbc", + "CBA", + 8 + ], + [ + "CaACcBACc", + "aAcc", + 10 + ], + [ + "CaAa", + "Bababa", + 7 + ], + [ + "CaAa", + "ccAC", + 5 + ], + [ + "CaAaAC", + "ACa", + 9 + ], + [ + "CaAaACCc", + "BaabbBCbC", + 12 + ], + [ + "CaAaBbB", + "cbAb", + 9 + ], + [ + "CaAaa", + "cAAACBa", + 7 + ], + [ + "CaAab", + "BBbcbaa", + 12 + ], + [ + "CaAabcABB", + "ACcBacC", + 14 + ], + [ + "CaAbC", + "bC", + 6 + ], + [ + "CaAbbBa", + "c", + 13 + ], + [ + "CaAc", + "Ac", + 4 + ], + [ + "CaAc", + "abB", + 6 + ], + [ + "CaAcCAb", + "cABCaAA", + 9 + ], + [ + "CaAcCa", + "bBabcCCC", + 10 + ], + [ + "CaAca", + "bcAcBAABA", + 13 + ], + [ + "CaAcb", + "aBbACB", + 8 + ], + [ + "CaAcc", + "ACc", + 5 + ], + [ + "CaB", + "B", + 4 + ], + [ + "CaB", + "C", + 4 + ], + [ + "CaB", + "acbCAcC", + 11 + ], + [ + "CaB", + "bbAaAaBB", + 12 + ], + [ + "CaB", + "cabbb", + 6 + ], + [ + "CaBA", + "cbcAB", + 7 + ], + [ + "CaBAAB", + "BBAC", + 8 + ], + [ + "CaBAAB", + "a", + 10 + ], + [ + "CaBAAaBB", + "AACB", + 10 + ], + [ + "CaBAbCb", + "baA", + 10 + ], + [ + "CaBAcBCcb", + "A", + 16 + ], + [ + "CaBAcc", + "AACCaA", + 11 + ], + [ + "CaBAcc", + "bAcBbA", + 11 + ], + [ + "CaBB", + "aCCBbcaba", + 13 + ], + [ + "CaBB", + "babABcb", + 9 + ], + [ + "CaBBBa", + "Cc", + 10 + ], + [ + "CaBBBbBB", + "CAAaC", + 13 + ], + [ + "CaBBCccC", + "bAac", + 13 + ], + [ + "CaBBaA", + "a", + 10 + ], + [ + "CaBBabBcc", + "b", + 16 + ], + [ + "CaBBcC", + "BA", + 10 + ], + [ + "CaBBccac", + "accCBA", + 11 + ], + [ + "CaBC", + "CaaacC", + 6 + ], + [ + "CaBCACabA", + "CbcABbc", + 10 + ], + [ + "CaBCBbC", + "ccbBbaC", + 8 + ], + [ + "CaBCCcaCc", + "CaC", + 12 + ], + [ + "CaBaaBcA", + "C", + 14 + ], + [ + "CaBacBc", + "c", + 12 + ], + [ + "CaBba", + "bbcCaAa", + 10 + ], + [ + "CaBbac", + "A", + 11 + ], + [ + "CaBcAc", + "CAAab", + 8 + ], + [ + "CaBcBAbB", + "BABCCca", + 12 + ], + [ + "CaBcaA", + "bcC", + 9 + ], + [ + "CaBccaAc", + "bBa", + 12 + ], + [ + "CaC", + "aC", + 2 + ], + [ + "CaCA", + "bcBA", + 6 + ], + [ + "CaCAAbCcB", + "aBCc", + 11 + ], + [ + "CaCABCcbA", + "bB", + 16 + ], + [ + "CaCACCaA", + "BCBBaaBCA", + 13 + ], + [ + "CaCAbBA", + "bCBb", + 10 + ], + [ + "CaCB", + "AB", + 5 + ], + [ + "CaCB", + "bAb", + 6 + ], + [ + "CaCBa", + "ACabCC", + 8 + ], + [ + "CaCBa", + "abBaa", + 6 + ], + [ + "CaCBaaC", + "bcACBa", + 8 + ], + [ + "CaCBbBBb", + "bab", + 12 + ], + [ + "CaCBc", + "cAC", + 6 + ], + [ + "CaCCbB", + "babA", + 8 + ], + [ + "CaCCbc", + "BAbcCcBB", + 11 + ], + [ + "CaCa", + "Aab", + 6 + ], + [ + "CaCa", + "CCccAab", + 9 + ], + [ + "CaCa", + "CcbcAAC", + 10 + ], + [ + "CaCa", + "abCCa", + 6 + ], + [ + "CaCaAB", + "aBb", + 9 + ], + [ + "CaCb", + "bCab", + 4 + ], + [ + "CaCbBcCCC", + "CaBBab", + 11 + ], + [ + "CaCbbCAcb", + "aABcAc", + 10 + ], + [ + "CaCbbaB", + "ABB", + 10 + ], + [ + "CaCc", + "A", + 7 + ], + [ + "CaCc", + "ccBbc", + 7 + ], + [ + "CaCcAAb", + "baCBCB", + 9 + ], + [ + "CaCcB", + "AA", + 9 + ], + [ + "CaCcBa", + "cAc", + 8 + ], + [ + "CaCcaa", + "CABabbB", + 11 + ], + [ + "CaCcbBA", + "CAbccCaa", + 9 + ], + [ + "Caa", + "Acc", + 6 + ], + [ + "Caa", + "BAcBcBCbb", + 16 + ], + [ + "Caa", + "abbAb", + 9 + ], + [ + "CaaABcBab", + "acACccaCa", + 12 + ], + [ + "CaaAaAa", + "bbaabAc", + 9 + ], + [ + "CaaAaAaba", + "aaCBc", + 13 + ], + [ + "CaaAbCcAb", + "bc", + 14 + ], + [ + "CaaBAab", + "CcC", + 12 + ], + [ + "CaaBcB", + "Caaa", + 6 + ], + [ + "CaaC", + "AAbBaC", + 7 + ], + [ + "CaaCA", + "A", + 8 + ], + [ + "CaaCbCB", + "c", + 13 + ], + [ + "Caaa", + "BBaBcA", + 9 + ], + [ + "CaaaCabaC", + "CAa", + 13 + ], + [ + "Caaab", + "a", + 8 + ], + [ + "CaaabAbc", + "ACc", + 12 + ], + [ + "CaaabCbB", + "ABcB", + 11 + ], + [ + "Caaabcc", + "babAaAC", + 10 + ], + [ + "Caaac", + "AAbbccabb", + 15 + ], + [ + "Caab", + "BacAc", + 7 + ], + [ + "CaacAaCb", + "AbccbacB", + 10 + ], + [ + "CaacCb", + "acbcCABBB", + 13 + ], + [ + "Caacaccbc", + "bCcbB", + 13 + ], + [ + "CaacccACC", + "baBCAaCA", + 12 + ], + [ + "Cab", + "AbBCbcBbc", + 14 + ], + [ + "Cab", + "AcC", + 6 + ], + [ + "Cab", + "BCA", + 5 + ], + [ + "Cab", + "BaB", + 3 + ], + [ + "Cab", + "c", + 5 + ], + [ + "CabA", + "b", + 6 + ], + [ + "CabAabA", + "cbCaBBb", + 10 + ], + [ + "CabAbcCB", + "acbCa", + 10 + ], + [ + "CabB", + "AACcCBcab", + 14 + ], + [ + "CabB", + "BabB", + 2 + ], + [ + "CabB", + "Ccba", + 4 + ], + [ + "CabBCa", + "AaCaab", + 10 + ], + [ + "CabBacbC", + "C", + 14 + ], + [ + "CabBbab", + "Bc", + 12 + ], + [ + "CabBbc", + "BbBCbcBca", + 12 + ], + [ + "CabBcC", + "AaAcCAC", + 9 + ], + [ + "CabCB", + "AaCBCab", + 8 + ], + [ + "CabCaAABb", + "BbaB", + 12 + ], + [ + "CabCb", + "BcCbacbb", + 10 + ], + [ + "Caba", + "AA", + 6 + ], + [ + "Caba", + "CBbBBbCC", + 12 + ], + [ + "CabaAAcbc", + "aA", + 14 + ], + [ + "CabaAaab", + "Bccb", + 13 + ], + [ + "CabaBCc", + "acBcBb", + 11 + ], + [ + "CabaCAC", + "bAaa", + 10 + ], + [ + "CababAAcB", + "a", + 16 + ], + [ + "Cabb", + "aBbABaA", + 11 + ], + [ + "CabbAAABB", + "ccA", + 15 + ], + [ + "CabbACc", + "ABCCca", + 10 + ], + [ + "CabbB", + "BabCaAC", + 10 + ], + [ + "CabbB", + "CbBbAccbc", + 12 + ], + [ + "CabbCaa", + "bbaBbcacC", + 10 + ], + [ + "CabbaAba", + "CABaCBAbA", + 9 + ], + [ + "CabbbAACB", + "Babac", + 12 + ], + [ + "CabcB", + "aBAbaA", + 9 + ], + [ + "CabcBaC", + "Cac", + 8 + ], + [ + "CabcaABb", + "cBBc", + 12 + ], + [ + "CabcaBcB", + "bAABcbCA", + 12 + ], + [ + "Cabcb", + "Ba", + 8 + ], + [ + "Cabccb", + "BBBA", + 11 + ], + [ + "Cac", + "A", + 5 + ], + [ + "Cac", + "BAcb", + 5 + ], + [ + "Cac", + "BCcCCac", + 8 + ], + [ + "Cac", + "Bc", + 4 + ], + [ + "Cac", + "CBaacCcCb", + 12 + ], + [ + "Cac", + "cC", + 4 + ], + [ + "Cac", + "cbACbC", + 9 + ], + [ + "CacA", + "BaBabBAA", + 12 + ], + [ + "CacA", + "CacbC", + 4 + ], + [ + "CacAb", + "acCAb", + 4 + ], + [ + "CacAcAa", + "ACbcC", + 10 + ], + [ + "CacAcBc", + "caABbAAAA", + 13 + ], + [ + "CacBBBC", + "B", + 12 + ], + [ + "CacBCa", + "CcbcBA", + 7 + ], + [ + "CacBaCbca", + "aaBaaaBc", + 11 + ], + [ + "CacBbbB", + "CaCbbaBbb", + 7 + ], + [ + "CacBc", + "bBbAC", + 9 + ], + [ + "CacC", + "AcC", + 3 + ], + [ + "CacCbaAcc", + "bCaAaCB", + 13 + ], + [ + "CacaAc", + "aAbC", + 8 + ], + [ + "Cacab", + "AaBCAa", + 8 + ], + [ + "Cacab", + "c", + 8 + ], + [ + "CacacC", + "AABCCAA", + 12 + ], + [ + "Cacb", + "ac", + 4 + ], + [ + "CacbB", + "BCbcBaa", + 9 + ], + [ + "CacbC", + "bBbCaBBB", + 11 + ], + [ + "CacbC", + "cCAAcCa", + 9 + ], + [ + "CacbCAccB", + "AAaBBcbBc", + 14 + ], + [ + "Cacc", + "AACc", + 4 + ], + [ + "CaccAC", + "BabBAaC", + 8 + ], + [ + "CaccBca", + "aCbb", + 10 + ], + [ + "CaccaAbbC", + "BBbC", + 13 + ], + [ + "CaccaCCa", + "cc", + 12 + ], + [ + "Cb", + "A", + 4 + ], + [ + "Cb", + "AACB", + 5 + ], + [ + "Cb", + "AACCbcBc", + 12 + ], + [ + "Cb", + "ABcAcaCcC", + 16 + ], + [ + "Cb", + "ACCBAaCaA", + 15 + ], + [ + "Cb", + "AaBCBCc", + 11 + ], + [ + "Cb", + "Aaa", + 6 + ], + [ + "Cb", + "AacabCcB", + 13 + ], + [ + "Cb", + "Ac", + 4 + ], + [ + "Cb", + "BABB", + 7 + ], + [ + "Cb", + "BBcAabC", + 11 + ], + [ + "Cb", + "Ba", + 4 + ], + [ + "Cb", + "BaaB", + 7 + ], + [ + "Cb", + "BbCaabCCC", + 14 + ], + [ + "Cb", + "CA", + 2 + ], + [ + "Cb", + "CCcBaAB", + 11 + ], + [ + "Cb", + "Cbb", + 2 + ], + [ + "Cb", + "CcBccBacC", + 15 + ], + [ + "Cb", + "aBcaCb", + 8 + ], + [ + "Cb", + "aCBCcAACA", + 15 + ], + [ + "Cb", + "aaAbc", + 8 + ], + [ + "Cb", + "aaCBBaB", + 11 + ], + [ + "Cb", + "aaaB", + 7 + ], + [ + "Cb", + "abBaC", + 8 + ], + [ + "Cb", + "abaaaCABA", + 15 + ], + [ + "Cb", + "acBbAAca", + 13 + ], + [ + "Cb", + "acbbabbCC", + 15 + ], + [ + "Cb", + "bcCCBA", + 9 + ], + [ + "Cb", + "c", + 3 + ], + [ + "Cb", + "cAbcbCA", + 11 + ], + [ + "Cb", + "cAcCbBA", + 10 + ], + [ + "Cb", + "cB", + 2 + ], + [ + "Cb", + "cBcB", + 6 + ], + [ + "Cb", + "caacb", + 7 + ], + [ + "Cb", + "cbCBBbacA", + 14 + ], + [ + "Cb", + "cbcbccA", + 11 + ], + [ + "CbA", + "AAC", + 6 + ], + [ + "CbA", + "BBCBaCC", + 10 + ], + [ + "CbA", + "BBcBB", + 8 + ], + [ + "CbA", + "baaA", + 6 + ], + [ + "CbA", + "c", + 5 + ], + [ + "CbA", + "caBAbA", + 7 + ], + [ + "CbAABaAC", + "A", + 14 + ], + [ + "CbAACACcC", + "ba", + 15 + ], + [ + "CbAACa", + "AaCaAccab", + 11 + ], + [ + "CbAAaAB", + "BCaBCcaaC", + 12 + ], + [ + "CbAAb", + "bacAabaAB", + 11 + ], + [ + "CbAAcAb", + "caC", + 11 + ], + [ + "CbAAcC", + "ABbcaCa", + 10 + ], + [ + "CbABACcaB", + "Babb", + 14 + ], + [ + "CbABC", + "b", + 8 + ], + [ + "CbABaBaAC", + "acaC", + 12 + ], + [ + "CbAC", + "A", + 6 + ], + [ + "CbAC", + "c", + 7 + ], + [ + "CbACaB", + "aBcA", + 9 + ], + [ + "CbACb", + "bcABcCC", + 10 + ], + [ + "CbACc", + "BbabcaBCA", + 13 + ], + [ + "CbACcABba", + "baABAcBc", + 12 + ], + [ + "CbAaAaaa", + "ACbBACbbc", + 13 + ], + [ + "CbAaabbC", + "A", + 14 + ], + [ + "CbAacB", + "c", + 10 + ], + [ + "CbAacBB", + "CacbBBbBA", + 12 + ], + [ + "CbAb", + "baBac", + 8 + ], + [ + "CbAbBaC", + "aCAaACb", + 11 + ], + [ + "CbAba", + "CbaA", + 4 + ], + [ + "CbAbb", + "Bc", + 9 + ], + [ + "CbAc", + "a", + 7 + ], + [ + "CbAcAC", + "bcbCB", + 8 + ], + [ + "CbAcB", + "aac", + 7 + ], + [ + "CbAcBCcaa", + "ccABcacAc", + 11 + ], + [ + "CbAcCb", + "AC", + 8 + ], + [ + "CbAcaAAA", + "accCbbACC", + 15 + ], + [ + "CbAcc", + "AABbcBBc", + 11 + ], + [ + "CbB", + "ABcCCBb", + 10 + ], + [ + "CbB", + "B", + 4 + ], + [ + "CbB", + "BB", + 3 + ], + [ + "CbB", + "BBb", + 4 + ], + [ + "CbB", + "ababacC", + 11 + ], + [ + "CbB", + "bAaaABc", + 12 + ], + [ + "CbB", + "cCb", + 4 + ], + [ + "CbBABACb", + "BCcBAA", + 10 + ], + [ + "CbBABBaA", + "BacaabAc", + 13 + ], + [ + "CbBAb", + "cB", + 7 + ], + [ + "CbBB", + "CcBCBCcAC", + 12 + ], + [ + "CbBBB", + "BCCcb", + 9 + ], + [ + "CbBBaC", + "ab", + 10 + ], + [ + "CbBBcCCBb", + "B", + 16 + ], + [ + "CbBCCbbB", + "cC", + 13 + ], + [ + "CbBa", + "a", + 6 + ], + [ + "CbBa", + "aAb", + 7 + ], + [ + "CbBabA", + "CcCB", + 9 + ], + [ + "CbBb", + "aBccCBA", + 11 + ], + [ + "CbBbC", + "CCCbbC", + 5 + ], + [ + "CbBbbba", + "BaCCABCcC", + 15 + ], + [ + "CbBbc", + "C", + 8 + ], + [ + "CbBc", + "aAaAAbBA", + 12 + ], + [ + "CbBcB", + "b", + 8 + ], + [ + "CbC", + "AcacbAB", + 11 + ], + [ + "CbC", + "BCAc", + 5 + ], + [ + "CbC", + "Bc", + 4 + ], + [ + "CbC", + "CbB", + 2 + ], + [ + "CbC", + "b", + 4 + ], + [ + "CbCAAbb", + "bAAaC", + 8 + ], + [ + "CbCABcaC", + "aCcAACB", + 12 + ], + [ + "CbCACA", + "baAac", + 8 + ], + [ + "CbCAa", + "cCbabBb", + 10 + ], + [ + "CbCB", + "BAcbcAba", + 11 + ], + [ + "CbCBBb", + "cAbACcA", + 11 + ], + [ + "CbCBC", + "cacCccCb", + 11 + ], + [ + "CbCBCAB", + "bBBcC", + 9 + ], + [ + "CbCBcCAaa", + "aAAaAa", + 14 + ], + [ + "CbCCCc", + "BcBCaba", + 10 + ], + [ + "CbCCbAccA", + "cCbBB", + 13 + ], + [ + "CbCCcCCaB", + "bcaaab", + 11 + ], + [ + "CbCa", + "cBBaCacA", + 10 + ], + [ + "CbCaAaB", + "acaa", + 9 + ], + [ + "CbCaB", + "ACbaa", + 6 + ], + [ + "CbCaB", + "AcbAa", + 7 + ], + [ + "CbCaBCb", + "bcA", + 10 + ], + [ + "CbCaa", + "ABCBCcAb", + 10 + ], + [ + "CbCaa", + "ba", + 6 + ], + [ + "CbCaaaC", + "bAcBcBaAa", + 12 + ], + [ + "CbCb", + "Bbc", + 5 + ], + [ + "CbCbaBa", + "AbacBbabc", + 10 + ], + [ + "CbCbcC", + "Ca", + 10 + ], + [ + "CbCc", + "ACC", + 5 + ], + [ + "CbCc", + "Cabbb", + 6 + ], + [ + "CbCcAaBaA", + "cbC", + 13 + ], + [ + "CbCcAcAc", + "AcBaCaAC", + 11 + ], + [ + "CbCcBAb", + "c", + 12 + ], + [ + "CbCcBC", + "CacBbCAc", + 10 + ], + [ + "CbCcBac", + "AAbAC", + 11 + ], + [ + "Cba", + "ABBBbaab", + 12 + ], + [ + "Cba", + "AcbAbB", + 8 + ], + [ + "Cba", + "C", + 4 + ], + [ + "Cba", + "CCCbbbC", + 10 + ], + [ + "Cba", + "CCaaAa", + 8 + ], + [ + "Cba", + "CcBCCAAAA", + 14 + ], + [ + "Cba", + "aABbaC", + 8 + ], + [ + "CbaA", + "CAbCBaB", + 8 + ], + [ + "CbaA", + "CBb", + 5 + ], + [ + "CbaABAC", + "aBCBCBc", + 12 + ], + [ + "CbaAaBCaB", + "AcCcB", + 12 + ], + [ + "CbaAcBb", + "C", + 12 + ], + [ + "CbaAcc", + "acaCaaccA", + 10 + ], + [ + "CbaBA", + "ab", + 7 + ], + [ + "CbaBBcBaB", + "bB", + 14 + ], + [ + "CbaBa", + "CBC", + 6 + ], + [ + "CbaBb", + "CCCbcB", + 8 + ], + [ + "CbaBba", + "BC", + 10 + ], + [ + "CbaBcAcbC", + "cc", + 14 + ], + [ + "CbaC", + "B", + 7 + ], + [ + "CbaCA", + "BAbaAacA", + 9 + ], + [ + "CbaCABBb", + "cAB", + 11 + ], + [ + "CbaCABcbA", + "BbaCbC", + 10 + ], + [ + "CbaCAaCc", + "a", + 14 + ], + [ + "CbaCAcCba", + "BCcaB", + 12 + ], + [ + "CbaCbCA", + "ccBBAcBc", + 12 + ], + [ + "CbaaA", + "BA", + 7 + ], + [ + "CbaaBA", + "AcccAAaac", + 14 + ], + [ + "CbabAACaC", + "cABaCcbB", + 13 + ], + [ + "CbabACa", + "A", + 12 + ], + [ + "CbabAac", + "aaaB", + 9 + ], + [ + "CbabAcC", + "cbb", + 9 + ], + [ + "CbabBBBcc", + "c", + 16 + ], + [ + "CbabBCCab", + "bB", + 14 + ], + [ + "CbabC", + "CacBB", + 7 + ], + [ + "CbabbBbb", + "BaAABCC", + 11 + ], + [ + "Cbabc", + "aACbC", + 7 + ], + [ + "CbabcB", + "CBcbAcb", + 6 + ], + [ + "CbacAab", + "c", + 12 + ], + [ + "CbacCAB", + "cCC", + 10 + ], + [ + "CbacCCb", + "caaC", + 9 + ], + [ + "CbacCaCAB", + "Cbbba", + 12 + ], + [ + "Cbacaca", + "BACAc", + 8 + ], + [ + "Cbb", + "Ababcb", + 8 + ], + [ + "Cbb", + "Bb", + 3 + ], + [ + "Cbb", + "CACbbBCb", + 10 + ], + [ + "Cbb", + "CBaABccaA", + 14 + ], + [ + "Cbb", + "abcb", + 4 + ], + [ + "Cbb", + "bACBBCCaa", + 14 + ], + [ + "Cbb", + "caCCACBaB", + 14 + ], + [ + "Cbb", + "cbAcABac", + 12 + ], + [ + "CbbA", + "cAACAA", + 9 + ], + [ + "CbbAACbC", + "ccaABC", + 9 + ], + [ + "CbbABCCA", + "CBCABCA", + 5 + ], + [ + "CbbAb", + "B", + 9 + ], + [ + "CbbAbbac", + "bCBbca", + 10 + ], + [ + "CbbAcaCbC", + "CAbBbcAc", + 11 + ], + [ + "CbbB", + "A", + 8 + ], + [ + "CbbB", + "C", + 6 + ], + [ + "CbbBA", + "BBCabAB", + 10 + ], + [ + "CbbBAbcA", + "CcCCAaBb", + 12 + ], + [ + "CbbBAcCba", + "BBbbCaBA", + 11 + ], + [ + "CbbBaBcB", + "cCabACB", + 10 + ], + [ + "CbbCCacBc", + "cbbAcacab", + 8 + ], + [ + "CbbaAacC", + "bcbcbbaBa", + 13 + ], + [ + "CbbaCBAb", + "aBCBBcC", + 13 + ], + [ + "CbbbBb", + "BA", + 10 + ], + [ + "CbbbaA", + "aacCccBbB", + 15 + ], + [ + "CbbbaACba", + "aCBba", + 12 + ], + [ + "Cbbbab", + "AABAbbc", + 11 + ], + [ + "Cbbc", + "bcaAb", + 9 + ], + [ + "Cbbc", + "cba", + 5 + ], + [ + "CbbcBcAc", + "BbC", + 12 + ], + [ + "CbbcCBacB", + "AaC", + 15 + ], + [ + "Cbbcbb", + "bBaBCBA", + 10 + ], + [ + "CbbcbcabC", + "AcBcBcba", + 10 + ], + [ + "Cbbcc", + "B", + 9 + ], + [ + "Cbbcc", + "CcbA", + 6 + ], + [ + "CbbccBAaB", + "cCA", + 13 + ], + [ + "Cbbccc", + "BcA", + 9 + ], + [ + "Cbc", + "A", + 6 + ], + [ + "Cbc", + "BAa", + 6 + ], + [ + "Cbc", + "BaabcacC", + 12 + ], + [ + "Cbc", + "CBbAc", + 4 + ], + [ + "Cbc", + "aCCbab", + 8 + ], + [ + "Cbc", + "baB", + 6 + ], + [ + "Cbc", + "bcB", + 4 + ], + [ + "Cbc", + "caACbaC", + 9 + ], + [ + "Cbc", + "cbabc", + 5 + ], + [ + "CbcAB", + "AabCABAb", + 9 + ], + [ + "CbcAaaBa", + "aACA", + 12 + ], + [ + "CbcB", + "CBcC", + 3 + ], + [ + "CbcBCbB", + "CAAacC", + 11 + ], + [ + "CbcBb", + "aaCACAAcC", + 15 + ], + [ + "CbcBcb", + "AabcC", + 9 + ], + [ + "CbcBcbC", + "aCa", + 13 + ], + [ + "CbcCAAc", + "aaBBbbc", + 12 + ], + [ + "CbcCAB", + "bbaCCcc", + 9 + ], + [ + "CbcCB", + "c", + 8 + ], + [ + "CbcCBB", + "aB", + 10 + ], + [ + "CbcCCa", + "BB", + 11 + ], + [ + "CbcCcA", + "aCaacCaAa", + 10 + ], + [ + "Cbca", + "cABAAaaA", + 12 + ], + [ + "CbcaC", + "bCcabCa", + 8 + ], + [ + "Cbcb", + "AA", + 8 + ], + [ + "Cbcb", + "AaCc", + 7 + ], + [ + "Cbcb", + "CbBAa", + 6 + ], + [ + "CbcbA", + "bccbACcC", + 10 + ], + [ + "CbcbB", + "aC", + 9 + ], + [ + "CbcbBCBC", + "cBB", + 10 + ], + [ + "CbcbC", + "a", + 10 + ], + [ + "Cbcbb", + "cccAB", + 6 + ], + [ + "Cbcbcbc", + "CbaccAcCC", + 9 + ], + [ + "Cbcc", + "BbAcAabc", + 10 + ], + [ + "Cbcc", + "bbBBA", + 8 + ], + [ + "CbccCAcBA", + "acCA", + 12 + ], + [ + "CbccaACc", + "AC", + 12 + ], + [ + "Cc", + "AAbABC", + 11 + ], + [ + "Cc", + "AAcbCAca", + 12 + ], + [ + "Cc", + "ABaC", + 7 + ], + [ + "Cc", + "AC", + 3 + ], + [ + "Cc", + "ACCcCa", + 8 + ], + [ + "Cc", + "AaCCC", + 7 + ], + [ + "Cc", + "BBCBCbbb", + 13 + ], + [ + "Cc", + "BbAbcC", + 10 + ], + [ + "Cc", + "Bc", + 2 + ], + [ + "Cc", + "Bccba", + 7 + ], + [ + "Cc", + "CABACbAcB", + 14 + ], + [ + "Cc", + "CAaCbCA", + 11 + ], + [ + "Cc", + "CCCCAaC", + 11 + ], + [ + "Cc", + "CCbCaAb", + 11 + ], + [ + "Cc", + "Ccb", + 2 + ], + [ + "Cc", + "a", + 4 + ], + [ + "Cc", + "aAABCBacb", + 14 + ], + [ + "Cc", + "aAAccbC", + 11 + ], + [ + "Cc", + "aBBCbc", + 8 + ], + [ + "Cc", + "aCAcaCca", + 12 + ], + [ + "Cc", + "bB", + 4 + ], + [ + "Cc", + "baABAABbC", + 17 + ], + [ + "Cc", + "bbacA", + 8 + ], + [ + "Cc", + "bc", + 2 + ], + [ + "Cc", + "c", + 2 + ], + [ + "Cc", + "cAaAAbc", + 11 + ], + [ + "Cc", + "cBbbBbcA", + 13 + ], + [ + "Cc", + "ca", + 3 + ], + [ + "Cc", + "ccAC", + 5 + ], + [ + "Cc", + "ccCb", + 5 + ], + [ + "CcA", + "BbcbaAB", + 10 + ], + [ + "CcA", + "aCA", + 3 + ], + [ + "CcA", + "bAAbca", + 9 + ], + [ + "CcA", + "bBccA", + 5 + ], + [ + "CcA", + "bCCBb", + 7 + ], + [ + "CcA", + "cBAbaC", + 9 + ], + [ + "CcAA", + "bcACbc", + 8 + ], + [ + "CcAAAAC", + "abCa", + 12 + ], + [ + "CcAAAB", + "bBAbaC", + 9 + ], + [ + "CcAAAcCb", + "cAcBcBBC", + 11 + ], + [ + "CcAABaC", + "BbC", + 10 + ], + [ + "CcAABbc", + "BCaAbab", + 9 + ], + [ + "CcAACB", + "b", + 11 + ], + [ + "CcAAaC", + "CAbaab", + 7 + ], + [ + "CcAB", + "BbcCBc", + 8 + ], + [ + "CcAB", + "accAaa", + 7 + ], + [ + "CcABACACb", + "abBBbCAA", + 12 + ], + [ + "CcABBccA", + "cc", + 12 + ], + [ + "CcABb", + "a", + 9 + ], + [ + "CcABbabBA", + "cCA", + 14 + ], + [ + "CcABcAaa", + "Ba", + 12 + ], + [ + "CcACAbc", + "AbBB", + 11 + ], + [ + "CcACbaCA", + "abbBaBbBa", + 16 + ], + [ + "CcACcCAa", + "AaB", + 13 + ], + [ + "CcAa", + "aAbBCCc", + 13 + ], + [ + "CcAa", + "ccBC", + 5 + ], + [ + "CcAaA", + "cbcC", + 8 + ], + [ + "CcAaB", + "babCBbCAC", + 15 + ], + [ + "CcAaCABA", + "cbaBaCCCb", + 12 + ], + [ + "CcAaCcbcB", + "aBbaCAAC", + 13 + ], + [ + "CcAac", + "Cabaac", + 5 + ], + [ + "CcAacBAaC", + "bCACB", + 12 + ], + [ + "CcAb", + "b", + 6 + ], + [ + "CcAbBcBaa", + "CBc", + 12 + ], + [ + "CcAbaaA", + "cCa", + 10 + ], + [ + "CcAbcaB", + "bcA", + 9 + ], + [ + "CcAc", + "AAaaABABA", + 16 + ], + [ + "CcAcAB", + "aabAabCcb", + 14 + ], + [ + "CcAcCcC", + "cACbbac", + 10 + ], + [ + "CcAcCcc", + "A", + 12 + ], + [ + "CcB", + "AbcBBCAac", + 14 + ], + [ + "CcB", + "Ac", + 4 + ], + [ + "CcB", + "Ccb", + 1 + ], + [ + "CcB", + "ba", + 6 + ], + [ + "CcB", + "cab", + 4 + ], + [ + "CcB", + "cbc", + 5 + ], + [ + "CcBABc", + "cAAC", + 7 + ], + [ + "CcBACb", + "ac", + 10 + ], + [ + "CcBAaAa", + "aCCcB", + 12 + ], + [ + "CcBB", + "bab", + 7 + ], + [ + "CcBBA", + "B", + 8 + ], + [ + "CcBBAb", + "aBCAAc", + 10 + ], + [ + "CcBBCAAC", + "A", + 14 + ], + [ + "CcBBbac", + "Bb", + 10 + ], + [ + "CcBCBaa", + "bbbbB", + 11 + ], + [ + "CcBCCaAbC", + "aAaCaCA", + 14 + ], + [ + "CcBCCbABb", + "BbBCbccaC", + 14 + ], + [ + "CcBCaacCB", + "CAbCcbA", + 11 + ], + [ + "CcBCc", + "C", + 8 + ], + [ + "CcBCc", + "cBcba", + 7 + ], + [ + "CcBCccbb", + "abaAAAa", + 15 + ], + [ + "CcBaBb", + "BAabCcBcB", + 12 + ], + [ + "CcBaa", + "abBac", + 6 + ], + [ + "CcBabaabc", + "bCB", + 15 + ], + [ + "CcBacBC", + "aa", + 12 + ], + [ + "CcBb", + "AAB", + 6 + ], + [ + "CcBb", + "BaAcbA", + 9 + ], + [ + "CcBbC", + "A", + 10 + ], + [ + "CcBbaCbb", + "abcBCCBab", + 11 + ], + [ + "CcBbbaB", + "baCb", + 11 + ], + [ + "CcBc", + "aBabCbbcB", + 13 + ], + [ + "CcBc", + "cabcACa", + 10 + ], + [ + "CcBcABAC", + "AAC", + 10 + ], + [ + "CcBcBCBaa", + "CBA", + 13 + ], + [ + "CcC", + "CBAacacBa", + 13 + ], + [ + "CcC", + "CBbcac", + 7 + ], + [ + "CcCAA", + "cB", + 8 + ], + [ + "CcCABC", + "ACA", + 8 + ], + [ + "CcCACCb", + "CbAbCccC", + 11 + ], + [ + "CcCAaCAaB", + "ABbAAaBb", + 12 + ], + [ + "CcCAbB", + "abb", + 8 + ], + [ + "CcCAbCBc", + "cbaCBaB", + 11 + ], + [ + "CcCB", + "ABbCBAc", + 10 + ], + [ + "CcCB", + "bcaabaccc", + 14 + ], + [ + "CcCB", + "cbBaC", + 8 + ], + [ + "CcCBA", + "CbAbcb", + 9 + ], + [ + "CcCBCcCA", + "BbCcaa", + 10 + ], + [ + "CcCBb", + "aBBC", + 8 + ], + [ + "CcCBc", + "BCBb", + 6 + ], + [ + "CcCC", + "b", + 8 + ], + [ + "CcCCAB", + "CCbB", + 6 + ], + [ + "CcCCBACCa", + "ABAABAAAb", + 14 + ], + [ + "CcCCacbB", + "aACA", + 13 + ], + [ + "CcCaA", + "baa", + 7 + ], + [ + "CcCaaaaA", + "bCbbACC", + 13 + ], + [ + "CcCacAB", + "BCCBacCcb", + 10 + ], + [ + "CcCacbC", + "ACBabB", + 9 + ], + [ + "CcCbB", + "BABCbb", + 7 + ], + [ + "CcCbCA", + "aBcA", + 8 + ], + [ + "CcCbCbBbc", + "ccabB", + 11 + ], + [ + "CcCba", + "BCb", + 6 + ], + [ + "CcCbaAbB", + "aBbcaCB", + 11 + ], + [ + "CcCbcaC", + "CcBbaCbCb", + 9 + ], + [ + "CcCc", + "cBAACa", + 9 + ], + [ + "CcCcAB", + "aCBbA", + 9 + ], + [ + "CcCcAc", + "baBacaB", + 11 + ], + [ + "CcCcBB", + "bcbBbcAbB", + 11 + ], + [ + "CcCcCCAb", + "BCBBaABab", + 13 + ], + [ + "CcCca", + "bCaBAB", + 10 + ], + [ + "CcCcaB", + "aAacCCb", + 10 + ], + [ + "CcCcbCb", + "BBBCBaCC", + 12 + ], + [ + "CcCcbCb", + "cC", + 10 + ], + [ + "CcCccCbbc", + "cabBaCB", + 14 + ], + [ + "Cca", + "AbbaBABCA", + 16 + ], + [ + "Cca", + "AcacBcc", + 10 + ], + [ + "Cca", + "a", + 4 + ], + [ + "Cca", + "b", + 6 + ], + [ + "Cca", + "bAbaCBB", + 12 + ], + [ + "CcaAB", + "B", + 8 + ], + [ + "CcaABA", + "a", + 10 + ], + [ + "CcaAC", + "BaCbAbaAa", + 12 + ], + [ + "CcaACB", + "BcBbAAA", + 10 + ], + [ + "CcaACCA", + "bCacCB", + 8 + ], + [ + "CcaAa", + "cCC", + 8 + ], + [ + "CcaAbCCC", + "AbCBCb", + 10 + ], + [ + "CcaAcb", + "bcBAa", + 8 + ], + [ + "CcaAcc", + "babAAAcbB", + 13 + ], + [ + "CcaB", + "BbaCbBbCB", + 14 + ], + [ + "CcaBA", + "ACCCABCBC", + 12 + ], + [ + "CcaBaBC", + "bacAbAab", + 11 + ], + [ + "CcaBaa", + "A", + 11 + ], + [ + "CcaBcBAB", + "Ac", + 13 + ], + [ + "CcaBcC", + "A", + 11 + ], + [ + "CcaCAaabB", + "CABCacccB", + 11 + ], + [ + "CcaCCAAA", + "BCAaCCcc", + 10 + ], + [ + "CcaCa", + "BAa", + 7 + ], + [ + "CcaCabAcb", + "BaaaBAC", + 10 + ], + [ + "CcaCacbba", + "bccAB", + 13 + ], + [ + "CcaCbbA", + "CcB", + 9 + ], + [ + "CcaCcBc", + "cbaCa", + 9 + ], + [ + "Ccaa", + "AabAcaC", + 10 + ], + [ + "CcaaA", + "aaCAA", + 7 + ], + [ + "CcaaAAbc", + "aABCaCacc", + 13 + ], + [ + "CcaaBC", + "abBCaC", + 10 + ], + [ + "Ccaaa", + "cc", + 7 + ], + [ + "CcaaaAB", + "bc", + 12 + ], + [ + "Ccaaaa", + "ABbbbb", + 12 + ], + [ + "Ccaaabccc", + "cA", + 15 + ], + [ + "CcaabbCCA", + "Bbc", + 14 + ], + [ + "Ccab", + "AC", + 7 + ], + [ + "Ccab", + "Cba", + 4 + ], + [ + "Ccab", + "abBAaBB", + 11 + ], + [ + "Ccab", + "abbBCaBb", + 11 + ], + [ + "CcabACcb", + "cCAaC", + 10 + ], + [ + "Ccac", + "cbCCA", + 8 + ], + [ + "CcacB", + "cCBbaCB", + 7 + ], + [ + "CcacBabBC", + "CaBBaB", + 8 + ], + [ + "CcacC", + "AbbacBa", + 10 + ], + [ + "CcacCABB", + "ba", + 14 + ], + [ + "CcacCaA", + "CaAC", + 8 + ], + [ + "CcacaBB", + "B", + 12 + ], + [ + "CcacbC", + "cCb", + 7 + ], + [ + "Ccb", + "AcbABaCC", + 12 + ], + [ + "Ccb", + "CAA", + 4 + ], + [ + "Ccb", + "CbAcC", + 6 + ], + [ + "Ccb", + "c", + 4 + ], + [ + "CcbA", + "BccAc", + 6 + ], + [ + "CcbA", + "aabCBb", + 10 + ], + [ + "CcbA", + "bABb", + 7 + ], + [ + "CcbAACA", + "bcc", + 11 + ], + [ + "CcbAACB", + "acBBAcac", + 10 + ], + [ + "CcbAAaA", + "ccBCaCa", + 8 + ], + [ + "CcbACBA", + "A", + 12 + ], + [ + "CcbACC", + "bbaC", + 7 + ], + [ + "CcbAaBcAA", + "bAcac", + 11 + ], + [ + "CcbAc", + "AcbbBBBa", + 12 + ], + [ + "CcbBAbCcC", + "AAb", + 14 + ], + [ + "CcbBaCC", + "ABcA", + 11 + ], + [ + "CcbBac", + "BCAAA", + 10 + ], + [ + "CcbBbBBA", + "AbB", + 12 + ], + [ + "CcbBbbbbb", + "bb", + 14 + ], + [ + "CcbBc", + "cBAcacB", + 11 + ], + [ + "CcbBcAC", + "BbCcAbcbB", + 11 + ], + [ + "CcbBcaA", + "bbcca", + 8 + ], + [ + "CcbBcc", + "bbcc", + 5 + ], + [ + "CcbCA", + "a", + 9 + ], + [ + "CcbCA", + "aabAA", + 6 + ], + [ + "CcbCbB", + "CACab", + 7 + ], + [ + "CcbCbCA", + "BcaCaCAb", + 8 + ], + [ + "CcbCc", + "AC", + 8 + ], + [ + "CcbCc", + "cBbbc", + 5 + ], + [ + "CcbCcC", + "c", + 10 + ], + [ + "CcbaccBCb", + "bbCBA", + 13 + ], + [ + "CcbbABb", + "CbCbacB", + 8 + ], + [ + "CcbbaAbbB", + "bc", + 16 + ], + [ + "Ccbbb", + "CBAcACB", + 9 + ], + [ + "CcbbbC", + "cBacabCb", + 11 + ], + [ + "CcbbbCbc", + "BbCAaBaB", + 15 + ], + [ + "CcbbcABBa", + "BcAcCcBCB", + 13 + ], + [ + "Ccbc", + "ABAaaBAb", + 15 + ], + [ + "Ccc", + "B", + 6 + ], + [ + "Ccc", + "BABa", + 8 + ], + [ + "Ccc", + "BcaBAc", + 8 + ], + [ + "Ccc", + "CBB", + 4 + ], + [ + "Ccc", + "CC", + 3 + ], + [ + "Ccc", + "aaaaCC", + 10 + ], + [ + "Ccc", + "ac", + 4 + ], + [ + "Ccc", + "bb", + 6 + ], + [ + "CccABA", + "AAbBcA", + 10 + ], + [ + "CccACCB", + "AcbaC", + 9 + ], + [ + "CccAaB", + "baCaBcCa", + 12 + ], + [ + "CccAb", + "aABBc", + 10 + ], + [ + "CccAcbbca", + "c", + 16 + ], + [ + "CccBBBcB", + "bBbbCa", + 11 + ], + [ + "CccBBbC", + "Bba", + 10 + ], + [ + "CccBa", + "B", + 8 + ], + [ + "CccBa", + "caBA", + 5 + ], + [ + "CccBaaAB", + "C", + 14 + ], + [ + "CccBbBCb", + "a", + 16 + ], + [ + "CccBbca", + "BacAbCBbC", + 12 + ], + [ + "CccCAa", + "CcBcBCCc", + 8 + ], + [ + "CccCCa", + "Ba", + 10 + ], + [ + "CccCa", + "aA", + 9 + ], + [ + "CccCb", + "baA", + 10 + ], + [ + "Ccca", + "A", + 7 + ], + [ + "CccaABbBb", + "aAcB", + 12 + ], + [ + "CccaBBCb", + "BBbaaCCC", + 12 + ], + [ + "Cccaa", + "BbbC", + 10 + ], + [ + "CccacCBb", + "ccBa", + 10 + ], + [ + "CccbABBbb", + "B", + 16 + ], + [ + "Cccbbc", + "CCcBcBa", + 7 + ], + [ + "Cccc", + "BaCbb", + 9 + ], + [ + "CcccAB", + "aCBAB", + 7 + ], + [ + "CcccAbbA", + "BCBcBc", + 12 + ], + [ + "CcccBacc", + "BCAcB", + 11 + ], + [ + "CcccBcCB", + "cCAb", + 12 + ], + [ + "a", + "AA", + 3 + ], + [ + "a", + "AABaCbca", + 14 + ], + [ + "a", + "AACaBBAC", + 14 + ], + [ + "a", + "ABCaa", + 8 + ], + [ + "a", + "ABaaBCCb", + 14 + ], + [ + "a", + "ABab", + 6 + ], + [ + "a", + "ABcB", + 7 + ], + [ + "a", + "ACbcCBCB", + 15 + ], + [ + "a", + "ACbcaA", + 10 + ], + [ + "a", + "AaAA", + 6 + ], + [ + "a", + "AaABacaAA", + 16 + ], + [ + "a", + "AaAbBca", + 12 + ], + [ + "a", + "AabBCaab", + 14 + ], + [ + "a", + "AabCbCb", + 12 + ], + [ + "a", + "AaccB", + 8 + ], + [ + "a", + "Ab", + 3 + ], + [ + "a", + "AbBBaB", + 10 + ], + [ + "a", + "AbCb", + 7 + ], + [ + "a", + "AbCc", + 7 + ], + [ + "a", + "AbbbBacAa", + 16 + ], + [ + "a", + "AbcCaCbac", + 16 + ], + [ + "a", + "AcABaA", + 10 + ], + [ + "a", + "AcACCA", + 11 + ], + [ + "a", + "AcCbc", + 9 + ], + [ + "a", + "AcaB", + 6 + ], + [ + "a", + "B", + 2 + ], + [ + "a", + "BA", + 3 + ], + [ + "a", + "BABAAcBBc", + 17 + ], + [ + "a", + "BABaBa", + 10 + ], + [ + "a", + "BAC", + 5 + ], + [ + "a", + "BAaAbAc", + 12 + ], + [ + "a", + "BAabccCBc", + 16 + ], + [ + "a", + "BAbaaa", + 10 + ], + [ + "a", + "BAbb", + 7 + ], + [ + "a", + "BBAcbBB", + 13 + ], + [ + "a", + "BBBaCab", + 12 + ], + [ + "a", + "BBC", + 6 + ], + [ + "a", + "BBaCbBAC", + 14 + ], + [ + "a", + "BBcca", + 8 + ], + [ + "a", + "BCA", + 5 + ], + [ + "a", + "BCBab", + 8 + ], + [ + "a", + "BaAAAcAbc", + 16 + ], + [ + "a", + "BaAcABBC", + 14 + ], + [ + "a", + "BaBcaC", + 10 + ], + [ + "a", + "BaC", + 4 + ], + [ + "a", + "BaabBAabb", + 16 + ], + [ + "a", + "BbCaCa", + 10 + ], + [ + "a", + "BbcACCA", + 13 + ], + [ + "a", + "Bbcba", + 8 + ], + [ + "a", + "BcAb", + 7 + ], + [ + "a", + "BcBABC", + 11 + ], + [ + "a", + "BcBaAacC", + 14 + ], + [ + "a", + "Bca", + 4 + ], + [ + "a", + "Bcabba", + 10 + ], + [ + "a", + "BccCbbAb", + 15 + ], + [ + "a", + "C", + 2 + ], + [ + "a", + "CA", + 3 + ], + [ + "a", + "CABAcBbc", + 15 + ], + [ + "a", + "CABCB", + 9 + ], + [ + "a", + "CAbC", + 7 + ], + [ + "a", + "CAba", + 6 + ], + [ + "a", + "CAcCAa", + 10 + ], + [ + "a", + "CAcc", + 7 + ], + [ + "a", + "CBA", + 5 + ], + [ + "a", + "CBABB", + 9 + ], + [ + "a", + "CBAa", + 6 + ], + [ + "a", + "CBBCcB", + 12 + ], + [ + "a", + "CBBcA", + 9 + ], + [ + "a", + "CBaCAC", + 10 + ], + [ + "a", + "CBbaCCbC", + 14 + ], + [ + "a", + "CCBc", + 8 + ], + [ + "a", + "CCcaC", + 8 + ], + [ + "a", + "Ca", + 2 + ], + [ + "a", + "CaAaC", + 8 + ], + [ + "a", + "CaBacbb", + 12 + ], + [ + "a", + "CaabacC", + 12 + ], + [ + "a", + "CbBc", + 8 + ], + [ + "a", + "CbbcAB", + 11 + ], + [ + "a", + "CcBBaaCc", + 14 + ], + [ + "a", + "CcBbabBAc", + 16 + ], + [ + "a", + "CcaACcB", + 12 + ], + [ + "a", + "Ccca", + 6 + ], + [ + "a", + "a", + 0 + ], + [ + "a", + "aA", + 2 + ], + [ + "a", + "aAACAbCC", + 14 + ], + [ + "a", + "aACABcAb", + 14 + ], + [ + "a", + "aAbaB", + 8 + ], + [ + "a", + "aBB", + 4 + ], + [ + "a", + "aBBCb", + 8 + ], + [ + "a", + "aBaCbb", + 10 + ], + [ + "a", + "aBb", + 4 + ], + [ + "a", + "aC", + 2 + ], + [ + "a", + "aCCCCcab", + 14 + ], + [ + "a", + "aCaBBABa", + 14 + ], + [ + "a", + "aCaC", + 6 + ], + [ + "a", + "aCb", + 4 + ], + [ + "a", + "aCcBaAa", + 12 + ], + [ + "a", + "aa", + 2 + ], + [ + "a", + "aaBbC", + 8 + ], + [ + "a", + "aaCA", + 6 + ], + [ + "a", + "ab", + 2 + ], + [ + "a", + "abACBBAcC", + 16 + ], + [ + "a", + "abBbBba", + 12 + ], + [ + "a", + "abCaAaCbC", + 16 + ], + [ + "a", + "aba", + 4 + ], + [ + "a", + "abcac", + 8 + ], + [ + "a", + "acAcBAbaB", + 16 + ], + [ + "a", + "acBcAA", + 10 + ], + [ + "a", + "accCCCc", + 12 + ], + [ + "a", + "b", + 2 + ], + [ + "a", + "bA", + 3 + ], + [ + "a", + "bAAaB", + 8 + ], + [ + "a", + "bABBa", + 8 + ], + [ + "a", + "bACca", + 8 + ], + [ + "a", + "bAbAccb", + 13 + ], + [ + "a", + "bAccbcaAB", + 16 + ], + [ + "a", + "bBAca", + 8 + ], + [ + "a", + "bBBC", + 8 + ], + [ + "a", + "bBBCaccC", + 14 + ], + [ + "a", + "bBCb", + 8 + ], + [ + "a", + "bBbC", + 8 + ], + [ + "a", + "bBbCABa", + 12 + ], + [ + "a", + "bC", + 4 + ], + [ + "a", + "bCBacAcC", + 14 + ], + [ + "a", + "bCC", + 6 + ], + [ + "a", + "bCCbaccA", + 14 + ], + [ + "a", + "bCbA", + 7 + ], + [ + "a", + "bCbBab", + 10 + ], + [ + "a", + "bCcaCbc", + 12 + ], + [ + "a", + "bCcaaCbB", + 14 + ], + [ + "a", + "ba", + 2 + ], + [ + "a", + "baa", + 4 + ], + [ + "a", + "baacacaba", + 16 + ], + [ + "a", + "babcA", + 8 + ], + [ + "a", + "babcCAA", + 12 + ], + [ + "a", + "bb", + 4 + ], + [ + "a", + "bbAc", + 7 + ], + [ + "a", + "bbB", + 6 + ], + [ + "a", + "bbBA", + 7 + ], + [ + "a", + "bbBBcaBc", + 14 + ], + [ + "a", + "bbBbAaAAc", + 16 + ], + [ + "a", + "bbCCaACb", + 14 + ], + [ + "a", + "bbCaca", + 10 + ], + [ + "a", + "bbaC", + 6 + ], + [ + "a", + "bbbAcAB", + 13 + ], + [ + "a", + "bbbBAABC", + 15 + ], + [ + "a", + "bbcCBcB", + 14 + ], + [ + "a", + "bcAca", + 8 + ], + [ + "a", + "bcAcacCB", + 14 + ], + [ + "a", + "bcaAcBAbC", + 16 + ], + [ + "a", + "bcb", + 6 + ], + [ + "a", + "bcc", + 6 + ], + [ + "a", + "c", + 2 + ], + [ + "a", + "cAC", + 5 + ], + [ + "a", + "cACBACca", + 14 + ], + [ + "a", + "cACaCa", + 10 + ], + [ + "a", + "cAa", + 4 + ], + [ + "a", + "cBAaa", + 8 + ], + [ + "a", + "cBbBbab", + 12 + ], + [ + "a", + "cC", + 4 + ], + [ + "a", + "cCBA", + 7 + ], + [ + "a", + "cCCa", + 6 + ], + [ + "a", + "cCCbaa", + 10 + ], + [ + "a", + "cCaCcb", + 10 + ], + [ + "a", + "cCacac", + 10 + ], + [ + "a", + "cCcBbCBAa", + 16 + ], + [ + "a", + "cabbcaBBc", + 16 + ], + [ + "a", + "cb", + 4 + ], + [ + "a", + "cbAcAab", + 12 + ], + [ + "a", + "cbAcCB", + 11 + ], + [ + "a", + "cbBaC", + 8 + ], + [ + "a", + "cbCcbC", + 12 + ], + [ + "a", + "cbaCbbBb", + 14 + ], + [ + "a", + "cbbAaCA", + 12 + ], + [ + "a", + "ccACCaBAa", + 16 + ], + [ + "a", + "ccACbaC", + 12 + ], + [ + "a", + "ccAbAC", + 11 + ], + [ + "a", + "ccAbbaBA", + 14 + ], + [ + "a", + "ccCBaCcB", + 14 + ], + [ + "a", + "ccac", + 6 + ], + [ + "aA", + "AAc", + 3 + ], + [ + "aA", + "ABcACbCcb", + 15 + ], + [ + "aA", + "ACcbCBA", + 11 + ], + [ + "aA", + "AacACCa", + 10 + ], + [ + "aA", + "B", + 4 + ], + [ + "aA", + "BabBBcCbC", + 16 + ], + [ + "aA", + "Babb", + 6 + ], + [ + "aA", + "BcAcB", + 8 + ], + [ + "aA", + "CAacB", + 8 + ], + [ + "aA", + "CBABBaAB", + 12 + ], + [ + "aA", + "CaBbbbA", + 10 + ], + [ + "aA", + "CcAabB", + 10 + ], + [ + "aA", + "CcaAb", + 6 + ], + [ + "aA", + "a", + 2 + ], + [ + "aA", + "aCAcAb", + 8 + ], + [ + "aA", + "aCBb", + 6 + ], + [ + "aA", + "aCacaAA", + 10 + ], + [ + "aA", + "aaACBacA", + 12 + ], + [ + "aA", + "aaC", + 3 + ], + [ + "aA", + "abbAaBC", + 10 + ], + [ + "aA", + "abbBBbc", + 12 + ], + [ + "aA", + "bAc", + 4 + ], + [ + "aA", + "bB", + 4 + ], + [ + "aA", + "bBCBC", + 10 + ], + [ + "aA", + "baCBAbCa", + 12 + ], + [ + "aA", + "cABBaA", + 8 + ], + [ + "aA", + "cBc", + 6 + ], + [ + "aA", + "cabbabbA", + 12 + ], + [ + "aA", + "ccbcB", + 10 + ], + [ + "aAA", + "aACbBCCb", + 12 + ], + [ + "aAA", + "ac", + 4 + ], + [ + "aAA", + "bAc", + 4 + ], + [ + "aAAA", + "AaAC", + 4 + ], + [ + "aAAAA", + "c", + 10 + ], + [ + "aAAAAC", + "CbaaCCBBa", + 14 + ], + [ + "aAAAAaAA", + "cbAabBb", + 13 + ], + [ + "aAAABb", + "BABBaB", + 9 + ], + [ + "aAAAC", + "bC", + 8 + ], + [ + "aAAAaAb", + "AAC", + 10 + ], + [ + "aAAAabBca", + "AB", + 14 + ], + [ + "aAAAcCbCB", + "CcBbABBB", + 15 + ], + [ + "aAAB", + "A", + 6 + ], + [ + "aAAB", + "bcabAb", + 7 + ], + [ + "aAAB", + "c", + 8 + ], + [ + "aAAB", + "cCAB", + 4 + ], + [ + "aAABCcCcC", + "CbBBCAB", + 14 + ], + [ + "aAABbBACb", + "aACb", + 10 + ], + [ + "aAACC", + "ccbbaBA", + 13 + ], + [ + "aAACaCa", + "ACbabAc", + 11 + ], + [ + "aAAaAAaa", + "ACBcbbcaA", + 14 + ], + [ + "aAAaaaB", + "BABbaaAbA", + 10 + ], + [ + "aAAabaAbA", + "bcCBbaBB", + 13 + ], + [ + "aAAacaaC", + "a", + 14 + ], + [ + "aAAb", + "aCAA", + 4 + ], + [ + "aAAbBbaA", + "BaaaAbb", + 11 + ], + [ + "aAAbCbCcC", + "cBC", + 14 + ], + [ + "aAAbbA", + "bcbbaBa", + 10 + ], + [ + "aAAc", + "AccBbB", + 10 + ], + [ + "aAAcA", + "BBBABa", + 9 + ], + [ + "aAAcA", + "bb", + 10 + ], + [ + "aAAcAA", + "aCAA", + 5 + ], + [ + "aAAcAa", + "CbCAa", + 7 + ], + [ + "aAAcAaa", + "bAAACb", + 8 + ], + [ + "aAAcB", + "AbcACA", + 8 + ], + [ + "aAAcBBba", + "BcaAcACb", + 11 + ], + [ + "aAAcCCbCB", + "C", + 16 + ], + [ + "aAAcCaCc", + "caACAaAa", + 10 + ], + [ + "aAAcCb", + "bbAbA", + 10 + ], + [ + "aAAcabaab", + "cAACAbb", + 8 + ], + [ + "aAAcbACab", + "bCcAaBbCC", + 15 + ], + [ + "aAAcc", + "aaAC", + 4 + ], + [ + "aAB", + "AAaAcCaa", + 12 + ], + [ + "aAB", + "AbCBB", + 7 + ], + [ + "aAB", + "AcaAB", + 4 + ], + [ + "aAB", + "CAbaA", + 7 + ], + [ + "aAB", + "CCBAcbaCC", + 15 + ], + [ + "aAB", + "aBcbbB", + 8 + ], + [ + "aAB", + "aCCcaBCC", + 11 + ], + [ + "aAB", + "bcbccABCA", + 14 + ], + [ + "aABA", + "cCac", + 8 + ], + [ + "aABAB", + "AaaBBcCA", + 11 + ], + [ + "aABACAa", + "CcBBbA", + 10 + ], + [ + "aABAaA", + "C", + 12 + ], + [ + "aABAb", + "BbBCC", + 8 + ], + [ + "aABAb", + "ca", + 9 + ], + [ + "aABAbab", + "a", + 12 + ], + [ + "aABAc", + "AabAabaB", + 10 + ], + [ + "aABAcCbbA", + "aB", + 14 + ], + [ + "aABB", + "CCAaaccbA", + 14 + ], + [ + "aABB", + "aCac", + 6 + ], + [ + "aABB", + "cb", + 7 + ], + [ + "aABBBBCc", + "cBAcbb", + 13 + ], + [ + "aABBBBa", + "CaBaCCBB", + 11 + ], + [ + "aABBCc", + "cc", + 9 + ], + [ + "aABBacCA", + "BBbacBc", + 9 + ], + [ + "aABBbA", + "aCBbC", + 6 + ], + [ + "aABBbaB", + "CbbBCbacb", + 10 + ], + [ + "aABCAa", + "bBbC", + 9 + ], + [ + "aABCB", + "cAB", + 6 + ], + [ + "aABCCcbB", + "CBbaCcbc", + 9 + ], + [ + "aABCaca", + "Bb", + 12 + ], + [ + "aABCbCba", + "CabccBaB", + 11 + ], + [ + "aABaBA", + "BC", + 10 + ], + [ + "aABaBbAAA", + "B", + 16 + ], + [ + "aABb", + "BC", + 6 + ], + [ + "aABbAa", + "bCbBcC", + 10 + ], + [ + "aABbBbCca", + "ABCCBAbAc", + 12 + ], + [ + "aABbaA", + "bcBC", + 10 + ], + [ + "aABbac", + "AbBbABAAb", + 12 + ], + [ + "aABbccca", + "BcaA", + 11 + ], + [ + "aABcACB", + "A", + 12 + ], + [ + "aABccB", + "A", + 10 + ], + [ + "aAC", + "BA", + 4 + ], + [ + "aAC", + "CAbbaab", + 11 + ], + [ + "aAC", + "CbC", + 4 + ], + [ + "aAC", + "b", + 6 + ], + [ + "aAC", + "bACCbC", + 8 + ], + [ + "aAC", + "bcaBacc", + 10 + ], + [ + "aACABAcCa", + "ccCB", + 13 + ], + [ + "aACBA", + "Aa", + 7 + ], + [ + "aACBACA", + "BaCBa", + 8 + ], + [ + "aACBBcBC", + "A", + 14 + ], + [ + "aACCA", + "Cb", + 8 + ], + [ + "aACCAAb", + "Bca", + 12 + ], + [ + "aACCBc", + "AcbAaCB", + 9 + ], + [ + "aACCa", + "ACccABA", + 10 + ], + [ + "aACCbAA", + "AAaa", + 9 + ], + [ + "aACCc", + "BBcc", + 7 + ], + [ + "aACCc", + "cbaBAC", + 10 + ], + [ + "aACaaA", + "bcACaCbA", + 8 + ], + [ + "aACacACa", + "aaBccaB", + 10 + ], + [ + "aACacCb", + "C", + 12 + ], + [ + "aACaccCAC", + "cC", + 14 + ], + [ + "aACbAC", + "AB", + 9 + ], + [ + "aACbBBcb", + "b", + 14 + ], + [ + "aACbbbaBC", + "aBBACCB", + 13 + ], + [ + "aACcaBb", + "ABabA", + 9 + ], + [ + "aACcbCa", + "b", + 12 + ], + [ + "aAa", + "BbBb", + 8 + ], + [ + "aAa", + "CBCBAabb", + 12 + ], + [ + "aAa", + "Cc", + 6 + ], + [ + "aAa", + "bCaaCBcAa", + 12 + ], + [ + "aAa", + "bc", + 6 + ], + [ + "aAaA", + "b", + 8 + ], + [ + "aAaAcc", + "B", + 12 + ], + [ + "aAaAccAb", + "BCAbabcAA", + 11 + ], + [ + "aAaBBCb", + "Cbbc", + 11 + ], + [ + "aAaBaBBBc", + "cAcCCCA", + 16 + ], + [ + "aAaBaBa", + "bBbBc", + 10 + ], + [ + "aAaCB", + "cCc", + 8 + ], + [ + "aAaCCCc", + "bbcBabaAA", + 16 + ], + [ + "aAaCaaAc", + "BCaA", + 10 + ], + [ + "aAaa", + "bb", + 8 + ], + [ + "aAaaAcCA", + "cBbcccB", + 13 + ], + [ + "aAaaBbbb", + "bCca", + 14 + ], + [ + "aAaaC", + "aCAC", + 5 + ], + [ + "aAaaCBa", + "aaCBCc", + 8 + ], + [ + "aAaaaA", + "aBACbaBAC", + 10 + ], + [ + "aAaabBcb", + "AcCAcBA", + 12 + ], + [ + "aAab", + "C", + 8 + ], + [ + "aAab", + "bCaB", + 5 + ], + [ + "aAab", + "bbaacCCB", + 12 + ], + [ + "aAabC", + "ccbBa", + 9 + ], + [ + "aAabCAc", + "aBCb", + 9 + ], + [ + "aAabCCBaa", + "bBC", + 14 + ], + [ + "aAabbbbB", + "CBAACBCCb", + 13 + ], + [ + "aAabbcCaA", + "A", + 16 + ], + [ + "aAac", + "cCBbCAaBB", + 14 + ], + [ + "aAacB", + "cAcbcBa", + 8 + ], + [ + "aAacBcCBa", + "cba", + 13 + ], + [ + "aAb", + "A", + 4 + ], + [ + "aAb", + "a", + 4 + ], + [ + "aAb", + "b", + 4 + ], + [ + "aAbA", + "bccaaaCAB", + 13 + ], + [ + "aAbABAA", + "bCBAcabBb", + 14 + ], + [ + "aAbAC", + "cACcCa", + 8 + ], + [ + "aAbACaBAA", + "CB", + 14 + ], + [ + "aAbAb", + "cBBabc", + 8 + ], + [ + "aAbAcCb", + "c", + 12 + ], + [ + "aAbBB", + "AC", + 8 + ], + [ + "aAbBaBcAA", + "C", + 17 + ], + [ + "aAbCAcbCC", + "BAAaA", + 14 + ], + [ + "aAbCBA", + "aCBaCABc", + 9 + ], + [ + "aAbCCACCb", + "AbcacbAb", + 11 + ], + [ + "aAbCCC", + "Aca", + 9 + ], + [ + "aAbCa", + "bABc", + 6 + ], + [ + "aAbCbCa", + "AabbBCbA", + 8 + ], + [ + "aAbCbbaba", + "CcBbccaB", + 14 + ], + [ + "aAbCc", + "cCa", + 8 + ], + [ + "aAba", + "BbcbcCBcC", + 16 + ], + [ + "aAbaBA", + "aBAAAA", + 7 + ], + [ + "aAbaBBba", + "Cb", + 14 + ], + [ + "aAbaCAA", + "cAB", + 11 + ], + [ + "aAbaaBA", + "bcCbaccBA", + 10 + ], + [ + "aAbb", + "bBBaACCBC", + 13 + ], + [ + "aAbbABabC", + "cbACbB", + 12 + ], + [ + "aAbbBab", + "caaA", + 11 + ], + [ + "aAbbCAA", + "BBbBcbA", + 8 + ], + [ + "aAbbCbCCa", + "AAB", + 14 + ], + [ + "aAbbCbcb", + "CBc", + 11 + ], + [ + "aAbbaB", + "a", + 10 + ], + [ + "aAbbacAb", + "BccBb", + 11 + ], + [ + "aAbbcbA", + "A", + 12 + ], + [ + "aAbcCaA", + "CCc", + 11 + ], + [ + "aAbcaABCA", + "BCAcbCab", + 13 + ], + [ + "aAbcaAa", + "CCababB", + 11 + ], + [ + "aAbcbbB", + "baba", + 10 + ], + [ + "aAc", + "aBbBCbac", + 11 + ], + [ + "aAc", + "abbAbaC", + 9 + ], + [ + "aAcA", + "A", + 6 + ], + [ + "aAcA", + "ACABBCB", + 10 + ], + [ + "aAcAAb", + "ACC", + 9 + ], + [ + "aAcAAbB", + "cAbbBaCBb", + 13 + ], + [ + "aAcAabacA", + "aCbBcA", + 9 + ], + [ + "aAcAacaba", + "caACb", + 11 + ], + [ + "aAcAbaCac", + "bBc", + 14 + ], + [ + "aAcAcAbaB", + "bcCCa", + 13 + ], + [ + "aAcB", + "aaaCC", + 6 + ], + [ + "aAcB", + "caC", + 6 + ], + [ + "aAcBACBB", + "a", + 14 + ], + [ + "aAcBC", + "aCca", + 6 + ], + [ + "aAcBbABB", + "AA", + 12 + ], + [ + "aAcBbAaBC", + "BbCbaCcaC", + 14 + ], + [ + "aAcCAaC", + "b", + 14 + ], + [ + "aAcCCcAc", + "baabCBAaA", + 12 + ], + [ + "aAcCccBB", + "AaAa", + 14 + ], + [ + "aAcaAaB", + "AcC", + 10 + ], + [ + "aAcaBaA", + "ACcaaA", + 5 + ], + [ + "aAcaaac", + "BaABaBaBA", + 10 + ], + [ + "aAcb", + "AABbA", + 5 + ], + [ + "aAcb", + "aABC", + 4 + ], + [ + "aAcbA", + "cbcAAC", + 8 + ], + [ + "aAcbBb", + "CbcccCBcC", + 14 + ], + [ + "aAcbBcca", + "A", + 14 + ], + [ + "aAcba", + "acBcBBBAc", + 12 + ], + [ + "aAcba", + "b", + 8 + ], + [ + "aAcc", + "CACC", + 4 + ], + [ + "aAccac", + "ACABCBBAC", + 12 + ], + [ + "aAccbCaba", + "BCBbBc", + 14 + ], + [ + "aB", + "A", + 3 + ], + [ + "aB", + "AA", + 3 + ], + [ + "aB", + "AB", + 1 + ], + [ + "aB", + "ABAb", + 5 + ], + [ + "aB", + "ACcb", + 6 + ], + [ + "aB", + "Aabaaa", + 9 + ], + [ + "aB", + "AccabABB", + 12 + ], + [ + "aB", + "B", + 2 + ], + [ + "aB", + "BBcbB", + 8 + ], + [ + "aB", + "BCc", + 6 + ], + [ + "aB", + "Bca", + 6 + ], + [ + "aB", + "C", + 4 + ], + [ + "aB", + "CAAaa", + 8 + ], + [ + "aB", + "CbccACC", + 13 + ], + [ + "aB", + "Cc", + 4 + ], + [ + "aB", + "CcAcCC", + 11 + ], + [ + "aB", + "aCbBBc", + 8 + ], + [ + "aB", + "aaAbCb", + 9 + ], + [ + "aB", + "aaaAb", + 7 + ], + [ + "aB", + "aabCACc", + 11 + ], + [ + "aB", + "abAC", + 5 + ], + [ + "aB", + "bB", + 2 + ], + [ + "aB", + "bBB", + 4 + ], + [ + "aB", + "bb", + 3 + ], + [ + "aB", + "bbbca", + 9 + ], + [ + "aB", + "bbc", + 5 + ], + [ + "aB", + "bc", + 4 + ], + [ + "aB", + "bcb", + 5 + ], + [ + "aB", + "cB", + 2 + ], + [ + "aB", + "cCC", + 6 + ], + [ + "aB", + "cc", + 4 + ], + [ + "aBA", + "BAaAaaCBc", + 14 + ], + [ + "aBAAA", + "CBCAbbcAA", + 10 + ], + [ + "aBAAB", + "BacBABcAc", + 10 + ], + [ + "aBAAaB", + "caAc", + 9 + ], + [ + "aBAAacaa", + "acabBBcBC", + 13 + ], + [ + "aBAAbCcB", + "BCB", + 10 + ], + [ + "aBAAcbCc", + "cBCBBcAAB", + 14 + ], + [ + "aBAB", + "aaCc", + 6 + ], + [ + "aBAB", + "bbaAbA", + 8 + ], + [ + "aBABa", + "bacAa", + 6 + ], + [ + "aBABaABc", + "AAaCCC", + 10 + ], + [ + "aBABcaABA", + "CBcbB", + 12 + ], + [ + "aBACC", + "a", + 8 + ], + [ + "aBACa", + "c", + 9 + ], + [ + "aBACba", + "C", + 10 + ], + [ + "aBACcAA", + "B", + 12 + ], + [ + "aBAa", + "aaB", + 5 + ], + [ + "aBAaC", + "cCACbbBb", + 14 + ], + [ + "aBAabaAB", + "cacCbb", + 13 + ], + [ + "aBAabba", + "AaBABbCBc", + 9 + ], + [ + "aBAacCbbb", + "aBaCaAc", + 10 + ], + [ + "aBAacc", + "aAaAAcABC", + 10 + ], + [ + "aBAb", + "aBBbAac", + 8 + ], + [ + "aBAbAC", + "aABAbCAc", + 5 + ], + [ + "aBAbaaab", + "CCbbAaba", + 11 + ], + [ + "aBAbbB", + "aCacbAB", + 7 + ], + [ + "aBAbbcbb", + "bcBAb", + 11 + ], + [ + "aBAcABbb", + "Ca", + 14 + ], + [ + "aBAcBa", + "CbBcCaa", + 9 + ], + [ + "aBAcBbC", + "aaAaBac", + 7 + ], + [ + "aBAca", + "AACACCcAa", + 11 + ], + [ + "aBAcabBA", + "BBACb", + 9 + ], + [ + "aBAcba", + "bBaaCcB", + 10 + ], + [ + "aBAccCaba", + "CAcabBBbC", + 14 + ], + [ + "aBAcca", + "ABAABbCa", + 8 + ], + [ + "aBB", + "AaAbaAaBb", + 13 + ], + [ + "aBB", + "AaCaB", + 6 + ], + [ + "aBB", + "Acbc", + 6 + ], + [ + "aBB", + "aC", + 4 + ], + [ + "aBB", + "aa", + 4 + ], + [ + "aBB", + "bbCAAaBBB", + 12 + ], + [ + "aBBA", + "BAAc", + 6 + ], + [ + "aBBABAaAA", + "AbCAcbAA", + 10 + ], + [ + "aBBABaaaa", + "bCC", + 17 + ], + [ + "aBBABca", + "bA", + 11 + ], + [ + "aBBACC", + "caaCB", + 9 + ], + [ + "aBBAc", + "aCAA", + 6 + ], + [ + "aBBB", + "AcBcBa", + 7 + ], + [ + "aBBBA", + "CcbaAB", + 9 + ], + [ + "aBBBABAB", + "BBA", + 10 + ], + [ + "aBBBACB", + "Aa", + 12 + ], + [ + "aBBBAaA", + "bB", + 11 + ], + [ + "aBBBB", + "AaBb", + 6 + ], + [ + "aBBBCCBbb", + "BCBca", + 12 + ], + [ + "aBBBaBaAa", + "cCCB", + 16 + ], + [ + "aBBC", + "AbabA", + 7 + ], + [ + "aBBC", + "CcbcBBCb", + 10 + ], + [ + "aBBC", + "cCbbBa", + 9 + ], + [ + "aBBCBac", + "AbacacaCC", + 12 + ], + [ + "aBBCBbab", + "bb", + 12 + ], + [ + "aBBCaCCAB", + "ccBAcA", + 12 + ], + [ + "aBBCbAb", + "cACACb", + 10 + ], + [ + "aBBCbB", + "aCabbA", + 8 + ], + [ + "aBBCcabCb", + "CCacAacc", + 14 + ], + [ + "aBBaB", + "BCaccaa", + 10 + ], + [ + "aBBaB", + "bACCbA", + 11 + ], + [ + "aBBaCCab", + "CBcAbcC", + 12 + ], + [ + "aBBaa", + "a", + 8 + ], + [ + "aBBabAb", + "bACbcAa", + 11 + ], + [ + "aBBac", + "BbAa", + 6 + ], + [ + "aBBacABac", + "CbCbc", + 13 + ], + [ + "aBBb", + "BCAaBa", + 9 + ], + [ + "aBBbA", + "aaa", + 7 + ], + [ + "aBBbB", + "bbBaba", + 7 + ], + [ + "aBBbba", + "bCCcacbB", + 14 + ], + [ + "aBBc", + "BccbaCCac", + 14 + ], + [ + "aBBcABB", + "BBcAbCBAB", + 8 + ], + [ + "aBBcBAab", + "CcCBBA", + 12 + ], + [ + "aBBcBBA", + "cC", + 12 + ], + [ + "aBBcBcb", + "BBBca", + 6 + ], + [ + "aBBcaB", + "AbBAa", + 6 + ], + [ + "aBBccB", + "abcac", + 7 + ], + [ + "aBBcca", + "bABb", + 10 + ], + [ + "aBBcccbCB", + "bCcab", + 12 + ], + [ + "aBC", + "AABCBaAAC", + 13 + ], + [ + "aBC", + "CcaCAB", + 10 + ], + [ + "aBC", + "aACCc", + 6 + ], + [ + "aBC", + "aaaCcCcB", + 12 + ], + [ + "aBC", + "bABBAa", + 9 + ], + [ + "aBC", + "bC", + 3 + ], + [ + "aBC", + "baCCa", + 6 + ], + [ + "aBC", + "bc", + 4 + ], + [ + "aBCA", + "AbbcbAb", + 9 + ], + [ + "aBCA", + "CBaAa", + 6 + ], + [ + "aBCAa", + "BbCCAbAAb", + 12 + ], + [ + "aBCAcBAa", + "BC", + 12 + ], + [ + "aBCAcBCAC", + "AABaCbBcA", + 12 + ], + [ + "aBCAccBCB", + "b", + 17 + ], + [ + "aBCBA", + "b", + 9 + ], + [ + "aBCBBCa", + "aCCAaa", + 8 + ], + [ + "aBCBC", + "bCccB", + 8 + ], + [ + "aBCBC", + "bbB", + 7 + ], + [ + "aBCBabb", + "a", + 12 + ], + [ + "aBCCcc", + "caAAc", + 10 + ], + [ + "aBCaB", + "cB", + 7 + ], + [ + "aBCaCa", + "aCA", + 7 + ], + [ + "aBCaCbC", + "BcaaBaABA", + 13 + ], + [ + "aBCab", + "cAacAcCaA", + 12 + ], + [ + "aBCababA", + "ccbABc", + 11 + ], + [ + "aBCac", + "CbCBbaAB", + 11 + ], + [ + "aBCb", + "acabaC", + 8 + ], + [ + "aBCbb", + "C", + 8 + ], + [ + "aBCbbB", + "bCcBb", + 7 + ], + [ + "aBCbcCBc", + "cBaCAc", + 10 + ], + [ + "aBCc", + "bABcbCa", + 9 + ], + [ + "aBCcAc", + "cCAaaAc", + 10 + ], + [ + "aBCcAcAC", + "BCcBaca", + 8 + ], + [ + "aBCcCBaa", + "b", + 15 + ], + [ + "aBCcCBbcC", + "ba", + 16 + ], + [ + "aBCcaBAaC", + "bBbcaCC", + 10 + ], + [ + "aBa", + "cBCbb", + 8 + ], + [ + "aBa", + "caB", + 4 + ], + [ + "aBaA", + "caBAC", + 5 + ], + [ + "aBaAaAcC", + "CAbBbbabc", + 13 + ], + [ + "aBaAcCBBA", + "AcAcc", + 12 + ], + [ + "aBaAcCC", + "Bc", + 10 + ], + [ + "aBaB", + "CBBbc", + 7 + ], + [ + "aBaB", + "bBAaA", + 6 + ], + [ + "aBaBC", + "CBbcaaBA", + 10 + ], + [ + "aBaBbA", + "CCabBBAaC", + 11 + ], + [ + "aBaBbCcc", + "aB", + 12 + ], + [ + "aBaBc", + "CcCAb", + 10 + ], + [ + "aBaC", + "A", + 7 + ], + [ + "aBaC", + "aa", + 4 + ], + [ + "aBaC", + "accAbAC", + 8 + ], + [ + "aBaCCAb", + "cbBC", + 11 + ], + [ + "aBaCaA", + "Aa", + 9 + ], + [ + "aBaCaC", + "CbaCccAc", + 9 + ], + [ + "aBaCaabAc", + "bCcACb", + 13 + ], + [ + "aBaCba", + "baB", + 8 + ], + [ + "aBaa", + "CBaBAABbB", + 12 + ], + [ + "aBaa", + "Cb", + 7 + ], + [ + "aBaaA", + "aAAaABCB", + 9 + ], + [ + "aBaabC", + "bCBbAabAC", + 9 + ], + [ + "aBaabCba", + "bCCaB", + 12 + ], + [ + "aBaabaB", + "AA", + 12 + ], + [ + "aBaacBbca", + "cCc", + 14 + ], + [ + "aBabACAA", + "acABb", + 12 + ], + [ + "aBabBcb", + "bAbA", + 10 + ], + [ + "aBabbaBbc", + "cbaccAb", + 12 + ], + [ + "aBac", + "c", + 6 + ], + [ + "aBacAAcA", + "aaCB", + 11 + ], + [ + "aBacBB", + "cb", + 9 + ], + [ + "aBacC", + "ACCCcAb", + 11 + ], + [ + "aBacCB", + "abccAac", + 9 + ], + [ + "aBaccCAa", + "aBcacaaCb", + 10 + ], + [ + "aBb", + "BCBAabbac", + 13 + ], + [ + "aBb", + "Ccb", + 4 + ], + [ + "aBb", + "aaac", + 6 + ], + [ + "aBb", + "bbcbbbAaC", + 15 + ], + [ + "aBb", + "cbCaBb", + 6 + ], + [ + "aBb", + "cbaB", + 6 + ], + [ + "aBbA", + "bbaaAbab", + 11 + ], + [ + "aBbACC", + "cCcccBCC", + 12 + ], + [ + "aBbAcCBCA", + "cCBab", + 12 + ], + [ + "aBbB", + "BacA", + 8 + ], + [ + "aBbB", + "aCcCb", + 7 + ], + [ + "aBbBA", + "aAcC", + 8 + ], + [ + "aBbBB", + "aBCc", + 6 + ], + [ + "aBbBCbBAa", + "Ac", + 16 + ], + [ + "aBbBa", + "bAa", + 6 + ], + [ + "aBbBbAbC", + "Abc", + 11 + ], + [ + "aBbBbaBA", + "AbbBCccC", + 10 + ], + [ + "aBbBbcBcb", + "abaBAAAAB", + 12 + ], + [ + "aBbC", + "bCbBAcBc", + 12 + ], + [ + "aBbCABBA", + "Caa", + 12 + ], + [ + "aBbCACaCb", + "A", + 16 + ], + [ + "aBbCBCaBC", + "aCcc", + 12 + ], + [ + "aBbCaa", + "cbAC", + 9 + ], + [ + "aBbCbbbBA", + "aA", + 14 + ], + [ + "aBbCbcCAb", + "BcAC", + 12 + ], + [ + "aBbCc", + "AcACCCba", + 12 + ], + [ + "aBba", + "BcAcCBB", + 12 + ], + [ + "aBbaAACBA", + "BacacbaC", + 12 + ], + [ + "aBbaAB", + "bbCc", + 9 + ], + [ + "aBbaBcC", + "c", + 12 + ], + [ + "aBbaCcbC", + "bbbbcab", + 10 + ], + [ + "aBbaaCcaA", + "bBABBAbCc", + 13 + ], + [ + "aBbabCc", + "CaCB", + 10 + ], + [ + "aBbb", + "Bb", + 4 + ], + [ + "aBbb", + "cccAbB", + 9 + ], + [ + "aBbbA", + "AAC", + 9 + ], + [ + "aBbbAB", + "C", + 12 + ], + [ + "aBbbCab", + "AaCBacaba", + 10 + ], + [ + "aBbbbBA", + "BcbbBBA", + 5 + ], + [ + "aBbcCBaCc", + "C", + 16 + ], + [ + "aBbcCba", + "BbAcaBac", + 9 + ], + [ + "aBc", + "A", + 5 + ], + [ + "aBc", + "Bac", + 4 + ], + [ + "aBc", + "bCCc", + 6 + ], + [ + "aBc", + "baC", + 5 + ], + [ + "aBcAA", + "ABc", + 5 + ], + [ + "aBcAABabC", + "cab", + 12 + ], + [ + "aBcAaAccc", + "BacacaA", + 12 + ], + [ + "aBcAabAA", + "aBcB", + 9 + ], + [ + "aBcAc", + "bCCbbCc", + 11 + ], + [ + "aBcB", + "c", + 6 + ], + [ + "aBcBBBA", + "BbBBaAAab", + 12 + ], + [ + "aBcBBCAbb", + "AAA", + 15 + ], + [ + "aBcBaa", + "bbB", + 9 + ], + [ + "aBcBbbCb", + "cAcBCca", + 11 + ], + [ + "aBcC", + "CCAAA", + 10 + ], + [ + "aBcCABCc", + "BCbbcC", + 9 + ], + [ + "aBcCBbAc", + "cAbABaAa", + 12 + ], + [ + "aBcCCbA", + "cbA", + 8 + ], + [ + "aBcCcbaAC", + "aBBbBaB", + 11 + ], + [ + "aBca", + "BacbCCcB", + 11 + ], + [ + "aBca", + "CBAcCcCA", + 11 + ], + [ + "aBcaCC", + "c", + 10 + ], + [ + "aBcaaBB", + "BAbBaACbc", + 12 + ], + [ + "aBcbABAAa", + "bCCc", + 16 + ], + [ + "aBcbCAb", + "abA", + 8 + ], + [ + "aBcba", + "cAbCC", + 9 + ], + [ + "aBccABa", + "baBbCAAB", + 9 + ], + [ + "aBccAbac", + "CAaaCbb", + 14 + ], + [ + "aBccacCBa", + "ccbc", + 12 + ], + [ + "aBcccC", + "CccbACAcc", + 12 + ], + [ + "aC", + "AABacca", + 11 + ], + [ + "aC", + "ABABACC", + 11 + ], + [ + "aC", + "AcaBa", + 8 + ], + [ + "aC", + "AcaCBcaCB", + 14 + ], + [ + "aC", + "BBbbBcA", + 13 + ], + [ + "aC", + "BCABB", + 8 + ], + [ + "aC", + "Ba", + 4 + ], + [ + "aC", + "BaACBc", + 8 + ], + [ + "aC", + "BacccCb", + 10 + ], + [ + "aC", + "BcACaaCa", + 12 + ], + [ + "aC", + "BcB", + 5 + ], + [ + "aC", + "CAAcA", + 8 + ], + [ + "aC", + "CBbB", + 8 + ], + [ + "aC", + "CCA", + 4 + ], + [ + "aC", + "CaAA", + 6 + ], + [ + "aC", + "a", + 2 + ], + [ + "aC", + "aACaBBaB", + 12 + ], + [ + "aC", + "abCBcb", + 8 + ], + [ + "aC", + "abcabC", + 8 + ], + [ + "aC", + "bA", + 4 + ], + [ + "aC", + "bABAabc", + 11 + ], + [ + "aC", + "bBAAbCAcB", + 15 + ], + [ + "aC", + "bBaaaA", + 10 + ], + [ + "aC", + "bCBA", + 6 + ], + [ + "aC", + "baaaCC", + 8 + ], + [ + "aC", + "babcC", + 6 + ], + [ + "aC", + "bbc", + 5 + ], + [ + "aC", + "caCbAcaCc", + 14 + ], + [ + "aC", + "cbCaCaB", + 10 + ], + [ + "aCA", + "BCcBBaBc", + 13 + ], + [ + "aCA", + "CAACacC", + 10 + ], + [ + "aCA", + "bAaaC", + 8 + ], + [ + "aCA", + "babAaaBaC", + 14 + ], + [ + "aCA", + "baccbBBC", + 13 + ], + [ + "aCA", + "cBCc", + 6 + ], + [ + "aCAAA", + "aB", + 8 + ], + [ + "aCAAAaAba", + "BAbB", + 14 + ], + [ + "aCAABBBBb", + "BBABB", + 11 + ], + [ + "aCAACBcA", + "aA", + 12 + ], + [ + "aCAAaaa", + "AcbacaACb", + 12 + ], + [ + "aCAAbCBbA", + "cBc", + 15 + ], + [ + "aCAAcbC", + "CAaAcCcCc", + 10 + ], + [ + "aCAB", + "AbabAb", + 7 + ], + [ + "aCAB", + "BA", + 6 + ], + [ + "aCAB", + "CBAC", + 6 + ], + [ + "aCABAcBac", + "CCBCcbCC", + 10 + ], + [ + "aCABBC", + "ABb", + 7 + ], + [ + "aCABaAaC", + "BCaaABcC", + 9 + ], + [ + "aCABaBca", + "c", + 14 + ], + [ + "aCABaa", + "C", + 10 + ], + [ + "aCACACAC", + "baAbACC", + 8 + ], + [ + "aCACBBC", + "aCACBaAac", + 7 + ], + [ + "aCACBaaA", + "acCcCB", + 10 + ], + [ + "aCACCca", + "aBA", + 10 + ], + [ + "aCACa", + "baBbABB", + 10 + ], + [ + "aCACcbaa", + "CbAbBCBb", + 14 + ], + [ + "aCAa", + "bBCCcc", + 10 + ], + [ + "aCAaCba", + "AabAc", + 9 + ], + [ + "aCAab", + "CCCcCCAA", + 13 + ], + [ + "aCAacCAAa", + "abAcBacA", + 10 + ], + [ + "aCAb", + "Aaac", + 6 + ], + [ + "aCAbCC", + "bca", + 9 + ], + [ + "aCAbCC", + "cCABABac", + 10 + ], + [ + "aCAbCaCca", + "cACA", + 12 + ], + [ + "aCAbaBab", + "BABbA", + 11 + ], + [ + "aCAbca", + "B", + 11 + ], + [ + "aCAcBbbC", + "acaAbACAB", + 12 + ], + [ + "aCAcaCB", + "CbC", + 10 + ], + [ + "aCAcacc", + "aA", + 10 + ], + [ + "aCAcbA", + "aa", + 9 + ], + [ + "aCAcbabB", + "Aabbca", + 12 + ], + [ + "aCAccCa", + "AaBca", + 8 + ], + [ + "aCB", + "A", + 5 + ], + [ + "aCB", + "aAaCbBcA", + 10 + ], + [ + "aCB", + "aBABAaccA", + 14 + ], + [ + "aCB", + "aBcAb", + 6 + ], + [ + "aCB", + "abCbBA", + 6 + ], + [ + "aCB", + "b", + 5 + ], + [ + "aCB", + "cCcaBbc", + 10 + ], + [ + "aCBA", + "BCcCccBA", + 10 + ], + [ + "aCBA", + "cCCbcA", + 7 + ], + [ + "aCBAabBa", + "aA", + 12 + ], + [ + "aCBAb", + "B", + 8 + ], + [ + "aCBAbb", + "c", + 11 + ], + [ + "aCBB", + "bAb", + 7 + ], + [ + "aCBBAAB", + "bcbCAAAbc", + 11 + ], + [ + "aCBBBAab", + "CBcBbBcb", + 9 + ], + [ + "aCBBCcC", + "aBaCAa", + 8 + ], + [ + "aCBBabca", + "bCaaC", + 11 + ], + [ + "aCBBbb", + "cb", + 9 + ], + [ + "aCBBcBBC", + "c", + 14 + ], + [ + "aCBCCa", + "AaaABcA", + 10 + ], + [ + "aCBCc", + "bacBbaaaA", + 13 + ], + [ + "aCBa", + "cAA", + 6 + ], + [ + "aCBaABA", + "ba", + 11 + ], + [ + "aCBaAC", + "a", + 10 + ], + [ + "aCBaacBc", + "cacacCBB", + 10 + ], + [ + "aCBb", + "Ba", + 6 + ], + [ + "aCBb", + "CaAc", + 8 + ], + [ + "aCBb", + "CaBb", + 4 + ], + [ + "aCBbABbA", + "CbBcaA", + 10 + ], + [ + "aCBbACC", + "cAaBBC", + 10 + ], + [ + "aCBbBAcBB", + "aabBbCcaB", + 9 + ], + [ + "aCBbBc", + "aBabCbbaC", + 10 + ], + [ + "aCBbcccA", + "cCA", + 11 + ], + [ + "aCBc", + "ABAAaC", + 10 + ], + [ + "aCBc", + "BCcc", + 4 + ], + [ + "aCBcB", + "bBCCAC", + 10 + ], + [ + "aCBcaC", + "A", + 11 + ], + [ + "aCC", + "AB", + 5 + ], + [ + "aCC", + "Ac", + 4 + ], + [ + "aCC", + "abABbCBc", + 11 + ], + [ + "aCC", + "cBAcBCcb", + 12 + ], + [ + "aCC", + "cCCcBaa", + 10 + ], + [ + "aCCAA", + "C", + 8 + ], + [ + "aCCAB", + "bbCB", + 6 + ], + [ + "aCCABabA", + "aCa", + 10 + ], + [ + "aCCAcCbA", + "cBbCc", + 13 + ], + [ + "aCCAccAA", + "CB", + 14 + ], + [ + "aCCBBB", + "cccacc", + 10 + ], + [ + "aCCBCcA", + "aAc", + 10 + ], + [ + "aCCBCcCC", + "acBCbA", + 9 + ], + [ + "aCCC", + "ACbCcBa", + 8 + ], + [ + "aCCCBAC", + "AB", + 11 + ], + [ + "aCCCBCBa", + "a", + 14 + ], + [ + "aCCCBaAAa", + "CcBb", + 13 + ], + [ + "aCCCCB", + "cCABAcbA", + 12 + ], + [ + "aCCCCbCc", + "B", + 15 + ], + [ + "aCCCCcCb", + "AAB", + 14 + ], + [ + "aCCCab", + "BAaBcaC", + 10 + ], + [ + "aCCCc", + "aBabBBaa", + 14 + ], + [ + "aCCaA", + "aAAbb", + 8 + ], + [ + "aCCaA", + "cAA", + 6 + ], + [ + "aCCaBCcb", + "BAcc", + 12 + ], + [ + "aCCaC", + "CcacABac", + 10 + ], + [ + "aCCbBBbCB", + "baAcABCC", + 13 + ], + [ + "aCCbbAC", + "BAcbcB", + 11 + ], + [ + "aCCbbbaaB", + "abbC", + 12 + ], + [ + "aCCc", + "bAa", + 8 + ], + [ + "aCCc", + "cbaB", + 8 + ], + [ + "aCCcABBCB", + "bbC", + 14 + ], + [ + "aCCcBBABA", + "bABaacb", + 15 + ], + [ + "aCCcBCCBc", + "a", + 16 + ], + [ + "aCCcbaa", + "B", + 13 + ], + [ + "aCCccBBa", + "baAB", + 14 + ], + [ + "aCa", + "B", + 6 + ], + [ + "aCa", + "BCcC", + 6 + ], + [ + "aCa", + "bbbbCBA", + 11 + ], + [ + "aCaAAACa", + "AcCAA", + 10 + ], + [ + "aCaAAb", + "BCBccaB", + 10 + ], + [ + "aCaABBc", + "aCAbcbB", + 8 + ], + [ + "aCaABbAb", + "aBc", + 12 + ], + [ + "aCaAb", + "aCc", + 6 + ], + [ + "aCaAcCBb", + "baCcbb", + 9 + ], + [ + "aCaAcaC", + "BBCcCcbc", + 11 + ], + [ + "aCaB", + "BacaaAcBA", + 11 + ], + [ + "aCaB", + "C", + 6 + ], + [ + "aCaBAC", + "ab", + 9 + ], + [ + "aCaBBa", + "B", + 10 + ], + [ + "aCaBCB", + "a", + 10 + ], + [ + "aCaBCCB", + "cAccc", + 10 + ], + [ + "aCaBa", + "aC", + 6 + ], + [ + "aCaBacB", + "AAbcaB", + 9 + ], + [ + "aCaC", + "BBcc", + 7 + ], + [ + "aCaC", + "CAAaCAB", + 9 + ], + [ + "aCaC", + "accBcAAbA", + 14 + ], + [ + "aCaCAAac", + "CcCAB", + 10 + ], + [ + "aCaCBaaBb", + "CC", + 14 + ], + [ + "aCaCacAba", + "cbaAbBB", + 13 + ], + [ + "aCaCccca", + "CbACAc", + 11 + ], + [ + "aCaa", + "BAbB", + 8 + ], + [ + "aCaaC", + "ABacC", + 5 + ], + [ + "aCaac", + "AcabAC", + 6 + ], + [ + "aCaacB", + "CA", + 9 + ], + [ + "aCaacacbC", + "b", + 16 + ], + [ + "aCab", + "AcccBcB", + 11 + ], + [ + "aCab", + "BaaacBbbA", + 12 + ], + [ + "aCabBacCB", + "AAc", + 14 + ], + [ + "aCabCBBB", + "CBBcBbBBB", + 10 + ], + [ + "aCabCbbb", + "CBCCCcA", + 12 + ], + [ + "aCabaA", + "bABbaB", + 8 + ], + [ + "aCabbbCA", + "Ba", + 14 + ], + [ + "aCabcb", + "bBBb", + 9 + ], + [ + "aCacA", + "BbAB", + 9 + ], + [ + "aCacCa", + "baABcBcAb", + 12 + ], + [ + "aCacCcAaC", + "CbAbBa", + 14 + ], + [ + "aCacbC", + "CcbABcb", + 10 + ], + [ + "aCb", + "AaBaAc", + 10 + ], + [ + "aCb", + "BB", + 5 + ], + [ + "aCb", + "a", + 4 + ], + [ + "aCb", + "cCbBC", + 6 + ], + [ + "aCbA", + "CcCccBc", + 11 + ], + [ + "aCbA", + "bBaC", + 8 + ], + [ + "aCbAa", + "AABbbcCAC", + 13 + ], + [ + "aCbBAA", + "BcCC", + 11 + ], + [ + "aCbBAB", + "bcCBb", + 8 + ], + [ + "aCbBB", + "B", + 8 + ], + [ + "aCbBCaB", + "aCcAcaAbB", + 9 + ], + [ + "aCbBb", + "CBbA", + 6 + ], + [ + "aCbCBB", + "CAaACaA", + 11 + ], + [ + "aCbCBc", + "ABaBAcAcC", + 13 + ], + [ + "aCbCcCBCB", + "CBbc", + 13 + ], + [ + "aCba", + "aCba", + 0 + ], + [ + "aCbaa", + "BacbBccb", + 11 + ], + [ + "aCbb", + "bBcCBAAcc", + 15 + ], + [ + "aCbbCCCB", + "AaAC", + 13 + ], + [ + "aCbbaBAa", + "BbB", + 11 + ], + [ + "aCbbc", + "BBCAb", + 8 + ], + [ + "aCbbcAcB", + "accCaaB", + 9 + ], + [ + "aCbca", + "AbAc", + 7 + ], + [ + "aCbcb", + "cBcacaa", + 11 + ], + [ + "aCbcc", + "cBCbBAbB", + 12 + ], + [ + "aCbcca", + "CBbCaaCC", + 11 + ], + [ + "aCbccb", + "cAAab", + 9 + ], + [ + "aCc", + "AaAbC", + 7 + ], + [ + "aCc", + "BCbb", + 6 + ], + [ + "aCc", + "CCABBCAa", + 13 + ], + [ + "aCc", + "aAB", + 4 + ], + [ + "aCc", + "bbAcb", + 8 + ], + [ + "aCc", + "bbCcccb", + 10 + ], + [ + "aCc", + "caCAC", + 5 + ], + [ + "aCcA", + "a", + 6 + ], + [ + "aCcA", + "aC", + 4 + ], + [ + "aCcA", + "aaAAaaCBa", + 13 + ], + [ + "aCcA", + "cABb", + 8 + ], + [ + "aCcAAA", + "AbaBACBC", + 13 + ], + [ + "aCcABAc", + "BC", + 11 + ], + [ + "aCcABCB", + "b", + 13 + ], + [ + "aCcACA", + "baCcaaAa", + 7 + ], + [ + "aCcAaAba", + "aCAca", + 8 + ], + [ + "aCcAbB", + "cBCAB", + 7 + ], + [ + "aCcB", + "CCbcBaBBb", + 12 + ], + [ + "aCcBbCAC", + "CACbBb", + 11 + ], + [ + "aCcCC", + "A", + 9 + ], + [ + "aCcCCa", + "CcCabCc", + 8 + ], + [ + "aCcCabC", + "BcACaABCC", + 10 + ], + [ + "aCcCcAaA", + "baaAacBAb", + 13 + ], + [ + "aCcCca", + "cBCbCCAcb", + 11 + ], + [ + "aCcaAABb", + "BCACAAb", + 8 + ], + [ + "aCcaCAA", + "AabCbBBAC", + 12 + ], + [ + "aCcacbabC", + "a", + 16 + ], + [ + "aCcbAb", + "CbbcbaCAB", + 11 + ], + [ + "aCcbB", + "aA", + 8 + ], + [ + "aCcbBbAcC", + "bBBc", + 11 + ], + [ + "aCcbaCbca", + "bcBCCcbC", + 12 + ], + [ + "aCcbb", + "ccAabbABa", + 13 + ], + [ + "aCcbbaccb", + "AaCaCaCC", + 12 + ], + [ + "aCcbcBBA", + "CaBccABCa", + 11 + ], + [ + "aCcbcb", + "AcaacBCc", + 10 + ], + [ + "aCcc", + "baCACCa", + 8 + ], + [ + "aCccAaBa", + "BAaBcB", + 12 + ], + [ + "aCccAb", + "bBbaAcCC", + 13 + ], + [ + "aCccBB", + "CCa", + 9 + ], + [ + "aCccaaABC", + "CcBac", + 11 + ], + [ + "aCcccAaba", + "ccBbaA", + 11 + ], + [ + "aa", + "AACcaACB", + 13 + ], + [ + "aa", + "AcBCa", + 7 + ], + [ + "aa", + "Bbaba", + 6 + ], + [ + "aa", + "Bbc", + 6 + ], + [ + "aa", + "BcaBA", + 7 + ], + [ + "aa", + "C", + 4 + ], + [ + "aa", + "CBcC", + 8 + ], + [ + "aa", + "CbB", + 6 + ], + [ + "aa", + "CbBC", + 8 + ], + [ + "aa", + "a", + 2 + ], + [ + "aa", + "aABACBBb", + 13 + ], + [ + "aa", + "aBC", + 4 + ], + [ + "aa", + "aBcaaCAC", + 12 + ], + [ + "aa", + "aCbbAaaCa", + 14 + ], + [ + "aa", + "aaAaA", + 6 + ], + [ + "aa", + "aaAbcb", + 8 + ], + [ + "aa", + "aabCb", + 6 + ], + [ + "aa", + "abCBc", + 8 + ], + [ + "aa", + "abCbAb", + 9 + ], + [ + "aa", + "acAAbAAB", + 13 + ], + [ + "aa", + "bAaB", + 5 + ], + [ + "aa", + "bB", + 4 + ], + [ + "aa", + "bBAaBCA", + 11 + ], + [ + "aa", + "bCaaBbCA", + 12 + ], + [ + "aa", + "baCCBCBB", + 14 + ], + [ + "aa", + "bbAabBcCc", + 15 + ], + [ + "aa", + "bbcAC", + 9 + ], + [ + "aa", + "cAaabc", + 8 + ], + [ + "aa", + "cB", + 4 + ], + [ + "aa", + "cBB", + 6 + ], + [ + "aa", + "cBcBAAbA", + 14 + ], + [ + "aa", + "cCAB", + 7 + ], + [ + "aa", + "cCB", + 6 + ], + [ + "aa", + "caccAcB", + 11 + ], + [ + "aa", + "cb", + 4 + ], + [ + "aa", + "cbbcCB", + 12 + ], + [ + "aaA", + "A", + 4 + ], + [ + "aaA", + "ACBACBC", + 11 + ], + [ + "aaA", + "bCCA", + 6 + ], + [ + "aaA", + "bcaaAB", + 6 + ], + [ + "aaAA", + "aab", + 4 + ], + [ + "aaAAaaAaB", + "BAA", + 14 + ], + [ + "aaAAbbAC", + "cCbBbCAab", + 14 + ], + [ + "aaAAcC", + "cabbCa", + 9 + ], + [ + "aaAB", + "cAa", + 6 + ], + [ + "aaAB", + "cCaBCcB", + 10 + ], + [ + "aaABAbAC", + "CCAB", + 12 + ], + [ + "aaABAbb", + "CC", + 14 + ], + [ + "aaABB", + "Ccb", + 9 + ], + [ + "aaABCC", + "acABACB", + 6 + ], + [ + "aaABCb", + "CABbAaa", + 12 + ], + [ + "aaABaaA", + "CaaaBBabc", + 9 + ], + [ + "aaABbbCbc", + "B", + 16 + ], + [ + "aaAC", + "Ac", + 5 + ], + [ + "aaAC", + "bccBBBb", + 14 + ], + [ + "aaACa", + "cbAAaCB", + 9 + ], + [ + "aaAaAcbBC", + "A", + 16 + ], + [ + "aaAaaaAb", + "bcBc", + 16 + ], + [ + "aaAabBCbc", + "B", + 16 + ], + [ + "aaAabCaAa", + "BbbbcA", + 13 + ], + [ + "aaAabbbaC", + "bbbCaAbC", + 14 + ], + [ + "aaAac", + "cBCCc", + 8 + ], + [ + "aaAbaBbBa", + "baaCCa", + 12 + ], + [ + "aaAbcbBa", + "BbBbaaC", + 12 + ], + [ + "aaAc", + "bbCBca", + 10 + ], + [ + "aaAccCbC", + "BBa", + 15 + ], + [ + "aaB", + "bAAa", + 6 + ], + [ + "aaB", + "cccCAcAaC", + 15 + ], + [ + "aaBA", + "ABccb", + 9 + ], + [ + "aaBA", + "aca", + 5 + ], + [ + "aaBABCc", + "Aaa", + 10 + ], + [ + "aaBABaCc", + "acBaac", + 7 + ], + [ + "aaBABbCAc", + "cCb", + 16 + ], + [ + "aaBB", + "BbbBbcAB", + 12 + ], + [ + "aaBB", + "bAaAaAB", + 8 + ], + [ + "aaBB", + "bBabBA", + 7 + ], + [ + "aaBBa", + "caaB", + 6 + ], + [ + "aaBC", + "BacaAB", + 8 + ], + [ + "aaBCAaaaB", + "aaACA", + 10 + ], + [ + "aaBCC", + "BAAaBb", + 9 + ], + [ + "aaBCCa", + "CA", + 9 + ], + [ + "aaBCCbAac", + "AcaaBacc", + 13 + ], + [ + "aaBa", + "BabbcC", + 9 + ], + [ + "aaBaAaCa", + "aBbcAcAb", + 11 + ], + [ + "aaBaC", + "bcCAbac", + 9 + ], + [ + "aaBaCc", + "BCAaCCb", + 9 + ], + [ + "aaBaa", + "AAb", + 7 + ], + [ + "aaBaa", + "ccba", + 7 + ], + [ + "aaBaaAcBA", + "aCcBCAc", + 12 + ], + [ + "aaBabACBC", + "CCCc", + 15 + ], + [ + "aaBabbBA", + "aAbB", + 9 + ], + [ + "aaBacaB", + "BCABCCCbc", + 13 + ], + [ + "aaBbAaBA", + "BAc", + 12 + ], + [ + "aaBbb", + "aA", + 7 + ], + [ + "aaBbcA", + "CBcb", + 8 + ], + [ + "aaBbcbb", + "C", + 13 + ], + [ + "aaBcBaBa", + "aaBaaaB", + 6 + ], + [ + "aaBccACb", + "bCCBccC", + 10 + ], + [ + "aaC", + "ABcAca", + 9 + ], + [ + "aaC", + "BAbaCaa", + 9 + ], + [ + "aaC", + "CaCbcCc", + 10 + ], + [ + "aaC", + "Cc", + 5 + ], + [ + "aaC", + "aCa", + 4 + ], + [ + "aaC", + "caBBCb", + 8 + ], + [ + "aaCA", + "B", + 8 + ], + [ + "aaCAA", + "BcbBC", + 10 + ], + [ + "aaCABbb", + "CCcbBAb", + 9 + ], + [ + "aaCACa", + "Bb", + 12 + ], + [ + "aaCAcbc", + "AaaAAa", + 9 + ], + [ + "aaCAccaAB", + "CcCABcA", + 10 + ], + [ + "aaCC", + "B", + 8 + ], + [ + "aaCCC", + "ABAcC", + 6 + ], + [ + "aaCCCc", + "cCAaaAbAb", + 14 + ], + [ + "aaCCa", + "CAA", + 7 + ], + [ + "aaCCbaCaB", + "cCCcCAccC", + 13 + ], + [ + "aaCCcbCA", + "acAcb", + 9 + ], + [ + "aaCa", + "aCBca", + 5 + ], + [ + "aaCa", + "cAAca", + 5 + ], + [ + "aaCaABa", + "ba", + 11 + ], + [ + "aaCaABbc", + "B", + 14 + ], + [ + "aaCaBaA", + "aBcAa", + 8 + ], + [ + "aaCaa", + "Ac", + 8 + ], + [ + "aaCacA", + "CbabcA", + 8 + ], + [ + "aaCacAAb", + "CcCAc", + 10 + ], + [ + "aaCacaBC", + "BBaCcA", + 11 + ], + [ + "aaCacaba", + "ACbc", + 11 + ], + [ + "aaCbAAB", + "ABc", + 12 + ], + [ + "aaCbabb", + "Bc", + 13 + ], + [ + "aaCbabc", + "C", + 12 + ], + [ + "aaCbbAAc", + "BaBa", + 12 + ], + [ + "aaCcAcc", + "aC", + 10 + ], + [ + "aaCcaBcB", + "ccAaAbaBC", + 12 + ], + [ + "aaCcaC", + "AAABCccC", + 8 + ], + [ + "aaCccbAaa", + "B", + 17 + ], + [ + "aaa", + "ABAbCbC", + 12 + ], + [ + "aaa", + "abABCcbA", + 12 + ], + [ + "aaa", + "abaC", + 4 + ], + [ + "aaa", + "baa", + 2 + ], + [ + "aaa", + "cCbC", + 8 + ], + [ + "aaaA", + "BaCacbbc", + 12 + ], + [ + "aaaA", + "aaaabAC", + 6 + ], + [ + "aaaAAb", + "abCC", + 10 + ], + [ + "aaaAAcb", + "cbABcb", + 8 + ], + [ + "aaaABb", + "BBCCbBaba", + 14 + ], + [ + "aaaACcA", + "aaa", + 8 + ], + [ + "aaaAac", + "CCAaC", + 7 + ], + [ + "aaaAb", + "ACc", + 9 + ], + [ + "aaaB", + "b", + 7 + ], + [ + "aaaBBB", + "CcACCaCB", + 13 + ], + [ + "aaaBCAbBc", + "baCaab", + 12 + ], + [ + "aaaBaBCAb", + "BaBccAb", + 9 + ], + [ + "aaaBbAC", + "baBCAc", + 7 + ], + [ + "aaaBbBA", + "a", + 12 + ], + [ + "aaaC", + "aca", + 4 + ], + [ + "aaaCAAcAA", + "cCacb", + 13 + ], + [ + "aaaCBacCc", + "aA", + 15 + ], + [ + "aaaCaBcCC", + "ACAbBb", + 13 + ], + [ + "aaaCaaC", + "C", + 12 + ], + [ + "aaaCaaCb", + "bC", + 14 + ], + [ + "aaaCb", + "bcBbaB", + 11 + ], + [ + "aaaCbC", + "AabAc", + 8 + ], + [ + "aaaCcB", + "A", + 11 + ], + [ + "aaaa", + "CacBC", + 8 + ], + [ + "aaaaB", + "aCBcbC", + 9 + ], + [ + "aaaaBBCc", + "aCcCcaab", + 14 + ], + [ + "aaaaBCc", + "bcbaB", + 10 + ], + [ + "aaaaC", + "CBb", + 10 + ], + [ + "aaabCBaAB", + "cbBccA", + 14 + ], + [ + "aaac", + "BAaB", + 5 + ], + [ + "aaacCCB", + "aaAaacA", + 8 + ], + [ + "aab", + "CcCcaACB", + 12 + ], + [ + "aab", + "ab", + 2 + ], + [ + "aab", + "b", + 4 + ], + [ + "aab", + "bBb", + 4 + ], + [ + "aab", + "ccbCc", + 8 + ], + [ + "aabAA", + "CBCcBcac", + 14 + ], + [ + "aabACB", + "AACA", + 7 + ], + [ + "aabACB", + "Ca", + 10 + ], + [ + "aabAacba", + "bCbcBC", + 11 + ], + [ + "aabAbBc", + "AbCaCcCB", + 13 + ], + [ + "aabBAA", + "CbaACAbAA", + 10 + ], + [ + "aabBBcB", + "CbACCCc", + 13 + ], + [ + "aabCAAc", + "CBaA", + 10 + ], + [ + "aabCAcB", + "BBCA", + 9 + ], + [ + "aabaCc", + "Acab", + 9 + ], + [ + "aabb", + "a", + 6 + ], + [ + "aabbA", + "bA", + 6 + ], + [ + "aabbCAA", + "AaAAAbbC", + 11 + ], + [ + "aabbacA", + "AbC", + 10 + ], + [ + "aabbcABba", + "AC", + 16 + ], + [ + "aabc", + "A", + 7 + ], + [ + "aabc", + "Baab", + 4 + ], + [ + "aabcA", + "cabBcCc", + 8 + ], + [ + "aabcBcB", + "Ab", + 11 + ], + [ + "aabcaAA", + "abccacA", + 6 + ], + [ + "aabcbBc", + "BaCaaaA", + 12 + ], + [ + "aabcbaaac", + "BbCbccCc", + 11 + ], + [ + "aac", + "BCC", + 5 + ], + [ + "aac", + "BbCaCBbBc", + 14 + ], + [ + "aac", + "C", + 5 + ], + [ + "aac", + "a", + 4 + ], + [ + "aacA", + "CABCabaa", + 12 + ], + [ + "aacAaCCA", + "CBCbB", + 13 + ], + [ + "aacAaCCAb", + "BBCAB", + 13 + ], + [ + "aacAcb", + "baAacaCAB", + 9 + ], + [ + "aacB", + "ABcCcBAca", + 13 + ], + [ + "aacB", + "CACAABc", + 10 + ], + [ + "aacB", + "c", + 6 + ], + [ + "aacBAabc", + "caCBa", + 9 + ], + [ + "aacBc", + "aBCaCbAc", + 8 + ], + [ + "aacC", + "Ba", + 6 + ], + [ + "aacC", + "Bc", + 6 + ], + [ + "aacC", + "CAbBAbCaA", + 14 + ], + [ + "aacCCb", + "ccBAc", + 10 + ], + [ + "aacCc", + "CaB", + 8 + ], + [ + "aacaA", + "Bbaa", + 7 + ], + [ + "aacaa", + "cCB", + 8 + ], + [ + "aacaaCbBc", + "BCab", + 13 + ], + [ + "aacabaBA", + "cbbA", + 9 + ], + [ + "aacac", + "ABBCabcc", + 10 + ], + [ + "aacacAaB", + "aAbbA", + 11 + ], + [ + "aacb", + "BCcAb", + 6 + ], + [ + "aacbAAC", + "BbabBAC", + 8 + ], + [ + "aacbAcA", + "acbccC", + 6 + ], + [ + "aacbCABAC", + "AAbaCAbbA", + 11 + ], + [ + "aacbb", + "BBAbAC", + 10 + ], + [ + "aacc", + "acACBAa", + 10 + ], + [ + "aaccAaABA", + "BAbBacAC", + 14 + ], + [ + "aacccAccB", + "BBCAb", + 14 + ], + [ + "ab", + "ABca", + 6 + ], + [ + "ab", + "ACA", + 5 + ], + [ + "ab", + "AaB", + 3 + ], + [ + "ab", + "B", + 3 + ], + [ + "ab", + "BAabAa", + 8 + ], + [ + "ab", + "BAacbb", + 8 + ], + [ + "ab", + "Ba", + 4 + ], + [ + "ab", + "BbaBBa", + 9 + ], + [ + "ab", + "CACABaC", + 12 + ], + [ + "ab", + "CCbaA", + 8 + ], + [ + "ab", + "CbBACbc", + 11 + ], + [ + "ab", + "CbcbcbB", + 12 + ], + [ + "ab", + "CcB", + 5 + ], + [ + "ab", + "CcacBC", + 9 + ], + [ + "ab", + "aBBAbbaB", + 12 + ], + [ + "ab", + "aBCbBCACa", + 14 + ], + [ + "ab", + "abAbbc", + 8 + ], + [ + "ab", + "abB", + 2 + ], + [ + "ab", + "bAbCcBAaA", + 15 + ], + [ + "ab", + "baBcBa", + 9 + ], + [ + "ab", + "bb", + 2 + ], + [ + "ab", + "c", + 4 + ], + [ + "ab", + "cAAabacc", + 12 + ], + [ + "ab", + "cABcCCb", + 11 + ], + [ + "ab", + "cCbbbCa", + 12 + ], + [ + "ab", + "cbcb", + 6 + ], + [ + "ab", + "ccb", + 4 + ], + [ + "ab", + "cccaaCAA", + 14 + ], + [ + "abA", + "AaacB", + 8 + ], + [ + "abA", + "BAB", + 5 + ], + [ + "abA", + "abca", + 3 + ], + [ + "abA", + "bAAcC", + 8 + ], + [ + "abA", + "bBbaa", + 7 + ], + [ + "abA", + "bbbAcabb", + 12 + ], + [ + "abA", + "cAbCCcbb", + 13 + ], + [ + "abA", + "cBaaC", + 8 + ], + [ + "abAAB", + "BAbACC", + 7 + ], + [ + "abAAcaCa", + "c", + 14 + ], + [ + "abAB", + "AaCBBbb", + 10 + ], + [ + "abABAA", + "aBABbaabA", + 8 + ], + [ + "abABcAaC", + "aCbAcb", + 10 + ], + [ + "abABcb", + "b", + 10 + ], + [ + "abAC", + "BAC", + 3 + ], + [ + "abACAC", + "CBCaaCcA", + 12 + ], + [ + "abACC", + "BbCbBb", + 10 + ], + [ + "abACCBBa", + "abb", + 11 + ], + [ + "abACCaABb", + "aAcbb", + 10 + ], + [ + "abACb", + "Ca", + 8 + ], + [ + "abACbBAaA", + "cAAcCaCaB", + 13 + ], + [ + "abACccA", + "bBaCBC", + 9 + ], + [ + "abAaAaCCC", + "cACCbAbCC", + 13 + ], + [ + "abAaBAAC", + "ABca", + 11 + ], + [ + "abAacCCaa", + "acbCAC", + 11 + ], + [ + "abAbAcCc", + "b", + 14 + ], + [ + "abAbbBC", + "CAbbBbBb", + 8 + ], + [ + "abAc", + "bC", + 5 + ], + [ + "abAcBBbBA", + "CabccbcaA", + 11 + ], + [ + "abAccAbba", + "caCACCC", + 14 + ], + [ + "abAccBA", + "ACAbbABaa", + 12 + ], + [ + "abAccCbBa", + "cbccBbBac", + 8 + ], + [ + "abB", + "BAAA", + 7 + ], + [ + "abB", + "BACbb", + 6 + ], + [ + "abB", + "BcaBabA", + 10 + ], + [ + "abB", + "a", + 4 + ], + [ + "abB", + "cbBaACaAB", + 14 + ], + [ + "abBA", + "AACBCAbA", + 11 + ], + [ + "abBAA", + "bA", + 6 + ], + [ + "abBAAcaA", + "AACCC", + 11 + ], + [ + "abBAAcbb", + "bCCcbb", + 8 + ], + [ + "abBAB", + "aa", + 7 + ], + [ + "abBABBA", + "BBCBccbaA", + 12 + ], + [ + "abBAaBa", + "bbaBCcbc", + 11 + ], + [ + "abBAaCacb", + "bbaca", + 10 + ], + [ + "abBAaCcc", + "ccCACB", + 12 + ], + [ + "abBAc", + "bB", + 6 + ], + [ + "abBAc", + "bBB", + 6 + ], + [ + "abBB", + "BbaBbCbb", + 10 + ], + [ + "abBBCc", + "b", + 10 + ], + [ + "abBBaAcBc", + "bbCaB", + 11 + ], + [ + "abBBaBbCc", + "Cac", + 14 + ], + [ + "abBBac", + "BAc", + 7 + ], + [ + "abBBb", + "A", + 9 + ], + [ + "abBBb", + "ACAAc", + 9 + ], + [ + "abBBbB", + "b", + 10 + ], + [ + "abBCACBBc", + "B", + 16 + ], + [ + "abBCBAB", + "cCA", + 10 + ], + [ + "abBCCb", + "AbaCcCcA", + 9 + ], + [ + "abBCCcc", + "BcBCCbCbb", + 11 + ], + [ + "abBCac", + "cCcbACcC", + 11 + ], + [ + "abBCcCbA", + "bC", + 12 + ], + [ + "abBa", + "cbabba", + 5 + ], + [ + "abBaA", + "CA", + 8 + ], + [ + "abBaAAc", + "BB", + 11 + ], + [ + "abBaBcabA", + "BBBAabAba", + 10 + ], + [ + "abBaCbB", + "CaCaacbC", + 9 + ], + [ + "abBabAb", + "Aacb", + 9 + ], + [ + "abBacbc", + "cAccCaaa", + 15 + ], + [ + "abBacca", + "bAACac", + 10 + ], + [ + "abBb", + "ABCaACb", + 10 + ], + [ + "abBb", + "BAABc", + 7 + ], + [ + "abBb", + "c", + 8 + ], + [ + "abBb", + "cAB", + 6 + ], + [ + "abBbBB", + "Aaccb", + 10 + ], + [ + "abBbC", + "aaccbac", + 9 + ], + [ + "abBbCBBa", + "C", + 14 + ], + [ + "abBbb", + "BBA", + 7 + ], + [ + "abBbba", + "CBbAAb", + 9 + ], + [ + "abBc", + "AABBCcBcc", + 12 + ], + [ + "abBc", + "bcbbCABca", + 12 + ], + [ + "abBcB", + "CbCaCca", + 10 + ], + [ + "abBcBBCA", + "BCCCacAac", + 15 + ], + [ + "abBcaBa", + "bcBCCc", + 11 + ], + [ + "abC", + "ACAbb", + 7 + ], + [ + "abC", + "Cc", + 5 + ], + [ + "abC", + "aAabBb", + 8 + ], + [ + "abC", + "b", + 4 + ], + [ + "abC", + "bccBAC", + 9 + ], + [ + "abC", + "bccabCBc", + 10 + ], + [ + "abC", + "cBA", + 5 + ], + [ + "abC", + "cCB", + 6 + ], + [ + "abCA", + "Acb", + 6 + ], + [ + "abCA", + "cc", + 7 + ], + [ + "abCAA", + "BAcbCCBc", + 11 + ], + [ + "abCAAcbCB", + "acCabB", + 9 + ], + [ + "abCAaA", + "AcbaAB", + 8 + ], + [ + "abCB", + "BABBbAABA", + 13 + ], + [ + "abCBB", + "Bb", + 7 + ], + [ + "abCBCB", + "baCaaa", + 10 + ], + [ + "abCC", + "CA", + 6 + ], + [ + "abCC", + "CCcCAcaA", + 13 + ], + [ + "abCCAacb", + "babaA", + 12 + ], + [ + "abCCBB", + "baB", + 8 + ], + [ + "abCCBBC", + "aAc", + 11 + ], + [ + "abCCCBaAC", + "CCBbCbBA", + 13 + ], + [ + "abCCaAca", + "bBCBCaC", + 10 + ], + [ + "abCCbBBaA", + "Ab", + 15 + ], + [ + "abCCc", + "BcbBabBc", + 12 + ], + [ + "abCCcAcBB", + "BaAAAaAB", + 13 + ], + [ + "abCa", + "ABA", + 5 + ], + [ + "abCaBC", + "bCCaCaA", + 10 + ], + [ + "abCaC", + "acc", + 6 + ], + [ + "abCaCAbB", + "aCaba", + 8 + ], + [ + "abCabA", + "ACba", + 6 + ], + [ + "abCabbccb", + "CcAcA", + 14 + ], + [ + "abCacaaC", + "aC", + 12 + ], + [ + "abCb", + "cBCBaA", + 8 + ], + [ + "abCbBCcAa", + "a", + 16 + ], + [ + "abCbaCC", + "CAabaabBB", + 14 + ], + [ + "abCbbba", + "CcbBCBAA", + 12 + ], + [ + "abCc", + "Ca", + 6 + ], + [ + "abCcAbbC", + "B", + 15 + ], + [ + "abCcCCC", + "BbAaca", + 11 + ], + [ + "abCcCc", + "ACacA", + 8 + ], + [ + "abCcaA", + "aca", + 6 + ], + [ + "abCcaABB", + "AbcBb", + 8 + ], + [ + "aba", + "CBb", + 5 + ], + [ + "aba", + "CBbbaAb", + 10 + ], + [ + "aba", + "aCccA", + 7 + ], + [ + "aba", + "acaB", + 4 + ], + [ + "aba", + "bcCCa", + 8 + ], + [ + "aba", + "cBB", + 5 + ], + [ + "abaAAA", + "cAbB", + 10 + ], + [ + "abaACCcC", + "abacCBA", + 7 + ], + [ + "abaAaccC", + "CCaacc", + 8 + ], + [ + "abaAbb", + "a", + 10 + ], + [ + "abaBCABc", + "aCcccB", + 11 + ], + [ + "abaBaCca", + "aAcAbaB", + 12 + ], + [ + "abaC", + "Bba", + 4 + ], + [ + "abaCACccC", + "BBBBB", + 17 + ], + [ + "abaCBABaB", + "abacbacc", + 9 + ], + [ + "abaCCbcA", + "AcBBc", + 11 + ], + [ + "abaCbB", + "cC", + 10 + ], + [ + "abaCbaCB", + "AAcC", + 11 + ], + [ + "abaCbaa", + "aCCaCCA", + 9 + ], + [ + "abaaACcB", + "cBBCBBAbC", + 16 + ], + [ + "abab", + "CcaCC", + 8 + ], + [ + "abab", + "aCAAaaaB", + 11 + ], + [ + "abab", + "acBbA", + 6 + ], + [ + "ababA", + "CBBC", + 8 + ], + [ + "ababACcC", + "BC", + 13 + ], + [ + "ababCBBbB", + "CbC", + 14 + ], + [ + "ababa", + "CABCcCcb", + 14 + ], + [ + "ababcCb", + "aCAbCc", + 7 + ], + [ + "abacA", + "ABb", + 8 + ], + [ + "abacABc", + "babCCAbb", + 8 + ], + [ + "abacAc", + "BacbCaaB", + 11 + ], + [ + "abacBBa", + "CbCAa", + 9 + ], + [ + "abacBbaCC", + "a", + 16 + ], + [ + "abacCb", + "b", + 10 + ], + [ + "abaca", + "cAB", + 9 + ], + [ + "abacaCbBc", + "bACcACB", + 10 + ], + [ + "abaccABb", + "cBcCc", + 12 + ], + [ + "abaccaAcA", + "B", + 17 + ], + [ + "abacccc", + "Cba", + 10 + ], + [ + "abb", + "B", + 5 + ], + [ + "abb", + "CbBBAc", + 9 + ], + [ + "abb", + "aC", + 4 + ], + [ + "abb", + "bbbcaca", + 10 + ], + [ + "abbAAABBb", + "CbCcB", + 14 + ], + [ + "abbAcB", + "BbaaBc", + 8 + ], + [ + "abbBBCcB", + "aaa", + 14 + ], + [ + "abbBbAbcc", + "c", + 16 + ], + [ + "abbBbbbcC", + "aBbBaB", + 10 + ], + [ + "abbCBA", + "aA", + 8 + ], + [ + "abbCBA", + "bb", + 8 + ], + [ + "abbCBabac", + "CBAB", + 12 + ], + [ + "abbCaBB", + "Ab", + 11 + ], + [ + "abbCaBC", + "aBcc", + 9 + ], + [ + "abbCbbBB", + "cbbaaca", + 12 + ], + [ + "abbCc", + "bC", + 6 + ], + [ + "abbaA", + "BabaAAaBb", + 11 + ], + [ + "abbaBaaBc", + "CAcBbc", + 13 + ], + [ + "abbaBbc", + "aABbAcc", + 8 + ], + [ + "abbaCcba", + "Cba", + 10 + ], + [ + "abbbA", + "ACAcc", + 9 + ], + [ + "abbbBAbAA", + "aCACcAbA", + 10 + ], + [ + "abbbCCCaC", + "acBCBABcC", + 13 + ], + [ + "abbbb", + "cCcbBcb", + 9 + ], + [ + "abbbc", + "C", + 9 + ], + [ + "abbbca", + "BBAcBbAB", + 12 + ], + [ + "abbcABB", + "ac", + 10 + ], + [ + "abbcB", + "ABbcCB", + 4 + ], + [ + "abbcBac", + "bACca", + 9 + ], + [ + "abbccAa", + "AcC", + 10 + ], + [ + "abbccb", + "cbCb", + 7 + ], + [ + "abc", + "AbC", + 2 + ], + [ + "abc", + "BAAb", + 7 + ], + [ + "abc", + "C", + 5 + ], + [ + "abc", + "aB", + 3 + ], + [ + "abcAA", + "CAAabC", + 11 + ], + [ + "abcAaaccb", + "cAcAAcbcA", + 11 + ], + [ + "abcAbB", + "AbcCacc", + 8 + ], + [ + "abcAcaB", + "acBcBb", + 7 + ], + [ + "abcAcbbb", + "CaABc", + 13 + ], + [ + "abcB", + "AaCcAbCa", + 11 + ], + [ + "abcB", + "bbab", + 5 + ], + [ + "abcBaBb", + "cBBa", + 8 + ], + [ + "abcBacc", + "ACBa", + 8 + ], + [ + "abcBb", + "BBbcccBB", + 9 + ], + [ + "abcBbBAb", + "cB", + 12 + ], + [ + "abcC", + "BCc", + 5 + ], + [ + "abcCAB", + "CbCcC", + 8 + ], + [ + "abcCBAc", + "ACCBaA", + 7 + ], + [ + "abcCaBcCB", + "AAba", + 15 + ], + [ + "abcCbBCA", + "Ba", + 13 + ], + [ + "abca", + "cbaccAA", + 9 + ], + [ + "abcaCB", + "CCaaAB", + 8 + ], + [ + "abcb", + "a", + 6 + ], + [ + "abcbbC", + "BBc", + 9 + ], + [ + "abcbbbcb", + "b", + 14 + ], + [ + "abccC", + "bbcBab", + 8 + ], + [ + "abccCBabC", + "AbABaaabc", + 10 + ], + [ + "abccCaB", + "Bbaa", + 10 + ], + [ + "abccCcAa", + "BCbb", + 13 + ], + [ + "abccaB", + "aCABaba", + 9 + ], + [ + "abccaC", + "ABbCB", + 9 + ], + [ + "abccaCcBB", + "AAB", + 14 + ], + [ + "abccbCC", + "BAc", + 11 + ], + [ + "ac", + "A", + 3 + ], + [ + "ac", + "AAaCcbBa", + 12 + ], + [ + "ac", + "AAcCbbCca", + 15 + ], + [ + "ac", + "ACAcCa", + 9 + ], + [ + "ac", + "ACCBbAcCc", + 15 + ], + [ + "ac", + "B", + 4 + ], + [ + "ac", + "BAab", + 6 + ], + [ + "ac", + "BCCBbccb", + 14 + ], + [ + "ac", + "BCc", + 4 + ], + [ + "ac", + "BaBbbAA", + 12 + ], + [ + "ac", + "BcCACbB", + 12 + ], + [ + "ac", + "CBA", + 6 + ], + [ + "ac", + "CccBcaB", + 12 + ], + [ + "ac", + "aB", + 2 + ], + [ + "ac", + "aBBCacBbC", + 14 + ], + [ + "ac", + "aC", + 1 + ], + [ + "ac", + "aCcCC", + 6 + ], + [ + "ac", + "aaCaCaca", + 12 + ], + [ + "ac", + "ab", + 2 + ], + [ + "ac", + "acacCAbBA", + 14 + ], + [ + "ac", + "bACaaCCCC", + 15 + ], + [ + "ac", + "bAbaCCcBC", + 14 + ], + [ + "ac", + "bCCaAaa", + 12 + ], + [ + "ac", + "cBAcc", + 7 + ], + [ + "acA", + "AaCCAaabc", + 13 + ], + [ + "acA", + "aABBBACc", + 12 + ], + [ + "acA", + "ac", + 2 + ], + [ + "acA", + "cBcBb", + 8 + ], + [ + "acAA", + "bc", + 6 + ], + [ + "acAAABBa", + "cbcC", + 13 + ], + [ + "acAACBA", + "BcbaCbbA", + 8 + ], + [ + "acAB", + "ccbAAAa", + 10 + ], + [ + "acABCCA", + "BCcABBAa", + 9 + ], + [ + "acABc", + "BBcABbCba", + 11 + ], + [ + "acAC", + "A", + 6 + ], + [ + "acACCB", + "AAcBCba", + 9 + ], + [ + "acAaaAcba", + "BabCcbCbb", + 15 + ], + [ + "acAaaaBc", + "BacCBc", + 10 + ], + [ + "acAabACa", + "acaCB", + 8 + ], + [ + "acAabBbA", + "cabCb", + 8 + ], + [ + "acAacCc", + "BBCC", + 11 + ], + [ + "acAb", + "cc", + 6 + ], + [ + "acAbabcBa", + "ABABcaB", + 11 + ], + [ + "acAc", + "ABa", + 6 + ], + [ + "acAc", + "CcBCaa", + 9 + ], + [ + "acAc", + "aBbCaB", + 8 + ], + [ + "acAcBCCa", + "BaB", + 13 + ], + [ + "acAcCAc", + "cAbab", + 9 + ], + [ + "acAcaC", + "B", + 12 + ], + [ + "acAcabB", + "baaacbB", + 7 + ], + [ + "acAcbcAC", + "ccaCbCcc", + 8 + ], + [ + "acB", + "ABcAcBAcb", + 13 + ], + [ + "acB", + "acb", + 1 + ], + [ + "acB", + "bCccA", + 8 + ], + [ + "acB", + "cbabb", + 7 + ], + [ + "acBA", + "BcAaBCBAA", + 11 + ], + [ + "acBAcc", + "AAabb", + 10 + ], + [ + "acBB", + "AaaAbB", + 7 + ], + [ + "acBB", + "cA", + 6 + ], + [ + "acBBAb", + "bCcAbBCAC", + 11 + ], + [ + "acBBAbacB", + "aaCBB", + 12 + ], + [ + "acBBBbAcb", + "BAa", + 14 + ], + [ + "acBBCC", + "ccAbcBbCb", + 10 + ], + [ + "acBBCc", + "bcCaAbAa", + 13 + ], + [ + "acBBbBa", + "Cba", + 9 + ], + [ + "acBBcCC", + "ABbb", + 10 + ], + [ + "acBCBcabA", + "caCbca", + 9 + ], + [ + "acBCCbab", + "ccA", + 12 + ], + [ + "acBCa", + "BabbAcb", + 10 + ], + [ + "acBa", + "AC", + 6 + ], + [ + "acBaA", + "ABBCC", + 7 + ], + [ + "acBaAacc", + "bbab", + 13 + ], + [ + "acBaBCab", + "ac", + 12 + ], + [ + "acBaBaC", + "bbccbac", + 10 + ], + [ + "acBacCbAc", + "bBBACbaAA", + 11 + ], + [ + "acBb", + "CA", + 7 + ], + [ + "acBb", + "bCBAAB", + 8 + ], + [ + "acBbAc", + "aaaBCA", + 8 + ], + [ + "acBbCca", + "acA", + 9 + ], + [ + "acBcCcaB", + "CC", + 13 + ], + [ + "acBcabcAC", + "Aac", + 13 + ], + [ + "acC", + "AABcbaC", + 9 + ], + [ + "acC", + "AcBAaC", + 7 + ], + [ + "acC", + "CabBabaBC", + 14 + ], + [ + "acC", + "b", + 6 + ], + [ + "acC", + "baBCBCbAC", + 13 + ], + [ + "acC", + "baaCBCBC", + 11 + ], + [ + "acCA", + "C", + 6 + ], + [ + "acCA", + "aabBCaaaC", + 13 + ], + [ + "acCAAaCB", + "ba", + 14 + ], + [ + "acCAB", + "BccCcCbB", + 10 + ], + [ + "acCAa", + "c", + 8 + ], + [ + "acCAabBcc", + "Bb", + 16 + ], + [ + "acCAb", + "abB", + 7 + ], + [ + "acCAcc", + "cccCCacCC", + 10 + ], + [ + "acCB", + "B", + 6 + ], + [ + "acCBAcb", + "acaBcA", + 6 + ], + [ + "acCBBA", + "caccBbabC", + 9 + ], + [ + "acCBBb", + "aCbc", + 7 + ], + [ + "acCBCbaBC", + "bCabBc", + 11 + ], + [ + "acCBaaAc", + "BAAABCA", + 13 + ], + [ + "acCC", + "BAB", + 8 + ], + [ + "acCCABBb", + "Bbcca", + 13 + ], + [ + "acCCCaAb", + "acCCbAcca", + 9 + ], + [ + "acCCab", + "aB", + 9 + ], + [ + "acCCbA", + "CCAbCbBA", + 9 + ], + [ + "acCCbBcc", + "aAAaA", + 14 + ], + [ + "acCCcaC", + "AcbBabcCb", + 13 + ], + [ + "acCCcab", + "ccab", + 6 + ], + [ + "acCa", + "aaAcAbBbB", + 14 + ], + [ + "acCaAA", + "BAbcAcbC", + 13 + ], + [ + "acCaABcbb", + "A", + 16 + ], + [ + "acCaC", + "abBACBCB", + 10 + ], + [ + "acCaCa", + "aBa", + 8 + ], + [ + "acCb", + "aACBa", + 5 + ], + [ + "acCbBBa", + "BbbACCccb", + 15 + ], + [ + "acCbCCCBb", + "B", + 16 + ], + [ + "acCbbBB", + "bcca", + 11 + ], + [ + "acCc", + "A", + 7 + ], + [ + "acCc", + "AABBa", + 9 + ], + [ + "acCcA", + "CBBcaBCCA", + 11 + ], + [ + "acCcAcB", + "b", + 13 + ], + [ + "acCcB", + "BBCCBA", + 7 + ], + [ + "acCcaAA", + "b", + 14 + ], + [ + "acCcacB", + "cbAccBcb", + 10 + ], + [ + "acCcc", + "AC", + 7 + ], + [ + "aca", + "ACAB", + 5 + ], + [ + "aca", + "AaAc", + 6 + ], + [ + "aca", + "cAAbacBCa", + 12 + ], + [ + "aca", + "cbCCabAaB", + 14 + ], + [ + "acaA", + "C", + 7 + ], + [ + "acaA", + "aCbcACABa", + 11 + ], + [ + "acaAAAbc", + "B", + 15 + ], + [ + "acaABAcA", + "Cacca", + 10 + ], + [ + "acaAcb", + "cbc", + 8 + ], + [ + "acaBA", + "baBCbCBBC", + 13 + ], + [ + "acaBcacBc", + "b", + 17 + ], + [ + "acaBccaC", + "C", + 14 + ], + [ + "acaC", + "bccCAccA", + 12 + ], + [ + "acaCAaBB", + "Bc", + 14 + ], + [ + "acaCBBB", + "A", + 13 + ], + [ + "acaCaA", + "BCB", + 10 + ], + [ + "acaCaC", + "c", + 10 + ], + [ + "acaCbC", + "acaabCaa", + 6 + ], + [ + "acaCcaAB", + "ABAaaaba", + 12 + ], + [ + "acaCcbcB", + "CcbACBBc", + 10 + ], + [ + "acaCcbcc", + "C", + 14 + ], + [ + "acaa", + "AabBa", + 6 + ], + [ + "acaaBAA", + "cBBbaAB", + 10 + ], + [ + "acaaCC", + "cAACA", + 6 + ], + [ + "acaaaCCA", + "AaC", + 11 + ], + [ + "acaacAbB", + "bCbb", + 12 + ], + [ + "acaaca", + "cBACccCa", + 11 + ], + [ + "acaacb", + "ccCbBca", + 10 + ], + [ + "acabCb", + "ccaCbA", + 6 + ], + [ + "acabaBCbB", + "cab", + 12 + ], + [ + "acabcA", + "AB", + 10 + ], + [ + "acacA", + "bcBACcaa", + 10 + ], + [ + "acacbA", + "A", + 10 + ], + [ + "acacbABa", + "bCbcBa", + 9 + ], + [ + "acacbBa", + "BAbA", + 10 + ], + [ + "acb", + "AabAbA", + 8 + ], + [ + "acb", + "BCCa", + 7 + ], + [ + "acb", + "cBCB", + 6 + ], + [ + "acbA", + "AcBCbCa", + 8 + ], + [ + "acbA", + "aAaBCAB", + 9 + ], + [ + "acbAA", + "bc", + 8 + ], + [ + "acbACbbA", + "a", + 14 + ], + [ + "acbAbacc", + "caAcB", + 10 + ], + [ + "acbB", + "aBAb", + 5 + ], + [ + "acbBBC", + "caB", + 8 + ], + [ + "acbBaAAb", + "bbBcABBaC", + 13 + ], + [ + "acbBaC", + "CaA", + 9 + ], + [ + "acbBc", + "abB", + 4 + ], + [ + "acbC", + "ABABaBbca", + 13 + ], + [ + "acbC", + "bbBbcaaA", + 13 + ], + [ + "acbCA", + "A", + 8 + ], + [ + "acbCA", + "cBb", + 7 + ], + [ + "acbCBb", + "acAbCCaBc", + 8 + ], + [ + "acbCaaAaB", + "CaCc", + 14 + ], + [ + "acbCabC", + "cBACbcBC", + 10 + ], + [ + "acbaBaA", + "BBcA", + 9 + ], + [ + "acbabAa", + "CaAbcbc", + 10 + ], + [ + "acbabbaB", + "c", + 14 + ], + [ + "acbb", + "AAa", + 7 + ], + [ + "acbb", + "AbCaBCcBA", + 13 + ], + [ + "acbb", + "BbAca", + 9 + ], + [ + "acbb", + "CBaCBcCbA", + 12 + ], + [ + "acbbA", + "AcCAAbc", + 9 + ], + [ + "acbbAbA", + "CCCaccab", + 13 + ], + [ + "acbbcABBB", + "BcCBbb", + 11 + ], + [ + "acbc", + "aBAbB", + 6 + ], + [ + "acbcAa", + "BaCC", + 10 + ], + [ + "acbcBbc", + "B", + 12 + ], + [ + "acbcCAC", + "ACaAab", + 11 + ], + [ + "acbcCab", + "BAb", + 10 + ], + [ + "acbcaacbc", + "CbccCCBcb", + 11 + ], + [ + "acbcbbcc", + "CcAbaCCbb", + 13 + ], + [ + "acbccACA", + "b", + 14 + ], + [ + "acbccBcB", + "CCcc", + 10 + ], + [ + "acbccCB", + "bbbaaCAAB", + 12 + ], + [ + "acc", + "C", + 5 + ], + [ + "acc", + "bAac", + 5 + ], + [ + "accA", + "CBBaA", + 8 + ], + [ + "accAA", + "C", + 9 + ], + [ + "accABbb", + "cBbAAbaC", + 12 + ], + [ + "accAac", + "CCcacb", + 7 + ], + [ + "accAb", + "bcbAbB", + 6 + ], + [ + "accAcbAa", + "B", + 15 + ], + [ + "accB", + "bBbAb", + 9 + ], + [ + "accBC", + "bCCCCab", + 10 + ], + [ + "accBbcCaa", + "aaCcaBACB", + 12 + ], + [ + "accC", + "CCcbBc", + 8 + ], + [ + "accC", + "aa", + 6 + ], + [ + "accCAcACa", + "AABbCCc", + 14 + ], + [ + "accCCaCB", + "aCBba", + 11 + ], + [ + "accCCcA", + "cBbA", + 10 + ], + [ + "accCaB", + "aBaB", + 6 + ], + [ + "accCbbC", + "cBBcaa", + 12 + ], + [ + "acca", + "BBCAcbaba", + 13 + ], + [ + "acca", + "BBcCC", + 7 + ], + [ + "accaACAAc", + "cbbCABA", + 12 + ], + [ + "accaaAcAb", + "ccB", + 13 + ], + [ + "accaaC", + "CbA", + 10 + ], + [ + "accab", + "AAaAcA", + 9 + ], + [ + "accabbcB", + "aa", + 12 + ], + [ + "accac", + "C", + 9 + ], + [ + "accb", + "CBCAcCaBB", + 13 + ], + [ + "accbAA", + "Ba", + 10 + ], + [ + "accbAa", + "Ca", + 9 + ], + [ + "accbBac", + "caC", + 9 + ], + [ + "accbCcBAc", + "cbA", + 12 + ], + [ + "accbaAc", + "ACAaCAAA", + 11 + ], + [ + "accbb", + "CCcC", + 7 + ], + [ + "acccAB", + "cb", + 9 + ], + [ + "acccC", + "baaaaB", + 10 + ], + [ + "acccaAaB", + "C", + 15 + ], + [ + "acccc", + "cAAC", + 7 + ], + [ + "b", + "A", + 2 + ], + [ + "b", + "AAbAaCb", + 12 + ], + [ + "b", + "ABBBC", + 9 + ], + [ + "b", + "ABbCCBC", + 12 + ], + [ + "b", + "ABc", + 5 + ], + [ + "b", + "ABcba", + 8 + ], + [ + "b", + "ACAAC", + 10 + ], + [ + "b", + "ACBbAcabc", + 16 + ], + [ + "b", + "ACBbCBcCC", + 16 + ], + [ + "b", + "ACCaA", + 10 + ], + [ + "b", + "Aa", + 4 + ], + [ + "b", + "AaAbBCA", + 12 + ], + [ + "b", + "Aac", + 6 + ], + [ + "b", + "AacaBc", + 11 + ], + [ + "b", + "AbaC", + 6 + ], + [ + "b", + "Abcba", + 8 + ], + [ + "b", + "Ac", + 4 + ], + [ + "b", + "AcCbB", + 8 + ], + [ + "b", + "AcCbBca", + 12 + ], + [ + "b", + "AcaaCaAa", + 16 + ], + [ + "b", + "B", + 1 + ], + [ + "b", + "BBA", + 5 + ], + [ + "b", + "BBaaabB", + 12 + ], + [ + "b", + "BBbaBcba", + 14 + ], + [ + "b", + "BCBaC", + 9 + ], + [ + "b", + "BCcAABb", + 12 + ], + [ + "b", + "Ba", + 3 + ], + [ + "b", + "BaC", + 5 + ], + [ + "b", + "BaCbBc", + 10 + ], + [ + "b", + "Bac", + 5 + ], + [ + "b", + "BacBAc", + 11 + ], + [ + "b", + "Bb", + 2 + ], + [ + "b", + "BbA", + 4 + ], + [ + "b", + "BbBacbb", + 12 + ], + [ + "b", + "BbacaCc", + 12 + ], + [ + "b", + "BbbcAcAab", + 16 + ], + [ + "b", + "Bc", + 3 + ], + [ + "b", + "BcBA", + 7 + ], + [ + "b", + "BcBCAAaCB", + 17 + ], + [ + "b", + "BcC", + 5 + ], + [ + "b", + "BcCa", + 7 + ], + [ + "b", + "C", + 2 + ], + [ + "b", + "CAB", + 5 + ], + [ + "b", + "CACCBca", + 13 + ], + [ + "b", + "CBABaBAB", + 15 + ], + [ + "b", + "CBCCa", + 9 + ], + [ + "b", + "CBCCcC", + 11 + ], + [ + "b", + "CBCcaBBAB", + 17 + ], + [ + "b", + "CBaBaaCbB", + 16 + ], + [ + "b", + "CBaabA", + 10 + ], + [ + "b", + "CCBABBcC", + 15 + ], + [ + "b", + "CCC", + 6 + ], + [ + "b", + "CCbCA", + 8 + ], + [ + "b", + "Ca", + 4 + ], + [ + "b", + "CaCBcA", + 11 + ], + [ + "b", + "Cabacc", + 10 + ], + [ + "b", + "CabccCBCB", + 16 + ], + [ + "b", + "CacAbAAac", + 16 + ], + [ + "b", + "CbA", + 4 + ], + [ + "b", + "CbcBbA", + 10 + ], + [ + "b", + "Cc", + 4 + ], + [ + "b", + "CcAABbA", + 12 + ], + [ + "b", + "CcabBB", + 10 + ], + [ + "b", + "CcacCcAcC", + 18 + ], + [ + "b", + "CcbCACB", + 12 + ], + [ + "b", + "Ccbc", + 6 + ], + [ + "b", + "a", + 2 + ], + [ + "b", + "aA", + 4 + ], + [ + "b", + "aABBCcC", + 13 + ], + [ + "b", + "aABccbaCc", + 16 + ], + [ + "b", + "aACCCCba", + 14 + ], + [ + "b", + "aACCa", + 10 + ], + [ + "b", + "aAaBA", + 9 + ], + [ + "b", + "aAacBab", + 12 + ], + [ + "b", + "aB", + 3 + ], + [ + "b", + "aBCAac", + 11 + ], + [ + "b", + "aBCBC", + 9 + ], + [ + "b", + "aBCbacab", + 14 + ], + [ + "b", + "aBaCa", + 9 + ], + [ + "b", + "aBc", + 5 + ], + [ + "b", + "aC", + 4 + ], + [ + "b", + "aCBA", + 7 + ], + [ + "b", + "aCCa", + 8 + ], + [ + "b", + "aa", + 4 + ], + [ + "b", + "aabCCBca", + 14 + ], + [ + "b", + "aabbAb", + 10 + ], + [ + "b", + "aabbcC", + 10 + ], + [ + "b", + "ab", + 2 + ], + [ + "b", + "abB", + 4 + ], + [ + "b", + "abBBcb", + 10 + ], + [ + "b", + "abCCBCA", + 12 + ], + [ + "b", + "abcACcaa", + 14 + ], + [ + "b", + "abcBccCa", + 14 + ], + [ + "b", + "abca", + 6 + ], + [ + "b", + "ac", + 4 + ], + [ + "b", + "acACCaAca", + 18 + ], + [ + "b", + "acC", + 6 + ], + [ + "b", + "aca", + 6 + ], + [ + "b", + "acaabCC", + 12 + ], + [ + "b", + "b", + 0 + ], + [ + "b", + "bACAbB", + 10 + ], + [ + "b", + "bBAccb", + 10 + ], + [ + "b", + "bBBC", + 6 + ], + [ + "b", + "bBbaAA", + 10 + ], + [ + "b", + "bC", + 2 + ], + [ + "b", + "bCA", + 4 + ], + [ + "b", + "bCBCAbaCA", + 16 + ], + [ + "b", + "bCCbA", + 8 + ], + [ + "b", + "bCaAC", + 8 + ], + [ + "b", + "bCaCAB", + 10 + ], + [ + "b", + "bCabBB", + 10 + ], + [ + "b", + "bCbBbcA", + 12 + ], + [ + "b", + "bCcbAb", + 10 + ], + [ + "b", + "baBACBaaB", + 16 + ], + [ + "b", + "baCCbBC", + 12 + ], + [ + "b", + "baabBABb", + 14 + ], + [ + "b", + "baabbaCBB", + 16 + ], + [ + "b", + "baaccc", + 10 + ], + [ + "b", + "babBAcb", + 12 + ], + [ + "b", + "bb", + 2 + ], + [ + "b", + "bcBACbaBb", + 16 + ], + [ + "b", + "bcCa", + 6 + ], + [ + "b", + "bcCcAcbc", + 14 + ], + [ + "b", + "c", + 2 + ], + [ + "b", + "cABCaB", + 11 + ], + [ + "b", + "cAC", + 6 + ], + [ + "b", + "cAbBbBcc", + 14 + ], + [ + "b", + "cAbaBABBc", + 16 + ], + [ + "b", + "cAbaCBAa", + 14 + ], + [ + "b", + "cBAAAcaA", + 15 + ], + [ + "b", + "cBBcbAabb", + 16 + ], + [ + "b", + "cBCCAcb", + 12 + ], + [ + "b", + "cBCCa", + 9 + ], + [ + "b", + "cBCaCAc", + 13 + ], + [ + "b", + "cBbaBbcB", + 14 + ], + [ + "b", + "cBbbBaAa", + 14 + ], + [ + "b", + "cCAACba", + 12 + ], + [ + "b", + "cCaccAaCb", + 16 + ], + [ + "b", + "cCcbA", + 8 + ], + [ + "b", + "caA", + 6 + ], + [ + "b", + "caAb", + 6 + ], + [ + "b", + "caAccaCbb", + 16 + ], + [ + "b", + "caB", + 5 + ], + [ + "b", + "cabBbcA", + 12 + ], + [ + "b", + "cacbbBAb", + 14 + ], + [ + "b", + "cb", + 2 + ], + [ + "b", + "cbAa", + 6 + ], + [ + "b", + "cbAaCa", + 10 + ], + [ + "b", + "cbcBAcCbA", + 16 + ], + [ + "b", + "ccCaacc", + 14 + ], + [ + "b", + "ccCbaaaa", + 14 + ], + [ + "b", + "ccaC", + 8 + ], + [ + "b", + "ccbbcb", + 10 + ], + [ + "bA", + "AAAcACAbA", + 14 + ], + [ + "bA", + "AAbB", + 6 + ], + [ + "bA", + "ABaC", + 6 + ], + [ + "bA", + "ACBbbA", + 8 + ], + [ + "bA", + "ACBbccB", + 12 + ], + [ + "bA", + "ACbbBB", + 10 + ], + [ + "bA", + "Accc", + 8 + ], + [ + "bA", + "BAcCabc", + 11 + ], + [ + "bA", + "BCCcccB", + 13 + ], + [ + "bA", + "BabcCBCaa", + 15 + ], + [ + "bA", + "BbCaCBca", + 13 + ], + [ + "bA", + "BbbcbCCB", + 14 + ], + [ + "bA", + "Bbca", + 5 + ], + [ + "bA", + "C", + 4 + ], + [ + "bA", + "CAbABbBC", + 12 + ], + [ + "bA", + "CB", + 4 + ], + [ + "bA", + "CBCBa", + 8 + ], + [ + "bA", + "CbAaAaB", + 10 + ], + [ + "bA", + "CbcAAc", + 8 + ], + [ + "bA", + "CcAAcbcBC", + 16 + ], + [ + "bA", + "aAc", + 4 + ], + [ + "bA", + "aBCCcAcb", + 13 + ], + [ + "bA", + "aCb", + 6 + ], + [ + "bA", + "aCcbbc", + 10 + ], + [ + "bA", + "bAbcaAA", + 10 + ], + [ + "bA", + "bAc", + 2 + ], + [ + "bA", + "ba", + 1 + ], + [ + "bA", + "bb", + 2 + ], + [ + "bA", + "cAcCABaAb", + 15 + ], + [ + "bA", + "cCAABbBb", + 14 + ], + [ + "bA", + "cCC", + 6 + ], + [ + "bA", + "cabc", + 6 + ], + [ + "bA", + "ccCbcC", + 10 + ], + [ + "bAA", + "AabBB", + 8 + ], + [ + "bAA", + "BBAAaBCbC", + 13 + ], + [ + "bAA", + "C", + 6 + ], + [ + "bAA", + "CBaBcBb", + 12 + ], + [ + "bAA", + "CCBAc", + 7 + ], + [ + "bAA", + "abBBBAA", + 8 + ], + [ + "bAA", + "baacAbb", + 9 + ], + [ + "bAA", + "bcbBccAb", + 12 + ], + [ + "bAAA", + "C", + 8 + ], + [ + "bAAA", + "bBAAC", + 4 + ], + [ + "bAAAA", + "c", + 10 + ], + [ + "bAAAAbaCa", + "abacbC", + 12 + ], + [ + "bAAACbbb", + "Aaa", + 12 + ], + [ + "bAAACccC", + "CC", + 12 + ], + [ + "bAAAacAc", + "caC", + 12 + ], + [ + "bAAAbABC", + "CBCbBa", + 12 + ], + [ + "bAAB", + "bC", + 6 + ], + [ + "bAABaCA", + "A", + 12 + ], + [ + "bAABb", + "CaBBB", + 6 + ], + [ + "bAABcbbB", + "CbACCB", + 11 + ], + [ + "bAAC", + "AabbbaBA", + 13 + ], + [ + "bAAC", + "Acb", + 6 + ], + [ + "bAAC", + "aaB", + 6 + ], + [ + "bAACcACbc", + "Aaa", + 14 + ], + [ + "bAAa", + "AaABbabC", + 11 + ], + [ + "bAAa", + "bCAbbaa", + 7 + ], + [ + "bAAaCba", + "bAca", + 7 + ], + [ + "bAAaaaa", + "BC", + 13 + ], + [ + "bAAbAab", + "CBcA", + 12 + ], + [ + "bAAbCa", + "C", + 10 + ], + [ + "bAAbb", + "A", + 8 + ], + [ + "bAAc", + "CCAbca", + 8 + ], + [ + "bAAc", + "cCBAba", + 9 + ], + [ + "bAAcA", + "bbc", + 6 + ], + [ + "bAAccABbb", + "aAc", + 13 + ], + [ + "bAAccaCC", + "A", + 14 + ], + [ + "bAB", + "ACBcB", + 7 + ], + [ + "bAB", + "CbAA", + 4 + ], + [ + "bAB", + "bbABC", + 4 + ], + [ + "bABAC", + "BCaabaCBC", + 12 + ], + [ + "bABACCCB", + "abB", + 12 + ], + [ + "bABAaB", + "cCCCBbc", + 13 + ], + [ + "bABAaCBBA", + "BAbA", + 11 + ], + [ + "bABAaba", + "A", + 12 + ], + [ + "bABAba", + "BBBAccBA", + 9 + ], + [ + "bABAbbCbB", + "bA", + 14 + ], + [ + "bABAcCCb", + "abCaB", + 11 + ], + [ + "bABBCCC", + "bbCbBc", + 10 + ], + [ + "bABBa", + "bA", + 6 + ], + [ + "bABBcAc", + "bAC", + 9 + ], + [ + "bABC", + "AbBca", + 7 + ], + [ + "bABCBcAc", + "cBCAA", + 10 + ], + [ + "bABCC", + "aBCa", + 5 + ], + [ + "bABCCbbbC", + "cABACBBa", + 10 + ], + [ + "bABCaBCa", + "CaBbB", + 10 + ], + [ + "bABCbaB", + "ACCaCBc", + 10 + ], + [ + "bABa", + "BA", + 5 + ], + [ + "bABa", + "BAB", + 3 + ], + [ + "bABaCBBAa", + "Aa", + 14 + ], + [ + "bABb", + "acA", + 7 + ], + [ + "bABb", + "ccCA", + 8 + ], + [ + "bABbAB", + "ABCBCACc", + 11 + ], + [ + "bABbAC", + "bcBaCcAb", + 10 + ], + [ + "bABbBCB", + "cb", + 12 + ], + [ + "bABbaB", + "cAcAbbCAb", + 11 + ], + [ + "bABbaBAA", + "C", + 16 + ], + [ + "bABbbABBB", + "cAcb", + 14 + ], + [ + "bABbbbC", + "a", + 13 + ], + [ + "bABbcCab", + "ccccaa", + 11 + ], + [ + "bAC", + "AcAbc", + 7 + ], + [ + "bAC", + "bCcbAabb", + 12 + ], + [ + "bACA", + "ACaAAAaC", + 12 + ], + [ + "bACA", + "CAc", + 5 + ], + [ + "bACA", + "c", + 7 + ], + [ + "bACACAAa", + "cbBbC", + 14 + ], + [ + "bACAb", + "Cbc", + 8 + ], + [ + "bACAcc", + "BacBcAB", + 9 + ], + [ + "bACB", + "cBccC", + 8 + ], + [ + "bACBAC", + "BCCABaC", + 6 + ], + [ + "bACBBaA", + "bBACB", + 8 + ], + [ + "bACBCbb", + "b", + 12 + ], + [ + "bACBa", + "BCC", + 7 + ], + [ + "bACBaAcAb", + "C", + 16 + ], + [ + "bACBabABB", + "aCA", + 13 + ], + [ + "bACBcB", + "accBbaCcC", + 13 + ], + [ + "bACCaCcBb", + "aBaBcb", + 11 + ], + [ + "bACaBC", + "bA", + 8 + ], + [ + "bACaCCBAB", + "aaBacb", + 13 + ], + [ + "bACaCcC", + "aBCac", + 8 + ], + [ + "bACab", + "aBcBcAB", + 10 + ], + [ + "bACac", + "Bc", + 7 + ], + [ + "bACac", + "cBAacCAcB", + 10 + ], + [ + "bACbAA", + "B", + 11 + ], + [ + "bACbBbc", + "CbcAcBcA", + 11 + ], + [ + "bACc", + "bcA", + 5 + ], + [ + "bACc", + "cCb", + 6 + ], + [ + "bACcABb", + "BaC", + 10 + ], + [ + "bACcCaaC", + "AAbAbb", + 13 + ], + [ + "bAa", + "AaaACbAcc", + 14 + ], + [ + "bAa", + "AcAabb", + 8 + ], + [ + "bAa", + "bCbBCABBA", + 13 + ], + [ + "bAaA", + "aACcbaB", + 10 + ], + [ + "bAaAA", + "BacbCbBaa", + 14 + ], + [ + "bAaAAAa", + "cabAbA", + 9 + ], + [ + "bAaABAAC", + "AB", + 12 + ], + [ + "bAaAcA", + "cbC", + 11 + ], + [ + "bAaB", + "B", + 6 + ], + [ + "bAaB", + "acCCbb", + 11 + ], + [ + "bAaBAA", + "a", + 10 + ], + [ + "bAaBAcac", + "AABcaa", + 7 + ], + [ + "bAaBC", + "CBcaaaBa", + 10 + ], + [ + "bAaBa", + "C", + 10 + ], + [ + "bAaBaCBAB", + "CCbCCC", + 15 + ], + [ + "bAaBb", + "bCAA", + 7 + ], + [ + "bAaBbBc", + "AC", + 11 + ], + [ + "bAaBbc", + "cbbcAaab", + 10 + ], + [ + "bAaBc", + "a", + 8 + ], + [ + "bAaBc", + "cbCCBBbcc", + 12 + ], + [ + "bAaC", + "ABA", + 6 + ], + [ + "bAaC", + "BAaACAB", + 7 + ], + [ + "bAaCCa", + "CbCBB", + 10 + ], + [ + "bAaCa", + "bCBaABcbB", + 13 + ], + [ + "bAaCab", + "caBC", + 9 + ], + [ + "bAaa", + "AAcCbAC", + 11 + ], + [ + "bAaa", + "bBAAb", + 5 + ], + [ + "bAaaA", + "AbA", + 6 + ], + [ + "bAaaB", + "CABBC", + 8 + ], + [ + "bAaaB", + "bABBaC", + 6 + ], + [ + "bAaabaAc", + "cBaa", + 12 + ], + [ + "bAab", + "CcC", + 8 + ], + [ + "bAab", + "bAc", + 4 + ], + [ + "bAabAbC", + "A", + 12 + ], + [ + "bAabBACA", + "cCca", + 14 + ], + [ + "bAabBBCb", + "CbaCbA", + 12 + ], + [ + "bAabCAB", + "aA", + 10 + ], + [ + "bAabb", + "AaacAAA", + 11 + ], + [ + "bAabbC", + "babAb", + 6 + ], + [ + "bAabcA", + "bA", + 8 + ], + [ + "bAabcbc", + "BaAabcAaa", + 9 + ], + [ + "bAabccAa", + "aCbaB", + 12 + ], + [ + "bAac", + "ccACbbAA", + 13 + ], + [ + "bAacA", + "BBaACC", + 8 + ], + [ + "bAacAb", + "bacccBCc", + 10 + ], + [ + "bAacAbBca", + "C", + 17 + ], + [ + "bAacC", + "bbaAb", + 6 + ], + [ + "bAaccCB", + "c", + 12 + ], + [ + "bAb", + "ABCCAACCA", + 15 + ], + [ + "bAb", + "CCBAA", + 7 + ], + [ + "bAbACaC", + "bABcCAb", + 6 + ], + [ + "bAbAaC", + "BAbbA", + 6 + ], + [ + "bAbAabcab", + "CAabcBa", + 10 + ], + [ + "bAbAbcA", + "CAABBca", + 8 + ], + [ + "bAbBC", + "AACcbCca", + 11 + ], + [ + "bAbBCbaB", + "acacab", + 11 + ], + [ + "bAbBaCBcb", + "BaA", + 14 + ], + [ + "bAbBbbBbb", + "cAaacAC", + 16 + ], + [ + "bAbCAABaB", + "ACcBAccB", + 11 + ], + [ + "bAbCbCcCA", + "aBB", + 15 + ], + [ + "bAbCcB", + "aCAbA", + 10 + ], + [ + "bAba", + "CaAAb", + 8 + ], + [ + "bAbaCC", + "Bba", + 7 + ], + [ + "bAbab", + "CbCAcBcB", + 10 + ], + [ + "bAbb", + "acAbCBb", + 8 + ], + [ + "bAbb", + "b", + 6 + ], + [ + "bAbb", + "bAaAcBBab", + 11 + ], + [ + "bAbbB", + "AaACcBacC", + 14 + ], + [ + "bAbbaa", + "cCBC", + 11 + ], + [ + "bAbbbbBc", + "cABcBBC", + 9 + ], + [ + "bAbbbc", + "aCA", + 11 + ], + [ + "bAbc", + "Accbacc", + 9 + ], + [ + "bAbcBB", + "A", + 10 + ], + [ + "bAbcCaA", + "bACaCAcbC", + 11 + ], + [ + "bAbcCab", + "ABC", + 9 + ], + [ + "bAbcCbAA", + "BcBb", + 11 + ], + [ + "bAbcbA", + "CBCBCac", + 11 + ], + [ + "bAc", + "aaC", + 4 + ], + [ + "bAcA", + "bb", + 6 + ], + [ + "bAcAABA", + "aAAbca", + 9 + ], + [ + "bAcAAbA", + "BABabc", + 8 + ], + [ + "bAcAAbb", + "aC", + 12 + ], + [ + "bAcAbc", + "CAAcaaBa", + 10 + ], + [ + "bAcAc", + "BaC", + 7 + ], + [ + "bAcB", + "bcBC", + 4 + ], + [ + "bAcB", + "bccaA", + 6 + ], + [ + "bAcBAaaCa", + "aABCccaBa", + 12 + ], + [ + "bAcBAbBcC", + "B", + 16 + ], + [ + "bAcBBBABa", + "caA", + 14 + ], + [ + "bAcBCCbac", + "accBBAB", + 12 + ], + [ + "bAcBabbC", + "ca", + 12 + ], + [ + "bAcBbAb", + "cbaBA", + 9 + ], + [ + "bAcBbaA", + "AaBaaA", + 6 + ], + [ + "bAcC", + "Acac", + 5 + ], + [ + "bAcC", + "bba", + 6 + ], + [ + "bAcCAa", + "C", + 10 + ], + [ + "bAcCaCc", + "abbCcb", + 11 + ], + [ + "bAcCb", + "Bbcacac", + 9 + ], + [ + "bAcCbACCB", + "aA", + 15 + ], + [ + "bAcCcACA", + "ABCAaBB", + 11 + ], + [ + "bAcaAbA", + "baCCbBca", + 10 + ], + [ + "bAcac", + "BBbAbCccA", + 11 + ], + [ + "bAcb", + "aa", + 7 + ], + [ + "bAcb", + "cAbbb", + 6 + ], + [ + "bAcbAbBAC", + "CbBc", + 12 + ], + [ + "bAcbBbcb", + "cCcBBCaa", + 11 + ], + [ + "bAcc", + "aCcABC", + 9 + ], + [ + "bAccA", + "ccBaaCA", + 9 + ], + [ + "bAccCabB", + "aABCAAaAA", + 13 + ], + [ + "bAccCbb", + "BaAAcB", + 10 + ], + [ + "bAcca", + "CBbcBBBba", + 13 + ], + [ + "bAccaACaa", + "CCcBCaA", + 10 + ], + [ + "bAccaCCA", + "aABAABBCb", + 13 + ], + [ + "bAcccAC", + "ABC", + 10 + ], + [ + "bAcccAbaA", + "CBcCAbcC", + 11 + ], + [ + "bAcccaB", + "BccACBBC", + 10 + ], + [ + "bB", + "ACaAbC", + 10 + ], + [ + "bB", + "BCbCb", + 7 + ], + [ + "bB", + "Baa", + 5 + ], + [ + "bB", + "BbbbBAb", + 10 + ], + [ + "bB", + "Bcba", + 6 + ], + [ + "bB", + "CAAcBb", + 10 + ], + [ + "bB", + "CbbC", + 5 + ], + [ + "bB", + "aACCab", + 11 + ], + [ + "bB", + "aBaccb", + 10 + ], + [ + "bB", + "aBbC", + 6 + ], + [ + "bB", + "aCACcaA", + 14 + ], + [ + "bB", + "aaAABaCCc", + 16 + ], + [ + "bB", + "ac", + 4 + ], + [ + "bB", + "acACccAac", + 18 + ], + [ + "bB", + "bAAacb", + 9 + ], + [ + "bB", + "bBCccbcC", + 12 + ], + [ + "bB", + "bBbbcb", + 8 + ], + [ + "bB", + "bCbBAAbCc", + 14 + ], + [ + "bB", + "ba", + 2 + ], + [ + "bB", + "baAbcC", + 9 + ], + [ + "bB", + "bcBbACaa", + 12 + ], + [ + "bB", + "c", + 4 + ], + [ + "bB", + "cBCcc", + 8 + ], + [ + "bB", + "cCBa", + 6 + ], + [ + "bB", + "caBc", + 6 + ], + [ + "bB", + "caCCACb", + 13 + ], + [ + "bBA", + "AB", + 4 + ], + [ + "bBA", + "B", + 4 + ], + [ + "bBA", + "abBCbccC", + 12 + ], + [ + "bBA", + "bCaaa", + 7 + ], + [ + "bBA", + "cBB", + 4 + ], + [ + "bBAA", + "cAb", + 6 + ], + [ + "bBAAba", + "C", + 12 + ], + [ + "bBAAcb", + "BCcBABB", + 10 + ], + [ + "bBAB", + "ccaa", + 7 + ], + [ + "bBABbBc", + "Bb", + 10 + ], + [ + "bBAC", + "aCBb", + 8 + ], + [ + "bBACA", + "bbcaBB", + 8 + ], + [ + "bBACBA", + "CA", + 8 + ], + [ + "bBACBABc", + "ca", + 14 + ], + [ + "bBACCbCB", + "B", + 14 + ], + [ + "bBACbaC", + "aabAbaCC", + 9 + ], + [ + "bBAa", + "CBBbBaa", + 7 + ], + [ + "bBAa", + "CabC", + 8 + ], + [ + "bBAa", + "cCcCCCA", + 13 + ], + [ + "bBAaBAcB", + "CbCaaAbAA", + 12 + ], + [ + "bBAaBcAaB", + "ACcb", + 13 + ], + [ + "bBAaC", + "acBaAcC", + 8 + ], + [ + "bBAaCB", + "bcBbCBBca", + 12 + ], + [ + "bBAaCc", + "Aca", + 9 + ], + [ + "bBAac", + "AcccCbBc", + 14 + ], + [ + "bBAacA", + "aBCbaAAA", + 10 + ], + [ + "bBAbCA", + "cBAaaCACb", + 10 + ], + [ + "bBAba", + "B", + 8 + ], + [ + "bBAbac", + "cBbAaccb", + 10 + ], + [ + "bBAc", + "Bc", + 4 + ], + [ + "bBAcAbca", + "baB", + 12 + ], + [ + "bBAcBA", + "aAa", + 9 + ], + [ + "bBAcC", + "abbBbCBc", + 10 + ], + [ + "bBAcC", + "c", + 8 + ], + [ + "bBAca", + "Ba", + 6 + ], + [ + "bBAca", + "b", + 8 + ], + [ + "bBAca", + "c", + 8 + ], + [ + "bBB", + "ABACC", + 8 + ], + [ + "bBB", + "AcaBacAc", + 14 + ], + [ + "bBB", + "BCCBCB", + 7 + ], + [ + "bBB", + "Cba", + 5 + ], + [ + "bBB", + "bACbcbAc", + 12 + ], + [ + "bBBABBb", + "Cbab", + 10 + ], + [ + "bBBAa", + "c", + 10 + ], + [ + "bBBBB", + "AacCA", + 10 + ], + [ + "bBBBBAa", + "AbbBAB", + 8 + ], + [ + "bBBBCb", + "B", + 10 + ], + [ + "bBBBabC", + "abBacCCBA", + 13 + ], + [ + "bBBBc", + "AB", + 8 + ], + [ + "bBBC", + "B", + 6 + ], + [ + "bBBC", + "abAbBBcab", + 11 + ], + [ + "bBBCABbB", + "caaCC", + 14 + ], + [ + "bBBCAbB", + "BcBCBCcBc", + 10 + ], + [ + "bBBa", + "abbCAA", + 8 + ], + [ + "bBBaB", + "cacCCbAA", + 14 + ], + [ + "bBBaC", + "Bcc", + 7 + ], + [ + "bBBaaBC", + "bcbcbBba", + 11 + ], + [ + "bBBaaBb", + "a", + 12 + ], + [ + "bBBaabc", + "caaBba", + 10 + ], + [ + "bBBabac", + "aaAA", + 11 + ], + [ + "bBBb", + "Accaab", + 10 + ], + [ + "bBBb", + "aAacAbca", + 14 + ], + [ + "bBBbAcBB", + "BBAbBAb", + 9 + ], + [ + "bBBbBbbbB", + "cc", + 18 + ], + [ + "bBBbCa", + "BcC", + 8 + ], + [ + "bBBbaB", + "caAaaC", + 10 + ], + [ + "bBBbaBbCa", + "a", + 16 + ], + [ + "bBBbacabB", + "Ba", + 14 + ], + [ + "bBBbcc", + "bcA", + 8 + ], + [ + "bBBc", + "ABBbACc", + 8 + ], + [ + "bBBc", + "BAAb", + 7 + ], + [ + "bBBcA", + "caB", + 8 + ], + [ + "bBBcaca", + "CBBBbABcb", + 10 + ], + [ + "bBC", + "AB", + 4 + ], + [ + "bBC", + "aaCaCAcBc", + 15 + ], + [ + "bBC", + "bcaBACB", + 8 + ], + [ + "bBC", + "cAB", + 6 + ], + [ + "bBC", + "cBaCcbca", + 12 + ], + [ + "bBCA", + "AAc", + 7 + ], + [ + "bBCA", + "bCCACBc", + 8 + ], + [ + "bBCA", + "babCCBAC", + 9 + ], + [ + "bBCA", + "cBaabcA", + 9 + ], + [ + "bBCAB", + "ACcBBBBab", + 13 + ], + [ + "bBCABBA", + "BCabbCAB", + 9 + ], + [ + "bBCAbAb", + "caAbA", + 8 + ], + [ + "bBCAbBba", + "CbbBbbC", + 10 + ], + [ + "bBCAcBcCA", + "B", + 16 + ], + [ + "bBCBa", + "cbAAB", + 8 + ], + [ + "bBCBaBCaa", + "bA", + 15 + ], + [ + "bBCBaC", + "aABCAca", + 10 + ], + [ + "bBCBabBC", + "cCAc", + 12 + ], + [ + "bBCC", + "baAbacaA", + 12 + ], + [ + "bBCCA", + "aa", + 9 + ], + [ + "bBCCCCABc", + "babacAA", + 13 + ], + [ + "bBCCCb", + "ACc", + 9 + ], + [ + "bBCCbAb", + "a", + 13 + ], + [ + "bBCaABB", + "CbACac", + 10 + ], + [ + "bBCaCCC", + "ABccBC", + 8 + ], + [ + "bBCaabcAA", + "CBABCC", + 13 + ], + [ + "bBCac", + "BA", + 7 + ], + [ + "bBCacbCA", + "BaCBcCBC", + 10 + ], + [ + "bBCb", + "bBcCA", + 4 + ], + [ + "bBCbAAaAC", + "CCbCCA", + 12 + ], + [ + "bBCbBa", + "AbBaACCB", + 10 + ], + [ + "bBCbb", + "aCcaC", + 9 + ], + [ + "bBCc", + "ccB", + 7 + ], + [ + "bBCcaAAa", + "caabAbC", + 13 + ], + [ + "bBa", + "Bba", + 2 + ], + [ + "bBa", + "b", + 4 + ], + [ + "bBa", + "bBcAca", + 6 + ], + [ + "bBa", + "bCA", + 3 + ], + [ + "bBa", + "cACaAbBa", + 10 + ], + [ + "bBa", + "ccBcbcAcc", + 15 + ], + [ + "bBaABCcC", + "ABA", + 12 + ], + [ + "bBaACBaB", + "BCBBABbb", + 10 + ], + [ + "bBaAaA", + "Bb", + 10 + ], + [ + "bBaAaBc", + "BAbcaACBc", + 8 + ], + [ + "bBaAaC", + "cCBACaaA", + 10 + ], + [ + "bBaAb", + "bACbbacAB", + 10 + ], + [ + "bBaBCAAaa", + "CACBAc", + 13 + ], + [ + "bBaBCCcA", + "bb", + 13 + ], + [ + "bBaBCaAaB", + "bCAbCbbB", + 10 + ], + [ + "bBaBCcBcB", + "cabcc", + 11 + ], + [ + "bBaBa", + "BC", + 8 + ], + [ + "bBaBbAbAB", + "aBAAbCCBA", + 12 + ], + [ + "bBaBbb", + "BaCAbbb", + 7 + ], + [ + "bBaC", + "BAaAcb", + 8 + ], + [ + "bBaCbcC", + "bA", + 11 + ], + [ + "bBaCcAA", + "cAbb", + 12 + ], + [ + "bBaaBb", + "Caa", + 8 + ], + [ + "bBaaBcBbb", + "cABAaA", + 15 + ], + [ + "bBaaCABa", + "bcbaBcbC", + 11 + ], + [ + "bBaaCa", + "bAAC", + 6 + ], + [ + "bBaabbbbc", + "cbA", + 16 + ], + [ + "bBaacaC", + "bccaBC", + 8 + ], + [ + "bBabAcBA", + "ccaAbA", + 9 + ], + [ + "bBabB", + "BabcBBB", + 8 + ], + [ + "bBabC", + "ABc", + 7 + ], + [ + "bBabaAC", + "ccaCb", + 12 + ], + [ + "bBac", + "AbaAcAaB", + 11 + ], + [ + "bBac", + "BBCc", + 3 + ], + [ + "bBacbBcb", + "bCbc", + 9 + ], + [ + "bBb", + "AA", + 6 + ], + [ + "bBb", + "AbCaacc", + 12 + ], + [ + "bBb", + "CaaCCCaA", + 16 + ], + [ + "bBb", + "Ccac", + 8 + ], + [ + "bBb", + "a", + 6 + ], + [ + "bBb", + "aCC", + 6 + ], + [ + "bBb", + "baAC", + 6 + ], + [ + "bBb", + "cBA", + 4 + ], + [ + "bBb", + "caBaCBa", + 11 + ], + [ + "bBbA", + "C", + 8 + ], + [ + "bBbA", + "CBcBAA", + 7 + ], + [ + "bBbA", + "caBBABaBa", + 13 + ], + [ + "bBbAAa", + "aabbb", + 10 + ], + [ + "bBbB", + "AcbAA", + 8 + ], + [ + "bBbB", + "a", + 8 + ], + [ + "bBbBBaBAc", + "c", + 16 + ], + [ + "bBbBBba", + "Bbc", + 10 + ], + [ + "bBbBb", + "B", + 8 + ], + [ + "bBbCA", + "abBBcBBbc", + 12 + ], + [ + "bBbCCca", + "aCaCBCB", + 11 + ], + [ + "bBba", + "aAA", + 7 + ], + [ + "bBba", + "abB", + 6 + ], + [ + "bBbaA", + "cBCBCcCCB", + 15 + ], + [ + "bBbaC", + "bc", + 7 + ], + [ + "bBbaCba", + "aBBccAaB", + 10 + ], + [ + "bBbabBAC", + "bCBa", + 11 + ], + [ + "bBbacbBCA", + "BA", + 14 + ], + [ + "bBbbCA", + "bACbBAa", + 8 + ], + [ + "bBbbbc", + "c", + 10 + ], + [ + "bBbcAAba", + "cC", + 14 + ], + [ + "bBbcBBCa", + "b", + 14 + ], + [ + "bBbcCC", + "aacCBb", + 10 + ], + [ + "bBbcCC", + "acabCCb", + 9 + ], + [ + "bBbcbA", + "BcBCabB", + 9 + ], + [ + "bBbcbA", + "cBaCcCbB", + 10 + ], + [ + "bBbcbBa", + "BbA", + 9 + ], + [ + "bBc", + "A", + 6 + ], + [ + "bBc", + "ACA", + 6 + ], + [ + "bBc", + "CC", + 5 + ], + [ + "bBc", + "bCbA", + 5 + ], + [ + "bBc", + "bbcA", + 3 + ], + [ + "bBcA", + "BaCba", + 7 + ], + [ + "bBcAACbB", + "bCba", + 10 + ], + [ + "bBcABCb", + "BAca", + 9 + ], + [ + "bBcAa", + "Ca", + 7 + ], + [ + "bBcAcBabB", + "ccaCAb", + 11 + ], + [ + "bBcAccBAc", + "cAc", + 12 + ], + [ + "bBcB", + "Ca", + 7 + ], + [ + "bBcBAbB", + "A", + 12 + ], + [ + "bBcBAca", + "BcbC", + 8 + ], + [ + "bBcBc", + "C", + 9 + ], + [ + "bBcCCbcb", + "CAAcacCaC", + 14 + ], + [ + "bBcCaAb", + "CbbbCa", + 9 + ], + [ + "bBcCbAAa", + "c", + 14 + ], + [ + "bBcaaaCAC", + "ACBbc", + 16 + ], + [ + "bBcb", + "bCCac", + 7 + ], + [ + "bBcba", + "bba", + 4 + ], + [ + "bBcbaB", + "BaCCbc", + 10 + ], + [ + "bBcbaBAbB", + "AAb", + 13 + ], + [ + "bBcbaBbAB", + "c", + 16 + ], + [ + "bBcc", + "CcBABA", + 10 + ], + [ + "bBcc", + "aAc", + 6 + ], + [ + "bBccACBbA", + "CaCCCB", + 12 + ], + [ + "bBccBc", + "Caac", + 9 + ], + [ + "bBccC", + "bAcc", + 4 + ], + [ + "bBccb", + "bccAbBbC", + 10 + ], + [ + "bBccb", + "caBAA", + 10 + ], + [ + "bBccbBbCb", + "AAAbbaBA", + 15 + ], + [ + "bBccbBccC", + "c", + 16 + ], + [ + "bBccbb", + "AaABa", + 11 + ], + [ + "bBcccB", + "bcCAca", + 7 + ], + [ + "bC", + "A", + 4 + ], + [ + "bC", + "ACBBaBa", + 12 + ], + [ + "bC", + "Abc", + 3 + ], + [ + "bC", + "BAAcb", + 8 + ], + [ + "bC", + "BAa", + 5 + ], + [ + "bC", + "BAcaBCAcc", + 15 + ], + [ + "bC", + "BCbcCA", + 8 + ], + [ + "bC", + "BacAcb", + 10 + ], + [ + "bC", + "CC", + 2 + ], + [ + "bC", + "CbBcAbcc", + 13 + ], + [ + "bC", + "aBaCA", + 7 + ], + [ + "bC", + "aCCbCABBB", + 14 + ], + [ + "bC", + "aaBBCAA", + 11 + ], + [ + "bC", + "aaCbaAcB", + 13 + ], + [ + "bC", + "acCcbcB", + 11 + ], + [ + "bC", + "b", + 2 + ], + [ + "bC", + "bBBBC", + 6 + ], + [ + "bC", + "bBb", + 4 + ], + [ + "bC", + "bC", + 0 + ], + [ + "bC", + "baAAcb", + 9 + ], + [ + "bC", + "bac", + 3 + ], + [ + "bC", + "bbBb", + 6 + ], + [ + "bC", + "c", + 3 + ], + [ + "bC", + "cABAac", + 10 + ], + [ + "bC", + "cABcACACa", + 15 + ], + [ + "bC", + "cAaCb", + 8 + ], + [ + "bC", + "cBbB", + 6 + ], + [ + "bC", + "cBcCbcc", + 11 + ], + [ + "bC", + "cBcCcAa", + 11 + ], + [ + "bC", + "cccCBcaba", + 16 + ], + [ + "bCA", + "BbCaCc", + 7 + ], + [ + "bCA", + "ac", + 5 + ], + [ + "bCA", + "bCB", + 2 + ], + [ + "bCA", + "ba", + 3 + ], + [ + "bCA", + "cbcbAc", + 7 + ], + [ + "bCAAA", + "Cc", + 8 + ], + [ + "bCAAA", + "cBabbA", + 9 + ], + [ + "bCAAABc", + "Aabab", + 11 + ], + [ + "bCAABaCbc", + "Cab", + 12 + ], + [ + "bCAABccA", + "AaacCBBcB", + 13 + ], + [ + "bCAAC", + "bBAaCcc", + 7 + ], + [ + "bCAACaCa", + "CcAACaC", + 5 + ], + [ + "bCAAaBbCA", + "CbaAcB", + 13 + ], + [ + "bCAB", + "bAAA", + 4 + ], + [ + "bCABB", + "bA", + 6 + ], + [ + "bCABBCAA", + "bBCbcCc", + 11 + ], + [ + "bCABBcB", + "bBbACbaBb", + 11 + ], + [ + "bCABa", + "AaAa", + 6 + ], + [ + "bCABaBAcc", + "CBbaCCba", + 13 + ], + [ + "bCAC", + "ABCcc", + 6 + ], + [ + "bCAC", + "caCa", + 6 + ], + [ + "bCAC", + "cbCcb", + 6 + ], + [ + "bCACBA", + "cbac", + 10 + ], + [ + "bCACBB", + "Bcb", + 9 + ], + [ + "bCACa", + "c", + 9 + ], + [ + "bCACaaC", + "Cacaaa", + 6 + ], + [ + "bCACaaa", + "cC", + 11 + ], + [ + "bCACb", + "B", + 9 + ], + [ + "bCACbA", + "Cb", + 8 + ], + [ + "bCAaCB", + "aCAbcCbB", + 8 + ], + [ + "bCAaCbac", + "a", + 14 + ], + [ + "bCAaccaCC", + "b", + 16 + ], + [ + "bCAbAAcA", + "bBBCAabAc", + 10 + ], + [ + "bCAbab", + "ca", + 9 + ], + [ + "bCAbb", + "CBAcBAcBB", + 12 + ], + [ + "bCAc", + "BaCAaaBa", + 11 + ], + [ + "bCAcBA", + "CBBcCCA", + 10 + ], + [ + "bCAcC", + "BbaccaCA", + 10 + ], + [ + "bCAcC", + "BcA", + 6 + ], + [ + "bCAcc", + "cbCAB", + 6 + ], + [ + "bCB", + "CCabaC", + 9 + ], + [ + "bCB", + "abaaAbbAC", + 15 + ], + [ + "bCB", + "bCAca", + 6 + ], + [ + "bCB", + "cBAB", + 5 + ], + [ + "bCBA", + "BccBbcAA", + 10 + ], + [ + "bCBAAa", + "c", + 11 + ], + [ + "bCBBBCbc", + "CAbbbB", + 10 + ], + [ + "bCBBBcB", + "BBaBCBbC", + 10 + ], + [ + "bCBBa", + "CccaaC", + 9 + ], + [ + "bCBBbCc", + "bBaAB", + 10 + ], + [ + "bCBC", + "AbaCcaCB", + 10 + ], + [ + "bCBC", + "CcbaCaA", + 10 + ], + [ + "bCBC", + "aCAbcbCC", + 10 + ], + [ + "bCBCAB", + "C", + 10 + ], + [ + "bCBCACBb", + "bBacBCaA", + 11 + ], + [ + "bCBCBcbc", + "bA", + 14 + ], + [ + "bCBCaAc", + "a", + 12 + ], + [ + "bCBCbC", + "A", + 12 + ], + [ + "bCBa", + "Ba", + 4 + ], + [ + "bCBaAb", + "CABCCcb", + 10 + ], + [ + "bCBaabC", + "ACb", + 10 + ], + [ + "bCBabCa", + "cAbaccCC", + 11 + ], + [ + "bCBacABab", + "ccAacAaCc", + 11 + ], + [ + "bCBacaAB", + "ACbABB", + 10 + ], + [ + "bCBb", + "aaAbAbbC", + 11 + ], + [ + "bCBb", + "cBabcA", + 9 + ], + [ + "bCBb", + "cBcccaA", + 12 + ], + [ + "bCBbAaAb", + "BabA", + 11 + ], + [ + "bCBbAac", + "CBcca", + 8 + ], + [ + "bCBbAcC", + "CBbBbb", + 8 + ], + [ + "bCBbC", + "CA", + 8 + ], + [ + "bCBbaCCA", + "BAAab", + 13 + ], + [ + "bCBbabb", + "BcCBA", + 10 + ], + [ + "bCBbbCb", + "ABcbAba", + 10 + ], + [ + "bCBc", + "bbBACc", + 6 + ], + [ + "bCBcB", + "bCAbccACb", + 10 + ], + [ + "bCBcBBc", + "b", + 12 + ], + [ + "bCBca", + "cAABaCCa", + 11 + ], + [ + "bCBcabA", + "CAcbbcBcB", + 14 + ], + [ + "bCBcba", + "ABAcbc", + 8 + ], + [ + "bCBcccbC", + "Cbbbc", + 10 + ], + [ + "bCC", + "A", + 6 + ], + [ + "bCC", + "ABBbCCC", + 8 + ], + [ + "bCC", + "AaC", + 4 + ], + [ + "bCC", + "BB", + 5 + ], + [ + "bCC", + "a", + 6 + ], + [ + "bCC", + "aCbBaaCB", + 12 + ], + [ + "bCC", + "b", + 4 + ], + [ + "bCC", + "bbBaCaCAc", + 12 + ], + [ + "bCC", + "cB", + 5 + ], + [ + "bCCAACA", + "bc", + 11 + ], + [ + "bCCAAbAa", + "aC", + 14 + ], + [ + "bCCACBb", + "BcAA", + 10 + ], + [ + "bCCAc", + "bcBCABabC", + 10 + ], + [ + "bCCBACAa", + "cCB", + 11 + ], + [ + "bCCBC", + "CbbbACC", + 10 + ], + [ + "bCCBaBa", + "BC", + 11 + ], + [ + "bCCBbBAA", + "ACBbbBc", + 9 + ], + [ + "bCCCABcb", + "BaBbbAb", + 12 + ], + [ + "bCCCa", + "aA", + 9 + ], + [ + "bCCCbccc", + "aCAA", + 14 + ], + [ + "bCCa", + "AabcB", + 9 + ], + [ + "bCCa", + "BccBa", + 5 + ], + [ + "bCCa", + "bCACAaBb", + 8 + ], + [ + "bCCaA", + "cbAbaAA", + 8 + ], + [ + "bCCaAbBB", + "CcC", + 13 + ], + [ + "bCCaBa", + "AcCabCAc", + 9 + ], + [ + "bCCaCc", + "abAcCA", + 9 + ], + [ + "bCCaa", + "cA", + 8 + ], + [ + "bCCaaCaa", + "cbcc", + 14 + ], + [ + "bCCab", + "B", + 9 + ], + [ + "bCCaccbbB", + "cC", + 15 + ], + [ + "bCCb", + "cBBaBB", + 10 + ], + [ + "bCCbA", + "bcBC", + 6 + ], + [ + "bCCbACBBB", + "CA", + 14 + ], + [ + "bCCbB", + "aaBcCbAA", + 10 + ], + [ + "bCCbCc", + "aACbC", + 6 + ], + [ + "bCCbcA", + "caACc", + 10 + ], + [ + "bCCcAcAB", + "aCAB", + 10 + ], + [ + "bCCcc", + "aACCcacB", + 8 + ], + [ + "bCCccC", + "aabAb", + 12 + ], + [ + "bCa", + "Cb", + 4 + ], + [ + "bCa", + "aBbb", + 7 + ], + [ + "bCa", + "bBACAbba", + 10 + ], + [ + "bCa", + "baba", + 4 + ], + [ + "bCa", + "cbacb", + 7 + ], + [ + "bCa", + "ccaCaCCc", + 12 + ], + [ + "bCaA", + "AABbbA", + 9 + ], + [ + "bCaA", + "CBc", + 6 + ], + [ + "bCaAC", + "ACaaABaCa", + 10 + ], + [ + "bCaBAc", + "bbbBcbcAc", + 10 + ], + [ + "bCaBBBA", + "caCcBBCcB", + 12 + ], + [ + "bCaBBC", + "cacbBC", + 6 + ], + [ + "bCaBbCAC", + "aaCAcCAbC", + 11 + ], + [ + "bCaCAabc", + "bBaB", + 11 + ], + [ + "bCaCAcaC", + "bcccaa", + 8 + ], + [ + "bCaCCC", + "cAbabbACA", + 14 + ], + [ + "bCaCcB", + "ABbbcCABC", + 12 + ], + [ + "bCaCcac", + "CBbCAA", + 10 + ], + [ + "bCaa", + "a", + 6 + ], + [ + "bCaa", + "aBC", + 7 + ], + [ + "bCaabBC", + "CCABcA", + 10 + ], + [ + "bCabAacbC", + "BAB", + 14 + ], + [ + "bCabCc", + "CA", + 9 + ], + [ + "bCabaAAB", + "A", + 14 + ], + [ + "bCabbAacb", + "Cba", + 12 + ], + [ + "bCabbC", + "CAacA", + 9 + ], + [ + "bCac", + "C", + 6 + ], + [ + "bCac", + "cC", + 6 + ], + [ + "bCacAbBa", + "a", + 14 + ], + [ + "bCacBA", + "abB", + 8 + ], + [ + "bCacCcB", + "aAabBBbC", + 13 + ], + [ + "bCaccBAa", + "CB", + 12 + ], + [ + "bCb", + "aCBc", + 5 + ], + [ + "bCb", + "ab", + 4 + ], + [ + "bCb", + "ccA", + 5 + ], + [ + "bCbA", + "ab", + 6 + ], + [ + "bCbAAAB", + "AccbcBaB", + 10 + ], + [ + "bCbAB", + "BcBB", + 5 + ], + [ + "bCbACcbc", + "b", + 14 + ], + [ + "bCbAa", + "acCBABB", + 9 + ], + [ + "bCbAbcBc", + "b", + 14 + ], + [ + "bCbB", + "b", + 6 + ], + [ + "bCbBabC", + "cbacBCaBc", + 10 + ], + [ + "bCbBacaA", + "aBAaBCaa", + 11 + ], + [ + "bCbBbbbC", + "a", + 16 + ], + [ + "bCbBbcAb", + "ba", + 13 + ], + [ + "bCbCAbB", + "CABBbb", + 9 + ], + [ + "bCbCCCAcb", + "a", + 17 + ], + [ + "bCbCbb", + "AAACCCB", + 11 + ], + [ + "bCbCccc", + "abbAbC", + 11 + ], + [ + "bCbaACBbb", + "bCCccaa", + 13 + ], + [ + "bCbaAc", + "BA", + 9 + ], + [ + "bCbaaBc", + "CbAAcaBc", + 7 + ], + [ + "bCbac", + "aAcBABbaA", + 13 + ], + [ + "bCbac", + "b", + 8 + ], + [ + "bCbacABa", + "cCaac", + 10 + ], + [ + "bCbb", + "bbCBabAC", + 9 + ], + [ + "bCbbb", + "AcBc", + 8 + ], + [ + "bCbbba", + "AaBbc", + 9 + ], + [ + "bCbc", + "ABb", + 6 + ], + [ + "bCbc", + "CCb", + 4 + ], + [ + "bCbcB", + "AbAab", + 9 + ], + [ + "bCbcCBA", + "BCbBacC", + 9 + ], + [ + "bCbcCC", + "BbbABA", + 9 + ], + [ + "bCbcCbAC", + "ccccC", + 10 + ], + [ + "bCbca", + "bcbBCBa", + 6 + ], + [ + "bCbcaAAAA", + "A", + 16 + ], + [ + "bCbcaca", + "bBBAcc", + 8 + ], + [ + "bCbcbbBbC", + "CaC", + 14 + ], + [ + "bCc", + "BabBBCb", + 10 + ], + [ + "bCc", + "CCBcAA", + 8 + ], + [ + "bCc", + "CCCcCBcC", + 12 + ], + [ + "bCc", + "aaAABCCaB", + 14 + ], + [ + "bCc", + "aabbbaaB", + 14 + ], + [ + "bCc", + "bccB", + 3 + ], + [ + "bCcABC", + "aAcBcCC", + 10 + ], + [ + "bCcACCBa", + "cac", + 12 + ], + [ + "bCcACCaC", + "AbcaC", + 9 + ], + [ + "bCcB", + "BBB", + 5 + ], + [ + "bCcBA", + "CcAbcCbaA", + 11 + ], + [ + "bCcBB", + "cBb", + 5 + ], + [ + "bCcC", + "ACBaB", + 8 + ], + [ + "bCcCb", + "ccbBac", + 10 + ], + [ + "bCcCcCCCB", + "BCAaAa", + 15 + ], + [ + "bCca", + "CCaaaBbc", + 12 + ], + [ + "bCcaA", + "cACB", + 9 + ], + [ + "bCcaBbac", + "bBa", + 10 + ], + [ + "bCcaCc", + "A", + 11 + ], + [ + "bCcaCcC", + "bcCB", + 8 + ], + [ + "bCcaaa", + "B", + 11 + ], + [ + "bCcaabCB", + "bbAbcaAA", + 13 + ], + [ + "bCcacBCA", + "aACBccC", + 11 + ], + [ + "bCcbAaBBc", + "c", + 16 + ], + [ + "bCcbCCbb", + "bBbacaC", + 11 + ], + [ + "bCcbbaCab", + "BC", + 15 + ], + [ + "bCcbbc", + "bBBCB", + 9 + ], + [ + "bCcbc", + "acBCAcc", + 9 + ], + [ + "bCcc", + "CAbacbA", + 10 + ], + [ + "bCccbbbc", + "bBABAaBAc", + 13 + ], + [ + "ba", + "ABbbC", + 8 + ], + [ + "ba", + "BA", + 2 + ], + [ + "ba", + "BAABBb", + 10 + ], + [ + "ba", + "BacAAbA", + 11 + ], + [ + "ba", + "BbAaaCACc", + 14 + ], + [ + "ba", + "BcCAAC", + 10 + ], + [ + "ba", + "Bcc", + 5 + ], + [ + "ba", + "CCccCCa", + 12 + ], + [ + "ba", + "CaAbAb", + 9 + ], + [ + "ba", + "Cba", + 2 + ], + [ + "ba", + "Cca", + 4 + ], + [ + "ba", + "Ccac", + 6 + ], + [ + "ba", + "a", + 2 + ], + [ + "ba", + "aBABA", + 8 + ], + [ + "ba", + "aBaABB", + 9 + ], + [ + "ba", + "aCcaCbAbB", + 15 + ], + [ + "ba", + "bBC", + 4 + ], + [ + "ba", + "bBCC", + 6 + ], + [ + "ba", + "baABc", + 6 + ], + [ + "ba", + "baCAbB", + 8 + ], + [ + "ba", + "cA", + 3 + ], + [ + "ba", + "cABA", + 6 + ], + [ + "ba", + "cBbCCaB", + 10 + ], + [ + "ba", + "cBc", + 5 + ], + [ + "ba", + "cCCBcC", + 11 + ], + [ + "ba", + "caBBCAca", + 13 + ], + [ + "ba", + "cbBbb", + 8 + ], + [ + "ba", + "ccb", + 6 + ], + [ + "baA", + "AbAAaCaa", + 11 + ], + [ + "baA", + "B", + 5 + ], + [ + "baA", + "BBB", + 5 + ], + [ + "baA", + "a", + 4 + ], + [ + "baA", + "aCaaB", + 7 + ], + [ + "baA", + "cAaBCBaCB", + 15 + ], + [ + "baA", + "cBBcccca", + 14 + ], + [ + "baA", + "ccABbC", + 10 + ], + [ + "baAA", + "BBAaCacBC", + 14 + ], + [ + "baAA", + "cbbbCcbac", + 15 + ], + [ + "baAAA", + "CCcC", + 10 + ], + [ + "baAAABbC", + "BcACCbBa", + 11 + ], + [ + "baAAAbAC", + "caBb", + 12 + ], + [ + "baAAa", + "cACaAB", + 8 + ], + [ + "baAAc", + "bC", + 7 + ], + [ + "baAAcCa", + "CCABACcBC", + 12 + ], + [ + "baABBcc", + "cbcaaABc", + 9 + ], + [ + "baABCaCA", + "CABCbCAC", + 8 + ], + [ + "baABaBaAA", + "CAabb", + 13 + ], + [ + "baABbAcC", + "BcCBCCC", + 10 + ], + [ + "baAC", + "bCbAbBCA", + 10 + ], + [ + "baACCCcaA", + "Cb", + 16 + ], + [ + "baACca", + "bBcB", + 8 + ], + [ + "baAa", + "AbAab", + 6 + ], + [ + "baAaCAcc", + "acAbB", + 11 + ], + [ + "baAbCAc", + "baAacAac", + 5 + ], + [ + "baAbaAaba", + "cBA", + 15 + ], + [ + "baAbabAb", + "CC", + 16 + ], + [ + "baAcABAC", + "aabCBbBcB", + 13 + ], + [ + "baAcB", + "bbcb", + 5 + ], + [ + "baAcCcaaa", + "ccAA", + 12 + ], + [ + "baAca", + "aaAcCc", + 6 + ], + [ + "baAcaA", + "C", + 11 + ], + [ + "baAcab", + "B", + 11 + ], + [ + "baAcc", + "AbacACCCb", + 10 + ], + [ + "baB", + "BbccbbBbC", + 14 + ], + [ + "baB", + "acbAB", + 5 + ], + [ + "baB", + "accCa", + 10 + ], + [ + "baB", + "cA", + 5 + ], + [ + "baB", + "ca", + 4 + ], + [ + "baB", + "ccBACC", + 10 + ], + [ + "baBA", + "aa", + 5 + ], + [ + "baBAC", + "aA", + 6 + ], + [ + "baBAbccc", + "BCBaC", + 11 + ], + [ + "baBB", + "bCCaAca", + 10 + ], + [ + "baBB", + "cacbb", + 6 + ], + [ + "baBBC", + "BbBAA", + 7 + ], + [ + "baBBCAAc", + "bBaabAccc", + 11 + ], + [ + "baBBCcBCa", + "ABbBca", + 9 + ], + [ + "baBBaa", + "BCABAa", + 6 + ], + [ + "baBBbAb", + "bACa", + 10 + ], + [ + "baBBbCbBC", + "cBABabC", + 11 + ], + [ + "baBBcaBcC", + "CCCb", + 16 + ], + [ + "baBBcb", + "BBCbCCb", + 9 + ], + [ + "baBCcbbA", + "BBacCC", + 11 + ], + [ + "baBa", + "aA", + 5 + ], + [ + "baBa", + "bBbcAAb", + 10 + ], + [ + "baBa", + "cCbbbcBC", + 12 + ], + [ + "baBaABBb", + "bBcB", + 10 + ], + [ + "baBaaC", + "A", + 11 + ], + [ + "baBaaC", + "bBbAcc", + 7 + ], + [ + "baBbC", + "ACAc", + 8 + ], + [ + "baBbcCbaC", + "C", + 16 + ], + [ + "baBc", + "BAAbCa", + 8 + ], + [ + "baBcAAC", + "aBAAbACa", + 8 + ], + [ + "baBcC", + "Abc", + 6 + ], + [ + "baBcCCBBA", + "bbacBaaaC", + 14 + ], + [ + "baBcCCBb", + "Bccacb", + 9 + ], + [ + "baBcaAc", + "bc", + 10 + ], + [ + "baBcbccA", + "bCCa", + 11 + ], + [ + "baBccBC", + "C", + 12 + ], + [ + "baBccccb", + "AAbbaBCb", + 11 + ], + [ + "baC", + "AABBAb", + 10 + ], + [ + "baC", + "AB", + 5 + ], + [ + "baC", + "BBa", + 5 + ], + [ + "baC", + "CAaCCcbb", + 12 + ], + [ + "baC", + "CC", + 4 + ], + [ + "baC", + "caBcB", + 7 + ], + [ + "baCA", + "Bc", + 6 + ], + [ + "baCACBCac", + "a", + 16 + ], + [ + "baCAaAaBa", + "bcBcCab", + 12 + ], + [ + "baCAbc", + "CCc", + 8 + ], + [ + "baCB", + "B", + 6 + ], + [ + "baCBACbBa", + "b", + 16 + ], + [ + "baCBC", + "CAAcaCaB", + 12 + ], + [ + "baCBbCB", + "abc", + 9 + ], + [ + "baCCCCBB", + "b", + 14 + ], + [ + "baCCCcC", + "bbcAcABac", + 13 + ], + [ + "baCCbAB", + "CbBAC", + 9 + ], + [ + "baCCc", + "BcACCa", + 6 + ], + [ + "baCCc", + "ab", + 8 + ], + [ + "baCa", + "BAabaACcC", + 12 + ], + [ + "baCa", + "CCAABc", + 11 + ], + [ + "baCaAAB", + "aCB", + 8 + ], + [ + "baCaCa", + "caC", + 7 + ], + [ + "baCaCcbAb", + "bCB", + 13 + ], + [ + "baCb", + "CAbA", + 7 + ], + [ + "baCbCAB", + "aCBccCcC", + 11 + ], + [ + "baCbCC", + "ACCbA", + 8 + ], + [ + "baCbbCb", + "bACAc", + 8 + ], + [ + "baCbc", + "aaAbAb", + 8 + ], + [ + "baCbcACBc", + "CACcBb", + 11 + ], + [ + "baCc", + "BbcaaB", + 8 + ], + [ + "baCcA", + "bbbBcB", + 8 + ], + [ + "baCcaaC", + "cCCA", + 10 + ], + [ + "baCcb", + "BaCacAa", + 7 + ], + [ + "baa", + "ACbBca", + 8 + ], + [ + "baa", + "BaC", + 3 + ], + [ + "baaABBcBb", + "ABcacAC", + 14 + ], + [ + "baaAC", + "aCAb", + 6 + ], + [ + "baaAaBB", + "ccABBac", + 12 + ], + [ + "baaAb", + "bAcBabC", + 8 + ], + [ + "baaAcA", + "B", + 11 + ], + [ + "baaBAA", + "CbabaCCAA", + 8 + ], + [ + "baaBAAabB", + "cCB", + 16 + ], + [ + "baaBBBb", + "aC", + 12 + ], + [ + "baaBabb", + "cAaB", + 9 + ], + [ + "baaBb", + "CBbaBAbC", + 9 + ], + [ + "baaBcaa", + "CcCCbb", + 13 + ], + [ + "baaCCcAB", + "cCA", + 11 + ], + [ + "baaCaac", + "aCAaCCbA", + 11 + ], + [ + "baaCbA", + "ccBAAcC", + 12 + ], + [ + "baaa", + "CaCbCb", + 10 + ], + [ + "baaa", + "bBbBAAC", + 10 + ], + [ + "baaaAc", + "CBBbBBB", + 13 + ], + [ + "baaaCA", + "aab", + 8 + ], + [ + "baaaaabB", + "BCbbccA", + 15 + ], + [ + "baaacac", + "ABa", + 11 + ], + [ + "baab", + "aABbABC", + 11 + ], + [ + "baabA", + "cACC", + 9 + ], + [ + "baabaBaab", + "aCcA", + 15 + ], + [ + "baac", + "ACBbca", + 10 + ], + [ + "baacACa", + "bccCB", + 8 + ], + [ + "baacBbA", + "AaaCCbaaA", + 9 + ], + [ + "baacCc", + "CACBAA", + 11 + ], + [ + "baacaCcb", + "BBbbbAaBA", + 16 + ], + [ + "baacabBAA", + "aaaaBaB", + 9 + ], + [ + "bab", + "Cc", + 6 + ], + [ + "bab", + "CcBC", + 7 + ], + [ + "bab", + "a", + 4 + ], + [ + "bab", + "aabbAaBca", + 13 + ], + [ + "babA", + "AB", + 6 + ], + [ + "babA", + "bCb", + 4 + ], + [ + "babA", + "cccABCc", + 12 + ], + [ + "babAAaBC", + "Cb", + 14 + ], + [ + "babAaAA", + "bc", + 12 + ], + [ + "babAaBA", + "BbcBAbC", + 10 + ], + [ + "babBBB", + "b", + 10 + ], + [ + "babBa", + "BB", + 7 + ], + [ + "babBbAbAC", + "CbabB", + 12 + ], + [ + "babC", + "BCB", + 6 + ], + [ + "babC", + "CABB", + 6 + ], + [ + "babCABaa", + "BbcCAab", + 9 + ], + [ + "babCB", + "bcCCaABBC", + 12 + ], + [ + "babCBb", + "AAcbb", + 7 + ], + [ + "babCCCc", + "cCAbbca", + 12 + ], + [ + "babCCa", + "c", + 11 + ], + [ + "babCabCab", + "caac", + 13 + ], + [ + "babCbcc", + "AaAAcA", + 10 + ], + [ + "baba", + "abBcbAc", + 9 + ], + [ + "babaBA", + "CbcBbBabB", + 11 + ], + [ + "babaC", + "CbAaAA", + 8 + ], + [ + "babaCBaa", + "BbCa", + 9 + ], + [ + "babaa", + "C", + 10 + ], + [ + "babaaCBCC", + "BbaaAc", + 10 + ], + [ + "babb", + "AcB", + 6 + ], + [ + "babbA", + "BabaAAaB", + 9 + ], + [ + "babbCaaCB", + "cCaAAaBc", + 15 + ], + [ + "babbCbC", + "AA", + 13 + ], + [ + "babbCc", + "acbACaB", + 10 + ], + [ + "babbbbA", + "acA", + 10 + ], + [ + "babbcccAA", + "bAA", + 12 + ], + [ + "babcacA", + "c", + 12 + ], + [ + "bac", + "CBC", + 5 + ], + [ + "bac", + "CcBc", + 6 + ], + [ + "bac", + "Ccbca", + 8 + ], + [ + "bac", + "aacAc", + 6 + ], + [ + "bac", + "ba", + 2 + ], + [ + "bacABcBc", + "ABccc", + 8 + ], + [ + "bacAbca", + "aBCACc", + 9 + ], + [ + "bacBCbBb", + "ccAbcABA", + 12 + ], + [ + "bacBCc", + "cBcBB", + 8 + ], + [ + "bacBaBc", + "AcbcAcA", + 10 + ], + [ + "bacC", + "bbAbaCAB", + 11 + ], + [ + "bacCACCA", + "b", + 14 + ], + [ + "bacCAa", + "b", + 10 + ], + [ + "bacCCCccC", + "CCBbcacA", + 13 + ], + [ + "bacCbaBcB", + "Cab", + 13 + ], + [ + "bacCcBC", + "aBcbcccB", + 10 + ], + [ + "bacaCC", + "BAaAbcAC", + 10 + ], + [ + "bacaCcA", + "BcBAc", + 9 + ], + [ + "bacabaAA", + "AAACCBaC", + 13 + ], + [ + "bacacBa", + "BCAAaAA", + 11 + ], + [ + "bacbABaB", + "bCABBAc", + 10 + ], + [ + "bacbCAbbb", + "BaA", + 13 + ], + [ + "bacc", + "aAcAA", + 7 + ], + [ + "baccAA", + "C", + 11 + ], + [ + "baccAa", + "abCCB", + 9 + ], + [ + "baccBAA", + "ccCCbcCC", + 13 + ], + [ + "baccBBc", + "CAb", + 12 + ], + [ + "baccBbBaa", + "AbcaCcbb", + 12 + ], + [ + "baccCaCA", + "CAccb", + 11 + ], + [ + "baccaBc", + "AccABCbCC", + 11 + ], + [ + "baccccCB", + "cC", + 12 + ], + [ + "bb", + "A", + 4 + ], + [ + "bb", + "AABcCbCB", + 13 + ], + [ + "bb", + "AC", + 4 + ], + [ + "bb", + "Aabc", + 6 + ], + [ + "bb", + "AbC", + 4 + ], + [ + "bb", + "AbCC", + 6 + ], + [ + "bb", + "AbCCbbB", + 10 + ], + [ + "bb", + "Acaa", + 8 + ], + [ + "bb", + "B", + 3 + ], + [ + "bb", + "BAbcaB", + 9 + ], + [ + "bb", + "BCAC", + 7 + ], + [ + "bb", + "Bbccc", + 7 + ], + [ + "bb", + "CC", + 4 + ], + [ + "bb", + "a", + 4 + ], + [ + "bb", + "aBcCcc", + 11 + ], + [ + "bb", + "aaBBCc", + 10 + ], + [ + "bb", + "acCc", + 8 + ], + [ + "bb", + "accBa", + 9 + ], + [ + "bb", + "bA", + 2 + ], + [ + "bb", + "bAc", + 4 + ], + [ + "bb", + "bCbAcbaC", + 12 + ], + [ + "bb", + "baAB", + 5 + ], + [ + "bb", + "cAbBAbC", + 10 + ], + [ + "bb", + "cB", + 3 + ], + [ + "bb", + "cBC", + 5 + ], + [ + "bb", + "cbAacCaca", + 16 + ], + [ + "bb", + "cbBbABC", + 10 + ], + [ + "bbA", + "ABC", + 5 + ], + [ + "bbA", + "BBCabAA", + 9 + ], + [ + "bbA", + "BcCC", + 7 + ], + [ + "bbA", + "CB", + 5 + ], + [ + "bbA", + "bBbaababa", + 13 + ], + [ + "bbA", + "cCabBCAc", + 11 + ], + [ + "bbAA", + "B", + 7 + ], + [ + "bbAA", + "CC", + 8 + ], + [ + "bbAAaBb", + "AcC", + 12 + ], + [ + "bbAAb", + "BBBaaACAb", + 10 + ], + [ + "bbAAc", + "bBaCb", + 6 + ], + [ + "bbAAcAca", + "CCbBaccCB", + 12 + ], + [ + "bbAAcBca", + "bcA", + 11 + ], + [ + "bbAAcbCb", + "bAabCBAAC", + 12 + ], + [ + "bbAB", + "AaABCCB", + 10 + ], + [ + "bbAB", + "AbabABcC", + 8 + ], + [ + "bbAB", + "cBb", + 6 + ], + [ + "bbABACAbC", + "Ccabbc", + 13 + ], + [ + "bbABBC", + "CaAA", + 10 + ], + [ + "bbABCBA", + "ACb", + 9 + ], + [ + "bbABCBCbc", + "b", + 16 + ], + [ + "bbABab", + "CAcaba", + 8 + ], + [ + "bbABc", + "Bac", + 6 + ], + [ + "bbABcBBc", + "Aa", + 14 + ], + [ + "bbABcb", + "c", + 10 + ], + [ + "bbACABB", + "acC", + 12 + ], + [ + "bbACCcCbb", + "CCcC", + 10 + ], + [ + "bbAa", + "CbCaC", + 6 + ], + [ + "bbAa", + "cBcBBC", + 10 + ], + [ + "bbAaC", + "bBcCcAB", + 10 + ], + [ + "bbAaCaBAB", + "aabBc", + 13 + ], + [ + "bbAabBcca", + "BbaabB", + 8 + ], + [ + "bbAaccbAb", + "aCbBAAc", + 14 + ], + [ + "bbAb", + "B", + 7 + ], + [ + "bbAb", + "baaCABAa", + 11 + ], + [ + "bbAb", + "caAABCaac", + 15 + ], + [ + "bbAbAcaCA", + "cacab", + 13 + ], + [ + "bbAbB", + "ccAaCc", + 10 + ], + [ + "bbAbBc", + "AbcAbaBa", + 8 + ], + [ + "bbAbC", + "BBa", + 7 + ], + [ + "bbAbC", + "BcAcaCaaa", + 13 + ], + [ + "bbAbcBA", + "cAbbcBC", + 8 + ], + [ + "bbAc", + "bB", + 5 + ], + [ + "bbAcBabb", + "b", + 14 + ], + [ + "bbAcCaAC", + "CccacCABB", + 12 + ], + [ + "bbAcaA", + "caCabBaAB", + 13 + ], + [ + "bbAcabA", + "bBcbCb", + 9 + ], + [ + "bbB", + "Ac", + 6 + ], + [ + "bbB", + "aCbba", + 6 + ], + [ + "bbB", + "cAcbbBb", + 8 + ], + [ + "bbBA", + "B", + 6 + ], + [ + "bbBACc", + "AcCCBa", + 12 + ], + [ + "bbBAab", + "caAaBCbCb", + 14 + ], + [ + "bbBAbaa", + "aa", + 10 + ], + [ + "bbBBA", + "A", + 8 + ], + [ + "bbBBaaC", + "aCB", + 12 + ], + [ + "bbBBbc", + "ab", + 10 + ], + [ + "bbBCCCAbA", + "aAcCBBc", + 14 + ], + [ + "bbBCCaa", + "AACaAbAA", + 14 + ], + [ + "bbBCaaAC", + "bBAc", + 9 + ], + [ + "bbBCcbcbc", + "BBBaCcbBc", + 7 + ], + [ + "bbBa", + "BBcbc", + 7 + ], + [ + "bbBa", + "Cc", + 8 + ], + [ + "bbBaAcBaC", + "Bcc", + 13 + ], + [ + "bbBaCacbC", + "BAAaBcCa", + 13 + ], + [ + "bbBbA", + "Bbca", + 6 + ], + [ + "bbBbB", + "bBCaCbABa", + 11 + ], + [ + "bbBbBc", + "ABbBbc", + 5 + ], + [ + "bbBbCcBa", + "ba", + 12 + ], + [ + "bbBbaaaBB", + "C", + 18 + ], + [ + "bbBbabc", + "ABACBBacC", + 11 + ], + [ + "bbBbbcCA", + "AaabaCabb", + 15 + ], + [ + "bbBbc", + "Bab", + 7 + ], + [ + "bbBc", + "AaCcBCbCb", + 15 + ], + [ + "bbBcBCA", + "acaba", + 11 + ], + [ + "bbBcCc", + "BAc", + 8 + ], + [ + "bbBcab", + "cAabaC", + 10 + ], + [ + "bbBcbAba", + "b", + 14 + ], + [ + "bbBccBaCB", + "AcbAaCc", + 12 + ], + [ + "bbC", + "A", + 6 + ], + [ + "bbC", + "AAccbCBAA", + 14 + ], + [ + "bbC", + "BCAC", + 5 + ], + [ + "bbC", + "BacBCA", + 8 + ], + [ + "bbC", + "acBbCB", + 7 + ], + [ + "bbCAAb", + "aBAAcAc", + 9 + ], + [ + "bbCABbc", + "aCa", + 11 + ], + [ + "bbCACA", + "cBc", + 10 + ], + [ + "bbCAaBb", + "babcacCA", + 10 + ], + [ + "bbCAaa", + "cBaBbCa", + 11 + ], + [ + "bbCBCca", + "bC", + 10 + ], + [ + "bbCBacAB", + "aC", + 13 + ], + [ + "bbCBcBBcb", + "bCCCaa", + 13 + ], + [ + "bbCC", + "cAbBccA", + 9 + ], + [ + "bbCCAA", + "b", + 10 + ], + [ + "bbCCCBc", + "CCBAcbab", + 14 + ], + [ + "bbCCaccaA", + "ba", + 14 + ], + [ + "bbCCbBA", + "A", + 12 + ], + [ + "bbCCcABa", + "CBBbC", + 13 + ], + [ + "bbCCcCc", + "BbaabbaCc", + 11 + ], + [ + "bbCa", + "BC", + 5 + ], + [ + "bbCa", + "abBbA", + 6 + ], + [ + "bbCaAB", + "CCACcabAC", + 12 + ], + [ + "bbCaBabbC", + "CaBABbacC", + 10 + ], + [ + "bbCaaC", + "BbaBCcbCA", + 11 + ], + [ + "bbCaaCA", + "BcBBB", + 12 + ], + [ + "bbCabA", + "BaBBA", + 8 + ], + [ + "bbCabcAA", + "bc", + 12 + ], + [ + "bbCbAaBaA", + "CbAaAAc", + 9 + ], + [ + "bbCbCB", + "AaBB", + 9 + ], + [ + "bbCbaACb", + "Abbc", + 11 + ], + [ + "bbCbcaBA", + "AB", + 13 + ], + [ + "bbCc", + "aBaC", + 6 + ], + [ + "bbCcAA", + "ccaBAabBA", + 14 + ], + [ + "bbCcBaA", + "aAbCCabAb", + 11 + ], + [ + "bbCcaABc", + "a", + 14 + ], + [ + "bbCcc", + "BBAAA", + 8 + ], + [ + "bbCccBCCa", + "cacaCc", + 12 + ], + [ + "bba", + "ABBcccabc", + 14 + ], + [ + "bba", + "Bb", + 3 + ], + [ + "bba", + "C", + 6 + ], + [ + "bba", + "CBACA", + 8 + ], + [ + "bba", + "aCCAABCcb", + 17 + ], + [ + "bba", + "bbba", + 2 + ], + [ + "bbaA", + "CaCcCbb", + 14 + ], + [ + "bbaACCB", + "bAcAccABC", + 10 + ], + [ + "bbaAaaacc", + "CbaACB", + 11 + ], + [ + "bbaAcBA", + "cBbcaa", + 10 + ], + [ + "bbaB", + "Acc", + 8 + ], + [ + "bbaBAccC", + "cBBAb", + 11 + ], + [ + "bbaBBCc", + "C", + 12 + ], + [ + "bbaBCaAb", + "cCbA", + 12 + ], + [ + "bbaBaAc", + "cABCaA", + 9 + ], + [ + "bbaBbCbCb", + "aBccbBC", + 11 + ], + [ + "bbaBbaAbC", + "acaccca", + 15 + ], + [ + "bbaBc", + "bAbbCc", + 6 + ], + [ + "bbaCbCB", + "CCCA", + 10 + ], + [ + "bbaCbbC", + "c", + 13 + ], + [ + "bbaCc", + "CbCaBAaC", + 11 + ], + [ + "bbaCcBccb", + "C", + 16 + ], + [ + "bbab", + "ACB", + 7 + ], + [ + "bbab", + "CbbCbCbcC", + 12 + ], + [ + "bbabAbca", + "cBbbAB", + 10 + ], + [ + "bbabBcc", + "CAa", + 12 + ], + [ + "bbabC", + "abBAaAba", + 9 + ], + [ + "bbabC", + "cBb", + 7 + ], + [ + "bbabc", + "AAccbABAc", + 12 + ], + [ + "bbacC", + "aabaacA", + 8 + ], + [ + "bbacacb", + "acbCBaAcC", + 12 + ], + [ + "bbacccaa", + "AbBABACaa", + 9 + ], + [ + "bbb", + "ABC", + 5 + ], + [ + "bbb", + "C", + 6 + ], + [ + "bbb", + "aCBc", + 7 + ], + [ + "bbbA", + "bccbbcCCB", + 12 + ], + [ + "bbbABA", + "a", + 11 + ], + [ + "bbbACCCa", + "CBbcC", + 10 + ], + [ + "bbbAaBB", + "B", + 12 + ], + [ + "bbbAaCac", + "cCc", + 12 + ], + [ + "bbbAcbcC", + "aBBACacb", + 9 + ], + [ + "bbbB", + "ABAc", + 7 + ], + [ + "bbbB", + "AbABcABAB", + 12 + ], + [ + "bbbB", + "BB", + 5 + ], + [ + "bbbB", + "aC", + 8 + ], + [ + "bbbB", + "cCCcBcbCb", + 14 + ], + [ + "bbbBBBBCb", + "BcaBa", + 14 + ], + [ + "bbbBbA", + "bbA", + 6 + ], + [ + "bbbBbCBcB", + "bbcBcCC", + 9 + ], + [ + "bbbCa", + "CCB", + 8 + ], + [ + "bbbCccAA", + "bBAa", + 10 + ], + [ + "bbbaABaac", + "abaCbCB", + 13 + ], + [ + "bbbaAa", + "b", + 10 + ], + [ + "bbbaB", + "AC", + 9 + ], + [ + "bbbaa", + "CBBccC", + 10 + ], + [ + "bbbaaBba", + "Baa", + 11 + ], + [ + "bbbaacbb", + "cBbaC", + 10 + ], + [ + "bbbacBbBc", + "bcbcbCCC", + 10 + ], + [ + "bbbb", + "AbaACaC", + 12 + ], + [ + "bbbbbAB", + "aaAaCb", + 13 + ], + [ + "bbbbc", + "BBbC", + 5 + ], + [ + "bbbcAB", + "aBAbBBa", + 11 + ], + [ + "bbbcAcCAC", + "AaBbcCCcb", + 12 + ], + [ + "bbbcB", + "bcCc", + 6 + ], + [ + "bbbcBa", + "CAAccB", + 10 + ], + [ + "bbbcCB", + "aAbBABa", + 10 + ], + [ + "bbbca", + "cBbcabCC", + 9 + ], + [ + "bbbcbBAbc", + "AAacbcc", + 12 + ], + [ + "bbbcbcbc", + "Cbbaac", + 10 + ], + [ + "bbc", + "ACBaABaCC", + 15 + ], + [ + "bbc", + "BcCBaBcCc", + 14 + ], + [ + "bbc", + "CcB", + 6 + ], + [ + "bbc", + "bCCcBAac", + 11 + ], + [ + "bbc", + "c", + 4 + ], + [ + "bbcA", + "AAC", + 7 + ], + [ + "bbcACCca", + "bAABA", + 11 + ], + [ + "bbcAbBa", + "CaaA", + 11 + ], + [ + "bbcAbca", + "CCCCc", + 11 + ], + [ + "bbcAcbBab", + "CBacCca", + 12 + ], + [ + "bbcB", + "BaAbbccBb", + 10 + ], + [ + "bbcBAa", + "baaacc", + 10 + ], + [ + "bbcBCa", + "CaBabc", + 11 + ], + [ + "bbcCA", + "CAb", + 8 + ], + [ + "bbcCBACB", + "bcBBCaBc", + 10 + ], + [ + "bbcCBbAA", + "CcAc", + 12 + ], + [ + "bbcCC", + "BcbCc", + 6 + ], + [ + "bbcCCcB", + "b", + 12 + ], + [ + "bbca", + "AbbcaAcCa", + 10 + ], + [ + "bbca", + "acaBbbB", + 11 + ], + [ + "bbcaACCab", + "aACBab", + 8 + ], + [ + "bbcaAb", + "b", + 10 + ], + [ + "bbcaCab", + "B", + 13 + ], + [ + "bbcaaBCA", + "BCaaC", + 8 + ], + [ + "bbcaacCC", + "bCCcBACB", + 11 + ], + [ + "bbcab", + "bABaa", + 6 + ], + [ + "bbcabAB", + "BcC", + 11 + ], + [ + "bbcb", + "abAA", + 6 + ], + [ + "bbcbB", + "bCbAab", + 7 + ], + [ + "bbcbBccAa", + "bA", + 14 + ], + [ + "bbcbCcAB", + "cacaCCaBA", + 10 + ], + [ + "bbcc", + "B", + 7 + ], + [ + "bbcc", + "CcBBaABa", + 14 + ], + [ + "bbccAcBCB", + "CbacCAaB", + 11 + ], + [ + "bbccC", + "bac", + 6 + ], + [ + "bbccCa", + "baacc", + 7 + ], + [ + "bbccCcA", + "CbbbC", + 10 + ], + [ + "bbccaca", + "bb", + 10 + ], + [ + "bbccb", + "ABCA", + 8 + ], + [ + "bbccbB", + "Cbccbcb", + 5 + ], + [ + "bbccbBB", + "cbCB", + 8 + ], + [ + "bc", + "ACcCb", + 8 + ], + [ + "bc", + "Aa", + 4 + ], + [ + "bc", + "Ac", + 2 + ], + [ + "bc", + "BBAA", + 7 + ], + [ + "bc", + "BcA", + 3 + ], + [ + "bc", + "BcbBaCaCB", + 15 + ], + [ + "bc", + "CACcCBc", + 11 + ], + [ + "bc", + "CBBaAccB", + 13 + ], + [ + "bc", + "CaBa", + 7 + ], + [ + "bc", + "CcCaaABa", + 14 + ], + [ + "bc", + "a", + 4 + ], + [ + "bc", + "aAA", + 6 + ], + [ + "bc", + "aACBcB", + 9 + ], + [ + "bc", + "aBacc", + 7 + ], + [ + "bc", + "aBbAa", + 8 + ], + [ + "bc", + "acb", + 4 + ], + [ + "bc", + "b", + 2 + ], + [ + "bc", + "bA", + 2 + ], + [ + "bc", + "bBCcbA", + 8 + ], + [ + "bc", + "c", + 2 + ], + [ + "bc", + "cBb", + 5 + ], + [ + "bc", + "caaabc", + 8 + ], + [ + "bc", + "ccAB", + 6 + ], + [ + "bcA", + "ABbA", + 5 + ], + [ + "bcA", + "BBB", + 5 + ], + [ + "bcA", + "aabaAAB", + 10 + ], + [ + "bcA", + "acacbc", + 9 + ], + [ + "bcA", + "cbaACAB", + 9 + ], + [ + "bcAA", + "BaCc", + 7 + ], + [ + "bcAABAab", + "CB", + 13 + ], + [ + "bcAABbbBa", + "ABcccc", + 14 + ], + [ + "bcAABcb", + "c", + 12 + ], + [ + "bcAAbc", + "B", + 11 + ], + [ + "bcAB", + "a", + 7 + ], + [ + "bcAB", + "bcB", + 2 + ], + [ + "bcABAABcB", + "Cc", + 15 + ], + [ + "bcABAbBC", + "c", + 14 + ], + [ + "bcABAcB", + "bcCBCAbCa", + 9 + ], + [ + "bcAC", + "AAaC", + 5 + ], + [ + "bcACABab", + "c", + 14 + ], + [ + "bcACBbBaC", + "baaAbBbB", + 10 + ], + [ + "bcACa", + "bbaAcCBB", + 10 + ], + [ + "bcACbCa", + "aaaBBAbC", + 13 + ], + [ + "bcAa", + "BC", + 6 + ], + [ + "bcAa", + "b", + 6 + ], + [ + "bcAaAabA", + "abBB", + 13 + ], + [ + "bcAaBABA", + "CABac", + 10 + ], + [ + "bcAaBAaC", + "AaC", + 10 + ], + [ + "bcAaBCc", + "ca", + 10 + ], + [ + "bcAaaC", + "cbA", + 9 + ], + [ + "bcAabc", + "BAABb", + 7 + ], + [ + "bcAb", + "BcbbbA", + 7 + ], + [ + "bcAbAAAAb", + "abCBaAABc", + 11 + ], + [ + "bcAbACaB", + "cCaCAC", + 10 + ], + [ + "bcAbacAbc", + "BccaBBCbb", + 12 + ], + [ + "bcAc", + "BCBcaBCaB", + 13 + ], + [ + "bcAca", + "A", + 8 + ], + [ + "bcAcba", + "CcAc", + 6 + ], + [ + "bcAcbccB", + "Abbac", + 10 + ], + [ + "bcAccA", + "bbabbAcC", + 11 + ], + [ + "bcAccab", + "CCACcbcC", + 10 + ], + [ + "bcB", + "AaCbCccbb", + 13 + ], + [ + "bcB", + "BaAACbA", + 11 + ], + [ + "bcB", + "CAAABA", + 10 + ], + [ + "bcB", + "aaAaBccC", + 13 + ], + [ + "bcB", + "bAAAACCA", + 13 + ], + [ + "bcB", + "cAbcb", + 5 + ], + [ + "bcBA", + "bBBAb", + 4 + ], + [ + "bcBAAc", + "BccA", + 7 + ], + [ + "bcBACabcB", + "abBA", + 14 + ], + [ + "bcBAab", + "baaaBbcc", + 11 + ], + [ + "bcBAcAC", + "aCCC", + 10 + ], + [ + "bcBBC", + "c", + 8 + ], + [ + "bcBBCbAbA", + "bcCCb", + 10 + ], + [ + "bcBBaA", + "ABCBb", + 9 + ], + [ + "bcBBabABb", + "BcbaBBBCa", + 11 + ], + [ + "bcBBbB", + "bbbCC", + 8 + ], + [ + "bcBCAaaaB", + "CC", + 15 + ], + [ + "bcBCBC", + "aCB", + 8 + ], + [ + "bcBCCAB", + "ABCbc", + 10 + ], + [ + "bcBCCB", + "cAb", + 9 + ], + [ + "bcBCCcbA", + "c", + 14 + ], + [ + "bcBCa", + "CBccAbA", + 10 + ], + [ + "bcBa", + "abaBbcaba", + 11 + ], + [ + "bcBaBB", + "CB", + 9 + ], + [ + "bcBaCc", + "aaCBa", + 9 + ], + [ + "bcBaa", + "BcBB", + 5 + ], + [ + "bcBabcbc", + "accAbcB", + 8 + ], + [ + "bcBac", + "BB", + 7 + ], + [ + "bcBbAB", + "aCAbA", + 7 + ], + [ + "bcBbACCB", + "BAbcCc", + 10 + ], + [ + "bcBbACCac", + "CB", + 15 + ], + [ + "bcBbBCB", + "BcCbb", + 8 + ], + [ + "bcBbbAcac", + "ACbccac", + 9 + ], + [ + "bcBbc", + "caC", + 7 + ], + [ + "bcBc", + "A", + 8 + ], + [ + "bcBcaa", + "BbbCAaBB", + 10 + ], + [ + "bcBccBCBA", + "ABaCCAba", + 13 + ], + [ + "bcBccbcC", + "aC", + 14 + ], + [ + "bcC", + "ACabb", + 9 + ], + [ + "bcC", + "Ac", + 4 + ], + [ + "bcC", + "CACac", + 8 + ], + [ + "bcC", + "CAaCbCbb", + 13 + ], + [ + "bcC", + "CacBAbbac", + 15 + ], + [ + "bcC", + "a", + 6 + ], + [ + "bcC", + "abBBcB", + 8 + ], + [ + "bcC", + "acaAAaCbb", + 14 + ], + [ + "bcC", + "bBCaBBC", + 9 + ], + [ + "bcC", + "bbbCCABb", + 11 + ], + [ + "bcC", + "cBBBBCac", + 13 + ], + [ + "bcC", + "cCAAb", + 8 + ], + [ + "bcC", + "ccBcB", + 7 + ], + [ + "bcCAAA", + "CCCBbBAC", + 11 + ], + [ + "bcCAaA", + "AbCabC", + 9 + ], + [ + "bcCAb", + "CbBccBC", + 9 + ], + [ + "bcCAcAa", + "cB", + 12 + ], + [ + "bcCAcbC", + "AcAAbc", + 7 + ], + [ + "bcCB", + "a", + 8 + ], + [ + "bcCBAB", + "AaBAcB", + 8 + ], + [ + "bcCBBcb", + "CaCbaa", + 11 + ], + [ + "bcCBC", + "cb", + 7 + ], + [ + "bcCBaCAAc", + "aBb", + 16 + ], + [ + "bcCBaCb", + "aaaacC", + 11 + ], + [ + "bcCCAbB", + "BBcCAcA", + 8 + ], + [ + "bcCCa", + "BACbBaAcC", + 13 + ], + [ + "bcCCaa", + "cBcC", + 9 + ], + [ + "bcCCcbA", + "AAaAcCCA", + 12 + ], + [ + "bcCa", + "bCCC", + 3 + ], + [ + "bcCaaAB", + "cAAc", + 9 + ], + [ + "bcCaacC", + "BbcaccCCB", + 10 + ], + [ + "bcCab", + "BcCABbCAC", + 10 + ], + [ + "bcCacAA", + "CcbB", + 10 + ], + [ + "bcCacCaa", + "ABc", + 14 + ], + [ + "bcCacba", + "Bc", + 11 + ], + [ + "bcCb", + "aCbcBbCc", + 10 + ], + [ + "bcCbB", + "bAbCc", + 8 + ], + [ + "bcCbBCbaB", + "BC", + 14 + ], + [ + "bcCbaa", + "BbAACBAaC", + 10 + ], + [ + "bcCbbc", + "baAA", + 10 + ], + [ + "bcCc", + "AAA", + 8 + ], + [ + "bcCcBB", + "BaAccBC", + 8 + ], + [ + "bcCcaCCca", + "AaCc", + 12 + ], + [ + "bcCcbAAc", + "Ca", + 13 + ], + [ + "bca", + "AB", + 6 + ], + [ + "bca", + "AcaaBC", + 8 + ], + [ + "bca", + "CACBbcC", + 10 + ], + [ + "bca", + "bccb", + 4 + ], + [ + "bcaA", + "aBaCBBAa", + 12 + ], + [ + "bcaAAbCbC", + "CCcAA", + 13 + ], + [ + "bcaABAa", + "Ac", + 12 + ], + [ + "bcaACb", + "bAbCaCB", + 8 + ], + [ + "bcaAa", + "BbCcc", + 9 + ], + [ + "bcaAaaAC", + "acaaABc", + 8 + ], + [ + "bcaAba", + "BabBAaccb", + 14 + ], + [ + "bcaB", + "bCBCBAA", + 9 + ], + [ + "bcaBBAC", + "Cb", + 12 + ], + [ + "bcaCA", + "ACA", + 5 + ], + [ + "bcaCA", + "aB", + 8 + ], + [ + "bcaCAABcB", + "bBAbCcbc", + 12 + ], + [ + "bcaCB", + "AbaACbaC", + 10 + ], + [ + "bcaCBa", + "BaAbAB", + 9 + ], + [ + "bcaCa", + "cCbccbA", + 9 + ], + [ + "bcaCbBA", + "bcBabAb", + 8 + ], + [ + "bcaCbCbaA", + "bbCBb", + 11 + ], + [ + "bcaCcB", + "cAab", + 8 + ], + [ + "bcaa", + "CC", + 7 + ], + [ + "bcaa", + "aaBA", + 7 + ], + [ + "bcaaA", + "BAAacaAba", + 11 + ], + [ + "bcaaABBA", + "baa", + 10 + ], + [ + "bcaaAcbC", + "aBACAcaab", + 13 + ], + [ + "bcaaBaAb", + "b", + 14 + ], + [ + "bcaacb", + "acCb", + 7 + ], + [ + "bcab", + "a", + 6 + ], + [ + "bcab", + "cCAabCBCb", + 13 + ], + [ + "bcabAA", + "BAAcca", + 9 + ], + [ + "bcabC", + "Aac", + 7 + ], + [ + "bcabb", + "aAaA", + 8 + ], + [ + "bcac", + "CCAAbCbC", + 12 + ], + [ + "bcb", + "A", + 6 + ], + [ + "bcb", + "aBabCcB", + 9 + ], + [ + "bcb", + "abACACc", + 11 + ], + [ + "bcbA", + "AcbAcbCaB", + 11 + ], + [ + "bcbAAA", + "a", + 11 + ], + [ + "bcbAAAB", + "a", + 13 + ], + [ + "bcbACB", + "CBbcbCca", + 9 + ], + [ + "bcbACCA", + "AabaACAcB", + 11 + ], + [ + "bcbACCC", + "c", + 12 + ], + [ + "bcbAbCbB", + "caA", + 12 + ], + [ + "bcbAc", + "BACCccbCb", + 13 + ], + [ + "bcbBA", + "baaaa", + 7 + ], + [ + "bcbBAABCa", + "cBAcaa", + 10 + ], + [ + "bcbCAaAC", + "bB", + 13 + ], + [ + "bcbCAc", + "caccbaABC", + 11 + ], + [ + "bcbCB", + "c", + 8 + ], + [ + "bcbCcCa", + "CCCbACb", + 11 + ], + [ + "bcbaAACb", + "aBAcA", + 11 + ], + [ + "bcbaBBB", + "AA", + 13 + ], + [ + "bcbaccb", + "ACACCB", + 9 + ], + [ + "bcbb", + "cBcACA", + 9 + ], + [ + "bcbb", + "cCCBaaA", + 12 + ], + [ + "bcbbABC", + "CAAaa", + 11 + ], + [ + "bcbbB", + "ABB", + 7 + ], + [ + "bcbbBc", + "cCc", + 8 + ], + [ + "bcbbCBabA", + "cCccbccBA", + 12 + ], + [ + "bcbc", + "AAA", + 8 + ], + [ + "bcbc", + "Ca", + 7 + ], + [ + "bcbcAC", + "bBAaaacb", + 12 + ], + [ + "bcc", + "C", + 5 + ], + [ + "bcc", + "CaACa", + 9 + ], + [ + "bcc", + "bACb", + 5 + ], + [ + "bcc", + "bbcCcAa", + 8 + ], + [ + "bccA", + "C", + 7 + ], + [ + "bccA", + "aAb", + 8 + ], + [ + "bccA", + "ab", + 8 + ], + [ + "bccABAba", + "acacC", + 13 + ], + [ + "bccAC", + "BbbBcA", + 8 + ], + [ + "bccAbaA", + "aAc", + 12 + ], + [ + "bccAbcbcb", + "CbbcbbA", + 11 + ], + [ + "bccB", + "BcacaCB", + 7 + ], + [ + "bccB", + "cAcb", + 5 + ], + [ + "bccBABc", + "bAacbb", + 10 + ], + [ + "bccBBB", + "aAbbbbC", + 11 + ], + [ + "bccBBCbBa", + "bbbaabCbB", + 11 + ], + [ + "bccBBc", + "cCcbac", + 6 + ], + [ + "bccBBccA", + "bB", + 12 + ], + [ + "bccBCCAC", + "bABBCAabC", + 9 + ], + [ + "bccBCCBc", + "ACBaa", + 13 + ], + [ + "bccBa", + "CAcbB", + 7 + ], + [ + "bccBbA", + "aBCcBBBC", + 9 + ], + [ + "bccCA", + "BaBAAba", + 11 + ], + [ + "bccCAbbB", + "ccC", + 10 + ], + [ + "bccCBab", + "c", + 12 + ], + [ + "bccCCAaBB", + "bBa", + 14 + ], + [ + "bccCaBCC", + "Bb", + 14 + ], + [ + "bcca", + "AaAaabA", + 12 + ], + [ + "bccaCBb", + "bAbcabA", + 9 + ], + [ + "bccaCCC", + "CCBCBbAAA", + 16 + ], + [ + "bccaa", + "C", + 9 + ], + [ + "bccaab", + "cAcc", + 9 + ], + [ + "bccbAbab", + "b", + 14 + ], + [ + "bccbBCcCA", + "B", + 16 + ], + [ + "bccbBb", + "ccba", + 6 + ], + [ + "bccbC", + "bBC", + 5 + ], + [ + "bccbbCC", + "bBccA", + 9 + ], + [ + "bccbbbCB", + "BbcABC", + 10 + ], + [ + "bccbccAc", + "bBC", + 12 + ], + [ + "bccc", + "ACcca", + 5 + ], + [ + "bcccAA", + "bCBAbca", + 10 + ], + [ + "bcccAaBca", + "BaC", + 14 + ], + [ + "bcccBBCaA", + "bcAcaCcb", + 10 + ], + [ + "bcccC", + "aAAaa", + 10 + ], + [ + "bcccCCCbc", + "Bac", + 15 + ], + [ + "c", + "A", + 2 + ], + [ + "c", + "AA", + 4 + ], + [ + "c", + "AABacBC", + 12 + ], + [ + "c", + "AAbBBaBB", + 16 + ], + [ + "c", + "AB", + 4 + ], + [ + "c", + "ABA", + 6 + ], + [ + "c", + "ABBaCCBBB", + 17 + ], + [ + "c", + "ABBbBBCBA", + 17 + ], + [ + "c", + "ABbCb", + 9 + ], + [ + "c", + "ABbaaba", + 14 + ], + [ + "c", + "AC", + 3 + ], + [ + "c", + "ACABa", + 9 + ], + [ + "c", + "ACCB", + 7 + ], + [ + "c", + "ACCcbCCbC", + 16 + ], + [ + "c", + "ACbBBC", + 11 + ], + [ + "c", + "Aa", + 4 + ], + [ + "c", + "AaABA", + 10 + ], + [ + "c", + "Aaa", + 6 + ], + [ + "c", + "AaaaBbBC", + 15 + ], + [ + "c", + "AaabCAb", + 13 + ], + [ + "c", + "Ab", + 4 + ], + [ + "c", + "AbaCAAA", + 13 + ], + [ + "c", + "AcCACaAA", + 14 + ], + [ + "c", + "AcCCBAcaB", + 16 + ], + [ + "c", + "Acb", + 4 + ], + [ + "c", + "AcccbCacB", + 16 + ], + [ + "c", + "B", + 2 + ], + [ + "c", + "BA", + 4 + ], + [ + "c", + "BAAAAbB", + 14 + ], + [ + "c", + "BACA", + 7 + ], + [ + "c", + "BACbAC", + 11 + ], + [ + "c", + "BAbBbAc", + 12 + ], + [ + "c", + "BAbb", + 8 + ], + [ + "c", + "BAcBb", + 8 + ], + [ + "c", + "BB", + 4 + ], + [ + "c", + "BBAACAb", + 13 + ], + [ + "c", + "BBABBC", + 11 + ], + [ + "c", + "BBBcBccc", + 14 + ], + [ + "c", + "BBbbc", + 8 + ], + [ + "c", + "BBbcbbac", + 14 + ], + [ + "c", + "BBcBc", + 8 + ], + [ + "c", + "BC", + 3 + ], + [ + "c", + "BCA", + 5 + ], + [ + "c", + "BCAAcBC", + 12 + ], + [ + "c", + "BCAbCb", + 11 + ], + [ + "c", + "BCBc", + 6 + ], + [ + "c", + "BCa", + 5 + ], + [ + "c", + "BCcbaB", + 10 + ], + [ + "c", + "Ba", + 4 + ], + [ + "c", + "BaAaaA", + 12 + ], + [ + "c", + "BaBacca", + 12 + ], + [ + "c", + "BaCCBaCAC", + 17 + ], + [ + "c", + "BaaBcAcBb", + 16 + ], + [ + "c", + "BaaccC", + 10 + ], + [ + "c", + "BabCcAAcA", + 16 + ], + [ + "c", + "BbABABaBC", + 17 + ], + [ + "c", + "BbCa", + 7 + ], + [ + "c", + "BbaAacbb", + 14 + ], + [ + "c", + "BbaCAAcBa", + 16 + ], + [ + "c", + "BbcBb", + 8 + ], + [ + "c", + "Bbcc", + 6 + ], + [ + "c", + "Bc", + 2 + ], + [ + "c", + "BcBa", + 6 + ], + [ + "c", + "Bcb", + 4 + ], + [ + "c", + "Bcccb", + 8 + ], + [ + "c", + "C", + 1 + ], + [ + "c", + "CB", + 3 + ], + [ + "c", + "CBABBC", + 11 + ], + [ + "c", + "CBAcCA", + 10 + ], + [ + "c", + "CBBAbbA", + 13 + ], + [ + "c", + "CBCBb", + 9 + ], + [ + "c", + "CBCaBa", + 11 + ], + [ + "c", + "CBb", + 5 + ], + [ + "c", + "CCCBaaCB", + 15 + ], + [ + "c", + "CCCCCC", + 11 + ], + [ + "c", + "Ca", + 3 + ], + [ + "c", + "CaAb", + 7 + ], + [ + "c", + "CaCa", + 7 + ], + [ + "c", + "Cab", + 5 + ], + [ + "c", + "CabC", + 7 + ], + [ + "c", + "CbAC", + 7 + ], + [ + "c", + "CbabCAaAc", + 16 + ], + [ + "c", + "CcAABA", + 10 + ], + [ + "c", + "CccaaAa", + 12 + ], + [ + "c", + "a", + 2 + ], + [ + "c", + "aAAa", + 8 + ], + [ + "c", + "aAAc", + 6 + ], + [ + "c", + "aACbcB", + 10 + ], + [ + "c", + "aAbACcCA", + 14 + ], + [ + "c", + "aAbab", + 10 + ], + [ + "c", + "aB", + 4 + ], + [ + "c", + "aBAC", + 7 + ], + [ + "c", + "aBBbbc", + 10 + ], + [ + "c", + "aBaaA", + 10 + ], + [ + "c", + "aBb", + 6 + ], + [ + "c", + "aBcABcCCb", + 16 + ], + [ + "c", + "aBcBBCBbc", + 16 + ], + [ + "c", + "aCa", + 5 + ], + [ + "c", + "aa", + 4 + ], + [ + "c", + "aaCBCbC", + 13 + ], + [ + "c", + "aabcBAAcc", + 16 + ], + [ + "c", + "aabcBcA", + 12 + ], + [ + "c", + "aacABB", + 10 + ], + [ + "c", + "aacb", + 6 + ], + [ + "c", + "aaccAca", + 12 + ], + [ + "c", + "ab", + 4 + ], + [ + "c", + "abAa", + 8 + ], + [ + "c", + "abCAcC", + 10 + ], + [ + "c", + "abCCAB", + 11 + ], + [ + "c", + "abCc", + 6 + ], + [ + "c", + "ac", + 2 + ], + [ + "c", + "accbaBBAB", + 16 + ], + [ + "c", + "b", + 2 + ], + [ + "c", + "bAAccBAa", + 14 + ], + [ + "c", + "bAaBAbcCc", + 16 + ], + [ + "c", + "bAcacAbb", + 14 + ], + [ + "c", + "bBAaCa", + 11 + ], + [ + "c", + "bBAcaCaca", + 16 + ], + [ + "c", + "bBBCbaB", + 13 + ], + [ + "c", + "bBBa", + 8 + ], + [ + "c", + "bBab", + 8 + ], + [ + "c", + "bBcCbcABC", + 16 + ], + [ + "c", + "bBcCca", + 10 + ], + [ + "c", + "bCBC", + 7 + ], + [ + "c", + "bCC", + 5 + ], + [ + "c", + "bCCAbcBBa", + 16 + ], + [ + "c", + "bCCa", + 7 + ], + [ + "c", + "bCabaBC", + 13 + ], + [ + "c", + "bCc", + 4 + ], + [ + "c", + "bCcA", + 6 + ], + [ + "c", + "baaAB", + 10 + ], + [ + "c", + "baaABBC", + 13 + ], + [ + "c", + "baaAccba", + 14 + ], + [ + "c", + "bbBbbBBa", + 16 + ], + [ + "c", + "bbCccAAC", + 14 + ], + [ + "c", + "bc", + 2 + ], + [ + "c", + "bcAbC", + 8 + ], + [ + "c", + "bcBAcAcB", + 14 + ], + [ + "c", + "bcbBbCcb", + 14 + ], + [ + "c", + "bccAAa", + 10 + ], + [ + "c", + "c", + 0 + ], + [ + "c", + "cAABBb", + 10 + ], + [ + "c", + "cAACa", + 8 + ], + [ + "c", + "cABc", + 6 + ], + [ + "c", + "cAaCBc", + 10 + ], + [ + "c", + "cAacbCCA", + 14 + ], + [ + "c", + "cBCCAbA", + 12 + ], + [ + "c", + "cBacAc", + 10 + ], + [ + "c", + "cBcCBb", + 10 + ], + [ + "c", + "cCAB", + 6 + ], + [ + "c", + "cCCbbBBcA", + 16 + ], + [ + "c", + "cCcaBbcba", + 16 + ], + [ + "c", + "ca", + 2 + ], + [ + "c", + "caC", + 4 + ], + [ + "c", + "caa", + 4 + ], + [ + "c", + "cabA", + 6 + ], + [ + "c", + "cb", + 2 + ], + [ + "c", + "cbCaBBab", + 14 + ], + [ + "c", + "cbCbAa", + 10 + ], + [ + "c", + "cbbBbCcC", + 14 + ], + [ + "c", + "cbcaAAC", + 12 + ], + [ + "c", + "cc", + 2 + ], + [ + "c", + "ccB", + 4 + ], + [ + "c", + "ccCBaBAC", + 14 + ], + [ + "c", + "ccc", + 4 + ], + [ + "cA", + "A", + 2 + ], + [ + "cA", + "AAbaCB", + 10 + ], + [ + "cA", + "AC", + 4 + ], + [ + "cA", + "Ac", + 4 + ], + [ + "cA", + "Acc", + 4 + ], + [ + "cA", + "BABCB", + 8 + ], + [ + "cA", + "BBc", + 6 + ], + [ + "cA", + "BaaBCbA", + 11 + ], + [ + "cA", + "Bbb", + 6 + ], + [ + "cA", + "BbbAAccB", + 14 + ], + [ + "cA", + "Bc", + 4 + ], + [ + "cA", + "C", + 3 + ], + [ + "cA", + "CAaCAABb", + 13 + ], + [ + "cA", + "CAaacAcc", + 12 + ], + [ + "cA", + "CbaAccc", + 11 + ], + [ + "cA", + "Cbbb", + 7 + ], + [ + "cA", + "CcC", + 4 + ], + [ + "cA", + "aB", + 4 + ], + [ + "cA", + "aC", + 4 + ], + [ + "cA", + "ab", + 4 + ], + [ + "cA", + "abAAcc", + 10 + ], + [ + "cA", + "abC", + 6 + ], + [ + "cA", + "bBCAbCa", + 11 + ], + [ + "cA", + "bCbbBCAC", + 13 + ], + [ + "cA", + "bCcCBcCA", + 12 + ], + [ + "cA", + "bcaCcbC", + 11 + ], + [ + "cA", + "cABaBCb", + 10 + ], + [ + "cA", + "cBaAB", + 6 + ], + [ + "cA", + "cCBababA", + 12 + ], + [ + "cA", + "ccCBbBab", + 13 + ], + [ + "cA", + "cca", + 3 + ], + [ + "cAA", + "AaaCaBcCb", + 16 + ], + [ + "cAA", + "AbBC", + 8 + ], + [ + "cAA", + "a", + 5 + ], + [ + "cAA", + "cb", + 4 + ], + [ + "cAAA", + "cBaC", + 5 + ], + [ + "cAAAA", + "bcA", + 8 + ], + [ + "cAAAC", + "aBAaCcCca", + 13 + ], + [ + "cAAAacbCB", + "BBaACbA", + 12 + ], + [ + "cAAAbabc", + "BCAacBCBc", + 10 + ], + [ + "cAAAbbc", + "AAbb", + 6 + ], + [ + "cAAAcB", + "bbAB", + 8 + ], + [ + "cAAB", + "ABCaCbCbC", + 15 + ], + [ + "cAAB", + "Aab", + 4 + ], + [ + "cAABaBAa", + "bAaBaBA", + 5 + ], + [ + "cAAC", + "CAB", + 5 + ], + [ + "cAACA", + "CaABCCbAb", + 10 + ], + [ + "cAACAC", + "BC", + 10 + ], + [ + "cAACaCc", + "AcabA", + 9 + ], + [ + "cAACb", + "BCAcca", + 8 + ], + [ + "cAACbABCc", + "CCbAB", + 9 + ], + [ + "cAACbb", + "aAaC", + 7 + ], + [ + "cAACcAA", + "bAaCcacb", + 8 + ], + [ + "cAAa", + "caABcbBAB", + 12 + ], + [ + "cAAaB", + "BBaAb", + 7 + ], + [ + "cAAaBAAA", + "aCb", + 14 + ], + [ + "cAAaCbCc", + "CC", + 12 + ], + [ + "cAAaba", + "bcb", + 10 + ], + [ + "cAAabcCCA", + "aAa", + 13 + ], + [ + "cAAbB", + "AAcaBaa", + 10 + ], + [ + "cAAbBA", + "caCcABAB", + 9 + ], + [ + "cAAbBBaCa", + "CabcB", + 12 + ], + [ + "cAAbC", + "BAbbb", + 6 + ], + [ + "cAAba", + "CCBAabaAB", + 10 + ], + [ + "cAAc", + "bCcCCB", + 10 + ], + [ + "cAAcABCcb", + "BCBB", + 13 + ], + [ + "cAAcB", + "ca", + 7 + ], + [ + "cAAcCCAc", + "Cb", + 14 + ], + [ + "cAAcaabB", + "A", + 14 + ], + [ + "cAAccaaB", + "BBbbAcBc", + 15 + ], + [ + "cAB", + "B", + 4 + ], + [ + "cAB", + "BCAAacb", + 10 + ], + [ + "cAB", + "CaCbCca", + 11 + ], + [ + "cAB", + "CbacbCcbB", + 14 + ], + [ + "cAB", + "b", + 5 + ], + [ + "cAB", + "baabbCA", + 12 + ], + [ + "cAB", + "cbbaBbCb", + 11 + ], + [ + "cAB", + "cccBBCBC", + 12 + ], + [ + "cAB", + "cccCCCCb", + 13 + ], + [ + "cABA", + "CA", + 5 + ], + [ + "cABAABc", + "acabCCb", + 11 + ], + [ + "cABABAACA", + "cBaBCcaa", + 10 + ], + [ + "cABAa", + "c", + 8 + ], + [ + "cABAcaBb", + "BCBaBCBc", + 11 + ], + [ + "cABBAaCac", + "aAacACCcc", + 10 + ], + [ + "cABBCaBbc", + "bBAaBcACC", + 13 + ], + [ + "cABBc", + "B", + 8 + ], + [ + "cABBcacb", + "bBcCcCAA", + 13 + ], + [ + "cABC", + "B", + 6 + ], + [ + "cABCAB", + "bAcb", + 8 + ], + [ + "cABCAc", + "cBCBa", + 6 + ], + [ + "cABCAca", + "ABaCCCbcc", + 12 + ], + [ + "cABCBbbCA", + "ABBccc", + 11 + ], + [ + "cABCa", + "CaA", + 7 + ], + [ + "cABaA", + "aAAbaBb", + 9 + ], + [ + "cABaAaA", + "BAB", + 10 + ], + [ + "cABaBAC", + "acCa", + 12 + ], + [ + "cABaCAbbA", + "A", + 16 + ], + [ + "cABac", + "BaCc", + 6 + ], + [ + "cABbACA", + "BbccCBCBc", + 15 + ], + [ + "cABba", + "AAC", + 8 + ], + [ + "cABc", + "AaccB", + 7 + ], + [ + "cABcABc", + "cAcBBca", + 6 + ], + [ + "cABcBcb", + "cbBCa", + 8 + ], + [ + "cABcb", + "Cac", + 6 + ], + [ + "cABcba", + "BAab", + 8 + ], + [ + "cAC", + "BCaCAbaCa", + 13 + ], + [ + "cAC", + "C", + 4 + ], + [ + "cAC", + "CABbA", + 7 + ], + [ + "cAC", + "CAcABbA", + 10 + ], + [ + "cAC", + "bAa", + 4 + ], + [ + "cAC", + "cBBac", + 6 + ], + [ + "cACAACCbc", + "CAcABb", + 10 + ], + [ + "cACABcACC", + "CAc", + 12 + ], + [ + "cACACbC", + "BbCcAa", + 11 + ], + [ + "cACAabC", + "BbcbBcAAa", + 14 + ], + [ + "cACBCaC", + "C", + 12 + ], + [ + "cACBCcCBb", + "bBaaBaCbC", + 14 + ], + [ + "cACBaaB", + "bccBcBaB", + 9 + ], + [ + "cACCBBcb", + "Ba", + 14 + ], + [ + "cACCaa", + "aBcCaab", + 7 + ], + [ + "cACCc", + "ccAa", + 7 + ], + [ + "cACa", + "aBBcB", + 9 + ], + [ + "cACa", + "cAbCCA", + 5 + ], + [ + "cACaB", + "cAbbaBB", + 6 + ], + [ + "cACac", + "cBCBbbbB", + 12 + ], + [ + "cACbCBB", + "bBbAC", + 12 + ], + [ + "cACbaCc", + "AacCACBaB", + 11 + ], + [ + "cACbaba", + "acAACBcCC", + 11 + ], + [ + "cACbbC", + "Cba", + 8 + ], + [ + "cACbbb", + "ccacBccbA", + 11 + ], + [ + "cACbcb", + "b", + 10 + ], + [ + "cACc", + "Cc", + 4 + ], + [ + "cACc", + "bAba", + 6 + ], + [ + "cACcA", + "Caccc", + 5 + ], + [ + "cACcAcc", + "bbB", + 14 + ], + [ + "cACca", + "acabb", + 9 + ], + [ + "cACcaA", + "bB", + 12 + ], + [ + "cACccAacA", + "bcCBabCc", + 14 + ], + [ + "cAa", + "BacbCAbcA", + 13 + ], + [ + "cAa", + "Bbcb", + 8 + ], + [ + "cAa", + "C", + 5 + ], + [ + "cAa", + "aCBcCAcc", + 12 + ], + [ + "cAa", + "aCacc", + 8 + ], + [ + "cAa", + "caBcA", + 6 + ], + [ + "cAa", + "cacAbaAb", + 10 + ], + [ + "cAa", + "ccccAcaBC", + 12 + ], + [ + "cAaA", + "CbbaAaba", + 10 + ], + [ + "cAaAA", + "CaCbcbab", + 13 + ], + [ + "cAaAA", + "acAc", + 7 + ], + [ + "cAaAABAcb", + "bAba", + 14 + ], + [ + "cAaACBaa", + "cbBabcaac", + 11 + ], + [ + "cAaACc", + "ccbBB", + 10 + ], + [ + "cAaAaaB", + "CBCbBccBa", + 15 + ], + [ + "cAaAc", + "ABCACcb", + 10 + ], + [ + "cAaB", + "C", + 7 + ], + [ + "cAaB", + "aBaBA", + 6 + ], + [ + "cAaBCBbAA", + "aAc", + 14 + ], + [ + "cAaBaA", + "a", + 10 + ], + [ + "cAaC", + "BcbAbbCb", + 10 + ], + [ + "cAaC", + "CbB", + 7 + ], + [ + "cAaCAc", + "ac", + 8 + ], + [ + "cAaCaAc", + "bBBBbaC", + 12 + ], + [ + "cAaCaC", + "ABBa", + 8 + ], + [ + "cAaCc", + "c", + 8 + ], + [ + "cAaaAAA", + "BBCAa", + 11 + ], + [ + "cAaaaBCA", + "AcABc", + 10 + ], + [ + "cAaaacAB", + "aAbCB", + 10 + ], + [ + "cAaaacaa", + "ABB", + 14 + ], + [ + "cAaacB", + "AaCBbACB", + 10 + ], + [ + "cAaaca", + "bAbbc", + 8 + ], + [ + "cAabACa", + "bcaabaaAb", + 9 + ], + [ + "cAabCA", + "CbACAAcb", + 11 + ], + [ + "cAaba", + "a", + 8 + ], + [ + "cAabcacaa", + "aA", + 15 + ], + [ + "cAacAaBBC", + "bBaBAbcaC", + 12 + ], + [ + "cAaca", + "AcacAA", + 7 + ], + [ + "cAaca", + "BcCcBBBA", + 13 + ], + [ + "cAacc", + "BBCaCA", + 9 + ], + [ + "cAacc", + "a", + 8 + ], + [ + "cAacccAb", + "ccbbBACA", + 13 + ], + [ + "cAb", + "B", + 5 + ], + [ + "cAb", + "BAbABAC", + 10 + ], + [ + "cAb", + "BBAAc", + 8 + ], + [ + "cAb", + "CBABCa", + 8 + ], + [ + "cAb", + "CbBBaA", + 10 + ], + [ + "cAb", + "caBACCa", + 10 + ], + [ + "cAbA", + "Cacc", + 6 + ], + [ + "cAbA", + "aAAACA", + 8 + ], + [ + "cAbABbab", + "aCA", + 13 + ], + [ + "cAbAC", + "BAAc", + 5 + ], + [ + "cAbACB", + "bAcCC", + 8 + ], + [ + "cAbACcCaa", + "cBa", + 13 + ], + [ + "cAbAb", + "acBC", + 9 + ], + [ + "cAbAbA", + "bACaC", + 9 + ], + [ + "cAbAbAAAa", + "A", + 16 + ], + [ + "cAbBB", + "AbaaC", + 8 + ], + [ + "cAbBaAAAA", + "BabaaBAa", + 9 + ], + [ + "cAbBaB", + "AcB", + 8 + ], + [ + "cAbaBbBC", + "a", + 14 + ], + [ + "cAbab", + "Ba", + 7 + ], + [ + "cAbabb", + "bC", + 10 + ], + [ + "cAbbB", + "aBBcaAA", + 13 + ], + [ + "cAbbBCAcB", + "aabbaC", + 11 + ], + [ + "cAbbBaca", + "CbCAcBaC", + 12 + ], + [ + "cAbbBbcC", + "CB", + 13 + ], + [ + "cAbbb", + "cCbaBAAAc", + 13 + ], + [ + "cAbcAcBA", + "AcA", + 10 + ], + [ + "cAbcaCccC", + "ABbAbbcB", + 12 + ], + [ + "cAbcbB", + "AAABaac", + 11 + ], + [ + "cAbccBCBc", + "AAACAcbAc", + 12 + ], + [ + "cAc", + "AbBCcaBB", + 13 + ], + [ + "cAc", + "BCCcCaBac", + 13 + ], + [ + "cAc", + "CB", + 5 + ], + [ + "cAc", + "bBb", + 6 + ], + [ + "cAc", + "bCaCaCb", + 11 + ], + [ + "cAc", + "bcb", + 6 + ], + [ + "cAc", + "cAAcaa", + 6 + ], + [ + "cAcA", + "CaaB", + 6 + ], + [ + "cAcAA", + "cbAcBbB", + 8 + ], + [ + "cAcABAAc", + "aABaBBc", + 9 + ], + [ + "cAcACcBb", + "cacbCccC", + 7 + ], + [ + "cAcAb", + "aBbAA", + 8 + ], + [ + "cAcAcAcCc", + "caaAAcbc", + 7 + ], + [ + "cAcAcCB", + "ccbcbBbC", + 10 + ], + [ + "cAcBBaBCb", + "C", + 16 + ], + [ + "cAcBCb", + "abCbAAB", + 11 + ], + [ + "cAcBCcBba", + "A", + 16 + ], + [ + "cAcBbA", + "aBabBACbB", + 13 + ], + [ + "cAcBbB", + "ABBcacBBA", + 10 + ], + [ + "cAcCAB", + "CBBbc", + 11 + ], + [ + "cAcCBCa", + "CCA", + 9 + ], + [ + "cAcCBcBBa", + "CacACBBBB", + 8 + ], + [ + "cAcCCBCBA", + "ccCcbcbc", + 8 + ], + [ + "cAcCbCcAB", + "caC", + 13 + ], + [ + "cAcCcbaaA", + "CcBaa", + 9 + ], + [ + "cAca", + "BAcbCbC", + 10 + ], + [ + "cAcaAa", + "caAAaab", + 7 + ], + [ + "cAcaBAAC", + "BBB", + 14 + ], + [ + "cAcaaa", + "BaBaBC", + 9 + ], + [ + "cAcaaca", + "aaCBA", + 10 + ], + [ + "cAcabCbCB", + "BacBcbABb", + 11 + ], + [ + "cAcabaB", + "Cc", + 11 + ], + [ + "cAcac", + "acbA", + 7 + ], + [ + "cAcbABCBB", + "aABc", + 12 + ], + [ + "cAcbBCBac", + "bCbAAacB", + 13 + ], + [ + "cAcbbccAc", + "AABAaBbCb", + 16 + ], + [ + "cAcc", + "aCa", + 6 + ], + [ + "cAcc", + "bCAAcaCC", + 10 + ], + [ + "cAcc", + "cc", + 4 + ], + [ + "cAccCB", + "aAABca", + 9 + ], + [ + "cAccaBCAb", + "BBcBCCAbb", + 12 + ], + [ + "cAccaaABa", + "bCAcB", + 13 + ], + [ + "cAcccAA", + "bCAb", + 11 + ], + [ + "cAcccaCaA", + "abaB", + 14 + ], + [ + "cAcccacab", + "BbCCbaB", + 13 + ], + [ + "cB", + "AbAB", + 6 + ], + [ + "cB", + "AbBbCA", + 10 + ], + [ + "cB", + "B", + 2 + ], + [ + "cB", + "BACbBc", + 9 + ], + [ + "cB", + "BAcB", + 4 + ], + [ + "cB", + "BBaBaaccC", + 16 + ], + [ + "cB", + "BcAaA", + 8 + ], + [ + "cB", + "C", + 3 + ], + [ + "cB", + "CAA", + 5 + ], + [ + "cB", + "CAAA", + 7 + ], + [ + "cB", + "CAacabCCC", + 15 + ], + [ + "cB", + "CBBB", + 5 + ], + [ + "cB", + "CCBCABaaA", + 15 + ], + [ + "cB", + "Cc", + 3 + ], + [ + "cB", + "CcBAc", + 6 + ], + [ + "cB", + "a", + 4 + ], + [ + "cB", + "aACCba", + 10 + ], + [ + "cB", + "aacbcC", + 9 + ], + [ + "cB", + "acaaAABB", + 12 + ], + [ + "cB", + "b", + 3 + ], + [ + "cB", + "ba", + 4 + ], + [ + "cB", + "baacB", + 6 + ], + [ + "cB", + "bc", + 4 + ], + [ + "cB", + "cABAbCBc", + 12 + ], + [ + "cB", + "cCAB", + 4 + ], + [ + "cB", + "caAAbbBC", + 12 + ], + [ + "cB", + "caBAAa", + 8 + ], + [ + "cBA", + "Ac", + 6 + ], + [ + "cBA", + "CB", + 3 + ], + [ + "cBA", + "a", + 5 + ], + [ + "cBA", + "ac", + 6 + ], + [ + "cBA", + "b", + 5 + ], + [ + "cBA", + "bbaBac", + 9 + ], + [ + "cBA", + "cAaabbab", + 12 + ], + [ + "cBAA", + "AAB", + 6 + ], + [ + "cBAA", + "aCabbA", + 8 + ], + [ + "cBAAB", + "CCa", + 8 + ], + [ + "cBAABa", + "bA", + 9 + ], + [ + "cBAABccc", + "caBcbAAa", + 13 + ], + [ + "cBAAbCcC", + "Bcc", + 11 + ], + [ + "cBAAcAbAC", + "bBaacb", + 10 + ], + [ + "cBAB", + "cBBcaCCb", + 10 + ], + [ + "cBABaBCc", + "BAABc", + 7 + ], + [ + "cBABaac", + "baCbbb", + 12 + ], + [ + "cBABbBAaA", + "c", + 16 + ], + [ + "cBABba", + "AaAccABac", + 12 + ], + [ + "cBABca", + "aBcB", + 7 + ], + [ + "cBACBAAcA", + "BBCbCCBb", + 13 + ], + [ + "cBACa", + "cbaabBBcA", + 12 + ], + [ + "cBACc", + "cBbBaCb", + 7 + ], + [ + "cBACcBBC", + "AbaaBbcC", + 11 + ], + [ + "cBACcbCaa", + "ab", + 15 + ], + [ + "cBAa", + "cCCBaAc", + 8 + ], + [ + "cBAaBcaB", + "b", + 15 + ], + [ + "cBAacbBa", + "aaBb", + 11 + ], + [ + "cBAbCaBA", + "ccaB", + 9 + ], + [ + "cBAba", + "B", + 8 + ], + [ + "cBAbaa", + "CaccCBA", + 12 + ], + [ + "cBAbb", + "ca", + 7 + ], + [ + "cBAbbCaCc", + "aaCA", + 13 + ], + [ + "cBAbca", + "aaACbA", + 9 + ], + [ + "cBAc", + "BA", + 4 + ], + [ + "cBAc", + "aCCcbbbcC", + 13 + ], + [ + "cBAcAb", + "BCabCbcc", + 13 + ], + [ + "cBAcAbaa", + "AAC", + 12 + ], + [ + "cBAcBBc", + "cCcaaa", + 10 + ], + [ + "cBAcCCb", + "cCcAc", + 9 + ], + [ + "cBAcb", + "CaAAACba", + 10 + ], + [ + "cBB", + "BacacBcCA", + 14 + ], + [ + "cBB", + "Bc", + 4 + ], + [ + "cBB", + "bAACA", + 10 + ], + [ + "cBB", + "bCbBBC", + 7 + ], + [ + "cBBA", + "AcAAcABa", + 11 + ], + [ + "cBBA", + "Cca", + 6 + ], + [ + "cBBABC", + "bCcbaAB", + 9 + ], + [ + "cBBABC", + "cCCcBcBc", + 9 + ], + [ + "cBBAac", + "CbbbcB", + 9 + ], + [ + "cBBBABBA", + "AAAbAAAB", + 13 + ], + [ + "cBBBCA", + "caacccAbA", + 13 + ], + [ + "cBBBCacAB", + "Accab", + 13 + ], + [ + "cBBBcbA", + "cB", + 10 + ], + [ + "cBBC", + "acaC", + 6 + ], + [ + "cBBCBaCbA", + "CBcBaAc", + 10 + ], + [ + "cBBCCAca", + "ABCcCa", + 8 + ], + [ + "cBBCb", + "cBBbaaC", + 8 + ], + [ + "cBBCbB", + "aCbBaCaB", + 8 + ], + [ + "cBBaBaAac", + "C", + 17 + ], + [ + "cBBaaCbBb", + "baCCBa", + 11 + ], + [ + "cBBabaAc", + "b", + 14 + ], + [ + "cBBac", + "ba", + 7 + ], + [ + "cBBacC", + "aBcCAC", + 8 + ], + [ + "cBBbAaabA", + "aaA", + 12 + ], + [ + "cBBbB", + "BCcAACb", + 11 + ], + [ + "cBBbBaBcA", + "bbCCCc", + 13 + ], + [ + "cBBbCbc", + "ACccCBaCB", + 13 + ], + [ + "cBBbbaBcb", + "Cc", + 15 + ], + [ + "cBBbbbb", + "acBbaBca", + 10 + ], + [ + "cBBbbcAC", + "bCaBbbcbB", + 9 + ], + [ + "cBBbbcacb", + "BcCcaaBA", + 13 + ], + [ + "cBBbca", + "BbA", + 7 + ], + [ + "cBBcAccA", + "AcbbAC", + 11 + ], + [ + "cBBcBb", + "BaCb", + 7 + ], + [ + "cBBcaBcB", + "Aa", + 14 + ], + [ + "cBBcbC", + "bcCBacb", + 8 + ], + [ + "cBC", + "cACacBc", + 9 + ], + [ + "cBC", + "cCa", + 4 + ], + [ + "cBCA", + "C", + 6 + ], + [ + "cBCACaCaC", + "ccBBBCaac", + 9 + ], + [ + "cBCAaac", + "cB", + 10 + ], + [ + "cBCAacA", + "aaAcA", + 8 + ], + [ + "cBCAcCccC", + "cbB", + 15 + ], + [ + "cBCAcbC", + "CcAbCbbaA", + 13 + ], + [ + "cBCB", + "bbabCAAbB", + 13 + ], + [ + "cBCBCbCa", + "bcCAaCCa", + 10 + ], + [ + "cBCBacA", + "AcbBaA", + 7 + ], + [ + "cBCBbCBa", + "c", + 14 + ], + [ + "cBCCABcC", + "Baa", + 13 + ], + [ + "cBCCB", + "a", + 10 + ], + [ + "cBCCBcaBa", + "AcAAba", + 13 + ], + [ + "cBCCC", + "A", + 10 + ], + [ + "cBCCcBc", + "ccaBCB", + 9 + ], + [ + "cBCaB", + "BBbAA", + 7 + ], + [ + "cBCab", + "aaABbC", + 10 + ], + [ + "cBCacA", + "abcbAbAa", + 11 + ], + [ + "cBCb", + "aCbCc", + 6 + ], + [ + "cBCb", + "b", + 6 + ], + [ + "cBCb", + "cAA", + 6 + ], + [ + "cBCbCAc", + "ACacAB", + 9 + ], + [ + "cBCbCca", + "aaB", + 13 + ], + [ + "cBCbabA", + "bbBbABB", + 9 + ], + [ + "cBCbbBBbc", + "aCaBBbbb", + 10 + ], + [ + "cBCbcCC", + "ACBbBBc", + 10 + ], + [ + "cBCc", + "AcaCBCb", + 8 + ], + [ + "cBCcAbcac", + "aBcac", + 10 + ], + [ + "cBCcbccaa", + "bBbCaBB", + 13 + ], + [ + "cBa", + "b", + 5 + ], + [ + "cBa", + "bAAaA", + 8 + ], + [ + "cBaAA", + "baaCbca", + 11 + ], + [ + "cBaAAB", + "BcbB", + 8 + ], + [ + "cBaAACacA", + "bBa", + 14 + ], + [ + "cBaABacb", + "ACb", + 11 + ], + [ + "cBaB", + "BabB", + 4 + ], + [ + "cBaB", + "CBBBC", + 5 + ], + [ + "cBaBBAB", + "B", + 12 + ], + [ + "cBaBBCc", + "BacCACa", + 10 + ], + [ + "cBaBaABBB", + "bcc", + 17 + ], + [ + "cBaBbCBC", + "cAAB", + 11 + ], + [ + "cBaBc", + "bbC", + 7 + ], + [ + "cBaC", + "BacB", + 5 + ], + [ + "cBaC", + "bA", + 6 + ], + [ + "cBaCA", + "CAcabbb", + 11 + ], + [ + "cBaCAbCC", + "B", + 14 + ], + [ + "cBaCAccbc", + "bbccAcCBB", + 10 + ], + [ + "cBaCCC", + "ab", + 10 + ], + [ + "cBaCaBC", + "Cb", + 11 + ], + [ + "cBaCab", + "BC", + 8 + ], + [ + "cBaa", + "BAab", + 5 + ], + [ + "cBaaA", + "Bb", + 8 + ], + [ + "cBaaCabBb", + "aCACcCbbb", + 11 + ], + [ + "cBaaaBBac", + "bcaAbBcC", + 10 + ], + [ + "cBaab", + "acAacAbCc", + 11 + ], + [ + "cBab", + "ACCAB", + 7 + ], + [ + "cBabB", + "C", + 9 + ], + [ + "cBabBCaca", + "C", + 16 + ], + [ + "cBabBcab", + "CbcAAAa", + 12 + ], + [ + "cBabCaA", + "aaAbAaac", + 10 + ], + [ + "cBabbBCBb", + "C", + 16 + ], + [ + "cBac", + "A", + 7 + ], + [ + "cBacCAB", + "B", + 12 + ], + [ + "cBacabC", + "aAAaB", + 10 + ], + [ + "cBacac", + "c", + 10 + ], + [ + "cBb", + "BbbA", + 5 + ], + [ + "cBb", + "CAbcC", + 7 + ], + [ + "cBb", + "acAB", + 5 + ], + [ + "cBbACBB", + "bCCCAbbC", + 12 + ], + [ + "cBbACC", + "ACa", + 8 + ], + [ + "cBbAcabB", + "aA", + 14 + ], + [ + "cBbC", + "abAabbbc", + 12 + ], + [ + "cBbCAACc", + "bBB", + 13 + ], + [ + "cBbCAB", + "cbCb", + 5 + ], + [ + "cBbCAa", + "cacaBBAb", + 11 + ], + [ + "cBbCAaA", + "AB", + 12 + ], + [ + "cBbCAb", + "bC", + 8 + ], + [ + "cBbCBAAA", + "AbBbc", + 12 + ], + [ + "cBbCCC", + "BCba", + 8 + ], + [ + "cBbCc", + "ccaCBA", + 8 + ], + [ + "cBbCccaC", + "bA", + 13 + ], + [ + "cBba", + "cb", + 4 + ], + [ + "cBbaACaa", + "cC", + 12 + ], + [ + "cBbaCbc", + "ccaaca", + 9 + ], + [ + "cBbaaac", + "b", + 12 + ], + [ + "cBbaab", + "BbbBcC", + 9 + ], + [ + "cBbaccBA", + "c", + 14 + ], + [ + "cBbb", + "CCcCaa", + 10 + ], + [ + "cBbb", + "abCb", + 5 + ], + [ + "cBbbA", + "A", + 8 + ], + [ + "cBbbA", + "B", + 8 + ], + [ + "cBbbBAAb", + "AAaa", + 13 + ], + [ + "cBbbBBacb", + "cCaCCcAac", + 14 + ], + [ + "cBbbBCaa", + "AaBc", + 13 + ], + [ + "cBbbCa", + "CB", + 9 + ], + [ + "cBbbcBB", + "bAACBBb", + 10 + ], + [ + "cBbcBaCBC", + "abA", + 15 + ], + [ + "cBbcCAC", + "b", + 12 + ], + [ + "cBbcCb", + "caBBbb", + 7 + ], + [ + "cBbcaBAAc", + "bB", + 14 + ], + [ + "cBbcbAAb", + "aba", + 13 + ], + [ + "cBbcbcBB", + "AccaA", + 12 + ], + [ + "cBbccCcc", + "aaCBabcBB", + 15 + ], + [ + "cBbccb", + "caaABcBb", + 9 + ], + [ + "cBc", + "BCaAAAbB", + 14 + ], + [ + "cBc", + "CAAC", + 6 + ], + [ + "cBc", + "aAc", + 4 + ], + [ + "cBc", + "aBaAAc", + 8 + ], + [ + "cBc", + "aabbAc", + 9 + ], + [ + "cBc", + "aac", + 4 + ], + [ + "cBc", + "c", + 4 + ], + [ + "cBcA", + "bC", + 6 + ], + [ + "cBcAAbbcb", + "BAbb", + 10 + ], + [ + "cBcACAA", + "a", + 13 + ], + [ + "cBcACb", + "Bc", + 8 + ], + [ + "cBcAaACbc", + "abbB", + 14 + ], + [ + "cBcBaaC", + "AbC", + 11 + ], + [ + "cBcBbCC", + "aBbCaba", + 11 + ], + [ + "cBcBbCbbA", + "cBa", + 13 + ], + [ + "cBcBbb", + "Acb", + 8 + ], + [ + "cBcCaaAcb", + "baccACCCc", + 13 + ], + [ + "cBcCbcCa", + "aCa", + 12 + ], + [ + "cBcCccaAa", + "BCaCCabbA", + 12 + ], + [ + "cBcaAaC", + "bACBCAB", + 12 + ], + [ + "cBcaAbBCb", + "baCbbaa", + 12 + ], + [ + "cBcaAbc", + "aa", + 11 + ], + [ + "cBcaAcaaA", + "CACbcC", + 14 + ], + [ + "cBcaBcBc", + "aBbBCACc", + 11 + ], + [ + "cBcaCcCA", + "Bcc", + 10 + ], + [ + "cBcabC", + "bAAAabb", + 10 + ], + [ + "cBcbACbBB", + "ACBc", + 12 + ], + [ + "cBcbBAbAC", + "A", + 16 + ], + [ + "cBcbC", + "AAbBcCa", + 10 + ], + [ + "cBcbcaCb", + "cCbcCB", + 6 + ], + [ + "cBcc", + "bAcAcaaba", + 14 + ], + [ + "cBcc", + "cacc", + 2 + ], + [ + "cBccaAac", + "aab", + 12 + ], + [ + "cBccaBCB", + "Caa", + 13 + ], + [ + "cBccacbCA", + "bBCBAca", + 11 + ], + [ + "cBccbc", + "CAbA", + 9 + ], + [ + "cBcccCAC", + "BA", + 12 + ], + [ + "cC", + "A", + 4 + ], + [ + "cC", + "ABCCCa", + 9 + ], + [ + "cC", + "AbAacCAc", + 12 + ], + [ + "cC", + "AccAAABc", + 13 + ], + [ + "cC", + "BBAabcC", + 10 + ], + [ + "cC", + "BBc", + 5 + ], + [ + "cC", + "BBcaACaA", + 12 + ], + [ + "cC", + "BCABBCAaC", + 15 + ], + [ + "cC", + "BaAbcaBc", + 13 + ], + [ + "cC", + "BaBaccb", + 11 + ], + [ + "cC", + "CBA", + 5 + ], + [ + "cC", + "CacCac", + 8 + ], + [ + "cC", + "CcACcacbB", + 14 + ], + [ + "cC", + "CcBbb", + 8 + ], + [ + "cC", + "aACbbCAB", + 13 + ], + [ + "cC", + "aBABCBbc", + 14 + ], + [ + "cC", + "aCCcAAca", + 13 + ], + [ + "cC", + "aaBaBbAA", + 16 + ], + [ + "cC", + "acAb", + 6 + ], + [ + "cC", + "b", + 4 + ], + [ + "cC", + "bA", + 4 + ], + [ + "cC", + "bACBaA", + 10 + ], + [ + "cC", + "bacAcbc", + 11 + ], + [ + "cC", + "bcaCBaB", + 10 + ], + [ + "cC", + "c", + 2 + ], + [ + "cC", + "cA", + 2 + ], + [ + "cC", + "cABAc", + 7 + ], + [ + "cC", + "cAbc", + 5 + ], + [ + "cC", + "cBA", + 4 + ], + [ + "cC", + "cBCAccaCb", + 14 + ], + [ + "cC", + "cabcBCB", + 10 + ], + [ + "cC", + "cccaBa", + 9 + ], + [ + "cCA", + "c", + 4 + ], + [ + "cCAABb", + "ACbaBC", + 7 + ], + [ + "cCAACb", + "bbC", + 10 + ], + [ + "cCAAacBb", + "a", + 14 + ], + [ + "cCAAc", + "ABbCcbBb", + 14 + ], + [ + "cCAAc", + "AC", + 7 + ], + [ + "cCAB", + "AAaCAABab", + 12 + ], + [ + "cCABbAb", + "Ab", + 10 + ], + [ + "cCABbaBaA", + "aacC", + 15 + ], + [ + "cCAC", + "bCbbbbAC", + 10 + ], + [ + "cCACAb", + "AbC", + 10 + ], + [ + "cCACCc", + "cccbAabc", + 9 + ], + [ + "cCACc", + "BbAcaB", + 9 + ], + [ + "cCAa", + "CBA", + 5 + ], + [ + "cCAaabBbA", + "bCCCcacb", + 13 + ], + [ + "cCAac", + "BBA", + 8 + ], + [ + "cCAacBCc", + "CCBBaA", + 11 + ], + [ + "cCAacCCbA", + "bCACCa", + 9 + ], + [ + "cCAbA", + "aBBcaccbA", + 11 + ], + [ + "cCAbABC", + "bbACCaACB", + 13 + ], + [ + "cCAbACcc", + "AbBAccbb", + 11 + ], + [ + "cCAbCacAA", + "BcABBAC", + 12 + ], + [ + "cCAbbaA", + "CcACabA", + 8 + ], + [ + "cCAbcA", + "BABABC", + 10 + ], + [ + "cCAcAcbaB", + "BccBaCC", + 13 + ], + [ + "cCAcCAB", + "bcccCbbab", + 11 + ], + [ + "cCAcaabBA", + "bBcbCCBa", + 13 + ], + [ + "cCAcbBBa", + "AaBca", + 10 + ], + [ + "cCAcbcB", + "c", + 12 + ], + [ + "cCB", + "BBCAbba", + 11 + ], + [ + "cCB", + "BCcab", + 7 + ], + [ + "cCB", + "CC", + 3 + ], + [ + "cCB", + "CbBa", + 5 + ], + [ + "cCB", + "a", + 6 + ], + [ + "cCB", + "aC", + 4 + ], + [ + "cCBA", + "abcA", + 6 + ], + [ + "cCBACb", + "abBccaC", + 11 + ], + [ + "cCBAbCCa", + "a", + 14 + ], + [ + "cCBAbaC", + "aAbaAa", + 10 + ], + [ + "cCBAcCB", + "CbaCB", + 6 + ], + [ + "cCBBAb", + "cBcaC", + 7 + ], + [ + "cCBBBc", + "Cc", + 8 + ], + [ + "cCBBaa", + "CBC", + 8 + ], + [ + "cCBCBAc", + "ABCBCBbCB", + 9 + ], + [ + "cCBCa", + "AcacaBCAc", + 10 + ], + [ + "cCBCbB", + "Cc", + 9 + ], + [ + "cCBCcbB", + "cCBaAaBc", + 8 + ], + [ + "cCBaAac", + "C", + 12 + ], + [ + "cCBaCc", + "CcbBBbB", + 10 + ], + [ + "cCBac", + "bb", + 9 + ], + [ + "cCBbAC", + "abAbC", + 8 + ], + [ + "cCBbAaabb", + "BCc", + 16 + ], + [ + "cCBbAcCAC", + "acBCCbA", + 12 + ], + [ + "cCBbAccB", + "abA", + 12 + ], + [ + "cCBbbB", + "bbaaCCBbB", + 11 + ], + [ + "cCBc", + "ABbacCc", + 10 + ], + [ + "cCBc", + "cAcBb", + 5 + ], + [ + "cCBcABc", + "ac", + 11 + ], + [ + "cCBcACBa", + "cACcB", + 10 + ], + [ + "cCBcACCa", + "BACAAaa", + 11 + ], + [ + "cCBca", + "cbCacC", + 6 + ], + [ + "cCBcb", + "BCbAABAba", + 12 + ], + [ + "cCBcbCaCb", + "cbBCa", + 10 + ], + [ + "cCC", + "BbacaAb", + 12 + ], + [ + "cCC", + "CB", + 4 + ], + [ + "cCC", + "accaCCCcb", + 12 + ], + [ + "cCC", + "bAbcCcbcb", + 13 + ], + [ + "cCC", + "bB", + 6 + ], + [ + "cCC", + "bCBbABBCc", + 14 + ], + [ + "cCC", + "baAAb", + 10 + ], + [ + "cCC", + "cABBc", + 7 + ], + [ + "cCC", + "cCCbaac", + 8 + ], + [ + "cCCAbbbaa", + "BccccA", + 15 + ], + [ + "cCCAc", + "ccAbbb", + 9 + ], + [ + "cCCBBaBC", + "bcaA", + 13 + ], + [ + "cCCBCAaA", + "baB", + 13 + ], + [ + "cCCBc", + "bAccaAcB", + 11 + ], + [ + "cCCCC", + "cBC", + 6 + ], + [ + "cCCCaA", + "cCbB", + 8 + ], + [ + "cCCCaac", + "BCAcCb", + 11 + ], + [ + "cCCCbA", + "CcBBBBb", + 11 + ], + [ + "cCCCbb", + "ABccCcA", + 10 + ], + [ + "cCCCbbbAa", + "cA", + 14 + ], + [ + "cCCa", + "AccBc", + 7 + ], + [ + "cCCaA", + "BcACCA", + 6 + ], + [ + "cCCaAAAC", + "aBbcC", + 12 + ], + [ + "cCCaCB", + "CaaAcbCAB", + 12 + ], + [ + "cCCaccA", + "bc", + 12 + ], + [ + "cCCaccAC", + "abAACcCa", + 12 + ], + [ + "cCCb", + "C", + 6 + ], + [ + "cCCb", + "cAcBa", + 6 + ], + [ + "cCCbBCBab", + "CCcaca", + 11 + ], + [ + "cCCbb", + "BCBbbcA", + 8 + ], + [ + "cCCbc", + "bCCC", + 5 + ], + [ + "cCCbcbabA", + "aaCCc", + 14 + ], + [ + "cCCcABBB", + "aBBbABcb", + 11 + ], + [ + "cCCcCCcAa", + "bBCab", + 15 + ], + [ + "cCCcaaC", + "aBC", + 10 + ], + [ + "cCa", + "BacAaCAcA", + 13 + ], + [ + "cCa", + "C", + 4 + ], + [ + "cCa", + "CAACAAaC", + 11 + ], + [ + "cCa", + "aBBcbc", + 10 + ], + [ + "cCa", + "aCBab", + 6 + ], + [ + "cCa", + "aaCCAAAb", + 12 + ], + [ + "cCa", + "bBAcCcB", + 10 + ], + [ + "cCa", + "cBBcaCB", + 9 + ], + [ + "cCaA", + "CCaca", + 4 + ], + [ + "cCaA", + "cBcccC", + 9 + ], + [ + "cCaAAACA", + "cBBbacc", + 12 + ], + [ + "cCaAAacCc", + "cbCaBcA", + 12 + ], + [ + "cCaABCAAA", + "A", + 16 + ], + [ + "cCaAb", + "aBaAB", + 5 + ], + [ + "cCaAbAABA", + "CCabcAAbb", + 8 + ], + [ + "cCaAc", + "ABAbbBBB", + 15 + ], + [ + "cCaAcACaa", + "caaBcBc", + 11 + ], + [ + "cCaAcCBca", + "ABcc", + 12 + ], + [ + "cCaAcbA", + "aBCBcCCba", + 12 + ], + [ + "cCaB", + "ABABAbBBc", + 15 + ], + [ + "cCaBBaAb", + "aA", + 12 + ], + [ + "cCaBBaCAC", + "bC", + 15 + ], + [ + "cCaBBba", + "aa", + 10 + ], + [ + "cCaBCbb", + "BcaCB", + 8 + ], + [ + "cCaBb", + "Ac", + 9 + ], + [ + "cCaC", + "AC", + 5 + ], + [ + "cCaCA", + "BBBbbCAC", + 12 + ], + [ + "cCaCBBBCb", + "B", + 16 + ], + [ + "cCaCCB", + "BcBac", + 9 + ], + [ + "cCaCaB", + "AbcA", + 10 + ], + [ + "cCaCaaaA", + "abcCcc", + 14 + ], + [ + "cCaCc", + "aA", + 8 + ], + [ + "cCaa", + "CcCbcbA", + 9 + ], + [ + "cCaaBCb", + "A", + 13 + ], + [ + "cCaab", + "a", + 8 + ], + [ + "cCabB", + "AbbA", + 8 + ], + [ + "cCabB", + "AcaaACCb", + 11 + ], + [ + "cCabC", + "aBcB", + 8 + ], + [ + "cCabaBC", + "aB", + 10 + ], + [ + "cCabc", + "cabcccAB", + 10 + ], + [ + "cCabcBa", + "abccAaAB", + 12 + ], + [ + "cCac", + "AacBAaA", + 10 + ], + [ + "cCacacaa", + "CACbAbcC", + 13 + ], + [ + "cCacba", + "aAbAbBAc", + 13 + ], + [ + "cCb", + "AccbcbcB", + 11 + ], + [ + "cCb", + "BaC", + 6 + ], + [ + "cCb", + "BabCcAA", + 12 + ], + [ + "cCb", + "aCaBbBbC", + 12 + ], + [ + "cCb", + "accCcBAbA", + 12 + ], + [ + "cCb", + "cC", + 2 + ], + [ + "cCbAB", + "bcaac", + 8 + ], + [ + "cCbAB", + "cca", + 6 + ], + [ + "cCbAc", + "baB", + 7 + ], + [ + "cCbAcaB", + "AaBB", + 10 + ], + [ + "cCbBAc", + "bbBBBCa", + 10 + ], + [ + "cCbBBAbC", + "BCa", + 13 + ], + [ + "cCbBa", + "AAcCCaAB", + 11 + ], + [ + "cCbBacc", + "aBAbCAc", + 11 + ], + [ + "cCbBb", + "aacaBa", + 9 + ], + [ + "cCbBb", + "baAAacaCA", + 17 + ], + [ + "cCbBbc", + "acaAbC", + 8 + ], + [ + "cCbC", + "B", + 7 + ], + [ + "cCbC", + "CcBaA", + 7 + ], + [ + "cCbC", + "aC", + 6 + ], + [ + "cCbCCbCaA", + "acBBbCcC", + 12 + ], + [ + "cCbCaCBa", + "aBBAAbaA", + 13 + ], + [ + "cCbCac", + "CBAB", + 8 + ], + [ + "cCbCcaCc", + "cAbbAab", + 10 + ], + [ + "cCbCcbcbA", + "cbb", + 12 + ], + [ + "cCbaACaB", + "AB", + 12 + ], + [ + "cCbaAcaa", + "BCcCcb", + 12 + ], + [ + "cCbaB", + "aCCbc", + 7 + ], + [ + "cCbbAaaba", + "CccBCA", + 14 + ], + [ + "cCbbAbBC", + "acaAcBC", + 9 + ], + [ + "cCbbBC", + "bca", + 10 + ], + [ + "cCbbCB", + "AaBb", + 9 + ], + [ + "cCbbCBc", + "aBBAbba", + 12 + ], + [ + "cCbbCaA", + "A", + 12 + ], + [ + "cCbbabAC", + "B", + 15 + ], + [ + "cCbbccCb", + "bAcCc", + 10 + ], + [ + "cCbcABAa", + "ABaACa", + 11 + ], + [ + "cCbcB", + "bBBcbbA", + 10 + ], + [ + "cCbcbc", + "Aa", + 12 + ], + [ + "cCbccbB", + "abaaBc", + 11 + ], + [ + "cCbccbbaC", + "aCBAcBB", + 11 + ], + [ + "cCc", + "ABcAcB", + 8 + ], + [ + "cCc", + "BbaaC", + 9 + ], + [ + "cCc", + "CAbAabcAc", + 14 + ], + [ + "cCc", + "CccC", + 4 + ], + [ + "cCcA", + "CBaCbc", + 9 + ], + [ + "cCcAAC", + "cCbaCa", + 7 + ], + [ + "cCcAAbbcB", + "A", + 16 + ], + [ + "cCcAB", + "BCCa", + 6 + ], + [ + "cCcACAaCB", + "cccb", + 12 + ], + [ + "cCcAbbBbc", + "CBbaAb", + 12 + ], + [ + "cCcAcAa", + "c", + 12 + ], + [ + "cCcBC", + "CBBAbBAcC", + 13 + ], + [ + "cCcBCcbcb", + "C", + 16 + ], + [ + "cCcC", + "aAcbBCb", + 10 + ], + [ + "cCcCAb", + "bAaCcaCCB", + 11 + ], + [ + "cCcCaCaCB", + "AC", + 15 + ], + [ + "cCcCcB", + "b", + 11 + ], + [ + "cCcaAbCC", + "cAAaCC", + 7 + ], + [ + "cCcaBA", + "C", + 10 + ], + [ + "cCcaBBB", + "BCaCCcB", + 10 + ], + [ + "cCcaC", + "CCAcbCcCc", + 11 + ], + [ + "cCcaCa", + "AAcb", + 10 + ], + [ + "cCcaabba", + "A", + 15 + ], + [ + "cCcabc", + "bc", + 8 + ], + [ + "cCcb", + "BBaAbbaAb", + 16 + ], + [ + "cCcbAbcb", + "AcCbcAAA", + 12 + ], + [ + "cCcbBCCBC", + "cC", + 14 + ], + [ + "cCcbCCB", + "BAbABC", + 12 + ], + [ + "cCcbCaaC", + "cbbcccbca", + 12 + ], + [ + "cCcbaCca", + "baBCABcC", + 13 + ], + [ + "cCcbaba", + "cCBBA", + 7 + ], + [ + "cCcc", + "a", + 8 + ], + [ + "cCccBaa", + "CBcBA", + 7 + ], + [ + "cCccCaCbA", + "caABbAcB", + 13 + ], + [ + "cCccaA", + "abb", + 12 + ], + [ + "cCccbA", + "BBabb", + 10 + ], + [ + "cCccbB", + "abAcB", + 8 + ], + [ + "cCcccAAb", + "aa", + 14 + ], + [ + "cCcccC", + "CBcBaAAc", + 12 + ], + [ + "cCcccCA", + "bcCACb", + 9 + ], + [ + "ca", + "AC", + 4 + ], + [ + "ca", + "ACA", + 4 + ], + [ + "ca", + "ACACb", + 8 + ], + [ + "ca", + "ACa", + 3 + ], + [ + "ca", + "Aaaa", + 6 + ], + [ + "ca", + "BAacabbab", + 14 + ], + [ + "ca", + "BB", + 4 + ], + [ + "ca", + "BBcaAbBcb", + 14 + ], + [ + "ca", + "BacCBC", + 10 + ], + [ + "ca", + "BbA", + 5 + ], + [ + "ca", + "BbBbBC", + 12 + ], + [ + "ca", + "BbCACC", + 10 + ], + [ + "ca", + "Bcbc", + 6 + ], + [ + "ca", + "BccA", + 5 + ], + [ + "ca", + "C", + 3 + ], + [ + "ca", + "CA", + 2 + ], + [ + "ca", + "CAC", + 4 + ], + [ + "ca", + "CCcbBaaab", + 14 + ], + [ + "ca", + "CbCaAaB", + 11 + ], + [ + "ca", + "a", + 2 + ], + [ + "ca", + "aBAA", + 7 + ], + [ + "ca", + "aC", + 4 + ], + [ + "ca", + "aaAbcACCc", + 15 + ], + [ + "ca", + "acccCcBc", + 14 + ], + [ + "ca", + "bAAaCCAc", + 14 + ], + [ + "ca", + "bBcAABbbB", + 15 + ], + [ + "ca", + "bbb", + 6 + ], + [ + "ca", + "cBC", + 4 + ], + [ + "ca", + "cCbba", + 6 + ], + [ + "ca", + "ccBBaba", + 10 + ], + [ + "caA", + "B", + 6 + ], + [ + "caA", + "CcCcAA", + 7 + ], + [ + "caA", + "aCAbCCCA", + 12 + ], + [ + "caA", + "aCcCbbAAC", + 13 + ], + [ + "caA", + "bBbac", + 8 + ], + [ + "caA", + "bCACCACC", + 12 + ], + [ + "caA", + "bbaA", + 4 + ], + [ + "caA", + "bbbBA", + 8 + ], + [ + "caA", + "cbAacAbbc", + 12 + ], + [ + "caAA", + "bAbaa", + 7 + ], + [ + "caAAAB", + "ab", + 9 + ], + [ + "caAAAcaA", + "CacaAcA", + 6 + ], + [ + "caAACbc", + "CBBCABbbb", + 13 + ], + [ + "caAB", + "A", + 6 + ], + [ + "caAB", + "BCAcbbB", + 10 + ], + [ + "caAB", + "CAaAab", + 6 + ], + [ + "caABabc", + "ACbbCCC", + 12 + ], + [ + "caABb", + "AaCaBC", + 7 + ], + [ + "caAC", + "CbCcB", + 8 + ], + [ + "caAC", + "bBBBCCBA", + 14 + ], + [ + "caACABAA", + "aa", + 13 + ], + [ + "caACBa", + "BbbBcAc", + 13 + ], + [ + "caACCBAaa", + "bbAcA", + 13 + ], + [ + "caACCac", + "a", + 12 + ], + [ + "caAa", + "abBcaB", + 10 + ], + [ + "caAa", + "acaCcBc", + 10 + ], + [ + "caAaBaBc", + "bcbCa", + 14 + ], + [ + "caAaa", + "ac", + 8 + ], + [ + "caAaaaaa", + "ccc", + 14 + ], + [ + "caAac", + "ACcACAbc", + 9 + ], + [ + "caAbAAcBb", + "c", + 16 + ], + [ + "caAbAcACB", + "CBCCaAaaB", + 13 + ], + [ + "caAbBBAbC", + "bacC", + 13 + ], + [ + "caAbBCb", + "bbaaaCc", + 11 + ], + [ + "caAbbBa", + "bAC", + 12 + ], + [ + "caAbbb", + "bbCACabaC", + 13 + ], + [ + "caAc", + "baCBB", + 8 + ], + [ + "caAcCc", + "ACc", + 6 + ], + [ + "caAcbbaCB", + "cbcBc", + 12 + ], + [ + "caAcc", + "BBbabc", + 9 + ], + [ + "caAcccaAb", + "Acab", + 10 + ], + [ + "caB", + "CbAcacAa", + 12 + ], + [ + "caB", + "ccBA", + 4 + ], + [ + "caBAA", + "aCbc", + 8 + ], + [ + "caBABbbC", + "CB", + 13 + ], + [ + "caBAabAcC", + "aCA", + 14 + ], + [ + "caBAbAAc", + "bcCBBa", + 12 + ], + [ + "caBAbaAb", + "ba", + 12 + ], + [ + "caBAcB", + "BaaaBA", + 9 + ], + [ + "caBBA", + "Bc", + 8 + ], + [ + "caBBA", + "CacB", + 5 + ], + [ + "caBBaBAB", + "acAbaa", + 11 + ], + [ + "caBBbaaA", + "bB", + 13 + ], + [ + "caBC", + "CBaaBaAa", + 11 + ], + [ + "caBCBaBC", + "aABaB", + 8 + ], + [ + "caBCac", + "b", + 11 + ], + [ + "caBCc", + "CBCabB", + 9 + ], + [ + "caBa", + "CaBabAC", + 7 + ], + [ + "caBa", + "CcbccA", + 9 + ], + [ + "caBaB", + "BcA", + 8 + ], + [ + "caBaCAb", + "AacCAA", + 8 + ], + [ + "caBaCc", + "cCCA", + 8 + ], + [ + "caBb", + "AaCbCAAa", + 12 + ], + [ + "caBb", + "BCBC", + 6 + ], + [ + "caBb", + "b", + 6 + ], + [ + "caBbCaa", + "caCCbb", + 8 + ], + [ + "caBbCbbBB", + "aBCBCabca", + 11 + ], + [ + "caBbCcBC", + "cccBaB", + 11 + ], + [ + "caBbac", + "bac", + 6 + ], + [ + "caBbbB", + "BcBBA", + 9 + ], + [ + "caBbbcBa", + "b", + 14 + ], + [ + "caBc", + "bA", + 7 + ], + [ + "caBc", + "bBA", + 6 + ], + [ + "caBc", + "cc", + 4 + ], + [ + "caBcAA", + "ccacac", + 7 + ], + [ + "caBcacCb", + "bBc", + 12 + ], + [ + "caC", + "BCbb", + 7 + ], + [ + "caC", + "BbBcA", + 9 + ], + [ + "caC", + "babCacB", + 10 + ], + [ + "caC", + "bb", + 6 + ], + [ + "caC", + "cCbAaa", + 8 + ], + [ + "caCAB", + "CBA", + 7 + ], + [ + "caCAC", + "AcaaCaacc", + 10 + ], + [ + "caCAb", + "BaC", + 6 + ], + [ + "caCAccca", + "bACc", + 11 + ], + [ + "caCBA", + "aBBBBcA", + 10 + ], + [ + "caCBC", + "BcBBabA", + 11 + ], + [ + "caCBC", + "ac", + 7 + ], + [ + "caCBba", + "cBA", + 7 + ], + [ + "caCBbaBB", + "a", + 14 + ], + [ + "caCBbc", + "A", + 11 + ], + [ + "caCC", + "CaBC", + 3 + ], + [ + "caCCAa", + "Baabb", + 10 + ], + [ + "caCCAcaa", + "aC", + 12 + ], + [ + "caCCC", + "bCBaAAbC", + 11 + ], + [ + "caCCaa", + "acabCacA", + 7 + ], + [ + "caCCbCc", + "aCBcC", + 7 + ], + [ + "caCCcc", + "cbaCAacc", + 6 + ], + [ + "caCaBB", + "ccbBaBA", + 8 + ], + [ + "caCaBbcAA", + "baCcAB", + 10 + ], + [ + "caCaCAaaC", + "ABB", + 16 + ], + [ + "caCabcBa", + "CAcCABcc", + 10 + ], + [ + "caCb", + "BcAcABAB", + 11 + ], + [ + "caCb", + "bAbBCb", + 7 + ], + [ + "caCbAAcb", + "aaBbAB", + 9 + ], + [ + "caCbCBBb", + "A", + 15 + ], + [ + "caCbbC", + "cCcc", + 7 + ], + [ + "caCbbcbC", + "CBAAbCC", + 10 + ], + [ + "caCbc", + "B", + 9 + ], + [ + "caCcCaaB", + "AacCbcca", + 11 + ], + [ + "caa", + "CCAcAcaab", + 12 + ], + [ + "caa", + "b", + 6 + ], + [ + "caa", + "bAB", + 5 + ], + [ + "caa", + "bacAa", + 5 + ], + [ + "caaA", + "BaCbcC", + 10 + ], + [ + "caaAABaac", + "AC", + 15 + ], + [ + "caaACA", + "cB", + 10 + ], + [ + "caaAa", + "CaBaAbcB", + 9 + ], + [ + "caaAa", + "bbC", + 10 + ], + [ + "caaAbbc", + "Aa", + 11 + ], + [ + "caaB", + "bAc", + 7 + ], + [ + "caaB", + "cB", + 4 + ], + [ + "caaBA", + "C", + 9 + ], + [ + "caaBAabC", + "BACB", + 11 + ], + [ + "caaBCC", + "Ab", + 10 + ], + [ + "caaBCc", + "CCb", + 9 + ], + [ + "caaCCAa", + "bCccbA", + 11 + ], + [ + "caaCaBcBb", + "aCabaa", + 11 + ], + [ + "caaCbaB", + "CCc", + 11 + ], + [ + "caaCc", + "b", + 10 + ], + [ + "caaCcc", + "cbA", + 9 + ], + [ + "caaa", + "AcCc", + 8 + ], + [ + "caaa", + "bCabaCaa", + 9 + ], + [ + "caaaCb", + "AabC", + 7 + ], + [ + "caaaaC", + "CaaBCCaC", + 7 + ], + [ + "caaab", + "BCC", + 10 + ], + [ + "caaac", + "AaBbACcBa", + 13 + ], + [ + "caab", + "C", + 7 + ], + [ + "caabACbb", + "bb", + 12 + ], + [ + "caabB", + "AbaCbac", + 10 + ], + [ + "caabaABA", + "cbacccaA", + 10 + ], + [ + "caabcbcBB", + "abaAAaC", + 14 + ], + [ + "caac", + "ABb", + 7 + ], + [ + "caacAABaB", + "CbBcbaB", + 10 + ], + [ + "caacaccC", + "BAaccaBA", + 11 + ], + [ + "caacbaAc", + "Abccbc", + 10 + ], + [ + "cab", + "CB", + 4 + ], + [ + "cab", + "a", + 4 + ], + [ + "cab", + "abCCb", + 7 + ], + [ + "cab", + "bAaaBBA", + 11 + ], + [ + "cab", + "bBbCABC", + 11 + ], + [ + "cabAA", + "c", + 8 + ], + [ + "cabACCCCb", + "b", + 16 + ], + [ + "cabACc", + "bBBAb", + 9 + ], + [ + "cabBAaaB", + "BbCCBaBc", + 12 + ], + [ + "cabBC", + "A", + 9 + ], + [ + "cabBCa", + "C", + 10 + ], + [ + "cabBCcBbA", + "BbCbAAaAb", + 16 + ], + [ + "cabBaA", + "cCa", + 8 + ], + [ + "cabC", + "BBCabBCa", + 9 + ], + [ + "cabC", + "BCBb", + 7 + ], + [ + "cabC", + "Baccb", + 7 + ], + [ + "cabCCAcC", + "cCcA", + 9 + ], + [ + "cabCCaBca", + "BCbBc", + 11 + ], + [ + "cabCaba", + "a", + 12 + ], + [ + "cabCb", + "cB", + 7 + ], + [ + "cabCcbB", + "BBbAcBaac", + 13 + ], + [ + "cabaBBb", + "AbC", + 11 + ], + [ + "cabaa", + "ccBCAbAB", + 10 + ], + [ + "cababBBaB", + "AcBCAC", + 14 + ], + [ + "cabac", + "BCa", + 8 + ], + [ + "cabb", + "CACcCcac", + 14 + ], + [ + "cabb", + "a", + 6 + ], + [ + "cabbBBA", + "CCab", + 11 + ], + [ + "cabbBc", + "aCccAAcc", + 12 + ], + [ + "cabbbACA", + "bcaCC", + 11 + ], + [ + "cabc", + "BCBcBca", + 10 + ], + [ + "cabc", + "CaaBBAABA", + 14 + ], + [ + "cabcBCc", + "BABAC", + 10 + ], + [ + "cabca", + "aAaC", + 8 + ], + [ + "cac", + "BBbaabba", + 14 + ], + [ + "cac", + "bb", + 6 + ], + [ + "cac", + "bcBa", + 6 + ], + [ + "cac", + "cBaaBa", + 8 + ], + [ + "cacABBaB", + "BaBAabBbA", + 11 + ], + [ + "cacAaA", + "bc", + 10 + ], + [ + "cacAaaaBb", + "Aba", + 14 + ], + [ + "cacAbbcCB", + "AcCcAaBC", + 11 + ], + [ + "cacAc", + "cAcaAB", + 5 + ], + [ + "cacB", + "BCcbAAcbB", + 11 + ], + [ + "cacB", + "c", + 6 + ], + [ + "cacBAA", + "BbbCCab", + 12 + ], + [ + "cacBAc", + "bbaBB", + 10 + ], + [ + "cacBaAa", + "aBBBCbbbA", + 15 + ], + [ + "cacBab", + "AC", + 10 + ], + [ + "cacCCaB", + "ABCc", + 10 + ], + [ + "caca", + "bCbbAa", + 9 + ], + [ + "cacaAbAAA", + "bc", + 16 + ], + [ + "cacaAcc", + "bc", + 12 + ], + [ + "cacaBc", + "BaabCbbB", + 12 + ], + [ + "cacaaBc", + "caAacCAAa", + 11 + ], + [ + "cacaabBCa", + "AACaccAc", + 13 + ], + [ + "cacacBAbC", + "C", + 16 + ], + [ + "cacaca", + "aB", + 10 + ], + [ + "cacbAAaCa", + "b", + 16 + ], + [ + "cacbB", + "AbBaCB", + 9 + ], + [ + "cacbCcbcB", + "cc", + 14 + ], + [ + "cacbc", + "BbAACAAC", + 13 + ], + [ + "cacc", + "CAccbBacC", + 11 + ], + [ + "caccA", + "ccc", + 4 + ], + [ + "caccbc", + "ab", + 8 + ], + [ + "cb", + "AAaBCAa", + 13 + ], + [ + "cb", + "AAaC", + 8 + ], + [ + "cb", + "ABAcbc", + 8 + ], + [ + "cb", + "ABccA", + 8 + ], + [ + "cb", + "ACABcb", + 8 + ], + [ + "cb", + "Aa", + 4 + ], + [ + "cb", + "AbBAaB", + 10 + ], + [ + "cb", + "BCcbbACB", + 12 + ], + [ + "cb", + "BabCaAAB", + 14 + ], + [ + "cb", + "BbBCACCBb", + 15 + ], + [ + "cb", + "C", + 3 + ], + [ + "cb", + "CAAbCCB", + 11 + ], + [ + "cb", + "CABBCa", + 10 + ], + [ + "cb", + "CCACAAB", + 12 + ], + [ + "cb", + "aBcA", + 6 + ], + [ + "cb", + "aCBAaabaa", + 15 + ], + [ + "cb", + "aCBaacAac", + 16 + ], + [ + "cb", + "aCC", + 5 + ], + [ + "cb", + "aaBcCC", + 10 + ], + [ + "cb", + "acBAC", + 7 + ], + [ + "cb", + "bCBcaCbA", + 12 + ], + [ + "cb", + "bcCCC", + 8 + ], + [ + "cb", + "cBCaBCC", + 11 + ], + [ + "cb", + "cCabBBabA", + 14 + ], + [ + "cb", + "cb", + 0 + ], + [ + "cb", + "cccCa", + 8 + ], + [ + "cbA", + "AaAaB", + 8 + ], + [ + "cbA", + "AcACCa", + 9 + ], + [ + "cbA", + "BbCBABAA", + 12 + ], + [ + "cbA", + "aBcCCAAca", + 14 + ], + [ + "cbA", + "aaab", + 7 + ], + [ + "cbAA", + "BAbBc", + 8 + ], + [ + "cbAAaBcc", + "cABABCbb", + 10 + ], + [ + "cbAAcB", + "a", + 11 + ], + [ + "cbAAcC", + "cacC", + 5 + ], + [ + "cbAB", + "CAABa", + 5 + ], + [ + "cbABBb", + "acBA", + 9 + ], + [ + "cbABc", + "cbcAaCb", + 7 + ], + [ + "cbAC", + "C", + 6 + ], + [ + "cbACAbc", + "aABCAbA", + 8 + ], + [ + "cbACcAC", + "bbcAcbB", + 10 + ], + [ + "cbACcBBC", + "cABbcbbaB", + 12 + ], + [ + "cbACcb", + "cabbaAC", + 10 + ], + [ + "cbAaB", + "babCC", + 9 + ], + [ + "cbAaBc", + "Acc", + 8 + ], + [ + "cbAaBcCA", + "C", + 14 + ], + [ + "cbAaCBbB", + "A", + 14 + ], + [ + "cbAabcC", + "BBBCAc", + 11 + ], + [ + "cbAbAaBbc", + "bC", + 15 + ], + [ + "cbAbBBAA", + "bcaBbAc", + 9 + ], + [ + "cbAbCBA", + "acABcaC", + 10 + ], + [ + "cbAbCBBB", + "AAaaa", + 14 + ], + [ + "cbAbCaA", + "cbAcBB", + 7 + ], + [ + "cbAbaCc", + "BACabcaa", + 11 + ], + [ + "cbAbcCA", + "ccC", + 8 + ], + [ + "cbAbcaB", + "cc", + 10 + ], + [ + "cbAbcbAbB", + "AccBbbcA", + 13 + ], + [ + "cbAbcbb", + "caCaAB", + 10 + ], + [ + "cbAc", + "BABcCB", + 9 + ], + [ + "cbAc", + "bacb", + 5 + ], + [ + "cbAcCBb", + "ABac", + 10 + ], + [ + "cbAcbcccB", + "CCC", + 15 + ], + [ + "cbB", + "Aaa", + 6 + ], + [ + "cbB", + "CbC", + 3 + ], + [ + "cbB", + "caCaB", + 6 + ], + [ + "cbBA", + "BcBBccBA", + 9 + ], + [ + "cbBA", + "aCcbB", + 6 + ], + [ + "cbBAAc", + "bcABA", + 8 + ], + [ + "cbBAB", + "AbcBc", + 8 + ], + [ + "cbBABc", + "Cbba", + 7 + ], + [ + "cbBACACa", + "aA", + 13 + ], + [ + "cbBACBccA", + "Ca", + 15 + ], + [ + "cbBAba", + "AcAca", + 8 + ], + [ + "cbBAbb", + "CbaaC", + 8 + ], + [ + "cbBB", + "cAb", + 5 + ], + [ + "cbBB", + "cAba", + 5 + ], + [ + "cbBBbB", + "BBBBcaA", + 9 + ], + [ + "cbBBbca", + "cACABCb", + 10 + ], + [ + "cbBBcACBb", + "AACbBbbb", + 13 + ], + [ + "cbBCCaA", + "AccCcBc", + 11 + ], + [ + "cbBCacA", + "cB", + 10 + ], + [ + "cbBCb", + "CAAbBA", + 9 + ], + [ + "cbBa", + "aaCC", + 8 + ], + [ + "cbBa", + "cBaB", + 4 + ], + [ + "cbBa", + "cccB", + 6 + ], + [ + "cbBaBcAc", + "Aab", + 13 + ], + [ + "cbBaCAC", + "CCcCCc", + 10 + ], + [ + "cbBac", + "c", + 8 + ], + [ + "cbBacA", + "bCaaaBaa", + 12 + ], + [ + "cbBb", + "AABA", + 6 + ], + [ + "cbBb", + "CCaa", + 7 + ], + [ + "cbBbAbB", + "cC", + 12 + ], + [ + "cbBbCACCC", + "Ccc", + 14 + ], + [ + "cbBba", + "ccb", + 6 + ], + [ + "cbBbbb", + "BccCbaAAB", + 13 + ], + [ + "cbBc", + "A", + 8 + ], + [ + "cbBcCAa", + "acab", + 11 + ], + [ + "cbBcCCbAA", + "caAbaCaBc", + 14 + ], + [ + "cbBcaCB", + "bA", + 11 + ], + [ + "cbBcbaccC", + "CaAACBAAA", + 16 + ], + [ + "cbBcbc", + "ABBBAAc", + 9 + ], + [ + "cbBcc", + "CACaBC", + 10 + ], + [ + "cbC", + "AcABa", + 7 + ], + [ + "cbC", + "bCCBBa", + 10 + ], + [ + "cbCAbaAa", + "ccBCaC", + 11 + ], + [ + "cbCB", + "AABCCaaA", + 13 + ], + [ + "cbCBABAAb", + "a", + 17 + ], + [ + "cbCBAcacb", + "CAa", + 12 + ], + [ + "cbCBC", + "cbcaCaaba", + 11 + ], + [ + "cbCBccbcc", + "BcCbA", + 11 + ], + [ + "cbCC", + "B", + 7 + ], + [ + "cbCCBBCB", + "baBaCbC", + 11 + ], + [ + "cbCCBccBb", + "BaAaC", + 16 + ], + [ + "cbCCC", + "AACACB", + 8 + ], + [ + "cbCCbABBB", + "CcaABC", + 11 + ], + [ + "cbCCbBac", + "CAaA", + 12 + ], + [ + "cbCa", + "CBA", + 5 + ], + [ + "cbCa", + "bbcacb", + 7 + ], + [ + "cbCaB", + "cbBaCb", + 5 + ], + [ + "cbCbAaCCa", + "ACAAb", + 13 + ], + [ + "cbCbaC", + "BACcBC", + 8 + ], + [ + "cbCbaCcaC", + "AaaaBC", + 14 + ], + [ + "cbCbb", + "A", + 10 + ], + [ + "cbCbbCAAc", + "caccc", + 12 + ], + [ + "cbCbcC", + "B", + 11 + ], + [ + "cbCcAAcA", + "AcaAaCcCa", + 13 + ], + [ + "cbCcCAc", + "babCC", + 9 + ], + [ + "cbCcCB", + "BCABA", + 9 + ], + [ + "cbCcCbA", + "cB", + 11 + ], + [ + "cbCcb", + "AacBB", + 8 + ], + [ + "cbCcbca", + "BCB", + 10 + ], + [ + "cbCccca", + "AAA", + 13 + ], + [ + "cba", + "A", + 5 + ], + [ + "cba", + "b", + 4 + ], + [ + "cba", + "bBCaaCc", + 11 + ], + [ + "cba", + "cAAbc", + 6 + ], + [ + "cba", + "caABABbab", + 12 + ], + [ + "cba", + "ccAB", + 5 + ], + [ + "cbaAa", + "cBbbcB", + 8 + ], + [ + "cbaAc", + "cAAc", + 3 + ], + [ + "cbaBACBAc", + "a", + 16 + ], + [ + "cbaBCAAC", + "BAAcCc", + 12 + ], + [ + "cbaBCbbcC", + "BcAaabac", + 12 + ], + [ + "cbaBbCa", + "BBACcA", + 10 + ], + [ + "cbaC", + "BBCaACabc", + 13 + ], + [ + "cbaC", + "aaA", + 6 + ], + [ + "cbaCABA", + "BbAaBccBC", + 11 + ], + [ + "cbaCCA", + "cbCABA", + 6 + ], + [ + "cbaCCCa", + "a", + 12 + ], + [ + "cbaCacac", + "AcBBbAA", + 13 + ], + [ + "cbaCcaA", + "C", + 12 + ], + [ + "cbaa", + "aaBcCBcBa", + 13 + ], + [ + "cbaaA", + "cBaa", + 3 + ], + [ + "cbaabCcC", + "bB", + 13 + ], + [ + "cbabAA", + "Bc", + 11 + ], + [ + "cbabBbc", + "AbcacAC", + 11 + ], + [ + "cbabC", + "CBbaAa", + 7 + ], + [ + "cbabbC", + "aCacAcBAC", + 13 + ], + [ + "cbac", + "cCbBcACa", + 10 + ], + [ + "cbacAAAA", + "BcAbcAbc", + 12 + ], + [ + "cbacAAacc", + "ab", + 16 + ], + [ + "cbacAC", + "BaaABAbC", + 10 + ], + [ + "cbacACa", + "AcBbcBaC", + 10 + ], + [ + "cbacB", + "CaBAa", + 9 + ], + [ + "cbacCCab", + "Acbc", + 12 + ], + [ + "cbacCabBB", + "CCcc", + 14 + ], + [ + "cbb", + "BBbB", + 5 + ], + [ + "cbb", + "CcAaAC", + 10 + ], + [ + "cbb", + "bBcABAcCC", + 15 + ], + [ + "cbb", + "cbccAbA", + 8 + ], + [ + "cbbABaCBa", + "Ca", + 14 + ], + [ + "cbbAa", + "aBBcAa", + 6 + ], + [ + "cbbAaC", + "cbc", + 7 + ], + [ + "cbbAbB", + "BABaBA", + 9 + ], + [ + "cbbAbaaB", + "CCababBa", + 10 + ], + [ + "cbbBAC", + "AacCcb", + 12 + ], + [ + "cbbBbA", + "AbCAACc", + 12 + ], + [ + "cbbBbb", + "b", + 10 + ], + [ + "cbbBbbbCA", + "bBB", + 13 + ], + [ + "cbbBcBbBb", + "BaABc", + 14 + ], + [ + "cbbBcC", + "C", + 10 + ], + [ + "cbbCAaB", + "A", + 12 + ], + [ + "cbbCbC", + "cBaCCABb", + 10 + ], + [ + "cbbCcbA", + "baCBaBab", + 12 + ], + [ + "cbbaCA", + "aCAbbbCcA", + 9 + ], + [ + "cbbaCb", + "ABaCaB", + 8 + ], + [ + "cbbaaC", + "BcAAbAba", + 11 + ], + [ + "cbbabc", + "BaAaacCAc", + 14 + ], + [ + "cbbacB", + "b", + 10 + ], + [ + "cbbb", + "CC", + 7 + ], + [ + "cbbb", + "bBcCaCAC", + 14 + ], + [ + "cbbbB", + "abacb", + 7 + ], + [ + "cbbbBBcb", + "cA", + 14 + ], + [ + "cbbbaBbC", + "CbAABAC", + 8 + ], + [ + "cbbbc", + "c", + 8 + ], + [ + "cbbc", + "aCBAcAc", + 10 + ], + [ + "cbbc", + "abBBCAB", + 10 + ], + [ + "cbbcBC", + "BAB", + 9 + ], + [ + "cbbcBcCCb", + "bc", + 14 + ], + [ + "cbc", + "AACAAbCCB", + 14 + ], + [ + "cbc", + "BcbaA", + 6 + ], + [ + "cbc", + "CbBbACB", + 10 + ], + [ + "cbc", + "b", + 4 + ], + [ + "cbc", + "cBCcBaC", + 9 + ], + [ + "cbcA", + "ABBAcC", + 9 + ], + [ + "cbcAA", + "c", + 8 + ], + [ + "cbcACAabb", + "bccBcbAaA", + 13 + ], + [ + "cbcAaAAb", + "BBccBbCA", + 13 + ], + [ + "cbcAb", + "CCCbbbcba", + 13 + ], + [ + "cbcB", + "Aba", + 6 + ], + [ + "cbcB", + "bCaaaAba", + 14 + ], + [ + "cbcBacB", + "acbB", + 9 + ], + [ + "cbcBbb", + "cBa", + 8 + ], + [ + "cbcBcc", + "bbCcA", + 7 + ], + [ + "cbcC", + "aABacCCC", + 11 + ], + [ + "cbcCA", + "bBAaba", + 10 + ], + [ + "cbcCBcbc", + "acBa", + 12 + ], + [ + "cbcCcBa", + "CbbACa", + 8 + ], + [ + "cbcaB", + "bCaAAc", + 9 + ], + [ + "cbcabBAac", + "bcBCbCBb", + 13 + ], + [ + "cbcb", + "bba", + 6 + ], + [ + "cbcbCCA", + "AaaCCabcc", + 15 + ], + [ + "cbcbaCBb", + "BccCAb", + 9 + ], + [ + "cbcbbC", + "BAaa", + 11 + ], + [ + "cbcbcaCC", + "BaBbbB", + 14 + ], + [ + "cbcccaCc", + "acAAAa", + 13 + ], + [ + "cc", + "AAAbBbC", + 13 + ], + [ + "cc", + "ABBaccbbB", + 14 + ], + [ + "cc", + "ABa", + 6 + ], + [ + "cc", + "ABaA", + 8 + ], + [ + "cc", + "BAbbAab", + 14 + ], + [ + "cc", + "BAbc", + 6 + ], + [ + "cc", + "BBacCb", + 9 + ], + [ + "cc", + "BCbaB", + 9 + ], + [ + "cc", + "BaBcBAAc", + 12 + ], + [ + "cc", + "BaC", + 5 + ], + [ + "cc", + "Bbca", + 6 + ], + [ + "cc", + "CAbbcb", + 9 + ], + [ + "cc", + "CCAcCb", + 9 + ], + [ + "cc", + "Ccb", + 3 + ], + [ + "cc", + "aAaBCaA", + 13 + ], + [ + "cc", + "aB", + 4 + ], + [ + "cc", + "aBA", + 6 + ], + [ + "cc", + "acCcCabA", + 12 + ], + [ + "cc", + "b", + 4 + ], + [ + "cc", + "bACABABA", + 15 + ], + [ + "cc", + "baCaAAAc", + 13 + ], + [ + "cc", + "bacAbacB", + 12 + ], + [ + "cc", + "bcbA", + 6 + ], + [ + "cc", + "cABcAA", + 8 + ], + [ + "cc", + "cACac", + 6 + ], + [ + "cc", + "cAbba", + 8 + ], + [ + "cc", + "cB", + 2 + ], + [ + "cc", + "cCac", + 4 + ], + [ + "cc", + "cCbbAcaA", + 12 + ], + [ + "cc", + "cb", + 2 + ], + [ + "cc", + "ccBc", + 4 + ], + [ + "cc", + "ccCab", + 6 + ], + [ + "ccA", + "AcCccAcc", + 10 + ], + [ + "ccA", + "CBAaC", + 7 + ], + [ + "ccA", + "b", + 6 + ], + [ + "ccAABB", + "ccCBBbC", + 7 + ], + [ + "ccAAb", + "BaABC", + 8 + ], + [ + "ccAAbaA", + "BaA", + 9 + ], + [ + "ccAAbbbaB", + "AABBCc", + 12 + ], + [ + "ccAB", + "BcA", + 4 + ], + [ + "ccABA", + "BcBBcbBA", + 8 + ], + [ + "ccABBAA", + "bCCac", + 12 + ], + [ + "ccABcAA", + "AABBaBaA", + 11 + ], + [ + "ccAC", + "CAACBCaCA", + 13 + ], + [ + "ccAC", + "bA", + 6 + ], + [ + "ccACAAcb", + "AbabCa", + 12 + ], + [ + "ccACABbAc", + "BCbAb", + 12 + ], + [ + "ccACB", + "CBbabcAC", + 11 + ], + [ + "ccACBAaBa", + "ca", + 14 + ], + [ + "ccACBca", + "Cb", + 11 + ], + [ + "ccACC", + "CcaAba", + 7 + ], + [ + "ccACC", + "aCc", + 6 + ], + [ + "ccACaABcb", + "CBBCCABCc", + 10 + ], + [ + "ccACaBbAC", + "cBaAaB", + 11 + ], + [ + "ccACcAAB", + "AbCbaCAbb", + 13 + ], + [ + "ccACcbb", + "abCcBCb", + 9 + ], + [ + "ccAa", + "bca", + 4 + ], + [ + "ccAa", + "c", + 6 + ], + [ + "ccAaACB", + "BCabaaA", + 11 + ], + [ + "ccAaAbccA", + "AAbAAb", + 12 + ], + [ + "ccAaC", + "bAaaACAB", + 11 + ], + [ + "ccAcAC", + "c", + 10 + ], + [ + "ccAcAb", + "AbABBCbBA", + 14 + ], + [ + "ccAcb", + "bcAb", + 4 + ], + [ + "ccAcccc", + "ccCca", + 7 + ], + [ + "ccB", + "B", + 4 + ], + [ + "ccB", + "CcBAB", + 5 + ], + [ + "ccB", + "aAa", + 6 + ], + [ + "ccB", + "b", + 5 + ], + [ + "ccB", + "cAaAbaa", + 11 + ], + [ + "ccB", + "cBbCACaAB", + 13 + ], + [ + "ccBA", + "B", + 6 + ], + [ + "ccBA", + "CAac", + 7 + ], + [ + "ccBACbca", + "Abb", + 12 + ], + [ + "ccBAc", + "baaaCaaB", + 14 + ], + [ + "ccBB", + "BcC", + 6 + ], + [ + "ccBBA", + "BbACCaA", + 11 + ], + [ + "ccBBbAa", + "b", + 12 + ], + [ + "ccBBbb", + "cBbBb", + 4 + ], + [ + "ccBBbcC", + "CAbacA", + 10 + ], + [ + "ccBC", + "AaBAA", + 8 + ], + [ + "ccBC", + "Baba", + 7 + ], + [ + "ccBC", + "b", + 7 + ], + [ + "ccBC", + "cbba", + 5 + ], + [ + "ccBCAaBc", + "bBCCCAA", + 12 + ], + [ + "ccBCAbC", + "abC", + 9 + ], + [ + "ccBCBBcac", + "BCabBA", + 12 + ], + [ + "ccBCC", + "AbbC", + 7 + ], + [ + "ccBCCCa", + "BaCaB", + 10 + ], + [ + "ccBCCaBcB", + "AcC", + 14 + ], + [ + "ccBCCc", + "aBb", + 10 + ], + [ + "ccBCbBcC", + "ca", + 14 + ], + [ + "ccBCcCaA", + "bcbBAcaBA", + 10 + ], + [ + "ccBa", + "ACCaABcCa", + 12 + ], + [ + "ccBaC", + "AC", + 7 + ], + [ + "ccBaaAa", + "Cc", + 11 + ], + [ + "ccBab", + "BACC", + 9 + ], + [ + "ccBabcaBA", + "CBbc", + 11 + ], + [ + "ccBacCAbB", + "cCCcA", + 11 + ], + [ + "ccBbAABB", + "ccb", + 10 + ], + [ + "ccBbAaB", + "Cc", + 11 + ], + [ + "ccBbaCAb", + "Bcc", + 13 + ], + [ + "ccBbab", + "cCbBc", + 7 + ], + [ + "ccBbbbcB", + "Aacaaa", + 16 + ], + [ + "ccBcb", + "bbcACCb", + 9 + ], + [ + "ccC", + "Ab", + 6 + ], + [ + "ccC", + "CBcCaAbAc", + 13 + ], + [ + "ccC", + "aaCB", + 6 + ], + [ + "ccC", + "bBBAA", + 10 + ], + [ + "ccCAA", + "aCC", + 7 + ], + [ + "ccCAcCB", + "baAAbBac", + 14 + ], + [ + "ccCBAa", + "AABbcA", + 10 + ], + [ + "ccCBCc", + "caBcB", + 7 + ], + [ + "ccCBbC", + "BbbA", + 9 + ], + [ + "ccCC", + "A", + 8 + ], + [ + "ccCC", + "CCaBcACBC", + 11 + ], + [ + "ccCC", + "aab", + 8 + ], + [ + "ccCCAaa", + "B", + 14 + ], + [ + "ccCCBAaaC", + "Bb", + 16 + ], + [ + "ccCCBbCaA", + "AcACCaB", + 10 + ], + [ + "ccCCCaaCb", + "BaAbBB", + 16 + ], + [ + "ccCCacA", + "ACAAa", + 10 + ], + [ + "ccCa", + "acbBa", + 6 + ], + [ + "ccCaa", + "aCBCa", + 7 + ], + [ + "ccCab", + "CCAb", + 4 + ], + [ + "ccCbBB", + "AAcabCa", + 10 + ], + [ + "ccCba", + "aacAbCBb", + 11 + ], + [ + "ccCbbCBA", + "AaCBcAb", + 12 + ], + [ + "ccCbc", + "aCbBCcb", + 10 + ], + [ + "ccCbcCcA", + "aAA", + 14 + ], + [ + "ccCbcaaAC", + "abB", + 16 + ], + [ + "ccCcAbCA", + "AB", + 13 + ], + [ + "ccCcAccA", + "CCABcCCbc", + 13 + ], + [ + "ccCcBaA", + "caC", + 10 + ], + [ + "ccCcBbc", + "aaacAca", + 12 + ], + [ + "ccCcBcC", + "CBCCCBAA", + 9 + ], + [ + "ccCcabcb", + "baAb", + 12 + ], + [ + "ccCccbABc", + "b", + 16 + ], + [ + "cca", + "ABc", + 6 + ], + [ + "cca", + "aa", + 4 + ], + [ + "cca", + "bACA", + 6 + ], + [ + "ccaA", + "AcBa", + 5 + ], + [ + "ccaA", + "Ca", + 5 + ], + [ + "ccaABaccb", + "AAaaCBb", + 10 + ], + [ + "ccaACAC", + "BAc", + 11 + ], + [ + "ccaAba", + "Ccb", + 7 + ], + [ + "ccaAc", + "B", + 10 + ], + [ + "ccaAcCB", + "ccBB", + 8 + ], + [ + "ccaB", + "BbcB", + 6 + ], + [ + "ccaBC", + "CbbbB", + 8 + ], + [ + "ccaCA", + "ABBbb", + 10 + ], + [ + "ccaCAB", + "cCbcb", + 7 + ], + [ + "ccaCAcAB", + "acbb", + 11 + ], + [ + "ccaCAcBb", + "ccCbb", + 7 + ], + [ + "ccaCCCcCC", + "AACAc", + 13 + ], + [ + "ccaCaABb", + "bA", + 14 + ], + [ + "ccaCc", + "babb", + 8 + ], + [ + "ccaCcAAaB", + "BcAAaB", + 8 + ], + [ + "ccaCcB", + "B", + 10 + ], + [ + "ccaaBCB", + "AAA", + 12 + ], + [ + "ccaaaa", + "Aa", + 9 + ], + [ + "ccaab", + "acbbC", + 8 + ], + [ + "ccaac", + "BCBBb", + 9 + ], + [ + "ccab", + "C", + 7 + ], + [ + "ccab", + "aabBBcABa", + 14 + ], + [ + "ccabaB", + "CACba", + 7 + ], + [ + "ccabaCa", + "ccAac", + 6 + ], + [ + "ccababcA", + "abc", + 10 + ], + [ + "ccacAbBBB", + "Cb", + 15 + ], + [ + "ccacb", + "cbCA", + 7 + ], + [ + "ccb", + "A", + 6 + ], + [ + "ccb", + "Aa", + 6 + ], + [ + "ccb", + "Aca", + 4 + ], + [ + "ccb", + "acBAcb", + 6 + ], + [ + "ccb", + "acBccbAcA", + 12 + ], + [ + "ccbA", + "ba", + 5 + ], + [ + "ccbAAA", + "BAaca", + 9 + ], + [ + "ccbAACAc", + "CaaCBcBAa", + 14 + ], + [ + "ccbABAA", + "BBaccbb", + 13 + ], + [ + "ccbACa", + "cabACc", + 4 + ], + [ + "ccbAaCA", + "cCccaCCA", + 7 + ], + [ + "ccbAb", + "bbbCaACa", + 12 + ], + [ + "ccbAbA", + "cbcaBb", + 8 + ], + [ + "ccbAbBB", + "cBcbA", + 8 + ], + [ + "ccbAcC", + "AbBCcaacB", + 12 + ], + [ + "ccbBbcb", + "bCbCAC", + 10 + ], + [ + "ccbC", + "ACccBAc", + 8 + ], + [ + "ccbC", + "CB", + 6 + ], + [ + "ccbC", + "aCAA", + 7 + ], + [ + "ccbCC", + "AcCcCcb", + 8 + ], + [ + "ccbCa", + "bcBBcb", + 8 + ], + [ + "ccbCaACca", + "BBcaA", + 12 + ], + [ + "ccbCbbB", + "b", + 12 + ], + [ + "ccba", + "CAbbBc", + 9 + ], + [ + "ccba", + "Caa", + 5 + ], + [ + "ccba", + "bA", + 5 + ], + [ + "ccba", + "cbc", + 4 + ], + [ + "ccbabbC", + "AB", + 12 + ], + [ + "ccbbBbAb", + "CBc", + 13 + ], + [ + "ccbbabB", + "bCcaCa", + 11 + ], + [ + "ccbbcB", + "ACbcaaA", + 11 + ], + [ + "ccbbccCAC", + "CcccBcBC", + 10 + ], + [ + "ccbcABBCb", + "aAcAacC", + 12 + ], + [ + "ccbcAbBbc", + "a", + 17 + ], + [ + "ccbcB", + "CBaCAAcBc", + 12 + ], + [ + "ccbcBBAbB", + "cBccCc", + 13 + ], + [ + "ccbcCaab", + "cCaAAB", + 10 + ], + [ + "ccbcCbcB", + "CCCa", + 12 + ], + [ + "ccbcba", + "CA", + 10 + ], + [ + "ccc", + "AcBcbBcb", + 10 + ], + [ + "ccc", + "BbcCbB", + 9 + ], + [ + "ccc", + "CACABCbb", + 13 + ], + [ + "ccc", + "ac", + 4 + ], + [ + "ccc", + "baAABCbcc", + 13 + ], + [ + "ccc", + "c", + 4 + ], + [ + "ccc", + "cabACBAbB", + 15 + ], + [ + "cccA", + "cbBAbbB", + 10 + ], + [ + "cccAAA", + "AcB", + 10 + ], + [ + "cccAABCcA", + "ba", + 16 + ], + [ + "cccAC", + "ABCaa", + 8 + ], + [ + "cccAaccc", + "bcCC", + 12 + ], + [ + "cccAbC", + "cBbCAb", + 7 + ], + [ + "cccB", + "bACBcb", + 8 + ], + [ + "cccB", + "c", + 6 + ], + [ + "cccBAA", + "cbB", + 8 + ], + [ + "cccBAbb", + "bCCacb", + 9 + ], + [ + "cccBBAC", + "bbbcB", + 12 + ], + [ + "cccBBCc", + "CAaaAb", + 13 + ], + [ + "cccC", + "ACbB", + 7 + ], + [ + "cccC", + "bbbA", + 8 + ], + [ + "cccCabBA", + "AaBc", + 12 + ], + [ + "cccCbbcc", + "CcC", + 11 + ], + [ + "cccCccaC", + "b", + 16 + ], + [ + "ccca", + "cCcCAa", + 5 + ], + [ + "cccaAb", + "Abb", + 9 + ], + [ + "cccaCbA", + "cBcCBBbaA", + 9 + ], + [ + "cccaaa", + "BaABcbcca", + 14 + ], + [ + "cccacB", + "abBB", + 10 + ], + [ + "cccb", + "AABc", + 8 + ], + [ + "cccb", + "CbBAAbA", + 11 + ], + [ + "cccbABcc", + "a", + 15 + ], + [ + "cccbB", + "bcCAcaaba", + 11 + ], + [ + "cccbaBC", + "cbACAac", + 10 + ], + [ + "cccbabBb", + "ACc", + 13 + ], + [ + "cccbbA", + "bbcAc", + 10 + ], + [ + "cccbcaCC", + "AbCACBcCb", + 13 + ], + [ + "cccbcaaaC", + "baCcCc", + 14 + ], + [ + "ccccBa", + "abcbaaCbA", + 13 + ], + [ + "ccccCBcCA", + "Cbbab", + 15 + ], + [ + "ccccCCc", + "aabB", + 14 + ], + [ + "ccccaCA", + "Cb", + 12 + ], + [ + "cccccb", + "a", + 12 + ], + [ + "ccccccAcc", + "AB", + 16 + ] +] \ No newline at end of file diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py new file mode 100644 index 00000000000..fee062ecc9b --- /dev/null +++ b/Lib/test/memory_watchdog.py @@ -0,0 +1,21 @@ +"""Memory watchdog: periodically read the memory usage of the main test process +and print it out, until terminated.""" +# stdin should refer to the process' /proc//statm: we don't pass the +# process' PID to avoid a race condition in case of - unlikely - PID recycling. +# If the process crashes, reading from the /proc entry will fail with ESRCH. + + +import sys +import time +from test.support import get_pagesize + + +while True: + page_size = get_pagesize() + sys.stdin.seek(0) + statm = sys.stdin.read() + data = int(statm.split()[5]) + sys.stdout.write(" ... process data size: {data:.1f}G\n" + .format(data=data * page_size / (1024 ** 3))) + sys.stdout.flush() + time.sleep(1) diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py new file mode 100644 index 00000000000..6b4c57d0b4b --- /dev/null +++ b/Lib/test/multibytecodec_support.py @@ -0,0 +1,400 @@ +# +# multibytecodec_support.py +# Common Unittest Routines for CJK codecs +# + +import codecs +import os +import re +import sys +import unittest +from http.client import HTTPException +from test import support +from io import BytesIO + +class TestBase: + encoding = '' # codec name + codec = None # codec tuple (with 4 elements) + tstring = None # must set. 2 strings to test StreamReader + + codectests = None # must set. codec test tuple + roundtriptest = 1 # set if roundtrip is possible with unicode + has_iso10646 = 0 # set if this encoding contains whole iso10646 map + xmlcharnametest = None # string to test xmlcharrefreplace + unmappedunicode = '\udeee' # a unicode code point that is not mapped. + + def setUp(self): + if self.codec is None: + self.codec = codecs.lookup(self.encoding) + self.encode = self.codec.encode + self.decode = self.codec.decode + self.reader = self.codec.streamreader + self.writer = self.codec.streamwriter + self.incrementalencoder = self.codec.incrementalencoder + self.incrementaldecoder = self.codec.incrementaldecoder + + def test_chunkcoding(self): + tstring_lines = [] + for b in self.tstring: + lines = b.split(b"\n") + last = lines.pop() + assert last == b"" + lines = [line + b"\n" for line in lines] + tstring_lines.append(lines) + for native, utf8 in zip(*tstring_lines): + u = self.decode(native)[0] + self.assertEqual(u, utf8.decode('utf-8')) + if self.roundtriptest: + self.assertEqual(native, self.encode(u)[0]) + + def test_errorhandle(self): + for source, scheme, expected in self.codectests: + if isinstance(source, bytes): + func = self.decode + else: + func = self.encode + if expected: + result = func(source, scheme)[0] + if func is self.decode: + self.assertTrue(type(result) is str, type(result)) + self.assertEqual(result, expected, + '%a.decode(%r, %r)=%a != %a' + % (source, self.encoding, scheme, result, + expected)) + else: + self.assertTrue(type(result) is bytes, type(result)) + self.assertEqual(result, expected, + '%a.encode(%r, %r)=%a != %a' + % (source, self.encoding, scheme, result, + expected)) + else: + self.assertRaises(UnicodeError, func, source, scheme) + + def test_xmlcharrefreplace(self): + if self.has_iso10646: + self.skipTest('encoding contains full ISO 10646 map') + + s = "\u0b13\u0b23\u0b60 nd eggs" + self.assertEqual( + self.encode(s, "xmlcharrefreplace")[0], + b"ଓଣୠ nd eggs" + ) + + def test_customreplace_encode(self): + if self.has_iso10646: + self.skipTest('encoding contains full ISO 10646 map') + + from html.entities import codepoint2name + + def xmlcharnamereplace(exc): + if not isinstance(exc, UnicodeEncodeError): + raise TypeError("don't know how to handle %r" % exc) + l = [] + for c in exc.object[exc.start:exc.end]: + if ord(c) in codepoint2name: + l.append("&%s;" % codepoint2name[ord(c)]) + else: + l.append("&#%d;" % ord(c)) + return ("".join(l), exc.end) + + codecs.register_error("test.xmlcharnamereplace", xmlcharnamereplace) + + if self.xmlcharnametest: + sin, sout = self.xmlcharnametest + else: + sin = "\xab\u211c\xbb = \u2329\u1234\u232a" + sout = b"«ℜ» = ⟨ሴ⟩" + self.assertEqual(self.encode(sin, + "test.xmlcharnamereplace")[0], sout) + + def test_callback_returns_bytes(self): + def myreplace(exc): + return (b"1234", exc.end) + codecs.register_error("test.cjktest", myreplace) + enc = self.encode("abc" + self.unmappedunicode + "def", "test.cjktest")[0] + self.assertEqual(enc, b"abc1234def") + + def test_callback_wrong_objects(self): + def myreplace(exc): + return (ret, exc.end) + codecs.register_error("test.cjktest", myreplace) + + for ret in ([1, 2, 3], [], None, object()): + self.assertRaises(TypeError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_callback_long_index(self): + def myreplace(exc): + return ('x', int(exc.end)) + codecs.register_error("test.cjktest", myreplace) + self.assertEqual(self.encode('abcd' + self.unmappedunicode + 'efgh', + 'test.cjktest'), (b'abcdxefgh', 9)) + + def myreplace(exc): + return ('x', sys.maxsize + 1) + codecs.register_error("test.cjktest", myreplace) + self.assertRaises(IndexError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_callback_None_index(self): + def myreplace(exc): + return ('x', None) + codecs.register_error("test.cjktest", myreplace) + self.assertRaises(TypeError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_callback_backward_index(self): + def myreplace(exc): + if myreplace.limit > 0: + myreplace.limit -= 1 + return ('REPLACED', 0) + else: + return ('TERMINAL', exc.end) + myreplace.limit = 3 + codecs.register_error("test.cjktest", myreplace) + self.assertEqual(self.encode('abcd' + self.unmappedunicode + 'efgh', + 'test.cjktest'), + (b'abcdREPLACEDabcdREPLACEDabcdREPLACEDabcdTERMINALefgh', 9)) + + def test_callback_forward_index(self): + def myreplace(exc): + return ('REPLACED', exc.end + 2) + codecs.register_error("test.cjktest", myreplace) + self.assertEqual(self.encode('abcd' + self.unmappedunicode + 'efgh', + 'test.cjktest'), (b'abcdREPLACEDgh', 9)) + + def test_callback_index_outofbound(self): + def myreplace(exc): + return ('TERM', 100) + codecs.register_error("test.cjktest", myreplace) + self.assertRaises(IndexError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_incrementalencoder(self): + UTF8Reader = codecs.getreader('utf-8') + for sizehint in [None] + list(range(1, 33)) + \ + [64, 128, 256, 512, 1024]: + istream = UTF8Reader(BytesIO(self.tstring[1])) + ostream = BytesIO() + encoder = self.incrementalencoder() + while 1: + if sizehint is not None: + data = istream.read(sizehint) + else: + data = istream.read() + + if not data: + break + e = encoder.encode(data) + ostream.write(e) + + self.assertEqual(ostream.getvalue(), self.tstring[0]) + + def test_incrementaldecoder(self): + UTF8Writer = codecs.getwriter('utf-8') + for sizehint in [None, -1] + list(range(1, 33)) + \ + [64, 128, 256, 512, 1024]: + istream = BytesIO(self.tstring[0]) + ostream = UTF8Writer(BytesIO()) + decoder = self.incrementaldecoder() + while 1: + data = istream.read(sizehint) + if not data: + break + else: + u = decoder.decode(data) + ostream.write(u) + + self.assertEqual(ostream.getvalue(), self.tstring[1]) + + def test_incrementalencoder_error_callback(self): + inv = self.unmappedunicode + + e = self.incrementalencoder() + self.assertRaises(UnicodeEncodeError, e.encode, inv, True) + + e.errors = 'ignore' + self.assertEqual(e.encode(inv, True), b'') + + e.reset() + def tempreplace(exc): + return ('called', exc.end) + codecs.register_error('test.incremental_error_callback', tempreplace) + e.errors = 'test.incremental_error_callback' + self.assertEqual(e.encode(inv, True), b'called') + + # again + e.errors = 'ignore' + self.assertEqual(e.encode(inv, True), b'') + + def test_streamreader(self): + UTF8Writer = codecs.getwriter('utf-8') + for name in ["read", "readline", "readlines"]: + for sizehint in [None, -1] + list(range(1, 33)) + \ + [64, 128, 256, 512, 1024]: + istream = self.reader(BytesIO(self.tstring[0])) + ostream = UTF8Writer(BytesIO()) + func = getattr(istream, name) + while 1: + data = func(sizehint) + if not data: + break + if name == "readlines": + ostream.writelines(data) + else: + ostream.write(data) + + self.assertEqual(ostream.getvalue(), self.tstring[1]) + + def test_streamwriter(self): + readfuncs = ('read', 'readline', 'readlines') + UTF8Reader = codecs.getreader('utf-8') + for name in readfuncs: + for sizehint in [None] + list(range(1, 33)) + \ + [64, 128, 256, 512, 1024]: + istream = UTF8Reader(BytesIO(self.tstring[1])) + ostream = self.writer(BytesIO()) + func = getattr(istream, name) + while 1: + if sizehint is not None: + data = func(sizehint) + else: + data = func() + + if not data: + break + if name == "readlines": + ostream.writelines(data) + else: + ostream.write(data) + + self.assertEqual(ostream.getvalue(), self.tstring[0]) + + def test_streamwriter_reset_no_pending(self): + # Issue #23247: Calling reset() on a fresh StreamWriter instance + # (without pending data) must not crash + stream = BytesIO() + writer = self.writer(stream) + writer.reset() + + def test_incrementalencoder_del_segfault(self): + e = self.incrementalencoder() + with self.assertRaises(AttributeError): + del e.errors + + def test_null_terminator(self): + # see gh-101828 + text = "フルーツ" + try: + text.encode(self.encoding) + except UnicodeEncodeError: + text = "Python is cool" + encode_w_null = (text + "\0").encode(self.encoding) + encode_plus_null = text.encode(self.encoding) + "\0".encode(self.encoding) + self.assertTrue(encode_w_null.endswith(b'\x00')) + self.assertEqual(encode_w_null, encode_plus_null) + + encode_w_null_2 = (text + "\0" + text + "\0").encode(self.encoding) + encode_plus_null_2 = encode_plus_null + encode_plus_null + self.assertEqual(encode_w_null_2.count(b'\x00'), 2) + self.assertEqual(encode_w_null_2, encode_plus_null_2) + + +class TestBase_Mapping(unittest.TestCase): + pass_enctest = [] + pass_dectest = [] + supmaps = [] + codectests = [] + + def setUp(self): + try: + self.open_mapping_file().close() # test it to report the error early + except (OSError, HTTPException): + self.skipTest("Could not retrieve "+self.mapfileurl) + + def open_mapping_file(self): + return support.open_urlresource(self.mapfileurl, encoding="utf-8") + + def test_mapping_file(self): + if self.mapfileurl.endswith('.xml'): + self._test_mapping_file_ucm() + else: + self._test_mapping_file_plain() + + def _test_mapping_file_plain(self): + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + + urt_wa = {} + + with self.open_mapping_file() as f: + for line in f: + if not line: + break + data = line.split('#')[0].split() + if len(data) != 2: + continue + + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: + continue + + unich = unichrs(data[1]) + if ord(unich) == 0xfffd or unich in urt_wa: + continue + urt_wa[unich] = csetch + + self._testpoint(csetch, unich) + + def _test_mapping_file_ucm(self): + with self.open_mapping_file() as f: + ucmdata = f.read() + uc = re.findall('', ucmdata) + for uni, coded in uc: + unich = chr(int(uni, 16)) + codech = bytes.fromhex(coded) + self._testpoint(codech, unich) + + def test_mapping_supplemental(self): + for mapping in self.supmaps: + self._testpoint(*mapping) + + def _testpoint(self, csetch, unich): + if (csetch, unich) not in self.pass_enctest: + self.assertEqual(unich.encode(self.encoding), csetch) + if (csetch, unich) not in self.pass_dectest: + self.assertEqual(str(csetch, self.encoding), unich) + + def test_errorhandle(self): + for source, scheme, expected in self.codectests: + if isinstance(source, bytes): + func = source.decode + else: + func = source.encode + if expected: + if isinstance(source, bytes): + result = func(self.encoding, scheme) + self.assertTrue(type(result) is str, type(result)) + self.assertEqual(result, expected, + '%a.decode(%r, %r)=%a != %a' + % (source, self.encoding, scheme, result, + expected)) + else: + result = func(self.encoding, scheme) + self.assertTrue(type(result) is bytes, type(result)) + self.assertEqual(result, expected, + '%a.encode(%r, %r)=%a != %a' + % (source, self.encoding, scheme, result, + expected)) + else: + self.assertRaises(UnicodeError, func, self.encoding, scheme) + +def load_teststring(name): + dir = os.path.join(os.path.dirname(__file__), 'cjkencodings') + with open(os.path.join(dir, name + '.txt'), 'rb') as f: + encoded = f.read() + with open(os.path.join(dir, name + '-utf8.txt'), 'rb') as f: + utf8 = f.read() + return encoded, utf8 diff --git a/Lib/test/profilee.py b/Lib/test/profilee.py new file mode 100644 index 00000000000..b6a090a2e34 --- /dev/null +++ b/Lib/test/profilee.py @@ -0,0 +1,115 @@ +""" +Input for test_profile.py and test_cprofile.py. + +IMPORTANT: This stuff is touchy. If you modify anything above the +test class you'll have to regenerate the stats by running the two +test files. + +*ALL* NUMBERS in the expected output are relevant. If you change +the formatting of pstats, please don't just regenerate the expected +output without checking very carefully that not a single number has +changed. +""" + +import sys + +# In order to have reproducible time, we simulate a timer in the global +# variable 'TICKS', which represents simulated time in milliseconds. +# (We can't use a helper function increment the timer since it would be +# included in the profile and would appear to consume all the time.) +TICKS = 42000 + +def timer(): + return TICKS + +def testfunc(): + # 1 call + # 1000 ticks total: 270 ticks local, 730 ticks in subfunctions + global TICKS + TICKS += 99 + helper() # 300 + helper() # 300 + TICKS += 171 + factorial(14) # 130 + +def factorial(n): + # 23 calls total + # 170 ticks total, 150 ticks local + # 3 primitive calls, 130, 20 and 20 ticks total + # including 116, 17, 17 ticks local + global TICKS + if n > 0: + TICKS += n + return mul(n, factorial(n-1)) + else: + TICKS += 11 + return 1 + +def mul(a, b): + # 20 calls + # 1 tick, local + global TICKS + TICKS += 1 + return a * b + +def helper(): + # 2 calls + # 300 ticks total: 20 ticks local, 260 ticks in subfunctions + global TICKS + TICKS += 1 + helper1() # 30 + TICKS += 2 + helper1() # 30 + TICKS += 6 + helper2() # 50 + TICKS += 3 + helper2() # 50 + TICKS += 2 + helper2() # 50 + TICKS += 5 + helper2_indirect() # 70 + TICKS += 1 + +def helper1(): + # 4 calls + # 30 ticks total: 29 ticks local, 1 tick in subfunctions + global TICKS + TICKS += 10 + hasattr(C(), "foo") # 1 + TICKS += 19 + lst = [] + lst.append(42) # 0 + sys.exception() # 0 + +def helper2_indirect(): + helper2() # 50 + factorial(3) # 20 + +def helper2(): + # 8 calls + # 50 ticks local: 39 ticks local, 11 ticks in subfunctions + global TICKS + TICKS += 11 + hasattr(C(), "bar") # 1 + TICKS += 13 + subhelper() # 10 + TICKS += 15 + +def subhelper(): + # 8 calls + # 10 ticks total: 8 ticks local, 2 ticks in subfunctions + global TICKS + TICKS += 2 + for i in range(2): # 0 + try: + C().foo # 1 x 2 + except AttributeError: + TICKS += 3 # 3 x 2 + +class C: + def __getattr__(self, name): + # 28 calls + # 1 tick, local + global TICKS + TICKS += 1 + raise AttributeError diff --git a/Lib/test/pstats.pck b/Lib/test/pstats.pck new file mode 100644 index 0000000000000000000000000000000000000000..c48ccb73a9fe3ff50aba3704a17e1e08ccc4ce08 GIT binary patch literal 66607 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 i3k 1: + print(n, "times", end=' ') + print(func.__name__, "%.3f" % (t1-t0), "CPU seconds") + +main() diff --git a/Lib/test/ssltests.py b/Lib/test/ssltests.py new file mode 100644 index 00000000000..ee03aed5cca --- /dev/null +++ b/Lib/test/ssltests.py @@ -0,0 +1,37 @@ +# Convenience test module to run all of the OpenSSL-related tests in the +# standard library. + +import ssl +import sys +import subprocess + +TESTS = [ + 'test_asyncio', 'test_ensurepip.py', 'test_ftplib', 'test_hashlib', + 'test_hmac', 'test_httplib', 'test_imaplib', + 'test_poplib', 'test_ssl', 'test_smtplib', 'test_smtpnet', + 'test_urllib2_localnet', 'test_venv', 'test_xmlrpc' +] + +def run_regrtests(*extra_args): + print(ssl.OPENSSL_VERSION) + args = [ + sys.executable, + '-Werror', '-bb', # turn warnings into exceptions + '-m', 'test', + ] + if not extra_args: + args.extend([ + '-r', # randomize + '-w', # re-run failed tests with -v + '-u', 'network', # use network + '-u', 'urlfetch', # download test vectors + '-j', '0' # use multiple CPUs + ]) + else: + args.extend(extra_args) + args.extend(TESTS) + result = subprocess.call(args) + sys.exit(result) + +if __name__ == '__main__': + run_regrtests(*sys.argv[1:]) diff --git a/Lib/test/test__interpchannels.py b/Lib/test/test__interpchannels.py new file mode 100644 index 00000000000..4a7f04b9df9 --- /dev/null +++ b/Lib/test/test__interpchannels.py @@ -0,0 +1,1810 @@ +from collections import namedtuple +import contextlib +import sys +from textwrap import dedent +import threading +import time +import unittest + +from test.support import import_helper + +_channels = import_helper.import_module('_interpchannels') +from test.support.interpreters import _crossinterp +from test.test__interpreters import ( + _interpreters, + _run_output, + clean_up_interpreters, +) + + +REPLACE = _crossinterp._UNBOUND_CONSTANT_TO_FLAG[_crossinterp.UNBOUND] + + +# Additional tests are found in Lib/test/test_interpreters/test_channels.py. +# New tests should be added there. +# XXX The tests here should be moved there. See the note under LowLevelTests. + + +################################## +# helpers + +def recv_wait(cid): + while True: + try: + obj, unboundop = _channels.recv(cid) + except _channels.ChannelEmptyError: + time.sleep(0.1) + else: + assert unboundop is None, repr(unboundop) + return obj + + +def recv_nowait(cid, *args, unbound=False): + obj, unboundop = _channels.recv(cid, *args) + assert (unboundop is None) != unbound, repr(unboundop) + return obj + + +#@contextmanager +#def run_threaded(id, source, **shared): +# def run(): +# run_interp(id, source, **shared) +# t = threading.Thread(target=run) +# t.start() +# yield +# t.join() + + +def run_interp(id, source, **shared): + _run_interp(id, source, shared) + + +def _run_interp(id, source, shared, _mainns={}): + source = dedent(source) + main, *_ = _interpreters.get_main() + if main == id: + cur, *_ = _interpreters.get_current() + if cur != main: + raise RuntimeError + # XXX Run a func? + exec(source, _mainns) + else: + _interpreters.run_string(id, source, shared) + + +class Interpreter(namedtuple('Interpreter', 'name id')): + + @classmethod + def from_raw(cls, raw): + if isinstance(raw, cls): + return raw + elif isinstance(raw, str): + return cls(raw) + else: + raise NotImplementedError + + def __new__(cls, name=None, id=None): + main, *_ = _interpreters.get_main() + if id == main: + if not name: + name = 'main' + elif name != 'main': + raise ValueError( + 'name mismatch (expected "main", got "{}")'.format(name)) + id = main + elif id is not None: + if not name: + name = 'interp' + elif name == 'main': + raise ValueError('name mismatch (unexpected "main")') + assert isinstance(id, int), repr(id) + elif not name or name == 'main': + name = 'main' + id = main + else: + id = _interpreters.create() + self = super().__new__(cls, name, id) + return self + + +# XXX expect_channel_closed() is unnecessary once we improve exc propagation. + +@contextlib.contextmanager +def expect_channel_closed(): + try: + yield + except _channels.ChannelClosedError: + pass + else: + assert False, 'channel not closed' + + +class ChannelAction(namedtuple('ChannelAction', 'action end interp')): + + def __new__(cls, action, end=None, interp=None): + if not end: + end = 'both' + if not interp: + interp = 'main' + self = super().__new__(cls, action, end, interp) + return self + + def __init__(self, *args, **kwargs): + if self.action == 'use': + if self.end not in ('same', 'opposite', 'send', 'recv'): + raise ValueError(self.end) + elif self.action in ('close', 'force-close'): + if self.end not in ('both', 'same', 'opposite', 'send', 'recv'): + raise ValueError(self.end) + else: + raise ValueError(self.action) + if self.interp not in ('main', 'same', 'other', 'extra'): + raise ValueError(self.interp) + + def resolve_end(self, end): + if self.end == 'same': + return end + elif self.end == 'opposite': + return 'recv' if end == 'send' else 'send' + else: + return self.end + + def resolve_interp(self, interp, other, extra): + if self.interp == 'same': + return interp + elif self.interp == 'other': + if other is None: + raise RuntimeError + return other + elif self.interp == 'extra': + if extra is None: + raise RuntimeError + return extra + elif self.interp == 'main': + if interp.name == 'main': + return interp + elif other and other.name == 'main': + return other + else: + raise RuntimeError + # Per __init__(), there aren't any others. + + +class ChannelState(namedtuple('ChannelState', 'pending closed')): + + def __new__(cls, pending=0, *, closed=False): + self = super().__new__(cls, pending, closed) + return self + + def incr(self): + return type(self)(self.pending + 1, closed=self.closed) + + def decr(self): + return type(self)(self.pending - 1, closed=self.closed) + + def close(self, *, force=True): + if self.closed: + if not force or self.pending == 0: + return self + return type(self)(0 if force else self.pending, closed=True) + + +def run_action(cid, action, end, state, *, hideclosed=True): + if state.closed: + if action == 'use' and end == 'recv' and state.pending: + expectfail = False + else: + expectfail = True + else: + expectfail = False + + try: + result = _run_action(cid, action, end, state) + except _channels.ChannelClosedError: + if not hideclosed and not expectfail: + raise + result = state.close() + else: + if expectfail: + raise ... # XXX + return result + + +def _run_action(cid, action, end, state): + if action == 'use': + if end == 'send': + _channels.send(cid, b'spam', blocking=False) + return state.incr() + elif end == 'recv': + if not state.pending: + try: + _channels.recv(cid) + except _channels.ChannelEmptyError: + return state + else: + raise Exception('expected ChannelEmptyError') + else: + recv_nowait(cid) + return state.decr() + else: + raise ValueError(end) + elif action == 'close': + kwargs = {} + if end in ('recv', 'send'): + kwargs[end] = True + _channels.close(cid, **kwargs) + return state.close() + elif action == 'force-close': + kwargs = { + 'force': True, + } + if end in ('recv', 'send'): + kwargs[end] = True + _channels.close(cid, **kwargs) + return state.close(force=True) + else: + raise ValueError(action) + + +def clean_up_channels(): + for cid, _ in _channels.list_all(): + try: + _channels.destroy(cid) + except _channels.ChannelNotFoundError: + pass # already destroyed + + +class TestBase(unittest.TestCase): + + def tearDown(self): + clean_up_channels() + clean_up_interpreters() + + +################################## +# channel tests + +class ChannelIDTests(TestBase): + + def test_default_kwargs(self): + cid = _channels._channel_id(10, force=True) + + self.assertEqual(int(cid), 10) + self.assertEqual(cid.end, 'both') + + def test_with_kwargs(self): + cid = _channels._channel_id(10, send=True, force=True) + self.assertEqual(cid.end, 'send') + + cid = _channels._channel_id(10, send=True, recv=False, force=True) + self.assertEqual(cid.end, 'send') + + cid = _channels._channel_id(10, recv=True, force=True) + self.assertEqual(cid.end, 'recv') + + cid = _channels._channel_id(10, recv=True, send=False, force=True) + self.assertEqual(cid.end, 'recv') + + cid = _channels._channel_id(10, send=True, recv=True, force=True) + self.assertEqual(cid.end, 'both') + + def test_coerce_id(self): + class Int(str): + def __index__(self): + return 10 + + cid = _channels._channel_id(Int(), force=True) + self.assertEqual(int(cid), 10) + + def test_bad_id(self): + self.assertRaises(TypeError, _channels._channel_id, object()) + self.assertRaises(TypeError, _channels._channel_id, 10.0) + self.assertRaises(TypeError, _channels._channel_id, '10') + self.assertRaises(TypeError, _channels._channel_id, b'10') + self.assertRaises(ValueError, _channels._channel_id, -1) + self.assertRaises(OverflowError, _channels._channel_id, 2**64) + + def test_bad_kwargs(self): + with self.assertRaises(ValueError): + _channels._channel_id(10, send=False, recv=False) + + def test_does_not_exist(self): + cid = _channels.create(REPLACE) + with self.assertRaises(_channels.ChannelNotFoundError): + _channels._channel_id(int(cid) + 1) # unforced + + def test_str(self): + cid = _channels._channel_id(10, force=True) + self.assertEqual(str(cid), '10') + + def test_repr(self): + cid = _channels._channel_id(10, force=True) + self.assertEqual(repr(cid), 'ChannelID(10)') + + cid = _channels._channel_id(10, send=True, force=True) + self.assertEqual(repr(cid), 'ChannelID(10, send=True)') + + cid = _channels._channel_id(10, recv=True, force=True) + self.assertEqual(repr(cid), 'ChannelID(10, recv=True)') + + cid = _channels._channel_id(10, send=True, recv=True, force=True) + self.assertEqual(repr(cid), 'ChannelID(10)') + + def test_equality(self): + cid1 = _channels.create(REPLACE) + cid2 = _channels._channel_id(int(cid1)) + cid3 = _channels.create(REPLACE) + + self.assertTrue(cid1 == cid1) + self.assertTrue(cid1 == cid2) + self.assertTrue(cid1 == int(cid1)) + self.assertTrue(int(cid1) == cid1) + self.assertTrue(cid1 == float(int(cid1))) + self.assertTrue(float(int(cid1)) == cid1) + self.assertFalse(cid1 == float(int(cid1)) + 0.1) + self.assertFalse(cid1 == str(int(cid1))) + self.assertFalse(cid1 == 2**1000) + self.assertFalse(cid1 == float('inf')) + self.assertFalse(cid1 == 'spam') + self.assertFalse(cid1 == cid3) + + self.assertFalse(cid1 != cid1) + self.assertFalse(cid1 != cid2) + self.assertTrue(cid1 != cid3) + + def test_shareable(self): + chan = _channels.create(REPLACE) + + obj = _channels.create(REPLACE) + _channels.send(chan, obj, blocking=False) + got = recv_nowait(chan) + + self.assertEqual(got, obj) + self.assertIs(type(got), type(obj)) + # XXX Check the following in the channel tests? + #self.assertIsNot(got, obj) + + +class ChannelTests(TestBase): + + def test_create_cid(self): + cid = _channels.create(REPLACE) + self.assertIsInstance(cid, _channels.ChannelID) + + def test_sequential_ids(self): + before = [cid for cid, _ in _channels.list_all()] + id1 = _channels.create(REPLACE) + id2 = _channels.create(REPLACE) + id3 = _channels.create(REPLACE) + after = [cid for cid, _ in _channels.list_all()] + + self.assertEqual(id2, int(id1) + 1) + self.assertEqual(id3, int(id2) + 1) + self.assertEqual(set(after) - set(before), {id1, id2, id3}) + + def test_ids_global(self): + id1 = _interpreters.create() + out = _run_output(id1, dedent(""" + import _interpchannels as _channels + cid = _channels.create(3) + print(cid) + """)) + cid1 = int(out.strip()) + + id2 = _interpreters.create() + out = _run_output(id2, dedent(""" + import _interpchannels as _channels + cid = _channels.create(3) + print(cid) + """)) + cid2 = int(out.strip()) + + self.assertEqual(cid2, int(cid1) + 1) + + def test_channel_list_interpreters_none(self): + """Test listing interpreters for a channel with no associations.""" + # Test for channel with no associated _interpreters. + cid = _channels.create(REPLACE) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, []) + self.assertEqual(recv_interps, []) + + def test_channel_list_interpreters_basic(self): + """Test basic listing channel _interpreters.""" + interp0, *_ = _interpreters.get_main() + cid = _channels.create(REPLACE) + _channels.send(cid, "send", blocking=False) + # Test for a channel that has one end associated to an interpreter. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, []) + + interp1 = _interpreters.create() + _run_output(interp1, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + # Test for channel that has both ends associated to an interpreter. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, [interp1]) + + def test_channel_list_interpreters_multiple(self): + """Test listing interpreters for a channel with many associations.""" + interp0, *_ = _interpreters.get_main() + interp1 = _interpreters.create() + interp2 = _interpreters.create() + interp3 = _interpreters.create() + cid = _channels.create(REPLACE) + + _channels.send(cid, "send", blocking=False) + _run_output(interp1, dedent(f""" + import _interpchannels as _channels + _channels.send({cid}, "send", blocking=False) + """)) + _run_output(interp2, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + _run_output(interp3, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(set(send_interps), {interp0, interp1}) + self.assertEqual(set(recv_interps), {interp2, interp3}) + + def test_channel_list_interpreters_destroyed(self): + """Test listing channel interpreters with a destroyed interpreter.""" + interp0, *_ = _interpreters.get_main() + interp1 = _interpreters.create() + cid = _channels.create(REPLACE) + _channels.send(cid, "send", blocking=False) + _run_output(interp1, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + # Should be one interpreter associated with each end. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, [interp1]) + + _interpreters.destroy(interp1) + # Destroyed interpreter should not be listed. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, []) + + def test_channel_list_interpreters_released(self): + """Test listing channel interpreters with a released channel.""" + # Set up one channel with main interpreter on the send end and two + # subinterpreters on the receive end. + interp0, *_ = _interpreters.get_main() + interp1 = _interpreters.create() + interp2 = _interpreters.create() + cid = _channels.create(REPLACE) + _channels.send(cid, "data", blocking=False) + _run_output(interp1, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + _channels.send(cid, "data", blocking=False) + _run_output(interp2, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + # Check the setup. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 2) + + # Release the main interpreter from the send end. + _channels.release(cid, send=True) + # Send end should have no associated _interpreters. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 0) + self.assertEqual(len(recv_interps), 2) + + # Release one of the subinterpreters from the receive end. + _run_output(interp2, dedent(f""" + import _interpchannels as _channels + _channels.release({cid}) + """)) + # Receive end should have the released interpreter removed. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 0) + self.assertEqual(recv_interps, [interp1]) + + def test_channel_list_interpreters_closed(self): + """Test listing channel interpreters with a closed channel.""" + interp0, *_ = _interpreters.get_main() + interp1 = _interpreters.create() + cid = _channels.create(REPLACE) + # Put something in the channel so that it's not empty. + _channels.send(cid, "send", blocking=False) + + # Check initial state. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 0) + + # Force close the channel. + _channels.close(cid, force=True) + # Both ends should raise an error. + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=False) + + def test_channel_list_interpreters_closed_send_end(self): + """Test listing channel interpreters with a channel's send end closed.""" + interp0, *_ = _interpreters.get_main() + interp1 = _interpreters.create() + cid = _channels.create(REPLACE) + # Put something in the channel so that it's not empty. + _channels.send(cid, "send", blocking=False) + + # Check initial state. + send_interps = _channels.list_interpreters(cid, send=True) + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 0) + + # Close the send end of the channel. + _channels.close(cid, send=True) + # Send end should raise an error. + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) + # Receive end should not be closed (since channel is not empty). + recv_interps = _channels.list_interpreters(cid, send=False) + self.assertEqual(len(recv_interps), 0) + + # Close the receive end of the channel from a subinterpreter. + _run_output(interp1, dedent(f""" + import _interpchannels as _channels + _channels.close({cid}, force=True) + """)) + return + # Both ends should raise an error. + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=False) + + def test_allowed_types(self): + cid = _channels.create(REPLACE) + objects = [ + None, + 'spam', + b'spam', + 42, + ] + for obj in objects: + with self.subTest(obj): + _channels.send(cid, obj, blocking=False) + got = recv_nowait(cid) + + self.assertEqual(got, obj) + self.assertIs(type(got), type(obj)) + # XXX Check the following? + #self.assertIsNot(got, obj) + # XXX What about between interpreters? + + def test_run_string_arg_unresolved(self): + cid = _channels.create(REPLACE) + interp = _interpreters.create() + + _interpreters.set___main___attrs(interp, dict(cid=cid.send)) + out = _run_output(interp, dedent(""" + import _interpchannels as _channels + print(cid.end) + _channels.send(cid, b'spam', blocking=False) + """)) + obj = recv_nowait(cid) + + self.assertEqual(obj, b'spam') + self.assertEqual(out.strip(), 'send') + + # XXX For now there is no high-level channel into which the + # sent channel ID can be converted... + # Note: this test caused crashes on some buildbots (bpo-33615). + @unittest.skip('disabled until high-level channels exist') + def test_run_string_arg_resolved(self): + cid = _channels.create(REPLACE) + cid = _channels._channel_id(cid, _resolve=True) + interp = _interpreters.create() + + out = _run_output(interp, dedent(""" + import _interpchannels as _channels + print(chan.id.end) + _channels.send(chan.id, b'spam', blocking=False) + """), + dict(chan=cid.send)) + obj = recv_nowait(cid) + + self.assertEqual(obj, b'spam') + self.assertEqual(out.strip(), 'send') + + #------------------- + # send/recv + + def test_send_recv_main(self): + cid = _channels.create(REPLACE) + orig = b'spam' + _channels.send(cid, orig, blocking=False) + obj = recv_nowait(cid) + + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) + + def test_send_recv_same_interpreter(self): + id1 = _interpreters.create() + out = _run_output(id1, dedent(""" + import _interpchannels as _channels + cid = _channels.create(REPLACE) + orig = b'spam' + _channels.send(cid, orig, blocking=False) + obj, _ = _channels.recv(cid) + assert obj is not orig + assert obj == orig + """)) + + def test_send_recv_different_interpreters(self): + cid = _channels.create(REPLACE) + id1 = _interpreters.create() + out = _run_output(id1, dedent(f""" + import _interpchannels as _channels + _channels.send({cid}, b'spam', blocking=False) + """)) + obj = recv_nowait(cid) + + self.assertEqual(obj, b'spam') + + def test_send_recv_different_threads(self): + cid = _channels.create(REPLACE) + + def f(): + obj = recv_wait(cid) + _channels.send(cid, obj) + t = threading.Thread(target=f) + t.start() + + _channels.send(cid, b'spam') + obj = recv_wait(cid) + t.join() + + self.assertEqual(obj, b'spam') + + def test_send_recv_different_interpreters_and_threads(self): + cid = _channels.create(REPLACE) + id1 = _interpreters.create() + out = None + + def f(): + nonlocal out + out = _run_output(id1, dedent(f""" + import time + import _interpchannels as _channels + while True: + try: + obj, _ = _channels.recv({cid}) + break + except _channels.ChannelEmptyError: + time.sleep(0.1) + assert(obj == b'spam') + _channels.send({cid}, b'eggs') + """)) + t = threading.Thread(target=f) + t.start() + + _channels.send(cid, b'spam') + obj = recv_wait(cid) + t.join() + + self.assertEqual(obj, b'eggs') + + def test_send_not_found(self): + with self.assertRaises(_channels.ChannelNotFoundError): + _channels.send(10, b'spam') + + def test_recv_not_found(self): + with self.assertRaises(_channels.ChannelNotFoundError): + _channels.recv(10) + + def test_recv_empty(self): + cid = _channels.create(REPLACE) + with self.assertRaises(_channels.ChannelEmptyError): + _channels.recv(cid) + + def test_recv_default(self): + default = object() + cid = _channels.create(REPLACE) + obj1 = recv_nowait(cid, default) + _channels.send(cid, None, blocking=False) + _channels.send(cid, 1, blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'eggs', blocking=False) + obj2 = recv_nowait(cid, default) + obj3 = recv_nowait(cid, default) + obj4 = recv_nowait(cid) + obj5 = recv_nowait(cid, default) + obj6 = recv_nowait(cid, default) + + self.assertIs(obj1, default) + self.assertIs(obj2, None) + self.assertEqual(obj3, 1) + self.assertEqual(obj4, b'spam') + self.assertEqual(obj5, b'eggs') + self.assertIs(obj6, default) + + def test_recv_sending_interp_destroyed(self): + with self.subTest('closed'): + cid1 = _channels.create(REPLACE) + interp = _interpreters.create() + _interpreters.run_string(interp, dedent(f""" + import _interpchannels as _channels + _channels.send({cid1}, b'spam', blocking=False) + """)) + _interpreters.destroy(interp) + + with self.assertRaisesRegex(RuntimeError, + f'channel {cid1} is closed'): + _channels.recv(cid1) + del cid1 + with self.subTest('still open'): + cid2 = _channels.create(REPLACE) + interp = _interpreters.create() + _interpreters.run_string(interp, dedent(f""" + import _interpchannels as _channels + _channels.send({cid2}, b'spam', blocking=False) + """)) + _channels.send(cid2, b'eggs', blocking=False) + _interpreters.destroy(interp) + + recv_nowait(cid2, unbound=True) + recv_nowait(cid2, unbound=False) + with self.assertRaisesRegex(RuntimeError, + f'channel {cid2} is empty'): + _channels.recv(cid2) + del cid2 + + #------------------- + # send_buffer + + def test_send_buffer(self): + buf = bytearray(b'spamspamspam') + cid = _channels.create(REPLACE) + _channels.send_buffer(cid, buf, blocking=False) + obj = recv_nowait(cid) + + self.assertIsNot(obj, buf) + self.assertIsInstance(obj, memoryview) + self.assertEqual(obj, buf) + + buf[4:8] = b'eggs' + self.assertEqual(obj, buf) + obj[4:8] = b'ham.' + self.assertEqual(obj, buf) + + #------------------- + # send with waiting + + def build_send_waiter(self, obj, *, buffer=False): + # We want a long enough sleep that send() actually has to wait. + + if buffer: + send = _channels.send_buffer + else: + send = _channels.send + + cid = _channels.create(REPLACE) + try: + started = time.monotonic() + send(cid, obj, blocking=False) + stopped = time.monotonic() + recv_nowait(cid) + finally: + _channels.destroy(cid) + delay = stopped - started # seconds + delay *= 3 + + def wait(): + time.sleep(delay) + return wait + + def test_send_blocking_waiting(self): + received = None + obj = b'spam' + wait = self.build_send_waiter(obj) + cid = _channels.create(REPLACE) + def f(): + nonlocal received + wait() + received = recv_wait(cid) + t = threading.Thread(target=f) + t.start() + _channels.send(cid, obj, blocking=True) + t.join() + + self.assertEqual(received, obj) + + def test_send_buffer_blocking_waiting(self): + received = None + obj = bytearray(b'spam') + wait = self.build_send_waiter(obj, buffer=True) + cid = _channels.create(REPLACE) + def f(): + nonlocal received + wait() + received = recv_wait(cid) + t = threading.Thread(target=f) + t.start() + _channels.send_buffer(cid, obj, blocking=True) + t.join() + + self.assertEqual(received, obj) + + def test_send_blocking_no_wait(self): + received = None + obj = b'spam' + cid = _channels.create(REPLACE) + def f(): + nonlocal received + received = recv_wait(cid) + t = threading.Thread(target=f) + t.start() + _channels.send(cid, obj, blocking=True) + t.join() + + self.assertEqual(received, obj) + + def test_send_buffer_blocking_no_wait(self): + received = None + obj = bytearray(b'spam') + cid = _channels.create(REPLACE) + def f(): + nonlocal received + received = recv_wait(cid) + t = threading.Thread(target=f) + t.start() + _channels.send_buffer(cid, obj, blocking=True) + t.join() + + self.assertEqual(received, obj) + + def test_send_timeout(self): + obj = b'spam' + + with self.subTest('non-blocking with timeout'): + cid = _channels.create(REPLACE) + with self.assertRaises(ValueError): + _channels.send(cid, obj, blocking=False, timeout=0.1) + + with self.subTest('timeout hit'): + cid = _channels.create(REPLACE) + with self.assertRaises(TimeoutError): + _channels.send(cid, obj, blocking=True, timeout=0.1) + with self.assertRaises(_channels.ChannelEmptyError): + received = recv_nowait(cid) + print(repr(received)) + + with self.subTest('timeout not hit'): + cid = _channels.create(REPLACE) + def f(): + recv_wait(cid) + t = threading.Thread(target=f) + t.start() + _channels.send(cid, obj, blocking=True, timeout=10) + t.join() + + def test_send_buffer_timeout(self): + try: + self._has_run_once_timeout + except AttributeError: + # At the moment, this test leaks a few references. + # It looks like the leak originates with the addition + # of _channels.send_buffer() (gh-110246), whereas the + # tests were added afterward. We want this test even + # if the refleak isn't fixed yet, so we skip here. + raise unittest.SkipTest('temporarily skipped due to refleaks') + else: + self._has_run_once_timeout = True + + obj = bytearray(b'spam') + + with self.subTest('non-blocking with timeout'): + cid = _channels.create(REPLACE) + with self.assertRaises(ValueError): + _channels.send_buffer(cid, obj, blocking=False, timeout=0.1) + + with self.subTest('timeout hit'): + cid = _channels.create(REPLACE) + with self.assertRaises(TimeoutError): + _channels.send_buffer(cid, obj, blocking=True, timeout=0.1) + with self.assertRaises(_channels.ChannelEmptyError): + received = recv_nowait(cid) + print(repr(received)) + + with self.subTest('timeout not hit'): + cid = _channels.create(REPLACE) + def f(): + recv_wait(cid) + t = threading.Thread(target=f) + t.start() + _channels.send_buffer(cid, obj, blocking=True, timeout=10) + t.join() + + def test_send_closed_while_waiting(self): + obj = b'spam' + wait = self.build_send_waiter(obj) + + with self.subTest('without timeout'): + cid = _channels.create(REPLACE) + def f(): + wait() + _channels.close(cid, force=True) + t = threading.Thread(target=f) + t.start() + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, obj, blocking=True) + t.join() + + with self.subTest('with timeout'): + cid = _channels.create(REPLACE) + def f(): + wait() + _channels.close(cid, force=True) + t = threading.Thread(target=f) + t.start() + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, obj, blocking=True, timeout=30) + t.join() + + def test_send_buffer_closed_while_waiting(self): + try: + self._has_run_once_closed + except AttributeError: + # At the moment, this test leaks a few references. + # It looks like the leak originates with the addition + # of _channels.send_buffer() (gh-110246), whereas the + # tests were added afterward. We want this test even + # if the refleak isn't fixed yet, so we skip here. + raise unittest.SkipTest('temporarily skipped due to refleaks') + else: + self._has_run_once_closed = True + + obj = bytearray(b'spam') + wait = self.build_send_waiter(obj, buffer=True) + + with self.subTest('without timeout'): + cid = _channels.create(REPLACE) + def f(): + wait() + _channels.close(cid, force=True) + t = threading.Thread(target=f) + t.start() + with self.assertRaises(_channels.ChannelClosedError): + _channels.send_buffer(cid, obj, blocking=True) + t.join() + + with self.subTest('with timeout'): + cid = _channels.create(REPLACE) + def f(): + wait() + _channels.close(cid, force=True) + t = threading.Thread(target=f) + t.start() + with self.assertRaises(_channels.ChannelClosedError): + _channels.send_buffer(cid, obj, blocking=True, timeout=30) + t.join() + + #------------------- + # close + + def test_close_single_user(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.close(cid) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_multiple_users(self): + cid = _channels.create(REPLACE) + id1 = _interpreters.create() + id2 = _interpreters.create() + _interpreters.run_string(id1, dedent(f""" + import _interpchannels as _channels + _channels.send({cid}, b'spam', blocking=False) + """)) + _interpreters.run_string(id2, dedent(f""" + import _interpchannels as _channels + _channels.recv({cid}) + """)) + _channels.close(cid) + + excsnap = _interpreters.run_string(id1, dedent(f""" + _channels.send({cid}, b'spam') + """)) + self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') + + excsnap = _interpreters.run_string(id2, dedent(f""" + _channels.send({cid}, b'spam') + """)) + self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') + + def test_close_multiple_times(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.close(cid) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(cid) + + def test_close_empty(self): + tests = [ + (False, False), + (True, False), + (False, True), + (True, True), + ] + for send, recv in tests: + with self.subTest((send, recv)): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.close(cid, send=send, recv=recv) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_defaults_with_unused_items(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + + with self.assertRaises(_channels.ChannelNotEmptyError): + _channels.close(cid) + recv_nowait(cid) + _channels.send(cid, b'eggs', blocking=False) + + def test_close_recv_with_unused_items_unforced(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + + with self.assertRaises(_channels.ChannelNotEmptyError): + _channels.close(cid, recv=True) + recv_nowait(cid) + _channels.send(cid, b'eggs', blocking=False) + recv_nowait(cid) + recv_nowait(cid) + _channels.close(cid, recv=True) + + def test_close_send_with_unused_items_unforced(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, send=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + recv_nowait(cid) + recv_nowait(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_both_with_unused_items_unforced(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + + with self.assertRaises(_channels.ChannelNotEmptyError): + _channels.close(cid, recv=True, send=True) + recv_nowait(cid) + _channels.send(cid, b'eggs', blocking=False) + recv_nowait(cid) + recv_nowait(cid) + _channels.close(cid, recv=True) + + def test_close_recv_with_unused_items_forced(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, recv=True, force=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_send_with_unused_items_forced(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, send=True, force=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_both_with_unused_items_forced(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.close(cid, send=True, recv=True, force=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_never_used(self): + cid = _channels.create(REPLACE) + _channels.close(cid) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'spam') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_close_by_unassociated_interp(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + interp = _interpreters.create() + _interpreters.run_string(interp, dedent(f""" + import _interpchannels as _channels + _channels.close({cid}, force=True) + """)) + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(cid) + + def test_close_used_multiple_times_by_single_user(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.close(cid, force=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_channel_list_interpreters_invalid_channel(self): + cid = _channels.create(REPLACE) + # Test for invalid channel ID. + with self.assertRaises(_channels.ChannelNotFoundError): + _channels.list_interpreters(1000, send=True) + + _channels.close(cid) + # Test for a channel that has been closed. + with self.assertRaises(_channels.ChannelClosedError): + _channels.list_interpreters(cid, send=True) + + def test_channel_list_interpreters_invalid_args(self): + # Tests for invalid arguments passed to the API. + cid = _channels.create(REPLACE) + with self.assertRaises(TypeError): + _channels.list_interpreters(cid) + + +class ChannelReleaseTests(TestBase): + + # XXX Add more test coverage a la the tests for close(). + + """ + - main / interp / other + - run in: current thread / new thread / other thread / different threads + - end / opposite + - force / no force + - used / not used (associated / not associated) + - empty / emptied / never emptied / partly emptied + - closed / not closed + - released / not released + - creator (interp) / other + - associated interpreter not running + - associated interpreter destroyed + """ + + """ + use + pre-release + release + after + check + """ + + """ + release in: main, interp1 + creator: same, other (incl. interp2) + + use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all + pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all + pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all + + release: same + release forced: same + + use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all + release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all + check released: send/recv for same/other(incl. interp2) + check closed: send/recv for same/other(incl. interp2) + """ + + def test_single_user(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.release(cid, send=True, recv=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_multiple_users(self): + cid = _channels.create(REPLACE) + id1 = _interpreters.create() + id2 = _interpreters.create() + _interpreters.run_string(id1, dedent(f""" + import _interpchannels as _channels + _channels.send({cid}, b'spam', blocking=False) + """)) + out = _run_output(id2, dedent(f""" + import _interpchannels as _channels + obj, _ = _channels.recv({cid}) + _channels.release({cid}) + print(repr(obj)) + """)) + _interpreters.run_string(id1, dedent(f""" + _channels.release({cid}) + """)) + + self.assertEqual(out.strip(), "b'spam'") + + def test_no_kwargs(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.release(cid) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_multiple_times(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.release(cid, send=True, recv=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.release(cid, send=True, recv=True) + + def test_with_unused_items(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'ham', blocking=False) + _channels.release(cid, send=True, recv=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_never_used(self): + cid = _channels.create(REPLACE) + _channels.release(cid) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'spam') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_by_unassociated_interp(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + interp = _interpreters.create() + _interpreters.run_string(interp, dedent(f""" + import _interpchannels as _channels + _channels.release({cid}) + """)) + obj = recv_nowait(cid) + _channels.release(cid) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + self.assertEqual(obj, b'spam') + + def test_close_if_unassociated(self): + # XXX Something's not right with this test... + cid = _channels.create(REPLACE) + interp = _interpreters.create() + _interpreters.run_string(interp, dedent(f""" + import _interpchannels as _channels + obj = _channels.send({cid}, b'spam', blocking=False) + _channels.release({cid}) + """)) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + def test_partially(self): + # XXX Is partial close too weird/confusing? + cid = _channels.create(REPLACE) + _channels.send(cid, None, blocking=False) + recv_nowait(cid) + _channels.send(cid, b'spam', blocking=False) + _channels.release(cid, send=True) + obj = recv_nowait(cid) + + self.assertEqual(obj, b'spam') + + def test_used_multiple_times_by_single_user(self): + cid = _channels.create(REPLACE) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + _channels.send(cid, b'spam', blocking=False) + recv_nowait(cid) + _channels.release(cid, send=True, recv=True) + + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(cid, b'eggs') + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(cid) + + +class ChannelCloseFixture(namedtuple('ChannelCloseFixture', + 'end interp other extra creator')): + + # Set this to True to avoid creating interpreters, e.g. when + # scanning through test permutations without running them. + QUICK = False + + def __new__(cls, end, interp, other, extra, creator): + assert end in ('send', 'recv') + if cls.QUICK: + known = {} + else: + interp = Interpreter.from_raw(interp) + other = Interpreter.from_raw(other) + extra = Interpreter.from_raw(extra) + known = { + interp.name: interp, + other.name: other, + extra.name: extra, + } + if not creator: + creator = 'same' + self = super().__new__(cls, end, interp, other, extra, creator) + self._prepped = set() + self._state = ChannelState() + self._known = known + return self + + @property + def state(self): + return self._state + + @property + def cid(self): + try: + return self._cid + except AttributeError: + creator = self._get_interpreter(self.creator) + self._cid = self._new_channel(creator) + return self._cid + + def get_interpreter(self, interp): + interp = self._get_interpreter(interp) + self._prep_interpreter(interp) + return interp + + def expect_closed_error(self, end=None): + if end is None: + end = self.end + if end == 'recv' and self.state.closed == 'send': + return False + return bool(self.state.closed) + + def prep_interpreter(self, interp): + self._prep_interpreter(interp) + + def record_action(self, action, result): + self._state = result + + def clean_up(self): + clean_up_interpreters() + clean_up_channels() + + # internal methods + + def _new_channel(self, creator): + if creator.name == 'main': + return _channels.create(REPLACE) + else: + ch = _channels.create(REPLACE) + run_interp(creator.id, f""" + import _interpreters + cid = _xxsubchannels.create() + # We purposefully send back an int to avoid tying the + # channel to the other interpreter. + _xxsubchannels.send({ch}, int(cid), blocking=False) + del _interpreters + """) + self._cid = recv_nowait(ch) + return self._cid + + def _get_interpreter(self, interp): + if interp in ('same', 'interp'): + return self.interp + elif interp == 'other': + return self.other + elif interp == 'extra': + return self.extra + else: + name = interp + try: + interp = self._known[name] + except KeyError: + interp = self._known[name] = Interpreter(name) + return interp + + def _prep_interpreter(self, interp): + if interp.id in self._prepped: + return + self._prepped.add(interp.id) + if interp.name == 'main': + return + run_interp(interp.id, f""" + import _interpchannels as channels + import test.test__interpchannels as helpers + ChannelState = helpers.ChannelState + try: + cid + except NameError: + cid = _channels._channel_id({self.cid}) + """) + + +@unittest.skip('these tests take several hours to run') +class ExhaustiveChannelTests(TestBase): + + """ + - main / interp / other + - run in: current thread / new thread / other thread / different threads + - end / opposite + - force / no force + - used / not used (associated / not associated) + - empty / emptied / never emptied / partly emptied + - closed / not closed + - released / not released + - creator (interp) / other + - associated interpreter not running + - associated interpreter destroyed + + - close after unbound + """ + + """ + use + pre-close + close + after + check + """ + + """ + close in: main, interp1 + creator: same, other, extra + + use: None,send,recv,send/recv in None,same,other,same+other,all + pre-close: None,send,recv in None,same,other,same+other,all + pre-close forced: None,send,recv in None,same,other,same+other,all + + close: same + close forced: same + + use after: None,send,recv,send/recv in None,same,other,extra,same+other,all + close after: None,send,recv,send/recv in None,same,other,extra,same+other,all + check closed: send/recv for same/other(incl. interp2) + """ + + def iter_action_sets(self): + # - used / not used (associated / not associated) + # - empty / emptied / never emptied / partly emptied + # - closed / not closed + # - released / not released + + # never used + yield [] + + # only pre-closed (and possible used after) + for closeactions in self._iter_close_action_sets('same', 'other'): + yield closeactions + for postactions in self._iter_post_close_action_sets(): + yield closeactions + postactions + for closeactions in self._iter_close_action_sets('other', 'extra'): + yield closeactions + for postactions in self._iter_post_close_action_sets(): + yield closeactions + postactions + + # used + for useactions in self._iter_use_action_sets('same', 'other'): + yield useactions + for closeactions in self._iter_close_action_sets('same', 'other'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + for closeactions in self._iter_close_action_sets('other', 'extra'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + for useactions in self._iter_use_action_sets('other', 'extra'): + yield useactions + for closeactions in self._iter_close_action_sets('same', 'other'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + for closeactions in self._iter_close_action_sets('other', 'extra'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + + def _iter_use_action_sets(self, interp1, interp2): + interps = (interp1, interp2) + + # only recv end used + yield [ + ChannelAction('use', 'recv', interp1), + ] + yield [ + ChannelAction('use', 'recv', interp2), + ] + yield [ + ChannelAction('use', 'recv', interp1), + ChannelAction('use', 'recv', interp2), + ] + + # never emptied + yield [ + ChannelAction('use', 'send', interp1), + ] + yield [ + ChannelAction('use', 'send', interp2), + ] + yield [ + ChannelAction('use', 'send', interp1), + ChannelAction('use', 'send', interp2), + ] + + # partially emptied + for interp1 in interps: + for interp2 in interps: + for interp3 in interps: + yield [ + ChannelAction('use', 'send', interp1), + ChannelAction('use', 'send', interp2), + ChannelAction('use', 'recv', interp3), + ] + + # fully emptied + for interp1 in interps: + for interp2 in interps: + for interp3 in interps: + for interp4 in interps: + yield [ + ChannelAction('use', 'send', interp1), + ChannelAction('use', 'send', interp2), + ChannelAction('use', 'recv', interp3), + ChannelAction('use', 'recv', interp4), + ] + + def _iter_close_action_sets(self, interp1, interp2): + ends = ('recv', 'send') + interps = (interp1, interp2) + for force in (True, False): + op = 'force-close' if force else 'close' + for interp in interps: + for end in ends: + yield [ + ChannelAction(op, end, interp), + ] + for recvop in ('close', 'force-close'): + for sendop in ('close', 'force-close'): + for recv in interps: + for send in interps: + yield [ + ChannelAction(recvop, 'recv', recv), + ChannelAction(sendop, 'send', send), + ] + + def _iter_post_close_action_sets(self): + for interp in ('same', 'extra', 'other'): + yield [ + ChannelAction('use', 'recv', interp), + ] + yield [ + ChannelAction('use', 'send', interp), + ] + + def run_actions(self, fix, actions): + for action in actions: + self.run_action(fix, action) + + def run_action(self, fix, action, *, hideclosed=True): + end = action.resolve_end(fix.end) + interp = action.resolve_interp(fix.interp, fix.other, fix.extra) + fix.prep_interpreter(interp) + if interp.name == 'main': + result = run_action( + fix.cid, + action.action, + end, + fix.state, + hideclosed=hideclosed, + ) + fix.record_action(action, result) + else: + _cid = _channels.create(REPLACE) + run_interp(interp.id, f""" + result = helpers.run_action( + {fix.cid}, + {repr(action.action)}, + {repr(end)}, + {repr(fix.state)}, + hideclosed={hideclosed}, + ) + _channels.send({_cid}, result.pending.to_bytes(1, 'little'), blocking=False) + _channels.send({_cid}, b'X' if result.closed else b'', blocking=False) + """) + result = ChannelState( + pending=int.from_bytes(recv_nowait(_cid), 'little'), + closed=bool(recv_nowait(_cid)), + ) + fix.record_action(action, result) + + def iter_fixtures(self): + # XXX threads? + interpreters = [ + ('main', 'interp', 'extra'), + ('interp', 'main', 'extra'), + ('interp1', 'interp2', 'extra'), + ('interp1', 'interp2', 'main'), + ] + for interp, other, extra in interpreters: + for creator in ('same', 'other', 'creator'): + for end in ('send', 'recv'): + yield ChannelCloseFixture(end, interp, other, extra, creator) + + def _close(self, fix, *, force): + op = 'force-close' if force else 'close' + close = ChannelAction(op, fix.end, 'same') + if not fix.expect_closed_error(): + self.run_action(fix, close, hideclosed=False) + else: + with self.assertRaises(_channels.ChannelClosedError): + self.run_action(fix, close, hideclosed=False) + + def _assert_closed_in_interp(self, fix, interp=None): + if interp is None or interp.name == 'main': + with self.assertRaises(_channels.ChannelClosedError): + _channels.recv(fix.cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.send(fix.cid, b'spam') + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(fix.cid) + with self.assertRaises(_channels.ChannelClosedError): + _channels.close(fix.cid, force=True) + else: + run_interp(interp.id, """ + with helpers.expect_channel_closed(): + _channels.recv(cid) + """) + run_interp(interp.id, """ + with helpers.expect_channel_closed(): + _channels.send(cid, b'spam', blocking=False) + """) + run_interp(interp.id, """ + with helpers.expect_channel_closed(): + _channels.close(cid) + """) + run_interp(interp.id, """ + with helpers.expect_channel_closed(): + _channels.close(cid, force=True) + """) + + def _assert_closed(self, fix): + self.assertTrue(fix.state.closed) + + for _ in range(fix.state.pending): + recv_nowait(fix.cid) + self._assert_closed_in_interp(fix) + + for interp in ('same', 'other'): + interp = fix.get_interpreter(interp) + if interp.name == 'main': + continue + self._assert_closed_in_interp(fix, interp) + + interp = fix.get_interpreter('fresh') + self._assert_closed_in_interp(fix, interp) + + def _iter_close_tests(self, verbose=False): + i = 0 + for actions in self.iter_action_sets(): + print() + for fix in self.iter_fixtures(): + i += 1 + if i > 1000: + return + if verbose: + if (i - 1) % 6 == 0: + print() + print(i, fix, '({} actions)'.format(len(actions))) + else: + if (i - 1) % 6 == 0: + print(' ', end='') + print('.', end=''); sys.stdout.flush() + yield i, fix, actions + if verbose: + print('---') + print() + + # This is useful for scanning through the possible tests. + def _skim_close_tests(self): + ChannelCloseFixture.QUICK = True + for i, fix, actions in self._iter_close_tests(): + pass + + def test_close(self): + for i, fix, actions in self._iter_close_tests(): + with self.subTest('{} {} {}'.format(i, fix, actions)): + fix.prep_interpreter(fix.interp) + self.run_actions(fix, actions) + + self._close(fix, force=False) + + self._assert_closed(fix) + # XXX Things slow down if we have too many interpreters. + fix.clean_up() + + def test_force_close(self): + for i, fix, actions in self._iter_close_tests(): + with self.subTest('{} {} {}'.format(i, fix, actions)): + fix.prep_interpreter(fix.interp) + self.run_actions(fix, actions) + + self._close(fix, force=True) + + self._assert_closed(fix) + # XXX Things slow down if we have too many interpreters. + fix.clean_up() + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py new file mode 100644 index 00000000000..6fb2bc24fa3 --- /dev/null +++ b/Lib/test/test__interpreters.py @@ -0,0 +1,1202 @@ +import contextlib +import itertools +import os +import pickle +import sys +from textwrap import dedent +import threading +import unittest + +from test import support +from test.support import import_helper +from test.support import os_helper +from test.support import script_helper + + +_interpreters = import_helper.import_module('_interpreters') +_testinternalcapi = import_helper.import_module('_testinternalcapi') +from _interpreters import InterpreterNotFoundError + + +################################## +# helpers + +def _captured_script(script): + r, w = os.pipe() + indented = script.replace('\n', '\n ') + wrapped = dedent(f""" + import contextlib + with open({w}, 'w', encoding="utf-8") as spipe: + with contextlib.redirect_stdout(spipe): + {indented} + """) + return wrapped, open(r, encoding="utf-8") + + +def _run_output(interp, request): + script, rpipe = _captured_script(request) + with rpipe: + _interpreters.run_string(interp, script) + return rpipe.read() + + +def _wait_for_interp_to_run(interp, timeout=None): + # bpo-37224: Running this test file in multiprocesses will fail randomly. + # The failure reason is that the thread can't acquire the cpu to + # run subinterpreter eariler than the main thread in multiprocess. + if timeout is None: + timeout = support.SHORT_TIMEOUT + for _ in support.sleeping_retry(timeout, error=False): + if _interpreters.is_running(interp): + break + else: + raise RuntimeError('interp is not running') + + +@contextlib.contextmanager +def _running(interp): + r, w = os.pipe() + def run(): + _interpreters.run_string(interp, dedent(f""" + # wait for "signal" + with open({r}, encoding="utf-8") as rpipe: + rpipe.read() + """)) + + t = threading.Thread(target=run) + t.start() + _wait_for_interp_to_run(interp) + + yield + + with open(w, 'w', encoding="utf-8") as spipe: + spipe.write('done') + t.join() + + +def clean_up_interpreters(): + for id, *_ in _interpreters.list_all(): + if id == 0: # main + continue + try: + _interpreters.destroy(id) + except _interpreters.InterpreterError: + pass # already destroyed + + +class TestBase(unittest.TestCase): + + def tearDown(self): + clean_up_interpreters() + + +################################## +# misc. tests + +class IsShareableTests(unittest.TestCase): + + def test_default_shareables(self): + shareables = [ + # singletons + None, + # builtin objects + b'spam', + 'spam', + 10, + -10, + True, + False, + 100.0, + (1, ('spam', 'eggs')), + ] + for obj in shareables: + with self.subTest(obj): + self.assertTrue( + _interpreters.is_shareable(obj)) + + def test_not_shareable(self): + class Cheese: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + + class SubBytes(bytes): + """A subclass of a shareable type.""" + + not_shareables = [ + # singletons + NotImplemented, + ..., + # builtin types and objects + type, + object, + object(), + Exception(), + # user-defined types and objects + Cheese, + Cheese('Wensleydale'), + SubBytes(b'spam'), + ] + for obj in not_shareables: + with self.subTest(repr(obj)): + self.assertFalse( + _interpreters.is_shareable(obj)) + + +class ShareableTypeTests(unittest.TestCase): + + def _assert_values(self, values): + for obj in values: + with self.subTest(obj): + xid = _testinternalcapi.get_crossinterp_data(obj) + got = _testinternalcapi.restore_crossinterp_data(xid) + + self.assertEqual(got, obj) + self.assertIs(type(got), type(obj)) + + def test_singletons(self): + for obj in [None]: + with self.subTest(obj): + xid = _testinternalcapi.get_crossinterp_data(obj) + got = _testinternalcapi.restore_crossinterp_data(xid) + + # XXX What about between interpreters? + self.assertIs(got, obj) + + def test_types(self): + self._assert_values([ + b'spam', + 9999, + ]) + + def test_bytes(self): + self._assert_values(i.to_bytes(2, 'little', signed=True) + for i in range(-1, 258)) + + def test_strs(self): + self._assert_values(['hello world', '你好世界', '']) + + def test_int(self): + self._assert_values(itertools.chain(range(-1, 258), + [sys.maxsize, -sys.maxsize - 1])) + + def test_non_shareable_int(self): + ints = [ + sys.maxsize + 1, + -sys.maxsize - 2, + 2**1000, + ] + for i in ints: + with self.subTest(i): + with self.assertRaises(OverflowError): + _testinternalcapi.get_crossinterp_data(i) + + def test_bool(self): + self._assert_values([True, False]) + + def test_float(self): + self._assert_values([0.0, 1.1, -1.0, 0.12345678, -0.12345678]) + + def test_tuple(self): + self._assert_values([(), (1,), ("hello", "world", ), (1, True, "hello")]) + # Test nesting + self._assert_values([ + ((1,),), + ((1, 2), (3, 4)), + ((1, 2), (3, 4), (5, 6)), + ]) + + def test_tuples_containing_non_shareable_types(self): + non_shareables = [ + Exception(), + object(), + ] + for s in non_shareables: + value = tuple([0, 1.0, s]) + with self.subTest(repr(value)): + # XXX Assert the NotShareableError when it is exported + with self.assertRaises(ValueError): + _testinternalcapi.get_crossinterp_data(value) + # Check nested as well + value = tuple([0, 1., (s,)]) + with self.subTest("nested " + repr(value)): + # XXX Assert the NotShareableError when it is exported + with self.assertRaises(ValueError): + _testinternalcapi.get_crossinterp_data(value) + + +class ModuleTests(TestBase): + + def test_import_in_interpreter(self): + _run_output( + _interpreters.create(), + 'import _interpreters', + ) + + +################################## +# interpreter tests + +class ListAllTests(TestBase): + + def test_initial(self): + main, *_ = _interpreters.get_main() + ids = [id for id, *_ in _interpreters.list_all()] + self.assertEqual(ids, [main]) + + def test_after_creating(self): + main, *_ = _interpreters.get_main() + first = _interpreters.create() + second = _interpreters.create() + ids = [id for id, *_ in _interpreters.list_all()] + self.assertEqual(ids, [main, first, second]) + + def test_after_destroying(self): + main, *_ = _interpreters.get_main() + first = _interpreters.create() + second = _interpreters.create() + _interpreters.destroy(first) + ids = [id for id, *_ in _interpreters.list_all()] + self.assertEqual(ids, [main, second]) + + +class GetCurrentTests(TestBase): + + def test_main(self): + main, *_ = _interpreters.get_main() + cur, *_ = _interpreters.get_current() + self.assertEqual(cur, main) + self.assertIsInstance(cur, int) + + def test_subinterpreter(self): + main, *_ = _interpreters.get_main() + interp = _interpreters.create() + out = _run_output(interp, dedent(""" + import _interpreters + cur, *_ = _interpreters.get_current() + print(cur) + assert isinstance(cur, int) + """)) + cur = int(out.strip()) + _, expected = [id for id, *_ in _interpreters.list_all()] + self.assertEqual(cur, expected) + self.assertNotEqual(cur, main) + + +class GetMainTests(TestBase): + + def test_from_main(self): + [expected] = [id for id, *_ in _interpreters.list_all()] + main, *_ = _interpreters.get_main() + self.assertEqual(main, expected) + self.assertIsInstance(main, int) + + def test_from_subinterpreter(self): + [expected] = [id for id, *_ in _interpreters.list_all()] + interp = _interpreters.create() + out = _run_output(interp, dedent(""" + import _interpreters + main, *_ = _interpreters.get_main() + print(main) + assert isinstance(main, int) + """)) + main = int(out.strip()) + self.assertEqual(main, expected) + + +class IsRunningTests(TestBase): + + def test_main(self): + main, *_ = _interpreters.get_main() + self.assertTrue(_interpreters.is_running(main)) + + @unittest.skip('Fails on FreeBSD') + def test_subinterpreter(self): + interp = _interpreters.create() + self.assertFalse(_interpreters.is_running(interp)) + + with _running(interp): + self.assertTrue(_interpreters.is_running(interp)) + self.assertFalse(_interpreters.is_running(interp)) + + def test_from_subinterpreter(self): + interp = _interpreters.create() + out = _run_output(interp, dedent(f""" + import _interpreters + if _interpreters.is_running({interp}): + print(True) + else: + print(False) + """)) + self.assertEqual(out.strip(), 'True') + + def test_already_destroyed(self): + interp = _interpreters.create() + _interpreters.destroy(interp) + with self.assertRaises(InterpreterNotFoundError): + _interpreters.is_running(interp) + + def test_does_not_exist(self): + with self.assertRaises(InterpreterNotFoundError): + _interpreters.is_running(1_000_000) + + def test_bad_id(self): + with self.assertRaises(ValueError): + _interpreters.is_running(-1) + + +class CreateTests(TestBase): + + def test_in_main(self): + id = _interpreters.create() + self.assertIsInstance(id, int) + + after = [id for id, *_ in _interpreters.list_all()] + self.assertIn(id, after) + + @unittest.skip('enable this test when working on pystate.c') + def test_unique_id(self): + seen = set() + for _ in range(100): + id = _interpreters.create() + _interpreters.destroy(id) + seen.add(id) + + self.assertEqual(len(seen), 100) + + def test_in_thread(self): + lock = threading.Lock() + id = None + def f(): + nonlocal id + id = _interpreters.create() + lock.acquire() + lock.release() + + t = threading.Thread(target=f) + with lock: + t.start() + t.join() + after = set(id for id, *_ in _interpreters.list_all()) + self.assertIn(id, after) + + def test_in_subinterpreter(self): + main, = [id for id, *_ in _interpreters.list_all()] + id1 = _interpreters.create() + out = _run_output(id1, dedent(""" + import _interpreters + id = _interpreters.create() + print(id) + assert isinstance(id, int) + """)) + id2 = int(out.strip()) + + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, {main, id1, id2}) + + def test_in_threaded_subinterpreter(self): + main, = [id for id, *_ in _interpreters.list_all()] + id1 = _interpreters.create() + id2 = None + def f(): + nonlocal id2 + out = _run_output(id1, dedent(""" + import _interpreters + id = _interpreters.create() + print(id) + """)) + id2 = int(out.strip()) + + t = threading.Thread(target=f) + t.start() + t.join() + + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, {main, id1, id2}) + + def test_after_destroy_all(self): + before = set(id for id, *_ in _interpreters.list_all()) + # Create 3 subinterpreters. + ids = [] + for _ in range(3): + id = _interpreters.create() + ids.append(id) + # Now destroy them. + for id in ids: + _interpreters.destroy(id) + # Finally, create another. + id = _interpreters.create() + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, before | {id}) + + def test_after_destroy_some(self): + before = set(id for id, *_ in _interpreters.list_all()) + # Create 3 subinterpreters. + id1 = _interpreters.create() + id2 = _interpreters.create() + id3 = _interpreters.create() + # Now destroy 2 of them. + _interpreters.destroy(id1) + _interpreters.destroy(id3) + # Finally, create another. + id = _interpreters.create() + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, before | {id, id2}) + + +class DestroyTests(TestBase): + + def test_one(self): + id1 = _interpreters.create() + id2 = _interpreters.create() + id3 = _interpreters.create() + before = set(id for id, *_ in _interpreters.list_all()) + self.assertIn(id2, before) + + _interpreters.destroy(id2) + + after = set(id for id, *_ in _interpreters.list_all()) + self.assertNotIn(id2, after) + self.assertIn(id1, after) + self.assertIn(id3, after) + + def test_all(self): + initial = set(id for id, *_ in _interpreters.list_all()) + ids = set() + for _ in range(3): + id = _interpreters.create() + ids.add(id) + before = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(before, initial | ids) + for id in ids: + _interpreters.destroy(id) + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, initial) + + def test_main(self): + main, = [id for id, *_ in _interpreters.list_all()] + with self.assertRaises(_interpreters.InterpreterError): + _interpreters.destroy(main) + + def f(): + with self.assertRaises(_interpreters.InterpreterError): + _interpreters.destroy(main) + + t = threading.Thread(target=f) + t.start() + t.join() + + def test_already_destroyed(self): + id = _interpreters.create() + _interpreters.destroy(id) + with self.assertRaises(InterpreterNotFoundError): + _interpreters.destroy(id) + + def test_does_not_exist(self): + with self.assertRaises(InterpreterNotFoundError): + _interpreters.destroy(1_000_000) + + def test_bad_id(self): + with self.assertRaises(ValueError): + _interpreters.destroy(-1) + + def test_from_current(self): + main, = [id for id, *_ in _interpreters.list_all()] + id = _interpreters.create() + script = dedent(f""" + import _interpreters + try: + _interpreters.destroy({id}) + except _interpreters.InterpreterError: + pass + """) + + _interpreters.run_string(id, script) + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, {main, id}) + + def test_from_sibling(self): + main, = [id for id, *_ in _interpreters.list_all()] + id1 = _interpreters.create() + id2 = _interpreters.create() + script = dedent(f""" + import _interpreters + _interpreters.destroy({id2}) + """) + _interpreters.run_string(id1, script) + + after = set(id for id, *_ in _interpreters.list_all()) + self.assertEqual(after, {main, id1}) + + def test_from_other_thread(self): + id = _interpreters.create() + def f(): + _interpreters.destroy(id) + + t = threading.Thread(target=f) + t.start() + t.join() + + def test_still_running(self): + main, = [id for id, *_ in _interpreters.list_all()] + interp = _interpreters.create() + with _running(interp): + self.assertTrue(_interpreters.is_running(interp), + msg=f"Interp {interp} should be running before destruction.") + + with self.assertRaises(_interpreters.InterpreterError, + msg=f"Should not be able to destroy interp {interp} while it's still running."): + _interpreters.destroy(interp) + self.assertTrue(_interpreters.is_running(interp)) + + +class CommonTests(TestBase): + def setUp(self): + super().setUp() + self.id = _interpreters.create() + + def test_signatures(self): + # See https://github.com/python/cpython/issues/126654 + msg = "expected 'shared' to be a dict" + with self.assertRaisesRegex(TypeError, msg): + _interpreters.exec(self.id, 'a', 1) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.exec(self.id, 'a', shared=1) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_string(self.id, 'a', shared=1) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_func(self.id, lambda: None, shared=1) + # See https://github.com/python/cpython/issues/135855 + msg = r'_interpreters.set___main___attrs\(\) argument 2 must be dict, not int' + with self.assertRaisesRegex(TypeError, msg): + _interpreters.set___main___attrs(self.id, 1) + + def test_invalid_shared_none(self): + msg = "expected 'shared' to be a dict" + with self.assertRaisesRegex(TypeError, msg): + _interpreters.exec(self.id, 'a', shared=None) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_string(self.id, 'a', shared=None) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_func(self.id, lambda: None, shared=None) + msg = "must be dict, not None" + with self.assertRaisesRegex(TypeError, msg): + _interpreters.set___main___attrs(self.id, None) + + def test_invalid_shared_encoding(self): + # See https://github.com/python/cpython/issues/127196 + bad_shared = {"\uD82A": 0} + msg = 'surrogates not allowed' + with self.assertRaisesRegex(UnicodeEncodeError, msg): + _interpreters.exec(self.id, 'a', shared=bad_shared) + with self.assertRaisesRegex(UnicodeEncodeError, msg): + _interpreters.run_string(self.id, 'a', shared=bad_shared) + with self.assertRaisesRegex(UnicodeEncodeError, msg): + _interpreters.run_func(self.id, lambda: None, shared=bad_shared) + + +class RunStringTests(TestBase): + + def setUp(self): + super().setUp() + self.id = _interpreters.create() + + def test_success(self): + script, file = _captured_script('print("it worked!", end="")') + with file: + _interpreters.run_string(self.id, script) + out = file.read() + + self.assertEqual(out, 'it worked!') + + def test_in_thread(self): + script, file = _captured_script('print("it worked!", end="")') + with file: + def f(): + _interpreters.run_string(self.id, script) + + t = threading.Thread(target=f) + t.start() + t.join() + out = file.read() + + self.assertEqual(out, 'it worked!') + + def test_create_thread(self): + subinterp = _interpreters.create() + script, file = _captured_script(""" + import threading + def f(): + print('it worked!', end='') + + t = threading.Thread(target=f) + t.start() + t.join() + """) + with file: + _interpreters.run_string(subinterp, script) + out = file.read() + + self.assertEqual(out, 'it worked!') + + def test_create_daemon_thread(self): + with self.subTest('isolated'): + expected = 'spam spam spam spam spam' + subinterp = _interpreters.create('isolated') + script, file = _captured_script(f""" + import threading + def f(): + print('it worked!', end='') + + try: + t = threading.Thread(target=f, daemon=True) + t.start() + t.join() + except RuntimeError: + print('{expected}', end='') + """) + with file: + _interpreters.run_string(subinterp, script) + out = file.read() + + self.assertEqual(out, expected) + + with self.subTest('not isolated'): + subinterp = _interpreters.create('legacy') + script, file = _captured_script(""" + import threading + def f(): + print('it worked!', end='') + + t = threading.Thread(target=f, daemon=True) + t.start() + t.join() + """) + with file: + _interpreters.run_string(subinterp, script) + out = file.read() + + self.assertEqual(out, 'it worked!') + + def test_shareable_types(self): + interp = _interpreters.create() + objects = [ + None, + 'spam', + b'spam', + 42, + ] + for obj in objects: + with self.subTest(obj): + _interpreters.set___main___attrs(interp, dict(obj=obj)) + _interpreters.run_string( + interp, + f'assert(obj == {obj!r})', + ) + + def test_os_exec(self): + expected = 'spam spam spam spam spam' + subinterp = _interpreters.create() + script, file = _captured_script(f""" + import os, sys + try: + os.execl(sys.executable) + except RuntimeError: + print('{expected}', end='') + """) + with file: + _interpreters.run_string(subinterp, script) + out = file.read() + + self.assertEqual(out, expected) + + @support.requires_fork() + def test_fork(self): + import tempfile + with tempfile.NamedTemporaryFile('w+', encoding="utf-8") as file: + file.write('') + file.flush() + + expected = 'spam spam spam spam spam' + script = dedent(f""" + import os + try: + os.fork() + except RuntimeError: + with open('{file.name}', 'w', encoding='utf-8') as out: + out.write('{expected}') + """) + _interpreters.run_string(self.id, script) + + file.seek(0) + content = file.read() + self.assertEqual(content, expected) + + def test_already_running(self): + with _running(self.id): + with self.assertRaises(_interpreters.InterpreterError): + _interpreters.run_string(self.id, 'print("spam")') + + def test_does_not_exist(self): + id = 0 + while id in set(id for id, *_ in _interpreters.list_all()): + id += 1 + with self.assertRaises(InterpreterNotFoundError): + _interpreters.run_string(id, 'print("spam")') + + def test_error_id(self): + with self.assertRaises(ValueError): + _interpreters.run_string(-1, 'print("spam")') + + def test_bad_id(self): + with self.assertRaises(TypeError): + _interpreters.run_string('spam', 'print("spam")') + + def test_bad_script(self): + with self.assertRaises(TypeError): + _interpreters.run_string(self.id, 10) + + def test_bytes_for_script(self): + with self.assertRaises(TypeError): + _interpreters.run_string(self.id, b'print("spam")') + + def test_str_subclass_string(self): + class StrSubclass(str): pass + + output = _run_output(self.id, StrSubclass('print(1 + 2)')) + self.assertEqual(output, '3\n') + + def test_with_shared(self): + r, w = os.pipe() + + shared = { + 'spam': b'ham', + 'eggs': b'-1', + 'cheddar': None, + } + script = dedent(f""" + eggs = int(eggs) + spam = 42 + result = spam + eggs + + ns = dict(vars()) + del ns['__builtins__'] + import pickle + with open({w}, 'wb') as chan: + pickle.dump(ns, chan) + """) + _interpreters.set___main___attrs(self.id, shared) + _interpreters.run_string(self.id, script) + with open(r, 'rb') as chan: + ns = pickle.load(chan) + + self.assertEqual(ns['spam'], 42) + self.assertEqual(ns['eggs'], -1) + self.assertEqual(ns['result'], 41) + self.assertIsNone(ns['cheddar']) + + def test_shared_overwrites(self): + _interpreters.run_string(self.id, dedent(""" + spam = 'eggs' + ns1 = dict(vars()) + del ns1['__builtins__'] + """)) + + shared = {'spam': b'ham'} + script = dedent(""" + ns2 = dict(vars()) + del ns2['__builtins__'] + """) + _interpreters.set___main___attrs(self.id, shared) + _interpreters.run_string(self.id, script) + + r, w = os.pipe() + script = dedent(f""" + ns = dict(vars()) + del ns['__builtins__'] + import pickle + with open({w}, 'wb') as chan: + pickle.dump(ns, chan) + """) + _interpreters.run_string(self.id, script) + with open(r, 'rb') as chan: + ns = pickle.load(chan) + + self.assertEqual(ns['ns1']['spam'], 'eggs') + self.assertEqual(ns['ns2']['spam'], b'ham') + self.assertEqual(ns['spam'], b'ham') + + def test_shared_overwrites_default_vars(self): + r, w = os.pipe() + + shared = {'__name__': b'not __main__'} + script = dedent(f""" + spam = 42 + + ns = dict(vars()) + del ns['__builtins__'] + import pickle + with open({w}, 'wb') as chan: + pickle.dump(ns, chan) + """) + _interpreters.set___main___attrs(self.id, shared) + _interpreters.run_string(self.id, script) + with open(r, 'rb') as chan: + ns = pickle.load(chan) + + self.assertEqual(ns['__name__'], b'not __main__') + + def test_main_reused(self): + r, w = os.pipe() + _interpreters.run_string(self.id, dedent(f""" + spam = True + + ns = dict(vars()) + del ns['__builtins__'] + import pickle + with open({w}, 'wb') as chan: + pickle.dump(ns, chan) + del ns, pickle, chan + """)) + with open(r, 'rb') as chan: + ns1 = pickle.load(chan) + + r, w = os.pipe() + _interpreters.run_string(self.id, dedent(f""" + eggs = False + + ns = dict(vars()) + del ns['__builtins__'] + import pickle + with open({w}, 'wb') as chan: + pickle.dump(ns, chan) + """)) + with open(r, 'rb') as chan: + ns2 = pickle.load(chan) + + self.assertIn('spam', ns1) + self.assertNotIn('eggs', ns1) + self.assertIn('eggs', ns2) + self.assertIn('spam', ns2) + + def test_execution_namespace_is_main(self): + r, w = os.pipe() + + script = dedent(f""" + spam = 42 + + ns = dict(vars()) + ns['__builtins__'] = str(ns['__builtins__']) + import pickle + with open({w}, 'wb') as chan: + pickle.dump(ns, chan) + """) + _interpreters.run_string(self.id, script) + with open(r, 'rb') as chan: + ns = pickle.load(chan) + + ns.pop('__builtins__') + ns.pop('__loader__') + self.assertEqual(ns, { + '__name__': '__main__', + '__annotations__': {}, + '__doc__': None, + '__package__': None, + '__spec__': None, + 'spam': 42, + }) + + # XXX Fix this test! + @unittest.skip('blocking forever') + def test_still_running_at_exit(self): + script = dedent(""" + from textwrap import dedent + import threading + import _interpreters + id = _interpreters.create() + def f(): + _interpreters.run_string(id, dedent(''' + import time + # Give plenty of time for the main interpreter to finish. + time.sleep(1_000_000) + ''')) + + t = threading.Thread(target=f) + t.start() + """) + with support.temp_dir() as dirname: + filename = script_helper.make_script(dirname, 'interp', script) + with script_helper.spawn_python(filename) as proc: + retcode = proc.wait() + + self.assertEqual(retcode, 0) + + +class RunFailedTests(TestBase): + + def setUp(self): + super().setUp() + self.id = _interpreters.create() + + def add_module(self, modname, text): + import tempfile + tempdir = tempfile.mkdtemp() + self.addCleanup(lambda: os_helper.rmtree(tempdir)) + _interpreters.run_string(self.id, dedent(f""" + import sys + sys.path.insert(0, {tempdir!r}) + """)) + return script_helper.make_script(tempdir, modname, text) + + def run_script(self, text, *, fails=False): + r, w = os.pipe() + try: + script = dedent(f""" + import os, sys + os.write({w}, b'0') + + # This raises an exception: + {{}} + + # Nothing from here down should ever run. + os.write({w}, b'1') + class NeverError(Exception): pass + raise NeverError # never raised + """).format(dedent(text)) + if fails: + err = _interpreters.run_string(self.id, script) + self.assertIsNot(err, None) + return err + else: + err = _interpreters.run_string(self.id, script) + self.assertIs(err, None) + return None + except: + raise # re-raise + else: + msg = os.read(r, 100) + self.assertEqual(msg, b'0') + finally: + os.close(r) + os.close(w) + + def _assert_run_failed(self, exctype, msg, script): + if isinstance(exctype, str): + exctype_name = exctype + exctype = None + else: + exctype_name = exctype.__name__ + + # Run the script. + excinfo = self.run_script(script, fails=True) + + # Check the wrapper exception. + self.assertEqual(excinfo.type.__name__, exctype_name) + if msg is None: + self.assertEqual(excinfo.formatted.split(':')[0], + exctype_name) + else: + self.assertEqual(excinfo.formatted, + '{}: {}'.format(exctype_name, msg)) + + return excinfo + + def assert_run_failed(self, exctype, script): + self._assert_run_failed(exctype, None, script) + + def assert_run_failed_msg(self, exctype, msg, script): + self._assert_run_failed(exctype, msg, script) + + def test_exit(self): + with self.subTest('sys.exit(0)'): + # XXX Should an unhandled SystemExit(0) be handled as not-an-error? + self.assert_run_failed(SystemExit, """ + sys.exit(0) + """) + + with self.subTest('sys.exit()'): + self.assert_run_failed(SystemExit, """ + import sys + sys.exit() + """) + + with self.subTest('sys.exit(42)'): + self.assert_run_failed_msg(SystemExit, '42', """ + import sys + sys.exit(42) + """) + + with self.subTest('SystemExit'): + self.assert_run_failed_msg(SystemExit, '42', """ + raise SystemExit(42) + """) + + # XXX Also check os._exit() (via a subprocess)? + + def test_plain_exception(self): + self.assert_run_failed_msg(Exception, 'spam', """ + raise Exception("spam") + """) + + def test_invalid_syntax(self): + script = dedent(""" + x = 1 + 2 + y = 2 + 4 + z = 4 + 8 + + # missing close paren + print("spam" + + if x + y + z < 20: + ... + """) + + with self.subTest('script'): + self.assert_run_failed(SyntaxError, script) + + with self.subTest('module'): + modname = 'spam_spam_spam' + filename = self.add_module(modname, script) + self.assert_run_failed(SyntaxError, f""" + import {modname} + """) + + def test_NameError(self): + self.assert_run_failed(NameError, """ + res = spam + eggs + """) + # XXX check preserved suggestions + + def test_AttributeError(self): + self.assert_run_failed(AttributeError, """ + object().spam + """) + # XXX check preserved suggestions + + def test_ExceptionGroup(self): + self.assert_run_failed(ExceptionGroup, """ + raise ExceptionGroup('exceptions', [ + Exception('spam'), + ImportError('eggs'), + ]) + """) + + def test_user_defined_exception(self): + self.assert_run_failed_msg('MyError', 'spam', """ + class MyError(Exception): + pass + raise MyError('spam') + """) + + +class RunFuncTests(TestBase): + + def setUp(self): + super().setUp() + self.id = _interpreters.create() + + def test_success(self): + r, w = os.pipe() + def script(): + global w + import contextlib + with open(w, 'w', encoding="utf-8") as spipe: + with contextlib.redirect_stdout(spipe): + print('it worked!', end='') + _interpreters.set___main___attrs(self.id, dict(w=w)) + _interpreters.run_func(self.id, script) + + with open(r, encoding="utf-8") as outfile: + out = outfile.read() + + self.assertEqual(out, 'it worked!') + + def test_in_thread(self): + r, w = os.pipe() + def script(): + global w + import contextlib + with open(w, 'w', encoding="utf-8") as spipe: + with contextlib.redirect_stdout(spipe): + print('it worked!', end='') + def f(): + _interpreters.set___main___attrs(self.id, dict(w=w)) + _interpreters.run_func(self.id, script) + t = threading.Thread(target=f) + t.start() + t.join() + + with open(r, encoding="utf-8") as outfile: + out = outfile.read() + + self.assertEqual(out, 'it worked!') + + def test_code_object(self): + r, w = os.pipe() + + def script(): + global w + import contextlib + with open(w, 'w', encoding="utf-8") as spipe: + with contextlib.redirect_stdout(spipe): + print('it worked!', end='') + code = script.__code__ + _interpreters.set___main___attrs(self.id, dict(w=w)) + _interpreters.run_func(self.id, code) + + with open(r, encoding="utf-8") as outfile: + out = outfile.read() + + self.assertEqual(out, 'it worked!') + + def test_closure(self): + spam = True + def script(): + assert spam + + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + # XXX This hasn't been fixed yet. + @unittest.expectedFailure + def test_return_value(self): + def script(): + return 'spam' + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + def test_args(self): + with self.subTest('args'): + def script(a, b=0): + assert a == b + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + with self.subTest('*args'): + def script(*args): + assert not args + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + with self.subTest('**kwargs'): + def script(**kwargs): + assert not kwargs + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + with self.subTest('kwonly'): + def script(*, spam=True): + assert spam + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + with self.subTest('posonly'): + def script(spam, /): + assert spam + with self.assertRaises(ValueError): + _interpreters.run_func(self.id, script) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py new file mode 100644 index 00000000000..606dd297452 --- /dev/null +++ b/Lib/test/test__locale.py @@ -0,0 +1,310 @@ +from _locale import (setlocale, LC_ALL, LC_CTYPE, LC_NUMERIC, LC_TIME, localeconv, Error) +try: + from _locale import (RADIXCHAR, THOUSEP, nl_langinfo) +except ImportError: + nl_langinfo = None + +import locale +import sys +import unittest +from platform import uname + +from test import support + +if uname().system == "Darwin": + maj, min, mic = [int(part) for part in uname().release.split(".")] + if (maj, min, mic) < (8, 0, 0): + raise unittest.SkipTest("locale support broken for OS X < 10.4") + +candidate_locales = ['es_UY', 'fr_FR', 'fi_FI', 'es_CO', 'pt_PT', 'it_IT', + 'et_EE', 'es_PY', 'no_NO', 'nl_NL', 'lv_LV', 'el_GR', 'be_BY', 'fr_BE', + 'ro_RO', 'ru_UA', 'ru_RU', 'es_VE', 'ca_ES', 'se_NO', 'es_EC', 'id_ID', + 'ka_GE', 'es_CL', 'wa_BE', 'hu_HU', 'lt_LT', 'sl_SI', 'hr_HR', 'es_AR', + 'es_ES', 'oc_FR', 'gl_ES', 'bg_BG', 'is_IS', 'mk_MK', 'de_AT', 'pt_BR', + 'da_DK', 'nn_NO', 'cs_CZ', 'de_LU', 'es_BO', 'sq_AL', 'sk_SK', 'fr_CH', + 'de_DE', 'sr_YU', 'br_FR', 'nl_BE', 'sv_FI', 'pl_PL', 'fr_CA', 'fo_FO', + 'bs_BA', 'fr_LU', 'kl_GL', 'fa_IR', 'de_BE', 'sv_SE', 'it_CH', 'uk_UA', + 'eu_ES', 'vi_VN', 'af_ZA', 'nb_NO', 'en_DK', 'tg_TJ', 'ps_AF', 'en_US', + 'fr_FR.ISO8859-1', 'fr_FR.UTF-8', 'fr_FR.ISO8859-15@euro', + 'ru_RU.KOI8-R', 'ko_KR.eucKR', + 'ja_JP.UTF-8', 'lzh_TW.UTF-8', 'my_MM.UTF-8', 'or_IN.UTF-8', 'shn_MM.UTF-8', + 'ar_AE.UTF-8', 'bn_IN.UTF-8', 'mr_IN.UTF-8', 'th_TH.TIS620', +] + +def setUpModule(): + global candidate_locales + # Issue #13441: Skip some locales (e.g. cs_CZ and hu_HU) on Solaris to + # workaround a mbstowcs() bug. For example, on Solaris, the hu_HU locale uses + # the locale encoding ISO-8859-2, the thousands separator is b'\xA0' and it is + # decoded as U+30000020 (an invalid character) by mbstowcs(). + if sys.platform == 'sunos5': + old_locale = locale.setlocale(locale.LC_ALL) + try: + locales = [] + for loc in candidate_locales: + try: + locale.setlocale(locale.LC_ALL, loc) + except Error: + continue + encoding = locale.getencoding() + try: + localeconv() + except Exception as err: + print("WARNING: Skip locale %s (encoding %s): [%s] %s" + % (loc, encoding, type(err), err)) + else: + locales.append(loc) + candidate_locales = locales + finally: + locale.setlocale(locale.LC_ALL, old_locale) + + # Workaround for MSVC6(debug) crash bug + if "MSC v.1200" in sys.version: + def accept(loc): + a = loc.split(".") + return not(len(a) == 2 and len(a[-1]) >= 9) + candidate_locales = [loc for loc in candidate_locales if accept(loc)] + +# List known locale values to test against when available. +# Dict formatted as `` : (, )``. If a +# value is not known, use '' . +known_numerics = { + 'en_US': ('.', ','), + 'de_DE' : (',', '.'), + # The French thousands separator may be a breaking or non-breaking space + # depending on the platform, so do not test it + 'fr_FR' : (',', ''), + 'ps_AF': ('\u066b', '\u066c'), +} + +known_alt_digits = { + 'C': (0, {}), + 'en_US': (0, {}), + 'fa_IR': (100, {0: '\u06f0\u06f0', 10: '\u06f1\u06f0', 99: '\u06f9\u06f9'}), + 'ja_JP': (100, {1: '\u4e00', 10: '\u5341', 99: '\u4e5d\u5341\u4e5d'}), + 'lzh_TW': (32, {0: '\u3007', 10: '\u5341', 31: '\u5345\u4e00'}), + 'my_MM': (100, {0: '\u1040\u1040', 10: '\u1041\u1040', 99: '\u1049\u1049'}), + 'or_IN': (100, {0: '\u0b66', 10: '\u0b67\u0b66', 99: '\u0b6f\u0b6f'}), + 'shn_MM': (100, {0: '\u1090\u1090', 10: '\u1091\u1090', 99: '\u1099\u1099'}), + 'ar_AE': (100, {0: '\u0660', 10: '\u0661\u0660', 99: '\u0669\u0669'}), + 'bn_IN': (100, {0: '\u09e6', 10: '\u09e7\u09e6', 99: '\u09ef\u09ef'}), +} + +known_era = { + 'C': (0, ''), + 'en_US': (0, ''), + 'ja_JP': (11, '+:1:2019/05/01:2019/12/31:令和:%EC元年'), + 'zh_TW': (3, '+:1:1912/01/01:1912/12/31:民國:%EC元年'), + 'th_TW': (1, '+:1:-543/01/01:+*:พ.ศ.:%EC %Ey'), +} + +if sys.platform == 'win32': + # ps_AF doesn't work on Windows: see bpo-38324 (msg361830) + del known_numerics['ps_AF'] + +if sys.platform == 'sunos5': + # On Solaris, Japanese ERAs start with the year 1927, + # and thus there's less of them. + known_era['ja_JP'] = (5, '+:1:2019/05/01:2019/12/31:令和:%EC元年') + +class _LocaleTests(unittest.TestCase): + + def setUp(self): + self.oldlocale = setlocale(LC_ALL) + + def tearDown(self): + setlocale(LC_ALL, self.oldlocale) + + # Want to know what value was calculated, what it was compared against, + # what function was used for the calculation, what type of data was used, + # the locale that was supposedly set, and the actual locale that is set. + lc_numeric_err_msg = "%s != %s (%s for %s; set to %s, using %s)" + + def numeric_tester(self, calc_type, calc_value, data_type, used_locale): + """Compare calculation against known value, if available""" + try: + set_locale = setlocale(LC_NUMERIC) + except Error: + set_locale = "" + known_value = known_numerics.get(used_locale, + ('', ''))[data_type == 'thousands_sep'] + if known_value and calc_value: + self.assertEqual(calc_value, known_value, + self.lc_numeric_err_msg % ( + calc_value, known_value, + calc_type, data_type, set_locale, + used_locale)) + return True + + @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available") + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "musl libc issue on Emscripten, bpo-46390" + ) + def test_lc_numeric_nl_langinfo(self): + # Test nl_langinfo against known values + tested = False + for loc in candidate_locales: + try: + setlocale(LC_NUMERIC, loc) + setlocale(LC_CTYPE, loc) + except Error: + continue + for li, lc in ((RADIXCHAR, "decimal_point"), + (THOUSEP, "thousands_sep")): + if self.numeric_tester('nl_langinfo', nl_langinfo(li), lc, loc): + tested = True + if not tested: + self.skipTest('no suitable locales') + + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "musl libc issue on Emscripten, bpo-46390" + ) + def test_lc_numeric_localeconv(self): + # Test localeconv against known values + tested = False + for loc in candidate_locales: + try: + setlocale(LC_NUMERIC, loc) + setlocale(LC_CTYPE, loc) + except Error: + continue + formatting = localeconv() + for lc in ("decimal_point", + "thousands_sep"): + if self.numeric_tester('localeconv', formatting[lc], lc, loc): + tested = True + if not tested: + self.skipTest('no suitable locales') + + @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available") + def test_lc_numeric_basic(self): + # Test nl_langinfo against localeconv + tested = False + for loc in candidate_locales: + try: + setlocale(LC_NUMERIC, loc) + setlocale(LC_CTYPE, loc) + except Error: + continue + for li, lc in ((RADIXCHAR, "decimal_point"), + (THOUSEP, "thousands_sep")): + nl_radixchar = nl_langinfo(li) + li_radixchar = localeconv()[lc] + try: + set_locale = setlocale(LC_NUMERIC) + except Error: + set_locale = "" + self.assertEqual(nl_radixchar, li_radixchar, + "%s (nl_langinfo) != %s (localeconv) " + "(set to %s, using %s)" % ( + nl_radixchar, li_radixchar, + loc, set_locale)) + tested = True + if not tested: + self.skipTest('no suitable locales') + + @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available") + @unittest.skipUnless(hasattr(locale, 'ALT_DIGITS'), "requires locale.ALT_DIGITS") + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "musl libc issue on Emscripten, bpo-46390" + ) + def test_alt_digits_nl_langinfo(self): + # Test nl_langinfo(ALT_DIGITS) + tested = False + for loc in candidate_locales: + with self.subTest(locale=loc): + try: + setlocale(LC_TIME, loc) + setlocale(LC_CTYPE, loc) + except Error: + self.skipTest(f'no locale {loc!r}') + continue + + with self.subTest(locale=loc): + alt_digits = nl_langinfo(locale.ALT_DIGITS) + self.assertIsInstance(alt_digits, str) + alt_digits = alt_digits.split(';') if alt_digits else [] + if alt_digits: + self.assertGreaterEqual(len(alt_digits), 10, alt_digits) + loc1 = loc.split('.', 1)[0] + if loc1 in known_alt_digits: + count, samples = known_alt_digits[loc1] + if count and not alt_digits: + self.skipTest(f'ALT_DIGITS is not set for locale {loc!r} on this platform') + self.assertEqual(len(alt_digits), count, alt_digits) + for i in samples: + self.assertEqual(alt_digits[i], samples[i]) + tested = True + if not tested: + self.skipTest('no suitable locales') + + @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available") + @unittest.skipUnless(hasattr(locale, 'ERA'), "requires locale.ERA") + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "musl libc issue on Emscripten, bpo-46390" + ) + def test_era_nl_langinfo(self): + # Test nl_langinfo(ERA) + tested = False + for loc in candidate_locales: + with self.subTest(locale=loc): + try: + setlocale(LC_TIME, loc) + setlocale(LC_CTYPE, loc) + except Error: + self.skipTest(f'no locale {loc!r}') + continue + + with self.subTest(locale=loc): + era = nl_langinfo(locale.ERA) + self.assertIsInstance(era, str) + if era: + self.assertEqual(era.count(':'), (era.count(';') + 1) * 5, era) + + loc1 = loc.split('.', 1)[0] + if loc1 in known_era: + count, sample = known_era[loc1] + if count: + if not era: + self.skipTest(f'ERA is not set for locale {loc!r} on this platform') + self.assertGreaterEqual(era.count(';') + 1, count) + self.assertIn(sample, era) + else: + self.assertEqual(era, '') + tested = True + if not tested: + self.skipTest('no suitable locales') + + def test_float_parsing(self): + # Bug #1391872: Test whether float parsing is okay on European + # locales. + tested = False + for loc in candidate_locales: + try: + setlocale(LC_NUMERIC, loc) + setlocale(LC_CTYPE, loc) + except Error: + continue + + # Ignore buggy locale databases. (Mac OS 10.4 and some other BSDs) + if loc == 'eu_ES' and localeconv()['decimal_point'] == "' ": + continue + + self.assertEqual(int(eval('3.14') * 100), 314, + "using eval('3.14') failed for %s" % loc) + self.assertEqual(int(float('3.14') * 100), 314, + "using float('3.14') failed for %s" % loc) + if localeconv()['decimal_point'] != '.': + self.assertRaises(ValueError, float, + localeconv()['decimal_point'].join(['1', '23'])) + tested = True + if not tested: + self.skipTest('no suitable locales') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_apple.py b/Lib/test/test_apple.py new file mode 100644 index 00000000000..f14db75e2f2 --- /dev/null +++ b/Lib/test/test_apple.py @@ -0,0 +1,155 @@ +import unittest +from _apple_support import SystemLog +from test.support import is_apple_mobile +from unittest.mock import Mock, call + +if not is_apple_mobile: + raise unittest.SkipTest("iOS-specific") + + +# Test redirection of stdout and stderr to the Apple system log. +class TestAppleSystemLogOutput(unittest.TestCase): + maxDiff = None + + def assert_writes(self, output): + self.assertEqual( + self.log_write.mock_calls, + [ + call(self.log_level, line) + for line in output + ] + ) + + self.log_write.reset_mock() + + def setUp(self): + self.log_write = Mock() + self.log_level = 42 + self.log = SystemLog(self.log_write, self.log_level, errors="replace") + + def test_repr(self): + self.assertEqual(repr(self.log), "") + self.assertEqual(repr(self.log.buffer), "") + + def test_log_config(self): + self.assertIs(self.log.writable(), True) + self.assertIs(self.log.readable(), False) + + self.assertEqual("UTF-8", self.log.encoding) + self.assertEqual("replace", self.log.errors) + + self.assertIs(self.log.line_buffering, True) + self.assertIs(self.log.write_through, False) + + def test_empty_str(self): + self.log.write("") + self.log.flush() + + self.assert_writes([]) + + def test_simple_str(self): + self.log.write("hello world\n") + + self.assert_writes([b"hello world\n"]) + + def test_buffered_str(self): + self.log.write("h") + self.log.write("ello") + self.log.write(" ") + self.log.write("world\n") + self.log.write("goodbye.") + self.log.flush() + + self.assert_writes([b"hello world\n", b"goodbye."]) + + def test_manual_flush(self): + self.log.write("Hello") + + self.assert_writes([]) + + self.log.write(" world\nHere for a while...\nGoodbye") + self.assert_writes([b"Hello world\n", b"Here for a while...\n"]) + + self.log.write(" world\nHello again") + self.assert_writes([b"Goodbye world\n"]) + + self.log.flush() + self.assert_writes([b"Hello again"]) + + def test_non_ascii(self): + # Spanish + self.log.write("ol\u00e9\n") + self.assert_writes([b"ol\xc3\xa9\n"]) + + # Chinese + self.log.write("\u4e2d\u6587\n") + self.assert_writes([b"\xe4\xb8\xad\xe6\x96\x87\n"]) + + # Printing Non-BMP emoji + self.log.write("\U0001f600\n") + self.assert_writes([b"\xf0\x9f\x98\x80\n"]) + + # Non-encodable surrogates are replaced + self.log.write("\ud800\udc00\n") + self.assert_writes([b"??\n"]) + + def test_modified_null(self): + # Null characters are logged using "modified UTF-8". + self.log.write("\u0000\n") + self.assert_writes([b"\xc0\x80\n"]) + self.log.write("a\u0000\n") + self.assert_writes([b"a\xc0\x80\n"]) + self.log.write("\u0000b\n") + self.assert_writes([b"\xc0\x80b\n"]) + self.log.write("a\u0000b\n") + self.assert_writes([b"a\xc0\x80b\n"]) + + def test_nonstandard_str(self): + # String subclasses are accepted, but they should be converted + # to a standard str without calling any of their methods. + class CustomStr(str): + def splitlines(self, *args, **kwargs): + raise AssertionError() + + def __len__(self): + raise AssertionError() + + def __str__(self): + raise AssertionError() + + self.log.write(CustomStr("custom\n")) + self.assert_writes([b"custom\n"]) + + def test_non_str(self): + # Non-string classes are not accepted. + for obj in [b"", b"hello", None, 42]: + with self.subTest(obj=obj): + with self.assertRaisesRegex( + TypeError, + fr"write\(\) argument must be str, not " + fr"{type(obj).__name__}" + ): + self.log.write(obj) + + def test_byteslike_in_buffer(self): + # The underlying buffer *can* accept bytes-like objects + self.log.buffer.write(bytearray(b"hello")) + self.log.flush() + + self.log.buffer.write(b"") + self.log.flush() + + self.log.buffer.write(b"goodbye") + self.log.flush() + + self.assert_writes([b"hello", b"goodbye"]) + + def test_non_byteslike_in_buffer(self): + for obj in ["hello", None, 42]: + with self.subTest(obj=obj): + with self.assertRaisesRegex( + TypeError, + fr"write\(\) argument must be bytes-like, not " + fr"{type(obj).__name__}" + ): + self.log.buffer.write(obj) diff --git a/Lib/test/test_asdl_parser.py b/Lib/test/test_asdl_parser.py new file mode 100644 index 00000000000..2c198a6b8b2 --- /dev/null +++ b/Lib/test/test_asdl_parser.py @@ -0,0 +1,131 @@ +"""Tests for the asdl parser in Parser/asdl.py""" + +import importlib.machinery +import importlib.util +import os +from os.path import dirname +import sys +import sysconfig +import unittest + + +# This test is only relevant for from-source builds of Python. +if not sysconfig.is_python_build(): + raise unittest.SkipTest('test irrelevant for an installed Python') + +src_base = dirname(dirname(dirname(__file__))) +parser_dir = os.path.join(src_base, 'Parser') + + +class TestAsdlParser(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Loads the asdl module dynamically, since it's not in a real importable + # package. + # Parses Python.asdl into an ast.Module and run the check on it. + # There's no need to do this for each test method, hence setUpClass. + sys.path.insert(0, parser_dir) + loader = importlib.machinery.SourceFileLoader( + 'asdl', os.path.join(parser_dir, 'asdl.py')) + spec = importlib.util.spec_from_loader('asdl', loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + cls.asdl = module + cls.mod = cls.asdl.parse(os.path.join(parser_dir, 'Python.asdl')) + cls.assertTrue(cls.asdl.check(cls.mod), 'Module validation failed') + + @classmethod + def tearDownClass(cls): + del sys.path[0] + + def setUp(self): + # alias stuff from the class, for convenience + self.asdl = TestAsdlParser.asdl + self.mod = TestAsdlParser.mod + self.types = self.mod.types + + def test_module(self): + self.assertEqual(self.mod.name, 'Python') + self.assertIn('stmt', self.types) + self.assertIn('expr', self.types) + self.assertIn('mod', self.types) + + def test_definitions(self): + defs = self.mod.dfns + self.assertIsInstance(defs[0], self.asdl.Type) + self.assertIsInstance(defs[0].value, self.asdl.Sum) + + self.assertIsInstance(self.types['withitem'], self.asdl.Product) + self.assertIsInstance(self.types['alias'], self.asdl.Product) + + def test_product(self): + alias = self.types['alias'] + self.assertEqual( + str(alias), + 'Product([Field(identifier, name), Field(identifier, asname, opt=True)], ' + '[Field(int, lineno), Field(int, col_offset), ' + 'Field(int, end_lineno, opt=True), Field(int, end_col_offset, opt=True)])') + + def test_attributes(self): + stmt = self.types['stmt'] + self.assertEqual(len(stmt.attributes), 4) + self.assertEqual(repr(stmt.attributes[0]), 'Field(int, lineno)') + self.assertEqual(repr(stmt.attributes[1]), 'Field(int, col_offset)') + self.assertEqual(repr(stmt.attributes[2]), 'Field(int, end_lineno, opt=True)') + self.assertEqual(repr(stmt.attributes[3]), 'Field(int, end_col_offset, opt=True)') + + def test_constructor_fields(self): + ehandler = self.types['excepthandler'] + self.assertEqual(len(ehandler.types), 1) + self.assertEqual(len(ehandler.attributes), 4) + + cons = ehandler.types[0] + self.assertIsInstance(cons, self.asdl.Constructor) + self.assertEqual(len(cons.fields), 3) + + f0 = cons.fields[0] + self.assertEqual(f0.type, 'expr') + self.assertEqual(f0.name, 'type') + self.assertTrue(f0.opt) + + f1 = cons.fields[1] + self.assertEqual(f1.type, 'identifier') + self.assertEqual(f1.name, 'name') + self.assertTrue(f1.opt) + + f2 = cons.fields[2] + self.assertEqual(f2.type, 'stmt') + self.assertEqual(f2.name, 'body') + self.assertFalse(f2.opt) + self.assertTrue(f2.seq) + + def test_visitor(self): + class CustomVisitor(self.asdl.VisitorBase): + def __init__(self): + super().__init__() + self.names_with_seq = [] + + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type): + self.visit(type.value) + + def visitSum(self, sum): + for t in sum.types: + self.visit(t) + + def visitConstructor(self, cons): + for f in cons.fields: + if f.seq: + self.names_with_seq.append(cons.name) + + v = CustomVisitor() + v.visit(self.types['mod']) + self.assertEqual(v.names_with_seq, + ['Module', 'Module', 'Interactive', 'FunctionType']) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py new file mode 100644 index 00000000000..737644ff1be --- /dev/null +++ b/Lib/test/test_clinic.py @@ -0,0 +1,4299 @@ +# Argument Clinic +# Copyright 2012-2013 by Larry Hastings. +# Licensed to the PSF under a contributor agreement. + +from functools import partial +from test import support, test_tools +from test.support import os_helper +from test.support.os_helper import TESTFN, unlink, rmtree +from textwrap import dedent +from unittest import TestCase +import inspect +import os.path +import re +import sys +import unittest + +test_tools.skip_if_missing('clinic') +with test_tools.imports_under_tool('clinic'): + import libclinic + from libclinic import ClinicError, unspecified, NULL, fail + from libclinic.converters import int_converter, str_converter, self_converter + from libclinic.function import ( + Module, Class, Function, FunctionKind, Parameter, + permute_optional_groups, permute_right_option_groups, + permute_left_option_groups) + import clinic + from libclinic.clanguage import CLanguage + from libclinic.converter import converters, legacy_converters + from libclinic.return_converters import return_converters, int_return_converter + from libclinic.block_parser import Block, BlockParser + from libclinic.codegen import BlockPrinter, Destination + from libclinic.dsl_parser import DSLParser + from libclinic.cli import parse_file, Clinic + + +def _make_clinic(*, filename='clinic_tests', limited_capi=False): + clang = CLanguage(filename) + c = Clinic(clang, filename=filename, limited_capi=limited_capi) + c.block_parser = BlockParser('', clang) + return c + + +def _expect_failure(tc, parser, code, errmsg, *, filename=None, lineno=None, + strip=True): + """Helper for the parser tests. + + tc: unittest.TestCase; passed self in the wrapper + parser: the clinic parser used for this test case + code: a str with input text (clinic code) + errmsg: the expected error message + filename: str, optional filename + lineno: int, optional line number + """ + code = dedent(code) + if strip: + code = code.strip() + errmsg = re.escape(errmsg) + with tc.assertRaisesRegex(ClinicError, errmsg) as cm: + parser(code) + if filename is not None: + tc.assertEqual(cm.exception.filename, filename) + if lineno is not None: + tc.assertEqual(cm.exception.lineno, lineno) + return cm.exception + + +def restore_dict(converters, old_converters): + converters.clear() + converters.update(old_converters) + + +def save_restore_converters(testcase): + testcase.addCleanup(restore_dict, converters, + converters.copy()) + testcase.addCleanup(restore_dict, legacy_converters, + legacy_converters.copy()) + testcase.addCleanup(restore_dict, return_converters, + return_converters.copy()) + + +class ClinicWholeFileTest(TestCase): + maxDiff = None + + def expect_failure(self, raw, errmsg, *, filename=None, lineno=None): + _expect_failure(self, self.clinic.parse, raw, errmsg, + filename=filename, lineno=lineno) + + def setUp(self): + save_restore_converters(self) + self.clinic = _make_clinic(filename="test.c") + + def test_eol(self): + # regression test: + # clinic's block parser didn't recognize + # the "end line" for the block if it + # didn't end in "\n" (as in, the last) + # byte of the file was '/'. + # so it would spit out an end line for you. + # and since you really already had one, + # the last line of the block got corrupted. + raw = "/*[clinic]\nfoo\n[clinic]*/" + cooked = self.clinic.parse(raw).splitlines() + end_line = cooked[2].rstrip() + # this test is redundant, it's just here explicitly to catch + # the regression test so we don't forget what it looked like + self.assertNotEqual(end_line, "[clinic]*/[clinic]*/") + self.assertEqual(end_line, "[clinic]*/") + + def test_mangled_marker_line(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/ + /*[clinic end generated code: foo]*/ + """ + err = ( + "Mangled Argument Clinic marker line: " + "'/*[clinic end generated code: foo]*/'" + ) + self.expect_failure(raw, err, filename="test.c", lineno=3) + + def test_checksum_mismatch(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/ + /*[clinic end generated code: output=0123456789abcdef input=fedcba9876543210]*/ + """ + err = ("Checksum mismatch! " + "Expected '0123456789abcdef', computed 'da39a3ee5e6b4b0d'") + self.expect_failure(raw, err, filename="test.c", lineno=3) + + def test_garbage_after_stop_line(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/foobarfoobar! + """ + err = "Garbage after stop line: 'foobarfoobar!'" + self.expect_failure(raw, err, filename="test.c", lineno=2) + + def test_whitespace_before_stop_line(self): + raw = """ + /*[clinic input] + [clinic start generated code]*/ + """ + err = ( + "Whitespace is not allowed before the stop line: " + "' [clinic start generated code]*/'" + ) + self.expect_failure(raw, err, filename="test.c", lineno=2) + + def test_parse_with_body_prefix(self): + clang = CLanguage(None) + clang.body_prefix = "//" + clang.start_line = "//[{dsl_name} start]" + clang.stop_line = "//[{dsl_name} stop]" + cl = Clinic(clang, filename="test.c", limited_capi=False) + raw = dedent(""" + //[clinic start] + //module test + //[clinic stop] + """).strip() + out = cl.parse(raw) + expected = dedent(""" + //[clinic start] + //module test + // + //[clinic stop] + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=65fab8adff58cf08]*/ + """).lstrip() # Note, lstrip() because of the newline + self.assertEqual(out, expected) + + def test_cpp_monitor_fail_nested_block_comment(self): + raw = """ + /* start + /* nested + */ + */ + """ + err = 'Nested block comment!' + self.expect_failure(raw, err, filename="test.c", lineno=2) + + def test_cpp_monitor_fail_invalid_format_noarg(self): + raw = """ + #if + a() + #endif + """ + err = 'Invalid format for #if line: no argument!' + self.expect_failure(raw, err, filename="test.c", lineno=1) + + def test_cpp_monitor_fail_invalid_format_toomanyargs(self): + raw = """ + #ifdef A B + a() + #endif + """ + err = 'Invalid format for #ifdef line: should be exactly one argument!' + self.expect_failure(raw, err, filename="test.c", lineno=1) + + def test_cpp_monitor_fail_no_matching_if(self): + raw = '#else' + err = '#else without matching #if / #ifdef / #ifndef!' + self.expect_failure(raw, err, filename="test.c", lineno=1) + + def test_directive_output_unknown_preset(self): + raw = """ + /*[clinic input] + output preset nosuchpreset + [clinic start generated code]*/ + """ + err = "Unknown preset 'nosuchpreset'" + self.expect_failure(raw, err) + + def test_directive_output_cant_pop(self): + raw = """ + /*[clinic input] + output pop + [clinic start generated code]*/ + """ + err = "Can't 'output pop', stack is empty" + self.expect_failure(raw, err) + + def test_directive_output_print(self): + raw = dedent(""" + /*[clinic input] + output print 'I told you once.' + [clinic start generated code]*/ + """) + out = self.clinic.parse(raw) + # The generated output will differ for every run, but we can check that + # it starts with the clinic block, we check that it contains all the + # expected fields, and we check that it contains the checksum line. + self.assertTrue(out.startswith(dedent(""" + /*[clinic input] + output print 'I told you once.' + [clinic start generated code]*/ + """))) + fields = { + "cpp_endif", + "cpp_if", + "docstring_definition", + "docstring_prototype", + "impl_definition", + "impl_prototype", + "methoddef_define", + "methoddef_ifndef", + "parser_definition", + "parser_prototype", + } + for field in fields: + with self.subTest(field=field): + self.assertIn(field, out) + last_line = out.rstrip().split("\n")[-1] + self.assertTrue( + last_line.startswith("/*[clinic end generated code: output=") + ) + + def test_directive_wrong_arg_number(self): + raw = dedent(""" + /*[clinic input] + preserve foo bar baz eggs spam ham mushrooms + [clinic start generated code]*/ + """) + err = "takes 1 positional argument but 8 were given" + self.expect_failure(raw, err) + + def test_unknown_destination_command(self): + raw = """ + /*[clinic input] + destination buffer nosuchcommand + [clinic start generated code]*/ + """ + err = "unknown destination command 'nosuchcommand'" + self.expect_failure(raw, err) + + def test_no_access_to_members_in_converter_init(self): + raw = """ + /*[python input] + class Custom_converter(CConverter): + converter = "some_c_function" + def converter_init(self): + self.function.noaccess + [python start generated code]*/ + /*[clinic input] + module test + test.fn + a: Custom + [clinic start generated code]*/ + """ + err = ( + "accessing self.function inside converter_init is disallowed!" + ) + self.expect_failure(raw, err) + + def test_clone_mismatch(self): + err = "'kind' of function and cloned function don't match!" + block = """ + /*[clinic input] + module m + @classmethod + m.f1 + a: object + [clinic start generated code]*/ + /*[clinic input] + @staticmethod + m.f2 = m.f1 + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=9) + + def test_badly_formed_return_annotation(self): + err = "Badly formed annotation for 'm.f': 'Custom'" + block = """ + /*[python input] + class Custom_return_converter(CReturnConverter): + def __init__(self): + raise ValueError("abc") + [python start generated code]*/ + /*[clinic input] + module m + m.f -> Custom + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=8) + + def test_star_after_vararg(self): + err = "'my_test_func' uses '*' more than once." + block = """ + /*[clinic input] + my_test_func + + pos_arg: object + *args: object + * + kw_arg: object + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=6) + + def test_vararg_after_star(self): + err = "'my_test_func' uses '*' more than once." + block = """ + /*[clinic input] + my_test_func + + pos_arg: object + * + *args: object + kw_arg: object + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=6) + + def test_module_already_got_one(self): + err = "Already defined module 'm'!" + block = """ + /*[clinic input] + module m + module m + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=3) + + def test_destination_already_got_one(self): + err = "Destination already exists: 'test'" + block = """ + /*[clinic input] + destination test new buffer + destination test new buffer + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=3) + + def test_destination_does_not_exist(self): + err = "Destination does not exist: '/dev/null'" + block = """ + /*[clinic input] + output everything /dev/null + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=2) + + def test_class_already_got_one(self): + err = "Already defined class 'C'!" + block = """ + /*[clinic input] + class C "" "" + class C "" "" + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=3) + + def test_cant_nest_module_inside_class(self): + err = "Can't nest a module inside a class!" + block = """ + /*[clinic input] + class C "" "" + module C.m + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=3) + + def test_dest_buffer_not_empty_at_eof(self): + expected_warning = ("Destination buffer 'buffer' not empty at " + "end of file, emptying.") + expected_generated = dedent(""" + /*[clinic input] + output everything buffer + fn + a: object + / + [clinic start generated code]*/ + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=1c4668687f5fd002]*/ + + /*[clinic input] + dump buffer + [clinic start generated code]*/ + + PyDoc_VAR(fn__doc__); + + PyDoc_STRVAR(fn__doc__, + "fn($module, a, /)\\n" + "--\\n" + "\\n"); + + #define FN_METHODDEF \\ + {"fn", (PyCFunction)fn, METH_O, fn__doc__}, + + static PyObject * + fn(PyObject *module, PyObject *a) + /*[clinic end generated code: output=be6798b148ab4e53 input=524ce2e021e4eba6]*/ + """) + block = dedent(""" + /*[clinic input] + output everything buffer + fn + a: object + / + [clinic start generated code]*/ + """) + with support.captured_stdout() as stdout: + generated = self.clinic.parse(block) + self.assertIn(expected_warning, stdout.getvalue()) + self.assertEqual(generated, expected_generated) + + def test_dest_clear(self): + err = "Can't clear destination 'file': it's not of type 'buffer'" + block = """ + /*[clinic input] + destination file clear + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=2) + + def test_directive_set_misuse(self): + err = "unknown variable 'ets'" + block = """ + /*[clinic input] + set ets tse + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=2) + + def test_directive_set_prefix(self): + block = dedent(""" + /*[clinic input] + set line_prefix '// ' + output everything suppress + output docstring_prototype buffer + fn + a: object + / + [clinic start generated code]*/ + /* We need to dump the buffer. + * If not, Argument Clinic will emit a warning */ + /*[clinic input] + dump buffer + [clinic start generated code]*/ + """) + generated = self.clinic.parse(block) + expected_docstring_prototype = "// PyDoc_VAR(fn__doc__);" + self.assertIn(expected_docstring_prototype, generated) + + def test_directive_set_suffix(self): + block = dedent(""" + /*[clinic input] + set line_suffix ' // test' + output everything suppress + output docstring_prototype buffer + fn + a: object + / + [clinic start generated code]*/ + /* We need to dump the buffer. + * If not, Argument Clinic will emit a warning */ + /*[clinic input] + dump buffer + [clinic start generated code]*/ + """) + generated = self.clinic.parse(block) + expected_docstring_prototype = "PyDoc_VAR(fn__doc__); // test" + self.assertIn(expected_docstring_prototype, generated) + + def test_directive_set_prefix_and_suffix(self): + block = dedent(""" + /*[clinic input] + set line_prefix '{block comment start} ' + set line_suffix ' {block comment end}' + output everything suppress + output docstring_prototype buffer + fn + a: object + / + [clinic start generated code]*/ + /* We need to dump the buffer. + * If not, Argument Clinic will emit a warning */ + /*[clinic input] + dump buffer + [clinic start generated code]*/ + """) + generated = self.clinic.parse(block) + expected_docstring_prototype = "/* PyDoc_VAR(fn__doc__); */" + self.assertIn(expected_docstring_prototype, generated) + + def test_directive_printout(self): + block = dedent(""" + /*[clinic input] + output everything buffer + printout test + [clinic start generated code]*/ + """) + expected = dedent(""" + /*[clinic input] + output everything buffer + printout test + [clinic start generated code]*/ + test + /*[clinic end generated code: output=4e1243bd22c66e76 input=898f1a32965d44ca]*/ + """) + generated = self.clinic.parse(block) + self.assertEqual(generated, expected) + + def test_directive_preserve_twice(self): + err = "Can't have 'preserve' twice in one block!" + block = """ + /*[clinic input] + preserve + preserve + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=3) + + def test_directive_preserve_input(self): + err = "'preserve' only works for blocks that don't produce any output!" + block = """ + /*[clinic input] + preserve + fn + a: object + / + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=6) + + def test_directive_preserve_output(self): + block = dedent(""" + /*[clinic input] + output everything buffer + preserve + [clinic start generated code]*/ + // Preserve this + /*[clinic end generated code: output=eaa49677ae4c1f7d input=559b5db18fddae6a]*/ + /*[clinic input] + dump buffer + [clinic start generated code]*/ + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=524ce2e021e4eba6]*/ + """) + generated = self.clinic.parse(block) + self.assertEqual(generated, block) + + def test_directive_output_invalid_command(self): + err = dedent(""" + Invalid command or destination name 'cmd'. Must be one of: + - 'preset' + - 'push' + - 'pop' + - 'print' + - 'everything' + - 'cpp_if' + - 'docstring_prototype' + - 'docstring_definition' + - 'methoddef_define' + - 'impl_prototype' + - 'parser_prototype' + - 'parser_definition' + - 'cpp_endif' + - 'methoddef_ifndef' + - 'impl_definition' + """).strip() + block = """ + /*[clinic input] + output cmd buffer + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=2) + + def test_validate_cloned_init(self): + block = """ + /*[clinic input] + class C "void *" "" + C.meth + a: int + [clinic start generated code]*/ + /*[clinic input] + @classmethod + C.__init__ = C.meth + [clinic start generated code]*/ + """ + err = "'__init__' must be a normal method; got 'FunctionKind.CLASS_METHOD'!" + self.expect_failure(block, err, lineno=8) + + def test_validate_cloned_new(self): + block = """ + /*[clinic input] + class C "void *" "" + C.meth + a: int + [clinic start generated code]*/ + /*[clinic input] + C.__new__ = C.meth + [clinic start generated code]*/ + """ + err = "'__new__' must be a class method" + self.expect_failure(block, err, lineno=7) + + def test_no_c_basename_cloned(self): + block = """ + /*[clinic input] + foo2 + [clinic start generated code]*/ + /*[clinic input] + foo as = foo2 + [clinic start generated code]*/ + """ + err = "No C basename provided after 'as' keyword" + self.expect_failure(block, err, lineno=5) + + def test_cloned_with_custom_c_basename(self): + raw = dedent(""" + /*[clinic input] + # Make sure we don't create spurious clinic/ directories. + output everything suppress + foo2 + [clinic start generated code]*/ + + /*[clinic input] + foo as foo1 = foo2 + [clinic start generated code]*/ + """) + self.clinic.parse(raw) + funcs = self.clinic.functions + self.assertEqual(len(funcs), 2) + self.assertEqual(funcs[1].name, "foo") + self.assertEqual(funcs[1].c_basename, "foo1") + + def test_cloned_with_illegal_c_basename(self): + block = """ + /*[clinic input] + class C "void *" "" + foo1 + [clinic start generated code]*/ + + /*[clinic input] + foo2 as .illegal. = foo1 + [clinic start generated code]*/ + """ + err = "Illegal C basename: '.illegal.'" + self.expect_failure(block, err, lineno=7) + + def test_cloned_forced_text_signature(self): + block = dedent(""" + /*[clinic input] + @text_signature "($module, a[, b])" + src + a: object + param a + b: object = NULL + / + + docstring + [clinic start generated code]*/ + + /*[clinic input] + dst = src + [clinic start generated code]*/ + """) + self.clinic.parse(block) + self.addCleanup(rmtree, "clinic") + funcs = self.clinic.functions + self.assertEqual(len(funcs), 2) + + src_docstring_lines = funcs[0].docstring.split("\n") + dst_docstring_lines = funcs[1].docstring.split("\n") + + # Signatures are copied. + self.assertEqual(src_docstring_lines[0], "src($module, a[, b])") + self.assertEqual(dst_docstring_lines[0], "dst($module, a[, b])") + + # Param docstrings are copied. + self.assertIn(" param a", src_docstring_lines) + self.assertIn(" param a", dst_docstring_lines) + + # Docstrings are not copied. + self.assertIn("docstring", src_docstring_lines) + self.assertNotIn("docstring", dst_docstring_lines) + + def test_cloned_forced_text_signature_illegal(self): + block = """ + /*[clinic input] + @text_signature "($module, a[, b])" + src + a: object + b: object = NULL + / + [clinic start generated code]*/ + + /*[clinic input] + @text_signature "($module, a_override[, b])" + dst = src + [clinic start generated code]*/ + """ + err = "Cannot use @text_signature when cloning a function" + self.expect_failure(block, err, lineno=11) + + def test_ignore_preprocessor_in_comments(self): + for dsl in "clinic", "python": + raw = dedent(f"""\ + /*[{dsl} input] + # CPP directives, valid or not, should be ignored in C comments. + # + [{dsl} start generated code]*/ + """) + self.clinic.parse(raw) + + +class ParseFileUnitTest(TestCase): + def expect_parsing_failure( + self, *, filename, expected_error, verify=True, output=None + ): + errmsg = re.escape(dedent(expected_error).strip()) + with self.assertRaisesRegex(ClinicError, errmsg): + parse_file(filename, limited_capi=False) + + def test_parse_file_no_extension(self) -> None: + self.expect_parsing_failure( + filename="foo", + expected_error="Can't extract file type for file 'foo'" + ) + + def test_parse_file_strange_extension(self) -> None: + filenames_to_errors = { + "foo.rs": "Can't identify file type for file 'foo.rs'", + "foo.hs": "Can't identify file type for file 'foo.hs'", + "foo.js": "Can't identify file type for file 'foo.js'", + } + for filename, errmsg in filenames_to_errors.items(): + with self.subTest(filename=filename): + self.expect_parsing_failure(filename=filename, expected_error=errmsg) + + +class ClinicGroupPermuterTest(TestCase): + def _test(self, l, m, r, output): + computed = permute_optional_groups(l, m, r) + self.assertEqual(output, computed) + + def test_range(self): + self._test([['start']], ['stop'], [['step']], + ( + ('stop',), + ('start', 'stop',), + ('start', 'stop', 'step',), + )) + + def test_add_window(self): + self._test([['x', 'y']], ['ch'], [['attr']], + ( + ('ch',), + ('ch', 'attr'), + ('x', 'y', 'ch',), + ('x', 'y', 'ch', 'attr'), + )) + + def test_ludicrous(self): + self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']], + ( + ('c1',), + ('b1', 'b2', 'c1'), + ('b1', 'b2', 'c1', 'd1', 'd2'), + ('a1', 'a2', 'a3', 'b1', 'b2', 'c1'), + ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'), + ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'), + )) + + def test_right_only(self): + self._test([], [], [['a'],['b'],['c']], + ( + (), + ('a',), + ('a', 'b'), + ('a', 'b', 'c') + )) + + def test_have_left_options_but_required_is_empty(self): + def fn(): + permute_optional_groups(['a'], [], []) + self.assertRaises(ValueError, fn) + + +class ClinicLinearFormatTest(TestCase): + def _test(self, input, output, **kwargs): + computed = libclinic.linear_format(input, **kwargs) + self.assertEqual(output, computed) + + def test_empty_strings(self): + self._test('', '') + + def test_solo_newline(self): + self._test('\n', '\n') + + def test_no_substitution(self): + self._test(""" + abc + """, """ + abc + """) + + def test_empty_substitution(self): + self._test(""" + abc + {name} + def + """, """ + abc + def + """, name='') + + def test_single_line_substitution(self): + self._test(""" + abc + {name} + def + """, """ + abc + GARGLE + def + """, name='GARGLE') + + def test_multiline_substitution(self): + self._test(""" + abc + {name} + def + """, """ + abc + bingle + bungle + + def + """, name='bingle\nbungle\n') + + def test_text_before_block_marker(self): + regex = re.escape("found before '{marker}'") + with self.assertRaisesRegex(ClinicError, regex): + libclinic.linear_format("no text before marker for you! {marker}", + marker="not allowed!") + + def test_text_after_block_marker(self): + regex = re.escape("found after '{marker}'") + with self.assertRaisesRegex(ClinicError, regex): + libclinic.linear_format("{marker} no text after marker for you!", + marker="not allowed!") + + +class InertParser: + def __init__(self, clinic): + pass + + def parse(self, block): + pass + +class CopyParser: + def __init__(self, clinic): + pass + + def parse(self, block): + block.output = block.input + + +class ClinicBlockParserTest(TestCase): + def _test(self, input, output): + language = CLanguage(None) + + blocks = list(BlockParser(input, language)) + writer = BlockPrinter(language) + for block in blocks: + writer.print_block(block) + output = writer.f.getvalue() + assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input) + + def round_trip(self, input): + return self._test(input, input) + + def test_round_trip_1(self): + self.round_trip(""" + verbatim text here + lah dee dah + """) + def test_round_trip_2(self): + self.round_trip(""" + verbatim text here + lah dee dah +/*[inert] +abc +[inert]*/ +def +/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/ +xyz +""") + + def _test_clinic(self, input, output): + language = CLanguage(None) + c = Clinic(language, filename="file", limited_capi=False) + c.parsers['inert'] = InertParser(c) + c.parsers['copy'] = CopyParser(c) + computed = c.parse(input) + self.assertEqual(output, computed) + + def test_clinic_1(self): + self._test_clinic(""" + verbatim text here + lah dee dah +/*[copy input] +def +[copy start generated code]*/ +abc +/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/ +xyz +""", """ + verbatim text here + lah dee dah +/*[copy input] +def +[copy start generated code]*/ +def +/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/ +xyz +""") + + +class ClinicParserTest(TestCase): + + def parse(self, text): + c = _make_clinic() + parser = DSLParser(c) + block = Block(text) + parser.parse(block) + return block + + def parse_function(self, text, signatures_in_block=2, function_index=1): + block = self.parse(text) + s = block.signatures + self.assertEqual(len(s), signatures_in_block) + assert isinstance(s[0], Module) + assert isinstance(s[function_index], Function) + return s[function_index] + + def expect_failure(self, block, err, *, + filename=None, lineno=None, strip=True): + return _expect_failure(self, self.parse_function, block, err, + filename=filename, lineno=lineno, strip=strip) + + def checkDocstring(self, fn, expected): + self.assertTrue(hasattr(fn, "docstring")) + self.assertEqual(dedent(expected).strip(), + fn.docstring.strip()) + + def test_trivial(self): + parser = DSLParser(_make_clinic()) + block = Block(""" + module os + os.access + """) + parser.parse(block) + module, function = block.signatures + self.assertEqual("access", function.name) + self.assertEqual("os", module.name) + + def test_ignore_line(self): + block = self.parse(dedent(""" + # + module os + os.access + """)) + module, function = block.signatures + self.assertEqual("access", function.name) + self.assertEqual("os", module.name) + + def test_param(self): + function = self.parse_function(""" + module os + os.access + path: int + """) + self.assertEqual("access", function.name) + self.assertEqual(2, len(function.parameters)) + p = function.parameters['path'] + self.assertEqual('path', p.name) + self.assertIsInstance(p.converter, int_converter) + + def test_param_default(self): + function = self.parse_function(""" + module os + os.access + follow_symlinks: bool = True + """) + p = function.parameters['follow_symlinks'] + self.assertEqual(True, p.default) + + def test_param_with_continuations(self): + function = self.parse_function(r""" + module os + os.access + follow_symlinks: \ + bool \ + = \ + True + """) + p = function.parameters['follow_symlinks'] + self.assertEqual(True, p.default) + + def test_param_default_expr_named_constant(self): + function = self.parse_function(""" + module os + os.access + follow_symlinks: int(c_default='MAXSIZE') = sys.maxsize + """) + p = function.parameters['follow_symlinks'] + self.assertEqual(sys.maxsize, p.default) + self.assertEqual("MAXSIZE", p.converter.c_default) + + err = ( + "When you specify a named constant ('sys.maxsize') as your default value, " + "you MUST specify a valid c_default." + ) + block = """ + module os + os.access + follow_symlinks: int = sys.maxsize + """ + self.expect_failure(block, err, lineno=2) + + def test_param_with_bizarre_default_fails_correctly(self): + template = """ + module os + os.access + follow_symlinks: int = {default} + """ + err = "Unsupported expression as default value" + for bad_default_value in ( + "{1, 2, 3}", + "3 if bool() else 4", + "[x for x in range(42)]" + ): + with self.subTest(bad_default=bad_default_value): + block = template.format(default=bad_default_value) + self.expect_failure(block, err, lineno=2) + + def test_unspecified_not_allowed_as_default_value(self): + block = """ + module os + os.access + follow_symlinks: int(c_default='MAXSIZE') = unspecified + """ + err = "'unspecified' is not a legal default value!" + exc = self.expect_failure(block, err, lineno=2) + self.assertNotIn('Malformed expression given as default value', str(exc)) + + def test_malformed_expression_as_default_value(self): + block = """ + module os + os.access + follow_symlinks: int(c_default='MAXSIZE') = 1/0 + """ + err = "Malformed expression given as default value" + self.expect_failure(block, err, lineno=2) + + def test_param_default_expr_binop(self): + err = ( + "When you specify an expression ('a + b') as your default value, " + "you MUST specify a valid c_default." + ) + block = """ + fn + follow_symlinks: int = a + b + """ + self.expect_failure(block, err, lineno=1) + + def test_param_no_docstring(self): + function = self.parse_function(""" + module os + os.access + follow_symlinks: bool = True + something_else: str = '' + """) + self.assertEqual(3, len(function.parameters)) + conv = function.parameters['something_else'].converter + self.assertIsInstance(conv, str_converter) + + def test_param_default_parameters_out_of_order(self): + err = ( + "Can't have a parameter without a default ('something_else') " + "after a parameter with a default!" + ) + block = """ + module os + os.access + follow_symlinks: bool = True + something_else: str + """ + self.expect_failure(block, err, lineno=3) + + def disabled_test_converter_arguments(self): + function = self.parse_function(""" + module os + os.access + path: path_t(allow_fd=1) + """) + p = function.parameters['path'] + self.assertEqual(1, p.converter.args['allow_fd']) + + def test_function_docstring(self): + function = self.parse_function(""" + module os + os.stat as os_stat_fn + + path: str + Path to be examined + Ensure that multiple lines are indented correctly. + + Perform a stat system call on the given path. + + Ensure that multiple lines are indented correctly. + Ensure that multiple lines are indented correctly. + """) + self.checkDocstring(function, """ + stat($module, /, path) + -- + + Perform a stat system call on the given path. + + path + Path to be examined + Ensure that multiple lines are indented correctly. + + Ensure that multiple lines are indented correctly. + Ensure that multiple lines are indented correctly. + """) + + def test_docstring_trailing_whitespace(self): + function = self.parse_function( + "module t\n" + "t.s\n" + " a: object\n" + " Param docstring with trailing whitespace \n" + "Func docstring summary with trailing whitespace \n" + " \n" + "Func docstring body with trailing whitespace \n" + ) + self.checkDocstring(function, """ + s($module, /, a) + -- + + Func docstring summary with trailing whitespace + + a + Param docstring with trailing whitespace + + Func docstring body with trailing whitespace + """) + + def test_explicit_parameters_in_docstring(self): + function = self.parse_function(dedent(""" + module foo + foo.bar + x: int + Documentation for x. + y: int + + This is the documentation for foo. + + Okay, we're done here. + """)) + self.checkDocstring(function, """ + bar($module, /, x, y) + -- + + This is the documentation for foo. + + x + Documentation for x. + + Okay, we're done here. + """) + + def test_docstring_with_comments(self): + function = self.parse_function(dedent(""" + module foo + foo.bar + x: int + # We're about to have + # the documentation for x. + Documentation for x. + # We've just had + # the documentation for x. + y: int + + # We're about to have + # the documentation for foo. + This is the documentation for foo. + # We've just had + # the documentation for foo. + + Okay, we're done here. + """)) + self.checkDocstring(function, """ + bar($module, /, x, y) + -- + + This is the documentation for foo. + + x + Documentation for x. + + Okay, we're done here. + """) + + def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self): + function = self.parse_function(dedent(""" + module os + os.stat + path: str + This/used to break Clinic! + """)) + self.checkDocstring(function, """ + stat($module, /, path) + -- + + This/used to break Clinic! + """) + + def test_c_name(self): + function = self.parse_function(""" + module os + os.stat as os_stat_fn + """) + self.assertEqual("os_stat_fn", function.c_basename) + + def test_base_invalid_syntax(self): + block = """ + module os + os.stat + invalid syntax: int = 42 + """ + err = dedent(r""" + Function 'stat' has an invalid parameter declaration: + \s+'invalid syntax: int = 42' + """).strip() + with self.assertRaisesRegex(ClinicError, err): + self.parse_function(block) + + def test_param_default_invalid_syntax(self): + block = """ + module os + os.stat + x: int = invalid syntax + """ + err = r"Syntax error: 'x = invalid syntax\n'" + self.expect_failure(block, err, lineno=2) + + def test_cloning_nonexistent_function_correctly_fails(self): + block = """ + cloned = fooooooooooooooooo + This is trying to clone a nonexistent function!! + """ + err = "Couldn't find existing function 'fooooooooooooooooo'!" + with support.captured_stderr() as stderr: + self.expect_failure(block, err, lineno=0) + expected_debug_print = dedent("""\ + cls=None, module=, existing='fooooooooooooooooo' + (cls or module).functions=[] + """) + stderr = stderr.getvalue() + self.assertIn(expected_debug_print, stderr) + + def test_return_converter(self): + function = self.parse_function(""" + module os + os.stat -> int + """) + self.assertIsInstance(function.return_converter, int_return_converter) + + def test_return_converter_invalid_syntax(self): + block = """ + module os + os.stat -> invalid syntax + """ + err = "Badly formed annotation for 'os.stat': 'invalid syntax'" + self.expect_failure(block, err) + + def test_legacy_converter_disallowed_in_return_annotation(self): + block = """ + module os + os.stat -> "s" + """ + err = "Legacy converter 's' not allowed as a return converter" + self.expect_failure(block, err) + + def test_unknown_return_converter(self): + block = """ + module os + os.stat -> fooooooooooooooooooooooo + """ + err = "No available return converter called 'fooooooooooooooooooooooo'" + self.expect_failure(block, err) + + def test_star(self): + function = self.parse_function(""" + module os + os.access + * + follow_symlinks: bool = True + """) + p = function.parameters['follow_symlinks'] + self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind) + self.assertEqual(0, p.group) + + def test_group(self): + function = self.parse_function(""" + module window + window.border + [ + ls: int + ] + / + """) + p = function.parameters['ls'] + self.assertEqual(1, p.group) + + def test_left_group(self): + function = self.parse_function(""" + module curses + curses.addch + [ + y: int + Y-coordinate. + x: int + X-coordinate. + ] + ch: char + Character to add. + [ + attr: long + Attributes for the character. + ] + / + """) + dataset = ( + ('y', -1), ('x', -1), + ('ch', 0), + ('attr', 1), + ) + for name, group in dataset: + with self.subTest(name=name, group=group): + p = function.parameters[name] + self.assertEqual(p.group, group) + self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY) + self.checkDocstring(function, """ + addch([y, x,] ch, [attr]) + + + y + Y-coordinate. + x + X-coordinate. + ch + Character to add. + attr + Attributes for the character. + """) + + def test_nested_groups(self): + function = self.parse_function(""" + module curses + curses.imaginary + [ + [ + y1: int + Y-coordinate. + y2: int + Y-coordinate. + ] + x1: int + X-coordinate. + x2: int + X-coordinate. + ] + ch: char + Character to add. + [ + attr1: long + Attributes for the character. + attr2: long + Attributes for the character. + attr3: long + Attributes for the character. + [ + attr4: long + Attributes for the character. + attr5: long + Attributes for the character. + attr6: long + Attributes for the character. + ] + ] + / + """) + dataset = ( + ('y1', -2), ('y2', -2), + ('x1', -1), ('x2', -1), + ('ch', 0), + ('attr1', 1), ('attr2', 1), ('attr3', 1), + ('attr4', 2), ('attr5', 2), ('attr6', 2), + ) + for name, group in dataset: + with self.subTest(name=name, group=group): + p = function.parameters[name] + self.assertEqual(p.group, group) + self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY) + + self.checkDocstring(function, """ + imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5, + attr6]]) + + + y1 + Y-coordinate. + y2 + Y-coordinate. + x1 + X-coordinate. + x2 + X-coordinate. + ch + Character to add. + attr1 + Attributes for the character. + attr2 + Attributes for the character. + attr3 + Attributes for the character. + attr4 + Attributes for the character. + attr5 + Attributes for the character. + attr6 + Attributes for the character. + """) + + def test_disallowed_grouping__two_top_groups_on_left(self): + err = ( + "Function 'two_top_groups_on_left' has an unsupported group " + "configuration. (Unexpected state 2.b)" + ) + block = """ + module foo + foo.two_top_groups_on_left + [ + group1 : int + ] + [ + group2 : int + ] + param: int + """ + self.expect_failure(block, err, lineno=5) + + def test_disallowed_grouping__two_top_groups_on_right(self): + block = """ + module foo + foo.two_top_groups_on_right + param: int + [ + group1 : int + ] + [ + group2 : int + ] + """ + err = ( + "Function 'two_top_groups_on_right' has an unsupported group " + "configuration. (Unexpected state 6.b)" + ) + self.expect_failure(block, err) + + def test_disallowed_grouping__parameter_after_group_on_right(self): + block = """ + module foo + foo.parameter_after_group_on_right + param: int + [ + [ + group1 : int + ] + group2 : int + ] + """ + err = ( + "Function parameter_after_group_on_right has an unsupported group " + "configuration. (Unexpected state 6.a)" + ) + self.expect_failure(block, err) + + def test_disallowed_grouping__group_after_parameter_on_left(self): + block = """ + module foo + foo.group_after_parameter_on_left + [ + group2 : int + [ + group1 : int + ] + ] + param: int + """ + err = ( + "Function 'group_after_parameter_on_left' has an unsupported group " + "configuration. (Unexpected state 2.b)" + ) + self.expect_failure(block, err) + + def test_disallowed_grouping__empty_group_on_left(self): + block = """ + module foo + foo.empty_group + [ + [ + ] + group2 : int + ] + param: int + """ + err = ( + "Function 'empty_group' has an empty group. " + "All groups must contain at least one parameter." + ) + self.expect_failure(block, err) + + def test_disallowed_grouping__empty_group_on_right(self): + block = """ + module foo + foo.empty_group + param: int + [ + [ + ] + group2 : int + ] + """ + err = ( + "Function 'empty_group' has an empty group. " + "All groups must contain at least one parameter." + ) + self.expect_failure(block, err) + + def test_disallowed_grouping__no_matching_bracket(self): + block = """ + module foo + foo.empty_group + param: int + ] + group2: int + ] + """ + err = "Function 'empty_group' has a ']' without a matching '['" + self.expect_failure(block, err) + + def test_disallowed_grouping__must_be_position_only(self): + dataset = (""" + with_kwds + [ + * + a: object + ] + """, """ + with_kwds + [ + a: object + ] + """) + err = ( + "You cannot use optional groups ('[' and ']') unless all " + "parameters are positional-only ('/')" + ) + for block in dataset: + with self.subTest(block=block): + self.expect_failure(block, err) + + def test_no_parameters(self): + function = self.parse_function(""" + module foo + foo.bar + + Docstring + + """) + self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring) + self.assertEqual(1, len(function.parameters)) # self! + + def test_init_with_no_parameters(self): + function = self.parse_function(""" + module foo + class foo.Bar "unused" "notneeded" + foo.Bar.__init__ + + Docstring + + """, signatures_in_block=3, function_index=2) + + # self is not in the signature + self.assertEqual("Bar()\n--\n\nDocstring", function.docstring) + # but it *is* a parameter + self.assertEqual(1, len(function.parameters)) + + def test_illegal_module_line(self): + block = """ + module foo + foo.bar => int + / + """ + err = "Illegal function name: 'foo.bar => int'" + self.expect_failure(block, err) + + def test_illegal_c_basename(self): + block = """ + module foo + foo.bar as 935 + / + """ + err = "Illegal C basename: '935'" + self.expect_failure(block, err) + + def test_no_c_basename(self): + block = "foo as " + err = "No C basename provided after 'as' keyword" + self.expect_failure(block, err, strip=False) + + def test_single_star(self): + block = """ + module foo + foo.bar + * + * + """ + err = "Function 'bar' uses '*' more than once." + self.expect_failure(block, err) + + def test_parameters_required_after_star(self): + dataset = ( + "module foo\nfoo.bar\n *", + "module foo\nfoo.bar\n *\nDocstring here.", + "module foo\nfoo.bar\n this: int\n *", + "module foo\nfoo.bar\n this: int\n *\nDocstring.", + ) + err = "Function 'bar' specifies '*' without following parameters." + for block in dataset: + with self.subTest(block=block): + self.expect_failure(block, err) + + def test_fulldisplayname_class(self): + dataset = ( + ("T", """ + class T "void *" "" + T.__init__ + """), + ("m.T", """ + module m + class m.T "void *" "" + @classmethod + m.T.__new__ + """), + ("m.T.C", """ + module m + class m.T "void *" "" + class m.T.C "void *" "" + m.T.C.__init__ + """), + ) + for name, code in dataset: + with self.subTest(name=name, code=code): + block = self.parse(code) + func = block.signatures[-1] + self.assertEqual(func.fulldisplayname, name) + + def test_fulldisplayname_meth(self): + dataset = ( + ("func", "func"), + ("m.func", """ + module m + m.func + """), + ("T.meth", """ + class T "void *" "" + T.meth + """), + ("m.T.meth", """ + module m + class m.T "void *" "" + m.T.meth + """), + ("m.T.C.meth", """ + module m + class m.T "void *" "" + class m.T.C "void *" "" + m.T.C.meth + """), + ) + for name, code in dataset: + with self.subTest(name=name, code=code): + block = self.parse(code) + func = block.signatures[-1] + self.assertEqual(func.fulldisplayname, name) + + def test_depr_star_invalid_format_1(self): + block = """ + module foo + foo.bar + this: int + * [from 3] + Docstring. + """ + err = ( + "Function 'bar': expected format '[from major.minor]' " + "where 'major' and 'minor' are integers; got '3'" + ) + self.expect_failure(block, err, lineno=3) + + def test_depr_star_invalid_format_2(self): + block = """ + module foo + foo.bar + this: int + * [from a.b] + Docstring. + """ + err = ( + "Function 'bar': expected format '[from major.minor]' " + "where 'major' and 'minor' are integers; got 'a.b'" + ) + self.expect_failure(block, err, lineno=3) + + def test_depr_star_invalid_format_3(self): + block = """ + module foo + foo.bar + this: int + * [from 1.2.3] + Docstring. + """ + err = ( + "Function 'bar': expected format '[from major.minor]' " + "where 'major' and 'minor' are integers; got '1.2.3'" + ) + self.expect_failure(block, err, lineno=3) + + def test_parameters_required_after_depr_star(self): + block = """ + module foo + foo.bar + this: int + * [from 3.14] + Docstring. + """ + err = ( + "Function 'bar' specifies '* [from ...]' without " + "following parameters." + ) + self.expect_failure(block, err, lineno=4) + + def test_parameters_required_after_depr_star2(self): + block = """ + module foo + foo.bar + a: int + * [from 3.14] + * + b: int + Docstring. + """ + err = ( + "Function 'bar' specifies '* [from ...]' without " + "following parameters." + ) + self.expect_failure(block, err, lineno=4) + + def test_parameters_required_after_depr_star3(self): + block = """ + module foo + foo.bar + a: int + * [from 3.14] + *args: object + b: int + Docstring. + """ + err = ( + "Function 'bar' specifies '* [from ...]' without " + "following parameters." + ) + self.expect_failure(block, err, lineno=4) + + def test_depr_star_must_come_before_star(self): + block = """ + module foo + foo.bar + a: int + * + * [from 3.14] + b: int + Docstring. + """ + err = "Function 'bar': '* [from ...]' must precede '*'" + self.expect_failure(block, err, lineno=4) + + def test_depr_star_must_come_before_vararg(self): + block = """ + module foo + foo.bar + a: int + *args: object + * [from 3.14] + b: int + Docstring. + """ + err = "Function 'bar': '* [from ...]' must precede '*'" + self.expect_failure(block, err, lineno=4) + + def test_depr_star_duplicate(self): + block = """ + module foo + foo.bar + a: int + * [from 3.14] + b: int + * [from 3.14] + c: int + Docstring. + """ + err = "Function 'bar' uses '* [from 3.14]' more than once." + self.expect_failure(block, err, lineno=5) + + def test_depr_star_duplicate2(self): + block = """ + module foo + foo.bar + a: int + * [from 3.14] + b: int + * [from 3.15] + c: int + Docstring. + """ + err = "Function 'bar': '* [from 3.15]' must precede '* [from 3.14]'" + self.expect_failure(block, err, lineno=5) + + def test_depr_slash_duplicate(self): + block = """ + module foo + foo.bar + a: int + / [from 3.14] + b: int + / [from 3.14] + c: int + Docstring. + """ + err = "Function 'bar' uses '/ [from 3.14]' more than once." + self.expect_failure(block, err, lineno=5) + + def test_depr_slash_duplicate2(self): + block = """ + module foo + foo.bar + a: int + / [from 3.15] + b: int + / [from 3.14] + c: int + Docstring. + """ + err = "Function 'bar': '/ [from 3.14]' must precede '/ [from 3.15]'" + self.expect_failure(block, err, lineno=5) + + def test_single_slash(self): + block = """ + module foo + foo.bar + / + / + """ + err = ( + "Function 'bar' has an unsupported group configuration. " + "(Unexpected state 0.d)" + ) + self.expect_failure(block, err) + + def test_parameters_required_before_depr_slash(self): + block = """ + module foo + foo.bar + / [from 3.14] + Docstring. + """ + err = ( + "Function 'bar' specifies '/ [from ...]' without " + "preceding parameters." + ) + self.expect_failure(block, err, lineno=2) + + def test_parameters_required_before_depr_slash2(self): + block = """ + module foo + foo.bar + a: int + / + / [from 3.14] + Docstring. + """ + err = ( + "Function 'bar' specifies '/ [from ...]' without " + "preceding parameters." + ) + self.expect_failure(block, err, lineno=4) + + def test_double_slash(self): + block = """ + module foo + foo.bar + a: int + / + b: int + / + """ + err = "Function 'bar' uses '/' more than once." + self.expect_failure(block, err) + + def test_slash_after_star(self): + block = """ + module foo + foo.bar + x: int + y: int + * + z: int + / + """ + err = "Function 'bar': '/' must precede '*'" + self.expect_failure(block, err) + + def test_slash_after_vararg(self): + block = """ + module foo + foo.bar + x: int + y: int + *args: object + z: int + / + """ + err = "Function 'bar': '/' must precede '*'" + self.expect_failure(block, err) + + def test_depr_star_must_come_after_slash(self): + block = """ + module foo + foo.bar + a: int + * [from 3.14] + / + b: int + Docstring. + """ + err = "Function 'bar': '/' must precede '* [from ...]'" + self.expect_failure(block, err, lineno=4) + + def test_depr_star_must_come_after_depr_slash(self): + block = """ + module foo + foo.bar + a: int + * [from 3.14] + / [from 3.14] + b: int + Docstring. + """ + err = "Function 'bar': '/ [from ...]' must precede '* [from ...]'" + self.expect_failure(block, err, lineno=4) + + def test_star_must_come_after_depr_slash(self): + block = """ + module foo + foo.bar + a: int + * + / [from 3.14] + b: int + Docstring. + """ + err = "Function 'bar': '/ [from ...]' must precede '*'" + self.expect_failure(block, err, lineno=4) + + def test_vararg_must_come_after_depr_slash(self): + block = """ + module foo + foo.bar + a: int + *args: object + / [from 3.14] + b: int + Docstring. + """ + err = "Function 'bar': '/ [from ...]' must precede '*'" + self.expect_failure(block, err, lineno=4) + + def test_depr_slash_must_come_after_slash(self): + block = """ + module foo + foo.bar + a: int + / [from 3.14] + / + b: int + Docstring. + """ + err = "Function 'bar': '/' must precede '/ [from ...]'" + self.expect_failure(block, err, lineno=4) + + def test_parameters_not_permitted_after_slash_for_now(self): + block = """ + module foo + foo.bar + / + x: int + """ + err = ( + "Function 'bar' has an unsupported group configuration. " + "(Unexpected state 0.d)" + ) + self.expect_failure(block, err) + + def test_parameters_no_more_than_one_vararg(self): + err = "Function 'bar' uses '*' more than once." + block = """ + module foo + foo.bar + *vararg1: object + *vararg2: object + """ + self.expect_failure(block, err, lineno=3) + + def test_function_not_at_column_0(self): + function = self.parse_function(""" + module foo + foo.bar + x: int + Nested docstring here, goeth. + * + y: str + Not at column 0! + """) + self.checkDocstring(function, """ + bar($module, /, x, *, y) + -- + + Not at column 0! + + x + Nested docstring here, goeth. + """) + + def test_docstring_only_summary(self): + function = self.parse_function(""" + module m + m.f + summary + """) + self.checkDocstring(function, """ + f($module, /) + -- + + summary + """) + + def test_docstring_empty_lines(self): + function = self.parse_function(""" + module m + m.f + + + """) + self.checkDocstring(function, """ + f($module, /) + -- + """) + + def test_docstring_explicit_params_placement(self): + function = self.parse_function(""" + module m + m.f + a: int + Param docstring for 'a' will be included + b: int + c: int + Param docstring for 'c' will be included + This is the summary line. + + We'll now place the params section here: + {parameters} + And now for something completely different! + (Note the added newline) + """) + self.checkDocstring(function, """ + f($module, /, a, b, c) + -- + + This is the summary line. + + We'll now place the params section here: + a + Param docstring for 'a' will be included + c + Param docstring for 'c' will be included + + And now for something completely different! + (Note the added newline) + """) + + def test_indent_stack_no_tabs(self): + block = """ + module foo + foo.bar + *vararg1: object + \t*vararg2: object + """ + err = ("Tab characters are illegal in the Clinic DSL: " + r"'\t*vararg2: object'") + self.expect_failure(block, err) + + def test_indent_stack_illegal_outdent(self): + block = """ + module foo + foo.bar + a: object + b: object + """ + err = "Illegal outdent" + self.expect_failure(block, err) + + def test_directive(self): + parser = DSLParser(_make_clinic()) + parser.flag = False + parser.directives['setflag'] = lambda : setattr(parser, 'flag', True) + block = Block("setflag") + parser.parse(block) + self.assertTrue(parser.flag) + + def test_legacy_converters(self): + block = self.parse('module os\nos.access\n path: "s"') + module, function = block.signatures + conv = (function.parameters['path']).converter + self.assertIsInstance(conv, str_converter) + + def test_legacy_converters_non_string_constant_annotation(self): + err = "Annotations must be either a name, a function call, or a string" + dataset = ( + 'module os\nos.access\n path: 42', + 'module os\nos.access\n path: 42.42', + 'module os\nos.access\n path: 42j', + 'module os\nos.access\n path: b"42"', + ) + for block in dataset: + with self.subTest(block=block): + self.expect_failure(block, err, lineno=2) + + def test_other_bizarre_things_in_annotations_fail(self): + err = "Annotations must be either a name, a function call, or a string" + dataset = ( + 'module os\nos.access\n path: {"some": "dictionary"}', + 'module os\nos.access\n path: ["list", "of", "strings"]', + 'module os\nos.access\n path: (x for x in range(42))', + ) + for block in dataset: + with self.subTest(block=block): + self.expect_failure(block, err, lineno=2) + + def test_kwarg_splats_disallowed_in_function_call_annotations(self): + err = "Cannot use a kwarg splat in a function-call annotation" + dataset = ( + 'module fo\nfo.barbaz\n o: bool(**{None: "bang!"})', + 'module fo\nfo.barbaz -> bool(**{None: "bang!"})', + 'module fo\nfo.barbaz -> bool(**{"bang": 42})', + 'module fo\nfo.barbaz\n o: bool(**{"bang": None})', + ) + for block in dataset: + with self.subTest(block=block): + self.expect_failure(block, err) + + def test_self_param_placement(self): + err = ( + "A 'self' parameter, if specified, must be the very first thing " + "in the parameter block." + ) + block = """ + module foo + foo.func + a: int + self: self(type="PyObject *") + """ + self.expect_failure(block, err, lineno=3) + + def test_self_param_cannot_be_optional(self): + err = "A 'self' parameter cannot be marked optional." + block = """ + module foo + foo.func + self: self(type="PyObject *") = None + """ + self.expect_failure(block, err, lineno=2) + + def test_defining_class_param_placement(self): + err = ( + "A 'defining_class' parameter, if specified, must either be the " + "first thing in the parameter block, or come just after 'self'." + ) + block = """ + module foo + foo.func + self: self(type="PyObject *") + a: int + cls: defining_class + """ + self.expect_failure(block, err, lineno=4) + + def test_defining_class_param_cannot_be_optional(self): + err = "A 'defining_class' parameter cannot be marked optional." + block = """ + module foo + foo.func + cls: defining_class(type="PyObject *") = None + """ + self.expect_failure(block, err, lineno=2) + + def test_slot_methods_cannot_access_defining_class(self): + block = """ + module foo + class Foo "" "" + Foo.__init__ + cls: defining_class + a: object + """ + err = "Slot methods cannot access their defining class." + with self.assertRaisesRegex(ValueError, err): + self.parse_function(block) + + def test_new_must_be_a_class_method(self): + err = "'__new__' must be a class method!" + block = """ + module foo + class Foo "" "" + Foo.__new__ + """ + self.expect_failure(block, err, lineno=2) + + def test_init_must_be_a_normal_method(self): + err_template = "'__init__' must be a normal method; got 'FunctionKind.{}'!" + annotations = { + "@classmethod": "CLASS_METHOD", + "@staticmethod": "STATIC_METHOD", + "@getter": "GETTER", + } + for annotation, invalid_kind in annotations.items(): + with self.subTest(annotation=annotation, invalid_kind=invalid_kind): + block = f""" + module foo + class Foo "" "" + {annotation} + Foo.__init__ + """ + expected_error = err_template.format(invalid_kind) + self.expect_failure(block, expected_error, lineno=3) + + def test_init_cannot_define_a_return_type(self): + block = """ + class Foo "" "" + Foo.__init__ -> long + """ + expected_error = "__init__ methods cannot define a return type" + self.expect_failure(block, expected_error, lineno=1) + + def test_invalid_getset(self): + annotations = ["@getter", "@setter"] + for annotation in annotations: + with self.subTest(annotation=annotation): + block = f""" + module foo + class Foo "" "" + {annotation} + Foo.property -> int + """ + expected_error = f"{annotation} method cannot define a return type" + self.expect_failure(block, expected_error, lineno=3) + + block = f""" + module foo + class Foo "" "" + {annotation} + Foo.property + obj: int + / + """ + expected_error = f"{annotation} methods cannot define parameters" + self.expect_failure(block, expected_error) + + def test_setter_docstring(self): + block = """ + module foo + class Foo "" "" + @setter + Foo.property + + foo + + bar + [clinic start generated code]*/ + """ + expected_error = "docstrings are only supported for @getter, not @setter" + self.expect_failure(block, expected_error) + + def test_duplicate_getset(self): + annotations = ["@getter", "@setter"] + for annotation in annotations: + with self.subTest(annotation=annotation): + block = f""" + module foo + class Foo "" "" + {annotation} + {annotation} + Foo.property -> int + """ + expected_error = f"Cannot apply {annotation} twice to the same function!" + self.expect_failure(block, expected_error, lineno=3) + + def test_getter_and_setter_disallowed_on_same_function(self): + dup_annotations = [("@getter", "@setter"), ("@setter", "@getter")] + for dup in dup_annotations: + with self.subTest(dup=dup): + block = f""" + module foo + class Foo "" "" + {dup[0]} + {dup[1]} + Foo.property -> int + """ + expected_error = "Cannot apply both @getter and @setter to the same function!" + self.expect_failure(block, expected_error, lineno=3) + + def test_getset_no_class(self): + for annotation in "@getter", "@setter": + with self.subTest(annotation=annotation): + block = f""" + module m + {annotation} + m.func + """ + expected_error = "@getter and @setter must be methods" + self.expect_failure(block, expected_error, lineno=2) + + def test_duplicate_coexist(self): + err = "Called @coexist twice" + block = """ + module m + @coexist + @coexist + m.fn + """ + self.expect_failure(block, err, lineno=2) + + def test_unused_param(self): + block = self.parse(""" + module foo + foo.func + fn: object + k: float + i: float(unused=True) + / + * + flag: bool(unused=True) = False + """) + sig = block.signatures[1] # Function index == 1 + params = sig.parameters + conv = lambda fn: params[fn].converter + dataset = ( + {"name": "fn", "unused": False}, + {"name": "k", "unused": False}, + {"name": "i", "unused": True}, + {"name": "flag", "unused": True}, + ) + for param in dataset: + name, unused = param.values() + with self.subTest(name=name, unused=unused): + p = conv(name) + # Verify that the unused flag is parsed correctly. + self.assertEqual(unused, p.unused) + + # Now, check that we'll produce correct code. + decl = p.simple_declaration(in_parser=False) + if unused: + self.assertIn("Py_UNUSED", decl) + else: + self.assertNotIn("Py_UNUSED", decl) + + # Make sure the Py_UNUSED macro is not used in the parser body. + parser_decl = p.simple_declaration(in_parser=True) + self.assertNotIn("Py_UNUSED", parser_decl) + + def test_scaffolding(self): + # test repr on special values + self.assertEqual(repr(unspecified), '') + self.assertEqual(repr(NULL), '') + + # test that fail fails + with support.captured_stdout() as stdout: + errmsg = 'The igloos are melting' + with self.assertRaisesRegex(ClinicError, errmsg) as cm: + fail(errmsg, filename='clown.txt', line_number=69) + exc = cm.exception + self.assertEqual(exc.filename, 'clown.txt') + self.assertEqual(exc.lineno, 69) + self.assertEqual(stdout.getvalue(), "") + + def test_non_ascii_character_in_docstring(self): + block = """ + module test + test.fn + a: int + á param docstring + docstring fü bár baß + """ + with support.captured_stdout() as stdout: + self.parse(block) + # The line numbers are off; this is a known limitation. + expected = dedent("""\ + Warning: + Non-ascii characters are not allowed in docstrings: 'á' + + Warning: + Non-ascii characters are not allowed in docstrings: 'ü', 'á', 'ß' + + """) + self.assertEqual(stdout.getvalue(), expected) + + def test_illegal_c_identifier(self): + err = "Illegal C identifier: 17a" + block = """ + module test + test.fn + a as 17a: int + """ + self.expect_failure(block, err, lineno=2) + + def test_cannot_convert_special_method(self): + err = "'__len__' is a special method and cannot be converted" + block = """ + class T "" "" + T.__len__ + """ + self.expect_failure(block, err, lineno=1) + + def test_cannot_specify_pydefault_without_default(self): + err = "You can't specify py_default without specifying a default value!" + block = """ + fn + a: object(py_default='NULL') + """ + self.expect_failure(block, err, lineno=1) + + def test_vararg_cannot_take_default_value(self): + err = "Vararg can't take a default value!" + block = """ + fn + *args: object = None + """ + self.expect_failure(block, err, lineno=1) + + def test_default_is_not_of_correct_type(self): + err = ("int_converter: default value 2.5 for field 'a' " + "is not of type 'int'") + block = """ + fn + a: int = 2.5 + """ + self.expect_failure(block, err, lineno=1) + + def test_invalid_legacy_converter(self): + err = "'fhi' is not a valid legacy converter" + block = """ + fn + a: 'fhi' + """ + self.expect_failure(block, err, lineno=1) + + def test_parent_class_or_module_does_not_exist(self): + err = "Parent class or module 'baz' does not exist" + block = """ + module m + baz.func + """ + self.expect_failure(block, err, lineno=1) + + def test_duplicate_param_name(self): + err = "You can't have two parameters named 'a'" + block = """ + module m + m.func + a: int + a: float + """ + self.expect_failure(block, err, lineno=3) + + def test_param_requires_custom_c_name(self): + err = "Parameter 'module' requires a custom C name" + block = """ + module m + m.func + module: int + """ + self.expect_failure(block, err, lineno=2) + + def test_state_func_docstring_assert_no_group(self): + err = "Function 'func' has a ']' without a matching '['" + block = """ + module m + m.func + ] + docstring + """ + self.expect_failure(block, err, lineno=2) + + def test_state_func_docstring_no_summary(self): + err = "Docstring for 'm.func' does not have a summary line!" + block = """ + module m + m.func + docstring1 + docstring2 + """ + self.expect_failure(block, err, lineno=3) + + def test_state_func_docstring_only_one_param_template(self): + err = "You may not specify {parameters} more than once in a docstring!" + block = """ + module m + m.func + docstring summary + + these are the params: + {parameters} + these are the params again: + {parameters} + """ + self.expect_failure(block, err, lineno=7) + + def test_kind_defining_class(self): + function = self.parse_function(""" + module m + class m.C "PyObject *" "" + m.C.meth + cls: defining_class + """, signatures_in_block=3, function_index=2) + p = function.parameters['cls'] + self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY) + + def test_disallow_defining_class_at_module_level(self): + err = "A 'defining_class' parameter cannot be defined at module level." + block = """ + module m + m.func + cls: defining_class + """ + self.expect_failure(block, err, lineno=2) + + +class ClinicExternalTest(TestCase): + maxDiff = None + + def setUp(self): + save_restore_converters(self) + + def run_clinic(self, *args): + with ( + support.captured_stdout() as out, + support.captured_stderr() as err, + self.assertRaises(SystemExit) as cm + ): + clinic.main(args) + return out.getvalue(), err.getvalue(), cm.exception.code + + def expect_success(self, *args): + out, err, code = self.run_clinic(*args) + if code != 0: + self.fail("\n".join([f"Unexpected failure: {args=}", out, err])) + self.assertEqual(err, "") + return out + + def expect_failure(self, *args): + out, err, code = self.run_clinic(*args) + self.assertNotEqual(code, 0, f"Unexpected success: {args=}") + return out, err + + def test_external(self): + CLINIC_TEST = 'clinic.test.c' + source = support.findfile(CLINIC_TEST) + with open(source, encoding='utf-8') as f: + orig_contents = f.read() + + # Run clinic CLI and verify that it does not complain. + self.addCleanup(unlink, TESTFN) + out = self.expect_success("-f", "-o", TESTFN, source) + self.assertEqual(out, "") + + with open(TESTFN, encoding='utf-8') as f: + new_contents = f.read() + + self.assertEqual(new_contents, orig_contents) + + def test_no_change(self): + # bpo-42398: Test that the destination file is left unchanged if the + # content does not change. Moreover, check also that the file + # modification time does not change in this case. + code = dedent(""" + /*[clinic input] + [clinic start generated code]*/ + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=da39a3ee5e6b4b0d]*/ + """) + with os_helper.temp_dir() as tmp_dir: + fn = os.path.join(tmp_dir, "test.c") + with open(fn, "w", encoding="utf-8") as f: + f.write(code) + pre_mtime = os.stat(fn).st_mtime_ns + self.expect_success(fn) + post_mtime = os.stat(fn).st_mtime_ns + # Don't change the file modification time + # if the content does not change + self.assertEqual(pre_mtime, post_mtime) + + def test_cli_force(self): + invalid_input = dedent(""" + /*[clinic input] + output preset block + module test + test.fn + a: int + [clinic start generated code]*/ + + const char *hand_edited = "output block is overwritten"; + /*[clinic end generated code: output=bogus input=bogus]*/ + """) + fail_msg = ( + "Checksum mismatch! Expected 'bogus', computed '2ed19'. " + "Suggested fix: remove all generated code including the end marker, " + "or use the '-f' option.\n" + ) + with os_helper.temp_dir() as tmp_dir: + fn = os.path.join(tmp_dir, "test.c") + with open(fn, "w", encoding="utf-8") as f: + f.write(invalid_input) + # First, run the CLI without -f and expect failure. + # Note, we cannot check the entire fail msg, because the path to + # the tmp file will change for every run. + _, err = self.expect_failure(fn) + self.assertTrue(err.endswith(fail_msg), + f"{err!r} does not end with {fail_msg!r}") + # Then, force regeneration; success expected. + out = self.expect_success("-f", fn) + self.assertEqual(out, "") + # Verify by checking the checksum. + checksum = ( + "/*[clinic end generated code: " + "output=0acbef4794cb933e input=9543a8d2da235301]*/\n" + ) + with open(fn, encoding='utf-8') as f: + generated = f.read() + self.assertTrue(generated.endswith(checksum), + (generated, checksum)) + + def test_cli_make(self): + c_code = dedent(""" + /*[clinic input] + [clinic start generated code]*/ + """) + py_code = "pass" + c_files = "file1.c", "file2.c" + py_files = "file1.py", "file2.py" + + def create_files(files, srcdir, code): + for fn in files: + path = os.path.join(srcdir, fn) + with open(path, "w", encoding="utf-8") as f: + f.write(code) + + with os_helper.temp_dir() as tmp_dir: + # add some folders, some C files and a Python file + create_files(c_files, tmp_dir, c_code) + create_files(py_files, tmp_dir, py_code) + + # create C files in externals/ dir + ext_path = os.path.join(tmp_dir, "externals") + with os_helper.temp_dir(path=ext_path) as externals: + create_files(c_files, externals, c_code) + + # run clinic in verbose mode with --make on tmpdir + out = self.expect_success("-v", "--make", "--srcdir", tmp_dir) + + # expect verbose mode to only mention the C files in tmp_dir + for filename in c_files: + with self.subTest(filename=filename): + path = os.path.join(tmp_dir, filename) + self.assertIn(path, out) + for filename in py_files: + with self.subTest(filename=filename): + path = os.path.join(tmp_dir, filename) + self.assertNotIn(path, out) + # don't expect C files from the externals dir + for filename in c_files: + with self.subTest(filename=filename): + path = os.path.join(ext_path, filename) + self.assertNotIn(path, out) + + def test_cli_make_exclude(self): + code = dedent(""" + /*[clinic input] + [clinic start generated code]*/ + """) + with os_helper.temp_dir(quiet=False) as tmp_dir: + # add some folders, some C files and a Python file + for fn in "file1.c", "file2.c", "file3.c", "file4.c": + path = os.path.join(tmp_dir, fn) + with open(path, "w", encoding="utf-8") as f: + f.write(code) + + # Run clinic in verbose mode with --make on tmpdir. + # Exclude file2.c and file3.c. + out = self.expect_success( + "-v", "--make", "--srcdir", tmp_dir, + "--exclude", os.path.join(tmp_dir, "file2.c"), + # The added ./ should be normalised away. + "--exclude", os.path.join(tmp_dir, "./file3.c"), + # Relative paths should also work. + "--exclude", "file4.c" + ) + + # expect verbose mode to only mention the C files in tmp_dir + self.assertIn("file1.c", out) + self.assertNotIn("file2.c", out) + self.assertNotIn("file3.c", out) + self.assertNotIn("file4.c", out) + + def test_cli_verbose(self): + with os_helper.temp_dir() as tmp_dir: + fn = os.path.join(tmp_dir, "test.c") + with open(fn, "w", encoding="utf-8") as f: + f.write("") + out = self.expect_success("-v", fn) + self.assertEqual(out.strip(), fn) + + def test_cli_help(self): + out = self.expect_success("-h") + self.assertIn("usage: clinic.py", out) + + def test_cli_converters(self): + prelude = dedent(""" + Legacy converters: + B C D L O S U Y Z Z# + b c d f h i l p s s# s* u u# w* y y# y* z z# z* + + Converters: + """) + expected_converters = ( + "bool", + "byte", + "char", + "defining_class", + "double", + "fildes", + "float", + "int", + "long", + "long_long", + "object", + "Py_buffer", + "Py_complex", + "Py_ssize_t", + "Py_UNICODE", + "PyByteArrayObject", + "PyBytesObject", + "self", + "short", + "size_t", + "slice_index", + "str", + "unicode", + "unicode_fs_decoded", + "unicode_fs_encoded", + "unsigned_char", + "unsigned_int", + "unsigned_long", + "unsigned_long_long", + "unsigned_short", + ) + finale = dedent(""" + Return converters: + bool() + double() + float() + int() + long() + object() + Py_ssize_t() + size_t() + unsigned_int() + unsigned_long() + + All converters also accept (c_default=None, py_default=None, annotation=None). + All return converters also accept (py_default=None). + """) + out = self.expect_success("--converters") + # We cannot simply compare the output, because the repr of the *accept* + # param may change (it's a set, thus unordered). So, let's compare the + # start and end of the expected output, and then assert that the + # converters appear lined up in alphabetical order. + self.assertTrue(out.startswith(prelude), out) + self.assertTrue(out.endswith(finale), out) + + out = out.removeprefix(prelude) + out = out.removesuffix(finale) + lines = out.split("\n") + for converter, line in zip(expected_converters, lines): + line = line.lstrip() + with self.subTest(converter=converter): + self.assertTrue( + line.startswith(converter), + f"expected converter {converter!r}, got {line!r}" + ) + + def test_cli_fail_converters_and_filename(self): + _, err = self.expect_failure("--converters", "test.c") + msg = "can't specify --converters and a filename at the same time" + self.assertIn(msg, err) + + def test_cli_fail_no_filename(self): + _, err = self.expect_failure() + self.assertIn("no input files", err) + + def test_cli_fail_output_and_multiple_files(self): + _, err = self.expect_failure("-o", "out.c", "input.c", "moreinput.c") + msg = "error: can't use -o with multiple filenames" + self.assertIn(msg, err) + + def test_cli_fail_filename_or_output_and_make(self): + msg = "can't use -o or filenames with --make" + for opts in ("-o", "out.c"), ("filename.c",): + with self.subTest(opts=opts): + _, err = self.expect_failure("--make", *opts) + self.assertIn(msg, err) + + def test_cli_fail_make_without_srcdir(self): + _, err = self.expect_failure("--make", "--srcdir", "") + msg = "error: --srcdir must not be empty with --make" + self.assertIn(msg, err) + + def test_file_dest(self): + block = dedent(""" + /*[clinic input] + destination test new file {path}.h + output everything test + func + a: object + / + [clinic start generated code]*/ + """) + expected_checksum_line = ( + "/*[clinic end generated code: " + "output=da39a3ee5e6b4b0d input=b602ab8e173ac3bd]*/\n" + ) + expected_output = dedent("""\ + /*[clinic input] + preserve + [clinic start generated code]*/ + + PyDoc_VAR(func__doc__); + + PyDoc_STRVAR(func__doc__, + "func($module, a, /)\\n" + "--\\n" + "\\n"); + + #define FUNC_METHODDEF \\ + {"func", (PyCFunction)func, METH_O, func__doc__}, + + static PyObject * + func(PyObject *module, PyObject *a) + /*[clinic end generated code: output=3dde2d13002165b9 input=a9049054013a1b77]*/ + """) + with os_helper.temp_dir() as tmp_dir: + in_fn = os.path.join(tmp_dir, "test.c") + out_fn = os.path.join(tmp_dir, "test.c.h") + with open(in_fn, "w", encoding="utf-8") as f: + f.write(block) + with open(out_fn, "w", encoding="utf-8") as f: + f.write("") # Write an empty output file! + # Clinic should complain about the empty output file. + _, err = self.expect_failure(in_fn) + expected_err = (f"Modified destination file {out_fn!r}; " + "not overwriting!") + self.assertIn(expected_err, err) + # Run clinic again, this time with the -f option. + _ = self.expect_success("-f", in_fn) + # Read back the generated output. + with open(in_fn, encoding="utf-8") as f: + data = f.read() + expected_block = f"{block}{expected_checksum_line}" + self.assertEqual(data, expected_block) + with open(out_fn, encoding="utf-8") as f: + data = f.read() + self.assertEqual(data, expected_output) + +try: + import _testclinic as ac_tester +except ImportError: + ac_tester = None + +@unittest.skipIf(ac_tester is None, "_testclinic is missing") +class ClinicFunctionalTest(unittest.TestCase): + locals().update((name, getattr(ac_tester, name)) + for name in dir(ac_tester) if name.startswith('test_')) + + def check_depr(self, regex, fn, /, *args, **kwds): + with self.assertWarnsRegex(DeprecationWarning, regex) as cm: + # Record the line number, so we're sure we've got the correct stack + # level on the deprecation warning. + _, lineno = fn(*args, **kwds), sys._getframe().f_lineno + self.assertEqual(cm.filename, __file__) + self.assertEqual(cm.lineno, lineno) + + def check_depr_star(self, pnames, fn, /, *args, name=None, **kwds): + if name is None: + name = fn.__qualname__ + if isinstance(fn, type): + name = f'{fn.__module__}.{name}' + regex = ( + fr"Passing( more than)?( [0-9]+)? positional argument(s)? to " + fr"{re.escape(name)}\(\) is deprecated. Parameters? {pnames} will " + fr"become( a)? keyword-only parameters? in Python 3\.14" + ) + self.check_depr(regex, fn, *args, **kwds) + + def check_depr_kwd(self, pnames, fn, *args, name=None, **kwds): + if name is None: + name = fn.__qualname__ + if isinstance(fn, type): + name = f'{fn.__module__}.{name}' + pl = 's' if ' ' in pnames else '' + regex = ( + fr"Passing keyword argument{pl} {pnames} to " + fr"{re.escape(name)}\(\) is deprecated. Parameter{pl} {pnames} " + fr"will become positional-only in Python 3\.14." + ) + self.check_depr(regex, fn, *args, **kwds) + + def test_objects_converter(self): + with self.assertRaises(TypeError): + ac_tester.objects_converter() + self.assertEqual(ac_tester.objects_converter(1, 2), (1, 2)) + self.assertEqual(ac_tester.objects_converter([], 'whatever class'), ([], 'whatever class')) + self.assertEqual(ac_tester.objects_converter(1), (1, None)) + + def test_bytes_object_converter(self): + with self.assertRaises(TypeError): + ac_tester.bytes_object_converter(1) + self.assertEqual(ac_tester.bytes_object_converter(b'BytesObject'), (b'BytesObject',)) + + def test_byte_array_object_converter(self): + with self.assertRaises(TypeError): + ac_tester.byte_array_object_converter(1) + byte_arr = bytearray(b'ByteArrayObject') + self.assertEqual(ac_tester.byte_array_object_converter(byte_arr), (byte_arr,)) + + def test_unicode_converter(self): + with self.assertRaises(TypeError): + ac_tester.unicode_converter(1) + self.assertEqual(ac_tester.unicode_converter('unicode'), ('unicode',)) + + def test_bool_converter(self): + with self.assertRaises(TypeError): + ac_tester.bool_converter(False, False, 'not a int') + self.assertEqual(ac_tester.bool_converter(), (True, True, True)) + self.assertEqual(ac_tester.bool_converter('', [], 5), (False, False, True)) + self.assertEqual(ac_tester.bool_converter(('not empty',), {1: 2}, 0), (True, True, False)) + + def test_bool_converter_c_default(self): + self.assertEqual(ac_tester.bool_converter_c_default(), (1, 0, -2, -3)) + self.assertEqual(ac_tester.bool_converter_c_default(False, True, False, True), + (0, 1, 0, 1)) + + def test_char_converter(self): + with self.assertRaises(TypeError): + ac_tester.char_converter(1) + with self.assertRaises(TypeError): + ac_tester.char_converter(b'ab') + chars = [b'A', b'\a', b'\b', b'\t', b'\n', b'\v', b'\f', b'\r', b'"', b"'", b'?', b'\\', b'\000', b'\377'] + expected = tuple(ord(c) for c in chars) + self.assertEqual(ac_tester.char_converter(), expected) + chars = [b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'a', b'b', b'c', b'd'] + expected = tuple(ord(c) for c in chars) + self.assertEqual(ac_tester.char_converter(*chars), expected) + + def test_unsigned_char_converter(self): + from _testcapi import UCHAR_MAX + with self.assertRaises(OverflowError): + ac_tester.unsigned_char_converter(-1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_char_converter(UCHAR_MAX + 1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_char_converter(0, UCHAR_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.unsigned_char_converter([]) + self.assertEqual(ac_tester.unsigned_char_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, UCHAR_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, (UCHAR_MAX + 1) * 3 + 123), (0, 0, 123)) + + def test_short_converter(self): + from _testcapi import SHRT_MIN, SHRT_MAX + with self.assertRaises(OverflowError): + ac_tester.short_converter(SHRT_MIN - 1) + with self.assertRaises(OverflowError): + ac_tester.short_converter(SHRT_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.short_converter([]) + self.assertEqual(ac_tester.short_converter(-1234), (-1234,)) + self.assertEqual(ac_tester.short_converter(4321), (4321,)) + + def test_unsigned_short_converter(self): + from _testcapi import USHRT_MAX + with self.assertRaises(ValueError): + ac_tester.unsigned_short_converter(-1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_short_converter(USHRT_MAX + 1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_short_converter(0, USHRT_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.unsigned_short_converter([]) + self.assertEqual(ac_tester.unsigned_short_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, USHRT_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, (USHRT_MAX + 1) * 3 + 123), (0, 0, 123)) + + def test_int_converter(self): + from _testcapi import INT_MIN, INT_MAX + with self.assertRaises(OverflowError): + ac_tester.int_converter(INT_MIN - 1) + with self.assertRaises(OverflowError): + ac_tester.int_converter(INT_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.int_converter(1, 2, 3) + with self.assertRaises(TypeError): + ac_tester.int_converter([]) + self.assertEqual(ac_tester.int_converter(), (12, 34, 45)) + self.assertEqual(ac_tester.int_converter(1, 2, '3'), (1, 2, ord('3'))) + + def test_unsigned_int_converter(self): + from _testcapi import UINT_MAX + with self.assertRaises(ValueError): + ac_tester.unsigned_int_converter(-1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_int_converter(UINT_MAX + 1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_int_converter(0, UINT_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.unsigned_int_converter([]) + self.assertEqual(ac_tester.unsigned_int_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, UINT_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, (UINT_MAX + 1) * 3 + 123), (0, 0, 123)) + + def test_long_converter(self): + from _testcapi import LONG_MIN, LONG_MAX + with self.assertRaises(OverflowError): + ac_tester.long_converter(LONG_MIN - 1) + with self.assertRaises(OverflowError): + ac_tester.long_converter(LONG_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.long_converter([]) + self.assertEqual(ac_tester.long_converter(), (12,)) + self.assertEqual(ac_tester.long_converter(-1234), (-1234,)) + + def test_unsigned_long_converter(self): + from _testcapi import ULONG_MAX + with self.assertRaises(ValueError): + ac_tester.unsigned_long_converter(-1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_long_converter(ULONG_MAX + 1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_long_converter(0, ULONG_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.unsigned_long_converter([]) + self.assertEqual(ac_tester.unsigned_long_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, ULONG_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, (ULONG_MAX + 1) * 3 + 123), (0, 0, 123)) + + def test_long_long_converter(self): + from _testcapi import LLONG_MIN, LLONG_MAX + with self.assertRaises(OverflowError): + ac_tester.long_long_converter(LLONG_MIN - 1) + with self.assertRaises(OverflowError): + ac_tester.long_long_converter(LLONG_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.long_long_converter([]) + self.assertEqual(ac_tester.long_long_converter(), (12,)) + self.assertEqual(ac_tester.long_long_converter(-1234), (-1234,)) + + def test_unsigned_long_long_converter(self): + from _testcapi import ULLONG_MAX + with self.assertRaises(ValueError): + ac_tester.unsigned_long_long_converter(-1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_long_long_converter(ULLONG_MAX + 1) + with self.assertRaises(OverflowError): + ac_tester.unsigned_long_long_converter(0, ULLONG_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.unsigned_long_long_converter([]) + self.assertEqual(ac_tester.unsigned_long_long_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, ULLONG_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, (ULLONG_MAX + 1) * 3 + 123), (0, 0, 123)) + + def test_py_ssize_t_converter(self): + from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX + with self.assertRaises(OverflowError): + ac_tester.py_ssize_t_converter(PY_SSIZE_T_MIN - 1) + with self.assertRaises(OverflowError): + ac_tester.py_ssize_t_converter(PY_SSIZE_T_MAX + 1) + with self.assertRaises(TypeError): + ac_tester.py_ssize_t_converter([]) + self.assertEqual(ac_tester.py_ssize_t_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.py_ssize_t_converter(1, 2, None), (1, 2, 56)) + + def test_slice_index_converter(self): + from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX + with self.assertRaises(TypeError): + ac_tester.slice_index_converter([]) + self.assertEqual(ac_tester.slice_index_converter(), (12, 34, 56)) + self.assertEqual(ac_tester.slice_index_converter(1, 2, None), (1, 2, 56)) + self.assertEqual(ac_tester.slice_index_converter(PY_SSIZE_T_MAX, PY_SSIZE_T_MAX + 1, PY_SSIZE_T_MAX + 1234), + (PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX)) + self.assertEqual(ac_tester.slice_index_converter(PY_SSIZE_T_MIN, PY_SSIZE_T_MIN - 1, PY_SSIZE_T_MIN - 1234), + (PY_SSIZE_T_MIN, PY_SSIZE_T_MIN, PY_SSIZE_T_MIN)) + + def test_size_t_converter(self): + with self.assertRaises(ValueError): + ac_tester.size_t_converter(-1) + with self.assertRaises(TypeError): + ac_tester.size_t_converter([]) + self.assertEqual(ac_tester.size_t_converter(), (12,)) + + def test_float_converter(self): + with self.assertRaises(TypeError): + ac_tester.float_converter([]) + self.assertEqual(ac_tester.float_converter(), (12.5,)) + self.assertEqual(ac_tester.float_converter(-0.5), (-0.5,)) + + def test_double_converter(self): + with self.assertRaises(TypeError): + ac_tester.double_converter([]) + self.assertEqual(ac_tester.double_converter(), (12.5,)) + self.assertEqual(ac_tester.double_converter(-0.5), (-0.5,)) + + def test_py_complex_converter(self): + with self.assertRaises(TypeError): + ac_tester.py_complex_converter([]) + self.assertEqual(ac_tester.py_complex_converter(complex(1, 2)), (complex(1, 2),)) + self.assertEqual(ac_tester.py_complex_converter(complex('-1-2j')), (complex('-1-2j'),)) + self.assertEqual(ac_tester.py_complex_converter(-0.5), (-0.5,)) + self.assertEqual(ac_tester.py_complex_converter(10), (10,)) + + def test_str_converter(self): + with self.assertRaises(TypeError): + ac_tester.str_converter(1) + with self.assertRaises(TypeError): + ac_tester.str_converter('a', 'b', 'c') + with self.assertRaises(ValueError): + ac_tester.str_converter('a', b'b\0b', 'c') + self.assertEqual(ac_tester.str_converter('a', b'b', 'c'), ('a', 'b', 'c')) + self.assertEqual(ac_tester.str_converter('a', b'b', b'c'), ('a', 'b', 'c')) + self.assertEqual(ac_tester.str_converter('a', b'b', 'c\0c'), ('a', 'b', 'c\0c')) + + def test_str_converter_encoding(self): + with self.assertRaises(TypeError): + ac_tester.str_converter_encoding(1) + self.assertEqual(ac_tester.str_converter_encoding('a', 'b', 'c'), ('a', 'b', 'c')) + with self.assertRaises(TypeError): + ac_tester.str_converter_encoding('a', b'b\0b', 'c') + self.assertEqual(ac_tester.str_converter_encoding('a', b'b', bytearray([ord('c')])), ('a', 'b', 'c')) + self.assertEqual(ac_tester.str_converter_encoding('a', b'b', bytearray([ord('c'), 0, ord('c')])), + ('a', 'b', 'c\x00c')) + self.assertEqual(ac_tester.str_converter_encoding('a', b'b', b'c\x00c'), ('a', 'b', 'c\x00c')) + + def test_py_buffer_converter(self): + with self.assertRaises(TypeError): + ac_tester.py_buffer_converter('a', 'b') + self.assertEqual(ac_tester.py_buffer_converter('abc', bytearray([1, 2, 3])), (b'abc', b'\x01\x02\x03')) + + def test_keywords(self): + self.assertEqual(ac_tester.keywords(1, 2), (1, 2)) + self.assertEqual(ac_tester.keywords(1, b=2), (1, 2)) + self.assertEqual(ac_tester.keywords(a=1, b=2), (1, 2)) + + def test_keywords_kwonly(self): + with self.assertRaises(TypeError): + ac_tester.keywords_kwonly(1, 2) + self.assertEqual(ac_tester.keywords_kwonly(1, b=2), (1, 2)) + self.assertEqual(ac_tester.keywords_kwonly(a=1, b=2), (1, 2)) + + def test_keywords_opt(self): + self.assertEqual(ac_tester.keywords_opt(1), (1, None, None)) + self.assertEqual(ac_tester.keywords_opt(1, 2), (1, 2, None)) + self.assertEqual(ac_tester.keywords_opt(1, 2, 3), (1, 2, 3)) + self.assertEqual(ac_tester.keywords_opt(1, b=2), (1, 2, None)) + self.assertEqual(ac_tester.keywords_opt(1, 2, c=3), (1, 2, 3)) + self.assertEqual(ac_tester.keywords_opt(a=1, c=3), (1, None, 3)) + self.assertEqual(ac_tester.keywords_opt(a=1, b=2, c=3), (1, 2, 3)) + + def test_keywords_opt_kwonly(self): + self.assertEqual(ac_tester.keywords_opt_kwonly(1), (1, None, None, None)) + self.assertEqual(ac_tester.keywords_opt_kwonly(1, 2), (1, 2, None, None)) + with self.assertRaises(TypeError): + ac_tester.keywords_opt_kwonly(1, 2, 3) + self.assertEqual(ac_tester.keywords_opt_kwonly(1, b=2), (1, 2, None, None)) + self.assertEqual(ac_tester.keywords_opt_kwonly(1, 2, c=3), (1, 2, 3, None)) + self.assertEqual(ac_tester.keywords_opt_kwonly(a=1, c=3), (1, None, 3, None)) + self.assertEqual(ac_tester.keywords_opt_kwonly(a=1, b=2, c=3, d=4), (1, 2, 3, 4)) + + def test_keywords_kwonly_opt(self): + self.assertEqual(ac_tester.keywords_kwonly_opt(1), (1, None, None)) + with self.assertRaises(TypeError): + ac_tester.keywords_kwonly_opt(1, 2) + self.assertEqual(ac_tester.keywords_kwonly_opt(1, b=2), (1, 2, None)) + self.assertEqual(ac_tester.keywords_kwonly_opt(a=1, c=3), (1, None, 3)) + self.assertEqual(ac_tester.keywords_kwonly_opt(a=1, b=2, c=3), (1, 2, 3)) + + def test_posonly_keywords(self): + with self.assertRaises(TypeError): + ac_tester.posonly_keywords(1) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords(a=1, b=2) + self.assertEqual(ac_tester.posonly_keywords(1, 2), (1, 2)) + self.assertEqual(ac_tester.posonly_keywords(1, b=2), (1, 2)) + + def test_posonly_kwonly(self): + with self.assertRaises(TypeError): + ac_tester.posonly_kwonly(1) + with self.assertRaises(TypeError): + ac_tester.posonly_kwonly(1, 2) + with self.assertRaises(TypeError): + ac_tester.posonly_kwonly(a=1, b=2) + self.assertEqual(ac_tester.posonly_kwonly(1, b=2), (1, 2)) + + def test_posonly_keywords_kwonly(self): + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly(1) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly(1, 2, 3) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly(a=1, b=2, c=3) + self.assertEqual(ac_tester.posonly_keywords_kwonly(1, 2, c=3), (1, 2, 3)) + self.assertEqual(ac_tester.posonly_keywords_kwonly(1, b=2, c=3), (1, 2, 3)) + + def test_posonly_keywords_opt(self): + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_opt(1) + self.assertEqual(ac_tester.posonly_keywords_opt(1, 2), (1, 2, None, None)) + self.assertEqual(ac_tester.posonly_keywords_opt(1, 2, 3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_keywords_opt(1, 2, 3, 4), (1, 2, 3, 4)) + self.assertEqual(ac_tester.posonly_keywords_opt(1, b=2), (1, 2, None, None)) + self.assertEqual(ac_tester.posonly_keywords_opt(1, 2, c=3), (1, 2, 3, None)) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_opt(a=1, b=2, c=3, d=4) + self.assertEqual(ac_tester.posonly_keywords_opt(1, b=2, c=3, d=4), (1, 2, 3, 4)) + + def test_posonly_opt_keywords_opt(self): + self.assertEqual(ac_tester.posonly_opt_keywords_opt(1), (1, None, None, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt(1, 2), (1, 2, None, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt(1, 2, 3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt(1, 2, 3, 4), (1, 2, 3, 4)) + with self.assertRaises(TypeError): + ac_tester.posonly_opt_keywords_opt(1, b=2) + self.assertEqual(ac_tester.posonly_opt_keywords_opt(1, 2, c=3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt(1, 2, c=3, d=4), (1, 2, 3, 4)) + with self.assertRaises(TypeError): + ac_tester.posonly_opt_keywords_opt(a=1, b=2, c=3, d=4) + + def test_posonly_kwonly_opt(self): + with self.assertRaises(TypeError): + ac_tester.posonly_kwonly_opt(1) + with self.assertRaises(TypeError): + ac_tester.posonly_kwonly_opt(1, 2) + self.assertEqual(ac_tester.posonly_kwonly_opt(1, b=2), (1, 2, None, None)) + self.assertEqual(ac_tester.posonly_kwonly_opt(1, b=2, c=3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_kwonly_opt(1, b=2, c=3, d=4), (1, 2, 3, 4)) + with self.assertRaises(TypeError): + ac_tester.posonly_kwonly_opt(a=1, b=2, c=3, d=4) + + def test_posonly_opt_kwonly_opt(self): + self.assertEqual(ac_tester.posonly_opt_kwonly_opt(1), (1, None, None, None)) + self.assertEqual(ac_tester.posonly_opt_kwonly_opt(1, 2), (1, 2, None, None)) + with self.assertRaises(TypeError): + ac_tester.posonly_opt_kwonly_opt(1, 2, 3) + with self.assertRaises(TypeError): + ac_tester.posonly_opt_kwonly_opt(1, b=2) + self.assertEqual(ac_tester.posonly_opt_kwonly_opt(1, 2, c=3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_opt_kwonly_opt(1, 2, c=3, d=4), (1, 2, 3, 4)) + + def test_posonly_keywords_kwonly_opt(self): + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly_opt(1) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly_opt(1, 2) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly_opt(1, b=2) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly_opt(1, 2, 3) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_kwonly_opt(a=1, b=2, c=3) + self.assertEqual(ac_tester.posonly_keywords_kwonly_opt(1, 2, c=3), (1, 2, 3, None, None)) + self.assertEqual(ac_tester.posonly_keywords_kwonly_opt(1, b=2, c=3), (1, 2, 3, None, None)) + self.assertEqual(ac_tester.posonly_keywords_kwonly_opt(1, 2, c=3, d=4), (1, 2, 3, 4, None)) + self.assertEqual(ac_tester.posonly_keywords_kwonly_opt(1, 2, c=3, d=4, e=5), (1, 2, 3, 4, 5)) + + def test_posonly_keywords_opt_kwonly_opt(self): + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_opt_kwonly_opt(1) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, 2), (1, 2, None, None, None)) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, b=2), (1, 2, None, None, None)) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_opt_kwonly_opt(1, 2, 3, 4) + with self.assertRaises(TypeError): + ac_tester.posonly_keywords_opt_kwonly_opt(a=1, b=2) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, 2, c=3), (1, 2, 3, None, None)) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, b=2, c=3), (1, 2, 3, None, None)) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, 2, 3, d=4), (1, 2, 3, 4, None)) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, 2, c=3, d=4), (1, 2, 3, 4, None)) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, 2, 3, d=4, e=5), (1, 2, 3, 4, 5)) + self.assertEqual(ac_tester.posonly_keywords_opt_kwonly_opt(1, 2, c=3, d=4, e=5), (1, 2, 3, 4, 5)) + + def test_posonly_opt_keywords_opt_kwonly_opt(self): + self.assertEqual(ac_tester.posonly_opt_keywords_opt_kwonly_opt(1), (1, None, None, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, 2), (1, 2, None, None)) + with self.assertRaises(TypeError): + ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, b=2) + self.assertEqual(ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, 2, 3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, 2, c=3), (1, 2, 3, None)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, 2, 3, d=4), (1, 2, 3, 4)) + self.assertEqual(ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, 2, c=3, d=4), (1, 2, 3, 4)) + with self.assertRaises(TypeError): + ac_tester.posonly_opt_keywords_opt_kwonly_opt(1, 2, 3, 4) + + def test_keyword_only_parameter(self): + with self.assertRaises(TypeError): + ac_tester.keyword_only_parameter() + with self.assertRaises(TypeError): + ac_tester.keyword_only_parameter(1) + self.assertEqual(ac_tester.keyword_only_parameter(a=1), (1,)) + + def test_posonly_vararg(self): + with self.assertRaises(TypeError): + ac_tester.posonly_vararg() + self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ())) + self.assertEqual(ac_tester.posonly_vararg(1, b=2), (1, 2, ())) + self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4))) + with self.assertRaises(TypeError): + ac_tester.posonly_vararg(b=4) + with self.assertRaises(TypeError): + ac_tester.posonly_vararg(1, 2, 3, b=4) + + def test_vararg(self): + with self.assertRaises(TypeError): + ac_tester.vararg() + with self.assertRaises(TypeError): + ac_tester.vararg(1, b=2) + self.assertEqual(ac_tester.vararg(1, 2, 3, 4), (1, (2, 3, 4))) + + def test_vararg_with_default(self): + fn = ac_tester.vararg_with_default + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, 1, a=2) + self.assertEqual(fn(1, b=2), (1, (), True)) + self.assertEqual(fn(1, 2, 3, 4), (1, (2, 3, 4), False)) + self.assertEqual(fn(1, 2, 3, 4, b=5), (1, (2, 3, 4), True)) + self.assertEqual(fn(a=1), (1, (), False)) + self.assertEqual(fn(a=1, b=2), (1, (), True)) + + def test_vararg_with_default2(self): + fn = ac_tester.vararg_with_default2 + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, 1, a=2) + self.assertEqual(fn(1, b=2), (1, (), 2, None)) + self.assertEqual(fn(1, b=2, c=3), (1, (), 2, 3)) + self.assertEqual(fn(1, 2, 3), (1, (2, 3), None, None)) + self.assertEqual(fn(1, 2, 3, b=4), (1, (2, 3), 4, None)) + self.assertEqual(fn(1, 2, 3, b=4, c=5), (1, (2, 3), 4, 5)) + self.assertEqual(fn(a=1), (1, (), None, None)) + self.assertEqual(fn(a=1, b=2), (1, (), 2, None)) + self.assertEqual(fn(a=1, b=2, c=3), (1, (), 2, 3)) + + def test_vararg_with_only_defaults(self): + self.assertEqual(ac_tester.vararg_with_only_defaults(), ((), None)) + self.assertEqual(ac_tester.vararg_with_only_defaults(b=2), ((), 2)) + self.assertEqual(ac_tester.vararg_with_only_defaults(1, b=2), ((1, ), 2)) + self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None)) + self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5)) + + def test_vararg_kwonly_req_opt(self): + fn = ac_tester.vararg_kwonly_req_opt + self.assertRaises(TypeError, fn) + self.assertEqual(fn(a=1), ((), 1, None, None)) + self.assertEqual(fn(a=1, b=2), ((), 1, 2, None)) + self.assertEqual(fn(a=1, b=2, c=3), ((), 1, 2, 3)) + self.assertRaises(TypeError, fn, 1) + self.assertEqual(fn(1, a=2), ((1,), 2, None, None)) + self.assertEqual(fn(1, a=2, b=3), ((1,), 2, 3, None)) + self.assertEqual(fn(1, a=2, b=3, c=4), ((1,), 2, 3, 4)) + + def test_gh_32092_oob(self): + ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6) + + def test_gh_32092_kw_pass(self): + ac_tester.gh_32092_kw_pass(1, 2, 3) + + def test_gh_99233_refcount(self): + arg = '*A unique string is not referenced by anywhere else.*' + arg_refcount_origin = sys.getrefcount(arg) + ac_tester.gh_99233_refcount(arg) + arg_refcount_after = sys.getrefcount(arg) + self.assertEqual(arg_refcount_origin, arg_refcount_after) + + def test_gh_99240_double_free(self): + err = re.escape( + "gh_99240_double_free() argument 2 must be encoded string " + "without null bytes, not str" + ) + with self.assertRaisesRegex(TypeError, err): + ac_tester.gh_99240_double_free('a', '\0b') + + def test_null_or_tuple_for_varargs(self): + # All of these should not crash: + valid_args_for_test = [ + (('a',), {}, + ('a', (), False)), + (('a', 1, 2, 3), {'covariant': True}, + ('a', (1, 2, 3), True)), + ((), {'name': 'a'}, + ('a', (), False)), + ((), {'name': 'a', 'covariant': True}, + ('a', (), True)), + ((), {'covariant': True, 'name': 'a'}, + ('a', (), True)), + ] + for args, kwargs, expected in valid_args_for_test: + with self.subTest(args=args, kwargs=kwargs): + self.assertEqual( + ac_tester.null_or_tuple_for_varargs(*args, **kwargs), + expected, + ) + + def test_null_or_tuple_for_varargs_error(self): + with self.assertRaises(TypeError): + ac_tester.null_or_tuple_for_varargs(covariant=True) + with self.assertRaises(TypeError): + ac_tester.null_or_tuple_for_varargs(1, name='a') + with self.assertRaises(TypeError): + ac_tester.null_or_tuple_for_varargs(1, 2, 3, name='a', covariant=True) + with self.assertRaises(TypeError): + ac_tester.null_or_tuple_for_varargs(1, 2, 3, covariant=True, name='a') + + def test_cloned_func_exception_message(self): + incorrect_arg = -1 # f1() and f2() accept a single str + with self.assertRaisesRegex(TypeError, "clone_f1"): + ac_tester.clone_f1(incorrect_arg) + with self.assertRaisesRegex(TypeError, "clone_f2"): + ac_tester.clone_f2(incorrect_arg) + + def test_cloned_func_with_converter_exception_message(self): + for name in "clone_with_conv_f1", "clone_with_conv_f2": + with self.subTest(name=name): + func = getattr(ac_tester, name) + self.assertEqual(func(), name) + + def test_get_defining_class(self): + obj = ac_tester.TestClass() + meth = obj.get_defining_class + self.assertIs(obj.get_defining_class(), ac_tester.TestClass) + + # 'defining_class' argument is a positional only argument + with self.assertRaises(TypeError): + obj.get_defining_class_arg(cls=ac_tester.TestClass) + + check = partial(self.assertRaisesRegex, TypeError, "no arguments") + check(meth, 1) + check(meth, a=1) + + def test_get_defining_class_capi(self): + from _testcapi import pyobject_vectorcall + obj = ac_tester.TestClass() + meth = obj.get_defining_class + pyobject_vectorcall(meth, None, None) + pyobject_vectorcall(meth, (), None) + pyobject_vectorcall(meth, (), ()) + pyobject_vectorcall(meth, None, ()) + self.assertIs(pyobject_vectorcall(meth, (), ()), ac_tester.TestClass) + + check = partial(self.assertRaisesRegex, TypeError, "no arguments") + check(pyobject_vectorcall, meth, (1,), None) + check(pyobject_vectorcall, meth, (1,), ("a",)) + + def test_get_defining_class_arg(self): + obj = ac_tester.TestClass() + self.assertEqual(obj.get_defining_class_arg("arg"), + (ac_tester.TestClass, "arg")) + self.assertEqual(obj.get_defining_class_arg(arg=123), + (ac_tester.TestClass, 123)) + + # 'defining_class' argument is a positional only argument + with self.assertRaises(TypeError): + obj.get_defining_class_arg(cls=ac_tester.TestClass, arg="arg") + + # wrong number of arguments + with self.assertRaises(TypeError): + obj.get_defining_class_arg() + with self.assertRaises(TypeError): + obj.get_defining_class_arg("arg1", "arg2") + + def test_depr_star_new(self): + cls = ac_tester.DeprStarNew + cls() + cls(a=None) + self.check_depr_star("'a'", cls, None) + + def test_depr_star_new_cloned(self): + fn = ac_tester.DeprStarNew().cloned + fn() + fn(a=None) + self.check_depr_star("'a'", fn, None, name='_testclinic.DeprStarNew.cloned') + + def test_depr_star_init(self): + cls = ac_tester.DeprStarInit + cls() + cls(a=None) + self.check_depr_star("'a'", cls, None) + + def test_depr_star_init_cloned(self): + fn = ac_tester.DeprStarInit().cloned + fn() + fn(a=None) + self.check_depr_star("'a'", fn, None, name='_testclinic.DeprStarInit.cloned') + + def test_depr_star_init_noinline(self): + cls = ac_tester.DeprStarInitNoInline + self.assertRaises(TypeError, cls, "a") + cls(a="a", b="b") + cls(a="a", b="b", c="c") + cls("a", b="b") + cls("a", b="b", c="c") + check = partial(self.check_depr_star, "'b' and 'c'", cls) + check("a", "b") + check("a", "b", "c") + check("a", "b", c="c") + self.assertRaises(TypeError, cls, "a", "b", "c", "d") + + def test_depr_kwd_new(self): + cls = ac_tester.DeprKwdNew + cls() + cls(None) + self.check_depr_kwd("'a'", cls, a=None) + + def test_depr_kwd_init(self): + cls = ac_tester.DeprKwdInit + cls() + cls(None) + self.check_depr_kwd("'a'", cls, a=None) + + def test_depr_kwd_init_noinline(self): + cls = ac_tester.DeprKwdInitNoInline + cls = ac_tester.depr_star_noinline + self.assertRaises(TypeError, cls, "a") + cls(a="a", b="b") + cls(a="a", b="b", c="c") + cls("a", b="b") + cls("a", b="b", c="c") + check = partial(self.check_depr_star, "'b' and 'c'", cls) + check("a", "b") + check("a", "b", "c") + check("a", "b", c="c") + self.assertRaises(TypeError, cls, "a", "b", "c", "d") + + def test_depr_star_pos0_len1(self): + fn = ac_tester.depr_star_pos0_len1 + fn(a=None) + self.check_depr_star("'a'", fn, "a") + + def test_depr_star_pos0_len2(self): + fn = ac_tester.depr_star_pos0_len2 + fn(a=0, b=0) + check = partial(self.check_depr_star, "'a' and 'b'", fn) + check("a", b=0) + check("a", "b") + + def test_depr_star_pos0_len3_with_kwd(self): + fn = ac_tester.depr_star_pos0_len3_with_kwd + fn(a=0, b=0, c=0, d=0) + check = partial(self.check_depr_star, "'a', 'b' and 'c'", fn) + check("a", b=0, c=0, d=0) + check("a", "b", c=0, d=0) + check("a", "b", "c", d=0) + + def test_depr_star_pos1_len1_opt(self): + fn = ac_tester.depr_star_pos1_len1_opt + fn(a=0, b=0) + fn("a", b=0) + fn(a=0) # b is optional + check = partial(self.check_depr_star, "'b'", fn) + check("a", "b") + + def test_depr_star_pos1_len1(self): + fn = ac_tester.depr_star_pos1_len1 + fn(a=0, b=0) + fn("a", b=0) + check = partial(self.check_depr_star, "'b'", fn) + check("a", "b") + + def test_depr_star_pos1_len2_with_kwd(self): + fn = ac_tester.depr_star_pos1_len2_with_kwd + fn(a=0, b=0, c=0, d=0), + fn("a", b=0, c=0, d=0), + check = partial(self.check_depr_star, "'b' and 'c'", fn) + check("a", "b", c=0, d=0), + check("a", "b", "c", d=0), + + def test_depr_star_pos2_len1(self): + fn = ac_tester.depr_star_pos2_len1 + fn(a=0, b=0, c=0) + fn("a", b=0, c=0) + fn("a", "b", c=0) + check = partial(self.check_depr_star, "'c'", fn) + check("a", "b", "c") + + def test_depr_star_pos2_len2(self): + fn = ac_tester.depr_star_pos2_len2 + fn(a=0, b=0, c=0, d=0) + fn("a", b=0, c=0, d=0) + fn("a", "b", c=0, d=0) + check = partial(self.check_depr_star, "'c' and 'd'", fn) + check("a", "b", "c", d=0) + check("a", "b", "c", "d") + + def test_depr_star_pos2_len2_with_kwd(self): + fn = ac_tester.depr_star_pos2_len2_with_kwd + fn(a=0, b=0, c=0, d=0, e=0) + fn("a", b=0, c=0, d=0, e=0) + fn("a", "b", c=0, d=0, e=0) + check = partial(self.check_depr_star, "'c' and 'd'", fn) + check("a", "b", "c", d=0, e=0) + check("a", "b", "c", "d", e=0) + + def test_depr_star_noinline(self): + fn = ac_tester.depr_star_noinline + self.assertRaises(TypeError, fn, "a") + fn(a="a", b="b") + fn(a="a", b="b", c="c") + fn("a", b="b") + fn("a", b="b", c="c") + check = partial(self.check_depr_star, "'b' and 'c'", fn) + check("a", "b") + check("a", "b", "c") + check("a", "b", c="c") + self.assertRaises(TypeError, fn, "a", "b", "c", "d") + + def test_depr_star_multi(self): + fn = ac_tester.depr_star_multi + self.assertRaises(TypeError, fn, "a") + fn("a", b="b", c="c", d="d", e="e", f="f", g="g", h="h") + errmsg = ( + "Passing more than 1 positional argument to depr_star_multi() is deprecated. " + "Parameter 'b' will become a keyword-only parameter in Python 3.16. " + "Parameters 'c' and 'd' will become keyword-only parameters in Python 3.15. " + "Parameters 'e', 'f' and 'g' will become keyword-only parameters in Python 3.14.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", c="c", d="d", e="e", f="f", g="g", h="h") + check("a", "b", "c", d="d", e="e", f="f", g="g", h="h") + check("a", "b", "c", "d", e="e", f="f", g="g", h="h") + check("a", "b", "c", "d", "e", f="f", g="g", h="h") + check("a", "b", "c", "d", "e", "f", g="g", h="h") + check("a", "b", "c", "d", "e", "f", "g", h="h") + self.assertRaises(TypeError, fn, "a", "b", "c", "d", "e", "f", "g", "h") + + def test_depr_kwd_required_1(self): + fn = ac_tester.depr_kwd_required_1 + fn("a", "b") + self.assertRaises(TypeError, fn, "a") + self.assertRaises(TypeError, fn, "a", "b", "c") + check = partial(self.check_depr_kwd, "'b'", fn) + check("a", b="b") + self.assertRaises(TypeError, fn, a="a", b="b") + + def test_depr_kwd_required_2(self): + fn = ac_tester.depr_kwd_required_2 + fn("a", "b", "c") + self.assertRaises(TypeError, fn, "a", "b") + self.assertRaises(TypeError, fn, "a", "b", "c", "d") + check = partial(self.check_depr_kwd, "'b' and 'c'", fn) + check("a", "b", c="c") + check("a", b="b", c="c") + self.assertRaises(TypeError, fn, a="a", b="b", c="c") + + def test_depr_kwd_optional_1(self): + fn = ac_tester.depr_kwd_optional_1 + fn("a") + fn("a", "b") + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, "a", "b", "c") + check = partial(self.check_depr_kwd, "'b'", fn) + check("a", b="b") + self.assertRaises(TypeError, fn, a="a", b="b") + + def test_depr_kwd_optional_2(self): + fn = ac_tester.depr_kwd_optional_2 + fn("a") + fn("a", "b") + fn("a", "b", "c") + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, "a", "b", "c", "d") + check = partial(self.check_depr_kwd, "'b' and 'c'", fn) + check("a", b="b") + check("a", c="c") + check("a", b="b", c="c") + check("a", c="c", b="b") + check("a", "b", c="c") + self.assertRaises(TypeError, fn, a="a", b="b", c="c") + + def test_depr_kwd_optional_3(self): + fn = ac_tester.depr_kwd_optional_3 + fn() + fn("a") + fn("a", "b") + fn("a", "b", "c") + self.assertRaises(TypeError, fn, "a", "b", "c", "d") + check = partial(self.check_depr_kwd, "'a', 'b' and 'c'", fn) + check("a", "b", c="c") + check("a", b="b") + check(a="a") + + def test_depr_kwd_required_optional(self): + fn = ac_tester.depr_kwd_required_optional + fn("a", "b") + fn("a", "b", "c") + self.assertRaises(TypeError, fn) + self.assertRaises(TypeError, fn, "a") + self.assertRaises(TypeError, fn, "a", "b", "c", "d") + check = partial(self.check_depr_kwd, "'b' and 'c'", fn) + check("a", b="b") + check("a", b="b", c="c") + check("a", c="c", b="b") + check("a", "b", c="c") + self.assertRaises(TypeError, fn, "a", c="c") + self.assertRaises(TypeError, fn, a="a", b="b", c="c") + + def test_depr_kwd_noinline(self): + fn = ac_tester.depr_kwd_noinline + fn("a", "b") + fn("a", "b", "c") + self.assertRaises(TypeError, fn, "a") + check = partial(self.check_depr_kwd, "'b' and 'c'", fn) + check("a", b="b") + check("a", b="b", c="c") + check("a", c="c", b="b") + check("a", "b", c="c") + self.assertRaises(TypeError, fn, "a", c="c") + self.assertRaises(TypeError, fn, a="a", b="b", c="c") + + def test_depr_kwd_multi(self): + fn = ac_tester.depr_kwd_multi + fn("a", "b", "c", "d", "e", "f", "g", h="h") + errmsg = ( + "Passing keyword arguments 'b', 'c', 'd', 'e', 'f' and 'g' to depr_kwd_multi() is deprecated. " + "Parameter 'b' will become positional-only in Python 3.14. " + "Parameters 'c' and 'd' will become positional-only in Python 3.15. " + "Parameters 'e', 'f' and 'g' will become positional-only in Python 3.16.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", "c", "d", "e", "f", g="g", h="h") + check("a", "b", "c", "d", "e", f="f", g="g", h="h") + check("a", "b", "c", "d", e="e", f="f", g="g", h="h") + check("a", "b", "c", d="d", e="e", f="f", g="g", h="h") + check("a", "b", c="c", d="d", e="e", f="f", g="g", h="h") + check("a", b="b", c="c", d="d", e="e", f="f", g="g", h="h") + self.assertRaises(TypeError, fn, a="a", b="b", c="c", d="d", e="e", f="f", g="g", h="h") + + def test_depr_multi(self): + fn = ac_tester.depr_multi + self.assertRaises(TypeError, fn, "a", "b", "c", "d", "e", "f", "g") + errmsg = ( + "Passing more than 4 positional arguments to depr_multi() is deprecated. " + "Parameter 'e' will become a keyword-only parameter in Python 3.15. " + "Parameter 'f' will become a keyword-only parameter in Python 3.14.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", "c", "d", "e", "f", g="g") + check("a", "b", "c", "d", "e", f="f", g="g") + fn("a", "b", "c", "d", e="e", f="f", g="g") + fn("a", "b", "c", d="d", e="e", f="f", g="g") + errmsg = ( + "Passing keyword arguments 'b' and 'c' to depr_multi() is deprecated. " + "Parameter 'b' will become positional-only in Python 3.14. " + "Parameter 'c' will become positional-only in Python 3.15.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", c="c", d="d", e="e", f="f", g="g") + check("a", b="b", c="c", d="d", e="e", f="f", g="g") + self.assertRaises(TypeError, fn, a="a", b="b", c="c", d="d", e="e", f="f", g="g") + + +class LimitedCAPIOutputTests(unittest.TestCase): + + def setUp(self): + self.clinic = _make_clinic(limited_capi=True) + + @staticmethod + def wrap_clinic_input(block): + return dedent(f""" + /*[clinic input] + output everything buffer + {block} + [clinic start generated code]*/ + /*[clinic input] + dump buffer + [clinic start generated code]*/ + """) + + def test_limited_capi_float(self): + block = self.wrap_clinic_input(""" + func + f: float + / + """) + generated = self.clinic.parse(block) + self.assertNotIn("PyFloat_AS_DOUBLE", generated) + self.assertIn("float f;", generated) + self.assertIn("f = (float) PyFloat_AsDouble", generated) + + def test_limited_capi_double(self): + block = self.wrap_clinic_input(""" + func + f: double + / + """) + generated = self.clinic.parse(block) + self.assertNotIn("PyFloat_AS_DOUBLE", generated) + self.assertIn("double f;", generated) + self.assertIn("f = PyFloat_AsDouble", generated) + + +try: + import _testclinic_limited +except ImportError: + _testclinic_limited = None + +@unittest.skipIf(_testclinic_limited is None, "_testclinic_limited is missing") +class LimitedCAPIFunctionalTest(unittest.TestCase): + locals().update((name, getattr(_testclinic_limited, name)) + for name in dir(_testclinic_limited) if name.startswith('test_')) + + def test_my_int_func(self): + with self.assertRaises(TypeError): + _testclinic_limited.my_int_func() + self.assertEqual(_testclinic_limited.my_int_func(3), 3) + with self.assertRaises(TypeError): + _testclinic_limited.my_int_func(1.0) + with self.assertRaises(TypeError): + _testclinic_limited.my_int_func("xyz") + + def test_my_int_sum(self): + with self.assertRaises(TypeError): + _testclinic_limited.my_int_sum() + with self.assertRaises(TypeError): + _testclinic_limited.my_int_sum(1) + self.assertEqual(_testclinic_limited.my_int_sum(1, 2), 3) + with self.assertRaises(TypeError): + _testclinic_limited.my_int_sum(1.0, 2) + with self.assertRaises(TypeError): + _testclinic_limited.my_int_sum(1, "str") + + def test_my_double_sum(self): + for func in ( + _testclinic_limited.my_float_sum, + _testclinic_limited.my_double_sum, + ): + with self.subTest(func=func.__name__): + self.assertEqual(func(1.0, 2.5), 3.5) + with self.assertRaises(TypeError): + func() + with self.assertRaises(TypeError): + func(1) + with self.assertRaises(TypeError): + func(1., "2") + + def test_get_file_descriptor(self): + # test 'file descriptor' converter: call PyObject_AsFileDescriptor() + get_fd = _testclinic_limited.get_file_descriptor + + class MyInt(int): + pass + + class MyFile: + def __init__(self, fd): + self._fd = fd + def fileno(self): + return self._fd + + for fd in (0, 1, 2, 5, 123_456): + self.assertEqual(get_fd(fd), fd) + + myint = MyInt(fd) + self.assertEqual(get_fd(myint), fd) + + myfile = MyFile(fd) + self.assertEqual(get_fd(myfile), fd) + + with self.assertRaises(OverflowError): + get_fd(2**256) + with self.assertWarnsRegex(RuntimeWarning, + "bool is used as a file descriptor"): + get_fd(True) + with self.assertRaises(TypeError): + get_fd(1.0) + with self.assertRaises(TypeError): + get_fd("abc") + with self.assertRaises(TypeError): + get_fd(None) + + +class PermutationTests(unittest.TestCase): + """Test permutation support functions.""" + + def test_permute_left_option_groups(self): + expected = ( + (), + (3,), + (2, 3), + (1, 2, 3), + ) + data = list(zip([1, 2, 3])) # Generate a list of 1-tuples. + actual = tuple(permute_left_option_groups(data)) + self.assertEqual(actual, expected) + + def test_permute_right_option_groups(self): + expected = ( + (), + (1,), + (1, 2), + (1, 2, 3), + ) + data = list(zip([1, 2, 3])) # Generate a list of 1-tuples. + actual = tuple(permute_right_option_groups(data)) + self.assertEqual(actual, expected) + + def test_permute_optional_groups(self): + empty = { + "left": (), "required": (), "right": (), + "expected": ((),), + } + noleft1 = { + "left": (), "required": ("b",), "right": ("c",), + "expected": ( + ("b",), + ("b", "c"), + ), + } + noleft2 = { + "left": (), "required": ("b", "c",), "right": ("d",), + "expected": ( + ("b", "c"), + ("b", "c", "d"), + ), + } + noleft3 = { + "left": (), "required": ("b", "c",), "right": ("d", "e"), + "expected": ( + ("b", "c"), + ("b", "c", "d"), + ("b", "c", "d", "e"), + ), + } + noright1 = { + "left": ("a",), "required": ("b",), "right": (), + "expected": ( + ("b",), + ("a", "b"), + ), + } + noright2 = { + "left": ("a",), "required": ("b", "c"), "right": (), + "expected": ( + ("b", "c"), + ("a", "b", "c"), + ), + } + noright3 = { + "left": ("a", "b"), "required": ("c",), "right": (), + "expected": ( + ("c",), + ("b", "c"), + ("a", "b", "c"), + ), + } + leftandright1 = { + "left": ("a",), "required": ("b",), "right": ("c",), + "expected": ( + ("b",), + ("a", "b"), # Prefer left. + ("a", "b", "c"), + ), + } + leftandright2 = { + "left": ("a", "b"), "required": ("c", "d"), "right": ("e", "f"), + "expected": ( + ("c", "d"), + ("b", "c", "d"), # Prefer left. + ("a", "b", "c", "d"), # Prefer left. + ("a", "b", "c", "d", "e"), + ("a", "b", "c", "d", "e", "f"), + ), + } + dataset = ( + empty, + noleft1, noleft2, noleft3, + noright1, noright2, noright3, + leftandright1, leftandright2, + ) + for params in dataset: + with self.subTest(**params): + left, required, right, expected = params.values() + permutations = permute_optional_groups(left, required, right) + actual = tuple(permutations) + self.assertEqual(actual, expected) + + +class FormatHelperTests(unittest.TestCase): + + def test_strip_leading_and_trailing_blank_lines(self): + dataset = ( + # Input lines, expected output. + ("a\nb", "a\nb"), + ("a\nb\n", "a\nb"), + ("a\nb ", "a\nb"), + ("\na\nb\n\n", "a\nb"), + ("\n\na\nb\n\n", "a\nb"), + ("\n\na\n\nb\n\n", "a\n\nb"), + # Note, leading whitespace is preserved: + (" a\nb", " a\nb"), + (" a\nb ", " a\nb"), + (" \n \n a\nb \n \n ", " a\nb"), + ) + for lines, expected in dataset: + with self.subTest(lines=lines, expected=expected): + out = libclinic.normalize_snippet(lines) + self.assertEqual(out, expected) + + def test_normalize_snippet(self): + snippet = """ + one + two + three + """ + + # Expected outputs: + zero_indent = ( + "one\n" + "two\n" + "three" + ) + four_indent = ( + " one\n" + " two\n" + " three" + ) + eight_indent = ( + " one\n" + " two\n" + " three" + ) + expected_outputs = {0: zero_indent, 4: four_indent, 8: eight_indent} + for indent, expected in expected_outputs.items(): + with self.subTest(indent=indent): + actual = libclinic.normalize_snippet(snippet, indent=indent) + self.assertEqual(actual, expected) + + def test_escaped_docstring(self): + dataset = ( + # input, expected + (r"abc", r'"abc"'), + (r"\abc", r'"\\abc"'), + (r"\a\bc", r'"\\a\\bc"'), + (r"\a\\bc", r'"\\a\\\\bc"'), + (r'"abc"', r'"\"abc\""'), + (r"'a'", r'"\'a\'"'), + ) + for line, expected in dataset: + with self.subTest(line=line, expected=expected): + out = libclinic.docstring_for_c_string(line) + self.assertEqual(out, expected) + + def test_format_escape(self): + line = "{}, {a}" + expected = "{{}}, {{a}}" + out = libclinic.format_escape(line) + self.assertEqual(out, expected) + + def test_indent_all_lines(self): + # Blank lines are expected to be unchanged. + self.assertEqual(libclinic.indent_all_lines("", prefix="bar"), "") + + lines = ( + "one\n" + "two" # The missing newline is deliberate. + ) + expected = ( + "barone\n" + "bartwo" + ) + out = libclinic.indent_all_lines(lines, prefix="bar") + self.assertEqual(out, expected) + + # If last line is empty, expect it to be unchanged. + lines = ( + "\n" + "one\n" + "two\n" + "" + ) + expected = ( + "bar\n" + "barone\n" + "bartwo\n" + "" + ) + out = libclinic.indent_all_lines(lines, prefix="bar") + self.assertEqual(out, expected) + + def test_suffix_all_lines(self): + # Blank lines are expected to be unchanged. + self.assertEqual(libclinic.suffix_all_lines("", suffix="foo"), "") + + lines = ( + "one\n" + "two" # The missing newline is deliberate. + ) + expected = ( + "onefoo\n" + "twofoo" + ) + out = libclinic.suffix_all_lines(lines, suffix="foo") + self.assertEqual(out, expected) + + # If last line is empty, expect it to be unchanged. + lines = ( + "\n" + "one\n" + "two\n" + "" + ) + expected = ( + "foo\n" + "onefoo\n" + "twofoo\n" + "" + ) + out = libclinic.suffix_all_lines(lines, suffix="foo") + self.assertEqual(out, expected) + + +class ClinicReprTests(unittest.TestCase): + def test_Block_repr(self): + block = Block("foo") + expected_repr = "" + self.assertEqual(repr(block), expected_repr) + + block2 = Block("bar", "baz", [], "eggs", "spam") + expected_repr_2 = "" + self.assertEqual(repr(block2), expected_repr_2) + + block3 = Block( + input="longboi_" * 100, + dsl_name="wow_so_long", + signatures=[], + output="very_long_" * 100, + indent="" + ) + expected_repr_3 = ( + "" + ) + self.assertEqual(repr(block3), expected_repr_3) + + def test_Destination_repr(self): + c = _make_clinic() + + destination = Destination( + "foo", type="file", clinic=c, args=("eggs",) + ) + self.assertEqual( + repr(destination), "" + ) + + destination2 = Destination("bar", type="buffer", clinic=c) + self.assertEqual(repr(destination2), "") + + def test_Module_repr(self): + module = Module("foo", _make_clinic()) + self.assertRegex(repr(module), r"") + + def test_Class_repr(self): + cls = Class("foo", _make_clinic(), None, 'some_typedef', 'some_type_object') + self.assertRegex(repr(cls), r"") + + def test_FunctionKind_repr(self): + self.assertEqual( + repr(FunctionKind.CLASS_METHOD), "" + ) + + def test_Function_and_Parameter_reprs(self): + function = Function( + name='foo', + module=_make_clinic(), + cls=None, + c_basename=None, + full_name='foofoo', + return_converter=int_return_converter(), + kind=FunctionKind.METHOD_INIT, + coexist=False + ) + self.assertEqual(repr(function), "") + + converter = self_converter('bar', 'bar', function) + parameter = Parameter( + "bar", + kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, + function=function, + converter=converter + ) + self.assertEqual(repr(parameter), "") + + def test_Monitor_repr(self): + monitor = libclinic.cpp.Monitor("test.c") + self.assertRegex(repr(monitor), r"") + + monitor.line_number = 42 + monitor.stack.append(("token1", "condition1")) + self.assertRegex( + repr(monitor), r"" + ) + + monitor.stack.append(("token2", "condition2")) + self.assertRegex( + repr(monitor), + r"" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecencodings_cn.py b/Lib/test/test_codecencodings_cn.py new file mode 100644 index 00000000000..2a450718d5a --- /dev/null +++ b/Lib/test/test_codecencodings_cn.py @@ -0,0 +1,96 @@ +# +# test_codecencodings_cn.py +# Codec encoding tests for PRC encodings. +# + +from test import multibytecodec_support +import unittest + +class Test_GB2312(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'gb2312' + tstring = multibytecodec_support.load_teststring('gb2312') + codectests = ( + # invalid bytes + (b"abc\x81\x81\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x81\x81\xc1\xc4", "replace", "abc\ufffd\ufffd\u804a"), + (b"abc\x81\x81\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\u804a\ufffd"), + (b"abc\x81\x81\xc1\xc4", "ignore", "abc\u804a"), + (b"\xc1\x64", "strict", None), + ) + +class Test_GBK(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'gbk' + tstring = multibytecodec_support.load_teststring('gbk') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\u804a"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\u804a\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\u804a"), + (b"\x83\x34\x83\x31", "strict", None), + ("\u30fb", "strict", None), + ) + +class Test_GB18030(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'gb18030' + tstring = multibytecodec_support.load_teststring('gb18030') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\u804a"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\u804a\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\u804a"), + (b"abc\x84\x39\x84\x39\xc1\xc4", "replace", "abc\ufffd9\ufffd9\u804a"), + ("\u30fb", "strict", b"\x819\xa79"), + (b"abc\x84\x32\x80\x80def", "replace", 'abc\ufffd2\ufffd\ufffddef'), + (b"abc\x81\x30\x81\x30def", "strict", 'abc\x80def'), + (b"abc\x86\x30\x81\x30def", "replace", 'abc\ufffd0\ufffd0def'), + # issue29990 + (b"\xff\x30\x81\x30", "strict", None), + (b"\x81\x30\xff\x30", "strict", None), + (b"abc\x81\x39\xff\x39\xc1\xc4", "replace", "abc\ufffd\x39\ufffd\x39\u804a"), + (b"abc\xab\x36\xff\x30def", "replace", 'abc\ufffd\x36\ufffd\x30def'), + (b"abc\xbf\x38\xff\x32\xc1\xc4", "ignore", "abc\x38\x32\u804a"), + ) + has_iso10646 = True + +class Test_HZ(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'hz' + tstring = multibytecodec_support.load_teststring('hz') + codectests = ( + # test '~\n' (3 lines) + (b'This sentence is in ASCII.\n' + b'The next sentence is in GB.~{<:Ky2;S{#,~}~\n' + b'~{NpJ)l6HK!#~}Bye.\n', + 'strict', + 'This sentence is in ASCII.\n' + 'The next sentence is in GB.' + '\u5df1\u6240\u4e0d\u6b32\uff0c\u52ff\u65bd\u65bc\u4eba\u3002' + 'Bye.\n'), + # test '~\n' (4 lines) + (b'This sentence is in ASCII.\n' + b'The next sentence is in GB.~\n' + b'~{<:Ky2;S{#,NpJ)l6HK!#~}~\n' + b'Bye.\n', + 'strict', + 'This sentence is in ASCII.\n' + 'The next sentence is in GB.' + '\u5df1\u6240\u4e0d\u6b32\uff0c\u52ff\u65bd\u65bc\u4eba\u3002' + 'Bye.\n'), + # invalid bytes + (b'ab~cd', 'replace', 'ab\uFFFDcd'), + (b'ab\xffcd', 'replace', 'ab\uFFFDcd'), + (b'ab~{\x81\x81\x41\x44~}cd', 'replace', 'ab\uFFFD\uFFFD\u804Acd'), + (b'ab~{\x41\x44~}cd', 'replace', 'ab\u804Acd'), + (b"ab~{\x79\x79\x41\x44~}cd", "replace", "ab\ufffd\ufffd\u804acd"), + # issue 30003 + ('ab~cd', 'strict', b'ab~~cd'), # escape ~ + (b'~{Dc~~:C~}', 'strict', None), # ~~ only in ASCII mode + (b'~{Dc~\n:C~}', 'strict', None), # ~\n only in ASCII mode + ) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecencodings_hk.py b/Lib/test/test_codecencodings_hk.py new file mode 100644 index 00000000000..c5e2f999efc --- /dev/null +++ b/Lib/test/test_codecencodings_hk.py @@ -0,0 +1,22 @@ +# +# test_codecencodings_hk.py +# Codec encoding tests for HongKong encodings. +# + +from test import multibytecodec_support +import unittest + +class Test_Big5HKSCS(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'big5hkscs' + tstring = multibytecodec_support.load_teststring('big5hkscs') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\u8b10"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\u8b10\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\u8b10"), + ) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecencodings_iso2022.py b/Lib/test/test_codecencodings_iso2022.py new file mode 100644 index 00000000000..027dbecc613 --- /dev/null +++ b/Lib/test/test_codecencodings_iso2022.py @@ -0,0 +1,87 @@ +# Codec encoding tests for ISO 2022 encodings. + +from test import multibytecodec_support +import unittest + +COMMON_CODEC_TESTS = ( + # invalid bytes + (b'ab\xFFcd', 'replace', 'ab\uFFFDcd'), + (b'ab\x1Bdef', 'replace', 'ab\x1Bdef'), + (b'ab\x1B$def', 'replace', 'ab\uFFFD'), + ) + +class Test_ISO2022_JP(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + ) + +class Test_ISO2022_JP2(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_2' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'abdef'), + ) + +class Test_ISO2022_JP3(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_3' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + (b'\x1B$(O\x2E\x23\x1B(B', 'strict', '\u3402' ), + (b'\x1B$(O\x2E\x22\x1B(B', 'strict', '\U0002000B' ), + (b'\x1B$(O\x24\x77\x1B(B', 'strict', '\u304B\u309A'), + (b'\x1B$(P\x21\x22\x1B(B', 'strict', '\u4E02' ), + (b'\x1B$(P\x7E\x76\x1B(B', 'strict', '\U0002A6B2' ), + ('\u3402', 'strict', b'\x1B$(O\x2E\x23\x1B(B'), + ('\U0002000B', 'strict', b'\x1B$(O\x2E\x22\x1B(B'), + ('\u304B\u309A', 'strict', b'\x1B$(O\x24\x77\x1B(B'), + ('\u4E02', 'strict', b'\x1B$(P\x21\x22\x1B(B'), + ('\U0002A6B2', 'strict', b'\x1B$(P\x7E\x76\x1B(B'), + (b'ab\x1B$(O\x2E\x21\x1B(Bdef', 'replace', 'ab\uFFFDdef'), + ('ab\u4FF1def', 'replace', b'ab?def'), + ) + xmlcharnametest = ( + '\xAB\u211C\xBB = \u2329\u1234\u232A', + b'\x1B$(O\x29\x28\x1B(Bℜ\x1B$(O\x29\x32\x1B(B = ⟨ሴ⟩' + ) + +class Test_ISO2022_JP2004(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_2004' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + (b'\x1B$(Q\x2E\x23\x1B(B', 'strict', '\u3402' ), + (b'\x1B$(Q\x2E\x22\x1B(B', 'strict', '\U0002000B' ), + (b'\x1B$(Q\x24\x77\x1B(B', 'strict', '\u304B\u309A'), + (b'\x1B$(P\x21\x22\x1B(B', 'strict', '\u4E02' ), + (b'\x1B$(P\x7E\x76\x1B(B', 'strict', '\U0002A6B2' ), + ('\u3402', 'strict', b'\x1B$(Q\x2E\x23\x1B(B'), + ('\U0002000B', 'strict', b'\x1B$(Q\x2E\x22\x1B(B'), + ('\u304B\u309A', 'strict', b'\x1B$(Q\x24\x77\x1B(B'), + ('\u4E02', 'strict', b'\x1B$(P\x21\x22\x1B(B'), + ('\U0002A6B2', 'strict', b'\x1B$(P\x7E\x76\x1B(B'), + (b'ab\x1B$(Q\x2E\x21\x1B(Bdef', 'replace', 'ab\u4FF1def'), + ('ab\u4FF1def', 'replace', b'ab\x1B$(Q\x2E\x21\x1B(Bdef'), + ) + xmlcharnametest = ( + '\xAB\u211C\xBB = \u2329\u1234\u232A', + b'\x1B$(Q\x29\x28\x1B(Bℜ\x1B$(Q\x29\x32\x1B(B = ⟨ሴ⟩' + ) + +class Test_ISO2022_KR(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_kr' + tstring = multibytecodec_support.load_teststring('iso2022_kr') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + ) + + # iso2022_kr.txt cannot be used to test "chunk coding": the escape + # sequence is only written on the first line + @unittest.skip('iso2022_kr.txt cannot be used to test "chunk coding"') + def test_chunkcoding(self): + pass + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecencodings_jp.py b/Lib/test/test_codecencodings_jp.py new file mode 100644 index 00000000000..94378d124f7 --- /dev/null +++ b/Lib/test/test_codecencodings_jp.py @@ -0,0 +1,126 @@ +# +# test_codecencodings_jp.py +# Codec encoding tests for Japanese encodings. +# + +from test import multibytecodec_support +import unittest + +class Test_CP932(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'cp932' + tstring = multibytecodec_support.load_teststring('shift_jis') + codectests = ( + # invalid bytes + (b"abc\x81\x00\x81\x00\x82\x84", "strict", None), + (b"abc\xf8", "strict", None), + (b"abc\x81\x00\x82\x84", "replace", "abc\ufffd\x00\uff44"), + (b"abc\x81\x00\x82\x84\x88", "replace", "abc\ufffd\x00\uff44\ufffd"), + (b"abc\x81\x00\x82\x84", "ignore", "abc\x00\uff44"), + (b"ab\xEBxy", "replace", "ab\uFFFDxy"), + (b"ab\xF0\x39xy", "replace", "ab\uFFFD9xy"), + (b"ab\xEA\xF0xy", "replace", 'ab\ufffd\ue038y'), + # sjis vs cp932 + (b"\\\x7e", "replace", "\\\x7e"), + (b"\x81\x5f\x81\x61\x81\x7c", "replace", "\uff3c\u2225\uff0d"), + ) + +euc_commontests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\u7956"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\u7956\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\u7956"), + (b"abc\xc8", "strict", None), + (b"abc\x8f\x83\x83", "replace", "abc\ufffd\ufffd\ufffd"), + (b"\x82\xFCxy", "replace", "\ufffd\ufffdxy"), + (b"\xc1\x64", "strict", None), + (b"\xa1\xc0", "strict", "\uff3c"), + (b"\xa1\xc0\\", "strict", "\uff3c\\"), + (b"\x8eXY", "replace", "\ufffdXY"), +) + +class Test_EUC_JIS_2004(multibytecodec_support.TestBase, + unittest.TestCase): + encoding = 'euc_jis_2004' + tstring = multibytecodec_support.load_teststring('euc_jisx0213') + codectests = euc_commontests + xmlcharnametest = ( + "\xab\u211c\xbb = \u2329\u1234\u232a", + b"\xa9\xa8ℜ\xa9\xb2 = ⟨ሴ⟩" + ) + +class Test_EUC_JISX0213(multibytecodec_support.TestBase, + unittest.TestCase): + encoding = 'euc_jisx0213' + tstring = multibytecodec_support.load_teststring('euc_jisx0213') + codectests = euc_commontests + xmlcharnametest = ( + "\xab\u211c\xbb = \u2329\u1234\u232a", + b"\xa9\xa8ℜ\xa9\xb2 = ⟨ሴ⟩" + ) + +class Test_EUC_JP_COMPAT(multibytecodec_support.TestBase, + unittest.TestCase): + encoding = 'euc_jp' + tstring = multibytecodec_support.load_teststring('euc_jp') + codectests = euc_commontests + ( + ("\xa5", "strict", b"\x5c"), + ("\u203e", "strict", b"\x7e"), + ) + +shiftjis_commonenctests = ( + (b"abc\x80\x80\x82\x84", "strict", None), + (b"abc\xf8", "strict", None), + (b"abc\x80\x80\x82\x84def", "ignore", "abc\uff44def"), +) + +class Test_SJIS_COMPAT(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'shift_jis' + tstring = multibytecodec_support.load_teststring('shift_jis') + codectests = shiftjis_commonenctests + ( + (b"abc\x80\x80\x82\x84", "replace", "abc\ufffd\ufffd\uff44"), + (b"abc\x80\x80\x82\x84\x88", "replace", "abc\ufffd\ufffd\uff44\ufffd"), + + (b"\\\x7e", "strict", "\\\x7e"), + (b"\x81\x5f\x81\x61\x81\x7c", "strict", "\uff3c\u2016\u2212"), + (b"abc\x81\x39", "replace", "abc\ufffd9"), + (b"abc\xEA\xFC", "replace", "abc\ufffd\ufffd"), + (b"abc\xFF\x58", "replace", "abc\ufffdX"), + ) + +class Test_SJIS_2004(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'shift_jis_2004' + tstring = multibytecodec_support.load_teststring('shift_jis') + codectests = shiftjis_commonenctests + ( + (b"\\\x7e", "strict", "\xa5\u203e"), + (b"\x81\x5f\x81\x61\x81\x7c", "strict", "\\\u2016\u2212"), + (b"abc\xEA\xFC", "strict", "abc\u64bf"), + (b"\x81\x39xy", "replace", "\ufffd9xy"), + (b"\xFF\x58xy", "replace", "\ufffdXxy"), + (b"\x80\x80\x82\x84xy", "replace", "\ufffd\ufffd\uff44xy"), + (b"\x80\x80\x82\x84\x88xy", "replace", "\ufffd\ufffd\uff44\u5864y"), + (b"\xFC\xFBxy", "replace", '\ufffd\u95b4y'), + ) + xmlcharnametest = ( + "\xab\u211c\xbb = \u2329\u1234\u232a", + b"\x85Gℜ\x85Q = ⟨ሴ⟩" + ) + +class Test_SJISX0213(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'shift_jisx0213' + tstring = multibytecodec_support.load_teststring('shift_jisx0213') + codectests = shiftjis_commonenctests + ( + (b"abc\x80\x80\x82\x84", "replace", "abc\ufffd\ufffd\uff44"), + (b"abc\x80\x80\x82\x84\x88", "replace", "abc\ufffd\ufffd\uff44\ufffd"), + + # sjis vs cp932 + (b"\\\x7e", "replace", "\xa5\u203e"), + (b"\x81\x5f\x81\x61\x81\x7c", "replace", "\x5c\u2016\u2212"), + ) + xmlcharnametest = ( + "\xab\u211c\xbb = \u2329\u1234\u232a", + b"\x85Gℜ\x85Q = ⟨ሴ⟩" + ) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecencodings_kr.py b/Lib/test/test_codecencodings_kr.py new file mode 100644 index 00000000000..863d16b8fd6 --- /dev/null +++ b/Lib/test/test_codecencodings_kr.py @@ -0,0 +1,69 @@ +# +# test_codecencodings_kr.py +# Codec encoding tests for ROK encodings. +# + +from test import multibytecodec_support +import unittest + +class Test_CP949(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'cp949' + tstring = multibytecodec_support.load_teststring('cp949') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\uc894"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\uc894\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\uc894"), + ) + +class Test_EUCKR(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'euc_kr' + tstring = multibytecodec_support.load_teststring('euc_kr') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", 'abc\ufffd\ufffd\uc894'), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\uc894\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\uc894"), + + # composed make-up sequence errors + (b"\xa4\xd4", "strict", None), + (b"\xa4\xd4\xa4", "strict", None), + (b"\xa4\xd4\xa4\xb6", "strict", None), + (b"\xa4\xd4\xa4\xb6\xa4", "strict", None), + (b"\xa4\xd4\xa4\xb6\xa4\xd0", "strict", None), + (b"\xa4\xd4\xa4\xb6\xa4\xd0\xa4", "strict", None), + (b"\xa4\xd4\xa4\xb6\xa4\xd0\xa4\xd4", "strict", "\uc4d4"), + (b"\xa4\xd4\xa4\xb6\xa4\xd0\xa4\xd4x", "strict", "\uc4d4x"), + (b"a\xa4\xd4\xa4\xb6\xa4", "replace", 'a\ufffd'), + (b"\xa4\xd4\xa3\xb6\xa4\xd0\xa4\xd4", "strict", None), + (b"\xa4\xd4\xa4\xb6\xa3\xd0\xa4\xd4", "strict", None), + (b"\xa4\xd4\xa4\xb6\xa4\xd0\xa3\xd4", "strict", None), + (b"\xa4\xd4\xa4\xff\xa4\xd0\xa4\xd4", "replace", '\ufffd\u6e21\ufffd\u3160\ufffd'), + (b"\xa4\xd4\xa4\xb6\xa4\xff\xa4\xd4", "replace", '\ufffd\u6e21\ub544\ufffd\ufffd'), + (b"\xa4\xd4\xa4\xb6\xa4\xd0\xa4\xff", "replace", '\ufffd\u6e21\ub544\u572d\ufffd'), + (b"\xa4\xd4\xff\xa4\xd4\xa4\xb6\xa4\xd0\xa4\xd4", "replace", '\ufffd\ufffd\ufffd\uc4d4'), + (b"\xc1\xc4", "strict", "\uc894"), + ) + +class Test_JOHAB(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'johab' + tstring = multibytecodec_support.load_teststring('johab') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\ucd27"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\ucd27\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\ucd27"), + (b"\xD8abc", "replace", "\uFFFDabc"), + (b"\xD8\xFFabc", "replace", "\uFFFD\uFFFDabc"), + (b"\x84bxy", "replace", "\uFFFDbxy"), + (b"\x8CBxy", "replace", "\uFFFDBxy"), + ) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecencodings_tw.py b/Lib/test/test_codecencodings_tw.py new file mode 100644 index 00000000000..bb1d133cad1 --- /dev/null +++ b/Lib/test/test_codecencodings_tw.py @@ -0,0 +1,22 @@ +# +# test_codecencodings_tw.py +# Codec encoding tests for ROC encodings. +# + +from test import multibytecodec_support +import unittest + +class Test_Big5(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'big5' + tstring = multibytecodec_support.load_teststring('big5') + codectests = ( + # invalid bytes + (b"abc\x80\x80\xc1\xc4", "strict", None), + (b"abc\xc8", "strict", None), + (b"abc\x80\x80\xc1\xc4", "replace", "abc\ufffd\ufffd\u8b10"), + (b"abc\x80\x80\xc1\xc4\xc8", "replace", "abc\ufffd\ufffd\u8b10\ufffd"), + (b"abc\x80\x80\xc1\xc4", "ignore", "abc\u8b10"), + ) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecmaps_cn.py b/Lib/test/test_codecmaps_cn.py new file mode 100644 index 00000000000..89e51c6e2a1 --- /dev/null +++ b/Lib/test/test_codecmaps_cn.py @@ -0,0 +1,26 @@ +# +# test_codecmaps_cn.py +# Codec mapping tests for PRC encodings +# + +from test import multibytecodec_support +import unittest + +class TestGB2312Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'gb2312' + mapfileurl = 'http://www.pythontest.net/unicode/EUC-CN.TXT' + +class TestGBKMap(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'gbk' + mapfileurl = 'http://www.pythontest.net/unicode/CP936.TXT' + +class TestGB18030Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'gb18030' + mapfileurl = 'http://www.pythontest.net/unicode/gb-18030-2000.xml' + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py new file mode 100644 index 00000000000..7a48d24fddb --- /dev/null +++ b/Lib/test/test_codecmaps_hk.py @@ -0,0 +1,15 @@ +# +# test_codecmaps_hk.py +# Codec mapping tests for HongKong encodings +# + +from test import multibytecodec_support +import unittest + +class TestBig5HKSCSMap(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'big5hkscs' + mapfileurl = 'http://www.pythontest.net/unicode/BIG5HKSCS-2004.TXT' + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecmaps_jp.py b/Lib/test/test_codecmaps_jp.py new file mode 100644 index 00000000000..fdfec8085b2 --- /dev/null +++ b/Lib/test/test_codecmaps_jp.py @@ -0,0 +1,60 @@ +# +# test_codecmaps_jp.py +# Codec mapping tests for Japanese encodings +# + +from test import multibytecodec_support +import unittest + +class TestCP932Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'cp932' + mapfileurl = 'http://www.pythontest.net/unicode/CP932.TXT' + supmaps = [ + (b'\x80', '\u0080'), + (b'\xa0', '\uf8f0'), + (b'\xfd', '\uf8f1'), + (b'\xfe', '\uf8f2'), + (b'\xff', '\uf8f3'), + ] + for i in range(0xa1, 0xe0): + supmaps.append((bytes([i]), chr(i+0xfec0))) + + +class TestEUCJPCOMPATMap(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'euc_jp' + mapfilename = 'EUC-JP.TXT' + mapfileurl = 'http://www.pythontest.net/unicode/EUC-JP.TXT' + + +class TestSJISCOMPATMap(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'shift_jis' + mapfilename = 'SHIFTJIS.TXT' + mapfileurl = 'http://www.pythontest.net/unicode/SHIFTJIS.TXT' + pass_enctest = [ + (b'\x81_', '\\'), + ] + pass_dectest = [ + (b'\\', '\xa5'), + (b'~', '\u203e'), + (b'\x81_', '\\'), + ] + +class TestEUCJISX0213Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'euc_jisx0213' + mapfilename = 'EUC-JISX0213.TXT' + mapfileurl = 'http://www.pythontest.net/unicode/EUC-JISX0213.TXT' + + +class TestSJISX0213Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'shift_jisx0213' + mapfilename = 'SHIFT_JISX0213.TXT' + mapfileurl = 'http://www.pythontest.net/unicode/SHIFT_JISX0213.TXT' + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecmaps_kr.py b/Lib/test/test_codecmaps_kr.py new file mode 100644 index 00000000000..1b6f5ca3080 --- /dev/null +++ b/Lib/test/test_codecmaps_kr.py @@ -0,0 +1,37 @@ +# +# test_codecmaps_kr.py +# Codec mapping tests for ROK encodings +# + +from test import multibytecodec_support +import unittest + +class TestCP949Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'cp949' + mapfileurl = 'http://www.pythontest.net/unicode/CP949.TXT' + + +class TestEUCKRMap(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'euc_kr' + mapfileurl = 'http://www.pythontest.net/unicode/EUC-KR.TXT' + + # A4D4 HANGUL FILLER indicates the begin of 8-bytes make-up sequence. + pass_enctest = [(b'\xa4\xd4', '\u3164')] + pass_dectest = [(b'\xa4\xd4', '\u3164')] + + +class TestJOHABMap(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'johab' + mapfileurl = 'http://www.pythontest.net/unicode/JOHAB.TXT' + # KS X 1001 standard assigned 0x5c as WON SIGN. + # But the early 90s is the only era that used johab widely, + # most software implements it as REVERSE SOLIDUS. + # So, we ignore the standard here. + pass_enctest = [(b'\\', '\u20a9')] + pass_dectest = [(b'\\', '\u20a9')] + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_codecmaps_tw.py b/Lib/test/test_codecmaps_tw.py new file mode 100644 index 00000000000..145a97d511e --- /dev/null +++ b/Lib/test/test_codecmaps_tw.py @@ -0,0 +1,27 @@ +# +# test_codecmaps_tw.py +# Codec mapping tests for ROC encodings +# + +from test import multibytecodec_support +import unittest + +class TestBIG5Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'big5' + mapfileurl = 'http://www.pythontest.net/unicode/BIG5.TXT' + +class TestCP950Map(multibytecodec_support.TestBase_Mapping, + unittest.TestCase): + encoding = 'cp950' + mapfileurl = 'http://www.pythontest.net/unicode/CP950.TXT' + pass_enctest = [ + (b'\xa2\xcc', '\u5341'), + (b'\xa2\xce', '\u5345'), + ] + codectests = ( + (b"\xFFxy", "replace", "\ufffdxy"), + ) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py new file mode 100644 index 00000000000..f705f4f5bfb --- /dev/null +++ b/Lib/test/test_coroutines.py @@ -0,0 +1,2483 @@ +import contextlib +import copy +import inspect +import pickle +import sys +import types +import traceback +import unittest +import warnings +from test import support +from test.support import import_helper +from test.support import warnings_helper +from test.support.script_helper import assert_python_ok +try: + import _testcapi +except ImportError: + _testcapi = None + + +class AsyncYieldFrom: + def __init__(self, obj): + self.obj = obj + + def __await__(self): + yield from self.obj + + +class AsyncYield: + def __init__(self, value): + self.value = value + + def __await__(self): + yield self.value + + +async def asynciter(iterable): + """Convert an iterable to an asynchronous iterator.""" + for x in iterable: + yield x + + +def run_async(coro): + assert coro.__class__ in {types.GeneratorType, types.CoroutineType} + + buffer = [] + result = None + while True: + try: + buffer.append(coro.send(None)) + except StopIteration as ex: + result = ex.args[0] if ex.args else None + break + return buffer, result + + +def run_async__await__(coro): + assert coro.__class__ is types.CoroutineType + aw = coro.__await__() + buffer = [] + result = None + i = 0 + while True: + try: + if i % 2: + buffer.append(next(aw)) + else: + buffer.append(aw.send(None)) + i += 1 + except StopIteration as ex: + result = ex.args[0] if ex.args else None + break + return buffer, result + + +@contextlib.contextmanager +def silence_coro_gc(): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + yield + support.gc_collect() + + +class AsyncBadSyntaxTest(unittest.TestCase): + + def test_badsyntax_1(self): + samples = [ + """def foo(): + await something() + """, + + """await something()""", + + """async def foo(): + yield from [] + """, + + """async def foo(): + await await fut + """, + + """async def foo(a=await something()): + pass + """, + + """async def foo(a:await something()): + pass + """, + + """async def foo(): + def bar(): + [i async for i in els] + """, + + """async def foo(): + def bar(): + [await i for i in els] + """, + + """async def foo(): + def bar(): + [i for i in els + async for b in els] + """, + + """async def foo(): + def bar(): + [i for i in els + for c in b + async for b in els] + """, + + """async def foo(): + def bar(): + [i for i in els + async for b in els + for c in b] + """, + + """async def foo(): + def bar(): + [[async for i in b] for b in els] + """, + + """async def foo(): + def bar(): + [i for i in els + for b in await els] + """, + + """async def foo(): + def bar(): + [i for i in els + for b in els + if await b] + """, + + """async def foo(): + def bar(): + [i for i in await els] + """, + + """async def foo(): + def bar(): + [i for i in els if await i] + """, + + """def bar(): + [i async for i in els] + """, + + """def bar(): + {i: i async for i in els} + """, + + """def bar(): + {i async for i in els} + """, + + """def bar(): + [await i for i in els] + """, + + """def bar(): + [i for i in els + async for b in els] + """, + + """def bar(): + [i for i in els + for c in b + async for b in els] + """, + + """def bar(): + [i for i in els + async for b in els + for c in b] + """, + + """def bar(): + [i for i in els + for b in await els] + """, + + """def bar(): + [i for i in els + for b in els + if await b] + """, + + """def bar(): + [i for i in await els] + """, + + """def bar(): + [i for i in els if await i] + """, + + """def bar(): + [[i async for i in a] for a in elts] + """, + + """[[i async for i in a] for a in elts] + """, + + """async def foo(): + await + """, + + """async def foo(): + def bar(): pass + await = 1 + """, + + """async def foo(): + + def bar(): pass + await = 1 + """, + + """async def foo(): + def bar(): pass + if 1: + await = 1 + """, + + """def foo(): + async def bar(): pass + if 1: + await a + """, + + """def foo(): + async def bar(): pass + await a + """, + + """def foo(): + def baz(): pass + async def bar(): pass + await a + """, + + """def foo(): + def baz(): pass + # 456 + async def bar(): pass + # 123 + await a + """, + + """async def foo(): + def baz(): pass + # 456 + async def bar(): pass + # 123 + await = 2 + """, + + """def foo(): + + def baz(): pass + + async def bar(): pass + + await a + """, + + """async def foo(): + + def baz(): pass + + async def bar(): pass + + await = 2 + """, + + """async def foo(): + def async(): pass + """, + + """async def foo(): + def await(): pass + """, + + """async def foo(): + def bar(): + await + """, + + """async def foo(): + return lambda async: await + """, + + """async def foo(): + return lambda a: await + """, + + """await a()""", + + """async def foo(a=await b): + pass + """, + + """async def foo(a:await b): + pass + """, + + """def baz(): + async def foo(a=await b): + pass + """, + + """async def foo(async): + pass + """, + + """async def foo(): + def bar(): + def baz(): + async = 1 + """, + + """async def foo(): + def bar(): + def baz(): + pass + async = 1 + """, + + """def foo(): + async def bar(): + + async def baz(): + pass + + def baz(): + 42 + + async = 1 + """, + + """async def foo(): + def bar(): + def baz(): + pass\nawait foo() + """, + + """def foo(): + def bar(): + async def baz(): + pass\nawait foo() + """, + + """async def foo(await): + pass + """, + + """def foo(): + + async def bar(): pass + + await a + """, + + """def foo(): + async def bar(): + pass\nawait a + """, + """def foo(): + async for i in arange(2): + pass + """, + """def foo(): + async with resource: + pass + """, + """async with resource: + pass + """, + """async for i in arange(2): + pass + """, + ] + + for code in samples: + with self.subTest(code=code), self.assertRaises(SyntaxError): + compile(code, "", "exec") + + def test_badsyntax_2(self): + samples = [ + """def foo(): + await = 1 + """, + + """class Bar: + def async(): pass + """, + + """class Bar: + async = 1 + """, + + """class async: + pass + """, + + """class await: + pass + """, + + """import math as await""", + + """def async(): + pass""", + + """def foo(*, await=1): + pass""" + + """async = 1""", + + """print(await=1)""" + ] + + for code in samples: + with self.subTest(code=code), self.assertRaises(SyntaxError): + compile(code, "", "exec") + + def test_badsyntax_3(self): + with self.assertRaises(SyntaxError): + compile("async = 1", "", "exec") + + def test_badsyntax_4(self): + samples = [ + '''def foo(await): + async def foo(): pass + async def foo(): + pass + return await + 1 + ''', + + '''def foo(await): + async def foo(): pass + async def foo(): pass + return await + 1 + ''', + + '''def foo(await): + + async def foo(): pass + + async def foo(): pass + + return await + 1 + ''', + + '''def foo(await): + """spam""" + async def foo(): \ + pass + # 123 + async def foo(): pass + # 456 + return await + 1 + ''', + + '''def foo(await): + def foo(): pass + def foo(): pass + async def bar(): return await_ + await_ = await + try: + bar().send(None) + except StopIteration as ex: + return ex.args[0] + 1 + ''' + ] + + for code in samples: + with self.subTest(code=code), self.assertRaises(SyntaxError): + compile(code, "", "exec") + + +class TokenizerRegrTest(unittest.TestCase): + + def test_oneline_defs(self): + buf = [] + for i in range(500): + buf.append('def i{i}(): return {i}'.format(i=i)) + buf = '\n'.join(buf) + + # Test that 500 consequent, one-line defs is OK + ns = {} + exec(buf, ns, ns) + self.assertEqual(ns['i499'](), 499) + + # Test that 500 consequent, one-line defs *and* + # one 'async def' following them is OK + buf += '\nasync def foo():\n return' + ns = {} + exec(buf, ns, ns) + self.assertEqual(ns['i499'](), 499) + self.assertTrue(inspect.iscoroutinefunction(ns['foo'])) + + +class CoroutineTest(unittest.TestCase): + + def test_gen_1(self): + def gen(): yield + self.assertFalse(hasattr(gen, '__await__')) + + def test_func_1(self): + async def foo(): + return 10 + + f = foo() + self.assertIsInstance(f, types.CoroutineType) + self.assertTrue(bool(foo.__code__.co_flags & inspect.CO_COROUTINE)) + self.assertFalse(bool(foo.__code__.co_flags & inspect.CO_GENERATOR)) + self.assertTrue(bool(f.cr_code.co_flags & inspect.CO_COROUTINE)) + self.assertFalse(bool(f.cr_code.co_flags & inspect.CO_GENERATOR)) + self.assertEqual(run_async(f), ([], 10)) + + self.assertEqual(run_async__await__(foo()), ([], 10)) + + def bar(): pass + self.assertFalse(bool(bar.__code__.co_flags & inspect.CO_COROUTINE)) + + def test_func_2(self): + async def foo(): + raise StopIteration + + with self.assertRaisesRegex( + RuntimeError, "coroutine raised StopIteration"): + + run_async(foo()) + + def test_func_3(self): + async def foo(): + raise StopIteration + + coro = foo() + self.assertRegex(repr(coro), '^$') + coro.close() + + def test_func_4(self): + async def foo(): + raise StopIteration + coro = foo() + + check = lambda: self.assertRaisesRegex( + TypeError, "'coroutine' object is not iterable") + + with check(): + list(coro) + + with check(): + tuple(coro) + + with check(): + sum(coro) + + with check(): + iter(coro) + + with check(): + for i in coro: + pass + + with check(): + [i for i in coro] + + coro.close() + + def test_func_5(self): + @types.coroutine + def bar(): + yield 1 + + async def foo(): + await bar() + + check = lambda: self.assertRaisesRegex( + TypeError, "'coroutine' object is not iterable") + + coro = foo() + with check(): + for el in coro: + pass + coro.close() + + # the following should pass without an error + for el in bar(): + self.assertEqual(el, 1) + self.assertEqual([el for el in bar()], [1]) + self.assertEqual(tuple(bar()), (1,)) + self.assertEqual(next(iter(bar())), 1) + + def test_func_6(self): + @types.coroutine + def bar(): + yield 1 + yield 2 + + async def foo(): + await bar() + + f = foo() + self.assertEqual(f.send(None), 1) + self.assertEqual(f.send(None), 2) + with self.assertRaises(StopIteration): + f.send(None) + + def test_func_7(self): + async def bar(): + return 10 + coro = bar() + + def foo(): + yield from coro + + with self.assertRaisesRegex( + TypeError, + "cannot 'yield from' a coroutine object in " + "a non-coroutine generator"): + list(foo()) + + coro.close() + + def test_func_8(self): + @types.coroutine + def bar(): + return (yield from coro) + + async def foo(): + return 'spam' + + coro = foo() + self.assertEqual(run_async(bar()), ([], 'spam')) + coro.close() + + def test_func_9(self): + async def foo(): + pass + + with self.assertWarnsRegex( + RuntimeWarning, + r"coroutine '.*test_func_9.*foo' was never awaited"): + + foo() + support.gc_collect() + + with self.assertWarnsRegex( + RuntimeWarning, + r"coroutine '.*test_func_9.*foo' was never awaited"): + + with self.assertRaises(TypeError): + # See bpo-32703. + for _ in foo(): + pass + + support.gc_collect() + + def test_func_10(self): + N = 0 + + @types.coroutine + def gen(): + nonlocal N + try: + a = yield + yield (a ** 2) + except ZeroDivisionError: + N += 100 + raise + finally: + N += 1 + + async def foo(): + await gen() + + coro = foo() + aw = coro.__await__() + self.assertIs(aw, iter(aw)) + next(aw) + self.assertEqual(aw.send(10), 100) + + self.assertEqual(N, 0) + aw.close() + self.assertEqual(N, 1) + + coro = foo() + aw = coro.__await__() + next(aw) + with self.assertRaises(ZeroDivisionError): + aw.throw(ZeroDivisionError()) + self.assertEqual(N, 102) + + coro = foo() + aw = coro.__await__() + next(aw) + with self.assertRaises(ZeroDivisionError): + with self.assertWarns(DeprecationWarning): + aw.throw(ZeroDivisionError, ZeroDivisionError(), None) + + def test_func_11(self): + async def func(): pass + coro = func() + # Test that PyCoro_Type and _PyCoroWrapper_Type types were properly + # initialized + self.assertIn('__await__', dir(coro)) + self.assertIn('__iter__', dir(coro.__await__())) + self.assertIn('coroutine_wrapper', repr(coro.__await__())) + coro.close() # avoid RuntimeWarning + + def test_func_12(self): + async def g(): + i = me.send(None) + await foo + me = g() + with self.assertRaisesRegex(ValueError, + "coroutine already executing"): + me.send(None) + + def test_func_13(self): + async def g(): + pass + + coro = g() + with self.assertRaisesRegex( + TypeError, + "can't send non-None value to a just-started coroutine"): + coro.send('spam') + + coro.close() + + def test_func_14(self): + @types.coroutine + def gen(): + yield + async def coro(): + try: + await gen() + except GeneratorExit: + await gen() + c = coro() + c.send(None) + with self.assertRaisesRegex(RuntimeError, + "coroutine ignored GeneratorExit"): + c.close() + + def test_func_15(self): + # See http://bugs.python.org/issue25887 for details + + async def spammer(): + return 'spam' + async def reader(coro): + return await coro + + spammer_coro = spammer() + + with self.assertRaisesRegex(StopIteration, 'spam'): + reader(spammer_coro).send(None) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + reader(spammer_coro).send(None) + + def test_func_16(self): + # See http://bugs.python.org/issue25887 for details + + @types.coroutine + def nop(): + yield + async def send(): + await nop() + return 'spam' + async def read(coro): + await nop() + return await coro + + spammer = send() + + reader = read(spammer) + reader.send(None) + reader.send(None) + with self.assertRaisesRegex(Exception, 'ham'): + reader.throw(Exception('ham')) + + reader = read(spammer) + reader.send(None) + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + reader.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + reader.throw(Exception('wat')) + + def test_func_17(self): + # See http://bugs.python.org/issue25887 for details + + async def coroutine(): + return 'spam' + + coro = coroutine() + with self.assertRaisesRegex(StopIteration, 'spam'): + coro.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + coro.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + coro.throw(Exception('wat')) + + # Closing a coroutine shouldn't raise any exception even if it's + # already closed/exhausted (similar to generators) + coro.close() + coro.close() + + def test_func_18(self): + # See http://bugs.python.org/issue25887 for details + + async def coroutine(): + return 'spam' + + coro = coroutine() + await_iter = coro.__await__() + it = iter(await_iter) + + with self.assertRaisesRegex(StopIteration, 'spam'): + it.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + it.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + # Although the iterator protocol requires iterators to + # raise another StopIteration here, we don't want to do + # that. In this particular case, the iterator will raise + # a RuntimeError, so that 'yield from' and 'await' + # expressions will trigger the error, instead of silently + # ignoring the call. + next(it) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + it.throw(Exception('wat')) + + with self.assertRaisesRegex(RuntimeError, + 'cannot reuse already awaited coroutine'): + it.throw(Exception('wat')) + + # Closing a coroutine shouldn't raise any exception even if it's + # already closed/exhausted (similar to generators) + it.close() + it.close() + + def test_func_19(self): + CHK = 0 + + @types.coroutine + def foo(): + nonlocal CHK + yield + try: + yield + except GeneratorExit: + CHK += 1 + + async def coroutine(): + await foo() + + coro = coroutine() + + coro.send(None) + coro.send(None) + + self.assertEqual(CHK, 0) + coro.close() + self.assertEqual(CHK, 1) + + for _ in range(3): + # Closing a coroutine shouldn't raise any exception even if it's + # already closed/exhausted (similar to generators) + coro.close() + self.assertEqual(CHK, 1) + + def test_coro_wrapper_send_tuple(self): + async def foo(): + return (10,) + + result = run_async__await__(foo()) + self.assertEqual(result, ([], (10,))) + + def test_coro_wrapper_send_stop_iterator(self): + async def foo(): + return StopIteration(10) + + result = run_async__await__(foo()) + self.assertIsInstance(result[1], StopIteration) + self.assertEqual(result[1].value, 10) + + def test_cr_await(self): + @types.coroutine + def a(): + self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_RUNNING) + self.assertIsNone(coro_b.cr_await) + yield + self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_RUNNING) + self.assertIsNone(coro_b.cr_await) + + async def c(): + await a() + + async def b(): + self.assertIsNone(coro_b.cr_await) + await c() + self.assertIsNone(coro_b.cr_await) + + coro_b = b() + self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_CREATED) + self.assertIsNone(coro_b.cr_await) + + coro_b.send(None) + self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_SUSPENDED) + self.assertEqual(coro_b.cr_await.cr_await.gi_code.co_name, 'a') + + with self.assertRaises(StopIteration): + coro_b.send(None) # complete coroutine + self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_CLOSED) + self.assertIsNone(coro_b.cr_await) + + def test_corotype_1(self): + ct = types.CoroutineType + if not support.MISSING_C_DOCSTRINGS: + self.assertIn('into coroutine', ct.send.__doc__) + self.assertIn('inside coroutine', ct.close.__doc__) + self.assertIn('in coroutine', ct.throw.__doc__) + self.assertIn('of the coroutine', ct.__dict__['__name__'].__doc__) + self.assertIn('of the coroutine', ct.__dict__['__qualname__'].__doc__) + self.assertEqual(ct.__name__, 'coroutine') + + async def f(): pass + c = f() + self.assertIn('coroutine object', repr(c)) + c.close() + + def test_await_1(self): + + async def foo(): + await 1 + with self.assertRaisesRegex(TypeError, "object int can.t.*await"): + run_async(foo()) + + def test_await_2(self): + async def foo(): + await [] + with self.assertRaisesRegex(TypeError, "object list can.t.*await"): + run_async(foo()) + + def test_await_3(self): + async def foo(): + await AsyncYieldFrom([1, 2, 3]) + + self.assertEqual(run_async(foo()), ([1, 2, 3], None)) + self.assertEqual(run_async__await__(foo()), ([1, 2, 3], None)) + + def test_await_4(self): + async def bar(): + return 42 + + async def foo(): + return await bar() + + self.assertEqual(run_async(foo()), ([], 42)) + + def test_await_5(self): + class Awaitable: + def __await__(self): + return + + async def foo(): + return (await Awaitable()) + + with self.assertRaisesRegex( + TypeError, "__await__.*returned non-iterator of type"): + + run_async(foo()) + + def test_await_6(self): + class Awaitable: + def __await__(self): + return iter([52]) + + async def foo(): + return (await Awaitable()) + + self.assertEqual(run_async(foo()), ([52], None)) + + def test_await_7(self): + class Awaitable: + def __await__(self): + yield 42 + return 100 + + async def foo(): + return (await Awaitable()) + + self.assertEqual(run_async(foo()), ([42], 100)) + + def test_await_8(self): + class Awaitable: + pass + + async def foo(): return await Awaitable() + + with self.assertRaisesRegex( + TypeError, "object Awaitable can't be used in 'await' expression"): + + run_async(foo()) + + def test_await_9(self): + def wrap(): + return bar + + async def bar(): + return 42 + + async def foo(): + db = {'b': lambda: wrap} + + class DB: + b = wrap + + return (await bar() + await wrap()() + await db['b']()()() + + await bar() * 1000 + await DB.b()()) + + async def foo2(): + return -await bar() + + self.assertEqual(run_async(foo()), ([], 42168)) + self.assertEqual(run_async(foo2()), ([], -42)) + + def test_await_10(self): + async def baz(): + return 42 + + async def bar(): + return baz() + + async def foo(): + return await (await bar()) + + self.assertEqual(run_async(foo()), ([], 42)) + + def test_await_11(self): + def ident(val): + return val + + async def bar(): + return 'spam' + + async def foo(): + return ident(val=await bar()) + + async def foo2(): + return await bar(), 'ham' + + self.assertEqual(run_async(foo2()), ([], ('spam', 'ham'))) + + def test_await_12(self): + async def coro(): + return 'spam' + c = coro() + + class Awaitable: + def __await__(self): + return c + + async def foo(): + return await Awaitable() + + with self.assertRaisesRegex( + TypeError, r"__await__\(\) returned a coroutine"): + run_async(foo()) + + c.close() + + def test_await_13(self): + class Awaitable: + def __await__(self): + return self + + async def foo(): + return await Awaitable() + + with self.assertRaisesRegex( + TypeError, "__await__.*returned non-iterator of type"): + + run_async(foo()) + + def test_await_14(self): + class Wrapper: + # Forces the interpreter to use CoroutineType.__await__ + def __init__(self, coro): + assert coro.__class__ is types.CoroutineType + self.coro = coro + def __await__(self): + return self.coro.__await__() + + class FutureLike: + def __await__(self): + return (yield) + + class Marker(Exception): + pass + + async def coro1(): + try: + return await FutureLike() + except ZeroDivisionError: + raise Marker + async def coro2(): + return await Wrapper(coro1()) + + c = coro2() + c.send(None) + with self.assertRaisesRegex(StopIteration, 'spam'): + c.send('spam') + + c = coro2() + c.send(None) + with self.assertRaises(Marker): + c.throw(ZeroDivisionError) + + def test_await_15(self): + @types.coroutine + def nop(): + yield + + async def coroutine(): + await nop() + + async def waiter(coro): + await coro + + coro = coroutine() + coro.send(None) + + with self.assertRaisesRegex(RuntimeError, + "coroutine is being awaited already"): + waiter(coro).send(None) + + def test_await_16(self): + # See https://bugs.python.org/issue29600 for details. + + async def f(): + return ValueError() + + async def g(): + try: + raise KeyError + except: + return await f() + + _, result = run_async(g()) + self.assertIsNone(result.__context__) + + def test_with_1(self): + class Manager: + def __init__(self, name): + self.name = name + + async def __aenter__(self): + await AsyncYieldFrom(['enter-1-' + self.name, + 'enter-2-' + self.name]) + return self + + async def __aexit__(self, *args): + await AsyncYieldFrom(['exit-1-' + self.name, + 'exit-2-' + self.name]) + + if self.name == 'B': + return True + + + async def foo(): + async with Manager("A") as a, Manager("B") as b: + await AsyncYieldFrom([('managers', a.name, b.name)]) + 1/0 + + f = foo() + result, _ = run_async(f) + + self.assertEqual( + result, ['enter-1-A', 'enter-2-A', 'enter-1-B', 'enter-2-B', + ('managers', 'A', 'B'), + 'exit-1-B', 'exit-2-B', 'exit-1-A', 'exit-2-A'] + ) + + async def foo(): + async with Manager("A") as a, Manager("C") as c: + await AsyncYieldFrom([('managers', a.name, c.name)]) + 1/0 + + with self.assertRaises(ZeroDivisionError): + run_async(foo()) + + def test_with_2(self): + class CM: + def __aenter__(self): + pass + + body_executed = None + async def foo(): + nonlocal body_executed + body_executed = False + async with CM(): + body_executed = True + + with self.assertRaisesRegex(TypeError, 'asynchronous context manager.*__aexit__'): + run_async(foo()) + self.assertIs(body_executed, False) + + def test_with_3(self): + class CM: + def __aexit__(self): + pass + + body_executed = None + async def foo(): + nonlocal body_executed + body_executed = False + async with CM(): + body_executed = True + + with self.assertRaisesRegex(TypeError, 'asynchronous context manager'): + run_async(foo()) + self.assertIs(body_executed, False) + + def test_with_4(self): + class CM: + pass + + body_executed = None + async def foo(): + nonlocal body_executed + body_executed = False + async with CM(): + body_executed = True + + with self.assertRaisesRegex(TypeError, 'asynchronous context manager'): + run_async(foo()) + self.assertIs(body_executed, False) + + def test_with_5(self): + # While this test doesn't make a lot of sense, + # it's a regression test for an early bug with opcodes + # generation + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *exc): + pass + + async def func(): + async with CM(): + self.assertEqual((1, ), 1) + + with self.assertRaises(AssertionError): + run_async(func()) + + def test_with_6(self): + class CM: + def __aenter__(self): + return 123 + + def __aexit__(self, *e): + return 456 + + async def foo(): + async with CM(): + pass + + with self.assertRaisesRegex( + TypeError, + "'async with' received an object from __aenter__ " + "that does not implement __await__: int"): + # it's important that __aexit__ wasn't called + run_async(foo()) + + def test_with_7(self): + class CM: + async def __aenter__(self): + return self + + def __aexit__(self, *e): + return 444 + + # Exit with exception + async def foo(): + async with CM(): + 1/0 + + try: + run_async(foo()) + except TypeError as exc: + self.assertRegex( + exc.args[0], + "'async with' received an object from __aexit__ " + "that does not implement __await__: int") + self.assertTrue(exc.__context__ is not None) + self.assertTrue(isinstance(exc.__context__, ZeroDivisionError)) + else: + self.fail('invalid asynchronous context manager did not fail') + + + def test_with_8(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + def __aexit__(self, *e): + return 456 + + # Normal exit + async def foo(): + nonlocal CNT + async with CM(): + CNT += 1 + with self.assertRaisesRegex( + TypeError, + "'async with' received an object from __aexit__ " + "that does not implement __await__: int"): + run_async(foo()) + self.assertEqual(CNT, 1) + + # Exit with 'break' + async def foo(): + nonlocal CNT + for i in range(2): + async with CM(): + CNT += 1 + break + with self.assertRaisesRegex( + TypeError, + "'async with' received an object from __aexit__ " + "that does not implement __await__: int"): + run_async(foo()) + self.assertEqual(CNT, 2) + + # Exit with 'continue' + async def foo(): + nonlocal CNT + for i in range(2): + async with CM(): + CNT += 1 + continue + with self.assertRaisesRegex( + TypeError, + "'async with' received an object from __aexit__ " + "that does not implement __await__: int"): + run_async(foo()) + self.assertEqual(CNT, 3) + + # Exit with 'return' + async def foo(): + nonlocal CNT + async with CM(): + CNT += 1 + return + with self.assertRaisesRegex( + TypeError, + "'async with' received an object from __aexit__ " + "that does not implement __await__: int"): + run_async(foo()) + self.assertEqual(CNT, 4) + + + def test_with_9(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *e): + 1/0 + + async def foo(): + nonlocal CNT + async with CM(): + CNT += 1 + + with self.assertRaises(ZeroDivisionError): + run_async(foo()) + + self.assertEqual(CNT, 1) + + def test_with_10(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *e): + 1/0 + + async def foo(): + nonlocal CNT + async with CM(): + async with CM(): + raise RuntimeError + + try: + run_async(foo()) + except ZeroDivisionError as exc: + self.assertTrue(exc.__context__ is not None) + self.assertTrue(isinstance(exc.__context__, ZeroDivisionError)) + self.assertTrue(isinstance(exc.__context__.__context__, + RuntimeError)) + else: + self.fail('exception from __aexit__ did not propagate') + + def test_with_11(self): + CNT = 0 + + class CM: + async def __aenter__(self): + raise NotImplementedError + + async def __aexit__(self, *e): + 1/0 + + async def foo(): + nonlocal CNT + async with CM(): + raise RuntimeError + + try: + run_async(foo()) + except NotImplementedError as exc: + self.assertTrue(exc.__context__ is None) + else: + self.fail('exception from __aenter__ did not propagate') + + def test_with_12(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *e): + return True + + async def foo(): + nonlocal CNT + async with CM() as cm: + self.assertIs(cm.__class__, CM) + raise RuntimeError + + run_async(foo()) + + def test_with_13(self): + CNT = 0 + + class CM: + async def __aenter__(self): + 1/0 + + async def __aexit__(self, *e): + return True + + async def foo(): + nonlocal CNT + CNT += 1 + async with CM(): + CNT += 1000 + CNT += 10000 + + with self.assertRaises(ZeroDivisionError): + run_async(foo()) + self.assertEqual(CNT, 1) + + def test_for_1(self): + aiter_calls = 0 + + class AsyncIter: + def __init__(self): + self.i = 0 + + def __aiter__(self): + nonlocal aiter_calls + aiter_calls += 1 + return self + + async def __anext__(self): + self.i += 1 + + if not (self.i % 10): + await AsyncYield(self.i * 10) + + if self.i > 100: + raise StopAsyncIteration + + return self.i, self.i + + + buffer = [] + async def test1(): + async for i1, i2 in AsyncIter(): + buffer.append(i1 + i2) + + yielded, _ = run_async(test1()) + # Make sure that __aiter__ was called only once + self.assertEqual(aiter_calls, 1) + self.assertEqual(yielded, [i * 100 for i in range(1, 11)]) + self.assertEqual(buffer, [i*2 for i in range(1, 101)]) + + + buffer = [] + async def test2(): + nonlocal buffer + async for i in AsyncIter(): + buffer.append(i[0]) + if i[0] == 20: + break + else: + buffer.append('what?') + buffer.append('end') + + yielded, _ = run_async(test2()) + # Make sure that __aiter__ was called only once + self.assertEqual(aiter_calls, 2) + self.assertEqual(yielded, [100, 200]) + self.assertEqual(buffer, [i for i in range(1, 21)] + ['end']) + + + buffer = [] + async def test3(): + nonlocal buffer + async for i in AsyncIter(): + if i[0] > 20: + continue + buffer.append(i[0]) + else: + buffer.append('what?') + buffer.append('end') + + yielded, _ = run_async(test3()) + # Make sure that __aiter__ was called only once + self.assertEqual(aiter_calls, 3) + self.assertEqual(yielded, [i * 100 for i in range(1, 11)]) + self.assertEqual(buffer, [i for i in range(1, 21)] + + ['what?', 'end']) + + def test_for_2(self): + tup = (1, 2, 3) + refs_before = sys.getrefcount(tup) + + async def foo(): + async for i in tup: + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, "async for' requires an object.*__aiter__.*tuple"): + + run_async(foo()) + + self.assertEqual(sys.getrefcount(tup), refs_before) + + def test_for_3(self): + class I: + def __aiter__(self): + return self + + aiter = I() + refs_before = sys.getrefcount(aiter) + + async def foo(): + async for i in aiter: + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, + r"that does not implement __anext__"): + + run_async(foo()) + + self.assertEqual(sys.getrefcount(aiter), refs_before) + + def test_for_4(self): + class I: + def __aiter__(self): + return self + + def __anext__(self): + return () + + aiter = I() + refs_before = sys.getrefcount(aiter) + + async def foo(): + async for i in aiter: + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, + "async for' received an invalid object.*__anext__.*tuple"): + + run_async(foo()) + + self.assertEqual(sys.getrefcount(aiter), refs_before) + + def test_for_6(self): + I = 0 + + class Manager: + async def __aenter__(self): + nonlocal I + I += 10000 + + async def __aexit__(self, *args): + nonlocal I + I += 100000 + + class Iterable: + def __init__(self): + self.i = 0 + + def __aiter__(self): + return self + + async def __anext__(self): + if self.i > 10: + raise StopAsyncIteration + self.i += 1 + return self.i + + ############## + + manager = Manager() + iterable = Iterable() + mrefs_before = sys.getrefcount(manager) + irefs_before = sys.getrefcount(iterable) + + async def main(): + nonlocal I + + async with manager: + async for i in iterable: + I += 1 + I += 1000 + + with warnings.catch_warnings(): + warnings.simplefilter("error") + # Test that __aiter__ that returns an asynchronous iterator + # directly does not throw any warnings. + run_async(main()) + self.assertEqual(I, 111011) + + self.assertEqual(sys.getrefcount(manager), mrefs_before) + self.assertEqual(sys.getrefcount(iterable), irefs_before) + + ############## + + async def main(): + nonlocal I + + async with Manager(): + async for i in Iterable(): + I += 1 + I += 1000 + + async with Manager(): + async for i in Iterable(): + I += 1 + I += 1000 + + run_async(main()) + self.assertEqual(I, 333033) + + ############## + + async def main(): + nonlocal I + + async with Manager(): + I += 100 + async for i in Iterable(): + I += 1 + else: + I += 10000000 + I += 1000 + + async with Manager(): + I += 100 + async for i in Iterable(): + I += 1 + else: + I += 10000000 + I += 1000 + + run_async(main()) + self.assertEqual(I, 20555255) + + def test_for_7(self): + CNT = 0 + class AI: + def __aiter__(self): + 1/0 + async def foo(): + nonlocal CNT + async for i in AI(): + CNT += 1 + CNT += 10 + with self.assertRaises(ZeroDivisionError): + run_async(foo()) + self.assertEqual(CNT, 0) + + def test_for_8(self): + CNT = 0 + class AI: + def __aiter__(self): + 1/0 + async def foo(): + nonlocal CNT + async for i in AI(): + CNT += 1 + CNT += 10 + with self.assertRaises(ZeroDivisionError): + with warnings.catch_warnings(): + warnings.simplefilter("error") + # Test that if __aiter__ raises an exception it propagates + # without any kind of warning. + run_async(foo()) + self.assertEqual(CNT, 0) + + def test_for_11(self): + class F: + def __aiter__(self): + return self + def __anext__(self): + return self + def __await__(self): + 1 / 0 + + async def main(): + async for _ in F(): + pass + + with self.assertRaisesRegex(TypeError, + 'an invalid object from __anext__') as c: + main().send(None) + + err = c.exception + self.assertIsInstance(err.__cause__, ZeroDivisionError) + + def test_for_tuple(self): + class Done(Exception): pass + + class AIter(tuple): + i = 0 + def __aiter__(self): + return self + async def __anext__(self): + if self.i >= len(self): + raise StopAsyncIteration + self.i += 1 + return self[self.i - 1] + + result = [] + async def foo(): + async for i in AIter([42]): + result.append(i) + raise Done + + with self.assertRaises(Done): + foo().send(None) + self.assertEqual(result, [42]) + + def test_for_stop_iteration(self): + class Done(Exception): pass + + class AIter(StopIteration): + i = 0 + def __aiter__(self): + return self + async def __anext__(self): + if self.i: + raise StopAsyncIteration + self.i += 1 + return self.value + + result = [] + async def foo(): + async for i in AIter(42): + result.append(i) + raise Done + + with self.assertRaises(Done): + foo().send(None) + self.assertEqual(result, [42]) + + def test_comp_1(self): + async def f(i): + return i + + async def run_list(): + return [await c for c in [f(1), f(41)]] + + async def run_set(): + return {await c for c in [f(1), f(41)]} + + async def run_dict1(): + return {await c: 'a' for c in [f(1), f(41)]} + + async def run_dict2(): + return {i: await c for i, c in enumerate([f(1), f(41)])} + + self.assertEqual(run_async(run_list()), ([], [1, 41])) + self.assertEqual(run_async(run_set()), ([], {1, 41})) + self.assertEqual(run_async(run_dict1()), ([], {1: 'a', 41: 'a'})) + self.assertEqual(run_async(run_dict2()), ([], {0: 1, 1: 41})) + + def test_comp_2(self): + async def f(i): + return i + + async def run_list(): + return [s for c in [f(''), f('abc'), f(''), f(['de', 'fg'])] + for s in await c] + + self.assertEqual( + run_async(run_list()), + ([], ['a', 'b', 'c', 'de', 'fg'])) + + async def run_set(): + return {d + for c in [f([f([10, 30]), + f([20])])] + for s in await c + for d in await s} + + self.assertEqual( + run_async(run_set()), + ([], {10, 20, 30})) + + async def run_set2(): + return {await s + for c in [f([f(10), f(20)])] + for s in await c} + + self.assertEqual( + run_async(run_set2()), + ([], {10, 20})) + + def test_comp_3(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 async for i in f([10, 20])] + self.assertEqual( + run_async(run_list()), + ([], [11, 21])) + + async def run_set(): + return {i + 1 async for i in f([10, 20])} + self.assertEqual( + run_async(run_set()), + ([], {11, 21})) + + async def run_dict(): + return {i + 1: i + 2 async for i in f([10, 20])} + self.assertEqual( + run_async(run_dict()), + ([], {11: 12, 21: 22})) + + async def run_gen(): + gen = (i + 1 async for i in f([10, 20])) + return [g + 100 async for g in gen] + self.assertEqual( + run_async(run_gen()), + ([], [111, 121])) + + def test_comp_4(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 async for i in f([10, 20]) if i > 10] + self.assertEqual( + run_async(run_list()), + ([], [21])) + + async def run_set(): + return {i + 1 async for i in f([10, 20]) if i > 10} + self.assertEqual( + run_async(run_set()), + ([], {21})) + + async def run_dict(): + return {i + 1: i + 2 async for i in f([10, 20]) if i > 10} + self.assertEqual( + run_async(run_dict()), + ([], {21: 22})) + + async def run_gen(): + gen = (i + 1 async for i in f([10, 20]) if i > 10) + return [g + 100 async for g in gen] + self.assertEqual( + run_async(run_gen()), + ([], [121])) + + def test_comp_4_2(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 10 async for i in f(range(5)) if 0 < i < 4] + self.assertEqual( + run_async(run_list()), + ([], [11, 12, 13])) + + async def run_set(): + return {i + 10 async for i in f(range(5)) if 0 < i < 4} + self.assertEqual( + run_async(run_set()), + ([], {11, 12, 13})) + + async def run_dict(): + return {i + 10: i + 100 async for i in f(range(5)) if 0 < i < 4} + self.assertEqual( + run_async(run_dict()), + ([], {11: 101, 12: 102, 13: 103})) + + async def run_gen(): + gen = (i + 10 async for i in f(range(5)) if 0 < i < 4) + return [g + 100 async for g in gen] + self.assertEqual( + run_async(run_gen()), + ([], [111, 112, 113])) + + def test_comp_5(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 for pair in ([10, 20], [30, 40]) if pair[0] > 10 + async for i in f(pair) if i > 30] + self.assertEqual( + run_async(run_list()), + ([], [41])) + + def test_comp_6(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 async for seq in f([(10, 20), (30,)]) + for i in seq] + + self.assertEqual( + run_async(run_list()), + ([], [11, 21, 31])) + + def test_comp_7(self): + async def f(): + yield 1 + yield 2 + raise Exception('aaa') + + async def run_list(): + return [i async for i in f()] + + with self.assertRaisesRegex(Exception, 'aaa'): + run_async(run_list()) + + def test_comp_8(self): + async def f(): + return [i for i in [1, 2, 3]] + + self.assertEqual( + run_async(f()), + ([], [1, 2, 3])) + + def test_comp_9(self): + async def gen(): + yield 1 + yield 2 + async def f(): + l = [i async for i in gen()] + return [i for i in l] + + self.assertEqual( + run_async(f()), + ([], [1, 2])) + + def test_comp_10(self): + async def f(): + xx = {i for i in [1, 2, 3]} + return {x: x for x in xx} + + self.assertEqual( + run_async(f()), + ([], {1: 1, 2: 2, 3: 3})) + + def test_nested_comp(self): + async def run_list_inside_list(): + return [[i + j async for i in asynciter([1, 2])] for j in [10, 20]] + self.assertEqual( + run_async(run_list_inside_list()), + ([], [[11, 12], [21, 22]])) + + async def run_set_inside_list(): + return [{i + j async for i in asynciter([1, 2])} for j in [10, 20]] + self.assertEqual( + run_async(run_set_inside_list()), + ([], [{11, 12}, {21, 22}])) + + async def run_list_inside_set(): + return {sum([i async for i in asynciter(range(j))]) for j in [3, 5]} + self.assertEqual( + run_async(run_list_inside_set()), + ([], {3, 10})) + + async def run_dict_inside_dict(): + return {j: {i: i + j async for i in asynciter([1, 2])} for j in [10, 20]} + self.assertEqual( + run_async(run_dict_inside_dict()), + ([], {10: {1: 11, 2: 12}, 20: {1: 21, 2: 22}})) + + async def run_list_inside_gen(): + gen = ([i + j async for i in asynciter([1, 2])] for j in [10, 20]) + return [x async for x in gen] + self.assertEqual( + run_async(run_list_inside_gen()), + ([], [[11, 12], [21, 22]])) + + async def run_gen_inside_list(): + gens = [(i async for i in asynciter(range(j))) for j in [3, 5]] + return [x for g in gens async for x in g] + self.assertEqual( + run_async(run_gen_inside_list()), + ([], [0, 1, 2, 0, 1, 2, 3, 4])) + + async def run_gen_inside_gen(): + gens = ((i async for i in asynciter(range(j))) for j in [3, 5]) + return [x for g in gens async for x in g] + self.assertEqual( + run_async(run_gen_inside_gen()), + ([], [0, 1, 2, 0, 1, 2, 3, 4])) + + async def run_list_inside_list_inside_list(): + return [[[i + j + k async for i in asynciter([1, 2])] + for j in [10, 20]] + for k in [100, 200]] + self.assertEqual( + run_async(run_list_inside_list_inside_list()), + ([], [[[111, 112], [121, 122]], [[211, 212], [221, 222]]])) + + def test_copy(self): + async def func(): pass + coro = func() + with self.assertRaises(TypeError): + copy.copy(coro) + + aw = coro.__await__() + try: + with self.assertRaises(TypeError): + copy.copy(aw) + finally: + aw.close() + + def test_pickle(self): + async def func(): pass + coro = func() + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises((TypeError, pickle.PicklingError)): + pickle.dumps(coro, proto) + + aw = coro.__await__() + try: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises((TypeError, pickle.PicklingError)): + pickle.dumps(aw, proto) + finally: + aw.close() + + def test_fatal_coro_warning(self): + # Issue 27811 + async def func(): pass + with warnings.catch_warnings(), \ + support.catch_unraisable_exception() as cm: + warnings.filterwarnings("error") + coro = func() + # only store repr() to avoid keeping the coroutine alive + coro_repr = repr(coro) + coro = None + support.gc_collect() + + self.assertIn("was never awaited", str(cm.unraisable.exc_value)) + self.assertEqual(repr(cm.unraisable.object), coro_repr) + + def test_for_assign_raising_stop_async_iteration(self): + class BadTarget: + def __setitem__(self, key, value): + raise StopAsyncIteration(42) + tgt = BadTarget() + async def source(): + yield 10 + + async def run_for(): + with self.assertRaises(StopAsyncIteration) as cm: + async for tgt[0] in source(): + pass + self.assertEqual(cm.exception.args, (42,)) + return 'end' + self.assertEqual(run_async(run_for()), ([], 'end')) + + async def run_list(): + with self.assertRaises(StopAsyncIteration) as cm: + return [0 async for tgt[0] in source()] + self.assertEqual(cm.exception.args, (42,)) + return 'end' + self.assertEqual(run_async(run_list()), ([], 'end')) + + async def run_gen(): + gen = (0 async for tgt[0] in source()) + a = gen.asend(None) + with self.assertRaises(RuntimeError) as cm: + await a + self.assertIsInstance(cm.exception.__cause__, StopAsyncIteration) + self.assertEqual(cm.exception.__cause__.args, (42,)) + return 'end' + self.assertEqual(run_async(run_gen()), ([], 'end')) + + def test_for_assign_raising_stop_async_iteration_2(self): + class BadIterable: + def __iter__(self): + raise StopAsyncIteration(42) + async def badpairs(): + yield BadIterable() + + async def run_for(): + with self.assertRaises(StopAsyncIteration) as cm: + async for i, j in badpairs(): + pass + self.assertEqual(cm.exception.args, (42,)) + return 'end' + self.assertEqual(run_async(run_for()), ([], 'end')) + + async def run_list(): + with self.assertRaises(StopAsyncIteration) as cm: + return [0 async for i, j in badpairs()] + self.assertEqual(cm.exception.args, (42,)) + return 'end' + self.assertEqual(run_async(run_list()), ([], 'end')) + + async def run_gen(): + gen = (0 async for i, j in badpairs()) + a = gen.asend(None) + with self.assertRaises(RuntimeError) as cm: + await a + self.assertIsInstance(cm.exception.__cause__, StopAsyncIteration) + self.assertEqual(cm.exception.__cause__.args, (42,)) + return 'end' + self.assertEqual(run_async(run_gen()), ([], 'end')) + + def test_bpo_45813_1(self): + 'This would crash the interpreter in 3.11a2' + async def f(): + pass + with self.assertWarns(RuntimeWarning): + frame = f().cr_frame + frame.clear() + + def test_bpo_45813_2(self): + 'This would crash the interpreter in 3.11a2' + async def f(): + pass + gen = f() + with self.assertWarns(RuntimeWarning): + gen.cr_frame.clear() + gen.close() + + def test_cr_frame_after_close(self): + async def f(): + pass + gen = f() + self.assertIsNotNone(gen.cr_frame) + gen.close() + self.assertIsNone(gen.cr_frame) + + def test_stack_in_coroutine_throw(self): + # Regression test for https://github.com/python/cpython/issues/93592 + async def a(): + return await b() + + async def b(): + return await c() + + @types.coroutine + def c(): + try: + # traceback.print_stack() + yield len(traceback.extract_stack()) + except ZeroDivisionError: + # traceback.print_stack() + yield len(traceback.extract_stack()) + + coro = a() + len_send = coro.send(None) + len_throw = coro.throw(ZeroDivisionError) + # before fixing, visible stack from throw would be shorter than from send. + self.assertEqual(len_send, len_throw) + + +@unittest.skipIf( + support.is_emscripten or support.is_wasi, + "asyncio does not work under Emscripten/WASI yet." +) +class CoroAsyncIOCompatTest(unittest.TestCase): + + def test_asyncio_1(self): + # asyncio cannot be imported when Python is compiled without thread + # support + asyncio = import_helper.import_module('asyncio') + + class MyException(Exception): + pass + + buffer = [] + + class CM: + async def __aenter__(self): + buffer.append(1) + await asyncio.sleep(0.01) + buffer.append(2) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await asyncio.sleep(0.01) + buffer.append(exc_type.__name__) + + async def f(): + async with CM() as c: + await asyncio.sleep(0.01) + raise MyException + buffer.append('unreachable') + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + loop.run_until_complete(f()) + except MyException: + pass + finally: + loop.close() + asyncio.set_event_loop_policy(None) + + self.assertEqual(buffer, [1, 2, 'MyException']) + + +class OriginTrackingTest(unittest.TestCase): + def here(self): + info = inspect.getframeinfo(inspect.currentframe().f_back) + return (info.filename, info.lineno) + + def test_origin_tracking(self): + orig_depth = sys.get_coroutine_origin_tracking_depth() + try: + async def corofn(): + pass + + sys.set_coroutine_origin_tracking_depth(0) + self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 0) + + with contextlib.closing(corofn()) as coro: + self.assertIsNone(coro.cr_origin) + + sys.set_coroutine_origin_tracking_depth(1) + self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 1) + + fname, lineno = self.here() + with contextlib.closing(corofn()) as coro: + self.assertEqual(coro.cr_origin, + ((fname, lineno + 1, "test_origin_tracking"),)) + + sys.set_coroutine_origin_tracking_depth(2) + self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 2) + + def nested(): + return (self.here(), corofn()) + fname, lineno = self.here() + ((nested_fname, nested_lineno), coro) = nested() + with contextlib.closing(coro): + self.assertEqual(coro.cr_origin, + ((nested_fname, nested_lineno, "nested"), + (fname, lineno + 1, "test_origin_tracking"))) + + # Check we handle running out of frames correctly + sys.set_coroutine_origin_tracking_depth(1000) + with contextlib.closing(corofn()) as coro: + self.assertTrue(2 < len(coro.cr_origin) < 1000) + + # We can't set depth negative + with self.assertRaises(ValueError): + sys.set_coroutine_origin_tracking_depth(-1) + # And trying leaves it unchanged + self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 1000) + + finally: + sys.set_coroutine_origin_tracking_depth(orig_depth) + + def test_origin_tracking_warning(self): + async def corofn(): + pass + + a1_filename, a1_lineno = self.here() + def a1(): + return corofn() # comment in a1 + a1_lineno += 2 + + a2_filename, a2_lineno = self.here() + def a2(): + return a1() # comment in a2 + a2_lineno += 2 + + def check(depth, msg): + sys.set_coroutine_origin_tracking_depth(depth) + with self.assertWarns(RuntimeWarning) as cm: + a2() + support.gc_collect() + self.assertEqual(msg, str(cm.warning)) + + orig_depth = sys.get_coroutine_origin_tracking_depth() + try: + msg = check(0, f"coroutine '{corofn.__qualname__}' was never awaited") + check(1, "".join([ + f"coroutine '{corofn.__qualname__}' was never awaited\n", + "Coroutine created at (most recent call last)\n", + f' File "{a1_filename}", line {a1_lineno}, in a1\n', + " return corofn() # comment in a1", + ])) + check(2, "".join([ + f"coroutine '{corofn.__qualname__}' was never awaited\n", + "Coroutine created at (most recent call last)\n", + f' File "{a2_filename}", line {a2_lineno}, in a2\n', + " return a1() # comment in a2\n", + f' File "{a1_filename}", line {a1_lineno}, in a1\n', + " return corofn() # comment in a1", + ])) + + finally: + sys.set_coroutine_origin_tracking_depth(orig_depth) + + def test_unawaited_warning_when_module_broken(self): + # Make sure we don't blow up too bad if + # warnings._warn_unawaited_coroutine is broken somehow (e.g. because + # of shutdown problems) + async def corofn(): + pass + + orig_wuc = warnings._warn_unawaited_coroutine + try: + warnings._warn_unawaited_coroutine = lambda coro: 1/0 + with support.catch_unraisable_exception() as cm, \ + warnings_helper.check_warnings( + (r'coroutine .* was never awaited', + RuntimeWarning)): + # only store repr() to avoid keeping the coroutine alive + coro = corofn() + coro_repr = repr(coro) + + # clear reference to the coroutine without awaiting for it + del coro + support.gc_collect() + + self.assertEqual(repr(cm.unraisable.object), coro_repr) + self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError) + + del warnings._warn_unawaited_coroutine + with warnings_helper.check_warnings( + (r'coroutine .* was never awaited', RuntimeWarning)): + corofn() + support.gc_collect() + + finally: + warnings._warn_unawaited_coroutine = orig_wuc + + +class UnawaitedWarningDuringShutdownTest(unittest.TestCase): + # https://bugs.python.org/issue32591#msg310726 + def test_unawaited_warning_during_shutdown(self): + code = ("import asyncio\n" + "async def f(): pass\n" + "async def t(): asyncio.gather(f())\n" + "asyncio.run(t())\n") + assert_python_ok("-c", code) + + code = ("import sys\n" + "async def f(): pass\n" + "sys.coro = f()\n") + assert_python_ok("-c", code) + + code = ("import sys\n" + "async def f(): pass\n" + "sys.corocycle = [f()]\n" + "sys.corocycle.append(sys.corocycle)\n") + assert_python_ok("-c", code) + + +@support.cpython_only +@unittest.skipIf(_testcapi is None, "requires _testcapi") +class CAPITest(unittest.TestCase): + + def test_tp_await_1(self): + from _testcapi import awaitType as at + + async def foo(): + future = at(iter([1])) + return (await future) + + self.assertEqual(foo().send(None), 1) + + def test_tp_await_2(self): + # Test tp_await to __await__ mapping + from _testcapi import awaitType as at + future = at(iter([1])) + self.assertEqual(next(future.__await__()), 1) + + def test_tp_await_3(self): + from _testcapi import awaitType as at + + async def foo(): + future = at(1) + return (await future) + + with self.assertRaisesRegex( + TypeError, "__await__.*returned non-iterator of type 'int'"): + self.assertEqual(foo().send(None), 1) + + +if __name__=="__main__": + unittest.main() diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py new file mode 100644 index 00000000000..778f040f7a6 --- /dev/null +++ b/Lib/test/test_curses.py @@ -0,0 +1,1432 @@ +import functools +import inspect +import os +import string +import sys +import tempfile +import unittest +from unittest.mock import MagicMock + +from test.support import (requires, verbose, SaveSignals, cpython_only, + check_disallow_instantiation, MISSING_C_DOCSTRINGS, + gc_collect) +from test.support.import_helper import import_module + +# Optionally test curses module. This currently requires that the +# 'curses' resource be given on the regrtest command line using the -u +# option. If not available, nothing after this line will be executed. +requires('curses') + +# If either of these don't exist, skip the tests. +curses = import_module('curses') +import_module('curses.ascii') +import_module('curses.textpad') +try: + import curses.panel +except ImportError: + pass + +def requires_curses_func(name): + return unittest.skipUnless(hasattr(curses, name), + 'requires curses.%s' % name) + +def requires_curses_window_meth(name): + def deco(test): + @functools.wraps(test) + def wrapped(self, *args, **kwargs): + if not hasattr(self.stdscr, name): + raise unittest.SkipTest('requires curses.window.%s' % name) + test(self, *args, **kwargs) + return wrapped + return deco + + +def requires_colors(test): + @functools.wraps(test) + def wrapped(self, *args, **kwargs): + if not curses.has_colors(): + self.skipTest('requires colors support') + curses.start_color() + test(self, *args, **kwargs) + return wrapped + +term = os.environ.get('TERM') +SHORT_MAX = 0x7fff +DEFAULT_PAIR_CONTENTS = [ + (curses.COLOR_WHITE, curses.COLOR_BLACK), + (0, 0), + (-1, -1), + (15, 0), # for xterm-256color (15 is for BRIGHT WHITE) +] + +# If newterm was supported we could use it instead of initscr and not exit +@unittest.skipIf(not term or term == 'unknown', + "$TERM=%r, calling initscr() may cause exit" % term) +@unittest.skipIf(sys.platform == "cygwin", + "cygwin's curses mostly just hangs") +class TestCurses(unittest.TestCase): + + @classmethod + def setUpClass(cls): + if verbose: + print(f'TERM={term}', file=sys.stderr, flush=True) + # testing setupterm() inside initscr/endwin + # causes terminal breakage + stdout_fd = sys.__stdout__.fileno() + curses.setupterm(fd=stdout_fd) + + def setUp(self): + self.isatty = True + self.output = sys.__stdout__ + stdout_fd = sys.__stdout__.fileno() + if not sys.__stdout__.isatty(): + # initstr() unconditionally uses C stdout. + # If it is redirected to file or pipe, try to attach it + # to terminal. + # First, save a copy of the file descriptor of stdout, so it + # can be restored after finishing the test. + dup_fd = os.dup(stdout_fd) + self.addCleanup(os.close, dup_fd) + self.addCleanup(os.dup2, dup_fd, stdout_fd) + + if sys.__stderr__.isatty(): + # If stderr is connected to terminal, use it. + tmp = sys.__stderr__ + self.output = sys.__stderr__ + else: + try: + # Try to open the terminal device. + tmp = open('/dev/tty', 'wb', buffering=0) + except OSError: + # As a fallback, use regular file to write control codes. + # Some functions (like savetty) will not work, but at + # least the garbage control sequences will not be mixed + # with the testing report. + tmp = tempfile.TemporaryFile(mode='wb', buffering=0) + self.isatty = False + self.addCleanup(tmp.close) + self.output = None + os.dup2(tmp.fileno(), stdout_fd) + + self.save_signals = SaveSignals() + self.save_signals.save() + self.addCleanup(self.save_signals.restore) + if verbose and self.output is not None: + # just to make the test output a little more readable + sys.stderr.flush() + sys.stdout.flush() + print(file=self.output, flush=True) + self.stdscr = curses.initscr() + if self.isatty: + curses.savetty() + self.addCleanup(curses.endwin) + self.addCleanup(curses.resetty) + self.stdscr.erase() + + @requires_curses_func('filter') + def test_filter(self): + # TODO: Should be called before initscr() or newterm() are called. + # TODO: nofilter() + curses.filter() + + @requires_curses_func('use_env') + def test_use_env(self): + # TODO: Should be called before initscr() or newterm() are called. + # TODO: use_tioctl() + curses.use_env(False) + curses.use_env(True) + + def test_create_windows(self): + win = curses.newwin(5, 10) + self.assertEqual(win.getbegyx(), (0, 0)) + self.assertEqual(win.getparyx(), (-1, -1)) + self.assertEqual(win.getmaxyx(), (5, 10)) + + win = curses.newwin(10, 15, 2, 5) + self.assertEqual(win.getbegyx(), (2, 5)) + self.assertEqual(win.getparyx(), (-1, -1)) + self.assertEqual(win.getmaxyx(), (10, 15)) + + win2 = win.subwin(3, 7) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (1, 2)) + self.assertEqual(win2.getmaxyx(), (9, 13)) + + win2 = win.subwin(5, 10, 3, 7) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (1, 2)) + self.assertEqual(win2.getmaxyx(), (5, 10)) + + win3 = win.derwin(2, 3) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 3)) + self.assertEqual(win3.getmaxyx(), (8, 12)) + + win3 = win.derwin(6, 11, 2, 3) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 3)) + self.assertEqual(win3.getmaxyx(), (6, 11)) + + win.mvwin(0, 1) + self.assertEqual(win.getbegyx(), (0, 1)) + self.assertEqual(win.getparyx(), (-1, -1)) + self.assertEqual(win.getmaxyx(), (10, 15)) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (1, 2)) + self.assertEqual(win2.getmaxyx(), (5, 10)) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 3)) + self.assertEqual(win3.getmaxyx(), (6, 11)) + + win2.mvderwin(2, 1) + self.assertEqual(win2.getbegyx(), (3, 7)) + self.assertEqual(win2.getparyx(), (2, 1)) + self.assertEqual(win2.getmaxyx(), (5, 10)) + + win3.mvderwin(2, 1) + self.assertEqual(win3.getbegyx(), (4, 8)) + self.assertEqual(win3.getparyx(), (2, 1)) + self.assertEqual(win3.getmaxyx(), (6, 11)) + + def test_subwindows_references(self): + win = curses.newwin(5, 10) + win2 = win.subwin(3, 7) + del win + gc_collect() + del win2 + gc_collect() + + def test_move_cursor(self): + stdscr = self.stdscr + win = stdscr.subwin(10, 15, 2, 5) + stdscr.move(1, 2) + win.move(2, 4) + self.assertEqual(stdscr.getyx(), (1, 2)) + self.assertEqual(win.getyx(), (2, 4)) + + win.cursyncup() + self.assertEqual(stdscr.getyx(), (4, 9)) + + def test_refresh_control(self): + stdscr = self.stdscr + # touchwin()/untouchwin()/is_wintouched() + stdscr.refresh() + self.assertIs(stdscr.is_wintouched(), False) + stdscr.touchwin() + self.assertIs(stdscr.is_wintouched(), True) + stdscr.refresh() + self.assertIs(stdscr.is_wintouched(), False) + stdscr.touchwin() + self.assertIs(stdscr.is_wintouched(), True) + stdscr.untouchwin() + self.assertIs(stdscr.is_wintouched(), False) + + # touchline()/untouchline()/is_linetouched() + stdscr.touchline(5, 2) + self.assertIs(stdscr.is_linetouched(5), True) + self.assertIs(stdscr.is_linetouched(6), True) + self.assertIs(stdscr.is_wintouched(), True) + stdscr.touchline(5, 1, False) + self.assertIs(stdscr.is_linetouched(5), False) + + # syncup() + win = stdscr.subwin(10, 15, 2, 5) + win2 = win.subwin(5, 10, 3, 7) + win2.touchwin() + stdscr.untouchwin() + win2.syncup() + self.assertIs(win.is_wintouched(), True) + self.assertIs(stdscr.is_wintouched(), True) + + # syncdown() + stdscr.touchwin() + win.untouchwin() + win2.untouchwin() + win2.syncdown() + self.assertIs(win2.is_wintouched(), True) + + # syncok() + if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"): + win.untouchwin() + stdscr.untouchwin() + for syncok in [False, True]: + win2.syncok(syncok) + win2.addch('a') + self.assertIs(win.is_wintouched(), syncok) + self.assertIs(stdscr.is_wintouched(), syncok) + + def test_output_character(self): + stdscr = self.stdscr + encoding = stdscr.encoding + # addch() + stdscr.refresh() + stdscr.move(0, 0) + stdscr.addch('A') + stdscr.addch(b'A') + stdscr.addch(65) + c = '\u20ac' + try: + stdscr.addch(c) + except UnicodeEncodeError: + self.assertRaises(UnicodeEncodeError, c.encode, encoding) + except OverflowError: + encoded = c.encode(encoding) + self.assertNotEqual(len(encoded), 1, repr(encoded)) + stdscr.addch('A', curses.A_BOLD) + stdscr.addch(1, 2, 'A') + stdscr.addch(2, 3, 'A', curses.A_BOLD) + self.assertIs(stdscr.is_wintouched(), True) + + # echochar() + stdscr.refresh() + stdscr.move(0, 0) + stdscr.echochar('A') + stdscr.echochar(b'A') + stdscr.echochar(65) + with self.assertRaises((UnicodeEncodeError, OverflowError)): + # Unicode is not fully supported yet, but at least it does + # not crash. + # It is supposed to fail because either the character is + # not encodable with the current encoding, or it is encoded to + # a multibyte sequence. + stdscr.echochar('\u0114') + stdscr.echochar('A', curses.A_BOLD) + self.assertIs(stdscr.is_wintouched(), False) + + def test_output_string(self): + stdscr = self.stdscr + encoding = stdscr.encoding + # addstr()/insstr() + for func in [stdscr.addstr, stdscr.insstr]: + with self.subTest(func.__qualname__): + stdscr.move(0, 0) + func('abcd') + func(b'abcd') + s = 'àßçđ' + try: + func(s) + except UnicodeEncodeError: + self.assertRaises(UnicodeEncodeError, s.encode, encoding) + func('abcd', curses.A_BOLD) + func(1, 2, 'abcd') + func(2, 3, 'abcd', curses.A_BOLD) + + # addnstr()/insnstr() + for func in [stdscr.addnstr, stdscr.insnstr]: + with self.subTest(func.__qualname__): + stdscr.move(0, 0) + func('1234', 3) + func(b'1234', 3) + s = '\u0661\u0662\u0663\u0664' + try: + func(s, 3) + except UnicodeEncodeError: + self.assertRaises(UnicodeEncodeError, s.encode, encoding) + func('1234', 5) + func('1234', 3, curses.A_BOLD) + func(1, 2, '1234', 3) + func(2, 3, '1234', 3, curses.A_BOLD) + + def test_output_string_embedded_null_chars(self): + # reject embedded null bytes and characters + stdscr = self.stdscr + for arg in ['a\0', b'a\0']: + with self.subTest(arg=arg): + self.assertRaises(ValueError, stdscr.addstr, arg) + self.assertRaises(ValueError, stdscr.addnstr, arg, 1) + self.assertRaises(ValueError, stdscr.insstr, arg) + self.assertRaises(ValueError, stdscr.insnstr, arg, 1) + + def test_read_from_window(self): + stdscr = self.stdscr + stdscr.addstr(0, 1, 'ABCD', curses.A_BOLD) + # inch() + stdscr.move(0, 1) + self.assertEqual(stdscr.inch(), 65 | curses.A_BOLD) + self.assertEqual(stdscr.inch(0, 3), 67 | curses.A_BOLD) + stdscr.move(0, 0) + # instr() + self.assertEqual(stdscr.instr()[:6], b' ABCD ') + self.assertEqual(stdscr.instr(3)[:6], b' AB') + self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ') + self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ') + self.assertRaises(ValueError, stdscr.instr, -2) + self.assertRaises(ValueError, stdscr.instr, 0, 2, -2) + + def test_getch(self): + win = curses.newwin(5, 12, 5, 2) + + # TODO: Test with real input by writing to master fd. + for c in 'spam\n'[::-1]: + curses.ungetch(c) + self.assertEqual(win.getch(3, 1), b's'[0]) + self.assertEqual(win.getyx(), (3, 1)) + self.assertEqual(win.getch(3, 4), b'p'[0]) + self.assertEqual(win.getyx(), (3, 4)) + self.assertEqual(win.getch(), b'a'[0]) + self.assertEqual(win.getyx(), (3, 4)) + self.assertEqual(win.getch(), b'm'[0]) + self.assertEqual(win.getch(), b'\n'[0]) + + def test_getstr(self): + win = curses.newwin(5, 12, 5, 2) + curses.echo() + self.addCleanup(curses.noecho) + + self.assertRaises(ValueError, win.getstr, -400) + self.assertRaises(ValueError, win.getstr, 2, 3, -400) + + # TODO: Test with real input by writing to master fd. + for c in 'Lorem\nipsum\ndolor\nsit\namet\n'[::-1]: + curses.ungetch(c) + self.assertEqual(win.getstr(3, 1, 2), b'Lo') + self.assertEqual(win.instr(3, 0), b' Lo ') + self.assertEqual(win.getstr(3, 5, 10), b'ipsum') + self.assertEqual(win.instr(3, 0), b' Lo ipsum ') + self.assertEqual(win.getstr(1, 5), b'dolor') + self.assertEqual(win.instr(1, 0), b' dolor ') + self.assertEqual(win.getstr(2), b'si') + self.assertEqual(win.instr(1, 0), b'si dolor ') + self.assertEqual(win.getstr(), b'amet') + self.assertEqual(win.instr(1, 0), b'amet dolor ') + + def test_clear(self): + win = curses.newwin(5, 15, 5, 2) + lorem_ipsum(win) + + win.move(0, 8) + win.clrtoeol() + self.assertEqual(win.instr(0, 0).rstrip(), b'Lorem ip') + self.assertEqual(win.instr(1, 0).rstrip(), b'dolor sit amet,') + + win.move(0, 3) + win.clrtobot() + self.assertEqual(win.instr(0, 0).rstrip(), b'Lor') + self.assertEqual(win.instr(1, 0).rstrip(), b'') + + for func in [win.erase, win.clear]: + lorem_ipsum(win) + func() + self.assertEqual(win.instr(0, 0).rstrip(), b'') + self.assertEqual(win.instr(1, 0).rstrip(), b'') + + def test_insert_delete(self): + win = curses.newwin(5, 15, 5, 2) + lorem_ipsum(win) + + win.move(0, 2) + win.delch() + self.assertEqual(win.instr(0, 0), b'Loem ipsum ') + win.delch(0, 7) + self.assertEqual(win.instr(0, 0), b'Loem ipum ') + + win.move(1, 5) + win.deleteln() + self.assertEqual(win.instr(0, 0), b'Loem ipum ') + self.assertEqual(win.instr(1, 0), b'consectetur ') + self.assertEqual(win.instr(2, 0), b'adipiscing elit') + self.assertEqual(win.instr(3, 0), b'sed do eiusmod ') + self.assertEqual(win.instr(4, 0), b' ') + + win.move(1, 5) + win.insertln() + self.assertEqual(win.instr(0, 0), b'Loem ipum ') + self.assertEqual(win.instr(1, 0), b' ') + self.assertEqual(win.instr(2, 0), b'consectetur ') + + win.clear() + lorem_ipsum(win) + win.move(1, 5) + win.insdelln(2) + self.assertEqual(win.instr(0, 0), b'Lorem ipsum ') + self.assertEqual(win.instr(1, 0), b' ') + self.assertEqual(win.instr(2, 0), b' ') + self.assertEqual(win.instr(3, 0), b'dolor sit amet,') + + win.clear() + lorem_ipsum(win) + win.move(1, 5) + win.insdelln(-2) + self.assertEqual(win.instr(0, 0), b'Lorem ipsum ') + self.assertEqual(win.instr(1, 0), b'adipiscing elit') + self.assertEqual(win.instr(2, 0), b'sed do eiusmod ') + self.assertEqual(win.instr(3, 0), b' ') + + def test_scroll(self): + win = curses.newwin(5, 15, 5, 2) + lorem_ipsum(win) + win.scrollok(True) + win.scroll() + self.assertEqual(win.instr(0, 0), b'dolor sit amet,') + win.scroll(2) + self.assertEqual(win.instr(0, 0), b'adipiscing elit') + win.scroll(-3) + self.assertEqual(win.instr(0, 0), b' ') + self.assertEqual(win.instr(2, 0), b' ') + self.assertEqual(win.instr(3, 0), b'adipiscing elit') + win.scrollok(False) + + def test_attributes(self): + # TODO: attr_get(), attr_set(), ... + win = curses.newwin(5, 15, 5, 2) + win.attron(curses.A_BOLD) + win.attroff(curses.A_BOLD) + win.attrset(curses.A_BOLD) + + win.standout() + win.standend() + + @requires_curses_window_meth('chgat') + def test_chgat(self): + win = curses.newwin(5, 15, 5, 2) + win.addstr(2, 0, 'Lorem ipsum') + win.addstr(3, 0, 'dolor sit amet') + + win.move(2, 8) + win.chgat(curses.A_BLINK) + self.assertEqual(win.inch(2, 7), b'p'[0]) + self.assertEqual(win.inch(2, 8), b's'[0] | curses.A_BLINK) + self.assertEqual(win.inch(2, 14), b' '[0] | curses.A_BLINK) + + win.move(2, 1) + win.chgat(3, curses.A_BOLD) + self.assertEqual(win.inch(2, 0), b'L'[0]) + self.assertEqual(win.inch(2, 1), b'o'[0] | curses.A_BOLD) + self.assertEqual(win.inch(2, 3), b'e'[0] | curses.A_BOLD) + self.assertEqual(win.inch(2, 4), b'm'[0]) + + win.chgat(3, 2, curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 1), b'o'[0]) + self.assertEqual(win.inch(3, 2), b'l'[0] | curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE) + + win.chgat(3, 4, 7, curses.A_BLINK) + self.assertEqual(win.inch(3, 3), b'o'[0] | curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 4), b'r'[0] | curses.A_BLINK) + self.assertEqual(win.inch(3, 10), b'a'[0] | curses.A_BLINK) + self.assertEqual(win.inch(3, 11), b'm'[0] | curses.A_UNDERLINE) + self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE) + + def test_background(self): + win = curses.newwin(5, 15, 5, 2) + win.addstr(0, 0, 'Lorem ipsum') + + self.assertIn(win.getbkgd(), (0, 32)) + + # bkgdset() + win.bkgdset('_') + self.assertEqual(win.getbkgd(), b'_'[0]) + win.bkgdset(b'#') + self.assertEqual(win.getbkgd(), b'#'[0]) + win.bkgdset(65) + self.assertEqual(win.getbkgd(), 65) + win.bkgdset(0) + self.assertEqual(win.getbkgd(), 32) + + win.bkgdset('#', curses.A_REVERSE) + self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE) + self.assertEqual(win.inch(0, 0), b'L'[0]) + self.assertEqual(win.inch(0, 5), b' '[0]) + win.bkgdset(0) + + # bkgd() + win.bkgd('_') + self.assertEqual(win.getbkgd(), b'_'[0]) + self.assertEqual(win.inch(0, 0), b'L'[0]) + self.assertEqual(win.inch(0, 5), b'_'[0]) + + win.bkgd('#', curses.A_REVERSE) + self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE) + self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE) + self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE) + + def test_overlay(self): + srcwin = curses.newwin(5, 18, 3, 4) + lorem_ipsum(srcwin) + dstwin = curses.newwin(7, 17, 5, 7) + for i in range(6): + dstwin.addstr(i, 0, '_'*17) + + srcwin.overlay(dstwin) + self.assertEqual(dstwin.instr(0, 0), b'sectetur_________') + self.assertEqual(dstwin.instr(1, 0), b'piscing_elit,____') + self.assertEqual(dstwin.instr(2, 0), b'_do_eiusmod______') + self.assertEqual(dstwin.instr(3, 0), b'_________________') + + srcwin.overwrite(dstwin) + self.assertEqual(dstwin.instr(0, 0), b'sectetur __') + self.assertEqual(dstwin.instr(1, 0), b'piscing elit, __') + self.assertEqual(dstwin.instr(2, 0), b' do eiusmod __') + self.assertEqual(dstwin.instr(3, 0), b'_________________') + + srcwin.overlay(dstwin, 1, 4, 3, 2, 4, 11) + self.assertEqual(dstwin.instr(3, 0), b'__r_sit_amet_____') + self.assertEqual(dstwin.instr(4, 0), b'__ectetur________') + self.assertEqual(dstwin.instr(5, 0), b'_________________') + + srcwin.overwrite(dstwin, 1, 4, 3, 2, 4, 11) + self.assertEqual(dstwin.instr(3, 0), b'__r sit amet_____') + self.assertEqual(dstwin.instr(4, 0), b'__ectetur _____') + self.assertEqual(dstwin.instr(5, 0), b'_________________') + + def test_refresh(self): + win = curses.newwin(5, 15, 2, 5) + win.noutrefresh() + win.redrawln(1, 2) + win.redrawwin() + win.refresh() + curses.doupdate() + + @requires_curses_window_meth('resize') + def test_resize(self): + win = curses.newwin(5, 15, 2, 5) + win.resize(4, 20) + self.assertEqual(win.getmaxyx(), (4, 20)) + win.resize(5, 15) + self.assertEqual(win.getmaxyx(), (5, 15)) + + @requires_curses_window_meth('enclose') + def test_enclose(self): + win = curses.newwin(5, 15, 2, 5) + self.assertIs(win.enclose(2, 5), True) + self.assertIs(win.enclose(1, 5), False) + self.assertIs(win.enclose(2, 4), False) + self.assertIs(win.enclose(6, 19), True) + self.assertIs(win.enclose(7, 19), False) + self.assertIs(win.enclose(6, 20), False) + + def test_putwin(self): + win = curses.newwin(5, 12, 1, 2) + win.addstr(2, 1, 'Lorem ipsum') + with tempfile.TemporaryFile() as f: + win.putwin(f) + del win + f.seek(0) + win = curses.getwin(f) + self.assertEqual(win.getbegyx(), (1, 2)) + self.assertEqual(win.getmaxyx(), (5, 12)) + self.assertEqual(win.instr(2, 0), b' Lorem ipsum') + + def test_borders_and_lines(self): + win = curses.newwin(5, 10, 5, 2) + win.border('|', '!', '-', '_', + '+', '\\', '#', '/') + self.assertEqual(win.instr(0, 0), b'+--------\\') + self.assertEqual(win.instr(1, 0), b'| !') + self.assertEqual(win.instr(4, 0), b'#________/') + win.border(b'|', b'!', b'-', b'_', + b'+', b'\\', b'#', b'/') + win.border(65, 66, 67, 68, + 69, 70, 71, 72) + self.assertRaises(TypeError, win.border, + 65, 66, 67, 68, 69, [], 71, 72) + self.assertRaises(TypeError, win.border, + 65, 66, 67, 68, 69, 70, 71, 72, 73) + self.assertRaises(TypeError, win.border, + 65, 66, 67, 68, 69, 70, 71, 72, 73) + win.border(65, 66, 67, 68, 69, 70, 71) + win.border(65, 66, 67, 68, 69, 70) + win.border(65, 66, 67, 68, 69) + win.border(65, 66, 67, 68) + win.border(65, 66, 67) + win.border(65, 66) + win.border(65) + win.border() + + win.box(':', '~') + self.assertEqual(win.instr(0, 1, 8), b'~~~~~~~~') + self.assertEqual(win.instr(1, 0), b': :') + self.assertEqual(win.instr(4, 1, 8), b'~~~~~~~~') + win.box(b':', b'~') + win.box(65, 67) + self.assertRaises(TypeError, win.box, 65, 66, 67) + self.assertRaises(TypeError, win.box, 65) + win.box() + + win.move(1, 2) + win.hline('-', 5) + self.assertEqual(win.instr(1, 1, 7), b' ----- ') + win.hline(b'-', 5) + win.hline(45, 5) + win.hline('-', 5, curses.A_BOLD) + win.hline(1, 1, '-', 5) + win.hline(1, 1, '-', 5, curses.A_BOLD) + + win.move(1, 2) + win.vline('a', 3) + win.vline(b'a', 3) + win.vline(97, 3) + win.vline('a', 3, curses.A_STANDOUT) + win.vline(1, 1, 'a', 3) + win.vline(1, 1, ';', 2, curses.A_STANDOUT) + self.assertEqual(win.inch(1, 1), b';'[0] | curses.A_STANDOUT) + self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT) + self.assertEqual(win.inch(3, 1), b'a'[0]) + + def test_unctrl(self): + # TODO: wunctrl() + self.assertEqual(curses.unctrl(b'A'), b'A') + self.assertEqual(curses.unctrl('A'), b'A') + self.assertEqual(curses.unctrl(65), b'A') + self.assertEqual(curses.unctrl(b'\n'), b'^J') + self.assertEqual(curses.unctrl('\n'), b'^J') + self.assertEqual(curses.unctrl(10), b'^J') + self.assertRaises(TypeError, curses.unctrl, b'') + self.assertRaises(TypeError, curses.unctrl, b'AB') + self.assertRaises(TypeError, curses.unctrl, '') + self.assertRaises(TypeError, curses.unctrl, 'AB') + self.assertRaises(OverflowError, curses.unctrl, 2**64) + + def test_endwin(self): + if not self.isatty: + self.skipTest('requires terminal') + self.assertIs(curses.isendwin(), False) + curses.endwin() + self.assertIs(curses.isendwin(), True) + curses.doupdate() + self.assertIs(curses.isendwin(), False) + + def test_terminfo(self): + self.assertIsInstance(curses.tigetflag('hc'), int) + self.assertEqual(curses.tigetflag('cols'), -1) + self.assertEqual(curses.tigetflag('cr'), -1) + + self.assertIsInstance(curses.tigetnum('cols'), int) + self.assertEqual(curses.tigetnum('hc'), -2) + self.assertEqual(curses.tigetnum('cr'), -2) + + self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None))) + self.assertIsNone(curses.tigetstr('hc')) + self.assertIsNone(curses.tigetstr('cols')) + + cud = curses.tigetstr('cud') + if cud is not None: + # See issue10570. + self.assertIsInstance(cud, bytes) + curses.tparm(cud, 2) + cud_2 = curses.tparm(cud, 2) + self.assertIsInstance(cud_2, bytes) + curses.putp(cud_2) + + curses.putp(b'abc\n') + + def test_misc_module_funcs(self): + curses.delay_output(1) + curses.flushinp() + + curses.doupdate() + self.assertIs(curses.isendwin(), False) + + curses.napms(100) + + curses.newpad(50, 50) + + def test_env_queries(self): + # TODO: term_attrs(), erasewchar(), killwchar() + self.assertIsInstance(curses.termname(), bytes) + self.assertIsInstance(curses.longname(), bytes) + self.assertIsInstance(curses.baudrate(), int) + self.assertIsInstance(curses.has_ic(), bool) + self.assertIsInstance(curses.has_il(), bool) + self.assertIsInstance(curses.termattrs(), int) + + c = curses.killchar() + self.assertIsInstance(c, bytes) + self.assertEqual(len(c), 1) + c = curses.erasechar() + self.assertIsInstance(c, bytes) + self.assertEqual(len(c), 1) + + def test_output_options(self): + stdscr = self.stdscr + + stdscr.clearok(True) + stdscr.clearok(False) + + stdscr.idcok(True) + stdscr.idcok(False) + + stdscr.idlok(False) + stdscr.idlok(True) + + if hasattr(stdscr, 'immedok'): + stdscr.immedok(True) + stdscr.immedok(False) + + stdscr.leaveok(True) + stdscr.leaveok(False) + + stdscr.scrollok(True) + stdscr.scrollok(False) + + stdscr.setscrreg(5, 10) + + curses.nonl() + curses.nl(True) + curses.nl(False) + curses.nl() + + def test_input_options(self): + stdscr = self.stdscr + + if self.isatty: + curses.nocbreak() + curses.cbreak() + curses.cbreak(False) + curses.cbreak(True) + + curses.intrflush(True) + curses.intrflush(False) + + curses.raw() + curses.raw(False) + curses.raw(True) + curses.noraw() + + curses.noecho() + curses.echo() + curses.echo(False) + curses.echo(True) + + curses.halfdelay(255) + curses.halfdelay(1) + + stdscr.keypad(True) + stdscr.keypad(False) + + curses.meta(True) + curses.meta(False) + + stdscr.nodelay(True) + stdscr.nodelay(False) + + curses.noqiflush() + curses.qiflush(True) + curses.qiflush(False) + curses.qiflush() + + stdscr.notimeout(True) + stdscr.notimeout(False) + + stdscr.timeout(-1) + stdscr.timeout(0) + stdscr.timeout(5) + + @requires_curses_func('typeahead') + def test_typeahead(self): + curses.typeahead(sys.__stdin__.fileno()) + curses.typeahead(-1) + + def test_prog_mode(self): + if not self.isatty: + self.skipTest('requires terminal') + curses.def_prog_mode() + curses.reset_prog_mode() + + def test_beep(self): + if (curses.tigetstr("bel") is not None + or curses.tigetstr("flash") is not None): + curses.beep() + else: + try: + curses.beep() + except curses.error: + self.skipTest('beep() failed') + + def test_flash(self): + if (curses.tigetstr("bel") is not None + or curses.tigetstr("flash") is not None): + curses.flash() + else: + try: + curses.flash() + except curses.error: + self.skipTest('flash() failed') + + def test_curs_set(self): + for vis, cap in [(0, 'civis'), (2, 'cvvis'), (1, 'cnorm')]: + if curses.tigetstr(cap) is not None: + curses.curs_set(vis) + else: + try: + curses.curs_set(vis) + except curses.error: + pass + + @requires_curses_func('get_escdelay') + def test_escdelay(self): + escdelay = curses.get_escdelay() + self.assertIsInstance(escdelay, int) + curses.set_escdelay(25) + self.assertEqual(curses.get_escdelay(), 25) + curses.set_escdelay(escdelay) + + @requires_curses_func('get_tabsize') + def test_tabsize(self): + tabsize = curses.get_tabsize() + self.assertIsInstance(tabsize, int) + curses.set_tabsize(4) + self.assertEqual(curses.get_tabsize(), 4) + curses.set_tabsize(tabsize) + + @requires_curses_func('getsyx') + def test_getsyx(self): + y, x = curses.getsyx() + self.assertIsInstance(y, int) + self.assertIsInstance(x, int) + curses.setsyx(4, 5) + self.assertEqual(curses.getsyx(), (4, 5)) + + def bad_colors(self): + return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) + + def bad_colors2(self): + return (curses.COLORS, 2**31, 2**63, 2**64) + + def bad_pairs(self): + return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) + + def test_has_colors(self): + self.assertIsInstance(curses.has_colors(), bool) + self.assertIsInstance(curses.can_change_color(), bool) + + def test_start_color(self): + if not curses.has_colors(): + self.skipTest('requires colors support') + curses.start_color() + if verbose: + print(f'COLORS = {curses.COLORS}', file=sys.stderr) + print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr) + + @requires_colors + def test_color_content(self): + self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0)) + curses.color_content(0) + maxcolor = curses.COLORS - 1 + curses.color_content(maxcolor) + + for color in self.bad_colors(): + self.assertRaises(ValueError, curses.color_content, color) + + @requires_colors + def test_init_color(self): + if not curses.can_change_color(): + self.skipTest('cannot change color') + + old = curses.color_content(0) + try: + curses.init_color(0, *old) + except curses.error: + self.skipTest('cannot change color (init_color() failed)') + self.addCleanup(curses.init_color, 0, *old) + curses.init_color(0, 0, 0, 0) + self.assertEqual(curses.color_content(0), (0, 0, 0)) + curses.init_color(0, 1000, 1000, 1000) + self.assertEqual(curses.color_content(0), (1000, 1000, 1000)) + + maxcolor = curses.COLORS - 1 + old = curses.color_content(maxcolor) + curses.init_color(maxcolor, *old) + self.addCleanup(curses.init_color, maxcolor, *old) + curses.init_color(maxcolor, 0, 500, 1000) + self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000)) + + for color in self.bad_colors(): + self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0) + for comp in (-1, 1001): + self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0) + self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0) + self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp) + + def get_pair_limit(self): + pair_limit = curses.COLOR_PAIRS + if hasattr(curses, 'ncurses_version'): + if curses.has_extended_color_support(): + pair_limit += 2*curses.COLORS + 1 + if (not curses.has_extended_color_support() + or (6, 1) <= curses.ncurses_version < (6, 2)): + pair_limit = min(pair_limit, SHORT_MAX) + # If use_default_colors() is called, the upper limit of the extended + # range may be restricted, so we need to check if the limit is still + # correct + try: + curses.init_pair(pair_limit - 1, 0, 0) + except ValueError: + pair_limit = curses.COLOR_PAIRS + return pair_limit + + @requires_colors + def test_pair_content(self): + if not hasattr(curses, 'use_default_colors'): + self.assertIn(curses.pair_content(0), DEFAULT_PAIR_CONTENTS) + curses.pair_content(0) + maxpair = self.get_pair_limit() - 1 + if maxpair > 0: + curses.pair_content(maxpair) + + for pair in self.bad_pairs(): + self.assertRaises(ValueError, curses.pair_content, pair) + + @requires_colors + def test_init_pair(self): + old = curses.pair_content(1) + curses.init_pair(1, *old) + self.addCleanup(curses.init_pair, 1, *old) + + curses.init_pair(1, 0, 0) + self.assertEqual(curses.pair_content(1), (0, 0)) + maxcolor = curses.COLORS - 1 + curses.init_pair(1, maxcolor, 0) + self.assertEqual(curses.pair_content(1), (maxcolor, 0)) + curses.init_pair(1, 0, maxcolor) + self.assertEqual(curses.pair_content(1), (0, maxcolor)) + maxpair = self.get_pair_limit() - 1 + if maxpair > 1: + curses.init_pair(maxpair, 0, 0) + self.assertEqual(curses.pair_content(maxpair), (0, 0)) + + for pair in self.bad_pairs(): + self.assertRaises(ValueError, curses.init_pair, pair, 0, 0) + for color in self.bad_colors2(): + self.assertRaises(ValueError, curses.init_pair, 1, color, 0) + self.assertRaises(ValueError, curses.init_pair, 1, 0, color) + + @requires_colors + def test_color_attrs(self): + for pair in 0, 1, 255: + attr = curses.color_pair(pair) + self.assertEqual(curses.pair_number(attr), pair, attr) + self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair) + self.assertEqual(curses.color_pair(0), 0) + self.assertEqual(curses.pair_number(0), 0) + + @requires_curses_func('use_default_colors') + @requires_colors + def test_use_default_colors(self): + old = curses.pair_content(0) + try: + curses.use_default_colors() + except curses.error: + self.skipTest('cannot change color (use_default_colors() failed)') + self.assertEqual(curses.pair_content(0), (-1, -1)) + self.assertIn(old, DEFAULT_PAIR_CONTENTS) + + def test_keyname(self): + # TODO: key_name() + self.assertEqual(curses.keyname(65), b'A') + self.assertEqual(curses.keyname(13), b'^M') + self.assertEqual(curses.keyname(127), b'^?') + self.assertEqual(curses.keyname(0), b'^@') + self.assertRaises(ValueError, curses.keyname, -1) + self.assertIsInstance(curses.keyname(256), bytes) + + @requires_curses_func('has_key') + def test_has_key(self): + curses.has_key(13) + + @requires_curses_func('getmouse') + def test_getmouse(self): + (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED) + if availmask == 0: + self.skipTest('mouse stuff not available') + curses.mouseinterval(10) + # just verify these don't cause errors + curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) + m = curses.getmouse() + + @requires_curses_func('panel') + def test_userptr_without_set(self): + w = curses.newwin(10, 10) + p = curses.panel.new_panel(w) + # try to access userptr() before calling set_userptr() -- segfaults + with self.assertRaises(curses.panel.error, + msg='userptr should fail since not set'): + p.userptr() + + @requires_curses_func('panel') + def test_userptr_memory_leak(self): + w = curses.newwin(10, 10) + p = curses.panel.new_panel(w) + obj = object() + nrefs = sys.getrefcount(obj) + for i in range(100): + p.set_userptr(obj) + + p.set_userptr(None) + self.assertEqual(sys.getrefcount(obj), nrefs, + "set_userptr leaked references") + + @requires_curses_func('panel') + def test_userptr_segfault(self): + w = curses.newwin(10, 10) + panel = curses.panel.new_panel(w) + class A: + def __del__(self): + panel.set_userptr(None) + panel.set_userptr(A()) + panel.set_userptr(None) + + @cpython_only + @requires_curses_func('panel') + def test_disallow_instantiation(self): + # Ensure that the type disallows instantiation (bpo-43916) + w = curses.newwin(10, 10) + panel = curses.panel.new_panel(w) + check_disallow_instantiation(self, type(panel)) + + @requires_curses_func('is_term_resized') + def test_is_term_resized(self): + lines, cols = curses.LINES, curses.COLS + self.assertIs(curses.is_term_resized(lines, cols), False) + self.assertIs(curses.is_term_resized(lines-1, cols-1), True) + + @requires_curses_func('resize_term') + def test_resize_term(self): + curses.update_lines_cols() + lines, cols = curses.LINES, curses.COLS + new_lines = lines - 1 + new_cols = cols + 1 + curses.resize_term(new_lines, new_cols) + self.assertEqual(curses.LINES, new_lines) + self.assertEqual(curses.COLS, new_cols) + + curses.resize_term(lines, cols) + self.assertEqual(curses.LINES, lines) + self.assertEqual(curses.COLS, cols) + + with self.assertRaises(OverflowError): + curses.resize_term(35000, 1) + with self.assertRaises(OverflowError): + curses.resize_term(1, 35000) + # GH-120378: Overflow failure in resize_term() causes refresh to fail + tmp = curses.initscr() + tmp.erase() + + @requires_curses_func('resizeterm') + def test_resizeterm(self): + curses.update_lines_cols() + lines, cols = curses.LINES, curses.COLS + new_lines = lines - 1 + new_cols = cols + 1 + curses.resizeterm(new_lines, new_cols) + self.assertEqual(curses.LINES, new_lines) + self.assertEqual(curses.COLS, new_cols) + + curses.resizeterm(lines, cols) + self.assertEqual(curses.LINES, lines) + self.assertEqual(curses.COLS, cols) + + with self.assertRaises(OverflowError): + curses.resizeterm(35000, 1) + with self.assertRaises(OverflowError): + curses.resizeterm(1, 35000) + # GH-120378: Overflow failure in resizeterm() causes refresh to fail + tmp = curses.initscr() + tmp.erase() + + def test_ungetch(self): + curses.ungetch(b'A') + self.assertEqual(self.stdscr.getkey(), 'A') + curses.ungetch('B') + self.assertEqual(self.stdscr.getkey(), 'B') + curses.ungetch(67) + self.assertEqual(self.stdscr.getkey(), 'C') + + def test_issue6243(self): + curses.ungetch(1025) + self.stdscr.getkey() + + @requires_curses_func('unget_wch') + @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8), + "unget_wch is broken in ncurses 5.7 and earlier") + def test_unget_wch(self): + stdscr = self.stdscr + encoding = stdscr.encoding + for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'): + try: + ch.encode(encoding) + except UnicodeEncodeError: + continue + try: + curses.unget_wch(ch) + except Exception as err: + self.fail("unget_wch(%a) failed with encoding %s: %s" + % (ch, stdscr.encoding, err)) + read = stdscr.get_wch() + self.assertEqual(read, ch) + + code = ord(ch) + curses.unget_wch(code) + read = stdscr.get_wch() + self.assertEqual(read, ch) + + def test_encoding(self): + stdscr = self.stdscr + import codecs + encoding = stdscr.encoding + codecs.lookup(encoding) + with self.assertRaises(TypeError): + stdscr.encoding = 10 + stdscr.encoding = encoding + with self.assertRaises(TypeError): + del stdscr.encoding + + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_issue21088(self): + stdscr = self.stdscr + # + # http://bugs.python.org/issue21088 + # + # the bug: + # when converting curses.window.addch to Argument Clinic + # the first two parameters were switched. + + # if someday we can represent the signature of addch + # we will need to rewrite this test. + try: + signature = inspect.signature(stdscr.addch) + self.assertFalse(signature) + except ValueError: + # not generating a signature is fine. + pass + + # So. No signature for addch. + # But Argument Clinic gave us a human-readable equivalent + # as the first line of the docstring. So we parse that, + # and ensure that the parameters appear in the correct order. + # Since this is parsing output from Argument Clinic, we can + # be reasonably certain the generated parsing code will be + # correct too. + human_readable_signature = stdscr.addch.__doc__.split("\n")[0] + self.assertIn("[y, x,]", human_readable_signature) + + @requires_curses_window_meth('resize') + def test_issue13051(self): + win = curses.newwin(5, 15, 2, 5) + box = curses.textpad.Textbox(win, insert_mode=True) + lines, cols = win.getmaxyx() + win.resize(lines-2, cols-2) + # this may cause infinite recursion, leading to a RuntimeError + box._insert_printable_char('a') + + +class MiscTests(unittest.TestCase): + + @requires_curses_func('update_lines_cols') + def test_update_lines_cols(self): + curses.update_lines_cols() + lines, cols = curses.LINES, curses.COLS + curses.LINES = curses.COLS = 0 + curses.update_lines_cols() + self.assertEqual(curses.LINES, lines) + self.assertEqual(curses.COLS, cols) + + @requires_curses_func('ncurses_version') + def test_ncurses_version(self): + v = curses.ncurses_version + if verbose: + print(f'ncurses_version = {curses.ncurses_version}', flush=True) + self.assertIsInstance(v[:], tuple) + self.assertEqual(len(v), 3) + self.assertIsInstance(v[0], int) + self.assertIsInstance(v[1], int) + self.assertIsInstance(v[2], int) + self.assertIsInstance(v.major, int) + self.assertIsInstance(v.minor, int) + self.assertIsInstance(v.patch, int) + self.assertEqual(v[0], v.major) + self.assertEqual(v[1], v.minor) + self.assertEqual(v[2], v.patch) + self.assertGreaterEqual(v.major, 0) + self.assertGreaterEqual(v.minor, 0) + self.assertGreaterEqual(v.patch, 0) + + def test_has_extended_color_support(self): + r = curses.has_extended_color_support() + self.assertIsInstance(r, bool) + + +class TestAscii(unittest.TestCase): + + def test_controlnames(self): + for name in curses.ascii.controlnames: + self.assertTrue(hasattr(curses.ascii, name), name) + + def test_ctypes(self): + def check(func, expected): + with self.subTest(ch=c, func=func): + self.assertEqual(func(i), expected) + self.assertEqual(func(c), expected) + + for i in range(256): + c = chr(i) + b = bytes([i]) + check(curses.ascii.isalnum, b.isalnum()) + check(curses.ascii.isalpha, b.isalpha()) + check(curses.ascii.isdigit, b.isdigit()) + check(curses.ascii.islower, b.islower()) + check(curses.ascii.isspace, b.isspace()) + check(curses.ascii.isupper, b.isupper()) + + check(curses.ascii.isascii, i < 128) + check(curses.ascii.ismeta, i >= 128) + check(curses.ascii.isctrl, i < 32) + check(curses.ascii.iscntrl, i < 32 or i == 127) + check(curses.ascii.isblank, c in ' \t') + check(curses.ascii.isgraph, 32 < i <= 126) + check(curses.ascii.isprint, 32 <= i <= 126) + check(curses.ascii.ispunct, c in string.punctuation) + check(curses.ascii.isxdigit, c in string.hexdigits) + + for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1): + self.assertFalse(curses.ascii.isalnum(i)) + self.assertFalse(curses.ascii.isalpha(i)) + self.assertFalse(curses.ascii.isdigit(i)) + self.assertFalse(curses.ascii.islower(i)) + self.assertFalse(curses.ascii.isspace(i)) + self.assertFalse(curses.ascii.isupper(i)) + + self.assertFalse(curses.ascii.isascii(i)) + self.assertFalse(curses.ascii.isctrl(i)) + self.assertFalse(curses.ascii.iscntrl(i)) + self.assertFalse(curses.ascii.isblank(i)) + self.assertFalse(curses.ascii.isgraph(i)) + self.assertFalse(curses.ascii.isprint(i)) + self.assertFalse(curses.ascii.ispunct(i)) + self.assertFalse(curses.ascii.isxdigit(i)) + + self.assertFalse(curses.ascii.ismeta(-1)) + + def test_ascii(self): + ascii = curses.ascii.ascii + self.assertEqual(ascii('\xc1'), 'A') + self.assertEqual(ascii('A'), 'A') + self.assertEqual(ascii(ord('\xc1')), ord('A')) + + def test_ctrl(self): + ctrl = curses.ascii.ctrl + self.assertEqual(ctrl('J'), '\n') + self.assertEqual(ctrl('\n'), '\n') + self.assertEqual(ctrl('@'), '\0') + self.assertEqual(ctrl(ord('J')), ord('\n')) + + def test_alt(self): + alt = curses.ascii.alt + self.assertEqual(alt('\n'), '\x8a') + self.assertEqual(alt('A'), '\xc1') + self.assertEqual(alt(ord('A')), 0xc1) + + def test_unctrl(self): + unctrl = curses.ascii.unctrl + self.assertEqual(unctrl('a'), 'a') + self.assertEqual(unctrl('A'), 'A') + self.assertEqual(unctrl(';'), ';') + self.assertEqual(unctrl(' '), ' ') + self.assertEqual(unctrl('\x7f'), '^?') + self.assertEqual(unctrl('\n'), '^J') + self.assertEqual(unctrl('\0'), '^@') + self.assertEqual(unctrl(ord('A')), 'A') + self.assertEqual(unctrl(ord('\n')), '^J') + # Meta-bit characters + self.assertEqual(unctrl('\x8a'), '!^J') + self.assertEqual(unctrl('\xc1'), '!A') + self.assertEqual(unctrl(ord('\x8a')), '!^J') + self.assertEqual(unctrl(ord('\xc1')), '!A') + + +def lorem_ipsum(win): + text = [ + 'Lorem ipsum', + 'dolor sit amet,', + 'consectetur', + 'adipiscing elit,', + 'sed do eiusmod', + 'tempor incididunt', + 'ut labore et', + 'dolore magna', + 'aliqua.', + ] + maxy, maxx = win.getmaxyx() + for y, line in enumerate(text[:maxy]): + win.addstr(y, 0, line[:maxx - (y == maxy - 1)]) + + +class TextboxTest(unittest.TestCase): + def setUp(self): + self.mock_win = MagicMock(spec=curses.window) + self.mock_win.getyx.return_value = (1, 1) + self.mock_win.getmaxyx.return_value = (10, 20) + self.textbox = curses.textpad.Textbox(self.mock_win) + + def test_init(self): + """Test textbox initialization.""" + self.mock_win.reset_mock() + tb = curses.textpad.Textbox(self.mock_win) + self.mock_win.getmaxyx.assert_called_once_with() + self.mock_win.keypad.assert_called_once_with(1) + self.assertEqual(tb.insert_mode, False) + self.assertEqual(tb.stripspaces, 1) + self.assertIsNone(tb.lastcmd) + self.mock_win.reset_mock() + + def test_insert(self): + """Test inserting a printable character.""" + self.mock_win.reset_mock() + self.textbox.do_command(ord('a')) + self.mock_win.addch.assert_called_with(ord('a')) + self.textbox.do_command(ord('b')) + self.mock_win.addch.assert_called_with(ord('b')) + self.textbox.do_command(ord('c')) + self.mock_win.addch.assert_called_with(ord('c')) + self.mock_win.reset_mock() + + def test_delete(self): + """Test deleting a character.""" + self.mock_win.reset_mock() + self.textbox.do_command(curses.ascii.BS) + self.textbox.do_command(curses.KEY_BACKSPACE) + self.textbox.do_command(curses.ascii.DEL) + assert self.mock_win.delch.call_count == 3 + self.mock_win.reset_mock() + + def test_move_left(self): + """Test moving the cursor left.""" + self.mock_win.reset_mock() + self.textbox.do_command(curses.KEY_LEFT) + self.mock_win.move.assert_called_with(1, 0) + self.mock_win.reset_mock() + + def test_move_right(self): + """Test moving the cursor right.""" + self.mock_win.reset_mock() + self.textbox.do_command(curses.KEY_RIGHT) + self.mock_win.move.assert_called_with(1, 2) + self.mock_win.reset_mock() + + def test_move_left_and_right(self): + """Test moving the cursor left and then right.""" + self.mock_win.reset_mock() + self.textbox.do_command(curses.KEY_LEFT) + self.mock_win.move.assert_called_with(1, 0) + self.textbox.do_command(curses.KEY_RIGHT) + self.mock_win.move.assert_called_with(1, 2) + self.mock_win.reset_mock() + + def test_move_up(self): + """Test moving the cursor up.""" + self.mock_win.reset_mock() + self.textbox.do_command(curses.KEY_UP) + self.mock_win.move.assert_called_with(0, 1) + self.mock_win.reset_mock() + + def test_move_down(self): + """Test moving the cursor down.""" + self.mock_win.reset_mock() + self.textbox.do_command(curses.KEY_DOWN) + self.mock_win.move.assert_called_with(2, 1) + self.mock_win.reset_mock() + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index 3f1e2331bc2..29d88386e81 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -4920,4 +4920,4 @@ def test_make_dataclass(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py new file mode 100644 index 00000000000..66268c42a30 --- /dev/null +++ b/Lib/test/test_dbm_gnu.py @@ -0,0 +1,222 @@ +import os +import unittest +from test import support +from test.support import cpython_only, import_helper +from test.support.os_helper import (TESTFN, TESTFN_NONASCII, FakePath, + create_empty_file, temp_dir, unlink) + +gdbm = import_helper.import_module("dbm.gnu") # skip if not supported + +filename = TESTFN + +class TestGdbm(unittest.TestCase): + @staticmethod + def setUpClass(): + if support.verbose: + try: + from _gdbm import _GDBM_VERSION as version + except ImportError: + pass + else: + print(f"gdbm version: {version}") + + def setUp(self): + self.g = None + + def tearDown(self): + if self.g is not None: + self.g.close() + unlink(filename) + + @cpython_only + def test_disallow_instantiation(self): + # Ensure that the type disallows instantiation (bpo-43916) + self.g = gdbm.open(filename, 'c') + support.check_disallow_instantiation(self, type(self.g)) + + def test_key_methods(self): + self.g = gdbm.open(filename, 'c') + self.assertEqual(self.g.keys(), []) + self.g['a'] = 'b' + self.g['12345678910'] = '019237410982340912840198242' + self.g[b'bytes'] = b'data' + key_set = set(self.g.keys()) + self.assertEqual(key_set, set([b'a', b'bytes', b'12345678910'])) + self.assertIn('a', self.g) + self.assertIn(b'a', self.g) + self.assertEqual(self.g[b'bytes'], b'data') + key = self.g.firstkey() + while key: + self.assertIn(key, key_set) + key_set.remove(key) + key = self.g.nextkey(key) + # get() and setdefault() work as in the dict interface + self.assertEqual(self.g.get(b'a'), b'b') + self.assertIsNone(self.g.get(b'xxx')) + self.assertEqual(self.g.get(b'xxx', b'foo'), b'foo') + with self.assertRaises(KeyError): + self.g['xxx'] + self.assertEqual(self.g.setdefault(b'xxx', b'foo'), b'foo') + self.assertEqual(self.g[b'xxx'], b'foo') + + def test_error_conditions(self): + # Try to open a non-existent database. + unlink(filename) + self.assertRaises(gdbm.error, gdbm.open, filename, 'r') + # Try to access a closed database. + self.g = gdbm.open(filename, 'c') + self.g.close() + self.assertRaises(gdbm.error, lambda: self.g['a']) + # try pass an invalid open flag + self.assertRaises(gdbm.error, lambda: gdbm.open(filename, 'rx').close()) + + def test_flags(self): + # Test the flag parameter open() by trying all supported flag modes. + all = set(gdbm.open_flags) + # Test standard flags (presumably "crwn"). + modes = all - set('fsu') + for mode in sorted(modes): # put "c" mode first + self.g = gdbm.open(filename, mode) + self.g.close() + + # Test additional flags (presumably "fsu"). + flags = all - set('crwn') + for mode in modes: + for flag in flags: + self.g = gdbm.open(filename, mode + flag) + self.g.close() + + def test_reorganize(self): + self.g = gdbm.open(filename, 'c') + size0 = os.path.getsize(filename) + + # bpo-33901: on macOS with gdbm 1.15, an empty database uses 16 MiB + # and adding an entry of 10,000 B has no effect on the file size. + # Add size0 bytes to make sure that the file size changes. + value_size = max(size0, 10000) + self.g['x'] = 'x' * value_size + size1 = os.path.getsize(filename) + self.assertGreater(size1, size0) + + del self.g['x'] + # 'size' is supposed to be the same even after deleting an entry. + self.assertEqual(os.path.getsize(filename), size1) + + self.g.reorganize() + size2 = os.path.getsize(filename) + self.assertLess(size2, size1) + self.assertGreaterEqual(size2, size0) + + def test_context_manager(self): + with gdbm.open(filename, 'c') as db: + db["gdbm context manager"] = "context manager" + + with gdbm.open(filename, 'r') as db: + self.assertEqual(list(db.keys()), [b"gdbm context manager"]) + + with self.assertRaises(gdbm.error) as cm: + db.keys() + self.assertEqual(str(cm.exception), + "GDBM object has already been closed") + + def test_bool_empty(self): + with gdbm.open(filename, 'c') as db: + self.assertFalse(bool(db)) + + def test_bool_not_empty(self): + with gdbm.open(filename, 'c') as db: + db['a'] = 'b' + self.assertTrue(bool(db)) + + def test_bool_on_closed_db_raises(self): + with gdbm.open(filename, 'c') as db: + db['a'] = 'b' + self.assertRaises(gdbm.error, bool, db) + + def test_bytes(self): + with gdbm.open(filename, 'c') as db: + db[b'bytes key \xbd'] = b'bytes value \xbd' + with gdbm.open(filename, 'r') as db: + self.assertEqual(list(db.keys()), [b'bytes key \xbd']) + self.assertTrue(b'bytes key \xbd' in db) + self.assertEqual(db[b'bytes key \xbd'], b'bytes value \xbd') + + def test_unicode(self): + with gdbm.open(filename, 'c') as db: + db['Unicode key \U0001f40d'] = 'Unicode value \U0001f40d' + with gdbm.open(filename, 'r') as db: + self.assertEqual(list(db.keys()), ['Unicode key \U0001f40d'.encode()]) + self.assertTrue('Unicode key \U0001f40d'.encode() in db) + self.assertTrue('Unicode key \U0001f40d' in db) + self.assertEqual(db['Unicode key \U0001f40d'.encode()], + 'Unicode value \U0001f40d'.encode()) + self.assertEqual(db['Unicode key \U0001f40d'], + 'Unicode value \U0001f40d'.encode()) + + def test_write_readonly_file(self): + with gdbm.open(filename, 'c') as db: + db[b'bytes key'] = b'bytes value' + with gdbm.open(filename, 'r') as db: + with self.assertRaises(gdbm.error): + del db[b'not exist key'] + with self.assertRaises(gdbm.error): + del db[b'bytes key'] + with self.assertRaises(gdbm.error): + db[b'not exist key'] = b'not exist value' + + @unittest.skipUnless(TESTFN_NONASCII, + 'requires OS support of non-ASCII encodings') + def test_nonascii_filename(self): + filename = TESTFN_NONASCII + self.addCleanup(unlink, filename) + with gdbm.open(filename, 'c') as db: + db[b'key'] = b'value' + self.assertTrue(os.path.exists(filename)) + with gdbm.open(filename, 'r') as db: + self.assertEqual(list(db.keys()), [b'key']) + self.assertTrue(b'key' in db) + self.assertEqual(db[b'key'], b'value') + + def test_nonexisting_file(self): + nonexisting_file = 'nonexisting-file' + with self.assertRaises(gdbm.error) as cm: + gdbm.open(nonexisting_file) + self.assertIn(nonexisting_file, str(cm.exception)) + self.assertEqual(cm.exception.filename, nonexisting_file) + + def test_open_with_pathlib_path(self): + gdbm.open(FakePath(filename), "c").close() + + def test_open_with_bytes_path(self): + gdbm.open(os.fsencode(filename), "c").close() + + def test_open_with_pathlib_bytes_path(self): + gdbm.open(FakePath(os.fsencode(filename)), "c").close() + + def test_clear(self): + kvs = [('foo', 'bar'), ('1234', '5678')] + with gdbm.open(filename, 'c') as db: + for k, v in kvs: + db[k] = v + self.assertIn(k, db) + self.assertEqual(len(db), len(kvs)) + + db.clear() + for k, v in kvs: + self.assertNotIn(k, db) + self.assertEqual(len(db), 0) + + @support.run_with_locale( + 'LC_ALL', + 'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk', + 'fr_FR.utf8', 'en_US.utf8', + '', + ) + def test_localized_error(self): + with temp_dir() as d: + create_empty_file(os.path.join(d, 'test')) + self.assertRaises(gdbm.error, gdbm.open, filename, 'r') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py new file mode 100644 index 00000000000..e0f31c9a9a3 --- /dev/null +++ b/Lib/test/test_dbm_ndbm.py @@ -0,0 +1,165 @@ +from test.support import import_helper +from test.support import os_helper +import_helper.import_module("dbm.ndbm") #skip if not supported +import os +import unittest +import dbm.ndbm +from dbm.ndbm import error + +class DbmTestCase(unittest.TestCase): + + def setUp(self): + self.filename = os_helper.TESTFN + self.d = dbm.ndbm.open(self.filename, 'c') + self.d.close() + + def tearDown(self): + for suffix in ['', '.pag', '.dir', '.db']: + os_helper.unlink(self.filename + suffix) + + def test_keys(self): + self.d = dbm.ndbm.open(self.filename, 'c') + self.assertEqual(self.d.keys(), []) + self.d['a'] = 'b' + self.d[b'bytes'] = b'data' + self.d['12345678910'] = '019237410982340912840198242' + self.d.keys() + self.assertIn('a', self.d) + self.assertIn(b'a', self.d) + self.assertEqual(self.d[b'bytes'], b'data') + # get() and setdefault() work as in the dict interface + self.assertEqual(self.d.get(b'a'), b'b') + self.assertIsNone(self.d.get(b'xxx')) + self.assertEqual(self.d.get(b'xxx', b'foo'), b'foo') + with self.assertRaises(KeyError): + self.d['xxx'] + self.assertEqual(self.d.setdefault(b'xxx', b'foo'), b'foo') + self.assertEqual(self.d[b'xxx'], b'foo') + self.d.close() + + def test_empty_value(self): + if dbm.ndbm.library == 'Berkeley DB': + self.skipTest("Berkeley DB doesn't distinguish the empty value " + "from the absent one") + self.d = dbm.ndbm.open(self.filename, 'c') + self.assertEqual(self.d.keys(), []) + self.d['empty'] = '' + self.assertEqual(self.d.keys(), [b'empty']) + self.assertIn(b'empty', self.d) + self.assertEqual(self.d[b'empty'], b'') + self.assertEqual(self.d.get(b'empty'), b'') + self.assertEqual(self.d.setdefault(b'empty'), b'') + self.d.close() + + def test_modes(self): + for mode in ['r', 'rw', 'w', 'n']: + try: + self.d = dbm.ndbm.open(self.filename, mode) + self.d.close() + except error: + self.fail() + + def test_context_manager(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db["ndbm context manager"] = "context manager" + + with dbm.ndbm.open(self.filename, 'r') as db: + self.assertEqual(list(db.keys()), [b"ndbm context manager"]) + + with self.assertRaises(dbm.ndbm.error) as cm: + db.keys() + self.assertEqual(str(cm.exception), + "DBM object has already been closed") + + def test_bytes(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db[b'bytes key \xbd'] = b'bytes value \xbd' + with dbm.ndbm.open(self.filename, 'r') as db: + self.assertEqual(list(db.keys()), [b'bytes key \xbd']) + self.assertTrue(b'bytes key \xbd' in db) + self.assertEqual(db[b'bytes key \xbd'], b'bytes value \xbd') + + def test_unicode(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db['Unicode key \U0001f40d'] = 'Unicode value \U0001f40d' + with dbm.ndbm.open(self.filename, 'r') as db: + self.assertEqual(list(db.keys()), ['Unicode key \U0001f40d'.encode()]) + self.assertTrue('Unicode key \U0001f40d'.encode() in db) + self.assertTrue('Unicode key \U0001f40d' in db) + self.assertEqual(db['Unicode key \U0001f40d'.encode()], + 'Unicode value \U0001f40d'.encode()) + self.assertEqual(db['Unicode key \U0001f40d'], + 'Unicode value \U0001f40d'.encode()) + + def test_write_readonly_file(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db[b'bytes key'] = b'bytes value' + with dbm.ndbm.open(self.filename, 'r') as db: + with self.assertRaises(error): + del db[b'not exist key'] + with self.assertRaises(error): + del db[b'bytes key'] + with self.assertRaises(error): + db[b'not exist key'] = b'not exist value' + + @unittest.skipUnless(os_helper.TESTFN_NONASCII, + 'requires OS support of non-ASCII encodings') + def test_nonascii_filename(self): + filename = os_helper.TESTFN_NONASCII + for suffix in ['', '.pag', '.dir', '.db']: + self.addCleanup(os_helper.unlink, filename + suffix) + with dbm.ndbm.open(filename, 'c') as db: + db[b'key'] = b'value' + self.assertTrue(any(os.path.exists(filename + suffix) + for suffix in ['', '.pag', '.dir', '.db'])) + with dbm.ndbm.open(filename, 'r') as db: + self.assertEqual(list(db.keys()), [b'key']) + self.assertTrue(b'key' in db) + self.assertEqual(db[b'key'], b'value') + + def test_nonexisting_file(self): + nonexisting_file = 'nonexisting-file' + with self.assertRaises(dbm.ndbm.error) as cm: + dbm.ndbm.open(nonexisting_file) + self.assertIn(nonexisting_file, str(cm.exception)) + self.assertEqual(cm.exception.filename, nonexisting_file) + + def test_open_with_pathlib_path(self): + dbm.ndbm.open(os_helper.FakePath(self.filename), "c").close() + + def test_open_with_bytes_path(self): + dbm.ndbm.open(os.fsencode(self.filename), "c").close() + + def test_open_with_pathlib_bytes_path(self): + dbm.ndbm.open(os_helper.FakePath(os.fsencode(self.filename)), "c").close() + + def test_bool_empty(self): + with dbm.ndbm.open(self.filename, 'c') as db: + self.assertFalse(bool(db)) + + def test_bool_not_empty(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db['a'] = 'b' + self.assertTrue(bool(db)) + + def test_bool_on_closed_db_raises(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db['a'] = 'b' + self.assertRaises(dbm.ndbm.error, bool, db) + + def test_clear(self): + kvs = [('foo', 'bar'), ('1234', '5678')] + with dbm.ndbm.open(self.filename, 'c') as db: + for k, v in kvs: + db[k] = v + self.assertIn(k, db) + self.assertEqual(len(db), len(kvs)) + + db.clear() + for k, v in kvs: + self.assertNotIn(k, db) + self.assertEqual(len(db), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py new file mode 100644 index 00000000000..7cc275adc63 --- /dev/null +++ b/Lib/test/test_embed.py @@ -0,0 +1,1995 @@ +# Run the tests in Programs/_testembed.c (tests for the CPython embedding APIs) +from test import support +from test.support import import_helper, os_helper, threading_helper, MS_WINDOWS +import unittest + +from collections import namedtuple +import contextlib +import io +import json +import os +import os.path +import re +import shutil +import subprocess +import sys +import sysconfig +import tempfile +import textwrap + +if not support.has_subprocess_support: + raise unittest.SkipTest("test module requires subprocess") + + +try: + import _testinternalcapi +except ImportError: + _testinternalcapi = None + + +MACOS = (sys.platform == 'darwin') +PYMEM_ALLOCATOR_NOT_SET = 0 +PYMEM_ALLOCATOR_DEBUG = 2 +PYMEM_ALLOCATOR_MALLOC = 3 +PYMEM_ALLOCATOR_MIMALLOC = 7 +if support.Py_GIL_DISABLED: + ALLOCATOR_FOR_CONFIG = PYMEM_ALLOCATOR_MIMALLOC +else: + ALLOCATOR_FOR_CONFIG = PYMEM_ALLOCATOR_MALLOC + +Py_STATS = hasattr(sys, '_stats_on') + +# _PyCoreConfig_InitCompatConfig() +API_COMPAT = 1 +# _PyCoreConfig_InitPythonConfig() +API_PYTHON = 2 +# _PyCoreConfig_InitIsolatedConfig() +API_ISOLATED = 3 + +INIT_LOOPS = 4 +MAX_HASH_SEED = 4294967295 + +ABI_THREAD = 't' if sysconfig.get_config_var('Py_GIL_DISABLED') else '' + + +# If we are running from a build dir, but the stdlib has been installed, +# some tests need to expect different results. +STDLIB_INSTALL = os.path.join(sys.prefix, sys.platlibdir, + f'python{sys.version_info.major}.{sys.version_info.minor}') +if not os.path.isfile(os.path.join(STDLIB_INSTALL, 'os.py')): + STDLIB_INSTALL = None + +def debug_build(program): + program = os.path.basename(program) + name = os.path.splitext(program)[0] + return name.casefold().endswith("_d".casefold()) + + +def remove_python_envvars(): + env = dict(os.environ) + # Remove PYTHON* environment variables to get deterministic environment + for key in list(env): + if key.startswith('PYTHON'): + del env[key] + return env + + +class EmbeddingTestsMixin: + def setUp(self): + exename = "_testembed" + builddir = os.path.dirname(sys.executable) + if MS_WINDOWS: + ext = ("_d" if debug_build(sys.executable) else "") + ".exe" + exename += ext + exepath = builddir + else: + exepath = os.path.join(builddir, 'Programs') + self.test_exe = exe = os.path.join(exepath, exename) + if not os.path.exists(exe): + self.skipTest("%r doesn't exist" % exe) + # This is needed otherwise we get a fatal error: + # "Py_Initialize: Unable to get the locale encoding + # LookupError: no codec search functions registered: can't find encoding" + self.oldcwd = os.getcwd() + os.chdir(builddir) + + def tearDown(self): + os.chdir(self.oldcwd) + + def run_embedded_interpreter(self, *args, env=None, + timeout=None, returncode=0, input=None, + cwd=None): + """Runs a test in the embedded interpreter""" + cmd = [self.test_exe] + cmd.extend(args) + if env is not None and MS_WINDOWS: + # Windows requires at least the SYSTEMROOT environment variable to + # start Python. + env = env.copy() + env['SYSTEMROOT'] = os.environ['SYSTEMROOT'] + + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + env=env, + cwd=cwd) + try: + (out, err) = p.communicate(input=input, timeout=timeout) + except: + p.terminate() + p.wait() + raise + if p.returncode != returncode and support.verbose: + print(f"--- {cmd} failed ---") + print(f"stdout:\n{out}") + print(f"stderr:\n{err}") + print("------") + + self.assertEqual(p.returncode, returncode, + "bad returncode %d, stderr is %r" % + (p.returncode, err)) + return out, err + + def run_repeated_init_and_subinterpreters(self): + out, err = self.run_embedded_interpreter("test_repeated_init_and_subinterpreters") + self.assertEqual(err, "") + + # The output from _testembed looks like this: + # --- Pass 1 --- + # interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728 + # interp 1 <0x1d4f690>, thread state <0x1d35350>: id(modules) = 139650431165784 + # interp 2 <0x1d5a690>, thread state <0x1d99ed0>: id(modules) = 139650413140368 + # interp 3 <0x1d4f690>, thread state <0x1dc3340>: id(modules) = 139650412862200 + # interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728 + # --- Pass 2 --- + # ... + + interp_pat = (r"^interp (\d+) <(0x[\dA-F]+)>, " + r"thread state <(0x[\dA-F]+)>: " + r"id\(modules\) = ([\d]+)$") + Interp = namedtuple("Interp", "id interp tstate modules") + + numloops = 1 + current_run = [] + for line in out.splitlines(): + if line == "--- Pass {} ---".format(numloops): + self.assertEqual(len(current_run), 0) + if support.verbose > 1: + print(line) + numloops += 1 + continue + + self.assertLess(len(current_run), 5) + match = re.match(interp_pat, line) + if match is None: + self.assertRegex(line, interp_pat) + + # Parse the line from the loop. The first line is the main + # interpreter and the 3 afterward are subinterpreters. + interp = Interp(*match.groups()) + if support.verbose > 2: + # 5 lines per pass is super-spammy, so limit that to -vvv + print(interp) + self.assertTrue(interp.interp) + self.assertTrue(interp.tstate) + self.assertTrue(interp.modules) + current_run.append(interp) + + # The last line in the loop should be the same as the first. + if len(current_run) == 5: + main = current_run[0] + self.assertEqual(interp, main) + yield current_run + current_run = [] + + +class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase): + maxDiff = 100 * 50 + + def test_subinterps_main(self): + for run in self.run_repeated_init_and_subinterpreters(): + main = run[0] + + self.assertEqual(main.id, '0') + + def test_subinterps_different_ids(self): + for run in self.run_repeated_init_and_subinterpreters(): + main, *subs, _ = run + + mainid = int(main.id) + for i, sub in enumerate(subs): + self.assertEqual(sub.id, str(mainid + i + 1)) + + def test_subinterps_distinct_state(self): + for run in self.run_repeated_init_and_subinterpreters(): + main, *subs, _ = run + + if '0x0' in main: + # XXX Fix on Windows (and other platforms): something + # is going on with the pointers in Programs/_testembed.c. + # interp.interp is 0x0 and interp.modules is the same + # between interpreters. + raise unittest.SkipTest('platform prints pointers as 0x0') + + for sub in subs: + # A new subinterpreter may have the same + # PyInterpreterState pointer as a previous one if + # the earlier one has already been destroyed. So + # we compare with the main interpreter. The same + # applies to tstate. + self.assertNotEqual(sub.interp, main.interp) + self.assertNotEqual(sub.tstate, main.tstate) + self.assertNotEqual(sub.modules, main.modules) + + def test_repeated_init_and_inittab(self): + out, err = self.run_embedded_interpreter("test_repeated_init_and_inittab") + self.assertEqual(err, "") + + lines = [f"--- Pass {i} ---" for i in range(1, INIT_LOOPS+1)] + lines = "\n".join(lines) + "\n" + self.assertEqual(out, lines) + + def test_inittab_submodule_multiphase(self): + out, err = self.run_embedded_interpreter("test_inittab_submodule_multiphase") + self.assertEqual(err, "") + self.assertEqual(out, + "\n" + "\n" + "Hello from sub-module\n" + "mp_pkg.mp_submod.mp_submod_exec_slot_ran='yes'\n" + "mp_pkg.mp_pkg_exec_slot_ran='yes'\n" + ) + + def test_inittab_submodule_singlephase(self): + out, err = self.run_embedded_interpreter("test_inittab_submodule_singlephase") + self.assertEqual(self._nogil_filtered_err(err, "sp_pkg"), "") + self.assertEqual(out, + "\n" + "\n" + "Hello from sub-module\n" + ) + + def test_forced_io_encoding(self): + # Checks forced configuration of embedded interpreter IO streams + env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape") + out, err = self.run_embedded_interpreter("test_forced_io_encoding", env=env) + if support.verbose > 1: + print() + print(out) + print(err) + expected_stream_encoding = "utf-8" + expected_errors = "surrogateescape" + expected_output = '\n'.join([ + "--- Use defaults ---", + "Expected encoding: default", + "Expected errors: default", + "stdin: {in_encoding}:{errors}", + "stdout: {out_encoding}:{errors}", + "stderr: {out_encoding}:backslashreplace", + "--- Set errors only ---", + "Expected encoding: default", + "Expected errors: ignore", + "stdin: {in_encoding}:ignore", + "stdout: {out_encoding}:ignore", + "stderr: {out_encoding}:backslashreplace", + "--- Set encoding only ---", + "Expected encoding: iso8859-1", + "Expected errors: default", + "stdin: iso8859-1:{errors}", + "stdout: iso8859-1:{errors}", + "stderr: iso8859-1:backslashreplace", + "--- Set encoding and errors ---", + "Expected encoding: iso8859-1", + "Expected errors: replace", + "stdin: iso8859-1:replace", + "stdout: iso8859-1:replace", + "stderr: iso8859-1:backslashreplace"]) + expected_output = expected_output.format( + in_encoding=expected_stream_encoding, + out_encoding=expected_stream_encoding, + errors=expected_errors) + # This is useful if we ever trip over odd platform behaviour + self.maxDiff = None + self.assertEqual(out.strip(), expected_output) + + def test_pre_initialization_api(self): + """ + Checks some key parts of the C-API that need to work before the runtime + is initialized (via Py_Initialize()). + """ + env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path)) + out, err = self.run_embedded_interpreter("test_pre_initialization_api", env=env) + if support.verbose > 1: + print() + print(out) + print(err) + if MS_WINDOWS: + expected_path = self.test_exe + else: + expected_path = os.path.join(os.getcwd(), "spam") + expected_output = f"sys.executable: {expected_path}\n" + self.assertIn(expected_output, out) + self.assertEqual(err, '') + + def test_pre_initialization_sys_options(self): + """ + Checks that sys.warnoptions and sys._xoptions can be set before the + runtime is initialized (otherwise they won't be effective). + """ + env = remove_python_envvars() + env['PYTHONPATH'] = os.pathsep.join(sys.path) + out, err = self.run_embedded_interpreter( + "test_pre_initialization_sys_options", env=env) + if support.verbose > 1: + print() + print(out) + print(err) + expected_output = ( + "sys.warnoptions: ['once', 'module', 'default']\n" + "sys._xoptions: {'not_an_option': '1', 'also_not_an_option': '2'}\n" + "warnings.filters[:3]: ['default', 'module', 'once']\n" + ) + self.assertIn(expected_output, out) + self.assertEqual(err, '') + + def test_bpo20891(self): + """ + bpo-20891: Calling PyGILState_Ensure in a non-Python thread must not + crash. + """ + out, err = self.run_embedded_interpreter("test_bpo20891") + self.assertEqual(out, '') + self.assertEqual(err, '') + + def test_initialize_twice(self): + """ + bpo-33932: Calling Py_Initialize() twice should do nothing (and not + crash!). + """ + out, err = self.run_embedded_interpreter("test_initialize_twice") + self.assertEqual(out, '') + self.assertEqual(err, '') + + def test_initialize_pymain(self): + """ + bpo-34008: Calling Py_Main() after Py_Initialize() must not fail. + """ + out, err = self.run_embedded_interpreter("test_initialize_pymain") + self.assertEqual(out.rstrip(), "Py_Main() after Py_Initialize: sys.argv=['-c', 'arg2']") + self.assertEqual(err, '') + + def test_run_main(self): + out, err = self.run_embedded_interpreter("test_run_main") + self.assertEqual(out.rstrip(), "Py_RunMain(): sys.argv=['-c', 'arg2']") + self.assertEqual(err, '') + + def test_run_main_loop(self): + # bpo-40413: Calling Py_InitializeFromConfig()+Py_RunMain() multiple + # times must not crash. + nloop = 5 + out, err = self.run_embedded_interpreter("test_run_main_loop") + self.assertEqual(out, "Py_RunMain(): sys.argv=['-c', 'arg2']\n" * nloop) + self.assertEqual(err, '') + + def test_finalize_structseq(self): + # bpo-46417: Py_Finalize() clears structseq static types. Check that + # sys attributes using struct types still work when + # Py_Finalize()/Py_Initialize() is called multiple times. + # print() calls type->tp_repr(instance) and so checks that the types + # are still working properly. + script = support.findfile('_test_embed_structseq.py') + with open(script, encoding="utf-8") as fp: + code = fp.read() + out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) + self.assertEqual(out, 'Tests passed\n' * INIT_LOOPS) + + def test_simple_initialization_api(self): + # _testembed now uses Py_InitializeFromConfig by default + # This case specifically checks Py_Initialize(Ex) still works + out, err = self.run_embedded_interpreter("test_repeated_simple_init") + self.assertEqual(out, 'Finalized\n' * INIT_LOOPS) + + @support.requires_specialization + @unittest.skipUnless(support.TEST_MODULES_ENABLED, "requires test modules") + def test_specialized_static_code_gets_unspecialized_at_Py_FINALIZE(self): + # https://github.com/python/cpython/issues/92031 + + code = textwrap.dedent("""\ + import dis + import importlib._bootstrap + import opcode + import test.test_dis + + def is_specialized(f): + for instruction in dis.get_instructions(f, adaptive=True): + opname = instruction.opname + if ( + opname in opcode._specialized_opmap + # Exclude superinstructions: + and "__" not in opname + ): + return True + return False + + func = importlib._bootstrap._handle_fromlist + + # "copy" the code to un-specialize it: + func.__code__ = func.__code__.replace() + + assert not is_specialized(func), "specialized instructions found" + + for i in range(test.test_dis.ADAPTIVE_WARMUP_DELAY): + func(importlib._bootstrap, ["x"], lambda *args: None) + + assert is_specialized(func), "no specialized instructions found" + + print("Tests passed") + """) + run = self.run_embedded_interpreter + out, err = run("test_repeated_init_exec", code) + self.assertEqual(out, 'Tests passed\n' * INIT_LOOPS) + + def test_ucnhash_capi_reset(self): + # bpo-47182: unicodeobject.c:ucnhash_capi was not reset on shutdown. + code = "print('\\N{digit nine}')" + out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) + self.assertEqual(out, '9\n' * INIT_LOOPS) + + def test_datetime_reset_strptime(self): + code = ( + "import datetime;" + "d = datetime.datetime.strptime('2000-01-01', '%Y-%m-%d');" + "print(d.strftime('%Y%m%d'))" + ) + out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) + self.assertEqual(out, '20000101\n' * INIT_LOOPS) + + def test_static_types_inherited_slots(self): + script = textwrap.dedent(""" + import test.support + + results = {} + def add(cls, slot, own): + value = getattr(cls, slot) + try: + subresults = results[cls.__name__] + except KeyError: + subresults = results[cls.__name__] = {} + subresults[slot] = [repr(value), own] + + for cls in test.support.iter_builtin_types(): + for slot, own in test.support.iter_slot_wrappers(cls): + add(cls, slot, own) + """) + + ns = {} + exec(script, ns, ns) + all_expected = ns['results'] + del ns + + script += textwrap.dedent(""" + import json + import sys + text = json.dumps(results) + print(text, file=sys.stderr) + """) + out, err = self.run_embedded_interpreter( + "test_repeated_init_exec", script, script) + results = err.split('--- Loop #')[1:] + results = [res.rpartition(' ---\n')[-1] for res in results] + + self.maxDiff = None + for i, text in enumerate(results, start=1): + result = json.loads(text) + for classname, expected in all_expected.items(): + with self.subTest(loop=i, cls=classname): + slots = result.pop(classname) + self.assertEqual(slots, expected) + self.assertEqual(result, {}) + self.assertEqual(out, '') + + def test_getargs_reset_static_parser(self): + # Test _PyArg_Parser initializations via _PyArg_UnpackKeywords() + # https://github.com/python/cpython/issues/122334 + code = textwrap.dedent(""" + try: + import _ssl + except ModuleNotFoundError: + _ssl = None + if _ssl is not None: + _ssl.txt2obj(txt='1.3') + print('1') + + import _queue + _queue.SimpleQueue().put_nowait(item=None) + print('2') + + import _zoneinfo + _zoneinfo.ZoneInfo.clear_cache(only_keys=['Foo/Bar']) + print('3') + """) + out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) + self.assertEqual(out, '1\n2\n3\n' * INIT_LOOPS) + + @staticmethod + def _nogil_filtered_err(err: str, mod_name: str) -> str: + if not support.Py_GIL_DISABLED: + return err + + # the test imports a singlephase init extension, so it emits a warning + # under the free-threaded build + expected_runtime_warning = ( + "RuntimeWarning: The global interpreter lock (GIL)" + f" has been enabled to load module '{mod_name}'" + ) + filtered_err_lines = [ + line + for line in err.strip().splitlines() + if expected_runtime_warning not in line + ] + return "\n".join(filtered_err_lines) + + +@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") +class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): + maxDiff = 4096 + UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape') + + # Marker to read the default configuration: get_default_config() + GET_DEFAULT_CONFIG = object() + + # Marker to ignore a configuration parameter + IGNORE_CONFIG = object() + + PRE_CONFIG_COMPAT = { + '_config_init': API_COMPAT, + 'allocator': PYMEM_ALLOCATOR_NOT_SET, + 'parse_argv': 0, + 'configure_locale': 1, + 'coerce_c_locale': 0, + 'coerce_c_locale_warn': 0, + 'utf8_mode': 0, + } + if MS_WINDOWS: + PRE_CONFIG_COMPAT.update({ + 'legacy_windows_fs_encoding': 0, + }) + PRE_CONFIG_PYTHON = dict(PRE_CONFIG_COMPAT, + _config_init=API_PYTHON, + parse_argv=1, + coerce_c_locale=GET_DEFAULT_CONFIG, + utf8_mode=GET_DEFAULT_CONFIG, + ) + PRE_CONFIG_ISOLATED = dict(PRE_CONFIG_COMPAT, + _config_init=API_ISOLATED, + configure_locale=0, + isolated=1, + use_environment=0, + utf8_mode=0, + dev_mode=0, + coerce_c_locale=0, + ) + + COPY_PRE_CONFIG = [ + 'dev_mode', + 'isolated', + 'use_environment', + ] + + CONFIG_COMPAT = { + '_config_init': API_COMPAT, + 'isolated': False, + 'use_environment': True, + 'dev_mode': False, + + 'install_signal_handlers': True, + 'use_hash_seed': False, + 'hash_seed': 0, + 'int_max_str_digits': sys.int_info.default_max_str_digits, + 'cpu_count': -1, + 'faulthandler': False, + 'tracemalloc': 0, + 'perf_profiling': False, + 'import_time': False, + 'code_debug_ranges': True, + 'show_ref_count': False, + 'dump_refs': False, + 'dump_refs_file': None, + 'malloc_stats': False, + + 'filesystem_encoding': GET_DEFAULT_CONFIG, + 'filesystem_errors': GET_DEFAULT_CONFIG, + + 'pycache_prefix': None, + 'program_name': GET_DEFAULT_CONFIG, + 'parse_argv': False, + 'argv': [""], + 'orig_argv': [], + + 'xoptions': [], + 'warnoptions': [], + + 'pythonpath_env': None, + 'home': None, + 'executable': GET_DEFAULT_CONFIG, + 'base_executable': GET_DEFAULT_CONFIG, + + 'prefix': GET_DEFAULT_CONFIG, + 'base_prefix': GET_DEFAULT_CONFIG, + 'exec_prefix': GET_DEFAULT_CONFIG, + 'base_exec_prefix': GET_DEFAULT_CONFIG, + 'module_search_paths': GET_DEFAULT_CONFIG, + 'module_search_paths_set': True, + 'platlibdir': sys.platlibdir, + 'stdlib_dir': GET_DEFAULT_CONFIG, + + 'site_import': True, + 'bytes_warning': 0, + 'warn_default_encoding': False, + 'inspect': False, + 'interactive': False, + 'optimization_level': 0, + 'parser_debug': False, + 'write_bytecode': True, + 'verbose': 0, + 'quiet': False, + 'user_site_directory': True, + 'configure_c_stdio': False, + 'buffered_stdio': True, + + 'stdio_encoding': GET_DEFAULT_CONFIG, + 'stdio_errors': GET_DEFAULT_CONFIG, + + 'skip_source_first_line': False, + 'run_command': None, + 'run_module': None, + 'run_filename': None, + 'sys_path_0': None, + + '_install_importlib': True, + 'check_hash_pycs_mode': 'default', + 'pathconfig_warnings': True, + '_init_main': True, + 'use_frozen_modules': not support.Py_DEBUG, + 'safe_path': False, + '_is_python_build': IGNORE_CONFIG, + } + if Py_STATS: + CONFIG_COMPAT['_pystats'] = 0 + if support.Py_DEBUG: + CONFIG_COMPAT['run_presite'] = None + if support.Py_GIL_DISABLED: + CONFIG_COMPAT['enable_gil'] = -1 + if MS_WINDOWS: + CONFIG_COMPAT.update({ + 'legacy_windows_stdio': 0, + }) + + CONFIG_PYTHON = dict(CONFIG_COMPAT, + _config_init=API_PYTHON, + configure_c_stdio=True, + parse_argv=True, + ) + CONFIG_ISOLATED = dict(CONFIG_COMPAT, + _config_init=API_ISOLATED, + isolated=True, + use_environment=False, + user_site_directory=False, + safe_path=True, + dev_mode=False, + install_signal_handlers=False, + use_hash_seed=False, + faulthandler=False, + tracemalloc=0, + perf_profiling=False, + pathconfig_warnings=False, + ) + if MS_WINDOWS: + CONFIG_ISOLATED['legacy_windows_stdio'] = 0 + + # global config + DEFAULT_GLOBAL_CONFIG = { + 'Py_HasFileSystemDefaultEncoding': 0, + 'Py_HashRandomizationFlag': 1, + '_Py_HasFileSystemDefaultEncodeErrors': 0, + } + COPY_GLOBAL_PRE_CONFIG = [ + ('Py_UTF8Mode', 'utf8_mode'), + ] + COPY_GLOBAL_CONFIG = [ + # Copy core config to global config for expected values + # True means that the core config value is inverted (0 => 1 and 1 => 0) + ('Py_BytesWarningFlag', 'bytes_warning'), + ('Py_DebugFlag', 'parser_debug'), + ('Py_DontWriteBytecodeFlag', 'write_bytecode', True), + ('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'), + ('Py_FileSystemDefaultEncoding', 'filesystem_encoding'), + ('Py_FrozenFlag', 'pathconfig_warnings', True), + ('Py_IgnoreEnvironmentFlag', 'use_environment', True), + ('Py_InspectFlag', 'inspect'), + ('Py_InteractiveFlag', 'interactive'), + ('Py_IsolatedFlag', 'isolated'), + ('Py_NoSiteFlag', 'site_import', True), + ('Py_NoUserSiteDirectory', 'user_site_directory', True), + ('Py_OptimizeFlag', 'optimization_level'), + ('Py_QuietFlag', 'quiet'), + ('Py_UnbufferedStdioFlag', 'buffered_stdio', True), + ('Py_VerboseFlag', 'verbose'), + ] + if MS_WINDOWS: + COPY_GLOBAL_PRE_CONFIG.extend(( + ('Py_LegacyWindowsFSEncodingFlag', 'legacy_windows_fs_encoding'), + )) + COPY_GLOBAL_CONFIG.extend(( + ('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'), + )) + + EXPECTED_CONFIG = None + + @classmethod + def tearDownClass(cls): + # clear cache + cls.EXPECTED_CONFIG = None + + def main_xoptions(self, xoptions_list): + xoptions = {} + for opt in xoptions_list: + if '=' in opt: + key, value = opt.split('=', 1) + xoptions[key] = value + else: + xoptions[opt] = True + return xoptions + + def _get_expected_config_impl(self): + env = remove_python_envvars() + code = textwrap.dedent(''' + import json + import sys + import _testinternalcapi + + configs = _testinternalcapi.get_configs() + + data = json.dumps(configs) + data = data.encode('utf-8') + sys.stdout.buffer.write(data) + sys.stdout.buffer.flush() + ''') + + # Use -S to not import the site module: get the proper configuration + # when test_embed is run from a venv (bpo-35313) + args = [sys.executable, '-S', '-c', code] + proc = subprocess.run(args, env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if proc.returncode: + raise Exception(f"failed to get the default config: " + f"stdout={proc.stdout!r} stderr={proc.stderr!r}") + stdout = proc.stdout.decode('utf-8') + # ignore stderr + try: + return json.loads(stdout) + except json.JSONDecodeError: + self.fail(f"fail to decode stdout: {stdout!r}") + + def _get_expected_config(self): + cls = InitConfigTests + if cls.EXPECTED_CONFIG is None: + cls.EXPECTED_CONFIG = self._get_expected_config_impl() + + # get a copy + configs = {} + for config_key, config_value in cls.EXPECTED_CONFIG.items(): + config = {} + for key, value in config_value.items(): + if isinstance(value, list): + value = value.copy() + config[key] = value + configs[config_key] = config + return configs + + def get_expected_config(self, expected_preconfig, expected, + env, api, modify_path_cb=None): + configs = self._get_expected_config() + + pre_config = configs['pre_config'] + for key, value in expected_preconfig.items(): + if value is self.GET_DEFAULT_CONFIG: + expected_preconfig[key] = pre_config[key] + + if not expected_preconfig['configure_locale'] or api == API_COMPAT: + # there is no easy way to get the locale encoding before + # setlocale(LC_CTYPE, "") is called: don't test encodings + for key in ('filesystem_encoding', 'filesystem_errors', + 'stdio_encoding', 'stdio_errors'): + expected[key] = self.IGNORE_CONFIG + + if not expected_preconfig['configure_locale']: + # UTF-8 Mode depends on the locale. There is no easy way + # to guess if UTF-8 Mode will be enabled or not if the locale + # is not configured. + expected_preconfig['utf8_mode'] = self.IGNORE_CONFIG + + if expected_preconfig['utf8_mode'] == 1: + if expected['filesystem_encoding'] is self.GET_DEFAULT_CONFIG: + expected['filesystem_encoding'] = 'utf-8' + if expected['filesystem_errors'] is self.GET_DEFAULT_CONFIG: + expected['filesystem_errors'] = self.UTF8_MODE_ERRORS + if expected['stdio_encoding'] is self.GET_DEFAULT_CONFIG: + expected['stdio_encoding'] = 'utf-8' + if expected['stdio_errors'] is self.GET_DEFAULT_CONFIG: + expected['stdio_errors'] = 'surrogateescape' + + if MS_WINDOWS: + default_executable = self.test_exe + elif expected['program_name'] is not self.GET_DEFAULT_CONFIG: + default_executable = os.path.abspath(expected['program_name']) + else: + default_executable = os.path.join(os.getcwd(), '_testembed') + if expected['executable'] is self.GET_DEFAULT_CONFIG: + expected['executable'] = default_executable + if expected['base_executable'] is self.GET_DEFAULT_CONFIG: + expected['base_executable'] = default_executable + if expected['program_name'] is self.GET_DEFAULT_CONFIG: + expected['program_name'] = './_testembed' + + config = configs['config'] + for key, value in expected.items(): + if value is self.GET_DEFAULT_CONFIG: + expected[key] = config[key] + + if expected['module_search_paths'] is not self.IGNORE_CONFIG: + pythonpath_env = expected['pythonpath_env'] + if pythonpath_env is not None: + paths = pythonpath_env.split(os.path.pathsep) + expected['module_search_paths'] = [*paths, *expected['module_search_paths']] + if modify_path_cb is not None: + expected['module_search_paths'] = expected['module_search_paths'].copy() + modify_path_cb(expected['module_search_paths']) + + for key in self.COPY_PRE_CONFIG: + if key not in expected_preconfig: + expected_preconfig[key] = expected[key] + + def check_pre_config(self, configs, expected): + pre_config = dict(configs['pre_config']) + for key, value in list(expected.items()): + if value is self.IGNORE_CONFIG: + pre_config.pop(key, None) + del expected[key] + self.assertEqual(pre_config, expected) + + def check_config(self, configs, expected): + config = dict(configs['config']) + if MS_WINDOWS: + value = config.get(key := 'program_name') + if value and isinstance(value, str): + value = value[:len(value.lower().removesuffix('.exe'))] + if debug_build(sys.executable): + value = value[:len(value.lower().removesuffix('_d'))] + config[key] = value + for key, value in list(expected.items()): + if value is self.IGNORE_CONFIG: + config.pop(key, None) + del expected[key] + # Resolve bool/int mismatches to reduce noise in diffs + if isinstance(value, (bool, int)) and isinstance(config.get(key), (bool, int)): + expected[key] = type(config[key])(expected[key]) + self.assertEqual(config, expected) + + def check_global_config(self, configs): + pre_config = configs['pre_config'] + config = configs['config'] + + expected = dict(self.DEFAULT_GLOBAL_CONFIG) + for item in self.COPY_GLOBAL_CONFIG: + if len(item) == 3: + global_key, core_key, opposite = item + expected[global_key] = 0 if config[core_key] else 1 + else: + global_key, core_key = item + expected[global_key] = config[core_key] + for item in self.COPY_GLOBAL_PRE_CONFIG: + if len(item) == 3: + global_key, core_key, opposite = item + expected[global_key] = 0 if pre_config[core_key] else 1 + else: + global_key, core_key = item + expected[global_key] = pre_config[core_key] + + self.assertEqual(configs['global_config'], expected) + + def check_all_configs(self, testname, expected_config=None, + expected_preconfig=None, + modify_path_cb=None, + stderr=None, *, api, preconfig_api=None, + env=None, ignore_stderr=False, cwd=None): + new_env = remove_python_envvars() + if env is not None: + new_env.update(env) + env = new_env + + if preconfig_api is None: + preconfig_api = api + if preconfig_api == API_ISOLATED: + default_preconfig = self.PRE_CONFIG_ISOLATED + elif preconfig_api == API_PYTHON: + default_preconfig = self.PRE_CONFIG_PYTHON + else: + default_preconfig = self.PRE_CONFIG_COMPAT + if expected_preconfig is None: + expected_preconfig = {} + expected_preconfig = dict(default_preconfig, **expected_preconfig) + + if expected_config is None: + expected_config = {} + + if api == API_PYTHON: + default_config = self.CONFIG_PYTHON + elif api == API_ISOLATED: + default_config = self.CONFIG_ISOLATED + else: + default_config = self.CONFIG_COMPAT + expected_config = dict(default_config, **expected_config) + + self.get_expected_config(expected_preconfig, + expected_config, + env, + api, modify_path_cb) + + out, err = self.run_embedded_interpreter(testname, + env=env, cwd=cwd) + if stderr is None and not expected_config['verbose']: + stderr = "" + if stderr is not None and not ignore_stderr: + self.assertEqual(err.rstrip(), stderr) + try: + configs = json.loads(out) + except json.JSONDecodeError: + self.fail(f"fail to decode stdout: {out!r}") + + self.check_pre_config(configs, expected_preconfig) + self.check_config(configs, expected_config) + self.check_global_config(configs) + return configs + + def test_init_default_config(self): + self.check_all_configs("test_init_initialize_config", api=API_COMPAT) + + def test_preinit_compat_config(self): + self.check_all_configs("test_preinit_compat_config", api=API_COMPAT) + + def test_init_compat_config(self): + self.check_all_configs("test_init_compat_config", api=API_COMPAT) + + def test_init_global_config(self): + preconfig = { + 'utf8_mode': 1, + } + config = { + 'program_name': './globalvar', + 'site_import': 0, + 'bytes_warning': 1, + 'warnoptions': ['default::BytesWarning'], + 'inspect': 1, + 'interactive': 1, + 'optimization_level': 2, + 'write_bytecode': 0, + 'verbose': 1, + 'quiet': 1, + 'buffered_stdio': 0, + + 'user_site_directory': 0, + 'pathconfig_warnings': 0, + } + self.check_all_configs("test_init_global_config", config, preconfig, + api=API_COMPAT) + + def test_init_from_config(self): + preconfig = { + 'allocator': ALLOCATOR_FOR_CONFIG, + 'utf8_mode': 1, + } + config = { + 'install_signal_handlers': False, + 'use_hash_seed': True, + 'hash_seed': 123, + 'tracemalloc': 2, + 'perf_profiling': False, + 'import_time': True, + 'code_debug_ranges': False, + 'show_ref_count': True, + 'malloc_stats': True, + + 'stdio_encoding': 'iso8859-1', + 'stdio_errors': 'replace', + + 'pycache_prefix': 'conf_pycache_prefix', + 'program_name': './conf_program_name', + 'argv': ['-c', 'arg2'], + 'orig_argv': ['python3', + '-W', 'cmdline_warnoption', + '-X', 'cmdline_xoption', + '-c', 'pass', + 'arg2'], + 'parse_argv': True, + 'xoptions': [ + 'config_xoption1=3', + 'config_xoption2=', + 'config_xoption3', + 'cmdline_xoption', + ], + 'warnoptions': [ + 'cmdline_warnoption', + 'default::BytesWarning', + 'config_warnoption', + ], + 'run_command': 'pass\n', + + 'site_import': False, + 'bytes_warning': 1, + 'inspect': True, + 'interactive': True, + 'optimization_level': 2, + 'write_bytecode': False, + 'verbose': 1, + 'quiet': True, + 'configure_c_stdio': True, + 'buffered_stdio': False, + 'user_site_directory': False, + 'faulthandler': True, + 'platlibdir': 'my_platlibdir', + 'module_search_paths': self.IGNORE_CONFIG, + 'safe_path': True, + 'int_max_str_digits': 31337, + 'cpu_count': 4321, + + 'check_hash_pycs_mode': 'always', + 'pathconfig_warnings': False, + } + if Py_STATS: + config['_pystats'] = 1 + self.check_all_configs("test_init_from_config", config, preconfig, + api=API_COMPAT) + + def test_init_compat_env(self): + preconfig = { + 'allocator': ALLOCATOR_FOR_CONFIG, + } + config = { + 'use_hash_seed': True, + 'hash_seed': 42, + 'tracemalloc': 2, + 'perf_profiling': False, + 'import_time': True, + 'code_debug_ranges': False, + 'malloc_stats': True, + 'inspect': True, + 'optimization_level': 2, + 'pythonpath_env': '/my/path', + 'pycache_prefix': 'env_pycache_prefix', + 'write_bytecode': False, + 'verbose': 1, + 'buffered_stdio': False, + 'stdio_encoding': 'iso8859-1', + 'stdio_errors': 'replace', + 'user_site_directory': False, + 'faulthandler': True, + 'warnoptions': ['EnvVar'], + 'platlibdir': 'env_platlibdir', + 'module_search_paths': self.IGNORE_CONFIG, + 'safe_path': True, + 'int_max_str_digits': 4567, + } + if Py_STATS: + config['_pystats'] = 1 + self.check_all_configs("test_init_compat_env", config, preconfig, + api=API_COMPAT) + + def test_init_python_env(self): + preconfig = { + 'allocator': ALLOCATOR_FOR_CONFIG, + 'utf8_mode': 1, + } + config = { + 'use_hash_seed': True, + 'hash_seed': 42, + 'tracemalloc': 2, + 'perf_profiling': False, + 'import_time': True, + 'code_debug_ranges': False, + 'malloc_stats': True, + 'inspect': True, + 'optimization_level': 2, + 'pythonpath_env': '/my/path', + 'pycache_prefix': 'env_pycache_prefix', + 'write_bytecode': False, + 'verbose': 1, + 'buffered_stdio': False, + 'stdio_encoding': 'iso8859-1', + 'stdio_errors': 'replace', + 'user_site_directory': False, + 'faulthandler': True, + 'warnoptions': ['EnvVar'], + 'platlibdir': 'env_platlibdir', + 'module_search_paths': self.IGNORE_CONFIG, + 'safe_path': True, + 'int_max_str_digits': 4567, + } + if Py_STATS: + config['_pystats'] = True + self.check_all_configs("test_init_python_env", config, preconfig, + api=API_PYTHON) + + def test_init_env_dev_mode(self): + preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG) + config = dict(dev_mode=1, + faulthandler=1, + warnoptions=['default']) + self.check_all_configs("test_init_env_dev_mode", config, preconfig, + api=API_COMPAT) + + def test_init_env_dev_mode_alloc(self): + preconfig = dict(allocator=ALLOCATOR_FOR_CONFIG) + config = dict(dev_mode=1, + faulthandler=1, + warnoptions=['default']) + self.check_all_configs("test_init_env_dev_mode_alloc", config, preconfig, + api=API_COMPAT) + + def test_init_dev_mode(self): + preconfig = { + 'allocator': PYMEM_ALLOCATOR_DEBUG, + } + config = { + 'faulthandler': True, + 'dev_mode': True, + 'warnoptions': ['default'], + } + self.check_all_configs("test_init_dev_mode", config, preconfig, + api=API_PYTHON) + + def test_preinit_parse_argv(self): + # Pre-initialize implicitly using argv: make sure that -X dev + # is used to configure the allocation in preinitialization + preconfig = { + 'allocator': PYMEM_ALLOCATOR_DEBUG, + } + config = { + 'argv': ['script.py'], + 'orig_argv': ['python3', '-X', 'dev', '-P', 'script.py'], + 'run_filename': os.path.abspath('script.py'), + 'dev_mode': True, + 'faulthandler': True, + 'warnoptions': ['default'], + 'xoptions': ['dev'], + 'safe_path': True, + } + self.check_all_configs("test_preinit_parse_argv", config, preconfig, + api=API_PYTHON) + + def test_preinit_dont_parse_argv(self): + # -X dev must be ignored by isolated preconfiguration + preconfig = { + 'isolated': 0, + } + argv = ["python3", + "-E", "-I", "-P", + "-X", "dev", + "-X", "utf8", + "script.py"] + config = { + 'argv': argv, + 'orig_argv': argv, + 'isolated': 0, + } + self.check_all_configs("test_preinit_dont_parse_argv", config, preconfig, + api=API_ISOLATED) + + def test_init_isolated_flag(self): + config = { + 'isolated': True, + 'safe_path': True, + 'use_environment': False, + 'user_site_directory': False, + } + self.check_all_configs("test_init_isolated_flag", config, api=API_PYTHON) + + def test_preinit_isolated1(self): + # _PyPreConfig.isolated=1, _PyCoreConfig.isolated not set + config = { + 'isolated': True, + 'safe_path': True, + 'use_environment': False, + 'user_site_directory': False, + } + self.check_all_configs("test_preinit_isolated1", config, api=API_COMPAT) + + def test_preinit_isolated2(self): + # _PyPreConfig.isolated=0, _PyCoreConfig.isolated=1 + config = { + 'isolated': True, + 'safe_path': True, + 'use_environment': False, + 'user_site_directory': False, + } + self.check_all_configs("test_preinit_isolated2", config, api=API_COMPAT) + + def test_preinit_isolated_config(self): + self.check_all_configs("test_preinit_isolated_config", api=API_ISOLATED) + + def test_init_isolated_config(self): + self.check_all_configs("test_init_isolated_config", api=API_ISOLATED) + + def test_preinit_python_config(self): + self.check_all_configs("test_preinit_python_config", api=API_PYTHON) + + def test_init_python_config(self): + self.check_all_configs("test_init_python_config", api=API_PYTHON) + + def test_init_dont_configure_locale(self): + # _PyPreConfig.configure_locale=0 + preconfig = { + 'configure_locale': 0, + 'coerce_c_locale': 0, + } + self.check_all_configs("test_init_dont_configure_locale", {}, preconfig, + api=API_PYTHON) + + @unittest.skip('as of 3.11 this test no longer works because ' + 'path calculations do not occur on read') + def test_init_read_set(self): + config = { + 'program_name': './init_read_set', + 'executable': 'my_executable', + 'base_executable': 'my_executable', + } + def modify_path(path): + path.insert(1, "test_path_insert1") + path.append("test_path_append") + self.check_all_configs("test_init_read_set", config, + api=API_PYTHON, + modify_path_cb=modify_path) + + def test_init_sys_add(self): + config = { + 'faulthandler': 1, + 'xoptions': [ + 'config_xoption', + 'cmdline_xoption', + 'sysadd_xoption', + 'faulthandler', + ], + 'warnoptions': [ + 'ignore:::cmdline_warnoption', + 'ignore:::sysadd_warnoption', + 'ignore:::config_warnoption', + ], + 'orig_argv': ['python3', + '-W', 'ignore:::cmdline_warnoption', + '-X', 'cmdline_xoption'], + } + self.check_all_configs("test_init_sys_add", config, api=API_PYTHON) + + def test_init_run_main(self): + code = ('import _testinternalcapi, json; ' + 'print(json.dumps(_testinternalcapi.get_configs()))') + config = { + 'argv': ['-c', 'arg2'], + 'orig_argv': ['python3', '-c', code, 'arg2'], + 'program_name': './python3', + 'run_command': code + '\n', + 'parse_argv': True, + 'sys_path_0': '', + } + self.check_all_configs("test_init_run_main", config, api=API_PYTHON) + + def test_init_main(self): + code = ('import _testinternalcapi, json; ' + 'print(json.dumps(_testinternalcapi.get_configs()))') + config = { + 'argv': ['-c', 'arg2'], + 'orig_argv': ['python3', + '-c', code, + 'arg2'], + 'program_name': './python3', + 'run_command': code + '\n', + 'parse_argv': True, + '_init_main': 0, + 'sys_path_0': '', + } + self.check_all_configs("test_init_main", config, + api=API_PYTHON, + stderr="Run Python code before _Py_InitializeMain") + + def test_init_parse_argv(self): + config = { + 'parse_argv': True, + 'argv': ['-c', 'arg1', '-v', 'arg3'], + 'orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'], + 'program_name': './argv0', + 'run_command': 'pass\n', + 'use_environment': False, + } + self.check_all_configs("test_init_parse_argv", config, api=API_PYTHON) + + def test_init_dont_parse_argv(self): + pre_config = { + 'parse_argv': 0, + } + config = { + 'parse_argv': False, + 'argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'], + 'orig_argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'], + 'program_name': './argv0', + } + self.check_all_configs("test_init_dont_parse_argv", config, pre_config, + api=API_PYTHON) + + def default_program_name(self, config): + if MS_WINDOWS: + program_name = 'python' + executable = self.test_exe + else: + program_name = 'python3' + if MACOS: + executable = self.test_exe + else: + executable = shutil.which(program_name) or '' + config.update({ + 'program_name': program_name, + 'base_executable': executable, + 'executable': executable, + }) + + def test_init_setpath(self): + # Test Py_SetPath() + config = self._get_expected_config() + paths = config['config']['module_search_paths'] + + config = { + 'module_search_paths': paths, + 'prefix': '', + 'base_prefix': '', + 'exec_prefix': '', + 'base_exec_prefix': '', + # The current getpath.c doesn't determine the stdlib dir + # in this case. + 'stdlib_dir': '', + } + self.default_program_name(config) + env = {'TESTPATH': os.path.pathsep.join(paths)} + + self.check_all_configs("test_init_setpath", config, + api=API_COMPAT, env=env, + ignore_stderr=True) + + def test_init_setpath_config(self): + # Test Py_SetPath() with PyConfig + config = self._get_expected_config() + paths = config['config']['module_search_paths'] + + config = { + # set by Py_SetPath() + 'module_search_paths': paths, + 'prefix': '', + 'base_prefix': '', + 'exec_prefix': '', + 'base_exec_prefix': '', + # The current getpath.c doesn't determine the stdlib dir + # in this case. + 'stdlib_dir': '', + 'use_frozen_modules': not support.Py_DEBUG, + # overridden by PyConfig + 'program_name': 'conf_program_name', + 'base_executable': 'conf_executable', + 'executable': 'conf_executable', + } + env = {'TESTPATH': os.path.pathsep.join(paths)} + self.check_all_configs("test_init_setpath_config", config, + api=API_PYTHON, env=env, ignore_stderr=True) + + def module_search_paths(self, prefix=None, exec_prefix=None): + config = self._get_expected_config() + if prefix is None: + prefix = config['config']['prefix'] + if exec_prefix is None: + exec_prefix = config['config']['prefix'] + if MS_WINDOWS: + return config['config']['module_search_paths'] + else: + ver = sys.version_info + return [ + os.path.join(prefix, sys.platlibdir, + f'python{ver.major}{ver.minor}{ABI_THREAD}.zip'), + os.path.join(prefix, sys.platlibdir, + f'python{ver.major}.{ver.minor}{ABI_THREAD}'), + os.path.join(exec_prefix, sys.platlibdir, + f'python{ver.major}.{ver.minor}{ABI_THREAD}', 'lib-dynload'), + ] + + @contextlib.contextmanager + def tmpdir_with_python(self, subdir=None): + # Temporary directory with a copy of the Python program + with tempfile.TemporaryDirectory() as tmpdir: + # bpo-38234: On macOS and FreeBSD, the temporary directory + # can be symbolic link. For example, /tmp can be a symbolic link + # to /var/tmp. Call realpath() to resolve all symbolic links. + tmpdir = os.path.realpath(tmpdir) + if subdir: + tmpdir = os.path.normpath(os.path.join(tmpdir, subdir)) + os.makedirs(tmpdir) + + if MS_WINDOWS: + # Copy pythonXY.dll (or pythonXY_d.dll) + import fnmatch + exedir = os.path.dirname(self.test_exe) + for f in os.listdir(exedir): + if fnmatch.fnmatch(f, '*.dll'): + shutil.copyfile(os.path.join(exedir, f), os.path.join(tmpdir, f)) + + # Copy Python program + exec_copy = os.path.join(tmpdir, os.path.basename(self.test_exe)) + shutil.copyfile(self.test_exe, exec_copy) + shutil.copystat(self.test_exe, exec_copy) + self.test_exe = exec_copy + + yield tmpdir + + def test_init_setpythonhome(self): + # Test Py_SetPythonHome(home) with PYTHONPATH env var + config = self._get_expected_config() + paths = config['config']['module_search_paths'] + paths_str = os.path.pathsep.join(paths) + + for path in paths: + if not os.path.isdir(path): + continue + if os.path.exists(os.path.join(path, 'os.py')): + home = os.path.dirname(path) + break + else: + self.fail(f"Unable to find home in {paths!r}") + + prefix = exec_prefix = home + if MS_WINDOWS: + stdlib = os.path.join(home, "Lib") + # Because we are specifying 'home', module search paths + # are fairly static + expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib] + else: + version = f'{sys.version_info.major}.{sys.version_info.minor}' + stdlib = os.path.join(home, sys.platlibdir, f'python{version}{ABI_THREAD}') + expected_paths = self.module_search_paths(prefix=home, exec_prefix=home) + + config = { + 'home': home, + 'module_search_paths': expected_paths, + 'prefix': prefix, + 'base_prefix': prefix, + 'exec_prefix': exec_prefix, + 'base_exec_prefix': exec_prefix, + 'pythonpath_env': paths_str, + 'stdlib_dir': stdlib, + } + self.default_program_name(config) + env = {'TESTHOME': home, 'PYTHONPATH': paths_str} + self.check_all_configs("test_init_setpythonhome", config, + api=API_COMPAT, env=env) + + def test_init_is_python_build_with_home(self): + # Test _Py_path_config._is_python_build configuration (gh-91985) + config = self._get_expected_config() + paths = config['config']['module_search_paths'] + paths_str = os.path.pathsep.join(paths) + + for path in paths: + if not os.path.isdir(path): + continue + if os.path.exists(os.path.join(path, 'os.py')): + home = os.path.dirname(path) + break + else: + self.fail(f"Unable to find home in {paths!r}") + + prefix = exec_prefix = home + if MS_WINDOWS: + stdlib = os.path.join(home, "Lib") + # Because we are specifying 'home', module search paths + # are fairly static + expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib] + else: + version = f'{sys.version_info.major}.{sys.version_info.minor}' + stdlib = os.path.join(home, sys.platlibdir, f'python{version}{ABI_THREAD}') + expected_paths = self.module_search_paths(prefix=home, exec_prefix=home) + + config = { + 'home': home, + 'module_search_paths': expected_paths, + 'prefix': prefix, + 'base_prefix': prefix, + 'exec_prefix': exec_prefix, + 'base_exec_prefix': exec_prefix, + 'pythonpath_env': paths_str, + 'stdlib_dir': stdlib, + } + # The code above is taken from test_init_setpythonhome() + env = {'TESTHOME': home, 'PYTHONPATH': paths_str} + + env['NEGATIVE_ISPYTHONBUILD'] = '1' + config['_is_python_build'] = 0 + self.check_all_configs("test_init_is_python_build", config, + api=API_COMPAT, env=env) + + env['NEGATIVE_ISPYTHONBUILD'] = '0' + config['_is_python_build'] = 1 + exedir = os.path.dirname(sys.executable) + with open(os.path.join(exedir, 'pybuilddir.txt'), encoding='utf8') as f: + expected_paths[1 if MS_WINDOWS else 2] = os.path.normpath( + os.path.join(exedir, f'{f.read()}\n$'.splitlines()[0])) + if not MS_WINDOWS: + # PREFIX (default) is set when running in build directory + prefix = exec_prefix = sys.prefix + # stdlib calculation (/Lib) is not yet supported + expected_paths[0] = self.module_search_paths(prefix=prefix)[0] + config.update(prefix=prefix, base_prefix=prefix, + exec_prefix=exec_prefix, base_exec_prefix=exec_prefix) + self.check_all_configs("test_init_is_python_build", config, + api=API_COMPAT, env=env) + + def copy_paths_by_env(self, config): + all_configs = self._get_expected_config() + paths = all_configs['config']['module_search_paths'] + paths_str = os.path.pathsep.join(paths) + config['pythonpath_env'] = paths_str + env = {'PYTHONPATH': paths_str} + return env + + @unittest.skipIf(MS_WINDOWS, 'See test_init_pybuilddir_win32') + def test_init_pybuilddir(self): + # Test path configuration with pybuilddir.txt configuration file + + with self.tmpdir_with_python() as tmpdir: + # pybuilddir.txt is a sub-directory relative to the current + # directory (tmpdir) + vpath = sysconfig.get_config_var("VPATH") or '' + subdir = 'libdir' + libdir = os.path.join(tmpdir, subdir) + # The stdlib dir is dirname(executable) + VPATH + 'Lib' + stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib')) + os.mkdir(libdir) + + filename = os.path.join(tmpdir, 'pybuilddir.txt') + with open(filename, "w", encoding="utf8") as fp: + fp.write(subdir) + + module_search_paths = self.module_search_paths() + module_search_paths[-2] = stdlibdir + module_search_paths[-1] = libdir + + executable = self.test_exe + config = { + 'base_exec_prefix': sysconfig.get_config_var("exec_prefix"), + 'base_prefix': sysconfig.get_config_var("prefix"), + 'base_executable': executable, + 'executable': executable, + 'module_search_paths': module_search_paths, + 'stdlib_dir': stdlibdir, + } + env = self.copy_paths_by_env(config) + self.check_all_configs("test_init_compat_config", config, + api=API_COMPAT, env=env, + ignore_stderr=True, cwd=tmpdir) + + @unittest.skipUnless(MS_WINDOWS, 'See test_init_pybuilddir') + def test_init_pybuilddir_win32(self): + # Test path configuration with pybuilddir.txt configuration file + + vpath = sysconfig.get_config_var("VPATH") + subdir = r'PCbuild\arch' + if os.path.normpath(vpath).count(os.sep) == 2: + subdir = os.path.join(subdir, 'instrumented') + + with self.tmpdir_with_python(subdir) as tmpdir: + # The prefix is dirname(executable) + VPATH + prefix = os.path.normpath(os.path.join(tmpdir, vpath)) + # The stdlib dir is dirname(executable) + VPATH + 'Lib' + stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib')) + + filename = os.path.join(tmpdir, 'pybuilddir.txt') + with open(filename, "w", encoding="utf8") as fp: + fp.write(tmpdir) + + module_search_paths = self.module_search_paths() + module_search_paths[-3] = os.path.join(tmpdir, os.path.basename(module_search_paths[-3])) + module_search_paths[-2] = tmpdir + module_search_paths[-1] = stdlibdir + + executable = self.test_exe + config = { + 'base_exec_prefix': prefix, + 'base_prefix': prefix, + 'base_executable': executable, + 'executable': executable, + 'prefix': prefix, + 'exec_prefix': prefix, + 'module_search_paths': module_search_paths, + 'stdlib_dir': stdlibdir, + } + env = self.copy_paths_by_env(config) + self.check_all_configs("test_init_compat_config", config, + api=API_COMPAT, env=env, + ignore_stderr=False, cwd=tmpdir) + + def test_init_pyvenv_cfg(self): + # Test path configuration with pyvenv.cfg configuration file + + with self.tmpdir_with_python() as tmpdir, \ + tempfile.TemporaryDirectory() as pyvenv_home: + ver = sys.version_info + + if not MS_WINDOWS: + lib_dynload = os.path.join(pyvenv_home, + sys.platlibdir, + f'python{ver.major}.{ver.minor}{ABI_THREAD}', + 'lib-dynload') + os.makedirs(lib_dynload) + else: + lib_folder = os.path.join(pyvenv_home, 'Lib') + os.makedirs(lib_folder) + # getpath.py uses Lib\os.py as the LANDMARK + shutil.copyfile( + os.path.join(support.STDLIB_DIR, 'os.py'), + os.path.join(lib_folder, 'os.py'), + ) + + filename = os.path.join(tmpdir, 'pyvenv.cfg') + with open(filename, "w", encoding="utf8") as fp: + print("home = %s" % pyvenv_home, file=fp) + print("include-system-site-packages = false", file=fp) + + paths = self.module_search_paths() + if not MS_WINDOWS: + paths[-1] = lib_dynload + else: + paths = [ + os.path.join(tmpdir, os.path.basename(paths[0])), + pyvenv_home, + os.path.join(pyvenv_home, "Lib"), + ] + + executable = self.test_exe + base_executable = os.path.join(pyvenv_home, os.path.basename(executable)) + exec_prefix = pyvenv_home + config = { + 'base_prefix': sysconfig.get_config_var("prefix"), + 'base_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'base_executable': base_executable, + 'executable': executable, + 'module_search_paths': paths, + } + if MS_WINDOWS: + config['base_prefix'] = pyvenv_home + config['prefix'] = pyvenv_home + config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib') + config['use_frozen_modules'] = int(not support.Py_DEBUG) + else: + # cannot reliably assume stdlib_dir here because it + # depends too much on our build. But it ought to be found + config['stdlib_dir'] = self.IGNORE_CONFIG + config['use_frozen_modules'] = int(not support.Py_DEBUG) + + env = self.copy_paths_by_env(config) + self.check_all_configs("test_init_compat_config", config, + api=API_COMPAT, env=env, + ignore_stderr=True, cwd=tmpdir) + + @unittest.skipUnless(MS_WINDOWS, 'specific to Windows') + def test_getpath_abspath_win32(self): + # Check _Py_abspath() is passed a backslashed path not to fall back to + # GetFullPathNameW() on startup, which (re-)normalizes the path overly. + # Currently, _Py_normpath() doesn't trim trailing dots and spaces. + CASES = [ + ("C:/a. . .", "C:\\a. . ."), + ("C:\\a. . .", "C:\\a. . ."), + ("\\\\?\\C:////a////b. . .", "\\\\?\\C:\\a\\b. . ."), + ("//a/b/c. . .", "\\\\a\\b\\c. . ."), + ("\\\\a\\b\\c. . .", "\\\\a\\b\\c. . ."), + ("a. . .", f"{os.getcwd()}\\a"), # relpath gets fully normalized + ] + out, err = self.run_embedded_interpreter( + "test_init_initialize_config", + env={**remove_python_envvars(), + "PYTHONPATH": os.path.pathsep.join(c[0] for c in CASES)} + ) + self.assertEqual(err, "") + try: + out = json.loads(out) + except json.JSONDecodeError: + self.fail(f"fail to decode stdout: {out!r}") + + results = out['config']["module_search_paths"] + for (_, expected), result in zip(CASES, results): + self.assertEqual(result, expected) + + def test_global_pathconfig(self): + # Test C API functions getting the path configuration: + # + # - Py_GetExecPrefix() + # - Py_GetPath() + # - Py_GetPrefix() + # - Py_GetProgramFullPath() + # - Py_GetProgramName() + # - Py_GetPythonHome() + # + # The global path configuration (_Py_path_config) must be a copy + # of the path configuration of PyInterpreter.config (PyConfig). + ctypes = import_helper.import_module('ctypes') + + def get_func(name): + func = getattr(ctypes.pythonapi, name) + func.argtypes = () + func.restype = ctypes.c_wchar_p + return func + + Py_GetPath = get_func('Py_GetPath') + Py_GetPrefix = get_func('Py_GetPrefix') + Py_GetExecPrefix = get_func('Py_GetExecPrefix') + Py_GetProgramName = get_func('Py_GetProgramName') + Py_GetProgramFullPath = get_func('Py_GetProgramFullPath') + Py_GetPythonHome = get_func('Py_GetPythonHome') + + config = _testinternalcapi.get_configs()['config'] + + self.assertEqual(Py_GetPath().split(os.path.pathsep), + config['module_search_paths']) + self.assertEqual(Py_GetPrefix(), config['prefix']) + self.assertEqual(Py_GetExecPrefix(), config['exec_prefix']) + self.assertEqual(Py_GetProgramName(), config['program_name']) + self.assertEqual(Py_GetProgramFullPath(), config['executable']) + self.assertEqual(Py_GetPythonHome(), config['home']) + + def test_init_warnoptions(self): + # lowest to highest priority + warnoptions = [ + 'ignore:::PyConfig_Insert0', # PyWideStringList_Insert(0) + 'default', # PyConfig.dev_mode=1 + 'ignore:::env1', # PYTHONWARNINGS env var + 'ignore:::env2', # PYTHONWARNINGS env var + 'ignore:::cmdline1', # -W opt command line option + 'ignore:::cmdline2', # -W opt command line option + 'default::BytesWarning', # PyConfig.bytes_warnings=1 + 'ignore:::PySys_AddWarnOption1', # PySys_AddWarnOption() + 'ignore:::PySys_AddWarnOption2', # PySys_AddWarnOption() + 'ignore:::PyConfig_BeforeRead', # PyConfig.warnoptions + 'ignore:::PyConfig_AfterRead'] # PyWideStringList_Append() + preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG) + config = { + 'dev_mode': 1, + 'faulthandler': 1, + 'bytes_warning': 1, + 'warnoptions': warnoptions, + 'orig_argv': ['python3', + '-Wignore:::cmdline1', + '-Wignore:::cmdline2'], + } + self.check_all_configs("test_init_warnoptions", config, preconfig, + api=API_PYTHON) + + def test_init_set_config(self): + config = { + '_init_main': 0, + 'bytes_warning': 2, + 'warnoptions': ['error::BytesWarning'], + } + self.check_all_configs("test_init_set_config", config, + api=API_ISOLATED) + + def test_get_argc_argv(self): + self.run_embedded_interpreter("test_get_argc_argv") + # ignore output + + def test_init_use_frozen_modules(self): + tests = { + ('=on', True), + ('=off', False), + ('=', True), + ('', True), + } + for raw, expected in tests: + optval = f'frozen_modules{raw}' + config = { + 'parse_argv': True, + 'argv': ['-c'], + 'orig_argv': ['./argv0', '-X', optval, '-c', 'pass'], + 'program_name': './argv0', + 'run_command': 'pass\n', + 'use_environment': True, + 'xoptions': [optval], + 'use_frozen_modules': expected, + } + env = {'TESTFROZEN': raw[1:]} if raw else None + with self.subTest(repr(raw)): + self.check_all_configs("test_init_use_frozen_modules", config, + api=API_PYTHON, env=env) + + def test_init_main_interpreter_settings(self): + OBMALLOC = 1<<5 + EXTENSIONS = 1<<8 + THREADS = 1<<10 + DAEMON_THREADS = 1<<11 + FORK = 1<<15 + EXEC = 1<<16 + expected = { + # All optional features should be enabled. + 'feature_flags': + OBMALLOC | FORK | EXEC | THREADS | DAEMON_THREADS, + 'own_gil': True, + } + out, err = self.run_embedded_interpreter( + 'test_init_main_interpreter_settings', + ) + self.assertEqual(err, '') + try: + out = json.loads(out) + except json.JSONDecodeError: + self.fail(f'fail to decode stdout: {out!r}') + + self.assertEqual(out, expected) + + @threading_helper.requires_working_threading() + def test_init_in_background_thread(self): + # gh-123022: Check that running Py_Initialize() in a background + # thread doesn't crash. + out, err = self.run_embedded_interpreter("test_init_in_background_thread") + self.assertEqual(err, "") + + +class SetConfigTests(unittest.TestCase): + def test_set_config(self): + # bpo-42260: Test _PyInterpreterState_SetConfig() + import_helper.import_module('_testcapi') + cmd = [sys.executable, '-X', 'utf8', '-I', '-m', 'test._test_embed_set_config'] + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding='utf-8', errors='backslashreplace') + if proc.returncode and support.verbose: + print(proc.stdout) + print(proc.stderr) + self.assertEqual(proc.returncode, 0, + (proc.returncode, proc.stdout, proc.stderr)) + + +class AuditingTests(EmbeddingTestsMixin, unittest.TestCase): + def test_open_code_hook(self): + self.run_embedded_interpreter("test_open_code_hook") + + def test_audit(self): + self.run_embedded_interpreter("test_audit") + + def test_audit_tuple(self): + self.run_embedded_interpreter("test_audit_tuple") + + def test_audit_subinterpreter(self): + self.run_embedded_interpreter("test_audit_subinterpreter") + + def test_audit_run_command(self): + self.run_embedded_interpreter("test_audit_run_command", + timeout=support.SHORT_TIMEOUT, + returncode=1) + + def test_audit_run_file(self): + self.run_embedded_interpreter("test_audit_run_file", + timeout=support.SHORT_TIMEOUT, + returncode=1) + + def test_audit_run_interactivehook(self): + startup = os.path.join(self.oldcwd, os_helper.TESTFN) + ".py" + with open(startup, "w", encoding="utf-8") as f: + print("import sys", file=f) + print("sys.__interactivehook__ = lambda: None", file=f) + try: + env = {**remove_python_envvars(), "PYTHONSTARTUP": startup} + self.run_embedded_interpreter("test_audit_run_interactivehook", + timeout=support.SHORT_TIMEOUT, + returncode=10, env=env) + finally: + os.unlink(startup) + + def test_audit_run_startup(self): + startup = os.path.join(self.oldcwd, os_helper.TESTFN) + ".py" + with open(startup, "w", encoding="utf-8") as f: + print("pass", file=f) + try: + env = {**remove_python_envvars(), "PYTHONSTARTUP": startup} + self.run_embedded_interpreter("test_audit_run_startup", + timeout=support.SHORT_TIMEOUT, + returncode=10, env=env) + finally: + os.unlink(startup) + + def test_audit_run_stdin(self): + self.run_embedded_interpreter("test_audit_run_stdin", + timeout=support.SHORT_TIMEOUT, + returncode=1) + + def test_get_incomplete_frame(self): + self.run_embedded_interpreter("test_get_incomplete_frame") + + +class MiscTests(EmbeddingTestsMixin, unittest.TestCase): + def test_unicode_id_init(self): + # bpo-42882: Test that _PyUnicode_FromId() works + # when Python is initialized multiples times. + self.run_embedded_interpreter("test_unicode_id_init") + + # See bpo-44133 + @unittest.skipIf(os.name == 'nt', + 'Py_FrozenMain is not exported on Windows') + @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") + def test_frozenmain(self): + env = dict(os.environ) + env['PYTHONUNBUFFERED'] = '1' + out, err = self.run_embedded_interpreter("test_frozenmain", env=env) + executable = os.path.realpath('./argv0') + expected = textwrap.dedent(f""" + Frozen Hello World + sys.argv ['./argv0', '-E', 'arg1', 'arg2'] + config program_name: ./argv0 + config executable: {executable} + config use_environment: True + config configure_c_stdio: True + config buffered_stdio: False + """).lstrip() + self.assertEqual(out, expected) + + @unittest.skipUnless(support.Py_DEBUG, + '-X showrefcount requires a Python debug build') + def test_no_memleak(self): + # bpo-1635741: Python must release all memory at exit + tests = ( + ('off', 'pass'), + ('on', 'pass'), + ('off', 'import __hello__'), + ('on', 'import __hello__'), + ) + for flag, stmt in tests: + xopt = f"frozen_modules={flag}" + cmd = [sys.executable, "-I", "-X", "showrefcount", "-X", xopt, "-c", stmt] + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True) + self.assertEqual(proc.returncode, 0) + out = proc.stdout.rstrip() + match = re.match(r'^\[(-?\d+) refs, (-?\d+) blocks\]', out) + if not match: + self.fail(f"unexpected output: {out!a}") + refs = int(match.group(1)) + blocks = int(match.group(2)) + with self.subTest(frozen_modules=flag, stmt=stmt): + self.assertEqual(refs, 0, out) + self.assertEqual(blocks, 0, out) + + @unittest.skipUnless(support.Py_DEBUG, + '-X presite requires a Python debug build') + def test_presite(self): + cmd = [sys.executable, "-I", "-X", "presite=test.reperf", "-c", "print('cmd')"] + proc = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + self.assertEqual(proc.returncode, 0) + out = proc.stdout.strip() + self.assertIn("10 times sub", out) + self.assertIn("CPU seconds", out) + self.assertIn("cmd", out) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_except_star.py b/Lib/test/test_except_star.py index 3e0f8caa9b2..6f94dc0129d 100644 --- a/Lib/test/test_except_star.py +++ b/Lib/test/test_except_star.py @@ -1217,4 +1217,4 @@ def test_reraise_unhashable_eg(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Lib/test/test_fileutils.py b/Lib/test/test_fileutils.py new file mode 100644 index 00000000000..ff13498fbfe --- /dev/null +++ b/Lib/test/test_fileutils.py @@ -0,0 +1,30 @@ +# Run tests for functions in Python/fileutils.c. + +import os +import os.path +import unittest +from test.support import import_helper + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testinternalcapi') + + +class PathTests(unittest.TestCase): + + def test_capi_normalize_path(self): + if os.name == 'nt': + raise unittest.SkipTest('Windows has its own helper for this') + else: + from test.test_posixpath import PosixPathTest as posixdata + tests = posixdata.NORMPATH_CASES + for filename, expected in tests: + if not os.path.isabs(filename): + continue + with self.subTest(filename): + result = _testcapi.normalize_path(filename) + self.assertEqual(result, expected, + msg=f'input: {filename!r} expected output: {expected!r}') + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_flufl.py b/Lib/test/test_flufl.py new file mode 100644 index 00000000000..d77e481c81d --- /dev/null +++ b/Lib/test/test_flufl.py @@ -0,0 +1,65 @@ +import __future__ +import unittest + + +class FLUFLTests(unittest.TestCase): + + def test_barry_as_bdfl(self): + code = "from __future__ import barry_as_FLUFL\n2 {0} 3" + compile(code.format('<>'), '', 'exec', + __future__.CO_FUTURE_BARRY_AS_BDFL) + with self.assertRaises(SyntaxError) as cm: + compile(code.format('!='), '', 'exec', + __future__.CO_FUTURE_BARRY_AS_BDFL) + self.assertRegex(str(cm.exception), + "with Barry as BDFL, use '<>' instead of '!='") + self.assertIn('2 != 3', cm.exception.text) + self.assertEqual(cm.exception.filename, '') + + self.assertEqual(cm.exception.lineno, 2) + # The old parser reports the end of the token and the new + # parser reports the start of the token + self.assertEqual(cm.exception.offset, 3) + + def test_guido_as_bdfl(self): + code = '2 {0} 3' + compile(code.format('!='), '', 'exec') + with self.assertRaises(SyntaxError) as cm: + compile(code.format('<>'), '', 'exec') + self.assertRegex(str(cm.exception), "invalid syntax") + self.assertIn('2 <> 3', cm.exception.text) + self.assertEqual(cm.exception.filename, '') + self.assertEqual(cm.exception.lineno, 1) + # The old parser reports the end of the token and the new + # parser reports the start of the token + self.assertEqual(cm.exception.offset, 3) + + def test_barry_as_bdfl_look_ma_with_no_compiler_flags(self): + # Check that the future import is handled by the parser + # even if the compiler flags are not passed. + code = "from __future__ import barry_as_FLUFL;2 {0} 3" + compile(code.format('<>'), '', 'exec') + with self.assertRaises(SyntaxError) as cm: + compile(code.format('!='), '', 'exec') + self.assertRegex(str(cm.exception), "with Barry as BDFL, use '<>' instead of '!='") + self.assertIn('2 != 3', cm.exception.text) + self.assertEqual(cm.exception.filename, '') + self.assertEqual(cm.exception.lineno, 1) + self.assertEqual(cm.exception.offset, len(code) - 4) + + def test_barry_as_bdfl_relative_import(self): + code = "from .__future__ import barry_as_FLUFL;2 {0} 3" + compile(code.format('!='), '', 'exec') + with self.assertRaises(SyntaxError) as cm: + compile(code.format('<>'), '', 'exec') + self.assertRegex(str(cm.exception), "") + self.assertIn('2 <> 3', cm.exception.text) + self.assertEqual(cm.exception.filename, '') + self.assertEqual(cm.exception.lineno, 1) + self.assertEqual(cm.exception.offset, len(code) - 4) + + + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py new file mode 100644 index 00000000000..a6523bbc518 --- /dev/null +++ b/Lib/test/test_fork1.py @@ -0,0 +1,102 @@ +"""This test checks for correct fork() behavior. +""" + +import _imp as imp +import os +import signal +import sys +import threading +import time +import unittest + +from test.fork_wait import ForkWait +from test import support + + +# Skip test if fork does not exist. +if not support.has_fork_support: + raise unittest.SkipTest("test module requires working os.fork") + + +class ForkTest(ForkWait): + def test_threaded_import_lock_fork(self): + """Check fork() in main thread works while a subthread is doing an import""" + import_started = threading.Event() + fake_module_name = "fake test module" + partial_module = "partial" + complete_module = "complete" + def importer(): + imp.acquire_lock() + sys.modules[fake_module_name] = partial_module + import_started.set() + time.sleep(0.01) # Give the other thread time to try and acquire. + sys.modules[fake_module_name] = complete_module + imp.release_lock() + t = threading.Thread(target=importer) + t.start() + import_started.wait() + exitcode = 42 + pid = os.fork() + try: + # PyOS_BeforeFork should have waited for the import to complete + # before forking, so the child can recreate the import lock + # correctly, but also won't see a partially initialised module + if not pid: + m = __import__(fake_module_name) + if m == complete_module: + os._exit(exitcode) + else: + if support.verbose > 1: + print("Child encountered partial module") + os._exit(1) + else: + t.join() + # Exitcode 1 means the child got a partial module (bad.) No + # exitcode (but a hang, which manifests as 'got pid 0') + # means the child deadlocked (also bad.) + self.wait_impl(pid, exitcode=exitcode) + finally: + try: + os.kill(pid, signal.SIGKILL) + except OSError: + pass + + + def test_nested_import_lock_fork(self): + """Check fork() in main thread works while the main thread is doing an import""" + exitcode = 42 + # Issue 9573: this used to trigger RuntimeError in the child process + def fork_with_import_lock(level): + release = 0 + in_child = False + try: + try: + for i in range(level): + imp.acquire_lock() + release += 1 + pid = os.fork() + in_child = not pid + finally: + for i in range(release): + imp.release_lock() + except RuntimeError: + if in_child: + if support.verbose > 1: + print("RuntimeError in child") + os._exit(1) + raise + if in_child: + os._exit(exitcode) + self.wait_impl(pid, exitcode=exitcode) + + # Check this works with various levels of nested + # import in the main thread + for level in range(5): + fork_with_import_lock(level) + + +def tearDownModule(): + support.reap_children() + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py new file mode 100644 index 00000000000..2f802179cb3 --- /dev/null +++ b/Lib/test/test_frame.py @@ -0,0 +1,781 @@ +import copy +import gc +import operator +import re +import sys +import textwrap +import threading +import types +import unittest +import weakref +try: + import _testcapi +except ImportError: + _testcapi = None + +from collections.abc import Mapping +from test import support +from test.support import import_helper, threading_helper, Py_GIL_DISABLED +from test.support.script_helper import assert_python_ok +from test import mapping_tests + + +class ClearTest(unittest.TestCase): + """ + Tests for frame.clear(). + """ + + def inner(self, x=5, **kwargs): + 1/0 + + def outer(self, **kwargs): + try: + self.inner(**kwargs) + except ZeroDivisionError as e: + exc = e + return exc + + def clear_traceback_frames(self, tb): + """ + Clear all frames in a traceback. + """ + while tb is not None: + tb.tb_frame.clear() + tb = tb.tb_next + + def test_clear_locals(self): + class C: + pass + c = C() + wr = weakref.ref(c) + exc = self.outer(c=c) + del c + support.gc_collect() + # A reference to c is held through the frames + self.assertIsNot(None, wr()) + self.clear_traceback_frames(exc.__traceback__) + support.gc_collect() + # The reference was released by .clear() + self.assertIs(None, wr()) + + def test_clear_locals_after_f_locals_access(self): + # see gh-113939 + class C: + pass + + wr = None + def inner(): + nonlocal wr + c = C() + wr = weakref.ref(c) + 1/0 + + try: + inner() + except ZeroDivisionError as exc: + support.gc_collect() + self.assertIsNotNone(wr()) + exc.__traceback__.tb_next.tb_frame.clear() + support.gc_collect() + self.assertIsNone(wr()) + + def test_clear_does_not_clear_specials(self): + class C: + pass + c = C() + exc = self.outer(c=c) + del c + f = exc.__traceback__.tb_frame + f.clear() + self.assertIsNot(f.f_code, None) + self.assertIsNot(f.f_locals, None) + self.assertIsNot(f.f_builtins, None) + self.assertIsNot(f.f_globals, None) + + def test_clear_generator(self): + endly = False + def g(): + nonlocal endly + try: + yield + self.inner() + finally: + endly = True + gen = g() + next(gen) + self.assertFalse(endly) + + # Cannot clear a suspended frame + with self.assertRaisesRegex(RuntimeError, r'suspended frame'): + gen.gi_frame.clear() + self.assertFalse(endly) + + def test_clear_executing(self): + # Attempting to clear an executing frame is forbidden. + try: + 1/0 + except ZeroDivisionError as e: + f = e.__traceback__.tb_frame + with self.assertRaises(RuntimeError): + f.clear() + with self.assertRaises(RuntimeError): + f.f_back.clear() + + def test_clear_executing_generator(self): + # Attempting to clear an executing generator frame is forbidden. + endly = False + def g(): + nonlocal endly + try: + 1/0 + except ZeroDivisionError as e: + f = e.__traceback__.tb_frame + with self.assertRaises(RuntimeError): + f.clear() + with self.assertRaises(RuntimeError): + f.f_back.clear() + yield f + finally: + endly = True + gen = g() + f = next(gen) + self.assertFalse(endly) + # Cannot clear a suspended frame + with self.assertRaisesRegex(RuntimeError, 'suspended frame'): + f.clear() + self.assertFalse(endly) + + def test_lineno_with_tracing(self): + def record_line(): + f = sys._getframe(1) + lines.append(f.f_lineno-f.f_code.co_firstlineno) + + def test(trace): + record_line() + if trace: + sys._getframe(0).f_trace = True + record_line() + record_line() + + expected_lines = [1, 4, 5] + lines = [] + test(False) + self.assertEqual(lines, expected_lines) + lines = [] + test(True) + self.assertEqual(lines, expected_lines) + + @support.cpython_only + def test_clear_refcycles(self): + # .clear() doesn't leave any refcycle behind + with support.disable_gc(): + class C: + pass + c = C() + wr = weakref.ref(c) + exc = self.outer(c=c) + del c + self.assertIsNot(None, wr()) + self.clear_traceback_frames(exc.__traceback__) + self.assertIs(None, wr()) + + +class FrameAttrsTest(unittest.TestCase): + + def make_frames(self): + def outer(): + x = 5 + y = 6 + def inner(): + z = x + 2 + 1/0 + t = 9 + return inner() + try: + outer() + except ZeroDivisionError as e: + tb = e.__traceback__ + frames = [] + while tb: + frames.append(tb.tb_frame) + tb = tb.tb_next + return frames + + def test_clear_locals(self): + # Test f_locals after clear() (issue #21897) + f, outer, inner = self.make_frames() + outer.clear() + inner.clear() + self.assertEqual(outer.f_locals, {}) + self.assertEqual(inner.f_locals, {}) + + def test_locals_clear_locals(self): + # Test f_locals before and after clear() (to exercise caching) + f, outer, inner = self.make_frames() + self.assertNotEqual(outer.f_locals, {}) + self.assertNotEqual(inner.f_locals, {}) + outer.clear() + inner.clear() + self.assertEqual(outer.f_locals, {}) + self.assertEqual(inner.f_locals, {}) + + def test_f_lineno_del_segfault(self): + f, _, _ = self.make_frames() + with self.assertRaises(AttributeError): + del f.f_lineno + + +class ReprTest(unittest.TestCase): + """ + Tests for repr(frame). + """ + + def test_repr(self): + def outer(): + x = 5 + y = 6 + def inner(): + z = x + 2 + 1/0 + t = 9 + return inner() + + offset = outer.__code__.co_firstlineno + try: + outer() + except ZeroDivisionError as e: + tb = e.__traceback__ + frames = [] + while tb: + frames.append(tb.tb_frame) + tb = tb.tb_next + else: + self.fail("should have raised") + + f_this, f_outer, f_inner = frames + file_repr = re.escape(repr(__file__)) + self.assertRegex(repr(f_this), + r"^$" + % (file_repr, offset + 23)) + self.assertRegex(repr(f_outer), + r"^$" + % (file_repr, offset + 7)) + self.assertRegex(repr(f_inner), + r"^$" + % (file_repr, offset + 5)) + +class TestFrameLocals(unittest.TestCase): + def test_scope(self): + class A: + x = 1 + sys._getframe().f_locals['x'] = 2 + sys._getframe().f_locals['y'] = 2 + + self.assertEqual(A.x, 2) + self.assertEqual(A.y, 2) + + def f(): + x = 1 + sys._getframe().f_locals['x'] = 2 + sys._getframe().f_locals['y'] = 2 + self.assertEqual(x, 2) + self.assertEqual(locals()['y'], 2) + f() + + def test_closure(self): + x = 1 + y = 2 + + def f(): + z = x + y + d = sys._getframe().f_locals + self.assertEqual(d['x'], 1) + self.assertEqual(d['y'], 2) + d['x'] = 2 + d['y'] = 3 + + f() + self.assertEqual(x, 2) + self.assertEqual(y, 3) + + def test_closure_with_inline_comprehension(self): + lambda: k + k = 1 + lst = [locals() for k in [0]] + self.assertEqual(lst[0]['k'], 0) + + def test_as_dict(self): + x = 1 + y = 2 + d = sys._getframe().f_locals + # self, x, y, d + self.assertEqual(len(d), 4) + self.assertIs(d['d'], d) + self.assertEqual(set(d.keys()), set(['x', 'y', 'd', 'self'])) + self.assertEqual(len(d.values()), 4) + self.assertIn(1, d.values()) + self.assertEqual(len(d.items()), 4) + self.assertIn(('x', 1), d.items()) + self.assertEqual(d.__getitem__('x'), 1) + d.__setitem__('x', 2) + self.assertEqual(d['x'], 2) + self.assertEqual(d.get('x'), 2) + self.assertIs(d.get('non_exist', None), None) + self.assertEqual(d.__len__(), 4) + self.assertEqual(set([key for key in d]), set(['x', 'y', 'd', 'self'])) + self.assertIn('x', d) + self.assertTrue(d.__contains__('x')) + + self.assertEqual(reversed(d), list(reversed(d.keys()))) + + d.update({'x': 3, 'z': 4}) + self.assertEqual(d['x'], 3) + self.assertEqual(d['z'], 4) + + with self.assertRaises(TypeError): + d.update([1, 2]) + + self.assertEqual(d.setdefault('x', 5), 3) + self.assertEqual(d.setdefault('new', 5), 5) + self.assertEqual(d['new'], 5) + + with self.assertRaises(KeyError): + d['non_exist'] + + def test_as_number(self): + x = 1 + y = 2 + d = sys._getframe().f_locals + self.assertIn('z', d | {'z': 3}) + d |= {'z': 3} + self.assertEqual(d['z'], 3) + d |= {'y': 3} + self.assertEqual(d['y'], 3) + with self.assertRaises(TypeError): + d |= 3 + with self.assertRaises(TypeError): + _ = d | [3] + + def test_non_string_key(self): + d = sys._getframe().f_locals + d[1] = 2 + self.assertEqual(d[1], 2) + + def test_write_with_hidden(self): + def f(): + f_locals = [sys._getframe().f_locals for b in [0]][0] + f_locals['b'] = 2 + f_locals['c'] = 3 + self.assertEqual(b, 2) + self.assertEqual(c, 3) + b = 0 + c = 0 + f() + + def test_local_objects(self): + o = object() + k = '.'.join(['a', 'b', 'c']) + f_locals = sys._getframe().f_locals + f_locals['o'] = f_locals['k'] + self.assertEqual(o, 'a.b.c') + + def test_copy(self): + x = 0 + d = sys._getframe().f_locals + d_copy = d.copy() + self.assertIsInstance(d_copy, dict) + self.assertEqual(d_copy['x'], 0) + d_copy['x'] = 1 + self.assertEqual(x, 0) + + def test_update_with_self(self): + def f(): + f_locals = sys._getframe().f_locals + f_locals.update(f_locals) + f_locals.update(f_locals) + f_locals.update(f_locals) + f() + + def test_repr(self): + x = 1 + # Introduce a reference cycle + frame = sys._getframe() + self.assertEqual(repr(frame.f_locals), repr(dict(frame.f_locals))) + + def test_delete(self): + x = 1 + d = sys._getframe().f_locals + + # This needs to be tested before f_extra_locals is created + with self.assertRaisesRegex(KeyError, 'non_exist'): + del d['non_exist'] + + with self.assertRaises(KeyError): + d.pop('non_exist') + + with self.assertRaisesRegex(ValueError, 'local variables'): + del d['x'] + + with self.assertRaises(AttributeError): + d.clear() + + with self.assertRaises(ValueError): + d.pop('x') + + with self.assertRaises(ValueError): + d.pop('x', None) + + # 'm', 'n' is stored in f_extra_locals + d['m'] = 1 + d['n'] = 1 + + with self.assertRaises(KeyError): + d.pop('non_exist') + + del d['m'] + self.assertEqual(d.pop('n'), 1) + + self.assertNotIn('m', d) + self.assertNotIn('n', d) + + self.assertEqual(d.pop('n', 2), 2) + + @support.cpython_only + def test_sizeof(self): + proxy = sys._getframe().f_locals + support.check_sizeof(self, proxy, support.calcobjsize("P")) + + def test_unsupport(self): + x = 1 + d = sys._getframe().f_locals + with self.assertRaises(TypeError): + copy.copy(d) + + with self.assertRaises(TypeError): + copy.deepcopy(d) + + def test_is_mapping(self): + x = 1 + d = sys._getframe().f_locals + self.assertIsInstance(d, Mapping) + match d: + case {"x": value}: + self.assertEqual(value, 1) + kind = "mapping" + case _: + kind = "other" + self.assertEqual(kind, "mapping") + + def _x_stringlikes(self): + class StringSubclass(str): + pass + + class ImpostorX: + def __hash__(self): + return hash('x') + + def __eq__(self, other): + return other == 'x' + + return StringSubclass('x'), ImpostorX(), 'x' + + def test_proxy_key_stringlikes_overwrite(self): + def f(obj): + x = 1 + proxy = sys._getframe().f_locals + proxy[obj] = 2 + return ( + list(proxy.keys()), + dict(proxy), + proxy + ) + + for obj in self._x_stringlikes(): + with self.subTest(cls=type(obj).__name__): + + keys_snapshot, proxy_snapshot, proxy = f(obj) + expected_keys = ['obj', 'x', 'proxy'] + expected_dict = {'obj': 'x', 'x': 2, 'proxy': proxy} + self.assertEqual(proxy.keys(), expected_keys) + self.assertEqual(proxy, expected_dict) + self.assertEqual(keys_snapshot, expected_keys) + self.assertEqual(proxy_snapshot, expected_dict) + + def test_proxy_key_stringlikes_ftrst_write(self): + def f(obj): + proxy = sys._getframe().f_locals + proxy[obj] = 2 + self.assertEqual(x, 2) + x = 1 + + for obj in self._x_stringlikes(): + with self.subTest(cls=type(obj).__name__): + f(obj) + + def test_proxy_key_unhashables(self): + class StringSubclass(str): + __hash__ = None + + class ObjectSubclass: + __hash__ = None + + proxy = sys._getframe().f_locals + + for obj in StringSubclass('x'), ObjectSubclass(): + with self.subTest(cls=type(obj).__name__): + with self.assertRaises(TypeError): + proxy[obj] + with self.assertRaises(TypeError): + proxy[obj] = 0 + + def test_constructor(self): + FrameLocalsProxy = type([sys._getframe().f_locals + for x in range(1)][0]) + self.assertEqual(FrameLocalsProxy.__name__, 'FrameLocalsProxy') + + def make_frame(): + x = 1 + y = 2 + return sys._getframe() + + proxy = FrameLocalsProxy(make_frame()) + self.assertEqual(proxy, {'x': 1, 'y': 2}) + + # constructor expects 1 frame argument + with self.assertRaises(TypeError): + FrameLocalsProxy() # no arguments + with self.assertRaises(TypeError): + FrameLocalsProxy(123) # wrong type + with self.assertRaises(TypeError): + FrameLocalsProxy(frame=sys._getframe()) # no keyword arguments + + +class FrameLocalsProxyMappingTests(mapping_tests.TestHashMappingProtocol): + """Test that FrameLocalsProxy behaves like a Mapping (with exceptions)""" + + def _f(*args, **kwargs): + def _f(): + return sys._getframe().f_locals + return _f() + type2test = _f + + @unittest.skipIf(True, 'Locals proxies for different frames never compare as equal') + def test_constructor(self): + pass + + @unittest.skipIf(True, 'Unlike a mapping: del proxy[key] fails') + def test_write(self): + pass + + @unittest.skipIf(True, 'Unlike a mapping: no proxy.popitem') + def test_popitem(self): + pass + + @unittest.skipIf(True, 'Unlike a mapping: no proxy.pop') + def test_pop(self): + pass + + @unittest.skipIf(True, 'Unlike a mapping: no proxy.clear') + def test_clear(self): + pass + + @unittest.skipIf(True, 'Unlike a mapping: no proxy.fromkeys') + def test_fromkeys(self): + pass + + # no del + def test_getitem(self): + mapping_tests.BasicTestMappingProtocol.test_getitem(self) + d = self._full_mapping({'a': 1, 'b': 2}) + self.assertEqual(d['a'], 1) + self.assertEqual(d['b'], 2) + d['c'] = 3 + d['a'] = 4 + self.assertEqual(d['c'], 3) + self.assertEqual(d['a'], 4) + + @unittest.skipIf(True, 'Unlike a mapping: no proxy.update') + def test_update(self): + pass + + # proxy.copy returns a regular dict + def test_copy(self): + d = self._full_mapping({1:1, 2:2, 3:3}) + self.assertEqual(d.copy(), {1:1, 2:2, 3:3}) + d = self._empty_mapping() + self.assertEqual(d.copy(), d) + self.assertRaises(TypeError, d.copy, None) + + self.assertIsInstance(d.copy(), dict) + + @unittest.skipIf(True, 'Locals proxies for different frames never compare as equal') + def test_eq(self): + pass + + +class TestFrameCApi(unittest.TestCase): + def test_basic(self): + x = 1 + ctypes = import_helper.import_module('ctypes') + PyEval_GetFrameLocals = ctypes.pythonapi.PyEval_GetFrameLocals + PyEval_GetFrameLocals.restype = ctypes.py_object + frame_locals = PyEval_GetFrameLocals() + self.assertTrue(type(frame_locals), dict) + self.assertEqual(frame_locals['x'], 1) + frame_locals['x'] = 2 + self.assertEqual(x, 1) + + PyEval_GetFrameGlobals = ctypes.pythonapi.PyEval_GetFrameGlobals + PyEval_GetFrameGlobals.restype = ctypes.py_object + frame_globals = PyEval_GetFrameGlobals() + self.assertTrue(type(frame_globals), dict) + self.assertIs(frame_globals, globals()) + + PyEval_GetFrameBuiltins = ctypes.pythonapi.PyEval_GetFrameBuiltins + PyEval_GetFrameBuiltins.restype = ctypes.py_object + frame_builtins = PyEval_GetFrameBuiltins() + self.assertEqual(frame_builtins, __builtins__) + + PyFrame_GetLocals = ctypes.pythonapi.PyFrame_GetLocals + PyFrame_GetLocals.argtypes = [ctypes.py_object] + PyFrame_GetLocals.restype = ctypes.py_object + frame = sys._getframe() + f_locals = PyFrame_GetLocals(frame) + self.assertTrue(f_locals['x'], 1) + f_locals['x'] = 2 + self.assertEqual(x, 2) + + +class TestIncompleteFrameAreInvisible(unittest.TestCase): + + def test_issue95818(self): + # See GH-95818 for details + code = textwrap.dedent(f""" + import gc + + gc.set_threshold(1,1,1) + class GCHello: + def __del__(self): + print("Destroyed from gc") + + def gen(): + yield + + fd = open({__file__!r}) + l = [fd, GCHello()] + l.append(l) + del fd + del l + gen() + """) + assert_python_ok("-c", code) + + @support.cpython_only + @threading_helper.requires_working_threading() + def test_sneaky_frame_object_teardown(self): + + class SneakyDel: + def __del__(self): + """ + Stash a reference to the entire stack for walking later. + + It may look crazy, but you'd be surprised how common this is + when using a test runner (like pytest). The typical recipe is: + ResourceWarning + -Werror + a custom sys.unraisablehook. + """ + nonlocal sneaky_frame_object + sneaky_frame_object = sys._getframe() + + class SneakyThread(threading.Thread): + """ + A separate thread isn't needed to make this code crash, but it does + make crashes more consistent, since it means sneaky_frame_object is + backed by freed memory after the thread completes! + """ + + def run(self): + """Run SneakyDel.__del__ as this frame is popped.""" + ref = SneakyDel() + + sneaky_frame_object = None + t = SneakyThread() + t.start() + t.join() + # sneaky_frame_object can be anything, really, but it's crucial that + # SneakyThread.run's frame isn't anywhere on the stack while it's being + # torn down: + self.assertIsNotNone(sneaky_frame_object) + while sneaky_frame_object is not None: + self.assertIsNot( + sneaky_frame_object.f_code, SneakyThread.run.__code__ + ) + sneaky_frame_object = sneaky_frame_object.f_back + + def test_entry_frames_are_invisible_during_teardown(self): + class C: + """A weakref'able class.""" + + def f(): + """Try to find globals and locals as this frame is being cleared.""" + ref = C() + # Ignore the fact that exec(C()) is a nonsense callback. We're only + # using exec here because it tries to access the current frame's + # globals and locals. If it's trying to get those from a shim frame, + # we'll crash before raising: + return weakref.ref(ref, exec) + + with support.catch_unraisable_exception() as catcher: + # Call from C, so there is a shim frame directly above f: + weak = operator.call(f) # BOOM! + # Cool, we didn't crash. Check that the callback actually happened: + self.assertIs(catcher.unraisable.exc_type, TypeError) + self.assertIsNone(weak()) + +@unittest.skipIf(_testcapi is None, 'need _testcapi') +class TestCAPI(unittest.TestCase): + def getframe(self): + return sys._getframe() + + def test_frame_getters(self): + frame = self.getframe() + self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame)) + self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame)) + self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame)) + self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame)) + + def test_getvar(self): + current_frame = sys._getframe() + x = 1 + self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1) + self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1) + with self.assertRaises(NameError): + _testcapi.frame_getvar(current_frame, "y") + with self.assertRaises(NameError): + _testcapi.frame_getvarstring(current_frame, b"y") + + # wrong name type + with self.assertRaises(TypeError): + _testcapi.frame_getvar(current_frame, b'x') + with self.assertRaises(TypeError): + _testcapi.frame_getvar(current_frame, 123) + + def getgenframe(self): + yield sys._getframe() + + def test_frame_get_generator(self): + gen = self.getgenframe() + frame = next(gen) + self.assertIs(gen, _testcapi.frame_getgenerator(frame)) + + def test_frame_fback_api(self): + """Test that accessing `f_back` does not cause a segmentation fault on + a frame created with `PyFrame_New` (GH-99110).""" + def dummy(): + pass + + frame = _testcapi.frame_new(dummy.__code__, globals(), locals()) + # The following line should not cause a segmentation fault. + self.assertIsNone(frame.f_back) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/__init__.py b/Lib/test/test_free_threading/__init__.py new file mode 100644 index 00000000000..34e1ad30e11 --- /dev/null +++ b/Lib/test/test_free_threading/__init__.py @@ -0,0 +1,11 @@ +import os +import unittest + +from test import support + + +if not support.Py_GIL_DISABLED: + raise unittest.SkipTest("GIL enabled") + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_free_threading/test_bisect.py b/Lib/test/test_free_threading/test_bisect.py new file mode 100644 index 00000000000..b8645d1c8c2 --- /dev/null +++ b/Lib/test/test_free_threading/test_bisect.py @@ -0,0 +1,79 @@ +import unittest +from test.support import import_helper, threading_helper +import random +from threading import Thread, Barrier + +py_bisect = import_helper.import_fresh_module('bisect', blocked=['_bisect']) +c_bisect = import_helper.import_fresh_module('bisect', fresh=['_bisect']) + + +NTHREADS = 4 +OBJECT_COUNT = 500 + + +class TestBase: + def do_racing_insort(self, insert_method): + def insert(data): + for _ in range(OBJECT_COUNT): + x = random.randint(-OBJECT_COUNT, OBJECT_COUNT) + insert_method(data, x) + + data = list(range(OBJECT_COUNT)) + self.run_concurrently( + worker_func=insert, args=(data,), nthreads=NTHREADS + ) + if False: + # These functions are not thread-safe and so the list can become + # unsorted. However, we don't want Python to crash if these + # functions are used concurrently on the same sequence. This + # should also not produce any TSAN warnings. + self.assertTrue(self.is_sorted_ascending(data)) + + def test_racing_insert_right(self): + self.do_racing_insort(self.mod.insort_right) + + def test_racing_insert_left(self): + self.do_racing_insort(self.mod.insort_left) + + @staticmethod + def is_sorted_ascending(lst): + """ + Check if the list is sorted in ascending order (non-decreasing). + """ + return all(lst[i - 1] <= lst[i] for i in range(1, len(lst))) + + def run_concurrently(self, worker_func, args, nthreads): + """ + Run the worker function concurrently in multiple threads. + """ + barrier = Barrier(nthreads) + + def wrapper_func(*args): + # Wait for all threads to reach this point before proceeding. + barrier.wait() + worker_func(*args) + + with threading_helper.catch_threading_exception() as cm: + workers = ( + Thread(target=wrapper_func, args=args) + for _ in range(nthreads) + ) + with threading_helper.start_threads(workers): + pass + + # Worker threads should not raise any exceptions + self.assertIsNone(cm.exc_value) + + +@threading_helper.requires_working_threading() +class TestPyBisect(unittest.TestCase, TestBase): + mod = py_bisect + + +@threading_helper.requires_working_threading() +class TestCBisect(unittest.TestCase, TestBase): + mod = c_bisect + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_code.py b/Lib/test/test_free_threading/test_code.py new file mode 100644 index 00000000000..a5136a3ba4e --- /dev/null +++ b/Lib/test/test_free_threading/test_code.py @@ -0,0 +1,30 @@ +import unittest + +from threading import Thread +from unittest import TestCase + +from test.support import threading_helper + +@threading_helper.requires_working_threading() +class TestCode(TestCase): + def test_code_attrs(self): + """Test concurrent accesses to lazily initialized code attributes""" + code_objects = [] + for _ in range(1000): + code_objects.append(compile("a + b", "", "eval")) + + def run_in_thread(): + for code in code_objects: + self.assertIsInstance(code.co_code, bytes) + self.assertIsInstance(code.co_freevars, tuple) + self.assertIsInstance(code.co_varnames, tuple) + + threads = [Thread(target=run_in_thread) for _ in range(2)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_dict.py b/Lib/test/test_free_threading/test_dict.py new file mode 100644 index 00000000000..af6c8b69743 --- /dev/null +++ b/Lib/test/test_free_threading/test_dict.py @@ -0,0 +1,201 @@ +import gc +import time +import unittest +import weakref + +from ast import Or +from functools import partial +from threading import Thread, Barrier +from unittest import TestCase + +try: + import _testcapi +except ImportError: + _testcapi = None + +from test.support import threading_helper + + +@threading_helper.requires_working_threading() +class TestDict(TestCase): + def test_racing_creation_shared_keys(self): + """Verify that creating dictionaries is thread safe when we + have a type with shared keys""" + class C(int): + pass + + self.racing_creation(C) + + def test_racing_creation_no_shared_keys(self): + """Verify that creating dictionaries is thread safe when we + have a type with an ordinary dict""" + self.racing_creation(Or) + + def test_racing_creation_inline_values_invalid(self): + """Verify that re-creating a dict after we have invalid inline values + is thread safe""" + class C: + pass + + def make_obj(): + a = C() + # Make object, make inline values invalid, and then delete dict + a.__dict__ = {} + del a.__dict__ + return a + + self.racing_creation(make_obj) + + def test_racing_creation_nonmanaged_dict(self): + """Verify that explicit creation of an unmanaged dict is thread safe + outside of the normal attribute setting code path""" + def make_obj(): + def f(): pass + return f + + def set(func, name, val): + # Force creation of the dict via PyObject_GenericGetDict + func.__dict__[name] = val + + self.racing_creation(make_obj, set) + + def racing_creation(self, cls, set=setattr): + objects = [] + processed = [] + + OBJECT_COUNT = 100 + THREAD_COUNT = 10 + CUR = 0 + + for i in range(OBJECT_COUNT): + objects.append(cls()) + + def writer_func(name): + last = -1 + while True: + if CUR == last: + continue + elif CUR == OBJECT_COUNT: + break + + obj = objects[CUR] + set(obj, name, name) + last = CUR + processed.append(name) + + writers = [] + for x in range(THREAD_COUNT): + writer = Thread(target=partial(writer_func, f"a{x:02}")) + writers.append(writer) + writer.start() + + for i in range(OBJECT_COUNT): + CUR = i + while len(processed) != THREAD_COUNT: + time.sleep(0.001) + processed.clear() + + CUR = OBJECT_COUNT + + for writer in writers: + writer.join() + + for obj_idx, obj in enumerate(objects): + assert ( + len(obj.__dict__) == THREAD_COUNT + ), f"{len(obj.__dict__)} {obj.__dict__!r} {obj_idx}" + for i in range(THREAD_COUNT): + assert f"a{i:02}" in obj.__dict__, f"a{i:02} missing at {obj_idx}" + + def test_racing_set_dict(self): + """Races assigning to __dict__ should be thread safe""" + + def f(): pass + l = [] + THREAD_COUNT = 10 + class MyDict(dict): pass + + def writer_func(l): + for i in range(1000): + d = MyDict() + l.append(weakref.ref(d)) + f.__dict__ = d + + lists = [] + writers = [] + for x in range(THREAD_COUNT): + thread_list = [] + lists.append(thread_list) + writer = Thread(target=partial(writer_func, thread_list)) + writers.append(writer) + + for writer in writers: + writer.start() + + for writer in writers: + writer.join() + + f.__dict__ = {} + gc.collect() + + for thread_list in lists: + for ref in thread_list: + self.assertIsNone(ref()) + + def test_getattr_setattr(self): + NUM_THREADS = 10 + b = Barrier(NUM_THREADS) + + def closure(b, c): + b.wait() + for i in range(10): + getattr(c, f'attr_{i}', None) + setattr(c, f'attr_{i}', 99) + + class MyClass: + pass + + o = MyClass() + threads = [Thread(target=closure, args=(b, o)) + for _ in range(NUM_THREADS)] + with threading_helper.start_threads(threads): + pass + + @unittest.skipIf(_testcapi is None, 'need _testcapi module') + def test_dict_version(self): + dict_version = _testcapi.dict_version + THREAD_COUNT = 10 + DICT_COUNT = 10000 + lists = [] + writers = [] + + def writer_func(thread_list): + for i in range(DICT_COUNT): + thread_list.append(dict_version({})) + + for x in range(THREAD_COUNT): + thread_list = [] + lists.append(thread_list) + writer = Thread(target=partial(writer_func, thread_list)) + writers.append(writer) + + for writer in writers: + writer.start() + + for writer in writers: + writer.join() + + total_len = 0 + values = set() + for thread_list in lists: + for v in thread_list: + if v in values: + print('dup', v, (v/4096)%256) + values.add(v) + total_len += len(thread_list) + versions = set(dict_version for thread_list in lists for dict_version in thread_list) + self.assertEqual(len(versions), THREAD_COUNT*DICT_COUNT) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_func_annotations.py b/Lib/test/test_free_threading/test_func_annotations.py new file mode 100644 index 00000000000..1a6461953d4 --- /dev/null +++ b/Lib/test/test_free_threading/test_func_annotations.py @@ -0,0 +1,67 @@ +import concurrent.futures +import unittest +import inspect +from threading import Thread, Barrier +from unittest import TestCase + +from test.support import threading_helper, Py_GIL_DISABLED + +threading_helper.requires_working_threading(module=True) + + +def get_func_annotation(f, b): + b.wait() + return inspect.get_annotations(f) + + +def get_func_annotation_dunder(f, b): + b.wait() + return f.__annotations__ + + +def set_func_annotation(f, b): + b.wait() + f.__annotations__ = {'x': int, 'y': int, 'return': int} + return f.__annotations__ + + +@unittest.skipUnless(Py_GIL_DISABLED, "Enable only in FT build") +class TestFTFuncAnnotations(TestCase): + NUM_THREADS = 8 + + def test_concurrent_read(self): + def f(x: int) -> int: + return x + 1 + + for _ in range(100): + with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: + b = Barrier(self.NUM_THREADS) + futures = {executor.submit(get_func_annotation, f, b): i for i in range(self.NUM_THREADS)} + for fut in concurrent.futures.as_completed(futures): + annotate = fut.result() + self.assertIsNotNone(annotate) + self.assertEqual(annotate, {'x': int, 'return': int}) + + with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: + b = Barrier(self.NUM_THREADS) + futures = {executor.submit(get_func_annotation_dunder, f, b): i for i in range(self.NUM_THREADS)} + for fut in concurrent.futures.as_completed(futures): + annotate = fut.result() + self.assertIsNotNone(annotate) + self.assertEqual(annotate, {'x': int, 'return': int}) + + def test_concurrent_write(self): + def bar(x: int, y: float) -> float: + return y ** x + + for _ in range(100): + with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: + b = Barrier(self.NUM_THREADS) + futures = {executor.submit(set_func_annotation, bar, b): i for i in range(self.NUM_THREADS)} + for fut in concurrent.futures.as_completed(futures): + annotate = fut.result() + self.assertIsNotNone(annotate) + self.assertEqual(annotate, {'x': int, 'y': int, 'return': int}) + + # func_get_annotations returns in-place dict, so bar.__annotations__ should be modified as well + self.assertEqual(bar.__annotations__, {'x': int, 'y': int, 'return': int}) diff --git a/Lib/test/test_free_threading/test_gc.py b/Lib/test/test_free_threading/test_gc.py new file mode 100644 index 00000000000..401067fe9c6 --- /dev/null +++ b/Lib/test/test_free_threading/test_gc.py @@ -0,0 +1,61 @@ +import unittest + +import threading +from threading import Thread +from unittest import TestCase +import gc + +from test.support import threading_helper + + +class MyObj: + pass + + +@threading_helper.requires_working_threading() +class TestGC(TestCase): + def test_get_objects(self): + event = threading.Event() + + def gc_thread(): + for i in range(100): + o = gc.get_objects() + event.set() + + def mutator_thread(): + while not event.is_set(): + o1 = MyObj() + o2 = MyObj() + o3 = MyObj() + o4 = MyObj() + + gcs = [Thread(target=gc_thread)] + mutators = [Thread(target=mutator_thread) for _ in range(4)] + with threading_helper.start_threads(gcs + mutators): + pass + + def test_get_referrers(self): + event = threading.Event() + + obj = MyObj() + + def gc_thread(): + for i in range(100): + o = gc.get_referrers(obj) + event.set() + + def mutator_thread(): + while not event.is_set(): + d1 = { "key": obj } + d2 = { "key": obj } + d3 = { "key": obj } + d4 = { "key": obj } + + gcs = [Thread(target=gc_thread) for _ in range(2)] + mutators = [Thread(target=mutator_thread) for _ in range(4)] + with threading_helper.start_threads(gcs + mutators): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_list.py b/Lib/test/test_free_threading/test_list.py new file mode 100644 index 00000000000..a705161369e --- /dev/null +++ b/Lib/test/test_free_threading/test_list.py @@ -0,0 +1,76 @@ +import unittest + +from threading import Thread +from unittest import TestCase + +from test.support import threading_helper + + +NTHREAD = 10 +OBJECT_COUNT = 5_000 + + +class C: + def __init__(self, v): + self.v = v + + +@threading_helper.requires_working_threading() +class TestList(TestCase): + def test_racing_iter_append(self): + l = [] + + def writer_func(): + for i in range(OBJECT_COUNT): + l.append(C(i + OBJECT_COUNT)) + + def reader_func(): + while True: + count = len(l) + for i, x in enumerate(l): + self.assertEqual(x.v, i + OBJECT_COUNT) + if count == OBJECT_COUNT: + break + + writer = Thread(target=writer_func) + readers = [] + for x in range(NTHREAD): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + + def test_racing_iter_extend(self): + l = [] + + def writer_func(): + for i in range(OBJECT_COUNT): + l.extend([C(i + OBJECT_COUNT)]) + + def reader_func(): + while True: + count = len(l) + for i, x in enumerate(l): + self.assertEqual(x.v, i + OBJECT_COUNT) + if count == OBJECT_COUNT: + break + + writer = Thread(target=writer_func) + readers = [] + for x in range(NTHREAD): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_monitoring.py b/Lib/test/test_free_threading/test_monitoring.py new file mode 100644 index 00000000000..4e8ee2e2ac0 --- /dev/null +++ b/Lib/test/test_free_threading/test_monitoring.py @@ -0,0 +1,295 @@ +"""Tests monitoring, sys.settrace, and sys.setprofile in a multi-threaded +environment to verify things are thread-safe in a free-threaded build""" + +import sys +import threading +import time +import unittest +import weakref + +from sys import monitoring +from test.support import threading_helper +from threading import Thread, _PyRLock, Barrier +from unittest import TestCase + + +class InstrumentationMultiThreadedMixin: + thread_count = 10 + func_count = 50 + fib = 12 + + def after_threads(self): + """Runs once after all the threads have started""" + pass + + def during_threads(self): + """Runs repeatedly while the threads are still running""" + pass + + def work(self, n, funcs): + """Fibonacci function which also calls a bunch of random functions""" + for func in funcs: + func() + if n < 2: + return n + return self.work(n - 1, funcs) + self.work(n - 2, funcs) + + def start_work(self, n, funcs, barrier): + # With the GIL builds we need to make sure that the hooks have + # a chance to run as it's possible to run w/o releasing the GIL. + barrier.wait() + self.work(n, funcs) + + def after_test(self): + """Runs once after the test is done""" + pass + + def test_instrumentation(self): + # Setup a bunch of functions which will need instrumentation... + funcs = [] + for i in range(self.func_count): + x = {} + exec("def f(): pass", x) + funcs.append(x["f"]) + + barrier = Barrier(self.thread_count + 1) + threads = [] + for i in range(self.thread_count): + # Each thread gets a copy of the func list to avoid contention + t = Thread(target=self.start_work, args=(self.fib, list(funcs), barrier)) + t.start() + threads.append(t) + + self.after_threads() + barrier.wait() + + while True: + any_alive = False + for t in threads: + if t.is_alive(): + any_alive = True + break + + if not any_alive: + break + + self.during_threads() + # Sleep to avoid setting monitoring events too rapidly and + # overflowing the global version counter + time.sleep(0.0001) + + self.after_test() + + +class MonitoringTestMixin: + def setUp(self): + for i in range(6): + if monitoring.get_tool(i) is None: + self.tool_id = i + monitoring.use_tool_id(i, self.__class__.__name__) + break + + def tearDown(self): + monitoring.free_tool_id(self.tool_id) + + +@threading_helper.requires_working_threading() +class SetPreTraceMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): + """Sets tracing one time after the threads have started""" + + def setUp(self): + super().setUp() + self.called = False + + def after_test(self): + self.assertTrue(self.called) + + def trace_func(self, frame, event, arg): + self.called = True + return self.trace_func + + def after_threads(self): + sys.settrace(self.trace_func) + + +@threading_helper.requires_working_threading() +class MonitoringMultiThreaded( + MonitoringTestMixin, InstrumentationMultiThreadedMixin, TestCase +): + """Uses sys.monitoring and repeatedly toggles instrumentation on and off""" + + def setUp(self): + super().setUp() + self.set = False + monitoring.register_callback( + self.tool_id, monitoring.events.LINE, self.callback + ) + + def tearDown(self): + monitoring.set_events(self.tool_id, 0) + super().tearDown() + + def callback(self, *args): + pass + + def during_threads(self): + if self.set: + monitoring.set_events( + self.tool_id, monitoring.events.CALL | monitoring.events.LINE + ) + else: + monitoring.set_events(self.tool_id, 0) + self.set = not self.set + + +@threading_helper.requires_working_threading() +class SetTraceMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): + """Uses sys.settrace and repeatedly toggles instrumentation on and off""" + + def setUp(self): + self.set = False + + def tearDown(self): + sys.settrace(None) + + def trace_func(self, frame, event, arg): + return self.trace_func + + def during_threads(self): + if self.set: + sys.settrace(self.trace_func) + else: + sys.settrace(None) + self.set = not self.set + + +@threading_helper.requires_working_threading() +class SetProfileMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): + """Uses sys.setprofile and repeatedly toggles instrumentation on and off""" + + def setUp(self): + self.set = False + + def tearDown(self): + sys.setprofile(None) + + def trace_func(self, frame, event, arg): + return self.trace_func + + def during_threads(self): + if self.set: + sys.setprofile(self.trace_func) + else: + sys.setprofile(None) + self.set = not self.set + + +@threading_helper.requires_working_threading() +class SetProfileAllThreadsMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): + """Uses threading.setprofile_all_threads and repeatedly toggles instrumentation on and off""" + + def setUp(self): + self.set = False + + def tearDown(self): + threading.setprofile_all_threads(None) + + def trace_func(self, frame, event, arg): + return self.trace_func + + def during_threads(self): + if self.set: + threading.setprofile_all_threads(self.trace_func) + else: + threading.setprofile_all_threads(None) + self.set = not self.set + + +@threading_helper.requires_working_threading() +class SetProfileAllMultiThreaded(TestCase): + def test_profile_all_threads(self): + done = threading.Event() + + def func(): + pass + + def bg_thread(): + while not done.is_set(): + func() + func() + func() + func() + func() + + def my_profile(frame, event, arg): + return None + + bg_threads = [] + for i in range(10): + t = threading.Thread(target=bg_thread) + t.start() + bg_threads.append(t) + + for i in range(100): + threading.setprofile_all_threads(my_profile) + threading.setprofile_all_threads(None) + + done.set() + for t in bg_threads: + t.join() + + +@threading_helper.requires_working_threading() +class MonitoringMisc(MonitoringTestMixin, TestCase): + def register_callback(self): + def callback(*args): + pass + + for i in range(200): + monitoring.register_callback(self.tool_id, monitoring.events.LINE, callback) + + self.refs.append(weakref.ref(callback)) + + def test_register_callback(self): + self.refs = [] + threads = [] + for i in range(50): + t = Thread(target=self.register_callback) + t.start() + threads.append(t) + + for thread in threads: + thread.join() + + monitoring.register_callback(self.tool_id, monitoring.events.LINE, None) + for ref in self.refs: + self.assertEqual(ref(), None) + + def test_set_local_trace_opcodes(self): + def trace(frame, event, arg): + frame.f_trace_opcodes = True + return trace + + loops = 1_000 + + sys.settrace(trace) + try: + l = _PyRLock() + + def f(): + for i in range(loops): + with l: + pass + + t = Thread(target=f) + t.start() + for i in range(loops): + with l: + pass + t.join() + finally: + sys.settrace(None) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_set.py b/Lib/test/test_free_threading/test_set.py new file mode 100644 index 00000000000..a66e03bcc4c --- /dev/null +++ b/Lib/test/test_free_threading/test_set.py @@ -0,0 +1,41 @@ +import unittest + +from threading import Thread, Barrier +from unittest import TestCase + +from test.support import threading_helper + + +@threading_helper.requires_working_threading() +class TestSet(TestCase): + def test_repr_clear(self): + """Test repr() of a set while another thread is calling clear()""" + NUM_ITERS = 10 + NUM_REPR_THREADS = 10 + barrier = Barrier(NUM_REPR_THREADS + 1) + s = {1, 2, 3, 4, 5, 6, 7, 8} + + def clear_set(): + barrier.wait() + s.clear() + + def repr_set(): + barrier.wait() + set_reprs.append(repr(s)) + + for _ in range(NUM_ITERS): + set_reprs = [] + threads = [Thread(target=clear_set)] + for _ in range(NUM_REPR_THREADS): + threads.append(Thread(target=repr_set)) + for t in threads: + t.start() + for t in threads: + t.join() + + for set_repr in set_reprs: + self.assertIn(set_repr, ("set()", "{1, 2, 3, 4, 5, 6, 7, 8}")) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_slots.py b/Lib/test/test_free_threading/test_slots.py new file mode 100644 index 00000000000..758f74f54d0 --- /dev/null +++ b/Lib/test/test_free_threading/test_slots.py @@ -0,0 +1,43 @@ +import threading +from test.support import threading_helper +from unittest import TestCase + + +def run_in_threads(targets): + """Run `targets` in separate threads""" + threads = [ + threading.Thread(target=target) + for target in targets + ] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + +@threading_helper.requires_working_threading() +class TestSlots(TestCase): + + def test_object(self): + class Spam: + __slots__ = [ + "eggs", + ] + + def __init__(self, initial_value): + self.eggs = initial_value + + spam = Spam(0) + iters = 20_000 + + def writer(): + for _ in range(iters): + spam.eggs += 1 + + def reader(): + for _ in range(iters): + eggs = spam.eggs + assert type(eggs) is int + assert 0 <= eggs <= iters + + run_in_threads([writer, reader, reader, reader]) diff --git a/Lib/test/test_free_threading/test_str.py b/Lib/test/test_free_threading/test_str.py new file mode 100644 index 00000000000..f5e93b7de0a --- /dev/null +++ b/Lib/test/test_free_threading/test_str.py @@ -0,0 +1,75 @@ +import sys +import unittest + +from itertools import cycle +from threading import Event, Thread +from unittest import TestCase + +from test.support import threading_helper + +@threading_helper.requires_working_threading() +class TestStr(TestCase): + def test_racing_join_extend(self): + '''Test joining a string being extended by another thread''' + l = [] + ITERS = 100 + READERS = 10 + done_event = Event() + def writer_func(): + for i in range(ITERS): + l.extend(map(str, range(i))) + l.clear() + done_event.set() + def reader_func(): + while not done_event.is_set(): + ''.join(l) + writer = Thread(target=writer_func) + readers = [] + for x in range(READERS): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + + def test_racing_join_replace(self): + ''' + Test joining a string of characters being replaced with ephemeral + strings by another thread. + ''' + l = [*'abcdefg'] + MAX_ORDINAL = 1_000 + READERS = 10 + done_event = Event() + + def writer_func(): + for i, c in zip(cycle(range(len(l))), + map(chr, range(128, MAX_ORDINAL))): + l[i] = c + done_event.set() + + def reader_func(): + while not done_event.is_set(): + ''.join(l) + ''.join(l) + ''.join(l) + ''.join(l) + + writer = Thread(target=writer_func) + readers = [] + for x in range(READERS): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_tokenize.py b/Lib/test/test_free_threading/test_tokenize.py new file mode 100644 index 00000000000..860cfec4d71 --- /dev/null +++ b/Lib/test/test_free_threading/test_tokenize.py @@ -0,0 +1,57 @@ +import io +import time +import unittest +import tokenize +from functools import partial +from threading import Thread + +from test.support import threading_helper + + +@threading_helper.requires_working_threading() +class TestTokenize(unittest.TestCase): + def test_tokenizer_iter(self): + source = io.StringIO("for _ in a:\n pass") + it = tokenize._tokenize.TokenizerIter(source.readline, extra_tokens=False) + + tokens = [] + def next_token(it): + while True: + try: + r = next(it) + tokens.append(tokenize.TokenInfo._make(r)) + time.sleep(0.03) + except StopIteration: + return + + threads = [] + for _ in range(5): + threads.append(Thread(target=partial(next_token, it))) + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + expected_tokens = [ + tokenize.TokenInfo(type=1, string='for', start=(1, 0), end=(1, 3), line='for _ in a:\n'), + tokenize.TokenInfo(type=1, string='_', start=(1, 4), end=(1, 5), line='for _ in a:\n'), + tokenize.TokenInfo(type=1, string='in', start=(1, 6), end=(1, 8), line='for _ in a:\n'), + tokenize.TokenInfo(type=1, string='a', start=(1, 9), end=(1, 10), line='for _ in a:\n'), + tokenize.TokenInfo(type=11, string=':', start=(1, 10), end=(1, 11), line='for _ in a:\n'), + tokenize.TokenInfo(type=4, string='', start=(1, 11), end=(1, 11), line='for _ in a:\n'), + tokenize.TokenInfo(type=5, string='', start=(2, -1), end=(2, -1), line=' pass'), + tokenize.TokenInfo(type=1, string='pass', start=(2, 2), end=(2, 6), line=' pass'), + tokenize.TokenInfo(type=4, string='', start=(2, 6), end=(2, 6), line=' pass'), + tokenize.TokenInfo(type=6, string='', start=(2, -1), end=(2, -1), line=' pass'), + tokenize.TokenInfo(type=0, string='', start=(2, -1), end=(2, -1), line=' pass'), + ] + + tokens.sort() + expected_tokens.sort() + self.assertListEqual(tokens, expected_tokens) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_type.py b/Lib/test/test_free_threading/test_type.py new file mode 100644 index 00000000000..d847acfd6f9 --- /dev/null +++ b/Lib/test/test_free_threading/test_type.py @@ -0,0 +1,157 @@ +import threading +import unittest + +from concurrent.futures import ThreadPoolExecutor +from threading import Thread +from unittest import TestCase + +from test import support +from test.support import threading_helper + + + +NTHREADS = 6 +BOTTOM = 0 +TOP = 1000 +ITERS = 100 + +class A: + attr = 1 + +@threading_helper.requires_working_threading() +class TestType(TestCase): + def test_attr_cache(self): + def read(id0): + for _ in range(ITERS): + for _ in range(BOTTOM, TOP): + A.attr + + def write(id0): + for _ in range(ITERS): + for _ in range(BOTTOM, TOP): + # Make _PyType_Lookup cache hot first + A.attr + A.attr + x = A.attr + x += 1 + A.attr = x + + + with ThreadPoolExecutor(NTHREADS) as pool: + pool.submit(read, (1,)) + pool.submit(write, (1,)) + pool.shutdown(wait=True) + + def test_attr_cache_consistency(self): + class C: + x = 0 + + DONE = False + def writer_func(): + for i in range(3000): + C.x + C.x + C.x += 1 + nonlocal DONE + DONE = True + + def reader_func(): + while True: + # We should always see a greater value read from the type than the + # dictionary + a = C.__dict__['x'] + b = C.x + self.assertGreaterEqual(b, a) + + if DONE: + break + + self.run_one(writer_func, reader_func) + + def test_attr_cache_consistency_subclass(self): + class C: + x = 0 + + class D(C): + pass + + DONE = False + def writer_func(): + for i in range(3000): + D.x + D.x + C.x += 1 + nonlocal DONE + DONE = True + + def reader_func(): + while True: + # We should always see a greater value read from the type than the + # dictionary + a = C.__dict__['x'] + b = D.x + self.assertGreaterEqual(b, a) + + if DONE: + break + + self.run_one(writer_func, reader_func) + + def test___class___modification(self): + loops = 200 + + class Foo: + pass + + class Bar: + pass + + thing = Foo() + def work(): + foo = thing + for _ in range(loops): + foo.__class__ = Bar + type(foo) + foo.__class__ = Foo + type(foo) + + + threads = [] + for i in range(NTHREADS): + thread = threading.Thread(target=work) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() + + def test_object_class_change(self): + class Base: + def __init__(self): + self.attr = 123 + class ClassA(Base): + pass + class ClassB(Base): + pass + + obj = ClassA() + # keep reference to __dict__ + d = obj.__dict__ + obj.__class__ = ClassB + + + def run_one(self, writer_func, reader_func): + writer = Thread(target=writer_func) + readers = [] + for x in range(30): + reader = Thread(target=reader_func) + readers.append(reader) + reader.start() + + writer.start() + writer.join() + for reader in readers: + reader.join() + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_frozen.py b/Lib/test/test_frozen.py new file mode 100644 index 00000000000..0b4a12bcf40 --- /dev/null +++ b/Lib/test/test_frozen.py @@ -0,0 +1,56 @@ +"""Basic test of the frozen module (source is in Python/frozen.c).""" + +# The Python/frozen.c source code contains a marshalled Python module +# and therefore depends on the marshal format as well as the bytecode +# format. If those formats have been changed then frozen.c needs to be +# updated. +# +# The test_importlib also tests this module but because those tests +# are much more complicated, it might be unclear why they are failing. +# Invalid marshalled data in frozen.c could case the interpreter to +# crash when __hello__ is imported. + +import importlib.machinery +import sys +import unittest +from test.support import captured_stdout, import_helper + + +class TestFrozen(unittest.TestCase): + def test_frozen(self): + name = '__hello__' + if name in sys.modules: + del sys.modules[name] + with import_helper.frozen_modules(): + import __hello__ + with captured_stdout() as out: + __hello__.main() + self.assertEqual(out.getvalue(), 'Hello world!\n') + + def test_frozen_submodule_in_unfrozen_package(self): + with import_helper.CleanImport('__phello__', '__phello__.spam'): + with import_helper.frozen_modules(enabled=False): + import __phello__ + with import_helper.frozen_modules(enabled=True): + import __phello__.spam as spam + self.assertIs(spam, __phello__.spam) + self.assertIsNot(__phello__.__spec__.loader, + importlib.machinery.FrozenImporter) + self.assertIs(spam.__spec__.loader, + importlib.machinery.FrozenImporter) + + def test_unfrozen_submodule_in_frozen_package(self): + with import_helper.CleanImport('__phello__', '__phello__.spam'): + with import_helper.frozen_modules(enabled=True): + import __phello__ + with import_helper.frozen_modules(enabled=False): + import __phello__.spam as spam + self.assertIs(spam, __phello__.spam) + self.assertIs(__phello__.__spec__.loader, + importlib.machinery.FrozenImporter) + self.assertIsNot(spam.__spec__.loader, + importlib.machinery.FrozenImporter) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_gdb/__init__.py b/Lib/test/test_gdb/__init__.py new file mode 100644 index 00000000000..0dd72178023 --- /dev/null +++ b/Lib/test/test_gdb/__init__.py @@ -0,0 +1,32 @@ +# Verify that gdb can pretty-print the various PyObject* types +# +# The code for testing gdb was adapted from similar work in Unladen Swallow's +# Lib/test/test_jit_gdb.py + +import os +import sysconfig +import unittest +from test import support + + +if support.MS_WINDOWS: + # On Windows, Python is usually built by MSVC. Passing /p:DebugSymbols=true + # option to MSBuild produces PDB debug symbols, but gdb doesn't support PDB + # debug symbol files. + raise unittest.SkipTest("test_gdb doesn't work on Windows") + +if support.PGO: + raise unittest.SkipTest("test_gdb is not useful for PGO") + +if not sysconfig.is_python_build(): + raise unittest.SkipTest("test_gdb only works on source builds at the moment.") + +if support.check_cflags_pgo(): + raise unittest.SkipTest("test_gdb is not reliable on PGO builds") + +if support.check_bolt_optimized(): + raise unittest.SkipTest("test_gdb is not reliable on BOLT optimized builds") + + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_gdb/gdb_sample.py b/Lib/test/test_gdb/gdb_sample.py new file mode 100644 index 00000000000..a7f23db73ea --- /dev/null +++ b/Lib/test/test_gdb/gdb_sample.py @@ -0,0 +1,12 @@ +# Sample script for use by test_gdb + +def foo(a, b, c): + bar(a=a, b=b, c=c) + +def bar(a, b, c): + baz(a, b, c) + +def baz(*args): + id(42) + +foo(1, 2, 3) diff --git a/Lib/test/test_gdb/test_backtrace.py b/Lib/test/test_gdb/test_backtrace.py new file mode 100644 index 00000000000..714853c7b47 --- /dev/null +++ b/Lib/test/test_gdb/test_backtrace.py @@ -0,0 +1,135 @@ +import textwrap +import unittest +from test import support +from test.support import python_is_optimized + +from .util import setup_module, DebuggerTests, CET_PROTECTION, SAMPLE_SCRIPT + + +def setUpModule(): + setup_module() + + +class PyBtTests(DebuggerTests): + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_bt(self): + 'Verify that the "py-bt" command works' + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-bt']) + self.assertMultilineMatches(bt, + r'''^.* +Traceback \(most recent call first\): + + File ".*gdb_sample.py", line 10, in baz + id\(42\) + File ".*gdb_sample.py", line 7, in bar + baz\(a, b, c\) + File ".*gdb_sample.py", line 4, in foo + bar\(a=a, b=b, c=c\) + File ".*gdb_sample.py", line 12, in + foo\(1, 2, 3\) +''') + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_bt_full(self): + 'Verify that the "py-bt-full" command works' + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-bt-full']) + self.assertMultilineMatches(bt, + r'''^.* +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) + baz\(a, b, c\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) + bar\(a=a, b=b, c=c\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in \(\) + foo\(1, 2, 3\) +''') + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + @support.requires_gil_enabled() + @support.requires_resource('cpu') + def test_threads(self): + 'Verify that "py-bt" indicates threads that are waiting for the GIL' + cmd = ''' +from threading import Thread + +class TestThread(Thread): + # These threads would run forever, but we'll interrupt things with the + # debugger + def run(self): + i = 0 + while 1: + i += 1 + +t = {} +for i in range(4): + t[i] = TestThread() + t[i].start() + +# Trigger a breakpoint on the main thread +id(42) + +''' + # Verify with "py-bt": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['thread apply all py-bt']) + self.assertIn('Waiting for the GIL', gdb_output) + + # Verify with "py-bt-full": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['thread apply all py-bt-full']) + self.assertIn('Waiting for the GIL', gdb_output) + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + # Some older versions of gdb will fail with + # "Cannot find new threads: generic error" + # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround + def test_gc(self): + 'Verify that "py-bt" indicates if a thread is garbage-collecting' + cmd = ('from gc import collect\n' + 'id(42)\n' + 'def foo():\n' + ' collect()\n' + 'def bar():\n' + ' foo()\n' + 'bar()\n') + # Verify with "py-bt": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'], + ) + self.assertIn('Garbage-collecting', gdb_output) + + # Verify with "py-bt-full": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'], + ) + self.assertIn('Garbage-collecting', gdb_output) + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_wrapper_call(self): + cmd = textwrap.dedent(''' + class MyList(list): + def __init__(self): + super(*[]).__init__() # wrapper_call() + + id("first break point") + l = MyList() + ''') + cmds_after_breakpoint = ['break wrapper_call', 'continue'] + if CET_PROTECTION: + # bpo-32962: same case as in get_stack_trace(): + # we need an additional 'next' command in order to read + # arguments of the innermost function of the call stack. + cmds_after_breakpoint.append('next') + cmds_after_breakpoint.append('py-bt') + + # Verify with "py-bt": + gdb_output = self.get_stack_trace(cmd, + cmds_after_breakpoint=cmds_after_breakpoint) + self.assertRegex(gdb_output, + r"10 id(42)\n' + ' 11 \n' + ' 12 foo(1, 2, 3)\n', + bt) + + def test_one_abs_arg(self): + 'Verify the "py-list" command with one absolute argument' + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-list 9']) + + self.assertListing(' 9 def baz(*args):\n' + ' >10 id(42)\n' + ' 11 \n' + ' 12 foo(1, 2, 3)\n', + bt) + + def test_two_abs_args(self): + 'Verify the "py-list" command with two absolute arguments' + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-list 1,3']) + + self.assertListing(' 1 # Sample script for use by test_gdb\n' + ' 2 \n' + ' 3 def foo(a, b, c):\n', + bt) + +SAMPLE_WITH_C_CALL = """ + +from _testcapi import pyobject_vectorcall + +def foo(a, b, c): + bar(a, b, c) + +def bar(a, b, c): + pyobject_vectorcall(baz, (a, b, c), None) + +def baz(*args): + id(42) + +foo(1, 2, 3) + +""" + + +class StackNavigationTests(DebuggerTests): + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_pyup_command(self): + 'Verify that the "py-up" command works' + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-up']) + self.assertMultilineMatches(bt, + r'''^.* +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ +$''') + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + def test_down_at_bottom(self): + 'Verify handling of "py-down" at the bottom of the stack' + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-down']) + self.assertEndsWith(bt, + 'Unable to find a newer python frame\n') + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + def test_up_at_top(self): + 'Verify handling of "py-up" at the top of the stack' + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-up'] * 5) + self.assertEndsWith(bt, + 'Unable to find an older python frame\n') + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_up_then_down(self): + 'Verify "py-up" followed by "py-down"' + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) + self.assertMultilineMatches(bt, + r'''^.* +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +$''') + +class PyPrintTests(DebuggerTests): + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_basic_command(self): + 'Verify that the "py-print" command works' + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-print args']) + self.assertMultilineMatches(bt, + r".*\nlocal 'args' = \(1, 2, 3\)\n.*") + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + def test_print_after_up(self): + bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, + cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a']) + self.assertMultilineMatches(bt, + r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*") + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_printing_global(self): + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-up', 'py-print __name__']) + self.assertMultilineMatches(bt, + r".*\nglobal '__name__' = '__main__'\n.*") + + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_printing_builtin(self): + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-up', 'py-print len']) + self.assertMultilineMatches(bt, + r".*\nbuiltin 'len' = \n.*") + +class PyLocalsTests(DebuggerTests): + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_basic_command(self): + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-up', 'py-locals']) + self.assertMultilineMatches(bt, + r".*\nargs = \(1, 2, 3\)\n.*") + + @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") + @unittest.skipIf(python_is_optimized(), + "Python was compiled with optimizations") + def test_locals_after_up(self): + bt = self.get_stack_trace(script=SAMPLE_SCRIPT, + cmds_after_breakpoint=['py-up', 'py-up', 'py-locals']) + self.assertMultilineMatches(bt, + r'''^.* +Locals for foo +a = 1 +b = 2 +c = 3 +Locals for +.*$''') diff --git a/Lib/test/test_gdb/test_pretty_print.py b/Lib/test/test_gdb/test_pretty_print.py new file mode 100644 index 00000000000..dfc77d65ab1 --- /dev/null +++ b/Lib/test/test_gdb/test_pretty_print.py @@ -0,0 +1,438 @@ +import re +import sys +from test import support + +from .util import ( + BREAKPOINT_FN, GDB_VERSION, + run_gdb, setup_module, DebuggerTests) + + +def setUpModule(): + setup_module() + + +class PrettyPrintTests(DebuggerTests): + def get_gdb_repr(self, source, + cmds_after_breakpoint=None, + import_site=False): + # Given an input python source representation of data, + # run "python -c'id(DATA)'" under gdb with a breakpoint on + # builtin_id and scrape out gdb's representation of the "op" + # parameter, and verify that the gdb displays the same string + # + # Verify that the gdb displays the expected string + # + # For a nested structure, the first time we hit the breakpoint will + # give us the top-level structure + + # NOTE: avoid decoding too much of the traceback as some + # undecodable characters may lurk there in optimized mode + # (issue #19743). + cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"] + gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, + cmds_after_breakpoint=cmds_after_breakpoint, + import_site=import_site) + # gdb can insert additional '\n' and space characters in various places + # in its output, depending on the width of the terminal it's connected + # to (using its "wrap_here" function) + m = re.search( + # Match '#0 builtin_id(self=..., v=...)' + r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)' + # Match ' at Python/bltinmodule.c'. + # bpo-38239: builtin_id() is defined in Python/bltinmodule.c, + # but accept any "Directory\file.c" to support Link Time + # Optimization (LTO). + r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c', + gdb_output, re.DOTALL) + if not m: + self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) + return m.group(1), gdb_output + + def test_getting_backtrace(self): + gdb_output = self.get_stack_trace('id(42)') + self.assertTrue(BREAKPOINT_FN in gdb_output) + + def assertGdbRepr(self, val, exp_repr=None): + # Ensure that gdb's rendering of the value in a debugged process + # matches repr(value) in this process: + gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')') + if not exp_repr: + exp_repr = repr(val) + self.assertEqual(gdb_repr, exp_repr, + ('%r did not equal expected %r; full output was:\n%s' + % (gdb_repr, exp_repr, gdb_output))) + + @support.requires_resource('cpu') + def test_int(self): + 'Verify the pretty-printing of various int values' + self.assertGdbRepr(42) + self.assertGdbRepr(0) + self.assertGdbRepr(-7) + self.assertGdbRepr(1000000000000) + self.assertGdbRepr(-1000000000000000) + + def test_singletons(self): + 'Verify the pretty-printing of True, False and None' + self.assertGdbRepr(True) + self.assertGdbRepr(False) + self.assertGdbRepr(None) + + def test_dicts(self): + 'Verify the pretty-printing of dictionaries' + self.assertGdbRepr({}) + self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}") + # Python preserves insertion order since 3.6 + self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}") + + def test_lists(self): + 'Verify the pretty-printing of lists' + self.assertGdbRepr([]) + self.assertGdbRepr(list(range(5))) + + @support.requires_resource('cpu') + def test_bytes(self): + 'Verify the pretty-printing of bytes' + self.assertGdbRepr(b'') + self.assertGdbRepr(b'And now for something hopefully the same') + self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text') + self.assertGdbRepr(b'this is a tab:\t' + b' this is a slash-N:\n' + b' this is a slash-R:\r' + ) + + self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80') + + self.assertGdbRepr(bytes([b for b in range(255)])) + + @support.requires_resource('cpu') + def test_strings(self): + 'Verify the pretty-printing of unicode strings' + # We cannot simply call locale.getpreferredencoding() here, + # as GDB might have been linked against a different version + # of Python with a different encoding and coercion policy + # with respect to PEP 538 and PEP 540. + stdout, stderr = run_gdb( + '--eval-command', + 'python import locale; print(locale.getpreferredencoding())') + + encoding = stdout + if stderr or not encoding: + raise RuntimeError( + f'unable to determine the Python locale preferred encoding ' + f'of embedded Python in GDB\n' + f'stdout={stdout!r}\n' + f'stderr={stderr!r}') + + def check_repr(text): + try: + text.encode(encoding) + except UnicodeEncodeError: + self.assertGdbRepr(text, ascii(text)) + else: + self.assertGdbRepr(text) + + self.assertGdbRepr('') + self.assertGdbRepr('And now for something hopefully the same') + self.assertGdbRepr('string with embedded NUL here \0 and then some more text') + + # Test printing a single character: + # U+2620 SKULL AND CROSSBONES + check_repr('\u2620') + + # Test printing a Japanese unicode string + # (I believe this reads "mojibake", using 3 characters from the CJK + # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE) + check_repr('\u6587\u5b57\u5316\u3051') + + # Test a character outside the BMP: + # U+1D121 MUSICAL SYMBOL C CLEF + # This is: + # UTF-8: 0xF0 0x9D 0x84 0xA1 + # UTF-16: 0xD834 0xDD21 + check_repr(chr(0x1D121)) + + def test_tuples(self): + 'Verify the pretty-printing of tuples' + self.assertGdbRepr(tuple(), '()') + self.assertGdbRepr((1,), '(1,)') + self.assertGdbRepr(('foo', 'bar', 'baz')) + + @support.requires_resource('cpu') + def test_sets(self): + 'Verify the pretty-printing of sets' + if GDB_VERSION < (7, 3): + self.skipTest("pretty-printing of sets needs gdb 7.3 or later") + self.assertGdbRepr(set(), "set()") + self.assertGdbRepr(set(['a']), "{'a'}") + # PYTHONHASHSEED is need to get the exact frozenset item order + if not sys.flags.ignore_environment: + self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}") + self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}") + + # Ensure that we handle sets containing the "dummy" key value, + # which happens on deletion: + gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b']) +s.remove('a') +id(s)''') + self.assertEqual(gdb_repr, "{'b'}") + + @support.requires_resource('cpu') + def test_frozensets(self): + 'Verify the pretty-printing of frozensets' + if GDB_VERSION < (7, 3): + self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later") + self.assertGdbRepr(frozenset(), "frozenset()") + self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})") + # PYTHONHASHSEED is need to get the exact frozenset item order + if not sys.flags.ignore_environment: + self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})") + self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})") + + def test_exceptions(self): + # Test a RuntimeError + gdb_repr, gdb_output = self.get_gdb_repr(''' +try: + raise RuntimeError("I am an error") +except RuntimeError as e: + id(e) +''') + self.assertEqual(gdb_repr, + "RuntimeError('I am an error',)") + + + # Test division by zero: + gdb_repr, gdb_output = self.get_gdb_repr(''' +try: + a = 1 / 0 +except ZeroDivisionError as e: + id(e) +''') + self.assertEqual(gdb_repr, + "ZeroDivisionError('division by zero',)") + + def test_modern_class(self): + 'Verify the pretty-printing of new-style class instances' + gdb_repr, gdb_output = self.get_gdb_repr(''' +class Foo: + pass +foo = Foo() +foo.an_int = 42 +id(foo)''') + m = re.match(r'', gdb_repr) + self.assertTrue(m, + msg='Unexpected new-style class rendering %r' % gdb_repr) + + def test_subclassing_list(self): + 'Verify the pretty-printing of an instance of a list subclass' + gdb_repr, gdb_output = self.get_gdb_repr(''' +class Foo(list): + pass +foo = Foo() +foo += [1, 2, 3] +foo.an_int = 42 +id(foo)''') + m = re.match(r'', gdb_repr) + + self.assertTrue(m, + msg='Unexpected new-style class rendering %r' % gdb_repr) + + def test_subclassing_tuple(self): + 'Verify the pretty-printing of an instance of a tuple subclass' + # This should exercise the negative tp_dictoffset code in the + # new-style class support + gdb_repr, gdb_output = self.get_gdb_repr(''' +class Foo(tuple): + pass +foo = Foo((1, 2, 3)) +foo.an_int = 42 +id(foo)''') + m = re.match(r'', gdb_repr) + + self.assertTrue(m, + msg='Unexpected new-style class rendering %r' % gdb_repr) + + def assertSane(self, source, corruption, exprepr=None): + '''Run Python under gdb, corrupting variables in the inferior process + immediately before taking a backtrace. + + Verify that the variable's representation is the expected failsafe + representation''' + if corruption: + cmds_after_breakpoint=[corruption, 'backtrace'] + else: + cmds_after_breakpoint=['backtrace'] + + gdb_repr, gdb_output = \ + self.get_gdb_repr(source, + cmds_after_breakpoint=cmds_after_breakpoint) + if exprepr: + if gdb_repr == exprepr: + # gdb managed to print the value in spite of the corruption; + # this is good (see http://bugs.python.org/issue8330) + return + + # Match anything for the type name; 0xDEADBEEF could point to + # something arbitrary (see http://bugs.python.org/issue8330) + pattern = '<.* at remote 0x-?[0-9a-f]+>' + + m = re.match(pattern, gdb_repr) + if not m: + self.fail('Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_NULL_ptr(self): + 'Ensure that a NULL PyObject* is handled gracefully' + gdb_repr, gdb_output = ( + self.get_gdb_repr('id(42)', + cmds_after_breakpoint=['set variable v=0', + 'backtrace']) + ) + + self.assertEqual(gdb_repr, '0x0') + + def test_NULL_ob_type(self): + 'Ensure that a PyObject* with NULL ob_type is handled gracefully' + self.assertSane('id(42)', + 'set v->ob_type=0') + + def test_corrupt_ob_type(self): + 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully' + self.assertSane('id(42)', + 'set v->ob_type=0xDEADBEEF', + exprepr='42') + + def test_corrupt_tp_flags(self): + 'Ensure that a PyObject* with a type with corrupt tp_flags is handled' + self.assertSane('id(42)', + 'set v->ob_type->tp_flags=0x0', + exprepr='42') + + def test_corrupt_tp_name(self): + 'Ensure that a PyObject* with a type with corrupt tp_name is handled' + self.assertSane('id(42)', + 'set v->ob_type->tp_name=0xDEADBEEF', + exprepr='42') + + def test_builtins_help(self): + 'Ensure that the new-style class _Helper in site.py can be handled' + + if sys.flags.no_site: + self.skipTest("need site module, but -S option was used") + + # (this was the issue causing tracebacks in + # http://bugs.python.org/issue8032#msg100537 ) + gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True) + + m = re.match(r'<_Helper\(\) at remote 0x-?[0-9a-f]+>', gdb_repr) + self.assertTrue(m, + msg='Unexpected rendering %r' % gdb_repr) + + def test_selfreferential_list(self): + '''Ensure that a reference loop involving a list doesn't lead proxyval + into an infinite loop:''' + gdb_repr, gdb_output = \ + self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)") + self.assertEqual(gdb_repr, '[3, 4, 5, [...]]') + + gdb_repr, gdb_output = \ + self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)") + self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]') + + def test_selfreferential_dict(self): + '''Ensure that a reference loop involving a dict doesn't lead proxyval + into an infinite loop:''' + gdb_repr, gdb_output = \ + self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)") + + self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}") + + def test_selfreferential_old_style_instance(self): + gdb_repr, gdb_output = \ + self.get_gdb_repr(''' +class Foo: + pass +foo = Foo() +foo.an_attr = foo +id(foo)''') + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_selfreferential_new_style_instance(self): + gdb_repr, gdb_output = \ + self.get_gdb_repr(''' +class Foo(object): + pass +foo = Foo() +foo.an_attr = foo +id(foo)''') + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + gdb_repr, gdb_output = \ + self.get_gdb_repr(''' +class Foo(object): + pass +a = Foo() +b = Foo() +a.an_attr = b +b.an_attr = a +id(a)''') + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_truncation(self): + 'Verify that very long output is truncated' + gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))') + self.assertEqual(gdb_repr, + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, " + "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, " + "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, " + "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, " + "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, " + "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, " + "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, " + "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, " + "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, " + "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, " + "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, " + "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, " + "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, " + "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, " + "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, " + "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, " + "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, " + "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, " + "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, " + "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, " + "224, 225, 226...(truncated)") + self.assertEqual(len(gdb_repr), + 1024 + len('...(truncated)')) + + def test_builtin_method(self): + gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)') + self.assertTrue(re.match(r'', + gdb_repr), + 'Unexpected gdb representation: %r\n%s' % \ + (gdb_repr, gdb_output)) + + def test_frames(self): + gdb_output = self.get_stack_trace(''' +import sys +def foo(a, b, c): + return sys._getframe(0) + +f = foo(3, 4, 5) +id(f)''', + breakpoint='builtin_id', + cmds_after_breakpoint=['print (PyFrameObject*)v'] + ) + self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 4, in foo \(a=3.*', + gdb_output, + re.DOTALL), + 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py new file mode 100644 index 00000000000..54c6b2de7cc --- /dev/null +++ b/Lib/test/test_gdb/util.py @@ -0,0 +1,287 @@ +import os +import re +import shlex +import shutil +import subprocess +import sys +import sysconfig +import unittest +from test import support +from test.support.testcase import ExtraAssertions + + +GDB_PROGRAM = shutil.which('gdb') or 'gdb' + +# Location of custom hooks file in a repository checkout. +CHECKOUT_HOOK_PATH = os.path.join(os.path.dirname(sys.executable), + 'python-gdb.py') + +SAMPLE_SCRIPT = os.path.join(os.path.dirname(__file__), 'gdb_sample.py') +BREAKPOINT_FN = 'builtin_id' + +PYTHONHASHSEED = '123' + + +def clean_environment(): + # Remove PYTHON* environment variables such as PYTHONHOME + return {name: value for name, value in os.environ.items() + if not name.startswith('PYTHON')} + + +# Temporary value until it's initialized by get_gdb_version() below +GDB_VERSION = (0, 0) + +def run_gdb(*args, exitcode=0, check=True, **env_vars): + """Runs gdb in --batch mode with the additional arguments given by *args. + + Returns its (stdout, stderr) decoded from utf-8 using the replace handler. + """ + env = clean_environment() + if env_vars: + env.update(env_vars) + + cmd = [GDB_PROGRAM, + # Batch mode: Exit after processing all the command files + # specified with -x/--command + '--batch', + # -nx: Do not execute commands from any .gdbinit initialization + # files (gh-66384) + '-nx'] + if GDB_VERSION >= (7, 4): + cmd.extend(('--init-eval-command', + f'add-auto-load-safe-path {CHECKOUT_HOOK_PATH}')) + cmd.extend(args) + + proc = subprocess.run( + cmd, + # Redirect stdin to prevent gdb from messing with the terminal settings + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf8", errors="backslashreplace", + env=env) + + stdout = proc.stdout + stderr = proc.stderr + if check and proc.returncode != exitcode: + cmd_text = shlex.join(cmd) + raise Exception(f"{cmd_text} failed with exit code {proc.returncode}, " + f"expected exit code {exitcode}:\n" + f"stdout={stdout!r}\n" + f"stderr={stderr!r}") + + return (stdout, stderr) + + +def get_gdb_version(): + try: + stdout, stderr = run_gdb('--version') + except OSError as exc: + # This is what "no gdb" looks like. There may, however, be other + # errors that manifest this way too. + raise unittest.SkipTest(f"Couldn't find gdb program on the path: {exc}") + + # Regex to parse: + # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 + # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9 + # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1 + # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5 + # 'HP gdb 6.7 for HP Itanium (32 or 64 bit) and target HP-UX 11iv2 and 11iv3.\n' -> 6.7 + match = re.search(r"^(?:GNU|HP) gdb.*?\b(\d+)\.(\d+)", stdout) + if match is None: + raise Exception("unable to parse gdb version: %r" % stdout) + version_text = stdout + major = int(match.group(1)) + minor = int(match.group(2)) + version = (major, minor) + return (version_text, version) + +GDB_VERSION_TEXT, GDB_VERSION = get_gdb_version() +if GDB_VERSION < (7, 0): + raise unittest.SkipTest( + f"gdb versions before 7.0 didn't support python embedding. " + f"Saw gdb version {GDB_VERSION[0]}.{GDB_VERSION[1]}:\n" + f"{GDB_VERSION_TEXT}") + + +def check_usable_gdb(): + # Verify that "gdb" was built with the embedded Python support enabled and + # verify that "gdb" can load our custom hooks, as OS security settings may + # disallow this without a customized .gdbinit. + stdout, stderr = run_gdb( + '--eval-command=python import sys; print(sys.version_info)', + '--args', sys.executable, + check=False) + + if "auto-loading has been declined" in stderr: + raise unittest.SkipTest( + f"gdb security settings prevent use of custom hooks; " + f"stderr: {stderr!r}") + + if not stdout: + raise unittest.SkipTest( + f"gdb not built with embedded python support; " + f"stderr: {stderr!r}") + + if "major=2" in stdout: + raise unittest.SkipTest("gdb built with Python 2") + +check_usable_gdb() + + +# Control-flow enforcement technology +def cet_protection(): + cflags = sysconfig.get_config_var('CFLAGS') + if not cflags: + return False + flags = cflags.split() + # True if "-mcet -fcf-protection" options are found, but false + # if "-fcf-protection=none" or "-fcf-protection=return" is found. + return (('-mcet' in flags) + and any((flag.startswith('-fcf-protection') + and not flag.endswith(("=none", "=return"))) + for flag in flags)) +CET_PROTECTION = cet_protection() + + +def setup_module(): + if support.verbose: + print(f"gdb version {GDB_VERSION[0]}.{GDB_VERSION[1]}:") + for line in GDB_VERSION_TEXT.splitlines(): + print(" " * 4 + line) + print(f" path: {GDB_PROGRAM}") + print() + + +class DebuggerTests(unittest.TestCase, ExtraAssertions): + + """Test that the debugger can debug Python.""" + + def get_stack_trace(self, source=None, script=None, + breakpoint=BREAKPOINT_FN, + cmds_after_breakpoint=None, + import_site=False, + ignore_stderr=False): + ''' + Run 'python -c SOURCE' under gdb with a breakpoint. + + Support injecting commands after the breakpoint is reached + + Returns the stdout from gdb + + cmds_after_breakpoint: if provided, a list of strings: gdb commands + ''' + # We use "set breakpoint pending yes" to avoid blocking with a: + # Function "foo" not defined. + # Make breakpoint pending on future shared library load? (y or [n]) + # error, which typically happens python is dynamically linked (the + # breakpoints of interest are to be found in the shared library) + # When this happens, we still get: + # Function "textiowrapper_write" not defined. + # emitted to stderr each time, alas. + + # Initially I had "--eval-command=continue" here, but removed it to + # avoid repeated print breakpoints when traversing hierarchical data + # structures + + # Generate a list of commands in gdb's language: + commands = [ + 'set breakpoint pending yes', + 'break %s' % breakpoint, + + # The tests assume that the first frame of printed + # backtrace will not contain program counter, + # that is however not guaranteed by gdb + # therefore we need to use 'set print address off' to + # make sure the counter is not there. For example: + # #0 in PyObject_Print ... + # is assumed, but sometimes this can be e.g. + # #0 0x00003fffb7dd1798 in PyObject_Print ... + 'set print address off', + + 'run', + ] + + # GDB as of 7.4 onwards can distinguish between the + # value of a variable at entry vs current value: + # http://sourceware.org/gdb/onlinedocs/gdb/Variables.html + # which leads to the selftests failing with errors like this: + # AssertionError: 'v@entry=()' != '()' + # Disable this: + if GDB_VERSION >= (7, 4): + commands += ['set print entry-values no'] + + if cmds_after_breakpoint: + if CET_PROTECTION: + # bpo-32962: When Python is compiled with -mcet + # -fcf-protection, function arguments are unusable before + # running the first instruction of the function entry point. + # The 'next' command makes the required first step. + commands += ['next'] + commands += cmds_after_breakpoint + else: + commands += ['backtrace'] + + # print commands + + # Use "commands" to generate the arguments with which to invoke "gdb": + args = ['--eval-command=%s' % cmd for cmd in commands] + args += ["--args", + sys.executable] + args.extend(subprocess._args_from_interpreter_flags()) + + if not import_site: + # -S suppresses the default 'import site' + args += ["-S"] + + if source: + args += ["-c", source] + elif script: + args += [script] + + # Use "args" to invoke gdb, capturing stdout, stderr: + out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED) + + if not ignore_stderr: + for line in err.splitlines(): + print(line, file=sys.stderr) + + # bpo-34007: Sometimes some versions of the shared libraries that + # are part of the traceback are compiled in optimised mode and the + # Program Counter (PC) is not present, not allowing gdb to walk the + # frames back. When this happens, the Python bindings of gdb raise + # an exception, making the test impossible to succeed. + if "PC not saved" in err: + raise unittest.SkipTest("gdb cannot walk the frame object" + " because the Program Counter is" + " not present") + + # bpo-40019: Skip the test if gdb failed to read debug information + # because the Python binary is optimized. + for pattern in ( + '(frame information optimized out)', + 'Unable to read information on python frame', + + # gh-91960: On Python built with "clang -Og", gdb gets + # "frame=" for _PyEval_EvalFrameDefault() parameter + '(unable to read python frame information)', + + # gh-104736: On Python built with "clang -Og" on ppc64le, + # "py-bt" displays a truncated or not traceback, but "where" + # logs this error message: + 'Backtrace stopped: frame did not save the PC', + + # gh-104736: When "bt" command displays something like: + # "#1 0x0000000000000000 in ?? ()", the traceback is likely + # truncated or wrong. + ' ?? ()', + ): + if pattern in out: + raise unittest.SkipTest(f"{pattern!r} found in gdb output") + + return out + + def assertMultilineMatches(self, actual, pattern): + m = re.match(pattern, actual, re.DOTALL) + if not m: + self.fail(msg='%r did not match %r' % (actual, pattern)) diff --git a/Lib/test/test_generator_stop.py b/Lib/test/test_generator_stop.py new file mode 100644 index 00000000000..bc235ceb00e --- /dev/null +++ b/Lib/test/test_generator_stop.py @@ -0,0 +1,34 @@ +from __future__ import generator_stop + +import unittest + + +class TestPEP479(unittest.TestCase): + def test_stopiteration_wrapping(self): + def f(): + raise StopIteration + def g(): + yield f() + with self.assertRaisesRegex(RuntimeError, + "generator raised StopIteration"): + next(g()) + + def test_stopiteration_wrapping_context(self): + def f(): + raise StopIteration + def g(): + yield f() + + try: + next(g()) + except RuntimeError as exc: + self.assertIs(type(exc.__cause__), StopIteration) + self.assertIs(type(exc.__context__), StopIteration) + self.assertTrue(exc.__suppress_context__) + else: + self.fail('__cause__, __context__, or __suppress_context__ ' + 'were not properly set') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py new file mode 100644 index 00000000000..3d8b7ecc0ec --- /dev/null +++ b/Lib/test/test_idle.py @@ -0,0 +1,25 @@ +import unittest +from test.support.import_helper import import_module +from test.support import check_sanitizer + +if check_sanitizer(address=True, memory=True): + # See gh-90791 for details + raise unittest.SkipTest("Tests involving libX11 can SEGFAULT on ASAN/MSAN builds") + +# Skip test_idle if _tkinter, tkinter, or idlelib are missing. +tk = import_module('tkinter') # Also imports _tkinter. +idlelib = import_module('idlelib') + +# Before importing and executing more of idlelib, +# tell IDLE to avoid changing the environment. +idlelib.testing = True + +# Unittest.main and test.libregrtest.runtest.runtest_inner +# call load_tests, when present here, to discover tests to run. +from idlelib.idle_test import load_tests + +if __name__ == '__main__': + tk.NoDefaultRoot() + unittest.main(exit=False) + tk._support_default_root = True + tk._default_root = None diff --git a/Lib/test/test_interpreters/__init__.py b/Lib/test/test_interpreters/__init__.py new file mode 100644 index 00000000000..e3d189c4efc --- /dev/null +++ b/Lib/test/test_interpreters/__init__.py @@ -0,0 +1,9 @@ +import os +from test.support import load_package_tests, Py_GIL_DISABLED +import unittest + +if Py_GIL_DISABLED: + raise unittest.SkipTest("GIL disabled") + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_interpreters/__main__.py b/Lib/test/test_interpreters/__main__.py new file mode 100644 index 00000000000..40a23a297ec --- /dev/null +++ b/Lib/test/test_interpreters/__main__.py @@ -0,0 +1,4 @@ +from . import load_tests +import unittest + +unittest.main() diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py new file mode 100644 index 00000000000..61beb317c37 --- /dev/null +++ b/Lib/test/test_interpreters/test_api.py @@ -0,0 +1,1672 @@ +import os +import pickle +import sys +from textwrap import dedent, indent +import threading +import types +import unittest + +from test import support +from test.support import import_helper +# Raise SkipTest if subinterpreters not supported. +_interpreters = import_helper.import_module('_interpreters') +from test.support import Py_GIL_DISABLED +from test.support import interpreters +from test.support import force_not_colorized +from test.support.interpreters import ( + InterpreterError, InterpreterNotFoundError, ExecutionFailed, +) +from .utils import ( + _captured_script, _run_output, _running, TestBase, + requires_test_modules, _testinternalcapi, +) + + +WHENCE_STR_UNKNOWN = 'unknown' +WHENCE_STR_RUNTIME = 'runtime init' +WHENCE_STR_LEGACY_CAPI = 'legacy C-API' +WHENCE_STR_CAPI = 'C-API' +WHENCE_STR_XI = 'cross-interpreter C-API' +WHENCE_STR_STDLIB = '_interpreters module' + + +class ModuleTests(TestBase): + + def test_queue_aliases(self): + first = [ + interpreters.create_queue, + interpreters.Queue, + interpreters.QueueEmpty, + interpreters.QueueFull, + ] + second = [ + interpreters.create_queue, + interpreters.Queue, + interpreters.QueueEmpty, + interpreters.QueueFull, + ] + self.assertEqual(second, first) + + +class CreateTests(TestBase): + + def test_in_main(self): + interp = interpreters.create() + self.assertIsInstance(interp, interpreters.Interpreter) + self.assertIn(interp, interpreters.list_all()) + + # GH-126221: Passing an invalid Unicode character used to cause a SystemError + self.assertRaises(UnicodeEncodeError, _interpreters.create, '\udc80') + + def test_in_thread(self): + lock = threading.Lock() + interp = None + def f(): + nonlocal interp + interp = interpreters.create() + lock.acquire() + lock.release() + t = threading.Thread(target=f) + with lock: + t.start() + t.join() + self.assertIn(interp, interpreters.list_all()) + + def test_in_subinterpreter(self): + main, = interpreters.list_all() + interp = interpreters.create() + out = _run_output(interp, dedent(""" + from test.support import interpreters + interp = interpreters.create() + print(interp.id) + """)) + interp2 = interpreters.Interpreter(int(out)) + self.assertEqual(interpreters.list_all(), [main, interp, interp2]) + + def test_after_destroy_all(self): + before = set(interpreters.list_all()) + # Create 3 subinterpreters. + interp_lst = [] + for _ in range(3): + interps = interpreters.create() + interp_lst.append(interps) + # Now destroy them. + for interp in interp_lst: + interp.close() + # Finally, create another. + interp = interpreters.create() + self.assertEqual(set(interpreters.list_all()), before | {interp}) + + def test_after_destroy_some(self): + before = set(interpreters.list_all()) + # Create 3 subinterpreters. + interp1 = interpreters.create() + interp2 = interpreters.create() + interp3 = interpreters.create() + # Now destroy 2 of them. + interp1.close() + interp2.close() + # Finally, create another. + interp = interpreters.create() + self.assertEqual(set(interpreters.list_all()), before | {interp3, interp}) + + +class GetMainTests(TestBase): + + def test_id(self): + main = interpreters.get_main() + self.assertEqual(main.id, 0) + + def test_current(self): + main = interpreters.get_main() + current = interpreters.get_current() + self.assertIs(main, current) + + def test_idempotent(self): + main1 = interpreters.get_main() + main2 = interpreters.get_main() + self.assertIs(main1, main2) + + +class GetCurrentTests(TestBase): + + def test_main(self): + main = interpreters.get_main() + current = interpreters.get_current() + self.assertEqual(current, main) + + def test_subinterpreter(self): + main = interpreters.get_main() + interp = interpreters.create() + out = _run_output(interp, dedent(""" + from test.support import interpreters + cur = interpreters.get_current() + print(cur.id) + """)) + current = interpreters.Interpreter(int(out)) + self.assertEqual(current, interp) + self.assertNotEqual(current, main) + + def test_idempotent(self): + with self.subTest('main'): + cur1 = interpreters.get_current() + cur2 = interpreters.get_current() + self.assertIs(cur1, cur2) + + with self.subTest('subinterpreter'): + interp = interpreters.create() + out = _run_output(interp, dedent(""" + from test.support import interpreters + cur = interpreters.get_current() + print(id(cur)) + cur = interpreters.get_current() + print(id(cur)) + """)) + objid1, objid2 = (int(v) for v in out.splitlines()) + self.assertEqual(objid1, objid2) + + with self.subTest('per-interpreter'): + interp = interpreters.create() + out = _run_output(interp, dedent(""" + from test.support import interpreters + cur = interpreters.get_current() + print(id(cur)) + """)) + id1 = int(out) + id2 = id(interp) + self.assertNotEqual(id1, id2) + + @requires_test_modules + def test_created_with_capi(self): + expected = _testinternalcapi.next_interpreter_id() + text = self.run_temp_from_capi(f""" + import {interpreters.__name__} as interpreters + interp = interpreters.get_current() + print((interp.id, interp.whence)) + """) + interpid, whence = eval(text) + self.assertEqual(interpid, expected) + self.assertEqual(whence, WHENCE_STR_CAPI) + + +class ListAllTests(TestBase): + + def test_initial(self): + interps = interpreters.list_all() + self.assertEqual(1, len(interps)) + + def test_after_creating(self): + main = interpreters.get_current() + first = interpreters.create() + second = interpreters.create() + + ids = [] + for interp in interpreters.list_all(): + ids.append(interp.id) + + self.assertEqual(ids, [main.id, first.id, second.id]) + + def test_after_destroying(self): + main = interpreters.get_current() + first = interpreters.create() + second = interpreters.create() + first.close() + + ids = [] + for interp in interpreters.list_all(): + ids.append(interp.id) + + self.assertEqual(ids, [main.id, second.id]) + + def test_idempotent(self): + main = interpreters.get_current() + first = interpreters.create() + second = interpreters.create() + expected = [main, first, second] + + actual = interpreters.list_all() + + self.assertEqual(actual, expected) + for interp1, interp2 in zip(actual, expected): + self.assertIs(interp1, interp2) + + def test_created_with_capi(self): + mainid, *_ = _interpreters.get_main() + interpid1 = _interpreters.create() + interpid2 = _interpreters.create() + interpid3 = _interpreters.create() + interpid4 = interpid3 + 1 + interpid5 = interpid4 + 1 + expected = [ + (mainid, WHENCE_STR_RUNTIME), + (interpid1, WHENCE_STR_STDLIB), + (interpid2, WHENCE_STR_STDLIB), + (interpid3, WHENCE_STR_STDLIB), + (interpid4, WHENCE_STR_CAPI), + (interpid5, WHENCE_STR_STDLIB), + ] + expected2 = expected[:-2] + text = self.run_temp_from_capi(f""" + import {interpreters.__name__} as interpreters + interp = interpreters.create() + print( + [(i.id, i.whence) for i in interpreters.list_all()]) + """) + res = eval(text) + res2 = [(i.id, i.whence) for i in interpreters.list_all()] + self.assertEqual(res, expected) + self.assertEqual(res2, expected2) + + +class InterpreterObjectTests(TestBase): + + def test_init_int(self): + interpid = interpreters.get_current().id + interp = interpreters.Interpreter(interpid) + self.assertEqual(interp.id, interpid) + + def test_init_interpreter_id(self): + interpid = interpreters.get_current()._id + interp = interpreters.Interpreter(interpid) + self.assertEqual(interp._id, interpid) + + def test_init_unsupported(self): + actualid = interpreters.get_current().id + for interpid in [ + str(actualid), + float(actualid), + object(), + None, + '', + ]: + with self.subTest(repr(interpid)): + with self.assertRaises(TypeError): + interpreters.Interpreter(interpid) + + def test_idempotent(self): + main = interpreters.get_main() + interp = interpreters.Interpreter(main.id) + self.assertIs(interp, main) + + def test_init_does_not_exist(self): + with self.assertRaises(InterpreterNotFoundError): + interpreters.Interpreter(1_000_000) + + def test_init_bad_id(self): + with self.assertRaises(ValueError): + interpreters.Interpreter(-1) + + def test_id_type(self): + main = interpreters.get_main() + current = interpreters.get_current() + interp = interpreters.create() + self.assertIsInstance(main.id, int) + self.assertIsInstance(current.id, int) + self.assertIsInstance(interp.id, int) + + def test_id_readonly(self): + interp = interpreters.create() + with self.assertRaises(AttributeError): + interp.id = 1_000_000 + + def test_whence(self): + main = interpreters.get_main() + interp = interpreters.create() + + with self.subTest('main'): + self.assertEqual(main.whence, WHENCE_STR_RUNTIME) + + with self.subTest('from _interpreters'): + self.assertEqual(interp.whence, WHENCE_STR_STDLIB) + + with self.subTest('from C-API'): + text = self.run_temp_from_capi(f""" + import {interpreters.__name__} as interpreters + interp = interpreters.get_current() + print(repr(interp.whence)) + """) + whence = eval(text) + self.assertEqual(whence, WHENCE_STR_CAPI) + + with self.subTest('readonly'): + for value in [ + None, + WHENCE_STR_UNKNOWN, + WHENCE_STR_RUNTIME, + WHENCE_STR_STDLIB, + WHENCE_STR_CAPI, + ]: + with self.assertRaises(AttributeError): + interp.whence = value + with self.assertRaises(AttributeError): + main.whence = value + + def test_hashable(self): + interp = interpreters.create() + expected = hash(interp.id) + actual = hash(interp) + self.assertEqual(actual, expected) + + def test_equality(self): + interp1 = interpreters.create() + interp2 = interpreters.create() + self.assertEqual(interp1, interp1) + self.assertNotEqual(interp1, interp2) + + def test_pickle(self): + interp = interpreters.create() + data = pickle.dumps(interp) + unpickled = pickle.loads(data) + self.assertEqual(unpickled, interp) + + +class TestInterpreterIsRunning(TestBase): + + def test_main(self): + main = interpreters.get_main() + self.assertTrue(main.is_running()) + + # XXX Is this still true? + @unittest.skip('Fails on FreeBSD') + def test_subinterpreter(self): + interp = interpreters.create() + self.assertFalse(interp.is_running()) + + with _running(interp): + self.assertTrue(interp.is_running()) + self.assertFalse(interp.is_running()) + + def test_finished(self): + r, w = self.pipe() + interp = interpreters.create() + interp.exec(f"""if True: + import os + os.write({w}, b'x') + """) + self.assertFalse(interp.is_running()) + self.assertEqual(os.read(r, 1), b'x') + + def test_from_subinterpreter(self): + interp = interpreters.create() + out = _run_output(interp, dedent(f""" + import _interpreters + if _interpreters.is_running({interp.id}): + print(True) + else: + print(False) + """)) + self.assertEqual(out.strip(), 'True') + + def test_already_destroyed(self): + interp = interpreters.create() + interp.close() + with self.assertRaises(InterpreterNotFoundError): + interp.is_running() + + def test_with_only_background_threads(self): + r_interp, w_interp = self.pipe() + r_thread, w_thread = self.pipe() + + DONE = b'D' + FINISHED = b'F' + + interp = interpreters.create() + interp.exec(f"""if True: + import os + import threading + + def task(): + v = os.read({r_thread}, 1) + assert v == {DONE!r} + os.write({w_interp}, {FINISHED!r}) + t = threading.Thread(target=task) + t.start() + """) + self.assertFalse(interp.is_running()) + + os.write(w_thread, DONE) + interp.exec('t.join()') + self.assertEqual(os.read(r_interp, 1), FINISHED) + + def test_created_with_capi(self): + script = dedent(f""" + import {interpreters.__name__} as interpreters + interp = interpreters.get_current() + print(interp.is_running()) + """) + def parse_results(text): + self.assertNotEqual(text, "") + try: + return eval(text) + except Exception: + raise Exception(repr(text)) + + with self.subTest('running __main__ (from self)'): + with self.interpreter_from_capi() as interpid: + text = self.run_from_capi(interpid, script, main=True) + running = parse_results(text) + self.assertTrue(running) + + with self.subTest('running, but not __main__ (from self)'): + text = self.run_temp_from_capi(script) + running = parse_results(text) + self.assertFalse(running) + + with self.subTest('running __main__ (from other)'): + with self.interpreter_obj_from_capi() as (interp, interpid): + before = interp.is_running() + with self.running_from_capi(interpid, main=True): + during = interp.is_running() + after = interp.is_running() + self.assertFalse(before) + self.assertTrue(during) + self.assertFalse(after) + + with self.subTest('running, but not __main__ (from other)'): + with self.interpreter_obj_from_capi() as (interp, interpid): + before = interp.is_running() + with self.running_from_capi(interpid, main=False): + during = interp.is_running() + after = interp.is_running() + self.assertFalse(before) + self.assertFalse(during) + self.assertFalse(after) + + with self.subTest('not running (from other)'): + with self.interpreter_obj_from_capi() as (interp, _): + running = interp.is_running() + self.assertFalse(running) + + +class TestInterpreterClose(TestBase): + + def test_basic(self): + main = interpreters.get_main() + interp1 = interpreters.create() + interp2 = interpreters.create() + interp3 = interpreters.create() + self.assertEqual(set(interpreters.list_all()), + {main, interp1, interp2, interp3}) + interp2.close() + self.assertEqual(set(interpreters.list_all()), + {main, interp1, interp3}) + + def test_all(self): + before = set(interpreters.list_all()) + interps = set() + for _ in range(3): + interp = interpreters.create() + interps.add(interp) + self.assertEqual(set(interpreters.list_all()), before | interps) + for interp in interps: + interp.close() + self.assertEqual(set(interpreters.list_all()), before) + + def test_main(self): + main, = interpreters.list_all() + with self.assertRaises(InterpreterError): + main.close() + + def f(): + with self.assertRaises(InterpreterError): + main.close() + + t = threading.Thread(target=f) + t.start() + t.join() + + def test_already_destroyed(self): + interp = interpreters.create() + interp.close() + with self.assertRaises(InterpreterNotFoundError): + interp.close() + + def test_from_current(self): + main, = interpreters.list_all() + interp = interpreters.create() + out = _run_output(interp, dedent(f""" + from test.support import interpreters + interp = interpreters.Interpreter({interp.id}) + try: + interp.close() + except interpreters.InterpreterError: + print('failed') + """)) + self.assertEqual(out.strip(), 'failed') + self.assertEqual(set(interpreters.list_all()), {main, interp}) + + def test_from_sibling(self): + main, = interpreters.list_all() + interp1 = interpreters.create() + interp2 = interpreters.create() + self.assertEqual(set(interpreters.list_all()), + {main, interp1, interp2}) + interp1.exec(dedent(f""" + from test.support import interpreters + interp2 = interpreters.Interpreter({interp2.id}) + interp2.close() + interp3 = interpreters.create() + interp3.close() + """)) + self.assertEqual(set(interpreters.list_all()), {main, interp1}) + + def test_from_other_thread(self): + interp = interpreters.create() + def f(): + interp.close() + + t = threading.Thread(target=f) + t.start() + t.join() + + # XXX Is this still true? + @unittest.skip('Fails on FreeBSD') + def test_still_running(self): + main, = interpreters.list_all() + interp = interpreters.create() + with _running(interp): + with self.assertRaises(InterpreterError): + interp.close() + self.assertTrue(interp.is_running()) + + def test_subthreads_still_running(self): + r_interp, w_interp = self.pipe() + r_thread, w_thread = self.pipe() + + FINISHED = b'F' + + interp = interpreters.create() + interp.exec(f"""if True: + import os + import threading + import time + + done = False + + def notify_fini(): + global done + done = True + t.join() + threading._register_atexit(notify_fini) + + def task(): + while not done: + time.sleep(0.1) + os.write({w_interp}, {FINISHED!r}) + t = threading.Thread(target=task) + t.start() + """) + interp.close() + + self.assertEqual(os.read(r_interp, 1), FINISHED) + + def test_created_with_capi(self): + script = dedent(f""" + import {interpreters.__name__} as interpreters + interp = interpreters.get_current() + interp.close() + """) + + with self.subTest('running __main__ (from self)'): + with self.interpreter_from_capi() as interpid: + with self.assertRaisesRegex(ExecutionFailed, + 'InterpreterError.*unrecognized'): + self.run_from_capi(interpid, script, main=True) + + with self.subTest('running, but not __main__ (from self)'): + with self.assertRaisesRegex(ExecutionFailed, + 'InterpreterError.*unrecognized'): + self.run_temp_from_capi(script) + + with self.subTest('running __main__ (from other)'): + with self.interpreter_obj_from_capi() as (interp, interpid): + with self.running_from_capi(interpid, main=True): + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + interp.close() + # Make sure it wssn't closed. + self.assertTrue( + self.interp_exists(interpid)) + + # The rest would be skipped until we deal with running threads when + # interp.close() is called. However, the "whence" restrictions + # trigger first. + + with self.subTest('running, but not __main__ (from other)'): + with self.interpreter_obj_from_capi() as (interp, interpid): + with self.running_from_capi(interpid, main=False): + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + interp.close() + # Make sure it wssn't closed. + self.assertTrue( + self.interp_exists(interpid)) + + with self.subTest('not running (from other)'): + with self.interpreter_obj_from_capi() as (interp, interpid): + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + interp.close() + self.assertTrue( + self.interp_exists(interpid)) + + +class TestInterpreterPrepareMain(TestBase): + + def test_empty(self): + interp = interpreters.create() + with self.assertRaises(ValueError): + interp.prepare_main() + + def test_dict(self): + values = {'spam': 42, 'eggs': 'ham'} + interp = interpreters.create() + interp.prepare_main(values) + out = _run_output(interp, dedent(""" + print(spam, eggs) + """)) + self.assertEqual(out.strip(), '42 ham') + + def test_tuple(self): + values = {'spam': 42, 'eggs': 'ham'} + values = tuple(values.items()) + interp = interpreters.create() + interp.prepare_main(values) + out = _run_output(interp, dedent(""" + print(spam, eggs) + """)) + self.assertEqual(out.strip(), '42 ham') + + def test_kwargs(self): + values = {'spam': 42, 'eggs': 'ham'} + interp = interpreters.create() + interp.prepare_main(**values) + out = _run_output(interp, dedent(""" + print(spam, eggs) + """)) + self.assertEqual(out.strip(), '42 ham') + + def test_dict_and_kwargs(self): + values = {'spam': 42, 'eggs': 'ham'} + interp = interpreters.create() + interp.prepare_main(values, foo='bar') + out = _run_output(interp, dedent(""" + print(spam, eggs, foo) + """)) + self.assertEqual(out.strip(), '42 ham bar') + + def test_not_shareable(self): + interp = interpreters.create() + # XXX TypeError? + with self.assertRaises(ValueError): + interp.prepare_main(spam={'spam': 'eggs', 'foo': 'bar'}) + + # Make sure neither was actually bound. + with self.assertRaises(ExecutionFailed): + interp.exec('print(foo)') + with self.assertRaises(ExecutionFailed): + interp.exec('print(spam)') + + def test_running(self): + interp = interpreters.create() + interp.prepare_main({'spam': True}) + with self.running(interp): + with self.assertRaisesRegex(InterpreterError, 'running'): + interp.prepare_main({'spam': False}) + interp.exec('assert spam is True') + + @requires_test_modules + def test_created_with_capi(self): + with self.interpreter_obj_from_capi() as (interp, interpid): + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + interp.prepare_main({'spam': True}) + with self.assertRaisesRegex(ExecutionFailed, 'NameError'): + self.run_from_capi(interpid, 'assert spam is True') + + +class TestInterpreterExec(TestBase): + + def test_success(self): + interp = interpreters.create() + script, results = _captured_script('print("it worked!", end="")') + with results: + interp.exec(script) + results = results.final() + results.raise_if_failed() + out = results.stdout + + self.assertEqual(out, 'it worked!') + + def test_failure(self): + interp = interpreters.create() + with self.assertRaises(ExecutionFailed): + interp.exec('raise Exception') + + @force_not_colorized + def test_display_preserved_exception(self): + tempdir = self.temp_dir() + modfile = self.make_module('spam', tempdir, text=""" + def ham(): + raise RuntimeError('uh-oh!') + + def eggs(): + ham() + """) + scriptfile = self.make_script('script.py', tempdir, text=""" + from test.support import interpreters + + def script(): + import spam + spam.eggs() + + interp = interpreters.create() + interp.exec(script) + """) + + stdout, stderr = self.assert_python_failure(scriptfile) + self.maxDiff = None + interpmod_line, = (l for l in stderr.splitlines() if ' exec' in l) + # File "{interpreters.__file__}", line 179, in exec + self.assertEqual(stderr, dedent(f"""\ + Traceback (most recent call last): + File "{scriptfile}", line 9, in + interp.exec(script) + ~~~~~~~~~~~^^^^^^^^ + {interpmod_line.strip()} + raise ExecutionFailed(excinfo) + test.support.interpreters.ExecutionFailed: RuntimeError: uh-oh! + + Uncaught in the interpreter: + + Traceback (most recent call last): + File "{scriptfile}", line 6, in script + spam.eggs() + ~~~~~~~~~^^ + File "{modfile}", line 6, in eggs + ham() + ~~~^^ + File "{modfile}", line 3, in ham + raise RuntimeError('uh-oh!') + RuntimeError: uh-oh! + """)) + self.assertEqual(stdout, '') + + def test_in_thread(self): + interp = interpreters.create() + script, results = _captured_script('print("it worked!", end="")') + with results: + def f(): + interp.exec(script) + + t = threading.Thread(target=f) + t.start() + t.join() + results = results.final() + results.raise_if_failed() + out = results.stdout + + self.assertEqual(out, 'it worked!') + + @support.requires_fork() + def test_fork(self): + interp = interpreters.create() + import tempfile + with tempfile.NamedTemporaryFile('w+', encoding='utf-8') as file: + file.write('') + file.flush() + + expected = 'spam spam spam spam spam' + script = dedent(f""" + import os + try: + os.fork() + except RuntimeError: + with open('{file.name}', 'w', encoding='utf-8') as out: + out.write('{expected}') + """) + interp.exec(script) + + file.seek(0) + content = file.read() + self.assertEqual(content, expected) + + # XXX Is this still true? + @unittest.skip('Fails on FreeBSD') + def test_already_running(self): + interp = interpreters.create() + with _running(interp): + with self.assertRaises(RuntimeError): + interp.exec('print("spam")') + + def test_bad_script(self): + interp = interpreters.create() + with self.assertRaises(TypeError): + interp.exec(10) + + def test_bytes_for_script(self): + interp = interpreters.create() + with self.assertRaises(TypeError): + interp.exec(b'print("spam")') + + def test_with_background_threads_still_running(self): + r_interp, w_interp = self.pipe() + r_thread, w_thread = self.pipe() + + RAN = b'R' + DONE = b'D' + FINISHED = b'F' + + interp = interpreters.create() + interp.exec(f"""if True: + import os + import threading + + def task(): + v = os.read({r_thread}, 1) + assert v == {DONE!r} + os.write({w_interp}, {FINISHED!r}) + t = threading.Thread(target=task) + t.start() + os.write({w_interp}, {RAN!r}) + """) + interp.exec(f"""if True: + os.write({w_interp}, {RAN!r}) + """) + + os.write(w_thread, DONE) + interp.exec('t.join()') + self.assertEqual(os.read(r_interp, 1), RAN) + self.assertEqual(os.read(r_interp, 1), RAN) + self.assertEqual(os.read(r_interp, 1), FINISHED) + + def test_created_with_capi(self): + with self.interpreter_obj_from_capi() as (interp, _): + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + interp.exec('raise Exception("it worked!")') + + # test__interpreters covers the remaining + # Interpreter.exec() behavior. + + +def call_func_noop(): + pass + + +def call_func_return_shareable(): + return (1, None) + + +def call_func_return_not_shareable(): + return [1, 2, 3] + + +def call_func_failure(): + raise Exception('spam!') + + +def call_func_ident(value): + return value + + +def get_call_func_closure(value): + def call_func_closure(): + return value + return call_func_closure + + +class Spam: + + @staticmethod + def noop(): + pass + + @classmethod + def from_values(cls, *values): + return cls(values) + + def __init__(self, value): + self.value = value + + def __call__(self, *args, **kwargs): + return (self.value, args, kwargs) + + def __eq__(self, other): + if not isinstance(other, Spam): + return NotImplemented + return self.value == other.value + + def run(self, *args, **kwargs): + return (self.value, args, kwargs) + + +def call_func_complex(op, /, value=None, *args, exc=None, **kwargs): + if exc is not None: + raise exc + if op == '': + raise ValueError('missing op') + elif op == 'ident': + if args or kwargs: + raise Exception((args, kwargs)) + return value + elif op == 'full-ident': + return (value, args, kwargs) + elif op == 'globals': + if value is not None or args or kwargs: + raise Exception((value, args, kwargs)) + return __name__ + elif op == 'interpid': + if value is not None or args or kwargs: + raise Exception((value, args, kwargs)) + return interpreters.get_current().id + elif op == 'closure': + if args or kwargs: + raise Exception((args, kwargs)) + return get_call_func_closure(value) + elif op == 'custom': + if args or kwargs: + raise Exception((args, kwargs)) + return Spam(value) + elif op == 'custom-inner': + if args or kwargs: + raise Exception((args, kwargs)) + class Eggs(Spam): + pass + return Eggs(value) + elif not isinstance(op, str): + raise TypeError(op) + else: + raise NotImplementedError(op) + + +class TestInterpreterCall(TestBase): + + # signature + # - blank + # - args + # - kwargs + # - args, kwargs + # return + # - nothing (None) + # - simple + # - closure + # - custom + # ops: + # - do nothing + # - fail + # - echo + # - do complex, relative to interpreter + # scope + # - global func + # - local closure + # - returned closure + # - callable type instance + # - type + # - classmethod + # - staticmethod + # - instance method + # exception + # - builtin + # - custom + # - preserves info (e.g. SyntaxError) + # - matching error display + + def test_call(self): + interp = interpreters.create() + + for i, (callable, args, kwargs) in enumerate([ + (call_func_noop, (), {}), + (call_func_return_shareable, (), {}), + (call_func_return_not_shareable, (), {}), + (Spam.noop, (), {}), + ]): + with self.subTest(f'success case #{i+1}'): + res = interp.call(callable) + self.assertIs(res, None) + + for i, (callable, args, kwargs) in enumerate([ + (call_func_ident, ('spamspamspam',), {}), + (get_call_func_closure, (42,), {}), + (get_call_func_closure(42), (), {}), + (Spam.from_values, (), {}), + (Spam.from_values, (1, 2, 3), {}), + (Spam, ('???'), {}), + (Spam(101), (), {}), + (Spam(10101).run, (), {}), + (call_func_complex, ('ident', 'spam'), {}), + (call_func_complex, ('full-ident', 'spam'), {}), + (call_func_complex, ('full-ident', 'spam', 'ham'), {'eggs': '!!!'}), + (call_func_complex, ('globals',), {}), + (call_func_complex, ('interpid',), {}), + (call_func_complex, ('closure',), {'value': '~~~'}), + (call_func_complex, ('custom', 'spam!'), {}), + (call_func_complex, ('custom-inner', 'eggs!'), {}), + (call_func_complex, ('???',), {'exc': ValueError('spam')}), + ]): + with self.subTest(f'invalid case #{i+1}'): + with self.assertRaises(Exception): + if args or kwargs: + raise Exception((args, kwargs)) + interp.call(callable) + + with self.assertRaises(ExecutionFailed): + interp.call(call_func_failure) + + def test_call_in_thread(self): + interp = interpreters.create() + + for i, (callable, args, kwargs) in enumerate([ + (call_func_noop, (), {}), + (call_func_return_shareable, (), {}), + (call_func_return_not_shareable, (), {}), + (Spam.noop, (), {}), + ]): + with self.subTest(f'success case #{i+1}'): + with self.captured_thread_exception() as ctx: + t = interp.call_in_thread(callable) + t.join() + self.assertIsNone(ctx.caught) + + for i, (callable, args, kwargs) in enumerate([ + (call_func_ident, ('spamspamspam',), {}), + (get_call_func_closure, (42,), {}), + (get_call_func_closure(42), (), {}), + (Spam.from_values, (), {}), + (Spam.from_values, (1, 2, 3), {}), + (Spam, ('???'), {}), + (Spam(101), (), {}), + (Spam(10101).run, (), {}), + (call_func_complex, ('ident', 'spam'), {}), + (call_func_complex, ('full-ident', 'spam'), {}), + (call_func_complex, ('full-ident', 'spam', 'ham'), {'eggs': '!!!'}), + (call_func_complex, ('globals',), {}), + (call_func_complex, ('interpid',), {}), + (call_func_complex, ('closure',), {'value': '~~~'}), + (call_func_complex, ('custom', 'spam!'), {}), + (call_func_complex, ('custom-inner', 'eggs!'), {}), + (call_func_complex, ('???',), {'exc': ValueError('spam')}), + ]): + with self.subTest(f'invalid case #{i+1}'): + if args or kwargs: + continue + with self.captured_thread_exception() as ctx: + t = interp.call_in_thread(callable) + t.join() + self.assertIsNotNone(ctx.caught) + + with self.captured_thread_exception() as ctx: + t = interp.call_in_thread(call_func_failure) + t.join() + self.assertIsNotNone(ctx.caught) + + +class TestIsShareable(TestBase): + + def test_default_shareables(self): + shareables = [ + # singletons + None, + # builtin objects + b'spam', + 'spam', + 10, + -10, + True, + False, + 100.0, + (), + (1, ('spam', 'eggs'), True), + ] + for obj in shareables: + with self.subTest(obj): + shareable = interpreters.is_shareable(obj) + self.assertTrue(shareable) + + def test_not_shareable(self): + class Cheese: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + + class SubBytes(bytes): + """A subclass of a shareable type.""" + + not_shareables = [ + # singletons + NotImplemented, + ..., + # builtin types and objects + type, + object, + object(), + Exception(), + # user-defined types and objects + Cheese, + Cheese('Wensleydale'), + SubBytes(b'spam'), + ] + for obj in not_shareables: + with self.subTest(repr(obj)): + self.assertFalse( + interpreters.is_shareable(obj)) + + +class LowLevelTests(TestBase): + + # The behaviors in the low-level module are important in as much + # as they are exercised by the high-level module. Therefore the + # most important testing happens in the high-level tests. + # These low-level tests cover corner cases that are not + # encountered by the high-level module, thus they + # mostly shouldn't matter as much. + + def test_new_config(self): + # This test overlaps with + # test.test_capi.test_misc.InterpreterConfigTests. + + default = _interpreters.new_config('isolated') + with self.subTest('no arg'): + config = _interpreters.new_config() + self.assert_ns_equal(config, default) + self.assertIsNot(config, default) + + with self.subTest('default'): + config1 = _interpreters.new_config('default') + self.assert_ns_equal(config1, default) + self.assertIsNot(config1, default) + + config2 = _interpreters.new_config('default') + self.assert_ns_equal(config2, config1) + self.assertIsNot(config2, config1) + + for arg in ['', 'default']: + with self.subTest(f'default ({arg!r})'): + config = _interpreters.new_config(arg) + self.assert_ns_equal(config, default) + self.assertIsNot(config, default) + + supported = { + 'isolated': types.SimpleNamespace( + use_main_obmalloc=False, + allow_fork=False, + allow_exec=False, + allow_threads=True, + allow_daemon_threads=False, + check_multi_interp_extensions=True, + gil='own', + ), + 'legacy': types.SimpleNamespace( + use_main_obmalloc=True, + allow_fork=True, + allow_exec=True, + allow_threads=True, + allow_daemon_threads=True, + check_multi_interp_extensions=bool(Py_GIL_DISABLED), + gil='shared', + ), + 'empty': types.SimpleNamespace( + use_main_obmalloc=False, + allow_fork=False, + allow_exec=False, + allow_threads=False, + allow_daemon_threads=False, + check_multi_interp_extensions=False, + gil='default', + ), + } + gil_supported = ['default', 'shared', 'own'] + + for name, vanilla in supported.items(): + with self.subTest(f'supported ({name})'): + expected = vanilla + config1 = _interpreters.new_config(name) + self.assert_ns_equal(config1, expected) + self.assertIsNot(config1, expected) + + config2 = _interpreters.new_config(name) + self.assert_ns_equal(config2, config1) + self.assertIsNot(config2, config1) + + with self.subTest(f'noop override ({name})'): + expected = vanilla + overrides = vars(vanilla) + config = _interpreters.new_config(name, **overrides) + self.assert_ns_equal(config, expected) + + with self.subTest(f'override all ({name})'): + overrides = {k: not v for k, v in vars(vanilla).items()} + for gil in gil_supported: + if vanilla.gil == gil: + continue + overrides['gil'] = gil + expected = types.SimpleNamespace(**overrides) + config = _interpreters.new_config(name, **overrides) + self.assert_ns_equal(config, expected) + + # Override individual fields. + for field, old in vars(vanilla).items(): + if field == 'gil': + values = [v for v in gil_supported if v != old] + else: + values = [not old] + for val in values: + with self.subTest(f'{name}.{field} ({old!r} -> {val!r})'): + overrides = {field: val} + expected = types.SimpleNamespace( + **dict(vars(vanilla), **overrides), + ) + config = _interpreters.new_config(name, **overrides) + self.assert_ns_equal(config, expected) + + with self.subTest('extra override'): + with self.assertRaises(ValueError): + _interpreters.new_config(spam=True) + + # Bad values for bool fields. + for field, value in vars(supported['empty']).items(): + if field == 'gil': + continue + assert isinstance(value, bool) + for value in [1, '', 'spam', 1.0, None, object()]: + with self.subTest(f'bad override ({field}={value!r})'): + with self.assertRaises(TypeError): + _interpreters.new_config(**{field: value}) + + # Bad values for .gil. + for value in [True, 1, 1.0, None, object()]: + with self.subTest(f'bad override (gil={value!r})'): + with self.assertRaises(TypeError): + _interpreters.new_config(gil=value) + for value in ['', 'spam']: + with self.subTest(f'bad override (gil={value!r})'): + with self.assertRaises(ValueError): + _interpreters.new_config(gil=value) + + def test_get_main(self): + interpid, whence = _interpreters.get_main() + self.assertEqual(interpid, 0) + self.assertEqual(whence, _interpreters.WHENCE_RUNTIME) + self.assertEqual( + _interpreters.whence(interpid), + _interpreters.WHENCE_RUNTIME) + + def test_get_current(self): + with self.subTest('main'): + main, *_ = _interpreters.get_main() + interpid, whence = _interpreters.get_current() + self.assertEqual(interpid, main) + self.assertEqual(whence, _interpreters.WHENCE_RUNTIME) + + script = f""" + import _interpreters + interpid, whence = _interpreters.get_current() + print((interpid, whence)) + """ + def parse_stdout(text): + interpid, whence = eval(text) + return interpid, whence + + with self.subTest('from _interpreters'): + orig = _interpreters.create() + text = self.run_and_capture(orig, script) + interpid, whence = parse_stdout(text) + self.assertEqual(interpid, orig) + self.assertEqual(whence, _interpreters.WHENCE_STDLIB) + + with self.subTest('from C-API'): + last = 0 + for id, *_ in _interpreters.list_all(): + last = max(last, id) + expected = last + 1 + text = self.run_temp_from_capi(script) + interpid, whence = parse_stdout(text) + self.assertEqual(interpid, expected) + self.assertEqual(whence, _interpreters.WHENCE_CAPI) + + def test_list_all(self): + mainid, *_ = _interpreters.get_main() + interpid1 = _interpreters.create() + interpid2 = _interpreters.create() + interpid3 = _interpreters.create() + expected = [ + (mainid, _interpreters.WHENCE_RUNTIME), + (interpid1, _interpreters.WHENCE_STDLIB), + (interpid2, _interpreters.WHENCE_STDLIB), + (interpid3, _interpreters.WHENCE_STDLIB), + ] + + with self.subTest('main'): + res = _interpreters.list_all() + self.assertEqual(res, expected) + + with self.subTest('via interp from _interpreters'): + text = self.run_and_capture(interpid2, f""" + import _interpreters + print( + _interpreters.list_all()) + """) + + res = eval(text) + self.assertEqual(res, expected) + + with self.subTest('via interp from C-API'): + interpid4 = interpid3 + 1 + interpid5 = interpid4 + 1 + expected2 = expected + [ + (interpid4, _interpreters.WHENCE_CAPI), + (interpid5, _interpreters.WHENCE_STDLIB), + ] + expected3 = expected + [ + (interpid5, _interpreters.WHENCE_STDLIB), + ] + text = self.run_temp_from_capi(f""" + import _interpreters + _interpreters.create() + print( + _interpreters.list_all()) + """) + res2 = eval(text) + res3 = _interpreters.list_all() + self.assertEqual(res2, expected2) + self.assertEqual(res3, expected3) + + def test_create(self): + isolated = _interpreters.new_config('isolated') + legacy = _interpreters.new_config('legacy') + default = isolated + + with self.subTest('no args'): + interpid = _interpreters.create() + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, default) + + with self.subTest('config: None'): + interpid = _interpreters.create(None) + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, default) + + with self.subTest('config: \'empty\''): + with self.assertRaises(InterpreterError): + # The "empty" config isn't viable on its own. + _interpreters.create('empty') + + for arg, expected in { + '': default, + 'default': default, + 'isolated': isolated, + 'legacy': legacy, + }.items(): + with self.subTest(f'str arg: {arg!r}'): + interpid = _interpreters.create(arg) + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, expected) + + with self.subTest('custom'): + orig = _interpreters.new_config('empty') + orig.use_main_obmalloc = True + orig.check_multi_interp_extensions = bool(Py_GIL_DISABLED) + orig.gil = 'shared' + interpid = _interpreters.create(orig) + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, orig) + + with self.subTest('missing fields'): + orig = _interpreters.new_config() + del orig.gil + with self.assertRaises(ValueError): + _interpreters.create(orig) + + with self.subTest('extra fields'): + orig = _interpreters.new_config() + orig.spam = True + with self.assertRaises(ValueError): + _interpreters.create(orig) + + with self.subTest('whence'): + interpid = _interpreters.create() + self.assertEqual( + _interpreters.whence(interpid), + _interpreters.WHENCE_STDLIB) + + @requires_test_modules + def test_destroy(self): + with self.subTest('from _interpreters'): + interpid = _interpreters.create() + before = [id for id, *_ in _interpreters.list_all()] + _interpreters.destroy(interpid) + after = [id for id, *_ in _interpreters.list_all()] + + self.assertIn(interpid, before) + self.assertNotIn(interpid, after) + self.assertFalse( + self.interp_exists(interpid)) + + with self.subTest('main'): + interpid, *_ = _interpreters.get_main() + with self.assertRaises(InterpreterError): + # It is the current interpreter. + _interpreters.destroy(interpid) + + with self.subTest('from C-API'): + interpid = _testinternalcapi.create_interpreter() + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + _interpreters.destroy(interpid, restrict=True) + self.assertTrue( + self.interp_exists(interpid)) + _interpreters.destroy(interpid) + self.assertFalse( + self.interp_exists(interpid)) + + def test_get_config(self): + # This test overlaps with + # test.test_capi.test_misc.InterpreterConfigTests. + + with self.subTest('main'): + expected = _interpreters.new_config('legacy') + expected.gil = 'own' + if Py_GIL_DISABLED: + expected.check_multi_interp_extensions = False + interpid, *_ = _interpreters.get_main() + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, expected) + + with self.subTest('isolated'): + expected = _interpreters.new_config('isolated') + interpid = _interpreters.create('isolated') + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, expected) + + with self.subTest('legacy'): + expected = _interpreters.new_config('legacy') + interpid = _interpreters.create('legacy') + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, expected) + + with self.subTest('from C-API'): + orig = _interpreters.new_config('isolated') + with self.interpreter_from_capi(orig) as interpid: + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + _interpreters.get_config(interpid, restrict=True) + config = _interpreters.get_config(interpid) + self.assert_ns_equal(config, orig) + + @requires_test_modules + def test_whence(self): + with self.subTest('main'): + interpid, *_ = _interpreters.get_main() + whence = _interpreters.whence(interpid) + self.assertEqual(whence, _interpreters.WHENCE_RUNTIME) + + with self.subTest('stdlib'): + interpid = _interpreters.create() + whence = _interpreters.whence(interpid) + self.assertEqual(whence, _interpreters.WHENCE_STDLIB) + + for orig, name in { + _interpreters.WHENCE_UNKNOWN: 'not ready', + _interpreters.WHENCE_LEGACY_CAPI: 'legacy C-API', + _interpreters.WHENCE_CAPI: 'C-API', + _interpreters.WHENCE_XI: 'cross-interpreter C-API', + }.items(): + with self.subTest(f'from C-API ({orig}: {name})'): + with self.interpreter_from_capi(whence=orig) as interpid: + whence = _interpreters.whence(interpid) + self.assertEqual(whence, orig) + + with self.subTest('from C-API, running'): + text = self.run_temp_from_capi(dedent(f""" + import _interpreters + interpid, *_ = _interpreters.get_current() + print(_interpreters.whence(interpid)) + """), + config=True) + whence = eval(text) + self.assertEqual(whence, _interpreters.WHENCE_CAPI) + + with self.subTest('from legacy C-API, running'): + ... + text = self.run_temp_from_capi(dedent(f""" + import _interpreters + interpid, *_ = _interpreters.get_current() + print(_interpreters.whence(interpid)) + """), + config=False) + whence = eval(text) + self.assertEqual(whence, _interpreters.WHENCE_LEGACY_CAPI) + + def test_is_running(self): + def check(interpid, expected): + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + _interpreters.is_running(interpid, restrict=True) + running = _interpreters.is_running(interpid) + self.assertIs(running, expected) + + with self.subTest('from _interpreters (running)'): + interpid = _interpreters.create() + with self.running(interpid): + running = _interpreters.is_running(interpid) + self.assertTrue(running) + + with self.subTest('from _interpreters (not running)'): + interpid = _interpreters.create() + running = _interpreters.is_running(interpid) + self.assertFalse(running) + + with self.subTest('main'): + interpid, *_ = _interpreters.get_main() + check(interpid, True) + + with self.subTest('from C-API (running __main__)'): + with self.interpreter_from_capi() as interpid: + with self.running_from_capi(interpid, main=True): + check(interpid, True) + + with self.subTest('from C-API (running, but not __main__)'): + with self.interpreter_from_capi() as interpid: + with self.running_from_capi(interpid, main=False): + check(interpid, False) + + with self.subTest('from C-API (not running)'): + with self.interpreter_from_capi() as interpid: + check(interpid, False) + + def test_exec(self): + with self.subTest('run script'): + interpid = _interpreters.create() + script, results = _captured_script('print("it worked!", end="")') + with results: + exc = _interpreters.exec(interpid, script) + results = results.final() + results.raise_if_failed() + out = results.stdout + self.assertEqual(out, 'it worked!') + + with self.subTest('uncaught exception'): + interpid = _interpreters.create() + script, results = _captured_script(""" + raise Exception('uh-oh!') + print("it worked!", end="") + """) + with results: + exc = _interpreters.exec(interpid, script) + out = results.stdout() + self.assertEqual(out, '') + self.assert_ns_equal(exc, types.SimpleNamespace( + type=types.SimpleNamespace( + __name__='Exception', + __qualname__='Exception', + __module__='builtins', + ), + msg='uh-oh!', + # We check these in other tests. + formatted=exc.formatted, + errdisplay=exc.errdisplay, + )) + + with self.subTest('from C-API'): + with self.interpreter_from_capi() as interpid: + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + _interpreters.exec(interpid, 'raise Exception("it worked!")', + restrict=True) + exc = _interpreters.exec(interpid, 'raise Exception("it worked!")') + self.assertIsNot(exc, None) + self.assertEqual(exc.msg, 'it worked!') + + def test_call(self): + with self.subTest('no args'): + interpid = _interpreters.create() + exc = _interpreters.call(interpid, call_func_return_shareable) + self.assertIs(exc, None) + + with self.subTest('uncaught exception'): + interpid = _interpreters.create() + exc = _interpreters.call(interpid, call_func_failure) + self.assertEqual(exc, types.SimpleNamespace( + type=types.SimpleNamespace( + __name__='Exception', + __qualname__='Exception', + __module__='builtins', + ), + msg='spam!', + # We check these in other tests. + formatted=exc.formatted, + errdisplay=exc.errdisplay, + )) + + @requires_test_modules + def test_set___main___attrs(self): + with self.subTest('from _interpreters'): + interpid = _interpreters.create() + before1 = _interpreters.exec(interpid, 'assert spam == \'eggs\'') + before2 = _interpreters.exec(interpid, 'assert ham == 42') + self.assertEqual(before1.type.__name__, 'NameError') + self.assertEqual(before2.type.__name__, 'NameError') + + _interpreters.set___main___attrs(interpid, dict( + spam='eggs', + ham=42, + )) + after1 = _interpreters.exec(interpid, 'assert spam == \'eggs\'') + after2 = _interpreters.exec(interpid, 'assert ham == 42') + after3 = _interpreters.exec(interpid, 'assert spam == 42') + self.assertIs(after1, None) + self.assertIs(after2, None) + self.assertEqual(after3.type.__name__, 'AssertionError') + + with self.assertRaises(ValueError): + # GH-127165: Embedded NULL characters broke the lookup + _interpreters.set___main___attrs(interpid, {"\x00": 1}) + + with self.subTest('from C-API'): + with self.interpreter_from_capi() as interpid: + with self.assertRaisesRegex(InterpreterError, 'unrecognized'): + _interpreters.set___main___attrs(interpid, {'spam': True}, + restrict=True) + _interpreters.set___main___attrs(interpid, {'spam': True}) + rc = _testinternalcapi.exec_interpreter( + interpid, + 'assert spam is True', + ) + self.assertEqual(rc, 0) + + +if __name__ == '__main__': + # Test needs to be a package, so we can do relative imports. + unittest.main() diff --git a/Lib/test/test_interpreters/test_channels.py b/Lib/test/test_interpreters/test_channels.py new file mode 100644 index 00000000000..eada18f99d0 --- /dev/null +++ b/Lib/test/test_interpreters/test_channels.py @@ -0,0 +1,600 @@ +import importlib +import pickle +import threading +from textwrap import dedent +import unittest +import time + +from test.support import import_helper +# Raise SkipTest if subinterpreters not supported. +_channels = import_helper.import_module('_interpchannels') +from test.support import interpreters +from test.support.interpreters import channels +from .utils import _run_output, TestBase + + +class LowLevelTests(TestBase): + + # The behaviors in the low-level module is important in as much + # as it is exercised by the high-level module. Therefore the + # most # important testing happens in the high-level tests. + # These low-level tests cover corner cases that are not + # encountered by the high-level module, thus they + # mostly shouldn't matter as much. + + # Additional tests are found in Lib/test/test__interpchannels.py. + # XXX Those should be either moved to LowLevelTests or eliminated + # in favor of high-level tests in this file. + + def test_highlevel_reloaded(self): + # See gh-115490 (https://github.com/python/cpython/issues/115490). + importlib.reload(channels) + + +class TestChannels(TestBase): + + def test_create(self): + r, s = channels.create() + self.assertIsInstance(r, channels.RecvChannel) + self.assertIsInstance(s, channels.SendChannel) + + def test_list_all(self): + self.assertEqual(channels.list_all(), []) + created = set() + for _ in range(3): + ch = channels.create() + created.add(ch) + after = set(channels.list_all()) + self.assertEqual(after, created) + + def test_shareable(self): + interp = interpreters.create() + rch, sch = channels.create() + + self.assertTrue( + interpreters.is_shareable(rch)) + self.assertTrue( + interpreters.is_shareable(sch)) + + sch.send_nowait(rch) + sch.send_nowait(sch) + rch2 = rch.recv() + sch2 = rch.recv() + + interp.prepare_main(rch=rch, sch=sch) + sch.send_nowait(rch) + sch.send_nowait(sch) + interp.exec(dedent(""" + rch2 = rch.recv() + sch2 = rch.recv() + assert rch2 == rch + assert sch2 == sch + + sch.send_nowait(rch2) + sch.send_nowait(sch2) + """)) + rch3 = rch.recv() + sch3 = rch.recv() + + self.assertEqual(rch2, rch) + self.assertEqual(sch2, sch) + self.assertEqual(rch3, rch) + self.assertEqual(sch3, sch) + + def test_is_closed(self): + rch, sch = channels.create() + rbefore = rch.is_closed + sbefore = sch.is_closed + rch.close() + rafter = rch.is_closed + safter = sch.is_closed + + self.assertFalse(rbefore) + self.assertFalse(sbefore) + self.assertTrue(rafter) + self.assertTrue(safter) + + +class TestRecvChannelAttrs(TestBase): + + def test_id_type(self): + rch, _ = channels.create() + self.assertIsInstance(rch.id, _channels.ChannelID) + + def test_custom_id(self): + rch = channels.RecvChannel(1) + self.assertEqual(rch.id, 1) + + with self.assertRaises(TypeError): + channels.RecvChannel('1') + + def test_id_readonly(self): + rch = channels.RecvChannel(1) + with self.assertRaises(AttributeError): + rch.id = 2 + + def test_equality(self): + ch1, _ = channels.create() + ch2, _ = channels.create() + self.assertEqual(ch1, ch1) + self.assertNotEqual(ch1, ch2) + + def test_pickle(self): + ch, _ = channels.create() + data = pickle.dumps(ch) + unpickled = pickle.loads(data) + self.assertEqual(unpickled, ch) + + +class TestSendChannelAttrs(TestBase): + + def test_id_type(self): + _, sch = channels.create() + self.assertIsInstance(sch.id, _channels.ChannelID) + + def test_custom_id(self): + sch = channels.SendChannel(1) + self.assertEqual(sch.id, 1) + + with self.assertRaises(TypeError): + channels.SendChannel('1') + + def test_id_readonly(self): + sch = channels.SendChannel(1) + with self.assertRaises(AttributeError): + sch.id = 2 + + def test_equality(self): + _, ch1 = channels.create() + _, ch2 = channels.create() + self.assertEqual(ch1, ch1) + self.assertNotEqual(ch1, ch2) + + def test_pickle(self): + _, ch = channels.create() + data = pickle.dumps(ch) + unpickled = pickle.loads(data) + self.assertEqual(unpickled, ch) + + +class TestSendRecv(TestBase): + + def test_send_recv_main(self): + r, s = channels.create() + orig = b'spam' + s.send_nowait(orig) + obj = r.recv() + + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) + + def test_send_recv_same_interpreter(self): + interp = interpreters.create() + interp.exec(dedent(""" + from test.support.interpreters import channels + r, s = channels.create() + orig = b'spam' + s.send_nowait(orig) + obj = r.recv() + assert obj == orig, 'expected: obj == orig' + assert obj is not orig, 'expected: obj is not orig' + """)) + + @unittest.skip('broken (see BPO-...)') + def test_send_recv_different_interpreters(self): + r1, s1 = channels.create() + r2, s2 = channels.create() + orig1 = b'spam' + s1.send_nowait(orig1) + out = _run_output( + interpreters.create(), + dedent(f""" + obj1 = r.recv() + assert obj1 == b'spam', 'expected: obj1 == orig1' + # When going to another interpreter we get a copy. + assert id(obj1) != {id(orig1)}, 'expected: obj1 is not orig1' + orig2 = b'eggs' + print(id(orig2)) + s.send_nowait(orig2) + """), + channels=dict(r=r1, s=s2), + ) + obj2 = r2.recv() + + self.assertEqual(obj2, b'eggs') + self.assertNotEqual(id(obj2), int(out)) + + def test_send_recv_different_threads(self): + r, s = channels.create() + + def f(): + while True: + try: + obj = r.recv() + break + except channels.ChannelEmptyError: + time.sleep(0.1) + s.send(obj) + t = threading.Thread(target=f) + t.start() + + orig = b'spam' + s.send(orig) + obj = r.recv() + t.join() + + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) + + def test_send_recv_nowait_main(self): + r, s = channels.create() + orig = b'spam' + s.send_nowait(orig) + obj = r.recv_nowait() + + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) + + def test_send_recv_nowait_main_with_default(self): + r, _ = channels.create() + obj = r.recv_nowait(None) + + self.assertIsNone(obj) + + def test_send_recv_nowait_same_interpreter(self): + interp = interpreters.create() + interp.exec(dedent(""" + from test.support.interpreters import channels + r, s = channels.create() + orig = b'spam' + s.send_nowait(orig) + obj = r.recv_nowait() + assert obj == orig, 'expected: obj == orig' + # When going back to the same interpreter we get the same object. + assert obj is not orig, 'expected: obj is not orig' + """)) + + @unittest.skip('broken (see BPO-...)') + def test_send_recv_nowait_different_interpreters(self): + r1, s1 = channels.create() + r2, s2 = channels.create() + orig1 = b'spam' + s1.send_nowait(orig1) + out = _run_output( + interpreters.create(), + dedent(f""" + obj1 = r.recv_nowait() + assert obj1 == b'spam', 'expected: obj1 == orig1' + # When going to another interpreter we get a copy. + assert id(obj1) != {id(orig1)}, 'expected: obj1 is not orig1' + orig2 = b'eggs' + print(id(orig2)) + s.send_nowait(orig2) + """), + channels=dict(r=r1, s=s2), + ) + obj2 = r2.recv_nowait() + + self.assertEqual(obj2, b'eggs') + self.assertNotEqual(id(obj2), int(out)) + + def test_recv_timeout(self): + r, _ = channels.create() + with self.assertRaises(TimeoutError): + r.recv(timeout=1) + + def test_recv_channel_does_not_exist(self): + ch = channels.RecvChannel(1_000_000) + with self.assertRaises(channels.ChannelNotFoundError): + ch.recv() + + def test_send_channel_does_not_exist(self): + ch = channels.SendChannel(1_000_000) + with self.assertRaises(channels.ChannelNotFoundError): + ch.send(b'spam') + + def test_recv_nowait_channel_does_not_exist(self): + ch = channels.RecvChannel(1_000_000) + with self.assertRaises(channels.ChannelNotFoundError): + ch.recv_nowait() + + def test_send_nowait_channel_does_not_exist(self): + ch = channels.SendChannel(1_000_000) + with self.assertRaises(channels.ChannelNotFoundError): + ch.send_nowait(b'spam') + + def test_recv_nowait_empty(self): + ch, _ = channels.create() + with self.assertRaises(channels.ChannelEmptyError): + ch.recv_nowait() + + def test_recv_nowait_default(self): + default = object() + rch, sch = channels.create() + obj1 = rch.recv_nowait(default) + sch.send_nowait(None) + sch.send_nowait(1) + sch.send_nowait(b'spam') + sch.send_nowait(b'eggs') + obj2 = rch.recv_nowait(default) + obj3 = rch.recv_nowait(default) + obj4 = rch.recv_nowait() + obj5 = rch.recv_nowait(default) + obj6 = rch.recv_nowait(default) + + self.assertIs(obj1, default) + self.assertIs(obj2, None) + self.assertEqual(obj3, 1) + self.assertEqual(obj4, b'spam') + self.assertEqual(obj5, b'eggs') + self.assertIs(obj6, default) + + def test_send_buffer(self): + buf = bytearray(b'spamspamspam') + obj = None + rch, sch = channels.create() + + def f(): + nonlocal obj + while True: + try: + obj = rch.recv() + break + except channels.ChannelEmptyError: + time.sleep(0.1) + t = threading.Thread(target=f) + t.start() + + sch.send_buffer(buf) + t.join() + + self.assertIsNot(obj, buf) + self.assertIsInstance(obj, memoryview) + self.assertEqual(obj, buf) + + buf[4:8] = b'eggs' + self.assertEqual(obj, buf) + obj[4:8] = b'ham.' + self.assertEqual(obj, buf) + + def test_send_buffer_nowait(self): + buf = bytearray(b'spamspamspam') + rch, sch = channels.create() + sch.send_buffer_nowait(buf) + obj = rch.recv() + + self.assertIsNot(obj, buf) + self.assertIsInstance(obj, memoryview) + self.assertEqual(obj, buf) + + buf[4:8] = b'eggs' + self.assertEqual(obj, buf) + obj[4:8] = b'ham.' + self.assertEqual(obj, buf) + + def test_send_cleared_with_subinterpreter(self): + def common(rch, sch, unbound=None, presize=0): + if not unbound: + extraargs = '' + elif unbound is channels.UNBOUND: + extraargs = ', unbound=channels.UNBOUND' + elif unbound is channels.UNBOUND_ERROR: + extraargs = ', unbound=channels.UNBOUND_ERROR' + elif unbound is channels.UNBOUND_REMOVE: + extraargs = ', unbound=channels.UNBOUND_REMOVE' + else: + raise NotImplementedError(repr(unbound)) + interp = interpreters.create() + + _run_output(interp, dedent(f""" + from test.support.interpreters import channels + sch = channels.SendChannel({sch.id}) + obj1 = b'spam' + obj2 = b'eggs' + sch.send_nowait(obj1{extraargs}) + sch.send_nowait(obj2{extraargs}) + """)) + self.assertEqual( + _channels.get_count(rch.id), + presize + 2, + ) + + if presize == 0: + obj1 = rch.recv() + self.assertEqual(obj1, b'spam') + self.assertEqual( + _channels.get_count(rch.id), + presize + 1, + ) + + return interp + + with self.subTest('default'): # UNBOUND + rch, sch = channels.create() + interp = common(rch, sch) + del interp + self.assertEqual(_channels.get_count(rch.id), 1) + obj1 = rch.recv() + self.assertEqual(_channels.get_count(rch.id), 0) + self.assertIs(obj1, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 0) + with self.assertRaises(channels.ChannelEmptyError): + rch.recv_nowait() + + with self.subTest('UNBOUND'): + rch, sch = channels.create() + interp = common(rch, sch, channels.UNBOUND) + del interp + self.assertEqual(_channels.get_count(rch.id), 1) + obj1 = rch.recv() + self.assertIs(obj1, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 0) + with self.assertRaises(channels.ChannelEmptyError): + rch.recv_nowait() + + with self.subTest('UNBOUND_ERROR'): + rch, sch = channels.create() + interp = common(rch, sch, channels.UNBOUND_ERROR) + + del interp + self.assertEqual(_channels.get_count(rch.id), 1) + with self.assertRaises(channels.ItemInterpreterDestroyed): + rch.recv() + + self.assertEqual(_channels.get_count(rch.id), 0) + with self.assertRaises(channels.ChannelEmptyError): + rch.recv_nowait() + + with self.subTest('UNBOUND_REMOVE'): + rch, sch = channels.create() + + interp = common(rch, sch, channels.UNBOUND_REMOVE) + del interp + self.assertEqual(_channels.get_count(rch.id), 0) + with self.assertRaises(channels.ChannelEmptyError): + rch.recv_nowait() + + sch.send_nowait(b'ham', unbound=channels.UNBOUND_REMOVE) + self.assertEqual(_channels.get_count(rch.id), 1) + interp = common(rch, sch, channels.UNBOUND_REMOVE, 1) + self.assertEqual(_channels.get_count(rch.id), 3) + sch.send_nowait(42, unbound=channels.UNBOUND_REMOVE) + self.assertEqual(_channels.get_count(rch.id), 4) + del interp + self.assertEqual(_channels.get_count(rch.id), 2) + obj1 = rch.recv() + obj2 = rch.recv() + self.assertEqual(obj1, b'ham') + self.assertEqual(obj2, 42) + self.assertEqual(_channels.get_count(rch.id), 0) + with self.assertRaises(channels.ChannelEmptyError): + rch.recv_nowait() + + def test_send_cleared_with_subinterpreter_mixed(self): + rch, sch = channels.create() + interp = interpreters.create() + + # If we don't associate the main interpreter with the channel + # then the channel will be automatically closed when interp + # is destroyed. + sch.send_nowait(None) + rch.recv() + self.assertEqual(_channels.get_count(rch.id), 0) + + _run_output(interp, dedent(f""" + from test.support.interpreters import channels + sch = channels.SendChannel({sch.id}) + sch.send_nowait(1, unbound=channels.UNBOUND) + sch.send_nowait(2, unbound=channels.UNBOUND_ERROR) + sch.send_nowait(3) + sch.send_nowait(4, unbound=channels.UNBOUND_REMOVE) + sch.send_nowait(5, unbound=channels.UNBOUND) + """)) + self.assertEqual(_channels.get_count(rch.id), 5) + + del interp + self.assertEqual(_channels.get_count(rch.id), 4) + + obj1 = rch.recv() + self.assertIs(obj1, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 3) + + with self.assertRaises(channels.ItemInterpreterDestroyed): + rch.recv() + self.assertEqual(_channels.get_count(rch.id), 2) + + obj2 = rch.recv() + self.assertIs(obj2, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 1) + + obj3 = rch.recv() + self.assertIs(obj3, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 0) + + def test_send_cleared_with_subinterpreter_multiple(self): + rch, sch = channels.create() + interp1 = interpreters.create() + interp2 = interpreters.create() + + sch.send_nowait(1) + _run_output(interp1, dedent(f""" + from test.support.interpreters import channels + rch = channels.RecvChannel({rch.id}) + sch = channels.SendChannel({sch.id}) + obj1 = rch.recv() + sch.send_nowait(2, unbound=channels.UNBOUND) + sch.send_nowait(obj1, unbound=channels.UNBOUND_REMOVE) + """)) + _run_output(interp2, dedent(f""" + from test.support.interpreters import channels + rch = channels.RecvChannel({rch.id}) + sch = channels.SendChannel({sch.id}) + obj2 = rch.recv() + obj1 = rch.recv() + """)) + self.assertEqual(_channels.get_count(rch.id), 0) + sch.send_nowait(3) + _run_output(interp1, dedent(""" + sch.send_nowait(4, unbound=channels.UNBOUND) + # interp closed here + sch.send_nowait(5, unbound=channels.UNBOUND_REMOVE) + sch.send_nowait(6, unbound=channels.UNBOUND) + """)) + _run_output(interp2, dedent(""" + sch.send_nowait(7, unbound=channels.UNBOUND_ERROR) + # interp closed here + sch.send_nowait(obj1, unbound=channels.UNBOUND_ERROR) + sch.send_nowait(obj2, unbound=channels.UNBOUND_REMOVE) + sch.send_nowait(8, unbound=channels.UNBOUND) + """)) + _run_output(interp1, dedent(""" + sch.send_nowait(9, unbound=channels.UNBOUND_REMOVE) + sch.send_nowait(10, unbound=channels.UNBOUND) + """)) + self.assertEqual(_channels.get_count(rch.id), 10) + + obj3 = rch.recv() + self.assertEqual(obj3, 3) + self.assertEqual(_channels.get_count(rch.id), 9) + + obj4 = rch.recv() + self.assertEqual(obj4, 4) + self.assertEqual(_channels.get_count(rch.id), 8) + + del interp1 + self.assertEqual(_channels.get_count(rch.id), 6) + + # obj5 was removed + + obj6 = rch.recv() + self.assertIs(obj6, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 5) + + obj7 = rch.recv() + self.assertEqual(obj7, 7) + self.assertEqual(_channels.get_count(rch.id), 4) + + del interp2 + self.assertEqual(_channels.get_count(rch.id), 3) + + # obj1 + with self.assertRaises(channels.ItemInterpreterDestroyed): + rch.recv() + self.assertEqual(_channels.get_count(rch.id), 2) + + # obj2 was removed + + obj8 = rch.recv() + self.assertIs(obj8, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 1) + + # obj9 was removed + + obj10 = rch.recv() + self.assertIs(obj10, channels.UNBOUND) + self.assertEqual(_channels.get_count(rch.id), 0) + + +if __name__ == '__main__': + # Test needs to be a package, so we can do relative imports. + unittest.main() diff --git a/Lib/test/test_interpreters/test_lifecycle.py b/Lib/test/test_interpreters/test_lifecycle.py new file mode 100644 index 00000000000..ac24f6568ac --- /dev/null +++ b/Lib/test/test_interpreters/test_lifecycle.py @@ -0,0 +1,190 @@ +import contextlib +import json +import os +import os.path +import sys +from textwrap import dedent +import unittest + +from test import support +from test.support import import_helper +from test.support import os_helper +# Raise SkipTest if subinterpreters not supported. +import_helper.import_module('_interpreters') +from .utils import TestBase + + +class StartupTests(TestBase): + + # We want to ensure the initial state of subinterpreters + # matches expectations. + + _subtest_count = 0 + + @contextlib.contextmanager + def subTest(self, *args): + with super().subTest(*args) as ctx: + self._subtest_count += 1 + try: + yield ctx + finally: + if self._debugged_in_subtest: + if self._subtest_count == 1: + # The first subtest adds a leading newline, so we + # compensate here by not printing a trailing newline. + print('### end subtest debug ###', end='') + else: + print('### end subtest debug ###') + self._debugged_in_subtest = False + + def debug(self, msg, *, header=None): + if header: + self._debug(f'--- {header} ---') + if msg: + if msg.endswith(os.linesep): + self._debug(msg[:-len(os.linesep)]) + else: + self._debug(msg) + self._debug('') + self._debug('------') + else: + self._debug(msg) + + _debugged = False + _debugged_in_subtest = False + def _debug(self, msg): + if not self._debugged: + print() + self._debugged = True + if self._subtest is not None: + if True: + if not self._debugged_in_subtest: + self._debugged_in_subtest = True + print('### start subtest debug ###') + print(msg) + else: + print(msg) + + def create_temp_dir(self): + import tempfile + tmp = tempfile.mkdtemp(prefix='test_interpreters_') + tmp = os.path.realpath(tmp) + self.addCleanup(os_helper.rmtree, tmp) + return tmp + + def write_script(self, *path, text): + filename = os.path.join(*path) + dirname = os.path.dirname(filename) + if dirname: + os.makedirs(dirname, exist_ok=True) + with open(filename, 'w', encoding='utf-8') as outfile: + outfile.write(dedent(text)) + return filename + + @support.requires_subprocess() + def run_python(self, argv, *, cwd=None): + # This method is inspired by + # EmbeddingTestsMixin.run_embedded_interpreter() in test_embed.py. + import shlex + import subprocess + if isinstance(argv, str): + argv = shlex.split(argv) + argv = [sys.executable, *argv] + try: + proc = subprocess.run( + argv, + cwd=cwd, + capture_output=True, + text=True, + ) + except Exception as exc: + self.debug(f'# cmd: {shlex.join(argv)}') + if isinstance(exc, FileNotFoundError) and not exc.filename: + if os.path.exists(argv[0]): + exists = 'exists' + else: + exists = 'does not exist' + self.debug(f'{argv[0]} {exists}') + raise # re-raise + assert proc.stderr == '' or proc.returncode != 0, proc.stderr + if proc.returncode != 0 and support.verbose: + self.debug(f'# python3 {shlex.join(argv[1:])} failed:') + self.debug(proc.stdout, header='stdout') + self.debug(proc.stderr, header='stderr') + self.assertEqual(proc.returncode, 0) + self.assertEqual(proc.stderr, '') + return proc.stdout + + def test_sys_path_0(self): + # The main interpreter's sys.path[0] should be used by subinterpreters. + script = ''' + import sys + from test.support import interpreters + + orig = sys.path[0] + + interp = interpreters.create() + interp.exec(f"""if True: + import json + import sys + print(json.dumps({{ + 'main': {orig!r}, + 'sub': sys.path[0], + }}, indent=4), flush=True) + """) + ''' + # / + # pkg/ + # __init__.py + # __main__.py + # script.py + # script.py + cwd = self.create_temp_dir() + self.write_script(cwd, 'pkg', '__init__.py', text='') + self.write_script(cwd, 'pkg', '__main__.py', text=script) + self.write_script(cwd, 'pkg', 'script.py', text=script) + self.write_script(cwd, 'script.py', text=script) + + cases = [ + ('script.py', cwd), + ('-m script', cwd), + ('-m pkg', cwd), + ('-m pkg.script', cwd), + ('-c "import script"', ''), + ] + for argv, expected in cases: + with self.subTest(f'python3 {argv}'): + out = self.run_python(argv, cwd=cwd) + data = json.loads(out) + sp0_main, sp0_sub = data['main'], data['sub'] + self.assertEqual(sp0_sub, sp0_main) + self.assertEqual(sp0_sub, expected) + # XXX Also check them all with the -P cmdline flag? + + +class FinalizationTests(TestBase): + + @support.requires_subprocess() + def test_gh_109793(self): + # Make sure finalization finishes and the correct error code + # is reported, even when subinterpreters get cleaned up at the end. + import subprocess + argv = [sys.executable, '-c', '''if True: + from test.support import interpreters + interp = interpreters.create() + raise Exception + '''] + proc = subprocess.run(argv, capture_output=True, text=True) + self.assertIn('Traceback', proc.stderr) + if proc.returncode == 0 and support.verbose: + print() + print("--- cmd unexpected succeeded ---") + print(f"stdout:\n{proc.stdout}") + print(f"stderr:\n{proc.stderr}") + print("------") + self.assertEqual(proc.returncode, 1) + + +if __name__ == '__main__': + # Test needs to be a package, so we can do relative imports. + unittest.main() diff --git a/Lib/test/test_interpreters/test_queues.py b/Lib/test/test_interpreters/test_queues.py new file mode 100644 index 00000000000..7858c4e602c --- /dev/null +++ b/Lib/test/test_interpreters/test_queues.py @@ -0,0 +1,707 @@ +import importlib +import pickle +import threading +from textwrap import dedent +import unittest +import time + +from test.support import import_helper, Py_DEBUG +# Raise SkipTest if subinterpreters not supported. +_queues = import_helper.import_module('_interpqueues') +from test.support import interpreters +from test.support.interpreters import queues, _crossinterp +from .utils import _run_output, TestBase as _TestBase + + +REPLACE = _crossinterp._UNBOUND_CONSTANT_TO_FLAG[_crossinterp.UNBOUND] + + +def get_num_queues(): + return len(_queues.list_all()) + + +class TestBase(_TestBase): + def tearDown(self): + for qid, _, _ in _queues.list_all(): + try: + _queues.destroy(qid) + except Exception: + pass + + +class LowLevelTests(TestBase): + + # The behaviors in the low-level module are important in as much + # as they are exercised by the high-level module. Therefore the + # most important testing happens in the high-level tests. + # These low-level tests cover corner cases that are not + # encountered by the high-level module, thus they + # mostly shouldn't matter as much. + + def test_highlevel_reloaded(self): + # See gh-115490 (https://github.com/python/cpython/issues/115490). + importlib.reload(queues) + + def test_create_destroy(self): + qid = _queues.create(2, 0, REPLACE) + _queues.destroy(qid) + self.assertEqual(get_num_queues(), 0) + with self.assertRaises(queues.QueueNotFoundError): + _queues.get(qid) + with self.assertRaises(queues.QueueNotFoundError): + _queues.destroy(qid) + + def test_not_destroyed(self): + # It should have cleaned up any remaining queues. + stdout, stderr = self.assert_python_ok( + '-c', + dedent(f""" + import {_queues.__name__} as _queues + _queues.create(2, 0, {REPLACE}) + """), + ) + self.assertEqual(stdout, '') + if Py_DEBUG: + self.assertNotEqual(stderr, '') + else: + self.assertEqual(stderr, '') + + def test_bind_release(self): + with self.subTest('typical'): + qid = _queues.create(2, 0, REPLACE) + _queues.bind(qid) + _queues.release(qid) + self.assertEqual(get_num_queues(), 0) + + with self.subTest('bind too much'): + qid = _queues.create(2, 0, REPLACE) + _queues.bind(qid) + _queues.bind(qid) + _queues.release(qid) + _queues.destroy(qid) + self.assertEqual(get_num_queues(), 0) + + with self.subTest('nested'): + qid = _queues.create(2, 0, REPLACE) + _queues.bind(qid) + _queues.bind(qid) + _queues.release(qid) + _queues.release(qid) + self.assertEqual(get_num_queues(), 0) + + with self.subTest('release without binding'): + qid = _queues.create(2, 0, REPLACE) + with self.assertRaises(queues.QueueError): + _queues.release(qid) + + +class QueueTests(TestBase): + + def test_create(self): + with self.subTest('vanilla'): + queue = queues.create() + self.assertEqual(queue.maxsize, 0) + + with self.subTest('small maxsize'): + queue = queues.create(3) + self.assertEqual(queue.maxsize, 3) + + with self.subTest('big maxsize'): + queue = queues.create(100) + self.assertEqual(queue.maxsize, 100) + + with self.subTest('no maxsize'): + queue = queues.create(0) + self.assertEqual(queue.maxsize, 0) + + with self.subTest('negative maxsize'): + queue = queues.create(-10) + self.assertEqual(queue.maxsize, -10) + + with self.subTest('bad maxsize'): + with self.assertRaises(TypeError): + queues.create('1') + + def test_shareable(self): + queue1 = queues.create() + + interp = interpreters.create() + interp.exec(dedent(f""" + from test.support.interpreters import queues + queue1 = queues.Queue({queue1.id}) + """)); + + with self.subTest('same interpreter'): + queue2 = queues.create() + queue1.put(queue2, syncobj=True) + queue3 = queue1.get() + self.assertIs(queue3, queue2) + + with self.subTest('from current interpreter'): + queue4 = queues.create() + queue1.put(queue4, syncobj=True) + out = _run_output(interp, dedent(""" + queue4 = queue1.get() + print(queue4.id) + """)) + qid = int(out) + self.assertEqual(qid, queue4.id) + + with self.subTest('from subinterpreter'): + out = _run_output(interp, dedent(""" + queue5 = queues.create() + queue1.put(queue5, syncobj=True) + print(queue5.id) + """)) + qid = int(out) + queue5 = queue1.get() + self.assertEqual(queue5.id, qid) + + def test_id_type(self): + queue = queues.create() + self.assertIsInstance(queue.id, int) + + def test_custom_id(self): + with self.assertRaises(queues.QueueNotFoundError): + queues.Queue(1_000_000) + + def test_id_readonly(self): + queue = queues.create() + with self.assertRaises(AttributeError): + queue.id = 1_000_000 + + def test_maxsize_readonly(self): + queue = queues.create(10) + with self.assertRaises(AttributeError): + queue.maxsize = 1_000_000 + + def test_hashable(self): + queue = queues.create() + expected = hash(queue.id) + actual = hash(queue) + self.assertEqual(actual, expected) + + def test_equality(self): + queue1 = queues.create() + queue2 = queues.create() + self.assertEqual(queue1, queue1) + self.assertNotEqual(queue1, queue2) + + def test_pickle(self): + queue = queues.create() + data = pickle.dumps(queue) + unpickled = pickle.loads(data) + self.assertEqual(unpickled, queue) + + +class TestQueueOps(TestBase): + + def test_empty(self): + queue = queues.create() + before = queue.empty() + queue.put(None, syncobj=True) + during = queue.empty() + queue.get() + after = queue.empty() + + self.assertIs(before, True) + self.assertIs(during, False) + self.assertIs(after, True) + + def test_full(self): + for maxsize in [1, 3, 11]: + with self.subTest(f'maxsize={maxsize}'): + num_to_add = maxsize + expected = [False] * (num_to_add * 2 + 3) + expected[maxsize] = True + expected[maxsize + 1] = True + + queue = queues.create(maxsize) + actual = [] + empty = [queue.empty()] + + for _ in range(num_to_add): + actual.append(queue.full()) + queue.put_nowait(None) + actual.append(queue.full()) + with self.assertRaises(queues.QueueFull): + queue.put_nowait(None) + empty.append(queue.empty()) + + for _ in range(num_to_add): + actual.append(queue.full()) + queue.get_nowait() + actual.append(queue.full()) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + actual.append(queue.full()) + empty.append(queue.empty()) + + self.assertEqual(actual, expected) + self.assertEqual(empty, [True, False, True]) + + # no max size + for args in [(), (0,), (-1,), (-10,)]: + with self.subTest(f'maxsize={args[0]}' if args else ''): + num_to_add = 13 + expected = [False] * (num_to_add * 2 + 3) + + queue = queues.create(*args) + actual = [] + empty = [queue.empty()] + + for _ in range(num_to_add): + actual.append(queue.full()) + queue.put_nowait(None) + actual.append(queue.full()) + empty.append(queue.empty()) + + for _ in range(num_to_add): + actual.append(queue.full()) + queue.get_nowait() + actual.append(queue.full()) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + actual.append(queue.full()) + empty.append(queue.empty()) + + self.assertEqual(actual, expected) + self.assertEqual(empty, [True, False, True]) + + def test_qsize(self): + expected = [0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0] + actual = [] + queue = queues.create() + for _ in range(3): + actual.append(queue.qsize()) + queue.put(None, syncobj=True) + actual.append(queue.qsize()) + queue.get() + actual.append(queue.qsize()) + queue.put(None, syncobj=True) + actual.append(queue.qsize()) + for _ in range(3): + queue.get() + actual.append(queue.qsize()) + queue.put(None, syncobj=True) + actual.append(queue.qsize()) + queue.get() + actual.append(queue.qsize()) + + self.assertEqual(actual, expected) + + def test_put_get_main(self): + expected = list(range(20)) + for syncobj in (True, False): + kwds = dict(syncobj=syncobj) + with self.subTest(f'syncobj={syncobj}'): + queue = queues.create() + for i in range(20): + queue.put(i, **kwds) + actual = [queue.get() for _ in range(20)] + + self.assertEqual(actual, expected) + + def test_put_timeout(self): + for syncobj in (True, False): + kwds = dict(syncobj=syncobj) + with self.subTest(f'syncobj={syncobj}'): + queue = queues.create(2) + queue.put(None, **kwds) + queue.put(None, **kwds) + with self.assertRaises(queues.QueueFull): + queue.put(None, timeout=0.1, **kwds) + queue.get() + queue.put(None, **kwds) + + def test_put_nowait(self): + for syncobj in (True, False): + kwds = dict(syncobj=syncobj) + with self.subTest(f'syncobj={syncobj}'): + queue = queues.create(2) + queue.put_nowait(None, **kwds) + queue.put_nowait(None, **kwds) + with self.assertRaises(queues.QueueFull): + queue.put_nowait(None, **kwds) + queue.get() + queue.put_nowait(None, **kwds) + + def test_put_syncobj(self): + for obj in [ + None, + True, + 10, + 'spam', + b'spam', + (0, 'a'), + ]: + with self.subTest(repr(obj)): + queue = queues.create() + + queue.put(obj, syncobj=True) + obj2 = queue.get() + self.assertEqual(obj2, obj) + + queue.put(obj, syncobj=True) + obj2 = queue.get_nowait() + self.assertEqual(obj2, obj) + + for obj in [ + [1, 2, 3], + {'a': 13, 'b': 17}, + ]: + with self.subTest(repr(obj)): + queue = queues.create() + with self.assertRaises(interpreters.NotShareableError): + queue.put(obj, syncobj=True) + + def test_put_not_syncobj(self): + for obj in [ + None, + True, + 10, + 'spam', + b'spam', + (0, 'a'), + # not shareable + [1, 2, 3], + {'a': 13, 'b': 17}, + ]: + with self.subTest(repr(obj)): + queue = queues.create() + + queue.put(obj, syncobj=False) + obj2 = queue.get() + self.assertEqual(obj2, obj) + + queue.put(obj, syncobj=False) + obj2 = queue.get_nowait() + self.assertEqual(obj2, obj) + + def test_get_timeout(self): + queue = queues.create() + with self.assertRaises(queues.QueueEmpty): + queue.get(timeout=0.1) + + def test_get_nowait(self): + queue = queues.create() + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + + def test_put_get_default_syncobj(self): + expected = list(range(20)) + queue = queues.create(syncobj=True) + for methname in ('get', 'get_nowait'): + with self.subTest(f'{methname}()'): + get = getattr(queue, methname) + for i in range(20): + queue.put(i) + actual = [get() for _ in range(20)] + self.assertEqual(actual, expected) + + obj = [1, 2, 3] # lists are not shareable + with self.assertRaises(interpreters.NotShareableError): + queue.put(obj) + + def test_put_get_default_not_syncobj(self): + expected = list(range(20)) + queue = queues.create(syncobj=False) + for methname in ('get', 'get_nowait'): + with self.subTest(f'{methname}()'): + get = getattr(queue, methname) + + for i in range(20): + queue.put(i) + actual = [get() for _ in range(20)] + self.assertEqual(actual, expected) + + obj = [1, 2, 3] # lists are not shareable + queue.put(obj) + obj2 = get() + self.assertEqual(obj, obj2) + self.assertIsNot(obj, obj2) + + def test_put_get_same_interpreter(self): + interp = interpreters.create() + interp.exec(dedent(""" + from test.support.interpreters import queues + queue = queues.create() + """)) + for methname in ('get', 'get_nowait'): + with self.subTest(f'{methname}()'): + interp.exec(dedent(f""" + orig = b'spam' + queue.put(orig, syncobj=True) + obj = queue.{methname}() + assert obj == orig, 'expected: obj == orig' + assert obj is not orig, 'expected: obj is not orig' + """)) + + def test_put_get_different_interpreters(self): + interp = interpreters.create() + queue1 = queues.create() + queue2 = queues.create() + self.assertEqual(len(queues.list_all()), 2) + + for methname in ('get', 'get_nowait'): + with self.subTest(f'{methname}()'): + obj1 = b'spam' + queue1.put(obj1, syncobj=True) + + out = _run_output( + interp, + dedent(f""" + from test.support.interpreters import queues + queue1 = queues.Queue({queue1.id}) + queue2 = queues.Queue({queue2.id}) + assert queue1.qsize() == 1, 'expected: queue1.qsize() == 1' + obj = queue1.{methname}() + assert queue1.qsize() == 0, 'expected: queue1.qsize() == 0' + assert obj == b'spam', 'expected: obj == obj1' + # When going to another interpreter we get a copy. + assert id(obj) != {id(obj1)}, 'expected: obj is not obj1' + obj2 = b'eggs' + print(id(obj2)) + assert queue2.qsize() == 0, 'expected: queue2.qsize() == 0' + queue2.put(obj2, syncobj=True) + assert queue2.qsize() == 1, 'expected: queue2.qsize() == 1' + """)) + self.assertEqual(len(queues.list_all()), 2) + self.assertEqual(queue1.qsize(), 0) + self.assertEqual(queue2.qsize(), 1) + + get = getattr(queue2, methname) + obj2 = get() + self.assertEqual(obj2, b'eggs') + self.assertNotEqual(id(obj2), int(out)) + + def test_put_cleared_with_subinterpreter(self): + def common(queue, unbound=None, presize=0): + if not unbound: + extraargs = '' + elif unbound is queues.UNBOUND: + extraargs = ', unbound=queues.UNBOUND' + elif unbound is queues.UNBOUND_ERROR: + extraargs = ', unbound=queues.UNBOUND_ERROR' + elif unbound is queues.UNBOUND_REMOVE: + extraargs = ', unbound=queues.UNBOUND_REMOVE' + else: + raise NotImplementedError(repr(unbound)) + interp = interpreters.create() + + _run_output(interp, dedent(f""" + from test.support.interpreters import queues + queue = queues.Queue({queue.id}) + obj1 = b'spam' + obj2 = b'eggs' + queue.put(obj1, syncobj=True{extraargs}) + queue.put(obj2, syncobj=True{extraargs}) + """)) + self.assertEqual(queue.qsize(), presize + 2) + + if presize == 0: + obj1 = queue.get() + self.assertEqual(obj1, b'spam') + self.assertEqual(queue.qsize(), presize + 1) + + return interp + + with self.subTest('default'): # UNBOUND + queue = queues.create() + interp = common(queue) + del interp + obj1 = queue.get() + self.assertIs(obj1, queues.UNBOUND) + self.assertEqual(queue.qsize(), 0) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + + with self.subTest('UNBOUND'): + queue = queues.create() + interp = common(queue, queues.UNBOUND) + del interp + obj1 = queue.get() + self.assertIs(obj1, queues.UNBOUND) + self.assertEqual(queue.qsize(), 0) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + + with self.subTest('UNBOUND_ERROR'): + queue = queues.create() + interp = common(queue, queues.UNBOUND_ERROR) + + del interp + self.assertEqual(queue.qsize(), 1) + with self.assertRaises(queues.ItemInterpreterDestroyed): + queue.get() + + self.assertEqual(queue.qsize(), 0) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + + with self.subTest('UNBOUND_REMOVE'): + queue = queues.create() + + interp = common(queue, queues.UNBOUND_REMOVE) + del interp + self.assertEqual(queue.qsize(), 0) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + + queue.put(b'ham', unbound=queues.UNBOUND_REMOVE) + self.assertEqual(queue.qsize(), 1) + interp = common(queue, queues.UNBOUND_REMOVE, 1) + self.assertEqual(queue.qsize(), 3) + queue.put(42, unbound=queues.UNBOUND_REMOVE) + self.assertEqual(queue.qsize(), 4) + del interp + self.assertEqual(queue.qsize(), 2) + obj1 = queue.get() + obj2 = queue.get() + self.assertEqual(obj1, b'ham') + self.assertEqual(obj2, 42) + self.assertEqual(queue.qsize(), 0) + with self.assertRaises(queues.QueueEmpty): + queue.get_nowait() + + def test_put_cleared_with_subinterpreter_mixed(self): + queue = queues.create() + interp = interpreters.create() + _run_output(interp, dedent(f""" + from test.support.interpreters import queues + queue = queues.Queue({queue.id}) + queue.put(1, syncobj=True, unbound=queues.UNBOUND) + queue.put(2, syncobj=True, unbound=queues.UNBOUND_ERROR) + queue.put(3, syncobj=True) + queue.put(4, syncobj=True, unbound=queues.UNBOUND_REMOVE) + queue.put(5, syncobj=True, unbound=queues.UNBOUND) + """)) + self.assertEqual(queue.qsize(), 5) + + del interp + self.assertEqual(queue.qsize(), 4) + + obj1 = queue.get() + self.assertIs(obj1, queues.UNBOUND) + self.assertEqual(queue.qsize(), 3) + + with self.assertRaises(queues.ItemInterpreterDestroyed): + queue.get() + self.assertEqual(queue.qsize(), 2) + + obj2 = queue.get() + self.assertIs(obj2, queues.UNBOUND) + self.assertEqual(queue.qsize(), 1) + + obj3 = queue.get() + self.assertIs(obj3, queues.UNBOUND) + self.assertEqual(queue.qsize(), 0) + + def test_put_cleared_with_subinterpreter_multiple(self): + queue = queues.create() + interp1 = interpreters.create() + interp2 = interpreters.create() + + queue.put(1, syncobj=True) + _run_output(interp1, dedent(f""" + from test.support.interpreters import queues + queue = queues.Queue({queue.id}) + obj1 = queue.get() + queue.put(2, syncobj=True, unbound=queues.UNBOUND) + queue.put(obj1, syncobj=True, unbound=queues.UNBOUND_REMOVE) + """)) + _run_output(interp2, dedent(f""" + from test.support.interpreters import queues + queue = queues.Queue({queue.id}) + obj2 = queue.get() + obj1 = queue.get() + """)) + self.assertEqual(queue.qsize(), 0) + queue.put(3) + _run_output(interp1, dedent(""" + queue.put(4, syncobj=True, unbound=queues.UNBOUND) + # interp closed here + queue.put(5, syncobj=True, unbound=queues.UNBOUND_REMOVE) + queue.put(6, syncobj=True, unbound=queues.UNBOUND) + """)) + _run_output(interp2, dedent(""" + queue.put(7, syncobj=True, unbound=queues.UNBOUND_ERROR) + # interp closed here + queue.put(obj1, syncobj=True, unbound=queues.UNBOUND_ERROR) + queue.put(obj2, syncobj=True, unbound=queues.UNBOUND_REMOVE) + queue.put(8, syncobj=True, unbound=queues.UNBOUND) + """)) + _run_output(interp1, dedent(""" + queue.put(9, syncobj=True, unbound=queues.UNBOUND_REMOVE) + queue.put(10, syncobj=True, unbound=queues.UNBOUND) + """)) + self.assertEqual(queue.qsize(), 10) + + obj3 = queue.get() + self.assertEqual(obj3, 3) + self.assertEqual(queue.qsize(), 9) + + obj4 = queue.get() + self.assertEqual(obj4, 4) + self.assertEqual(queue.qsize(), 8) + + del interp1 + self.assertEqual(queue.qsize(), 6) + + # obj5 was removed + + obj6 = queue.get() + self.assertIs(obj6, queues.UNBOUND) + self.assertEqual(queue.qsize(), 5) + + obj7 = queue.get() + self.assertEqual(obj7, 7) + self.assertEqual(queue.qsize(), 4) + + del interp2 + self.assertEqual(queue.qsize(), 3) + + # obj1 + with self.assertRaises(queues.ItemInterpreterDestroyed): + queue.get() + self.assertEqual(queue.qsize(), 2) + + # obj2 was removed + + obj8 = queue.get() + self.assertIs(obj8, queues.UNBOUND) + self.assertEqual(queue.qsize(), 1) + + # obj9 was removed + + obj10 = queue.get() + self.assertIs(obj10, queues.UNBOUND) + self.assertEqual(queue.qsize(), 0) + + def test_put_get_different_threads(self): + queue1 = queues.create() + queue2 = queues.create() + + def f(): + while True: + try: + obj = queue1.get(timeout=0.1) + break + except queues.QueueEmpty: + continue + queue2.put(obj, syncobj=True) + t = threading.Thread(target=f) + t.start() + + orig = b'spam' + queue1.put(orig, syncobj=True) + obj = queue2.get() + t.join() + + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) + + +if __name__ == '__main__': + # Test needs to be a package, so we can do relative imports. + unittest.main() diff --git a/Lib/test/test_interpreters/test_stress.py b/Lib/test/test_interpreters/test_stress.py new file mode 100644 index 00000000000..a39c9352f72 --- /dev/null +++ b/Lib/test/test_interpreters/test_stress.py @@ -0,0 +1,89 @@ +import threading +import unittest + +from test import support +from test.support import import_helper +from test.support import threading_helper +# Raise SkipTest if subinterpreters not supported. +import_helper.import_module('_interpreters') +from test.support import interpreters +from test.support.interpreters import InterpreterError +from .utils import TestBase + + +class StressTests(TestBase): + + # In these tests we generally want a lot of interpreters, + # but not so many that any test takes too long. + + @support.requires_resource('cpu') + def test_create_many_sequential(self): + alive = [] + for _ in range(100): + interp = interpreters.create() + alive.append(interp) + del alive + support.gc_collect() + + @support.bigmemtest(size=200, memuse=32*2**20, dry_run=False) + def test_create_many_threaded(self, size): + alive = [] + start = threading.Event() + def task(): + # try to create all interpreters simultaneously + if not start.wait(support.SHORT_TIMEOUT): + raise TimeoutError + interp = interpreters.create() + alive.append(interp) + threads = [threading.Thread(target=task) for _ in range(size)] + with threading_helper.start_threads(threads): + start.set() + del alive + support.gc_collect() + + @threading_helper.requires_working_threading() + @support.bigmemtest(size=200, memuse=34*2**20, dry_run=False) + def test_many_threads_running_interp_in_other_interp(self, size): + start = threading.Event() + interp = interpreters.create() + + script = f"""if True: + import _interpreters + _interpreters.run_string({interp.id}, '1') + """ + + def run(): + interp = interpreters.create() + alreadyrunning = (f'{interpreters.InterpreterError}: ' + 'interpreter already running') + # try to run all interpreters simultaneously + if not start.wait(support.SHORT_TIMEOUT): + raise TimeoutError + success = False + while not success: + try: + interp.exec(script) + except interpreters.ExecutionFailed as exc: + if exc.excinfo.msg != 'interpreter already running': + raise # re-raise + assert exc.excinfo.type.__name__ == 'InterpreterError' + else: + success = True + + threads = [threading.Thread(target=run) for _ in range(size)] + with threading_helper.start_threads(threads): + start.set() + support.gc_collect() + + def test_create_interpreter_no_memory(self): + import _interpreters + _testcapi = import_helper.import_module("_testcapi") + + with self.assertRaises(InterpreterError): + _testcapi.set_nomemory(0, 1) + _interpreters.create() + + +if __name__ == '__main__': + # Test needs to be a package, so we can do relative imports. + unittest.main() diff --git a/Lib/test/test_interpreters/utils.py b/Lib/test/test_interpreters/utils.py new file mode 100644 index 00000000000..980289f7ee7 --- /dev/null +++ b/Lib/test/test_interpreters/utils.py @@ -0,0 +1,690 @@ +from collections import namedtuple +import contextlib +import json +import io +import logging +import os +import os.path +import pickle +import queue +#import select +import subprocess +import sys +import tempfile +from textwrap import dedent, indent +import threading +import types +import unittest +import warnings + +from test import support + +# We would use test.support.import_helper.import_module(), +# but the indirect import of test.support.os_helper causes refleaks. +try: + import _interpreters +except ImportError as exc: + raise unittest.SkipTest(str(exc)) +from test.support import interpreters + + +try: + import _testinternalcapi + import _testcapi +except ImportError: + _testinternalcapi = None + _testcapi = None + +def requires_test_modules(func): + return unittest.skipIf(_testinternalcapi is None, "test requires _testinternalcapi module")(func) + + +def _dump_script(text): + lines = text.splitlines() + print() + print('-' * 20) + for i, line in enumerate(lines, 1): + print(f' {i:>{len(str(len(lines)))}} {line}') + print('-' * 20) + + +def _close_file(file): + try: + if hasattr(file, 'close'): + file.close() + else: + os.close(file) + except OSError as exc: + if exc.errno != 9: + raise # re-raise + # It was closed already. + + +def pack_exception(exc=None): + captured = _interpreters.capture_exception(exc) + data = dict(captured.__dict__) + data['type'] = dict(captured.type.__dict__) + return json.dumps(data) + + +def unpack_exception(packed): + try: + data = json.loads(packed) + except json.decoder.JSONDecodeError as e: + logging.getLogger(__name__).warning('incomplete exception data', exc_info=e) + print(packed if isinstance(packed, str) else packed.decode('utf-8')) + return None + exc = types.SimpleNamespace(**data) + exc.type = types.SimpleNamespace(**exc.type) + return exc; + + +class CapturingResults: + + STDIO = dedent("""\ + with open({w_pipe}, 'wb', buffering=0) as _spipe_{stream}: + _captured_std{stream} = io.StringIO() + with contextlib.redirect_std{stream}(_captured_std{stream}): + ######################### + # begin wrapped script + + {indented} + + # end wrapped script + ######################### + text = _captured_std{stream}.getvalue() + _spipe_{stream}.write(text.encode('utf-8')) + """)[:-1] + EXC = dedent("""\ + with open({w_pipe}, 'wb', buffering=0) as _spipe_exc: + try: + ######################### + # begin wrapped script + + {indented} + + # end wrapped script + ######################### + except Exception as exc: + text = _interp_utils.pack_exception(exc) + _spipe_exc.write(text.encode('utf-8')) + """)[:-1] + + @classmethod + def wrap_script(cls, script, *, stdout=True, stderr=False, exc=False): + script = dedent(script).strip(os.linesep) + imports = [ + f'import {__name__} as _interp_utils', + ] + wrapped = script + + # Handle exc. + if exc: + exc = os.pipe() + r_exc, w_exc = exc + indented = wrapped.replace('\n', '\n ') + wrapped = cls.EXC.format( + w_pipe=w_exc, + indented=indented, + ) + else: + exc = None + + # Handle stdout. + if stdout: + imports.extend([ + 'import contextlib, io', + ]) + stdout = os.pipe() + r_out, w_out = stdout + indented = wrapped.replace('\n', '\n ') + wrapped = cls.STDIO.format( + w_pipe=w_out, + indented=indented, + stream='out', + ) + else: + stdout = None + + # Handle stderr. + if stderr == 'stdout': + stderr = None + elif stderr: + if not stdout: + imports.extend([ + 'import contextlib, io', + ]) + stderr = os.pipe() + r_err, w_err = stderr + indented = wrapped.replace('\n', '\n ') + wrapped = cls.STDIO.format( + w_pipe=w_err, + indented=indented, + stream='err', + ) + else: + stderr = None + + if wrapped == script: + raise NotImplementedError + else: + for line in imports: + wrapped = f'{line}{os.linesep}{wrapped}' + + results = cls(stdout, stderr, exc) + return wrapped, results + + def __init__(self, out, err, exc): + self._rf_out = None + self._rf_err = None + self._rf_exc = None + self._w_out = None + self._w_err = None + self._w_exc = None + + if out is not None: + r_out, w_out = out + self._rf_out = open(r_out, 'rb', buffering=0) + self._w_out = w_out + + if err is not None: + r_err, w_err = err + self._rf_err = open(r_err, 'rb', buffering=0) + self._w_err = w_err + + if exc is not None: + r_exc, w_exc = exc + self._rf_exc = open(r_exc, 'rb', buffering=0) + self._w_exc = w_exc + + self._buf_out = b'' + self._buf_err = b'' + self._buf_exc = b'' + self._exc = None + + self._closed = False + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + @property + def closed(self): + return self._closed + + def close(self): + if self._closed: + return + self._closed = True + + if self._w_out is not None: + _close_file(self._w_out) + self._w_out = None + if self._w_err is not None: + _close_file(self._w_err) + self._w_err = None + if self._w_exc is not None: + _close_file(self._w_exc) + self._w_exc = None + + self._capture() + + if self._rf_out is not None: + _close_file(self._rf_out) + self._rf_out = None + if self._rf_err is not None: + _close_file(self._rf_err) + self._rf_err = None + if self._rf_exc is not None: + _close_file(self._rf_exc) + self._rf_exc = None + + def _capture(self): + # Ideally this is called only after the script finishes + # (and thus has closed the write end of the pipe. + if self._rf_out is not None: + chunk = self._rf_out.read(100) + while chunk: + self._buf_out += chunk + chunk = self._rf_out.read(100) + if self._rf_err is not None: + chunk = self._rf_err.read(100) + while chunk: + self._buf_err += chunk + chunk = self._rf_err.read(100) + if self._rf_exc is not None: + chunk = self._rf_exc.read(100) + while chunk: + self._buf_exc += chunk + chunk = self._rf_exc.read(100) + + def _unpack_stdout(self): + return self._buf_out.decode('utf-8') + + def _unpack_stderr(self): + return self._buf_err.decode('utf-8') + + def _unpack_exc(self): + if self._exc is not None: + return self._exc + if not self._buf_exc: + return None + self._exc = unpack_exception(self._buf_exc) + return self._exc + + def stdout(self): + if self.closed: + return self.final().stdout + self._capture() + return self._unpack_stdout() + + def stderr(self): + if self.closed: + return self.final().stderr + self._capture() + return self._unpack_stderr() + + def exc(self): + if self.closed: + return self.final().exc + self._capture() + return self._unpack_exc() + + def final(self, *, force=False): + try: + return self._final + except AttributeError: + if not self._closed: + if not force: + raise Exception('no final results available yet') + else: + return CapturedResults.Proxy(self) + self._final = CapturedResults( + self._unpack_stdout(), + self._unpack_stderr(), + self._unpack_exc(), + ) + return self._final + + +class CapturedResults(namedtuple('CapturedResults', 'stdout stderr exc')): + + class Proxy: + def __init__(self, capturing): + self._capturing = capturing + def _finish(self): + if self._capturing is None: + return + self._final = self._capturing.final() + self._capturing = None + def __iter__(self): + self._finish() + yield from self._final + def __len__(self): + self._finish() + return len(self._final) + def __getattr__(self, name): + self._finish() + if name.startswith('_'): + raise AttributeError(name) + return getattr(self._final, name) + + def raise_if_failed(self): + if self.exc is not None: + raise interpreters.ExecutionFailed(self.exc) + + +def _captured_script(script, *, stdout=True, stderr=False, exc=False): + return CapturingResults.wrap_script( + script, + stdout=stdout, + stderr=stderr, + exc=exc, + ) + + +def clean_up_interpreters(): + for interp in interpreters.list_all(): + if interp.id == 0: # main + continue + try: + interp.close() + except _interpreters.InterpreterError: + pass # already destroyed + + +def _run_output(interp, request, init=None): + script, results = _captured_script(request) + with results: + if init: + interp.prepare_main(init) + interp.exec(script) + return results.stdout() + + +@contextlib.contextmanager +def _running(interp): + r, w = os.pipe() + def run(): + interp.exec(dedent(f""" + # wait for "signal" + with open({r}) as rpipe: + rpipe.read() + """)) + + t = threading.Thread(target=run) + t.start() + + yield + + with open(w, 'w') as spipe: + spipe.write('done') + t.join() + + +class TestBase(unittest.TestCase): + + def tearDown(self): + clean_up_interpreters() + + def pipe(self): + def ensure_closed(fd): + try: + os.close(fd) + except OSError: + pass + r, w = os.pipe() + self.addCleanup(lambda: ensure_closed(r)) + self.addCleanup(lambda: ensure_closed(w)) + return r, w + + def temp_dir(self): + tempdir = tempfile.mkdtemp() + tempdir = os.path.realpath(tempdir) + from test.support import os_helper + self.addCleanup(lambda: os_helper.rmtree(tempdir)) + return tempdir + + @contextlib.contextmanager + def captured_thread_exception(self): + ctx = types.SimpleNamespace(caught=None) + def excepthook(args): + ctx.caught = args + orig_excepthook = threading.excepthook + threading.excepthook = excepthook + try: + yield ctx + finally: + threading.excepthook = orig_excepthook + + def make_script(self, filename, dirname=None, text=None): + if text: + text = dedent(text) + if dirname is None: + dirname = self.temp_dir() + filename = os.path.join(dirname, filename) + + os.makedirs(os.path.dirname(filename), exist_ok=True) + with open(filename, 'w', encoding='utf-8') as outfile: + outfile.write(text or '') + return filename + + def make_module(self, name, pathentry=None, text=None): + if text: + text = dedent(text) + if pathentry is None: + pathentry = self.temp_dir() + else: + os.makedirs(pathentry, exist_ok=True) + *subnames, basename = name.split('.') + + dirname = pathentry + for subname in subnames: + dirname = os.path.join(dirname, subname) + if os.path.isdir(dirname): + pass + elif os.path.exists(dirname): + raise Exception(dirname) + else: + os.mkdir(dirname) + initfile = os.path.join(dirname, '__init__.py') + if not os.path.exists(initfile): + with open(initfile, 'w'): + pass + filename = os.path.join(dirname, basename + '.py') + + with open(filename, 'w', encoding='utf-8') as outfile: + outfile.write(text or '') + return filename + + @support.requires_subprocess() + def run_python(self, *argv): + proc = subprocess.run( + [sys.executable, *argv], + capture_output=True, + text=True, + ) + return proc.returncode, proc.stdout, proc.stderr + + def assert_python_ok(self, *argv): + exitcode, stdout, stderr = self.run_python(*argv) + self.assertNotEqual(exitcode, 1) + return stdout, stderr + + def assert_python_failure(self, *argv): + exitcode, stdout, stderr = self.run_python(*argv) + self.assertNotEqual(exitcode, 0) + return stdout, stderr + + def assert_ns_equal(self, ns1, ns2, msg=None): + # This is mostly copied from TestCase.assertDictEqual. + self.assertEqual(type(ns1), type(ns2)) + if ns1 == ns2: + return + + import difflib + import pprint + from unittest.util import _common_shorten_repr + standardMsg = '%s != %s' % _common_shorten_repr(ns1, ns2) + diff = ('\n' + '\n'.join(difflib.ndiff( + pprint.pformat(vars(ns1)).splitlines(), + pprint.pformat(vars(ns2)).splitlines()))) + diff = f'namespace({diff})' + standardMsg = self._truncateMessage(standardMsg, diff) + self.fail(self._formatMessage(msg, standardMsg)) + + def _run_string(self, interp, script): + wrapped, results = _captured_script(script, exc=False) + #_dump_script(wrapped) + with results: + if isinstance(interp, interpreters.Interpreter): + interp.exec(script) + else: + err = _interpreters.run_string(interp, wrapped) + if err is not None: + return None, err + return results.stdout(), None + + def run_and_capture(self, interp, script): + text, err = self._run_string(interp, script) + if err is not None: + raise interpreters.ExecutionFailed(err) + else: + return text + + def interp_exists(self, interpid): + try: + _interpreters.whence(interpid) + except _interpreters.InterpreterNotFoundError: + return False + else: + return True + + @requires_test_modules + @contextlib.contextmanager + def interpreter_from_capi(self, config=None, whence=None): + if config is False: + if whence is None: + whence = _interpreters.WHENCE_LEGACY_CAPI + else: + assert whence in (_interpreters.WHENCE_LEGACY_CAPI, + _interpreters.WHENCE_UNKNOWN), repr(whence) + config = None + elif config is True: + config = _interpreters.new_config('default') + elif config is None: + if whence not in ( + _interpreters.WHENCE_LEGACY_CAPI, + _interpreters.WHENCE_UNKNOWN, + ): + config = _interpreters.new_config('legacy') + elif isinstance(config, str): + config = _interpreters.new_config(config) + + if whence is None: + whence = _interpreters.WHENCE_XI + + interpid = _testinternalcapi.create_interpreter(config, whence=whence) + try: + yield interpid + finally: + try: + _testinternalcapi.destroy_interpreter(interpid) + except _interpreters.InterpreterNotFoundError: + pass + + @contextlib.contextmanager + def interpreter_obj_from_capi(self, config='legacy'): + with self.interpreter_from_capi(config) as interpid: + interp = interpreters.Interpreter( + interpid, + _whence=_interpreters.WHENCE_CAPI, + _ownsref=False, + ) + yield interp, interpid + + @contextlib.contextmanager + def capturing(self, script): + wrapped, capturing = _captured_script(script, stdout=True, exc=True) + #_dump_script(wrapped) + with capturing: + yield wrapped, capturing.final(force=True) + + @requires_test_modules + def run_from_capi(self, interpid, script, *, main=False): + with self.capturing(script) as (wrapped, results): + rc = _testinternalcapi.exec_interpreter(interpid, wrapped, main=main) + assert rc == 0, rc + results.raise_if_failed() + return results.stdout + + @contextlib.contextmanager + def _running(self, run_interp, exec_interp): + token = b'\0' + r_in, w_in = self.pipe() + r_out, w_out = self.pipe() + + def close(): + _close_file(r_in) + _close_file(w_in) + _close_file(r_out) + _close_file(w_out) + + # Start running (and wait). + script = dedent(f""" + import os + try: + # handshake + token = os.read({r_in}, 1) + os.write({w_out}, token) + # Wait for the "done" message. + os.read({r_in}, 1) + except BrokenPipeError: + pass + except OSError as exc: + if exc.errno != 9: + raise # re-raise + # It was closed already. + """) + failed = None + def run(): + nonlocal failed + try: + run_interp(script) + except Exception as exc: + failed = exc + close() + t = threading.Thread(target=run) + t.start() + + # handshake + try: + os.write(w_in, token) + token2 = os.read(r_out, 1) + assert token2 == token, (token2, token) + except OSError: + t.join() + if failed is not None: + raise failed + + # CM __exit__() + try: + try: + yield + finally: + # Send "done". + os.write(w_in, b'\0') + finally: + close() + t.join() + if failed is not None: + raise failed + + @contextlib.contextmanager + def running(self, interp): + if isinstance(interp, int): + interpid = interp + def exec_interp(script): + exc = _interpreters.exec(interpid, script) + assert exc is None, exc + run_interp = exec_interp + else: + def run_interp(script): + text = self.run_and_capture(interp, script) + assert text == '', repr(text) + def exec_interp(script): + interp.exec(script) + with self._running(run_interp, exec_interp): + yield + + @requires_test_modules + @contextlib.contextmanager + def running_from_capi(self, interpid, *, main=False): + def run_interp(script): + text = self.run_from_capi(interpid, script, main=main) + assert text == '', repr(text) + def exec_interp(script): + rc = _testinternalcapi.exec_interpreter(interpid, script) + assert rc == 0, rc + with self._running(run_interp, exec_interp): + yield + + @requires_test_modules + def run_temp_from_capi(self, script, config='legacy'): + if config is False: + # Force using Py_NewInterpreter(). + run_in_interp = (lambda s, c: _testcapi.run_in_subinterp(s)) + config = None + else: + run_in_interp = _testinternalcapi.run_in_subinterp_with_config + if config is True: + config = 'default' + if isinstance(config, str): + config = _interpreters.new_config(config) + with self.capturing(script) as (wrapped, results): + rc = run_in_interp(wrapped, config) + assert rc == 0, rc + results.raise_if_failed() + return results.stdout diff --git a/Lib/test/test_lltrace.py b/Lib/test/test_lltrace.py new file mode 100644 index 00000000000..f638b649bfb --- /dev/null +++ b/Lib/test/test_lltrace.py @@ -0,0 +1,111 @@ +import dis +import textwrap +import unittest + +from test import support +from test.support import os_helper +from test.support.script_helper import assert_python_ok + +def example(): + x = [] + for i in range(0): + x.append(i) + x = "this is" + y = "an example" + print(x, y) + + +@unittest.skipUnless(support.Py_DEBUG, "lltrace requires Py_DEBUG") +class TestLLTrace(unittest.TestCase): + + def run_code(self, code): + code = textwrap.dedent(code).strip() + with open(os_helper.TESTFN, 'w', encoding='utf-8') as fd: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + fd.write(code) + status, stdout, stderr = assert_python_ok(os_helper.TESTFN) + self.assertEqual(stderr, b"") + self.assertEqual(status, 0) + result = stdout.decode('utf-8') + if support.verbose: + print("\n\n--- code ---") + print(code) + print("\n--- stdout ---") + print(result) + print() + return result + + def test_lltrace(self): + stdout = self.run_code(""" + def dont_trace_1(): + a = "a" + a = 10 * a + def trace_me(): + for i in range(3): + +i + def dont_trace_2(): + x = 42 + y = -x + dont_trace_1() + __lltrace__ = 1 + trace_me() + del __lltrace__ + dont_trace_2() + """) + self.assertIn("GET_ITER", stdout) + self.assertIn("FOR_ITER", stdout) + self.assertIn("CALL_INTRINSIC_1", stdout) + self.assertIn("POP_TOP", stdout) + self.assertNotIn("BINARY_OP", stdout) + self.assertNotIn("UNARY_NEGATIVE", stdout) + + self.assertIn("'trace_me' in module '__main__'", stdout) + self.assertNotIn("dont_trace_1", stdout) + self.assertNotIn("'dont_trace_2' in module", stdout) + + def test_lltrace_different_module(self): + stdout = self.run_code(""" + from test import test_lltrace + test_lltrace.__lltrace__ = 1 + test_lltrace.example() + """) + self.assertIn("'example' in module 'test.test_lltrace'", stdout) + self.assertIn('LOAD_CONST', stdout) + self.assertIn('FOR_ITER', stdout) + self.assertIn('this is an example', stdout) + + # check that offsets match the output of dis.dis() + instr_map = {i.offset: i for i in dis.get_instructions(example, adaptive=True)} + for line in stdout.splitlines(): + offset, colon, opname_oparg = line.partition(":") + if not colon: + continue + offset = int(offset) + opname_oparg = opname_oparg.split() + if len(opname_oparg) == 2: + opname, oparg = opname_oparg + oparg = int(oparg) + else: + (opname,) = opname_oparg + oparg = None + self.assertEqual(instr_map[offset].opname, opname) + self.assertEqual(instr_map[offset].arg, oparg) + + def test_lltrace_does_not_crash_on_subscript_operator(self): + # If this test fails, it will reproduce a crash reported as + # bpo-34113. The crash happened at the command line console of + # debug Python builds with __lltrace__ enabled (only possible in console), + # when the internal Python stack was negatively adjusted + stdout = self.run_code(""" + import code + + console = code.InteractiveConsole() + console.push('__lltrace__ = 1') + console.push('a = [1, 2, 3]') + console.push('a[0] = 1') + print('unreachable if bug exists') + """) + self.assertIn("unreachable if bug exists", stdout) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py index 59c74fd0d41..c0361518e29 100644 --- a/Lib/test/test_module/__init__.py +++ b/Lib/test/test_module/__init__.py @@ -1,8 +1,10 @@ # Test the module type +import importlib.machinery import unittest import weakref from test.support import gc_collect from test.support import import_helper +from test.support import suppress_immortalization from test.support.script_helper import assert_python_ok import sys @@ -29,7 +31,7 @@ def test_uninitialized(self): self.fail("__name__ = %s" % repr(s)) except AttributeError: pass - self.assertEqual(foo.__doc__, ModuleType.__doc__) + self.assertEqual(foo.__doc__, ModuleType.__doc__ or '') def test_uninitialized_missing_getattr(self): # Issue 8297 @@ -119,6 +121,7 @@ def __del__(self): gc_collect() self.assertEqual(destroyed, [1]) + @suppress_immortalization() def test_weakref(self): m = ModuleType("foo") wr = weakref.ref(m) @@ -270,11 +273,39 @@ def test_module_repr_source(self): self.assertEqual(r[-len(ends_with):], ends_with, '{!r} does not end with {!r}'.format(r, ends_with)) - # TODO: RUSTPYTHON - @unittest.expectedFailure + def test_module_repr_with_namespace_package(self): + m = ModuleType('foo') + loader = importlib.machinery.NamespaceLoader('foo', ['bar'], 'baz') + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + self.assertEqual(repr(m), "") + + def test_module_repr_with_namespace_package_and_custom_loader(self): + m = ModuleType('foo') + loader = BareLoader() + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + expected_repr_pattern = r"\)>" + self.assertRegex(repr(m), expected_repr_pattern) + self.assertNotIn('from', repr(m)) + + def test_module_repr_with_fake_namespace_package(self): + m = ModuleType('foo') + loader = BareLoader() + loader._path = ['spam'] + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + expected_repr_pattern = r"\)>" + self.assertRegex(repr(m), expected_repr_pattern) + self.assertNotIn('from', repr(m)) + + @unittest.expectedFailure # TODO: RUSTPYTHON def test_module_finalization_at_shutdown(self): # Module globals and builtins should still be available during shutdown - rc, out, err = assert_python_ok("-c", "from test import final_a") + rc, out, err = assert_python_ok("-c", "from test.test_module import final_a") self.assertFalse(err) lines = out.splitlines() self.assertEqual(set(lines), { @@ -335,8 +366,6 @@ def test_annotations_are_created_correctly(self): ann_module4 = import_helper.import_fresh_module( 'test.typinganndata.ann_module4', ) - self.assertFalse("__annotations__" in ann_module4.__dict__) - self.assertEqual(ann_module4.__annotations__, {"a": int, "b": str}) self.assertTrue("__annotations__" in ann_module4.__dict__) del ann_module4.__annotations__ self.assertFalse("__annotations__" in ann_module4.__dict__) diff --git a/Lib/test/test_module/final_a.py b/Lib/test/test_module/final_a.py new file mode 100644 index 00000000000..a983f311124 --- /dev/null +++ b/Lib/test/test_module/final_a.py @@ -0,0 +1,19 @@ +""" +Fodder for module finalization tests in test_module. +""" + +import shutil +import test.test_module.final_b + +x = 'a' + +class C: + def __del__(self): + # Inspect module globals and builtins + print("x =", x) + print("final_b.x =", test.test_module.final_b.x) + print("shutil.rmtree =", getattr(shutil.rmtree, '__name__', None)) + print("len =", getattr(len, '__name__', None)) + +c = C() +_underscored = C() diff --git a/Lib/test/test_module/final_b.py b/Lib/test/test_module/final_b.py new file mode 100644 index 00000000000..f3e8d559490 --- /dev/null +++ b/Lib/test/test_module/final_b.py @@ -0,0 +1,19 @@ +""" +Fodder for module finalization tests in test_module. +""" + +import shutil +import test.test_module.final_a + +x = 'b' + +class C: + def __del__(self): + # Inspect module globals and builtins + print("x =", x) + print("final_a.x =", test.test_module.final_a.x) + print("shutil.rmtree =", getattr(shutil.rmtree, '__name__', None)) + print("len =", getattr(len, '__name__', None)) + +c = C() +_underscored = C() diff --git a/Lib/test/test_osx_env.py b/Lib/test/test_osx_env.py new file mode 100644 index 00000000000..80198edcb80 --- /dev/null +++ b/Lib/test/test_osx_env.py @@ -0,0 +1,34 @@ +""" +Test suite for OS X interpreter environment variables. +""" + +from test.support.os_helper import EnvironmentVarGuard +import subprocess +import sys +import sysconfig +import unittest + +@unittest.skipUnless(sys.platform == 'darwin' and + sysconfig.get_config_var('WITH_NEXT_FRAMEWORK'), + 'unnecessary on this platform') +class OSXEnvironmentVariableTestCase(unittest.TestCase): + def _check_sys(self, ev, cond, sv, val = sys.executable + 'dummy'): + with EnvironmentVarGuard() as evg: + subpc = [str(sys.executable), '-c', + 'import sys; sys.exit(2 if "%s" %s %s else 3)' % (val, cond, sv)] + # ensure environment variable does not exist + evg.unset(ev) + # test that test on sys.xxx normally fails + rc = subprocess.call(subpc) + self.assertEqual(rc, 3, "expected %s not %s %s" % (ev, cond, sv)) + # set environ variable + evg.set(ev, val) + # test that sys.xxx has been influenced by the environ value + rc = subprocess.call(subpc) + self.assertEqual(rc, 2, "expected %s %s %s" % (ev, cond, sv)) + + def test_pythonexecutable_sets_sys_executable(self): + self._check_sys('PYTHONEXECUTABLE', '==', 'sys.executable') + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_pep646_syntax.py b/Lib/test/test_pep646_syntax.py new file mode 100644 index 00000000000..aac089b190b --- /dev/null +++ b/Lib/test/test_pep646_syntax.py @@ -0,0 +1,329 @@ +import doctest +import unittest + +doctests = """ + +Setup + + >>> class AClass: + ... def __init__(self): + ... self._setitem_name = None + ... self._setitem_val = None + ... self._delitem_name = None + ... def __setitem__(self, name, val): + ... self._delitem_name = None + ... self._setitem_name = name + ... self._setitem_val = val + ... def __repr__(self): + ... if self._setitem_name is not None: + ... return f"A[{self._setitem_name}]={self._setitem_val}" + ... elif self._delitem_name is not None: + ... return f"delA[{self._delitem_name}]" + ... def __getitem__(self, name): + ... return ParameterisedA(name) + ... def __delitem__(self, name): + ... self._setitem_name = None + ... self._delitem_name = name + ... + >>> class ParameterisedA: + ... def __init__(self, name): + ... self._name = name + ... def __repr__(self): + ... return f"A[{self._name}]" + ... def __iter__(self): + ... for p in self._name: + ... yield p + >>> class B: + ... def __iter__(self): + ... yield StarredB() + ... def __repr__(self): + ... return "B" + >>> class StarredB: + ... def __repr__(self): + ... return "StarredB" + >>> A = AClass() + >>> b = B() + +Slices that are supposed to work, starring our custom B class + + >>> A[*b] + A[(StarredB,)] + >>> A[*b] = 1; A + A[(StarredB,)]=1 + >>> del A[*b]; A + delA[(StarredB,)] + + >>> A[*b, *b] + A[(StarredB, StarredB)] + >>> A[*b, *b] = 1; A + A[(StarredB, StarredB)]=1 + >>> del A[*b, *b]; A + delA[(StarredB, StarredB)] + + >>> A[b, *b] + A[(B, StarredB)] + >>> A[b, *b] = 1; A + A[(B, StarredB)]=1 + >>> del A[b, *b]; A + delA[(B, StarredB)] + + >>> A[*b, b] + A[(StarredB, B)] + >>> A[*b, b] = 1; A + A[(StarredB, B)]=1 + >>> del A[*b, b]; A + delA[(StarredB, B)] + + >>> A[b, b, *b] + A[(B, B, StarredB)] + >>> A[b, b, *b] = 1; A + A[(B, B, StarredB)]=1 + >>> del A[b, b, *b]; A + delA[(B, B, StarredB)] + + >>> A[*b, b, b] + A[(StarredB, B, B)] + >>> A[*b, b, b] = 1; A + A[(StarredB, B, B)]=1 + >>> del A[*b, b, b]; A + delA[(StarredB, B, B)] + + >>> A[b, *b, b] + A[(B, StarredB, B)] + >>> A[b, *b, b] = 1; A + A[(B, StarredB, B)]=1 + >>> del A[b, *b, b]; A + delA[(B, StarredB, B)] + + >>> A[b, b, *b, b] + A[(B, B, StarredB, B)] + >>> A[b, b, *b, b] = 1; A + A[(B, B, StarredB, B)]=1 + >>> del A[b, b, *b, b]; A + delA[(B, B, StarredB, B)] + + >>> A[b, *b, b, b] + A[(B, StarredB, B, B)] + >>> A[b, *b, b, b] = 1; A + A[(B, StarredB, B, B)]=1 + >>> del A[b, *b, b, b]; A + delA[(B, StarredB, B, B)] + + >>> A[A[b, *b, b]] + A[A[(B, StarredB, B)]] + >>> A[A[b, *b, b]] = 1; A + A[A[(B, StarredB, B)]]=1 + >>> del A[A[b, *b, b]]; A + delA[A[(B, StarredB, B)]] + + >>> A[*A[b, *b, b]] + A[(B, StarredB, B)] + >>> A[*A[b, *b, b]] = 1; A + A[(B, StarredB, B)]=1 + >>> del A[*A[b, *b, b]]; A + delA[(B, StarredB, B)] + + >>> A[b, ...] + A[(B, Ellipsis)] + >>> A[b, ...] = 1; A + A[(B, Ellipsis)]=1 + >>> del A[b, ...]; A + delA[(B, Ellipsis)] + + >>> A[*A[b, ...]] + A[(B, Ellipsis)] + >>> A[*A[b, ...]] = 1; A + A[(B, Ellipsis)]=1 + >>> del A[*A[b, ...]]; A + delA[(B, Ellipsis)] + +Slices that are supposed to work, starring a list + + >>> l = [1, 2, 3] + + >>> A[*l] + A[(1, 2, 3)] + >>> A[*l] = 1; A + A[(1, 2, 3)]=1 + >>> del A[*l]; A + delA[(1, 2, 3)] + + >>> A[*l, 4] + A[(1, 2, 3, 4)] + >>> A[*l, 4] = 1; A + A[(1, 2, 3, 4)]=1 + >>> del A[*l, 4]; A + delA[(1, 2, 3, 4)] + + >>> A[0, *l] + A[(0, 1, 2, 3)] + >>> A[0, *l] = 1; A + A[(0, 1, 2, 3)]=1 + >>> del A[0, *l]; A + delA[(0, 1, 2, 3)] + + >>> A[1:2, *l] + A[(slice(1, 2, None), 1, 2, 3)] + >>> A[1:2, *l] = 1; A + A[(slice(1, 2, None), 1, 2, 3)]=1 + >>> del A[1:2, *l]; A + delA[(slice(1, 2, None), 1, 2, 3)] + + >>> repr(A[1:2, *l]) == repr(A[1:2, 1, 2, 3]) + True + +Slices that are supposed to work, starring a tuple + + >>> t = (1, 2, 3) + + >>> A[*t] + A[(1, 2, 3)] + >>> A[*t] = 1; A + A[(1, 2, 3)]=1 + >>> del A[*t]; A + delA[(1, 2, 3)] + + >>> A[*t, 4] + A[(1, 2, 3, 4)] + >>> A[*t, 4] = 1; A + A[(1, 2, 3, 4)]=1 + >>> del A[*t, 4]; A + delA[(1, 2, 3, 4)] + + >>> A[0, *t] + A[(0, 1, 2, 3)] + >>> A[0, *t] = 1; A + A[(0, 1, 2, 3)]=1 + >>> del A[0, *t]; A + delA[(0, 1, 2, 3)] + + >>> A[1:2, *t] + A[(slice(1, 2, None), 1, 2, 3)] + >>> A[1:2, *t] = 1; A + A[(slice(1, 2, None), 1, 2, 3)]=1 + >>> del A[1:2, *t]; A + delA[(slice(1, 2, None), 1, 2, 3)] + + >>> repr(A[1:2, *t]) == repr(A[1:2, 1, 2, 3]) + True + +Starring an expression (rather than a name) in a slice + + >>> def returns_list(): + ... return [1, 2, 3] + + >>> A[returns_list()] + A[[1, 2, 3]] + >>> A[returns_list()] = 1; A + A[[1, 2, 3]]=1 + >>> del A[returns_list()]; A + delA[[1, 2, 3]] + + >>> A[returns_list(), 4] + A[([1, 2, 3], 4)] + >>> A[returns_list(), 4] = 1; A + A[([1, 2, 3], 4)]=1 + >>> del A[returns_list(), 4]; A + delA[([1, 2, 3], 4)] + + >>> A[*returns_list()] + A[(1, 2, 3)] + >>> A[*returns_list()] = 1; A + A[(1, 2, 3)]=1 + >>> del A[*returns_list()]; A + delA[(1, 2, 3)] + + >>> A[*returns_list(), 4] + A[(1, 2, 3, 4)] + >>> A[*returns_list(), 4] = 1; A + A[(1, 2, 3, 4)]=1 + >>> del A[*returns_list(), 4]; A + delA[(1, 2, 3, 4)] + + >>> A[0, *returns_list()] + A[(0, 1, 2, 3)] + >>> A[0, *returns_list()] = 1; A + A[(0, 1, 2, 3)]=1 + >>> del A[0, *returns_list()]; A + delA[(0, 1, 2, 3)] + + >>> A[*returns_list(), *returns_list()] + A[(1, 2, 3, 1, 2, 3)] + >>> A[*returns_list(), *returns_list()] = 1; A + A[(1, 2, 3, 1, 2, 3)]=1 + >>> del A[*returns_list(), *returns_list()]; A + delA[(1, 2, 3, 1, 2, 3)] + +Using both a starred object and a start:stop in a slice +(See also tests in test_syntax confirming that starring *inside* a start:stop +is *not* valid syntax.) + + >>> A[1:2, *b] + A[(slice(1, 2, None), StarredB)] + >>> A[*b, 1:2] + A[(StarredB, slice(1, 2, None))] + >>> A[1:2, *b, 1:2] + A[(slice(1, 2, None), StarredB, slice(1, 2, None))] + >>> A[*b, 1:2, *b] + A[(StarredB, slice(1, 2, None), StarredB)] + + >>> A[1:, *b] + A[(slice(1, None, None), StarredB)] + >>> A[*b, 1:] + A[(StarredB, slice(1, None, None))] + >>> A[1:, *b, 1:] + A[(slice(1, None, None), StarredB, slice(1, None, None))] + >>> A[*b, 1:, *b] + A[(StarredB, slice(1, None, None), StarredB)] + + >>> A[:1, *b] + A[(slice(None, 1, None), StarredB)] + >>> A[*b, :1] + A[(StarredB, slice(None, 1, None))] + >>> A[:1, *b, :1] + A[(slice(None, 1, None), StarredB, slice(None, 1, None))] + >>> A[*b, :1, *b] + A[(StarredB, slice(None, 1, None), StarredB)] + + >>> A[:, *b] + A[(slice(None, None, None), StarredB)] + >>> A[*b, :] + A[(StarredB, slice(None, None, None))] + >>> A[:, *b, :] + A[(slice(None, None, None), StarredB, slice(None, None, None))] + >>> A[*b, :, *b] + A[(StarredB, slice(None, None, None), StarredB)] + +*args annotated as starred expression + + >>> def f1(*args: *b): pass + >>> f1.__annotations__ + {'args': StarredB} + + >>> def f2(*args: *b, arg1): pass + >>> f2.__annotations__ + {'args': StarredB} + + >>> def f3(*args: *b, arg1: int): pass + >>> f3.__annotations__ + {'args': StarredB, 'arg1': } + + >>> def f4(*args: *b, arg1: int = 2): pass + >>> f4.__annotations__ + {'args': StarredB, 'arg1': } + + >>> def f5(*args: *b = (1,)): pass + Traceback (most recent call last): + ... + SyntaxError: invalid syntax +""" + +__test__ = {'doctests' : doctests} + +def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite()) + return tests + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py new file mode 100644 index 00000000000..3c845419a3e --- /dev/null +++ b/Lib/test/test_perf_profiler.py @@ -0,0 +1,567 @@ +import unittest +import string +import subprocess +import sys +import sysconfig +import os +import pathlib +import shutil +from test import support +from test.support.script_helper import ( + make_script, + assert_python_failure, + assert_python_ok, +) +from test.support.os_helper import temp_dir + + +if not support.has_subprocess_support: + raise unittest.SkipTest("test module requires subprocess") + +if support.check_sanitizer(address=True, memory=True, ub=True): + # gh-109580: Skip the test because it does crash randomly if Python is + # built with ASAN. + raise unittest.SkipTest("test crash randomly on ASAN/MSAN/UBSAN build") + + +def supports_trampoline_profiling(): + perf_trampoline = sysconfig.get_config_var("PY_HAVE_PERF_TRAMPOLINE") + if not perf_trampoline: + return False + return int(perf_trampoline) == 1 + + +if not supports_trampoline_profiling(): + raise unittest.SkipTest("perf trampoline profiling not supported") + + +class TestPerfTrampoline(unittest.TestCase): + def setUp(self): + super().setUp() + self.perf_files = set(pathlib.Path("/tmp/").glob("perf-*.map")) + + def tearDown(self) -> None: + super().tearDown() + files_to_delete = ( + set(pathlib.Path("/tmp/").glob("perf-*.map")) - self.perf_files + ) + for file in files_to_delete: + file.unlink() + + def test_trampoline_works(self): + code = """if 1: + def foo(): + pass + + def bar(): + foo() + + def baz(): + bar() + + baz() + """ + with temp_dir() as script_dir: + script = make_script(script_dir, "perftest", code) + with subprocess.Popen( + [sys.executable, "-Xperf", script], + text=True, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as process: + stdout, stderr = process.communicate() + + self.assertEqual(stderr, "") + self.assertEqual(stdout, "") + + perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") + self.assertTrue(perf_file.exists()) + perf_file_contents = perf_file.read_text() + perf_lines = perf_file_contents.splitlines() + expected_symbols = [ + f"py::foo:{script}", + f"py::bar:{script}", + f"py::baz:{script}", + ] + for expected_symbol in expected_symbols: + perf_line = next( + (line for line in perf_lines if expected_symbol in line), None + ) + self.assertIsNotNone( + perf_line, f"Could not find {expected_symbol} in perf file" + ) + perf_addr = perf_line.split(" ")[0] + self.assertFalse( + perf_addr.startswith("0x"), "Address should not be prefixed with 0x" + ) + self.assertTrue( + set(perf_addr).issubset(string.hexdigits), + "Address should contain only hex characters", + ) + + def test_trampoline_works_with_forks(self): + code = """if 1: + import os, sys + + def foo_fork(): + pass + + def bar_fork(): + foo_fork() + + def baz_fork(): + bar_fork() + + def foo(): + pid = os.fork() + if pid == 0: + print(os.getpid()) + baz_fork() + else: + _, status = os.waitpid(-1, 0) + sys.exit(status) + + def bar(): + foo() + + def baz(): + bar() + + baz() + """ + with temp_dir() as script_dir: + script = make_script(script_dir, "perftest", code) + with subprocess.Popen( + [sys.executable, "-Xperf", script], + text=True, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as process: + stdout, stderr = process.communicate() + + self.assertEqual(process.returncode, 0) + self.assertEqual(stderr, "") + child_pid = int(stdout.strip()) + perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") + perf_child_file = pathlib.Path(f"/tmp/perf-{child_pid}.map") + self.assertTrue(perf_file.exists()) + self.assertTrue(perf_child_file.exists()) + + perf_file_contents = perf_file.read_text() + self.assertIn(f"py::foo:{script}", perf_file_contents) + self.assertIn(f"py::bar:{script}", perf_file_contents) + self.assertIn(f"py::baz:{script}", perf_file_contents) + + child_perf_file_contents = perf_child_file.read_text() + self.assertIn(f"py::foo_fork:{script}", child_perf_file_contents) + self.assertIn(f"py::bar_fork:{script}", child_perf_file_contents) + self.assertIn(f"py::baz_fork:{script}", child_perf_file_contents) + + # The parent's map should not contain the child's symbols. + self.assertNotIn(f"py::foo_fork:{script}", perf_file_contents) + self.assertNotIn(f"py::bar_fork:{script}", perf_file_contents) + self.assertNotIn(f"py::baz_fork:{script}", perf_file_contents) + + # The child's map should not contain the parent's symbols. + self.assertNotIn(f"py::foo:{script}", child_perf_file_contents) + self.assertNotIn(f"py::bar:{script}", child_perf_file_contents) + self.assertNotIn(f"py::baz:{script}", child_perf_file_contents) + + def test_sys_api(self): + code = """if 1: + import sys + def foo(): + pass + + def spam(): + pass + + def bar(): + sys.deactivate_stack_trampoline() + foo() + sys.activate_stack_trampoline("perf") + spam() + + def baz(): + bar() + + sys.activate_stack_trampoline("perf") + baz() + """ + with temp_dir() as script_dir: + script = make_script(script_dir, "perftest", code) + with subprocess.Popen( + [sys.executable, script], + text=True, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as process: + stdout, stderr = process.communicate() + + self.assertEqual(stderr, "") + self.assertEqual(stdout, "") + + perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") + self.assertTrue(perf_file.exists()) + perf_file_contents = perf_file.read_text() + self.assertNotIn(f"py::foo:{script}", perf_file_contents) + self.assertIn(f"py::spam:{script}", perf_file_contents) + self.assertIn(f"py::bar:{script}", perf_file_contents) + self.assertIn(f"py::baz:{script}", perf_file_contents) + + def test_sys_api_with_existing_trampoline(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("perf") + sys.activate_stack_trampoline("perf") + """ + assert_python_ok("-c", code) + + def test_sys_api_with_invalid_trampoline(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("invalid") + """ + rc, out, err = assert_python_failure("-c", code) + self.assertIn("invalid backend: invalid", err.decode()) + + def test_sys_api_get_status(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("perf") + assert sys.is_stack_trampoline_active() is True + sys.deactivate_stack_trampoline() + assert sys.is_stack_trampoline_active() is False + """ + assert_python_ok("-c", code) + + def test_sys_api_perf_jit_backend(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("perf_jit") + assert sys.is_stack_trampoline_active() is True + sys.deactivate_stack_trampoline() + assert sys.is_stack_trampoline_active() is False + """ + assert_python_ok("-c", code, PYTHON_JIT="0") + + def test_sys_api_with_existing_perf_jit_trampoline(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("perf_jit") + sys.activate_stack_trampoline("perf_jit") + """ + assert_python_ok("-c", code, PYTHON_JIT="0") + + +def is_unwinding_reliable_with_frame_pointers(): + cflags = sysconfig.get_config_var("PY_CORE_CFLAGS") + if not cflags: + return False + return "no-omit-frame-pointer" in cflags and "_Py_JIT" not in cflags + + +def perf_command_works(): + try: + cmd = ["perf", "--help"] + stdout = subprocess.check_output(cmd, text=True) + except (subprocess.SubprocessError, OSError): + return False + + # perf version does not return a version number on Fedora. Use presence + # of "perf.data" in help as indicator that it's perf from Linux tools. + if "perf.data" not in stdout: + return False + + # Check that we can run a simple perf run + with temp_dir() as script_dir: + try: + output_file = script_dir + "/perf_output.perf" + cmd = ( + "perf", + "record", + "--no-buildid", + "--no-buildid-cache", + "-g", + "--call-graph=fp", + "-o", + output_file, + "--", + sys.executable, + "-c", + 'print("hello")', + ) + stdout = subprocess.check_output( + cmd, cwd=script_dir, text=True, stderr=subprocess.STDOUT + ) + except (subprocess.SubprocessError, OSError): + return False + + if "hello" not in stdout: + return False + + return True + + +def run_perf(cwd, *args, use_jit=False, **env_vars): + if env_vars: + env = os.environ.copy() + env.update(env_vars) + else: + env = None + output_file = cwd + "/perf_output.perf" + if not use_jit: + base_cmd = ( + "perf", + "record", + "--no-buildid", + "--no-buildid-cache", + "-g", + "--call-graph=fp", + "-o", output_file, + "--" + ) + else: + base_cmd = ( + "perf", + "record", + "--no-buildid", + "--no-buildid-cache", + "-g", + "--call-graph=dwarf,65528", + "-F99", + "-k1", + "-o", + output_file, + "--", + ) + proc = subprocess.run( + base_cmd + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + text=True, + ) + if proc.returncode: + print(proc.stderr, file=sys.stderr) + raise ValueError(f"Perf failed with return code {proc.returncode}") + + if use_jit: + jit_output_file = cwd + "/jit_output.dump" + command = ("perf", "inject", "-j", "-i", output_file, "-o", jit_output_file) + proc = subprocess.run( + command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=env, text=True + ) + if proc.returncode: + print(proc.stderr, file=sys.stderr) + raise ValueError(f"Perf failed with return code {proc.returncode}") + # Copy the jit_output_file to the output_file + os.rename(jit_output_file, output_file) + + base_cmd = ("perf", "script") + proc = subprocess.run( + ("perf", "script", "-i", output_file), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + check=True, + text=True, + ) + return proc.stdout, proc.stderr + + +class TestPerfProfilerMixin: + def run_perf(self, script_dir, perf_mode, script): + raise NotImplementedError() + + def test_python_calls_appear_in_the_stack_if_perf_activated(self): + with temp_dir() as script_dir: + code = """if 1: + def foo(n): + x = 0 + for i in range(n): + x += i + + def bar(n): + foo(n) + + def baz(n): + bar(n) + + baz(10000000) + """ + script = make_script(script_dir, "perftest", code) + stdout, stderr = self.run_perf(script_dir, script) + self.assertEqual(stderr, "") + + self.assertIn(f"py::foo:{script}", stdout) + self.assertIn(f"py::bar:{script}", stdout) + self.assertIn(f"py::baz:{script}", stdout) + + def test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated(self): + with temp_dir() as script_dir: + code = """if 1: + def foo(n): + x = 0 + for i in range(n): + x += i + + def bar(n): + foo(n) + + def baz(n): + bar(n) + + baz(10000000) + """ + script = make_script(script_dir, "perftest", code) + stdout, stderr = self.run_perf( + script_dir, script, activate_trampoline=False + ) + self.assertEqual(stderr, "") + + self.assertNotIn(f"py::foo:{script}", stdout) + self.assertNotIn(f"py::bar:{script}", stdout) + self.assertNotIn(f"py::baz:{script}", stdout) + +@unittest.skipUnless(perf_command_works(), "perf command doesn't work") +@unittest.skipUnless( + is_unwinding_reliable_with_frame_pointers(), + "Unwinding is unreliable with frame pointers", +) +class TestPerfProfiler(unittest.TestCase, TestPerfProfilerMixin): + def run_perf(self, script_dir, script, activate_trampoline=True): + if activate_trampoline: + return run_perf(script_dir, sys.executable, "-Xperf", script) + return run_perf(script_dir, sys.executable, script) + + def setUp(self): + super().setUp() + self.perf_files = set(pathlib.Path("/tmp/").glob("perf-*.map")) + + def tearDown(self) -> None: + super().tearDown() + files_to_delete = ( + set(pathlib.Path("/tmp/").glob("perf-*.map")) - self.perf_files + ) + for file in files_to_delete: + file.unlink() + + def test_pre_fork_compile(self): + code = """if 1: + import sys + import os + import sysconfig + from _testinternalcapi import ( + compile_perf_trampoline_entry, + perf_trampoline_set_persist_after_fork, + ) + + def foo_fork(): + pass + + def bar_fork(): + foo_fork() + + def foo(): + import time; time.sleep(1) + + def bar(): + foo() + + def compile_trampolines_for_all_functions(): + perf_trampoline_set_persist_after_fork(1) + for _, obj in globals().items(): + if callable(obj) and hasattr(obj, '__code__'): + compile_perf_trampoline_entry(obj.__code__) + + if __name__ == "__main__": + compile_trampolines_for_all_functions() + pid = os.fork() + if pid == 0: + print(os.getpid()) + bar_fork() + else: + bar() + """ + + with temp_dir() as script_dir: + script = make_script(script_dir, "perftest", code) + with subprocess.Popen( + [sys.executable, "-Xperf", script], + universal_newlines=True, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as process: + stdout, stderr = process.communicate() + + self.assertEqual(process.returncode, 0) + self.assertNotIn("Error:", stderr) + child_pid = int(stdout.strip()) + perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") + perf_child_file = pathlib.Path(f"/tmp/perf-{child_pid}.map") + self.assertTrue(perf_file.exists()) + self.assertTrue(perf_child_file.exists()) + + perf_file_contents = perf_file.read_text() + self.assertIn(f"py::foo:{script}", perf_file_contents) + self.assertIn(f"py::bar:{script}", perf_file_contents) + self.assertIn(f"py::foo_fork:{script}", perf_file_contents) + self.assertIn(f"py::bar_fork:{script}", perf_file_contents) + + child_perf_file_contents = perf_child_file.read_text() + self.assertIn(f"py::foo_fork:{script}", child_perf_file_contents) + self.assertIn(f"py::bar_fork:{script}", child_perf_file_contents) + + # Pre-compiled perf-map entries of a forked process must be + # identical in both the parent and child perf-map files. + perf_file_lines = perf_file_contents.split("\n") + for line in perf_file_lines: + if f"py::foo_fork:{script}" in line or f"py::bar_fork:{script}" in line: + self.assertIn(line, child_perf_file_contents) + + +def _is_perf_vesion_at_least(major, minor): + # The output of perf --version looks like "perf version 6.7-3" but + # it can also be perf version "perf version 5.15.143", or even include + # a commit hash in the version string, like "6.12.9.g242e6068fd5c" + # + # PermissionError is raised if perf does not exist on the Windows Subsystem + # for Linux, see #134987 + try: + output = subprocess.check_output(["perf", "--version"], text=True) + except (subprocess.CalledProcessError, FileNotFoundError, PermissionError): + return False + version = output.split()[2] + version = version.split("-")[0] + version = version.split(".") + version = tuple(map(int, version[:2])) + return version >= (major, minor) + + +@unittest.skipUnless(perf_command_works(), "perf command doesn't work") +@unittest.skipUnless(_is_perf_vesion_at_least(6, 6), "perf command may not work due to a perf bug") +class TestPerfProfilerWithDwarf(unittest.TestCase, TestPerfProfilerMixin): + def run_perf(self, script_dir, script, activate_trampoline=True): + if activate_trampoline: + return run_perf( + script_dir, sys.executable, "-Xperf_jit", script, use_jit=True + ) + return run_perf(script_dir, sys.executable, script, use_jit=True) + + def setUp(self): + super().setUp() + self.perf_files = set(pathlib.Path("/tmp/").glob("jit*.dump")) + self.perf_files |= set(pathlib.Path("/tmp/").glob("jitted-*.so")) + + def tearDown(self) -> None: + super().tearDown() + files_to_delete = set(pathlib.Path("/tmp/").glob("jit*.dump")) + files_to_delete |= set(pathlib.Path("/tmp/").glob("jitted-*.so")) + files_to_delete = files_to_delete - self.perf_files + for file in files_to_delete: + file.unlink() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_perfmaps.py b/Lib/test/test_perfmaps.py new file mode 100644 index 00000000000..d4c6fe0124a --- /dev/null +++ b/Lib/test/test_perfmaps.py @@ -0,0 +1,23 @@ +import os +import sys +import unittest + +try: + from _testinternalcapi import perf_map_state_teardown, write_perf_map_entry +except ImportError: + raise unittest.SkipTest("requires _testinternalcapi") + + +if sys.platform != 'linux': + raise unittest.SkipTest('Linux only') + + +class TestPerfMapWriting(unittest.TestCase): + def test_write_perf_map_entry(self): + self.assertEqual(write_perf_map_entry(0x1234, 5678, "entry1"), 0) + self.assertEqual(write_perf_map_entry(0x2345, 6789, "entry2"), 0) + with open(f"/tmp/perf-{os.getpid()}.map") as f: + perf_file_contents = f.read() + self.assertIn("1234 162e entry1", perf_file_contents) + self.assertIn("2345 1a85 entry2", perf_file_contents) + perf_map_state_teardown() diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py new file mode 100644 index 00000000000..537a9fb0b29 --- /dev/null +++ b/Lib/test/test_readline.py @@ -0,0 +1,415 @@ +""" +Very minimal unittests for parts of the readline module. +""" +import codecs +import locale +import os +import sys +import tempfile +import textwrap +import unittest +from test.support import verbose +from test.support.import_helper import import_module +from test.support.os_helper import unlink, temp_dir, TESTFN +from test.support.pty_helper import run_pty +from test.support.script_helper import assert_python_ok +from test.support.threading_helper import requires_working_threading + +# Skip tests if there is no readline module +readline = import_module('readline') + +if hasattr(readline, "_READLINE_LIBRARY_VERSION"): + is_editline = ("EditLine wrapper" in readline._READLINE_LIBRARY_VERSION) +else: + is_editline = readline.backend == "editline" + + +def setUpModule(): + if verbose: + # Python implementations other than CPython may not have + # these private attributes + if hasattr(readline, "_READLINE_VERSION"): + print(f"readline version: {readline._READLINE_VERSION:#x}") + print(f"readline runtime version: {readline._READLINE_RUNTIME_VERSION:#x}") + if hasattr(readline, "_READLINE_LIBRARY_VERSION"): + print(f"readline library version: {readline._READLINE_LIBRARY_VERSION!r}") + print(f"use libedit emulation? {is_editline}") + + +@unittest.skipUnless(hasattr(readline, "clear_history"), + "The history update test cannot be run because the " + "clear_history method is not available.") +class TestHistoryManipulation (unittest.TestCase): + """ + These tests were added to check that the libedit emulation on OSX and the + "real" readline have the same interface for history manipulation. That's + why the tests cover only a small subset of the interface. + """ + + def testHistoryUpdates(self): + readline.clear_history() + + readline.add_history("first line") + readline.add_history("second line") + + self.assertEqual(readline.get_history_item(0), None) + self.assertEqual(readline.get_history_item(1), "first line") + self.assertEqual(readline.get_history_item(2), "second line") + + readline.replace_history_item(0, "replaced line") + self.assertEqual(readline.get_history_item(0), None) + self.assertEqual(readline.get_history_item(1), "replaced line") + self.assertEqual(readline.get_history_item(2), "second line") + + self.assertEqual(readline.get_current_history_length(), 2) + + readline.remove_history_item(0) + self.assertEqual(readline.get_history_item(0), None) + self.assertEqual(readline.get_history_item(1), "second line") + + self.assertEqual(readline.get_current_history_length(), 1) + + @unittest.skipUnless(hasattr(readline, "append_history_file"), + "append_history not available") + def test_write_read_append(self): + hfile = tempfile.NamedTemporaryFile(delete=False) + hfile.close() + hfilename = hfile.name + self.addCleanup(unlink, hfilename) + + # test write-clear-read == nop + readline.clear_history() + readline.add_history("first line") + readline.add_history("second line") + readline.write_history_file(hfilename) + + readline.clear_history() + self.assertEqual(readline.get_current_history_length(), 0) + + readline.read_history_file(hfilename) + self.assertEqual(readline.get_current_history_length(), 2) + self.assertEqual(readline.get_history_item(1), "first line") + self.assertEqual(readline.get_history_item(2), "second line") + + # test append + readline.append_history_file(1, hfilename) + readline.clear_history() + readline.read_history_file(hfilename) + self.assertEqual(readline.get_current_history_length(), 3) + self.assertEqual(readline.get_history_item(1), "first line") + self.assertEqual(readline.get_history_item(2), "second line") + self.assertEqual(readline.get_history_item(3), "second line") + + # test 'no such file' behaviour + os.unlink(hfilename) + try: + readline.append_history_file(1, hfilename) + except FileNotFoundError: + pass # Some implementations return this error (libreadline). + else: + os.unlink(hfilename) # Some create it anyways (libedit). + # If the file wasn't created, unlink will fail. + # We're just testing that one of the two expected behaviors happens + # instead of an incorrect error. + + # write_history_file can create the target + readline.write_history_file(hfilename) + + # Negative values should be disallowed + with self.assertRaises(ValueError): + readline.append_history_file(-42, hfilename) + + # See gh-122431, using the minimum signed integer value caused a segfault + with self.assertRaises(ValueError): + readline.append_history_file(-2147483648, hfilename) + + def test_nonascii_history(self): + readline.clear_history() + try: + readline.add_history("entrée 1") + except UnicodeEncodeError as err: + self.skipTest("Locale cannot encode test data: " + format(err)) + readline.add_history("entrée 2") + readline.replace_history_item(1, "entrée 22") + readline.write_history_file(TESTFN) + self.addCleanup(os.remove, TESTFN) + readline.clear_history() + readline.read_history_file(TESTFN) + if is_editline: + # An add_history() call seems to be required for get_history_ + # item() to register items from the file + readline.add_history("dummy") + self.assertEqual(readline.get_history_item(1), "entrée 1") + self.assertEqual(readline.get_history_item(2), "entrée 22") + + def test_write_read_limited_history(self): + previous_length = readline.get_history_length() + self.addCleanup(readline.set_history_length, previous_length) + + readline.clear_history() + readline.add_history("first line") + readline.add_history("second line") + readline.add_history("third line") + + readline.set_history_length(2) + self.assertEqual(readline.get_history_length(), 2) + readline.write_history_file(TESTFN) + self.addCleanup(os.remove, TESTFN) + + readline.clear_history() + self.assertEqual(readline.get_current_history_length(), 0) + self.assertEqual(readline.get_history_length(), 2) + + readline.read_history_file(TESTFN) + self.assertEqual(readline.get_history_item(1), "second line") + self.assertEqual(readline.get_history_item(2), "third line") + self.assertEqual(readline.get_history_item(3), None) + + # Readline seems to report an additional history element. + self.assertIn(readline.get_current_history_length(), (2, 3)) + + +class TestReadline(unittest.TestCase): + + @unittest.skipIf(readline._READLINE_VERSION < 0x0601 and not is_editline, + "not supported in this library version") + def test_init(self): + # Issue #19884: Ensure that the ANSI sequence "\033[1034h" is not + # written into stdout when the readline module is imported and stdout + # is redirected to a pipe. + rc, stdout, stderr = assert_python_ok('-c', 'import readline', + TERM='xterm-256color') + self.assertEqual(stdout, b'') + + def test_backend(self): + self.assertIn(readline.backend, ("readline", "editline")) + + auto_history_script = """\ +import readline +readline.set_auto_history({}) +input() +print("History length:", readline.get_current_history_length()) +""" + + def test_auto_history_enabled(self): + output = run_pty(self.auto_history_script.format(True)) + # bpo-44949: Sometimes, the newline character is not written at the + # end, so don't expect it in the output. + self.assertIn(b"History length: 1", output) + + def test_auto_history_disabled(self): + output = run_pty(self.auto_history_script.format(False)) + # bpo-44949: Sometimes, the newline character is not written at the + # end, so don't expect it in the output. + self.assertIn(b"History length: 0", output) + + def test_set_complete_delims(self): + script = textwrap.dedent(""" + import readline + def complete(text, state): + if state == 0 and text == "$": + return "$complete" + return None + if readline.backend == "editline": + readline.parse_and_bind(r'bind "\\t" rl_complete') + else: + readline.parse_and_bind(r'"\\t": complete') + readline.set_completer_delims(" \\t\\n") + readline.set_completer(complete) + print(input()) + """) + + output = run_pty(script, input=b"$\t\n") + self.assertIn(b"$complete", output) + + def test_nonascii(self): + loc = locale.setlocale(locale.LC_CTYPE, None) + if loc in ('C', 'POSIX'): + # bpo-29240: On FreeBSD, if the LC_CTYPE locale is C or POSIX, + # writing and reading non-ASCII bytes into/from a TTY works, but + # readline or ncurses ignores non-ASCII bytes on read. + self.skipTest(f"the LC_CTYPE locale is {loc!r}") + if sys.flags.utf8_mode: + encoding = locale.getencoding() + encoding = codecs.lookup(encoding).name # normalize the name + if encoding != "utf-8": + # gh-133711: The Python UTF-8 Mode ignores the LC_CTYPE locale + # and always use the UTF-8 encoding. + self.skipTest(f"the LC_CTYPE encoding is {encoding!r}") + + try: + readline.add_history("\xEB\xEF") + except UnicodeEncodeError as err: + self.skipTest("Locale cannot encode test data: " + format(err)) + + script = r"""import readline + +is_editline = readline.backend == "editline" +inserted = "[\xEFnserted]" +macro = "|t\xEB[after]" +set_pre_input_hook = getattr(readline, "set_pre_input_hook", None) +if is_editline or not set_pre_input_hook: + # The insert_line() call via pre_input_hook() does nothing with Editline, + # so include the extra text that would have been inserted here + macro = inserted + macro + +if is_editline: + readline.parse_and_bind(r'bind ^B ed-prev-char') + readline.parse_and_bind(r'bind "\t" rl_complete') + readline.parse_and_bind(r'bind -s ^A "{}"'.format(macro)) +else: + readline.parse_and_bind(r'Control-b: backward-char') + readline.parse_and_bind(r'"\t": complete') + readline.parse_and_bind(r'set disable-completion off') + readline.parse_and_bind(r'set show-all-if-ambiguous off') + readline.parse_and_bind(r'set show-all-if-unmodified off') + readline.parse_and_bind(r'Control-a: "{}"'.format(macro)) + +def pre_input_hook(): + readline.insert_text(inserted) + readline.redisplay() +if set_pre_input_hook: + set_pre_input_hook(pre_input_hook) + +def completer(text, state): + if text == "t\xEB": + if state == 0: + print("text", ascii(text)) + print("line", ascii(readline.get_line_buffer())) + print("indexes", readline.get_begidx(), readline.get_endidx()) + return "t\xEBnt" + if state == 1: + return "t\xEBxt" + if text == "t\xEBx" and state == 0: + return "t\xEBxt" + return None +readline.set_completer(completer) + +def display(substitution, matches, longest_match_length): + print("substitution", ascii(substitution)) + print("matches", ascii(matches)) +readline.set_completion_display_matches_hook(display) + +print("result", ascii(input())) +print("history", ascii(readline.get_history_item(1))) +""" + + input = b"\x01" # Ctrl-A, expands to "|t\xEB[after]" + input += b"\x02" * len("[after]") # Move cursor back + input += b"\t\t" # Display possible completions + input += b"x\t" # Complete "t\xEBx" -> "t\xEBxt" + input += b"\r" + output = run_pty(script, input) + self.assertIn(b"text 't\\xeb'\r\n", output) + self.assertIn(b"line '[\\xefnserted]|t\\xeb[after]'\r\n", output) + if sys.platform == "darwin" or not is_editline: + self.assertIn(b"indexes 11 13\r\n", output) + # Non-macOS libedit does not handle non-ASCII bytes + # the same way and generates character indices + # rather than byte indices via get_begidx() and + # get_endidx(). Ex: libedit2 3.1-20191231-2 on Debian + # winds up with "indexes 10 12". Stemming from the + # start and end values calls back into readline.c's + # rl_attempted_completion_function = flex_complete with: + # (11, 13) instead of libreadline's (12, 15). + + if not is_editline and hasattr(readline, "set_pre_input_hook"): + self.assertIn(b"substitution 't\\xeb'\r\n", output) + self.assertIn(b"matches ['t\\xebnt', 't\\xebxt']\r\n", output) + expected = br"'[\xefnserted]|t\xebxt[after]'" + self.assertIn(b"result " + expected + b"\r\n", output) + # bpo-45195: Sometimes, the newline character is not written at the + # end, so don't expect it in the output. + self.assertIn(b"history " + expected, output) + + # We have 2 reasons to skip this test: + # - readline: history size was added in 6.0 + # See https://cnswww.cns.cwru.edu/php/chet/readline/CHANGES + # - editline: history size is broken on OS X 10.11.6. + # Newer versions were not tested yet. + @unittest.skipIf(readline._READLINE_VERSION < 0x600, + "this readline version does not support history-size") + @unittest.skipIf(is_editline, + "editline history size configuration is broken") + def test_history_size(self): + history_size = 10 + with temp_dir() as test_dir: + inputrc = os.path.join(test_dir, "inputrc") + with open(inputrc, "wb") as f: + f.write(b"set history-size %d\n" % history_size) + + history_file = os.path.join(test_dir, "history") + with open(history_file, "wb") as f: + # history_size * 2 items crashes readline + data = b"".join(b"item %d\n" % i + for i in range(history_size * 2)) + f.write(data) + + script = """ +import os +import readline + +history_file = os.environ["HISTORY_FILE"] +readline.read_history_file(history_file) +input() +readline.write_history_file(history_file) +""" + + env = dict(os.environ) + env["INPUTRC"] = inputrc + env["HISTORY_FILE"] = history_file + + run_pty(script, input=b"last input\r", env=env) + + with open(history_file, "rb") as f: + lines = f.readlines() + self.assertEqual(len(lines), history_size) + self.assertEqual(lines[-1].strip(), b"last input") + + @requires_working_threading() + def test_gh123321_threadsafe(self): + """gh-123321: readline should be thread-safe and not crash""" + script = textwrap.dedent(r""" + import threading + from test.support.threading_helper import join_thread + + def func(): + input() + + thread1 = threading.Thread(target=func) + thread2 = threading.Thread(target=func) + thread1.start() + thread2.start() + join_thread(thread1) + join_thread(thread2) + print("done") + """) + + output = run_pty(script, input=b"input1\rinput2\r") + + self.assertIn(b"done", output) + + + def test_write_read_limited_history(self): + previous_length = readline.get_history_length() + self.addCleanup(readline.set_history_length, previous_length) + + readline.add_history("first line") + readline.add_history("second line") + readline.add_history("third line") + + readline.set_history_length(2) + self.assertEqual(readline.get_history_length(), 2) + readline.write_history_file(TESTFN) + self.addCleanup(os.remove, TESTFN) + + readline.read_history_file(TESTFN) + # Without clear_history() there's no good way to test if + # the correct entries are present (we're combining history limiting and + # possible deduplication with arbitrary previous content). + # So, we've only tested that the read did not fail. + # See TestHistoryManipulation for the full test. + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py new file mode 100644 index 00000000000..9b3014a94a0 --- /dev/null +++ b/Lib/test/test_sax.py @@ -0,0 +1,1561 @@ +# regression test for SAX 2.0 +# $Id$ + +from xml.sax import make_parser, ContentHandler, \ + SAXException, SAXReaderNotAvailable, SAXParseException +import unittest +from unittest import mock +try: + make_parser() +except SAXReaderNotAvailable: + # don't try to test this module if we cannot create a parser + raise unittest.SkipTest("no XML parsers available") +from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \ + XMLFilterBase, prepare_input_source +from xml.sax.expatreader import create_parser +from xml.sax.handler import (feature_namespaces, feature_external_ges, + LexicalHandler) +from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl +from io import BytesIO, StringIO +import codecs +import os.path +import pyexpat +import shutil +import sys +from urllib.error import URLError +import urllib.request +from test.support import os_helper +from test.support import findfile +from test.support.os_helper import FakePath, TESTFN + + +TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata") +TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata") +try: + TEST_XMLFILE.encode("utf-8") + TEST_XMLFILE_OUT.encode("utf-8") +except UnicodeEncodeError: + raise unittest.SkipTest("filename is not encodable to utf8") + +supports_nonascii_filenames = True +if not os.path.supports_unicode_filenames: + try: + os_helper.TESTFN_UNICODE.encode(sys.getfilesystemencoding()) + except (UnicodeError, TypeError): + # Either the file system encoding is None, or the file name + # cannot be encoded in the file system encoding. + supports_nonascii_filenames = False +requires_nonascii_filenames = unittest.skipUnless( + supports_nonascii_filenames, + 'Requires non-ascii filenames support') + +ns_uri = "http://www.python.org/xml-ns/saxtest/" + +class XmlTestBase(unittest.TestCase): + def verify_empty_attrs(self, attrs): + self.assertRaises(KeyError, attrs.getValue, "attr") + self.assertRaises(KeyError, attrs.getValueByQName, "attr") + self.assertRaises(KeyError, attrs.getNameByQName, "attr") + self.assertRaises(KeyError, attrs.getQNameByName, "attr") + self.assertRaises(KeyError, attrs.__getitem__, "attr") + self.assertEqual(attrs.getLength(), 0) + self.assertEqual(attrs.getNames(), []) + self.assertEqual(attrs.getQNames(), []) + self.assertEqual(len(attrs), 0) + self.assertNotIn("attr", attrs) + self.assertEqual(list(attrs.keys()), []) + self.assertEqual(attrs.get("attrs"), None) + self.assertEqual(attrs.get("attrs", 25), 25) + self.assertEqual(list(attrs.items()), []) + self.assertEqual(list(attrs.values()), []) + + def verify_empty_nsattrs(self, attrs): + self.assertRaises(KeyError, attrs.getValue, (ns_uri, "attr")) + self.assertRaises(KeyError, attrs.getValueByQName, "ns:attr") + self.assertRaises(KeyError, attrs.getNameByQName, "ns:attr") + self.assertRaises(KeyError, attrs.getQNameByName, (ns_uri, "attr")) + self.assertRaises(KeyError, attrs.__getitem__, (ns_uri, "attr")) + self.assertEqual(attrs.getLength(), 0) + self.assertEqual(attrs.getNames(), []) + self.assertEqual(attrs.getQNames(), []) + self.assertEqual(len(attrs), 0) + self.assertNotIn((ns_uri, "attr"), attrs) + self.assertEqual(list(attrs.keys()), []) + self.assertEqual(attrs.get((ns_uri, "attr")), None) + self.assertEqual(attrs.get((ns_uri, "attr"), 25), 25) + self.assertEqual(list(attrs.items()), []) + self.assertEqual(list(attrs.values()), []) + + def verify_attrs_wattr(self, attrs): + self.assertEqual(attrs.getLength(), 1) + self.assertEqual(attrs.getNames(), ["attr"]) + self.assertEqual(attrs.getQNames(), ["attr"]) + self.assertEqual(len(attrs), 1) + self.assertIn("attr", attrs) + self.assertEqual(list(attrs.keys()), ["attr"]) + self.assertEqual(attrs.get("attr"), "val") + self.assertEqual(attrs.get("attr", 25), "val") + self.assertEqual(list(attrs.items()), [("attr", "val")]) + self.assertEqual(list(attrs.values()), ["val"]) + self.assertEqual(attrs.getValue("attr"), "val") + self.assertEqual(attrs.getValueByQName("attr"), "val") + self.assertEqual(attrs.getNameByQName("attr"), "attr") + self.assertEqual(attrs["attr"], "val") + self.assertEqual(attrs.getQNameByName("attr"), "attr") + + +def xml_str(doc, encoding=None): + if encoding is None: + return doc + return '\n%s' % (encoding, doc) + +def xml_bytes(doc, encoding, decl_encoding=...): + if decl_encoding is ...: + decl_encoding = encoding + return xml_str(doc, decl_encoding).encode(encoding, 'xmlcharrefreplace') + +def make_xml_file(doc, encoding, decl_encoding=...): + if decl_encoding is ...: + decl_encoding = encoding + with open(TESTFN, 'w', encoding=encoding, errors='xmlcharrefreplace') as f: + f.write(xml_str(doc, decl_encoding)) + + +class ParseTest(unittest.TestCase): + data = '$\xa3\u20ac\U0001017b' + + def tearDown(self): + os_helper.unlink(TESTFN) + + def check_parse(self, f): + from xml.sax import parse + result = StringIO() + parse(f, XMLGenerator(result, 'utf-8')) + self.assertEqual(result.getvalue(), xml_str(self.data, 'utf-8')) + + def test_parse_text(self): + encodings = ('us-ascii', 'iso-8859-1', 'utf-8', + 'utf-16', 'utf-16le', 'utf-16be') + for encoding in encodings: + self.check_parse(StringIO(xml_str(self.data, encoding))) + make_xml_file(self.data, encoding) + with open(TESTFN, 'r', encoding=encoding) as f: + self.check_parse(f) + self.check_parse(StringIO(self.data)) + make_xml_file(self.data, encoding, None) + with open(TESTFN, 'r', encoding=encoding) as f: + self.check_parse(f) + + def test_parse_bytes(self): + # UTF-8 is default encoding, US-ASCII is compatible with UTF-8, + # UTF-16 is autodetected + encodings = ('us-ascii', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be') + for encoding in encodings: + self.check_parse(BytesIO(xml_bytes(self.data, encoding))) + make_xml_file(self.data, encoding) + self.check_parse(TESTFN) + with open(TESTFN, 'rb') as f: + self.check_parse(f) + self.check_parse(BytesIO(xml_bytes(self.data, encoding, None))) + make_xml_file(self.data, encoding, None) + self.check_parse(TESTFN) + with open(TESTFN, 'rb') as f: + self.check_parse(f) + # accept UTF-8 with BOM + self.check_parse(BytesIO(xml_bytes(self.data, 'utf-8-sig', 'utf-8'))) + make_xml_file(self.data, 'utf-8-sig', 'utf-8') + self.check_parse(TESTFN) + with open(TESTFN, 'rb') as f: + self.check_parse(f) + self.check_parse(BytesIO(xml_bytes(self.data, 'utf-8-sig', None))) + make_xml_file(self.data, 'utf-8-sig', None) + self.check_parse(TESTFN) + with open(TESTFN, 'rb') as f: + self.check_parse(f) + # accept data with declared encoding + self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1'))) + make_xml_file(self.data, 'iso-8859-1') + self.check_parse(TESTFN) + with open(TESTFN, 'rb') as f: + self.check_parse(f) + # fail on non-UTF-8 incompatible data without declared encoding + with self.assertRaises(SAXException): + self.check_parse(BytesIO(xml_bytes(self.data, 'iso-8859-1', None))) + make_xml_file(self.data, 'iso-8859-1', None) + with self.assertRaises(SAXException): + self.check_parse(TESTFN) + with open(TESTFN, 'rb') as f: + with self.assertRaises(SAXException): + self.check_parse(f) + + def test_parse_path_object(self): + make_xml_file(self.data, 'utf-8', None) + self.check_parse(FakePath(TESTFN)) + + def test_parse_InputSource(self): + # accept data without declared but with explicitly specified encoding + make_xml_file(self.data, 'iso-8859-1', None) + with open(TESTFN, 'rb') as f: + input = InputSource() + input.setByteStream(f) + input.setEncoding('iso-8859-1') + self.check_parse(input) + + def test_parse_close_source(self): + builtin_open = open + fileobj = None + + def mock_open(*args): + nonlocal fileobj + fileobj = builtin_open(*args) + return fileobj + + with mock.patch('xml.sax.saxutils.open', side_effect=mock_open): + make_xml_file(self.data, 'iso-8859-1', None) + with self.assertRaises(SAXException): + self.check_parse(TESTFN) + self.assertTrue(fileobj.closed) + + def check_parseString(self, s): + from xml.sax import parseString + result = StringIO() + parseString(s, XMLGenerator(result, 'utf-8')) + self.assertEqual(result.getvalue(), xml_str(self.data, 'utf-8')) + + def test_parseString_text(self): + encodings = ('us-ascii', 'iso-8859-1', 'utf-8', + 'utf-16', 'utf-16le', 'utf-16be') + for encoding in encodings: + self.check_parseString(xml_str(self.data, encoding)) + self.check_parseString(self.data) + + def test_parseString_bytes(self): + # UTF-8 is default encoding, US-ASCII is compatible with UTF-8, + # UTF-16 is autodetected + encodings = ('us-ascii', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be') + for encoding in encodings: + self.check_parseString(xml_bytes(self.data, encoding)) + self.check_parseString(xml_bytes(self.data, encoding, None)) + # accept UTF-8 with BOM + self.check_parseString(xml_bytes(self.data, 'utf-8-sig', 'utf-8')) + self.check_parseString(xml_bytes(self.data, 'utf-8-sig', None)) + # accept data with declared encoding + self.check_parseString(xml_bytes(self.data, 'iso-8859-1')) + # fail on non-UTF-8 incompatible data without declared encoding + with self.assertRaises(SAXException): + self.check_parseString(xml_bytes(self.data, 'iso-8859-1', None)) + +class MakeParserTest(unittest.TestCase): + def test_make_parser2(self): + # Creating parsers several times in a row should succeed. + # Testing this because there have been failures of this kind + # before. + from xml.sax import make_parser + p = make_parser() + from xml.sax import make_parser + p = make_parser() + from xml.sax import make_parser + p = make_parser() + from xml.sax import make_parser + p = make_parser() + from xml.sax import make_parser + p = make_parser() + from xml.sax import make_parser + p = make_parser() + + def test_make_parser3(self): + # Testing that make_parser can handle different types of + # iterables. + make_parser(['module']) + make_parser(('module', )) + make_parser({'module'}) + make_parser(frozenset({'module'})) + make_parser({'module': None}) + make_parser(iter(['module'])) + + def test_make_parser4(self): + # Testing that make_parser can handle empty iterables. + make_parser([]) + make_parser(tuple()) + make_parser(set()) + make_parser(frozenset()) + make_parser({}) + make_parser(iter([])) + + def test_make_parser5(self): + # Testing that make_parser can handle iterables with more than + # one item. + make_parser(['module1', 'module2']) + make_parser(('module1', 'module2')) + make_parser({'module1', 'module2'}) + make_parser(frozenset({'module1', 'module2'})) + make_parser({'module1': None, 'module2': None}) + make_parser(iter(['module1', 'module2'])) + +# =========================================================================== +# +# saxutils tests +# +# =========================================================================== + +class SaxutilsTest(unittest.TestCase): + # ===== escape + def test_escape_basic(self): + self.assertEqual(escape("Donald Duck & Co"), "Donald Duck & Co") + + def test_escape_all(self): + self.assertEqual(escape(""), + "<Donald Duck & Co>") + + def test_escape_extra(self): + self.assertEqual(escape("Hei på deg", {"å" : "å"}), + "Hei på deg") + + # ===== unescape + def test_unescape_basic(self): + self.assertEqual(unescape("Donald Duck & Co"), "Donald Duck & Co") + + def test_unescape_all(self): + self.assertEqual(unescape("<Donald Duck & Co>"), + "") + + def test_unescape_extra(self): + self.assertEqual(unescape("Hei på deg", {"å" : "å"}), + "Hei på deg") + + def test_unescape_amp_extra(self): + self.assertEqual(unescape("&foo;", {"&foo;": "splat"}), "&foo;") + + # ===== quoteattr + def test_quoteattr_basic(self): + self.assertEqual(quoteattr("Donald Duck & Co"), + '"Donald Duck & Co"') + + def test_single_quoteattr(self): + self.assertEqual(quoteattr('Includes "double" quotes'), + '\'Includes "double" quotes\'') + + def test_double_quoteattr(self): + self.assertEqual(quoteattr("Includes 'single' quotes"), + "\"Includes 'single' quotes\"") + + def test_single_double_quoteattr(self): + self.assertEqual(quoteattr("Includes 'single' and \"double\" quotes"), + "\"Includes 'single' and "double" quotes\"") + + # ===== make_parser + def test_make_parser(self): + # Creating a parser should succeed - it should fall back + # to the expatreader + p = make_parser(['xml.parsers.no_such_parser']) + + +class PrepareInputSourceTest(unittest.TestCase): + + def setUp(self): + self.file = os_helper.TESTFN + with open(self.file, "w") as tmp: + tmp.write("This was read from a file.") + + def tearDown(self): + os_helper.unlink(self.file) + + def make_byte_stream(self): + return BytesIO(b"This is a byte stream.") + + def make_character_stream(self): + return StringIO("This is a character stream.") + + def checkContent(self, stream, content): + self.assertIsNotNone(stream) + self.assertEqual(stream.read(), content) + stream.close() + + + def test_character_stream(self): + # If the source is an InputSource with a character stream, use it. + src = InputSource(self.file) + src.setCharacterStream(self.make_character_stream()) + prep = prepare_input_source(src) + self.assertIsNone(prep.getByteStream()) + self.checkContent(prep.getCharacterStream(), + "This is a character stream.") + + def test_byte_stream(self): + # If the source is an InputSource that does not have a character + # stream but does have a byte stream, use the byte stream. + src = InputSource(self.file) + src.setByteStream(self.make_byte_stream()) + prep = prepare_input_source(src) + self.assertIsNone(prep.getCharacterStream()) + self.checkContent(prep.getByteStream(), + b"This is a byte stream.") + + def test_system_id(self): + # If the source is an InputSource that has neither a character + # stream nor a byte stream, open the system ID. + src = InputSource(self.file) + prep = prepare_input_source(src) + self.assertIsNone(prep.getCharacterStream()) + self.checkContent(prep.getByteStream(), + b"This was read from a file.") + + def test_string(self): + # If the source is a string, use it as a system ID and open it. + prep = prepare_input_source(self.file) + self.assertIsNone(prep.getCharacterStream()) + self.checkContent(prep.getByteStream(), + b"This was read from a file.") + + def test_path_objects(self): + # If the source is a Path object, use it as a system ID and open it. + prep = prepare_input_source(FakePath(self.file)) + self.assertIsNone(prep.getCharacterStream()) + self.checkContent(prep.getByteStream(), + b"This was read from a file.") + + def test_binary_file(self): + # If the source is a binary file-like object, use it as a byte + # stream. + prep = prepare_input_source(self.make_byte_stream()) + self.assertIsNone(prep.getCharacterStream()) + self.checkContent(prep.getByteStream(), + b"This is a byte stream.") + + def test_text_file(self): + # If the source is a text file-like object, use it as a character + # stream. + prep = prepare_input_source(self.make_character_stream()) + self.assertIsNone(prep.getByteStream()) + self.checkContent(prep.getCharacterStream(), + "This is a character stream.") + + +# ===== XMLGenerator + +class XmlgenTest: + def test_xmlgen_basic(self): + result = self.ioclass() + gen = XMLGenerator(result) + gen.startDocument() + gen.startElement("doc", {}) + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml("")) + + def test_xmlgen_basic_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + gen.startDocument() + gen.startElement("doc", {}) + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml("")) + + def test_xmlgen_content(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {}) + gen.characters("huhei") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml("huhei")) + + def test_xmlgen_content_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + + gen.startDocument() + gen.startElement("doc", {}) + gen.characters("huhei") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml("huhei")) + + def test_xmlgen_pi(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.processingInstruction("test", "data") + gen.startElement("doc", {}) + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml("")) + + def test_xmlgen_content_escape(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {}) + gen.characters("<huhei&")) + + def test_xmlgen_attr_escape(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {"a": '"'}) + gen.startElement("e", {"a": "'"}) + gen.endElement("e") + gen.startElement("e", {"a": "'\""}) + gen.endElement("e") + gen.startElement("e", {"a": "\n\r\t"}) + gen.endElement("e") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml( + "" + "" + "")) + + def test_xmlgen_encoding(self): + encodings = ('iso-8859-15', 'utf-8', 'utf-8-sig', + 'utf-16', 'utf-16be', 'utf-16le', + 'utf-32', 'utf-32be', 'utf-32le') + for encoding in encodings: + result = self.ioclass() + gen = XMLGenerator(result, encoding=encoding) + + gen.startDocument() + gen.startElement("doc", {"a": '\u20ac'}) + gen.characters("\u20ac") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml('\u20ac', encoding=encoding)) + + def test_xmlgen_unencodable(self): + result = self.ioclass() + gen = XMLGenerator(result, encoding='ascii') + + gen.startDocument() + gen.startElement("doc", {"a": '\u20ac'}) + gen.characters("\u20ac") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml('', encoding='ascii')) + + def test_xmlgen_ignorable(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {}) + gen.ignorableWhitespace(" ") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml(" ")) + + def test_xmlgen_ignorable_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + + gen.startDocument() + gen.startElement("doc", {}) + gen.ignorableWhitespace(" ") + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml(" ")) + + def test_xmlgen_encoding_bytes(self): + encodings = ('iso-8859-15', 'utf-8', 'utf-8-sig', + 'utf-16', 'utf-16be', 'utf-16le', + 'utf-32', 'utf-32be', 'utf-32le') + for encoding in encodings: + result = self.ioclass() + gen = XMLGenerator(result, encoding=encoding) + + gen.startDocument() + gen.startElement("doc", {"a": '\u20ac'}) + gen.characters("\u20ac".encode(encoding)) + gen.ignorableWhitespace(" ".encode(encoding)) + gen.endElement("doc") + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml('\u20ac ', encoding=encoding)) + + def test_xmlgen_ns(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping("ns1", ns_uri) + gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) + # add an unqualified name + gen.startElementNS((None, "udoc"), None, {}) + gen.endElementNS((None, "udoc"), None) + gen.endElementNS((ns_uri, "doc"), "ns1:doc") + gen.endPrefixMapping("ns1") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml( + '' % + ns_uri)) + + def test_xmlgen_ns_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + + gen.startDocument() + gen.startPrefixMapping("ns1", ns_uri) + gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) + # add an unqualified name + gen.startElementNS((None, "udoc"), None, {}) + gen.endElementNS((None, "udoc"), None) + gen.endElementNS((ns_uri, "doc"), "ns1:doc") + gen.endPrefixMapping("ns1") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml( + '' % + ns_uri)) + + def test_1463026_1(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) + gen.endElementNS((None, 'a'), 'a') + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml('')) + + def test_1463026_1_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + + gen.startDocument() + gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) + gen.endElementNS((None, 'a'), 'a') + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml('')) + + def test_1463026_2(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping(None, 'qux') + gen.startElementNS(('qux', 'a'), 'a', {}) + gen.endElementNS(('qux', 'a'), 'a') + gen.endPrefixMapping(None) + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml('')) + + def test_1463026_2_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + + gen.startDocument() + gen.startPrefixMapping(None, 'qux') + gen.startElementNS(('qux', 'a'), 'a', {}) + gen.endElementNS(('qux', 'a'), 'a') + gen.endPrefixMapping(None) + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml('')) + + def test_1463026_3(self): + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping('my', 'qux') + gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) + gen.endElementNS(('qux', 'a'), 'a') + gen.endPrefixMapping('my') + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml('')) + + def test_1463026_3_empty(self): + result = self.ioclass() + gen = XMLGenerator(result, short_empty_elements=True) + + gen.startDocument() + gen.startPrefixMapping('my', 'qux') + gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) + gen.endElementNS(('qux', 'a'), 'a') + gen.endPrefixMapping('my') + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml('')) + + def test_5027_1(self): + # The xml prefix (as in xml:lang below) is reserved and bound by + # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had + # a bug whereby a KeyError is raised because this namespace is missing + # from a dictionary. + # + # This test demonstrates the bug by parsing a document. + test_xml = StringIO( + '' + '' + 'Hello' + '') + + parser = make_parser() + parser.setFeature(feature_namespaces, True) + result = self.ioclass() + gen = XMLGenerator(result) + parser.setContentHandler(gen) + parser.parse(test_xml) + + self.assertEqual(result.getvalue(), + self.xml( + '' + 'Hello' + '')) + + def test_5027_2(self): + # The xml prefix (as in xml:lang below) is reserved and bound by + # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had + # a bug whereby a KeyError is raised because this namespace is missing + # from a dictionary. + # + # This test demonstrates the bug by direct manipulation of the + # XMLGenerator. + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping('a', 'http://example.com/ns') + gen.startElementNS(('http://example.com/ns', 'g1'), 'g1', {}) + lang_attr = {('http://www.w3.org/XML/1998/namespace', 'lang'): 'en'} + gen.startElementNS(('http://example.com/ns', 'g2'), 'g2', lang_attr) + gen.characters('Hello') + gen.endElementNS(('http://example.com/ns', 'g2'), 'g2') + gen.endElementNS(('http://example.com/ns', 'g1'), 'g1') + gen.endPrefixMapping('a') + gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml( + '' + 'Hello' + '')) + + def test_no_close_file(self): + result = self.ioclass() + def func(out): + gen = XMLGenerator(out) + gen.startDocument() + gen.startElement("doc", {}) + func(result) + self.assertFalse(result.closed) + + def test_xmlgen_fragment(self): + result = self.ioclass() + gen = XMLGenerator(result) + + # Don't call gen.startDocument() + gen.startElement("foo", {"a": "1.0"}) + gen.characters("Hello") + gen.endElement("foo") + gen.startElement("bar", {"b": "2.0"}) + gen.endElement("bar") + # Don't call gen.endDocument() + + self.assertEqual(result.getvalue(), + self.xml('Hello')[len(self.xml('')):]) + +class StringXmlgenTest(XmlgenTest, unittest.TestCase): + ioclass = StringIO + + def xml(self, doc, encoding='iso-8859-1'): + return '\n%s' % (encoding, doc) + + test_xmlgen_unencodable = None + +class BytesXmlgenTest(XmlgenTest, unittest.TestCase): + ioclass = BytesIO + + def xml(self, doc, encoding='iso-8859-1'): + return ('\n%s' % + (encoding, doc)).encode(encoding, 'xmlcharrefreplace') + +class WriterXmlgenTest(BytesXmlgenTest): + class ioclass(list): + write = list.append + closed = False + + def seekable(self): + return True + + def tell(self): + # return 0 at start and not 0 after start + return len(self) + + def getvalue(self): + return b''.join(self) + +class StreamWriterXmlgenTest(XmlgenTest, unittest.TestCase): + def ioclass(self): + raw = BytesIO() + writer = codecs.getwriter('ascii')(raw, 'xmlcharrefreplace') + writer.getvalue = raw.getvalue + return writer + + def xml(self, doc, encoding='iso-8859-1'): + return ('\n%s' % + (encoding, doc)).encode('ascii', 'xmlcharrefreplace') + +class StreamReaderWriterXmlgenTest(XmlgenTest, unittest.TestCase): + fname = os_helper.TESTFN + '-codecs' + + def ioclass(self): + writer = codecs.open(self.fname, 'w', encoding='ascii', + errors='xmlcharrefreplace', buffering=0) + def cleanup(): + writer.close() + os_helper.unlink(self.fname) + self.addCleanup(cleanup) + def getvalue(): + # Windows will not let use reopen without first closing + writer.close() + with open(writer.name, 'rb') as f: + return f.read() + writer.getvalue = getvalue + return writer + + def xml(self, doc, encoding='iso-8859-1'): + return ('\n%s' % + (encoding, doc)).encode('ascii', 'xmlcharrefreplace') + +start = b'\n' + + +class XMLFilterBaseTest(unittest.TestCase): + def test_filter_basic(self): + result = BytesIO() + gen = XMLGenerator(result) + filter = XMLFilterBase() + filter.setContentHandler(gen) + + filter.startDocument() + filter.startElement("doc", {}) + filter.characters("content") + filter.ignorableWhitespace(" ") + filter.endElement("doc") + filter.endDocument() + + self.assertEqual(result.getvalue(), start + b"content ") + +# =========================================================================== +# +# expatreader tests +# +# =========================================================================== + +with open(TEST_XMLFILE_OUT, 'rb') as f: + xml_test_out = f.read() + +class ExpatReaderTest(XmlTestBase): + + # ===== XMLReader support + + def test_expat_binary_file(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + with open(TEST_XMLFILE, 'rb') as f: + parser.parse(f) + + self.assertEqual(result.getvalue(), xml_test_out) + + def test_expat_text_file(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + with open(TEST_XMLFILE, 'rt', encoding='iso-8859-1') as f: + parser.parse(f) + + self.assertEqual(result.getvalue(), xml_test_out) + + @requires_nonascii_filenames + def test_expat_binary_file_nonascii(self): + fname = os_helper.TESTFN_UNICODE + shutil.copyfile(TEST_XMLFILE, fname) + self.addCleanup(os_helper.unlink, fname) + + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + parser.parse(open(fname, 'rb')) + + self.assertEqual(result.getvalue(), xml_test_out) + + def test_expat_binary_file_bytes_name(self): + fname = os.fsencode(TEST_XMLFILE) + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + with open(fname, 'rb') as f: + parser.parse(f) + + self.assertEqual(result.getvalue(), xml_test_out) + + def test_expat_binary_file_int_name(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + with open(TEST_XMLFILE, 'rb') as f: + with open(f.fileno(), 'rb', closefd=False) as f2: + parser.parse(f2) + + self.assertEqual(result.getvalue(), xml_test_out) + + # ===== DTDHandler support + + class TestDTDHandler: + + def __init__(self): + self._notations = [] + self._entities = [] + + def notationDecl(self, name, publicId, systemId): + self._notations.append((name, publicId, systemId)) + + def unparsedEntityDecl(self, name, publicId, systemId, ndata): + self._entities.append((name, publicId, systemId, ndata)) + + + class TestEntityRecorder: + def __init__(self): + self.entities = [] + + def resolveEntity(self, publicId, systemId): + self.entities.append((publicId, systemId)) + source = InputSource() + source.setPublicId(publicId) + source.setSystemId(systemId) + return source + + def test_expat_dtdhandler(self): + parser = create_parser() + handler = self.TestDTDHandler() + parser.setDTDHandler(handler) + + parser.feed('\n') + parser.feed(' \n') + parser.feed(']>\n') + parser.feed('') + parser.close() + + self.assertEqual(handler._notations, + [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)]) + self.assertEqual(handler._entities, [("img", None, "expat.gif", "GIF")]) + + def test_expat_external_dtd_enabled(self): + # clear _opener global variable + self.addCleanup(urllib.request.urlcleanup) + + parser = create_parser() + parser.setFeature(feature_external_ges, True) + resolver = self.TestEntityRecorder() + parser.setEntityResolver(resolver) + + with self.assertRaises(URLError): + parser.feed( + '\n' + ) + self.assertEqual( + resolver.entities, [(None, 'unsupported://non-existing')] + ) + + def test_expat_external_dtd_default(self): + parser = create_parser() + resolver = self.TestEntityRecorder() + parser.setEntityResolver(resolver) + + parser.feed( + '\n' + ) + parser.feed('') + parser.close() + self.assertEqual(resolver.entities, []) + + # ===== EntityResolver support + + class TestEntityResolver: + + def resolveEntity(self, publicId, systemId): + inpsrc = InputSource() + inpsrc.setByteStream(BytesIO(b"")) + return inpsrc + + def test_expat_entityresolver_enabled(self): + parser = create_parser() + parser.setFeature(feature_external_ges, True) + parser.setEntityResolver(self.TestEntityResolver()) + result = BytesIO() + parser.setContentHandler(XMLGenerator(result)) + + parser.feed('\n') + parser.feed(']>\n') + parser.feed('&test;') + parser.close() + + self.assertEqual(result.getvalue(), start + + b"") + + def test_expat_entityresolver_default(self): + parser = create_parser() + self.assertEqual(parser.getFeature(feature_external_ges), False) + parser.setEntityResolver(self.TestEntityResolver()) + result = BytesIO() + parser.setContentHandler(XMLGenerator(result)) + + parser.feed('\n') + parser.feed(']>\n') + parser.feed('&test;') + parser.close() + + self.assertEqual(result.getvalue(), start + + b"") + + # ===== Attributes support + + class AttrGatherer(ContentHandler): + + def startElement(self, name, attrs): + self._attrs = attrs + + def startElementNS(self, name, qname, attrs): + self._attrs = attrs + + def test_expat_attrs_empty(self): + parser = create_parser() + gather = self.AttrGatherer() + parser.setContentHandler(gather) + + parser.feed("") + parser.close() + + self.verify_empty_attrs(gather._attrs) + + def test_expat_attrs_wattr(self): + parser = create_parser() + gather = self.AttrGatherer() + parser.setContentHandler(gather) + + parser.feed("") + parser.close() + + self.verify_attrs_wattr(gather._attrs) + + def test_expat_nsattrs_empty(self): + parser = create_parser(1) + gather = self.AttrGatherer() + parser.setContentHandler(gather) + + parser.feed("") + parser.close() + + self.verify_empty_nsattrs(gather._attrs) + + def test_expat_nsattrs_wattr(self): + parser = create_parser(1) + gather = self.AttrGatherer() + parser.setContentHandler(gather) + + parser.feed("" % ns_uri) + parser.close() + + attrs = gather._attrs + + self.assertEqual(attrs.getLength(), 1) + self.assertEqual(attrs.getNames(), [(ns_uri, "attr")]) + self.assertTrue((attrs.getQNames() == [] or + attrs.getQNames() == ["ns:attr"])) + self.assertEqual(len(attrs), 1) + self.assertIn((ns_uri, "attr"), attrs) + self.assertEqual(attrs.get((ns_uri, "attr")), "val") + self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val") + self.assertEqual(list(attrs.items()), [((ns_uri, "attr"), "val")]) + self.assertEqual(list(attrs.values()), ["val"]) + self.assertEqual(attrs.getValue((ns_uri, "attr")), "val") + self.assertEqual(attrs[(ns_uri, "attr")], "val") + + # ===== InputSource support + + def test_expat_inpsource_filename(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + parser.parse(TEST_XMLFILE) + + self.assertEqual(result.getvalue(), xml_test_out) + + def test_expat_inpsource_sysid(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + parser.parse(InputSource(TEST_XMLFILE)) + + self.assertEqual(result.getvalue(), xml_test_out) + + @requires_nonascii_filenames + def test_expat_inpsource_sysid_nonascii(self): + fname = os_helper.TESTFN_UNICODE + shutil.copyfile(TEST_XMLFILE, fname) + self.addCleanup(os_helper.unlink, fname) + + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + parser.parse(InputSource(fname)) + + self.assertEqual(result.getvalue(), xml_test_out) + + def test_expat_inpsource_byte_stream(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + inpsrc = InputSource() + with open(TEST_XMLFILE, 'rb') as f: + inpsrc.setByteStream(f) + parser.parse(inpsrc) + + self.assertEqual(result.getvalue(), xml_test_out) + + def test_expat_inpsource_character_stream(self): + parser = create_parser() + result = BytesIO() + xmlgen = XMLGenerator(result) + + parser.setContentHandler(xmlgen) + inpsrc = InputSource() + with open(TEST_XMLFILE, 'rt', encoding='iso-8859-1') as f: + inpsrc.setCharacterStream(f) + parser.parse(inpsrc) + + self.assertEqual(result.getvalue(), xml_test_out) + + # ===== IncrementalParser support + + def test_expat_incremental(self): + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + + parser.feed("") + parser.feed("") + parser.close() + + self.assertEqual(result.getvalue(), start + b"") + + def test_expat_incremental_reset(self): + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + + parser.feed("") + parser.feed("text") + + result = BytesIO() + xmlgen = XMLGenerator(result) + parser.setContentHandler(xmlgen) + parser.reset() + + parser.feed("") + parser.feed("text") + parser.feed("") + parser.close() + + self.assertEqual(result.getvalue(), start + b"text") + + @unittest.skipIf(pyexpat.version_info < (2, 6, 0), + f'Expat {pyexpat.version_info} does not ' + 'support reparse deferral') + def test_flush_reparse_deferral_enabled(self): + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + + for chunk in (""): + parser.feed(chunk) + + self.assertEqual(result.getvalue(), start) # i.e. no elements started + self.assertTrue(parser._parser.GetReparseDeferralEnabled()) + + parser.flush() + + self.assertTrue(parser._parser.GetReparseDeferralEnabled()) + self.assertEqual(result.getvalue(), start + b"") + + parser.feed("") + parser.close() + + self.assertEqual(result.getvalue(), start + b"") + + def test_flush_reparse_deferral_disabled(self): + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + + for chunk in (""): + parser.feed(chunk) + + if pyexpat.version_info >= (2, 6, 0): + parser._parser.SetReparseDeferralEnabled(False) + self.assertEqual(result.getvalue(), start) # i.e. no elements started + + self.assertFalse(parser._parser.GetReparseDeferralEnabled()) + + parser.flush() + + self.assertFalse(parser._parser.GetReparseDeferralEnabled()) + self.assertEqual(result.getvalue(), start + b"") + + parser.feed("") + parser.close() + + self.assertEqual(result.getvalue(), start + b"") + + # ===== Locator support + + def test_expat_locator_noinfo(self): + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + + parser.feed("") + parser.feed("") + parser.close() + + self.assertEqual(parser.getSystemId(), None) + self.assertEqual(parser.getPublicId(), None) + self.assertEqual(parser.getLineNumber(), 1) + + def test_expat_locator_withinfo(self): + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + parser.parse(TEST_XMLFILE) + + self.assertEqual(parser.getSystemId(), TEST_XMLFILE) + self.assertEqual(parser.getPublicId(), None) + + @requires_nonascii_filenames + def test_expat_locator_withinfo_nonascii(self): + fname = os_helper.TESTFN_UNICODE + shutil.copyfile(TEST_XMLFILE, fname) + self.addCleanup(os_helper.unlink, fname) + + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + parser.parse(fname) + + self.assertEqual(parser.getSystemId(), fname) + self.assertEqual(parser.getPublicId(), None) + + +# =========================================================================== +# +# error reporting +# +# =========================================================================== + +class ErrorReportingTest(unittest.TestCase): + def test_expat_inpsource_location(self): + parser = create_parser() + parser.setContentHandler(ContentHandler()) # do nothing + source = InputSource() + source.setByteStream(BytesIO(b"")) #ill-formed + name = "a file name" + source.setSystemId(name) + try: + parser.parse(source) + self.fail() + except SAXException as e: + self.assertEqual(e.getSystemId(), name) + + def test_expat_incomplete(self): + parser = create_parser() + parser.setContentHandler(ContentHandler()) # do nothing + self.assertRaises(SAXParseException, parser.parse, StringIO("")) + self.assertEqual(parser.getColumnNumber(), 5) + self.assertEqual(parser.getLineNumber(), 1) + + def test_sax_parse_exception_str(self): + # pass various values from a locator to the SAXParseException to + # make sure that the __str__() doesn't fall apart when None is + # passed instead of an integer line and column number + # + # use "normal" values for the locator: + str(SAXParseException("message", None, + self.DummyLocator(1, 1))) + # use None for the line number: + str(SAXParseException("message", None, + self.DummyLocator(None, 1))) + # use None for the column number: + str(SAXParseException("message", None, + self.DummyLocator(1, None))) + # use None for both: + str(SAXParseException("message", None, + self.DummyLocator(None, None))) + + class DummyLocator: + def __init__(self, lineno, colno): + self._lineno = lineno + self._colno = colno + + def getPublicId(self): + return "pubid" + + def getSystemId(self): + return "sysid" + + def getLineNumber(self): + return self._lineno + + def getColumnNumber(self): + return self._colno + +# =========================================================================== +# +# xmlreader tests +# +# =========================================================================== + +class XmlReaderTest(XmlTestBase): + + # ===== AttributesImpl + def test_attrs_empty(self): + self.verify_empty_attrs(AttributesImpl({})) + + def test_attrs_wattr(self): + self.verify_attrs_wattr(AttributesImpl({"attr" : "val"})) + + def test_nsattrs_empty(self): + self.verify_empty_nsattrs(AttributesNSImpl({}, {})) + + def test_nsattrs_wattr(self): + attrs = AttributesNSImpl({(ns_uri, "attr") : "val"}, + {(ns_uri, "attr") : "ns:attr"}) + + self.assertEqual(attrs.getLength(), 1) + self.assertEqual(attrs.getNames(), [(ns_uri, "attr")]) + self.assertEqual(attrs.getQNames(), ["ns:attr"]) + self.assertEqual(len(attrs), 1) + self.assertIn((ns_uri, "attr"), attrs) + self.assertEqual(list(attrs.keys()), [(ns_uri, "attr")]) + self.assertEqual(attrs.get((ns_uri, "attr")), "val") + self.assertEqual(attrs.get((ns_uri, "attr"), 25), "val") + self.assertEqual(list(attrs.items()), [((ns_uri, "attr"), "val")]) + self.assertEqual(list(attrs.values()), ["val"]) + self.assertEqual(attrs.getValue((ns_uri, "attr")), "val") + self.assertEqual(attrs.getValueByQName("ns:attr"), "val") + self.assertEqual(attrs.getNameByQName("ns:attr"), (ns_uri, "attr")) + self.assertEqual(attrs[(ns_uri, "attr")], "val") + self.assertEqual(attrs.getQNameByName((ns_uri, "attr")), "ns:attr") + + +class LexicalHandlerTest(unittest.TestCase): + def setUp(self): + self.parser = None + + self.specified_version = '1.0' + self.specified_encoding = 'UTF-8' + self.specified_doctype = 'wish' + self.specified_entity_names = ('nbsp', 'source', 'target') + self.specified_comment = ('Comment in a DTD', + 'Really! You think so?') + self.test_data = StringIO() + self.test_data.write('\n'. + format(self.specified_version, + self.specified_encoding)) + self.test_data.write('\n'. + format(self.specified_comment[0])) + self.test_data.write('\n'. + format(self.specified_doctype)) + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.write('\n'. + format(self.specified_entity_names[0])) + self.test_data.write('\n'. + format(self.specified_entity_names[1])) + self.test_data.write('\n'. + format(self.specified_entity_names[2])) + self.test_data.write(']>\n') + self.test_data.write('<{}>'.format(self.specified_doctype)) + self.test_data.write('Aristotle\n') + self.test_data.write('Alexander\n') + self.test_data.write('Supplication\n') + self.test_data.write('Teach me patience!\n') + self.test_data.write('
&{};&{};&{};
\n'. + format(self.specified_entity_names[1], + self.specified_entity_names[0], + self.specified_entity_names[2])) + self.test_data.write('\n'.format(self.specified_comment[1])) + self.test_data.write('\n'.format(self.specified_doctype)) + self.test_data.seek(0) + + # Data received from handlers - to be validated + self.version = None + self.encoding = None + self.standalone = None + self.doctype = None + self.publicID = None + self.systemID = None + self.end_of_dtd = False + self.comments = [] + + def test_handlers(self): + class TestLexicalHandler(LexicalHandler): + def __init__(self, test_harness, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_harness = test_harness + + def startDTD(self, doctype, publicID, systemID): + self.test_harness.doctype = doctype + self.test_harness.publicID = publicID + self.test_harness.systemID = systemID + + def endDTD(self): + self.test_harness.end_of_dtd = True + + def comment(self, text): + self.test_harness.comments.append(text) + + self.parser = create_parser() + self.parser.setContentHandler(ContentHandler()) + self.parser.setProperty( + 'http://xml.org/sax/properties/lexical-handler', + TestLexicalHandler(self)) + source = InputSource() + source.setCharacterStream(self.test_data) + self.parser.parse(source) + self.assertEqual(self.doctype, self.specified_doctype) + self.assertIsNone(self.publicID) + self.assertIsNone(self.systemID) + self.assertTrue(self.end_of_dtd) + self.assertEqual(len(self.comments), + len(self.specified_comment)) + self.assertEqual(f' {self.specified_comment[0]} ', self.comments[0]) + + +class CDATAHandlerTest(unittest.TestCase): + def setUp(self): + self.parser = None + self.specified_chars = [] + self.specified_chars.append(('Parseable character data', False)) + self.specified_chars.append(('<> &% - assorted other XML junk.', True)) + self.char_index = 0 # Used to index specified results within handlers + self.test_data = StringIO() + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.write(f'{self.specified_chars[0][0]}\n') + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.write(f'\n') + self.test_data.write('\n') + self.test_data.write('\n') + self.test_data.seek(0) + + # Data received from handlers - to be validated + self.chardata = [] + self.in_cdata = False + + def test_handlers(self): + class TestLexicalHandler(LexicalHandler): + def __init__(self, test_harness, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_harness = test_harness + + def startCDATA(self): + self.test_harness.in_cdata = True + + def endCDATA(self): + self.test_harness.in_cdata = False + + class TestCharHandler(ContentHandler): + def __init__(self, test_harness, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_harness = test_harness + + def characters(self, content): + if content != '\n': + h = self.test_harness + t = h.specified_chars[h.char_index] + h.assertEqual(t[0], content) + h.assertEqual(t[1], h.in_cdata) + h.char_index += 1 + + self.parser = create_parser() + self.parser.setContentHandler(TestCharHandler(self)) + self.parser.setProperty( + 'http://xml.org/sax/properties/lexical-handler', + TestLexicalHandler(self)) + source = InputSource() + source.setCharacterStream(self.test_data) + self.parser.parse(source) + + self.assertFalse(self.in_cdata) + self.assertEqual(self.char_index, 2) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_select.py b/Lib/test/test_select.py new file mode 100644 index 00000000000..a82584d6904 --- /dev/null +++ b/Lib/test/test_select.py @@ -0,0 +1,104 @@ +import errno +import select +import subprocess +import sys +import textwrap +import unittest +from test import support + +support.requires_working_socket(module=True) + +@unittest.skipIf((sys.platform[:3]=='win'), + "can't easily test on this system") +class SelectTestCase(unittest.TestCase): + + class Nope: + pass + + class Almost: + def fileno(self): + return 'fileno' + + def test_error_conditions(self): + self.assertRaises(TypeError, select.select, 1, 2, 3) + self.assertRaises(TypeError, select.select, [self.Nope()], [], []) + self.assertRaises(TypeError, select.select, [self.Almost()], [], []) + self.assertRaises(TypeError, select.select, [], [], [], "not a number") + self.assertRaises(ValueError, select.select, [], [], [], -1) + + # Issue #12367: http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/155606 + @unittest.skipIf(sys.platform.startswith('freebsd'), + 'skip because of a FreeBSD bug: kern/155606') + def test_errno(self): + with open(__file__, 'rb') as fp: + fd = fp.fileno() + fp.close() + try: + select.select([fd], [], [], 0) + except OSError as err: + self.assertEqual(err.errno, errno.EBADF) + else: + self.fail("exception not raised") + + def test_returned_list_identity(self): + # See issue #8329 + r, w, x = select.select([], [], [], 1) + self.assertIsNot(r, w) + self.assertIsNot(r, x) + self.assertIsNot(w, x) + + @support.requires_fork() + def test_select(self): + code = textwrap.dedent(''' + import time + for i in range(10): + print("testing...", flush=True) + time.sleep(0.050) + ''') + cmd = [sys.executable, '-I', '-c', code] + with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc: + pipe = proc.stdout + for timeout in (0, 1, 2, 4, 8, 16) + (None,)*10: + if support.verbose: + print(f'timeout = {timeout}') + rfd, wfd, xfd = select.select([pipe], [], [], timeout) + self.assertEqual(wfd, []) + self.assertEqual(xfd, []) + if not rfd: + continue + if rfd == [pipe]: + line = pipe.readline() + if support.verbose: + print(repr(line)) + if not line: + if support.verbose: + print('EOF') + break + continue + self.fail('Unexpected return values from select():', + rfd, wfd, xfd) + + # Issue 16230: Crash on select resized list + @unittest.skipIf( + support.is_emscripten, "Emscripten cannot select a fd multiple times." + ) + def test_select_mutated(self): + a = [] + class F: + def fileno(self): + del a[-1] + return sys.__stdout__.fileno() + a[:] = [F()] * 10 + self.assertEqual(select.select([], a, []), ([], a[:5], [])) + + def test_disallow_instantiation(self): + support.check_disallow_instantiation(self, type(select.poll())) + + if hasattr(select, 'devpoll'): + support.check_disallow_instantiation(self, type(select.devpoll())) + +def tearDownModule(): + support.reap_children() + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py new file mode 100644 index 00000000000..c5280673ab8 --- /dev/null +++ b/Lib/test/test_source_encoding.py @@ -0,0 +1,429 @@ +# -*- coding: utf-8 -*- + +import unittest +from test.support import script_helper, captured_stdout, requires_subprocess, requires_resource +from test.support.os_helper import TESTFN, unlink, rmtree +from test.support.import_helper import unload +import importlib +import os +import sys +import subprocess +import tempfile + +class MiscSourceEncodingTest(unittest.TestCase): + + def test_import_encoded_module(self): + from test.encoded_modules import test_strings + # Make sure we're actually testing something + self.assertGreaterEqual(len(test_strings), 1) + for modname, encoding, teststr in test_strings: + mod = importlib.import_module('test.encoded_modules.' + 'module_' + modname) + self.assertEqual(teststr, mod.test) + + def test_compilestring(self): + # see #1882 + c = compile(b"\n# coding: utf-8\nu = '\xc3\xb3'\n", "dummy", "exec") + d = {} + exec(c, d) + self.assertEqual(d['u'], '\xf3') + + def test_issue2301(self): + try: + compile(b"# coding: cp932\nprint '\x94\x4e'", "dummy", "exec") + except SyntaxError as v: + self.assertEqual(v.text.rstrip('\n'), "print '\u5e74'") + else: + self.fail() + + def test_issue4626(self): + c = compile("# coding=latin-1\n\u00c6 = '\u00c6'", "dummy", "exec") + d = {} + exec(c, d) + self.assertEqual(d['\xc6'], '\xc6') + + def test_issue3297(self): + c = compile("a, b = '\U0001010F', '\\U0001010F'", "dummy", "exec") + d = {} + exec(c, d) + self.assertEqual(d['a'], d['b']) + self.assertEqual(len(d['a']), len(d['b'])) + self.assertEqual(ascii(d['a']), ascii(d['b'])) + + def test_issue7820(self): + # Ensure that check_bom() restores all bytes in the right order if + # check_bom() fails in pydebug mode: a buffer starts with the first + # byte of a valid BOM, but next bytes are different + + # one byte in common with the UTF-16-LE BOM + self.assertRaises(SyntaxError, eval, b'\xff\x20') + + # one byte in common with the UTF-8 BOM + self.assertRaises(SyntaxError, eval, b'\xef\x20') + + # two bytes in common with the UTF-8 BOM + self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') + + @requires_subprocess() + def test_20731(self): + sub = subprocess.Popen([sys.executable, + os.path.join(os.path.dirname(__file__), + 'tokenizedata', + 'coding20731.py')], + stderr=subprocess.PIPE) + err = sub.communicate()[1] + self.assertEqual(sub.returncode, 0) + self.assertNotIn(b'SyntaxError', err) + + def test_error_message(self): + compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') + compile(b'\xef\xbb\xbf# -*- coding: utf-8 -*-\n', 'dummy', 'exec') + with self.assertRaisesRegex(SyntaxError, 'fake'): + compile(b'# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegex(SyntaxError, 'iso-8859-15'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegex(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: iso-8859-15 -*-\n', + 'dummy', 'exec') + with self.assertRaisesRegex(SyntaxError, 'fake'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + with self.assertRaisesRegex(SyntaxError, 'BOM'): + compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + + def test_bad_coding(self): + module_name = 'bad_coding' + self.verify_bad_module(module_name) + + def test_bad_coding2(self): + module_name = 'bad_coding2' + self.verify_bad_module(module_name) + + def verify_bad_module(self, module_name): + self.assertRaises(SyntaxError, __import__, 'test.tokenizedata.' + module_name) + + path = os.path.dirname(__file__) + filename = os.path.join(path, 'tokenizedata', module_name + '.py') + with open(filename, "rb") as fp: + bytes = fp.read() + self.assertRaises(SyntaxError, compile, bytes, filename, 'exec') + + def test_exec_valid_coding(self): + d = {} + exec(b'# coding: cp949\na = "\xaa\xa7"\n', d) + self.assertEqual(d['a'], '\u3047') + + def test_file_parse(self): + # issue1134: all encodings outside latin-1 and utf-8 fail on + # multiline strings and long lines (>512 columns) + unload(TESTFN) + filename = TESTFN + ".py" + f = open(filename, "w", encoding="cp1252") + sys.path.insert(0, os.curdir) + try: + with f: + f.write("# -*- coding: cp1252 -*-\n") + f.write("'''A short string\n") + f.write("'''\n") + f.write("'A very long string %s'\n" % ("X" * 1000)) + + importlib.invalidate_caches() + __import__(TESTFN) + finally: + del sys.path[0] + unlink(filename) + unlink(filename + "c") + unlink(filename + "o") + unload(TESTFN) + rmtree('__pycache__') + + def test_error_from_string(self): + # See http://bugs.python.org/issue6289 + input = "# coding: ascii\n\N{SNOWMAN}".encode('utf-8') + with self.assertRaises(SyntaxError) as c: + compile(input, "", "exec") + expected = "'ascii' codec can't decode byte 0xe2 in position 16: " \ + "ordinal not in range(128)" + self.assertTrue(c.exception.args[0].startswith(expected), + msg=c.exception.args[0]) + + def test_file_parse_error_multiline(self): + # gh96611: + with open(TESTFN, "wb") as fd: + fd.write(b'print("""\n\xb1""")\n') + + try: + retcode, stdout, stderr = script_helper.assert_python_failure(TESTFN) + + self.assertGreater(retcode, 0) + self.assertIn(b"Non-UTF-8 code starting with '\\xb1'", stderr) + finally: + os.unlink(TESTFN) + + def test_tokenizer_fstring_warning_in_first_line(self): + source = "0b1and 2" + with open(TESTFN, "w") as fd: + fd.write("{}".format(source)) + try: + retcode, stdout, stderr = script_helper.assert_python_ok(TESTFN) + self.assertIn(b"SyntaxWarning: invalid binary litera", stderr) + self.assertEqual(stderr.count(source.encode()), 1) + finally: + os.unlink(TESTFN) + + +BUFSIZ = 2**13 + +class AbstractSourceEncodingTest: + + def test_default_coding(self): + src = (b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_first_coding_line(self): + src = (b'#coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_second_coding_line(self): + src = (b'#!/usr/bin/python\n' + b'#coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_second_coding_line_empty_first_line(self): + src = (b'\n' + b'#coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_third_coding_line(self): + # Only first two lines are tested for a magic comment. + src = (b'#!/usr/bin/python\n' + b'#\n' + b'#coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_double_coding_line(self): + # If the first line matches the second line is ignored. + src = (b'#coding:iso8859-15\n' + b'#coding:latin1\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_double_coding_same_line(self): + src = (b'#coding:iso8859-15 coding:latin1\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_double_coding_utf8(self): + src = (b'#coding:utf-8\n' + b'#coding:latin1\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_long_first_coding_line(self): + src = (b'#' + b' '*BUFSIZ + b'coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_long_second_coding_line(self): + src = (b'#!/usr/bin/python\n' + b'#' + b' '*BUFSIZ + b'coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_long_coding_line(self): + src = (b'#coding:iso-8859-15' + b' '*BUFSIZ + b'\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_long_coding_name(self): + src = (b'#coding:iso-8859-1-' + b'x'*BUFSIZ + b'\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\xa4'") + + def test_long_first_utf8_line(self): + src = b'#' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + src = b'# ' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + + def test_long_second_utf8_line(self): + src = b'\n#' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + src = b'\n# ' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + + def test_first_non_utf8_coding_line(self): + src = (b'#coding:iso-8859-15 \xa4\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_second_non_utf8_coding_line(self): + src = (b'#!/usr/bin/python\n' + b'#coding:iso-8859-15 \xa4\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_utf8_bom(self): + src = (b'\xef\xbb\xbfprint(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_utf8_bom_utf8_comments(self): + src = (b'\xef\xbb\xbf#\xc3\xa4\n' + b'#\xc3\xa4\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_utf8_bom_and_utf8_coding_line(self): + src = (b'\xef\xbb\xbf#coding:utf-8\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_utf8_non_utf8_comment_line_error(self): + src = (b'#coding: utf8\n' + b'#\n' + b'#\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"'utf-8' codec can't decode byte|" + br"encoding problem: utf8") + + def test_crlf(self): + src = (b'print(ascii("""\r\n"""))\n') + self.check_script_output(src, br"'\n'") + + def test_crcrlf(self): + src = (b'print(ascii("""\r\r\n"""))\n') + self.check_script_output(src, br"'\n\n'") + + def test_crcrcrlf(self): + src = (b'print(ascii("""\r\r\r\n"""))\n') + self.check_script_output(src, br"'\n\n\n'") + + def test_crcrcrlf2(self): + src = (b'#coding:iso-8859-1\n' + b'print(ascii("""\r\r\r\n"""))\n') + self.check_script_output(src, br"'\n\n\n'") + + def test_nul_in_first_coding_line(self): + src = (b'#coding:iso8859-15\x00\n' + b'\n' + b'\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"source code (string )?cannot contain null bytes") + + def test_nul_in_second_coding_line(self): + src = (b'#!/usr/bin/python\n' + b'#coding:iso8859-15\x00\n' + b'\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"source code (string )?cannot contain null bytes") + + +class UTF8ValidatorTest(unittest.TestCase): + @unittest.skipIf(not sys.platform.startswith("linux"), + "Too slow to run on non-Linux platforms") + @requires_resource('cpu') + def test_invalid_utf8(self): + # This is a port of test_utf8_decode_invalid_sequences in + # test_unicode.py to exercise the separate utf8 validator in + # Parser/tokenizer/helpers.c used when reading source files. + + # That file is written using low-level C file I/O, so the only way to + # test it is to write actual files to disk. + + # Each example is put inside a string at the top of the file so + # it's an otherwise valid Python source file. Put some newlines + # beforehand so we can assert that the error is reported on the + # correct line. + template = b'\n\n\n"%s"\n' + + fn = TESTFN + self.addCleanup(unlink, fn) + + def check(content): + with open(fn, 'wb') as fp: + fp.write(template % content) + rc, stdout, stderr = script_helper.assert_python_failure(fn) + # We want to assert that the python subprocess failed gracefully, + # not via a signal. + self.assertGreaterEqual(rc, 1) + self.assertIn(b"Non-UTF-8 code starting with", stderr) + self.assertIn(b"on line 4", stderr) + + # continuation bytes in a sequence of 2, 3, or 4 bytes + continuation_bytes = [bytes([x]) for x in range(0x80, 0xC0)] + # start bytes of a 2-byte sequence equivalent to code points < 0x7F + invalid_2B_seq_start_bytes = [bytes([x]) for x in range(0xC0, 0xC2)] + # start bytes of a 4-byte sequence equivalent to code points > 0x10FFFF + invalid_4B_seq_start_bytes = [bytes([x]) for x in range(0xF5, 0xF8)] + invalid_start_bytes = ( + continuation_bytes + invalid_2B_seq_start_bytes + + invalid_4B_seq_start_bytes + [bytes([x]) for x in range(0xF7, 0x100)] + ) + + for byte in invalid_start_bytes: + check(byte) + + for sb in invalid_2B_seq_start_bytes: + for cb in continuation_bytes: + check(sb + cb) + + for sb in invalid_4B_seq_start_bytes: + for cb1 in continuation_bytes[:3]: + for cb3 in continuation_bytes[:3]: + check(sb+cb1+b'\x80'+cb3) + + for cb in [bytes([x]) for x in range(0x80, 0xA0)]: + check(b'\xE0'+cb+b'\x80') + check(b'\xE0'+cb+b'\xBF') + # surrogates + for cb in [bytes([x]) for x in range(0xA0, 0xC0)]: + check(b'\xED'+cb+b'\x80') + check(b'\xED'+cb+b'\xBF') + for cb in [bytes([x]) for x in range(0x80, 0x90)]: + check(b'\xF0'+cb+b'\x80\x80') + check(b'\xF0'+cb+b'\xBF\xBF') + for cb in [bytes([x]) for x in range(0x90, 0xC0)]: + check(b'\xF4'+cb+b'\x80\x80') + check(b'\xF4'+cb+b'\xBF\xBF') + + +class BytesSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): + + def check_script_output(self, src, expected): + with captured_stdout() as stdout: + exec(src) + out = stdout.getvalue().encode('latin1') + self.assertEqual(out.rstrip(), expected) + + def check_script_error(self, src, expected): + with self.assertRaisesRegex(SyntaxError, expected.decode()) as cm: + exec(src) + + +class FileSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): + + def check_script_output(self, src, expected): + with tempfile.TemporaryDirectory() as tmpd: + fn = os.path.join(tmpd, 'test.py') + with open(fn, 'wb') as fp: + fp.write(src) + res = script_helper.assert_python_ok(fn) + self.assertEqual(res.out.rstrip(), expected) + + def check_script_error(self, src, expected): + with tempfile.TemporaryDirectory() as tmpd: + fn = os.path.join(tmpd, 'test.py') + with open(fn, 'wb') as fp: + fp.write(src) + res = script_helper.assert_python_failure(fn) + self.assertRegex(res.err.rstrip().splitlines()[-1], b'SyntaxError.*?' + expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py new file mode 100644 index 00000000000..21e2c6f1d2c --- /dev/null +++ b/Lib/test/test_stable_abi_ctypes.py @@ -0,0 +1,985 @@ +# Generated by Tools/build/stable_abi.py + +"""Test that all symbols of the Stable ABI are accessible using ctypes +""" + +import sys +import unittest +from test.support.import_helper import import_module +try: + from _testcapi import get_feature_macros +except ImportError: + raise unittest.SkipTest("requires _testcapi") + +feature_macros = get_feature_macros() + +# Stable ABI is incompatible with Py_TRACE_REFS builds due to PyObject +# layout differences. +# See https://github.com/python/cpython/issues/88299#issuecomment-1113366226 +if feature_macros['Py_TRACE_REFS']: + raise unittest.SkipTest("incompatible with Py_TRACE_REFS.") + +ctypes_test = import_module('ctypes') + +class TestStableABIAvailability(unittest.TestCase): + def test_available_symbols(self): + + for symbol_name in SYMBOL_NAMES: + with self.subTest(symbol_name): + ctypes_test.pythonapi[symbol_name] + + def test_feature_macros(self): + self.assertEqual( + set(get_feature_macros()), EXPECTED_FEATURE_MACROS) + + # The feature macros for Windows are used in creating the DLL + # definition, so they must be known on all platforms. + # If we are on Windows, we check that the hardcoded data matches + # the reality. + @unittest.skipIf(sys.platform != "win32", "Windows specific test") + def test_windows_feature_macros(self): + for name, value in WINDOWS_FEATURE_MACROS.items(): + if value != 'maybe': + with self.subTest(name): + self.assertEqual(feature_macros[name], value) + +SYMBOL_NAMES = ( + + "PyAIter_Check", + "PyArg_Parse", + "PyArg_ParseTuple", + "PyArg_ParseTupleAndKeywords", + "PyArg_UnpackTuple", + "PyArg_VaParse", + "PyArg_VaParseTupleAndKeywords", + "PyArg_ValidateKeywordArguments", + "PyBaseObject_Type", + "PyBool_FromLong", + "PyBool_Type", + "PyBuffer_FillContiguousStrides", + "PyBuffer_FillInfo", + "PyBuffer_FromContiguous", + "PyBuffer_GetPointer", + "PyBuffer_IsContiguous", + "PyBuffer_Release", + "PyBuffer_SizeFromFormat", + "PyBuffer_ToContiguous", + "PyByteArrayIter_Type", + "PyByteArray_AsString", + "PyByteArray_Concat", + "PyByteArray_FromObject", + "PyByteArray_FromStringAndSize", + "PyByteArray_Resize", + "PyByteArray_Size", + "PyByteArray_Type", + "PyBytesIter_Type", + "PyBytes_AsString", + "PyBytes_AsStringAndSize", + "PyBytes_Concat", + "PyBytes_ConcatAndDel", + "PyBytes_DecodeEscape", + "PyBytes_FromFormat", + "PyBytes_FromFormatV", + "PyBytes_FromObject", + "PyBytes_FromString", + "PyBytes_FromStringAndSize", + "PyBytes_Repr", + "PyBytes_Size", + "PyBytes_Type", + "PyCFunction_Call", + "PyCFunction_GetFlags", + "PyCFunction_GetFunction", + "PyCFunction_GetSelf", + "PyCFunction_New", + "PyCFunction_NewEx", + "PyCFunction_Type", + "PyCMethod_New", + "PyCallIter_New", + "PyCallIter_Type", + "PyCallable_Check", + "PyCapsule_GetContext", + "PyCapsule_GetDestructor", + "PyCapsule_GetName", + "PyCapsule_GetPointer", + "PyCapsule_Import", + "PyCapsule_IsValid", + "PyCapsule_New", + "PyCapsule_SetContext", + "PyCapsule_SetDestructor", + "PyCapsule_SetName", + "PyCapsule_SetPointer", + "PyCapsule_Type", + "PyClassMethodDescr_Type", + "PyCodec_BackslashReplaceErrors", + "PyCodec_Decode", + "PyCodec_Decoder", + "PyCodec_Encode", + "PyCodec_Encoder", + "PyCodec_IgnoreErrors", + "PyCodec_IncrementalDecoder", + "PyCodec_IncrementalEncoder", + "PyCodec_KnownEncoding", + "PyCodec_LookupError", + "PyCodec_NameReplaceErrors", + "PyCodec_Register", + "PyCodec_RegisterError", + "PyCodec_ReplaceErrors", + "PyCodec_StreamReader", + "PyCodec_StreamWriter", + "PyCodec_StrictErrors", + "PyCodec_Unregister", + "PyCodec_XMLCharRefReplaceErrors", + "PyComplex_FromDoubles", + "PyComplex_ImagAsDouble", + "PyComplex_RealAsDouble", + "PyComplex_Type", + "PyDescr_NewClassMethod", + "PyDescr_NewGetSet", + "PyDescr_NewMember", + "PyDescr_NewMethod", + "PyDictItems_Type", + "PyDictIterItem_Type", + "PyDictIterKey_Type", + "PyDictIterValue_Type", + "PyDictKeys_Type", + "PyDictProxy_New", + "PyDictProxy_Type", + "PyDictRevIterItem_Type", + "PyDictRevIterKey_Type", + "PyDictRevIterValue_Type", + "PyDictValues_Type", + "PyDict_Clear", + "PyDict_Contains", + "PyDict_Copy", + "PyDict_DelItem", + "PyDict_DelItemString", + "PyDict_GetItem", + "PyDict_GetItemRef", + "PyDict_GetItemString", + "PyDict_GetItemStringRef", + "PyDict_GetItemWithError", + "PyDict_Items", + "PyDict_Keys", + "PyDict_Merge", + "PyDict_MergeFromSeq2", + "PyDict_New", + "PyDict_Next", + "PyDict_SetItem", + "PyDict_SetItemString", + "PyDict_Size", + "PyDict_Type", + "PyDict_Update", + "PyDict_Values", + "PyEllipsis_Type", + "PyEnum_Type", + "PyErr_BadArgument", + "PyErr_BadInternalCall", + "PyErr_CheckSignals", + "PyErr_Clear", + "PyErr_Display", + "PyErr_DisplayException", + "PyErr_ExceptionMatches", + "PyErr_Fetch", + "PyErr_Format", + "PyErr_FormatV", + "PyErr_GetExcInfo", + "PyErr_GetHandledException", + "PyErr_GetRaisedException", + "PyErr_GivenExceptionMatches", + "PyErr_NewException", + "PyErr_NewExceptionWithDoc", + "PyErr_NoMemory", + "PyErr_NormalizeException", + "PyErr_Occurred", + "PyErr_Print", + "PyErr_PrintEx", + "PyErr_ProgramText", + "PyErr_ResourceWarning", + "PyErr_Restore", + "PyErr_SetExcInfo", + "PyErr_SetFromErrno", + "PyErr_SetFromErrnoWithFilename", + "PyErr_SetFromErrnoWithFilenameObject", + "PyErr_SetFromErrnoWithFilenameObjects", + "PyErr_SetHandledException", + "PyErr_SetImportError", + "PyErr_SetImportErrorSubclass", + "PyErr_SetInterrupt", + "PyErr_SetInterruptEx", + "PyErr_SetNone", + "PyErr_SetObject", + "PyErr_SetRaisedException", + "PyErr_SetString", + "PyErr_SyntaxLocation", + "PyErr_SyntaxLocationEx", + "PyErr_WarnEx", + "PyErr_WarnExplicit", + "PyErr_WarnFormat", + "PyErr_WriteUnraisable", + "PyEval_AcquireLock", + "PyEval_AcquireThread", + "PyEval_CallFunction", + "PyEval_CallMethod", + "PyEval_CallObjectWithKeywords", + "PyEval_EvalCode", + "PyEval_EvalCodeEx", + "PyEval_EvalFrame", + "PyEval_EvalFrameEx", + "PyEval_GetBuiltins", + "PyEval_GetFrame", + "PyEval_GetFrameBuiltins", + "PyEval_GetFrameGlobals", + "PyEval_GetFrameLocals", + "PyEval_GetFuncDesc", + "PyEval_GetFuncName", + "PyEval_GetGlobals", + "PyEval_GetLocals", + "PyEval_InitThreads", + "PyEval_ReleaseLock", + "PyEval_ReleaseThread", + "PyEval_RestoreThread", + "PyEval_SaveThread", + "PyEval_ThreadsInitialized", + "PyExc_ArithmeticError", + "PyExc_AssertionError", + "PyExc_AttributeError", + "PyExc_BaseException", + "PyExc_BaseExceptionGroup", + "PyExc_BlockingIOError", + "PyExc_BrokenPipeError", + "PyExc_BufferError", + "PyExc_BytesWarning", + "PyExc_ChildProcessError", + "PyExc_ConnectionAbortedError", + "PyExc_ConnectionError", + "PyExc_ConnectionRefusedError", + "PyExc_ConnectionResetError", + "PyExc_DeprecationWarning", + "PyExc_EOFError", + "PyExc_EncodingWarning", + "PyExc_EnvironmentError", + "PyExc_Exception", + "PyExc_FileExistsError", + "PyExc_FileNotFoundError", + "PyExc_FloatingPointError", + "PyExc_FutureWarning", + "PyExc_GeneratorExit", + "PyExc_IOError", + "PyExc_ImportError", + "PyExc_ImportWarning", + "PyExc_IndentationError", + "PyExc_IndexError", + "PyExc_InterruptedError", + "PyExc_IsADirectoryError", + "PyExc_KeyError", + "PyExc_KeyboardInterrupt", + "PyExc_LookupError", + "PyExc_MemoryError", + "PyExc_ModuleNotFoundError", + "PyExc_NameError", + "PyExc_NotADirectoryError", + "PyExc_NotImplementedError", + "PyExc_OSError", + "PyExc_OverflowError", + "PyExc_PendingDeprecationWarning", + "PyExc_PermissionError", + "PyExc_ProcessLookupError", + "PyExc_RecursionError", + "PyExc_ReferenceError", + "PyExc_ResourceWarning", + "PyExc_RuntimeError", + "PyExc_RuntimeWarning", + "PyExc_StopAsyncIteration", + "PyExc_StopIteration", + "PyExc_SyntaxError", + "PyExc_SyntaxWarning", + "PyExc_SystemError", + "PyExc_SystemExit", + "PyExc_TabError", + "PyExc_TimeoutError", + "PyExc_TypeError", + "PyExc_UnboundLocalError", + "PyExc_UnicodeDecodeError", + "PyExc_UnicodeEncodeError", + "PyExc_UnicodeError", + "PyExc_UnicodeTranslateError", + "PyExc_UnicodeWarning", + "PyExc_UserWarning", + "PyExc_ValueError", + "PyExc_Warning", + "PyExc_ZeroDivisionError", + "PyExceptionClass_Name", + "PyException_GetArgs", + "PyException_GetCause", + "PyException_GetContext", + "PyException_GetTraceback", + "PyException_SetArgs", + "PyException_SetCause", + "PyException_SetContext", + "PyException_SetTraceback", + "PyFile_FromFd", + "PyFile_GetLine", + "PyFile_WriteObject", + "PyFile_WriteString", + "PyFilter_Type", + "PyFloat_AsDouble", + "PyFloat_FromDouble", + "PyFloat_FromString", + "PyFloat_GetInfo", + "PyFloat_GetMax", + "PyFloat_GetMin", + "PyFloat_Type", + "PyFrame_GetCode", + "PyFrame_GetLineNumber", + "PyFrozenSet_New", + "PyFrozenSet_Type", + "PyGC_Collect", + "PyGC_Disable", + "PyGC_Enable", + "PyGC_IsEnabled", + "PyGILState_Ensure", + "PyGILState_GetThisThreadState", + "PyGILState_Release", + "PyGetSetDescr_Type", + "PyImport_AddModule", + "PyImport_AddModuleObject", + "PyImport_AddModuleRef", + "PyImport_AppendInittab", + "PyImport_ExecCodeModule", + "PyImport_ExecCodeModuleEx", + "PyImport_ExecCodeModuleObject", + "PyImport_ExecCodeModuleWithPathnames", + "PyImport_GetImporter", + "PyImport_GetMagicNumber", + "PyImport_GetMagicTag", + "PyImport_GetModule", + "PyImport_GetModuleDict", + "PyImport_Import", + "PyImport_ImportFrozenModule", + "PyImport_ImportFrozenModuleObject", + "PyImport_ImportModule", + "PyImport_ImportModuleLevel", + "PyImport_ImportModuleLevelObject", + "PyImport_ImportModuleNoBlock", + "PyImport_ReloadModule", + "PyIndex_Check", + "PyInterpreterState_Clear", + "PyInterpreterState_Delete", + "PyInterpreterState_Get", + "PyInterpreterState_GetDict", + "PyInterpreterState_GetID", + "PyInterpreterState_New", + "PyIter_Check", + "PyIter_Next", + "PyIter_Send", + "PyListIter_Type", + "PyListRevIter_Type", + "PyList_Append", + "PyList_AsTuple", + "PyList_GetItem", + "PyList_GetItemRef", + "PyList_GetSlice", + "PyList_Insert", + "PyList_New", + "PyList_Reverse", + "PyList_SetItem", + "PyList_SetSlice", + "PyList_Size", + "PyList_Sort", + "PyList_Type", + "PyLongRangeIter_Type", + "PyLong_AsDouble", + "PyLong_AsInt", + "PyLong_AsLong", + "PyLong_AsLongAndOverflow", + "PyLong_AsLongLong", + "PyLong_AsLongLongAndOverflow", + "PyLong_AsSize_t", + "PyLong_AsSsize_t", + "PyLong_AsUnsignedLong", + "PyLong_AsUnsignedLongLong", + "PyLong_AsUnsignedLongLongMask", + "PyLong_AsUnsignedLongMask", + "PyLong_AsVoidPtr", + "PyLong_FromDouble", + "PyLong_FromLong", + "PyLong_FromLongLong", + "PyLong_FromSize_t", + "PyLong_FromSsize_t", + "PyLong_FromString", + "PyLong_FromUnsignedLong", + "PyLong_FromUnsignedLongLong", + "PyLong_FromVoidPtr", + "PyLong_GetInfo", + "PyLong_Type", + "PyMap_Type", + "PyMapping_Check", + "PyMapping_GetItemString", + "PyMapping_GetOptionalItem", + "PyMapping_GetOptionalItemString", + "PyMapping_HasKey", + "PyMapping_HasKeyString", + "PyMapping_HasKeyStringWithError", + "PyMapping_HasKeyWithError", + "PyMapping_Items", + "PyMapping_Keys", + "PyMapping_Length", + "PyMapping_SetItemString", + "PyMapping_Size", + "PyMapping_Values", + "PyMarshal_ReadObjectFromString", + "PyMarshal_WriteObjectToString", + "PyMem_Calloc", + "PyMem_Free", + "PyMem_Malloc", + "PyMem_RawCalloc", + "PyMem_RawFree", + "PyMem_RawMalloc", + "PyMem_RawRealloc", + "PyMem_Realloc", + "PyMemberDescr_Type", + "PyMember_GetOne", + "PyMember_SetOne", + "PyMemoryView_FromBuffer", + "PyMemoryView_FromMemory", + "PyMemoryView_FromObject", + "PyMemoryView_GetContiguous", + "PyMemoryView_Type", + "PyMethodDescr_Type", + "PyModuleDef_Init", + "PyModuleDef_Type", + "PyModule_Add", + "PyModule_AddFunctions", + "PyModule_AddIntConstant", + "PyModule_AddObject", + "PyModule_AddObjectRef", + "PyModule_AddStringConstant", + "PyModule_AddType", + "PyModule_Create2", + "PyModule_ExecDef", + "PyModule_FromDefAndSpec2", + "PyModule_GetDef", + "PyModule_GetDict", + "PyModule_GetFilename", + "PyModule_GetFilenameObject", + "PyModule_GetName", + "PyModule_GetNameObject", + "PyModule_GetState", + "PyModule_New", + "PyModule_NewObject", + "PyModule_SetDocString", + "PyModule_Type", + "PyNumber_Absolute", + "PyNumber_Add", + "PyNumber_And", + "PyNumber_AsSsize_t", + "PyNumber_Check", + "PyNumber_Divmod", + "PyNumber_Float", + "PyNumber_FloorDivide", + "PyNumber_InPlaceAdd", + "PyNumber_InPlaceAnd", + "PyNumber_InPlaceFloorDivide", + "PyNumber_InPlaceLshift", + "PyNumber_InPlaceMatrixMultiply", + "PyNumber_InPlaceMultiply", + "PyNumber_InPlaceOr", + "PyNumber_InPlacePower", + "PyNumber_InPlaceRemainder", + "PyNumber_InPlaceRshift", + "PyNumber_InPlaceSubtract", + "PyNumber_InPlaceTrueDivide", + "PyNumber_InPlaceXor", + "PyNumber_Index", + "PyNumber_Invert", + "PyNumber_Long", + "PyNumber_Lshift", + "PyNumber_MatrixMultiply", + "PyNumber_Multiply", + "PyNumber_Negative", + "PyNumber_Or", + "PyNumber_Positive", + "PyNumber_Power", + "PyNumber_Remainder", + "PyNumber_Rshift", + "PyNumber_Subtract", + "PyNumber_ToBase", + "PyNumber_TrueDivide", + "PyNumber_Xor", + "PyOS_FSPath", + "PyOS_InputHook", + "PyOS_InterruptOccurred", + "PyOS_double_to_string", + "PyOS_getsig", + "PyOS_mystricmp", + "PyOS_mystrnicmp", + "PyOS_setsig", + "PyOS_snprintf", + "PyOS_string_to_double", + "PyOS_strtol", + "PyOS_strtoul", + "PyOS_vsnprintf", + "PyObject_ASCII", + "PyObject_AsCharBuffer", + "PyObject_AsFileDescriptor", + "PyObject_AsReadBuffer", + "PyObject_AsWriteBuffer", + "PyObject_Bytes", + "PyObject_Call", + "PyObject_CallFunction", + "PyObject_CallFunctionObjArgs", + "PyObject_CallMethod", + "PyObject_CallMethodObjArgs", + "PyObject_CallNoArgs", + "PyObject_CallObject", + "PyObject_Calloc", + "PyObject_CheckBuffer", + "PyObject_CheckReadBuffer", + "PyObject_ClearWeakRefs", + "PyObject_CopyData", + "PyObject_DelAttr", + "PyObject_DelAttrString", + "PyObject_DelItem", + "PyObject_DelItemString", + "PyObject_Dir", + "PyObject_Format", + "PyObject_Free", + "PyObject_GC_Del", + "PyObject_GC_IsFinalized", + "PyObject_GC_IsTracked", + "PyObject_GC_Track", + "PyObject_GC_UnTrack", + "PyObject_GenericGetAttr", + "PyObject_GenericGetDict", + "PyObject_GenericSetAttr", + "PyObject_GenericSetDict", + "PyObject_GetAIter", + "PyObject_GetAttr", + "PyObject_GetAttrString", + "PyObject_GetBuffer", + "PyObject_GetItem", + "PyObject_GetIter", + "PyObject_GetOptionalAttr", + "PyObject_GetOptionalAttrString", + "PyObject_GetTypeData", + "PyObject_HasAttr", + "PyObject_HasAttrString", + "PyObject_HasAttrStringWithError", + "PyObject_HasAttrWithError", + "PyObject_Hash", + "PyObject_HashNotImplemented", + "PyObject_Init", + "PyObject_InitVar", + "PyObject_IsInstance", + "PyObject_IsSubclass", + "PyObject_IsTrue", + "PyObject_Length", + "PyObject_Malloc", + "PyObject_Not", + "PyObject_Realloc", + "PyObject_Repr", + "PyObject_RichCompare", + "PyObject_RichCompareBool", + "PyObject_SelfIter", + "PyObject_SetAttr", + "PyObject_SetAttrString", + "PyObject_SetItem", + "PyObject_Size", + "PyObject_Str", + "PyObject_Type", + "PyObject_Vectorcall", + "PyObject_VectorcallMethod", + "PyProperty_Type", + "PyRangeIter_Type", + "PyRange_Type", + "PyReversed_Type", + "PySeqIter_New", + "PySeqIter_Type", + "PySequence_Check", + "PySequence_Concat", + "PySequence_Contains", + "PySequence_Count", + "PySequence_DelItem", + "PySequence_DelSlice", + "PySequence_Fast", + "PySequence_GetItem", + "PySequence_GetSlice", + "PySequence_In", + "PySequence_InPlaceConcat", + "PySequence_InPlaceRepeat", + "PySequence_Index", + "PySequence_Length", + "PySequence_List", + "PySequence_Repeat", + "PySequence_SetItem", + "PySequence_SetSlice", + "PySequence_Size", + "PySequence_Tuple", + "PySetIter_Type", + "PySet_Add", + "PySet_Clear", + "PySet_Contains", + "PySet_Discard", + "PySet_New", + "PySet_Pop", + "PySet_Size", + "PySet_Type", + "PySlice_AdjustIndices", + "PySlice_GetIndices", + "PySlice_GetIndicesEx", + "PySlice_New", + "PySlice_Type", + "PySlice_Unpack", + "PyState_AddModule", + "PyState_FindModule", + "PyState_RemoveModule", + "PyStructSequence_GetItem", + "PyStructSequence_New", + "PyStructSequence_NewType", + "PyStructSequence_SetItem", + "PyStructSequence_UnnamedField", + "PySuper_Type", + "PySys_AddWarnOption", + "PySys_AddWarnOptionUnicode", + "PySys_AddXOption", + "PySys_Audit", + "PySys_AuditTuple", + "PySys_FormatStderr", + "PySys_FormatStdout", + "PySys_GetObject", + "PySys_GetXOptions", + "PySys_HasWarnOptions", + "PySys_ResetWarnOptions", + "PySys_SetArgv", + "PySys_SetArgvEx", + "PySys_SetObject", + "PySys_SetPath", + "PySys_WriteStderr", + "PySys_WriteStdout", + "PyThreadState_Clear", + "PyThreadState_Delete", + "PyThreadState_DeleteCurrent", + "PyThreadState_Get", + "PyThreadState_GetDict", + "PyThreadState_GetFrame", + "PyThreadState_GetID", + "PyThreadState_GetInterpreter", + "PyThreadState_New", + "PyThreadState_SetAsyncExc", + "PyThreadState_Swap", + "PyThread_GetInfo", + "PyThread_ReInitTLS", + "PyThread_acquire_lock", + "PyThread_acquire_lock_timed", + "PyThread_allocate_lock", + "PyThread_create_key", + "PyThread_delete_key", + "PyThread_delete_key_value", + "PyThread_exit_thread", + "PyThread_free_lock", + "PyThread_get_key_value", + "PyThread_get_stacksize", + "PyThread_get_thread_ident", + "PyThread_init_thread", + "PyThread_release_lock", + "PyThread_set_key_value", + "PyThread_set_stacksize", + "PyThread_start_new_thread", + "PyThread_tss_alloc", + "PyThread_tss_create", + "PyThread_tss_delete", + "PyThread_tss_free", + "PyThread_tss_get", + "PyThread_tss_is_created", + "PyThread_tss_set", + "PyTraceBack_Here", + "PyTraceBack_Print", + "PyTraceBack_Type", + "PyTupleIter_Type", + "PyTuple_GetItem", + "PyTuple_GetSlice", + "PyTuple_New", + "PyTuple_Pack", + "PyTuple_SetItem", + "PyTuple_Size", + "PyTuple_Type", + "PyType_ClearCache", + "PyType_FromMetaclass", + "PyType_FromModuleAndSpec", + "PyType_FromSpec", + "PyType_FromSpecWithBases", + "PyType_GenericAlloc", + "PyType_GenericNew", + "PyType_GetFlags", + "PyType_GetFullyQualifiedName", + "PyType_GetModule", + "PyType_GetModuleByDef", + "PyType_GetModuleName", + "PyType_GetModuleState", + "PyType_GetName", + "PyType_GetQualName", + "PyType_GetSlot", + "PyType_GetTypeDataSize", + "PyType_IsSubtype", + "PyType_Modified", + "PyType_Ready", + "PyType_Type", + "PyUnicodeDecodeError_Create", + "PyUnicodeDecodeError_GetEncoding", + "PyUnicodeDecodeError_GetEnd", + "PyUnicodeDecodeError_GetObject", + "PyUnicodeDecodeError_GetReason", + "PyUnicodeDecodeError_GetStart", + "PyUnicodeDecodeError_SetEnd", + "PyUnicodeDecodeError_SetReason", + "PyUnicodeDecodeError_SetStart", + "PyUnicodeEncodeError_GetEncoding", + "PyUnicodeEncodeError_GetEnd", + "PyUnicodeEncodeError_GetObject", + "PyUnicodeEncodeError_GetReason", + "PyUnicodeEncodeError_GetStart", + "PyUnicodeEncodeError_SetEnd", + "PyUnicodeEncodeError_SetReason", + "PyUnicodeEncodeError_SetStart", + "PyUnicodeIter_Type", + "PyUnicodeTranslateError_GetEnd", + "PyUnicodeTranslateError_GetObject", + "PyUnicodeTranslateError_GetReason", + "PyUnicodeTranslateError_GetStart", + "PyUnicodeTranslateError_SetEnd", + "PyUnicodeTranslateError_SetReason", + "PyUnicodeTranslateError_SetStart", + "PyUnicode_Append", + "PyUnicode_AppendAndDel", + "PyUnicode_AsASCIIString", + "PyUnicode_AsCharmapString", + "PyUnicode_AsDecodedObject", + "PyUnicode_AsDecodedUnicode", + "PyUnicode_AsEncodedObject", + "PyUnicode_AsEncodedString", + "PyUnicode_AsEncodedUnicode", + "PyUnicode_AsLatin1String", + "PyUnicode_AsRawUnicodeEscapeString", + "PyUnicode_AsUCS4", + "PyUnicode_AsUCS4Copy", + "PyUnicode_AsUTF16String", + "PyUnicode_AsUTF32String", + "PyUnicode_AsUTF8AndSize", + "PyUnicode_AsUTF8String", + "PyUnicode_AsUnicodeEscapeString", + "PyUnicode_AsWideChar", + "PyUnicode_AsWideCharString", + "PyUnicode_BuildEncodingMap", + "PyUnicode_Compare", + "PyUnicode_CompareWithASCIIString", + "PyUnicode_Concat", + "PyUnicode_Contains", + "PyUnicode_Count", + "PyUnicode_Decode", + "PyUnicode_DecodeASCII", + "PyUnicode_DecodeCharmap", + "PyUnicode_DecodeFSDefault", + "PyUnicode_DecodeFSDefaultAndSize", + "PyUnicode_DecodeLatin1", + "PyUnicode_DecodeLocale", + "PyUnicode_DecodeLocaleAndSize", + "PyUnicode_DecodeRawUnicodeEscape", + "PyUnicode_DecodeUTF16", + "PyUnicode_DecodeUTF16Stateful", + "PyUnicode_DecodeUTF32", + "PyUnicode_DecodeUTF32Stateful", + "PyUnicode_DecodeUTF7", + "PyUnicode_DecodeUTF7Stateful", + "PyUnicode_DecodeUTF8", + "PyUnicode_DecodeUTF8Stateful", + "PyUnicode_DecodeUnicodeEscape", + "PyUnicode_EncodeFSDefault", + "PyUnicode_EncodeLocale", + "PyUnicode_EqualToUTF8", + "PyUnicode_EqualToUTF8AndSize", + "PyUnicode_FSConverter", + "PyUnicode_FSDecoder", + "PyUnicode_Find", + "PyUnicode_FindChar", + "PyUnicode_Format", + "PyUnicode_FromEncodedObject", + "PyUnicode_FromFormat", + "PyUnicode_FromFormatV", + "PyUnicode_FromObject", + "PyUnicode_FromOrdinal", + "PyUnicode_FromString", + "PyUnicode_FromStringAndSize", + "PyUnicode_FromWideChar", + "PyUnicode_GetDefaultEncoding", + "PyUnicode_GetLength", + "PyUnicode_GetSize", + "PyUnicode_InternFromString", + "PyUnicode_InternImmortal", + "PyUnicode_InternInPlace", + "PyUnicode_IsIdentifier", + "PyUnicode_Join", + "PyUnicode_Partition", + "PyUnicode_RPartition", + "PyUnicode_RSplit", + "PyUnicode_ReadChar", + "PyUnicode_Replace", + "PyUnicode_Resize", + "PyUnicode_RichCompare", + "PyUnicode_Split", + "PyUnicode_Splitlines", + "PyUnicode_Substring", + "PyUnicode_Tailmatch", + "PyUnicode_Translate", + "PyUnicode_Type", + "PyUnicode_WriteChar", + "PyVectorcall_Call", + "PyVectorcall_NARGS", + "PyWeakref_GetObject", + "PyWeakref_GetRef", + "PyWeakref_NewProxy", + "PyWeakref_NewRef", + "PyWrapperDescr_Type", + "PyWrapper_New", + "PyZip_Type", + "Py_AddPendingCall", + "Py_AtExit", + "Py_BuildValue", + "Py_BytesMain", + "Py_CompileString", + "Py_DecRef", + "Py_DecodeLocale", + "Py_EncodeLocale", + "Py_EndInterpreter", + "Py_EnterRecursiveCall", + "Py_Exit", + "Py_FatalError", + "Py_FileSystemDefaultEncodeErrors", + "Py_FileSystemDefaultEncoding", + "Py_Finalize", + "Py_FinalizeEx", + "Py_GenericAlias", + "Py_GenericAliasType", + "Py_GetArgcArgv", + "Py_GetBuildInfo", + "Py_GetCompiler", + "Py_GetConstant", + "Py_GetConstantBorrowed", + "Py_GetCopyright", + "Py_GetExecPrefix", + "Py_GetPath", + "Py_GetPlatform", + "Py_GetPrefix", + "Py_GetProgramFullPath", + "Py_GetProgramName", + "Py_GetPythonHome", + "Py_GetRecursionLimit", + "Py_GetVersion", + "Py_HasFileSystemDefaultEncoding", + "Py_IncRef", + "Py_Initialize", + "Py_InitializeEx", + "Py_Is", + "Py_IsFalse", + "Py_IsFinalizing", + "Py_IsInitialized", + "Py_IsNone", + "Py_IsTrue", + "Py_LeaveRecursiveCall", + "Py_Main", + "Py_MakePendingCalls", + "Py_NewInterpreter", + "Py_NewRef", + "Py_ReprEnter", + "Py_ReprLeave", + "Py_SetPath", + "Py_SetProgramName", + "Py_SetPythonHome", + "Py_SetRecursionLimit", + "Py_UTF8Mode", + "Py_VaBuildValue", + "Py_Version", + "Py_XNewRef", + "_PyArg_ParseTupleAndKeywords_SizeT", + "_PyArg_ParseTuple_SizeT", + "_PyArg_Parse_SizeT", + "_PyArg_VaParseTupleAndKeywords_SizeT", + "_PyArg_VaParse_SizeT", + "_PyErr_BadInternalCall", + "_PyObject_CallFunction_SizeT", + "_PyObject_CallMethod_SizeT", + "_PyObject_GC_New", + "_PyObject_GC_NewVar", + "_PyObject_GC_Resize", + "_PyObject_New", + "_PyObject_NewVar", + "_PyState_AddModule", + "_PyThreadState_Init", + "_PyThreadState_Prealloc", + "_PyWeakref_CallableProxyType", + "_PyWeakref_ProxyType", + "_PyWeakref_RefType", + "_Py_BuildValue_SizeT", + "_Py_CheckRecursiveCall", + "_Py_Dealloc", + "_Py_DecRef", + "_Py_EllipsisObject", + "_Py_FalseStruct", + "_Py_IncRef", + "_Py_NoneStruct", + "_Py_NotImplementedStruct", + "_Py_SetRefcnt", + "_Py_SwappedOp", + "_Py_TrueStruct", + "_Py_VaBuildValue_SizeT", +) +if feature_macros['HAVE_FORK']: + SYMBOL_NAMES += ( + 'PyOS_AfterFork', + 'PyOS_AfterFork_Child', + 'PyOS_AfterFork_Parent', + 'PyOS_BeforeFork', + ) +if feature_macros['MS_WINDOWS']: + SYMBOL_NAMES += ( + 'PyErr_SetExcFromWindowsErr', + 'PyErr_SetExcFromWindowsErrWithFilename', + 'PyErr_SetExcFromWindowsErrWithFilenameObject', + 'PyErr_SetExcFromWindowsErrWithFilenameObjects', + 'PyErr_SetFromWindowsErr', + 'PyErr_SetFromWindowsErrWithFilename', + 'PyExc_WindowsError', + 'PyUnicode_AsMBCSString', + 'PyUnicode_DecodeCodePageStateful', + 'PyUnicode_DecodeMBCS', + 'PyUnicode_DecodeMBCSStateful', + 'PyUnicode_EncodeCodePage', + ) +if feature_macros['PY_HAVE_THREAD_NATIVE_ID']: + SYMBOL_NAMES += ( + 'PyThread_get_thread_native_id', + ) +if feature_macros['Py_REF_DEBUG']: + SYMBOL_NAMES += ( + '_Py_NegativeRefcount', + '_Py_RefTotal', + ) +if feature_macros['Py_TRACE_REFS']: + SYMBOL_NAMES += ( + ) +if feature_macros['USE_STACKCHECK']: + SYMBOL_NAMES += ( + 'PyOS_CheckStack', + ) + +EXPECTED_FEATURE_MACROS = set(['HAVE_FORK', + 'MS_WINDOWS', + 'PY_HAVE_THREAD_NATIVE_ID', + 'Py_REF_DEBUG', + 'Py_TRACE_REFS', + 'USE_STACKCHECK']) +WINDOWS_FEATURE_MACROS = {'HAVE_FORK': False, + 'MS_WINDOWS': True, + 'PY_HAVE_THREAD_NATIVE_ID': True, + 'Py_REF_DEBUG': 'maybe', + 'Py_TRACE_REFS': 'maybe', + 'USE_STACKCHECK': 'maybe'} diff --git a/Lib/test/test_startfile.py b/Lib/test/test_startfile.py new file mode 100644 index 00000000000..8c642998625 --- /dev/null +++ b/Lib/test/test_startfile.py @@ -0,0 +1,45 @@ +# Ridiculously simple test of the os.startfile function for Windows. +# +# empty.vbs is an empty file (except for a comment), which does +# nothing when run with cscript or wscript. +# +# A possible improvement would be to have empty.vbs do something that +# we can detect here, to make sure that not only the os.startfile() +# call succeeded, but also the script actually has run. + +import unittest +from test import support +from test.support import os_helper +import os +import platform +import sys +from os import path + +startfile = support.get_attribute(os, 'startfile') + + +@unittest.skipIf(platform.win32_is_iot(), "starting files is not supported on Windows IoT Core or nanoserver") +class TestCase(unittest.TestCase): + def test_nonexisting(self): + self.assertRaises(OSError, startfile, "nonexisting.vbs") + + def test_empty(self): + # We need to make sure the child process starts in a directory + # we're not about to delete. If we're running under -j, that + # means the test harness provided directory isn't a safe option. + # See http://bugs.python.org/issue15526 for more details + with os_helper.change_cwd(path.dirname(sys.executable)): + empty = path.join(path.dirname(__file__), "empty.vbs") + startfile(empty) + startfile(empty, "open") + startfile(empty, cwd=path.dirname(sys.executable)) + + def test_python(self): + # Passing "-V" ensures that it closes quickly, though still not + # quickly enough that we can run in the test directory + cwd, name = path.split(sys.executable) + startfile(name, arguments="-V", cwd=cwd) + startfile(name, arguments="-V", cwd=cwd, show_cmd=0) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py new file mode 100644 index 00000000000..5e6cd68b8db --- /dev/null +++ b/Lib/test/test_strptime.py @@ -0,0 +1,900 @@ +"""PyUnit testing against strptime""" + +import unittest +import time +import locale +import re +import os +import platform +import sys +from test import support +from test.support import warnings_helper +from test.support import skip_if_buggy_ucrt_strfptime, run_with_locales +from datetime import date as datetime_date + +import _strptime + +libc_ver = platform.libc_ver() +if libc_ver[0] == 'glibc': + glibc_ver = tuple(map(int, libc_ver[1].split('.'))) +else: + glibc_ver = None + + +class getlang_Tests(unittest.TestCase): + """Test _getlang""" + def test_basic(self): + self.assertEqual(_strptime._getlang(), locale.getlocale(locale.LC_TIME)) + +class LocaleTime_Tests(unittest.TestCase): + """Tests for _strptime.LocaleTime. + + All values are lower-cased when stored in LocaleTime, so make sure to + compare values after running ``lower`` on them. + + """ + + def setUp(self): + """Create time tuple based on current time.""" + self.time_tuple = time.localtime() + self.LT_ins = _strptime.LocaleTime() + + def compare_against_time(self, testing, directive, tuple_position, + error_msg): + """Helper method that tests testing against directive based on the + tuple_position of time_tuple. Uses error_msg as error message. + + """ + strftime_output = time.strftime(directive, self.time_tuple).lower() + comparison = testing[self.time_tuple[tuple_position]] + self.assertIn(strftime_output, testing, + "%s: not found in tuple" % error_msg) + self.assertEqual(comparison, strftime_output, + "%s: position within tuple incorrect; %s != %s" % + (error_msg, comparison, strftime_output)) + + def test_weekday(self): + # Make sure that full and abbreviated weekday names are correct in + # both string and position with tuple + self.compare_against_time(self.LT_ins.f_weekday, '%A', 6, + "Testing of full weekday name failed") + self.compare_against_time(self.LT_ins.a_weekday, '%a', 6, + "Testing of abbreviated weekday name failed") + + def test_month(self): + # Test full and abbreviated month names; both string and position + # within the tuple + self.compare_against_time(self.LT_ins.f_month, '%B', 1, + "Testing against full month name failed") + self.compare_against_time(self.LT_ins.a_month, '%b', 1, + "Testing against abbreviated month name failed") + + def test_am_pm(self): + # Make sure AM/PM representation done properly + strftime_output = time.strftime("%p", self.time_tuple).lower() + self.assertIn(strftime_output, self.LT_ins.am_pm, + "AM/PM representation not in tuple") + if self.time_tuple[3] < 12: position = 0 + else: position = 1 + self.assertEqual(self.LT_ins.am_pm[position], strftime_output, + "AM/PM representation in the wrong position within the tuple") + + @unittest.skipIf( + support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" + ) + def test_timezone(self): + # Make sure timezone is correct + timezone = time.strftime("%Z", self.time_tuple).lower() + if timezone: + self.assertTrue(timezone in self.LT_ins.timezone[0] or + timezone in self.LT_ins.timezone[1], + "timezone %s not found in %s" % + (timezone, self.LT_ins.timezone)) + + def test_date_time(self): + # Check that LC_date_time, LC_date, and LC_time are correct + # the magic date is used so as to not have issues with %c when day of + # the month is a single digit and has a leading space. This is not an + # issue since strptime still parses it correctly. The problem is + # testing these directives for correctness by comparing strftime + # output. + magic_date = (1999, 3, 17, 22, 44, 55, 2, 76, 0) + strftime_output = time.strftime("%c", magic_date) + self.assertEqual(time.strftime(self.LT_ins.LC_date_time, magic_date), + strftime_output, "LC_date_time incorrect") + strftime_output = time.strftime("%x", magic_date) + self.assertEqual(time.strftime(self.LT_ins.LC_date, magic_date), + strftime_output, "LC_date incorrect") + strftime_output = time.strftime("%X", magic_date) + self.assertEqual(time.strftime(self.LT_ins.LC_time, magic_date), + strftime_output, "LC_time incorrect") + LT = _strptime.LocaleTime() + LT.am_pm = ('', '') + self.assertTrue(LT.LC_time, "LocaleTime's LC directives cannot handle " + "empty strings") + + def test_lang(self): + # Make sure lang is set to what _getlang() returns + # Assuming locale has not changed between now and when self.LT_ins was created + self.assertEqual(self.LT_ins.lang, _strptime._getlang()) + + +class TimeRETests(unittest.TestCase): + """Tests for TimeRE.""" + + def setUp(self): + """Construct generic TimeRE object.""" + self.time_re = _strptime.TimeRE() + self.locale_time = _strptime.LocaleTime() + + def test_pattern(self): + # Test TimeRE.pattern + pattern_string = self.time_re.pattern(r"%a %A %d %Y") + self.assertTrue(pattern_string.find(self.locale_time.a_weekday[2]) != -1, + "did not find abbreviated weekday in pattern string '%s'" % + pattern_string) + self.assertTrue(pattern_string.find(self.locale_time.f_weekday[4]) != -1, + "did not find full weekday in pattern string '%s'" % + pattern_string) + self.assertTrue(pattern_string.find(self.time_re['d']) != -1, + "did not find 'd' directive pattern string '%s'" % + pattern_string) + + def test_pattern_escaping(self): + # Make sure any characters in the format string that might be taken as + # regex syntax is escaped. + pattern_string = self.time_re.pattern(r"\d+") + self.assertIn(r"\\d\+", pattern_string, + "%s does not have re characters escaped properly" % + pattern_string) + + @skip_if_buggy_ucrt_strfptime + def test_compile(self): + # Check that compiled regex is correct + found = self.time_re.compile(r"%A").match(self.locale_time.f_weekday[6]) + self.assertTrue(found and found.group('A') == self.locale_time.f_weekday[6], + "re object for '%A' failed") + compiled = self.time_re.compile(r"%a %b") + found = compiled.match("%s %s" % (self.locale_time.a_weekday[4], + self.locale_time.a_month[4])) + self.assertTrue(found, + "Match failed with '%s' regex and '%s' string" % + (compiled.pattern, "%s %s" % (self.locale_time.a_weekday[4], + self.locale_time.a_month[4]))) + self.assertTrue(found.group('a') == self.locale_time.a_weekday[4] and + found.group('b') == self.locale_time.a_month[4], + "re object couldn't find the abbreviated weekday month in " + "'%s' using '%s'; group 'a' = '%s', group 'b' = %s'" % + (found.string, found.re.pattern, found.group('a'), + found.group('b'))) + for directive in ('a','A','b','B','c','d','G','H','I','j','m','M','p', + 'S','u','U','V','w','W','x','X','y','Y','Z','%'): + fmt = "%d %Y" if directive == 'd' else "%" + directive + compiled = self.time_re.compile(fmt) + found = compiled.match(time.strftime(fmt)) + self.assertTrue(found, "Matching failed on '%s' using '%s' regex" % + (time.strftime(fmt), + compiled.pattern)) + + def test_blankpattern(self): + # Make sure when tuple or something has no values no regex is generated. + # Fixes bug #661354 + test_locale = _strptime.LocaleTime() + test_locale.timezone = (frozenset(), frozenset()) + self.assertEqual(_strptime.TimeRE(test_locale).pattern("%Z"), '', + "with timezone == ('',''), TimeRE().pattern('%Z') != ''") + + def test_matching_with_escapes(self): + # Make sure a format that requires escaping of characters works + compiled_re = self.time_re.compile(r"\w+ %m") + found = compiled_re.match(r"\w+ 10") + self.assertTrue(found, r"Escaping failed of format '\w+ 10'") + + def test_locale_data_w_regex_metacharacters(self): + # Check that if locale data contains regex metacharacters they are + # escaped properly. + # Discovered by bug #1039270 . + locale_time = _strptime.LocaleTime() + locale_time.timezone = (frozenset(("utc", "gmt", + "Tokyo (standard time)")), + frozenset("Tokyo (daylight time)")) + time_re = _strptime.TimeRE(locale_time) + self.assertTrue(time_re.compile("%Z").match("Tokyo (standard time)"), + "locale data that contains regex metacharacters is not" + " properly escaped") + + def test_whitespace_substitution(self): + # When pattern contains whitespace, make sure it is taken into account + # so as to not allow subpatterns to end up next to each other and + # "steal" characters from each other. + pattern = self.time_re.pattern('%j %H') + self.assertFalse(re.match(pattern, "180")) + self.assertTrue(re.match(pattern, "18 0")) + + +class StrptimeTests(unittest.TestCase): + """Tests for _strptime.strptime.""" + + def setUp(self): + """Create testing time tuples.""" + self.time_tuple = time.localtime() + + def test_ValueError(self): + # Make sure ValueError is raised when match fails or format is bad + self.assertRaises(ValueError, _strptime._strptime_time, data_string="%d", + format="%A") + for bad_format in ("%", "% ", "%\n"): + with (self.subTest(format=bad_format), + self.assertRaisesRegex(ValueError, "stray % in format ")): + _strptime._strptime_time("2005", bad_format) + for bad_format in ("%i", "%Oi", "%O", "%O ", "%Ee", "%E", "%E ", + "%.", "%+", "%~", "%\\", + "%O.", "%O+", "%O_", "%O~", "%O\\"): + directive = bad_format[1:].rstrip() + with (self.subTest(format=bad_format), + self.assertRaisesRegex(ValueError, + f"'{re.escape(directive)}' is a bad directive in format ")): + _strptime._strptime_time("2005", bad_format) + + msg_week_no_year_or_weekday = r"ISO week directive '%V' must be used with " \ + r"the ISO year directive '%G' and a weekday directive " \ + r"\('%A', '%a', '%w', or '%u'\)." + msg_week_not_compatible = r"ISO week directive '%V' is incompatible with " \ + r"the year directive '%Y'. Use the ISO year '%G' instead." + msg_julian_not_compatible = r"Day of the year directive '%j' is not " \ + r"compatible with ISO year directive '%G'. Use '%Y' instead." + msg_year_no_week_or_weekday = r"ISO year directive '%G' must be used with " \ + r"the ISO week directive '%V' and a weekday directive " \ + r"\('%A', '%a', '%w', or '%u'\)." + + locale_time = _strptime.LocaleTime() + + # Ambiguous or incomplete cases using ISO year/week/weekday directives + subtests = [ + # 1. ISO week (%V) is specified, but the year is specified with %Y + # instead of %G + ("1999 50", "%Y %V", msg_week_no_year_or_weekday), + ("1999 50 5", "%Y %V %u", msg_week_not_compatible), + # 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not + ("1999 51", "%G %V", msg_year_no_week_or_weekday), + # 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not + ("1999 {}".format(locale_time.f_weekday[5]), "%G %A", + msg_year_no_week_or_weekday), + ("1999 {}".format(locale_time.a_weekday[5]), "%G %a", + msg_year_no_week_or_weekday), + ("1999 5", "%G %w", msg_year_no_week_or_weekday), + ("1999 5", "%G %u", msg_year_no_week_or_weekday), + # 4. ISO year is specified alone (e.g. time.strptime('2015', '%G')) + ("2015", "%G", msg_year_no_week_or_weekday), + # 5. Julian/ordinal day (%j) is specified with %G, but not %Y + ("1999 256", "%G %j", msg_julian_not_compatible), + ("1999 50 5 256", "%G %V %u %j", msg_julian_not_compatible), + # ISO week specified alone + ("50", "%V", msg_week_no_year_or_weekday), + # ISO year is unspecified, falling back to year + ("50 5", "%V %u", msg_week_no_year_or_weekday), + # 6. Invalid ISO weeks + ("2019-00-1", "%G-%V-%u", + "time data '2019-00-1' does not match format '%G-%V-%u'"), + ("2019-54-1", "%G-%V-%u", + "time data '2019-54-1' does not match format '%G-%V-%u'"), + ("2021-53-1", "%G-%V-%u", "Invalid week: 53"), + ] + + for (data_string, format, message) in subtests: + with self.subTest(data_string=data_string, format=format): + with self.assertRaisesRegex(ValueError, message): + _strptime._strptime(data_string, format) + + def test_strptime_exception_context(self): + # check that this doesn't chain exceptions needlessly (see #17572) + with self.assertRaises(ValueError) as e: + _strptime._strptime_time('', '%D') + self.assertTrue(e.exception.__suppress_context__) + # additional check for stray % branch + with self.assertRaises(ValueError) as e: + _strptime._strptime_time('%', '%') + self.assertTrue(e.exception.__suppress_context__) + + def test_unconverteddata(self): + # Check ValueError is raised when there is unconverted data + self.assertRaises(ValueError, _strptime._strptime_time, "10 12", "%m") + + def roundtrip(self, fmt, position, time_tuple=None): + """Helper fxn in testing.""" + if time_tuple is None: + time_tuple = self.time_tuple + strf_output = time.strftime(fmt, time_tuple) + strp_output = _strptime._strptime_time(strf_output, fmt) + self.assertEqual(strp_output[position], time_tuple[position], + "testing of %r format failed; %r -> %r != %r" % + (fmt, strf_output, strp_output[position], + time_tuple[position])) + if support.verbose >= 3: + print("testing of %r format: %r -> %r" % + (fmt, strf_output, strp_output[position])) + + def test_year(self): + # Test that the year is handled properly + self.roundtrip('%Y', 0) + self.roundtrip('%y', 0) + self.roundtrip('%Y', 0, (1900, 1, 1, 0, 0, 0, 0, 1, 0)) + + # Must also make sure %y values are correct for bounds set by Open Group + strptime = _strptime._strptime_time + self.assertEqual(strptime('00', '%y')[0], 2000) + self.assertEqual(strptime('68', '%y')[0], 2068) + self.assertEqual(strptime('69', '%y')[0], 1969) + self.assertEqual(strptime('99', '%y')[0], 1999) + + def test_month(self): + # Test for month directives + self.roundtrip('%m', 1) + + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', 'he_IL', '') + def test_month_locale(self): + # Test for month directives + self.roundtrip('%B', 1) + self.roundtrip('%b', 1) + for m in range(1, 13): + self.roundtrip('%B', 1, (1900, m, 1, 0, 0, 0, 0, 1, 0)) + self.roundtrip('%b', 1, (1900, m, 1, 0, 0, 0, 0, 1, 0)) + + @run_with_locales('LC_TIME', 'az_AZ', 'ber_DZ', 'ber_MA', 'crh_UA') + def test_month_locale2(self): + # Test for month directives + # Month name contains 'İ' ('\u0130') + self.roundtrip('%B', 1, (2025, 6, 1, 0, 0, 0, 6, 152, 0)) + self.roundtrip('%b', 1, (2025, 6, 1, 0, 0, 0, 6, 152, 0)) + self.roundtrip('%B', 1, (2025, 7, 1, 0, 0, 0, 1, 182, 0)) + self.roundtrip('%b', 1, (2025, 7, 1, 0, 0, 0, 1, 182, 0)) + + def test_day(self): + # Test for day directives + self.roundtrip('%d %Y', 2) + + def test_hour(self): + # Test hour directives + self.roundtrip('%H', 3) + + # NB: Only works on locales with AM/PM + @run_with_locales('LC_TIME', 'C', 'en_US', 'ja_JP') + def test_hour_locale(self): + # Test hour directives + self.roundtrip('%I %p', 3) + + def test_minute(self): + # Test minute directives + self.roundtrip('%M', 4) + + def test_second(self): + # Test second directives + self.roundtrip('%S', 5) + + def test_fraction(self): + # Test microseconds + import datetime + d = datetime.datetime(2012, 12, 20, 12, 34, 56, 78987) + tup, frac, _ = _strptime._strptime(str(d), format="%Y-%m-%d %H:%M:%S.%f") + self.assertEqual(frac, d.microsecond) + + def test_weekday(self): + # Test weekday directives + self.roundtrip('%w', 6) + self.roundtrip('%u', 6) + + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', '') + def test_weekday_locale(self): + # Test weekday directives + self.roundtrip('%A', 6) + self.roundtrip('%a', 6) + + def test_julian(self): + # Test julian directives + self.roundtrip('%j', 7) + + def test_offset(self): + one_hour = 60 * 60 + half_hour = 30 * 60 + half_minute = 30 + (*_, offset), _, offset_fraction = _strptime._strptime("+0130", "%z") + self.assertEqual(offset, one_hour + half_hour) + self.assertEqual(offset_fraction, 0) + (*_, offset), _, offset_fraction = _strptime._strptime("-0100", "%z") + self.assertEqual(offset, -one_hour) + self.assertEqual(offset_fraction, 0) + (*_, offset), _, offset_fraction = _strptime._strptime("-013030", "%z") + self.assertEqual(offset, -(one_hour + half_hour + half_minute)) + self.assertEqual(offset_fraction, 0) + (*_, offset), _, offset_fraction = _strptime._strptime("-013030.000001", "%z") + self.assertEqual(offset, -(one_hour + half_hour + half_minute)) + self.assertEqual(offset_fraction, -1) + (*_, offset), _, offset_fraction = _strptime._strptime("+01:00", "%z") + self.assertEqual(offset, one_hour) + self.assertEqual(offset_fraction, 0) + (*_, offset), _, offset_fraction = _strptime._strptime("-01:30", "%z") + self.assertEqual(offset, -(one_hour + half_hour)) + self.assertEqual(offset_fraction, 0) + (*_, offset), _, offset_fraction = _strptime._strptime("-01:30:30", "%z") + self.assertEqual(offset, -(one_hour + half_hour + half_minute)) + self.assertEqual(offset_fraction, 0) + (*_, offset), _, offset_fraction = _strptime._strptime("-01:30:30.000001", "%z") + self.assertEqual(offset, -(one_hour + half_hour + half_minute)) + self.assertEqual(offset_fraction, -1) + (*_, offset), _, offset_fraction = _strptime._strptime("+01:30:30.001", "%z") + self.assertEqual(offset, one_hour + half_hour + half_minute) + self.assertEqual(offset_fraction, 1000) + (*_, offset), _, offset_fraction = _strptime._strptime("Z", "%z") + self.assertEqual(offset, 0) + self.assertEqual(offset_fraction, 0) + + def test_bad_offset(self): + with self.assertRaises(ValueError): + _strptime._strptime("-01:30:30.", "%z") + with self.assertRaises(ValueError): + _strptime._strptime("-0130:30", "%z") + with self.assertRaises(ValueError): + _strptime._strptime("-01:30:30.1234567", "%z") + with self.assertRaises(ValueError): + _strptime._strptime("-01:30:30:123456", "%z") + with self.assertRaises(ValueError) as err: + _strptime._strptime("-01:3030", "%z") + self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) + + @skip_if_buggy_ucrt_strfptime + @unittest.skipIf( + support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" + ) + def test_timezone(self): + # Test timezone directives. + # When gmtime() is used with %Z, entire result of strftime() is empty. + # Check for equal timezone names deals with bad locale info when this + # occurs; first found in FreeBSD 4.4. + strp_output = _strptime._strptime_time("UTC", "%Z") + self.assertEqual(strp_output.tm_isdst, 0) + strp_output = _strptime._strptime_time("GMT", "%Z") + self.assertEqual(strp_output.tm_isdst, 0) + time_tuple = time.localtime() + strf_output = time.strftime("%Z") #UTC does not have a timezone + strp_output = _strptime._strptime_time(strf_output, "%Z") + locale_time = _strptime.LocaleTime() + if time.tzname[0] != time.tzname[1] or not time.daylight: + self.assertTrue(strp_output[8] == time_tuple[8], + "timezone check failed; '%s' -> %s != %s" % + (strf_output, strp_output[8], time_tuple[8])) + else: + self.assertTrue(strp_output[8] == -1, + "LocaleTime().timezone has duplicate values and " + "time.daylight but timezone value not set to -1") + + @unittest.skipUnless( + hasattr(time, "tzset"), "time module has no attribute tzset" + ) + def test_bad_timezone(self): + # Explicitly test possibility of bad timezone; + # when time.tzname[0] == time.tzname[1] and time.daylight + tz_name = time.tzname[0] + if tz_name.upper() in ("UTC", "GMT"): + self.skipTest('need non-UTC/GMT timezone') + + with support.swap_attr(time, 'tzname', (tz_name, tz_name)), \ + support.swap_attr(time, 'daylight', 1), \ + support.swap_attr(time, 'tzset', lambda: None): + time.tzname = (tz_name, tz_name) + time.daylight = 1 + tz_value = _strptime._strptime_time(tz_name, "%Z")[8] + self.assertEqual(tz_value, -1, + "%s lead to a timezone value of %s instead of -1 when " + "time.daylight set to %s and passing in %s" % + (time.tzname, tz_value, time.daylight, tz_name)) + + # NB: Does not roundtrip in some locales due to the ambiguity of + # the date and time representation (bugs in locales?): + # * Seconds are not included: bem_ZM, bokmal, ff_SN, nb_NO, nn_NO, + # no_NO, norwegian, nynorsk. + # * Hours are in 12-hour notation without AM/PM indication: hy_AM, + # id_ID, ms_MY. + # * Year is not included: ha_NG. + # * Use non-Gregorian calendar: lo_LA, thai, th_TH. + # On Windows: ar_IN, ar_SA, fa_IR, ps_AF. + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', + 'he_IL', 'eu_ES', 'ar_AE', 'mfe_MU', 'yo_NG', + 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN', + 'my_MM', 'or_IN', 'shn_MM', 'az_IR', + 'byn_ER', 'wal_ET', 'lzh_TW') + def test_date_time_locale(self): + # Test %c directive + loc = locale.getlocale(locale.LC_TIME)[0] + if glibc_ver and glibc_ver < (2, 31) and loc == 'br_FR': + self.skipTest('%c in locale br_FR does not include time') + now = time.time() + self.roundtrip('%c', slice(0, 6), time.localtime(now)) + # 1 hour 20 minutes 30 seconds ago + self.roundtrip('%c', slice(0, 6), time.localtime(now - 4830)) + # 12 hours ago + self.roundtrip('%c', slice(0, 6), time.localtime(now - 12*3600)) + # different days of the week + for i in range(1, 7): + self.roundtrip('%c', slice(0, 6), time.localtime(now - i*24*3600)) + # different months + for i in range(1, 12): + self.roundtrip('%c', slice(0, 6), time.localtime(now - i*30*24*3600)) + # different year + self.roundtrip('%c', slice(0, 6), time.localtime(now - 366*24*3600)) + + # NB: Dates before 1969 do not roundtrip on some locales: + # az_IR, bo_CN, bo_IN, dz_BT, eu_ES, eu_FR, fa_IR, or_IN. + @support.run_with_tz('STD-1DST,M4.1.0,M10.1.0') + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', + 'he_IL', 'ar_AE', 'mfe_MU', 'yo_NG', + 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN', + 'my_MM', 'shn_MM') + def test_date_time_locale2(self): + # Test %c directive + loc = locale.getlocale(locale.LC_TIME)[0] + if sys.platform.startswith('sunos'): + if loc in ('ar_AE',): + self.skipTest(f'locale {loc!r} may not work on this platform') + self.roundtrip('%c', slice(0, 6), (1900, 1, 1, 0, 0, 0, 0, 1, 0)) + self.roundtrip('%c', slice(0, 6), (1800, 1, 1, 0, 0, 0, 0, 1, 0)) + + # NB: Does not roundtrip because use non-Gregorian calendar: + # lo_LA, thai, th_TH. On Windows: ar_IN, ar_SA, fa_IR, ps_AF. + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', + 'he_IL', 'eu_ES', 'ar_AE', + 'az_IR', 'my_MM', 'or_IN', 'shn_MM', 'lzh_TW') + def test_date_locale(self): + # Test %x directive + now = time.time() + self.roundtrip('%x', slice(0, 3), time.localtime(now)) + # different days of the week + for i in range(1, 7): + self.roundtrip('%x', slice(0, 3), time.localtime(now - i*24*3600)) + # different months + for i in range(1, 12): + self.roundtrip('%x', slice(0, 3), time.localtime(now - i*30*24*3600)) + # different year + self.roundtrip('%x', slice(0, 3), time.localtime(now - 366*24*3600)) + + # NB: Dates before 1969 do not roundtrip on many locales, including C. + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "musl libc issue on Emscripten, bpo-46390" + ) + @run_with_locales('LC_TIME', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', + 'eu_ES', 'ar_AE', 'my_MM', 'shn_MM', 'lzh_TW') + def test_date_locale2(self): + # Test %x directive + loc = locale.getlocale(locale.LC_TIME)[0] + if sys.platform.startswith(('sunos', 'aix')): + if loc in ('en_US', 'de_DE', 'ar_AE'): + self.skipTest(f'locale {loc!r} may not work on this platform') + self.roundtrip('%x', slice(0, 3), (1900, 1, 1, 0, 0, 0, 0, 1, 0)) + self.roundtrip('%x', slice(0, 3), (1800, 1, 1, 0, 0, 0, 0, 1, 0)) + + # NB: Does not roundtrip in some locales due to the ambiguity of + # the time representation (bugs in locales?): + # * Seconds are not included: bokmal, ff_SN, nb_NO, nn_NO, no_NO, + # norwegian, nynorsk. + # * Hours are in 12-hour notation without AM/PM indication: hy_AM, + # ms_MY, sm_WS. + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', + 'aa_ET', 'am_ET', 'az_IR', 'byn_ER', 'fa_IR', 'gez_ET', + 'my_MM', 'om_ET', 'or_IN', 'shn_MM', 'sid_ET', 'so_SO', + 'ti_ET', 'tig_ER', 'wal_ET', 'lzh_TW', + 'ar_SA', 'bg_BG') + def test_time_locale(self): + # Test %X directive + loc = locale.getlocale(locale.LC_TIME)[0] + pos = slice(3, 6) + if glibc_ver and glibc_ver < (2, 29) and loc in { + 'aa_ET', 'am_ET', 'byn_ER', 'gez_ET', 'om_ET', + 'sid_ET', 'so_SO', 'ti_ET', 'tig_ER', 'wal_ET'}: + # Hours are in 12-hour notation without AM/PM indication. + # Ignore hours. + pos = slice(4, 6) + now = time.time() + self.roundtrip('%X', pos, time.localtime(now)) + # 1 hour 20 minutes 30 seconds ago + self.roundtrip('%X', pos, time.localtime(now - 4830)) + # 12 hours ago + self.roundtrip('%X', pos, time.localtime(now - 12*3600)) + + def test_percent(self): + # Make sure % signs are handled properly + strf_output = time.strftime("%m %% %Y", self.time_tuple) + strp_output = _strptime._strptime_time(strf_output, "%m %% %Y") + self.assertTrue(strp_output[0] == self.time_tuple[0] and + strp_output[1] == self.time_tuple[1], + "handling of percent sign failed") + + def test_caseinsensitive(self): + # Should handle names case-insensitively. + strf_output = time.strftime("%B", self.time_tuple) + self.assertTrue(_strptime._strptime_time(strf_output.upper(), "%B"), + "strptime does not handle ALL-CAPS names properly") + self.assertTrue(_strptime._strptime_time(strf_output.lower(), "%B"), + "strptime does not handle lowercase names properly") + self.assertTrue(_strptime._strptime_time(strf_output.capitalize(), "%B"), + "strptime does not handle capword names properly") + + def test_defaults(self): + # Default return value should be (1900, 1, 1, 0, 0, 0, 0, 1, 0) + defaults = (1900, 1, 1, 0, 0, 0, 0, 1, -1) + strp_output = _strptime._strptime_time('1', '%m') + self.assertTrue(strp_output == defaults, + "Default values for strptime() are incorrect;" + " %s != %s" % (strp_output, defaults)) + + def test_escaping(self): + # Make sure all characters that have regex significance are escaped. + # Parentheses are in a purposeful order; will cause an error of + # unbalanced parentheses when the regex is compiled if they are not + # escaped. + # Test instigated by bug #796149 . + need_escaping = r".^$*+?{}\[]|)(" + self.assertTrue(_strptime._strptime_time(need_escaping, need_escaping)) + + @warnings_helper.ignore_warnings(category=DeprecationWarning) # gh-70647 + def test_feb29_on_leap_year_without_year(self): + time.strptime("Feb 29", "%b %d") + + @warnings_helper.ignore_warnings(category=DeprecationWarning) # gh-70647 + def test_mar1_comes_after_feb29_even_when_omitting_the_year(self): + self.assertLess( + time.strptime("Feb 29", "%b %d"), + time.strptime("Mar 1", "%b %d")) + +class Strptime12AMPMTests(unittest.TestCase): + """Test a _strptime regression in '%I %p' at 12 noon (12 PM)""" + + def test_twelve_noon_midnight(self): + eq = self.assertEqual + eq(time.strptime('12 PM', '%I %p')[3], 12) + eq(time.strptime('12 AM', '%I %p')[3], 0) + eq(_strptime._strptime_time('12 PM', '%I %p')[3], 12) + eq(_strptime._strptime_time('12 AM', '%I %p')[3], 0) + + +class JulianTests(unittest.TestCase): + """Test a _strptime regression that all julian (1-366) are accepted""" + + def test_all_julian_days(self): + eq = self.assertEqual + for i in range(1, 367): + # use 2004, since it is a leap year, we have 366 days + eq(_strptime._strptime_time('%d 2004' % i, '%j %Y')[7], i) + +class CalculationTests(unittest.TestCase): + """Test that strptime() fills in missing info correctly""" + + def setUp(self): + self.time_tuple = time.gmtime() + + @skip_if_buggy_ucrt_strfptime + def test_julian_calculation(self): + # Make sure that when Julian is missing that it is calculated + format_string = "%Y %m %d %H %M %S %w %Z" + result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple), + format_string) + self.assertTrue(result.tm_yday == self.time_tuple.tm_yday, + "Calculation of tm_yday failed; %s != %s" % + (result.tm_yday, self.time_tuple.tm_yday)) + + @skip_if_buggy_ucrt_strfptime + def test_gregorian_calculation(self): + # Test that Gregorian date can be calculated from Julian day + format_string = "%Y %H %M %S %w %j %Z" + result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple), + format_string) + self.assertTrue(result.tm_year == self.time_tuple.tm_year and + result.tm_mon == self.time_tuple.tm_mon and + result.tm_mday == self.time_tuple.tm_mday, + "Calculation of Gregorian date failed; " + "%s-%s-%s != %s-%s-%s" % + (result.tm_year, result.tm_mon, result.tm_mday, + self.time_tuple.tm_year, self.time_tuple.tm_mon, + self.time_tuple.tm_mday)) + + @skip_if_buggy_ucrt_strfptime + def test_day_of_week_calculation(self): + # Test that the day of the week is calculated as needed + format_string = "%Y %m %d %H %S %j %Z" + result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple), + format_string) + self.assertTrue(result.tm_wday == self.time_tuple.tm_wday, + "Calculation of day of the week failed; " + "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday)) + + if support.is_android: + # Issue #26929: strftime() on Android incorrectly formats %V or %G for + # the last or the first incomplete week in a year. + _ymd_excluded = ((1905, 1, 1), (1906, 12, 31), (2008, 12, 29), + (1917, 12, 31)) + _formats_excluded = ('%G %V',) + else: + _ymd_excluded = () + _formats_excluded = () + + @unittest.skipIf(sys.platform.startswith('aix'), + 'bpo-29972: broken test on AIX') + def test_week_of_year_and_day_of_week_calculation(self): + # Should be able to infer date if given year, week of year (%U or %W) + # and day of the week + def test_helper(ymd_tuple, test_reason): + for year_week_format in ('%Y %W', '%Y %U', '%G %V'): + if (year_week_format in self._formats_excluded and + ymd_tuple in self._ymd_excluded): + return + for weekday_format in ('%w', '%u', '%a', '%A'): + format_string = year_week_format + ' ' + weekday_format + with self.subTest(test_reason, + date=ymd_tuple, + format=format_string): + dt_date = datetime_date(*ymd_tuple) + strp_input = dt_date.strftime(format_string) + strp_output = _strptime._strptime_time(strp_input, + format_string) + msg = "%r: %s != %s" % (strp_input, + strp_output[7], + dt_date.timetuple()[7]) + self.assertEqual(strp_output[:3], ymd_tuple, msg) + test_helper((1901, 1, 3), "week 0") + test_helper((1901, 1, 8), "common case") + test_helper((1901, 1, 13), "day on Sunday") + test_helper((1901, 1, 14), "day on Monday") + test_helper((1905, 1, 1), "Jan 1 on Sunday") + test_helper((1906, 1, 1), "Jan 1 on Monday") + test_helper((1906, 1, 7), "first Sunday in a year starting on Monday") + test_helper((1905, 12, 31), "Dec 31 on Sunday") + test_helper((1906, 12, 31), "Dec 31 on Monday") + test_helper((2008, 12, 29), "Monday in the last week of the year") + test_helper((2008, 12, 22), "Monday in the second-to-last week of the " + "year") + test_helper((1978, 10, 23), "randomly chosen date") + test_helper((2004, 12, 18), "randomly chosen date") + test_helper((1978, 10, 23), "year starting and ending on Monday while " + "date not on Sunday or Monday") + test_helper((1917, 12, 17), "year starting and ending on Monday with " + "a Monday not at the beginning or end " + "of the year") + test_helper((1917, 12, 31), "Dec 31 on Monday with year starting and " + "ending on Monday") + test_helper((2007, 1, 7), "First Sunday of 2007") + test_helper((2007, 1, 14), "Second Sunday of 2007") + test_helper((2006, 12, 31), "Last Sunday of 2006") + test_helper((2006, 12, 24), "Second to last Sunday of 2006") + + def test_week_0(self): + def check(value, format, *expected): + self.assertEqual(_strptime._strptime_time(value, format)[:-1], expected) + check('2015 0 0', '%Y %U %w', 2014, 12, 28, 0, 0, 0, 6, 362) + check('2015 0 0', '%Y %W %w', 2015, 1, 4, 0, 0, 0, 6, 4) + check('2015 1 1', '%G %V %u', 2014, 12, 29, 0, 0, 0, 0, 363) + check('2015 0 1', '%Y %U %w', 2014, 12, 29, 0, 0, 0, 0, 363) + check('2015 0 1', '%Y %W %w', 2014, 12, 29, 0, 0, 0, 0, 363) + check('2015 1 2', '%G %V %u', 2014, 12, 30, 0, 0, 0, 1, 364) + check('2015 0 2', '%Y %U %w', 2014, 12, 30, 0, 0, 0, 1, 364) + check('2015 0 2', '%Y %W %w', 2014, 12, 30, 0, 0, 0, 1, 364) + check('2015 1 3', '%G %V %u', 2014, 12, 31, 0, 0, 0, 2, 365) + check('2015 0 3', '%Y %U %w', 2014, 12, 31, 0, 0, 0, 2, 365) + check('2015 0 3', '%Y %W %w', 2014, 12, 31, 0, 0, 0, 2, 365) + check('2015 1 4', '%G %V %u', 2015, 1, 1, 0, 0, 0, 3, 1) + check('2015 0 4', '%Y %U %w', 2015, 1, 1, 0, 0, 0, 3, 1) + check('2015 0 4', '%Y %W %w', 2015, 1, 1, 0, 0, 0, 3, 1) + check('2015 1 5', '%G %V %u', 2015, 1, 2, 0, 0, 0, 4, 2) + check('2015 0 5', '%Y %U %w', 2015, 1, 2, 0, 0, 0, 4, 2) + check('2015 0 5', '%Y %W %w', 2015, 1, 2, 0, 0, 0, 4, 2) + check('2015 1 6', '%G %V %u', 2015, 1, 3, 0, 0, 0, 5, 3) + check('2015 0 6', '%Y %U %w', 2015, 1, 3, 0, 0, 0, 5, 3) + check('2015 0 6', '%Y %W %w', 2015, 1, 3, 0, 0, 0, 5, 3) + check('2015 1 7', '%G %V %u', 2015, 1, 4, 0, 0, 0, 6, 4) + + check('2009 0 0', '%Y %U %w', 2008, 12, 28, 0, 0, 0, 6, 363) + check('2009 0 0', '%Y %W %w', 2009, 1, 4, 0, 0, 0, 6, 4) + check('2009 1 1', '%G %V %u', 2008, 12, 29, 0, 0, 0, 0, 364) + check('2009 0 1', '%Y %U %w', 2008, 12, 29, 0, 0, 0, 0, 364) + check('2009 0 1', '%Y %W %w', 2008, 12, 29, 0, 0, 0, 0, 364) + check('2009 1 2', '%G %V %u', 2008, 12, 30, 0, 0, 0, 1, 365) + check('2009 0 2', '%Y %U %w', 2008, 12, 30, 0, 0, 0, 1, 365) + check('2009 0 2', '%Y %W %w', 2008, 12, 30, 0, 0, 0, 1, 365) + check('2009 1 3', '%G %V %u', 2008, 12, 31, 0, 0, 0, 2, 366) + check('2009 0 3', '%Y %U %w', 2008, 12, 31, 0, 0, 0, 2, 366) + check('2009 0 3', '%Y %W %w', 2008, 12, 31, 0, 0, 0, 2, 366) + check('2009 1 4', '%G %V %u', 2009, 1, 1, 0, 0, 0, 3, 1) + check('2009 0 4', '%Y %U %w', 2009, 1, 1, 0, 0, 0, 3, 1) + check('2009 0 4', '%Y %W %w', 2009, 1, 1, 0, 0, 0, 3, 1) + check('2009 1 5', '%G %V %u', 2009, 1, 2, 0, 0, 0, 4, 2) + check('2009 0 5', '%Y %U %w', 2009, 1, 2, 0, 0, 0, 4, 2) + check('2009 0 5', '%Y %W %w', 2009, 1, 2, 0, 0, 0, 4, 2) + check('2009 1 6', '%G %V %u', 2009, 1, 3, 0, 0, 0, 5, 3) + check('2009 0 6', '%Y %U %w', 2009, 1, 3, 0, 0, 0, 5, 3) + check('2009 0 6', '%Y %W %w', 2009, 1, 3, 0, 0, 0, 5, 3) + check('2009 1 7', '%G %V %u', 2009, 1, 4, 0, 0, 0, 6, 4) + + +class CacheTests(unittest.TestCase): + """Test that caching works properly.""" + + def test_time_re_recreation(self): + # Make sure cache is recreated when current locale does not match what + # cached object was created with. + _strptime._strptime_time("10 2004", "%d %Y") + _strptime._strptime_time("2005", "%Y") + _strptime._TimeRE_cache.locale_time.lang = "Ni" + original_time_re = _strptime._TimeRE_cache + _strptime._strptime_time("10 2004", "%d %Y") + self.assertIsNot(original_time_re, _strptime._TimeRE_cache) + self.assertEqual(len(_strptime._regex_cache), 1) + + def test_regex_cleanup(self): + # Make sure cached regexes are discarded when cache becomes "full". + try: + del _strptime._regex_cache['%d %Y'] + except KeyError: + pass + bogus_key = 0 + while len(_strptime._regex_cache) <= _strptime._CACHE_MAX_SIZE: + _strptime._regex_cache[bogus_key] = None + bogus_key += 1 + _strptime._strptime_time("10 2004", "%d %Y") + self.assertEqual(len(_strptime._regex_cache), 1) + + def test_new_localetime(self): + # A new LocaleTime instance should be created when a new TimeRE object + # is created. + locale_time_id = _strptime._TimeRE_cache.locale_time + _strptime._TimeRE_cache.locale_time.lang = "Ni" + _strptime._strptime_time("10 2004", "%d %Y") + self.assertIsNot(locale_time_id, _strptime._TimeRE_cache.locale_time) + + def test_TimeRE_recreation_locale(self): + # The TimeRE instance should be recreated upon changing the locale. + with support.run_with_locale('LC_TIME', 'en_US.UTF8'): + _strptime._strptime_time('10 2004', '%d %Y') + # Get id of current cache object. + first_time_re = _strptime._TimeRE_cache + try: + # Change the locale and force a recreation of the cache. + locale.setlocale(locale.LC_TIME, ('de_DE', 'UTF8')) + _strptime._strptime_time('10 2004', '%d %Y') + # Get the new cache object's id. + second_time_re = _strptime._TimeRE_cache + # They should not be equal. + self.assertIsNot(first_time_re, second_time_re) + # Possible test locale is not supported while initial locale is. + # If this is the case just suppress the exception and fall-through + # to the resetting to the original locale. + except locale.Error: + self.skipTest('test needs de_DE.UTF8 locale') + + @support.run_with_tz('STD-1DST,M4.1.0,M10.1.0') + def test_TimeRE_recreation_timezone(self): + # The TimeRE instance should be recreated upon changing the timezone. + oldtzname = time.tzname + tm = _strptime._strptime_time(time.tzname[0], '%Z') + self.assertEqual(tm.tm_isdst, 0) + tm = _strptime._strptime_time(time.tzname[1], '%Z') + self.assertEqual(tm.tm_isdst, 1) + # Get id of current cache object. + first_time_re = _strptime._TimeRE_cache + # Change the timezone and force a recreation of the cache. + os.environ['TZ'] = 'EST+05EDT,M3.2.0,M11.1.0' + time.tzset() + tm = _strptime._strptime_time(time.tzname[0], '%Z') + self.assertEqual(tm.tm_isdst, 0) + tm = _strptime._strptime_time(time.tzname[1], '%Z') + self.assertEqual(tm.tm_isdst, 1) + # Get the new cache object's id. + second_time_re = _strptime._TimeRE_cache + # They should not be equal. + self.assertIsNot(first_time_re, second_time_re) + # Make sure old names no longer accepted. + with self.assertRaises(ValueError): + _strptime._strptime_time(oldtzname[0], '%Z') + with self.assertRaises(ValueError): + _strptime._strptime_time(oldtzname[1], '%Z') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index ef5602d083b..24e0b47c6be 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -970,4 +970,4 @@ def test_half_float(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py new file mode 100644 index 00000000000..d479f7d7515 --- /dev/null +++ b/Lib/test/test_tcl.py @@ -0,0 +1,806 @@ +import unittest +import subprocess +import sys +import os +from test import support +from test.support import import_helper +from test.support import os_helper + +# Skip this test if the _tkinter module wasn't built. +_tkinter = import_helper.import_module('_tkinter') + +import tkinter +from tkinter import Tcl +from _tkinter import TclError + +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = sys.maxsize + +tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) + + +class TkinterTest(unittest.TestCase): + + def testFlattenLen(self): + # Object without length. + self.assertRaises(TypeError, _tkinter._flatten, True) + # Object with length, but not sequence. + self.assertRaises(TypeError, _tkinter._flatten, {}) + # Sequence or set, but not tuple or list. + # (issue44608: there were leaks in the following cases) + self.assertRaises(TypeError, _tkinter._flatten, 'string') + self.assertRaises(TypeError, _tkinter._flatten, {'set'}) + + +class TclTest(unittest.TestCase): + + def setUp(self): + self.interp = Tcl() + self.wantobjects = self.interp.tk.wantobjects() + + def testEval(self): + tcl = self.interp + tcl.eval('set a 1') + self.assertEqual(tcl.eval('set a'),'1') + + def test_eval_null_in_result(self): + tcl = self.interp + self.assertEqual(tcl.eval('set a "a\\0b"'), 'a\x00b') + + def test_eval_surrogates_in_result(self): + tcl = self.interp + self.assertEqual(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>') + + def testEvalException(self): + tcl = self.interp + self.assertRaises(TclError,tcl.eval,'set a') + + def testEvalException2(self): + tcl = self.interp + self.assertRaises(TclError,tcl.eval,'this is wrong') + + def test_eval_returns_tcl_obj(self): + tcl = self.interp.tk + tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a') + a = tcl.eval('set a') + expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab' + self.assertEqual(a, expected) + + def testCall(self): + tcl = self.interp + tcl.call('set','a','1') + self.assertEqual(tcl.call('set','a'),'1') + + def test_call_passing_null(self): + tcl = self.interp + tcl.call('set', 'a', 'a\0b') # ASCII-only + self.assertEqual(tcl.getvar('a'), 'a\x00b') + self.assertEqual(tcl.call('set', 'a'), 'a\x00b') + self.assertEqual(tcl.eval('set a'), 'a\x00b') + + tcl.call('set', 'a', '\u20ac\0') # non-ASCII + self.assertEqual(tcl.getvar('a'), '\u20ac\x00') + self.assertEqual(tcl.call('set', 'a'), '\u20ac\x00') + self.assertEqual(tcl.eval('set a'), '\u20ac\x00') + + def testCallException(self): + tcl = self.interp + self.assertRaises(TclError,tcl.call,'set','a') + + def testCallException2(self): + tcl = self.interp + self.assertRaises(TclError,tcl.call,'this','is','wrong') + + def test_call_returns_tcl_obj(self): + tcl = self.interp.tk + tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a') + a = tcl.call('set', 'a') + expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab' + if self.wantobjects: + self.assertEqual(str(a), expected) + self.assertEqual(a.string, expected) + self.assertEqual(a.typename, 'regexp') + else: + self.assertEqual(a, expected) + + def testSetVar(self): + tcl = self.interp + tcl.setvar('a','1') + self.assertEqual(tcl.eval('set a'),'1') + + def test_setvar_passing_null(self): + tcl = self.interp + tcl.setvar('a', 'a\0b') # ASCII-only + self.assertEqual(tcl.getvar('a'), 'a\x00b') + self.assertEqual(tcl.call('set', 'a'), 'a\x00b') + self.assertEqual(tcl.eval('set a'), 'a\x00b') + + tcl.setvar('a', '\u20ac\0') # non-ASCII + self.assertEqual(tcl.getvar('a'), '\u20ac\x00') + self.assertEqual(tcl.call('set', 'a'), '\u20ac\x00') + self.assertEqual(tcl.eval('set a'), '\u20ac\x00') + + def testSetVarArray(self): + tcl = self.interp + tcl.setvar('a(1)','1') + self.assertEqual(tcl.eval('set a(1)'),'1') + + def testGetVar(self): + tcl = self.interp + tcl.eval('set a 1') + self.assertEqual(tcl.getvar('a'),'1') + + def testGetVarArray(self): + tcl = self.interp + tcl.eval('set a(1) 1') + self.assertEqual(tcl.getvar('a(1)'),'1') + + def testGetVarException(self): + tcl = self.interp + self.assertRaises(TclError,tcl.getvar,'a') + + def testGetVarArrayException(self): + tcl = self.interp + self.assertRaises(TclError,tcl.getvar,'a(1)') + + def test_getvar_returns_tcl_obj(self): + tcl = self.interp.tk + tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a') + a = tcl.getvar('a') + expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab' + if self.wantobjects: + self.assertEqual(str(a), expected) + self.assertEqual(a.string, expected) + self.assertEqual(a.typename, 'regexp') + else: + self.assertEqual(a, expected) + + def testUnsetVar(self): + tcl = self.interp + tcl.setvar('a',1) + self.assertEqual(tcl.eval('info exists a'),'1') + tcl.unsetvar('a') + self.assertEqual(tcl.eval('info exists a'),'0') + + def testUnsetVarArray(self): + tcl = self.interp + tcl.setvar('a(1)',1) + tcl.setvar('a(2)',2) + self.assertEqual(tcl.eval('info exists a(1)'),'1') + self.assertEqual(tcl.eval('info exists a(2)'),'1') + tcl.unsetvar('a(1)') + self.assertEqual(tcl.eval('info exists a(1)'),'0') + self.assertEqual(tcl.eval('info exists a(2)'),'1') + + def testUnsetVarException(self): + tcl = self.interp + self.assertRaises(TclError,tcl.unsetvar,'a') + + def get_integers(self): + return (0, 1, -1, + 2**31-1, -2**31, 2**31, -2**31-1, + 2**63-1, -2**63, 2**63, -2**63-1, + 2**1000, -2**1000) + + def test_getint(self): + tcl = self.interp.tk + for i in self.get_integers(): + self.assertEqual(tcl.getint(' %d ' % i), i) + self.assertEqual(tcl.getint(' %#o ' % i), i) + # Numbers starting with 0 are parsed as decimal in Tcl 9.0 + # and as octal in older versions. + self.assertEqual(tcl.getint((' %#o ' % i).replace('o', '')), + i if tcl_version < (9, 0) else int('%o' % i)) + self.assertEqual(tcl.getint(' %#x ' % i), i) + self.assertEqual(tcl.getint(42), 42) + self.assertRaises(TypeError, tcl.getint) + self.assertRaises(TypeError, tcl.getint, '42', '10') + self.assertRaises(TypeError, tcl.getint, b'42') + self.assertRaises(TypeError, tcl.getint, 42.0) + self.assertRaises(TclError, tcl.getint, 'a') + self.assertRaises((TypeError, ValueError, TclError), + tcl.getint, '42\0') + self.assertRaises((UnicodeEncodeError, ValueError, TclError), + tcl.getint, '42\ud800') + + def test_getdouble(self): + tcl = self.interp.tk + self.assertEqual(tcl.getdouble(' 42 '), 42.0) + self.assertEqual(tcl.getdouble(' 42.5 '), 42.5) + self.assertEqual(tcl.getdouble(42.5), 42.5) + self.assertEqual(tcl.getdouble(42), 42.0) + self.assertRaises(TypeError, tcl.getdouble) + self.assertRaises(TypeError, tcl.getdouble, '42.5', '10') + self.assertRaises(TypeError, tcl.getdouble, b'42.5') + self.assertRaises(TclError, tcl.getdouble, 'a') + self.assertRaises((TypeError, ValueError, TclError), + tcl.getdouble, '42.5\0') + self.assertRaises((UnicodeEncodeError, ValueError, TclError), + tcl.getdouble, '42.5\ud800') + + def test_getboolean(self): + tcl = self.interp.tk + self.assertIs(tcl.getboolean('on'), True) + self.assertIs(tcl.getboolean('1'), True) + self.assertIs(tcl.getboolean(42), True) + self.assertIs(tcl.getboolean(0), False) + self.assertRaises(TypeError, tcl.getboolean) + self.assertRaises(TypeError, tcl.getboolean, 'on', '1') + self.assertRaises(TypeError, tcl.getboolean, b'on') + self.assertRaises(TypeError, tcl.getboolean, 1.0) + self.assertRaises(TclError, tcl.getboolean, 'a') + self.assertRaises((TypeError, ValueError, TclError), + tcl.getboolean, 'on\0') + self.assertRaises((UnicodeEncodeError, ValueError, TclError), + tcl.getboolean, 'on\ud800') + + def testEvalFile(self): + tcl = self.interp + filename = os_helper.TESTFN_ASCII + self.addCleanup(os_helper.unlink, filename) + with open(filename, 'w') as f: + f.write("""set a 1 + set b 2 + set c [ expr $a + $b ] + """) + tcl.evalfile(filename) + self.assertEqual(tcl.eval('set a'),'1') + self.assertEqual(tcl.eval('set b'),'2') + self.assertEqual(tcl.eval('set c'),'3') + + def test_evalfile_null_in_result(self): + tcl = self.interp + filename = os_helper.TESTFN_ASCII + self.addCleanup(os_helper.unlink, filename) + with open(filename, 'w') as f: + f.write(""" + set a "a\0b" + set b "a\\0b" + """) + tcl.evalfile(filename) + self.assertEqual(tcl.eval('set a'), 'a\x00b') + self.assertEqual(tcl.eval('set b'), 'a\x00b') + + def test_evalfile_surrogates_in_result(self): + tcl = self.interp + encoding = tcl.call('encoding', 'system') + self.addCleanup(tcl.call, 'encoding', 'system', encoding) + tcl.call('encoding', 'system', 'utf-8') + + filename = os_helper.TESTFN_ASCII + self.addCleanup(os_helper.unlink, filename) + with open(filename, 'wb') as f: + f.write(b""" + set a "<\xed\xa0\xbd\xed\xb2\xbb>" + """) + if tcl_version >= (9, 0): + self.assertRaises(TclError, tcl.evalfile, filename) + else: + tcl.evalfile(filename) + self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>') + + with open(filename, 'wb') as f: + f.write(b""" + set b "<\\ud83d\\udcbb>" + """) + tcl.evalfile(filename) + self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>') + + def testEvalFileException(self): + tcl = self.interp + filename = "doesnotexists" + try: + os.remove(filename) + except Exception as e: + pass + self.assertRaises(TclError,tcl.evalfile,filename) + + def testPackageRequireException(self): + tcl = self.interp + self.assertRaises(TclError,tcl.eval,'package require DNE') + + @unittest.skipUnless(sys.platform == 'win32', 'Requires Windows') + def testLoadWithUNC(self): + # Build a UNC path from the regular path. + # Something like + # \\%COMPUTERNAME%\c$\python27\python.exe + + fullname = os.path.abspath(sys.executable) + if fullname[1] != ':': + raise unittest.SkipTest('Absolute path should have drive part') + unc_name = r'\\%s\%s$\%s' % (os.environ['COMPUTERNAME'], + fullname[0], + fullname[3:]) + if not os.path.exists(unc_name): + raise unittest.SkipTest('Cannot connect to UNC Path') + + with os_helper.EnvironmentVarGuard() as env: + env.unset("TCL_LIBRARY") + stdout = subprocess.check_output( + [unc_name, '-c', 'import tkinter; print(tkinter)']) + + self.assertIn(b'tkinter', stdout) + + def test_exprstring(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprstring(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, str) + + self.assertRaises(TypeError, tcl.exprstring) + self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprstring, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprstring, 'spam') + check('', '0') + check('8.2 + 6', '14.2') + check('3.1 + $a', '6.1') + check('2 + "$a.$b"', '5.6') + check('4*[llength "6 2"]', '8') + check('{word one} < "word $a"', '0') + check('4*2 < 7', '0') + check('hypot($a, 4)', '5.0') + check('5 / 4', '1') + check('5 / 4.0', '1.25') + check('5 / ( [string length "abcd"] + 0.0 )', '1.25') + check('20.0/5.0', '4.0') + check('"0x03" > "2"', '1') + check('[string length "a\xbd\u20ac"]', '3') + check(r'[string length "a\xbd\u20ac"]', '3') + check('"abc"', 'abc') + check('"a\xbd\u20ac"', 'a\xbd\u20ac') + check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') + check(r'"a\0b"', 'a\x00b') + check('2**64', str(2**64)) + + def test_exprdouble(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprdouble(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, float) + + self.assertRaises(TypeError, tcl.exprdouble) + self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprdouble, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprdouble, 'spam') + check('', 0.0) + check('8.2 + 6', 14.2) + check('3.1 + $a', 6.1) + check('2 + "$a.$b"', 5.6) + check('4*[llength "6 2"]', 8.0) + check('{word one} < "word $a"', 0.0) + check('4*2 < 7', 0.0) + check('hypot($a, 4)', 5.0) + check('5 / 4', 1.0) + check('5 / 4.0', 1.25) + check('5 / ( [string length "abcd"] + 0.0 )', 1.25) + check('20.0/5.0', 4.0) + check('"0x03" > "2"', 1.0) + check('[string length "a\xbd\u20ac"]', 3.0) + check(r'[string length "a\xbd\u20ac"]', 3.0) + self.assertRaises(TclError, tcl.exprdouble, '"abc"') + check('2**64', float(2**64)) + + def test_exprlong(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprlong(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + + self.assertRaises(TypeError, tcl.exprlong) + self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprlong, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprlong, 'spam') + check('', 0) + check('8.2 + 6', 14) + check('3.1 + $a', 6) + check('2 + "$a.$b"', 5) + check('4*[llength "6 2"]', 8) + check('{word one} < "word $a"', 0) + check('4*2 < 7', 0) + check('hypot($a, 4)', 5) + check('5 / 4', 1) + check('5 / 4.0', 1) + check('5 / ( [string length "abcd"] + 0.0 )', 1) + check('20.0/5.0', 4) + check('"0x03" > "2"', 1) + check('[string length "a\xbd\u20ac"]', 3) + check(r'[string length "a\xbd\u20ac"]', 3) + self.assertRaises(TclError, tcl.exprlong, '"abc"') + self.assertRaises(TclError, tcl.exprlong, '2**64') + + def test_exprboolean(self): + tcl = self.interp + tcl.call('set', 'a', 3) + tcl.call('set', 'b', 6) + def check(expr, expected): + result = tcl.exprboolean(expr) + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + self.assertNotIsInstance(result, bool) + + self.assertRaises(TypeError, tcl.exprboolean) + self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6') + self.assertRaises(TypeError, tcl.exprboolean, b'8.2 + 6') + self.assertRaises(TclError, tcl.exprboolean, 'spam') + check('', False) + for value in ('0', 'false', 'no', 'off'): + check(value, False) + check('"%s"' % value, False) + check('{%s}' % value, False) + for value in ('1', 'true', 'yes', 'on'): + check(value, True) + check('"%s"' % value, True) + check('{%s}' % value, True) + check('8.2 + 6', True) + check('3.1 + $a', True) + check('2 + "$a.$b"', True) + check('4*[llength "6 2"]', True) + check('{word one} < "word $a"', False) + check('4*2 < 7', False) + check('hypot($a, 4)', True) + check('5 / 4', True) + check('5 / 4.0', True) + check('5 / ( [string length "abcd"] + 0.0 )', True) + check('20.0/5.0', True) + check('"0x03" > "2"', True) + check('[string length "a\xbd\u20ac"]', True) + check(r'[string length "a\xbd\u20ac"]', True) + self.assertRaises(TclError, tcl.exprboolean, '"abc"') + check('2**64', True) + + def test_booleans(self): + tcl = self.interp + def check(expr, expected): + result = tcl.call('expr', expr) + if tcl.wantobjects(): + self.assertEqual(result, expected) + self.assertIsInstance(result, int) + else: + self.assertIn(result, (expr, str(int(expected)))) + self.assertIsInstance(result, str) + check('true', True) + check('yes', True) + check('on', True) + check('false', False) + check('no', False) + check('off', False) + check('1 < 2', True) + check('1 > 2', False) + + def test_expr_bignum(self): + tcl = self.interp + for i in self.get_integers(): + result = tcl.call('expr', str(i)) + if self.wantobjects: + self.assertEqual(result, i) + self.assertIsInstance(result, int) + else: + self.assertEqual(result, str(i)) + self.assertIsInstance(result, str) + + def test_passing_values(self): + def passValue(value): + return self.interp.call('set', '_', value) + + self.assertEqual(passValue(True), True if self.wantobjects else '1') + self.assertEqual(passValue(False), False if self.wantobjects else '0') + self.assertEqual(passValue('string'), 'string') + self.assertEqual(passValue('string\u20ac'), 'string\u20ac') + self.assertEqual(passValue('string\U0001f4bb'), 'string\U0001f4bb') + self.assertEqual(passValue('str\x00ing'), 'str\x00ing') + self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd') + self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac') + self.assertEqual(passValue('str\x00ing\U0001f4bb'), + 'str\x00ing\U0001f4bb') + if sys.platform != 'win32': + self.assertEqual(passValue('<\udce2\udc82\udcac>'), + '<\u20ac>') + self.assertEqual(passValue('<\udced\udca0\udcbd\udced\udcb2\udcbb>'), + '<\U0001f4bb>') + self.assertEqual(passValue(b'str\x00ing'), + b'str\x00ing' if self.wantobjects else 'str\x00ing') + self.assertEqual(passValue(b'str\xc0\x80ing'), + b'str\xc0\x80ing' if self.wantobjects else 'str\xc0\x80ing') + self.assertEqual(passValue(b'str\xbding'), + b'str\xbding' if self.wantobjects else 'str\xbding') + for i in self.get_integers(): + self.assertEqual(passValue(i), i if self.wantobjects else str(i)) + for f in (0.0, 1.0, -1.0, 1/3, + sys.float_info.min, sys.float_info.max, + -sys.float_info.min, -sys.float_info.max): + if self.wantobjects: + self.assertEqual(passValue(f), f) + else: + self.assertEqual(float(passValue(f)), f) + if self.wantobjects: + f = passValue(float('nan')) + self.assertNotEqual(f, f) + self.assertEqual(passValue(float('inf')), float('inf')) + self.assertEqual(passValue(-float('inf')), -float('inf')) + else: + self.assertEqual(float(passValue(float('inf'))), float('inf')) + self.assertEqual(float(passValue(-float('inf'))), -float('inf')) + # XXX NaN representation can be not parsable by float() + self.assertEqual(passValue((1, '2', (3.4,))), + (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4') + self.assertEqual(passValue(['a', ['b', 'c']]), + ('a', ('b', 'c')) if self.wantobjects else 'a {b c}') + + def test_user_command(self): + result = None + def testfunc(arg): + nonlocal result + result = arg + return arg + self.interp.createcommand('testfunc', testfunc) + self.addCleanup(self.interp.tk.deletecommand, 'testfunc') + def check(value, expected1=None, expected2=None, *, eq=self.assertEqual): + expected = value + if self.wantobjects >= 2: + if expected2 is not None: + expected = expected2 + expected_type = type(expected) + else: + if expected1 is not None: + expected = expected1 + expected_type = str + nonlocal result + result = None + r = self.interp.call('testfunc', value) + self.assertIsInstance(result, expected_type) + eq(result, expected) + self.assertIsInstance(r, expected_type) + eq(r, expected) + def float_eq(actual, expected): + self.assertAlmostEqual(float(actual), expected, + delta=abs(expected) * 1e-10) + + check(True, '1', 1) + check(False, '0', 0) + check('string') + check('string\xbd') + check('string\u20ac') + check('string\U0001f4bb') + if sys.platform != 'win32': + check('<\udce2\udc82\udcac>', '<\u20ac>', '<\u20ac>') + check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>', '<\U0001f4bb>') + check('') + check(b'string', 'string') + check(b'string\xe2\x82\xac', 'string\xe2\x82\xac') + check(b'string\xbd', 'string\xbd') + check(b'', '') + check('str\x00ing') + check('str\x00ing\xbd') + check('str\x00ing\u20ac') + check(b'str\x00ing', 'str\x00ing') + check(b'str\xc0\x80ing', 'str\xc0\x80ing') + check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac') + for i in self.get_integers(): + check(i, str(i)) + for f in (0.0, 1.0, -1.0): + check(f, repr(f)) + for f in (1/3.0, sys.float_info.min, sys.float_info.max, + -sys.float_info.min, -sys.float_info.max): + check(f, eq=float_eq) + check(float('inf'), eq=float_eq) + check(-float('inf'), eq=float_eq) + # XXX NaN representation can be not parsable by float() + check((), '', '') + check((1, (2,), (3, 4), '5 6', ()), + '1 2 {3 4} {5 6} {}', + (1, (2,), (3, 4), '5 6', '')) + check([1, [2,], [3, 4], '5 6', []], + '1 2 {3 4} {5 6} {}', + (1, (2,), (3, 4), '5 6', '')) + + def test_passing_tcl_obj(self): + tcl = self.interp.tk + a = None + def testfunc(arg): + nonlocal a + a = arg + self.interp.createcommand('testfunc', testfunc) + self.addCleanup(self.interp.tk.deletecommand, 'testfunc') + tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a') + tcl.eval(r'testfunc $a') + expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab' + if self.wantobjects >= 2: + self.assertEqual(str(a), expected) + self.assertEqual(a.string, expected) + self.assertEqual(a.typename, 'regexp') + else: + self.assertEqual(a, expected) + + def test_splitlist(self): + splitlist = self.interp.tk.splitlist + call = self.interp.tk.call + self.assertRaises(TypeError, splitlist) + self.assertRaises(TypeError, splitlist, 'a', 'b') + self.assertRaises(TypeError, splitlist, 2) + testcases = [ + ('2', ('2',)), + ('', ()), + ('{}', ('',)), + ('""', ('',)), + ('a\n b\t\r c\n ', ('a', 'b', 'c')), + (b'a\n b\t\r c\n ', ('a', 'b', 'c')), + ('a \u20ac', ('a', '\u20ac')), + ('a \U0001f4bb', ('a', '\U0001f4bb')), + (b'a \xe2\x82\xac', ('a', '\u20ac')), + (b'a \xf0\x9f\x92\xbb', ('a', '\U0001f4bb')), + (b'a \xed\xa0\xbd\xed\xb2\xbb', ('a', '\U0001f4bb')), + (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), + ('a {b c}', ('a', 'b c')), + (r'a b\ c', ('a', 'b c')), + (('a', 'b c'), ('a', 'b c')), + ('a 2', ('a', '2')), + (('a', 2), ('a', 2)), + ('a 3.4', ('a', '3.4')), + (('a', 3.4), ('a', 3.4)), + ((), ()), + ([], ()), + (['a', ['b', 'c']], ('a', ['b', 'c'])), + (call('list', 1, '2', (3.4,)), + (1, '2', (3.4,)) if self.wantobjects else + ('1', '2', '3.4')), + ] + if not self.wantobjects: + expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') + else: + expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,)) + testcases += [ + (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), + expected), + ] + dbg_info = ('want objects? %s, Tcl version: %s, Tcl patchlevel: %s' + % (self.wantobjects, tcl_version, self.interp.info_patchlevel())) + for arg, res in testcases: + self.assertEqual(splitlist(arg), res, + 'arg=%a, %s' % (arg, dbg_info)) + self.assertRaises(TclError, splitlist, '{') + + def test_splitdict(self): + splitdict = tkinter._splitdict + tcl = self.interp.tk + + arg = '-a {1 2 3} -something foo status {}' + self.assertEqual(splitdict(tcl, arg, False), + {'-a': '1 2 3', '-something': 'foo', 'status': ''}) + self.assertEqual(splitdict(tcl, arg), + {'a': '1 2 3', 'something': 'foo', 'status': ''}) + + arg = ('-a', (1, 2, 3), '-something', 'foo', 'status', '{}') + self.assertEqual(splitdict(tcl, arg, False), + {'-a': (1, 2, 3), '-something': 'foo', 'status': '{}'}) + self.assertEqual(splitdict(tcl, arg), + {'a': (1, 2, 3), 'something': 'foo', 'status': '{}'}) + + self.assertRaises(RuntimeError, splitdict, tcl, '-a b -c ') + self.assertRaises(RuntimeError, splitdict, tcl, ('-a', 'b', '-c')) + + arg = tcl.call('list', + '-a', (1, 2, 3), '-something', 'foo', 'status', ()) + self.assertEqual(splitdict(tcl, arg), + {'a': (1, 2, 3) if self.wantobjects else '1 2 3', + 'something': 'foo', 'status': ''}) + + arg = tcl.call('dict', 'create', + '-a', (1, 2, 3), '-something', 'foo', 'status', ()) + if not self.wantobjects: + expected = {'a': '1 2 3', 'something': 'foo', 'status': ''} + else: + expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''} + self.assertEqual(splitdict(tcl, arg), expected) + + def test_join(self): + join = tkinter._join + tcl = self.interp.tk + def unpack(s): + return tcl.call('lindex', s, 0) + def check(value): + self.assertEqual(unpack(join([value])), value) + self.assertEqual(unpack(join([value, 0])), value) + self.assertEqual(unpack(unpack(join([[value]]))), value) + self.assertEqual(unpack(unpack(join([[value, 0]]))), value) + self.assertEqual(unpack(unpack(join([[value], 0]))), value) + self.assertEqual(unpack(unpack(join([[value, 0], 0]))), value) + check('') + check('spam') + check('sp am') + check('sp\tam') + check('sp\nam') + check(' \t\n') + check('{spam}') + check('{sp am}') + check('"spam"') + check('"sp am"') + check('{"spam"}') + check('"{spam}"') + check('sp\\am') + check('"sp\\am"') + check('"{}" "{}"') + check('"\\') + check('"{') + check('"}') + check('\n\\') + check('\n{') + check('\n}') + check('\\\n') + check('{\n') + check('}\n') + + @support.cpython_only + def test_new_tcl_obj(self): + support.check_disallow_instantiation(self, _tkinter.Tcl_Obj) + support.check_disallow_instantiation(self, _tkinter.TkttType) + support.check_disallow_instantiation(self, _tkinter.TkappType) + + +class BigmemTclTest(unittest.TestCase): + + def setUp(self): + self.interp = Tcl() + + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False) + def test_huge_string_call(self, size): + value = ' ' * size + self.assertRaises(OverflowError, self.interp.call, 'string', 'index', value, 0) + + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=INT_MAX + 1, memuse=2, dry_run=False) + def test_huge_string_builtins(self, size): + tk = self.interp.tk + value = '1' + ' ' * size + self.assertRaises(OverflowError, tk.getint, value) + self.assertRaises(OverflowError, tk.getdouble, value) + self.assertRaises(OverflowError, tk.getboolean, value) + self.assertRaises(OverflowError, tk.eval, value) + self.assertRaises(OverflowError, tk.evalfile, value) + self.assertRaises(OverflowError, tk.record, value) + self.assertRaises(OverflowError, tk.adderrorinfo, value) + self.assertRaises(OverflowError, tk.setvar, value, 'x', 'a') + self.assertRaises(OverflowError, tk.setvar, 'x', value, 'a') + self.assertRaises(OverflowError, tk.unsetvar, value) + self.assertRaises(OverflowError, tk.unsetvar, 'x', value) + self.assertRaises(OverflowError, tk.adderrorinfo, value) + self.assertRaises(OverflowError, tk.exprstring, value) + self.assertRaises(OverflowError, tk.exprlong, value) + self.assertRaises(OverflowError, tk.exprboolean, value) + self.assertRaises(OverflowError, tk.splitlist, value) + self.assertRaises(OverflowError, tk.createcommand, value, max) + self.assertRaises(OverflowError, tk.deletecommand, value) + + @support.cpython_only + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") + @support.bigmemtest(size=INT_MAX + 1, memuse=6, dry_run=False) + def test_huge_string_builtins2(self, size): + # These commands require larger memory for possible error messages + tk = self.interp.tk + value = '1' + ' ' * size + self.assertRaises(OverflowError, tk.evalfile, value) + self.assertRaises(OverflowError, tk.unsetvar, value) + self.assertRaises(OverflowError, tk.unsetvar, 'x', value) + + +def setUpModule(): + if support.verbose: + tcl = Tcl() + print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_termios.py b/Lib/test/test_termios.py new file mode 100644 index 00000000000..5fd62b30263 --- /dev/null +++ b/Lib/test/test_termios.py @@ -0,0 +1,276 @@ +import errno +import os +import sys +import tempfile +import threading +import unittest +from test import support +from test.support import threading_helper +from test.support.import_helper import import_module + +termios = import_module('termios') + + +@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") +class TestFunctions(unittest.TestCase): + + def setUp(self): + self.master_fd, self.fd = os.openpty() + self.addCleanup(os.close, self.master_fd) + self.stream = self.enterContext(open(self.fd, 'wb', buffering=0)) + tmp = self.enterContext(tempfile.TemporaryFile(mode='wb', buffering=0)) + self.bad_fd = tmp.fileno() + + def assertRaisesTermiosError(self, errno, callable, *args): + with self.assertRaises(termios.error) as cm: + callable(*args) + self.assertEqual(cm.exception.args[0], errno) + + def test_tcgetattr(self): + attrs = termios.tcgetattr(self.fd) + self.assertIsInstance(attrs, list) + self.assertEqual(len(attrs), 7) + for i in range(6): + self.assertIsInstance(attrs[i], int) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = attrs + self.assertIsInstance(cc, list) + self.assertEqual(len(cc), termios.NCCS) + for i, x in enumerate(cc): + if ((lflag & termios.ICANON) == 0 and + (i == termios.VMIN or i == termios.VTIME)): + self.assertIsInstance(x, int) + else: + self.assertIsInstance(x, bytes) + self.assertEqual(len(x), 1) + self.assertEqual(termios.tcgetattr(self.stream), attrs) + + def test_tcgetattr_errors(self): + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetattr, self.bad_fd) + self.assertRaises(ValueError, termios.tcgetattr, -1) + self.assertRaises(OverflowError, termios.tcgetattr, 2**1000) + self.assertRaises(TypeError, termios.tcgetattr, object()) + self.assertRaises(TypeError, termios.tcgetattr) + + def test_tcsetattr(self): + attrs = termios.tcgetattr(self.fd) + termios.tcsetattr(self.fd, termios.TCSANOW, attrs) + termios.tcsetattr(self.fd, termios.TCSADRAIN, attrs) + termios.tcsetattr(self.fd, termios.TCSAFLUSH, attrs) + termios.tcsetattr(self.stream, termios.TCSANOW, attrs) + + def test_tcsetattr_errors(self): + attrs = termios.tcgetattr(self.fd) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, tuple(attrs)) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs[:-1]) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs + [0]) + for i in range(6): + attrs2 = attrs[:] + attrs2[i] = 2**1000 + self.assertRaises(OverflowError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[i] = object() + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs[:-1] + [attrs[-1][:-1]]) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs[:-1] + [attrs[-1] + [b'\0']]) + for i in range(len(attrs[-1])): + attrs2 = attrs[:] + attrs2[-1] = attrs2[-1][:] + attrs2[-1][i] = 2**1000 + self.assertRaises(OverflowError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[-1][i] = object() + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[-1][i] = b'' + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + attrs2[-1][i] = b'\0\0' + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, object()) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW) + self.assertRaisesTermiosError(errno.EINVAL, termios.tcsetattr, self.fd, -1, attrs) + self.assertRaises(OverflowError, termios.tcsetattr, self.fd, 2**1000, attrs) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, object(), attrs) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsetattr, self.bad_fd, termios.TCSANOW, attrs) + self.assertRaises(ValueError, termios.tcsetattr, -1, termios.TCSANOW, attrs) + self.assertRaises(OverflowError, termios.tcsetattr, 2**1000, termios.TCSANOW, attrs) + self.assertRaises(TypeError, termios.tcsetattr, object(), termios.TCSANOW, attrs) + self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW) + + def test_tcsendbreak(self): + try: + termios.tcsendbreak(self.fd, 1) + except termios.error as exc: + if exc.args[0] == errno.ENOTTY and sys.platform.startswith(('freebsd', "netbsd")): + self.skipTest('termios.tcsendbreak() is not supported ' + 'with pseudo-terminals (?) on this platform') + raise + termios.tcsendbreak(self.stream, 1) + + def test_tcsendbreak_errors(self): + self.assertRaises(OverflowError, termios.tcsendbreak, self.fd, 2**1000) + self.assertRaises(TypeError, termios.tcsendbreak, self.fd, 0.0) + self.assertRaises(TypeError, termios.tcsendbreak, self.fd, object()) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsendbreak, self.bad_fd, 0) + self.assertRaises(ValueError, termios.tcsendbreak, -1, 0) + self.assertRaises(OverflowError, termios.tcsendbreak, 2**1000, 0) + self.assertRaises(TypeError, termios.tcsendbreak, object(), 0) + self.assertRaises(TypeError, termios.tcsendbreak, self.fd) + + def test_tcdrain(self): + termios.tcdrain(self.fd) + termios.tcdrain(self.stream) + + def test_tcdrain_errors(self): + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcdrain, self.bad_fd) + self.assertRaises(ValueError, termios.tcdrain, -1) + self.assertRaises(OverflowError, termios.tcdrain, 2**1000) + self.assertRaises(TypeError, termios.tcdrain, object()) + self.assertRaises(TypeError, termios.tcdrain) + + def test_tcflush(self): + termios.tcflush(self.fd, termios.TCIFLUSH) + termios.tcflush(self.fd, termios.TCOFLUSH) + termios.tcflush(self.fd, termios.TCIOFLUSH) + + def test_tcflush_errors(self): + self.assertRaisesTermiosError(errno.EINVAL, termios.tcflush, self.fd, -1) + self.assertRaises(OverflowError, termios.tcflush, self.fd, 2**1000) + self.assertRaises(TypeError, termios.tcflush, self.fd, object()) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcflush, self.bad_fd, termios.TCIFLUSH) + self.assertRaises(ValueError, termios.tcflush, -1, termios.TCIFLUSH) + self.assertRaises(OverflowError, termios.tcflush, 2**1000, termios.TCIFLUSH) + self.assertRaises(TypeError, termios.tcflush, object(), termios.TCIFLUSH) + self.assertRaises(TypeError, termios.tcflush, self.fd) + + def test_tcflush_clear_input_or_output(self): + wfd = self.fd + rfd = self.master_fd + # The data is buffered in the input buffer on Linux, and in + # the output buffer on other platforms. + inbuf = sys.platform in ('linux', 'android') + + os.write(wfd, b'abcdef') + self.assertEqual(os.read(rfd, 2), b'ab') + if inbuf: + # don't flush input + termios.tcflush(rfd, termios.TCOFLUSH) + else: + # don't flush output + termios.tcflush(wfd, termios.TCIFLUSH) + self.assertEqual(os.read(rfd, 2), b'cd') + if inbuf: + # flush input + termios.tcflush(rfd, termios.TCIFLUSH) + else: + # flush output + termios.tcflush(wfd, termios.TCOFLUSH) + os.write(wfd, b'ABCDEF') + self.assertEqual(os.read(rfd, 1024), b'ABCDEF') + + def test_tcflow(self): + termios.tcflow(self.fd, termios.TCOOFF) + termios.tcflow(self.fd, termios.TCOON) + termios.tcflow(self.fd, termios.TCIOFF) + termios.tcflow(self.fd, termios.TCION) + + def test_tcflow_errors(self): + self.assertRaisesTermiosError(errno.EINVAL, termios.tcflow, self.fd, -1) + self.assertRaises(OverflowError, termios.tcflow, self.fd, 2**1000) + self.assertRaises(TypeError, termios.tcflow, self.fd, object()) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcflow, self.bad_fd, termios.TCOON) + self.assertRaises(ValueError, termios.tcflow, -1, termios.TCOON) + self.assertRaises(OverflowError, termios.tcflow, 2**1000, termios.TCOON) + self.assertRaises(TypeError, termios.tcflow, object(), termios.TCOON) + self.assertRaises(TypeError, termios.tcflow, self.fd) + + @unittest.skipUnless(sys.platform == 'linux', 'only works on Linux') + def test_tcflow_suspend_and_resume_output(self): + wfd = self.fd + rfd = self.master_fd + write_suspended = threading.Event() + write_finished = threading.Event() + + def writer(): + os.write(wfd, b'abc') + self.assertTrue(write_suspended.wait(support.SHORT_TIMEOUT)) + os.write(wfd, b'def') + write_finished.set() + + with threading_helper.start_threads([threading.Thread(target=writer)]): + self.assertEqual(os.read(rfd, 3), b'abc') + try: + try: + termios.tcflow(wfd, termios.TCOOFF) + finally: + write_suspended.set() + self.assertFalse(write_finished.wait(0.5), + 'output was not suspended') + finally: + termios.tcflow(wfd, termios.TCOON) + self.assertTrue(write_finished.wait(support.SHORT_TIMEOUT), + 'output was not resumed') + self.assertEqual(os.read(rfd, 1024), b'def') + + def test_tcgetwinsize(self): + size = termios.tcgetwinsize(self.fd) + self.assertIsInstance(size, tuple) + self.assertEqual(len(size), 2) + self.assertIsInstance(size[0], int) + self.assertIsInstance(size[1], int) + self.assertEqual(termios.tcgetwinsize(self.stream), size) + + def test_tcgetwinsize_errors(self): + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetwinsize, self.bad_fd) + self.assertRaises(ValueError, termios.tcgetwinsize, -1) + self.assertRaises(OverflowError, termios.tcgetwinsize, 2**1000) + self.assertRaises(TypeError, termios.tcgetwinsize, object()) + self.assertRaises(TypeError, termios.tcgetwinsize) + + def test_tcsetwinsize(self): + size = termios.tcgetwinsize(self.fd) + termios.tcsetwinsize(self.fd, size) + termios.tcsetwinsize(self.fd, list(size)) + termios.tcsetwinsize(self.stream, size) + + def test_tcsetwinsize_errors(self): + size = termios.tcgetwinsize(self.fd) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, size[:-1]) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, size + (0,)) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, object()) + self.assertRaises(OverflowError, termios.tcsetwinsize, self.fd, (size[0], 2**1000)) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (size[0], float(size[1]))) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (size[0], object())) + self.assertRaises(OverflowError, termios.tcsetwinsize, self.fd, (2**1000, size[1])) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (float(size[0]), size[1])) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, (object(), size[1])) + self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsetwinsize, self.bad_fd, size) + self.assertRaises(ValueError, termios.tcsetwinsize, -1, size) + self.assertRaises(OverflowError, termios.tcsetwinsize, 2**1000, size) + self.assertRaises(TypeError, termios.tcsetwinsize, object(), size) + self.assertRaises(TypeError, termios.tcsetwinsize, self.fd) + + +class TestModule(unittest.TestCase): + def test_constants(self): + self.assertIsInstance(termios.B0, int) + self.assertIsInstance(termios.B38400, int) + self.assertIsInstance(termios.TCSANOW, int) + self.assertIsInstance(termios.TCSADRAIN, int) + self.assertIsInstance(termios.TCSAFLUSH, int) + self.assertIsInstance(termios.TCIFLUSH, int) + self.assertIsInstance(termios.TCOFLUSH, int) + self.assertIsInstance(termios.TCIOFLUSH, int) + self.assertIsInstance(termios.TCOOFF, int) + self.assertIsInstance(termios.TCOON, int) + self.assertIsInstance(termios.TCIOFF, int) + self.assertIsInstance(termios.TCION, int) + self.assertIsInstance(termios.VTIME, int) + self.assertIsInstance(termios.VMIN, int) + self.assertIsInstance(termios.NCCS, int) + self.assertLess(termios.VTIME, termios.NCCS) + self.assertLess(termios.VMIN, termios.NCCS) + + def test_exception(self): + self.assertTrue(issubclass(termios.error, Exception)) + self.assertFalse(issubclass(termios.error, OSError)) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_threadsignals.py b/Lib/test/test_threadsignals.py new file mode 100644 index 00000000000..bf241ada90e --- /dev/null +++ b/Lib/test/test_threadsignals.py @@ -0,0 +1,237 @@ +"""PyUnit testing that threads honor our signal semantics""" + +import unittest +import signal +import os +import sys +from test.support import threading_helper +import _thread as thread +import time + +if (sys.platform[:3] == 'win'): + raise unittest.SkipTest("Can't test signal on %s" % sys.platform) + +process_pid = os.getpid() +signalled_all=thread.allocate_lock() + +USING_PTHREAD_COND = (sys.thread_info.name == 'pthread' + and sys.thread_info.lock == 'mutex+cond') + +def registerSignals(for_usr1, for_usr2, for_alrm): + usr1 = signal.signal(signal.SIGUSR1, for_usr1) + usr2 = signal.signal(signal.SIGUSR2, for_usr2) + alrm = signal.signal(signal.SIGALRM, for_alrm) + return usr1, usr2, alrm + + +# The signal handler. Just note that the signal occurred and +# from who. +def handle_signals(sig,frame): + signal_blackboard[sig]['tripped'] += 1 + signal_blackboard[sig]['tripped_by'] = thread.get_ident() + +# a function that will be spawned as a separate thread. +def send_signals(): + # We use `raise_signal` rather than `kill` because: + # * It verifies that a signal delivered to a background thread still has + # its Python-level handler called on the main thread. + # * It ensures the signal is handled before the thread exits. + signal.raise_signal(signal.SIGUSR1) + signal.raise_signal(signal.SIGUSR2) + signalled_all.release() + + +@threading_helper.requires_working_threading() +class ThreadSignals(unittest.TestCase): + + def test_signals(self): + with threading_helper.wait_threads_exit(): + # Test signal handling semantics of threads. + # We spawn a thread, have the thread send itself two signals, and + # wait for it to finish. Check that we got both signals + # and that they were run by the main thread. + signalled_all.acquire() + self.spawnSignallingThread() + signalled_all.acquire() + + self.assertEqual( signal_blackboard[signal.SIGUSR1]['tripped'], 1) + self.assertEqual( signal_blackboard[signal.SIGUSR1]['tripped_by'], + thread.get_ident()) + self.assertEqual( signal_blackboard[signal.SIGUSR2]['tripped'], 1) + self.assertEqual( signal_blackboard[signal.SIGUSR2]['tripped_by'], + thread.get_ident()) + signalled_all.release() + + def spawnSignallingThread(self): + thread.start_new_thread(send_signals, ()) + + def alarm_interrupt(self, sig, frame): + raise KeyboardInterrupt + + @unittest.skipIf(USING_PTHREAD_COND, + 'POSIX condition variables cannot be interrupted') + @unittest.skipIf(sys.platform.startswith('linux') and + not sys.thread_info.version, + 'Issue 34004: musl does not allow interruption of locks ' + 'by signals.') + # Issue #20564: sem_timedwait() cannot be interrupted on OpenBSD + @unittest.skipIf(sys.platform.startswith('openbsd'), + 'lock cannot be interrupted on OpenBSD') + def test_lock_acquire_interruption(self): + # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck + # in a deadlock. + # XXX this test can fail when the legacy (non-semaphore) implementation + # of locks is used in thread_pthread.h, see issue #11223. + oldalrm = signal.signal(signal.SIGALRM, self.alarm_interrupt) + try: + lock = thread.allocate_lock() + lock.acquire() + signal.alarm(1) + t1 = time.monotonic() + self.assertRaises(KeyboardInterrupt, lock.acquire, timeout=5) + dt = time.monotonic() - t1 + # Checking that KeyboardInterrupt was raised is not sufficient. + # We want to assert that lock.acquire() was interrupted because + # of the signal, not that the signal handler was called immediately + # after timeout return of lock.acquire() (which can fool assertRaises). + self.assertLess(dt, 3.0) + finally: + signal.alarm(0) + signal.signal(signal.SIGALRM, oldalrm) + + @unittest.skipIf(USING_PTHREAD_COND, + 'POSIX condition variables cannot be interrupted') + @unittest.skipIf(sys.platform.startswith('linux') and + not sys.thread_info.version, + 'Issue 34004: musl does not allow interruption of locks ' + 'by signals.') + # Issue #20564: sem_timedwait() cannot be interrupted on OpenBSD + @unittest.skipIf(sys.platform.startswith('openbsd'), + 'lock cannot be interrupted on OpenBSD') + def test_rlock_acquire_interruption(self): + # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck + # in a deadlock. + # XXX this test can fail when the legacy (non-semaphore) implementation + # of locks is used in thread_pthread.h, see issue #11223. + oldalrm = signal.signal(signal.SIGALRM, self.alarm_interrupt) + try: + rlock = thread.RLock() + # For reentrant locks, the initial acquisition must be in another + # thread. + def other_thread(): + rlock.acquire() + + with threading_helper.wait_threads_exit(): + thread.start_new_thread(other_thread, ()) + # Wait until we can't acquire it without blocking... + while rlock.acquire(blocking=False): + rlock.release() + time.sleep(0.01) + signal.alarm(1) + t1 = time.monotonic() + self.assertRaises(KeyboardInterrupt, rlock.acquire, timeout=5) + dt = time.monotonic() - t1 + # See rationale above in test_lock_acquire_interruption + self.assertLess(dt, 3.0) + finally: + signal.alarm(0) + signal.signal(signal.SIGALRM, oldalrm) + + def acquire_retries_on_intr(self, lock): + self.sig_recvd = False + def my_handler(signal, frame): + self.sig_recvd = True + + old_handler = signal.signal(signal.SIGUSR1, my_handler) + try: + def other_thread(): + # Acquire the lock in a non-main thread, so this test works for + # RLocks. + lock.acquire() + # Wait until the main thread is blocked in the lock acquire, and + # then wake it up with this. + time.sleep(0.5) + os.kill(process_pid, signal.SIGUSR1) + # Let the main thread take the interrupt, handle it, and retry + # the lock acquisition. Then we'll let it run. + time.sleep(0.5) + lock.release() + + with threading_helper.wait_threads_exit(): + thread.start_new_thread(other_thread, ()) + # Wait until we can't acquire it without blocking... + while lock.acquire(blocking=False): + lock.release() + time.sleep(0.01) + result = lock.acquire() # Block while we receive a signal. + self.assertTrue(self.sig_recvd) + self.assertTrue(result) + finally: + signal.signal(signal.SIGUSR1, old_handler) + + def test_lock_acquire_retries_on_intr(self): + self.acquire_retries_on_intr(thread.allocate_lock()) + + def test_rlock_acquire_retries_on_intr(self): + self.acquire_retries_on_intr(thread.RLock()) + + def test_interrupted_timed_acquire(self): + # Test to make sure we recompute lock acquisition timeouts when we + # receive a signal. Check this by repeatedly interrupting a lock + # acquire in the main thread, and make sure that the lock acquire times + # out after the right amount of time. + # NOTE: this test only behaves as expected if C signals get delivered + # to the main thread. Otherwise lock.acquire() itself doesn't get + # interrupted and the test trivially succeeds. + self.start = None + self.end = None + self.sigs_recvd = 0 + done = thread.allocate_lock() + done.acquire() + lock = thread.allocate_lock() + lock.acquire() + def my_handler(signum, frame): + self.sigs_recvd += 1 + old_handler = signal.signal(signal.SIGUSR1, my_handler) + try: + def timed_acquire(): + self.start = time.monotonic() + lock.acquire(timeout=0.5) + self.end = time.monotonic() + def send_signals(): + for _ in range(40): + time.sleep(0.02) + os.kill(process_pid, signal.SIGUSR1) + done.release() + + with threading_helper.wait_threads_exit(): + # Send the signals from the non-main thread, since the main thread + # is the only one that can process signals. + thread.start_new_thread(send_signals, ()) + timed_acquire() + # Wait for thread to finish + done.acquire() + # This allows for some timing and scheduling imprecision + self.assertLess(self.end - self.start, 2.0) + self.assertGreater(self.end - self.start, 0.3) + # If the signal is received several times before PyErr_CheckSignals() + # is called, the handler will get called less than 40 times. Just + # check it's been called at least once. + self.assertGreater(self.sigs_recvd, 0) + finally: + signal.signal(signal.SIGUSR1, old_handler) + + +def setUpModule(): + global signal_blackboard + + signal_blackboard = { signal.SIGUSR1 : {'tripped': 0, 'tripped_by': 0 }, + signal.SIGUSR2 : {'tripped': 0, 'tripped_by': 0 }, + signal.SIGALRM : {'tripped': 0, 'tripped_by': 0 } } + + oldsigs = registerSignals(handle_signals, handle_signals, handle_signals) + unittest.addModuleCleanup(registerSignals, *oldsigs) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_tkinter/README b/Lib/test/test_tkinter/README new file mode 100644 index 00000000000..79cd16c74d7 --- /dev/null +++ b/Lib/test/test_tkinter/README @@ -0,0 +1,14 @@ +Writing new tests +================= + +Precaution +---------- + + New tests should always use only one Tk window at once, like all the + current tests do. This means that you have to destroy the current window + before creating another one, and clean up after the test. The motivation + behind this is that some tests may depend on having its window focused + while it is running to work properly, and it may be hard to force focus + on your window across platforms (right now only test_traversal at + test_ttk.test_widgets.NotebookTest depends on this). + diff --git a/Lib/test/test_tkinter/__init__.py b/Lib/test/test_tkinter/__init__.py new file mode 100644 index 00000000000..aa196c12c80 --- /dev/null +++ b/Lib/test/test_tkinter/__init__.py @@ -0,0 +1,24 @@ +import os.path +import unittest + +from test.support import ( + check_sanitizer, + import_helper, + load_package_tests, + requires, + ) + + +if check_sanitizer(address=True, memory=True): + # See gh-90791 for details + raise unittest.SkipTest("Tests involving libX11 can SEGFAULT on ASAN/MSAN builds") + +# Skip test if _tkinter wasn't built. +import_helper.import_module('_tkinter') + +# Skip test if tk cannot be initialized. +requires('gui') + + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tkinter/__main__.py b/Lib/test/test_tkinter/__main__.py new file mode 100644 index 00000000000..40a23a297ec --- /dev/null +++ b/Lib/test/test_tkinter/__main__.py @@ -0,0 +1,4 @@ +from . import load_tests +import unittest + +unittest.main() diff --git a/Lib/test/test_tkinter/support.py b/Lib/test/test_tkinter/support.py new file mode 100644 index 00000000000..ebb9e00ff91 --- /dev/null +++ b/Lib/test/test_tkinter/support.py @@ -0,0 +1,134 @@ +import functools +import tkinter + +class AbstractTkTest: + + @classmethod + def setUpClass(cls): + cls._old_support_default_root = tkinter._support_default_root + destroy_default_root() + tkinter.NoDefaultRoot() + cls.root = tkinter.Tk() + cls.wantobjects = cls.root.wantobjects() + # De-maximize main window. + # Some window managers can maximize new windows. + cls.root.wm_state('normal') + try: + cls.root.wm_attributes(zoomed=False) + except tkinter.TclError: + pass + + @classmethod + def tearDownClass(cls): + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + tkinter._default_root = None + tkinter._support_default_root = cls._old_support_default_root + + def setUp(self): + self.root.deiconify() + + def tearDown(self): + for w in self.root.winfo_children(): + w.destroy() + self.root.withdraw() + + +class AbstractDefaultRootTest: + + def setUp(self): + self._old_support_default_root = tkinter._support_default_root + destroy_default_root() + tkinter._support_default_root = True + self.wantobjects = tkinter.wantobjects + + def tearDown(self): + destroy_default_root() + tkinter._default_root = None + tkinter._support_default_root = self._old_support_default_root + + def _test_widget(self, constructor): + # no master passing + x = constructor() + self.assertIsNotNone(tkinter._default_root) + self.assertIs(x.master, tkinter._default_root) + self.assertIs(x.tk, tkinter._default_root.tk) + x.destroy() + destroy_default_root() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, constructor) + self.assertFalse(hasattr(tkinter, '_default_root')) + + +def destroy_default_root(): + if getattr(tkinter, '_default_root', None): + tkinter._default_root.update_idletasks() + tkinter._default_root.destroy() + tkinter._default_root = None + +def simulate_mouse_click(widget, x, y): + """Generate proper events to click at the x, y position (tries to act + like an X server).""" + widget.event_generate('', x=0, y=0) + widget.event_generate('', x=x, y=y) + widget.event_generate('', x=x, y=y) + widget.event_generate('', x=x, y=y) + + +import _tkinter +tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) +tk_version = tuple(map(int, _tkinter.TK_VERSION.split('.'))) + +def requires_tk(*version): + if len(version) <= 2 and tk_version >= version: + return lambda test: test + + def deco(test): + @functools.wraps(test) + def newtest(self): + root = getattr(self, 'root', None) + if get_tk_patchlevel(root) < version: + self.skipTest('requires Tk version >= ' + + '.'.join(map(str, version))) + test(self) + return newtest + return deco + +_tk_patchlevel = None +def get_tk_patchlevel(root): + global _tk_patchlevel + if _tk_patchlevel is None: + _tk_patchlevel = tkinter._parse_version(root.tk.globalgetvar('tk_patchLevel')) + return _tk_patchlevel + +units = { + 'c': 72 / 2.54, # centimeters + 'i': 72, # inches + 'm': 72 / 25.4, # millimeters + 'p': 1, # points +} + +def pixels_conv(value): + return float(value[:-1]) * units[value[-1:]] + +def tcl_obj_eq(actual, expected): + if actual == expected: + return True + if isinstance(actual, _tkinter.Tcl_Obj): + if isinstance(expected, str): + return str(actual) == expected + if isinstance(actual, tuple): + if isinstance(expected, tuple): + return (len(actual) == len(expected) and + all(tcl_obj_eq(act, exp) + for act, exp in zip(actual, expected))) + return False + +def widget_eq(actual, expected): + if actual == expected: + return True + if isinstance(actual, (str, tkinter.Widget)): + if isinstance(expected, (str, tkinter.Widget)): + return str(actual) == str(expected) + return False diff --git a/Lib/test/test_tkinter/test_colorchooser.py b/Lib/test/test_tkinter/test_colorchooser.py new file mode 100644 index 00000000000..9bba21392d8 --- /dev/null +++ b/Lib/test/test_tkinter/test_colorchooser.py @@ -0,0 +1,68 @@ +import unittest +import tkinter +from test.support import requires, swap_attr +from test.test_tkinter.support import AbstractDefaultRootTest, AbstractTkTest +from tkinter import colorchooser +from tkinter.colorchooser import askcolor +from tkinter.commondialog import Dialog + +requires('gui') + + +class ChooserTest(AbstractTkTest, unittest.TestCase): + + @classmethod + def setUpClass(cls): + AbstractTkTest.setUpClass.__func__(cls) + cls.cc = colorchooser.Chooser(initialcolor='dark blue slate') + + def test_fixoptions(self): + cc = self.cc + cc._fixoptions() + self.assertEqual(cc.options['initialcolor'], 'dark blue slate') + + cc.options['initialcolor'] = '#D2D269691E1E' + cc._fixoptions() + self.assertEqual(cc.options['initialcolor'], '#D2D269691E1E') + + cc.options['initialcolor'] = (210, 105, 30) + cc._fixoptions() + self.assertEqual(cc.options['initialcolor'], '#d2691e') + + def test_fixresult(self): + cc = self.cc + self.assertEqual(cc._fixresult(self.root, ()), (None, None)) + self.assertEqual(cc._fixresult(self.root, ''), (None, None)) + self.assertEqual(cc._fixresult(self.root, 'chocolate'), + ((210, 105, 30), 'chocolate')) + self.assertEqual(cc._fixresult(self.root, '#4a3c8c'), + ((74, 60, 140), '#4a3c8c')) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_askcolor(self): + def test_callback(dialog, master): + nonlocal ismapped + master.update() + ismapped = master.winfo_ismapped() + raise ZeroDivisionError + + with swap_attr(Dialog, '_test_callback', test_callback): + ismapped = None + self.assertRaises(ZeroDivisionError, askcolor) + #askcolor() + self.assertEqual(ismapped, False) + + root = tkinter.Tk() + ismapped = None + self.assertRaises(ZeroDivisionError, askcolor) + self.assertEqual(ismapped, True) + root.destroy() + + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, askcolor) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_font.py b/Lib/test/test_tkinter/test_font.py new file mode 100644 index 00000000000..563707ddd2f --- /dev/null +++ b/Lib/test/test_tkinter/test_font.py @@ -0,0 +1,163 @@ +import unittest +import tkinter +from tkinter import font +from test.support import requires, gc_collect, ALWAYS_EQ +from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest + +requires('gui') + +fontname = "TkDefaultFont" + +class FontTest(AbstractTkTest, unittest.TestCase): + + @classmethod + def setUpClass(cls): + AbstractTkTest.setUpClass.__func__(cls) + try: + cls.font = font.Font(root=cls.root, name=fontname, exists=True) + except tkinter.TclError: + cls.font = font.Font(root=cls.root, name=fontname, exists=False) + + def test_configure(self): + options = self.font.configure() + self.assertGreaterEqual(set(options), + {'family', 'size', 'weight', 'slant', 'underline', 'overstrike'}) + for key in options: + self.assertEqual(self.font.cget(key), options[key]) + self.assertEqual(self.font[key], options[key]) + for key in 'family', 'weight', 'slant': + self.assertIsInstance(options[key], str) + self.assertIsInstance(self.font.cget(key), str) + self.assertIsInstance(self.font[key], str) + sizetype = int if self.wantobjects else str + for key in 'size', 'underline', 'overstrike': + self.assertIsInstance(options[key], sizetype) + self.assertIsInstance(self.font.cget(key), sizetype) + self.assertIsInstance(self.font[key], sizetype) + + def test_unicode_family(self): + family = 'MS \u30b4\u30b7\u30c3\u30af' + try: + f = font.Font(root=self.root, family=family, exists=True) + except tkinter.TclError: + f = font.Font(root=self.root, family=family, exists=False) + self.assertEqual(f.cget('family'), family) + del f + gc_collect() + + def test_actual(self): + options = self.font.actual() + self.assertGreaterEqual(set(options), + {'family', 'size', 'weight', 'slant', 'underline', 'overstrike'}) + for key in options: + self.assertEqual(self.font.actual(key), options[key]) + for key in 'family', 'weight', 'slant': + self.assertIsInstance(options[key], str) + self.assertIsInstance(self.font.actual(key), str) + sizetype = int if self.wantobjects else str + for key in 'size', 'underline', 'overstrike': + self.assertIsInstance(options[key], sizetype) + self.assertIsInstance(self.font.actual(key), sizetype) + + def test_name(self): + self.assertEqual(self.font.name, fontname) + self.assertEqual(str(self.font), fontname) + + def test_equality(self): + font1 = font.Font(root=self.root, name=fontname, exists=True) + font2 = font.Font(root=self.root, name=fontname, exists=True) + self.assertIsNot(font1, font2) + self.assertEqual(font1, font2) + self.assertNotEqual(font1, font1.copy()) + + self.assertNotEqual(font1, 0) + self.assertEqual(font1, ALWAYS_EQ) + + root2 = tkinter.Tk() + self.addCleanup(root2.destroy) + font3 = font.Font(root=root2, name=fontname, exists=True) + self.assertEqual(str(font1), str(font3)) + self.assertNotEqual(font1, font3) + + def test_measure(self): + self.assertIsInstance(self.font.measure('abc'), int) + + def test_metrics(self): + metrics = self.font.metrics() + self.assertGreaterEqual(set(metrics), + {'ascent', 'descent', 'linespace', 'fixed'}) + for key in metrics: + self.assertEqual(self.font.metrics(key), metrics[key]) + self.assertIsInstance(metrics[key], int) + self.assertIsInstance(self.font.metrics(key), int) + + def test_families(self): + families = font.families(self.root) + self.assertIsInstance(families, tuple) + self.assertTrue(families) + for family in families: + self.assertIsInstance(family, str) + self.assertTrue(family) + + def test_names(self): + names = font.names(self.root) + self.assertIsInstance(names, tuple) + self.assertTrue(names) + for name in names: + self.assertIsInstance(name, str) + self.assertTrue(name) + self.assertIn(fontname, names) + + def test_nametofont(self): + testfont = font.nametofont(fontname, root=self.root) + self.assertIsInstance(testfont, font.Font) + self.assertEqual(testfont.name, fontname) + + def test_repr(self): + self.assertEqual( + repr(self.font), f'' + ) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_families(self): + self.assertRaises(RuntimeError, font.families) + root = tkinter.Tk() + families = font.families() + self.assertIsInstance(families, tuple) + self.assertTrue(families) + for family in families: + self.assertIsInstance(family, str) + self.assertTrue(family) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.families) + + def test_names(self): + self.assertRaises(RuntimeError, font.names) + root = tkinter.Tk() + names = font.names() + self.assertIsInstance(names, tuple) + self.assertTrue(names) + for name in names: + self.assertIsInstance(name, str) + self.assertTrue(name) + self.assertIn(fontname, names) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.names) + + def test_nametofont(self): + self.assertRaises(RuntimeError, font.nametofont, fontname) + root = tkinter.Tk() + testfont = font.nametofont(fontname) + self.assertIsInstance(testfont, font.Font) + self.assertEqual(testfont.name, fontname) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.nametofont, fontname) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py new file mode 100644 index 00000000000..d71a634a767 --- /dev/null +++ b/Lib/test/test_tkinter/test_geometry_managers.py @@ -0,0 +1,908 @@ +import unittest +import re +import tkinter +from tkinter import TclError +from test.support import requires + +from test.test_tkinter.support import pixels_conv +from test.test_tkinter.widget_tests import AbstractWidgetTest + +requires('gui') + + +EXPECTED_FLOAT_ERRMSG = 'expected floating-point number but got "{}"' +EXPECTED_FLOAT_OR_EMPTY_ERRMSG = 'expected floating-point number (or "" )?but got "{}"' +EXPECTED_SCREEN_DISTANCE_ERRMSG = '(bad|expected) screen distance (but got )?"{}"' +EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG = '(bad|expected) screen distance (or "" but got )?"{}"' + +class PackTest(AbstractWidgetTest, unittest.TestCase): + + test_keys = None + + def create2(self): + pack = tkinter.Toplevel(self.root, name='pack') + pack.wm_geometry('300x200+0+0') + pack.wm_minsize(1, 1) + a = tkinter.Frame(pack, name='a', width=20, height=40, bg='red') + b = tkinter.Frame(pack, name='b', width=50, height=30, bg='blue') + c = tkinter.Frame(pack, name='c', width=80, height=80, bg='green') + d = tkinter.Frame(pack, name='d', width=40, height=30, bg='yellow') + return pack, a, b, c, d + + def test_pack_configure_after(self): + pack, a, b, c, d = self.create2() + with self.assertRaisesRegex(TclError, 'window "%s" isn\'t packed' % b): + a.pack_configure(after=b) + with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'): + a.pack_configure(after='.foo') + a.pack_configure(side='top') + b.pack_configure(side='top') + c.pack_configure(side='top') + d.pack_configure(side='top') + self.assertEqual(pack.pack_slaves(), [a, b, c, d]) + a.pack_configure(after=b) + self.assertEqual(pack.pack_slaves(), [b, a, c, d]) + a.pack_configure(after=a) + self.assertEqual(pack.pack_slaves(), [b, a, c, d]) + + def test_pack_configure_anchor(self): + pack, a, b, c, d = self.create2() + def check(anchor, geom): + a.pack_configure(side='top', ipadx=5, padx=10, ipady=15, pady=20, + expand=True, anchor=anchor) + self.root.update() + self.assertEqual(a.winfo_geometry(), geom) + check('n', '30x70+135+20') + check('ne', '30x70+260+20') + check('e', '30x70+260+65') + check('se', '30x70+260+110') + check('s', '30x70+135+110') + check('sw', '30x70+10+110') + check('w', '30x70+10+65') + check('nw', '30x70+10+20') + check('center', '30x70+135+65') + + def test_pack_configure_before(self): + pack, a, b, c, d = self.create2() + with self.assertRaisesRegex(TclError, 'window "%s" isn\'t packed' % b): + a.pack_configure(before=b) + with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'): + a.pack_configure(before='.foo') + a.pack_configure(side='top') + b.pack_configure(side='top') + c.pack_configure(side='top') + d.pack_configure(side='top') + self.assertEqual(pack.pack_slaves(), [a, b, c, d]) + a.pack_configure(before=d) + self.assertEqual(pack.pack_slaves(), [b, c, a, d]) + a.pack_configure(before=a) + self.assertEqual(pack.pack_slaves(), [b, c, a, d]) + + def test_pack_configure_expand(self): + pack, a, b, c, d = self.create2() + def check(*geoms): + self.root.update() + self.assertEqual(a.winfo_geometry(), geoms[0]) + self.assertEqual(b.winfo_geometry(), geoms[1]) + self.assertEqual(c.winfo_geometry(), geoms[2]) + self.assertEqual(d.winfo_geometry(), geoms[3]) + a.pack_configure(side='left') + b.pack_configure(side='top') + c.pack_configure(side='right') + d.pack_configure(side='bottom') + check('20x40+0+80', '50x30+135+0', '80x80+220+75', '40x30+100+170') + a.pack_configure(side='left', expand='yes') + b.pack_configure(side='top', expand='on') + c.pack_configure(side='right', expand=True) + d.pack_configure(side='bottom', expand=1) + check('20x40+40+80', '50x30+175+35', '80x80+180+110', '40x30+100+135') + a.pack_configure(side='left', expand='yes', fill='both') + b.pack_configure(side='top', expand='on', fill='both') + c.pack_configure(side='right', expand=True, fill='both') + d.pack_configure(side='bottom', expand=1, fill='both') + check('100x200+0+0', '200x100+100+0', '160x100+140+100', '40x100+100+100') + + def test_pack_configure_in(self): + pack, a, b, c, d = self.create2() + a.pack_configure(side='top') + b.pack_configure(side='top') + c.pack_configure(side='top') + d.pack_configure(side='top') + a.pack_configure(in_=pack) + self.assertEqual(pack.pack_slaves(), [b, c, d, a]) + a.pack_configure(in_=c) + self.assertEqual(pack.pack_slaves(), [b, c, d]) + self.assertEqual(c.pack_slaves(), [a]) + with self.assertRaisesRegex( + TclError, """can't pack "?%s"? inside itself""" % (a,)): + a.pack_configure(in_=a) + with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'): + a.pack_configure(in_='.foo') + + def test_pack_configure_padx_ipadx_fill(self): + pack, a, b, c, d = self.create2() + def check(geom1, geom2, **kwargs): + a.pack_forget() + b.pack_forget() + a.pack_configure(**kwargs) + b.pack_configure(expand=True, fill='both') + self.root.update() + self.assertEqual(a.winfo_geometry(), geom1) + self.assertEqual(b.winfo_geometry(), geom2) + check('20x40+260+80', '240x200+0+0', side='right', padx=20) + check('20x40+250+80', '240x200+0+0', side='right', padx=(10, 30)) + check('60x40+240+80', '240x200+0+0', side='right', ipadx=20) + check('30x40+260+80', '250x200+0+0', side='right', ipadx=5, padx=10) + check('20x40+260+80', '240x200+0+0', side='right', padx=20, fill='x') + check('20x40+249+80', '240x200+0+0', + side='right', padx=(9, 31), fill='x') + check('60x40+240+80', '240x200+0+0', side='right', ipadx=20, fill='x') + check('30x40+260+80', '250x200+0+0', + side='right', ipadx=5, padx=10, fill='x') + check('30x40+255+80', '250x200+0+0', + side='right', ipadx=5, padx=(5, 15), fill='x') + check('20x40+140+0', '300x160+0+40', side='top', padx=20) + check('20x40+120+0', '300x160+0+40', side='top', padx=(0, 40)) + check('60x40+120+0', '300x160+0+40', side='top', ipadx=20) + check('30x40+135+0', '300x160+0+40', side='top', ipadx=5, padx=10) + check('30x40+130+0', '300x160+0+40', side='top', ipadx=5, padx=(5, 15)) + check('260x40+20+0', '300x160+0+40', side='top', padx=20, fill='x') + check('260x40+25+0', '300x160+0+40', + side='top', padx=(25, 15), fill='x') + check('300x40+0+0', '300x160+0+40', side='top', ipadx=20, fill='x') + check('280x40+10+0', '300x160+0+40', + side='top', ipadx=5, padx=10, fill='x') + check('280x40+5+0', '300x160+0+40', + side='top', ipadx=5, padx=(5, 15), fill='x') + a.pack_configure(padx='1c') + self.assertEqual(a.pack_info()['padx'], + self._str(pack.winfo_pixels('1c'))) + a.pack_configure(ipadx='1c') + self.assertEqual(a.pack_info()['ipadx'], + self._str(pack.winfo_pixels('1c'))) + + def test_pack_configure_pady_ipady_fill(self): + pack, a, b, c, d = self.create2() + def check(geom1, geom2, **kwargs): + a.pack_forget() + b.pack_forget() + a.pack_configure(**kwargs) + b.pack_configure(expand=True, fill='both') + self.root.update() + self.assertEqual(a.winfo_geometry(), geom1) + self.assertEqual(b.winfo_geometry(), geom2) + check('20x40+280+80', '280x200+0+0', side='right', pady=20) + check('20x40+280+70', '280x200+0+0', side='right', pady=(10, 30)) + check('20x80+280+60', '280x200+0+0', side='right', ipady=20) + check('20x50+280+75', '280x200+0+0', side='right', ipady=5, pady=10) + check('20x40+280+80', '280x200+0+0', side='right', pady=20, fill='x') + check('20x40+280+69', '280x200+0+0', + side='right', pady=(9, 31), fill='x') + check('20x80+280+60', '280x200+0+0', side='right', ipady=20, fill='x') + check('20x50+280+75', '280x200+0+0', + side='right', ipady=5, pady=10, fill='x') + check('20x50+280+70', '280x200+0+0', + side='right', ipady=5, pady=(5, 15), fill='x') + check('20x40+140+20', '300x120+0+80', side='top', pady=20) + check('20x40+140+0', '300x120+0+80', side='top', pady=(0, 40)) + check('20x80+140+0', '300x120+0+80', side='top', ipady=20) + check('20x50+140+10', '300x130+0+70', side='top', ipady=5, pady=10) + check('20x50+140+5', '300x130+0+70', side='top', ipady=5, pady=(5, 15)) + check('300x40+0+20', '300x120+0+80', side='top', pady=20, fill='x') + check('300x40+0+25', '300x120+0+80', + side='top', pady=(25, 15), fill='x') + check('300x80+0+0', '300x120+0+80', side='top', ipady=20, fill='x') + check('300x50+0+10', '300x130+0+70', + side='top', ipady=5, pady=10, fill='x') + check('300x50+0+5', '300x130+0+70', + side='top', ipady=5, pady=(5, 15), fill='x') + a.pack_configure(pady='1c') + self.assertEqual(a.pack_info()['pady'], + self._str(pack.winfo_pixels('1c'))) + a.pack_configure(ipady='1c') + self.assertEqual(a.pack_info()['ipady'], + self._str(pack.winfo_pixels('1c'))) + + def test_pack_configure_side(self): + pack, a, b, c, d = self.create2() + def check(side, geom1, geom2): + a.pack_configure(side=side) + self.assertEqual(a.pack_info()['side'], side) + b.pack_configure(expand=True, fill='both') + self.root.update() + self.assertEqual(a.winfo_geometry(), geom1) + self.assertEqual(b.winfo_geometry(), geom2) + check('top', '20x40+140+0', '300x160+0+40') + check('bottom', '20x40+140+160', '300x160+0+0') + check('left', '20x40+0+80', '280x200+20+0') + check('right', '20x40+280+80', '280x200+0+0') + + def test_pack_forget(self): + pack, a, b, c, d = self.create2() + a.pack_configure() + b.pack_configure() + c.pack_configure() + self.assertEqual(pack.pack_slaves(), [a, b, c]) + b.pack_forget() + self.assertEqual(pack.pack_slaves(), [a, c]) + b.pack_forget() + self.assertEqual(pack.pack_slaves(), [a, c]) + d.pack_forget() + + def test_pack_info(self): + pack, a, b, c, d = self.create2() + with self.assertRaisesRegex(TclError, 'window "%s" isn\'t packed' % a): + a.pack_info() + a.pack_configure() + b.pack_configure(side='right', in_=a, anchor='s', expand=True, fill='x', + ipadx=5, padx=10, ipady=2, pady=(5, 15)) + info = a.pack_info() + self.assertIsInstance(info, dict) + self.assertEqual(info['anchor'], 'center') + self.assertEqual(info['expand'], self._str(0)) + self.assertEqual(info['fill'], 'none') + self.assertEqual(info['in'], pack) + self.assertEqual(info['ipadx'], self._str(0)) + self.assertEqual(info['ipady'], self._str(0)) + self.assertEqual(info['padx'], self._str(0)) + self.assertEqual(info['pady'], self._str(0)) + self.assertEqual(info['side'], 'top') + info = b.pack_info() + self.assertIsInstance(info, dict) + self.assertEqual(info['anchor'], 's') + self.assertEqual(info['expand'], self._str(1)) + self.assertEqual(info['fill'], 'x') + self.assertEqual(info['in'], a) + self.assertEqual(info['ipadx'], self._str(5)) + self.assertEqual(info['ipady'], self._str(2)) + self.assertEqual(info['padx'], self._str(10)) + self.assertEqual(info['pady'], self._str((5, 15))) + self.assertEqual(info['side'], 'right') + + def test_pack_propagate(self): + pack, a, b, c, d = self.create2() + pack.configure(width=300, height=200) + a.pack_configure() + pack.pack_propagate(False) + self.root.update() + self.assertEqual(pack.winfo_reqwidth(), 300) + self.assertEqual(pack.winfo_reqheight(), 200) + pack.pack_propagate(True) + self.root.update() + self.assertEqual(pack.winfo_reqwidth(), 20) + self.assertEqual(pack.winfo_reqheight(), 40) + + def test_pack_slaves(self): + pack, a, b, c, d = self.create2() + self.assertEqual(pack.pack_slaves(), []) + a.pack_configure() + self.assertEqual(pack.pack_slaves(), [a]) + b.pack_configure() + self.assertEqual(pack.pack_slaves(), [a, b]) + + +class PlaceTest(AbstractWidgetTest, unittest.TestCase): + + test_keys = None + + def create2(self): + t = tkinter.Toplevel(self.root, width=300, height=200, bd=0) + t.wm_geometry('300x200+0+0') + f = tkinter.Frame(t, width=154, height=84, bd=2, relief='raised') + f.place_configure(x=48, y=38) + f2 = tkinter.Frame(t, width=30, height=60, bd=2, relief='raised') + self.root.update() + return t, f, f2 + + def test_place_configure_in(self): + t, f, f2 = self.create2() + self.assertEqual(f2.winfo_manager(), '') + with self.assertRaisesRegex( + TclError, + """can't place "?%s"? relative to itself""" + % re.escape(str(f2))): + f2.place_configure(in_=f2) + self.assertEqual(f2.winfo_manager(), '') + with self.assertRaisesRegex(TclError, 'bad window path name'): + f2.place_configure(in_='spam') + f2.place_configure(in_=f) + self.assertEqual(f2.winfo_manager(), 'place') + + def test_place_configure_x(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f) + self.assertEqual(f2.place_info()['x'], '0') + self.root.update() + self.assertEqual(f2.winfo_x(), 50) + f2.place_configure(x=100) + self.assertEqual(f2.place_info()['x'], '100') + self.root.update() + self.assertEqual(f2.winfo_x(), 150) + f2.place_configure(x=-10, relx=1) + self.assertEqual(f2.place_info()['x'], '-10') + self.root.update() + self.assertEqual(f2.winfo_x(), 190) + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam')): + f2.place_configure(in_=f, x='spam') + + def test_place_configure_y(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f) + self.assertEqual(f2.place_info()['y'], '0') + self.root.update() + self.assertEqual(f2.winfo_y(), 40) + f2.place_configure(y=50) + self.assertEqual(f2.place_info()['y'], '50') + self.root.update() + self.assertEqual(f2.winfo_y(), 90) + f2.place_configure(y=-10, rely=1) + self.assertEqual(f2.place_info()['y'], '-10') + self.root.update() + self.assertEqual(f2.winfo_y(), 110) + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam')): + f2.place_configure(in_=f, y='spam') + + def test_place_configure_relx(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f) + self.assertEqual(f2.place_info()['relx'], '0') + self.root.update() + self.assertEqual(f2.winfo_x(), 50) + f2.place_configure(relx=0.5) + self.assertEqual(f2.place_info()['relx'], '0.5') + self.root.update() + self.assertEqual(f2.winfo_x(), 125) + f2.place_configure(relx=1) + self.assertEqual(f2.place_info()['relx'], '1') + self.root.update() + self.assertEqual(f2.winfo_x(), 200) + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_ERRMSG.format('spam')): + f2.place_configure(in_=f, relx='spam') + + def test_place_configure_rely(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f) + self.assertEqual(f2.place_info()['rely'], '0') + self.root.update() + self.assertEqual(f2.winfo_y(), 40) + f2.place_configure(rely=0.5) + self.assertEqual(f2.place_info()['rely'], '0.5') + self.root.update() + self.assertEqual(f2.winfo_y(), 80) + f2.place_configure(rely=1) + self.assertEqual(f2.place_info()['rely'], '1') + self.root.update() + self.assertEqual(f2.winfo_y(), 120) + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_ERRMSG.format('spam')): + f2.place_configure(in_=f, rely='spam') + + def test_place_configure_anchor(self): + f = tkinter.Frame(self.root) + with self.assertRaisesRegex(TclError, 'bad anchor "j"'): + f.place_configure(anchor='j') + with self.assertRaisesRegex(TclError, 'ambiguous anchor ""'): + f.place_configure(anchor='') + for value in 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center': + f.place_configure(anchor=value) + self.assertEqual(f.place_info()['anchor'], value) + + def test_place_configure_width(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f, width=120) + self.root.update() + self.assertEqual(f2.winfo_width(), 120) + f2.place_configure(width='') + self.root.update() + self.assertEqual(f2.winfo_width(), 30) + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('abcd')): + f2.place_configure(width='abcd') + + def test_place_configure_height(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f, height=120) + self.root.update() + self.assertEqual(f2.winfo_height(), 120) + f2.place_configure(height='') + self.root.update() + self.assertEqual(f2.winfo_height(), 60) + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('abcd')): + f2.place_configure(height='abcd') + + def test_place_configure_relwidth(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f, relwidth=0.5) + self.root.update() + self.assertEqual(f2.winfo_width(), 75) + f2.place_configure(relwidth='') + self.root.update() + self.assertEqual(f2.winfo_width(), 30) + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_OR_EMPTY_ERRMSG.format('abcd')): + f2.place_configure(relwidth='abcd') + + def test_place_configure_relheight(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f, relheight=0.5) + self.root.update() + self.assertEqual(f2.winfo_height(), 40) + f2.place_configure(relheight='') + self.root.update() + self.assertEqual(f2.winfo_height(), 60) + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_OR_EMPTY_ERRMSG.format('abcd')): + f2.place_configure(relheight='abcd') + + def test_place_configure_bordermode(self): + f = tkinter.Frame(self.root) + with self.assertRaisesRegex(TclError, 'bad bordermode "j"'): + f.place_configure(bordermode='j') + with self.assertRaisesRegex(TclError, 'ambiguous bordermode ""'): + f.place_configure(bordermode='') + for value in 'inside', 'outside', 'ignore': + f.place_configure(bordermode=value) + self.assertEqual(f.place_info()['bordermode'], value) + + def test_place_forget(self): + foo = tkinter.Frame(self.root) + foo.place_configure(width=50, height=50) + self.root.update() + foo.place_forget() + self.root.update() + self.assertFalse(foo.winfo_ismapped()) + with self.assertRaises(TypeError): + foo.place_forget(0) + + def test_place_info(self): + t, f, f2 = self.create2() + f2.place_configure(in_=f, x=1, y=2, width=3, height=4, + relx=0.1, rely=0.2, relwidth=0.3, relheight=0.4, + anchor='se', bordermode='outside') + info = f2.place_info() + self.assertIsInstance(info, dict) + self.assertEqual(info['x'], '1') + self.assertEqual(info['y'], '2') + self.assertEqual(info['width'], '3') + self.assertEqual(info['height'], '4') + self.assertEqual(info['relx'], '0.1') + self.assertEqual(info['rely'], '0.2') + self.assertEqual(info['relwidth'], '0.3') + self.assertEqual(info['relheight'], '0.4') + self.assertEqual(info['anchor'], 'se') + self.assertEqual(info['bordermode'], 'outside') + self.assertEqual(info['x'], '1') + self.assertEqual(info['x'], '1') + with self.assertRaises(TypeError): + f2.place_info(0) + + def test_place_slaves(self): + foo = tkinter.Frame(self.root) + bar = tkinter.Frame(self.root) + self.assertEqual(foo.place_slaves(), []) + bar.place_configure(in_=foo) + self.assertEqual(foo.place_slaves(), [bar]) + with self.assertRaises(TypeError): + foo.place_slaves(0) + + +class GridTest(AbstractWidgetTest, unittest.TestCase): + + test_keys = None + + def tearDown(self): + cols, rows = self.root.grid_size() + for i in range(cols + 1): + self.root.grid_columnconfigure(i, weight=0, minsize=0, pad=0, uniform='') + for i in range(rows + 1): + self.root.grid_rowconfigure(i, weight=0, minsize=0, pad=0, uniform='') + self.root.grid_propagate(1) + self.root.grid_anchor('nw') + super().tearDown() + + def test_grid_configure(self): + b = tkinter.Button(self.root) + self.assertEqual(b.grid_info(), {}) + b.grid_configure() + self.assertEqual(b.grid_info()['in'], self.root) + self.assertEqual(b.grid_info()['column'], self._str(0)) + self.assertEqual(b.grid_info()['row'], self._str(0)) + b.grid_configure({'column': 1}, row=2) + self.assertEqual(b.grid_info()['column'], self._str(1)) + self.assertEqual(b.grid_info()['row'], self._str(2)) + + def test_grid_configure_column(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad column value "-1": ' + 'must be a non-negative integer'): + b.grid_configure(column=-1) + b.grid_configure(column=2) + self.assertEqual(b.grid_info()['column'], self._str(2)) + + def test_grid_configure_columnspan(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad columnspan value "0": ' + 'must be a positive integer'): + b.grid_configure(columnspan=0) + b.grid_configure(columnspan=2) + self.assertEqual(b.grid_info()['columnspan'], self._str(2)) + + def test_grid_configure_in(self): + f = tkinter.Frame(self.root) + b = tkinter.Button(self.root) + self.assertEqual(b.grid_info(), {}) + b.grid_configure() + self.assertEqual(b.grid_info()['in'], self.root) + b.grid_configure(in_=f) + self.assertEqual(b.grid_info()['in'], f) + b.grid_configure({'in': self.root}) + self.assertEqual(b.grid_info()['in'], self.root) + + def test_grid_configure_ipadx(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad ipadx value "-1": ' + 'must be positive screen distance'): + b.grid_configure(ipadx=-1) + b.grid_configure(ipadx=1) + self.assertEqual(b.grid_info()['ipadx'], self._str(1)) + b.grid_configure(ipadx='.5c') + self.assertEqual(b.grid_info()['ipadx'], + self._str(round(pixels_conv('.5c') * self.scaling))) + + def test_grid_configure_ipady(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad ipady value "-1": ' + 'must be positive screen distance'): + b.grid_configure(ipady=-1) + b.grid_configure(ipady=1) + self.assertEqual(b.grid_info()['ipady'], self._str(1)) + b.grid_configure(ipady='.5c') + self.assertEqual(b.grid_info()['ipady'], + self._str(round(pixels_conv('.5c') * self.scaling))) + + def test_grid_configure_padx(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad pad value "-1": ' + 'must be positive screen distance'): + b.grid_configure(padx=-1) + b.grid_configure(padx=1) + self.assertEqual(b.grid_info()['padx'], self._str(1)) + b.grid_configure(padx=(10, 5)) + self.assertEqual(b.grid_info()['padx'], self._str((10, 5))) + b.grid_configure(padx='.5c') + self.assertEqual(b.grid_info()['padx'], + self._str(round(pixels_conv('.5c') * self.scaling))) + + def test_grid_configure_pady(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad pad value "-1": ' + 'must be positive screen distance'): + b.grid_configure(pady=-1) + b.grid_configure(pady=1) + self.assertEqual(b.grid_info()['pady'], self._str(1)) + b.grid_configure(pady=(10, 5)) + self.assertEqual(b.grid_info()['pady'], self._str((10, 5))) + b.grid_configure(pady='.5c') + self.assertEqual(b.grid_info()['pady'], + self._str(round(pixels_conv('.5c') * self.scaling))) + + def test_grid_configure_row(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad (row|grid) value "-1": ' + 'must be a non-negative integer'): + b.grid_configure(row=-1) + b.grid_configure(row=2) + self.assertEqual(b.grid_info()['row'], self._str(2)) + + def test_grid_configure_rownspan(self): + b = tkinter.Button(self.root) + with self.assertRaisesRegex(TclError, 'bad rowspan value "0": ' + 'must be a positive integer'): + b.grid_configure(rowspan=0) + b.grid_configure(rowspan=2) + self.assertEqual(b.grid_info()['rowspan'], self._str(2)) + + def test_grid_configure_sticky(self): + f = tkinter.Frame(self.root, bg='red') + with self.assertRaisesRegex(TclError, 'bad stickyness value "glue"'): + f.grid_configure(sticky='glue') + f.grid_configure(sticky='ne') + self.assertEqual(f.grid_info()['sticky'], 'ne') + f.grid_configure(sticky='n,s,e,w') + self.assertEqual(f.grid_info()['sticky'], 'nesw') + + def test_grid_columnconfigure(self): + with self.assertRaises(TypeError): + self.root.grid_columnconfigure() + self.assertEqual(self.root.grid_columnconfigure(0), + {'minsize': 0, 'pad': 0, 'uniform': None, 'weight': 0}) + with self.assertRaisesRegex(TclError, 'bad option "-foo"'): + self.root.grid_columnconfigure(0, 'foo') + self.root.grid_columnconfigure((0, 3), weight=2) + with self.assertRaisesRegex(TclError, + 'must specify a single element on retrieval'): + self.root.grid_columnconfigure((0, 3)) + b = tkinter.Button(self.root) + b.grid_configure(column=0, row=0) + self.root.grid_columnconfigure('all', weight=3) + with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): + self.root.grid_columnconfigure('all') + self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3) + self.assertEqual(self.root.grid_columnconfigure(3, 'weight'), 2) + self.assertEqual(self.root.grid_columnconfigure(265, 'weight'), 0) + self.root.grid_columnconfigure(b, weight=4) + self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4) + + def test_grid_columnconfigure_minsize(self): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): + self.root.grid_columnconfigure(0, minsize='foo') + self.root.grid_columnconfigure(0, minsize=10) + self.assertEqual(self.root.grid_columnconfigure(0, 'minsize'), 10) + self.assertEqual(self.root.grid_columnconfigure(0)['minsize'], 10) + + def test_grid_columnconfigure_weight(self): + with self.assertRaisesRegex(TclError, 'expected integer but got "bad"'): + self.root.grid_columnconfigure(0, weight='bad') + with self.assertRaisesRegex(TclError, 'invalid arg "-weight": ' + 'should be non-negative'): + self.root.grid_columnconfigure(0, weight=-3) + self.root.grid_columnconfigure(0, weight=3) + self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3) + self.assertEqual(self.root.grid_columnconfigure(0)['weight'], 3) + + def test_grid_columnconfigure_pad(self): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): + self.root.grid_columnconfigure(0, pad='foo') + with self.assertRaisesRegex(TclError, 'invalid arg "-pad": ' + 'should be non-negative'): + self.root.grid_columnconfigure(0, pad=-3) + self.root.grid_columnconfigure(0, pad=3) + self.assertEqual(self.root.grid_columnconfigure(0, 'pad'), 3) + self.assertEqual(self.root.grid_columnconfigure(0)['pad'], 3) + + def test_grid_columnconfigure_uniform(self): + self.root.grid_columnconfigure(0, uniform='foo') + self.assertEqual(self.root.grid_columnconfigure(0, 'uniform'), 'foo') + self.assertEqual(self.root.grid_columnconfigure(0)['uniform'], 'foo') + + def test_grid_rowconfigure(self): + with self.assertRaises(TypeError): + self.root.grid_rowconfigure() + self.assertEqual(self.root.grid_rowconfigure(0), + {'minsize': 0, 'pad': 0, 'uniform': None, 'weight': 0}) + with self.assertRaisesRegex(TclError, 'bad option "-foo"'): + self.root.grid_rowconfigure(0, 'foo') + self.root.grid_rowconfigure((0, 3), weight=2) + with self.assertRaisesRegex(TclError, + 'must specify a single element on retrieval'): + self.root.grid_rowconfigure((0, 3)) + b = tkinter.Button(self.root) + b.grid_configure(column=0, row=0) + self.root.grid_rowconfigure('all', weight=3) + with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): + self.root.grid_rowconfigure('all') + self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3) + self.assertEqual(self.root.grid_rowconfigure(3, 'weight'), 2) + self.assertEqual(self.root.grid_rowconfigure(265, 'weight'), 0) + self.root.grid_rowconfigure(b, weight=4) + self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4) + + def test_grid_rowconfigure_minsize(self): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): + self.root.grid_rowconfigure(0, minsize='foo') + self.root.grid_rowconfigure(0, minsize=10) + self.assertEqual(self.root.grid_rowconfigure(0, 'minsize'), 10) + self.assertEqual(self.root.grid_rowconfigure(0)['minsize'], 10) + + def test_grid_rowconfigure_weight(self): + with self.assertRaisesRegex(TclError, 'expected integer but got "bad"'): + self.root.grid_rowconfigure(0, weight='bad') + with self.assertRaisesRegex(TclError, 'invalid arg "-weight": ' + 'should be non-negative'): + self.root.grid_rowconfigure(0, weight=-3) + self.root.grid_rowconfigure(0, weight=3) + self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3) + self.assertEqual(self.root.grid_rowconfigure(0)['weight'], 3) + + def test_grid_rowconfigure_pad(self): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): + self.root.grid_rowconfigure(0, pad='foo') + with self.assertRaisesRegex(TclError, 'invalid arg "-pad": ' + 'should be non-negative'): + self.root.grid_rowconfigure(0, pad=-3) + self.root.grid_rowconfigure(0, pad=3) + self.assertEqual(self.root.grid_rowconfigure(0, 'pad'), 3) + self.assertEqual(self.root.grid_rowconfigure(0)['pad'], 3) + + def test_grid_rowconfigure_uniform(self): + self.root.grid_rowconfigure(0, uniform='foo') + self.assertEqual(self.root.grid_rowconfigure(0, 'uniform'), 'foo') + self.assertEqual(self.root.grid_rowconfigure(0)['uniform'], 'foo') + + def test_grid_forget(self): + b = tkinter.Button(self.root) + c = tkinter.Button(self.root) + b.grid_configure(row=2, column=2, rowspan=2, columnspan=2, + padx=3, pady=4, sticky='ns') + self.assertEqual(self.root.grid_slaves(), [b]) + b.grid_forget() + c.grid_forget() + self.assertEqual(self.root.grid_slaves(), []) + self.assertEqual(b.grid_info(), {}) + b.grid_configure(row=0, column=0) + info = b.grid_info() + self.assertEqual(info['row'], self._str(0)) + self.assertEqual(info['column'], self._str(0)) + self.assertEqual(info['rowspan'], self._str(1)) + self.assertEqual(info['columnspan'], self._str(1)) + self.assertEqual(info['padx'], self._str(0)) + self.assertEqual(info['pady'], self._str(0)) + self.assertEqual(info['sticky'], '') + + def test_grid_remove(self): + b = tkinter.Button(self.root) + c = tkinter.Button(self.root) + b.grid_configure(row=2, column=2, rowspan=2, columnspan=2, + padx=3, pady=4, sticky='ns') + self.assertEqual(self.root.grid_slaves(), [b]) + b.grid_remove() + c.grid_remove() + self.assertEqual(self.root.grid_slaves(), []) + self.assertEqual(b.grid_info(), {}) + b.grid_configure(row=0, column=0) + info = b.grid_info() + self.assertEqual(info['row'], self._str(0)) + self.assertEqual(info['column'], self._str(0)) + self.assertEqual(info['rowspan'], self._str(2)) + self.assertEqual(info['columnspan'], self._str(2)) + self.assertEqual(info['padx'], self._str(3)) + self.assertEqual(info['pady'], self._str(4)) + self.assertEqual(info['sticky'], 'ns') + + def test_grid_info(self): + b = tkinter.Button(self.root) + self.assertEqual(b.grid_info(), {}) + b.grid_configure(row=2, column=2, rowspan=2, columnspan=2, + padx=3, pady=4, sticky='ns') + info = b.grid_info() + self.assertIsInstance(info, dict) + self.assertEqual(info['in'], self.root) + self.assertEqual(info['row'], self._str(2)) + self.assertEqual(info['column'], self._str(2)) + self.assertEqual(info['rowspan'], self._str(2)) + self.assertEqual(info['columnspan'], self._str(2)) + self.assertEqual(info['padx'], self._str(3)) + self.assertEqual(info['pady'], self._str(4)) + self.assertEqual(info['sticky'], 'ns') + + def test_grid_anchor(self): + with self.assertRaisesRegex(TclError, 'bad anchor "x"'): + self.root.grid_anchor('x') + with self.assertRaisesRegex(TclError, 'ambiguous anchor ""'): + self.root.grid_anchor('') + with self.assertRaises(TypeError): + self.root.grid_anchor('se', 'nw') + self.root.grid_anchor('se') + self.assertEqual(self.root.tk.call('grid', 'anchor', self.root), 'se') + + def test_grid_bbox(self): + self.assertEqual(self.root.grid_bbox(), (0, 0, 0, 0)) + self.assertEqual(self.root.grid_bbox(0, 0), (0, 0, 0, 0)) + self.assertEqual(self.root.grid_bbox(0, 0, 1, 1), (0, 0, 0, 0)) + with self.assertRaisesRegex(TclError, 'expected integer but got "x"'): + self.root.grid_bbox('x', 0) + with self.assertRaisesRegex(TclError, 'expected integer but got "x"'): + self.root.grid_bbox(0, 'x') + with self.assertRaisesRegex(TclError, 'expected integer but got "x"'): + self.root.grid_bbox(0, 0, 'x', 0) + with self.assertRaisesRegex(TclError, 'expected integer but got "x"'): + self.root.grid_bbox(0, 0, 0, 'x') + with self.assertRaises(TypeError): + self.root.grid_bbox(0, 0, 0, 0, 0) + t = self.root + # de-maximize + t.wm_geometry('1x1+0+0') + t.wm_geometry('') + f1 = tkinter.Frame(t, width=75, height=75, bg='red') + f2 = tkinter.Frame(t, width=90, height=90, bg='blue') + f1.grid_configure(row=0, column=0) + f2.grid_configure(row=1, column=1) + self.root.update() + self.assertEqual(t.grid_bbox(), (0, 0, 165, 165)) + self.assertEqual(t.grid_bbox(0, 0), (0, 0, 75, 75)) + self.assertEqual(t.grid_bbox(0, 0, 1, 1), (0, 0, 165, 165)) + self.assertEqual(t.grid_bbox(1, 1), (75, 75, 90, 90)) + self.assertEqual(t.grid_bbox(10, 10, 0, 0), (0, 0, 165, 165)) + self.assertEqual(t.grid_bbox(-2, -2, -1, -1), (0, 0, 0, 0)) + self.assertEqual(t.grid_bbox(10, 10, 12, 12), (165, 165, 0, 0)) + + def test_grid_location(self): + with self.assertRaises(TypeError): + self.root.grid_location() + with self.assertRaises(TypeError): + self.root.grid_location(0) + with self.assertRaises(TypeError): + self.root.grid_location(0, 0, 0) + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('x')): + self.root.grid_location('x', 'y') + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('y')): + self.root.grid_location('1c', 'y') + t = self.root + # de-maximize + t.wm_geometry('1x1+0+0') + t.wm_geometry('') + f = tkinter.Frame(t, width=200, height=100, + highlightthickness=0, bg='red') + self.assertEqual(f.grid_location(10, 10), (-1, -1)) + f.grid_configure() + self.root.update() + self.assertEqual(t.grid_location(-10, -10), (-1, -1)) + self.assertEqual(t.grid_location(-10, 0), (-1, 0)) + self.assertEqual(t.grid_location(-1, 0), (-1, 0)) + self.assertEqual(t.grid_location(0, -10), (0, -1)) + self.assertEqual(t.grid_location(0, -1), (0, -1)) + self.assertEqual(t.grid_location(0, 0), (0, 0)) + self.assertEqual(t.grid_location(200, 0), (0, 0)) + self.assertEqual(t.grid_location(201, 0), (1, 0)) + self.assertEqual(t.grid_location(0, 100), (0, 0)) + self.assertEqual(t.grid_location(0, 101), (0, 1)) + self.assertEqual(t.grid_location(201, 101), (1, 1)) + + def test_grid_propagate(self): + self.assertEqual(self.root.grid_propagate(), True) + with self.assertRaises(TypeError): + self.root.grid_propagate(False, False) + self.root.grid_propagate(False) + self.assertFalse(self.root.grid_propagate()) + f = tkinter.Frame(self.root, width=100, height=100, bg='red') + f.grid_configure(row=0, column=0) + self.root.update() + self.assertEqual(f.winfo_width(), 100) + self.assertEqual(f.winfo_height(), 100) + f.grid_propagate(False) + g = tkinter.Frame(self.root, width=75, height=85, bg='green') + g.grid_configure(in_=f, row=0, column=0) + self.root.update() + self.assertEqual(f.winfo_width(), 100) + self.assertEqual(f.winfo_height(), 100) + f.grid_propagate(True) + self.root.update() + self.assertEqual(f.winfo_width(), 75) + self.assertEqual(f.winfo_height(), 85) + + def test_grid_size(self): + with self.assertRaises(TypeError): + self.root.grid_size(0) + self.assertEqual(self.root.grid_size(), (0, 0)) + f = tkinter.Scale(self.root) + f.grid_configure(row=0, column=0) + self.assertEqual(self.root.grid_size(), (1, 1)) + f.grid_configure(row=4, column=5) + self.assertEqual(self.root.grid_size(), (6, 5)) + + def test_grid_slaves(self): + self.assertEqual(self.root.grid_slaves(), []) + a = tkinter.Label(self.root) + a.grid_configure(row=0, column=1) + b = tkinter.Label(self.root) + b.grid_configure(row=1, column=0) + c = tkinter.Label(self.root) + c.grid_configure(row=1, column=1) + d = tkinter.Label(self.root) + d.grid_configure(row=1, column=1) + self.assertEqual(self.root.grid_slaves(), [d, c, b, a]) + self.assertEqual(self.root.grid_slaves(row=0), [a]) + self.assertEqual(self.root.grid_slaves(row=1), [d, c, b]) + self.assertEqual(self.root.grid_slaves(column=0), [b]) + self.assertEqual(self.root.grid_slaves(column=1), [d, c, a]) + self.assertEqual(self.root.grid_slaves(row=1, column=1), [d, c]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_tkinter/test_images.py b/Lib/test/test_tkinter/test_images.py new file mode 100644 index 00000000000..38371fe00d6 --- /dev/null +++ b/Lib/test/test_tkinter/test_images.py @@ -0,0 +1,653 @@ +import unittest +import tkinter +from test import support +from test.support import os_helper +from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest, requires_tk + +support.requires('gui') + + +class MiscTest(AbstractTkTest, unittest.TestCase): + + def test_image_types(self): + image_types = self.root.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + + def test_image_names(self): + image_names = self.root.image_names() + self.assertIsInstance(image_names, tuple) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_image_types(self): + self.assertRaises(RuntimeError, tkinter.image_types) + root = tkinter.Tk() + image_types = tkinter.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.image_types) + + def test_image_names(self): + self.assertRaises(RuntimeError, tkinter.image_names) + root = tkinter.Tk() + image_names = tkinter.image_names() + self.assertIsInstance(image_names, tuple) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.image_names) + + def test_image_create_bitmap(self): + self.assertRaises(RuntimeError, tkinter.BitmapImage) + root = tkinter.Tk() + image = tkinter.BitmapImage() + self.assertIn(image.name, tkinter.image_names()) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.BitmapImage) + + def test_image_create_photo(self): + self.assertRaises(RuntimeError, tkinter.PhotoImage) + root = tkinter.Tk() + image = tkinter.PhotoImage() + self.assertIn(image.name, tkinter.image_names()) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.PhotoImage) + + +class BitmapImageTest(AbstractTkTest, unittest.TestCase): + + @classmethod + def setUpClass(cls): + AbstractTkTest.setUpClass.__func__(cls) + cls.testfile = support.findfile('python.xbm', subdir='tkinterdata') + + def test_create_from_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + file=self.testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + support.gc_collect() # For PyPy or other GCs. + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_data(self): + with open(self.testfile, 'rb') as f: + data = f.read() + image = tkinter.BitmapImage('::img::test', master=self.root, + foreground='yellow', background='blue', + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'bitmap') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertIn('::img::test', self.root.image_names()) + del image + support.gc_collect() # For PyPy or other GCs. + self.assertNotIn('::img::test', self.root.image_names()) + + def assertEqualStrList(self, actual, expected): + self.assertIsInstance(actual, str) + self.assertEqual(self.root.splitlist(actual), expected) + + def test_configure_data(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['data'], '-data {} {} {} {}') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqualStrList(image['data'], + ('-data', '', '', '', data.decode('ascii'))) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskdata'], '-maskdata {} {} {} {}') + image.configure(maskdata=data) + self.assertEqualStrList(image['maskdata'], + ('-maskdata', '', '', '', data.decode('ascii'))) + + def test_configure_file(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['file'], '-file {} {} {} {}') + image.configure(file=self.testfile) + self.assertEqualStrList(image['file'], + ('-file', '', '', '',self.testfile)) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + self.assertEqual(image['maskfile'], '-maskfile {} {} {} {}') + image.configure(maskfile=self.testfile) + self.assertEqualStrList(image['maskfile'], + ('-maskfile', '', '', '', self.testfile)) + + def test_configure_background(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['background'], '-background {} {} {} {}') + image.configure(background='blue') + self.assertEqual(image['background'], '-background {} {} {} blue') + + def test_configure_foreground(self): + image = tkinter.BitmapImage('::img::test', master=self.root) + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 #000000') + image.configure(foreground='yellow') + self.assertEqual(image['foreground'], + '-foreground {} {} #000000 yellow') + + def test_bug_100814(self): + # gh-100814: Passing a callable option value causes AttributeError. + with self.assertRaises(tkinter.TclError): + tkinter.BitmapImage('::img::test', master=self.root, spam=print) + image = tkinter.BitmapImage('::img::test', master=self.root) + with self.assertRaises(tkinter.TclError): + image.configure(spam=print) + + +class PhotoImageTest(AbstractTkTest, unittest.TestCase): + + @classmethod + def setUpClass(cls): + AbstractTkTest.setUpClass.__func__(cls) + cls.testfile = support.findfile('python.gif', subdir='tkinterdata') + + def create(self): + return tkinter.PhotoImage('::img::test', master=self.root, + file=self.testfile) + + def colorlist(self, *args): + if tkinter.TkVersion >= 8.6 and self.wantobjects: + return args + else: + return tkinter._join(args) + + def check_create_from_file(self, ext): + testfile = support.findfile('python.' + ext, subdir='tkinterdata') + image = tkinter.PhotoImage('::img::test', master=self.root, + file=testfile) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], '') + self.assertEqual(image['file'], testfile) + self.assertIn('::img::test', self.root.image_names()) + del image + support.gc_collect() # For PyPy or other GCs. + self.assertNotIn('::img::test', self.root.image_names()) + + def check_create_from_data(self, ext): + testfile = support.findfile('python.' + ext, subdir='tkinterdata') + with open(testfile, 'rb') as f: + data = f.read() + image = tkinter.PhotoImage('::img::test', master=self.root, + data=data) + self.assertEqual(str(image), '::img::test') + self.assertEqual(image.type(), 'photo') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image['data'], data if self.wantobjects + else data.decode('latin1')) + self.assertEqual(image['file'], '') + self.assertIn('::img::test', self.root.image_names()) + del image + support.gc_collect() # For PyPy or other GCs. + self.assertNotIn('::img::test', self.root.image_names()) + + def test_create_from_ppm_file(self): + self.check_create_from_file('ppm') + + def test_create_from_ppm_data(self): + self.check_create_from_data('ppm') + + def test_create_from_pgm_file(self): + self.check_create_from_file('pgm') + + def test_create_from_pgm_data(self): + self.check_create_from_data('pgm') + + def test_create_from_gif_file(self): + self.check_create_from_file('gif') + + def test_create_from_gif_data(self): + self.check_create_from_data('gif') + + @requires_tk(8, 6) + def test_create_from_png_file(self): + self.check_create_from_file('png') + + @requires_tk(8, 6) + def test_create_from_png_data(self): + self.check_create_from_data('png') + + def test_configure_data(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['data'], '') + with open(self.testfile, 'rb') as f: + data = f.read() + image.configure(data=data) + self.assertEqual(image['data'], data if self.wantobjects + else data.decode('latin1')) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_format(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['format'], '') + image.configure(file=self.testfile, format='gif') + self.assertEqual(image['format'], ('gif',) if self.wantobjects + else 'gif') + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_file(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['file'], '') + image.configure(file=self.testfile) + self.assertEqual(image['file'], self.testfile) + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + + def test_configure_gamma(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['gamma'], '1.0') + image.configure(gamma=2.0) + self.assertEqual(image['gamma'], '2.0') + + def test_configure_width_height(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['width'], '0') + self.assertEqual(image['height'], '0') + image.configure(width=20) + image.configure(height=10) + self.assertEqual(image['width'], '20') + self.assertEqual(image['height'], '10') + self.assertEqual(image.width(), 20) + self.assertEqual(image.height(), 10) + + def test_configure_palette(self): + image = tkinter.PhotoImage('::img::test', master=self.root) + self.assertEqual(image['palette'], '') + image.configure(palette=256) + self.assertEqual(image['palette'], '256') + image.configure(palette='3/4/2') + self.assertEqual(image['palette'], '3/4/2') + + def test_bug_100814(self): + # gh-100814: Passing a callable option value causes AttributeError. + with self.assertRaises(tkinter.TclError): + tkinter.PhotoImage('::img::test', master=self.root, spam=print) + image = tkinter.PhotoImage('::img::test', master=self.root) + with self.assertRaises(tkinter.TclError): + image.configure(spam=print) + + def test_blank(self): + image = self.create() + image.blank() + self.assertEqual(image.width(), 16) + self.assertEqual(image.height(), 16) + self.assertEqual(image.get(4, 6), self.colorlist(0, 0, 0)) + + def test_copy(self): + image = self.create() + image2 = image.copy() + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(4, 6), image.get(4, 6)) + + image2 = image.copy(from_coords=(2, 3, 14, 11)) + self.assertEqual(image2.width(), 12) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(11, 7), image.get(13, 10)) + self.assertEqual(image2.get(2, 4), image.get(2+2, 4+3)) + + image2 = image.copy(from_coords=(2, 3, 14, 11), zoom=2) + self.assertEqual(image2.width(), 24) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(23, 15), image.get(13, 10)) + self.assertEqual(image2.get(2*2, 4*2), image.get(2+2, 4+3)) + self.assertEqual(image2.get(2*2+1, 4*2+1), image.get(6+2, 2+3)) + + image2 = image.copy(from_coords=(2, 3, 14, 11), subsample=2) + self.assertEqual(image2.width(), 6) + self.assertEqual(image2.height(), 4) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(5, 3), image.get(12, 9)) + self.assertEqual(image2.get(3, 2), image.get(3*2+2, 2*2+3)) + + image2 = image.copy(from_coords=(2, 3, 14, 11), subsample=2, zoom=3) + self.assertEqual(image2.width(), 18) + self.assertEqual(image2.height(), 12) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(17, 11), image.get(12, 9)) + self.assertEqual(image2.get(1*3, 2*3), image.get(1*2+2, 2*2+3)) + self.assertEqual(image2.get(1*3+2, 2*3+2), image.get(1*2+2, 2*2+3)) + + def test_subsample(self): + image = self.create() + image2 = image.subsample(2, 3) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 6) + self.assertEqual(image2.get(2, 2), image.get(4, 6)) + + image2 = image.subsample(2) + self.assertEqual(image2.width(), 8) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(2, 3), image.get(4, 6)) + + image2 = image.subsample(2, from_coords=(2, 3, 14, 11)) + self.assertEqual(image2.width(), 6) + self.assertEqual(image2.height(), 4) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(5, 3), image.get(12, 9)) + self.assertEqual(image2.get(1, 2), image.get(1*2+2, 2*2+3)) + + def test_zoom(self): + image = self.create() + image2 = image.zoom(2, 3) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 48) + self.assertEqual(image2.get(8, 18), image.get(4, 6)) + self.assertEqual(image2.get(9, 20), image.get(4, 6)) + + image2 = image.zoom(2) + self.assertEqual(image2.width(), 32) + self.assertEqual(image2.height(), 32) + self.assertEqual(image2.get(8, 12), image.get(4, 6)) + self.assertEqual(image2.get(9, 13), image.get(4, 6)) + + image2 = image.zoom(2, from_coords=(2, 3, 14, 11)) + self.assertEqual(image2.width(), 24) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(23, 15), image.get(13, 10)) + self.assertEqual(image2.get(2*2, 4*2), image.get(2+2, 4+3)) + self.assertEqual(image2.get(2*2+1, 4*2+1), image.get(6+2, 2+3)) + + def test_copy_replace(self): + image = self.create() + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image) + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(4, 6), image.get(4, 6)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image, from_coords=(2, 3, 14, 11)) + self.assertEqual(image2.width(), 12) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(11, 7), image.get(13, 10)) + self.assertEqual(image2.get(2, 4), image.get(2+2, 4+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image) + image2.copy_replace(image, from_coords=(2, 3, 14, 11), shrink=True) + self.assertEqual(image2.width(), 12) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(11, 7), image.get(13, 10)) + self.assertEqual(image2.get(2, 4), image.get(2+2, 4+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image, from_coords=(2, 3, 14, 11), to=(3, 6)) + self.assertEqual(image2.width(), 15) + self.assertEqual(image2.height(), 14) + self.assertEqual(image2.get(0+3, 0+6), image.get(2, 3)) + self.assertEqual(image2.get(11+3, 7+6), image.get(13, 10)) + self.assertEqual(image2.get(2+3, 4+6), image.get(2+2, 4+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image, from_coords=(2, 3, 14, 11), to=(0, 0, 100, 50)) + self.assertEqual(image2.width(), 100) + self.assertEqual(image2.height(), 50) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(11, 7), image.get(13, 10)) + self.assertEqual(image2.get(2, 4), image.get(2+2, 4+3)) + self.assertEqual(image2.get(2+12, 4+8), image.get(2+2, 4+3)) + self.assertEqual(image2.get(2+12*2, 4), image.get(2+2, 4+3)) + self.assertEqual(image2.get(2, 4+8*3), image.get(2+2, 4+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image, from_coords=(2, 3, 14, 11), zoom=2) + self.assertEqual(image2.width(), 24) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(23, 15), image.get(13, 10)) + self.assertEqual(image2.get(2*2, 4*2), image.get(2+2, 4+3)) + self.assertEqual(image2.get(2*2+1, 4*2+1), image.get(6+2, 2+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image, from_coords=(2, 3, 14, 11), subsample=2) + self.assertEqual(image2.width(), 6) + self.assertEqual(image2.height(), 4) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(5, 3), image.get(12, 9)) + self.assertEqual(image2.get(1, 2), image.get(1*2+2, 2*2+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.copy_replace(image, from_coords=(2, 3, 14, 11), subsample=2, zoom=3) + self.assertEqual(image2.width(), 18) + self.assertEqual(image2.height(), 12) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(17, 11), image.get(12, 9)) + self.assertEqual(image2.get(3*3, 2*3), image.get(3*2+2, 2*2+3)) + self.assertEqual(image2.get(3*3+2, 2*3+2), image.get(3*2+2, 2*2+3)) + self.assertEqual(image2.get(1*3, 2*3), image.get(1*2+2, 2*2+3)) + self.assertEqual(image2.get(1*3+2, 2*3+2), image.get(1*2+2, 2*2+3)) + + def checkImgTrans(self, image, expected): + actual = {(x, y) + for x in range(image.width()) + for y in range(image.height()) + if image.transparency_get(x, y)} + self.assertEqual(actual, expected) + + def test_copy_replace_compositingrule(self): + image1 = tkinter.PhotoImage(master=self.root, width=2, height=2) + image1.blank() + image1.put('black', to=(0, 0, 2, 2)) + image1.transparency_set(0, 0, True) + + # default compositingrule + image2 = tkinter.PhotoImage(master=self.root, width=3, height=3) + image2.blank() + image2.put('white', to=(0, 0, 2, 2)) + image2.copy_replace(image1, to=(1, 1)) + self.checkImgTrans(image2, {(0, 2), (2, 0)}) + + image3 = tkinter.PhotoImage(master=self.root, width=3, height=3) + image3.blank() + image3.put('white', to=(0, 0, 2, 2)) + image3.copy_replace(image1, to=(1, 1), compositingrule='overlay') + self.checkImgTrans(image3, {(0, 2), (2, 0)}) + + image4 = tkinter.PhotoImage(master=self.root, width=3, height=3) + image4.blank() + image4.put('white', to=(0, 0, 2, 2)) + image4.copy_replace(image1, to=(1, 1), compositingrule='set') + self.checkImgTrans(image4, {(0, 2), (1, 1), (2, 0)}) + + def test_put(self): + image = self.create() + image.put('{red green} {blue yellow}', to=(4, 6)) + self.assertEqual(image.get(4, 6), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(5, 6), + self.colorlist(0, 128 if tkinter.TkVersion >= 8.6 + else 255, 0)) + self.assertEqual(image.get(4, 7), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(5, 7), self.colorlist(255, 255, 0)) + + image.put((('#f00', '#00ff00'), ('#000000fff', '#ffffffff0000'))) + self.assertEqual(image.get(0, 0), self.colorlist(255, 0, 0)) + self.assertEqual(image.get(1, 0), self.colorlist(0, 255, 0)) + self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255)) + self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0)) + + def test_get(self): + image = self.create() + self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162)) + self.assertEqual(image.get(0, 0), self.colorlist(0, 0, 0)) + self.assertEqual(image.get(15, 15), self.colorlist(0, 0, 0)) + self.assertRaises(tkinter.TclError, image.get, -1, 0) + self.assertRaises(tkinter.TclError, image.get, 0, -1) + self.assertRaises(tkinter.TclError, image.get, 16, 15) + self.assertRaises(tkinter.TclError, image.get, 15, 16) + + def test_read(self): + # Due to the Tk bug https://core.tcl-lang.org/tk/tktview/1576528 + # the -from option does not work correctly for GIF and PNG files. + # Use the PPM file for this test. + testfile = support.findfile('python.ppm', subdir='tkinterdata') + image = tkinter.PhotoImage(master=self.root, file=testfile) + + image2 = tkinter.PhotoImage(master=self.root) + image2.read(testfile) + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(0, 0)) + self.assertEqual(image2.get(4, 6), image.get(4, 6)) + + self.assertRaises(tkinter.TclError, image2.read, self.testfile, 'ppm') + + image2 = tkinter.PhotoImage(master=self.root) + image2.read(testfile, from_coords=(2, 3, 14, 11)) + self.assertEqual(image2.width(), 12) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(11, 7), image.get(13, 10)) + self.assertEqual(image2.get(2, 4), image.get(2+2, 4+3)) + + image2 = tkinter.PhotoImage(master=self.root, file=testfile) + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + image2.read(testfile, from_coords=(2, 3, 14, 11), shrink=True) + self.assertEqual(image2.width(), 12) + self.assertEqual(image2.height(), 8) + self.assertEqual(image2.get(0, 0), image.get(2, 3)) + self.assertEqual(image2.get(11, 7), image.get(13, 10)) + self.assertEqual(image2.get(2, 4), image.get(2+2, 4+3)) + + image2 = tkinter.PhotoImage(master=self.root) + image2.read(testfile, from_coords=(2, 3, 14, 11), to=(3, 6)) + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 15) + self.assertEqual(image2.height(), 14) + self.assertEqual(image2.get(0+3, 0+6), image.get(2, 3)) + self.assertEqual(image2.get(11+3, 7+6), image.get(13, 10)) + self.assertEqual(image2.get(2+3, 4+6), image.get(2+2, 4+3)) + + def test_write(self): + filename = os_helper.TESTFN + import locale + if locale.getlocale()[0] is None: + # Tcl uses Latin1 in the C locale + filename = os_helper.TESTFN_ASCII + image = self.create() + self.addCleanup(os_helper.unlink, filename) + + image.write(filename) + image2 = tkinter.PhotoImage('::img::test2', master=self.root, + format='ppm', file=filename) + self.assertEqual(str(image2), '::img::test2') + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(0, 0)) + self.assertEqual(image2.get(4, 6), image.get(4, 6)) + + image.write(filename, format='gif', from_coords=(4, 6, 6, 9)) + image3 = tkinter.PhotoImage('::img::test3', master=self.root, + format='gif', file=filename) + self.assertEqual(str(image3), '::img::test3') + self.assertEqual(image3.type(), 'photo') + self.assertEqual(image3.width(), 2) + self.assertEqual(image3.height(), 3) + self.assertEqual(image3.get(0, 0), image.get(4, 6)) + self.assertEqual(image3.get(1, 2), image.get(5, 8)) + + image.write(filename, background='#ff0000') + image4 = tkinter.PhotoImage('::img::test4', master=self.root, + format='ppm', file=filename) + self.assertEqual(image4.get(0, 0), (255, 0, 0) if self.wantobjects else '255 0 0') + self.assertEqual(image4.get(4, 6), image.get(4, 6)) + + image.write(filename, grayscale=True) + image5 = tkinter.PhotoImage('::img::test5', master=self.root, + format='ppm', file=filename) + c = image5.get(4, 6) + if not self.wantobjects: + c = c.split() + self.assertTrue(c[0] == c[1] == c[2], c) + + def test_data(self): + image = self.create() + + data = image.data() + self.assertIsInstance(data, tuple) + for row in data: + self.assertIsInstance(row, str) + c = image.get(4, 6) + if not self.wantobjects: + c = tuple(map(int, c.split())) + self.assertEqual(data[6].split()[4], '#%02x%02x%02x' % c) + + data = image.data('ppm') + image2 = tkinter.PhotoImage('::img::test2', master=self.root, + format='ppm', data=data) + self.assertEqual(str(image2), '::img::test2') + self.assertEqual(image2.type(), 'photo') + self.assertEqual(image2.width(), 16) + self.assertEqual(image2.height(), 16) + self.assertEqual(image2.get(0, 0), image.get(0, 0)) + self.assertEqual(image2.get(4, 6), image.get(4, 6)) + + data = image.data(format='gif', from_coords=(4, 6, 6, 9)) + image3 = tkinter.PhotoImage('::img::test3', master=self.root, + format='gif', data=data) + self.assertEqual(str(image3), '::img::test3') + self.assertEqual(image3.type(), 'photo') + self.assertEqual(image3.width(), 2) + self.assertEqual(image3.height(), 3) + self.assertEqual(image3.get(0, 0), image.get(4, 6)) + self.assertEqual(image3.get(1, 2), image.get(5, 8)) + + data = image.data('ppm', background='#ff0000') + image4 = tkinter.PhotoImage('::img::test4', master=self.root, + format='ppm', data=data) + self.assertEqual(image4.get(0, 0), (255, 0, 0) if self.wantobjects else '255 0 0') + self.assertEqual(image4.get(4, 6), image.get(4, 6)) + + data = image.data('ppm', grayscale=True) + image5 = tkinter.PhotoImage('::img::test5', master=self.root, + format='ppm', data=data) + c = image5.get(4, 6) + if not self.wantobjects: + c = c.split() + self.assertTrue(c[0] == c[1] == c[2], c) + + + def test_transparency(self): + image = self.create() + self.assertEqual(image.transparency_get(0, 0), True) + self.assertEqual(image.transparency_get(4, 6), False) + image.transparency_set(4, 6, True) + self.assertEqual(image.transparency_get(4, 6), True) + image.transparency_set(4, 6, False) + self.assertEqual(image.transparency_get(4, 6), False) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_loadtk.py b/Lib/test/test_tkinter/test_loadtk.py new file mode 100644 index 00000000000..61b0eda2fc7 --- /dev/null +++ b/Lib/test/test_tkinter/test_loadtk.py @@ -0,0 +1,46 @@ +import os +import sys +import unittest +import test.support as test_support +from test.support import os_helper +from tkinter import Tcl, TclError + +test_support.requires('gui') + +class TkLoadTest(unittest.TestCase): + + @unittest.skipIf('DISPLAY' not in os.environ, 'No $DISPLAY set.') + def testLoadTk(self): + tcl = Tcl() + self.assertRaises(TclError,tcl.winfo_geometry) + tcl.loadtk() + self.assertEqual('1x1+0+0', tcl.winfo_geometry()) + tcl.destroy() + + def testLoadTkFailure(self): + old_display = None + if sys.platform.startswith(('win', 'darwin', 'cygwin')): + # no failure possible on windows? + + # XXX Maybe on tk older than 8.4.13 it would be possible, + # see tkinter.h. + return + with os_helper.EnvironmentVarGuard() as env: + if 'DISPLAY' in os.environ: + del env['DISPLAY'] + # on some platforms, deleting environment variables + # doesn't actually carry through to the process level + # because they don't support unsetenv + # If that's the case, abort. + with os.popen('echo $DISPLAY') as pipe: + display = pipe.read().strip() + if display: + return + + tcl = Tcl() + self.assertRaises(TclError, tcl.winfo_geometry) + self.assertRaises(TclError, tcl.loadtk) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_messagebox.py b/Lib/test/test_tkinter/test_messagebox.py new file mode 100644 index 00000000000..f41bdc98286 --- /dev/null +++ b/Lib/test/test_tkinter/test_messagebox.py @@ -0,0 +1,36 @@ +import unittest +import tkinter +from test.support import requires, swap_attr +from test.test_tkinter.support import AbstractDefaultRootTest +from tkinter.commondialog import Dialog +from tkinter.messagebox import showinfo + +requires('gui') + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_showinfo(self): + def test_callback(dialog, master): + nonlocal ismapped + master.update() + ismapped = master.winfo_ismapped() + raise ZeroDivisionError + + with swap_attr(Dialog, '_test_callback', test_callback): + ismapped = None + self.assertRaises(ZeroDivisionError, showinfo, "Spam", "Egg Information") + self.assertEqual(ismapped, False) + + root = tkinter.Tk() + ismapped = None + self.assertRaises(ZeroDivisionError, showinfo, "Spam", "Egg Information") + self.assertEqual(ismapped, True) + root.destroy() + + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, showinfo, "Spam", "Egg Information") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py new file mode 100644 index 00000000000..dbaf970161c --- /dev/null +++ b/Lib/test/test_tkinter/test_misc.py @@ -0,0 +1,1334 @@ +import functools +import unittest +import tkinter +from tkinter import TclError +import enum +from test import support +from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest, + requires_tk, get_tk_patchlevel) + +support.requires('gui') + +class MiscTest(AbstractTkTest, unittest.TestCase): + + def test_all(self): + self.assertIn("Widget", tkinter.__all__) + # Check that variables from tkinter.constants are also in tkinter.__all__ + self.assertIn("CASCADE", tkinter.__all__) + self.assertIsNotNone(tkinter.CASCADE) + # Check that sys, re, and constants are not in tkinter.__all__ + self.assertNotIn("re", tkinter.__all__) + self.assertNotIn("sys", tkinter.__all__) + self.assertNotIn("constants", tkinter.__all__) + # Check that an underscored functions is not in tkinter.__all__ + self.assertNotIn("_tkerror", tkinter.__all__) + # Check that wantobjects is not in tkinter.__all__ + self.assertNotIn("wantobjects", tkinter.__all__) + + def test_repr(self): + t = tkinter.Toplevel(self.root, name='top') + f = tkinter.Frame(t, name='child') + self.assertEqual(repr(f), '') + + def test_generated_names(self): + class Button2(tkinter.Button): + pass + + t = tkinter.Toplevel(self.root) + f = tkinter.Frame(t) + f2 = tkinter.Frame(t) + self.assertNotEqual(str(f), str(f2)) + b = tkinter.Button(f2) + b2 = Button2(f2) + for name in str(b).split('.') + str(b2).split('.'): + self.assertFalse(name.isidentifier(), msg=repr(name)) + b3 = tkinter.Button(f2) + b4 = Button2(f2) + self.assertEqual(len({str(b), str(b2), str(b3), str(b4)}), 4) + + @requires_tk(8, 6, 6) + def test_tk_busy(self): + root = self.root + f = tkinter.Frame(root, name='myframe') + f2 = tkinter.Frame(root) + f.pack() + f2.pack() + b = tkinter.Button(f) + b.pack() + f.tk_busy_hold() + with self.assertRaisesRegex(TclError, 'unknown option "-spam"'): + f.tk_busy_configure(spam='eggs') + with self.assertRaisesRegex(TclError, 'unknown option "-spam"'): + f.tk_busy_cget('spam') + with self.assertRaisesRegex(TclError, 'unknown option "-spam"'): + f.tk_busy_configure('spam') + self.assertIsInstance(f.tk_busy_configure(), dict) + + self.assertTrue(f.tk_busy_status()) + self.assertFalse(root.tk_busy_status()) + self.assertFalse(f2.tk_busy_status()) + self.assertFalse(b.tk_busy_status()) + self.assertIn(f, f.tk_busy_current()) + self.assertIn(f, f.tk_busy_current('*.m?f*me')) + self.assertNotIn(f, f.tk_busy_current('*spam')) + + f.tk_busy_forget() + self.assertFalse(f.tk_busy_status()) + self.assertFalse(f.tk_busy_current()) + with self.assertRaisesRegex(TclError, "can't find busy window"): + f.tk_busy_configure() + with self.assertRaisesRegex(TclError, "can't find busy window"): + f.tk_busy_forget() + + @requires_tk(8, 6, 6) + def test_tk_busy_with_cursor(self): + root = self.root + if root._windowingsystem == 'aqua': + self.skipTest('the cursor option is not supported on OSX/Aqua') + f = tkinter.Frame(root, name='myframe') + f.pack() + f.tk_busy_hold(cursor='gumby') + + self.assertEqual(f.tk_busy_cget('cursor'), 'gumby') + f.tk_busy_configure(cursor='heart') + self.assertEqual(f.tk_busy_cget('cursor'), 'heart') + self.assertEqual(f.tk_busy_configure()['cursor'][4], 'heart') + self.assertEqual(f.tk_busy_configure('cursor')[4], 'heart') + + f.tk_busy_forget() + with self.assertRaisesRegex(TclError, "can't find busy window"): + f.tk_busy_cget('cursor') + + def test_tk_setPalette(self): + root = self.root + root.tk_setPalette('black') + self.assertEqual(root['background'], 'black') + root.tk_setPalette('white') + self.assertEqual(root['background'], 'white') + self.assertRaisesRegex(tkinter.TclError, + '^unknown color name "spam"$', + root.tk_setPalette, 'spam') + + root.tk_setPalette(background='black') + self.assertEqual(root['background'], 'black') + root.tk_setPalette(background='blue', highlightColor='yellow') + self.assertEqual(root['background'], 'blue') + self.assertEqual(root['highlightcolor'], 'yellow') + root.tk_setPalette(background='yellow', highlightColor='blue') + self.assertEqual(root['background'], 'yellow') + self.assertEqual(root['highlightcolor'], 'blue') + self.assertRaisesRegex(tkinter.TclError, + '^unknown color name "spam"$', + root.tk_setPalette, background='spam') + self.assertRaisesRegex(tkinter.TclError, + '^must specify a background color$', + root.tk_setPalette, spam='white') + self.assertRaisesRegex(tkinter.TclError, + '^must specify a background color$', + root.tk_setPalette, highlightColor='blue') + + def test_after(self): + root = self.root + + def callback(start=0, step=1): + nonlocal count + count = start + step + + # Without function, sleeps for ms. + self.assertIsNone(root.after(1)) + + # Set up with callback with no args. + count = 0 + timer1 = root.after(0, callback) + self.assertIn(timer1, root.tk.call('after', 'info')) + (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1)) + root.update() # Process all pending events. + self.assertEqual(count, 1) + with self.assertRaises(tkinter.TclError): + root.tk.call(script) + + # Set up with callback with args. + count = 0 + timer1 = root.after(0, callback, 42, 11) + root.update() # Process all pending events. + self.assertEqual(count, 53) + + # Cancel before called. + timer1 = root.after(1000, callback) + self.assertIn(timer1, root.tk.call('after', 'info')) + (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1)) + root.after_cancel(timer1) # Cancel this event. + self.assertEqual(count, 53) + with self.assertRaises(tkinter.TclError): + root.tk.call(script) + + # Call with a callable class + count = 0 + timer1 = root.after(0, functools.partial(callback, 42, 11)) + root.update() # Process all pending events. + self.assertEqual(count, 53) + + def test_after_idle(self): + root = self.root + + def callback(start=0, step=1): + nonlocal count + count = start + step + + # Set up with callback with no args. + count = 0 + idle1 = root.after_idle(callback) + self.assertIn(idle1, root.tk.call('after', 'info')) + (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) + root.update_idletasks() # Process all pending events. + self.assertEqual(count, 1) + with self.assertRaises(tkinter.TclError): + root.tk.call(script) + + # Set up with callback with args. + count = 0 + idle1 = root.after_idle(callback, 42, 11) + root.update_idletasks() # Process all pending events. + self.assertEqual(count, 53) + + # Cancel before called. + idle1 = root.after_idle(callback) + self.assertIn(idle1, root.tk.call('after', 'info')) + (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) + root.after_cancel(idle1) # Cancel this event. + self.assertEqual(count, 53) + with self.assertRaises(tkinter.TclError): + root.tk.call(script) + + def test_after_cancel(self): + root = self.root + + def callback(): + nonlocal count + count += 1 + + timer1 = root.after(5000, callback) + idle1 = root.after_idle(callback) + + # No value for id raises a ValueError. + with self.assertRaises(ValueError): + root.after_cancel(None) + + # Cancel timer event. + count = 0 + (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1)) + root.tk.call(script) + self.assertEqual(count, 1) + root.after_cancel(timer1) + with self.assertRaises(tkinter.TclError): + root.tk.call(script) + self.assertEqual(count, 1) + with self.assertRaises(tkinter.TclError): + root.tk.call('after', 'info', timer1) + + # Cancel same event - nothing happens. + root.after_cancel(timer1) + + # Cancel idle event. + count = 0 + (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) + root.tk.call(script) + self.assertEqual(count, 1) + root.after_cancel(idle1) + with self.assertRaises(tkinter.TclError): + root.tk.call(script) + self.assertEqual(count, 1) + with self.assertRaises(tkinter.TclError): + root.tk.call('after', 'info', idle1) + + def test_after_info(self): + root = self.root + + # No events. + self.assertEqual(root.after_info(), ()) + + # Add timer. + timer = root.after(1, lambda: 'break') + + # With no parameter, it returns a tuple of the event handler ids. + self.assertEqual(root.after_info(), (timer, )) + root.after_cancel(timer) + + timer1 = root.after(5000, lambda: 'break') + timer2 = root.after(5000, lambda: 'break') + idle1 = root.after_idle(lambda: 'break') + # Only contains new events and not 'timer'. + self.assertEqual(root.after_info(), (idle1, timer2, timer1)) + + # With a parameter returns a tuple of (script, type). + timer1_info = root.after_info(timer1) + self.assertEqual(len(timer1_info), 2) + self.assertEqual(timer1_info[1], 'timer') + idle1_info = root.after_info(idle1) + self.assertEqual(len(idle1_info), 2) + self.assertEqual(idle1_info[1], 'idle') + + root.after_cancel(timer1) + with self.assertRaises(tkinter.TclError): + root.after_info(timer1) + root.after_cancel(timer2) + with self.assertRaises(tkinter.TclError): + root.after_info(timer2) + root.after_cancel(idle1) + with self.assertRaises(tkinter.TclError): + root.after_info(idle1) + + # No events. + self.assertEqual(root.after_info(), ()) + + def test_clipboard(self): + root = self.root + root.clipboard_clear() + root.clipboard_append('Ùñî') + self.assertEqual(root.clipboard_get(), 'Ùñî') + root.clipboard_append('çōđě') + self.assertEqual(root.clipboard_get(), 'Ùñîçōđě') + root.clipboard_clear() + with self.assertRaises(tkinter.TclError): + root.clipboard_get() + + def test_clipboard_astral(self): + root = self.root + root.clipboard_clear() + root.clipboard_append('𝔘𝔫𝔦') + self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦') + root.clipboard_append('𝔠𝔬𝔡𝔢') + self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦𝔠𝔬𝔡𝔢') + root.clipboard_clear() + with self.assertRaises(tkinter.TclError): + root.clipboard_get() + + def test_winfo_rgb(self): + + def assertApprox(col1, col2): + # A small amount of flexibility is required (bpo-45496) + # 33 is ~0.05% of 65535, which is a reasonable margin + for col1_channel, col2_channel in zip(col1, col2): + self.assertAlmostEqual(col1_channel, col2_channel, delta=33) + + root = self.root + rgb = root.winfo_rgb + + # Color name. + self.assertEqual(rgb('red'), (65535, 0, 0)) + self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723)) + # #RGB - extends each 4-bit hex value to be 16-bit. + self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF)) + # #RRGGBB - extends each 8-bit hex value to be 16-bit. + assertApprox(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c)) + # #RRRRGGGGBBBB + assertApprox(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939)) + # Invalid string. + with self.assertRaises(tkinter.TclError): + rgb('#123456789a') + # RGB triplet is invalid input. + with self.assertRaises(tkinter.TclError): + rgb((111, 78, 55)) + + def test_winfo_pathname(self): + t = tkinter.Toplevel(self.root) + w = tkinter.Button(t) + wid = w.winfo_id() + self.assertIsInstance(wid, int) + self.assertEqual(self.root.winfo_pathname(hex(wid)), str(w)) + self.assertEqual(self.root.winfo_pathname(hex(wid), displayof=None), str(w)) + self.assertEqual(self.root.winfo_pathname(hex(wid), displayof=t), str(w)) + self.assertEqual(self.root.winfo_pathname(wid), str(w)) + self.assertEqual(self.root.winfo_pathname(wid, displayof=None), str(w)) + self.assertEqual(self.root.winfo_pathname(wid, displayof=t), str(w)) + + def test_event_repr_defaults(self): + e = tkinter.Event() + e.serial = 12345 + e.num = '??' + e.height = '??' + e.keycode = '??' + e.state = 0 + e.time = 123456789 + e.width = '??' + e.x = '??' + e.y = '??' + e.char = '' + e.keysym = '??' + e.keysym_num = '??' + e.type = '100' + e.widget = '??' + e.x_root = '??' + e.y_root = '??' + e.delta = 0 + self.assertEqual(repr(e), '<100 event>') + + def test_event_repr(self): + e = tkinter.Event() + e.serial = 12345 + e.num = 3 + e.focus = True + e.height = 200 + e.keycode = 65 + e.state = 0x30405 + e.time = 123456789 + e.width = 300 + e.x = 10 + e.y = 20 + e.char = 'A' + e.send_event = True + e.keysym = 'Key-A' + e.keysym_num = ord('A') + e.type = tkinter.EventType.Configure + e.widget = '.text' + e.x_root = 1010 + e.y_root = 1020 + e.delta = -1 + self.assertEqual(repr(e), + "") + + def test_eventtype_enum(self): + class CheckedEventType(enum.StrEnum): + KeyPress = '2' + Key = KeyPress + KeyRelease = '3' + ButtonPress = '4' + Button = ButtonPress + ButtonRelease = '5' + Motion = '6' + Enter = '7' + Leave = '8' + FocusIn = '9' + FocusOut = '10' + Keymap = '11' # undocumented + Expose = '12' + GraphicsExpose = '13' # undocumented + NoExpose = '14' # undocumented + Visibility = '15' + Create = '16' + Destroy = '17' + Unmap = '18' + Map = '19' + MapRequest = '20' + Reparent = '21' + Configure = '22' + ConfigureRequest = '23' + Gravity = '24' + ResizeRequest = '25' + Circulate = '26' + CirculateRequest = '27' + Property = '28' + SelectionClear = '29' # undocumented + SelectionRequest = '30' # undocumented + Selection = '31' # undocumented + Colormap = '32' + ClientMessage = '33' # undocumented + Mapping = '34' # undocumented + VirtualEvent = '35' # undocumented + Activate = '36' + Deactivate = '37' + MouseWheel = '38' + enum._test_simple_enum(CheckedEventType, tkinter.EventType) + + def test_getboolean(self): + for v in 'true', 'yes', 'on', '1', 't', 'y', 1, True: + self.assertIs(self.root.getboolean(v), True) + for v in 'false', 'no', 'off', '0', 'f', 'n', 0, False: + self.assertIs(self.root.getboolean(v), False) + self.assertRaises(ValueError, self.root.getboolean, 'yea') + self.assertRaises(ValueError, self.root.getboolean, '') + self.assertRaises(TypeError, self.root.getboolean, None) + self.assertRaises(TypeError, self.root.getboolean, ()) + + def test_mainloop(self): + log = [] + def callback(): + log.append(1) + self.root.after(100, self.root.quit) + self.root.after(100, callback) + self.root.mainloop(1) + self.assertEqual(log, []) + self.root.mainloop(0) + self.assertEqual(log, [1]) + self.assertTrue(self.root.winfo_exists()) + + def test_info_patchlevel(self): + vi = self.root.info_patchlevel() + f = tkinter.Frame(self.root) + self.assertEqual(f.info_patchlevel(), vi) + # The following is almost a copy of tests for sys.version_info. + self.assertIsInstance(vi[:], tuple) + self.assertEqual(len(vi), 5) + self.assertIsInstance(vi[0], int) + self.assertIsInstance(vi[1], int) + self.assertIsInstance(vi[2], int) + self.assertIn(vi[3], ("alpha", "beta", "candidate", "final")) + self.assertIsInstance(vi[4], int) + self.assertIsInstance(vi.major, int) + self.assertIsInstance(vi.minor, int) + self.assertIsInstance(vi.micro, int) + self.assertIn(vi.releaselevel, ("alpha", "beta", "final")) + self.assertIsInstance(vi.serial, int) + self.assertEqual(vi[0], vi.major) + self.assertEqual(vi[1], vi.minor) + self.assertEqual(vi[2], vi.micro) + self.assertEqual(vi[3], vi.releaselevel) + self.assertEqual(vi[4], vi.serial) + self.assertTrue(vi > (1,0,0)) + if vi.releaselevel == 'final': + self.assertEqual(vi.serial, 0) + else: + self.assertEqual(vi.micro, 0) + self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}')) + + def test_embedded_null(self): + widget = tkinter.Entry(self.root) + widget.insert(0, 'abc\0def') # ASCII-only + widget.selection_range(0, 'end') + self.assertEqual(widget.selection_get(), 'abc\x00def') + widget.insert(0, '\u20ac\0') # non-ASCII + widget.selection_range(0, 'end') + self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def') + + +class WmTest(AbstractTkTest, unittest.TestCase): + + def test_wm_attribute(self): + w = self.root + attributes = w.wm_attributes(return_python_dict=True) + self.assertIsInstance(attributes, dict) + attributes2 = w.wm_attributes() + self.assertIsInstance(attributes2, tuple) + self.assertEqual(attributes2[::2], + tuple('-' + k for k in attributes)) + self.assertEqual(attributes2[1::2], tuple(attributes.values())) + # silently deprecated + attributes3 = w.wm_attributes(None) + if self.wantobjects: + self.assertEqual(attributes3, attributes2) + else: + self.assertIsInstance(attributes3, str) + + for name in attributes: + self.assertEqual(w.wm_attributes(name), attributes[name]) + # silently deprecated + for name in attributes: + self.assertEqual(w.wm_attributes('-' + name), attributes[name]) + + self.assertIn('alpha', attributes) + self.assertIn('fullscreen', attributes) + self.assertIn('topmost', attributes) + if w._windowingsystem == "win32": + self.assertIn('disabled', attributes) + self.assertIn('toolwindow', attributes) + self.assertIn('transparentcolor', attributes) + if w._windowingsystem == "aqua": + self.assertIn('modified', attributes) + self.assertIn('notify', attributes) + self.assertIn('titlepath', attributes) + self.assertIn('transparent', attributes) + if w._windowingsystem == "x11": + self.assertIn('type', attributes) + self.assertIn('zoomed', attributes) + + w.wm_attributes(alpha=0.5) + self.assertEqual(w.wm_attributes('alpha'), + 0.5 if self.wantobjects else '0.5') + w.wm_attributes(alpha=1.0) + self.assertEqual(w.wm_attributes('alpha'), + 1.0 if self.wantobjects else '1.0') + # silently deprecated + w.wm_attributes('-alpha', 0.5) + self.assertEqual(w.wm_attributes('alpha'), + 0.5 if self.wantobjects else '0.5') + w.wm_attributes(alpha=1.0) + self.assertEqual(w.wm_attributes('alpha'), + 1.0 if self.wantobjects else '1.0') + + def test_wm_iconbitmap(self): + t = tkinter.Toplevel(self.root) + self.assertEqual(t.wm_iconbitmap(), '') + t.wm_iconbitmap('hourglass') + bug = False + if t._windowingsystem == 'aqua': + # Tk bug 13ac26b35dc55f7c37f70b39d59d7ef3e63017c8. + patchlevel = get_tk_patchlevel(t) + if patchlevel < (8, 6, 17) or (9, 0) <= patchlevel < (9, 0, 2): + bug = True + if not bug: + self.assertEqual(t.wm_iconbitmap(), 'hourglass') + self.assertEqual(self.root.wm_iconbitmap(), '') + t.wm_iconbitmap('') + self.assertEqual(t.wm_iconbitmap(), '') + + if t._windowingsystem == 'win32': + t.wm_iconbitmap(default='hourglass') + self.assertEqual(t.wm_iconbitmap(), 'hourglass') + self.assertEqual(self.root.wm_iconbitmap(), '') + t.wm_iconbitmap(default='') + self.assertEqual(t.wm_iconbitmap(), '') + + t.destroy() + + +class EventTest(AbstractTkTest, unittest.TestCase): + + def test_focus(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + + f.focus_force() + self.root.update() + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.FocusIn) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, '??') + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.num, '??') + self.assertEqual(e.state, '??') + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, '??') + self.assertEqual(e.y, '??') + self.assertEqual(e.x_root, '??') + self.assertEqual(e.y_root, '??') + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), '') + + def test_configure(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + + f.configure(height=120, borderwidth=10) + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.Configure) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, '??') + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.num, '??') + self.assertEqual(e.state, '??') + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, 150) + self.assertEqual(e.height, 100) + self.assertEqual(e.x, 0) + self.assertEqual(e.y, 0) + self.assertEqual(e.x_root, '??') + self.assertEqual(e.y_root, '??') + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), '') + + def test_event_generate_key_press(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + f.focus_force() + + f.event_generate('') + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.KeyPress) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, 0) + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.num, '??') + self.assertIsInstance(e.state, int) + self.assertNotEqual(e.state, 0) + self.assertEqual(e.char, 'z') + self.assertIsInstance(e.keycode, int) + self.assertNotEqual(e.keycode, 0) + self.assertEqual(e.keysym, 'z') + self.assertEqual(e.keysym_num, ord('z')) + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, -1 - f.winfo_rootx()) + self.assertEqual(e.y, -1 - f.winfo_rooty()) + self.assertEqual(e.x_root, -1) + self.assertEqual(e.y_root, -1) + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), + f"") + + def test_event_generate_enter(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + + f.event_generate('', x=100, y=50) + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.Enter) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, 0) + self.assertIs(e.send_event, False) + self.assertIs(e.focus, False) + self.assertEqual(e.num, '??') + self.assertEqual(e.state, 0) + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, 100) + self.assertEqual(e.y, 50) + self.assertEqual(e.x_root, 100 + f.winfo_rootx()) + self.assertEqual(e.y_root, 50 + f.winfo_rooty()) + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), '') + + def test_event_generate_button_press(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + f.focus_force() + + f.event_generate('', x=100, y=50) + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.ButtonPress) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, 0) + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.num, 1) + self.assertEqual(e.state, 0) + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, 100) + self.assertEqual(e.y, 50) + self.assertEqual(e.x_root, f.winfo_rootx() + 100) + self.assertEqual(e.y_root, f.winfo_rooty() + 50) + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), '') + + def test_event_generate_motion(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + f.focus_force() + + f.event_generate('', x=100, y=50) + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.Motion) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, 0) + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.num, '??') + self.assertEqual(e.state, 0x100) + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, 100) + self.assertEqual(e.y, 50) + self.assertEqual(e.x_root, f.winfo_rootx() + 100) + self.assertEqual(e.y_root, f.winfo_rooty() + 50) + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), '') + + def test_event_generate_mouse_wheel(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('', events.append) + f.focus_force() + + f.event_generate('', x=100, y=50, delta=-5) + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.MouseWheel) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.time, 0) + self.assertEqual(e.num, '??') + self.assertEqual(e.state, 0) + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, 100) + self.assertEqual(e.y, 50) + self.assertEqual(e.x_root, f.winfo_rootx() + 100) + self.assertEqual(e.y_root, f.winfo_rooty() + 50) + self.assertEqual(e.delta, -5) + self.assertEqual(repr(e), '') + + def test_generate_event_virtual_event(self): + f = tkinter.Frame(self.root, width=150, height=100) + f.pack() + self.root.wait_visibility() # needed on Windows + self.root.update_idletasks() + + events = [] + f.bind('<>', events.append) + f.focus_force() + + f.event_generate('<>', x=50) + self.assertEqual(len(events), 1, events) + e = events[0] + self.assertIs(e.type, tkinter.EventType.VirtualEvent) + self.assertIs(e.widget, f) + self.assertIsInstance(e.serial, int) + self.assertEqual(e.time, 0) + self.assertIs(e.send_event, False) + self.assertFalse(hasattr(e, 'focus')) + self.assertEqual(e.num, '??') + self.assertEqual(e.state, 0) + self.assertEqual(e.char, '??') + self.assertEqual(e.keycode, '??') + self.assertEqual(e.keysym, '??') + self.assertEqual(e.keysym_num, '??') + self.assertEqual(e.width, '??') + self.assertEqual(e.height, '??') + self.assertEqual(e.x, 50) + self.assertEqual(e.y, 0) + self.assertEqual(e.x_root, f.winfo_rootx() + 50) + self.assertEqual(e.y_root, -1) + self.assertEqual(e.delta, 0) + self.assertEqual(repr(e), + f"") + + +class BindTest(AbstractTkTest, unittest.TestCase): + + def setUp(self): + super().setUp() + root = self.root + self.frame = tkinter.Frame(self.root, class_='Test', + width=150, height=100) + self.frame.pack() + + def assertCommandExist(self, funcid): + self.assertEqual(_info_commands(self.root, funcid), (funcid,)) + + def assertCommandNotExist(self, funcid): + self.assertEqual(_info_commands(self.root, funcid), ()) + + def test_bind(self): + event = '' + f = self.frame + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + + funcid = f.bind(event, test1) + self.assertEqual(f.bind(), (event,)) + script = f.bind(event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = f.bind(event, test2, add=True) + script = f.bind(event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def test_unbind(self): + event = '' + f = self.frame + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + + funcid = f.bind(event, test1) + funcid2 = f.bind(event, test2, add=True) + + self.assertRaises(TypeError, f.unbind) + f.unbind(event) + self.assertEqual(f.bind(event), '') + self.assertEqual(f.bind(), ()) + + def test_unbind2(self): + f = self.frame + f.wait_visibility() + f.focus_force() + f.update_idletasks() + event = '' + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): events.append('a') + def test2(e): events.append('b') + def test3(e): events.append('c') + + funcid = f.bind(event, test1) + funcid2 = f.bind(event, test2, add=True) + funcid3 = f.bind(event, test3, add=True) + events = [] + f.event_generate(event) + self.assertEqual(events, ['a', 'b', 'c']) + + f.unbind(event, funcid2) + script = f.bind(event) + self.assertNotIn(funcid2, script) + self.assertIn(funcid, script) + self.assertIn(funcid3, script) + self.assertEqual(f.bind(), (event,)) + self.assertCommandNotExist(funcid2) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid3) + events = [] + f.event_generate(event) + self.assertEqual(events, ['a', 'c']) + + f.unbind(event, funcid) + f.unbind(event, funcid3) + self.assertEqual(f.bind(event), '') + self.assertEqual(f.bind(), ()) + self.assertCommandNotExist(funcid) + self.assertCommandNotExist(funcid2) + self.assertCommandNotExist(funcid3) + events = [] + f.event_generate(event) + self.assertEqual(events, []) + + # non-idempotent + self.assertRaises(tkinter.TclError, f.unbind, event, funcid2) + + def test_bind_rebind(self): + event = '' + f = self.frame + self.assertEqual(f.bind(), ()) + self.assertEqual(f.bind(event), '') + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = f.bind(event, test1) + funcid2 = f.bind(event, test2, add=True) + script = f.bind(event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = f.bind(event, test3) + script = f.bind(event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid3) + + def test_bind_class(self): + event = '' + bind_class = self.root.bind_class + unbind_class = self.root.unbind_class + self.assertRaises(TypeError, bind_class) + self.assertEqual(bind_class('Test'), ()) + self.assertEqual(bind_class('Test', event), '') + self.addCleanup(unbind_class, 'Test', event) + def test1(e): pass + def test2(e): pass + + funcid = bind_class('Test', event, test1) + self.assertEqual(bind_class('Test'), (event,)) + script = bind_class('Test', event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = bind_class('Test', event, test2, add=True) + script = bind_class('Test', event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def test_unbind_class(self): + event = '' + bind_class = self.root.bind_class + unbind_class = self.root.unbind_class + self.assertEqual(bind_class('Test'), ()) + self.assertEqual(bind_class('Test', event), '') + self.addCleanup(unbind_class, 'Test', event) + def test1(e): pass + def test2(e): pass + + funcid = bind_class('Test', event, test1) + funcid2 = bind_class('Test', event, test2, add=True) + + self.assertRaises(TypeError, unbind_class) + self.assertRaises(TypeError, unbind_class, 'Test') + unbind_class('Test', event) + self.assertEqual(bind_class('Test', event), '') + self.assertEqual(bind_class('Test'), ()) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + unbind_class('Test', event) # idempotent + + def test_bind_class_rebind(self): + event = '' + bind_class = self.root.bind_class + unbind_class = self.root.unbind_class + self.assertEqual(bind_class('Test'), ()) + self.assertEqual(bind_class('Test', event), '') + self.addCleanup(unbind_class, 'Test', event) + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = bind_class('Test', event, test1) + funcid2 = bind_class('Test', event, test2, add=True) + script = bind_class('Test', event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = bind_class('Test', event, test3) + script = bind_class('Test', event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + self.assertCommandExist(funcid3) + + def test_bind_all(self): + event = '' + bind_all = self.root.bind_all + unbind_all = self.root.unbind_all + self.assertNotIn(event, bind_all()) + self.assertEqual(bind_all(event), '') + self.addCleanup(unbind_all, event) + def test1(e): pass + def test2(e): pass + + funcid = bind_all(event, test1) + self.assertIn(event, bind_all()) + script = bind_all(event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = bind_all(event, test2, add=True) + script = bind_all(event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def test_unbind_all(self): + event = '' + bind_all = self.root.bind_all + unbind_all = self.root.unbind_all + self.assertNotIn(event, bind_all()) + self.assertEqual(bind_all(event), '') + self.addCleanup(unbind_all, event) + def test1(e): pass + def test2(e): pass + + funcid = bind_all(event, test1) + funcid2 = bind_all(event, test2, add=True) + + unbind_all(event) + self.assertEqual(bind_all(event), '') + self.assertNotIn(event, bind_all()) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + unbind_all(event) # idempotent + + def test_bind_all_rebind(self): + event = '' + bind_all = self.root.bind_all + unbind_all = self.root.unbind_all + self.assertNotIn(event, bind_all()) + self.assertEqual(bind_all(event), '') + self.addCleanup(unbind_all, event) + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = bind_all(event, test1) + funcid2 = bind_all(event, test2, add=True) + script = bind_all(event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = bind_all(event, test3) + script = bind_all(event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + self.assertCommandExist(funcid3) + + def _test_tag_bind(self, w): + tag = 'sel' + event = '' + w.pack() + self.assertRaises(TypeError, w.tag_bind) + tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind + if isinstance(w, tkinter.Text): + self.assertRaises(TypeError, w.tag_bind, tag) + self.assertRaises(TypeError, w.tag_bind, tag, event) + self.assertEqual(tag_bind(tag), ()) + self.assertEqual(tag_bind(tag, event), '') + def test1(e): pass + def test2(e): pass + + funcid = w.tag_bind(tag, event, test1) + self.assertEqual(tag_bind(tag), (event,)) + script = tag_bind(tag, event) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + + funcid2 = w.tag_bind(tag, event, test2, add=True) + script = tag_bind(tag, event) + self.assertIn(funcid, script) + self.assertIn(funcid2, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + def _test_tag_unbind(self, w): + tag = 'sel' + event = '' + w.pack() + tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind + self.assertEqual(tag_bind(tag), ()) + self.assertEqual(tag_bind(tag, event), '') + def test1(e): pass + def test2(e): pass + + funcid = w.tag_bind(tag, event, test1) + funcid2 = w.tag_bind(tag, event, test2, add=True) + + self.assertRaises(TypeError, w.tag_unbind, tag) + w.tag_unbind(tag, event) + self.assertEqual(tag_bind(tag, event), '') + self.assertEqual(tag_bind(tag), ()) + + def _test_tag_bind_rebind(self, w): + tag = 'sel' + event = '' + w.pack() + tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind + self.assertEqual(tag_bind(tag), ()) + self.assertEqual(tag_bind(tag, event), '') + def test1(e): pass + def test2(e): pass + def test3(e): pass + + funcid = w.tag_bind(tag, event, test1) + funcid2 = w.tag_bind(tag, event, test2, add=True) + script = tag_bind(tag, event) + self.assertIn(funcid2, script) + self.assertIn(funcid, script) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid2) + + funcid3 = w.tag_bind(tag, event, test3) + script = tag_bind(tag, event) + self.assertNotIn(funcid, script) + self.assertNotIn(funcid2, script) + self.assertIn(funcid3, script) + self.assertCommandExist(funcid3) + + def test_canvas_tag_bind(self): + c = tkinter.Canvas(self.frame) + self._test_tag_bind(c) + + def test_canvas_tag_unbind(self): + c = tkinter.Canvas(self.frame) + self._test_tag_unbind(c) + + def test_canvas_tag_bind_rebind(self): + c = tkinter.Canvas(self.frame) + self._test_tag_bind_rebind(c) + + def test_text_tag_bind(self): + t = tkinter.Text(self.frame) + self._test_tag_bind(t) + + def test_text_tag_unbind(self): + t = tkinter.Text(self.frame) + self._test_tag_unbind(t) + + def test_text_tag_bind_rebind(self): + t = tkinter.Text(self.frame) + self._test_tag_bind_rebind(t) + + def test_bindtags(self): + f = self.frame + self.assertEqual(self.root.bindtags(), ('.', 'Tk', 'all')) + self.assertEqual(f.bindtags(), (str(f), 'Test', '.', 'all')) + f.bindtags(('a', 'b c')) + self.assertEqual(f.bindtags(), ('a', 'b c')) + + def test_bind_events(self): + event = '' + root = self.root + t = tkinter.Toplevel(root) + f = tkinter.Frame(t, class_='Test', width=150, height=100) + f.pack() + root.wait_visibility() # needed on Windows + root.update_idletasks() + self.addCleanup(root.unbind_class, 'Test', event) + self.addCleanup(root.unbind_class, 'Toplevel', event) + self.addCleanup(root.unbind_class, 'tag', event) + self.addCleanup(root.unbind_class, 'tag2', event) + self.addCleanup(root.unbind_all, event) + def test(what): + return lambda e: events.append((what, e.widget)) + + root.bind_all(event, test('all')) + root.bind_class('Test', event, test('frame class')) + root.bind_class('Toplevel', event, test('toplevel class')) + root.bind_class('tag', event, test('tag')) + root.bind_class('tag2', event, test('tag2')) + f.bind(event, test('frame')) + t.bind(event, test('toplevel')) + + events = [] + f.event_generate(event) + self.assertEqual(events, [ + ('frame', f), + ('frame class', f), + ('toplevel', f), + ('all', f), + ]) + + events = [] + t.event_generate(event) + self.assertEqual(events, [ + ('toplevel', t), + ('toplevel class', t), + ('all', t), + ]) + + f.bindtags(('tag', 'tag3')) + events = [] + f.event_generate(event) + self.assertEqual(events, [('tag', f)]) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_default_root(self): + self.assertIs(tkinter._support_default_root, True) + self.assertIsNone(tkinter._default_root) + root = tkinter.Tk() + root2 = tkinter.Tk() + root3 = tkinter.Tk() + self.assertIs(tkinter._default_root, root) + root2.destroy() + self.assertIs(tkinter._default_root, root) + root.destroy() + self.assertIsNone(tkinter._default_root) + root3.destroy() + self.assertIsNone(tkinter._default_root) + + def test_no_default_root(self): + self.assertIs(tkinter._support_default_root, True) + self.assertIsNone(tkinter._default_root) + root = tkinter.Tk() + self.assertIs(tkinter._default_root, root) + tkinter.NoDefaultRoot() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + # repeated call is no-op + tkinter.NoDefaultRoot() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root.destroy() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root = tkinter.Tk() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root.destroy() + + def test_getboolean(self): + self.assertRaises(RuntimeError, tkinter.getboolean, '1') + root = tkinter.Tk() + self.assertIs(tkinter.getboolean('1'), True) + self.assertRaises(ValueError, tkinter.getboolean, 'yea') + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.getboolean, '1') + + def test_mainloop(self): + self.assertRaises(RuntimeError, tkinter.mainloop) + root = tkinter.Tk() + root.after_idle(root.quit) + tkinter.mainloop() + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.mainloop) + + +def _info_commands(widget, pattern=None): + return widget.tk.splitlist(widget.tk.call('info', 'commands', pattern)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_simpledialog.py b/Lib/test/test_tkinter/test_simpledialog.py new file mode 100644 index 00000000000..502f7f7098a --- /dev/null +++ b/Lib/test/test_tkinter/test_simpledialog.py @@ -0,0 +1,35 @@ +import unittest +import tkinter +from test.support import requires, swap_attr +from test.test_tkinter.support import AbstractDefaultRootTest +from tkinter.simpledialog import Dialog, askinteger + +requires('gui') + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_askinteger(self): + @staticmethod + def mock_wait_window(w): + nonlocal ismapped + ismapped = w.master.winfo_ismapped() + w.destroy() + + with swap_attr(Dialog, 'wait_window', mock_wait_window): + ismapped = None + askinteger("Go To Line", "Line number") + self.assertEqual(ismapped, False) + + root = tkinter.Tk() + ismapped = None + askinteger("Go To Line", "Line number") + self.assertEqual(ismapped, True) + root.destroy() + + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_text.py b/Lib/test/test_tkinter/test_text.py new file mode 100644 index 00000000000..ed618c73901 --- /dev/null +++ b/Lib/test/test_tkinter/test_text.py @@ -0,0 +1,132 @@ +import unittest +import tkinter +from test.support import requires +from test.test_tkinter.support import AbstractTkTest + +requires('gui') + +class TextTest(AbstractTkTest, unittest.TestCase): + + def setUp(self): + super().setUp() + self.text = tkinter.Text(self.root) + self.text.pack() + + def test_debug(self): + text = self.text + olddebug = text.debug() + try: + text.debug(0) + self.assertEqual(text.debug(), 0) + text.debug(1) + self.assertEqual(text.debug(), 1) + finally: + text.debug(olddebug) + self.assertEqual(text.debug(), olddebug) + + def test_search(self): + text = self.text + + # pattern and index are obligatory arguments. + self.assertRaises(tkinter.TclError, text.search, None, '1.0') + self.assertRaises(tkinter.TclError, text.search, 'a', None) + self.assertRaises(tkinter.TclError, text.search, None, None) + + # Invalid text index. + self.assertRaises(tkinter.TclError, text.search, '', 0) + self.assertRaises(tkinter.TclError, text.search, '', '') + self.assertRaises(tkinter.TclError, text.search, '', 'invalid') + self.assertRaises(tkinter.TclError, text.search, '', '1.0', 'invalid') + + text.insert('1.0', + 'This is a test. This is only a test.\n' + 'Another line.\n' + 'Yet another line.\n' + '64-bit') + + self.assertEqual(text.search('test', '1.0'), '1.10') + self.assertEqual(text.search('test', '1.0', 'end'), '1.10') + self.assertEqual(text.search('test', '1.0', '1.10'), '') + self.assertEqual(text.search('test', '1.11'), '1.31') + self.assertEqual(text.search('test', '1.32', 'end'), '') + self.assertEqual(text.search('test', '1.32'), '1.10') + + self.assertEqual(text.search('', '1.0'), '1.0') # empty pattern + self.assertEqual(text.search('nonexistent', '1.0'), '') + self.assertEqual(text.search('-bit', '1.0'), '4.2') # starts with a hyphen + + self.assertEqual(text.search('line', '3.0'), '3.12') + self.assertEqual(text.search('line', '3.0', forwards=True), '3.12') + self.assertEqual(text.search('line', '3.0', backwards=True), '2.8') + self.assertEqual(text.search('line', '3.0', forwards=True, backwards=True), '2.8') + + self.assertEqual(text.search('t.', '1.0'), '1.13') + self.assertEqual(text.search('t.', '1.0', exact=True), '1.13') + self.assertEqual(text.search('t.', '1.0', regexp=True), '1.10') + self.assertEqual(text.search('t.', '1.0', exact=True, regexp=True), '1.10') + + self.assertEqual(text.search('TEST', '1.0'), '') + self.assertEqual(text.search('TEST', '1.0', nocase=True), '1.10') + + var = tkinter.Variable(self.root) + self.assertEqual(text.search('test', '1.0', count=var), '1.10') + self.assertEqual(var.get(), 4 if self.wantobjects else '4') + + # TODO: Add test for elide=True + + def test_count(self): + text = self.text + text.insert('1.0', + 'Lorem ipsum dolor sit amet,\n' + 'consectetur adipiscing elit,\n' + 'sed do eiusmod tempor incididunt\n' + 'ut labore et dolore magna aliqua.') + + options = ('chars', 'indices', 'lines', + 'displaychars', 'displayindices', 'displaylines', + 'xpixels', 'ypixels') + self.assertEqual(len(text.count('1.0', 'end', *options, return_ints=True)), 8) + self.assertEqual(len(text.count('1.0', 'end', *options)), 8) + self.assertEqual(text.count('1.0', 'end', 'chars', 'lines', return_ints=True), + (124, 4)) + self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3)) + self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines', return_ints=True), + (-92, -3)) + self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3)) + self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines', return_ints=True), + (0, 0)) + self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0)) + self.assertEqual(text.count('1.0', 'end', 'lines', return_ints=True), 4) + self.assertEqual(text.count('1.0', 'end', 'lines'), (4,)) + self.assertEqual(text.count('end', '1.0', 'lines', return_ints=True), -4) + self.assertEqual(text.count('end', '1.0', 'lines'), (-4,)) + self.assertEqual(text.count('1.3', '1.5', 'lines', return_ints=True), 0) + self.assertEqual(text.count('1.3', '1.5', 'lines'), None) + self.assertEqual(text.count('1.3', '1.3', 'lines', return_ints=True), 0) + self.assertEqual(text.count('1.3', '1.3', 'lines'), None) + # Count 'indices' by default. + self.assertEqual(text.count('1.0', 'end', return_ints=True), 124) + self.assertEqual(text.count('1.0', 'end'), (124,)) + self.assertEqual(text.count('1.0', 'end', 'indices', return_ints=True), 124) + self.assertEqual(text.count('1.0', 'end', 'indices'), (124,)) + self.assertRaises(tkinter.TclError, text.count, '1.0', 'end', 'spam') + self.assertRaises(tkinter.TclError, text.count, '1.0', 'end', '-lines') + + self.assertIsInstance(text.count('1.3', '1.5', 'ypixels', return_ints=True), int) + self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), tuple) + self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels', return_ints=True), int) + self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels'), int) + self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels', return_ints=True), 0) + self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), None) + self.assertEqual(text.count('1.3', '1.5', 'update', 'indices', return_ints=True), 2) + self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2) + self.assertEqual(text.count('1.3', '1.3', 'update', 'indices', return_ints=True), 0) + self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), None) + self.assertEqual(text.count('1.3', '1.5', 'update', return_ints=True), 2) + self.assertEqual(text.count('1.3', '1.5', 'update'), (2,)) + self.assertEqual(text.count('1.3', '1.3', 'update', return_ints=True), 0) + self.assertEqual(text.count('1.3', '1.3', 'update'), None) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_variables.py b/Lib/test/test_tkinter/test_variables.py new file mode 100644 index 00000000000..def7aec077e --- /dev/null +++ b/Lib/test/test_tkinter/test_variables.py @@ -0,0 +1,344 @@ +import unittest +from test import support + +import gc +import tkinter +from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl, + TclError) +from test.support import ALWAYS_EQ +from test.test_tkinter.support import AbstractDefaultRootTest, tcl_version + + +class Var(Variable): + + _default = "default" + side_effect = False + + def set(self, value): + self.side_effect = True + super().set(value) + + +class TestBase(unittest.TestCase): + + def setUp(self): + self.root = Tcl() + + def tearDown(self): + del self.root + + +class TestVariable(TestBase): + + def info_exists(self, *args): + return self.root.getboolean(self.root.call("info", "exists", *args)) + + def test_default(self): + v = Variable(self.root) + self.assertEqual("", v.get()) + self.assertRegex(str(v), r"^PY_VAR(\d+)$") + + def test_name_and_value(self): + v = Variable(self.root, "sample string", "varname") + self.assertEqual("sample string", v.get()) + self.assertEqual("varname", str(v)) + + def test___del__(self): + self.assertFalse(self.info_exists("varname")) + v = Variable(self.root, "sample string", "varname") + self.assertTrue(self.info_exists("varname")) + del v + support.gc_collect() # For PyPy or other GCs. + self.assertFalse(self.info_exists("varname")) + + def test_dont_unset_not_existing(self): + self.assertFalse(self.info_exists("varname")) + v1 = Variable(self.root, name="name") + v2 = Variable(self.root, name="name") + del v1 + support.gc_collect() # For PyPy or other GCs. + self.assertFalse(self.info_exists("name")) + # shouldn't raise exception + del v2 + support.gc_collect() # For PyPy or other GCs. + self.assertFalse(self.info_exists("name")) + + def test_equality(self): + # values doesn't matter, only class and name are checked + v1 = Variable(self.root, name="abc") + v2 = Variable(self.root, name="abc") + self.assertIsNot(v1, v2) + self.assertEqual(v1, v2) + + v3 = Variable(self.root, name="cba") + self.assertNotEqual(v1, v3) + + v4 = StringVar(self.root, name="abc") + self.assertEqual(str(v1), str(v4)) + self.assertNotEqual(v1, v4) + + V = type('Variable', (), {}) + self.assertNotEqual(v1, V()) + + self.assertNotEqual(v1, object()) + self.assertEqual(v1, ALWAYS_EQ) + + root2 = tkinter.Tk() + self.addCleanup(root2.destroy) + v5 = Variable(root2, name="abc") + self.assertEqual(str(v1), str(v5)) + self.assertNotEqual(v1, v5) + + def test_invalid_name(self): + with self.assertRaises(TypeError): + Variable(self.root, name=123) + + def test_null_in_name(self): + with self.assertRaises(ValueError): + Variable(self.root, name='var\x00name') + with self.assertRaises(ValueError): + self.root.globalsetvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.globalsetvar(b'var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar('var\x00name', "value") + with self.assertRaises(ValueError): + self.root.setvar(b'var\x00name', "value") + + def test_initialize(self): + v = Var(self.root) + self.assertFalse(v.side_effect) + v.set("value") + self.assertTrue(v.side_effect) + + def test_trace_old(self): + if tcl_version >= (9, 0): + self.skipTest('requires Tcl version < 9.0') + # Old interface + v = Variable(self.root) + vname = str(v) + trace = [] + def read_tracer(*args): + trace.append(('read',) + args) + def write_tracer(*args): + trace.append(('write',) + args) + cb1 = v.trace_variable('r', read_tracer) + cb2 = v.trace_variable('wu', write_tracer) + self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)]) + self.assertEqual(trace, []) + + v.set('spam') + self.assertEqual(trace, [('write', vname, '', 'w')]) + + trace = [] + v.get() + self.assertEqual(trace, [('read', vname, '', 'r')]) + + trace = [] + info = sorted(v.trace_vinfo()) + v.trace_vdelete('w', cb1) # Wrong mode + self.assertEqual(sorted(v.trace_vinfo()), info) + with self.assertRaises(TclError): + v.trace_vdelete('r', 'spam') # Wrong command name + self.assertEqual(sorted(v.trace_vinfo()), info) + v.trace_vdelete('r', (cb1, 43)) # Wrong arguments + self.assertEqual(sorted(v.trace_vinfo()), info) + v.get() + self.assertEqual(trace, [('read', vname, '', 'r')]) + + trace = [] + v.trace_vdelete('r', cb1) + self.assertEqual(v.trace_vinfo(), [('wu', cb2)]) + v.get() + self.assertEqual(trace, []) + + trace = [] + del write_tracer + gc.collect() + v.set('eggs') + self.assertEqual(trace, [('write', vname, '', 'w')]) + + trace = [] + del v + gc.collect() + self.assertEqual(trace, [('write', vname, '', 'u')]) + + def test_trace(self): + v = Variable(self.root) + vname = str(v) + trace = [] + def read_tracer(*args): + trace.append(('read',) + args) + def write_tracer(*args): + trace.append(('write',) + args) + tr1 = v.trace_add('read', read_tracer) + tr2 = v.trace_add(['write', 'unset'], write_tracer) + self.assertEqual(sorted(v.trace_info()), [ + (('read',), tr1), + (('write', 'unset'), tr2)]) + self.assertEqual(trace, []) + + v.set('spam') + self.assertEqual(trace, [('write', vname, '', 'write')]) + + trace = [] + v.get() + self.assertEqual(trace, [('read', vname, '', 'read')]) + + trace = [] + info = sorted(v.trace_info()) + v.trace_remove('write', tr1) # Wrong mode + self.assertEqual(sorted(v.trace_info()), info) + with self.assertRaises(TclError): + v.trace_remove('read', 'spam') # Wrong command name + self.assertEqual(sorted(v.trace_info()), info) + v.get() + self.assertEqual(trace, [('read', vname, '', 'read')]) + + trace = [] + v.trace_remove('read', tr1) + self.assertEqual(v.trace_info(), [(('write', 'unset'), tr2)]) + v.get() + self.assertEqual(trace, []) + + trace = [] + del write_tracer + gc.collect() + v.set('eggs') + self.assertEqual(trace, [('write', vname, '', 'write')]) + + trace = [] + del v + gc.collect() + self.assertEqual(trace, [('write', vname, '', 'unset')]) + + +class TestStringVar(TestBase): + + def test_default(self): + v = StringVar(self.root) + self.assertEqual("", v.get()) + + def test_get(self): + v = StringVar(self.root, "abc", "name") + self.assertEqual("abc", v.get()) + self.root.globalsetvar("name", "value") + self.assertEqual("value", v.get()) + + def test_get_null(self): + v = StringVar(self.root, "abc\x00def", "name") + self.assertEqual("abc\x00def", v.get()) + self.root.globalsetvar("name", "val\x00ue") + self.assertEqual("val\x00ue", v.get()) + + +class TestIntVar(TestBase): + + def test_default(self): + v = IntVar(self.root) + self.assertEqual(0, v.get()) + + def test_get(self): + v = IntVar(self.root, 123, "name") + self.assertEqual(123, v.get()) + self.root.globalsetvar("name", "345") + self.assertEqual(345, v.get()) + self.root.globalsetvar("name", "876.5") + self.assertEqual(876, v.get()) + + def test_invalid_value(self): + v = IntVar(self.root, name="name") + self.root.globalsetvar("name", "value") + with self.assertRaises((ValueError, TclError)): + v.get() + + +class TestDoubleVar(TestBase): + + def test_default(self): + v = DoubleVar(self.root) + self.assertEqual(0.0, v.get()) + + def test_get(self): + v = DoubleVar(self.root, 1.23, "name") + self.assertAlmostEqual(1.23, v.get()) + self.root.globalsetvar("name", "3.45") + self.assertAlmostEqual(3.45, v.get()) + + def test_get_from_int(self): + v = DoubleVar(self.root, 1.23, "name") + self.assertAlmostEqual(1.23, v.get()) + self.root.globalsetvar("name", "3.45") + self.assertAlmostEqual(3.45, v.get()) + self.root.globalsetvar("name", "456") + self.assertAlmostEqual(456, v.get()) + + def test_invalid_value(self): + v = DoubleVar(self.root, name="name") + self.root.globalsetvar("name", "value") + with self.assertRaises((ValueError, TclError)): + v.get() + + +class TestBooleanVar(TestBase): + + def test_default(self): + v = BooleanVar(self.root) + self.assertIs(v.get(), False) + + def test_get(self): + v = BooleanVar(self.root, True, "name") + self.assertIs(v.get(), True) + self.root.globalsetvar("name", "0") + self.assertIs(v.get(), False) + self.root.globalsetvar("name", 42 if self.root.wantobjects() else 1) + self.assertIs(v.get(), True) + self.root.globalsetvar("name", 0) + self.assertIs(v.get(), False) + self.root.globalsetvar("name", "on") + self.assertIs(v.get(), True) + + def test_set(self): + true = 1 if self.root.wantobjects() else "1" + false = 0 if self.root.wantobjects() else "0" + v = BooleanVar(self.root, name="name") + v.set(True) + self.assertEqual(self.root.globalgetvar("name"), true) + v.set("0") + self.assertEqual(self.root.globalgetvar("name"), false) + v.set(42) + self.assertEqual(self.root.globalgetvar("name"), true) + v.set(0) + self.assertEqual(self.root.globalgetvar("name"), false) + v.set("on") + self.assertEqual(self.root.globalgetvar("name"), true) + + def test_invalid_value_domain(self): + false = 0 if self.root.wantobjects() else "0" + v = BooleanVar(self.root, name="name") + with self.assertRaises(TclError): + v.set("value") + self.assertEqual(self.root.globalgetvar("name"), false) + self.root.globalsetvar("name", "value") + with self.assertRaises(ValueError): + v.get() + self.root.globalsetvar("name", "1.0") + with self.assertRaises(ValueError): + v.get() + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_variable(self): + self.assertRaises(RuntimeError, Variable) + root = tkinter.Tk() + v = Variable() + v.set("value") + self.assertEqual(v.get(), "value") + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, Variable) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py new file mode 100644 index 00000000000..9ea764ca2a3 --- /dev/null +++ b/Lib/test/test_tkinter/test_widgets.py @@ -0,0 +1,1514 @@ +import unittest +import tkinter +from tkinter import TclError +import os +from test.support import requires + +from test.test_tkinter.support import (requires_tk, tk_version, + get_tk_patchlevel, widget_eq, + AbstractDefaultRootTest) +from test.test_tkinter.widget_tests import ( + add_standard_options, + AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests) + +requires('gui') + + +EXPECTED_SCREEN_DISTANCE_ERRMSG = '(bad|expected) screen distance (but got )?"{}"' +EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG = '(bad|expected) screen distance (or "" but got )?"{}"' + +def float_round(x): + return float(round(x)) + + +class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): + _conv_pad_pixels = False + + def test_configure_class(self): + widget = self.create() + self.assertEqual(widget['class'], + widget.__class__.__name__.title()) + self.checkInvalidParam(widget, 'class', 'Foo', + errmsg="can't modify -class option after widget is created") + widget2 = self.create(class_='Foo') + self.assertEqual(widget2['class'], 'Foo') + + def test_configure_colormap(self): + widget = self.create() + self.assertEqual(widget['colormap'], '') + self.checkInvalidParam(widget, 'colormap', 'new', + errmsg="can't modify -colormap option after widget is created") + widget2 = self.create(colormap='new') + self.assertEqual(widget2['colormap'], 'new') + + def test_configure_container(self): + widget = self.create() + self.assertEqual(widget['container'], 0 if self.wantobjects else '0') + self.checkInvalidParam(widget, 'container', 1, + errmsg="can't modify -container option after widget is created") + widget2 = self.create(container=True) + self.assertEqual(widget2['container'], 1 if self.wantobjects else '1') + + def test_configure_visual(self): + widget = self.create() + self.assertEqual(widget['visual'], '') + self.checkInvalidParam(widget, 'visual', 'default', + errmsg="can't modify -visual option after widget is created") + widget2 = self.create(visual='default') + self.assertEqual(widget2['visual'], 'default') + + +@add_standard_options(StandardOptionsTests) +class ToplevelTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'background', 'backgroundimage', 'borderwidth', + 'class', 'colormap', 'container', 'cursor', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'menu', 'padx', 'pady', 'relief', 'screen', + 'takefocus', 'tile', 'use', 'visual', 'width', + ) + + def create(self, **kwargs): + return tkinter.Toplevel(self.root, **kwargs) + + def test_configure_menu(self): + widget = self.create() + menu = tkinter.Menu(self.root) + self.checkParam(widget, 'menu', menu, eq=widget_eq) + self.checkParam(widget, 'menu', '') + + def test_configure_screen(self): + widget = self.create() + if widget._windowingsystem != 'x11': + self.skipTest('Not using Tk for X11') + self.assertEqual(widget['screen'], '') + try: + display = os.environ['DISPLAY'] + except KeyError: + self.skipTest('No $DISPLAY set.') + self.checkInvalidParam(widget, 'screen', display, + errmsg="can't modify -screen option after widget is created") + widget2 = self.create(screen=display) + self.assertEqual(widget2['screen'], display) + + def test_configure_use(self): + widget = self.create() + self.assertEqual(widget['use'], '') + parent = self.create(container=True) + wid = hex(parent.winfo_id()) + with self.subTest(wid=wid): + widget2 = self.create(use=wid) + self.assertEqual(widget2['use'], wid) + + +@add_standard_options(StandardOptionsTests) +class FrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'background', 'backgroundimage', 'borderwidth', + 'class', 'colormap', 'container', 'cursor', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'padx', 'pady', 'relief', 'takefocus', 'tile', 'visual', 'width', + ) + + def create(self, **kwargs): + return tkinter.Frame(self.root, **kwargs) + + +@add_standard_options(StandardOptionsTests) +class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', + 'class', 'colormap', 'container', 'cursor', + 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'labelanchor', 'labelwidget', 'padx', 'pady', 'relief', + 'takefocus', 'text', 'visual', 'width', + ) + + def create(self, **kwargs): + return tkinter.LabelFrame(self.root, **kwargs) + + def test_configure_labelanchor(self): + widget = self.create() + self.checkEnumParam(widget, 'labelanchor', + 'e', 'en', 'es', 'n', 'ne', 'nw', + 's', 'se', 'sw', 'w', 'wn', 'ws') + self.checkInvalidParam(widget, 'labelanchor', 'center') + + def test_configure_labelwidget(self): + widget = self.create() + label = tkinter.Label(self.root, text='Mupp', name='foo') + self.checkParam(widget, 'labelwidget', label, expected='.foo') + label.destroy() + + +class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): + _conv_pixels = False + _clip_highlightthickness = tk_version >= (8, 7) + _clip_pad = tk_version >= (8, 7) + _clip_borderwidth = tk_version >= (8, 7) + + +@add_standard_options(StandardOptionsTests) +class LabelTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', 'compound', 'cursor', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'justify', 'padx', 'pady', 'relief', 'state', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength', + ) + + def create(self, **kwargs): + return tkinter.Label(self.root, **kwargs) + + +@add_standard_options(StandardOptionsTests) +class ButtonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'command', 'compound', 'cursor', 'default', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'justify', 'overrelief', 'padx', 'pady', 'relief', + 'repeatdelay', 'repeatinterval', + 'state', 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength') + + def create(self, **kwargs): + return tkinter.Button(self.root, **kwargs) + + def test_configure_default(self): + widget = self.create() + self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal') + + +@add_standard_options(StandardOptionsTests) +class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'command', 'compound', 'cursor', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'indicatoron', 'justify', + 'offrelief', 'offvalue', 'onvalue', 'overrelief', + 'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state', + 'takefocus', 'text', 'textvariable', + 'tristateimage', 'tristatevalue', + 'underline', 'variable', 'width', 'wraplength', + ) + + def create(self, **kwargs): + return tkinter.Checkbutton(self.root, **kwargs) + + + def test_configure_offvalue(self): + widget = self.create() + self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') + + def test_configure_onvalue(self): + widget = self.create() + self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') + + def test_unique_variables(self): + frames = [] + buttons = [] + for i in range(2): + f = tkinter.Frame(self.root) + f.pack() + frames.append(f) + for j in 'AB': + b = tkinter.Checkbutton(f, text=j) + b.pack() + buttons.append(b) + variables = [str(b['variable']) for b in buttons] + self.assertEqual(len(set(variables)), 4, variables) + + def test_same_name(self): + f1 = tkinter.Frame(self.root) + f2 = tkinter.Frame(self.root) + b1 = tkinter.Checkbutton(f1, name='test', text='Test1') + b2 = tkinter.Checkbutton(f2, name='test', text='Test2') + + v = tkinter.IntVar(self.root, name='test') + b1.select() + self.assertEqual(v.get(), 1) + b2.deselect() + self.assertEqual(v.get(), 0) + + +@add_standard_options(StandardOptionsTests) +class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'command', 'compound', 'cursor', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'indicatoron', 'justify', 'offrelief', 'overrelief', + 'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state', + 'takefocus', 'text', 'textvariable', + 'tristateimage', 'tristatevalue', + 'underline', 'value', 'variable', 'width', 'wraplength', + ) + + def create(self, **kwargs): + return tkinter.Radiobutton(self.root, **kwargs) + + def test_configure_value(self): + widget = self.create() + self.checkParams(widget, 'value', 1, 2.3, '', 'any string') + + +@add_standard_options(StandardOptionsTests) +class MenubuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'compound', 'cursor', 'direction', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'indicatoron', 'justify', 'menu', + 'padx', 'pady', 'relief', 'state', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength', + ) + _conv_pixels = round + _clip_highlightthickness = True + _clip_pad = True + _clip_borderwidth = False + + def create(self, **kwargs): + return tkinter.Menubutton(self.root, **kwargs) + + def test_configure_direction(self): + widget = self.create() + self.checkEnumParam(widget, 'direction', + 'above', 'below', 'flush', 'left', 'right') + + def test_configure_height(self): + widget = self.create() + self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) + + def test_configure_image(self): + widget = self.create() + image = tkinter.PhotoImage(master=self.root, name='image1') + self.checkParam(widget, 'image', image, conv=str) + errmsg = 'image "spam" doesn\'t exist' + with self.assertRaises(tkinter.TclError) as cm: + widget['image'] = 'spam' + if errmsg is not None: + self.assertEqual(str(cm.exception), errmsg) + with self.assertRaises(tkinter.TclError) as cm: + widget.configure({'image': 'spam'}) + if errmsg is not None: + self.assertEqual(str(cm.exception), errmsg) + + def test_configure_menu(self): + widget = self.create() + menu = tkinter.Menu(widget, name='menu') + self.checkParam(widget, 'menu', menu, eq=widget_eq) + menu.destroy() + + def test_configure_width(self): + widget = self.create() + self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) + + +class OptionMenuTest(MenubuttonTest, unittest.TestCase): + + def create(self, default='b', values=('a', 'b', 'c'), **kwargs): + return tkinter.OptionMenu(self.root, None, default, *values, **kwargs) + + def test_bad_kwarg(self): + with self.assertRaisesRegex(TclError, r"^unknown option -image$"): + tkinter.OptionMenu(self.root, None, 'b', image='') + + +@add_standard_options(IntegerSizeTests, StandardOptionsTests) +class EntryTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', 'cursor', + 'disabledbackground', 'disabledforeground', + 'exportselection', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'invalidcommand', 'justify', 'placeholder', 'placeholderforeground', + 'readonlybackground', 'relief', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'show', 'state', 'takefocus', 'textvariable', + 'validate', 'validatecommand', 'width', 'xscrollcommand', + ) + + def create(self, **kwargs): + return tkinter.Entry(self.root, **kwargs) + + def test_configure_disabledbackground(self): + widget = self.create() + self.checkColorParam(widget, 'disabledbackground') + + def test_configure_insertborderwidth(self): + widget = self.create(insertwidth=100) + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + # insertborderwidth is bounded above by a half of insertwidth. + self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) + + def test_configure_insertwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') + self.checkParam(widget, 'insertwidth', 0.1, expected=2) + self.checkParam(widget, 'insertwidth', -2, expected=2) + self.checkParam(widget, 'insertwidth', 0.9, expected=1) + + def test_configure_invalidcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'invalidcommand') + self.checkCommandParam(widget, 'invcmd') + + def test_configure_readonlybackground(self): + widget = self.create() + self.checkColorParam(widget, 'readonlybackground') + + def test_configure_show(self): + widget = self.create() + self.checkParam(widget, 'show', '*') + self.checkParam(widget, 'show', '') + self.checkParam(widget, 'show', ' ') + + def test_configure_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', + 'disabled', 'normal', 'readonly') + + def test_configure_validate(self): + widget = self.create() + self.checkEnumParam(widget, 'validate', + 'all', 'key', 'focus', 'focusin', 'focusout', 'none') + + def test_configure_validatecommand(self): + widget = self.create() + self.checkCommandParam(widget, 'validatecommand') + self.checkCommandParam(widget, 'vcmd') + + def test_selection_methods(self): + widget = self.create() + widget.insert(0, '12345') + self.assertFalse(widget.selection_present()) + widget.selection_range(0, 'end') + self.assertEqual(widget.selection_get(), '12345') + self.assertTrue(widget.selection_present()) + widget.selection_from(1) + widget.selection_to(2) + self.assertEqual(widget.selection_get(), '2') + widget.selection_range(3, 4) + self.assertEqual(widget.selection_get(), '4') + widget.selection_clear() + self.assertFalse(widget.selection_present()) + widget.selection_range(0, 'end') + widget.selection_adjust(4) + self.assertEqual(widget.selection_get(), '1234') + widget.selection_adjust(1) + self.assertEqual(widget.selection_get(), '234') + widget.selection_adjust(5) + self.assertEqual(widget.selection_get(), '2345') + widget.selection_adjust(0) + self.assertEqual(widget.selection_get(), '12345') + widget.selection_adjust(0) + + +@add_standard_options(StandardOptionsTests) +class SpinboxTest(EntryTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'background', 'borderwidth', + 'buttonbackground', 'buttoncursor', 'buttondownrelief', 'buttonuprelief', + 'command', 'cursor', 'disabledbackground', 'disabledforeground', + 'exportselection', 'font', 'foreground', 'format', 'from', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'increment', + 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'invalidcommand', 'justify', 'placeholder', 'placeholderforeground', + 'relief', 'readonlybackground', 'repeatdelay', 'repeatinterval', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'state', 'takefocus', 'textvariable', 'to', + 'validate', 'validatecommand', 'values', + 'width', 'wrap', 'xscrollcommand', + ) + + def create(self, **kwargs): + return tkinter.Spinbox(self.root, **kwargs) + + test_configure_show = None + + def test_configure_buttonbackground(self): + widget = self.create() + self.checkColorParam(widget, 'buttonbackground') + + def test_configure_buttoncursor(self): + widget = self.create() + self.checkCursorParam(widget, 'buttoncursor') + + def test_configure_buttondownrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'buttondownrelief') + + def test_configure_buttonuprelief(self): + widget = self.create() + self.checkReliefParam(widget, 'buttonuprelief') + + def test_configure_format(self): + widget = self.create() + self.checkParam(widget, 'format', '%2f') + self.checkParam(widget, 'format', '%2.2f') + self.checkParam(widget, 'format', '%.2f') + self.checkParam(widget, 'format', '%2.f') + self.checkInvalidParam(widget, 'format', '%2e-1f') + self.checkInvalidParam(widget, 'format', '2.2') + self.checkInvalidParam(widget, 'format', '%2.-2f') + self.checkParam(widget, 'format', '%-2.02f') + self.checkParam(widget, 'format', '% 2.02f') + self.checkParam(widget, 'format', '% -2.200f') + self.checkParam(widget, 'format', '%09.200f') + self.checkInvalidParam(widget, 'format', '%d') + + def test_configure_from(self): + widget = self.create() + self.checkParam(widget, 'to', 100.0) + self.checkFloatParam(widget, 'from', -10, 10.2, 11.7) + if tk_version >= (8, 7): + self.checkFloatParam(widget, 'from', 200, expected=100) + else: + self.checkInvalidParam( + widget, 'from', 200, + errmsg='-to value must be greater than -from value') + + def test_configure_increment(self): + widget = self.create() + self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0) + + def test_configure_to(self): + widget = self.create() + self.checkParam(widget, 'from', -100.0) + self.checkFloatParam(widget, 'to', -10, 10.2, 11.7) + if tk_version >= (8, 7): + self.checkFloatParam(widget, 'to', -200, expected=-100) + else: + self.checkInvalidParam( + widget, 'to', -200, + errmsg='-to value must be greater than -from value') + + def test_configure_values(self): + # XXX + widget = self.create() + self.assertEqual(widget['values'], '') + self.checkParam(widget, 'values', 'mon tue wed thur') + self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'), + expected='mon tue wed thur') + self.checkParam(widget, 'values', (42, 3.14, '', 'any string'), + expected='42 3.14 {} {any string}') + self.checkParam(widget, 'values', '') + + def test_configure_wrap(self): + widget = self.create() + self.checkBooleanParam(widget, 'wrap') + + def test_bbox(self): + widget = self.create() + self.assertIsBoundingBox(widget.bbox(0)) + self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') + self.assertRaises(tkinter.TclError, widget.bbox, None) + self.assertRaises(TypeError, widget.bbox) + self.assertRaises(TypeError, widget.bbox, 0, 1) + + def test_selection_methods(self): + widget = self.create() + widget.insert(0, '12345') + self.assertFalse(widget.selection_present()) + widget.selection_range(0, 'end') + self.assertEqual(widget.selection_get(), '12345') + self.assertTrue(widget.selection_present()) + widget.selection_from(1) + widget.selection_to(2) + self.assertEqual(widget.selection_get(), '2') + widget.selection_range(3, 4) + self.assertEqual(widget.selection_get(), '4') + widget.selection_clear() + self.assertFalse(widget.selection_present()) + widget.selection_range(0, 'end') + widget.selection_adjust(4) + self.assertEqual(widget.selection_get(), '1234') + widget.selection_adjust(1) + self.assertEqual(widget.selection_get(), '234') + widget.selection_adjust(5) + self.assertEqual(widget.selection_get(), '2345') + widget.selection_adjust(0) + self.assertEqual(widget.selection_get(), '12345') + + def test_selection_element(self): + widget = self.create() + self.assertEqual(widget.selection_element(), "none") + widget.selection_element("buttonup") + self.assertEqual(widget.selection_element(), "buttonup") + widget.selection_element("buttondown") + self.assertEqual(widget.selection_element(), "buttondown") + + +@add_standard_options(StandardOptionsTests) +class TextTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'autoseparators', 'background', 'blockcursor', 'borderwidth', + 'cursor', 'endline', 'exportselection', + 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'inactiveselectbackground', 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertunfocussed', 'insertwidth', + 'maxundo', 'padx', 'pady', 'relief', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'setgrid', 'spacing1', 'spacing2', 'spacing3', 'startline', 'state', + 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap', + 'xscrollcommand', 'yscrollcommand', + ) + + def create(self, **kwargs): + return tkinter.Text(self.root, **kwargs) + + def test_configure_autoseparators(self): + widget = self.create() + self.checkBooleanParam(widget, 'autoseparators') + + def test_configure_blockcursor(self): + widget = self.create() + self.checkBooleanParam(widget, 'blockcursor') + + def test_configure_endline(self): + widget = self.create() + text = '\n'.join('Line %d' for i in range(100)) + widget.insert('end', text) + self.checkParam(widget, 'endline', 200, expected='') + self.checkParam(widget, 'endline', -10, expected='') + self.checkInvalidParam(widget, 'endline', 'spam', + errmsg='expected integer but got "spam"') + self.checkParam(widget, 'endline', 50) + self.checkParam(widget, 'startline', 15) + self.checkInvalidParam(widget, 'endline', 10, + errmsg='-startline must be less than or equal to -endline') + + def test_configure_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') + self.checkParam(widget, 'height', -100, expected=1) + self.checkParam(widget, 'height', 0, expected=1) + + def test_configure_maxundo(self): + widget = self.create() + self.checkIntegerParam(widget, 'maxundo', 0, 5, -1) + + def test_configure_inactiveselectbackground(self): + widget = self.create() + self.checkColorParam(widget, 'inactiveselectbackground') + + @requires_tk(8, 6) + def test_configure_insertunfocussed(self): + widget = self.create() + self.checkEnumParam(widget, 'insertunfocussed', + 'hollow', 'none', 'solid') + + def test_configure_selectborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'selectborderwidth', + 1.3, 2.6, -2, '10p', conv=False) + + def test_configure_spacing1(self): + widget = self.create() + self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c') + self.checkParam(widget, 'spacing1', -5, expected=0) + + def test_configure_spacing2(self): + widget = self.create() + self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c') + self.checkParam(widget, 'spacing2', -1, expected=0) + + def test_configure_spacing3(self): + widget = self.create() + self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') + self.checkParam(widget, 'spacing3', -10, expected=0) + + def test_configure_startline(self): + widget = self.create() + text = '\n'.join('Line %d' for i in range(100)) + widget.insert('end', text) + self.checkParam(widget, 'startline', 200, expected='') + self.checkParam(widget, 'startline', -10, expected='') + self.checkInvalidParam(widget, 'startline', 'spam', + errmsg='expected integer but got "spam"') + self.checkParam(widget, 'startline', 10) + self.checkParam(widget, 'endline', 50) + self.checkInvalidParam(widget, 'startline', 70, + errmsg='-startline must be less than or equal to -endline') + + def test_configure_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'disabled', 'normal') + + def test_configure_tabs(self): + widget = self.create() + self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i')) + self.checkParam(widget, 'tabs', '10.2 20.7 1i 2i', + expected=(10.2, 20.7, '1i', '2i') + if get_tk_patchlevel(self.root) >= (8, 6, 14) + else ('10.2', '20.7', '1i', '2i')) + self.checkParam(widget, 'tabs', '2c left 4c 6c center', + expected=('2c', 'left', '4c', '6c', 'center')) + self.checkInvalidParam(widget, 'tabs', 'spam', + errmsg=EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam')) + + def test_configure_tabstyle(self): + widget = self.create() + self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor') + + def test_configure_undo(self): + widget = self.create() + self.checkBooleanParam(widget, 'undo') + + def test_configure_width(self): + widget = self.create() + self.checkIntegerParam(widget, 'width', 402) + self.checkParam(widget, 'width', -402, expected=1) + self.checkParam(widget, 'width', 0, expected=1) + + def test_configure_wrap(self): + widget = self.create() + self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word') + + def test_bbox(self): + widget = self.create() + self.assertIsBoundingBox(widget.bbox('1.1')) + self.assertIsNone(widget.bbox('end')) + self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') + self.assertRaises(tkinter.TclError, widget.bbox, None) + self.assertRaises(TypeError, widget.bbox) + self.assertRaises(TypeError, widget.bbox, '1.1', 'end') + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class CanvasTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', + 'closeenough', 'confine', 'cursor', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'offset', 'relief', 'scrollregion', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'state', 'takefocus', + 'xscrollcommand', 'xscrollincrement', + 'yscrollcommand', 'yscrollincrement', 'width', + ) + + _conv_pixels = round + _stringify = True + + def create(self, **kwargs): + return tkinter.Canvas(self.root, **kwargs) + + def test_configure_closeenough(self): + widget = self.create() + self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3, + conv=float) + + def test_configure_confine(self): + widget = self.create() + self.checkBooleanParam(widget, 'confine') + + def test_configure_offset(self): + widget = self.create() + self.assertEqual(widget['offset'], '0,0') + self.checkParams(widget, 'offset', + 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center') + self.checkParam(widget, 'offset', '10,20') + self.checkParam(widget, 'offset', '#5,6') + self.checkInvalidParam(widget, 'offset', 'spam') + + def test_configure_scrollregion(self): + widget = self.create() + self.checkParam(widget, 'scrollregion', '0 0 200 150') + self.checkParam(widget, 'scrollregion', (0, 0, 200, 150), + expected='0 0 200 150') + self.checkParam(widget, 'scrollregion', '') + self.checkInvalidParam(widget, 'scrollregion', 'spam', + errmsg='bad scrollRegion "spam"') + self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 'spam')) + self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200)) + self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0)) + + def test_configure_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'disabled', 'normal', + errmsg='bad state value "{}": must be normal or disabled') + + def test_configure_xscrollincrement(self): + widget = self.create() + self.checkPixelsParam(widget, 'xscrollincrement', + 40, 0, 41.2, 43.6, -40, '0.5i') + + def test_configure_yscrollincrement(self): + widget = self.create() + self.checkPixelsParam(widget, 'yscrollincrement', + 10, 0, 11.2, 13.6, -10, '0.1i') + + def _test_option_joinstyle(self, c, factory): + for joinstyle in 'bevel', 'miter', 'round': + i = factory(joinstyle=joinstyle) + self.assertEqual(c.itemcget(i, 'joinstyle'), joinstyle) + self.assertRaises(TclError, factory, joinstyle='spam') + + def _test_option_smooth(self, c, factory): + for smooth in 1, True, '1', 'true', 'yes', 'on': + i = factory(smooth=smooth) + self.assertEqual(c.itemcget(i, 'smooth'), 'true') + for smooth in 0, False, '0', 'false', 'no', 'off': + i = factory(smooth=smooth) + self.assertEqual(c.itemcget(i, 'smooth'), '0') + i = factory(smooth=True, splinestep=30) + self.assertEqual(c.itemcget(i, 'smooth'), 'true') + self.assertEqual(c.itemcget(i, 'splinestep'), '30') + i = factory(smooth='raw', splinestep=30) + self.assertEqual(c.itemcget(i, 'smooth'), 'raw') + self.assertEqual(c.itemcget(i, 'splinestep'), '30') + self.assertRaises(TclError, factory, smooth='spam') + + def test_create_rectangle(self): + c = self.create() + i1 = c.create_rectangle(20, 30, 60, 10) + self.assertEqual(c.coords(i1), [20.0, 10.0, 60.0, 30.0]) + self.assertEqual(c.bbox(i1), (19, 9, 61, 31)) + + i2 = c.create_rectangle([21, 31, 61, 11]) + self.assertEqual(c.coords(i2), [21.0, 11.0, 61.0, 31.0]) + self.assertEqual(c.bbox(i2), (20, 10, 62, 32)) + + i3 = c.create_rectangle((22, 32), (62, 12)) + self.assertEqual(c.coords(i3), [22.0, 12.0, 62.0, 32.0]) + self.assertEqual(c.bbox(i3), (21, 11, 63, 33)) + + i4 = c.create_rectangle([(23, 33), (63, 13)]) + self.assertEqual(c.coords(i4), [23.0, 13.0, 63.0, 33.0]) + self.assertEqual(c.bbox(i4), (22, 12, 64, 34)) + + self.assertRaises(TclError, c.create_rectangle, 20, 30, 60) + self.assertRaises(TclError, c.create_rectangle, [20, 30, 60]) + self.assertRaises(TclError, c.create_rectangle, 20, 30, 40, 50, 60, 10) + self.assertRaises(TclError, c.create_rectangle, [20, 30, 40, 50, 60, 10]) + self.assertRaises(TclError, c.create_rectangle, 20, 30) + self.assertRaises(TclError, c.create_rectangle, [20, 30]) + self.assertRaises(IndexError, c.create_rectangle) + self.assertRaises(IndexError, c.create_rectangle, []) + + def test_create_line(self): + c = self.create() + i1 = c.create_line(20, 30, 40, 50, 60, 10) + self.assertEqual(c.coords(i1), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0]) + self.assertEqual(c.bbox(i1), (18, 8, 62, 52)) + self.assertEqual(c.itemcget(i1, 'arrow'), 'none') + self.assertEqual(c.itemcget(i1, 'arrowshape'), '8 10 3') + self.assertEqual(c.itemcget(i1, 'capstyle'), 'butt') + self.assertEqual(c.itemcget(i1, 'joinstyle'), 'round') + self.assertEqual(c.itemcget(i1, 'smooth'), '0') + self.assertEqual(c.itemcget(i1, 'splinestep'), '12') + + i2 = c.create_line([21, 31, 41, 51, 61, 11]) + self.assertEqual(c.coords(i2), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0]) + self.assertEqual(c.bbox(i2), (19, 9, 63, 53)) + + i3 = c.create_line((22, 32), (42, 52), (62, 12)) + self.assertEqual(c.coords(i3), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0]) + self.assertEqual(c.bbox(i3), (20, 10, 64, 54)) + + i4 = c.create_line([(23, 33), (43, 53), (63, 13)]) + self.assertEqual(c.coords(i4), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0]) + self.assertEqual(c.bbox(i4), (21, 11, 65, 55)) + + self.assertRaises(TclError, c.create_line, 20, 30, 60) + self.assertRaises(TclError, c.create_line, [20, 30, 60]) + self.assertRaises(TclError, c.create_line, 20, 30) + self.assertRaises(TclError, c.create_line, [20, 30]) + self.assertRaises(IndexError, c.create_line) + self.assertRaises(IndexError, c.create_line, []) + + for arrow in 'none', 'first', 'last', 'both': + i = c.create_line(20, 30, 60, 10, arrow=arrow) + self.assertEqual(c.itemcget(i, 'arrow'), arrow) + i = c.create_line(20, 30, 60, 10, arrow='first', arrowshape=[10, 15, 5]) + self.assertEqual(c.itemcget(i, 'arrowshape'), '10 15 5') + self.assertRaises(TclError, c.create_line, 20, 30, 60, 10, arrow='spam') + + for capstyle in 'butt', 'projecting', 'round': + i = c.create_line(20, 30, 60, 10, capstyle=capstyle) + self.assertEqual(c.itemcget(i, 'capstyle'), capstyle) + self.assertRaises(TclError, c.create_line, 20, 30, 60, 10, capstyle='spam') + + self._test_option_joinstyle(c, + lambda **kwargs: c.create_line(20, 30, 40, 50, 60, 10, **kwargs)) + self._test_option_smooth(c, + lambda **kwargs: c.create_line(20, 30, 60, 10, **kwargs)) + + def test_create_polygon(self): + c = self.create() + tk87 = tk_version >= (8, 7) + # In Tk < 8.7 polygons are filled, but has no outline by default. + # This affects its size, so always explicitly specify outline. + i1 = c.create_polygon(20, 30, 40, 50, 60, 10, outline='red') + self.assertEqual(c.coords(i1), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0]) + self.assertEqual(c.bbox(i1), (18, 8, 62, 52)) + self.assertEqual(c.itemcget(i1, 'joinstyle'), 'round') + self.assertEqual(c.itemcget(i1, 'smooth'), '0') + self.assertEqual(c.itemcget(i1, 'splinestep'), '12') + + i2 = c.create_polygon([21, 31, 41, 51, 61, 11], outline='red') + self.assertEqual(c.coords(i2), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0]) + self.assertEqual(c.bbox(i2), (19, 9, 63, 53)) + + i3 = c.create_polygon((22, 32), (42, 52), (62, 12), outline='red') + self.assertEqual(c.coords(i3), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0]) + self.assertEqual(c.bbox(i3), (20, 10, 64, 54)) + + i4 = c.create_polygon([(23, 33), (43, 53), (63, 13)], outline='red') + self.assertEqual(c.coords(i4), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0]) + self.assertEqual(c.bbox(i4), (21, 11, 65, 55)) + + self.assertRaises(TclError, c.create_polygon, 20, 30, 60) + self.assertRaises(TclError, c.create_polygon, [20, 30, 60]) + self.assertRaises(IndexError, c.create_polygon) + self.assertRaises(IndexError, c.create_polygon, []) + + self._test_option_joinstyle(c, + lambda **kwargs: c.create_polygon(20, 30, 40, 50, 60, 10, **kwargs)) + self._test_option_smooth(c, + lambda **kwargs: c.create_polygon(20, 30, 40, 50, 60, 10, **kwargs)) + + def test_coords(self): + c = self.create() + i = c.create_line(20, 30, 40, 50, 60, 10, tags='x') + self.assertEqual(c.coords(i), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0]) + self.assertEqual(c.coords('x'), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0]) + self.assertEqual(c.bbox(i), (18, 8, 62, 52)) + + c.coords(i, 50, 60, 70, 80, 90, 40) + self.assertEqual(c.coords(i), [50.0, 60.0, 70.0, 80.0, 90.0, 40.0]) + self.assertEqual(c.bbox(i), (48, 38, 92, 82)) + + c.coords(i, [21, 31, 41, 51, 61, 11]) + self.assertEqual(c.coords(i), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0]) + + c.coords(i, (22, 32), (42, 52), (62, 12)) + self.assertEqual(c.coords(i), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0]) + + c.coords(i, [(23, 33), (43, 53), (63, 13)]) + self.assertEqual(c.coords(i), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0]) + + c.coords(i, 20, 30, 60, 10) + self.assertEqual(c.coords(i), [20.0, 30.0, 60.0, 10.0]) + self.assertEqual(c.bbox(i), (18, 8, 62, 32)) + + self.assertRaises(TclError, c.coords, i, 20, 30, 60) + self.assertRaises(TclError, c.coords, i, [20, 30, 60]) + self.assertRaises(TclError, c.coords, i, 20, 30) + self.assertRaises(TclError, c.coords, i, [20, 30]) + + c.coords(i, '20', '30c', '60i', '10p') + coords = c.coords(i) + self.assertIsInstance(coords, list) + self.assertEqual(len(coords), 4) + self.assertEqual(coords[0], 20) + for i in range(4): + self.assertIsInstance(coords[i], float) + + @requires_tk(8, 6) + def test_moveto(self): + widget = self.create() + i1 = widget.create_rectangle(1, 1, 20, 20, tags='group') + i2 = widget.create_rectangle(30, 30, 50, 70, tags='group') + x1, y1, _, _ = widget.bbox(i1) + x2, y2, _, _ = widget.bbox(i2) + widget.moveto('group', 200, 100) + x1_2, y1_2, _, _ = widget.bbox(i1) + x2_2, y2_2, _, _ = widget.bbox(i2) + self.assertEqual(x1_2, 200) + self.assertEqual(y1_2, 100) + self.assertEqual(x2 - x1, x2_2 - x1_2) + self.assertEqual(y2 - y1, y2_2 - y1_2) + widget.tag_lower(i2, i1) + widget.moveto('group', y=50) + x1_3, y1_3, _, _ = widget.bbox(i1) + x2_3, y2_3, _, _ = widget.bbox(i2) + self.assertEqual(y2_3, 50) + self.assertEqual(x2_3, x2_2) + self.assertEqual(x2_2 - x1_2, x2_3 - x1_3) + self.assertEqual(y2_2 - y1_2, y2_3 - y1_3) + + +@add_standard_options(IntegerSizeTests, StandardOptionsTests) +class ListboxTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activestyle', 'background', 'borderwidth', 'cursor', + 'disabledforeground', 'exportselection', + 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'justify', 'listvariable', 'relief', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'selectmode', 'setgrid', 'state', + 'takefocus', 'width', 'xscrollcommand', 'yscrollcommand', + ) + + def create(self, **kwargs): + return tkinter.Listbox(self.root, **kwargs) + + def test_configure_activestyle(self): + widget = self.create() + self.checkEnumParam(widget, 'activestyle', + 'dotbox', 'none', 'underline') + + test_configure_justify = requires_tk(8, 6, 5)(StandardOptionsTests.test_configure_justify) + + def test_configure_listvariable(self): + widget = self.create() + var = tkinter.DoubleVar(self.root) + self.checkVariableParam(widget, 'listvariable', var) + + def test_configure_selectmode(self): + widget = self.create() + self.checkParam(widget, 'selectmode', 'single') + self.checkParam(widget, 'selectmode', 'browse') + self.checkParam(widget, 'selectmode', 'multiple') + self.checkParam(widget, 'selectmode', 'extended') + + def test_configure_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'disabled', 'normal') + + def test_itemconfigure(self): + widget = self.create() + with self.assertRaisesRegex(TclError, 'item number "0" out of range'): + widget.itemconfigure(0) + colors = 'red orange yellow green blue white violet'.split() + widget.insert('end', *colors) + for i, color in enumerate(colors): + widget.itemconfigure(i, background=color) + with self.assertRaises(TypeError): + widget.itemconfigure() + with self.assertRaisesRegex(TclError, 'bad listbox index "red"'): + widget.itemconfigure('red') + if get_tk_patchlevel(self.root) >= (8, 6, 14): + prefix = ('background', '', '', '') + else: + prefix = ('background', 'background', 'Background', '') + self.assertEqual(widget.itemconfigure(0, 'background'), + (*prefix, 'red')) + self.assertEqual(widget.itemconfigure('end', 'background'), + (*prefix, 'violet')) + self.assertEqual(widget.itemconfigure('@0,0', 'background'), + (*prefix, 'red')) + + d = widget.itemconfigure(0) + self.assertIsInstance(d, dict) + for k, v in d.items(): + self.assertIn(len(v), (2, 5)) + if len(v) == 5: + self.assertEqual(v, widget.itemconfigure(0, k)) + self.assertEqual(v[4], widget.itemcget(0, k)) + + def check_itemconfigure(self, name, value): + widget = self.create() + widget.insert('end', 'a', 'b', 'c', 'd') + widget.itemconfigure(0, **{name: value}) + self.assertEqual(widget.itemconfigure(0, name)[4], value) + self.assertEqual(widget.itemcget(0, name), value) + with self.assertRaisesRegex(TclError, 'unknown color name "spam"'): + widget.itemconfigure(0, **{name: 'spam'}) + + def test_itemconfigure_background(self): + self.check_itemconfigure('background', '#ff0000') + + def test_itemconfigure_bg(self): + self.check_itemconfigure('bg', '#ff0000') + + def test_itemconfigure_fg(self): + self.check_itemconfigure('fg', '#110022') + + def test_itemconfigure_foreground(self): + self.check_itemconfigure('foreground', '#110022') + + def test_itemconfigure_selectbackground(self): + self.check_itemconfigure('selectbackground', '#110022') + + def test_itemconfigure_selectforeground(self): + self.check_itemconfigure('selectforeground', '#654321') + + def test_box(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.pack() + self.assertIsBoundingBox(lb.bbox(0)) + self.assertIsNone(lb.bbox(-1)) + self.assertIsNone(lb.bbox(10)) + self.assertRaises(TclError, lb.bbox, 'noindex') + self.assertRaises(TclError, lb.bbox, None) + self.assertRaises(TypeError, lb.bbox) + self.assertRaises(TypeError, lb.bbox, 0, 1) + + def test_curselection(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + lb.selection_clear(0, tkinter.END) + lb.selection_set(2, 4) + lb.selection_set(6) + self.assertEqual(lb.curselection(), (2, 3, 4, 6)) + self.assertRaises(TypeError, lb.curselection, 0) + + def test_get(self): + lb = self.create() + lb.insert(0, *('el%d' % i for i in range(8))) + self.assertEqual(lb.get(0), 'el0') + self.assertEqual(lb.get(3), 'el3') + self.assertEqual(lb.get('end'), 'el7') + self.assertEqual(lb.get(8), '') + self.assertEqual(lb.get(-1), '') + self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5')) + self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7')) + self.assertEqual(lb.get(5, 0), ()) + self.assertEqual(lb.get(0, 0), ('el0',)) + self.assertRaises(TclError, lb.get, 'noindex') + self.assertRaises(TclError, lb.get, None) + self.assertRaises(TypeError, lb.get) + self.assertRaises(TclError, lb.get, 'end', 'noindex') + self.assertRaises(TypeError, lb.get, 1, 2, 3) + self.assertRaises(TclError, lb.get, 2.4) + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class ScaleTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'background', 'bigincrement', 'borderwidth', + 'command', 'cursor', 'digits', 'font', 'foreground', 'from', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'label', 'length', 'orient', 'relief', + 'repeatdelay', 'repeatinterval', + 'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state', + 'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width', + ) + default_orient = 'vertical' + + def create(self, **kwargs): + return tkinter.Scale(self.root, **kwargs) + + def test_configure_bigincrement(self): + widget = self.create() + self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5) + + def test_configure_digits(self): + widget = self.create() + self.checkIntegerParam(widget, 'digits', 5, 0) + + def test_configure_from(self): + widget = self.create() + conv = float if get_tk_patchlevel(self.root) >= (8, 6, 10) else float_round + self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv) + + def test_configure_label(self): + widget = self.create() + self.checkParam(widget, 'label', 'any string') + self.checkParam(widget, 'label', '') + + def test_configure_length(self): + widget = self.create() + self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') + + def test_configure_resolution(self): + widget = self.create() + self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2) + + def test_configure_showvalue(self): + widget = self.create() + self.checkBooleanParam(widget, 'showvalue') + + def test_configure_sliderlength(self): + widget = self.create() + self.checkPixelsParam(widget, 'sliderlength', + 10, 11.2, 15.6, -3, '3m') + + def test_configure_sliderrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'sliderrelief') + + def test_configure_tickinterval(self): + widget = self.create() + self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0, + conv=float_round) + self.checkParam(widget, 'tickinterval', -2, expected=2, + conv=float_round) + + def test_configure_to(self): + widget = self.create() + self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, + conv=float_round) + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activerelief', + 'background', 'borderwidth', + 'command', 'cursor', 'elementborderwidth', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'jump', 'orient', 'relief', + 'repeatdelay', 'repeatinterval', + 'takefocus', 'troughcolor', 'width', + ) + _conv_pixels = round + _stringify = True + default_orient = 'vertical' + + def create(self, **kwargs): + return tkinter.Scrollbar(self.root, **kwargs) + + def test_configure_elementborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, '1m') + expected = self._default_pixels if tk_version >= (8, 7) else -2 + self.checkParam(widget, 'elementborderwidth', -2, expected=expected) + + def test_configure_orient(self): + widget = self.create() + self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal', + fullname='orientation', allow_empty=True) + + def test_activate(self): + sb = self.create() + for e in ('arrow1', 'slider', 'arrow2'): + sb.activate(e) + self.assertEqual(sb.activate(), e) + sb.activate('') + self.assertIsNone(sb.activate()) + self.assertRaises(TypeError, sb.activate, 'arrow1', 'arrow2') + + def test_set(self): + sb = self.create() + sb.set(0.2, 0.4) + self.assertEqual(sb.get(), (0.2, 0.4)) + self.assertRaises(TclError, sb.set, 'abc', 'def') + self.assertRaises(TclError, sb.set, 0.6, 'def') + self.assertRaises(TclError, sb.set, 0.6, None) + self.assertRaises(TypeError, sb.set, 0.6) + self.assertRaises(TypeError, sb.set, 0.6, 0.7, 0.8) + + +@add_standard_options(StandardOptionsTests) +class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', 'cursor', + 'handlepad', 'handlesize', 'height', + 'opaqueresize', 'orient', + 'proxybackground', 'proxyborderwidth', 'proxyrelief', + 'relief', + 'sashcursor', 'sashpad', 'sashrelief', 'sashwidth', + 'showhandle', 'width', + ) + default_orient = 'horizontal' + + def create(self, **kwargs): + return tkinter.PanedWindow(self.root, **kwargs) + + def test_configure_handlepad(self): + widget = self.create() + self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m') + + def test_configure_handlesize(self): + widget = self.create() + self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', + conv=False) + + def test_configure_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', + conv=False) + + def test_configure_opaqueresize(self): + widget = self.create() + self.checkBooleanParam(widget, 'opaqueresize') + + @requires_tk(8, 6, 5) + def test_configure_proxybackground(self): + widget = self.create() + self.checkColorParam(widget, 'proxybackground') + + @requires_tk(8, 6, 5) + def test_configure_proxyborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'proxyborderwidth', + 0, 1.3, 2.9, 6, -2, '10p', + conv=False) + + @requires_tk(8, 6, 5) + def test_configure_proxyrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'proxyrelief', + allow_empty=(tk_version >= (8, 7))) + + def test_configure_sashcursor(self): + widget = self.create() + self.checkCursorParam(widget, 'sashcursor') + + def test_configure_sashpad(self): + widget = self.create() + self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m') + + def test_configure_sashrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'sashrelief') + + def test_configure_sashwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', + conv=False) + + def test_configure_showhandle(self): + widget = self.create() + self.checkBooleanParam(widget, 'showhandle') + + def test_configure_width(self): + widget = self.create() + self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', + conv=False) + + def create2(self): + p = self.create() + b = tkinter.Button(p) + c = tkinter.Button(p) + p.add(b) + p.add(c) + return p, b, c + + def test_paneconfigure(self): + p, b, c = self.create2() + self.assertRaises(TypeError, p.paneconfigure) + d = p.paneconfigure(b) + self.assertIsInstance(d, dict) + for k, v in d.items(): + self.assertEqual(len(v), 5) + self.assertEqual(v, p.paneconfigure(b, k)) + self.assertEqual(v[4], p.panecget(b, k)) + + def check_paneconfigure(self, p, b, name, value, expected): + if not self.wantobjects: + expected = str(expected) + p.paneconfigure(b, **{name: value}) + self.assertEqual(p.paneconfigure(b, name)[4], expected) + self.assertEqual(p.panecget(b, name), expected) + + def check_paneconfigure_bad(self, p, b, name, msg): + with self.assertRaisesRegex(TclError, msg): + p.paneconfigure(b, **{name: 'badValue'}) + + def test_paneconfigure_after(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'after', c, str(c)) + self.check_paneconfigure_bad(p, b, 'after', + 'bad window path name "badValue"') + + def test_paneconfigure_before(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'before', c, str(c)) + self.check_paneconfigure_bad(p, b, 'before', + 'bad window path name "badValue"') + + def test_paneconfigure_height(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'height', 10, 10) + self.check_paneconfigure_bad(p, b, 'height', + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue')) + + def test_paneconfigure_hide(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'hide', False, 0) + self.check_paneconfigure_bad(p, b, 'hide', + 'expected boolean value but got "badValue"') + + def test_paneconfigure_minsize(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'minsize', 10, 10) + self.check_paneconfigure_bad(p, b, 'minsize', + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) + + def test_paneconfigure_padx(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'padx', 1.3, 1) + self.check_paneconfigure_bad(p, b, 'padx', + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) + + def test_paneconfigure_pady(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'pady', 1.3, 1) + self.check_paneconfigure_bad(p, b, 'pady', + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) + + def test_paneconfigure_sticky(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'sticky', 'nsew', 'nesw') + self.check_paneconfigure_bad(p, b, 'sticky', + 'bad stickyness value "badValue": must ' + 'be a string containing zero or more of ' + 'n, e, s, and w') + + def test_paneconfigure_stretch(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'stretch', 'alw', 'always') + self.check_paneconfigure_bad(p, b, 'stretch', + 'bad stretch "badValue": must be ' + 'always, first, last, middle, or never') + + def test_paneconfigure_width(self): + p, b, c = self.create2() + self.check_paneconfigure(p, b, 'width', 10, 10) + self.check_paneconfigure_bad(p, b, 'width', + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue')) + + +@add_standard_options(StandardOptionsTests) +class MenuTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeborderwidth', 'activeforeground', + 'activerelief', + 'background', 'borderwidth', 'cursor', + 'disabledforeground', 'font', 'foreground', + 'postcommand', 'relief', 'selectcolor', 'takefocus', + 'tearoff', 'tearoffcommand', 'title', 'type', + ) + _conv_pixels = False + + def create(self, **kwargs): + return tkinter.Menu(self.root, **kwargs) + + def test_indexcommand_none(self): + widget = self.create() + i = widget.index('none') + self.assertIsNone(i) + + test_configure_activerelief = requires_tk(8, 7)(StandardOptionsTests.test_configure_activerelief) + + def test_configure_postcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'postcommand') + + def test_configure_tearoff(self): + widget = self.create() + self.checkBooleanParam(widget, 'tearoff') + + def test_configure_tearoffcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'tearoffcommand') + + def test_configure_title(self): + widget = self.create() + self.checkParam(widget, 'title', 'any string') + + def test_configure_type(self): + widget = self.create() + values = ('normal', 'tearoff', 'menubar') + self.checkEnumParam(widget, 'type', *values, + allow_empty=tk_version < (8, 7), + sort=tk_version >= (8, 7)) + + def test_entryconfigure(self): + m1 = self.create() + m1.add_command(label='test') + self.assertRaises(TypeError, m1.entryconfigure) + with self.assertRaisesRegex(TclError, 'bad menu entry index "foo"'): + m1.entryconfigure('foo') + d = m1.entryconfigure(1) + self.assertIsInstance(d, dict) + for k, v in d.items(): + self.assertIsInstance(k, str) + self.assertIsInstance(v, tuple) + self.assertEqual(len(v), 5) + self.assertEqual(v[0], k) + self.assertEqual(m1.entrycget(1, k), v[4]) + m1.destroy() + + def test_entryconfigure_label(self): + m1 = self.create() + m1.add_command(label='test') + self.assertEqual(m1.entrycget(1, 'label'), 'test') + m1.entryconfigure(1, label='changed') + self.assertEqual(m1.entrycget(1, 'label'), 'changed') + + def test_entryconfigure_variable(self): + m1 = self.create() + v1 = tkinter.BooleanVar(self.root) + v2 = tkinter.BooleanVar(self.root) + m1.add_checkbutton(variable=v1, onvalue=True, offvalue=False, + label='Nonsense') + self.assertEqual(str(m1.entrycget(1, 'variable')), str(v1)) + m1.entryconfigure(1, variable=v2) + self.assertEqual(str(m1.entrycget(1, 'variable')), str(v2)) + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class MessageTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'anchor', 'aspect', 'background', 'borderwidth', + 'cursor', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'justify', 'padx', 'pady', 'relief', + 'takefocus', 'text', 'textvariable', 'width', + ) + _conv_pad_pixels = False + if tk_version >= (8, 7): + _conv_pixels = False + _clip_pad = tk_version >= (8, 7) + _clip_borderwidth = tk_version >= (8, 7) + + def create(self, **kwargs): + return tkinter.Message(self.root, **kwargs) + + def test_configure_aspect(self): + widget = self.create() + self.checkIntegerParam(widget, 'aspect', 250, 0, -300) + + def test_configure_padx(self): + widget = self.create() + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m', + conv=self._conv_pad_pixels) + expected = self._default_pixels if self._clip_pad else -2 + self.checkParam(widget, 'padx', -2, expected=expected) + + def test_configure_pady(self): + widget = self.create() + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m', + conv=self._conv_pad_pixels) + expected = self._default_pixels if self._clip_pad else -2 + self.checkParam(widget, 'pady', -2, expected=expected) + + def test_configure_width(self): + widget = self.create() + self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, 0, '5i') + expected = 0 if tk_version >= (8, 7) else -402 + self.checkParam(widget, 'width', -402, expected=expected) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_frame(self): + self._test_widget(tkinter.Frame) + + def test_label(self): + self._test_widget(tkinter.Label) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py new file mode 100644 index 00000000000..8ab2f742450 --- /dev/null +++ b/Lib/test/test_tkinter/widget_tests.py @@ -0,0 +1,583 @@ +# Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py + +import re +import tkinter +from test.test_tkinter.support import (AbstractTkTest, requires_tk, tk_version, + pixels_conv, tcl_obj_eq) +import test.support + + +_sentinel = object() + +class AbstractWidgetTest(AbstractTkTest): + _default_pixels = '' if tk_version >= (9, 0) else -1 if tk_version >= (8, 7) else '' + _conv_pixels = round + _conv_pad_pixels = None + _stringify = False + _clip_highlightthickness = True + _clip_pad = False + _clip_borderwidth = False + _allow_empty_justify = False + + @property + def scaling(self): + try: + return self._scaling + except AttributeError: + self._scaling = float(self.root.call('tk', 'scaling')) + return self._scaling + + def _str(self, value): + if not self._stringify and self.wantobjects and tk_version >= (8, 6): + return value + if isinstance(value, tuple): + return ' '.join(map(self._str, value)) + return str(value) + + def assertEqual2(self, actual, expected, msg=None, eq=object.__eq__): + if eq(actual, expected): + return + self.assertEqual(actual, expected, msg) + + def checkParam(self, widget, name, value, *, expected=_sentinel, + conv=False, eq=None): + widget[name] = value + if expected is _sentinel: + expected = value + if conv: + expected = conv(expected) + if self._stringify or not self.wantobjects: + if isinstance(expected, tuple): + expected = tkinter._join(expected) + else: + expected = str(expected) + if eq is None: + eq = tcl_obj_eq + self.assertEqual2(widget[name], expected, eq=eq) + self.assertEqual2(widget.cget(name), expected, eq=eq) + t = widget.configure(name) + self.assertEqual(len(t), 5) + self.assertEqual2(t[4], expected, eq=eq) + + def checkInvalidParam(self, widget, name, value, errmsg=None): + orig = widget[name] + if errmsg is not None: + errmsg = errmsg.format(re.escape(str(value))) + errmsg = fr'\A{errmsg}\Z' + with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): + widget[name] = value + self.assertEqual(widget[name], orig) + with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): + widget.configure({name: value}) + self.assertEqual(widget[name], orig) + + def checkParams(self, widget, name, *values, **kwargs): + for value in values: + self.checkParam(widget, name, value, **kwargs) + + def checkIntegerParam(self, widget, name, *values, **kwargs): + self.checkParams(widget, name, *values, **kwargs) + errmsg = 'expected integer but got "{}"' + self.checkInvalidParam(widget, name, '', errmsg=errmsg) + self.checkInvalidParam(widget, name, '10p', errmsg=errmsg) + self.checkInvalidParam(widget, name, 3.2, errmsg=errmsg) + + def checkFloatParam(self, widget, name, *values, conv=float, **kwargs): + for value in values: + self.checkParam(widget, name, value, conv=conv, **kwargs) + errmsg = 'expected floating-point number but got "{}"' + self.checkInvalidParam(widget, name, '', errmsg=errmsg) + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) + + def checkBooleanParam(self, widget, name): + for value in (False, 0, 'false', 'no', 'off'): + self.checkParam(widget, name, value, expected=0) + for value in (True, 1, 'true', 'yes', 'on'): + self.checkParam(widget, name, value, expected=1) + errmsg = 'expected boolean value but got "{}"' + self.checkInvalidParam(widget, name, '', errmsg=errmsg) + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) + + def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs): + self.checkParams(widget, name, + '#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey', + **kwargs) + self.checkInvalidParam(widget, name, 'spam', + errmsg='unknown color name "spam"') + + def checkCursorParam(self, widget, name, **kwargs): + self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs) + self.checkParam(widget, name, 'none') + self.checkInvalidParam(widget, name, 'spam', + errmsg='bad cursor spec "spam"') + + def checkCommandParam(self, widget, name): + def command(*args): + pass + widget[name] = command + self.assertTrue(widget[name]) + self.checkParams(widget, name, '') + + def checkEnumParam(self, widget, name, *values, + errmsg=None, allow_empty=False, fullname=None, + sort=False, **kwargs): + self.checkParams(widget, name, *values, **kwargs) + if errmsg is None: + if sort: + if values[-1]: + values = tuple(sorted(values)) + else: + values = tuple(sorted(values[:-1])) + ('',) + errmsg2 = ' %s "{}": must be %s%s or %s' % ( + fullname or name, + ', '.join(values[:-1]), + ',' if len(values) > 2 else '', + values[-1] or '""') + if '' not in values and not allow_empty: + self.checkInvalidParam(widget, name, '', + errmsg='ambiguous' + errmsg2) + errmsg = 'bad' + errmsg2 + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) + + def checkPixelsParam(self, widget, name, *values, + conv=None, **kwargs): + if conv is None: + conv = self._conv_pixels + for value in values: + expected = _sentinel + conv1 = conv + if isinstance(value, str): + if conv1 and conv1 is not str: + expected = pixels_conv(value) * self.scaling + conv1 = round + self.checkParam(widget, name, value, expected=expected, + conv=conv1, **kwargs) + errmsg = '(bad|expected) screen distance ((or "" )?but got )?"{}"' + self.checkInvalidParam(widget, name, '6x', errmsg=errmsg) + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) + + def checkReliefParam(self, widget, name, *, allow_empty=False): + values = ('flat', 'groove', 'raised', 'ridge', 'solid', 'sunken') + if allow_empty: + values += ('',) + self.checkParams(widget, name, *values) + errmsg = 'bad relief "{}": must be %s, or %s' % ( + ', '.join(values[:-1]), + values[-1] or '""') + if tk_version < (8, 6): + errmsg = None + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) + + def checkImageParam(self, widget, name): + image = tkinter.PhotoImage(master=self.root, name='image1') + self.checkParam(widget, name, image, conv=str) + self.checkInvalidParam(widget, name, 'spam', + errmsg='image "spam" doesn\'t exist') + widget[name] = '' + + def checkVariableParam(self, widget, name, var): + self.checkParam(widget, name, var, conv=str) + + def assertIsBoundingBox(self, bbox): + self.assertIsNotNone(bbox) + self.assertIsInstance(bbox, tuple) + if len(bbox) != 4: + self.fail('Invalid bounding box: %r' % (bbox,)) + for item in bbox: + if not isinstance(item, int): + self.fail('Invalid bounding box: %r' % (bbox,)) + break + + + def test_keys(self): + widget = self.create() + keys = widget.keys() + self.assertEqual(sorted(keys), sorted(widget.configure())) + for k in keys: + widget[k] + # Test if OPTIONS contains all keys + if test.support.verbose: + aliases = { + 'bd': 'borderwidth', + 'bg': 'background', + 'bgimg': 'backgroundimage', + 'fg': 'foreground', + 'invcmd': 'invalidcommand', + 'vcmd': 'validatecommand', + } + keys = set(keys) + expected = set(self.OPTIONS) + for k in sorted(keys - expected): + if not (k in aliases and + aliases[k] in keys and + aliases[k] in expected): + print('%s.OPTIONS doesn\'t contain "%s"' % + (self.__class__.__name__, k)) + + +class StandardOptionsTests: + STANDARD_OPTIONS = ( + 'activebackground', 'activeborderwidth', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', 'compound', 'cursor', + 'disabledforeground', 'exportselection', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'jump', 'justify', 'orient', 'padx', 'pady', 'relief', + 'repeatdelay', 'repeatinterval', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'setgrid', 'takefocus', 'text', 'textvariable', 'troughcolor', + 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand', + ) + + def test_configure_activebackground(self): + widget = self.create() + self.checkColorParam(widget, 'activebackground') + + def test_configure_activeborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'activeborderwidth', + 0, 1.3, 2.9, 6, -2, '10p') + + def test_configure_activeforeground(self): + widget = self.create() + self.checkColorParam(widget, 'activeforeground') + + def test_configure_activerelief(self): + widget = self.create() + self.checkReliefParam(widget, 'activerelief') + + def test_configure_anchor(self): + widget = self.create() + self.checkEnumParam(widget, 'anchor', + 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center') + + def test_configure_background(self): + widget = self.create() + self.checkColorParam(widget, 'background') + if 'bg' in self.OPTIONS: + self.checkColorParam(widget, 'bg') + + @requires_tk(8, 7) + def test_configure_backgroundimage(self): + widget = self.create() + self.checkImageParam(widget, 'backgroundimage') + + def test_configure_bitmap(self): + widget = self.create() + self.checkParam(widget, 'bitmap', 'questhead') + self.checkParam(widget, 'bitmap', 'gray50') + filename = test.support.findfile('python.xbm', subdir='tkinterdata') + self.checkParam(widget, 'bitmap', '@' + filename) + # Cocoa Tk widgets don't detect invalid -bitmap values + # See https://core.tcl.tk/tk/info/31cd33dbf0 + if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and + 'AppKit' in self.root.winfo_server()): + self.checkInvalidParam(widget, 'bitmap', 'spam', + errmsg='bitmap "spam" not defined') + + def test_configure_borderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'borderwidth', + 0, 1.3, 2.6, 6, '10p') + expected = 0 if self._clip_borderwidth else -2 + self.checkParam(widget, 'borderwidth', -2, expected=expected, + conv=self._conv_pixels) + if 'bd' in self.OPTIONS: + self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'bd', -2, expected=expected, + conv=self._conv_pixels) + + def test_configure_compound(self): + widget = self.create() + self.checkEnumParam(widget, 'compound', + 'bottom', 'center', 'left', 'none', 'right', 'top') + + def test_configure_cursor(self): + widget = self.create() + self.checkCursorParam(widget, 'cursor') + + def test_configure_disabledforeground(self): + widget = self.create() + self.checkColorParam(widget, 'disabledforeground') + + def test_configure_exportselection(self): + widget = self.create() + self.checkBooleanParam(widget, 'exportselection') + + def test_configure_font(self): + widget = self.create() + self.checkParam(widget, 'font', + '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') + is_ttk = widget.__class__.__module__ == 'tkinter.ttk' + if not is_ttk: + self.checkInvalidParam(widget, 'font', '', + errmsg='font "" doesn\'t exist') + + def test_configure_foreground(self): + widget = self.create() + self.checkColorParam(widget, 'foreground') + if 'fg' in self.OPTIONS: + self.checkColorParam(widget, 'fg') + + def test_configure_highlightbackground(self): + widget = self.create() + self.checkColorParam(widget, 'highlightbackground') + + def test_configure_highlightcolor(self): + widget = self.create() + self.checkColorParam(widget, 'highlightcolor') + + def test_configure_highlightthickness(self): + widget = self.create() + self.checkPixelsParam(widget, 'highlightthickness', + 0, 1.3, 2.6, 6, '10p') + expected = 0 if self._clip_highlightthickness else -2 + self.checkParam(widget, 'highlightthickness', -2, expected=expected, + conv=self._conv_pixels) + + def test_configure_image(self): + widget = self.create() + self.checkImageParam(widget, 'image') + + def test_configure_insertbackground(self): + widget = self.create() + self.checkColorParam(widget, 'insertbackground') + + def test_configure_insertborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + + def test_configure_insertofftime(self): + widget = self.create() + self.checkIntegerParam(widget, 'insertofftime', 100) + + def test_configure_insertontime(self): + widget = self.create() + self.checkIntegerParam(widget, 'insertontime', 100) + + def test_configure_insertwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') + + def test_configure_jump(self): + widget = self.create() + self.checkBooleanParam(widget, 'jump') + + def test_configure_justify(self): + widget = self.create() + values = ('left', 'right', 'center') + if self._allow_empty_justify: + values += ('',) + self.checkEnumParam(widget, 'justify', *values, + fullname='justification') + + def test_configure_orient(self): + widget = self.create() + self.assertEqual(str(widget['orient']), self.default_orient) + self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical') + + def test_configure_padx(self): + widget = self.create() + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m', + conv=self._conv_pad_pixels) + expected = 0 if self._clip_pad else -2 + self.checkParam(widget, 'padx', -2, expected=expected, + conv=self._conv_pad_pixels) + + def test_configure_pady(self): + widget = self.create() + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m', + conv=self._conv_pad_pixels) + expected = 0 if self._clip_pad else -2 + self.checkParam(widget, 'pady', -2, expected=expected, + conv=self._conv_pad_pixels) + + @requires_tk(8, 7) + def test_configure_placeholder(self): + widget = self.create() + self.checkParam(widget, 'placeholder', 'xxx') + + @requires_tk(8, 7) + def test_configure_placeholderforeground(self): + widget = self.create() + self.checkColorParam(widget, 'placeholderforeground') + + def test_configure_relief(self): + widget = self.create() + self.checkReliefParam(widget, 'relief') + + def test_configure_repeatdelay(self): + widget = self.create() + self.checkIntegerParam(widget, 'repeatdelay', -500, 500) + + def test_configure_repeatinterval(self): + widget = self.create() + self.checkIntegerParam(widget, 'repeatinterval', -500, 500) + + def test_configure_selectbackground(self): + widget = self.create() + self.checkColorParam(widget, 'selectbackground') + + def test_configure_selectborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') + + def test_configure_selectforeground(self): + widget = self.create() + self.checkColorParam(widget, 'selectforeground') + + def test_configure_setgrid(self): + widget = self.create() + self.checkBooleanParam(widget, 'setgrid') + + def test_configure_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal') + + def test_configure_takefocus(self): + widget = self.create() + self.checkParams(widget, 'takefocus', '0', '1', '') + + def test_configure_text(self): + widget = self.create() + self.checkParams(widget, 'text', '', 'any string') + + def test_configure_textvariable(self): + widget = self.create() + var = tkinter.StringVar(self.root) + self.checkVariableParam(widget, 'textvariable', var) + + @requires_tk(8, 7) + def test_configure_tile(self): + widget = self.create() + self.checkBooleanParam(widget, 'tile') + + def test_configure_troughcolor(self): + widget = self.create() + self.checkColorParam(widget, 'troughcolor') + + def test_configure_underline(self): + widget = self.create() + self.checkParams(widget, 'underline', 0, 1, 10) + if tk_version >= (8, 7): + is_ttk = widget.__class__.__module__ == 'tkinter.ttk' + self.checkParam(widget, 'underline', '', + expected='' if is_ttk else self._default_pixels) + self.checkParam(widget, 'underline', '5+2', + expected='5+2' if is_ttk else 7) + self.checkParam(widget, 'underline', '5-2', + expected='5-2' if is_ttk else 3) + self.checkParam(widget, 'underline', 'end', expected='end') + self.checkParam(widget, 'underline', 'end-2', expected='end-2') + errmsg = (r'bad index "{}": must be integer\?\[\+-\]integer\?, ' + r'end\?\[\+-\]integer\?, or ""') + else: + errmsg = 'expected integer but got "{}"' + self.checkInvalidParam(widget, 'underline', '', errmsg=errmsg) + self.checkInvalidParam(widget, 'underline', '10p', errmsg=errmsg) + self.checkInvalidParam(widget, 'underline', 3.2, errmsg=errmsg) + + def test_configure_wraplength(self): + widget = self.create() + self.checkPixelsParam(widget, 'wraplength', 100) + + def test_configure_xscrollcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'xscrollcommand') + + def test_configure_yscrollcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'yscrollcommand') + + # non-standard but common options + + def test_configure_command(self): + widget = self.create() + self.checkCommandParam(widget, 'command') + + def test_configure_indicatoron(self): + widget = self.create() + self.checkBooleanParam(widget, 'indicatoron') + + def test_configure_offrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'offrelief') + + def test_configure_overrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'overrelief', + allow_empty=(tk_version >= (8, 7))) + + def test_configure_selectcolor(self): + widget = self.create() + self.checkColorParam(widget, 'selectcolor') + + def test_configure_selectimage(self): + widget = self.create() + self.checkImageParam(widget, 'selectimage') + + def test_configure_tristateimage(self): + widget = self.create() + self.checkImageParam(widget, 'tristateimage') + + def test_configure_tristatevalue(self): + widget = self.create() + self.checkParam(widget, 'tristatevalue', 'unknowable') + + def test_configure_variable(self): + widget = self.create() + var = tkinter.DoubleVar(self.root) + self.checkVariableParam(widget, 'variable', var) + + +class IntegerSizeTests: + def test_configure_height(self): + widget = self.create() + self.checkIntegerParam(widget, 'height', 100, -100, 0) + + def test_configure_width(self): + widget = self.create() + self.checkIntegerParam(widget, 'width', 402, -402, 0) + + +class PixelSizeTests: + def test_configure_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') + + def test_configure_width(self): + widget = self.create() + self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') + + +def add_standard_options(*source_classes): + # This decorator adds test_configure_xxx methods from source classes for + # every xxx option in the OPTIONS class attribute if they are not defined + # explicitly. + def decorator(cls): + for option in cls.OPTIONS: + methodname = 'test_configure_' + option + if not hasattr(cls, methodname): + for source_class in source_classes: + if hasattr(source_class, methodname): + setattr(cls, methodname, + getattr(source_class, methodname)) + break + else: + def test(self, option=option): + widget = self.create() + widget[option] + raise AssertionError('Option "%s" is not tested in %s' % + (option, cls.__name__)) + test.__name__ = methodname + setattr(cls, methodname, test) + return cls + return decorator + +def setUpModule(): + if test.support.verbose: + tcl = tkinter.Tcl() + print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True) diff --git a/Lib/test/test_ttk/__init__.py b/Lib/test/test_ttk/__init__.py new file mode 100644 index 00000000000..7ee7ffbd6d7 --- /dev/null +++ b/Lib/test/test_ttk/__init__.py @@ -0,0 +1,39 @@ +import os.path +import unittest +from test import support +from test.support import import_helper + + +if support.check_sanitizer(address=True, memory=True): + raise unittest.SkipTest("Tests involving libX11 can SEGFAULT on ASAN/MSAN builds") + +# Skip this test if _tkinter wasn't built. +import_helper.import_module('_tkinter') + +# Skip test if tk cannot be initialized. +support.requires('gui') + + +import tkinter +from _tkinter import TclError +from tkinter import ttk + + +def setUpModule(): + root = None + try: + root = tkinter.Tk() + button = ttk.Button(root) + button.destroy() + del button + except TclError as msg: + # assuming ttk is not available + raise unittest.SkipTest("ttk not available: %s" % msg) + finally: + if root is not None: + root.destroy() + del root + + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_ttk/__main__.py b/Lib/test/test_ttk/__main__.py new file mode 100644 index 00000000000..40a23a297ec --- /dev/null +++ b/Lib/test/test_ttk/__main__.py @@ -0,0 +1,4 @@ +from . import load_tests +import unittest + +unittest.main() diff --git a/Lib/test/test_ttk/test_extensions.py b/Lib/test/test_ttk/test_extensions.py new file mode 100644 index 00000000000..d5e06971697 --- /dev/null +++ b/Lib/test/test_ttk/test_extensions.py @@ -0,0 +1,330 @@ +import sys +import unittest +import tkinter +from tkinter import ttk +from test.support import requires, gc_collect +from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest + +requires('gui') + +class LabeledScaleTest(AbstractTkTest, unittest.TestCase): + + def tearDown(self): + self.root.update_idletasks() + super().tearDown() + + def test_widget_destroy(self): + # automatically created variable + x = ttk.LabeledScale(self.root) + var = x._variable._name + x.destroy() + gc_collect() # For PyPy or other GCs. + self.assertRaises(tkinter.TclError, x.tk.globalgetvar, var) + + # manually created variable + myvar = tkinter.DoubleVar(self.root) + name = myvar._name + x = ttk.LabeledScale(self.root, variable=myvar) + x.destroy() + if self.wantobjects: + self.assertEqual(x.tk.globalgetvar(name), myvar.get()) + else: + self.assertEqual(float(x.tk.globalgetvar(name)), myvar.get()) + del myvar + gc_collect() # For PyPy or other GCs. + self.assertRaises(tkinter.TclError, x.tk.globalgetvar, name) + + # checking that the tracing callback is properly removed + myvar = tkinter.IntVar(self.root) + # LabeledScale will start tracing myvar + x = ttk.LabeledScale(self.root, variable=myvar) + x.destroy() + # Unless the tracing callback was removed, creating a new + # LabeledScale with the same var will cause an error now. This + # happens because the variable will be set to (possibly) a new + # value which causes the tracing callback to be called and then + # it tries calling instance attributes not yet defined. + ttk.LabeledScale(self.root, variable=myvar) + if hasattr(sys, 'last_exc'): + self.assertNotEqual(type(sys.last_exc), tkinter.TclError) + elif hasattr(sys, 'last_type'): + self.assertNotEqual(sys.last_type, tkinter.TclError) + + def test_initialization(self): + # master passing + master = tkinter.Frame(self.root) + x = ttk.LabeledScale(master) + self.assertEqual(x.master, master) + x.destroy() + + # variable initialization/passing + passed_expected = (('0', 0), (0, 0), (10, 10), + (-1, -1), (sys.maxsize + 1, sys.maxsize + 1), + (2.5, 2), ('2.5', 2)) + for pair in passed_expected: + x = ttk.LabeledScale(self.root, from_=pair[0]) + self.assertEqual(x.value, pair[1]) + x.destroy() + x = ttk.LabeledScale(self.root, from_=None) + self.assertRaises((ValueError, tkinter.TclError), x._variable.get) + x.destroy() + # variable should have its default value set to the from_ value + myvar = tkinter.DoubleVar(self.root, value=20) + x = ttk.LabeledScale(self.root, variable=myvar) + self.assertEqual(x.value, 0) + x.destroy() + # check that it is really using a DoubleVar + x = ttk.LabeledScale(self.root, variable=myvar, from_=0.5) + self.assertEqual(x.value, 0.5) + self.assertEqual(x._variable._name, myvar._name) + x.destroy() + + # widget positionment + def check_positions(scale, scale_pos, label, label_pos): + self.assertEqual(scale.pack_info()['side'], scale_pos) + self.assertEqual(label.place_info()['anchor'], label_pos) + x = ttk.LabeledScale(self.root, compound='top') + check_positions(x.scale, 'bottom', x.label, 'n') + x.destroy() + x = ttk.LabeledScale(self.root, compound='bottom') + check_positions(x.scale, 'top', x.label, 's') + x.destroy() + # invert default positions + x = ttk.LabeledScale(self.root, compound='unknown') + check_positions(x.scale, 'top', x.label, 's') + x.destroy() + x = ttk.LabeledScale(self.root) # take default positions + check_positions(x.scale, 'bottom', x.label, 'n') + x.destroy() + + # extra, and invalid, kwargs + self.assertRaises(tkinter.TclError, ttk.LabeledScale, master, a='b') + + + def test_horizontal_range(self): + lscale = ttk.LabeledScale(self.root, from_=0, to=10) + lscale.pack() + lscale.update() + + linfo_1 = lscale.label.place_info() + prev_xcoord = lscale.scale.coords()[0] + self.assertEqual(prev_xcoord, int(linfo_1['x'])) + # change range to: from -5 to 5. This should change the x coord of + # the scale widget, since 0 is at the middle of the new + # range. + lscale.scale.configure(from_=-5, to=5) + # The following update is needed since the test doesn't use mainloop, + # at the same time this shouldn't affect test outcome + lscale.update() + curr_xcoord = lscale.scale.coords()[0] + self.assertNotEqual(prev_xcoord, curr_xcoord) + # the label widget should have been repositioned too + linfo_2 = lscale.label.place_info() + self.assertEqual(lscale.label['text'], 0 if self.wantobjects else '0') + self.assertEqual(curr_xcoord, int(linfo_2['x'])) + # change the range back + lscale.scale.configure(from_=0, to=10) + self.assertNotEqual(prev_xcoord, curr_xcoord) + self.assertEqual(prev_xcoord, int(linfo_1['x'])) + + lscale.destroy() + + + def test_variable_change(self): + x = ttk.LabeledScale(self.root) + x.pack() + x.update() + + curr_xcoord = x.scale.coords()[0] + newval = x.value + 1 + x.value = newval + # The following update is needed since the test doesn't use mainloop, + # at the same time this shouldn't affect test outcome + x.update() + self.assertEqual(x.value, newval) + self.assertEqual(x.label['text'], + newval if self.wantobjects else str(newval)) + self.assertEqual(float(x.scale.get()), newval) + self.assertGreater(x.scale.coords()[0], curr_xcoord) + self.assertEqual(x.scale.coords()[0], + int(x.label.place_info()['x'])) + + # value outside range + if self.wantobjects: + conv = lambda x: x + else: + conv = int + x.value = conv(x.scale['to']) + 1 # no changes shouldn't happen + x.update() + self.assertEqual(x.value, newval) + self.assertEqual(conv(x.label['text']), newval) + self.assertEqual(float(x.scale.get()), newval) + self.assertEqual(x.scale.coords()[0], + int(x.label.place_info()['x'])) + + # non-integer value + x.value = newval = newval + 1.5 + x.update() + self.assertEqual(x.value, int(newval)) + self.assertEqual(conv(x.label['text']), int(newval)) + self.assertEqual(float(x.scale.get()), newval) + + x.destroy() + + + def test_resize(self): + x = ttk.LabeledScale(self.root) + x.pack(expand=True, fill='both') + gc_collect() # For PyPy or other GCs. + x.update() + + width, height = x.master.winfo_width(), x.master.winfo_height() + width_new, height_new = width * 2, height * 2 + + x.value = 3 + x.update() + x.master.wm_geometry("%dx%d" % (width_new, height_new)) + self.assertEqual(int(x.label.place_info()['x']), + x.scale.coords()[0]) + + # Reset geometry + x.master.wm_geometry("%dx%d" % (width, height)) + x.destroy() + + +class OptionMenuTest(AbstractTkTest, unittest.TestCase): + + def setUp(self): + super().setUp() + self.textvar = tkinter.StringVar(self.root) + + def tearDown(self): + del self.textvar + super().tearDown() + + + def test_widget_destroy(self): + var = tkinter.StringVar(self.root) + optmenu = ttk.OptionMenu(self.root, var) + name = var._name + optmenu.update_idletasks() + optmenu.destroy() + self.assertEqual(optmenu.tk.globalgetvar(name), var.get()) + del var + gc_collect() # For PyPy or other GCs. + self.assertRaises(tkinter.TclError, optmenu.tk.globalgetvar, name) + + + def test_initialization(self): + self.assertRaises(tkinter.TclError, + ttk.OptionMenu, self.root, self.textvar, invalid='thing') + + optmenu = ttk.OptionMenu(self.root, self.textvar, 'b', 'a', 'b') + self.assertEqual(optmenu._variable.get(), 'b') + + self.assertTrue(optmenu['menu']) + self.assertTrue(optmenu['textvariable']) + + optmenu.destroy() + + + def test_menu(self): + items = ('a', 'b', 'c') + default = 'a' + optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items) + found_default = False + for i in range(len(items)): + value = optmenu['menu'].entrycget(i, 'value') + self.assertEqual(value, items[i]) + if value == default: + found_default = True + self.assertTrue(found_default) + optmenu.destroy() + + # default shouldn't be in menu if it is not part of values + default = 'd' + optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items) + curr = None + i = 0 + while True: + last, curr = curr, optmenu['menu'].entryconfigure(i, 'value') + if last == curr: + # no more menu entries + break + self.assertNotEqual(curr, default) + i += 1 + self.assertEqual(i, len(items)) + + # check that variable is updated correctly + optmenu.pack() + gc_collect() # For PyPy or other GCs. + optmenu['menu'].invoke(0) + self.assertEqual(optmenu._variable.get(), items[0]) + + # changing to an invalid index shouldn't change the variable + self.assertRaises(tkinter.TclError, optmenu['menu'].invoke, -1) + self.assertEqual(optmenu._variable.get(), items[0]) + + optmenu.destroy() + + # specifying a callback + success = [] + def cb_test(item): + self.assertEqual(item, items[1]) + success.append(True) + optmenu = ttk.OptionMenu(self.root, self.textvar, 'a', command=cb_test, + *items) + optmenu['menu'].invoke(1) + if not success: + self.fail("Menu callback not invoked") + + optmenu.destroy() + + def test_unique_radiobuttons(self): + # check that radiobuttons are unique across instances (bpo25684) + items = ('a', 'b', 'c') + default = 'a' + optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items) + textvar2 = tkinter.StringVar(self.root) + optmenu2 = ttk.OptionMenu(self.root, textvar2, default, *items) + optmenu.pack() + optmenu2.pack() + optmenu['menu'].invoke(1) + optmenu2['menu'].invoke(2) + optmenu_stringvar_name = optmenu['menu'].entrycget(0, 'variable') + optmenu2_stringvar_name = optmenu2['menu'].entrycget(0, 'variable') + self.assertNotEqual(optmenu_stringvar_name, + optmenu2_stringvar_name) + self.assertEqual(self.root.tk.globalgetvar(optmenu_stringvar_name), + items[1]) + self.assertEqual(self.root.tk.globalgetvar(optmenu2_stringvar_name), + items[2]) + + optmenu.destroy() + optmenu2.destroy() + + def test_trace_variable(self): + # prior to bpo45160, tracing a variable would cause the callback to be made twice + success = [] + items = ('a', 'b', 'c') + textvar = tkinter.StringVar(self.root) + def cb_test(*args): + success.append(textvar.get()) + optmenu = ttk.OptionMenu(self.root, textvar, "a", *items) + optmenu.pack() + cb_name = textvar.trace_add("write", cb_test) + optmenu['menu'].invoke(1) + self.assertEqual(success, ['b']) + self.assertEqual(textvar.get(), 'b') + textvar.trace_remove("write", cb_name) + optmenu.destroy() + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_labeledscale(self): + self._test_widget(ttk.LabeledScale) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py new file mode 100644 index 00000000000..eeaf5de2e30 --- /dev/null +++ b/Lib/test/test_ttk/test_style.py @@ -0,0 +1,445 @@ +import unittest +import sys +import tkinter +from tkinter import ttk +from tkinter import TclError +from test import support +from test.support import requires +from test.test_tkinter.support import AbstractTkTest, get_tk_patchlevel + +requires('gui') + +CLASS_NAMES = [ + '.', 'ComboboxPopdownFrame', 'Heading', + 'Horizontal.TProgressbar', 'Horizontal.TScale', 'Item', 'Sash', + 'TButton', 'TCheckbutton', 'TCombobox', 'TEntry', + 'TLabelframe', 'TLabelframe.Label', 'TMenubutton', + 'TNotebook', 'TNotebook.Tab', 'Toolbutton', 'TProgressbar', + 'TRadiobutton', 'Treeview', 'TScale', 'TScrollbar', 'TSpinbox', + 'Vertical.TProgressbar', 'Vertical.TScale' +] + +class StyleTest(AbstractTkTest, unittest.TestCase): + + def setUp(self): + super().setUp() + self.style = ttk.Style(self.root) + + + def test_configure(self): + style = self.style + style.configure('TButton', background='yellow') + self.assertEqual(style.configure('TButton', 'background'), + 'yellow') + self.assertIsInstance(style.configure('TButton'), dict) + + + def test_map(self): + style = self.style + + # Single state + for states in ['active'], [('active',)]: + with self.subTest(states=states): + style.map('TButton', background=[(*states, 'white')]) + expected = [('active', 'white')] + self.assertEqual(style.map('TButton', 'background'), expected) + m = style.map('TButton') + self.assertIsInstance(m, dict) + self.assertEqual(m['background'], expected) + + # Multiple states + for states in ['pressed', '!disabled'], ['pressed !disabled'], [('pressed', '!disabled')]: + with self.subTest(states=states): + style.map('TButton', background=[(*states, 'black')]) + expected = [('pressed', '!disabled', 'black')] + self.assertEqual(style.map('TButton', 'background'), expected) + m = style.map('TButton') + self.assertIsInstance(m, dict) + self.assertEqual(m['background'], expected) + + # Default state + for states in [], [''], [()]: + with self.subTest(states=states): + style.map('TButton', background=[(*states, 'grey')]) + expected = [('grey',)] + self.assertEqual(style.map('TButton', 'background'), expected) + m = style.map('TButton') + self.assertIsInstance(m, dict) + self.assertEqual(m['background'], expected) + + + def test_lookup(self): + style = self.style + style.configure('TButton', background='yellow') + style.map('TButton', background=[('active', 'background', 'blue')]) + + self.assertEqual(style.lookup('TButton', 'background'), 'yellow') + self.assertEqual(style.lookup('TButton', 'background', + ['active', 'background']), 'blue') + self.assertEqual(style.lookup('TButton', 'optionnotdefined', + default='iknewit'), 'iknewit') + + + def test_layout(self): + style = self.style + self.assertRaises(tkinter.TclError, style.layout, 'NotALayout') + tv_style = style.layout('Treeview') + + # "erase" Treeview layout + style.layout('Treeview', '') + self.assertEqual(style.layout('Treeview'), + [('null', {'sticky': 'nswe'})] + ) + + # restore layout + style.layout('Treeview', tv_style) + self.assertEqual(style.layout('Treeview'), tv_style) + + # should return a list + self.assertIsInstance(style.layout('TButton'), list) + + # correct layout, but "option" doesn't exist as option + self.assertRaises(tkinter.TclError, style.layout, 'Treeview', + [('name', {'option': 'inexistent'})]) + + + def test_theme_use(self): + self.assertRaises(tkinter.TclError, self.style.theme_use, + 'nonexistingname') + + curr_theme = self.style.theme_use() + new_theme = None + for theme in self.style.theme_names(): + if theme != curr_theme: + new_theme = theme + self.style.theme_use(theme) + break + else: + # just one theme available, can't go on with tests + return + + self.assertFalse(curr_theme == new_theme) + self.assertFalse(new_theme != self.style.theme_use()) + + self.style.theme_use(curr_theme) + + def test_configure_custom_copy(self): + style = self.style + + curr_theme = self.style.theme_use() + self.addCleanup(self.style.theme_use, curr_theme) + for theme in self.style.theme_names(): + self.style.theme_use(theme) + for name in CLASS_NAMES: + default = style.configure(name) + if not default: + continue + with self.subTest(theme=theme, name=name): + if support.verbose >= 2: + print('configure', theme, name, default) + if (theme in ('vista', 'xpnative') + and sys.getwindowsversion()[:2] == (6, 1)): + # Fails on the Windows 7 buildbot + continue + newname = f'C.{name}' + self.assertEqual(style.configure(newname), None) + style.configure(newname, **default) + self.assertEqual(style.configure(newname), default) + for key, value in default.items(): + self.assertEqual(style.configure(newname, key), value) + + + def test_map_custom_copy(self): + style = self.style + + curr_theme = self.style.theme_use() + self.addCleanup(self.style.theme_use, curr_theme) + for theme in self.style.theme_names(): + self.style.theme_use(theme) + for name in CLASS_NAMES: + default = style.map(name) + if not default: + continue + with self.subTest(theme=theme, name=name): + if support.verbose >= 2: + print('map', theme, name, default) + if (theme in ('vista', 'xpnative') + and sys.getwindowsversion()[:2] == (6, 1)): + # Fails on the Windows 7 buildbot + continue + newname = f'C.{name}' + self.assertEqual(style.map(newname), {}) + style.map(newname, **default) + if theme == 'alt' and name == '.' and get_tk_patchlevel(self.root) < (8, 6, 1): + default['embossed'] = [('disabled', '1')] + self.assertEqual(style.map(newname), default) + for key, value in default.items(): + self.assertEqual(style.map(newname, key), value) + + def test_element_options(self): + style = self.style + element_names = style.element_names() + self.assertNotIsInstance(element_names, str) + for name in element_names: + self.assertIsInstance(name, str) + element_options = style.element_options(name) + self.assertNotIsInstance(element_options, str) + for optname in element_options: + self.assertIsInstance(optname, str) + + def test_element_create_errors(self): + style = self.style + with self.assertRaises(TypeError): + style.element_create('plain.newelem') + with self.assertRaisesRegex(TclError, 'No such element type spam'): + style.element_create('plain.newelem', 'spam') + + def test_element_create_from(self): + style = self.style + style.element_create('plain.background', 'from', 'default') + self.assertIn('plain.background', style.element_names()) + style.element_create('plain.arrow', 'from', 'default', 'rightarrow') + self.assertIn('plain.arrow', style.element_names()) + + def test_element_create_from_errors(self): + style = self.style + with self.assertRaises(IndexError): + style.element_create('plain.newelem', 'from') + with self.assertRaisesRegex(TclError, 'theme "spam" doesn\'t exist'): + style.element_create('plain.newelem', 'from', 'spam') + + def test_element_create_image(self): + style = self.style + image = tkinter.PhotoImage(master=self.root, width=12, height=10) + style.element_create('block', 'image', image) + self.assertIn('block', style.element_names()) + + style.layout('TestLabel1', [('block', {'sticky': 'news'})]) + a = ttk.Label(self.root, style='TestLabel1') + a.pack(expand=True, fill='both') + self.assertEqual(a.winfo_reqwidth(), 12) + self.assertEqual(a.winfo_reqheight(), 10) + + imgfile = support.findfile('python.xbm', subdir='tkinterdata') + img1 = tkinter.BitmapImage(master=self.root, file=imgfile, + foreground='yellow', background='blue') + img2 = tkinter.BitmapImage(master=self.root, file=imgfile, + foreground='blue', background='yellow') + img3 = tkinter.BitmapImage(master=self.root, file=imgfile, + foreground='white', background='black') + style.element_create('TestButton.button', 'image', + img1, ('pressed', img2), ('active', img3), + border=(2, 4), sticky='we') + self.assertIn('TestButton.button', style.element_names()) + + style.layout('TestButton', [('TestButton.button', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='TestButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 16) + self.assertEqual(b.winfo_reqheight(), 16) + + def test_element_create_image_errors(self): + style = self.style + image = tkinter.PhotoImage(master=self.root, width=10, height=10) + with self.assertRaises(IndexError): + style.element_create('block2', 'image') + with self.assertRaises(TypeError): + style.element_create('block2', 'image', image, 1) + with self.assertRaises(ValueError): + style.element_create('block2', 'image', image, ()) + with self.assertRaisesRegex(TclError, 'Invalid state name'): + style.element_create('block2', 'image', image, ('spam', image)) + with self.assertRaisesRegex(TclError, 'Invalid state name'): + style.element_create('block2', 'image', image, (1, image)) + with self.assertRaises(TypeError): + style.element_create('block2', 'image', image, ('pressed', 1, image)) + with self.assertRaises(TypeError): + style.element_create('block2', 'image', image, (1, 'selected', image)) + with self.assertRaisesRegex(TclError, 'bad option'): + style.element_create('block2', 'image', image, spam=1) + + def test_element_create_vsapi_1(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('smallclose', 'vsapi', 'WINDOW', 19, [ + ('disabled', 4), + ('pressed', 3), + ('active', 2), + ('', 1)]) + style.layout('CloseButton', + [('CloseButton.smallclose', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='CloseButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 13) + self.assertEqual(b.winfo_reqheight(), 13) + + def test_element_create_vsapi_2(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]) + style.layout('Explorer.Pin', + [('Explorer.Pin.pin', {'sticky': 'news'})]) + pin = ttk.Checkbutton(self.root, style='Explorer.Pin') + pin.pack(expand=True, fill='both') + self.assertEqual(pin.winfo_reqwidth(), 16) + self.assertEqual(pin.winfo_reqheight(), 16) + + def test_element_create_vsapi_3(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('headerclose', 'vsapi', 'EXPLORERBAR', 2, [ + ('pressed', 3), + ('active', 2), + ('', 1)]) + style.layout('Explorer.CloseButton', + [('Explorer.CloseButton.headerclose', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='Explorer.CloseButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 16) + self.assertEqual(b.winfo_reqheight(), 16) + + def test_theme_create(self): + style = self.style + curr_theme = style.theme_use() + curr_layout = style.layout('TLabel') + style.theme_create('testtheme1') + self.assertIn('testtheme1', style.theme_names()) + + style.theme_create('testtheme2', settings={ + 'elem' : {'element create': ['from', 'default'],}, + 'TLabel' : { + 'configure': {'padding': 10}, + 'layout': [('elem', {'sticky': 'we'})], + }, + }) + self.assertIn('testtheme2', style.theme_names()) + + style.theme_create('testtheme3', 'testtheme2') + self.assertIn('testtheme3', style.theme_names()) + + style.theme_use('testtheme1') + self.assertEqual(style.element_names(), ()) + self.assertEqual(style.layout('TLabel'), curr_layout) + + style.theme_use('testtheme2') + self.assertEqual(style.element_names(), ('elem',)) + self.assertEqual(style.lookup('TLabel', 'padding'), '10') + self.assertEqual(style.layout('TLabel'), [('elem', {'sticky': 'we'})]) + + style.theme_use('testtheme3') + self.assertEqual(style.element_names(), ()) + self.assertEqual(style.lookup('TLabel', 'padding'), '') + self.assertEqual(style.layout('TLabel'), [('elem', {'sticky': 'we'})]) + + style.theme_use(curr_theme) + + def test_theme_create_image(self): + style = self.style + curr_theme = style.theme_use() + image = tkinter.PhotoImage(master=self.root, width=10, height=10) + new_theme = 'testtheme4' + style.theme_create(new_theme, settings={ + 'block' : { + 'element create': ['image', image, {'width': 120, 'height': 100}], + }, + 'TestWidget.block2' : { + 'element create': ['image', image], + }, + 'TestWidget' : { + 'configure': { + 'anchor': 'left', + 'padding': (3, 0, 0, 2), + 'foreground': 'yellow', + }, + 'map': { + 'foreground': [ + ('pressed', 'red'), + ('active', 'disabled', 'blue'), + ], + }, + 'layout': [ + ('TestWidget.block', {'sticky': 'we', 'side': 'left'}), + ('TestWidget.border', { + 'sticky': 'nsw', + 'border': 1, + 'children': [ + ('TestWidget.block2', {'sticky': 'nswe'}) + ] + }) + ], + }, + }) + + style.theme_use(new_theme) + self.assertIn('block', style.element_names()) + self.assertEqual(style.lookup('TestWidget', 'anchor'), 'left') + self.assertEqual(style.lookup('TestWidget', 'padding'), '3 0 0 2') + self.assertEqual(style.lookup('TestWidget', 'foreground'), 'yellow') + self.assertEqual(style.lookup('TestWidget', 'foreground', + ['active']), 'yellow') + self.assertEqual(style.lookup('TestWidget', 'foreground', + ['active', 'pressed']), 'red') + self.assertEqual(style.lookup('TestWidget', 'foreground', + ['active', 'disabled']), 'blue') + self.assertEqual(style.layout('TestWidget'), + [ + ('TestWidget.block', {'side': 'left', 'sticky': 'we'}), + ('TestWidget.border', { + 'sticky': 'nsw', + 'border': '1', + 'children': [('TestWidget.block2', {'sticky': 'nswe'})] + }) + ]) + + b = ttk.Label(self.root, style='TestWidget') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 134) + self.assertEqual(b.winfo_reqheight(), 100) + + style.theme_use(curr_theme) + + def test_theme_create_vsapi(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + curr_theme = style.theme_use() + new_theme = 'testtheme5' + style.theme_create(new_theme, settings={ + 'pin' : { + 'element create': ['vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]], + }, + 'Explorer.Pin' : { + 'layout': [('Explorer.Pin.pin', {'sticky': 'news'})], + }, + }) + + style.theme_use(new_theme) + self.assertIn('pin', style.element_names()) + self.assertEqual(style.layout('Explorer.Pin'), + [('Explorer.Pin.pin', {'sticky': 'nswe'})]) + + pin = ttk.Checkbutton(self.root, style='Explorer.Pin') + pin.pack(expand=True, fill='both') + self.assertEqual(pin.winfo_reqwidth(), 16) + self.assertEqual(pin.winfo_reqheight(), 16) + + style.theme_use(curr_theme) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py new file mode 100644 index 00000000000..10bec33be61 --- /dev/null +++ b/Lib/test/test_ttk/test_widgets.py @@ -0,0 +1,1972 @@ +import unittest +import tkinter +from tkinter import ttk, TclError +from test.support import requires, gc_collect +import sys + +from test.test_ttk_textonly import MockTclObj +from test.test_tkinter.support import ( + AbstractTkTest, requires_tk, tk_version, get_tk_patchlevel, + simulate_mouse_click, AbstractDefaultRootTest) +from test.test_tkinter.widget_tests import (add_standard_options, + AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests) + +requires('gui') + + +class StandardTtkOptionsTests(StandardOptionsTests): + + def test_configure_class(self): + widget = self.create() + self.assertEqual(widget['class'], '') + errmsg='attempt to change read-only option' + if get_tk_patchlevel(self.root) < (8, 6, 0, 'beta', 3): + errmsg='Attempt to change read-only option' + self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg) + widget2 = self.create(class_='Foo') + self.assertEqual(widget2['class'], 'Foo') + + def test_configure_padding(self): + widget = self.create() + if get_tk_patchlevel(self.root) < (8, 6, 14): + def padding_conv(value): + self.assertIsInstance(value, tuple) + return tuple(map(str, value)) + else: + padding_conv = None + self.checkParam(widget, 'padding', 0, expected=(0,), conv=padding_conv) + self.checkParam(widget, 'padding', 5, expected=(5,), conv=padding_conv) + self.checkParam(widget, 'padding', (5, 6), + expected=(5, 6), conv=padding_conv) + self.checkParam(widget, 'padding', (5, 6, 7), + expected=(5, 6, 7), conv=padding_conv) + self.checkParam(widget, 'padding', (5, 6, 7, 8), + expected=(5, 6, 7, 8), conv=padding_conv) + self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p')) + self.checkParam(widget, 'padding', (), expected='') + + def test_configure_state(self): + widget = self.create() + self.checkParams(widget, 'state', 'active', 'disabled', 'readonly') + + def test_configure_style(self): + widget = self.create() + self.assertEqual(widget['style'], '') + errmsg = 'Layout Foo not found' + if hasattr(self, 'default_orient'): + errmsg = ('Layout %s.Foo not found' % + getattr(self, 'default_orient').title()) + self.checkInvalidParam(widget, 'style', 'Foo', + errmsg=errmsg) + widget2 = self.create(class_='Foo') + self.assertEqual(widget2['class'], 'Foo') + # XXX + + def test_configure_relief(self): + widget = self.create() + self.checkReliefParam(widget, 'relief', + allow_empty=(tk_version >= (8, 7))) + + +class WidgetTest(AbstractTkTest, unittest.TestCase): + """Tests methods available in every ttk widget.""" + + def setUp(self): + super().setUp() + self.widget = ttk.Button(self.root, width=0, text="Text") + self.widget.pack() + + def test_identify(self): + self.widget.update() + self.assertEqual(self.widget.identify( + int(self.widget.winfo_width() / 2), + int(self.widget.winfo_height() / 2) + ), "label") + self.assertEqual(self.widget.identify(-1, -1), "") + + self.assertRaises(tkinter.TclError, self.widget.identify, None, 5) + self.assertRaises(tkinter.TclError, self.widget.identify, 5, None) + self.assertRaises(tkinter.TclError, self.widget.identify, 5, '') + + def test_widget_state(self): + # XXX not sure about the portability of all these tests + self.assertEqual(self.widget.state(), ()) + self.assertEqual(self.widget.instate(['!disabled']), True) + + # changing from !disabled to disabled + self.assertEqual(self.widget.state(['disabled']), ('!disabled', )) + # no state change + self.assertEqual(self.widget.state(['disabled']), ()) + # change back to !disable but also active + self.assertEqual(self.widget.state(['!disabled', 'active']), + ('!active', 'disabled')) + # no state changes, again + self.assertEqual(self.widget.state(['!disabled', 'active']), ()) + self.assertEqual(self.widget.state(['active', '!disabled']), ()) + + def test_cb(arg1, **kw): + return arg1, kw + self.assertEqual(self.widget.instate(['!disabled'], + test_cb, "hi", **{"msg": "there"}), + ('hi', {'msg': 'there'})) + + # attempt to set invalid statespec + currstate = self.widget.state() + self.assertRaises(tkinter.TclError, self.widget.instate, + ['badstate']) + self.assertRaises(tkinter.TclError, self.widget.instate, + ['disabled', 'badstate']) + # verify that widget didn't change its state + self.assertEqual(currstate, self.widget.state()) + + # ensuring that passing None as state doesn't modify current state + self.widget.state(['active', '!disabled']) + self.assertEqual(self.widget.state(), ('active', )) + + +class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): + _conv_pixels = False + + +@add_standard_options(StandardTtkOptionsTests) +class FrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'borderwidth', 'class', 'cursor', 'height', + 'padding', 'relief', 'style', 'takefocus', + 'width', + ) + + def create(self, **kwargs): + return ttk.Frame(self.root, **kwargs) + + +@add_standard_options(StandardTtkOptionsTests) +class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'borderwidth', 'class', 'cursor', 'height', + 'labelanchor', 'labelwidget', + 'padding', 'relief', 'style', 'takefocus', + 'text', 'underline', 'width', + ) + + def create(self, **kwargs): + return ttk.LabelFrame(self.root, **kwargs) + + def test_configure_labelanchor(self): + widget = self.create() + self.checkEnumParam(widget, 'labelanchor', + 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws', + errmsg='Bad label anchor specification {}') + self.checkInvalidParam(widget, 'labelanchor', 'center') + + def test_configure_labelwidget(self): + widget = self.create() + label = ttk.Label(self.root, text='Mupp', name='foo') + self.checkParam(widget, 'labelwidget', label, expected='.foo') + label.destroy() + + +class AbstractLabelTest(AbstractWidgetTest): + _allow_empty_justify = True + + def checkImageParam(self, widget, name): + image = tkinter.PhotoImage(master=self.root, name='image1') + image2 = tkinter.PhotoImage(master=self.root, name='image2') + self.checkParam(widget, name, image, expected=('image1',)) + self.checkParam(widget, name, 'image1', expected=('image1',)) + self.checkParam(widget, name, (image,), expected=('image1',)) + self.checkParam(widget, name, (image, 'active', image2), + expected=('image1', 'active', 'image2')) + self.checkParam(widget, name, 'image1 active image2', + expected=('image1', 'active', 'image2')) + self.checkInvalidParam(widget, name, 'spam', + errmsg='image "spam" doesn\'t exist') + + def test_configure_compound(self): + values = ('none', 'text', 'image', 'center', 'top', 'bottom', 'left', 'right') + if tk_version >= (8, 7): + values += ('',) + widget = self.create() + self.checkEnumParam(widget, 'compound', *values, allow_empty=True) + + test_configure_justify = requires_tk(8, 7)(StandardOptionsTests.test_configure_justify) + + def test_configure_width(self): + widget = self.create() + self.checkParams(widget, 'width', 402, -402, 0) + + +@add_standard_options(StandardTtkOptionsTests) +class LabelTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'anchor', 'background', 'borderwidth', + 'class', 'compound', 'cursor', 'font', 'foreground', + 'image', 'justify', 'padding', 'relief', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength', + ) + _conv_pixels = False + _allow_empty_justify = tk_version >= (8, 7) + + def create(self, **kwargs): + return ttk.Label(self.root, **kwargs) + + test_configure_justify = StandardOptionsTests.test_configure_justify + + +@add_standard_options(StandardTtkOptionsTests) +class ButtonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'compound', 'cursor', 'default', + 'image', 'justify', 'padding', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', + ) + + def create(self, **kwargs): + return ttk.Button(self.root, **kwargs) + + def test_configure_default(self): + widget = self.create() + values = ('normal', 'active', 'disabled') + self.checkEnumParam(widget, 'default', *values, + sort=tk_version >= (8, 7)) + + def test_invoke(self): + success = [] + btn = ttk.Button(self.root, command=lambda: success.append(1)) + btn.invoke() + self.assertTrue(success) + + +@add_standard_options(StandardTtkOptionsTests) +class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'compound', 'cursor', + 'image', 'justify', + 'offvalue', 'onvalue', + 'padding', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'variable', 'width', + ) + + def create(self, **kwargs): + return ttk.Checkbutton(self.root, **kwargs) + + def test_configure_offvalue(self): + widget = self.create() + self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') + + def test_configure_onvalue(self): + widget = self.create() + self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') + + def test_invoke(self): + success = [] + def cb_test(): + success.append(1) + return "cb test called" + + cbtn = ttk.Checkbutton(self.root, command=cb_test) + # the variable automatically created by ttk.Checkbutton is actually + # undefined till we invoke the Checkbutton + self.assertEqual(cbtn.state(), ('alternate', )) + self.assertRaises(tkinter.TclError, cbtn.tk.globalgetvar, + cbtn['variable']) + + res = cbtn.invoke() + self.assertEqual(res, "cb test called") + self.assertEqual(cbtn['onvalue'], + cbtn.tk.globalgetvar(cbtn['variable'])) + self.assertTrue(success) + + cbtn['command'] = '' + res = cbtn.invoke() + if tk_version >= (8, 7) and self.wantobjects: + self.assertEqual(res, ()) + else: + self.assertEqual(str(res), '') + self.assertLessEqual(len(success), 1) + self.assertEqual(cbtn['offvalue'], + cbtn.tk.globalgetvar(cbtn['variable'])) + + def test_unique_variables(self): + frames = [] + buttons = [] + for i in range(2): + f = ttk.Frame(self.root) + f.pack() + frames.append(f) + for j in 'AB': + b = ttk.Checkbutton(f, text=j) + b.pack() + buttons.append(b) + variables = [str(b['variable']) for b in buttons] + self.assertEqual(len(set(variables)), 4, variables) + + def test_unique_variables2(self): + buttons = [] + f = ttk.Frame(self.root) + f.pack() + f = ttk.Frame(self.root) + f.pack() + for j in 'AB': + b = tkinter.Checkbutton(f, text=j) + b.pack() + buttons.append(b) + # Should be larger than the number of all previously created + # tkinter.Checkbutton widgets: + for j in range(100): + b = ttk.Checkbutton(f, text=str(j)) + b.pack() + buttons.append(b) + names = [str(b) for b in buttons] + self.assertEqual(len(set(names)), len(buttons), names) + variables = [str(b['variable']) for b in buttons] + self.assertEqual(len(set(variables)), len(buttons), variables) + + +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class EntryTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'class', 'cursor', + 'exportselection', 'font', 'foreground', + 'invalidcommand', 'justify', + 'placeholder', 'placeholderforeground', + 'show', 'state', 'style', 'takefocus', 'textvariable', + 'validate', 'validatecommand', 'width', 'xscrollcommand', + ) + # bpo-27313: macOS Tk/Tcl may or may not report 'Entry.field'. + IDENTIFY_AS = {'Entry.field', 'textarea'} + + def setUp(self): + super().setUp() + self.entry = self.create() + + def create(self, **kwargs): + return ttk.Entry(self.root, **kwargs) + + def test_configure_invalidcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'invalidcommand') + + def test_configure_show(self): + widget = self.create() + self.checkParam(widget, 'show', '*') + self.checkParam(widget, 'show', '') + self.checkParam(widget, 'show', ' ') + + def test_configure_validate(self): + widget = self.create() + self.checkEnumParam(widget, 'validate', + 'all', 'key', 'focus', 'focusin', 'focusout', 'none') + + def test_configure_validatecommand(self): + widget = self.create() + self.checkCommandParam(widget, 'validatecommand') + + def test_bbox(self): + self.assertIsBoundingBox(self.entry.bbox(0)) + self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex') + self.assertRaises(tkinter.TclError, self.entry.bbox, None) + + def test_identify(self): + self.entry.pack() + self.entry.update() + + self.assertIn(self.entry.identify(5, 5), self.IDENTIFY_AS) + self.assertEqual(self.entry.identify(-1, -1), "") + + self.assertRaises(tkinter.TclError, self.entry.identify, None, 5) + self.assertRaises(tkinter.TclError, self.entry.identify, 5, None) + self.assertRaises(tkinter.TclError, self.entry.identify, 5, '') + + def test_validation_options(self): + success = [] + test_invalid = lambda: success.append(True) + + self.entry['validate'] = 'none' + self.entry['validatecommand'] = lambda: False + + self.entry['invalidcommand'] = test_invalid + self.entry.validate() + self.assertTrue(success) + + self.entry['invalidcommand'] = '' + self.entry.validate() + self.assertEqual(len(success), 1) + + self.entry['invalidcommand'] = test_invalid + self.entry['validatecommand'] = lambda: True + self.entry.validate() + self.assertEqual(len(success), 1) + + self.entry['validatecommand'] = '' + self.entry.validate() + self.assertEqual(len(success), 1) + + self.entry['validatecommand'] = True + self.assertRaises(tkinter.TclError, self.entry.validate) + + def test_validation(self): + validation = [] + def validate(to_insert): + if not 'a' <= to_insert.lower() <= 'z': + validation.append(False) + return False + validation.append(True) + return True + + self.entry['validate'] = 'key' + self.entry['validatecommand'] = self.entry.register(validate), '%S' + + self.entry.insert('end', 1) + self.entry.insert('end', 'a') + self.assertEqual(validation, [False, True]) + self.assertEqual(self.entry.get(), 'a') + + def test_revalidation(self): + def validate(content): + for letter in content: + if not 'a' <= letter.lower() <= 'z': + return False + return True + + self.entry['validatecommand'] = self.entry.register(validate), '%P' + + self.entry.insert('end', 'avocado') + self.assertEqual(self.entry.validate(), True) + self.assertEqual(self.entry.state(), ()) + + self.entry.delete(0, 'end') + self.assertEqual(self.entry.get(), '') + + self.entry.insert('end', 'a1b') + self.assertEqual(self.entry.validate(), False) + self.assertEqual(self.entry.state(), ('invalid', )) + + self.entry.delete(1) + self.assertEqual(self.entry.validate(), True) + self.assertEqual(self.entry.state(), ()) + + +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class ComboboxTest(EntryTest, unittest.TestCase): + OPTIONS = ( + 'background', 'class', 'cursor', 'exportselection', + 'font', 'foreground', 'height', 'invalidcommand', + 'justify', 'placeholder', 'placeholderforeground', 'postcommand', + 'show', 'state', 'style', + 'takefocus', 'textvariable', + 'validate', 'validatecommand', 'values', + 'width', 'xscrollcommand', + ) + IDENTIFY_AS = {'Combobox.button', 'textarea'} + + def setUp(self): + super().setUp() + self.combo = self.create() + + def create(self, **kwargs): + return ttk.Combobox(self.root, **kwargs) + + def test_configure_height(self): + widget = self.create() + self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i') + + def _show_drop_down_listbox(self): + width = self.combo.winfo_width() + x, y = width - 5, 5 + if sys.platform != 'darwin': # there's no down arrow on macOS + self.assertRegex(self.combo.identify(x, y), r'.*downarrow\Z') + self.combo.event_generate('', x=x, y=y) + self.combo.event_generate('', x=x, y=y) + self.combo.update_idletasks() + + def test_virtual_event(self): + success = [] + + self.combo['values'] = [1] + self.combo.bind('<>', + lambda evt: success.append(True)) + self.combo.pack() + self.combo.update() + + height = self.combo.winfo_height() + self._show_drop_down_listbox() + self.combo.update() + self.combo.event_generate('') + self.combo.update() + + self.assertTrue(success) + + def test_configure_postcommand(self): + success = [] + + self.combo['postcommand'] = lambda: success.append(True) + self.combo.pack() + self.combo.update() + + self._show_drop_down_listbox() + self.assertTrue(success) + + # testing postcommand removal + self.combo['postcommand'] = '' + self._show_drop_down_listbox() + self.assertEqual(len(success), 1) + + def test_configure_values(self): + def check_get_current(getval, currval): + self.assertEqual(self.combo.get(), getval) + self.assertEqual(self.combo.current(), currval) + + self.assertIn(self.combo['values'], ((), '')) + check_get_current('', -1) + + self.checkParam(self.combo, 'values', 'mon tue wed thur', + expected=('mon', 'tue', 'wed', 'thur')) + self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur')) + self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string')) + self.checkParam(self.combo, 'values', '') + + self.combo['values'] = ['a', 1, 'c'] + + self.combo.set('c') + check_get_current('c', 2) + + self.combo.current(0) + check_get_current('a', 0) + + self.combo.set('d') + check_get_current('d', -1) + + # testing values with empty string + self.combo.set('') + self.combo['values'] = (1, 2, '', 3) + check_get_current('', 2) + + # testing values with empty string set through configure + self.combo.configure(values=[1, '', 2]) + self.assertEqual(self.combo['values'], + ('1', '', '2') if self.wantobjects else + '1 {} 2') + + # testing values with spaces + self.combo['values'] = ['a b', 'a\tb', 'a\nb'] + self.assertEqual(self.combo['values'], + ('a b', 'a\tb', 'a\nb') if self.wantobjects else + '{a b} {a\tb} {a\nb}') + + # testing values with special characters + self.combo['values'] = [r'a\tb', '"a"', '} {'] + self.assertEqual(self.combo['values'], + (r'a\tb', '"a"', '} {') if self.wantobjects else + r'a\\tb {"a"} \}\ \{') + + # out of range + self.assertRaises(tkinter.TclError, self.combo.current, + len(self.combo['values'])) + # it expects an integer (or something that can be converted to int) + self.assertRaises(tkinter.TclError, self.combo.current, '') + + # testing creating combobox with empty string in values + combo2 = ttk.Combobox(self.root, values=[1, 2, '']) + self.assertEqual(combo2['values'], + ('1', '2', '') if self.wantobjects else '1 2 {}') + combo2.destroy() + + +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'height', + 'orient', 'style', 'takefocus', 'width', + ) + + def setUp(self): + super().setUp() + self.paned = self.create() + + def create(self, **kwargs): + return ttk.PanedWindow(self.root, **kwargs) + + def test_configure_orient(self): + widget = self.create() + self.assertEqual(str(widget['orient']), 'vertical') + errmsg='attempt to change read-only option' + if get_tk_patchlevel(self.root) < (8, 6, 0, 'beta', 3): + errmsg='Attempt to change read-only option' + self.checkInvalidParam(widget, 'orient', 'horizontal', + errmsg=errmsg) + widget2 = self.create(orient='horizontal') + self.assertEqual(str(widget2['orient']), 'horizontal') + + def test_add(self): + # attempt to add a child that is not a direct child of the paned window + label = ttk.Label(self.paned) + child = ttk.Label(label) + self.assertRaises(tkinter.TclError, self.paned.add, child) + label.destroy() + child.destroy() + # another attempt + label = ttk.Label(self.root) + child = ttk.Label(label) + self.assertRaises(tkinter.TclError, self.paned.add, child) + child.destroy() + label.destroy() + + good_child = ttk.Label(self.root) + self.paned.add(good_child) + # re-adding a child is not accepted + self.assertRaises(tkinter.TclError, self.paned.add, good_child) + + other_child = ttk.Label(self.paned) + self.paned.add(other_child) + self.assertEqual(self.paned.pane(0), self.paned.pane(1)) + self.assertRaises(tkinter.TclError, self.paned.pane, 2) + good_child.destroy() + other_child.destroy() + self.assertRaises(tkinter.TclError, self.paned.pane, 0) + + def test_forget(self): + self.assertRaises(tkinter.TclError, self.paned.forget, None) + self.assertRaises(tkinter.TclError, self.paned.forget, 0) + + self.paned.add(ttk.Label(self.root)) + self.paned.forget(0) + self.assertRaises(tkinter.TclError, self.paned.forget, 0) + + def test_insert(self): + self.assertRaises(tkinter.TclError, self.paned.insert, None, 0) + self.assertRaises(tkinter.TclError, self.paned.insert, 0, None) + self.assertRaises(tkinter.TclError, self.paned.insert, 0, 0) + + child = ttk.Label(self.root) + child2 = ttk.Label(self.root) + child3 = ttk.Label(self.root) + + if tk_version >= (8, 7): + self.paned.insert(0, child) + self.assertEqual(self.paned.panes(), (str(child),)) + self.paned.forget(0) + else: + self.assertRaises(tkinter.TclError, self.paned.insert, 0, child) + + self.assertEqual(self.paned.panes(), ()) + self.paned.insert('end', child2) + self.paned.insert(0, child) + self.assertEqual(self.paned.panes(), (str(child), str(child2))) + + self.paned.insert(0, child2) + self.assertEqual(self.paned.panes(), (str(child2), str(child))) + + self.paned.insert('end', child3) + self.assertEqual(self.paned.panes(), + (str(child2), str(child), str(child3))) + + # reinserting a child should move it to its current position + panes = self.paned.panes() + self.paned.insert('end', child3) + self.assertEqual(panes, self.paned.panes()) + + # moving child3 to child2 position should result in child2 ending up + # in previous child position and child ending up in previous child3 + # position + self.paned.insert(child2, child3) + self.assertEqual(self.paned.panes(), + (str(child3), str(child2), str(child))) + + def test_pane(self): + self.assertRaises(tkinter.TclError, self.paned.pane, 0) + + child = ttk.Label(self.root) + self.paned.add(child) + self.assertIsInstance(self.paned.pane(0), dict) + self.assertEqual(self.paned.pane(0, weight=None), + 0 if self.wantobjects else '0') + # newer form for querying a single option + self.assertEqual(self.paned.pane(0, 'weight'), + 0 if self.wantobjects else '0') + self.assertEqual(self.paned.pane(0), self.paned.pane(str(child))) + + self.assertRaises(tkinter.TclError, self.paned.pane, 0, + badoption='somevalue') + + def test_sashpos(self): + self.assertRaises(tkinter.TclError, self.paned.sashpos, None) + self.assertRaises(tkinter.TclError, self.paned.sashpos, '') + self.assertRaises(tkinter.TclError, self.paned.sashpos, 0) + + child = ttk.Label(self.paned, text='a') + self.paned.add(child, weight=1) + self.assertRaises(tkinter.TclError, self.paned.sashpos, 0) + child2 = ttk.Label(self.paned, text='b') + self.paned.add(child2) + self.assertRaises(tkinter.TclError, self.paned.sashpos, 1) + + self.paned.pack(expand=True, fill='both') + + curr_pos = self.paned.sashpos(0) + self.paned.sashpos(0, 1000) + self.assertNotEqual(curr_pos, self.paned.sashpos(0)) + self.assertIsInstance(self.paned.sashpos(0), int) + + +@add_standard_options(StandardTtkOptionsTests) +class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'compound', 'cursor', + 'image', 'justify', + 'padding', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'value', 'variable', 'width', + ) + + def create(self, **kwargs): + return ttk.Radiobutton(self.root, **kwargs) + + def test_configure_value(self): + widget = self.create() + self.checkParams(widget, 'value', 1, 2.3, '', 'any string') + + def test_configure_invoke(self): + success = [] + def cb_test(): + success.append(1) + return "cb test called" + + myvar = tkinter.IntVar(self.root) + cbtn = ttk.Radiobutton(self.root, command=cb_test, + variable=myvar, value=0) + cbtn2 = ttk.Radiobutton(self.root, command=cb_test, + variable=myvar, value=1) + + if self.wantobjects: + conv = lambda x: x + else: + conv = int + + res = cbtn.invoke() + self.assertEqual(res, "cb test called") + self.assertEqual(conv(cbtn['value']), myvar.get()) + self.assertEqual(myvar.get(), + conv(cbtn.tk.globalgetvar(cbtn['variable']))) + self.assertTrue(success) + + cbtn2['command'] = '' + res = cbtn2.invoke() + if tk_version >= (8, 7) and self.wantobjects: + self.assertEqual(res, ()) + else: + self.assertEqual(str(res), '') + self.assertLessEqual(len(success), 1) + self.assertEqual(conv(cbtn2['value']), myvar.get()) + self.assertEqual(myvar.get(), + conv(cbtn.tk.globalgetvar(cbtn['variable']))) + + self.assertEqual(str(cbtn['variable']), str(cbtn2['variable'])) + + +class MenubuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'compound', 'cursor', 'direction', + 'image', 'justify', 'menu', 'padding', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', + ) + + def create(self, **kwargs): + return ttk.Menubutton(self.root, **kwargs) + + def test_configure_direction(self): + widget = self.create() + values = ('above', 'below', 'left', 'right', 'flush') + self.checkEnumParam(widget, 'direction', *values, + sort=tk_version >= (8, 7)) + + def test_configure_menu(self): + widget = self.create() + menu = tkinter.Menu(widget, name='menu') + self.checkParam(widget, 'menu', menu, conv=str) + menu.destroy() + + +@add_standard_options(StandardTtkOptionsTests) +class ScaleTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'cursor', 'from', 'length', + 'orient', 'state', 'style', 'takefocus', 'to', 'value', 'variable', + ) + _conv_pixels = False + default_orient = 'horizontal' + + def setUp(self): + super().setUp() + self.scale = self.create() + self.scale.pack() + self.scale.update() + + def create(self, **kwargs): + return ttk.Scale(self.root, **kwargs) + + def test_configure_from(self): + widget = self.create() + self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False) + + def test_configure_length(self): + widget = self.create() + self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') + + test_configure_state = requires_tk(8, 6, 9)(StandardTtkOptionsTests.test_configure_state) + + def test_configure_to(self): + widget = self.create() + self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False) + + def test_configure_value(self): + widget = self.create() + self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False) + + def test_custom_event(self): + failure = [1, 1, 1] # will need to be empty + + funcid = self.scale.bind('<>', lambda evt: failure.pop()) + + self.scale['from'] = 10 + self.scale['from_'] = 10 + self.scale['to'] = 3 + + self.assertFalse(failure) + + failure = [1, 1, 1] + self.scale.configure(from_=2, to=5) + self.scale.configure(from_=0, to=-2) + self.scale.configure(to=10) + + self.assertFalse(failure) + + def test_get(self): + if self.wantobjects: + conv = lambda x: x + else: + conv = float + + scale_width = self.scale.winfo_width() + self.assertEqual(self.scale.get(scale_width, 0), self.scale['to']) + + self.assertEqual(conv(self.scale.get(0, 0)), conv(self.scale['from'])) + self.assertEqual(self.scale.get(), self.scale['value']) + self.scale['value'] = 30 + self.assertEqual(self.scale.get(), self.scale['value']) + + self.assertRaises(tkinter.TclError, self.scale.get, '', 0) + self.assertRaises(tkinter.TclError, self.scale.get, 0, '') + + def test_set(self): + if self.wantobjects: + conv = lambda x: x + else: + conv = float + + # set restricts the max/min values according to the current range + max = conv(self.scale['to']) + new_max = max + 10 + self.scale.set(new_max) + self.assertEqual(conv(self.scale.get()), max) + min = conv(self.scale['from']) + self.scale.set(min - 1) + self.assertEqual(conv(self.scale.get()), min) + + # changing directly the variable doesn't impose this limitation tho + var = tkinter.DoubleVar(self.root) + self.scale['variable'] = var + var.set(max + 5) + self.assertEqual(conv(self.scale.get()), var.get()) + self.assertEqual(conv(self.scale.get()), max + 5) + del var + gc_collect() # For PyPy or other GCs. + + # the same happens with the value option + self.scale['value'] = max + 10 + self.assertEqual(conv(self.scale.get()), max + 10) + self.assertEqual(conv(self.scale.get()), conv(self.scale['value'])) + + # nevertheless, note that the max/min values we can get specifying + # x, y coords are the ones according to the current range + self.assertEqual(conv(self.scale.get(0, 0)), min) + self.assertEqual(conv(self.scale.get(self.scale.winfo_width(), 0)), max) + + self.assertRaises(tkinter.TclError, self.scale.set, None) + + +@add_standard_options(StandardTtkOptionsTests) +class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'anchor', 'class', 'cursor', 'font', 'foreground', 'justify', + 'orient', 'length', + 'mode', 'maximum', 'phase', 'text', 'wraplength', + 'style', 'takefocus', 'value', 'variable', + ) + _conv_pixels = False + _allow_empty_justify = True + default_orient = 'horizontal' + + def create(self, **kwargs): + return ttk.Progressbar(self.root, **kwargs) + + @requires_tk(8, 7) + def test_configure_anchor(self): + widget = self.create() + self.checkEnumParam(widget, 'anchor', + 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center', '') + + test_configure_font = requires_tk(8, 7)(StandardOptionsTests.test_configure_font) + test_configure_foreground = requires_tk(8, 7)(StandardOptionsTests.test_configure_foreground) + test_configure_justify = requires_tk(8, 7)(StandardTtkOptionsTests.test_configure_justify) + + def test_configure_length(self): + widget = self.create() + self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i') + + def test_configure_maximum(self): + widget = self.create() + self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False) + + def test_configure_mode(self): + widget = self.create() + self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate') + + def test_configure_phase(self): + # XXX + pass + + test_configure_text = requires_tk(8, 7)(StandardOptionsTests.test_configure_text) + + def test_configure_value(self): + widget = self.create() + self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10, + conv=False) + + test_configure_wraplength = requires_tk(8, 7)(StandardOptionsTests.test_configure_wraplength) + + +@unittest.skipIf(sys.platform == 'darwin', + 'ttk.Scrollbar is special on MacOSX') +@add_standard_options(StandardTtkOptionsTests) +class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'cursor', 'orient', 'style', 'takefocus', + ) + default_orient = 'vertical' + + def create(self, **kwargs): + return ttk.Scrollbar(self.root, **kwargs) + + +@add_standard_options(StandardTtkOptionsTests) +class NotebookTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width', + ) + if tk_version >= (8, 7): + _conv_pixels = False + + def setUp(self): + super().setUp() + self.nb = self.create(padding=0) + self.child1 = ttk.Label(self.root) + self.child2 = ttk.Label(self.root) + self.nb.add(self.child1, text='a') + self.nb.add(self.child2, text='b') + + def create(self, **kwargs): + return ttk.Notebook(self.root, **kwargs) + + def test_configure_height(self): + widget = self.create() + if get_tk_patchlevel(self.root) < (8, 6, 15): + self.checkIntegerParam(widget, 'height', 402, -402, 0) + else: + self.checkPixelsParam(widget, 'height', '10c', 402, -402, 0, conv=False) + + def test_configure_width(self): + widget = self.create() + if get_tk_patchlevel(self.root) < (8, 6, 15): + self.checkIntegerParam(widget, 'width', 402, -402, 0) + else: + self.checkPixelsParam(widget, 'width', '10c', 402, -402, 0, conv=False) + + def test_tab_identifiers(self): + self.nb.forget(0) + self.nb.hide(self.child2) + self.assertRaises(tkinter.TclError, self.nb.tab, self.child1) + self.assertEqual(self.nb.index('end'), 1) + self.nb.add(self.child2) + self.assertEqual(self.nb.index('end'), 1) + self.nb.select(self.child2) + + self.assertTrue(self.nb.tab('current')) + self.nb.add(self.child1, text='a') + + self.nb.pack() + self.nb.update() + if sys.platform == 'darwin': + tb_idx = "@20,5" + else: + tb_idx = "@5,5" + self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current')) + + for i in range(5, 100, 5): + try: + if self.nb.tab('@%d, 5' % i, text=None) == 'a': + break + except tkinter.TclError: + pass + + else: + self.fail("Tab with text 'a' not found") + + def test_add_and_hidden(self): + self.assertRaises(tkinter.TclError, self.nb.hide, -1) + self.assertRaises(tkinter.TclError, self.nb.hide, 'hi') + self.assertRaises(tkinter.TclError, self.nb.hide, None) + self.assertRaises(tkinter.TclError, self.nb.add, None) + self.assertRaises(tkinter.TclError, self.nb.add, ttk.Label(self.root), + unknown='option') + + tabs = self.nb.tabs() + self.nb.hide(self.child1) + self.nb.add(self.child1) + self.assertEqual(self.nb.tabs(), tabs) + + child = ttk.Label(self.root) + self.nb.add(child, text='c') + tabs = self.nb.tabs() + + curr = self.nb.index('current') + # verify that the tab gets re-added at its previous position + child2_index = self.nb.index(self.child2) + self.nb.hide(self.child2) + self.nb.add(self.child2) + self.assertEqual(self.nb.tabs(), tabs) + self.assertEqual(self.nb.index(self.child2), child2_index) + self.assertEqual(str(self.child2), self.nb.tabs()[child2_index]) + # but the tab next to it (not hidden) is the one selected now + self.assertEqual(self.nb.index('current'), curr + 1) + + def test_forget(self): + self.assertRaises(tkinter.TclError, self.nb.forget, -1) + self.assertRaises(tkinter.TclError, self.nb.forget, 'hi') + self.assertRaises(tkinter.TclError, self.nb.forget, None) + + tabs = self.nb.tabs() + child1_index = self.nb.index(self.child1) + self.nb.forget(self.child1) + self.assertNotIn(str(self.child1), self.nb.tabs()) + self.assertEqual(len(tabs) - 1, len(self.nb.tabs())) + + self.nb.add(self.child1) + self.assertEqual(self.nb.index(self.child1), 1) + self.assertNotEqual(child1_index, self.nb.index(self.child1)) + + def test_index(self): + self.assertRaises(tkinter.TclError, self.nb.index, -1) + self.assertRaises(tkinter.TclError, self.nb.index, None) + + self.assertIsInstance(self.nb.index('end'), int) + self.assertEqual(self.nb.index(self.child1), 0) + self.assertEqual(self.nb.index(self.child2), 1) + self.assertEqual(self.nb.index('end'), 2) + + def test_insert(self): + # moving tabs + tabs = self.nb.tabs() + self.nb.insert(1, tabs[0]) + self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) + self.nb.insert(self.child1, self.child2) + self.assertEqual(self.nb.tabs(), tabs) + self.nb.insert('end', self.child1) + self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) + self.nb.insert('end', 0) + self.assertEqual(self.nb.tabs(), tabs) + # bad moves + self.assertRaises(tkinter.TclError, self.nb.insert, 2, tabs[0]) + self.assertRaises(tkinter.TclError, self.nb.insert, -1, tabs[0]) + + # new tab + child3 = ttk.Label(self.root) + self.nb.insert(1, child3) + self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1])) + self.nb.forget(child3) + self.assertEqual(self.nb.tabs(), tabs) + self.nb.insert(self.child1, child3) + self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs) + self.nb.forget(child3) + if tk_version >= (8, 7): + self.nb.insert(2, child3) + self.assertEqual(self.nb.tabs(), (*tabs, str(child3))) + else: + self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3) + self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3) + + # bad inserts + self.assertRaises(tkinter.TclError, self.nb.insert, 'end', None) + self.assertRaises(tkinter.TclError, self.nb.insert, None, 0) + self.assertRaises(tkinter.TclError, self.nb.insert, None, None) + + def test_select(self): + self.nb.pack() + self.nb.update() + + success = [] + tab_changed = [] + + self.child1.bind('', lambda evt: success.append(True)) + self.nb.bind('<>', + lambda evt: tab_changed.append(True)) + + self.assertEqual(self.nb.select(), str(self.child1)) + self.nb.select(self.child2) + self.assertTrue(success) + self.assertEqual(self.nb.select(), str(self.child2)) + + self.nb.update() + self.assertTrue(tab_changed) + + def test_tab(self): + self.assertRaises(tkinter.TclError, self.nb.tab, -1) + self.assertRaises(tkinter.TclError, self.nb.tab, 'notab') + self.assertRaises(tkinter.TclError, self.nb.tab, None) + + self.assertIsInstance(self.nb.tab(self.child1), dict) + self.assertEqual(self.nb.tab(self.child1, text=None), 'a') + # newer form for querying a single option + self.assertEqual(self.nb.tab(self.child1, 'text'), 'a') + self.nb.tab(self.child1, text='abc') + self.assertEqual(self.nb.tab(self.child1, text=None), 'abc') + self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc') + + def test_configure_tabs(self): + self.assertEqual(len(self.nb.tabs()), 2) + + self.nb.forget(self.child1) + self.nb.forget(self.child2) + + self.assertEqual(self.nb.tabs(), ()) + + def test_traversal(self): + self.nb.pack() + self.nb.update() + + self.nb.select(0) + + focus_identify_as = 'focus' if sys.platform != 'darwin' else '' + self.assertEqual(self.nb.identify(5, 5), focus_identify_as) + simulate_mouse_click(self.nb, 5, 5) + self.nb.focus_force() + self.nb.event_generate('') + self.assertEqual(self.nb.select(), str(self.child2)) + self.nb.focus_force() + self.nb.event_generate('') + self.assertEqual(self.nb.select(), str(self.child1)) + self.nb.focus_force() + self.nb.event_generate('') + self.assertEqual(self.nb.select(), str(self.child2)) + + self.nb.tab(self.child1, text='a', underline=0) + self.nb.tab(self.child2, text='e', underline=0) + self.nb.enable_traversal() + self.nb.focus_force() + self.assertEqual(self.nb.identify(5, 5), focus_identify_as) + simulate_mouse_click(self.nb, 5, 5) + # on macOS Emacs-style keyboard shortcuts are region-dependent; + # let's use the regular arrow keys instead + if sys.platform == 'darwin': + begin = '' + end = '' + else: + begin = '' + end = '' + self.nb.event_generate(begin) + self.assertEqual(self.nb.select(), str(self.child1)) + self.nb.event_generate(end) + self.assertEqual(self.nb.select(), str(self.child2)) + + +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class SpinboxTest(EntryTest, unittest.TestCase): + OPTIONS = ( + 'background', 'class', 'command', 'cursor', 'exportselection', + 'font', 'foreground', 'format', 'from', 'increment', + 'invalidcommand', 'justify', + 'placeholder', 'placeholderforeground', + 'show', 'state', 'style', + 'takefocus', 'textvariable', 'to', 'validate', 'validatecommand', + 'values', 'width', 'wrap', 'xscrollcommand', + ) + IDENTIFY_AS = {'Spinbox.field', 'textarea'} + + def setUp(self): + super().setUp() + self.spin = self.create() + self.spin.pack() + + def create(self, **kwargs): + return ttk.Spinbox(self.root, **kwargs) + + def _click_increment_arrow(self): + width = self.spin.winfo_width() + height = self.spin.winfo_height() + x = width - 5 + y = height//2 - 5 + self.assertRegex(self.spin.identify(x, y), r'.*uparrow\Z') + self.spin.event_generate('', x=x, y=y) + self.spin.event_generate('', x=x, y=y) + self.spin.update_idletasks() + + def _click_decrement_arrow(self): + width = self.spin.winfo_width() + height = self.spin.winfo_height() + x = width - 5 + y = height//2 + 4 + self.assertRegex(self.spin.identify(x, y), r'.*downarrow\Z') + self.spin.event_generate('', x=x, y=y) + self.spin.event_generate('', x=x, y=y) + self.spin.update_idletasks() + + def test_configure_command(self): + success = [] + + self.spin['command'] = lambda: success.append(True) + self.spin.update() + self._click_increment_arrow() + self.spin.update() + self.assertTrue(success) + + self._click_decrement_arrow() + self.assertEqual(len(success), 2) + + # testing postcommand removal + self.spin['command'] = '' + self.spin.update_idletasks() + self._click_increment_arrow() + self._click_decrement_arrow() + self.spin.update() + self.assertEqual(len(success), 2) + + def test_configure_to(self): + self.spin['from'] = 0 + self.spin['to'] = 5 + self.spin.set(4) + self.spin.update() + self._click_increment_arrow() # 5 + + self.assertEqual(self.spin.get(), '5') + + self._click_increment_arrow() # 5 + self.assertEqual(self.spin.get(), '5') + + def test_configure_from(self): + self.spin['from'] = 1 + self.spin['to'] = 10 + self.spin.set(2) + self.spin.update() + self._click_decrement_arrow() # 1 + self.assertEqual(self.spin.get(), '1') + self._click_decrement_arrow() # 1 + self.assertEqual(self.spin.get(), '1') + + def test_configure_increment(self): + self.spin['from'] = 0 + self.spin['to'] = 10 + self.spin['increment'] = 4 + self.spin.set(1) + self.spin.update() + + self._click_increment_arrow() # 5 + self.assertEqual(self.spin.get(), '5') + self.spin['increment'] = 2 + self.spin.update() + self._click_decrement_arrow() # 3 + self.assertEqual(self.spin.get(), '3') + + def test_configure_format(self): + self.spin.set(1) + self.spin['format'] = '%10.3f' + self.spin.update() + self._click_increment_arrow() + value = self.spin.get() + + self.assertEqual(len(value), 10) + self.assertEqual(value.index('.'), 6) + + self.spin['format'] = '' + self.spin.update() + self._click_increment_arrow() + value = self.spin.get() + self.assertTrue('.' not in value) + self.assertEqual(len(value), 1) + + def test_configure_wrap(self): + self.spin['to'] = 10 + self.spin['from'] = 1 + self.spin.set(1) + self.spin['wrap'] = True + self.spin.update() + + self._click_decrement_arrow() + self.assertEqual(self.spin.get(), '10') + + self._click_increment_arrow() + self.assertEqual(self.spin.get(), '1') + + self.spin['wrap'] = False + self.spin.update() + + self._click_decrement_arrow() + self.assertEqual(self.spin.get(), '1') + + def test_configure_values(self): + self.assertEqual(self.spin['values'], '') + self.checkParam(self.spin, 'values', 'mon tue wed thur', + expected=('mon', 'tue', 'wed', 'thur')) + self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur')) + self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string')) + self.checkParam(self.spin, 'values', '') + + self.spin['values'] = ['a', 1, 'c'] + + # test incrementing / decrementing values + self.spin.set('a') + self.spin.update() + self._click_increment_arrow() + self.assertEqual(self.spin.get(), '1') + + self._click_decrement_arrow() + self.assertEqual(self.spin.get(), 'a') + + # testing values with empty string set through configure + self.spin.configure(values=[1, '', 2]) + self.assertEqual(self.spin['values'], + ('1', '', '2') if self.wantobjects else + '1 {} 2') + + # testing values with spaces + self.spin['values'] = ['a b', 'a\tb', 'a\nb'] + self.assertEqual(self.spin['values'], + ('a b', 'a\tb', 'a\nb') if self.wantobjects else + '{a b} {a\tb} {a\nb}') + + # testing values with special characters + self.spin['values'] = [r'a\tb', '"a"', '} {'] + self.assertEqual(self.spin['values'], + (r'a\tb', '"a"', '} {') if self.wantobjects else + r'a\\tb {"a"} \}\ \{') + + # testing creating spinbox with empty string in values + spin2 = ttk.Spinbox(self.root, values=[1, 2, '']) + self.assertEqual(spin2['values'], + ('1', '2', '') if self.wantobjects else '1 2 {}') + spin2.destroy() + + +@add_standard_options(StandardTtkOptionsTests) +class TreeviewTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'columns', 'cursor', 'displaycolumns', + 'height', 'padding', 'selectmode', 'selecttype', 'show', 'striped', + 'style', 'takefocus', 'titlecolumns', 'titleitems', + 'xscrollcommand', 'yscrollcommand', + ) + + def setUp(self): + super().setUp() + self.tv = self.create(padding=0) + + def create(self, **kwargs): + return ttk.Treeview(self.root, **kwargs) + + def test_configure_columns(self): + widget = self.create() + self.checkParam(widget, 'columns', 'a b c', + expected=('a', 'b', 'c')) + self.checkParam(widget, 'columns', ('a', 'b', 'c')) + self.checkParam(widget, 'columns', '', + expected=() if tk_version >= (8, 7) else '') + + def test_configure_displaycolumns(self): + widget = self.create() + widget['columns'] = ('a', 'b', 'c') + self.checkParam(widget, 'displaycolumns', 'b a c', + expected=('b', 'a', 'c')) + self.checkParam(widget, 'displaycolumns', ('b', 'a', 'c')) + self.checkParam(widget, 'displaycolumns', '#all', + expected=('#all',)) + self.checkParam(widget, 'displaycolumns', (2, 1, 0)) + self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'), + errmsg='Invalid column index "?d"?') + errmsg = 'Column index "?{}"? out of bounds' + self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3), + errmsg=errmsg.format(3)) + self.checkInvalidParam(widget, 'displaycolumns', (1, -2), + errmsg=errmsg.format(-2)) + + def test_configure_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) + self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=False) + + def test_configure_selectmode(self): + widget = self.create() + self.checkEnumParam(widget, 'selectmode', + 'none', 'browse', 'extended') + + @requires_tk(8, 7) + def test_configure_selecttype(self): + widget = self.create() + self.checkEnumParam(widget, 'selecttype', 'item', 'cell') + + def test_configure_show(self): + widget = self.create() + self.checkParam(widget, 'show', 'tree headings', + expected=('tree', 'headings')) + self.checkParam(widget, 'show', ('tree', 'headings')) + self.checkParam(widget, 'show', ('headings', 'tree')) + self.checkParam(widget, 'show', 'tree', expected=('tree',)) + self.checkParam(widget, 'show', 'headings', expected=('headings',)) + + @requires_tk(8, 7) + def test_configure_striped(self): + widget = self.create() + self.checkBooleanParam(widget, 'striped') + + @requires_tk(8, 7) + def test_configure_titlecolumns(self): + widget = self.create() + self.checkIntegerParam(widget, 'titlecolumns', 0, 1, 5) + self.checkInvalidParam(widget, 'titlecolumns', -2) + + @requires_tk(8, 7) + def test_configure_titleitems(self): + widget = self.create() + self.checkIntegerParam(widget, 'titleitems', 0, 1, 5) + self.checkInvalidParam(widget, 'titleitems', -2) + + def test_bbox(self): + self.tv.pack() + self.assertEqual(self.tv.bbox(''), '') + self.tv.update() + + item_id = self.tv.insert('', 'end') + children = self.tv.get_children() + self.assertTrue(children) + + bbox = self.tv.bbox(children[0]) + self.assertIsBoundingBox(bbox) + + # compare width in bboxes + self.tv['columns'] = ['test'] + self.tv.column('test', width=50) + bbox_column0 = self.tv.bbox(children[0], 0) + root_width = self.tv.column('#0', width=None) + if not self.wantobjects: + root_width = int(root_width) + self.assertEqual(bbox_column0[0], bbox[0] + root_width) + + # verify that bbox of a closed item is the empty string + child1 = self.tv.insert(item_id, 'end') + self.assertEqual(self.tv.bbox(child1), '') + + def test_children(self): + # no children yet, should get an empty tuple + self.assertEqual(self.tv.get_children(), ()) + + item_id = self.tv.insert('', 'end') + self.assertIsInstance(self.tv.get_children(), tuple) + self.assertEqual(self.tv.get_children()[0], item_id) + + # add item_id and child3 as children of child2 + child2 = self.tv.insert('', 'end') + child3 = self.tv.insert('', 'end') + self.tv.set_children(child2, item_id, child3) + self.assertEqual(self.tv.get_children(child2), (item_id, child3)) + + # child3 has child2 as parent, thus trying to set child2 as a children + # of child3 should result in an error + self.assertRaises(tkinter.TclError, + self.tv.set_children, child3, child2) + + # remove child2 children + self.tv.set_children(child2) + self.assertEqual(self.tv.get_children(child2), ()) + + # remove root's children + self.tv.set_children('') + self.assertEqual(self.tv.get_children(), ()) + + def test_column(self): + # return a dict with all options/values + self.assertIsInstance(self.tv.column('#0'), dict) + # return a single value of the given option + if self.wantobjects: + self.assertIsInstance(self.tv.column('#0', width=None), int) + # set a new value for an option + self.tv.column('#0', width=10) + # testing new way to get option value + self.assertEqual(self.tv.column('#0', 'width'), + 10 if self.wantobjects else '10') + self.assertEqual(self.tv.column('#0', width=None), + 10 if self.wantobjects else '10') + # check read-only option + self.assertRaises(tkinter.TclError, self.tv.column, '#0', id='X') + + self.assertRaises(tkinter.TclError, self.tv.column, 'invalid') + invalid_kws = [ + {'unknown_option': 'some value'}, {'stretch': 'wrong'}, + {'anchor': 'wrong'}, {'width': 'wrong'}, {'minwidth': 'wrong'} + ] + for kw in invalid_kws: + self.assertRaises(tkinter.TclError, self.tv.column, '#0', + **kw) + + def test_delete(self): + self.assertRaises(tkinter.TclError, self.tv.delete, '#0') + + item_id = self.tv.insert('', 'end') + item2 = self.tv.insert(item_id, 'end') + self.assertEqual(self.tv.get_children(), (item_id, )) + self.assertEqual(self.tv.get_children(item_id), (item2, )) + + self.tv.delete(item_id) + self.assertFalse(self.tv.get_children()) + + # reattach should fail + self.assertRaises(tkinter.TclError, + self.tv.reattach, item_id, '', 'end') + + # test multiple item delete + item1 = self.tv.insert('', 'end') + item2 = self.tv.insert('', 'end') + self.assertEqual(self.tv.get_children(), (item1, item2)) + + self.tv.delete(item1, item2) + self.assertFalse(self.tv.get_children()) + + def test_detach_reattach(self): + item_id = self.tv.insert('', 'end') + item2 = self.tv.insert(item_id, 'end') + + # calling detach without items is valid, although it does nothing + prev = self.tv.get_children() + self.tv.detach() # this should do nothing + self.assertEqual(prev, self.tv.get_children()) + + self.assertEqual(self.tv.get_children(), (item_id, )) + self.assertEqual(self.tv.get_children(item_id), (item2, )) + + # detach item with children + self.tv.detach(item_id) + self.assertFalse(self.tv.get_children()) + + # reattach item with children + self.tv.reattach(item_id, '', 'end') + self.assertEqual(self.tv.get_children(), (item_id, )) + self.assertEqual(self.tv.get_children(item_id), (item2, )) + + # move a children to the root + self.tv.move(item2, '', 'end') + self.assertEqual(self.tv.get_children(), (item_id, item2)) + self.assertEqual(self.tv.get_children(item_id), ()) + + # bad values + self.assertRaises(tkinter.TclError, + self.tv.reattach, 'nonexistent', '', 'end') + self.assertRaises(tkinter.TclError, + self.tv.detach, 'nonexistent') + self.assertRaises(tkinter.TclError, + self.tv.reattach, item2, 'otherparent', 'end') + self.assertRaises(tkinter.TclError, + self.tv.reattach, item2, '', 'invalid') + + # multiple detach + self.tv.detach(item_id, item2) + self.assertEqual(self.tv.get_children(), ()) + self.assertEqual(self.tv.get_children(item_id), ()) + + def test_exists(self): + self.assertEqual(self.tv.exists('something'), False) + self.assertEqual(self.tv.exists(''), True) + self.assertEqual(self.tv.exists({}), False) + + # the following will make a tk.call equivalent to + # tk.call(treeview, "exists") which should result in an error + # in the tcl interpreter since tk requires an item. + self.assertRaises(tkinter.TclError, self.tv.exists, None) + + def test_focus(self): + # nothing is focused right now + self.assertEqual(self.tv.focus(), '') + + item1 = self.tv.insert('', 'end') + self.tv.focus(item1) + self.assertEqual(self.tv.focus(), item1) + + self.tv.delete(item1) + self.assertEqual(self.tv.focus(), '') + + # try focusing inexistent item + self.assertRaises(tkinter.TclError, self.tv.focus, 'hi') + + def test_heading(self): + # check a dict is returned + self.assertIsInstance(self.tv.heading('#0'), dict) + + # check a value is returned + self.tv.heading('#0', text='hi') + self.assertEqual(self.tv.heading('#0', 'text'), 'hi') + self.assertEqual(self.tv.heading('#0', text=None), 'hi') + + # invalid option + self.assertRaises(tkinter.TclError, self.tv.heading, '#0', + background=None) + # invalid value + self.assertRaises(tkinter.TclError, self.tv.heading, '#0', + anchor=1) + + def test_heading_callback(self): + def simulate_heading_click(x, y): + if tk_version >= (8, 6): + self.assertEqual(self.tv.identify_column(x), '#0') + self.assertEqual(self.tv.identify_region(x, y), 'heading') + simulate_mouse_click(self.tv, x, y) + self.tv.update() + + success = [] # no success for now + + self.tv.pack() + self.tv.heading('#0', command=lambda: success.append(True)) + self.tv.column('#0', width=100) + self.tv.update() + + # assuming that the coords (5, 5) fall into heading #0 + simulate_heading_click(5, 5) + if not success: + self.fail("The command associated to the treeview heading wasn't " + "invoked.") + + success = [] + commands = self.tv.master._tclCommands + self.tv.heading('#0', command=str(self.tv.heading('#0', command=None))) + self.assertEqual(commands, self.tv.master._tclCommands) + simulate_heading_click(5, 5) + if not success: + self.fail("The command associated to the treeview heading wasn't " + "invoked.") + + # XXX The following raises an error in a tcl interpreter, but not in + # Python + #self.tv.heading('#0', command='I dont exist') + #simulate_heading_click(5, 5) + + def test_index(self): + # item 'what' doesn't exist + self.assertRaises(tkinter.TclError, self.tv.index, 'what') + + self.assertEqual(self.tv.index(''), 0) + + item1 = self.tv.insert('', 'end') + item2 = self.tv.insert('', 'end') + c1 = self.tv.insert(item1, 'end') + c2 = self.tv.insert(item1, 'end') + self.assertEqual(self.tv.index(item1), 0) + self.assertEqual(self.tv.index(c1), 0) + self.assertEqual(self.tv.index(c2), 1) + self.assertEqual(self.tv.index(item2), 1) + + self.tv.move(item2, '', 0) + self.assertEqual(self.tv.index(item2), 0) + self.assertEqual(self.tv.index(item1), 1) + + # check that index still works even after its parent and siblings + # have been detached + self.tv.detach(item1) + self.assertEqual(self.tv.index(c2), 1) + self.tv.detach(c1) + self.assertEqual(self.tv.index(c2), 0) + + # but it fails after item has been deleted + self.tv.delete(item1) + self.assertRaises(tkinter.TclError, self.tv.index, c2) + + def test_insert_item(self): + # parent 'none' doesn't exist + self.assertRaises(tkinter.TclError, self.tv.insert, 'none', 'end') + + # open values + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', + open='') + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', + open='please') + self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=True))) + self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=False))) + + # invalid index + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'middle') + + # trying to duplicate item id is invalid + itemid = self.tv.insert('', 'end', 'first-item') + self.assertEqual(itemid, 'first-item') + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', + 'first-item') + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', + MockTclObj('first-item')) + + # unicode values + value = '\xe1ba' + item = self.tv.insert('', 'end', values=(value, )) + self.assertEqual(self.tv.item(item, 'values'), + (value,) if self.wantobjects else value) + self.assertEqual(self.tv.item(item, values=None), + (value,) if self.wantobjects else value) + + self.tv.item(item, values=self.root.splitlist(self.tv.item(item, values=None))) + self.assertEqual(self.tv.item(item, values=None), + (value,) if self.wantobjects else value) + + self.assertIsInstance(self.tv.item(item), dict) + + # erase item values + self.tv.item(item, values='') + self.assertFalse(self.tv.item(item, values=None)) + + # item tags + item = self.tv.insert('', 'end', tags=[1, 2, value]) + self.assertEqual(self.tv.item(item, tags=None), + ('1', '2', value) if self.wantobjects else + '1 2 %s' % value) + self.tv.item(item, tags=[]) + self.assertFalse(self.tv.item(item, tags=None)) + self.tv.item(item, tags=(1, 2)) + self.assertEqual(self.tv.item(item, tags=None), + ('1', '2') if self.wantobjects else '1 2') + + # values with spaces + item = self.tv.insert('', 'end', values=('a b c', + '%s %s' % (value, value))) + self.assertEqual(self.tv.item(item, values=None), + ('a b c', '%s %s' % (value, value)) if self.wantobjects else + '{a b c} {%s %s}' % (value, value)) + + # text + self.assertEqual(self.tv.item( + self.tv.insert('', 'end', text="Label here"), text=None), + "Label here") + self.assertEqual(self.tv.item( + self.tv.insert('', 'end', text=value), text=None), + value) + + # test for values which are not None + itemid = self.tv.insert('', 'end', 0) + self.assertEqual(itemid, '0') + itemid = self.tv.insert('', 'end', 0.0) + self.assertEqual(itemid, '0.0') + # this is because False resolves to 0 and element with 0 iid is already present + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', False) + self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', '') + + def test_selection(self): + self.assertRaises(TypeError, self.tv.selection, 'spam') + # item 'none' doesn't exist + self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none') + self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none') + self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none') + self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none') + + item1 = self.tv.insert('', 'end') + item2 = self.tv.insert('', 'end') + c1 = self.tv.insert(item1, 'end') + c2 = self.tv.insert(item1, 'end') + c3 = self.tv.insert(item1, 'end') + self.assertEqual(self.tv.selection(), ()) + + self.tv.selection_set(c1, item2) + self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_set(c2) + self.assertEqual(self.tv.selection(), (c2,)) + + self.tv.selection_add(c1, item2) + self.assertEqual(self.tv.selection(), (c1, c2, item2)) + self.tv.selection_add(item1) + self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) + self.tv.selection_add() + self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) + + self.tv.selection_remove(item1, c3) + self.assertEqual(self.tv.selection(), (c1, c2, item2)) + self.tv.selection_remove(c2) + self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_remove() + self.assertEqual(self.tv.selection(), (c1, item2)) + + self.tv.selection_toggle(c1, c3) + self.assertEqual(self.tv.selection(), (c3, item2)) + self.tv.selection_toggle(item2) + self.assertEqual(self.tv.selection(), (c3,)) + self.tv.selection_toggle() + self.assertEqual(self.tv.selection(), (c3,)) + + self.tv.insert('', 'end', id='with spaces') + self.tv.selection_set('with spaces') + self.assertEqual(self.tv.selection(), ('with spaces',)) + + self.tv.insert('', 'end', id='{brace') + self.tv.selection_set('{brace') + self.assertEqual(self.tv.selection(), ('{brace',)) + + self.tv.insert('', 'end', id='unicode\u20ac') + self.tv.selection_set('unicode\u20ac') + self.assertEqual(self.tv.selection(), ('unicode\u20ac',)) + + self.tv.insert('', 'end', id=b'bytes\xe2\x82\xac') + self.tv.selection_set(b'bytes\xe2\x82\xac') + self.assertEqual(self.tv.selection(), ('bytes\xe2\x82\xac',)) + + self.tv.selection_set() + self.assertEqual(self.tv.selection(), ()) + + # Old interface + self.tv.selection_set((c1, item2)) + self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_add((c1, item1)) + self.assertEqual(self.tv.selection(), (item1, c1, item2)) + self.tv.selection_remove((item1, c3)) + self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_toggle((c1, c3)) + self.assertEqual(self.tv.selection(), (c3, item2)) + + def test_set(self): + self.tv['columns'] = ['A', 'B'] + item = self.tv.insert('', 'end', values=['a', 'b']) + self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'}) + + self.tv.set(item, 'B', 'a') + self.assertEqual(self.tv.item(item, values=None), + ('a', 'a') if self.wantobjects else 'a a') + + self.tv['columns'] = ['B'] + self.assertEqual(self.tv.set(item), {'B': 'a'}) + + self.tv.set(item, 'B', 'b') + self.assertEqual(self.tv.set(item, column='B'), 'b') + self.assertEqual(self.tv.item(item, values=None), + ('b', 'a') if self.wantobjects else 'b a') + + self.tv.set(item, 'B', 123) + self.assertEqual(self.tv.set(item, 'B'), + 123 if self.wantobjects else '123') + self.assertEqual(self.tv.item(item, values=None), + (123, 'a') if self.wantobjects else '123 a') + self.assertEqual(self.tv.set(item), + {'B': 123} if self.wantobjects else {'B': '123'}) + + # inexistent column + self.assertRaises(tkinter.TclError, self.tv.set, item, 'A') + self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b') + + # inexistent item + self.assertRaises(tkinter.TclError, self.tv.set, 'notme') + + def test_tag_bind(self): + events = [] + item1 = self.tv.insert('', 'end', tags=['call']) + item2 = self.tv.insert('', 'end', tags=['call']) + self.tv.tag_bind('call', '', + lambda evt: events.append(1)) + self.tv.tag_bind('call', '', + lambda evt: events.append(2)) + + self.tv.pack() + self.tv.update() + + pos_y = set() + found = set() + for i in range(0, 100, 10): + if len(found) == 2: # item1 and item2 already found + break + item_id = self.tv.identify_row(i) + if item_id and item_id not in found: + pos_y.add(i) + found.add(item_id) + + self.assertEqual(len(pos_y), 2) # item1 and item2 y pos + for y in pos_y: + simulate_mouse_click(self.tv, 0, y) + + # by now there should be 4 things in the events list, since each + # item had a bind for two events that were simulated above + self.assertEqual(len(events), 4) + for evt in zip(events[::2], events[1::2]): + self.assertEqual(evt, (1, 2)) + + def test_tag_configure(self): + # Just testing parameter passing for now + self.assertRaises(TypeError, self.tv.tag_configure) + self.assertRaises(tkinter.TclError, self.tv.tag_configure, + 'test', sky='blue') + self.tv.tag_configure('test', foreground='blue') + self.assertEqual(str(self.tv.tag_configure('test', 'foreground')), + 'blue') + self.assertEqual(str(self.tv.tag_configure('test', foreground=None)), + 'blue') + self.assertIsInstance(self.tv.tag_configure('test'), dict) + + def test_tag_has(self): + item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1']) + item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2']) + self.assertRaises(TypeError, self.tv.tag_has) + self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing') + self.assertTrue(self.tv.tag_has('tag1', item1)) + self.assertFalse(self.tv.tag_has('tag1', item2)) + self.assertFalse(self.tv.tag_has('tag2', item1)) + self.assertTrue(self.tv.tag_has('tag2', item2)) + self.assertFalse(self.tv.tag_has('tag3', item1)) + self.assertFalse(self.tv.tag_has('tag3', item2)) + self.assertEqual(self.tv.tag_has('tag1'), (item1,)) + self.assertEqual(self.tv.tag_has('tag2'), (item2,)) + self.assertEqual(self.tv.tag_has('tag3'), ()) + + +@add_standard_options(StandardTtkOptionsTests) +class SeparatorTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'orient', 'style', 'takefocus', + # 'state'? + ) + default_orient = 'horizontal' + + def create(self, **kwargs): + return ttk.Separator(self.root, **kwargs) + + +@add_standard_options(StandardTtkOptionsTests) +class SizegripTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'style', 'takefocus', + # 'state'? + ) + + def create(self, **kwargs): + return ttk.Sizegrip(self.root, **kwargs) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_frame(self): + self._test_widget(ttk.Frame) + + def test_label(self): + self._test_widget(ttk.Label) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_ttk_textonly.py b/Lib/test/test_ttk_textonly.py new file mode 100644 index 00000000000..e6525c4d6c6 --- /dev/null +++ b/Lib/test/test_ttk_textonly.py @@ -0,0 +1,481 @@ +from test.support import import_helper + +# Skip this test if _tkinter does not exist. +import_helper.import_module('_tkinter') + +import unittest +from tkinter import ttk + + +class MockTkApp: + + def splitlist(self, arg): + if isinstance(arg, tuple): + return arg + return arg.split(':') + + def wantobjects(self): + return True + + +class MockTclObj(object): + typename = 'test' + + def __init__(self, val): + self.val = val + + def __str__(self): + return str(self.val) + + +class MockStateSpec(object): + typename = 'StateSpec' + + def __init__(self, *args): + self.val = args + + def __str__(self): + return ' '.join(self.val) + + +class InternalFunctionsTest(unittest.TestCase): + + def test_format_optdict(self): + def check_against(fmt_opts, result): + for i in range(0, len(fmt_opts), 2): + self.assertEqual(result.pop(fmt_opts[i]), fmt_opts[i + 1]) + if result: + self.fail("result still got elements: %s" % result) + + # passing an empty dict should return an empty object (tuple here) + self.assertFalse(ttk._format_optdict({})) + + # check list formatting + check_against( + ttk._format_optdict({'fg': 'blue', 'padding': [1, 2, 3, 4]}), + {'-fg': 'blue', '-padding': '1 2 3 4'}) + + # check tuple formatting (same as list) + check_against( + ttk._format_optdict({'test': (1, 2, '', 0)}), + {'-test': '1 2 {} 0'}) + + # check untouched values + check_against( + ttk._format_optdict({'test': {'left': 'as is'}}), + {'-test': {'left': 'as is'}}) + + # check script formatting + check_against( + ttk._format_optdict( + {'test': [1, -1, '', '2m', 0], 'test2': 3, + 'test3': '', 'test4': 'abc def', + 'test5': '"abc"', 'test6': '{}', + 'test7': '} -spam {'}, script=True), + {'-test': '{1 -1 {} 2m 0}', '-test2': '3', + '-test3': '{}', '-test4': '{abc def}', + '-test5': '{"abc"}', '-test6': r'\{\}', + '-test7': r'\}\ -spam\ \{'}) + + opts = {'αβγ': True, 'á': False} + orig_opts = opts.copy() + # check if giving unicode keys is fine + check_against(ttk._format_optdict(opts), {'-αβγ': True, '-á': False}) + # opts should remain unchanged + self.assertEqual(opts, orig_opts) + + # passing values with spaces inside a tuple/list + check_against( + ttk._format_optdict( + {'option': ('one two', 'three')}), + {'-option': '{one two} three'}) + check_against( + ttk._format_optdict( + {'option': ('one\ttwo', 'three')}), + {'-option': '{one\ttwo} three'}) + + # passing empty strings inside a tuple/list + check_against( + ttk._format_optdict( + {'option': ('', 'one')}), + {'-option': '{} one'}) + + # passing values with braces inside a tuple/list + check_against( + ttk._format_optdict( + {'option': ('one} {two', 'three')}), + {'-option': r'one\}\ \{two three'}) + + # passing quoted strings inside a tuple/list + check_against( + ttk._format_optdict( + {'option': ('"one"', 'two')}), + {'-option': '{"one"} two'}) + check_against( + ttk._format_optdict( + {'option': ('{one}', 'two')}), + {'-option': r'\{one\} two'}) + + # ignore an option + amount_opts = len(ttk._format_optdict(opts, ignore=('á'))) / 2 + self.assertEqual(amount_opts, len(opts) - 1) + + # ignore non-existing options + amount_opts = len(ttk._format_optdict(opts, ignore=('á', 'b'))) / 2 + self.assertEqual(amount_opts, len(opts) - 1) + + # ignore every option + self.assertFalse(ttk._format_optdict(opts, ignore=list(opts.keys()))) + + + def test_format_mapdict(self): + opts = {'a': [('b', 'c', 'val'), ('d', 'otherval'), ('', 'single')]} + result = ttk._format_mapdict(opts) + self.assertEqual(len(result), len(list(opts.keys())) * 2) + self.assertEqual(result, ('-a', '{b c} val d otherval {} single')) + self.assertEqual(ttk._format_mapdict(opts, script=True), + ('-a', '{{b c} val d otherval {} single}')) + + self.assertEqual(ttk._format_mapdict({2: []}), ('-2', '')) + + opts = {'üñíćódè': [('á', 'vãl')]} + result = ttk._format_mapdict(opts) + self.assertEqual(result, ('-üñíćódè', 'á vãl')) + + self.assertEqual(ttk._format_mapdict({'opt': [('value',)]}), + ('-opt', '{} value')) + + # empty states + valid = {'opt': [('', '', 'hi')]} + self.assertEqual(ttk._format_mapdict(valid), ('-opt', '{ } hi')) + + # when passing multiple states, they all must be strings + invalid = {'opt': [(1, 2, 'valid val')]} + self.assertRaises(TypeError, ttk._format_mapdict, invalid) + invalid = {'opt': [([1], '2', 'valid val')]} + self.assertRaises(TypeError, ttk._format_mapdict, invalid) + # but when passing a single state, it can be anything + valid = {'opt': [[1, 'value']]} + self.assertEqual(ttk._format_mapdict(valid), ('-opt', '1 value')) + # special attention to single states which evaluate to False + for stateval in (None, 0, False, '', set()): # just some samples + valid = {'opt': [(stateval, 'value')]} + self.assertEqual(ttk._format_mapdict(valid), + ('-opt', '{} value')) + + # values must be iterable + opts = {'a': None} + self.assertRaises(TypeError, ttk._format_mapdict, opts) + + + def test_format_elemcreate(self): + self.assertTrue(ttk._format_elemcreate(None), (None, ())) + + ## Testing type = image + # image type expects at least an image name, so this should raise + # IndexError since it tries to access the index 0 of an empty tuple + self.assertRaises(IndexError, ttk._format_elemcreate, 'image') + + # don't format returned values as a tcl script + # minimum acceptable for image type + self.assertEqual(ttk._format_elemcreate('image', False, 'test'), + ("test", ())) + # specifying a state spec + self.assertEqual(ttk._format_elemcreate('image', False, 'test', + ('', 'a')), ("test {} a", ())) + # state spec with multiple states + self.assertEqual(ttk._format_elemcreate('image', False, 'test', + ('a', 'b', 'c')), ("test {a b} c", ())) + # state spec and options + self.assertEqual(ttk._format_elemcreate('image', False, 'test', + ('a', 'b'), a='x'), ("test a b", ("-a", "x"))) + # format returned values as a tcl script + # state spec with multiple states and an option with a multivalue + self.assertEqual(ttk._format_elemcreate('image', True, 'test', + ('a', 'b', 'c', 'd'), x=[2, 3]), ("{test {a b c} d}", "-x {2 3}")) + + ## Testing type = vsapi + # vsapi type expects at least a class name and a part_id, so this + # should raise a ValueError since it tries to get two elements from + # an empty tuple + self.assertRaises(ValueError, ttk._format_elemcreate, 'vsapi') + + # don't format returned values as a tcl script + # minimum acceptable for vsapi + self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'), + ('a', 'b', ('', 1), ())) + # now with a state spec with multiple states + self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', + [('a', 'b', 'c')]), ('a', 'b', ('a b', 'c'), ())) + # state spec and option + self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', + [('a', 'b')], opt='x'), ('a', 'b', ('a', 'b'), ("-opt", "x"))) + # format returned values as a tcl script + # state spec with a multivalue and an option + self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', + opt='x'), ("a b {{} 1}", "-opt x")) + self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', + [('a', 'b', [1, 2])], opt='x'), ("a b {{a b} {1 2}}", "-opt x")) + + # Testing type = from + # from type expects at least a type name + self.assertRaises(IndexError, ttk._format_elemcreate, 'from') + + self.assertEqual(ttk._format_elemcreate('from', False, 'a'), + ('a', ())) + self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'), + ('a', ('b',))) + self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'), + ('a', 'b')) + + + def test_format_layoutlist(self): + def sample(indent=0, indent_size=2): + return ttk._format_layoutlist( + [('a', {'other': [1, 2, 3], 'children': + [('b', {'children': + [('c', {'children': + [('d', {'nice': 'opt'})], 'something': (1, 2) + })] + })] + })], indent=indent, indent_size=indent_size)[0] + + def sample_expected(indent=0, indent_size=2): + spaces = lambda amount=0: ' ' * (amount + indent) + return ( + "%sa -other {1 2 3} -children {\n" + "%sb -children {\n" + "%sc -something {1 2} -children {\n" + "%sd -nice opt\n" + "%s}\n" + "%s}\n" + "%s}" % (spaces(), spaces(indent_size), + spaces(2 * indent_size), spaces(3 * indent_size), + spaces(2 * indent_size), spaces(indent_size), spaces())) + + # empty layout + self.assertEqual(ttk._format_layoutlist([])[0], '') + + # _format_layoutlist always expects the second item (in every item) + # to act like a dict (except when the value evaluates to False). + self.assertRaises(AttributeError, + ttk._format_layoutlist, [('a', 'b')]) + + smallest = ttk._format_layoutlist([('a', None)], indent=0) + self.assertEqual(smallest, + ttk._format_layoutlist([('a', '')], indent=0)) + self.assertEqual(smallest[0], 'a') + + # testing indentation levels + self.assertEqual(sample(), sample_expected()) + for i in range(4): + self.assertEqual(sample(i), sample_expected(i)) + self.assertEqual(sample(i, i), sample_expected(i, i)) + + # invalid layout format, different kind of exceptions will be + # raised by internal functions + + # plain wrong format + self.assertRaises(ValueError, ttk._format_layoutlist, + ['bad', 'format']) + # will try to use iteritems in the 'bad' string + self.assertRaises(AttributeError, ttk._format_layoutlist, + [('name', 'bad')]) + # bad children formatting + self.assertRaises(ValueError, ttk._format_layoutlist, + [('name', {'children': {'a': None}})]) + + + def test_script_from_settings(self): + # empty options + self.assertFalse(ttk._script_from_settings({'name': + {'configure': None, 'map': None, 'element create': None}})) + + # empty layout + self.assertEqual( + ttk._script_from_settings({'name': {'layout': None}}), + "ttk::style layout name {\nnull\n}") + + configdict = {'αβγ': True, 'á': False} + self.assertTrue( + ttk._script_from_settings({'name': {'configure': configdict}})) + + mapdict = {'üñíćódè': [('á', 'vãl')]} + self.assertTrue( + ttk._script_from_settings({'name': {'map': mapdict}})) + + # invalid image element + self.assertRaises(IndexError, + ttk._script_from_settings, {'name': {'element create': ['image']}}) + + # minimal valid image + self.assertTrue(ttk._script_from_settings({'name': + {'element create': ['image', 'name']}})) + + image = {'thing': {'element create': + ['image', 'name', ('state1', 'state2', 'val')]}} + self.assertEqual(ttk._script_from_settings(image), + "ttk::style element create thing image {name {state1 state2} val} ") + + image['thing']['element create'].append({'opt': 30}) + self.assertEqual(ttk._script_from_settings(image), + "ttk::style element create thing image {name {state1 state2} val} " + "-opt 30") + + image['thing']['element create'][-1]['opt'] = [MockTclObj(3), + MockTclObj('2m')] + self.assertEqual(ttk._script_from_settings(image), + "ttk::style element create thing image {name {state1 state2} val} " + "-opt {3 2m}") + + vsapi = {'pin': {'element create': + ['vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]]}} + self.assertEqual(ttk._script_from_settings(vsapi), + "ttk::style element create pin vsapi EXPLORERBAR 3 {" + "{pressed !selected} 3 " + "{active !selected} 2 " + "{pressed selected} 6 " + "{active selected} 5 " + "selected 4 " + "{} 1} ") + + def test_tclobj_to_py(self): + self.assertEqual( + ttk._tclobj_to_py((MockStateSpec('a', 'b'), 'val')), + [('a', 'b', 'val')]) + self.assertEqual( + ttk._tclobj_to_py([MockTclObj('1'), 2, MockTclObj('3m')]), + [1, 2, '3m']) + + + def test_list_from_statespec(self): + def test_it(sspec, value, res_value, states): + self.assertEqual(ttk._list_from_statespec( + (sspec, value)), [states + (res_value, )]) + + states_even = tuple('state%d' % i for i in range(6)) + statespec = MockStateSpec(*states_even) + test_it(statespec, 'val', 'val', states_even) + test_it(statespec, MockTclObj('val'), 'val', states_even) + + states_odd = tuple('state%d' % i for i in range(5)) + statespec = MockStateSpec(*states_odd) + test_it(statespec, 'val', 'val', states_odd) + + test_it(('a', 'b', 'c'), MockTclObj('val'), 'val', ('a', 'b', 'c')) + + + def test_list_from_layouttuple(self): + tk = MockTkApp() + + # empty layout tuple + self.assertFalse(ttk._list_from_layouttuple(tk, ())) + + # shortest layout tuple + self.assertEqual(ttk._list_from_layouttuple(tk, ('name', )), + [('name', {})]) + + # not so interesting ltuple + sample_ltuple = ('name', '-option', 'value') + self.assertEqual(ttk._list_from_layouttuple(tk, sample_ltuple), + [('name', {'option': 'value'})]) + + # empty children + self.assertEqual(ttk._list_from_layouttuple(tk, + ('something', '-children', ())), + [('something', {'children': []})] + ) + + # more interesting ltuple + ltuple = ( + 'name', '-option', 'niceone', '-children', ( + ('otherone', '-children', ( + ('child', )), '-otheropt', 'othervalue' + ) + ) + ) + self.assertEqual(ttk._list_from_layouttuple(tk, ltuple), + [('name', {'option': 'niceone', 'children': + [('otherone', {'otheropt': 'othervalue', 'children': + [('child', {})] + })] + })] + ) + + # bad tuples + self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, + ('name', 'no_minus')) + self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, + ('name', 'no_minus', 'value')) + self.assertRaises(ValueError, ttk._list_from_layouttuple, tk, + ('something', '-children')) # no children + + + def test_val_or_dict(self): + def func(res, opt=None, val=None): + if opt is None: + return res + if val is None: + return "test val" + return (opt, val) + + tk = MockTkApp() + tk.call = func + + self.assertEqual(ttk._val_or_dict(tk, {}, '-test:3'), + {'test': '3'}) + self.assertEqual(ttk._val_or_dict(tk, {}, ('-test', 3)), + {'test': 3}) + + self.assertEqual(ttk._val_or_dict(tk, {'test': None}, 'x:y'), + 'test val') + + self.assertEqual(ttk._val_or_dict(tk, {'test': 3}, 'x:y'), + {'test': 3}) + + + def test_convert_stringval(self): + tests = ( + (0, 0), ('09', 9), ('a', 'a'), ('áÚ', 'áÚ'), ([], '[]'), + (None, 'None') + ) + for orig, expected in tests: + self.assertEqual(ttk._convert_stringval(orig), expected) + + +class TclObjsToPyTest(unittest.TestCase): + + def test_unicode(self): + adict = {'opt': 'välúè'} + self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) + + adict['opt'] = MockTclObj(adict['opt']) + self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': 'välúè'}) + + def test_multivalues(self): + adict = {'opt': [1, 2, 3, 4]} + self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 2, 3, 4]}) + + adict['opt'] = [1, 'xm', 3] + self.assertEqual(ttk.tclobjs_to_py(adict), {'opt': [1, 'xm', 3]}) + + adict['opt'] = (MockStateSpec('a', 'b'), 'válũè') + self.assertEqual(ttk.tclobjs_to_py(adict), + {'opt': [('a', 'b', 'válũè')]}) + + self.assertEqual(ttk.tclobjs_to_py({'x': ['y z']}), + {'x': ['y z']}) + + def test_nosplit(self): + self.assertEqual(ttk.tclobjs_to_py({'text': 'some text'}), + {'text': 'some text'}) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py new file mode 100644 index 00000000000..a572ff4b9e9 --- /dev/null +++ b/Lib/test/test_turtle.py @@ -0,0 +1,546 @@ +import pickle +import unittest +import unittest.mock +from test import support +from test.support import import_helper +from test.support import os_helper + + +turtle = import_helper.import_module('turtle') +Vec2D = turtle.Vec2D + +test_config = """\ +width = 0.75 +height = 0.8 +canvwidth = 500 +canvheight = 200 +leftright = 100 +topbottom = 100 +mode = world +colormode = 255 +delay = 100 +undobuffersize = 10000 +shape = circle +pencolor = red +fillcolor = blue +resizemode = auto +visible = None +language = english +exampleturtle = turtle +examplescreen = screen +title = Python Turtle Graphics +using_IDLE = '' +""" + +test_config_two = """\ +# Comments! +# Testing comments! +pencolor = red +fillcolor = blue +visible = False +language = english +# Some more +# comments +using_IDLE = False +""" + +invalid_test_config = """ +pencolor = red +fillcolor: blue +visible = False +""" + + +def patch_screen(): + """Patch turtle._Screen for testing without a display. + + We must patch the _Screen class itself instead of the _Screen + instance because instantiating it requires a display. + """ + # Create a mock screen that delegates color validation to the real TurtleScreen methods + mock_screen = unittest.mock.MagicMock() + mock_screen.__class__ = turtle._Screen + mock_screen.mode.return_value = "standard" + mock_screen._colormode = 1.0 + + def mock_iscolorstring(color): + valid_colors = {'red', 'green', 'blue', 'black', 'white', 'yellow', + 'orange', 'purple', 'pink', 'brown', 'gray', 'grey', + 'cyan', 'magenta'} + + return color in valid_colors or (isinstance(color, str) and color.startswith('#')) + + mock_screen._iscolorstring = mock_iscolorstring + mock_screen._colorstr = turtle._Screen._colorstr.__get__(mock_screen) + + return unittest.mock.patch( + "turtle._Screen.__new__", + return_value=mock_screen + ) + + +class TurtleConfigTest(unittest.TestCase): + + def get_cfg_file(self, cfg_str): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, 'w') as f: + f.write(cfg_str) + return os_helper.TESTFN + + def test_config_dict(self): + + cfg_name = self.get_cfg_file(test_config) + parsed_cfg = turtle.config_dict(cfg_name) + + expected = { + 'width' : 0.75, + 'height' : 0.8, + 'canvwidth' : 500, + 'canvheight': 200, + 'leftright': 100, + 'topbottom': 100, + 'mode': 'world', + 'colormode': 255, + 'delay': 100, + 'undobuffersize': 10000, + 'shape': 'circle', + 'pencolor' : 'red', + 'fillcolor' : 'blue', + 'resizemode' : 'auto', + 'visible' : None, + 'language': 'english', + 'exampleturtle': 'turtle', + 'examplescreen': 'screen', + 'title': 'Python Turtle Graphics', + 'using_IDLE': '', + } + + self.assertEqual(parsed_cfg, expected) + + def test_partial_config_dict_with_comments(self): + + cfg_name = self.get_cfg_file(test_config_two) + parsed_cfg = turtle.config_dict(cfg_name) + + expected = { + 'pencolor': 'red', + 'fillcolor': 'blue', + 'visible': False, + 'language': 'english', + 'using_IDLE': False, + } + + self.assertEqual(parsed_cfg, expected) + + def test_config_dict_invalid(self): + + cfg_name = self.get_cfg_file(invalid_test_config) + + with support.captured_stdout() as stdout: + parsed_cfg = turtle.config_dict(cfg_name) + + err_msg = stdout.getvalue() + + self.assertIn('Bad line in config-file ', err_msg) + self.assertIn('fillcolor: blue', err_msg) + + self.assertEqual(parsed_cfg, { + 'pencolor': 'red', + 'visible': False, + }) + + +class VectorComparisonMixin: + + def assertVectorsAlmostEqual(self, vec1, vec2): + if len(vec1) != len(vec2): + self.fail("Tuples are not of equal size") + for idx, (i, j) in enumerate(zip(vec1, vec2)): + self.assertAlmostEqual( + i, j, msg='values at index {} do not match'.format(idx)) + +class Multiplier: + + def __mul__(self, other): + return f'M*{other}' + + def __rmul__(self, other): + return f'{other}*M' + + +class TestVec2D(VectorComparisonMixin, unittest.TestCase): + + def test_constructor(self): + vec = Vec2D(0.5, 2) + self.assertEqual(vec[0], 0.5) + self.assertEqual(vec[1], 2) + self.assertIsInstance(vec, Vec2D) + + self.assertRaises(TypeError, Vec2D) + self.assertRaises(TypeError, Vec2D, 0) + self.assertRaises(TypeError, Vec2D, (0, 1)) + self.assertRaises(TypeError, Vec2D, vec) + self.assertRaises(TypeError, Vec2D, 0, 1, 2) + + def test_repr(self): + vec = Vec2D(0.567, 1.234) + self.assertEqual(repr(vec), '(0.57,1.23)') + + def test_equality(self): + vec1 = Vec2D(0, 1) + vec2 = Vec2D(0.0, 1) + vec3 = Vec2D(42, 1) + self.assertEqual(vec1, vec2) + self.assertEqual(vec1, tuple(vec1)) + self.assertEqual(tuple(vec1), vec1) + self.assertNotEqual(vec1, vec3) + self.assertNotEqual(vec2, vec3) + + def test_pickling(self): + vec = Vec2D(0.5, 2) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(proto=proto): + pickled = pickle.dumps(vec, protocol=proto) + unpickled = pickle.loads(pickled) + self.assertEqual(unpickled, vec) + self.assertIsInstance(unpickled, Vec2D) + + def _assert_arithmetic_cases(self, test_cases, lambda_operator): + for test_case in test_cases: + with self.subTest(case=test_case): + + ((first, second), expected) = test_case + + op1 = Vec2D(*first) + op2 = Vec2D(*second) + + result = lambda_operator(op1, op2) + + expected = Vec2D(*expected) + + self.assertVectorsAlmostEqual(result, expected) + + def test_vector_addition(self): + + test_cases = [ + (((0, 0), (1, 1)), (1.0, 1.0)), + (((-1, 0), (2, 2)), (1, 2)), + (((1.5, 0), (1, 1)), (2.5, 1)), + ] + + self._assert_arithmetic_cases(test_cases, lambda x, y: x + y) + + def test_vector_subtraction(self): + + test_cases = [ + (((0, 0), (1, 1)), (-1, -1)), + (((10.625, 0.125), (10, 0)), (0.625, 0.125)), + ] + + self._assert_arithmetic_cases(test_cases, lambda x, y: x - y) + + def test_vector_multiply(self): + + vec1 = Vec2D(10, 10) + vec2 = Vec2D(0.5, 3) + answer = vec1 * vec2 + expected = 35 + self.assertAlmostEqual(answer, expected) + + vec = Vec2D(0.5, 3) + expected = Vec2D(5, 30) + self.assertVectorsAlmostEqual(vec * 10, expected) + self.assertVectorsAlmostEqual(10 * vec, expected) + self.assertVectorsAlmostEqual(vec * 10.0, expected) + self.assertVectorsAlmostEqual(10.0 * vec, expected) + + M = Multiplier() + self.assertEqual(vec * M, Vec2D(f"{vec[0]}*M", f"{vec[1]}*M")) + self.assertEqual(M * vec, f'M*{vec}') + + def test_vector_negative(self): + vec = Vec2D(10, -10) + expected = (-10, 10) + self.assertVectorsAlmostEqual(-vec, expected) + + def test_distance(self): + self.assertAlmostEqual(abs(Vec2D(6, 8)), 10) + self.assertEqual(abs(Vec2D(0, 0)), 0) + self.assertAlmostEqual(abs(Vec2D(2.5, 6)), 6.5) + + def test_rotate(self): + + cases = [ + (((0, 0), 0), (0, 0)), + (((0, 1), 90), (-1, 0)), + (((0, 1), -90), (1, 0)), + (((1, 0), 180), (-1, 0)), + (((1, 0), 360), (1, 0)), + ] + + for case in cases: + with self.subTest(case=case): + (vec, rot), expected = case + vec = Vec2D(*vec) + got = vec.rotate(rot) + self.assertVectorsAlmostEqual(got, expected) + + +class TestTNavigator(VectorComparisonMixin, unittest.TestCase): + + def setUp(self): + self.nav = turtle.TNavigator() + + def test_goto(self): + self.nav.goto(100, -100) + self.assertAlmostEqual(self.nav.xcor(), 100) + self.assertAlmostEqual(self.nav.ycor(), -100) + + def test_teleport(self): + self.nav.teleport(20, -30, fill_gap=True) + self.assertAlmostEqual(self.nav.xcor(), 20) + self.assertAlmostEqual(self.nav.ycor(), -30) + self.nav.teleport(-20, 30, fill_gap=False) + self.assertAlmostEqual(self.nav.xcor(), -20) + self.assertAlmostEqual(self.nav.ycor(), 30) + + def test_pos(self): + self.assertEqual(self.nav.pos(), self.nav._position) + self.nav.goto(100, -100) + self.assertEqual(self.nav.pos(), self.nav._position) + + def test_left(self): + self.assertEqual(self.nav._orient, (1.0, 0)) + self.nav.left(90) + self.assertVectorsAlmostEqual(self.nav._orient, (0.0, 1.0)) + + def test_right(self): + self.assertEqual(self.nav._orient, (1.0, 0)) + self.nav.right(90) + self.assertVectorsAlmostEqual(self.nav._orient, (0, -1.0)) + + def test_reset(self): + self.nav.goto(100, -100) + self.assertAlmostEqual(self.nav.xcor(), 100) + self.assertAlmostEqual(self.nav.ycor(), -100) + self.nav.reset() + self.assertAlmostEqual(self.nav.xcor(), 0) + self.assertAlmostEqual(self.nav.ycor(), 0) + + def test_forward(self): + self.nav.forward(150) + expected = Vec2D(150, 0) + self.assertVectorsAlmostEqual(self.nav.position(), expected) + + self.nav.reset() + self.nav.left(90) + self.nav.forward(150) + expected = Vec2D(0, 150) + self.assertVectorsAlmostEqual(self.nav.position(), expected) + + self.assertRaises(TypeError, self.nav.forward, 'skldjfldsk') + + def test_backwards(self): + self.nav.back(200) + expected = Vec2D(-200, 0) + self.assertVectorsAlmostEqual(self.nav.position(), expected) + + self.nav.reset() + self.nav.right(90) + self.nav.back(200) + expected = Vec2D(0, 200) + self.assertVectorsAlmostEqual(self.nav.position(), expected) + + def test_distance(self): + self.nav.forward(100) + expected = 100 + self.assertAlmostEqual(self.nav.distance(Vec2D(0,0)), expected) + + def test_radians_and_degrees(self): + self.nav.left(90) + self.assertAlmostEqual(self.nav.heading(), 90) + self.nav.radians() + self.assertAlmostEqual(self.nav.heading(), 1.57079633) + self.nav.degrees() + self.assertAlmostEqual(self.nav.heading(), 90) + + def test_towards(self): + + coordinates = [ + # coordinates, expected + ((100, 0), 0.0), + ((100, 100), 45.0), + ((0, 100), 90.0), + ((-100, 100), 135.0), + ((-100, 0), 180.0), + ((-100, -100), 225.0), + ((0, -100), 270.0), + ((100, -100), 315.0), + ] + + for (x, y), expected in coordinates: + self.assertEqual(self.nav.towards(x, y), expected) + self.assertEqual(self.nav.towards((x, y)), expected) + self.assertEqual(self.nav.towards(Vec2D(x, y)), expected) + + def test_heading(self): + + self.nav.left(90) + self.assertAlmostEqual(self.nav.heading(), 90) + self.nav.left(45) + self.assertAlmostEqual(self.nav.heading(), 135) + self.nav.right(1.6) + self.assertAlmostEqual(self.nav.heading(), 133.4) + self.assertRaises(TypeError, self.nav.right, 'sdkfjdsf') + self.nav.reset() + + rotations = [10, 20, 170, 300] + result = sum(rotations) % 360 + for num in rotations: + self.nav.left(num) + self.assertEqual(self.nav.heading(), result) + self.nav.reset() + + result = (360-sum(rotations)) % 360 + for num in rotations: + self.nav.right(num) + self.assertEqual(self.nav.heading(), result) + self.nav.reset() + + rotations = [10, 20, -170, 300, -210, 34.3, -50.2, -10, -29.98, 500] + sum_so_far = 0 + for num in rotations: + if num < 0: + self.nav.right(abs(num)) + else: + self.nav.left(num) + sum_so_far += num + self.assertAlmostEqual(self.nav.heading(), sum_so_far % 360) + + def test_setheading(self): + self.nav.setheading(102.32) + self.assertAlmostEqual(self.nav.heading(), 102.32) + self.nav.setheading(-123.23) + self.assertAlmostEqual(self.nav.heading(), (-123.23) % 360) + self.nav.setheading(-1000.34) + self.assertAlmostEqual(self.nav.heading(), (-1000.34) % 360) + self.nav.setheading(300000) + self.assertAlmostEqual(self.nav.heading(), 300000%360) + + def test_positions(self): + self.nav.forward(100) + self.nav.left(90) + self.nav.forward(-200) + self.assertVectorsAlmostEqual(self.nav.pos(), (100.0, -200.0)) + + def test_setx_and_sety(self): + self.nav.setx(-1023.2334) + self.nav.sety(193323.234) + self.assertVectorsAlmostEqual(self.nav.pos(), (-1023.2334, 193323.234)) + + def test_home(self): + self.nav.left(30) + self.nav.forward(-100000) + self.nav.home() + self.assertVectorsAlmostEqual(self.nav.pos(), (0,0)) + self.assertAlmostEqual(self.nav.heading(), 0) + + def test_distance_method(self): + self.assertAlmostEqual(self.nav.distance(30, 40), 50) + vec = Vec2D(0.22, .001) + self.assertAlmostEqual(self.nav.distance(vec), 0.22000227271553355) + another_turtle = turtle.TNavigator() + another_turtle.left(90) + another_turtle.forward(10000) + self.assertAlmostEqual(self.nav.distance(another_turtle), 10000) + + +class TestTPen(unittest.TestCase): + + def test_pendown_and_penup(self): + + tpen = turtle.TPen() + + self.assertTrue(tpen.isdown()) + tpen.penup() + self.assertFalse(tpen.isdown()) + tpen.pendown() + self.assertTrue(tpen.isdown()) + + def test_showturtle_hideturtle_and_isvisible(self): + + tpen = turtle.TPen() + + self.assertTrue(tpen.isvisible()) + tpen.hideturtle() + self.assertFalse(tpen.isvisible()) + tpen.showturtle() + self.assertTrue(tpen.isvisible()) + + def test_teleport(self): + + tpen = turtle.TPen() + + for fill_gap_value in [True, False]: + tpen.penup() + tpen.teleport(100, 100, fill_gap=fill_gap_value) + self.assertFalse(tpen.isdown()) + tpen.pendown() + tpen.teleport(-100, -100, fill_gap=fill_gap_value) + self.assertTrue(tpen.isdown()) + + +class TestTurtle(unittest.TestCase): + def setUp(self): + with patch_screen(): + self.turtle = turtle.Turtle() + + # Reset the Screen singleton to avoid reference leaks + self.addCleanup(setattr, turtle.Turtle, '_screen', None) + + def test_dot_signature(self): + self.turtle.dot() + self.turtle.dot(10) + self.turtle.dot(size=10) + self.turtle.dot((0, 0, 0)) + self.turtle.dot(size=(0, 0, 0)) + self.turtle.dot("blue") + self.turtle.dot("") + self.turtle.dot(size="blue") + self.turtle.dot(20, "blue") + self.turtle.dot(20, "blue") + self.turtle.dot(20, (0, 0, 0)) + self.turtle.dot(20, 0, 0, 0) + with self.assertRaises(TypeError): + self.turtle.dot(color="blue") + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, "_not_a_color_") + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, (0, 0, 0, 0)) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, 0, 0, 0, 0) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, (-1, 0, 0)) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, -1, 0, 0) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, (0, 257, 0)) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, 0, 257, 0) + + +class TestModuleLevel(unittest.TestCase): + def test_all_signatures(self): + import inspect + + known_signatures = { + 'teleport': + '(x=None, y=None, *, fill_gap: bool = False) -> None', + 'undo': '()', + 'goto': '(x, y=None)', + 'bgcolor': '(*args)', + 'pen': '(pen=None, **pendict)', + } + + for name in known_signatures: + with self.subTest(name=name): + obj = getattr(turtle, name) + sig = inspect.signature(obj) + self.assertEqual(str(sig), known_signatures[name]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py new file mode 100644 index 00000000000..3dbb35afcb6 --- /dev/null +++ b/Lib/test/test_type_annotations.py @@ -0,0 +1,214 @@ +import textwrap +import unittest +from test.support import run_code + +class TypeAnnotationTests(unittest.TestCase): + + def test_lazy_create_annotations(self): + # type objects lazy create their __annotations__ dict on demand. + # the annotations dict is stored in type.__dict__. + # a freshly created type shouldn't have an annotations dict yet. + foo = type("Foo", (), {}) + for i in range(3): + self.assertFalse("__annotations__" in foo.__dict__) + d = foo.__annotations__ + self.assertTrue("__annotations__" in foo.__dict__) + self.assertEqual(foo.__annotations__, d) + self.assertEqual(foo.__dict__['__annotations__'], d) + del foo.__annotations__ + + def test_setting_annotations(self): + foo = type("Foo", (), {}) + for i in range(3): + self.assertFalse("__annotations__" in foo.__dict__) + d = {'a': int} + foo.__annotations__ = d + self.assertTrue("__annotations__" in foo.__dict__) + self.assertEqual(foo.__annotations__, d) + self.assertEqual(foo.__dict__['__annotations__'], d) + del foo.__annotations__ + + def test_annotations_getset_raises(self): + # builtin types don't have __annotations__ (yet!) + with self.assertRaises(AttributeError): + print(float.__annotations__) + with self.assertRaises(TypeError): + float.__annotations__ = {} + with self.assertRaises(TypeError): + del float.__annotations__ + + # double delete + foo = type("Foo", (), {}) + foo.__annotations__ = {} + del foo.__annotations__ + with self.assertRaises(AttributeError): + del foo.__annotations__ + + def test_annotations_are_created_correctly(self): + class C: + a:int=3 + b:str=4 + self.assertTrue("__annotations__" in C.__dict__) + del C.__annotations__ + self.assertFalse("__annotations__" in C.__dict__) + + def test_descriptor_still_works(self): + class C: + def __init__(self, name=None, bases=None, d=None): + self.my_annotations = None + + @property + def __annotations__(self): + if not hasattr(self, 'my_annotations'): + self.my_annotations = {} + if not isinstance(self.my_annotations, dict): + self.my_annotations = {} + return self.my_annotations + + @__annotations__.setter + def __annotations__(self, value): + if not isinstance(value, dict): + raise ValueError("can only set __annotations__ to a dict") + self.my_annotations = value + + @__annotations__.deleter + def __annotations__(self): + if getattr(self, 'my_annotations', False) is None: + raise AttributeError('__annotations__') + self.my_annotations = None + + c = C() + self.assertEqual(c.__annotations__, {}) + d = {'a':'int'} + c.__annotations__ = d + self.assertEqual(c.__annotations__, d) + with self.assertRaises(ValueError): + c.__annotations__ = 123 + del c.__annotations__ + with self.assertRaises(AttributeError): + del c.__annotations__ + self.assertEqual(c.__annotations__, {}) + + + class D(metaclass=C): + pass + + self.assertEqual(D.__annotations__, {}) + d = {'a':'int'} + D.__annotations__ = d + self.assertEqual(D.__annotations__, d) + with self.assertRaises(ValueError): + D.__annotations__ = 123 + del D.__annotations__ + with self.assertRaises(AttributeError): + del D.__annotations__ + self.assertEqual(D.__annotations__, {}) + + +class TestSetupAnnotations(unittest.TestCase): + def check(self, code: str): + code = textwrap.dedent(code) + for scope in ("module", "class"): + with self.subTest(scope=scope): + if scope == "class": + code = f"class C:\n{textwrap.indent(code, ' ')}" + ns = run_code(code) + if scope == "class": + annotations = ns["C"].__annotations__ + else: + annotations = ns["__annotations__"] + self.assertEqual(annotations, {"x": int}) + + def test_top_level(self): + self.check("x: int = 1") + + def test_blocks(self): + self.check("if True:\n x: int = 1") + self.check(""" + while True: + x: int = 1 + break + """) + self.check(""" + while False: + pass + else: + x: int = 1 + """) + self.check(""" + for i in range(1): + x: int = 1 + """) + self.check(""" + for i in range(1): + pass + else: + x: int = 1 + """) + + def test_try(self): + self.check(""" + try: + x: int = 1 + except: + pass + """) + self.check(""" + try: + pass + except: + pass + else: + x: int = 1 + """) + self.check(""" + try: + pass + except: + pass + finally: + x: int = 1 + """) + self.check(""" + try: + 1/0 + except: + x: int = 1 + """) + + def test_try_star(self): + self.check(""" + try: + x: int = 1 + except* Exception: + pass + """) + self.check(""" + try: + pass + except* Exception: + pass + else: + x: int = 1 + """) + self.check(""" + try: + pass + except* Exception: + pass + finally: + x: int = 1 + """) + self.check(""" + try: + 1/0 + except* Exception: + x: int = 1 + """) + + def test_match(self): + self.check(""" + match 0: + case 0: + x: int = 1 + """) diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py new file mode 100644 index 00000000000..8e2bb0c2382 --- /dev/null +++ b/Lib/test/test_type_cache.py @@ -0,0 +1,254 @@ +""" Tests for the internal type cache in CPython. """ +import unittest +import dis +from test import support +from test.support import import_helper, requires_specialization +try: + from sys import _clear_type_cache +except ImportError: + _clear_type_cache = None + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module("_testcapi") +type_get_version = _testcapi.type_get_version +type_assign_specific_version_unsafe = _testcapi.type_assign_specific_version_unsafe +type_assign_version = _testcapi.type_assign_version +type_modified = _testcapi.type_modified + + +@support.cpython_only +@unittest.skipIf(_clear_type_cache is None, "requires sys._clear_type_cache") +class TypeCacheTests(unittest.TestCase): + def test_tp_version_tag_unique(self): + """tp_version_tag should be unique assuming no overflow, even after + clearing type cache. + """ + # Check if global version tag has already overflowed. + Y = type('Y', (), {}) + Y.x = 1 + Y.x # Force a _PyType_Lookup, populating version tag + y_ver = type_get_version(Y) + # Overflow, or not enough left to conduct the test. + if y_ver == 0 or y_ver > 0xFFFFF000: + self.skipTest("Out of type version tags") + # Note: try to avoid any method lookups within this loop, + # It will affect global version tag. + all_version_tags = [] + append_result = all_version_tags.append + assertNotEqual = self.assertNotEqual + for _ in range(30): + _clear_type_cache() + X = type('Y', (), {}) + X.x = 1 + X.x + tp_version_tag_after = type_get_version(X) + assertNotEqual(tp_version_tag_after, 0, msg="Version overflowed") + append_result(tp_version_tag_after) + self.assertEqual(len(set(all_version_tags)), 30, + msg=f"{all_version_tags} contains non-unique versions") + + def test_type_assign_version(self): + class C: + x = 5 + + self.assertEqual(type_assign_version(C), 1) + c_ver = type_get_version(C) + + C.x = 6 + self.assertEqual(type_get_version(C), 0) + self.assertEqual(type_assign_version(C), 1) + self.assertNotEqual(type_get_version(C), 0) + self.assertNotEqual(type_get_version(C), c_ver) + + def test_type_assign_specific_version(self): + """meta-test for type_assign_specific_version_unsafe""" + class C: + pass + + type_assign_version(C) + orig_version = type_get_version(C) + if orig_version == 0: + self.skipTest("Could not assign a valid type version") + + type_modified(C) + type_assign_specific_version_unsafe(C, orig_version + 5) + type_assign_version(C) # this should do nothing + + new_version = type_get_version(C) + self.assertEqual(new_version, orig_version + 5) + + _clear_type_cache() + + def test_per_class_limit(self): + class C: + x = 0 + + type_assign_version(C) + orig_version = type_get_version(C) + for i in range(1001): + C.x = i + type_assign_version(C) + + new_version = type_get_version(C) + self.assertEqual(new_version, 0) + + def test_119462(self): + + class Holder: + value = None + + @classmethod + def set_value(cls): + cls.value = object() + + class HolderSub(Holder): + pass + + for _ in range(1050): + Holder.set_value() + HolderSub.value + +@support.cpython_only +@requires_specialization +class TypeCacheWithSpecializationTests(unittest.TestCase): + def tearDown(self): + _clear_type_cache() + + def _assign_valid_version_or_skip(self, type_): + type_modified(type_) + type_assign_version(type_) + if type_get_version(type_) == 0: + self.skipTest("Could not assign valid type version") + + def _no_more_versions(self, user_type): + type_modified(user_type) + for _ in range(1001): + type_assign_specific_version_unsafe(user_type, 1000_000_000) + type_assign_specific_version_unsafe(user_type, 0) + self.assertEqual(type_get_version(user_type), 0) + + def _all_opnames(self, func): + return set(instr.opname for instr in dis.Bytecode(func, adaptive=True)) + + def _check_specialization(self, func, arg, opname, *, should_specialize): + for _ in range(100): + func(arg) + + if should_specialize: + self.assertNotIn(opname, self._all_opnames(func)) + else: + self.assertIn(opname, self._all_opnames(func)) + + def test_class_load_attr_specialization_user_type(self): + class A: + def foo(self): + pass + + self._assign_valid_version_or_skip(A) + + def load_foo_1(type_): + type_.foo + + self._check_specialization(load_foo_1, A, "LOAD_ATTR", should_specialize=True) + del load_foo_1 + + self._no_more_versions(A) + + def load_foo_2(type_): + return type_.foo + + self._check_specialization(load_foo_2, A, "LOAD_ATTR", should_specialize=False) + + def test_class_load_attr_specialization_static_type(self): + self.assertNotEqual(type_get_version(str), 0) + self.assertNotEqual(type_get_version(bytes), 0) + + def get_capitalize_1(type_): + return type_.capitalize + + self._check_specialization(get_capitalize_1, str, "LOAD_ATTR", should_specialize=True) + self.assertEqual(get_capitalize_1(str)('hello'), 'Hello') + self.assertEqual(get_capitalize_1(bytes)(b'hello'), b'Hello') + + def test_property_load_attr_specialization_user_type(self): + class G: + @property + def x(self): + return 9 + + self._assign_valid_version_or_skip(G) + + def load_x_1(instance): + instance.x + + self._check_specialization(load_x_1, G(), "LOAD_ATTR", should_specialize=True) + del load_x_1 + + self._no_more_versions(G) + + def load_x_2(instance): + instance.x + + self._check_specialization(load_x_2, G(), "LOAD_ATTR", should_specialize=False) + + def test_store_attr_specialization_user_type(self): + class B: + __slots__ = ("bar",) + + self._assign_valid_version_or_skip(B) + + def store_bar_1(type_): + type_.bar = 10 + + self._check_specialization(store_bar_1, B(), "STORE_ATTR", should_specialize=True) + del store_bar_1 + + self._no_more_versions(B) + + def store_bar_2(type_): + type_.bar = 10 + + self._check_specialization(store_bar_2, B(), "STORE_ATTR", should_specialize=False) + + def test_class_call_specialization_user_type(self): + class F: + def __init__(self): + pass + + self._assign_valid_version_or_skip(F) + + def call_class_1(type_): + type_() + + self._check_specialization(call_class_1, F, "CALL", should_specialize=True) + del call_class_1 + + self._no_more_versions(F) + + def call_class_2(type_): + type_() + + self._check_specialization(call_class_2, F, "CALL", should_specialize=False) + + def test_to_bool_specialization_user_type(self): + class H: + pass + + self._assign_valid_version_or_skip(H) + + def to_bool_1(instance): + not instance + + self._check_specialization(to_bool_1, H(), "TO_BOOL", should_specialize=True) + del to_bool_1 + + self._no_more_versions(H) + + def to_bool_2(instance): + not instance + + self._check_specialization(to_bool_2, H(), "TO_BOOL", should_specialize=False) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py new file mode 100644 index 00000000000..971fdb2ba32 --- /dev/null +++ b/Lib/test/test_unparse.py @@ -0,0 +1,848 @@ +"""Tests for ast.unparse.""" + +import unittest +import test.support +import pathlib +import random +import tokenize +import ast +from test.support.ast_helper import ASTTestMixin + + +def read_pyfile(filename): + """Read and return the contents of a Python source file (as a + string), taking into account the file encoding.""" + with tokenize.open(filename) as stream: + return stream.read() + + +for_else = """\ +def f(): + for x in range(10): + break + else: + y = 2 + z = 3 +""" + +while_else = """\ +def g(): + while True: + break + else: + y = 2 + z = 3 +""" + +relative_import = """\ +from . import fred +from .. import barney +from .australia import shrimp as prawns +""" + +nonlocal_ex = """\ +def f(): + x = 1 + def g(): + nonlocal x + x = 2 + y = 7 + def h(): + nonlocal x, y +""" + +# also acts as test for 'except ... as ...' +raise_from = """\ +try: + 1 / 0 +except ZeroDivisionError as e: + raise ArithmeticError from e +""" + +class_decorator = """\ +@f1(arg) +@f2 +class Foo: pass +""" + +elif1 = """\ +if cond1: + suite1 +elif cond2: + suite2 +else: + suite3 +""" + +elif2 = """\ +if cond1: + suite1 +elif cond2: + suite2 +""" + +try_except_finally = """\ +try: + suite1 +except ex1: + suite2 +except ex2: + suite3 +else: + suite4 +finally: + suite5 +""" + +try_except_star_finally = """\ +try: + suite1 +except* ex1: + suite2 +except* ex2: + suite3 +else: + suite4 +finally: + suite5 +""" + +with_simple = """\ +with f(): + suite1 +""" + +with_as = """\ +with f() as x: + suite1 +""" + +with_two_items = """\ +with f() as x, g() as y: + suite1 +""" + +docstring_prefixes = ( + "", + "class foo:\n ", + "def foo():\n ", + "async def foo():\n ", +) + +class ASTTestCase(ASTTestMixin, unittest.TestCase): + def check_ast_roundtrip(self, code1, **kwargs): + with self.subTest(code1=code1, ast_parse_kwargs=kwargs): + ast1 = ast.parse(code1, **kwargs) + code2 = ast.unparse(ast1) + ast2 = ast.parse(code2, **kwargs) + self.assertASTEqual(ast1, ast2) + + def check_invalid(self, node, raises=ValueError): + with self.subTest(node=node): + self.assertRaises(raises, ast.unparse, node) + + def get_source(self, code1, code2=None): + code2 = code2 or code1 + code1 = ast.unparse(ast.parse(code1)) + return code1, code2 + + def check_src_roundtrip(self, code1, code2=None): + code1, code2 = self.get_source(code1, code2) + with self.subTest(code1=code1, code2=code2): + self.assertEqual(code2, code1) + + def check_src_dont_roundtrip(self, code1, code2=None): + code1, code2 = self.get_source(code1, code2) + with self.subTest(code1=code1, code2=code2): + self.assertNotEqual(code2, code1) + +class UnparseTestCase(ASTTestCase): + # Tests for specific bugs found in earlier versions of unparse + + def test_fstrings(self): + self.check_ast_roundtrip("f'a'") + self.check_ast_roundtrip("f'{{}}'") + self.check_ast_roundtrip("f'{{5}}'") + self.check_ast_roundtrip("f'{{5}}5'") + self.check_ast_roundtrip("f'X{{}}X'") + self.check_ast_roundtrip("f'{a}'") + self.check_ast_roundtrip("f'{ {1:2}}'") + self.check_ast_roundtrip("f'a{a}a'") + self.check_ast_roundtrip("f'a{a}{a}a'") + self.check_ast_roundtrip("f'a{a}a{a}a'") + self.check_ast_roundtrip("f'{a!r}x{a!s}12{{}}{a!a}'") + self.check_ast_roundtrip("f'{a:10}'") + self.check_ast_roundtrip("f'{a:100_000{10}}'") + self.check_ast_roundtrip("f'{a!r:10}'") + self.check_ast_roundtrip("f'{a:a{b}10}'") + self.check_ast_roundtrip( + "f'a{b}{c!s}{d!r}{e!a}{f:a}{g:a{b}}{h!s:a}" + "{j!s:{a}b}{k!s:a{b}c}{l!a:{b}c{d}}{x+y=}'" + ) + + def test_fstrings_special_chars(self): + # See issue 25180 + self.check_ast_roundtrip(r"""f'{f"{0}"*3}'""") + self.check_ast_roundtrip(r"""f'{f"{y}"*3}'""") + self.check_ast_roundtrip("""f''""") + self.check_ast_roundtrip('''f"""'end' "quote\\""""''') + + def test_fstrings_complicated(self): + # See issue 28002 + self.check_ast_roundtrip("""f'''{"'"}'''""") + self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') + self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-'single quote\\'\'\'\'''') + self.check_ast_roundtrip('f"""{\'\'\'\n\'\'\'}"""') + self.check_ast_roundtrip('f"""{g(\'\'\'\n\'\'\')}"""') + self.check_ast_roundtrip('''f"a\\r\\nb"''') + self.check_ast_roundtrip('''f"\\u2028{'x'}"''') + + def test_fstrings_pep701(self): + self.check_ast_roundtrip('f" something { my_dict["key"] } something else "') + self.check_ast_roundtrip('f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"') + + def test_strings(self): + self.check_ast_roundtrip("u'foo'") + self.check_ast_roundtrip("r'foo'") + self.check_ast_roundtrip("b'foo'") + + def test_del_statement(self): + self.check_ast_roundtrip("del x, y, z") + + def test_shifts(self): + self.check_ast_roundtrip("45 << 2") + self.check_ast_roundtrip("13 >> 7") + + def test_for_else(self): + self.check_ast_roundtrip(for_else) + + def test_while_else(self): + self.check_ast_roundtrip(while_else) + + def test_unary_parens(self): + self.check_ast_roundtrip("(-1)**7") + self.check_ast_roundtrip("(-1.)**8") + self.check_ast_roundtrip("(-1j)**6") + self.check_ast_roundtrip("not True or False") + self.check_ast_roundtrip("True or not False") + + def test_integer_parens(self): + self.check_ast_roundtrip("3 .__abs__()") + + def test_huge_float(self): + self.check_ast_roundtrip("1e1000") + self.check_ast_roundtrip("-1e1000") + self.check_ast_roundtrip("1e1000j") + self.check_ast_roundtrip("-1e1000j") + + def test_nan(self): + self.assertASTEqual( + ast.parse(ast.unparse(ast.Constant(value=float('nan')))), + ast.parse('1e1000 - 1e1000') + ) + + def test_min_int(self): + self.check_ast_roundtrip(str(-(2 ** 31))) + self.check_ast_roundtrip(str(-(2 ** 63))) + + def test_imaginary_literals(self): + self.check_ast_roundtrip("7j") + self.check_ast_roundtrip("-7j") + self.check_ast_roundtrip("0j") + self.check_ast_roundtrip("-0j") + + def test_lambda_parentheses(self): + self.check_ast_roundtrip("(lambda: int)()") + + def test_chained_comparisons(self): + self.check_ast_roundtrip("1 < 4 <= 5") + self.check_ast_roundtrip("a is b is c is not d") + + def test_function_arguments(self): + self.check_ast_roundtrip("def f(): pass") + self.check_ast_roundtrip("def f(a): pass") + self.check_ast_roundtrip("def f(b = 2): pass") + self.check_ast_roundtrip("def f(a, b): pass") + self.check_ast_roundtrip("def f(a, b = 2): pass") + self.check_ast_roundtrip("def f(a = 5, b = 2): pass") + self.check_ast_roundtrip("def f(*, a = 1, b = 2): pass") + self.check_ast_roundtrip("def f(*, a = 1, b): pass") + self.check_ast_roundtrip("def f(*, a, b = 2): pass") + self.check_ast_roundtrip("def f(a, b = None, *, c, **kwds): pass") + self.check_ast_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass") + self.check_ast_roundtrip("def f(*args, **kwargs): pass") + + def test_relative_import(self): + self.check_ast_roundtrip(relative_import) + + def test_nonlocal(self): + self.check_ast_roundtrip(nonlocal_ex) + + def test_raise_from(self): + self.check_ast_roundtrip(raise_from) + + def test_bytes(self): + self.check_ast_roundtrip("b'123'") + + def test_annotations(self): + self.check_ast_roundtrip("def f(a : int): pass") + self.check_ast_roundtrip("def f(a: int = 5): pass") + self.check_ast_roundtrip("def f(*args: [int]): pass") + self.check_ast_roundtrip("def f(**kwargs: dict): pass") + self.check_ast_roundtrip("def f() -> None: pass") + + def test_set_literal(self): + self.check_ast_roundtrip("{'a', 'b', 'c'}") + + def test_empty_set(self): + self.assertASTEqual( + ast.parse(ast.unparse(ast.Set(elts=[]))), + ast.parse('{*()}') + ) + + def test_set_comprehension(self): + self.check_ast_roundtrip("{x for x in range(5)}") + + def test_dict_comprehension(self): + self.check_ast_roundtrip("{x: x*x for x in range(10)}") + + def test_class_decorators(self): + self.check_ast_roundtrip(class_decorator) + + def test_class_definition(self): + self.check_ast_roundtrip("class A(metaclass=type, *[], **{}): pass") + + def test_elifs(self): + self.check_ast_roundtrip(elif1) + self.check_ast_roundtrip(elif2) + + def test_try_except_finally(self): + self.check_ast_roundtrip(try_except_finally) + + def test_try_except_star_finally(self): + self.check_ast_roundtrip(try_except_star_finally) + + def test_starred_assignment(self): + self.check_ast_roundtrip("a, *b, c = seq") + self.check_ast_roundtrip("a, (*b, c) = seq") + self.check_ast_roundtrip("a, *b[0], c = seq") + self.check_ast_roundtrip("a, *(b, c) = seq") + + def test_with_simple(self): + self.check_ast_roundtrip(with_simple) + + def test_with_as(self): + self.check_ast_roundtrip(with_as) + + def test_with_two_items(self): + self.check_ast_roundtrip(with_two_items) + + def test_dict_unpacking_in_dict(self): + # See issue 26489 + self.check_ast_roundtrip(r"""{**{'y': 2}, 'x': 1}""") + self.check_ast_roundtrip(r"""{**{'y': 2}, **{'x': 1}}""") + + def test_slices(self): + self.check_ast_roundtrip("a[i]") + self.check_ast_roundtrip("a[i,]") + self.check_ast_roundtrip("a[i, j]") + # The AST for these next two both look like `a[(*a,)]` + self.check_ast_roundtrip("a[(*a,)]") + self.check_ast_roundtrip("a[*a]") + self.check_ast_roundtrip("a[b, *a]") + self.check_ast_roundtrip("a[*a, c]") + self.check_ast_roundtrip("a[b, *a, c]") + self.check_ast_roundtrip("a[*a, *a]") + self.check_ast_roundtrip("a[b, *a, *a]") + self.check_ast_roundtrip("a[*a, b, *a]") + self.check_ast_roundtrip("a[*a, *a, b]") + self.check_ast_roundtrip("a[b, *a, *a, c]") + self.check_ast_roundtrip("a[(a:=b)]") + self.check_ast_roundtrip("a[(a:=b,c)]") + self.check_ast_roundtrip("a[()]") + self.check_ast_roundtrip("a[i:j]") + self.check_ast_roundtrip("a[:j]") + self.check_ast_roundtrip("a[i:]") + self.check_ast_roundtrip("a[i:j:k]") + self.check_ast_roundtrip("a[:j:k]") + self.check_ast_roundtrip("a[i::k]") + self.check_ast_roundtrip("a[i:j,]") + self.check_ast_roundtrip("a[i:j, k]") + + def test_invalid_raise(self): + self.check_invalid(ast.Raise(exc=None, cause=ast.Name(id="X", ctx=ast.Load()))) + + def test_invalid_fstring_value(self): + self.check_invalid( + ast.JoinedStr( + values=[ + ast.Name(id="test", ctx=ast.Load()), + ast.Constant(value="test") + ] + ) + ) + + def test_fstring_backslash(self): + # valid since Python 3.12 + self.assertEqual(ast.unparse( + ast.FormattedValue( + value=ast.Constant(value="\\\\"), + conversion=-1, + format_spec=None, + ) + ), "{'\\\\\\\\'}") + + def test_invalid_yield_from(self): + self.check_invalid(ast.YieldFrom(value=None)) + + def test_import_from_level_none(self): + tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')]) + self.assertEqual(ast.unparse(tree), "from mod import x") + tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')], level=None) + self.assertEqual(ast.unparse(tree), "from mod import x") + + def test_docstrings(self): + docstrings = ( + 'this ends with double quote"', + 'this includes a """triple quote"""', + '\r', + '\\r', + '\t', + '\\t', + '\n', + '\\n', + '\r\\r\t\\t\n\\n', + '""">>> content = \"\"\"blabla\"\"\" <<<"""', + r'foo\n\x00', + "' \\'\\'\\'\"\"\" \"\"\\'\\' \\'", + '🐍⛎𩸽üéş^\\\\X\\\\BB\N{LONG RIGHTWARDS SQUIGGLE ARROW}' + ) + for docstring in docstrings: + # check as Module docstrings for easy testing + self.check_ast_roundtrip(f"'''{docstring}'''") + + def test_constant_tuples(self): + self.check_src_roundtrip(ast.Constant(value=(1,), kind=None), "(1,)") + self.check_src_roundtrip( + ast.Constant(value=(1, 2, 3), kind=None), "(1, 2, 3)" + ) + + def test_function_type(self): + for function_type in ( + "() -> int", + "(int, int) -> int", + "(Callable[complex], More[Complex(call.to_typevar())]) -> None" + ): + self.check_ast_roundtrip(function_type, mode="func_type") + + def test_type_comments(self): + for statement in ( + "a = 5 # type:", + "a = 5 # type: int", + "a = 5 # type: int and more", + "def x(): # type: () -> None\n\tpass", + "def x(y): # type: (int) -> None and more\n\tpass", + "async def x(): # type: () -> None\n\tpass", + "async def x(y): # type: (int) -> None and more\n\tpass", + "for x in y: # type: int\n\tpass", + "async for x in y: # type: int\n\tpass", + "with x(): # type: int\n\tpass", + "async with x(): # type: int\n\tpass" + ): + self.check_ast_roundtrip(statement, type_comments=True) + + def test_type_ignore(self): + for statement in ( + "a = 5 # type: ignore", + "a = 5 # type: ignore and more", + "def x(): # type: ignore\n\tpass", + "def x(y): # type: ignore and more\n\tpass", + "async def x(): # type: ignore\n\tpass", + "async def x(y): # type: ignore and more\n\tpass", + "for x in y: # type: ignore\n\tpass", + "async for x in y: # type: ignore\n\tpass", + "with x(): # type: ignore\n\tpass", + "async with x(): # type: ignore\n\tpass" + ): + self.check_ast_roundtrip(statement, type_comments=True) + + +class CosmeticTestCase(ASTTestCase): + """Test if there are cosmetic issues caused by unnecessary additions""" + + def test_simple_expressions_parens(self): + self.check_src_roundtrip("(a := b)") + self.check_src_roundtrip("await x") + self.check_src_roundtrip("x if x else y") + self.check_src_roundtrip("lambda x: x") + self.check_src_roundtrip("1 + 1") + self.check_src_roundtrip("1 + 2 / 3") + self.check_src_roundtrip("(1 + 2) / 3") + self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2)") + self.check_src_roundtrip("(1 + 2) * 3 + 4 * (5 + 2) ** 2") + self.check_src_roundtrip("~x") + self.check_src_roundtrip("x and y") + self.check_src_roundtrip("x and y and z") + self.check_src_roundtrip("x and (y and x)") + self.check_src_roundtrip("(x and y) and z") + self.check_src_roundtrip("(x ** y) ** z ** q") + self.check_src_roundtrip("x >> y") + self.check_src_roundtrip("x << y") + self.check_src_roundtrip("x >> y and x >> z") + self.check_src_roundtrip("x + y - z * q ^ t ** k") + self.check_src_roundtrip("P * V if P and V else n * R * T") + self.check_src_roundtrip("lambda P, V, n: P * V == n * R * T") + self.check_src_roundtrip("flag & (other | foo)") + self.check_src_roundtrip("not x == y") + self.check_src_roundtrip("x == (not y)") + self.check_src_roundtrip("yield x") + self.check_src_roundtrip("yield from x") + self.check_src_roundtrip("call((yield x))") + self.check_src_roundtrip("return x + (yield x)") + + def test_class_bases_and_keywords(self): + self.check_src_roundtrip("class X:\n pass") + self.check_src_roundtrip("class X(A):\n pass") + self.check_src_roundtrip("class X(A, B, C, D):\n pass") + self.check_src_roundtrip("class X(x=y):\n pass") + self.check_src_roundtrip("class X(metaclass=z):\n pass") + self.check_src_roundtrip("class X(x=y, z=d):\n pass") + self.check_src_roundtrip("class X(A, x=y):\n pass") + self.check_src_roundtrip("class X(A, **kw):\n pass") + self.check_src_roundtrip("class X(*args):\n pass") + self.check_src_roundtrip("class X(*args, **kwargs):\n pass") + + def test_fstrings(self): + self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') + self.check_src_roundtrip('''f\'-{f\'\'\'*{f"""+{f".{f'{x}'}."}+"""}*\'\'\'}-\'''') + self.check_src_roundtrip('''f\'-{f\'*{f\'\'\'+{f""".{f"{f'{x}'}"}."""}+\'\'\'}*\'}-\'''') + self.check_src_roundtrip('''f"\\u2028{'x'}"''') + self.check_src_roundtrip(r"f'{x}\n'") + self.check_src_roundtrip('''f"{'\\n'}\\n"''') + self.check_src_roundtrip('''f"{f'{x}\\n'}\\n"''') + + def test_docstrings(self): + docstrings = ( + '"""simple doc string"""', + '''"""A more complex one + with some newlines"""''', + '''"""Foo bar baz + + empty newline"""''', + '"""With some \t"""', + '"""Foo "bar" baz """', + '"""\\r"""', + '""""""', + '"""\'\'\'"""', + '"""\'\'\'\'\'\'"""', + '"""🐍⛎𩸽üéş^\\\\X\\\\BB⟿"""', + '"""end in single \'quote\'"""', + "'''end in double \"quote\"'''", + '"""almost end in double "quote"."""', + ) + + for prefix in docstring_prefixes: + for docstring in docstrings: + self.check_src_roundtrip(f"{prefix}{docstring}") + + def test_docstrings_negative_cases(self): + # Test some cases that involve strings in the children of the + # first node but aren't docstrings to make sure we don't have + # False positives. + docstrings_negative = ( + 'a = """false"""', + '"""false""" + """unless its optimized"""', + '1 + 1\n"""false"""', + 'f"""no, top level but f-fstring"""' + ) + for prefix in docstring_prefixes: + for negative in docstrings_negative: + # this cases should be result with single quote + # rather then triple quoted docstring + src = f"{prefix}{negative}" + self.check_ast_roundtrip(src) + self.check_src_dont_roundtrip(src) + + def test_unary_op_factor(self): + for prefix in ("+", "-", "~"): + self.check_src_roundtrip(f"{prefix}1") + for prefix in ("not",): + self.check_src_roundtrip(f"{prefix} 1") + + def test_slices(self): + self.check_src_roundtrip("a[()]") + self.check_src_roundtrip("a[1]") + self.check_src_roundtrip("a[1, 2]") + # Note that `a[*a]`, `a[*a,]`, and `a[(*a,)]` all evaluate to the same + # thing at runtime and have the same AST, but only `a[*a,]` passes + # this test, because that's what `ast.unparse` produces. + self.check_src_roundtrip("a[*a,]") + self.check_src_roundtrip("a[1, *a]") + self.check_src_roundtrip("a[*a, 2]") + self.check_src_roundtrip("a[1, *a, 2]") + self.check_src_roundtrip("a[*a, *a]") + self.check_src_roundtrip("a[1, *a, *a]") + self.check_src_roundtrip("a[*a, 1, *a]") + self.check_src_roundtrip("a[*a, *a, 1]") + self.check_src_roundtrip("a[1, *a, *a, 2]") + self.check_src_roundtrip("a[1:2, *a]") + self.check_src_roundtrip("a[*a, 1:2]") + + def test_lambda_parameters(self): + self.check_src_roundtrip("lambda: something") + self.check_src_roundtrip("four = lambda: 2 + 2") + self.check_src_roundtrip("lambda x: x * 2") + self.check_src_roundtrip("square = lambda n: n ** 2") + self.check_src_roundtrip("lambda x, y: x + y") + self.check_src_roundtrip("add = lambda x, y: x + y") + self.check_src_roundtrip("lambda x, y, /, z, q, *, u: None") + self.check_src_roundtrip("lambda x, *y, **z: None") + + def test_star_expr_assign_target(self): + for source_type, source in [ + ("single assignment", "{target} = foo"), + ("multiple assignment", "{target} = {target} = bar"), + ("for loop", "for {target} in foo:\n pass"), + ("async for loop", "async for {target} in foo:\n pass") + ]: + for target in [ + "a", + "a,", + "a, b", + "a, *b, c", + "a, (b, c), d", + "a, (b, c, d), *e", + "a, (b, *c, d), e", + "a, (b, *c, (d, e), f), g", + "[a]", + "[a, b]", + "[a, *b, c]", + "[a, [b, c], d]", + "[a, [b, c, d], *e]", + "[a, [b, *c, d], e]", + "[a, [b, *c, [d, e], f], g]", + "a, [b, c], d", + "[a, b, (c, d), (e, f)]", + "a, b, [*c], d, e" + ]: + with self.subTest(source_type=source_type, target=target): + self.check_src_roundtrip(source.format(target=target)) + + def test_star_expr_assign_target_multiple(self): + self.check_src_roundtrip("() = []") + self.check_src_roundtrip("[] = ()") + self.check_src_roundtrip("() = [a] = c, = [d] = e, f = () = g = h") + self.check_src_roundtrip("a = b = c = d") + self.check_src_roundtrip("a, b = c, d = e, f = g") + self.check_src_roundtrip("[a, b] = [c, d] = [e, f] = g") + self.check_src_roundtrip("a, b = [c, d] = e, f = g") + + def test_multiquote_joined_string(self): + self.check_ast_roundtrip("f\"'''{1}\\\"\\\"\\\"\" ") + self.check_ast_roundtrip("""f"'''{1}""\\"" """) + self.check_ast_roundtrip("""f'""\"{1}''' """) + self.check_ast_roundtrip("""f'""\"{1}""\\"' """) + + self.check_ast_roundtrip("""f"'''{"\\n"}""\\"" """) + self.check_ast_roundtrip("""f'""\"{"\\n"}''' """) + self.check_ast_roundtrip("""f'""\"{"\\n"}""\\"' """) + + self.check_ast_roundtrip("""f'''""\"''\\'{"\\n"}''' """) + self.check_ast_roundtrip("""f'''""\"''\\'{"\\n\\"'"}''' """) + self.check_ast_roundtrip("""f'''""\"''\\'{""\"\\n\\"'''""\" '''\\n'''}''' """) + + def test_backslash_in_format_spec(self): + import re + msg = re.escape("invalid escape sequence '\\ '") + with self.assertWarnsRegex(SyntaxWarning, msg): + self.check_ast_roundtrip("""f"{x:\\ }" """) + self.check_ast_roundtrip("""f"{x:\\n}" """) + + self.check_ast_roundtrip("""f"{x:\\\\ }" """) + + with self.assertWarnsRegex(SyntaxWarning, msg): + self.check_ast_roundtrip("""f"{x:\\\\\\ }" """) + self.check_ast_roundtrip("""f"{x:\\\\\\n}" """) + + self.check_ast_roundtrip("""f"{x:\\\\\\\\ }" """) + + def test_quote_in_format_spec(self): + self.check_ast_roundtrip("""f"{x:'}" """) + self.check_ast_roundtrip("""f"{x:\\'}" """) + self.check_ast_roundtrip("""f"{x:\\\\'}" """) + + self.check_ast_roundtrip("""f'\\'{x:"}' """) + self.check_ast_roundtrip("""f'\\'{x:\\"}' """) + self.check_ast_roundtrip("""f'\\'{x:\\\\"}' """) + + def test_type_params(self): + self.check_ast_roundtrip("type A = int") + self.check_ast_roundtrip("type A[T] = int") + self.check_ast_roundtrip("type A[T: int] = int") + self.check_ast_roundtrip("type A[T = int] = int") + self.check_ast_roundtrip("type A[T: int = int] = int") + self.check_ast_roundtrip("type A[**P] = int") + self.check_ast_roundtrip("type A[**P = int] = int") + self.check_ast_roundtrip("type A[*Ts] = int") + self.check_ast_roundtrip("type A[*Ts = int] = int") + self.check_ast_roundtrip("type A[*Ts = *int] = int") + self.check_ast_roundtrip("def f[T: int = int, **P = int, *Ts = *int]():\n pass") + self.check_ast_roundtrip("class C[T: int = int, **P = int, *Ts = *int]():\n pass") + + +class ManualASTCreationTestCase(unittest.TestCase): + """Test that AST nodes created without a type_params field unparse correctly.""" + + def test_class(self): + node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[]) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "class X:\n pass") + + def test_class_with_type_params(self): + node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[], + type_params=[ast.TypeVar("T")]) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "class X[T]:\n pass") + + def test_function(self): + node = ast.FunctionDef( + name="f", + args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), + body=[ast.Pass()], + decorator_list=[], + returns=None, + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "def f():\n pass") + + def test_function_with_type_params(self): + node = ast.FunctionDef( + name="f", + args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), + body=[ast.Pass()], + decorator_list=[], + returns=None, + type_params=[ast.TypeVar("T")], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "def f[T]():\n pass") + + def test_function_with_type_params_and_bound(self): + node = ast.FunctionDef( + name="f", + args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), + body=[ast.Pass()], + decorator_list=[], + returns=None, + type_params=[ast.TypeVar("T", bound=ast.Name("int", ctx=ast.Load()))], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "def f[T: int]():\n pass") + + def test_function_with_type_params_and_default(self): + node = ast.FunctionDef( + name="f", + args=ast.arguments(), + body=[ast.Pass()], + type_params=[ + ast.TypeVar("T", default_value=ast.Constant(value=1)), + ast.TypeVarTuple("Ts", default_value=ast.Starred(value=ast.Constant(value=1), ctx=ast.Load())), + ast.ParamSpec("P", default_value=ast.Constant(value=1)), + ], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "def f[T = 1, *Ts = *1, **P = 1]():\n pass") + + def test_async_function(self): + node = ast.AsyncFunctionDef( + name="f", + args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), + body=[ast.Pass()], + decorator_list=[], + returns=None, + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "async def f():\n pass") + + def test_async_function_with_type_params(self): + node = ast.AsyncFunctionDef( + name="f", + args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), + body=[ast.Pass()], + decorator_list=[], + returns=None, + type_params=[ast.TypeVar("T")], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "async def f[T]():\n pass") + + def test_async_function_with_type_params_and_default(self): + node = ast.AsyncFunctionDef( + name="f", + args=ast.arguments(), + body=[ast.Pass()], + type_params=[ + ast.TypeVar("T", default_value=ast.Constant(value=1)), + ast.TypeVarTuple("Ts", default_value=ast.Starred(value=ast.Constant(value=1), ctx=ast.Load())), + ast.ParamSpec("P", default_value=ast.Constant(value=1)), + ], + ) + ast.fix_missing_locations(node) + self.assertEqual(ast.unparse(node), "async def f[T = 1, *Ts = *1, **P = 1]():\n pass") + + +class DirectoryTestCase(ASTTestCase): + """Test roundtrip behaviour on all files in Lib and Lib/test.""" + + lib_dir = pathlib.Path(__file__).parent / ".." + test_directories = (lib_dir, lib_dir / "test") + run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py", + "test_ast.py", "test_asdl_parser.py", "test_fstring.py", + "test_patma.py", "test_type_alias.py", "test_type_params.py", + "test_tokenize.py"} + + _files_to_test = None + + @classmethod + def files_to_test(cls): + + if cls._files_to_test is not None: + return cls._files_to_test + + items = [ + item.resolve() + for directory in cls.test_directories + for item in directory.glob("*.py") + if not item.name.startswith("bad") + ] + + # Test limited subset of files unless the 'cpu' resource is specified. + if not test.support.is_resource_enabled("cpu"): + + tests_to_run_always = {item for item in items if + item.name in cls.run_always_files} + + items = set(random.sample(items, 10)) + + # Make sure that at least tests that heavily use grammar features are + # always considered in order to reduce the chance of missing something. + items = list(items | tests_to_run_always) + + # bpo-31174: Store the names sample to always test the same files. + # It prevents false alarms when hunting reference leaks. + cls._files_to_test = items + + return items + + def test_files(self): + for item in self.files_to_test(): + if test.support.verbose: + print(f"Testing {item.absolute()}") + + with self.subTest(filename=item): + source = read_pyfile(item) + self.check_ast_roundtrip(source) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_winapi.py b/Lib/test/test_winapi.py index 99b5a0dfd16..73b8228ddc9 100644 --- a/Lib/test/test_winapi.py +++ b/Lib/test/test_winapi.py @@ -158,4 +158,4 @@ def test_namedpipe(self): self.assertEqual((b'', 0), _winapi.PeekNamedPipe(pipe, 8)[:2]) pipe2.write(b'testdata') pipe2.flush() - self.assertEqual((b'testdata', 8), _winapi.PeekNamedPipe(pipe, 8)[:2]) \ No newline at end of file + self.assertEqual((b'testdata', 8), _winapi.PeekNamedPipe(pipe, 8)[:2]) diff --git a/Lib/test/test_winsound.py b/Lib/test/test_winsound.py new file mode 100644 index 00000000000..870ab7bd41d --- /dev/null +++ b/Lib/test/test_winsound.py @@ -0,0 +1,166 @@ +# Ridiculously simple test of the winsound module for Windows. + +import functools +import os +import time +import unittest + +from test import support +from test.support import import_helper +from test.support import os_helper + + +support.requires('audio') +winsound = import_helper.import_module('winsound') + + +# Unless we actually have an ear in the room, we have no idea whether a sound +# actually plays, and it's incredibly flaky trying to figure out if a sound +# even *should* play. Instead of guessing, just call the function and assume +# it either passed or raised the RuntimeError we expect in case of failure. +def sound_func(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + ret = func(*args, **kwargs) + except RuntimeError as e: + if support.verbose: + print(func.__name__, 'failed:', e) + else: + if support.verbose: + print(func.__name__, 'returned') + return ret + return wrapper + + +safe_Beep = sound_func(winsound.Beep) +safe_MessageBeep = sound_func(winsound.MessageBeep) +safe_PlaySound = sound_func(winsound.PlaySound) + + +class BeepTest(unittest.TestCase): + + def test_errors(self): + self.assertRaises(TypeError, winsound.Beep) + self.assertRaises(ValueError, winsound.Beep, 36, 75) + self.assertRaises(ValueError, winsound.Beep, 32768, 75) + + def test_extremes(self): + safe_Beep(37, 75) + safe_Beep(32767, 75) + + def test_increasingfrequency(self): + for i in range(100, 2000, 100): + safe_Beep(i, 75) + + def test_keyword_args(self): + safe_Beep(duration=75, frequency=2000) + + +class MessageBeepTest(unittest.TestCase): + + def tearDown(self): + time.sleep(0.5) + + def test_default(self): + self.assertRaises(TypeError, winsound.MessageBeep, "bad") + self.assertRaises(TypeError, winsound.MessageBeep, 42, 42) + safe_MessageBeep() + + def test_ok(self): + safe_MessageBeep(winsound.MB_OK) + + def test_asterisk(self): + safe_MessageBeep(winsound.MB_ICONASTERISK) + + def test_exclamation(self): + safe_MessageBeep(winsound.MB_ICONEXCLAMATION) + + def test_hand(self): + safe_MessageBeep(winsound.MB_ICONHAND) + + def test_question(self): + safe_MessageBeep(winsound.MB_ICONQUESTION) + + def test_keyword_args(self): + safe_MessageBeep(type=winsound.MB_OK) + + +class PlaySoundTest(unittest.TestCase): + + def test_errors(self): + self.assertRaises(TypeError, winsound.PlaySound) + self.assertRaises(TypeError, winsound.PlaySound, "bad", "bad") + self.assertRaises( + RuntimeError, + winsound.PlaySound, + "none", winsound.SND_ASYNC | winsound.SND_MEMORY + ) + self.assertRaises(TypeError, winsound.PlaySound, b"bad", 0) + self.assertRaises(TypeError, winsound.PlaySound, "bad", + winsound.SND_MEMORY) + self.assertRaises(TypeError, winsound.PlaySound, 1, 0) + # embedded null character + self.assertRaises(ValueError, winsound.PlaySound, 'bad\0', 0) + + def test_keyword_args(self): + safe_PlaySound(flags=winsound.SND_ALIAS, sound="SystemExit") + + def test_snd_memory(self): + with open(support.findfile('pluck-pcm8.wav', + subdir='audiodata'), 'rb') as f: + audio_data = f.read() + safe_PlaySound(audio_data, winsound.SND_MEMORY) + audio_data = bytearray(audio_data) + safe_PlaySound(audio_data, winsound.SND_MEMORY) + + def test_snd_filename(self): + fn = support.findfile('pluck-pcm8.wav', subdir='audiodata') + safe_PlaySound(fn, winsound.SND_FILENAME | winsound.SND_NODEFAULT) + + def test_snd_filepath(self): + fn = support.findfile('pluck-pcm8.wav', subdir='audiodata') + path = os_helper.FakePath(fn) + safe_PlaySound(path, winsound.SND_FILENAME | winsound.SND_NODEFAULT) + + def test_snd_filepath_as_bytes(self): + fn = support.findfile('pluck-pcm8.wav', subdir='audiodata') + self.assertRaises( + TypeError, + winsound.PlaySound, + os_helper.FakePath(os.fsencode(fn)), + winsound.SND_FILENAME | winsound.SND_NODEFAULT + ) + + def test_aliases(self): + aliases = [ + "SystemAsterisk", + "SystemExclamation", + "SystemExit", + "SystemHand", + "SystemQuestion", + ] + for alias in aliases: + with self.subTest(alias=alias): + safe_PlaySound(alias, winsound.SND_ALIAS) + + def test_alias_fallback(self): + safe_PlaySound('!"$%&/(#+*', winsound.SND_ALIAS) + + def test_alias_nofallback(self): + safe_PlaySound('!"$%&/(#+*', winsound.SND_ALIAS | winsound.SND_NODEFAULT) + + def test_stopasync(self): + safe_PlaySound( + 'SystemQuestion', + winsound.SND_ALIAS | winsound.SND_ASYNC | winsound.SND_LOOP + ) + time.sleep(0.5) + safe_PlaySound('SystemQuestion', winsound.SND_ALIAS | winsound.SND_NOSTOP) + # Issue 8367: PlaySound(None, winsound.SND_PURGE) + # does not raise on systems without a sound card. + winsound.PlaySound(None, winsound.SND_PURGE) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_zipimport_support.py b/Lib/test/test_zipimport_support.py new file mode 100644 index 00000000000..ae8a8c99762 --- /dev/null +++ b/Lib/test/test_zipimport_support.py @@ -0,0 +1,244 @@ +# This test module covers support in various parts of the standard library +# for working with modules located inside zipfiles +# The tests are centralised in this fashion to make it easy to drop them +# if a platform doesn't support zipimport +import test.support +import os +import os.path +import sys +import textwrap +import zipfile +import zipimport +import doctest +import inspect +import linecache +import unittest +from test.support import os_helper +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, + make_script, make_zip_script) + +verbose = test.support.verbose + +# Library modules covered by this test set +# pdb (Issue 4201) +# inspect (Issue 4223) +# doctest (Issue 4197) + +# Other test modules with zipimport related tests +# test_zipimport (of course!) +# test_cmd_line_script (covers the zipimport support in runpy) + +# Retrieve some helpers from other test cases +from test.test_doctest import (test_doctest, + sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings, sample_doctest_skip) + + +def _run_object_doctest(obj, module): + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) + runner = doctest.DocTestRunner(verbose=verbose) + # Use the object's fully qualified name if it has one + # Otherwise, use the module's name + try: + name = "%s.%s" % (obj.__module__, obj.__qualname__) + except AttributeError: + name = module.__name__ + for example in finder.find(obj, name, module): + runner.run(example) + f, t = runner.failures, runner.tries + if f: + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) + if verbose: + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) + return f, t + + + +class ZipSupportTests(unittest.TestCase): + # This used to use the ImportHooksBaseTestCase to restore + # the state of the import related information + # in the sys module after each test. However, that restores + # *too much* information and breaks for the invocation + # of test_doctest. So we do our own thing and leave + # sys.modules alone. + # We also clear the linecache and zipimport cache + # just to avoid any bogus errors due to name reuse in the tests + def setUp(self): + linecache.clearcache() + zipimport._zip_directory_cache.clear() + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + + def test_inspect_getsource_issue4223(self): + test_src = "def foo(): pass\n" + with os_helper.temp_dir() as d: + init_name = make_script(d, '__init__', test_src) + name_in_zip = os.path.join('zip_pkg', + os.path.basename(init_name)) + zip_name, run_name = make_zip_script(d, 'test_zip', + init_name, name_in_zip) + os.remove(init_name) + sys.path.insert(0, zip_name) + import zip_pkg + try: + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) + finally: + del sys.modules["zip_pkg"] + + def test_doctest_issue4197(self): + # To avoid having to keep two copies of the doctest module's + # unit tests in sync, this test works by taking the source of + # test_doctest itself, rewriting it a bit to cope with a new + # location, and then throwing it in a zip file to make sure + # everything still works correctly + test_src = inspect.getsource(test_doctest) + test_src = test_src.replace( + "from test.test_doctest import test_doctest", + "import test_zipped_doctest as test_doctest") + test_src = test_src.replace("test.test_doctest.test_doctest", + "test_zipped_doctest") + test_src = test_src.replace("test.test_doctest.sample_doctest", + "sample_zipped_doctest") + # The sample doctest files rewritten to include in the zipped version. + sample_sources = {} + for mod in [sample_doctest, sample_doctest_no_doctests, + sample_doctest_no_docstrings, sample_doctest_skip]: + src = inspect.getsource(mod) + src = src.replace("test.test_doctest.test_doctest", "test_zipped_doctest") + # Rewrite the module name so that, for example, + # "test.sample_doctest" becomes "sample_zipped_doctest". + mod_name = mod.__name__.split(".")[-1] + mod_name = mod_name.replace("sample_", "sample_zipped_") + sample_sources[mod_name] = src + + with os_helper.temp_dir() as d: + script_name = make_script(d, 'test_zipped_doctest', + test_src) + zip_name, run_name = make_zip_script(d, 'test_zip', + script_name) + with zipfile.ZipFile(zip_name, 'a') as z: + for mod_name, src in sample_sources.items(): + z.writestr(mod_name + ".py", src) + if verbose: + with zipfile.ZipFile(zip_name, 'r') as zip_file: + print ('Contents of %r:' % zip_name) + zip_file.printdir() + os.remove(script_name) + sys.path.insert(0, zip_name) + import test_zipped_doctest + try: + # Some of the doc tests depend on the colocated text files + # which aren't available to the zipped version (the doctest + # module currently requires real filenames for non-embedded + # tests). So we're forced to be selective about which tests + # to run. + # doctest could really use some APIs which take a text + # string or a file object instead of a filename... + known_good_tests = [ + test_zipped_doctest.SampleClass, + test_zipped_doctest.SampleClass.NestedClass, + test_zipped_doctest.SampleClass.NestedClass.__init__, + test_zipped_doctest.SampleClass.__init__, + test_zipped_doctest.SampleClass.a_classmethod, + test_zipped_doctest.SampleClass.a_property, + test_zipped_doctest.SampleClass.a_staticmethod, + test_zipped_doctest.SampleClass.double, + test_zipped_doctest.SampleClass.get, + test_zipped_doctest.SampleNewStyleClass, + test_zipped_doctest.SampleNewStyleClass.__init__, + test_zipped_doctest.SampleNewStyleClass.double, + test_zipped_doctest.SampleNewStyleClass.get, + test_zipped_doctest.sample_func, + test_zipped_doctest.test_DocTest, + test_zipped_doctest.test_DocTestParser, + test_zipped_doctest.test_DocTestRunner.basics, + test_zipped_doctest.test_DocTestRunner.exceptions, + test_zipped_doctest.test_DocTestRunner.option_directives, + test_zipped_doctest.test_DocTestRunner.optionflags, + test_zipped_doctest.test_DocTestRunner.verbose_flag, + test_zipped_doctest.test_Example, + test_zipped_doctest.test_debug, + test_zipped_doctest.test_testsource, + test_zipped_doctest.test_trailing_space_in_test, + test_zipped_doctest.test_DocTestSuite, + test_zipped_doctest.test_DocTestFinder, + ] + # These tests are the ones which need access + # to the data files, so we don't run them + fail_due_to_missing_data_files = [ + test_zipped_doctest.test_DocFileSuite, + test_zipped_doctest.test_testfile, + test_zipped_doctest.test_unittest_reportflags, + ] + + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) + finally: + del sys.modules["test_zipped_doctest"] + + def test_doctest_main_issue4197(self): + test_src = textwrap.dedent("""\ + class Test: + ">>> 'line 2'" + pass + + import doctest + doctest.testmod() + """) + pattern = 'File "%s", line 2, in %s' + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + rc, out, err = assert_python_ok(script_name) + expected = pattern % (script_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + rc, out, err = assert_python_ok(zip_name) + expected = pattern % (run_name, "__main__.Test") + if verbose: + print ("Expected line", expected) + print ("Got stdout:") + print (ascii(out)) + self.assertIn(expected.encode('utf-8'), out) + + def test_pdb_issue4201(self): + test_src = textwrap.dedent("""\ + def f(): + pass + + import pdb + pdb.Pdb(nosigint=True).runcall(f) + """) + with os_helper.temp_dir() as d: + script_name = make_script(d, 'script', test_src) + p = spawn_python(script_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) + zip_name, run_name = make_zip_script(d, "test_zip", + script_name, '__main__.py') + p = spawn_python(zip_name) + p.stdin.write(b'l\n') + data = kill_python(p) + # bdb/pdb applies normcase to its filename before displaying + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + + +def tearDownModule(): + test.support.reap_children() + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_zoneinfo/data/update_test_data.py b/Lib/test/test_zoneinfo/data/update_test_data.py index e69de29bb2d..f531ab316a1 100644 --- a/Lib/test/test_zoneinfo/data/update_test_data.py +++ b/Lib/test/test_zoneinfo/data/update_test_data.py @@ -0,0 +1,122 @@ +""" +Script to automatically generate a JSON file containing time zone information. + +This is done to allow "pinning" a small subset of the tzdata in the tests, +since we are testing properties of a file that may be subject to change. For +example, the behavior in the far future of any given zone is likely to change, +but "does this give the right answer for this file in 2040" is still an +important property to test. + +This must be run from a computer with zoneinfo data installed. +""" +from __future__ import annotations + +import base64 +import functools +import json +import lzma +import pathlib +import textwrap +import typing + +import zoneinfo + +KEYS = [ + "Africa/Abidjan", + "Africa/Casablanca", + "America/Los_Angeles", + "America/Santiago", + "Asia/Tokyo", + "Australia/Sydney", + "Europe/Dublin", + "Europe/Lisbon", + "Europe/London", + "Pacific/Kiritimati", + "UTC", +] + +TEST_DATA_LOC = pathlib.Path(__file__).parent + + +@functools.lru_cache(maxsize=None) +def get_zoneinfo_path() -> pathlib.Path: + """Get the first zoneinfo directory on TZPATH containing the "UTC" zone.""" + key = "UTC" + for path in map(pathlib.Path, zoneinfo.TZPATH): + if (path / key).exists(): + return path + else: + raise OSError("Cannot find time zone data.") + + +def get_zoneinfo_metadata() -> typing.Dict[str, str]: + path = get_zoneinfo_path() + + tzdata_zi = path / "tzdata.zi" + if not tzdata_zi.exists(): + # tzdata.zi is necessary to get the version information + raise OSError("Time zone data does not include tzdata.zi.") + + with open(tzdata_zi, "r") as f: + version_line = next(f) + + _, version = version_line.strip().rsplit(" ", 1) + + if ( + not version[0:4].isdigit() + or len(version) < 5 + or not version[4:].isalpha() + ): + raise ValueError( + "Version string should be YYYYx, " + + "where YYYY is the year and x is a letter; " + + f"found: {version}" + ) + + return {"version": version} + + +def get_zoneinfo(key: str) -> bytes: + path = get_zoneinfo_path() + + with open(path / key, "rb") as f: + return f.read() + + +def encode_compressed(data: bytes) -> typing.List[str]: + compressed_zone = lzma.compress(data) + raw = base64.b85encode(compressed_zone) + + raw_data_str = raw.decode("utf-8") + + data_str = textwrap.wrap(raw_data_str, width=70) + return data_str + + +def load_compressed_keys() -> typing.Dict[str, typing.List[str]]: + output = {key: encode_compressed(get_zoneinfo(key)) for key in KEYS} + + return output + + +def update_test_data(fname: str = "zoneinfo_data.json") -> None: + TEST_DATA_LOC.mkdir(exist_ok=True, parents=True) + + # Annotation required: https://github.com/python/mypy/issues/8772 + json_kwargs: typing.Dict[str, typing.Any] = dict( + indent=2, sort_keys=True, + ) + + compressed_keys = load_compressed_keys() + metadata = get_zoneinfo_metadata() + output = { + "metadata": metadata, + "data": compressed_keys, + } + + with open(TEST_DATA_LOC / fname, "w") as f: + json.dump(output, f, **json_kwargs) + + +if __name__ == "__main__": + update_test_data() diff --git a/Lib/test/test_zoneinfo/data/zoneinfo_data.json b/Lib/test/test_zoneinfo/data/zoneinfo_data.json index 48e70ac6d38..ec4414a0cde 100644 --- a/Lib/test/test_zoneinfo/data/zoneinfo_data.json +++ b/Lib/test/test_zoneinfo/data/zoneinfo_data.json @@ -1,190 +1,190 @@ { - "data": { - "Africa/Abidjan": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~f{VGF<>F7KxBg5R*{Ksocg8-YYVul=v7vZzaHN", - "uC=da5UI2rH18c!OnjV{y4u(+A!!VBKmY&$ORw>7UO^(500B;v0RR91bXh%WvBYQl0ssI2", - "00dcD" - ], - "Africa/Casablanca": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0b&Kz+C_;7KxBg5R*{N&yjMUR~;C-fDaSOU;q-~", - "FqW+4{YBjbcw}`a!dW>b)R2-0a+uwf`P3{_Y@HuCz}S$J$ZJ>R_V<~|Fk>sgX4=%0vUrh-", - "lt@YP^Wrus;j?`Th#xRPzf<<~Hp4DH^gZX>d{+WOp~HNu8!{uWu}&XphAd{j1;rB4|9?R!", - "pqruAFUMt8#*WcrVS{;kLlY(cJRV$w?d2car%Rs>q9BgTU4", - "Ht-tQKZ7Z`9QqOb?R#b%z?rk>!CkH7jy3wja4NG2q)H}fNRKg8v{);Em;K3Cncf4C6&Oaj", - "V+DbX%o4+)CV3+e!Lm6dutu(0BQpH1T?W(~cQtKV*^_Pdx!LirjpTs?Bmt@vktjLq4;)O!", - "rrly=c*rwTwMJFd0I57`hgkc?=nyI4RZf9W$6DCWugmf&)wk^tWH17owj=#PGH7Xv-?9$j", - "njwDlkOE+BFNR9YXEmBpO;rqEw=e2IR-8^(W;8ma?M3JVd($2T>IW+0tk|Gm8>ftukRQ9J", - "8k3brzqMnVyjsLI-CKneFa)Lxvp_aq40f}0J3VVoWL5rox", - "`Kptivcp}o5xA^@>qNI%?zo=Yj4AMV?kbAA)j(1%)+Pp)bSn+7Yk`M{oE}L-Z!G6OMr5G+h", - "p)$3Lg{ono{4cN>Vr&>L4kXH;_VnBL5U!LgzqE%P7QQ*tue}O`3(TZ0`aKn&~8trOQ-rBXCp)f@P6RMO4l0+;b|5-pk9_ryNh}Zc*v%mvz_#", - "yd6fjB0g9{MmMnu8bG%#C~ugXK^S^k@?ab#", - "O|aE>dDTt4s4n69(~@t~!wniV%g7khFx~I*4>Y|V$4j5%KPF*-FyKIi@!Ho&", - "x8QQsksYt8)D+W)Ni!=G`ogSu^vLL-l#7A7=iIAKL2SuZk9F}NfNk86VI)9WZE?%2wC-ya", - "F~z#Qsq)LH0|_D8^5fU8X%GeQ4TB>R-dlziA&tZe&1ada208!$nk`7bOFO2S00G`Z@1A~t&lyL{p{eM{5)QGf7Mo5FW9==mlyXJt2", - "UwpntR7H0eSq!(aYq#aqUz&RM*tvuMI)AsM?K3-dV3-TT{t)!Iy#JTo=tXkzAM9~j2YbiO", - "ls3(H8Dc>Y|D1aqL51vjLbpYG;GvGTQB4bXuJ%mA;(B4eUpu$$@zv2vVcq-Y)VKbzp^tei", - "uzy}R{LuvDjpuVb`79O+CBmg{Wx!bvx$eu4zRE&", - "PehMb=&G<9$>iZ|bFE)0=4I?KLFGBC0I(0_svgw0%FiMsT%koo*!nEYc6GY@QnU}&4Isg;", - "l=|khi(!VaiSE2=Ny`&&tpi~~;{$uN}%f|7mBhAy;s3YT^sy!$eG~?`9mNJC9@4Bac_p^BZh)Yd_rWW5qh-?tKY(>5VHO", - "L*iT8P@wCavLj^yYbnDR+4ukhS+xPrpl)iqB?u)bj9a2aW==g6G3lCJd>(+Blfr)~^40F4f>cRZ^UF;RibfZ>0m73hR", - "C{$vTfC(STN`g7(B<=Z2556{}0`?p&|Akkst!4Xy4OT;A@c$XTUI3FRRjy*KA7uC56FD)z", - "^X{WV*sr(w!c$W357o!&eLO2wTDNOyw@gf(&R<t;=-Tu1TV{>%8ZVATC9tjD8|(&`$9YHvZ9bVe#>w", - "|8c;Tg|xE&)`*}LwM*E}q}q8^Qja%p`_U)*5DdLI9O@!e=3jFjOCrCq28b_bb;s>%D#iJB", - "CWJi{JH!Js;6nfayos$kq^OEX00HO-lokL0!mqm{vBYQl0ssI200dcD" - ], - "America/Santiago": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0fRZ<6QtM7KxBg84(fsEAUJ$J{f-TXlPEUec5Ee", - "n+hsD4lC(QYax=JdSpoyje8%VM`GW}{bJ8@y$A8O&*$pw{(f~Os#}2w", - "eX6^Rgi$IT%n^V^85L>$_c7{cB^#ogV=rHBJGiz-RQNFGK?gdPi|q)j`&8)}KJ{qo6dixa", - "9@yYyVg+%lo0nO+Tw0-w2hJ%mafyWL)|", - ")?W6Bi%FWuGPA1Dru$XR4SZANsAthU2EoKHF6oEtKq`rwP", - "(VNegnI_NI%;ma$)wj{k!@KFB30Yo)IOrl>)$)D|+(5h&+%2vuwGuy^@S8FT^s21V5};>VA9Iu;?8bHz#r<;JtfZDI1(FT@edh0#", - "MYW$A1qkMGIwTZqqdYNE3gl#zp&NbL9Mp=voqN|;?gqR&4$)1`znddtEyuKS*^nMMD=0^>", - "7^z6-C4P67UWOXuMBubP>j6i~03aR@jD^-Y`JSYu#Yp0P8dLLJ0QOPE8=BoiuRX59YW7xg", - "WiexjHX%&0?`ZQCdxCdL^qd1v@kOjQKaWo2Y1++~LcA%FTq?5o%}fX1-RIvlB)1#iTNomGnUL=nM!>Ix|AGtON7!F1O?53kqlC2o-`ZGw*+s", - "NM$^9znsIJMwlgscE`|O3|;BRgsQMYm~`uv+nvuv`nigRa}X=BX=A5Sw$)WEklF7&c>_~$", - "zJ(m--bqXgiN^w-U=BJH9C0Qro(x90zo@rK;&TJ$nI@&k$ORgOb2s%gWbc}ok_27)Eoku~Fq|B-Ps+4J_", - "HPJMLJ2^_)cOU$p&3kNAlrV!)%~6r$BJ>OOi~=-<6byle{?zd4J{NG}o8tw|+#ZNLcpNwk", - "TuPE~sbJB8_RZb2DopStO+Wwux~F#S59zm%00I98;S&G=b(j+6vBYQl0ssI200dcD" - ], - "Asia/Tokyo": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~luMgIxeB7KxBg5R*;y?l4Rl4neXH3cv!OtfK@h", - "KZzauI)S!FSDREPhhBS6Fb$&Vv#7%;?Te|>pF^0HBr&z_Tk<%vMW_QqjevRZOp8XVFgP<8", - "TkT#`9H&0Ua;gT1#rZLV0HqbAKK;_z@nO;6t0L}hOdk<>TdUa07R(LPI6@!GU$ty4=mwqHG-XVe*n(Yvgdlr+FqIU18!osi)48t~eWX8)&L", - "G)Ud^0zz@*AF+2r7E}Nf9Y72K~o-T%}D&z%}#7g2br?oH6ZiYH^%>J3D)TPKV(JY*bwjuw5=DsPB@~CrROZeN", - "x>A*H&CHrWt0`EP`m!F%waepl#|w#&`XgVc?~2M3uw$fGX~tf_Il!q#Aa<*8xlzQ2+7r6Z", - "^;Laa9F(WB_O&Dy2r>~@kSi16W{=6+i5GV=Uq~KX*~&HUN4oz7*O(gXIr}sDVcD`Ikgw#|", - "50ssal8s)Qy;?YGCf;*UKKKN!T4!Kqy_G;7PfQapugqvVBKy12v3TVH^L2", - "0?#5*VP~MOYfe$h`*L!7@tiW|_^X1N%<}`7YahiUYtMu5XwmOf3?dr+@zXHwW`z}ZDqZlT", - "<2Cs(<1%M!i6o&VK89BY0J7HPIo;O62s=|IbV^@y$N&#=>i^F00FcHoDl#3", - "Mdv&xvBYQl0ssI200dcD" - ], - "Europe/Dublin": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0>b$_+0=h7KxBg5R*;&J77#T_U2R5sleVWFDmK~", - "Kzj5oh@`QKHvW^6V{jU-w>qg1tSt0c^vh;?qAqA0%t?;#S~6U8Qi", - "v&f1s9IH#g$m1k1a#3+lylw4mwT4QnEUUQdwg+xnEcBlgu31bAVabn41OMZVLGz6NDwG%X", - "uQar!b>GI{qSahE`AG}$kRWbuI~JCt;38)Xwbb~Qggs55t+MAHIxgDxzTJ;2xXx99+qCy4", - "45kC#v_l8fx|G&jlVvaciR<-wwf22l%4(t@S6tnX39#_K(4S0fu$FUs$isud9IKzCXB78NkARYq@9Dc0TGkhz);NtM_SSzEffN", - "l{2^*CKGdp52h!52A)6q9fUSltXF{T*Ehc9Q7u8!W7pE(Fv$D$cKUAt6wY=DA1mGgxC*VX", - "q_If3G#FY6-Voj`fIKk`0}Cc72_SD{v>468LV{pyBI33^p0E?}RwDA6Pkq--C~0jF&Z@Pv", - "!dx_1SN_)jwz@P$(oK%P!Tk9?fRjK88yxhxlcFtTjjZ$DYssSsa#ufYrR+}}nKS+r384o~", - "!Uw$nwTbF~qgRsgr0N#d@KIinx%hQB(SJyjJtDtIy(%mDm}ZBGN}dV6K~om|=U", - "VGkbciQ=^$_14|gT21!YQ)@y*Rd0i_lS6gtPBE9+ah%WIJPwzUTjIr+J1XckkmA!6WE16%", - "CVAl{Dn&-)=G$Bjh?bh0$Xt1UDcgXJjXzzojuw0>paV~?Sa`VN3FysqFxTzfKVAu*ucq#+m=|KSSMvp_#@-lwd+q*ue", - "FQ^5<|<0R-u4qYMbRqzSn&", - "Q7jSuvc%b+EZc%>nI(+&0Tl1Y>a6v4`uNFD-7$QrhHgS7Wnv~rDgfH;rQw3+m`LJxoM4v#", - "gK@?|B{RHJ*VxZgk#!p<_&-sjxOda0YaiJ1UnG41VPv(Et%ElzKRMcO$AfgU+Xnwg5p2_+", - "NrnZ1WfEj^fmHd^sx@%JWKkh#zaK0ox%rdP)zUmGZZnqmZ_9L=%6R8ibJH0bOT$AGhDo6{", - "fJ?;_U;D|^>5by2ul@i4Zf()InfFN}00EQ=q#FPL>RM>svBYQl0ssI200dcD" - ], - "Europe/Lisbon": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0=rf*IfWA7KxBg5R*;*X|PN+G3LqthM?xgkNUN_", - ")gCt1Sc%YT6^TTomk4yVHXeyvQj8}l<;q&s7K}#Vnc8lII1?)AHh$*>OKUU4S;*h>v*ep0", - "xTi1cK2{aY*|2D*-~K<;-{_W+r@NvZ7-|NZv($ek_C%VfP0xjWeZP#CPXD`IKkakjh(kUd", - "&H)m;^Q(jGjIyiyrcUMtOP)u3A>sw6ux;Bmp3x$4QvQKMx5TrCx_!$srWQuXNs&`9=^IY1", - "yc&C31!sQh7P=Mk*#6x8Z@5^%ehR8UW$OWw0KMw}P1ycI^", - "4eh12oBUOV?S>n*d!+EM@>x#9PZD12iD=zaC;7`8dTfkU_6d}OZvSFSbGgXeKw}XyX@D=(", - ")D0!^DBGr8pXWBT$S-yhLP>Z3ys^VW3}RQ6{NGGVJG6vf*MH93vvNW6yLjie1;{4tVhg-KnSf|G`!", - "Z;j$7gJ1ows~RD=@n7I6aFd8rOR_7Y?E-$clI%1o5gA@O!KPa^(8^iFFeFykI-+z>E$mvp", - "E_h`vbHPjqkLs`Dn-0FV`R@z|h!S(Lb;M&|Exr!biY`%bfp$6`hK;GDhdP|^Q", - "*Ty*}1d41K>H2B{jrjE9aFK>yAQJBX9CD%-384S;0fw`PlprHGS`^b$oS-`I4VH7ji8ou-", - "g|060jfb1XcxiInT0oOoeR7#%e5Ug5#KW)nVSRvLHNe$SQHM@2)`S9L7>RL@Qx%fmm7?3u7P5TywFQ}C@S(pq}|", - "eLPT{C^{<0Q?uU&kSVd%!~8q3;Z0s3OqzF`$HRkePL5Ywgiwn{R(zi+jmOBFrVpW;)@UsU#%$8BcV#h@}m$#!Fglo&bwb78aYqOG_W7h{eb(+39&-mk4EIXq_", - "_`30=8sfA3=!3TO_TyS5X22~?6nKngZ|bq=grdq=9X)3xAkA42L!~rmS)n3w-~;lgz%Fhn", - "(?rXdp2ho~9?wmVs2JwVt~?@FVD%`tN69{(i3oQa;O0$E$lF&~Y#_H6bu6(BiwblJ>;-Fs", - "gA$Y$*?=X)n1pFkKn}F~`>=4)+LLQk?L*P!bhAm0;`N~z3QbUIyVrm%kOZ(n1JJsm0pyb8", - "!GV{d*C!9KXv;4vD4Q>-k#+x(!V5L@w5M>v2V5a`B>t(|B", - "|Fqr4^-{S*%Ep~ojUtx_CRbSQ(uFwu2=KH)Q@EBs@ZqRXn4mU;B!68;;IQs3Ub=n&UU%*m", - "k&zwD36&JSwsN(%k&x?H+tN^6)23c`I0=5^N_R0~1>tsFZ`^`3z~rXSXT&qcwa#n!%+Z#P", - "PG}(D^_CCILXnF|GKwabBh*xFS?4rwGo2vtJUwzrbv_$5PO+`?$l{H-jGB@X%S!OAhw;D4", - "XFycN3!XqQ&EorJOD3>~^U%Luw!jF<;6_q-f-S|6{cQDfZ2(4Xf1MMLr1=SA=MwVf2%Pp%VP;jn)|5Tf!-DbUGn%I-rkYaH7?$$O!t)wwClAisr3eUoeB^~T=U*_P~Y2*KdnO87>B!19sV=xZ5", - "yApq26RxgqA|*tmsvtL#OhcF(C<0EGWHP)BFl?h)_*7!{LoJiv%RsOs!q->n+DcV%9~B@RbC_1G_1g6`Yd~8|%-=2l~oGN!~TVv2Bnk>7wW8L@^?vX$f3AiT)(4nrCuTm9%(XC6Nai", - "E(;}7&=YZagjAN$O-cN;1u{dTkElmB0GT$|Wa)QMmKrx<|LCJ9qlUoFsUbD^H^6_8(w<0{", - "ftj&O1~p_%lh5z;zNV&sP+", - "NF2>iK{8KMUf+)<-)VxXbLxD(alL}N$AT-ogNbJSMMYeX+Z{jS)b8TK^PB=FxyBxzfmFto", - "eo0R`a(%NO?#aEH9|?Cv00000NIsFh6BW2800DjO0RR918Pu^`vBYQl0ssI200dcD" - ], - "UTC": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~e#|9bEt_7KxBg5R*|3h1|xhHLji!C57qW6L*|H", - "pEErm00000ygu;I+>V)?00B92fhY-(AGY&-0RR9100dcD" - ] - }, - "metadata": { - "version": "2020a" - } - } \ No newline at end of file + "data": { + "Africa/Abidjan": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~f{VGF<>F7KxBg5R*{Ksocg8-YYVul=v7vZzaHN", + "uC=da5UI2rH18c!OnjV{y4u(+A!!VBKmY&$ORw>7UO^(500B;v0RR91bXh%WvBYQl0ssI2", + "00dcD" + ], + "Africa/Casablanca": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0b&Kz+C_;7KxBg5R*{N&yjMUR~;C-fDaSOU;q-~", + "FqW+4{YBjbcw}`a!dW>b)R2-0a+uwf`P3{_Y@HuCz}S$J$ZJ>R_V<~|Fk>sgX4=%0vUrh-", + "lt@YP^Wrus;j?`Th#xRPzf<<~Hp4DH^gZX>d{+WOp~HNu8!{uWu}&XphAd{j1;rB4|9?R!", + "pqruAFUMt8#*WcrVS{;kLlY(cJRV$w?d2car%Rs>q9BgTU4", + "Ht-tQKZ7Z`9QqOb?R#b%z?rk>!CkH7jy3wja4NG2q)H}fNRKg8v{);Em;K3Cncf4C6&Oaj", + "V+DbX%o4+)CV3+e!Lm6dutu(0BQpH1T?W(~cQtKV*^_Pdx!LirjpTs?Bmt@vktjLq4;)O!", + "rrly=c*rwTwMJFd0I57`hgkc?=nyI4RZf9W$6DCWugmf&)wk^tWH17owj=#PGH7Xv-?9$j", + "njwDlkOE+BFNR9YXEmBpO;rqEw=e2IR-8^(W;8ma?M3JVd($2T>IW+0tk|Gm8>ftukRQ9J", + "8k3brzqMnVyjsLI-CKneFa)Lxvp_aq40f}0J3VVoWL5rox", + "`Kptivcp}o5xA^@>qNI%?zo=Yj4AMV?kbAA)j(1%)+Pp)bSn+7Yk`M{oE}L-Z!G6OMr5G+h", + "p)$3Lg{ono{4cN>Vr&>L4kXH;_VnBL5U!LgzqE%P7QQ*tue}O`3(TZ0`aKn&~8trOQ-rBXCp)f@P6RMO4l0+;b|5-pk9_ryNh}Zc*v%mvz_#", + "yd6fjB0g9{MmMnu8bG%#C~ugXK^S^k@?ab#", + "O|aE>dDTt4s4n69(~@t~!wniV%g7khFx~I*4>Y|V$4j5%KPF*-FyKIi@!Ho&", + "x8QQsksYt8)D+W)Ni!=G`ogSu^vLL-l#7A7=iIAKL2SuZk9F}NfNk86VI)9WZE?%2wC-ya", + "F~z#Qsq)LH0|_D8^5fU8X%GeQ4TB>R-dlziA&tZe&1ada208!$nk`7bOFO2S00G`Z@1A~t&lyL{p{eM{5)QGf7Mo5FW9==mlyXJt2", + "UwpntR7H0eSq!(aYq#aqUz&RM*tvuMI)AsM?K3-dV3-TT{t)!Iy#JTo=tXkzAM9~j2YbiO", + "ls3(H8Dc>Y|D1aqL51vjLbpYG;GvGTQB4bXuJ%mA;(B4eUpu$$@zv2vVcq-Y)VKbzp^tei", + "uzy}R{LuvDjpuVb`79O+CBmg{Wx!bvx$eu4zRE&", + "PehMb=&G<9$>iZ|bFE)0=4I?KLFGBC0I(0_svgw0%FiMsT%koo*!nEYc6GY@QnU}&4Isg;", + "l=|khi(!VaiSE2=Ny`&&tpi~~;{$uN}%f|7mBhAy;s3YT^sy!$eG~?`9mNJC9@4Bac_p^BZh)Yd_rWW5qh-?tKY(>5VHO", + "L*iT8P@wCavLj^yYbnDR+4ukhS+xPrpl)iqB?u)bj9a2aW==g6G3lCJd>(+Blfr)~^40F4f>cRZ^UF;RibfZ>0m73hR", + "C{$vTfC(STN`g7(B<=Z2556{}0`?p&|Akkst!4Xy4OT;A@c$XTUI3FRRjy*KA7uC56FD)z", + "^X{WV*sr(w!c$W357o!&eLO2wTDNOyw@gf(&R<t;=-Tu1TV{>%8ZVATC9tjD8|(&`$9YHvZ9bVe#>w", + "|8c;Tg|xE&)`*}LwM*E}q}q8^Qja%p`_U)*5DdLI9O@!e=3jFjOCrCq28b_bb;s>%D#iJB", + "CWJi{JH!Js;6nfayos$kq^OEX00HO-lokL0!mqm{vBYQl0ssI200dcD" + ], + "America/Santiago": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0fRZ<6QtM7KxBg84(fsEAUJ$J{f-TXlPEUec5Ee", + "n+hsD4lC(QYax=JdSpoyje8%VM`GW}{bJ8@y$A8O&*$pw{(f~Os#}2w", + "eX6^Rgi$IT%n^V^85L>$_c7{cB^#ogV=rHBJGiz-RQNFGK?gdPi|q)j`&8)}KJ{qo6dixa", + "9@yYyVg+%lo0nO+Tw0-w2hJ%mafyWL)|", + ")?W6Bi%FWuGPA1Dru$XR4SZANsAthU2EoKHF6oEtKq`rwP", + "(VNegnI_NI%;ma$)wj{k!@KFB30Yo)IOrl>)$)D|+(5h&+%2vuwGuy^@S8FT^s21V5};>VA9Iu;?8bHz#r<;JtfZDI1(FT@edh0#", + "MYW$A1qkMGIwTZqqdYNE3gl#zp&NbL9Mp=voqN|;?gqR&4$)1`znddtEyuKS*^nMMD=0^>", + "7^z6-C4P67UWOXuMBubP>j6i~03aR@jD^-Y`JSYu#Yp0P8dLLJ0QOPE8=BoiuRX59YW7xg", + "WiexjHX%&0?`ZQCdxCdL^qd1v@kOjQKaWo2Y1++~LcA%FTq?5o%}fX1-RIvlB)1#iTNomGnUL=nM!>Ix|AGtON7!F1O?53kqlC2o-`ZGw*+s", + "NM$^9znsIJMwlgscE`|O3|;BRgsQMYm~`uv+nvuv`nigRa}X=BX=A5Sw$)WEklF7&c>_~$", + "zJ(m--bqXgiN^w-U=BJH9C0Qro(x90zo@rK;&TJ$nI@&k$ORgOb2s%gWbc}ok_27)Eoku~Fq|B-Ps+4J_", + "HPJMLJ2^_)cOU$p&3kNAlrV!)%~6r$BJ>OOi~=-<6byle{?zd4J{NG}o8tw|+#ZNLcpNwk", + "TuPE~sbJB8_RZb2DopStO+Wwux~F#S59zm%00I98;S&G=b(j+6vBYQl0ssI200dcD" + ], + "Asia/Tokyo": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~luMgIxeB7KxBg5R*;y?l4Rl4neXH3cv!OtfK@h", + "KZzauI)S!FSDREPhhBS6Fb$&Vv#7%;?Te|>pF^0HBr&z_Tk<%vMW_QqjevRZOp8XVFgP<8", + "TkT#`9H&0Ua;gT1#rZLV0HqbAKK;_z@nO;6t0L}hOdk<>TdUa07R(LPI6@!GU$ty4=mwqHG-XVe*n(Yvgdlr+FqIU18!osi)48t~eWX8)&L", + "G)Ud^0zz@*AF+2r7E}Nf9Y72K~o-T%}D&z%}#7g2br?oH6ZiYH^%>J3D)TPKV(JY*bwjuw5=DsPB@~CrROZeN", + "x>A*H&CHrWt0`EP`m!F%waepl#|w#&`XgVc?~2M3uw$fGX~tf_Il!q#Aa<*8xlzQ2+7r6Z", + "^;Laa9F(WB_O&Dy2r>~@kSi16W{=6+i5GV=Uq~KX*~&HUN4oz7*O(gXIr}sDVcD`Ikgw#|", + "50ssal8s)Qy;?YGCf;*UKKKN!T4!Kqy_G;7PfQapugqvVBKy12v3TVH^L2", + "0?#5*VP~MOYfe$h`*L!7@tiW|_^X1N%<}`7YahiUYtMu5XwmOf3?dr+@zXHwW`z}ZDqZlT", + "<2Cs(<1%M!i6o&VK89BY0J7HPIo;O62s=|IbV^@y$N&#=>i^F00FcHoDl#3", + "Mdv&xvBYQl0ssI200dcD" + ], + "Europe/Dublin": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0>b$_+0=h7KxBg5R*;&J77#T_U2R5sleVWFDmK~", + "Kzj5oh@`QKHvW^6V{jU-w>qg1tSt0c^vh;?qAqA0%t?;#S~6U8Qi", + "v&f1s9IH#g$m1k1a#3+lylw4mwT4QnEUUQdwg+xnEcBlgu31bAVabn41OMZVLGz6NDwG%X", + "uQar!b>GI{qSahE`AG}$kRWbuI~JCt;38)Xwbb~Qggs55t+MAHIxgDxzTJ;2xXx99+qCy4", + "45kC#v_l8fx|G&jlVvaciR<-wwf22l%4(t@S6tnX39#_K(4S0fu$FUs$isud9IKzCXB78NkARYq@9Dc0TGkhz);NtM_SSzEffN", + "l{2^*CKGdp52h!52A)6q9fUSltXF{T*Ehc9Q7u8!W7pE(Fv$D$cKUAt6wY=DA1mGgxC*VX", + "q_If3G#FY6-Voj`fIKk`0}Cc72_SD{v>468LV{pyBI33^p0E?}RwDA6Pkq--C~0jF&Z@Pv", + "!dx_1SN_)jwz@P$(oK%P!Tk9?fRjK88yxhxlcFtTjjZ$DYssSsa#ufYrR+}}nKS+r384o~", + "!Uw$nwTbF~qgRsgr0N#d@KIinx%hQB(SJyjJtDtIy(%mDm}ZBGN}dV6K~om|=U", + "VGkbciQ=^$_14|gT21!YQ)@y*Rd0i_lS6gtPBE9+ah%WIJPwzUTjIr+J1XckkmA!6WE16%", + "CVAl{Dn&-)=G$Bjh?bh0$Xt1UDcgXJjXzzojuw0>paV~?Sa`VN3FysqFxTzfKVAu*ucq#+m=|KSSMvp_#@-lwd+q*ue", + "FQ^5<|<0R-u4qYMbRqzSn&", + "Q7jSuvc%b+EZc%>nI(+&0Tl1Y>a6v4`uNFD-7$QrhHgS7Wnv~rDgfH;rQw3+m`LJxoM4v#", + "gK@?|B{RHJ*VxZgk#!p<_&-sjxOda0YaiJ1UnG41VPv(Et%ElzKRMcO$AfgU+Xnwg5p2_+", + "NrnZ1WfEj^fmHd^sx@%JWKkh#zaK0ox%rdP)zUmGZZnqmZ_9L=%6R8ibJH0bOT$AGhDo6{", + "fJ?;_U;D|^>5by2ul@i4Zf()InfFN}00EQ=q#FPL>RM>svBYQl0ssI200dcD" + ], + "Europe/Lisbon": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0=rf*IfWA7KxBg5R*;*X|PN+G3LqthM?xgkNUN_", + ")gCt1Sc%YT6^TTomk4yVHXeyvQj8}l<;q&s7K}#Vnc8lII1?)AHh$*>OKUU4S;*h>v*ep0", + "xTi1cK2{aY*|2D*-~K<;-{_W+r@NvZ7-|NZv($ek_C%VfP0xjWeZP#CPXD`IKkakjh(kUd", + "&H)m;^Q(jGjIyiyrcUMtOP)u3A>sw6ux;Bmp3x$4QvQKMx5TrCx_!$srWQuXNs&`9=^IY1", + "yc&C31!sQh7P=Mk*#6x8Z@5^%ehR8UW$OWw0KMw}P1ycI^", + "4eh12oBUOV?S>n*d!+EM@>x#9PZD12iD=zaC;7`8dTfkU_6d}OZvSFSbGgXeKw}XyX@D=(", + ")D0!^DBGr8pXWBT$S-yhLP>Z3ys^VW3}RQ6{NGGVJG6vf*MH93vvNW6yLjie1;{4tVhg-KnSf|G`!", + "Z;j$7gJ1ows~RD=@n7I6aFd8rOR_7Y?E-$clI%1o5gA@O!KPa^(8^iFFeFykI-+z>E$mvp", + "E_h`vbHPjqkLs`Dn-0FV`R@z|h!S(Lb;M&|Exr!biY`%bfp$6`hK;GDhdP|^Q", + "*Ty*}1d41K>H2B{jrjE9aFK>yAQJBX9CD%-384S;0fw`PlprHGS`^b$oS-`I4VH7ji8ou-", + "g|060jfb1XcxiInT0oOoeR7#%e5Ug5#KW)nVSRvLHNe$SQHM@2)`S9L7>RL@Qx%fmm7?3u7P5TywFQ}C@S(pq}|", + "eLPT{C^{<0Q?uU&kSVd%!~8q3;Z0s3OqzF`$HRkePL5Ywgiwn{R(zi+jmOBFrVpW;)@UsU#%$8BcV#h@}m$#!Fglo&bwb78aYqOG_W7h{eb(+39&-mk4EIXq_", + "_`30=8sfA3=!3TO_TyS5X22~?6nKngZ|bq=grdq=9X)3xAkA42L!~rmS)n3w-~;lgz%Fhn", + "(?rXdp2ho~9?wmVs2JwVt~?@FVD%`tN69{(i3oQa;O0$E$lF&~Y#_H6bu6(BiwblJ>;-Fs", + "gA$Y$*?=X)n1pFkKn}F~`>=4)+LLQk?L*P!bhAm0;`N~z3QbUIyVrm%kOZ(n1JJsm0pyb8", + "!GV{d*C!9KXv;4vD4Q>-k#+x(!V5L@w5M>v2V5a`B>t(|B", + "|Fqr4^-{S*%Ep~ojUtx_CRbSQ(uFwu2=KH)Q@EBs@ZqRXn4mU;B!68;;IQs3Ub=n&UU%*m", + "k&zwD36&JSwsN(%k&x?H+tN^6)23c`I0=5^N_R0~1>tsFZ`^`3z~rXSXT&qcwa#n!%+Z#P", + "PG}(D^_CCILXnF|GKwabBh*xFS?4rwGo2vtJUwzrbv_$5PO+`?$l{H-jGB@X%S!OAhw;D4", + "XFycN3!XqQ&EorJOD3>~^U%Luw!jF<;6_q-f-S|6{cQDfZ2(4Xf1MMLr1=SA=MwVf2%Pp%VP;jn)|5Tf!-DbUGn%I-rkYaH7?$$O!t)wwClAisr3eUoeB^~T=U*_P~Y2*KdnO87>B!19sV=xZ5", + "yApq26RxgqA|*tmsvtL#OhcF(C<0EGWHP)BFl?h)_*7!{LoJiv%RsOs!q->n+DcV%9~B@RbC_1G_1g6`Yd~8|%-=2l~oGN!~TVv2Bnk>7wW8L@^?vX$f3AiT)(4nrCuTm9%(XC6Nai", + "E(;}7&=YZagjAN$O-cN;1u{dTkElmB0GT$|Wa)QMmKrx<|LCJ9qlUoFsUbD^H^6_8(w<0{", + "ftj&O1~p_%lh5z;zNV&sP+", + "NF2>iK{8KMUf+)<-)VxXbLxD(alL}N$AT-ogNbJSMMYeX+Z{jS)b8TK^PB=FxyBxzfmFto", + "eo0R`a(%NO?#aEH9|?Cv00000NIsFh6BW2800DjO0RR918Pu^`vBYQl0ssI200dcD" + ], + "UTC": [ + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~e#|9bEt_7KxBg5R*|3h1|xhHLji!C57qW6L*|H", + "pEErm00000ygu;I+>V)?00B92fhY-(AGY&-0RR9100dcD" + ] + }, + "metadata": { + "version": "2020a" + } +} \ No newline at end of file diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index e05bd046e83..02b63b07479 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -17,10 +17,12 @@ from datetime import date, datetime, time, timedelta, timezone from functools import cached_property -from test.support import MISSING_C_DOCSTRINGS +from test.support import MISSING_C_DOCSTRINGS, requires_gil_enabled +from test.support.os_helper import FakePath from test.test_zoneinfo import _support as test_support from test.test_zoneinfo._support import OS_ENV_LOCK, TZPATH_TEST_LOCK, ZoneInfoTestBase from test.support.import_helper import import_module, CleanImport +from test.support.script_helper import assert_python_ok lzma = import_module('lzma') py_zoneinfo, c_zoneinfo = test_support.get_modules() @@ -57,6 +59,10 @@ def tearDownModule(): shutil.rmtree(TEMP_DIR) +class CustomError(Exception): + pass + + class TzPathUserMixin: """ Adds a setUp() and tearDown() to make TZPATH manipulations thread-safe. @@ -222,6 +228,7 @@ def test_bad_keys(self): "America.Los_Angeles", "🇨🇦", # Non-ascii "America/New\ud800York", # Contains surrogate character + "Europe", # Is a directory, see issue gh-85702 ] for bad_key in bad_keys: @@ -402,6 +409,25 @@ def test_time_fixed_offset(self): self.assertEqual(t.utcoffset(), offset.utcoffset) self.assertEqual(t.dst(), offset.dst) + def test_cache_exception(self): + class Incomparable(str): + eq_called = False + def __eq__(self, other): + self.eq_called = True + raise CustomError + __hash__ = str.__hash__ + + key = "America/Los_Angeles" + tz1 = self.klass(key) + key = Incomparable(key) + try: + tz2 = self.klass(key) + except CustomError: + self.assertTrue(key.eq_called) + else: + self.assertFalse(key.eq_called) + self.assertIs(tz2, tz1) + class CZoneInfoTest(ZoneInfoTest): module = c_zoneinfo @@ -1505,6 +1531,26 @@ def test_clear_cache_two_keys(self): self.assertIsNot(dub0, dub1) self.assertIs(tok0, tok1) + def test_clear_cache_refleak(self): + class Stringy(str): + allow_comparisons = True + def __eq__(self, other): + if not self.allow_comparisons: + raise CustomError + return super().__eq__(other) + __hash__ = str.__hash__ + + key = Stringy("America/Los_Angeles") + self.klass(key) + key.allow_comparisons = False + try: + # Note: This is try/except rather than assertRaises because + # there is no guarantee that the key is even still in the cache, + # or that the key for the cache is the original `key` object. + self.klass.clear_cache(only_keys="America/Los_Angeles") + except CustomError: + pass + class CZoneInfoCacheTest(ZoneInfoCacheTest): module = c_zoneinfo @@ -1755,6 +1801,7 @@ def test_reset_tzpath_relative_paths(self): ("/usr/share/zoneinfo", "../relative/path",), ("path/to/somewhere", "../relative/path",), ("/usr/share/zoneinfo", "path/to/somewhere", "../relative/path",), + (FakePath("path/to/somewhere"),) ] for input_paths in bad_values: with self.subTest(input_paths=input_paths): @@ -1766,6 +1813,9 @@ def test_tzpath_type_error(self): "/etc/zoneinfo:/usr/share/zoneinfo", b"/etc/zoneinfo:/usr/share/zoneinfo", 0, + (b"/bytes/path", "/valid/path"), + (FakePath(b"/bytes/path"),), + (0,), ] for bad_value in bad_values: @@ -1776,6 +1826,7 @@ def test_tzpath_type_error(self): def test_tzpath_attribute(self): tzpath_0 = [f"{DRIVE}/one", f"{DRIVE}/two"] tzpath_1 = [f"{DRIVE}/three"] + tzpath_pathlike = (FakePath(f"{DRIVE}/usr/share/zoneinfo"),) with self.tzpath_context(tzpath_0): query_0 = self.module.TZPATH @@ -1783,8 +1834,12 @@ def test_tzpath_attribute(self): with self.tzpath_context(tzpath_1): query_1 = self.module.TZPATH + with self.tzpath_context(tzpath_pathlike): + query_pathlike = self.module.TZPATH + self.assertSequenceEqual(tzpath_0, query_0) self.assertSequenceEqual(tzpath_1, query_1) + self.assertSequenceEqual(tuple([os.fspath(p) for p in tzpath_pathlike]), query_pathlike) class CTzPathTest(TzPathTest): @@ -1920,6 +1975,26 @@ class CTestModule(TestModule): module = c_zoneinfo +class MiscTests(unittest.TestCase): + def test_pydatetime(self): + # Test that zoneinfo works if the C implementation of datetime + # is not available and the Python implementation of datetime is used. + # The Python implementation of zoneinfo should be used in thet case. + # + # Run the test in a subprocess, as importing _zoneinfo with + # _datettime disabled causes crash in the previously imported + # _zoneinfo. + assert_python_ok('-c', '''if 1: + import sys + sys.modules['_datetime'] = None + import datetime + import zoneinfo + tzinfo = zoneinfo.ZoneInfo('Europe/London') + datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo) + ''', + PYTHONTZPATH=str(ZONEINFO_DATA.tzpath)) + + class ExtensionBuiltTest(unittest.TestCase): """Smoke test to ensure that the C and Python extensions are both tested. diff --git a/Lib/test/test_zoneinfo/test_zoneinfo_property.py b/Lib/test/test_zoneinfo/test_zoneinfo_property.py index feaa77f3e7f..c00815e2fd4 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo_property.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo_property.py @@ -146,20 +146,22 @@ def setUp(self): @add_key_examples def test_pickle_unpickle_cache(self, key): zi = self.klass(key) - pkl_str = pickle.dumps(zi) - zi_rt = pickle.loads(pkl_str) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + pkl_str = pickle.dumps(zi, proto) + zi_rt = pickle.loads(pkl_str) - self.assertIs(zi, zi_rt) + self.assertIs(zi, zi_rt) @hypothesis.given(key=valid_keys()) @add_key_examples def test_pickle_unpickle_no_cache(self, key): zi = self.klass.no_cache(key) - pkl_str = pickle.dumps(zi) - zi_rt = pickle.loads(pkl_str) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + pkl_str = pickle.dumps(zi, proto) + zi_rt = pickle.loads(pkl_str) - self.assertIsNot(zi, zi_rt) - self.assertEqual(str(zi), str(zi_rt)) + self.assertIsNot(zi, zi_rt) + self.assertEqual(str(zi), str(zi_rt)) @hypothesis.given(key=valid_keys()) @add_key_examples diff --git a/Lib/test/tkinterdata/python.gif b/Lib/test/tkinterdata/python.gif new file mode 100644 index 0000000000000000000000000000000000000000..efa0be3861d79f44b006b876a6408e42a09dc8fd GIT binary patch literal 405 zcmV;G0c!q7Nk%w1VGsZi0QElr>$@21z9I9$8v4f>{mUEw%pB{&D*w+T|IZ}<&?W!V zE$hWJ=*K<((=h+kF#Xjw`qMf8*E#vsKL6M{?9N61+Cl%@ME~AP|KCpk;#L3SSpVl@ z^5Jgv<8S@xYX9qOGGK=`W{@^#kv3_QH)xhOYL+={mO5{nI&hpjaGX4Go;`D-K6Ii# zcBMdgrb2tFLwu`4eXT`*uSS5eM}e|PgS1G6wn~P&Oo_Zqio8sUzfX<9P>;h>k;ec3 z{{R30A^!_WZDD6+O<`wgV`~RzVQp<;Jum=#+*1b_a3c>-Bw{Bd$c<%p6IREU0^XE<-od)M4GXMy?WS0N{ literal 0 HcmV?d00001 diff --git a/Lib/test/tkinterdata/python.png b/Lib/test/tkinterdata/python.png new file mode 100644 index 0000000000000000000000000000000000000000..1a987f79fcd248a94fcd1c45934136919cc0ffdd GIT binary patch literal 1020 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=IjczVPIf98sHP+3RK|Nd%&x0mrdb3^NeXgE;8`zKjPE1 z-=lS#bM0E&l7*Hz)6CK)KokT`Jofjl*UzhVK(0|jJCOW))eK0QW=sN-Z_a^CHp`qW zZ3toj!K1^je{VVh8E%c6%+ewSu^cS{_bjjjNq`0qVGWL+OG*#Ek0`S+6kuj7&jreo3K z(Y(c_ZmmPba-Xg}|NsAYtzYk0xx%hwkyZX|v&<=;t=ruiHabHjtJ|Ce>W+IPA)ZgQ?!ZC|$Z|8gnk%7jDF#&p$6AgEMe;uIFrk^Al-!^-G$=KGCmYgs2(3;7>L0);>u7?Zr+ zU80zGF9pTWS5Ftm5Q)pl2@Fhb%Qk%AND~V)W7Fv9>FVq3?M_c`nE~Gk@#z{`)21Y8cxw21 z>3R1~7n?p&cm0Yf$tzZ^TRAm6KqTZ*K-kLFYr<0lPM-<7e&I@1Mo!kc?98iSNf$ab zuUyT}&d+)EA}90hrRNC-25-!=UcY?%`o)|4x37%M5)Dj^w=;awUArbXC-<$z8%Y_N zxpS(2tNF|^+1Y*g&{5GxRogj(m-%&HD*ImWLj-nwqKP5A*5>tbL0Yt;W_if978W=oX{an^LB{Ts5zOa#I literal 0 HcmV?d00001 diff --git a/Lib/test/tkinterdata/python.ppm b/Lib/test/tkinterdata/python.ppm new file mode 100644 index 0000000000000000000000000000000000000000..7d9cdb3215877b4604de4c177fc81ebb645b8290 GIT binary patch literal 781 zcmWGA<1#c;Ff`*bGBxF5KmvZf2fW&Lc{Fcvsaxw%vD~I`o_WSJBzbhEPuHIR|Npzz zuXn6mVOO%qDu1?F<`i^ANPN%M?QRVlovK&al`gg}m}{Oj4Xb*;{v$qJ`#oB>IoGbW zEm>%pGtDe*fO9I=FWsDhN%DlDD>}Lub)@# zfNlxsKkU=F$D?H{#MwaeAHjtJ|Ce=v>b=@`x;Jieu32qgw$vkbAW8H;-vqNBmUp_ z`+pmvSRbMosQ%C_A)p#$@c%hf@r&cOuaBC%KBV<}pW^G?5 1 == 1 >= 1 <= 1 != 1 != 1 in 1 not in 1 is 1 is not 1: pass + +# binary +x = 1 & 1 +x = 1 ^ 1 +x = 1 | 1 + +# shift +x = 1 << 1 >> 1 + +# additive +x = 1 - 1 + 1 - 1 + 1 + +# multiplicative +x = 1 / 1 * 1 % 1 + +# unary +x = ~1 ^ 1 & 1 | 1 & 1 ^ -1 +x = -1*1/1 + 1*1 - ---1*1 + +# selector +import sys, time +x = sys.modules['time'].time() + +@staticmethod +def foo(): pass + +@staticmethod +def foo(x:1)->1: pass + diff --git a/Lib/test/wheeldata/setuptools-79.0.1-py3-none-any.whl b/Lib/test/wheeldata/setuptools-79.0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..3eb896915897fb771eab71c39814f94c44e449ba GIT binary patch literal 1256281 zcmZsiLyRy?)TP_DZQHhO+kD%$ZQHhO+qP}Hd%oHHnVC!_XHknvolPZA-FvQrG%yGX z00004KvrX#R;Z65uJ3=lwEu+hpG+*BoL!tP|2fgxJDM7snwZ)eo6^}kTZqOqKn)R~ z0KEB*Y~>0)fDhMT0tKKfe;5|AC>06X^!61fbne)Xjo8q)2wNh-sS5&L4y<=&oJl6F z0Pl#|RAZ9IAPKp~p?q7WkO}b_(Q5Jto11%r%9uJpa#Fk;`5iWb7ryr(GLqnH`@N-O z)!2FvqR)C+S#M*c*?#~i{Ez3v|2)$a@Ux&p0st6e0RUkC=UM-MOzT@18e7xr>s#7d zI_vAx*?aV8SjTO(qyF6L3r>62<8WlE@2+pKS(pU?T-Y)&Tk?Yh2uU~3#F9V}okVo4 z|LrU3(b9FujpR% zds;z(N4k26o%dC%AhN@-5l#u<54&7Q8Be5S*t?3u3@&D zRF%4r9fR1dOVY=jX$hjH=RwU(tJT-#{Ob1{O$65k!0H>j_N8Q^de1oyG%4gNR#P$; zn9iuLcOYr^CRMJ=tjH|0*4Aq>_9`ox(ZtDe*w1cFN0z+9$#4>EY| zR#-h);0c=_3U~e-v-N^5MyajxBKg5bbY@rB0`=R!NZ(;iL4|-01SPbUTGu9i|JvQ> zQ$pAxiyt?7-3>9?5zueI=|q%PR_$!B7tc8RH)Dzi9<@r|W(2%YSejB%F8#h)zxUPs zSM);j1J7=WFmPt;Hvzo>9VGZ{aJ+oWyKSehuiun3n~QwvHsUX4^GvKk$^{8@CHKpO zohQ)w{4gnkm-~^S=JQ~IKpH@At!VFfDC3!Rc8SO_1?cSMs@Px zy>Pe?)o$iw)2LH>+9R3N{ss41ZPrGVh7(}IP~K*yTNT&=Jo^4oEQ;Hhnxe$A36Mnr zkAQ4Kgi&6(B4;)jSj*qv!HYNLVQpD zJZAOHFz%J#^$cHlG`I>FNkQE(N2W2laq=sGi{1__gzyibu1uQd>JPwp|pF>fZ^VZRSqL=~22A(~BMoe@Q%31y^b`c^0z|E0p{&aIdh<&y1nK|?%_+Ft@5awIIfgQ5+TrXMe3#GU`zpyetj`_ zt*#oW2dITeegi~M)RXP92xYrigI*#F%cT)r#a3yKXIKVXqHBb^$cLG|P{o(v+XHL8 zO^!AGLSC1}Q-;(;Qn5uUd;#>NpTru#RVJe`8XU1oURewYD}&J(zdo67O+e@<v>FH2;Rwi84xjTftD=p8(Jv%CF$7#81Q&aJaovbVh#^bi{|$53M&@ zIbkE$P~GrRlK@4Ti56ml9R3e5ke7$%A8R>*lGH|af#ob2xxR9mz+9UYFAl_dp?^&< zg^%WqvyThkDymI~KYsa%|CS)eK1u7L3Wn-dL<_La_njeUVL80BH!f^HMpqbd{FWs| zQ!pZJCyxtJfSB|}7%N3)a-yVA#ct7^fn`<}RTgeGMK2*Q_*q9*1Tr-S8#ehjLxNG- zO}IuP_Yk-e5NR4X-C%izRY$@}3p4FG`1u*-$=tpuvGN!*?u3l8xVF%RcoB}0a8Ohg z)gHXIP9-O;p?H31sUtmMHKDCflYhcC~B6 z9e~N9&7%i{CmWEkBdU@PzQav7pl*|YHmRs7rzr&HuExBVT;_1tTs5YqT2dXJzu)11 z-dp;{R0MpS|HUsb~+IZ2|O(8KEJtByyCAc-*2k8t?VIUI;d?G`W z-#f#czMIWywmGM7F2v)iB7Ns=#W@N&3?%Dj!V$o3njVas)?5agl4UZ~{~EPj)W`c< z16xq~iAZ*uo`Up-ZX?zv$keHy8Ey0J*cSl!^$phNyx~_anKE1{GRmbL$&s?2{#>9s zL_tq-6T$fH69KlYXH+Gfx^wQ`Ska z6`|@QD&y#{phWABr`$SD$;DX(_eEnbKKK_`as-Nm-vzCV{KLg>J&t@%H}`W%;oM%s zYJz;XUN)!hY@*`^>^3NOeCQ>8J^rq`va~^LgcO#2Wlk`~^7g&@2VPx`MpuVhm7g8T ziVe5!uFPmUVZnu;lM7#y4u&~6kdUsw&|s3!Lm9Pdegn2O8gb{a7xpq$y?6=v9DI!G zifKO&GWhfg92&F{a_Hf479uewU6{a#`xWxFadD-Z_MK~f_r|NSKS%eZ9deegxe5B7 z!}8>OwWT~RGIvvc+)etx6li>=9+A5u>FG3|A5MwA99%Yh(XVKLt%QWl|A78)o{AuN z9;5q@qiFuq|Bt8aTumJvElvL8K#!=nxoO&Qda8)HHHA3&2^u;|+KFk(*f|NAS+%ug z>4~^GdTCm!Ng2tN`D38}2ixYj#(o%r006$O0Rb@oyUO0$T;I{u$m*wpF>qIR zD_88X28Pc(c}B1|E&vjH5`9NwwwrOJoBJqQ2T8~7Cd=Ca1dK#vAm)1*0w~32i#@J5 zXT5{vnyxA@PXQ?h*VhgMQnY}Jird=%R%Pv!0mC}?j5+&wq2%X0uLwe26wpk z>uYYim}%DWL}4$L`3F%44Z0{(Rh5gL+6Yev?b@0t=$tx?XX0{5}AQe&rf`;+D1heW}GgEd~Ny*lx0$LAEKhv4we5uXUD;S zx=RxZNZXdHF3rr>jR`$%ZSDI6$wZY$W5qqDfIYkc75SA(>W~qQR)92h6Ga-SA;q4O zxYxzrBx;{ zJAd={FQh-&03|K=^pq9ToNq6tPw&KUTkmv|uX+alv#<&HvN--fR&Os)-D%rIX@Wdi z()0k#iLDzwpgc1AfJ{BxN|z3r*sM}pv7jYdgGwnrdA~4jkP65Pc!{GkSOZf&j4eUa z`??P=HF`9Era&5g)TwSAfE+e|wJ+@@m6i|3jtYam73i-}QIoSUmcDcsKb=$N^(E1! zmbgl^wvr#KuFBPR{b}}HwiaAf8}a(ij?L<+kRPZea1gqkx5kwt?G>93HDOz*E~EP3 zQ@eE{Y7*nOpPSFt3ED@5uh+Yq%KD*>nJg%XG2gJC{DXe=-zZ7R8o@nTB?0?5Q%yNfPJw;enY|vKo}&c9*dCFx$0X`rZ#&`b*;(@_^}{tXO&g_=4WXZgW?a{u?*I z*t&#o0;%BKnCmMeXdB*(K){{ldxKODkt;Tb;rQt`(0wNFXCxa09|!T1Xt`7Q$E18_ z)2>#4@JYoXy7h2dcQsizga!QPwjRRtte`{)TvceME=}UJe%>CBpB!EG%yq9M$Hj@8ZWN%Z}#2r`%ixGpjCj4Db^c`il`JdhV(;bmYxl zc{Gzav%Eu~%HKg5GXNSYX7WaV1Y;3F_5q8sO#7>KkwhJOb?P?(8D&Ixy)KJjnM9^Y|Vd-`w>d8#6$~Z6gcj!}d&tCT6llg0EP!Q`eZ; zSh5}s18qNJyGa8i9Rg(bEc5)p{yv_-!=wV>2m-G-p<2_1h;X=>05lK%%1E` zZ2`ax9AR^MV16l0FOU-C{h3i<0iXG>Q+(Xm-OgFY_I#3qoB+*nfkVJA=Cj^#;O?GI zpf_Fj%g^5aUK4Jr=+$8$E^sU}C$;xAB);8Pu-G2k4sqlp~h4fcT>PDl!pH+?~A#^J{aG8&hX9kg|TG0zMhF1mJT zPQ%H8BEqALg@Fx5J^BB(VC@67Rd%{tpE@n74l-XFulc7?nMrIJmx;ldl2*RS$VKhZ ze+JLvKf8kMXTN^3kmibY5gOO*Q;AOW0GPSfzLtt)N&e32ej(02aJeT3i~gJ$xE8WT zuB*tM;_(5hjija-Z;-dKf4xmE8BGT2b`>fp9Y=aLfJzb(eu$lDqCW^6tsTyTx0ZiF z56Kxc6n=W&4;@}-lJ0uHJ|~v=2j;VgL?6Kuu~~!uJfBw1tCsxio~90`7SzMcL!p67_vuT9Hs1+?9d5SrIE zKBpB1IlJF)c?Gi2(%_8_$$RDrp<>IXh@i|a|~8B8Lk!zG0WhQ+0BZp-#=-@rEP9HwAyAr-x)G2 z?hf4H(GFb3CV{O@?z2SN&CZG6)4`z;JjbP(g$@OjuzE>t4#3w-s%<=JR0Sa!#|!vxq=2J>OFv3BTht9@H%2=r^v0Z*H_FKb5NyzY&mvlF1~^)BFJkr zA!`<802h&<`lAG(d^yaI*n)wZDaYfDne+Zb2)0xK$-E+UX7zfAtkG&a1z{n>PfdPRQ<)~H*3C}{fpRDQtUCnrH z9_T~Eev?TXvYT#}1?D{2;tRg;ahO6gGC@j@f3EoCC`)U0FDLzlP*RJv(W<)>Z#qOq zyY`QyN%md=jMZe&f5J0FR-3x8J_e9AexusfvfX_=BaO0 z7K{XfgHtN>B7TMd6E;9Dcj5i|p7|jDRG4@ypj2J9M>B@+4Sx!UJ`CjKW`KKT>MO|Y z`Sq*7M-=6k|L7zC+2?7aOcIIny{F%yMib^Wa+?4c2!!yNf)lxEV9i5&Hro_CwbWuG zmq*E`3iwDs9%wyYR<0|V#_kj`*L=n7gvG~hA#%#QNuz0Hc=oiJGK~{KB0>voCgPP( zG6od+P+?{r;5&i*d2(j#9D45z^Lg+TE{tGu-@}-L{7$G; zwOMHJK^aS@@wy7u!3-o<;#dPIs8{lqcq_T7bCbcsE95cflO!xR zHB08Ii@ACVs)XPPk8xGeM~grQ1}WCvnU7BgngJM`cQ;*QTM8JGH2TRNlZg!jXc_1&2H zm>=Ko?MTJ14L_y^ET$bn)G5*{Z+6LVXrlH|Y5Snzl6)MRix05YZ_*Mn=0ybKTs9a_ zTTyft_#Ob;jPxlxi$Q1~uD-5|uU75q7J^;?zpKXY(~J>fD@gc({9O7#Fn^F0&hM}V zfSI4O zo$Zqu-gu@<3xhcgPUTd0v)9W+RyRr4Xp?_ip0+Npfiv+GL=Mj1aUrQ|iq5f(VB%i4 zMnSmROe+b}{g*qfCOfU=&O6zaFF^@=xdlp;^r9$0GO}#+D=54meF2~;=68rY=))1= zrUxBF>D*R*+J4QAU~(QW-yJac52u9{7x#Za(p#oCwljd>Nij%3Gy+VjABV?7 z&3e~zKaL96SBk!*s#CH!vbb4okCH_<3uH;;2a3F7NLSosV3(DCajZ2kSkQ1AHcAIw*0#m6NlQUbv3P~)nb;##{DZp&5I$K8 z{;xg(hygQfYM#4v0oafKl^v!xjGK9TA{s-5Ud{Sh7>E?{9oO@o2HKzmc~i1Jv9KcY z_V5_nckkl8XUlP{6#C&Vmschfvbtf%4a_M&{KXDwN#dbDkVf~6+jY`BR(LFU$~W{) z#DSi6fxL~=x-;$F*Lm0W4Q4*a@_c>D9=v>b;TS+O=THP!hn3b!{_Z2@fqq=W)sgbH zBg%LEYL=-rN&f~Q3K=RR{aV)Vbm4|m1ZYOxVya-s4sNtjZ2Q-(sO#POJP#5oNR)>gU0cB7z0CkS@ol|e4IAE=0i}(5&4Y#K-;biI?;2>*7wz^SEJ%K&94m>_L%$LqE@|8Zc(^|RJ?mG`0D+8|)-6Lu7(w|ptecX? zp6y$9MWa+k;J45b4pZO^25QrJSlDzA9N1OI;J8uZW=q$7 z!0w`jtpeOfqs>(+Sh5P7?Jj)9Kx}$0rP#4l7ptmIe7x|vfdLN#a=hVa8Z92-h{dg3 z_dsoA`J>zbdtIGD;oUFfqK^-eD;Zt0b-qYYKhHjUi*nUjYA?8HlltWIL?P6T49JTX zKHl?ZkRz_lwESR`*sA^XynIuU?ly0Yk`+xu^_C4v3Qu4=_mvi_Mt!V&#NP!85 zyEu-HQga%omymP1;C3RK#J(jie^u(P%3>!qAA6E9Zc5q1yj*5uvxB@)%vA_x{MS!P z3#s%38@tLhN~q`KF3{;? zQCt~nPFU{LGc86)io9u&4tIk~P^Yy!T`PXLaJ7fmi=DgLChGL*?4AyzUWF-z_ACu1 z9kP3U)8g@ncef3bENRqV;njD)c~Z;G&nQo1n63|h%;a}EkxGkC{`Y|Ym%wwJ137GU}Fcf@X-23DCn|D`M(e5sX z%Kpp;#RxnVxNb+mcz;X2dkEwN6N1T5hV)-g%GBVNV*>#la=(^7`^)JiS3)>UhZjV2 z;Vw7to8FG}&^*1EJ>#e4ul={O)E`dtBKXmOdz^Upla1|R4=%t4`64`wk5^`AA$ndT ztFh~Q0uHE?)8^5_fei7dI@k9?+@!7fb6wWT(_rqY7(J4?&a%HtT;C?L z7do`@m6`JXA$p0Qlg?r4sNnu=%Ir1Hc_a?OS=91F*jaO#k21y1XjoU)T{*kzhig{1 zyK3{VSXe_k=K;Yv(Ru{ju3hWSLc-8qZqX2togsvWc5oonhshM>gxlZn_q`ZBJGwpq z!iDeHOv}Y$Bf76BZYe`Aw?R5(_%69u!^uy|QH4!5Y%kP`W&ob8$xt@zOsKRF&)z#d z!AfmX6p$|s5XWp0he!4=muzmo9{lbU{Tj+%h&i+_Be5_TPmtv}2De-FY@r@LJ^-AACUbpTs0z90Xp zIcl-i%b$w)i;Irg7tkD0It9HU#aGMXmgc&8_QC0aP^R~M5vJ)o8OC&34#n>Xf4HyP zs5#OnBKxUh64XXi`L-slMq`;FjqL>-W53q9E&`qXye20>&RIl9=C;rq$*-YE^KeK9 z3Nc3ye80N4uAdy1d6qG&Ap}!72Z}wKuVq8WNk=U)P#Ng#x75?i7B?9>hLEx%trwh>9OtL^R~=?MQ+qv$_yMeVZb6aO20+uz zweR;80MsF*jRmNmQG_Dg6W=F{9kc0Auf?jw1O^2WT=zRs4+ro?NT7H_rm4F6LnVbs z=<&K%J<=*DkirnEw@~)d$u^%VdSyyMl4NCyD`X_g^DQgOX_{P_ZYfk_mS%AvIO1Gz zc8FKOBjW;pIr+`W~+HN7`K-4n?MPO;WLjc{o ziMqMe=DWB3>jz*9CVtRH@JRueu^OE?z8E5%Fdk~jzd0+!BKgOw@t8yxkE11i3X}^S zz*R=owqv&>Fns(W+Gn?>MI7SZsaEq!l$lH%=Pm=|*bqNLYv#a6K?7PTpuX##YbX-q zaY5zG&nUW~;Q*dGfpt813f-@nw{vhC$CR73kp*zJ0fE;0^6Qps3FBL;dr4?Nj1tt@ zpVNkU$Z$3+0DaYuos=pU00A}T0)W z^W+=|nyn|n#EwFlx<=DwP!<$RE7MM}kb$0!ri>za@b1LP$Zdjw3=FIB*VHAM3NflK zmN7svzumcT_jpkrR-)m+@?6rcxit~FF=kN-i!U1u%M~-^T;V1@#wHEkrAq^)tKSq^ zatHf1gMDN7(3oN3oFGi%&8p9*`Y570f}{-?`D!*?M=jR{E#B~5FosDWIY4%mv833{&r zvN~ZoQV?Y}e(-=%1Aqb-I8Jd+$OE<>=$XeD$e6hHcHhVTb|sA=Ht3lyprgBEWlm-_e6(x%$jf!_E+Werl(2e6grjMCK&ASr_ELec}Fe4_u$IZ6pISBIL zxsjqAqv1I3*f!1BN7P2-wW6AQ9=1b>H`(dWS$*Oyap_+}UzT&tueh>K2*M2DD)ZfN zdoFtTlBT4XiO~VCU3d_4-RPE9Mp1%z8~!W&B0b%Y*Ya3nYRy>~38LvY^-4 ziTtttDv3mAM@%HZ>VwlbIUi%ppxvw!gMGbO25r=9fbEZF3LWN#wsAyk2<2Hg@Mn19)2KGB<6sJnE*;6Ye~k zSU6k}hSgDm(cU>m=om--bEU*-P39Q_ATG-Tqkoz zv^wlqs0!}3K&~q&8Eta9sr_`4F4!?_pBg9XZ_+k2*3x0RH7If|6s{4Qq>E}b8N@Js*I}@p#YAmtG9fF_kY2^2bBTg^#6(6*3C1XT##xcdURX{u?gcb>? zda;sDW23Nf12ggE;N~&ck<|BTjwn9wbPiiv%~{DIzJx3chbMwL9CeT>D<$p*6G=l; zGici(ZqcC@{I?lnaO5FB&IVh?<#-`GW!SgQanHpNZ{`B5(oor9+@a8Fe&TA>{n0ke zYcQJrNSCoFd_4p-iQe|nD!L~JXLch~D=;R;($Wm|?s->yL?I1f z5`?lI%>ddY;mK_6Ky3PriM6^lay`MV#^+lb$*P65s2g+!NzGwXL$JzvH&$}E=Ga5a zrr%K)5gx)4<|s>eMqyy#ZKV{1wq9fDh_EP{%?L3Ge2?{dsx;zA_%YQ9+#U9Q6eVtIK-p_1Q`T74gWf?s%{f zOey$pobLe`hcxV3-k$ZATMQHPCSeg0jKtqQRS_rp#X0>lLB@a_BoM#}m+5nEiDI_$ zQ`q6ZHmDqe4X8^eysYd(7NyOb+cf;C?jf(yl_>E2<#%*3`^9akt|Lc6O*a?ZE^9;S zYsZtg)B^s6LH;hr*I*Ma1+zaWR8O@@J0%FWer?qtuj!VX?Hi;`PLe8E6jQiI)6yxB z#z#yUB|{w)6YPAPWBO4k?65(%xF})ZJIChH&r5z*F~fRjhgMOKTn{#*kVt^J9Ksi# zmPX+lGT_F1r~B(apR6Lugqh7;Y$gzb5xWDm z=*{vNG~#6B%<3bfeK%H9=_>|-Bk5J9)P<>kl>~C2-J>R^&oy}EVmG#41%tA|Qz}-$Xdg-kDGM@0I*eXAV$P& zdzP9OQH4ag-FP33xD~)crw+7XAm$b(jl*E8WpMJZi$kZ79$ikE4RXp8n3c1l%rqw7 zJF~O)1ZO*RhT-plxQ2)h$jll987S}r7SMV)F3vdve{;Me)CT+9aDH#o7v_BCZnOeG zbPSxdksXOzu7&GpxNEPJk_*UgG$Jp&2~niTou=I$TdPe<&qM#%DRe5bBv{Imjkka}^l1uIXNrGC;m zLV`W3sy=mZl+b1aeWy7PWGH0HBAZ+>`3SXXz;^iK`;DZT_5k z6-7E7%R*d7@$hP3Q!h)!FYeDWE&Di^OQoE83O(4)VEJT>FeB!?$Ht%F_~&GVU^I3y z?8*cUs_CL#VmMDfGD&5Zp4J%;YcB$0H} z(6s-IAD-U47Pk7(V&++-qH?9g(OvAfzdj+w4jP<+wU9~^X!cN|Zqj3{lw%xkOb&i( zd1$pNS_L0>RBB)%z5nSOEPZEHXa!1Nov|o#l2eN4)a)l$P3Ipmp=~;JvYoip0{|^F zaUX4y7&N9NRn`z;C-aC@!IQGo@C?w6i&F?YEz$vF6 zt61^w%m+moGL73uB1Om%_oOHoOiDlazzdKKfSDN-EMT!%gs>5|<+EZuA=CgaMr)%z zma^bEpKMl#ZGLsr36Q;n^wcr#fPbjJBo=&=S74(yx2O@iD@#X$9PJBoLG$JZl7a5| zVjA|m+zsZi*w$vf!{IM&XGW3B%^w)LXV@Xj(2QB$lugm<_5SXfv!#zf;mz*>Yv{C+ z*>mZc+YwKK(m`V-Qt~BKv&lBtZ;c#Jt_kA7UOC673v zC^R`-I0J)@_!bDTPN&IRp{&+~1M?JyU|j5tnumpf=jDRuTSXEHwTV9#MRKsUj}_FI zKu=-Q$cKRQlgQl$lhc0EEr)6`dNE!x9UpsppTPf2317K}uZGhX=5 z3f3*Vy$h5>`=tecrqV=9hlRZ|H2==6t$`oo;!^1IlZ*eh4ke1jaj$Cp8~>&6zGZY; zg}m}?x`AQn2dr{3aA^m8w!gL9aoiX-36RyygCQCWL3u^*S!R0Yf!4}PGDRY%BrA&^ z5+ouBcH}}xKLwTi)u9c^(&LdMLJH-aAI=q&;nlIXth5}E`s5upx1;gpT--`fS9Aq_ zJ=Tp3Rf}kkvEna)_ACYu-iMARA()mtM_|%Bt=g?AzvbgL4vz-roT6y2NIq`F{U@q1 zRKu9-k{hphE9>3)HW=|sP)s}*Ae{wqB=!RIT<9DArI9h*k&i0XQACtUlXDaidi-8W zcg8JHVB*%J4P6UBj*L_dey-R=VWam6E*qka*pduNvAov_=S|FecFKfgM3 zD|XlS;ip)qCFjg3fC8V`Do6JOcX3PwTi3U9Vjf-ezLCd4XJ{|9b087#`Ju`?&z%j^ z`AXrqq(#f7E&$^rhVGjka*DDBaKyqL=f??ME@^<;aZj{5E0&c0B3x+NM;TKu)^he$PX^?daPCypU(oV1S8%A_U9Fz7olxE{=7gXE}yceKu>x%afszVc< zE!i3z`!>IPpq@b;w|AQE8fpE8gXii(1Z_+NxwO#7C1J9zs|l@#d$ai0{SR#yQn(y? z76kAH3lE4F}$dKzZqU;M4S&g zO3d6>hS^9qWpC%KLNNLTBDL`D`euCLG|ZMse@|z5eL$;zrv3tI2Ds&`j6v8ArvdaV zOiH-5+2a;22XzDcW%3T*zqmuebJwmLoyPJRic@F_x29S_x}hx9{@{0x{h#in7l5eT zH`=`wWCXUaLpnrdTrH-CS(+eiG|n=8QTSK0s=?P-@&@&@j+>LFK6xyY> za-(tH@vU^I8NPZw2n$k@qSKMpQ=qfinUmNjA%-gQI=Q4NQD0MO7o`ztq&YiiO-XY# zJ>yrC#3#^yO7y3-v#BJls7yxY#+Ab>wLOj|&A+RUbor+hyTA;Fn&4TTY#-PD82g4y znVuY3g=J8-#FHxwxbp25Wx|1boz~cJx?}Tlc#f>VGE^x8zXPz-rt?mY3#k&g$`IA& zYYj6(nz*!72Q`fkG-}S~M`WYGZUN`x`D$9i>ndh45j&H+1XfB%Fhg$B#(ME}P$M1` zmj%5Z=PFk)vIF@rw&Cx(SVEAmF&E$ms1n1}+uO7wHq(tDs~++Y zecX#l2-y2#H|Oka_TXQ%W7qV~o<7m!MXZ-tUZi+hFlVB@Uo`Eo&!8*Jn;;hQc{$8e7MG1yC|~w$aNM63VLP+ z>TIp3HtCwHV!Gx@o0%lY;%NS4yVchr-7Chn+K%Y$2DBbaJ(N0D__mT#WZX1N@4?bA zf45rv=*CCjP@`|X_Oxb;gnNbrLMk2}h$`7GTxG+@WfiLvt31;XVT z;3?9mJ$-ZN+lOg8j^)x?vC-D^r_f#?7+d8OjzA#bTTUMf<#B6sybk?ETG^&8>4_ly zArPS!=WL8F=Mn5=fCQg?5!sp8x=>O$H*=6ov_&BM^*TJ2ZJ^jaRt=x-USR)j8VS`g zm9-!riAY>G(Q(v`E4kj}&N((c7AKqZ&#>TG-q@i8Eeqp>!B~$?AE*0>yf%wi{&@d1 z1E)^KCv!Vd>hBV9&)-S@=Q`JZ^$zJFPqLJew3l!%_t^$w=2hR}gHcZyKi!ztLLJfRKgHCD)H8-(?UQAn0ohtW8g^ z8^w91ihhVSz&EzFW7pW@b5Xg1XsudT0DgA@EG)-UBGIO}|At__FWje(6(8F}fz?w& zy3{1R67i<6J=l@&UL3Q%^1`==>ACThs>^L>;7drLN|-JD(Uf&fRMn^%EKp6%=gKYV zKA&W=wogWtxIYf~<`#jN8;2A&ZVNq1$fA_=s{P=94<}u;WY=U}k8$I|Ug+h@0UF8W zy`>{G=@okJkguZzmNd`%#6yww>eCzf{<@Moho192!c=w*Y^q@r-?(du9_N1le(VcZ z#CstcTS8NHA<5(6EB8p<99Z*T0*&-kyz$&&OJBcox_@*&aK3gzJNkG0*tfI)GvMS7${#ZVfr4vOYkU^TvVQvJpS_n~7D8Qyaw^%xm*P5|{_=^J*h z=&_x9N$J81V)hp&IMdLdDw9Ez$905xIcn>Ll}z_lk2$2vRW!4QN%`(+6qa#6{92NH zE!>4Lr?Ns`_ZJ?Wz$|SDG5If+2?_jmYEnl^F!kS8jJDU$>ObHWGL@jo3KO3C*d(Gx za3l^*6cNYP7ZMl-xw-Z(lPFavoe#)H=+E3Gw~_0N>$TQFgxO#Sfm1`mkf#7h4VyxX zi8zIe{W1_>HWkklpv%y_vOYYyGUGGzc4Hdf5B2ZwlHK?<>jyO9f6~=*L2UmWL$uEq z$LOUA!gsnve+?3AZK+{L!>KQ{rfY1K!t3XJAm}C%x_wd?q3^fi#(0HED-DgLtpE&) zpV;y!=RL&ND9#ABiSaQg)H#S{{ORhY@eg`3bY=!KvUa97b>tWV!?=GvI_o z0+;qCRYy5-PHkC^SHOOy{-c@<5?GeiYHAZbFV^&UR}~L)TzA5hvSqweT4z;VO3uQb z+?dX|qjlL$*)?vNNvGoCZs4AKu^q;z1Zy;aKn$rw<_4-Rc z*NH!{VM?^!AkFKNul+LwM+MV&yER{Efkex8vbD@g*s+cKt_ihPI5Z_nRb(4!cX|zL zj_CMwc4J+6R}HeAPwQVep|G#oOL&OZ!jn`AxfAChGhW505<22mBVStmCLp%z)15ba zJHeg|a`Ew3yK=qh+VG?#E$t*fxx5DkyD-3ok4B@aY%LZM^R?c6mfbi+MS!Tm);h|* z`4=0uarGX5Jp)@{EqDku4~-D0aqAqL2CCLP#3-5gnApfx4$LzhZNi0&s|e3WYET5H z{)7=SGhG7cAke1U#Vy~UTY==SXZLWsixZYSHCtRxv;%GmVr(ef(>AIh6C2PMR}zTEG{01LYW-QKgLV?LuR5I78_fS#7rhG*}y|) zfS<$?!mkLoIv$SA%VQc@lS@GjEX#Prjb23o_-}j3#ZiDyJ_l#bDKfMNZmz0HM~Z-XZNRPG)$2WeZz+ zBtgIkp7S`HJNMz^NdHx#(ha-DY3hBcJamszD8bZc(P{1&!rm8x&cYU7$gH(giHtfA zuEm3JX`;86NW4s}8CO1!q~F%dtfckEtR|ex3-ekOI{?qKO8sSGE@a@;*mKAzf1qRe!a>tI+rQCLy z3iOhgfM^vXdXP}VCEi!xIAvbb-L_2YPefXBG_Aj5YdP>DU2yML+2R^mOWNGq$KtZt z^AptyAA3rAi0Ts65M<_OY{jviLk!{H^>zmUE+l+1G*)XQyI7A5kNE~`@}l8d=x6)7 z6G6Lys>WmIU>Q0W%G$Au^3z0C6>J6rksF)Cu!Jk)1Cf|MdCK2wtVpy?w+cF(m2wKd zU1UF~GDsrcoGtG$Lqoo5<2bw3HYd?Jzy%)O`vFftI-RsUY0{m6 zY~Jkh@VlfK!!_*Onp#({lcD02G)-AdATh<;U3r;&=9|POmYt$?klviW0Vm~y?}=od zAsW_0W-L9$Xt8*=0r{$i+i-TMtgEF`%0(IW1z>$J#B(Kg$Kyz>3SL{0^W2Vwk;gK(Xf_d8ug@~Bwp?Zs`6j7OwKKyRXG+Tx z6{b&EnQ||O@X%_9Yu?+XS;CYCc|qGWc~#al^1VvPA$Mijiy8qmmA~Sjz9^h&oszvb z&(Sh<#A%EiLBOWCsKi8q+;LQ`ChJ3|?F*{=HYJ$|mw*T4&W4&zAwj|R!M z;_AZ8VwS%T-jFgjZPq)y@dbK${=2-8HpfAnh*t6SA%AbfCl!OMA|jgZcZtH0?3J;C zF%;bZD&I6Xz+gds8cTemh+S4{59F0fqY2i zfw)IHB}`7$%QO--w?NPR`Mt^cz7BUQeDo8uSkO&x&+#q71l-?n#*iJGeDFyq9+vpJHOPw zyr-e$aNU!N6~HHv863xh6$kOWKAIL?kb~fF3|X8wsNA<(3rrqK+R4&Cd!v%2_DgGj zpEC$e5mq>L2{&;NDD=+Yn!c1%bU79!u?yNsuc}b;Qd`R4LEdp(VgDrU;v^Vr7ml4} zs%Lb-3D1v|udKh~!zpQhZgn0v_|aD#lWQ`Jc(HcSlSyku_W+({Ib6!xJ)08~$^o0)o4 z&cJJN0+_f)u0#&gw_RWCtEytr#@sUf(OqGxY;2Qe)g*;BpC7R&0eKw9sR$G9vO7O8 zg&+fX+y|70X?gyuCXGin1Z;pZB>Cts)?0LJ<=P-R_7h!|W#+0MtumLh)o5(4J6}Ht z`8ZJ#~pDKrVT7mN~wIB36{#RR4TY`!LT@ z*vtBQha7=+jJmvR!83HvR(45YU3%QMBg@{Dn{Ph*(X`$+cDmq?@63 z9T{_bn^md9>~rMMG+LZwFmqPo>bhL71rKR6 z4iWKL(Q6&0jIpAjm^^#fbU<5rU=ey2@ZySJ)%*=HXEjaOMukOHLZz72ID(#B-&P_` zoXipcJK+eH-+pb{pmUK-mz@P97KQ{F0fbJL@ZW5Or_B`t+#Q!3JnZ@?7Dn&FTHOBv zO+d20&0E& zFD=V)mzwn~sUFxPnkyU!-Pb7)ceAMYu$S8$i~uAb=Ew^!yL42EPe$)ZSRALZm!HC_ z;)#J_JV<3iq>+}EZZTOe_lMqS;r36YfMNtfC?7^;!g1=qIgUtfUIU?pHpM?iuqqRD zk8FPApOyXu$Ix2Ro{Fr6RLOD{h*U3{uTkGUrK7e9PYb&tJUe z9*|vEwvD>YG?{FS+$erT&K#x#a4cd?DK!&MGQuZylQ-M3ClewV@zgPE?iKj7GNel^ z4})YI0EV=0MKPj_O-72oKMu~AnuYUEbF|fGD^>5(gL4X%$*U-?uIU%6h3BSAZjdQfI(f2srOBGj= z>{)Lhinl9o(d^FObxl$?$HS}TKB15vAY?j99HE-ad%4%fZRy4eYl?`ZV}9cMLgQ${ zfFv*H0r}xaWX~x&hmC$j1w<1M&e4`Ym`+3fH=RZ~;Dnhgeyuku7k!7Zm@Fw634o#l z-$n7<(V@GZ(KS7DZ6ilmct)NWcbNbm&l>;eXP}R7o9hEn#?#!aw4YwZTpN4FY$=u5 zDbwkR>E%>74I$4&caflnkn!AeOu%ejr-V+hW(0fDAq@~SAqtpIMa{5BVN0IdO30fR zi(*~4m|ZL6wS34Te`PLZYXx6z@+Jp8sPMyO`#Q6Us^kdgNEXR<;Pt4;dGx%-VgOq~ z1<|xKzJx+Dm!o6k+G|kD0>qkug~!#_qC%rok*HvMkPMAj2MnnocN&9I5D1ifQZ2g4q?IF|4Gx!zyh5e#Ru^ zFQ{eAGH7xTwwuNTKJ&r2(7uG~F!J8oS~Z-qEt*`8n9)z2yh2^d$CHGK%T2|I^lgAt zO%UjU4#pW)Mg*#xi3U}>GBXyKllKG5!>w^)jp%Q=+p})l*;#>3yx3q3BHHE&%0-L9 z|)tgTXoD+4>A5G4_4X8AC5}Jxg<}*gQL^fH_mI zRhCzr*`7t80b8*Tj<2rTLFx@hrs32~iqjc}0~?O9{|q}5BPiZ|Krh(A(NMu3js+pE zaJe6Nr7`l9!;@&l%|*GULqnpKnbu5JOV8t!gGj3zXv_D9Yjd-O`nZX9{6jc5DpY_9ZUc7nyu+1^r@^8TB zZR0OLu$O~-!T(UW2U!g4KLYcM&(eoqMUVl%2dMZ!BwQDZ1rLpazczJpkxuvaw)N&VfV2yNsy4Ev_gdCB8L&KS=rVZIFuN zq-?;;!8>gIo^kn(;JqK14d(%pf5-P?-etvn3}+!PnzfLxRBvI|?l=AQymX-6>Uqg$ zGR^R(wDV4?-mxe;7wWt^O7gx(EqLa(-#|`iG9a7g!c_qDC+7(PCa02~hIf|hnZW3DZnD%G zpo2iJB3+af9FpPhGWbH{m@{%PN@IhO8*;amB6%O2Q=yro&5pGTo2eGAbAnRP{SzfF zH$D%No62mEZk7lELt3xKR%x<;QOQSMoYc56a}~%%;mkLiR(w8(t1TXL(Eku^a+}4j z^^+cS*>Fh+mVO_usKR=fQL9IMGoVC#`(Dz3d1TOp6Cc5%kOQ`J6n+j$tT6`Jg?i1w zbrucEih*1Sl*1f0M$YPq<&n6YpFjSLlBL7YoDcEUmGh_woQKVG&b%Fe&hQSC@919` z?7E)Si+gz?Ph^=6lk9u?IpWft#;jK65 zRK!pt;Bb2qu;Fk#_MKAhbam}>9RC3SRxhK&B`0*zlL$cdjWF(H`f=m?y2Ld1CoejK zEbEkRfKFbFOvbp5YvVH7Loa6Iex4sU?!dXz!z}9F_r|drtmh3L@zivOneVghbq+v2 zs&gE5W_Gx~TcCz_(2&{6R-e{WGC&o)zL`h_vj;O8a4ct(`Bi(aL?+@FaqOTg$i$`J zkB-^fQ}-USPGa>T!{u8L)q3Rnx^!G+^z8{~1haY$qS9u4dGOm|$3y0as>J8dsFf2b z-p;~{uoF)A%y|dM-Y8~B&(jqzE%5XxFSo(_>!#jZEQ^>*qJPq@c|sE3c;;_mGa-Q;$@dI0z67k~73x_Ain4JTj-Q6w1O>z!zbTnTu% z&_F&K8~#Y{9DuoS*}2_!4KKt_%p7gewW%`9K)za={A={3~4>imvWAoWKH4faDSmq4sS413jUUC2L7zjp$Vkfp9j}mi%}*7|Z}8u79~AdZzt{JcQrY^bL`GrX zv##FBh~7Lz4MM?Ffqr=kpBCj^Acf7}MN-ci#72pRq3lx}lIe zKS7&>qnn@|rI-!J0f#SUBLtwxTK%~z0GTu#WW{`sQsQmVIu@BgKb-}`IgdJ)k$umt zXFy2!dEEU;6F>`x;d(?1-`mh)B%snr(-5_;rw_re0@jCSCl?xllD$k~X9R8i=k@x; z=#9lZvi;@Gc_n^Zq_cB!Gvg|;smz^WF&FKRWB?U@pvI+-u>V;j^N$ldw%AejuNmN_ z$>oV&BwGEB9>duDr5gYZE+5C@& z-6I~`@AUTW8Sp(Ey`BA1`>o-NErlpGb1`X?J9s@)7vgLLMbfnko|j|Uu6myL+#R;C(%(lRN zzOLoI0QgZ}UR|#bHt5>kQbHc>gky%$41fA5j~bIDJ8rAb;UgN=;#>EZtq#{2amuWs0|Ao)GG zNq0uWk63r$Xa{oNrn-QUL@?$XV?Po{PfW5B!IOUGIoQcHPADLn0{|!#op#Df;y#G$ zlRq(kFIK(`#^G7=6o~ku4NN-Hs9N9EB8)Q`{PdGbsne;iBom32SF*oxjK$!?h$bKz ziMWlD zqy{Ixd6R=r+X(GxXsKx~k`9^*nr7ZXn&v5rq1((6&A;WC`c4B3@5vSF+)MAphM<|X88`o4@~>glad zj_9dd8t%XogXDGZH#V4M>M~218C%Fr7OAT%Gr5sBQ#&d1i}S|MlQ(6v-sB4vL=rE= z^FjA_n-zL9r1oWgEfoGqoXud5fZ#dO8^U`)sU5U4-V;pVUYJz1VwgfN@e53M+j3n) zjO2pZ4Pd4>#ajEB!V)eqRvRc1?>bw_OI#%S+EDTi604QiSa%4~TStiqz@YJFXcLk} zGUOB*Javry6|!$b6M@4B zLZNZ^ie~l=5EeT&3@5SY7#(4li~}4+&N~Nuk{~W%BL-VAb;wc&$a1yb-uDRM2rJ1n4iX;4R+@UKnAHb3v2!{B zu5AM^XJnJPEjUUOM7WA;m7m&h%d)yInv#?8p!HY}1^EI8Z-7jR*@#Mtejw))MXc1T z`X2FS=?XC*4PTL`l3esKBS3>YR=B{!dwDU>6U}p#vUJZaU@T3;;RkXGef@M-az6|Y zhjqoww_BTc3G=~tOYD~ZKg-qkc~xHGqK({b)!_SLS)=2tevbKJ=r4?)9E4RN|5mON z(;&Inlvs`2HV|5v;smD>#RI4IRMNP>`kcHtBYd4tC8!Ok>`hsdjf+_Scdt%4V@q^s zNcq!#7D3|~(Z2AHOgCl8hmr!=6F32TVxGQ6K$Luzf?CKYwOp2qyfGF$fFoBOHXO#j zO*b7_ew%Omm9RktE-~bPjnSGi2I1~HJ`UBuJQiT>8D|m-dj&mL%8iK_iaC8fcI7Em z%%fM^6$UvOeK4+pQ%i*~7vXgEn^LfuOnqCFZ2 zmMFsHTzp7%8g}m?HOFQU_>C&B`;9}^E1B?TYpSEv%Hf1Gh=pE}MC=BOQR_&535%;r zdxKuPr`n)~DudTsa`YVIl3pVvDJEiC9PXfX*BH~!9wwJCm9=9!II*a}xnuWqn5?-$ zIEM$Ft;^wZ;m*}~!4Dq3RBKhZjJTI@bes67+S(bel_0y}X5exG&Ah8?Z*t&FOOF`LmkIaLEv>GH zBW?Auuv=)ixe}y?D(xj80@kWfyK#P}D59xtMYAO}@Yc%x1fy2yI)Xs{P%HFbsl)LDq;rS{ufhgT`ytP|Pl( zdnONs@u~6PC}E3n6rcp~wt?iZ@1o&6MW2(QV8kp_D5gX%aqtpaVwg_5x$q1T`=)@8 zsrwl_=2TiWOuxr@luU*xckZ;Z+4zsN;Q$fg?|KiJmQl<^HHP7^`@XhVYJ`VIND1Ac zcQ}qnH%<;i>(-OD1^imqn#;jEe)qjKz8Wmz`TkBrdq2?16D38Hg`@`kOBpPWtQsX! z#MsrGvJlY|1WG$DRe)A)v601Um}54+|ytU3KDpjgaX zAuW77!c7Mj<9`5|7gFN(hja-i3&lHQFL3hMs zj(4|(vT1W;Ph1-qiKXwJAA@I2_B{-#y9wgKCm3l32gq%~g%&Y87U|0tf}ua_iDk#P zu5n5Fyao8c@f>>TC!M`PV-Vy^wsLWM-j8-CB5yQjm^W1|J;t7!g-%V980SoG9p&XY zjZP*NcR{=hkR9$$CkC;e7a1JVSqtYYO%vNH%E#XUdFBblFi~T*wAEsD3v=+n=}4ws#^xNB3} zB{I91_sGxrVe<5Mk3ZEEX(`{9WdUuhcpKJZ(G=)4J2Lh|_qwY+Niv~Hx1oJ1HU}3nayR$!UdShZe0lQvID7TO>FJA? zuVZ}j#j76PI2^!eIq4N&4&wJswJ58bflcJ+*kmn1a7Ixd_Atp#u)y66{T_qm0h`wc zNXKLlN)rmJ!&LCV)R#q07CcVFY)mrj`fICI50oiwkPbUtg_?1`Ff3arDUD?KwKFpe zFl4!VAx?~EdnN&@BimC{W^&_^P+V#3!>u=KOn^t(z0lUTyupiwRpeky|4rz6&^0Rn z^&W+kY&-0DI(z-MDW$a=@j#-tt_ml?tC+`^U-chth$Zr@Cdo+cBKv0m%&e474rtzY zGx2fPAzVf`&Hk=;#rh@W+XbWCqsx{ukUL26qDUW4A?CC1k`G-JGvuizYaUA}nEEbn z?In7CQNJnTwkU>%X@m%u+Tzf~S3sU|ST+mkyjWmPZI>en05sd^TU4zoNQc@#duC(` z%{zwNx5OVkNfie~Bi}(Cc+`;2nNx`#t(*l7Ne->vP-PVF8&?^{pfJSa&)c|km#!9F^=gQwcJ-0h@V zW_v|>8Ax5&MD=lLZYYBZ_(M7J-wOggL;WOBMs0)>Yc^4TZ>#m3o%_R8LgHvxk&Pf* zlou^IHeytQ-#7a^M!9k~_9zmp;*Te#^zrjBRfpt}+O#D72Ds~s z$K-7UCpNtmvY)^g(U(W6IhVf*(d>!rvJ<<=;UxG++Bq>ac$klIA!=f^>jf4kaA63> zSp(XWD_fi^lX;rnD=C*l|G5AU&$#J%ny*&4kPN&RHhwN(KitG=q7x+4(;Jd8^#nr6 zYBWCY5{O+nXbS(vRv3SF#Nk{-#9G?*?dTXBJX5Q@7Te~rg-_TZ9@pb~d`@~t7Fwe^ zquSMjf5+Uh6$@;-_VPfaE^t29xbc>I6?!DC1oy7 zf<_gojzXdBT*GeSJZ`0GNmz4A0?G*z-Dpew5mhlnkkxy4!ogWvjAd5$6IsR8A+>{JryA*p|Ih6Z7cxKAyc3Ov~&%+Ycxx@ow&(wT>Qc;bHY ztawu_>J{Zz!h37=PAr(0 zTSJdt5+c#7xZ~n$-yfenKKEe2>$*ZQ^+{byPC4r1$oA%l{>beig~cB#^w}-uA=(3M z3UiHO^WY6=^s6oYw^+}p_|%=4a{kwTjU)53@>m}4ntqA~Pj~I*z(!^*RCmWI!#UY> zO0B*F$In{j83IC|Z1bmf{kOQ#!o?kX17WhLuxlblZs~E6h=;vCrL!HqbhEh!Db7yo*U*dCj2unIHVxRs?bHgM?~QOCLy?8$qypHLwLb)6$VA@ zWjZ@E&?Xj5GZmTFRM9g-68?rBV4U|E<`_ZMDTC8s0g4peyUDfl5Z|Nxj4`nA0Q6ZC z8e>ITe#!vM@H6)6!vCISug@n*1m0n1XwY&mAQ{@;#oHXOGE-wc(3ha#YK3WWoc{fgn;_z*ch9lXjUu|3U#$ z{=%!$m~YCQor$R@Tc6h=mt5;J^VN-sw=7QqHwuj(*wl&wdScjcyWUUn%R9rVcD4(i z@mM1W#R_1pq?mf+?BT2mS}zj!d|4}wMfw+aA%`xNWUXMv|9exCaOYG3MKxy!pkaD@y%>5POSjhru*Ar{sJM?|WdZAU^S8=`IY>lBq!u8v zHKZY8Ek_)0AZF56B{b-B(qGTg<(=|Pn@$f>Q{%Y39g>40gC5ZV?`3c`)zo>`3|rQK zNU@Gii&X#31#uOnIdqFC4~M=9SqH3*1VWf;NF1ml;a@T<^Pcw_j+P_i0&0(v_egpf zPfUM4G)4qT3v(|-l`gep048&?qeCD>Y)%3>WuZ3GImmv$$SmPUM0P+R~eBYI!_MB6~u9B^BgNdK#Zk*`F1=vcA5;wGK5G9mPg# zLfZ5&yrG#|TX&C&M{>lc&VA1oijKANK&Q631&-&3tq;L24s(6dnB9)sZX@aug>Jew zAHY~V0&x?J+C+{#9Z6%OhupQA6bo(BISD{drt()u$*2z;91x2+$DYuiB(h=BcOCG^ z7#JIutQPBRTipaXz1_d~x*tnJ`aO(;D&e(sw2Wk23a9Aie8)Z~AS#3}XOAToc{2Ih zdBGg<5r$=6F?}1ty$?Jk7E3ULe#?gPaJegb(F$_J3{gR(UrkbZB0%Q(!foh@KDr%` zG_9%6zUGd>jsrs^E~`bzJRCeZmK@z)afaTZ8ITLX!ee*wrl3aXMbQf>ihF!x7(jepKPz3hgR_Ro@4j?rv$@!} z_%#)uh)w8r$2J)aO;t=|7m-Wjz#M>pb*y15O}|>4FeWlX)ySF?%$6|hx@$H^=%dlt zs}rGFMyC>~kBTR2bl;*P$@rFcgTU4tj4VW96!V_|-&pFz9y?OV_eE^eim~Z?bZRRE zlYJax``1sN8LmaYsR>h1>rV-UWy_h-{mDe!-&J*bEm^zb z#jq?@S*&#Hsdly6sHbaHo{$=obg$cnlpmow2xIFnLV@lrk%h zo*n=B_`4UU*|U>ZuP2rr;i^{XP(@M3Y|CGD_an<$Sbv2PO%3IkRN0}ucPQx#)-45E z?9FyceX-S>J68liFCWoAQhLU7PQu@PGGH{~@7DU|GxXThzNIrbw?R}OyI6KUF%XjO z34@$R94|Nq-v zSf&ErEqVE_M%~kN5{$<)olyQ01%>RlXh>N=nza0o<_LbQ<_$DRHe~+;@GAQ5Fi!StJ$I^F+#1=aU z42*gr@blHc^IXbGZx*2&osFu<)q98<43=`5m5-L^7F~v<$OcCus1z&}k5m^I%c?Wx zxsaF}f|Or16uO1B<`Pr)RRU7@mek3#SezIGw=J!Xo$77xqVr@3JZ+E?X?~fLX-w>o zvF}N#QWw5!Af+JYdWLO9FM0q^ z^G$%P3w!E|Uwz6dAfCzxl^>PF1M*68x&>pp+XeIU&h<>E7* z3(0&#g)jC$g^z7VhG*;CFhieVlLfF1!>Z!>ZU>ylVBlNyI@gb>LP2CUo)~Cf#;Kc| zOf+#KdPEg8i%?#g<~tV*zYG44$cV*X+LQ~G1j9k&H#hD8jR84R0FeA~8?-6Mzaz8n zcHkhTcAtA5%9PKHoj!xzVh|>3v=I$d^(Ka=joe|*>%nuk}TCi~F;PnU|&ImA;HtS(>=Sr$7*QTDBdkA zo147h$U6YL|Etd)6K~!B)u*4w6PEOPwM(a|?$oJ%%uH1>;us9d{xTYt)um4IhKXG1 zVzjL6)I#k$5(a}OJ*7C$7siv5&}uVLVxdX{Tca}yl<;{E2YTyyVOl!sOJ{&*m|i-h ztOE+PgPIu^dJRVxon>4{R=G-RhrL0x`zp2qKHvMVx*WA zi+-{WMU>>2;m+e7hspOFN)Po4z13mfoRH|K%XL^L0o9)K@2bwz2DdS7OyVLz-Y0y` zA4rVo8(UAg^Rh9;-Z?=+p2+xF76lp&(=|ND6YMz1rz2$GCn|ymrfQ4ydq`k3Oj`ie zytz+lZ!6R)HHP?$G*{m>F(pkPE$OSgN zp0kg!;1W#3?AG-jkm;`kfI3-A(>BIykY%DYG97LsoB|vmUVpLpd1qL^4Lj=HP zUKBGcTOiF|f1pp4PTH0DCwtBm^0Z#Au<`)x z*s3*$jKv&s<&bQA4_U75%cFoo}Nd?2lS^WVs zZKAD6?&%E4k-}#DiTp4)%0`6cR_^((yMcFkj_LiodeoQq(lMYyB}#@J{M@`R+x-!S z(@?~VO=!kY8`EXy^AN+wV6 z5J7%b)8Pd@%E@<2#`{%MU*zZ;d{L}vCzfV_=z5D>4vRn4CX+FrnVgydI%*3PNnnZ~ zcwJy@8m8Aa)qHwZ%M9EipH4uO$|Z#og+cN%42>6$)YT*=_WCcU$Jx_A9zXrl$@6ay z#c*wupsR9yy}1C@fB9&&U0>JLqvdwBeMHq@+DD)M`m!6mr{T#64I9eF60=ZzsZPKFt!H0%Jwi;N2Lxa-?&v4P2>%smH<^tif%{z!BgK)ra|IlI`b?1Xlf5Sge*<6wIIcLM%t8JPD1 zS&qdxj+9inB#ap>$f{v8X~3)6SZ5owQmY}NHk(=veODzovF~3z`{BD|fZw16lFP+5 zgP(36j($qvzvBo0HW;Uz%LY)CJ%93j(3qFRtj*}F(|^ZKzdZY|gY$=DMT`gd2slai z2|ZnH@i~QA##>kz$aud@9|oDwzuF7_lz(7g6QP>)&&Myne(~xU_z8SD`S$sXm&Z?^ zygCMHM`mcl7tuU@3dmkt;(fvQ(M=()GH6W$D$VDZ0=Gn)F$-sO4bM_gJcdy%+~U$H z!IiI<;75$Sktev|zz%J!k{F9d%1N5?OWn~1om~z+Pc2`^70;?}Jf+-yG3|`hIPa>U zHaL+Qd2)?Mc>pJRra`p4>=Qapy$9+Qdl!{;n-Qk)N)WTpkLX)iBZv@XoAB45jYQ_+ zKYB$0Z`uph%UnQVClVl!!+MXYdBTyuA83@i0S9_|@({NFAr@e_Ou)4rcRA#CY4Wm8izl^Br|{_y%?!OlV_=5KPM?AsOD zOK~St3}c7f^g(lBUuM_fLEc=!etuFZAIS#~Zf=90?;h8^M3oXvog6W^>rNHz#OtsH_iUx$Y_A$*fI~Kt(NKWE%uWi3(5L{ZsW-m_SuyE)a*{0itqPg8=8C3<^858R(Ot1iKygVLo6H| zj1D~Tb>8|lVSI*#oJ@u|B2W`ca*@iA&D8<6d8*7$mce}hS@N1v0%G!$ zTVZ3qV!-=lIR$av#LMF(Xuawd zZx=;o7{sJkn+DTS*A#}6FSrEeO}R?&TxWdfGt;K^#8DY`)W*duQ+7&5i;AV?JO~OS z(%wuQ^-cskM^R`LLxm3S8eBe~Pq-iB)&blT4x%cez0nMYKeZ2(pf|LdT>9{AUD0mGB~@~5-(!B6MZ;@^fw3&($=Wz<-rq%vx4 zGn1>kyR;kH?ymr$9`im$Q0UqUh+IV2wp1RULO)Zt@s3rqc3Z9Uw?I)?zncBO(N3s6 zvhgFD3r2ehBR{0S5ktutq2W_m_s`DVtFP@@LI5b$oztb{zLjWiK9P!bn1$nz`1f?` zZSm=p`)SYN0UIsIK}{$=ei*@+XA3f*AT_b#JOXTV68Z+kGo@a*J2R8zrln+IWt9vm ztCOICbv84!VZy1w1`(F&SJS4&*TKOQ2$E7X2Ngxr$Cju=IF*dZhieYBSiIwG$bS!2 zGEt*Q(qsHGJXbay7Lx1}=J?&zyU5(B8BP~ZXZpg^#;yRI}40@11Iw5zsYBL z+&RY7!>9=04fto6>`TWYMQa-6{c!c3K~j8tq_X|J-)ZCgjyR@3BT&3IgIrP z$LD7O)}d=U%N_kU7(}tM+Z$IKn;0_G%`iT5#kQG}D@ycv-01l{+o1CqX5mt(GEpq63^!hIxY%HLlu!<}RTn@UsbBo3L4aVtnJ0 z+UO6F{JUjq2aO}3@y0SwSB1S`QkCU~^6sJWvDC0Q-4I3Q1ic7C8I=+aOfjdFUciHa zrOf4`C?i)-p1gz)D>u)>p&$5wp5Fp#MDG~d8XLUWI}XjR>`MLE0K>s+jTx0@Lf^b&1##) zo1Ok8`~J!Qdht><6}N!h@N6XeA=XO7%uU~ZtApA5azLv|A#26u=5T5K09I`%=7^yu zp2-~IQ9ZU3GgZ@pr6>M3xEgV&!Rt)>C1_-QB`kjY24?9gw+sIn&fH)OVc0KzBVAbB zrZT{kJYLoD9l}CSo&rx`@Tk=NY!V>^UWXq-vS_m;dYE{|RWxNx-IS*weX|q)VmtB7 zsAS>+t%gjfY(Axms?LT=CXCGmO&#Cp+Ej`1x~w7GQ4>Q;6Sd}(M6L4~&h0#TkbKcW z5osw&Bu%hs;1Ml6Fie=wdo?(WZSX0Wjd7huX9~)>uZNQYD?1Qxwsm*C0X_+<~5Uv9hnLGFzm#S z6b}3*f{!EfhyNJqSEc*H0-JbsVmXmtV8MITUoEw-&gymEUeA`Ro;Q6|N&$wtxhm1X z$@=#^KF5GK_$A;U^>=XfOHAO)9q@mN34FO@0>#f^2a>(23tF@t zrQ^{XrpRGN$3d2L5fo?($=bR$BpN#fl9G#fwLqYJn-c z$hVzk=EYW$AEZe#D$^pJa6+$c&K&@jZ)wOVs!8Op5#Y=|H#a{x3_P)oSlwGT8kow5 zRhohqoECp~ktzEUoMrsqj%4cP`3w5Q-g-vvGt)?^F&%EtWHAUh@F1Y!XqcIZir$Ot ziq{ri?`cQkRXAc-16UH%Diw2`QBCO%G-7(-42G8j<3D&PZb2G@=nvC*(Zxd)Z(-{B zoU)J>;hfz#Q`2Nbr52FlFr@R>zxi$S1(zwU^Xr|n#j59gojZP?yuBIwQWz3tC^w zLWQfUL9wFbE}jB~)4ST_Om`1uedYbSlLesQxeswF1n>}lZo4^O;of6<(2zF{({;rP&J?XsWHNJ>mVAg z@O{bJ|M1Voj+tB+l)@*k=8s@X?>s>7y{w3ET;V_Oy9ZRyl`JV4VNkWg^5083A?FPsc=TzZT&wWoh+WXep2LTI*ZRiHJrS29bJ?m=P&69@|G=y#EMUtN zK3vQ)Z`$=|%d)xg<=;5DjX~46V-N@i!Fa}WeB~^>PxirEK=Wl^nm=_xY%avSYpxxw z<4b80Si(rjrezkja-Ng?-W*@gy}4zJ1~Y?(JVlxMD*i#=h@DeVXQUg8=)5hyDP;@iE z!!(d$cLR3JdAVpa+g|=a{B#XbB}+fjdc^5Cl`?I>$(60gRRFVUQFHDypg@3Wb&Q_T(mHz4PtAPE?m))yLb00o5&R?dI zF%ukr^`~y>Xu=r4JVX9{X_xOyF}f}e{b_fn%#x|*f{cZoXz(1_jDJZitc3P`XPbMw z{5#>uFSX|#Ysu90G#b0J7tAm+{W4x|NO=MzQYJRL-ENA8yUm^;xJJH?_DY$ z8>|nmODw7)s$cq$&lKIlf5+!hZPJvl+?l;ZyO@q7w&Xo!$OM73-OR3qC0!Od38xsp z@kwJ9v5p}I2fC}2Xh*i_lojpCt3@IUCo_`}zomb*3yW*(aNhGor3;a&;H*Pj6(d&t zbc`X(stl`1BpSdnUg>~YG!voi2VZOI&6U&vBBR)_B~u=0U|ALjZ~N#YI4k0#C|hQ* z7_wZx0bc3L+n!Koh8hv?MiPnY`s5t8DnEQuq*o~tD8`gaMs_F3zzjOo2lgFuFt;gM zyQC6QbS`e>LafK8@2Jc(Jad&m=fxV(d_L^x|D59?N_ufhyKj|uRq~=!#Z#Ad4?6GB z56p#rgbDY^+9syA&nLq5n*)PK!tkmy+T6*(&9Tv2BL&Mkl@28jjiv%5M;)G6agnkq z$i{qH9GT1gap1IOvOsoq6P2yWOHf6KDXSKw_^dYJwKDu8`F9-a!ul7`Ev0uW+{&W`=QY-5*sg<{QLjuG z<9#DTJ4c(d7Z_t7vjpR3G@P)%ddP?S(q%e$HubLmvs$si*@mSG^W*uTgY!YmM)l4Z z*#`c#mpLnW3iu_gZ%?Tc7sVBoV?_gA1!r-6RZ&i()#hSR&geZ-jY;oIJ2hJaE``lK z53MZRuPj0N8aIUlcUZSx19vv_RJ=6?8N#e_#B~f*!a$*4kKmLgUo!JZ2<-Q}*y(O?eel9n>_#kbkb*@vd5e$-V@58!!l+d2Ku|XRu4qrC_fUO? zspqW?+U}HKPm$YpVj%U>6xPg#0tpWhZ_h4jZiJ$ zL5Cd{D@p)wd~Uiy?QXEfOk%Ma23y7ayd750&v(Vn@94&?R1~Nm_r4oS5FggJ%zAy% zIExHP7Ma|SDdKA*S--c~BoYY{USzWkBfQLI-fxY-VG;#DuEBrFmueNoi#*Gw`5v2{ zcA<|;-kx!p2S`Q~A+2}sd+gBK3hl25(fVDxQH78Fy{r1DMN$o=l!#iUhrCm1wWd=9 zGo9)+(j)dshJER2rb>_8Prf<%kMEBUlc87k%4$KB33^@EbTBQ7x5?~zPD_El?@9X}_RR;MGNR+9_}_yAjK zNGAJZY4Sc*+gnTo2|R&{y`lX*4L}@s4hsRyem9g_i(~-G+0F@*R9QMZ{BHgOMt$;E zjM~WY63pd-M<{8hQyL1U^fmUaqMDoXCus~jm_6g3D5(RKBqK7mUtP7~T^^9_b$Nw` zt5s9h4c3x1XDM|QcCAafHcJE29G}4c;@EHn z{J=V}&pG1MBm0nH!|yQ;=s;Brt1BqN?`|P3Fl%JDl3?XEs!R_3(Tal>#XCU*qZ*09r3L~5jj|db? zFCdV!)!})3Z6cTwkfEh6{yrfPh;(*it)R2Ix?K1X*yh6t`iMu2Fy>5e1M{hZMpd+x zPlH|ByVnHnKloi+LXY=xWK!{s!TClxn|tS#klXAhD^p#rUBOE8iE&%VmcY!<)PI2m zerLZ=(8ZMYR-ty5g?4+5E*{;pDtf_Hp(twB1dEH{dVt=DWlN&BTYZKq{B$`-969r1 z@qbr$wM~s2NBH~winaK_3X8qt&Q6?5kpu%3Q#l{lKwK_PwOUClPDW_uT?tGs{P*w}fnnL)y*ryD1yq>kL%Tu2xh<(c5_AbkC~=5zvj6WTb!=1fPc2TCWXT z2%S<5($h8Oay&#pMC-$LsKRBjNQq-8WF<#2)S3$|P{K(V+j%RgUp|ez8rr{OO3ZZ+#$Z`o0-SI;1CHt5CqGfc!plX%qcbYDTWxaf~PIZuQYBlUxV9d za@o#3$5j+6IAW8z{dU7s$+O1`nTIuQUAtA24l|5LpQMkE(S2sF0?cYI&vu_C^T%F* z0&UVkn_$C0k0Ox3q3u($!H#B3K=_8}J@~c#30iW`K7Z6_%x3ZCk4ooqj))Rc=xJkh zKwje81-BuHmiG^LsqUP@3E>wL1(Z~<@(MBUpA=lP5ne!>CW`!`QIBjcn*ujLkN^tt%lWd zx?FK6qd8p=82vhqBpML0_1UXDNd$CA_FQE31;jjGInOC~HLEqaZBHVqVV;T>5}3wL zxw*Gc9Hvq8A9wgW8iRW3{cPJRWw(2cM}Rs9=#6tkn9Lr8Fd6T5(YMBWj+4=#(tFxm zZl>9H5{j|7npCgFsmIp47K+mV7Tb>{SL2+GX!x%?W)%8}QRQ9B7xxSsjvvIk(2d5D(>I1c?eRCy26uOVr|lZi=Jn;=wTKg8nr6h!`ch z2o(6bVO*y<2(4j5V_zR#c~I&Qp^6w12o_0nn<4eon0Bkn0&@3`)j0}h%FheZu3og4 z?CmA5l5u|b>dV(}UjHf2(0R#aHb1BGb2kyED9Ba#XSrb z$naYnE06w!`shg+t>uvMs>iY-Rv=c+jcSWd{7Pf!gE%!Huw3w43xa)X6Qvp&6ig#A7)KD49nT@ z{qe=Y(ZR(5i@W0=j(=$Kg2z`M;$9!w-Qo}9WQU01;;Y_=Zok`E>yoAzebl0yk=Ngc zN}_~e??z^FpAWLu!kee%CXlsTLgwA&DS%Hs=~^RVZ*Q~dj7{=oLVdw!Uius10a|A8 zjBhl5-59RHq5jX1=OYe+lv?m~*u^h|H|JO-p(30D_I!O~tZ2c~<*{wswr$(CZQHhO z;~v|#b&qXZbKm@!v{t?Ed)|EG399}(5)O5%q4Uh9okhnv`I zLK$E85&r`Be!0Z3RC_k;iImV8RH%3Ch(>8mbqW{tyH6M~ZM_uc9$g2gRp`JqvZZ9) z5-nt$-TYDQ=S=#gWgGURb;~^Pp$78d;;_PGO~F=kzDyxM`zo)Ak+-#b83IBgK7N4jV9_%|Ern=PJ~k!}EgAeIri{cQ&qhautp*t8CUFB4oQ`-6tPUn;OC0n%F`DO1-suCMc>NJ7Eis3mxrhC;`jA&@I?t+e&0Nu{bNmS z-4))T=e_J9_$+3*abt;3S>0%Ks-!;@rPP!@IZ0-RXya`w*v8x4A=r|b6zGU7OU1{$ zQ3{4KYokd(2l-Pir>|leHa(3mVJw*L($^zOgr=yvS3TQgo!m$N#SfPNLn_*WY&xo$ z)ZEivWBFyD2mMK%gABC9S{ScSkbibnHtw@v96q}&rq+@Ic>(6yS3bpIt8nHEZWj$v z&Tb#EtNmD5ge^W+;rzXrtF?btQJVVL@+yB|UT^CJJ^~Vdr&_RGc=OHYO&HQw zEaF8Ore;oTd_)R|4+Fk22Fn+S%*$9j8ywQu0ht8^%%SD{VBoW-df~4!c%~bfN`{c*thWeE z1d|M^lvDQ7WzU1@sXGL|S1m_li@RZ;E}MiVKDNbN^4vL4Ld{46*kWJ42g6G!|Tl zJ8E?cCHJG3g69|z#Xnk@M;5ywUvmu=Bqj;+iz353jEC4%hXMZzVwvB>DT}9zikP_v zM@M6f2H8|*;?@?uDoaYt5HW@l;*RWv=cT>{BgMQhyEj( zU4?GG5)5I@$0nBmLT%|$EHSLz+Rc$z4*ivc#G@Jjs2a{3vhS}MJHxOk>Qp^19T}~iL|jKi z!Vq}3X6b-lv$ikXHa=S4wG&+q&S@kvMr7Sa0Y@GGw}+EsPM&^VZtjmofdmg~`6&SF6HT^c80*1I3#u@wm1W9+jyz(-6-^(2-AzUrAJvn!QEkECE-m!2f zv#CBy=<|GXVzZ)Bwpt}jh3!Qf^{8NqG|5AnY<-FLtO5}_&hF4Lr*izHAU36NNz#0q z%oEo+%y5s9LVCk4gV)82?JFq7r-3ENkgWeQz)5;=q5k#(O~1%f3rIJt3H$gWq79+qyr2{s5KWw#Tt#3`QI+Vy zM|FRQ(b)2S|A}}N1pFOIinyM0!U~$_icb#MahIdrc3k+iX~WvsWt0Z zA;m0hPlv#45>p5CZ2-HKDtWiUHt}xlZFDQ7U#TLFmLu}vD3n>`!}AahJJ4W&Q0N{D zF51_g!fTPo9j^+Vf4X77uFyaZN}O{9!qNJhNsxnO{gd?B9>M>FJO zG9V{=hce5O!J^HK!GM>pTEj;k^ZS< zl~1rU@IbO;v@H6V48(?(1Je;DwF$`0*BJcNkF4?DBJ@FFkD^*KAuML$FNgw=i&y_aTx{%7?IIpMnJHDA=AfAnqy!odXg= zz!DUKl#@ppzxojbU7j1gf^U#^72Zt0(=?EgGApx5#(6^YYmPp4)bkHX-W3%NH6gf=0T|h}ZPB{y z0V07PHHImL+=6RK>C4)E=SP7n`uVfsotx7Rg6K$N{my%@v0_vxYx6GRphu5k|C;$w z+ESWFY^mU|#P}esW>!q(Sg2pza|A2Bt>rL=d@Gs|bm4TLtk$4w4JXvFv@64@L&ujb z16hcpf7|80exmx0qk^e%_vKxpjTY$G5F!1+KxFGTov*iZ%@me!1vC!dwsb-^96Y8iCyHwRLHJFxSJGNtZC@$ySU6Qe@2SSn%n`nPkBg@_dQU?@$8 z!UXU|q7#8}yyj|&`&S309hxXwkLto>FTsPg-j;Cvz470nK7@sq*hG-Bd?nU@pG8k^Bc5?GKX61I}<%$AZVs3_-&aEw*k%)Y6+R`(mr#=}yd8 zrGm69We&8IWutsy04@oVa}RIa+~~b4I9o2R9yh%H(c~u;D<8OY^*0qoAci zk&Jjpl{=^dCnhN%wMNNeMEt}X68YoemR~lbR9%$i#G?;P&|KcmYI4hy5uf`LF;?P^ zyZc%mo%u;pFkaGO5c7>mQK!xX?gWI{zY3yyv@jP`89k|2q5|D(M%*_)yHV#2BtP&9 z(!d}n0000G07-w+wXA0(Dbe5n0GJs80I>i4b+9(qcQSRhcXcv0b*49Tu+(=kb#`&4 zbMbH)(Xw^k8b$nx({~gk6aE9dkXF;Zj_frmnah@2wlHkTjXPE<*RR+OF-*uIfRZff z@14KR9RMCtMA6rrm1v6O{@Lq2ce77pPe*?&S~rZ}QcLZ-u)K+)mJ!OawJfb1p>C#F zZ(3TkF~G05OpJyb%CGn&G`#<)VL@FYA)8m7(o1jxol8x9QCcHfIw_^8x~zfnTi`@m z8)X?AbI>&T6(if2K=TKsw8kozt)oW7lI7)0XxXb2DY0^JnF<*ld-DxmRrBEcN)rY0 zi^u;*=Sael6I^{srSi{ql&GoNxA=9LLZO}K+i|-~(JXX_Qqvo@@qvgA zH>KJA{S!&v9Ul+E8w*3B#-ugqvs1Fv2NB|Bsa!fyYG``_xCwnxPDJ1D-I_=Slv%PT zoGx^`B6@n_WGxc*l7W1wh^>gRYK1SDpV4Z1Cca#`^2gGB-pEuZT|Tc@iQQ4v^a=}W zwfJfc%41~mPFe4&{sh(X$NiJ#-`cz<(S3Lg`UZ)ZSI6N|-OOpGZaVW0KVNir2EEk^XtKdu$t^zfSzxVZB8@nj`Sd1KnSJBh1}<z1JaAob^e-q~p1jiX_QJ-#bao|RBs%8owN^+FDp zf~lz0oDSjU^i@qEL~39}z|Sr;*4snzI>%YoD=e-Bvd9G~U<9lo3%3S^N;mn@^|ZLs z1oKs@Wt%Oa!2AMW=tcTtq?3-nd7~)U*+U>B@pC7LYF%onXbs_o*xE>pa5kcZcM>eC zq`*KRy(z=pX$(mb`?@QsEe|7JITj=dxY)Ew$7MnXEeCg?B87wRqlFk?!&*d5q?BY& zj0#P*1K1B$>>q}&$uP{J3Rn#|`~S$Ch0c>Gq*=CcL;3boX?V=hHBXj-1Qg*Dp7YDP ziaPZwx#Bx>FK>&lGaWy&T;`@CaDv*9h#)9sBZd4^LsAnPKt|(b6{X!*_>&I|0n;M1 z2pLp|#?RiLVY=o7kh*U91bZ-DToVF&Baf*)#QcZoR7d5{CWrUVNPubdD%YIhKv*Gt zjO4XIG#XGPqQF{IYsLvuq7JfUQa!RQR?i}JEFICVLbow3q4}(_FbmVwoa}^PCzx)9 z(2O{C-tep|!#~BI@x=A<0des9t5h^}c1#b;BXWn{I%{yXfxs4K5tc~=AWK^Vm~<=@ zn#etbRou?Ql=UzCT)yX#sz zRo*{9Fw`jwY`~b23bg4ZFM-nF%7Ipv1*uD0;MU8~YU}^Xi}N%l$lloY=3`n7Lmd}H z%+6`|o}Lm~Xlk}F8C7~-znBkdLc)Ntx&I3 z)={>PyWXkt!xLB3UiudG_$?vBpVSt;kt4RU|d#SXb9Gz7Gz=`BIIAzE&FFfVlruV{ilmX8BU-|rJ; zspvNPD`u%-?k!LV?J8b3H5>8A*@+uqca?9Ry#dzJKV0#qRaIliBB}%(o85a|Ec)tn zLraN|eLY-X$`_r-#m8M^zA8nIy&qYl&*O%Wvaj2Vj(X%asCc<2S6MKji(-&9>maa7s!2oOdX?)FRFiJFXUG>C$^ z22sP&1JYK*{#SRtn;+9}i#dRKWfha^9<<0z(@v#BzG6;c7wtt@7Jp+pt)O1b<)for zsGl(p6eQ5~le(Hx@lTIHxK<*eUJ}@$$9OOmXbWyP*W)=i-!yY%d+l|_iW|s&r%J<2 zsA%x?8p!fr-SH=>W?J@RP%S7?3&5hYP`6^>sf`+l8bHcQSC$%7T!S;*l=(cYg}B%< z7R|zUz^5rnxVo9t$@Xby0?NoRk?jCBcU4}zk%l0S1|8>A%g<#Ay~RMGW`bh?RZq-x zD8+UG-3!^Tl|f#2kK0UlyRV>EA0y-Y$o9|*Zsj=7YVQ*_k9u3^FvcuNx28zExJ$`l%Zs{}WS+gNK9J$<<`DKE89xMs0 zs@eyxfDnPiu;ISuxsdsLLW=uSjR2~DNEa8oYxm;YRhQh9@wD`BkU>goaVqw zy9d4o2`;l<`<;C5M=l#oXG&>0cp+Lef2XCDN#Oez5-`>C^Dt|)e;LW;1z|;l(|#(v zWs0GTf~*GJYiLPqFx~!Icrd3wYHjzDUv=HlYrGwZ(_sI7=Iy!e2r#oKj{_=QUV^wr zk;OKwY3lA+9>O-wvdhi4&kf&WMkk549mC{N7OvGglg{f01ig=Ga5;MODQfJWI?z%t zD@eLPq-Hx6OOj#9ER1D2Dl#T^w;T>%3T!h2eBWD~onnB-SN`%8U#v(22Zf0|OK{~D z;f7uu0Bs>}rPd3NmviU8@Fe+IHFlLH|5v4*iw@EHn9MVMQdUCI^V~21xV;X3lJgZ( z&#guE8PFmf?!5!u?dtbK_|cW$1S#LD+$biB(K9(>*Ds&r?Gh_fDz+wjoe)#)dcAg~ zK$M*aXl=D~|JDs)YN;$D&L|iO>Q`d`G6q2yJTB-Qa^q&)$&u*n1kJ6mDgWZq%>j#3 z;ebXodG0R21}aK0Zzz4>f*3x^p1qP6%o5$ya__Q4K~moPf7LDZYfj|}Y042FgU zGDc1og$QVr$+{I&9iN{%HOp+4k3++S1n-W_>oEp4s3^8Q3gCMj-Io}?rct`Qzb_T!Y4OiiO!u?`tC+&TdUo;Q*wRw*9~trOpk|znfaasJm<{O*bnjhp(WXuSbu7U@R(5F}^0XLy zJJx5{2juXYU+1+W-|0j_EAqEGF=sOMtA8f12!<$HqoZ_7CF(1@6Fh^>7~ZsMxvc!E8;9Jpo~&@TVy~kFwr2af3(>pw z0Oe;>SbUkz{4FYCY2KaEBIuFt5r3xdK6Ct#@9}>k{G0nSmhC8n_zAObrtVI6Um@AE zw~^#qfflpXZ*H29`dGe%h)8`LJCc8GT9t!>O)wl~v0I8@c81Qb2M@9rxF@Gsih3Sm zQFWvLNtNI7gav!yqntRwDgxFQtBz<_K zSW(?P`zUThyXTARD)x)NV<$Fx;ZOON3ky$=sVaSX-8XMY!M5mm@)#?JZJukewgp{vsi5+$ZdF)Ln1V$2_&NeG1ew!EgfIx!T5h4>s!8EQ}B?76aI2AWN zQVTs>Ay#Z8>oykH{X}fES$4kSg|kP(ju>pi=uqK|#0G;NZ^s5~CUl0&5&1ZO&TL-C@@icDqc5a61FDJ;aCIQFUF%ze}hk3@P8II=)0 ztt7(dLW9;Lgdz@Y1&ezI)UMUZ`kPnR?>NK zV9iF=D25Ln-+g+>cDh-Mb}jWL9{0c18|xwn=UmvWxQAp7bbo<7zI^W>+q2p6bRB50 z{^BZj=A$ceZYoel%FR9i+TnM6m2aO@+Z$s_>$=1JZ@fvJ()SGhgQ*J`007(n1Q<&@ z7gKX5!~Z}<-^<>?^dAOYOr6XOjZOanCs#$$dW!+U@3p?Yk{*%+YpXLB4aOS_W)$Oz zF-0YDE*49>T+%7K=Be+Ngj{y9Bu3Uin{PX}_wy-N=5$=#p*9Nkz0QKi)xgG~f;hua zv8}>Jwv&P_jQcp1>|901M%E6OakvPhH}^rhf=w**r=$_lz9%ZoMiIsQE|q5(R7gP@ z#EQV7Xw?FF&4vw7iR1|on8gg$doCFZ+qlup1IHhk;e`&VMeszo)sCICo=J}ZQ3V^q zAyh9rpkQK^3=qN+OC~n2Y2}Q^3bip!$>|I=(6XOswYxpIC^eMxk9+h)%iAA)9Stn= zEJZ+`R+Y&HIK-|6j2#t%UhJ)UkxX8mDuGrclPy;AD-7u;NC1%_u}rjaA-FxI{j0B` z5m50s=sJGmOj0`Dj!f;H*>2y7o1DT}KVIiyIztrhmwjQXeZ1@MH>O*~eh2o|Mo>Wh9`mHM7I*yhQ%iH7*QB~x}PW-v*%Z*-ly10(J*=>0@B|1$! z@4j2BT4lLy3cIpK_)up#6ob|ia>|fp>@&-erIBy4BZXh@x6K`Mg_Y-|cefBvDFpeK z=PLKF=UI#cnBNB2bus8Vy`KnKN%Y6)wG7tL!pTbcSofSiEREE4;5}3!#;v$0A6&8> zZSKMUdsbWn_K_6-n+^B>8u9-$D{Kv&tWBN%vv!)!&Or?_z<}L(=M#CHgDHJEBY=#A z-|0=J*_mEzExPP&bwzPUaGo5d)2A5)Fmanr!_bvTz!176BeaTHVzC&;g>p@6$3@?d zFkBc|2q6jaP$^D0u(L6F4zVdIoR1jiJO`!d<{aZebj){ZaPAyQ>Mq|CXfG6pZq^e& zOW5IM=~rGpzfc+ACNux^&0qBnqTRBCxxcyO9M^%S?@NM?VE^|RRWBW5dyxSE)`S26 zDF3H1{@2F!pIt&*H)%^O$?sMy79Jq(?y?y5<;nOMd{Qe)A5!q zRl$~_^p}g1Q&M_n3db9~kG}pRThPAwDLITER4*X%F^3G6q@LZA^A{p^n7qJyMSICa z=k^_v|MXHxsiUfYn|JN*qc(y_+}Iet#l9=sU@su*ZUtNNNVZ@Dj8?BCBG29U9t9(Q(ecoT=5%k&h7siN-XRhnzlsvLd?UTRXR5Op zvCT!ADNGpZsgj`>^8Frk5K~o+y*PjfgGP41pyEFhHrD`<3XL6rTnV zne?hcmPup&06>Nz4>h}1%pzS3@Py|lZ}GP|jR=s62u9kC+pecFHr1WS=(J;P^&u%mo4qAv4@jGu z`r}zNeUq(|hShgAGdlsX-4Qt)fuup0xJ{4RC79h3Tb(xyB`|(Ot$20~*QM7GQHK%? z0!(@$w^>I91BM*gjVqJ+Mn-1Q)U2_CG*(w#UI%hHa4Cz?qi=ZPj2m$u}xv* zljUWs9>Kb5w(lOrU;_g3dUgR*EVO-SuJo8m5+#hAuK60T#y;7M>M6+@ zehd(?i5P?VxX~WA(*aY!KvmaNXI(Srhy9vm2k?pFOFyhG*fEttw17}Md0MOJ|3n|q0KaIR&UBNPjaf#l^6PDC>qf_hOKiQ-dd0!** zCXi--bRk1PW@pNvMq|#fH@jy?!*&>LPvz^`SNOV(?C0j-6@=mx)=mSE)9H|znDd2u z^#2SG3@|Xo2w_~;#(?(v1?xj4uF6DZmKn z4H6U`pu~8Rexi}jaK!`hC=iX~eQ{fW60r|j11keDQlJ(iWK#~HoF@tkjJ3mmI2JI+ zRyJw}Xcj5zJGD$X4k;E#ho?u{NG*s?-Ze!p9lw(s=A6;huK zA8Qo*VK!Y4fD}=2*m;)*>GZ8-sR8)7=2rZ)uxgtQL_nu5cu+5xE0pMowKfFkA zkcwl-w+4RsKaoMTJO>G81T<7x*Y!_*AhFjj0rcdgvO<^-CCC{X=ocgp5x&vR1@hD< zI)s;V0-fn=RD40!TCy2fR%DO^+T!@3qshf`Z4SAKg(X!%SoB&Q zV$t>n%^Ij}X4yq%Q}UxGzc-Vk>lP`v#9^#6+lM)Cb{tgj9Z@r|8JgAbo=YGy z{^?`IQzywIbf{XU*R$-~*H+qx48Bc;+OEp>;+daId>+bn6fP41?yxNbw{#QEs5l0!~FRZF=t{D^hpp1;QIHi3* zw7qTJ0FjGE^I0Z6Y+dWessBAgVQYdFq-H*!-`8>Sk|>7&!21{Zd+G{6F<{h%2Aw4{ zAtC>JB@)LW^PkDb)>`c#lD-+o4PXYx&SBA!gy^cqLU8R&KS*0MFFR+QWxDRX^Hr`y zheME~rvjEZq4j29Cb03>)FVNONodT;tR@mkpqJB+Fn!~b(InN%&i| zD5z%w6l26ydW~ck5WXRoq^C`rF7>xR7whgH&tPVx%T4v={jnlyO{p zz1rrk|LO&cI5K`=WX?zurhN6G{-aXtvTx&ReetzfjuP;}fZ%*bol%_aKF&?!xJp0Y;M2^G!C5si`nL;kN*qq6(`uYjoOFR|6a?N)A#w`r#n3S-6=FxJp0%h_#I1j z2XYykdNVnxKSz5+Z^1fp#nN%Padqq^vBS^Z?|6x^;j&8>5E=zECw)sz@&&BRl(S|b z22Gjj)B^3Qvp#zF=>^qzweGfJu#fPabN<$r*l_05=i&Jr+F%l(v~_uI&B*kwr^=CX zXxd0&G;%Y5I!$C9o=`DxQ#D(iuyfNX%^uKSNe;O7P}>_wHPJ%wXV1z7$JkP^@TnJB zsv8r}gjB)5mXd*3puTvsJrxa;Ex7EXSq91v7c*+u#{nAs1V;U4dZ-IL)V)0hG{Z!; zT1(7jYP8!=*qPX$fb($SDMeugH>KqXdfWLQ@i`5@B`sJ&#~Y ziK+tM`%0>$D5HTcgb67HGC=XHH}YX@lJbk17%DK8KqXm6dNzB=6qLn{BmS30~XjnmBf`%#xWSx@l=L&((FC;vmw z+1XsL;t)!zG;`JDMX%HNP(lXm#)QCHeY=MT17k*X+WIu2N*p1VOd zz6x+G!ful;mQMtG1*mW#hZg!+$nmS0Gbnx)E=l!c=x<&0F+QPp^wO_z{=VqiWfnhP z>)q{*PYHQhHJlBC?xIRg^?OxP{q>#hVKsh}^C<9~jJV9|L1Ad^z8crAZ0(5b z@{^{X)RrEjh-OXG5B2);B5GF=lCAXsF6%AOKK8Mk3AxizpU7OLIIt6S3xyi90MHqc zb>MbXzYlCyvJ`uksxGIw%GKHRH-)9vfiof;)juXyaXm#7Jm?A#lv57IteohZ`i)-6 zg^l#Ct_WdAG@~@UiBRVZwp;L~{*w1X#-kiRE(C*$Ighu{RESo%O)S_4%p0z2MyFCX z%5PWXHh9fe_sP@T2MHl=gvb-+3kg2g+*np~0V4R~|_rjUbdbjqiiInrW-qkA!kISFr3Cd zR>N23-3&Na-A$62X>NyciV=hS!Ut^5Vq2KJhdka*nY<6(Kt{$N4>UcWCz;8jh)ik) zaQMvN4f8Y>Y}Vb?cZGd!dfTLh;SfL2@sQNfT2oIKpBvrP`hl%Evl?j!F%ZRD(X5s# zZ@{?*QXrU+5jhpksk4jAXmPD->=Zy4Q**lv&&;V#SsPUd`P5F`6}E2Vx=`IzyW&=| zcWX^FM4c!Je<_{+>-v`8^A(nH1J3<9?+`8wSBib%L5-!^@+=PEXOsk0Ibj|(z%8Rh zh3h}k`5qK-gK7-s)yxm{AK1B~{YSwRDR;Nboqd9(`yLsBp@stK&UBUT_|2FvbyPX! z2&PK?*=%fDV=A(ICQfITGj_6N|JVr)${X8(=1SpOTJ6w_S>A3ZYZjx)spEJ+TyzY| z`_CnMsOYr7A3@)*{y$^$(79PFELIMs@;6U9)Zg4@rm#MZdy`>rWiEC57G8(DC-OXc zqRgD^Hysiy6LaCe8hd+zMrVhO1(|0cx)&gABF8xvRf$;A$K&EFwprQMMXxdIFDnDM zO2W+x?5S%RJx00{llFv11Y-);>+I1WgR4U&du#CieLUKH`;>PYQ}u4=w&giwbc7lSNLb-D&|W>-B=UX0ny)(lU#*?AM`{0rEX!0dGxnp-L>JB12fnj~aqt;y6xR-ll+1L_5 zH=XX4#0jzn@_G?sHsC9m-C;S;u+JmPhJAW9WO}Bxr%pEnq9(y11LXA{B<3nP9qLx< zTa&R@CP>|!FE{P|)HiN_wqLg7x$~s<_Tv$p;}4VZP1awi_QmEBxR6R?QX1;*@{(Wi zA|RZ0$SRc{Uu?YaY&EQ-JQo<4kPsf=MYYPWz+Oh>wtPUpvM9$sd^zY%5ptb-yuDx3_D0%k7<$L@3fG9Xk;T-~`*;JCdEkA)Zqt-@!-oJz>bErhl8 z6*UOV&vI2nLx$>|eL|wo68X>-+6$t&;#z+Xq(08!t;1|j7 zP_Zj-Vmo1(Ol(Y1bdx4LEkcopK9DX=wAao$f+J+eBkfSOSoP^f&*5gu3^~-9wA15cm(cUic76P zKi}Qce^uh=FCET@FG)p~r40sZDqMQ4KRt-I3`|Yv zBm$TELt^M3_8rASv0jQ^1xQM>>ENdKoG;Qt{){wDy)`<=YS zR@Zl0zsKoL+|}69%66^W>RM)*{laydWR~^!^3szRZ?kBvGnG^{$$f3>{PzbSAw?qc zN5g$9ll}H#NDK`S0p#Dyk5b#jLa(V3)h)Bs?twIcZuy)t(ydeWuJubd$s<))rN4C` z3EDv`s>r8#a-L=C<2X|fFi+3a7?GAZ4qdjlbIE=ys!6P`KjWlWlI=qXB|79&n%E_e z@%2e2#CDR+B$dq9vKb7)bh_mgt6)=$Df zi%Viyz2 zw^c@fiw$eJMCcBg;$78qbyz7$MelrBZ7cWd#*50D@lrfd6T5q7Ov3TD^<{LT}MfL9wKAL#ml^#Sn{QaxMM{ z)|aBwNPDRv4cZsjXfjYvw}b$&Ck&MFi(p(tk+m8VrmQyAGr&s>7x(}EvZGH~0+0){bbcvu^I{v7p(;eh&Rt2us5X3(&jqQ?AvMNOw9|+hrH3^8N`OXl5QFg>Z%wQO%1aWTcSsii%+(9&oRYGj2UHIDcss(AlKq+5XJfdmQ zp%4q|Jk62v|Z&D#G|-dNJb^9->r}CS11T){Ue9=g+tR zVDz{Q9Q@MJPDcS#t<15SLn3E1cAeEgZtvXHT_A$}z^Y!q_5=tZFi>chs3Nk_53zxLXvG%cU)O3JKkpxS-%(DP(>h6-MqX|av(!Y>gE3wkDQ+; z>Jp@>HuCldNYj=e;Ts~sE4Z_sM{}UR7&qOjg!cJ)Wiix!+WA2E=~um>B&6gY?M|f_Nkp70uRhty=5MxsP=>@~&7?FnyHDZ;!Z9G9`pE&URAs zN>(meb4-f)Oaci$5hT+%=|{Kqww#qnWB^s`7D!#2Y{Mr;p*x)5~~jgywJAFW3CzH<1fo_dyABz zV*f@;ebH!c(;QEmqerk4Nz@axP(rwXDYGdQ(PEYjvV#H*@a7xJlR9yRaTR5jt=i$7x z@3Dq)aqXk!ycd2C!+HdB=P)b7IJ3{ViFe7B%sE${KB4}l2M2MpuW^i2P`28|`(;E8 zKE$=McSdzlX17p$PIYKt+AZZK%x<_=Z02y*j;*(<76dF1qMQAAB;_)Py-4rrM=GT< zS@v*s8drB#5@3hm&J^q=LqVZR z%)U(&Kv23~UsANRhVa5JX@`mTfH_1uhD8LW_o{LdUqPo38UM@E-Tw@j!q(%btvW7s_25O{` zY2x$+t+#doRqBP}n1%q@(D2Zo4xQV1=U)2merv3>$}j=h_(pg{jOj??^nSd`MsV?o zm;MMqV`rrstOdE0!>Rkmppu0{!aq6*queU>UO#@4p5iSi!JJOE@Swotf@nE*^BeXn zGA($!EwdM(Y&zA#>V^lBIJ=tcli=?=={jSQuZf->6|1zG#ld{DB*L_+S%FR7vZCAi z09QdP-S0@pYig&+T+dv>*Y|op@2di*PcGAi8wm_{0z7v1$+&Qt2Egby#AnZJ zpDwPC8p9KGgO!30AHzqHWC@Dk@)Jmpd1-}!62iT9XoXmQY#t8fep*tC)Hiok5EImt zoEyDfJkdx#y5eke_gwimf0A6|4KJ)z{e{ZYC(C6UC0war_%#NBC9V*CJUeOF88%HB zF$%B$^{*Hur>s96wo>MwWJzPFW~@Aw_*O}N^Hs&vc#k=~Tb=M;MSJ`~R>+qgBM`65x?C)fnXj50*0h1HRHa}k$w@I!KU-n;p_ zZ_mCxuyaImj}H6CftCxyKLk0+w+8QzccBySK+70ai#u+2bo2b0_Lhv)Zwt)!p0ucU z$fx~~=*r=J;_>LQIl=nT&G#vnrnybij|d)=To8vx<9 z82|`w+}pz$j*ZyJADz}d2xF>mS=`V}HC5Be$<+-s|CcHv+R2x!Jp8YMIX_iYEXAzY z(BoWH%Uj-YMGfl#KGUYoxz?i<{rTqVd}GU)4sRv$b;e`Lgty>_g~%(X|(l{x1a;}3R@2?QV0FNZ6{GAtxEwa)BLhRrFP0!`Zb_P;Np zXyPX5D8J|9g-&JUjb?31B-J5{nY|^DFJpKbIJw>G-X&U*UurovdhHKD@MOhe5_|*2 zr!X1HJBNl2`hz2V$yOviGUNEI-HPkB#~Jevmz&J49BV@zJZrh##z?X-C0|NR_#CDe zws(tMV3l|@y1mBHc73GMHZmd`ELkNOH6;uPfd!Ui6aa;tpp3;4oH#4W5gLe5Hc8O_xpk=Ae8<`lB+vZ2KiAl9y= zNL<(#{iC*!vlOS)V)w!#w3bt){o4hyVO+?qa~o<+YN^A=hAOnUK>hAip!M6vP}SE6 z@Cx#;sB^GI!B|0npdVwE4q0RPJJmP*&}zb8pD@rm>rkb#jQ*V=*O%(!%(-cfH?TLr`s&Z z%({SnG~M;*5fSjQB_~T-zID}8C2>;$CKcw$QEXC?~Wrh%><%nPPoZJ`6V`}60<-;%4T+Y%G%Dx)f`#4X!_P#U&C zfAr`l1;Pf(#K0zDB4k?c2be*vE0?aA)~$AO)mAqOj|RLm?L1hv!< z<*nG{#IA=w41MbRMq&>-8-o^Dq&8z4%QSS7G7VXO{52;ab9-R8Vw^ziK$-)3J3?!i z&jEQF)woF+m?M(JS+>&};YToHjCL+~tNm& zJf5M6J|d*VP3`=D>Ktw_LFhYky^u4Zb8lm3N8I#eo)u8`@(Jh7Q(>)}c;KvtkHZlmQqeFwB0^WV01V8p@}ZYk9>gzuoR3G(@I*SZo7X=~geL5XsW{>-rSQ{lYMLTk5G@PSY29iCmx+?Tfmwil2;_CIr} zKB0S>L2TC+71fF4x+cmL+;FXAdyp|eZEEgLm+^|Iz$O)1sbIqy@)GsEgvmo9)HEdR zhgSBWv*S_w9eG@U1f3}n|4__RLigd=$F4a^rpis)=G-rWy7B?cJU_KsA{9(YZH@JD zNhQyY8MLbP@KrM8e*^cMfNz;o-I;;&wCD^2C}Ely`LDuog85G7l8g>xo{I+9ZSw%` zw4M4RJs!kJr#aNg%C-$dN1g5 zhPz(c;b)m|lh#~ZR1Qcb=FW>2#zAm43|kEf)umIN=YA;OLf@Q!M?5k7Iv|%^*pVaw zb{(WRBs}P9%^m8SzQk=$2Y~u}`uI_c~yIfOn{={wE019B{cujImQNeBf3f&=jz> zNmfMdO=l0;`Zc!dzSUG2dS@k-E~&`7sw9&y@1*n#FYCbmD6ZJ`%8XaO_%-&hNw^aq zoYY4Zl(40_Qvg6!UIn`YaPb3nbO#mqRd$ToDQaFWSZ4$~5WjO3ZRoVqODarv?pvz* zYoJ-B4L!V3rj4pYrl(4h4{mxj@m2K!GdVx@b!ey2$qYBYh3}_Od`-8|Dp)eq*U^lh zbLtwrd5Q;1Yyu)sco9v1o+g8dlNOU!eJrtZnA&)pqR#p%_Tx{_tr(BTqLUcbe@k8|caY zxoEzAWt5JV$yB_W{iddcuPvZXvukYLb<5XMz2*-Cu0n#qn-~R_xSK0P@^(kbJ(d9= z?_uy*O9XHSFR}VHm_^QoCeB-X!jt~YTw}rkCdoycZR{OF-XS1KoDZI~!Q$~qYwIy;qDSJdYtUR3%oQVLv6-0)=_18Abwwjrm8&nRv z)-~XeFd8Pq+@2!S(CqWO6e*7Cm%Kp=Q<(Ml?pl-669h`Kl z_Kw=np|>1b_Q3LA+p*{`Ctr?eFrm3{{IE{N5A(IU*QIs|FI2yU1B^%ocL)U|z$Qr+ zghvpgiom%jnK9hyq}j-qtwBP&ag`}U{xaqF)6w@I?XgCtj-mVW^TdQw;S(vkIPQgc z8F+l7&M*$$?t}8D616md%lakqgclmhnD7#{cTP-*ld$1|UmhIHz2hV7nA|Zdc=Ke) zXG*e5*Do@%xl=iY61JTPx)E^G9*VlVjTaOnK`rMHcOXPxF7;Z z$p$(Q&y;M`hxWC9r?1Ev;7rWjv^t<%64)V$bG*s9)`?#6MEuZBn_`ue*C;R*UQa^; z+Htm&bd{LAQjXP6sjMBEPV&;GMBS-HlyZ9)QyX!90eAEpt(hR&9hYXkpY7Xlxi`!kIe9Erq=@_;ZY+zXjNMSxzB_+%=fLz~iu@O+D&oW$?1;D*U1?XxHd=&b40f%uPP2a6}~*%rW%%jENkN ztjGAQz0!$gwk=TKw>`6iPBhr}8f13>5Ttg7&}(bzNMat73wp+T-OZsUz%~>huZ)TB z@zOlX0g!w0-7kire&KnxSM8NUEuE=^&R0>B1vk@1ijcd){?!jB)pE!{~yJ&Z4O zaRHnx``D5eHpjR|@3MYb%%n{wz;o%T7(uEyJ$bLnq zqg}IJYi|o;b6uE{R≫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

s-A+F z+DsR#%Zx=ifKXmm$N;u`r-DK`p1eKmKyrm-hKXa{>@yAT!X7-_`nt+@*jq+Og}908 z#@Ex|5oI15(L$d|{M8^0s5T6T+8b^o&mC{5>_u!R3OUG*)AX+=&!~z;#nWWe0%6If zQ(TX!`J53dZD7V@oPs@;s2?wRorr1h-#8f7+!PC%6(HGFl2PebnFOF!YYV{qEkK+I zg5@0zJhv$5Y@2#pTo#?9KxWv~3N~oFF7^*ZYk&G?+f$+9dHUL*VJO~fOfq{_3zUYn`1u6sARecM}v|6{y zo2&=b{)RuM3o}-)kUI6g95si7BXRJ&neLkCLVbMqBc19FxuZrJe~H%R0u-tqhGAn} zWJu3&^Mz<$kZz5APNy0`K-LvgFFp#rP7P*@YC(^6bveBU2-dkD5>Si|b&EDWc^2_) zr2w;#zM#b29BeJB`R%nNd9bmm6O3A$KMxL?r`~?0Z7PXwwDdtWAf5yJdKf6(C;(N@ z+j_0K2g2HbhLD)1sb!$}U!S6rt|`g7Rpfn|-m{#IKAk_l7!y8zLdG313ZG+F3OG!8 z0`lV`NfHjC3 zD1kh!m}jeE$y)18B1?qj3x{a&Hr-@w@m1qgV0>L4X)t+=IU%?iN9~vVw8h)0H ziUjuum!jD_!voL80c6V@${lRx8Iw){=lZf@Rg(xLY-DUKtoNp914wmy&{jsc#-bLA zIH*v= zqD?H@7EP;i(hF?nV*r?cckvr~Ql+uHZMoxDl*bqE@SNQNls{Vhq~)e&+9~feia9smCk*c{KJm zdS0|_7d4x0n$PlAFFyS6=6U|dr|(~&iT=I6UnkzS4kqMA`Lq?}Gx%VjmqwUvYM!o`f%E#co2?_wBm81b7#{#1Pso=nyML10y zYew_)xpo*akG}I4PE#Qxzi4GX6l;j6@lr_Ll!2fq(W;_Zpt}vGAUra)w&Iv^XRErV zF4$tImI4gSb8Z|fx?Uyy+x8o>Ute&SqhSDeay`=-{OiU@oGPmaR^Jbi{i1?F(E}`4N7>-=Y?vEt50YMWJGwV^ev$sWe^<#OhV#^Y1 zj;nBb)IwSb!PuHn03iMV2frmA<{MI5;{)~_R!yr7VVh*610O0NHz+%xpc+ovh%#i6 z_2~_*J?@`18zT7ud;?Zj0`9v_(8DUxM5$VB2KG3fC2C9d?-31f@(!Lh`&Y?Q=+{&7 zxwXL}3mM-U!_p!x4djzpM&4_1fPpDLl)F z4afq`lDthY2G}Z6x^P_B92zZg)f+r4GdC~+=Mwa>7EdJO&kQNamORuZdSo<4+GXS_ znvy>h?O0o|8$r9b?Gj`#vy8mRgbtT=y<{2C^Zfd)TH~VY;9LPUF$M-^dReAD>AdUy zq9`#<?#PxR5K@Zre5A?=8`30llS#?#E!+hVO62BgMe zk0up9#yS1>{6b>g2lD1feOr4=aTIf80DAjx;84{&=klBY??HU<4m%K8;jMN0V@E?w zlAkPFO2|&7RRy9nrNkVsxpn|mH>xTrZbeI?OLe2xs6PiwVda4sP1S`t3TyJHdIpWp zcLR+;m5c)2=f@g!y>)m#z@Eu z{^d+XkEsK1df*5XbrON6Wf8me;qi-O`Ur92C zi(IUM+${IZv6f^zK|xlrL`nj9)uKg^@N5r+ZzE^6#UL7_Todf?mtCvl@>o%ID=4G{ zQcQ{lIhWX?M<7sS$Qe+?ELKEfKn{Ug0tzz*PrG5XxGE&eqPa+Mg}l|kYtfg$aWTf4 z1hqIG$;d%+tv!{DLNu78Goxx0Fs=nQMKewTyS}r~doq|4uYeuzNVrGW7VSSNAVAn@ zKY%*Vi}@Niq$NRn*UB+v$imSkz_gz!KE-DhwAUDkw%Z1nZx&FZJtA)(=U>y2%V2E` zA0+FW+`iFn-IW_y1~in3aFCYd@nF_cW*>84K--o$J*%ryEC2}%nsa&c^^83x*1?S! z1P?aEdL{!9IM60ov+6+_3wj~~xtmT61sBoAZvqfT?!i+!Lr+$xOsoP1xi!EslFMDu z0FEhTe6_?fp~(tr^(41$5D?BvkoHve-B|zjS+80wDnyuc#e~T zkrM)=*s2YP_}K{K<7i_tePR$XoJEQeasxQrtL*t4e!=50N+k-X1XS3R^wL=;Alcvg z2#G&>)r`0C!tBbqZQNf-vD(9xbkw06L*&emu7KA87D&HX%C$pDdmv5FJw+CBSYd#U zL@3Nn z!P17p{xDC~MVk&%KQ@r)es2|-uJFc24~i$gtjtlBrn3u}bBi)G3gaX>wC!%ASQZh@ zJ%!Lj-;wx-q25-uqCj(wmkR;_eU6WDcsVX+j*F>XJCbXL)~#Wxk21PK`5PIPX5WgV zr|}oEPO*?sRyL?Ru;2%M>7eBB`?z<~;j8s6NK;(woD&skE@vS1HC>A`*(jT41BYP_ z)aE({1$#U@!^kih4>xq8A}j5?!}@*t`_zwh=L+a>4w)VwT*La@kkT~$UNK5&VlSjM zmYC5DWTW3WWY;rk=n|B_dl0S|e?5!VUUd_8CTkyt&{0@4rWkV^68bizOOy`hZHb}C zqL%su2q6vQy>a-CPxHGJ5&|nANjp73p=1*!qI)0J4?sI#cxs|)p{IZi-Nr32ZLB!@&k`>Lb!?)>m_-*c;5=@0Rg7aw%mq63L3$_KS{Tg~NsNXek%%2c z@zU_THL%TL9ct8!M$yFuDo@uCWY0XmVM;=P@XFv0Z)@<3fsJyrKpf@5v&7a)VdK5t zun!y0c}zbk-Rse_V-5y*ocbN}hPsYOl-+2nbXg7b7|N?rRMb#na(6zAlLscW!WoH; zf5ib$4ta zVSxe5Ty#F@`e+EyrWQ{a@onOo%s7WD0%JRrc7C!xYTd6ko}Sjrb%-G)OXGO8Fc>^u zRKv&zqtlYjhzPTVG$KnV`Z?QfpESjS#Gz%$0S-bh|3EG;x^jT#0=#u)MF%NBxy73% zy%2N%rjL%9F2sc#bQ_P#nUY#BJKM;+P;aMeG*Am57{t|fd5%HZgb{0Wkc`DB3he^n z?vb&RB~r$@Yd7yKh>913fDwMI%1^X+VeCAV{RCK2-?m@9Di+`9OC~sbl&7TafbUDd zDE{_0PaOH%-}I>$!{$(Pv2crB)8M(Vqwdd!D2rP0m;8>y@*SmEq<3Y3; z8m&tsv}SY2A=$N!2$x@~iY(nNd!6cbhJVe<7F%s*Q zqi~*C+{i9wIgyn-AH%BP-dR9q1_APy+`kMRab8K0(pe$ z^8$FQnEmf;JX4>xaKx}LK&Y?mcs*YS2t77}nG9`wKYjtKlRY#ihdQ#gGEZB5x@5Y? zx0G<5k-3n4#hgKLsBFW*CBOjH*!&|ys!5^%=%e)c%l9Ah7e9Y^_cVX@=Jj_kzfZ?P zg)kw60neH^+E3!=cqlAYgdPy6#$86oZ*S?FT$Go5U&!2VaU|1P09Vchl7Lcj<=<)Yd(SFE7 zRkSM0Qk|Pt%!qkb>w+OMT1A4LDf3=*aq` zTcybDI48nq1M4o3*?CMetT!@sCP2d$HMTp;cHL_F`>b!IX_dbCTp1AhOVlFkc2ZY1 zmJi)1qDS2046qHOngwMTv2KUBLcvNH(wxlP>E|do{7Oj%ssIs(9SCTbwsS+hPY-W` zz~T-IJxOQ=|D9>KA{wticFD7YZG2ya+qJ%z;!*Mnt%=B%B+lU)VGIuW)#)lJhFc+x zZYTgIfWfLpd5LB#af-D0LTbTi4H(JN2F6)ETKWgIfa@>KpWwd8?ntm-(hb#2ZRXXbt3*06-}A-!lhsshhfyKvS~Zt#Np=T z`z}%h?ADH5G*^z$>JZOzZAM|D{)Ex| zMFEQ>L-nD9O^IE!-g^5W(8{B3WZ<4Qzfh(k<@Z>G7I`wEEEV^}$~pzska}ffjNMl9 zLiM0C96BEX8SakGqtgx?_LgblVV}1`aMz_dz ztaRF1w;`PSi;mkj2=+TR5*tVq z_~R_^JTzovUZ4-#-gMFoW0RveD2F-vN*K!HyUu1=f>D2?gO~W4fll3qq4gG2l{r?( z?M3E|k+ue5$PS0EV3^favBlsn1WM+DqP=dtV~RJt#&O4sbXfkRD4I~*lk#pyAzQ{! zK3w8g#R9#AEl``Xz$A4JiVmQGL(y8(8{eftSK|ViBpziQ-HSC+xnXI{nFCwaeT*|DrHA;j|ti?@OUv<6Hqj0?JVoa>e{$4aQ(gPT33_rt0k0T zML;snZB@A2H8zUPy2|$ykiS|fvi<37kbb13^Am~$6P)LTorN+Mk>~~*qq2@&CIS$e zY}S^RAoWm7Zd9--W~TOM8;^79B5m$m6YU6hiUN)oKc0o&?uiaSq=Iq4W^$&= z5>PHngifi`aAK7o;a4of5&wt_G%|%hJ$?5&nI=yjCtrHkL%p*>$(DEtT~M1c_SAB- zQOlAzZ_hT!h?)oMKawL3B<9AawSY`(4*c+|>E-0p?F&@^ydClKOp<<_0XzMDcc~$V zT4tb|u5`Z>Gq#lx0X=jZK1zO|nd%7XmSq@DHK#`GPF9R5~qaNf)(ZiAqaWHg=X4W0l~4OPys5XOpgHEvG79C}0`|IO)4qQbQM<#vUV~%g zt2RcM8F@}|6NZ|OLkl|IVpHq&sg2o?SH<-=7h|N|C=nLh*KOX3P?5TthGeBti~6#n z6n2_10L+AddhSMw7o?jOZJ)adAYUS)eJNl$@<^`2f+vdqMus2)Tidu?q}PHCRbHYo z;61*Lx{q&0pqZNcGiPbvyxLExLrr^EacTDEuFtY32M3TvDw7Mbi4jW5Dz`^Z4`j7? z8_G_8nmv8fi*9VV(En>&j^v_U1mSRC~wpZ)S1PVjVOze z*Z1Tae(|a7MyphZFMHR9Fm{Ijyvv03-J{zx5|EcJbWP0od3l-awtXR6r|(jaqt?42))s~rzvDZf<`PG^gcyUN8W8O@{+OF-m=dI@0z30u$K_gB^P(*mmVl?M zwXQHgrfl8DsO2m~u(CDzS4VcS?4`6QmPN9$_gTp*^=J3l0>5K2X?itX`2pL30N}+U z_tsC}kFx0wT$obXNqpg$J8O{-7Q&x{iE0;X;v2nuIf^w!n+e*sn#ndgHMDIQtS*PQ z4|8R`lD}zr0S#;IN4lqj=DCqu8om}k4wwD7?Z%=0#vQJRD^>#lR4_2S;wo7170xNti zUd;O$-CZ=yPu^Sd){{-RdlScCEiQrL1?Oa>Q;x4FW$l99g6UyCgZVL8$KX|K*7hf3P@GHd-j0BAFKqPZ)=ue_G(Y=7tuZXue+yhq_v3T@r zoT?8HFTDP&sLSRWlL-Ga{`N(4Cv*vpB+ovJdEaHH5f&{6(UbJcBDTw$Y6sJ9`Dczdq6S%6^6o|7$XC-;_dP##~*Iw=mgXzi9 zxa>9>WJh5{H-M*vvg3Gof{iG_c6|NS^Rbu|JiQoNx`}7a2EAKNWcKObXaN=Sz3RBS zOk8MEW1Cg3T`c&Z`xjLf^eCaDRc9xO+^*6-dgRAOp|o@7tZ#7zSEe=eQrC(Uy1`nS z_csP^k>acf=f80bG6>ga4okZj?#v|89kuf+`pJ)7DAokf2f3;>!`^XKVC$FGyQG72g$GxbD@T9Wo1vb#U zufU_cp$qoNc3P1;z%yRWgn>%D?P7t%O;yX9v>i@tM=Es9PZa1Y7^Uy-rU#FpZHEZn z3Qu9ek0==n5kBTus!QoD!l@!?JF5@Ho#KjK{wB{;17=%1K|z*vIya2kYTHg$UNv?LZP!&S>oJg z|K+Byp_&wN%~kR40=^*fuRHmzu zOmD13&AbbN_rv~LeeWZ07yygygTUW|Ycn*+!#?Kti2GKAmm-EDbc>8g6Ea^#w8caR zswR8^O}nQKSs8k>mWenAZ#?>3$AvTOx5`NP&aP!j<5n&Fxz;wqFtHQ<#8K(`fmTI5 zXd|~@AEldI52zxM862f#dV>#D%ATg(@4YY8A9L!4hSnd&eMq`W-w4M|_L(7=nCao` zpgkPp50+YasQ_f&UKAq+Vg_vJXDo66D6|VHR{A)ge^IlqIEQh{c_)z5}8l5PGvTG8jU)A1idEkcM@Ou|Q&_j?c!V^p%#0wrJAJN4_!~>1m(kN)> z$~my{+7w?k@8~8k`Q)X)^pT0}L|q2m1S3o*%AO>=8(QgTg(O_dRbAt2RH?K#-YP@4 zQd2yRyYTZkJ>rCSgsG;&D2{{uZwJv(R@WQ#Brvy<+svkLqI^~>PmZy8V=;{bzXTYE zBKYo@L9@Zjy+NOToP}-PGquCm!gJ&qOUgDVCIZ_97gKamJi~j)Cqjs`V!6~@9N(ol z2_f6eKJsMucn{Vw7c}s4M%YVK^uXsEd#_{Ev3UD?bT^x@92-YWEi!2SC|%L~Fg}9n z#aC8Yp&H2hV|(x)Vv(<-$mdPO`y_`u|7&$`??Xl;h9KUWls|FYd(t;0^y`Umd3nNf zn?=T{hbGI$1XI1{YgJw1P2`y6;Vn_O0v%yGL7efsQ@DUMO9Mru?6M;FWfsz!0rh~F zL}$hT8PvN4rjttyOIevJNPJo+@<3I{5fQoV(cKs=e?On z2tse8L|;eKxgbws^8>8H;58loZ>~u!OL~@wZH6hzoy(%Jj`-GL(ntrp@e@9l-*Z$( zp2K+ZoCVmFQp(0a$Ol$@q~b*FltU>A2fwpYaKXdR#K*vMzA;wMH+FTFX6 zDBx~sw-B&qa5W@bgEpF%$=2d5avEM4TZ|`DoKXJ^a{e1K%WszUjv)0T+*=MSJvJ+$ z%DMbD7)6imVc;R2)#O_Nx__#?ZcUpYiaJI~#aMvJz3<_oM^vSBc4*i!xoc?G z24b2LcY*(3A_~ooe{`&c{daUI#FOvX%slz>+TD8y`;-)#L(QyV3!6nvSx}Wg4*H;Uhgo(K z`FaljdX6RdoALrcvG6PDUg*MokkA%N(%#uk%~?V?@gdDPk@x8FB-TE{eZs}Un2T$X zPE=zEeT(ia9}iFap!9ttPkQeu4aK_;g>FXPx3{^PoFt`7GID?5oia>R>Qb+}r>+$#O7f!xh(khD+Qsb5pSCjii%#duSm zv-FVXBhh8wK%rNc6AzgLU803gs5BRQNuUQEC35gl#vkuYDFPc9%sWa)L-44;f{a)oafaOOuJB-+?I} zoQ*LH{140#jpQLp<$>gd!3&buPjmv@)8t3Wh`c_>XoU;;m?rUqXQs_;Hv7-$l|uF| zdj08#%3Qaxjt;@cBprG(F}OpF%?}JGUy)!}uhCh#HtGXc5@I<}T6z$#riW2%@&pPB z3aBp*f1G`zuf>PXmst0lSYBVtgl_Z=Y)AbYGjMyCSNhmHIl>jPw*!`SUy>%Um%Uh*15;THI;^1%k zz32Yd_-}_7u}3&Y{$**qBIYR+*??1advzZr7??(`9C?|LT#44}LS|c+cQOEAQ&)sH zutu-{IJ1$?6p`ZH<>c0l!eU%wOsRq_52dnGmlwlc%jcTCM@RTtebx_w%lL@l`*P-P z{zes5ID9w}x3z=Maa8mon=zHsFlPQ52LT>IU}GHB@y9(sGJwf>E74PgqQmA-tQ)rH zOwcuCOj-nvM*$^73WhwKB2mXMdO@Lr;P5M0Br#_KCX&HF;*}w!gF#G1YwW&=A;M7LPnKE2b0IdS!*fvA0@IlhE%? z9Ed)QRzEiFA<$S&qSm8uOaE)fVetIO zuI)XPK8$0(OoI)z1Vx3}onu&NY zbZPxid*z@QX>)0k0eVlM*Cg`p0LGrIYn3Xl!%X)276{sUoBFaTHW=1QQqsgc=0u!Q z2S1T;B(DNej(jVjn>!`W?d8OE)~TduZh`!z-eLTk$^X@;shLB6qX89xJD!^gc`Z)A#^#8#G9lgz2u4SaG@{rz2fywb!O|-C3rw$ zRRIMn&~A=q=1Pds{ZY#?>a;NC{nd?5N`%3S9cKNoU^oXzr|7P?;?(Y~4Azu3U!4Gi zBRu6X33ebVyFMW6Mc*Bz~16=ajZ&A8OPJ_3%90GpY_Fiib>x+D~GOXPes)1 z91h*I62!KD7oi8b_e59JERdTEaFM!kRn%y;a&8e$;!TXQXYb}h)}_^w=Ovavm_`w0 zGSD){LLz#x#4GZCzS53PoywW-3`H*94R}JGhHbS5W^^DROgOjFDLVQwAVsH|d-?ji zH<92gD4xOpN7RPei%1XHqGO`B-t8(sQ5aiqFeN8mz|qr+T?-31!AGOT_M)Wux-Rh$ zv?F>C%<@e_hlBdnv^Z-WzHsQp7(@|>F~O~g2=qC)sd$a-nVHEkE^XPzfXlPD3D0ZF z=17lHn^ZBxPU*#Kvsfce+r>fToPeD3fKKE|l5Cf9S;$wb0U)sp@nG+%cxb}eMRX&x z9|#NwBZ1pDaV(>qgwgmdrai~#7mW482*G7dqpmoAx_Msj5KIfpWW`kYKA+Ae5r`M` z)}FRX*Of_gjIX2C*CMbECPFfXPP`k+D28F`=tbhNQN+WH3}S8B7mV+M6WssK=5mwe zOs_;FQeV?k&*B$N?C70)yb>|Om(B5&(BwB5@YORH21eikZq=(l>q}m}Dzt`&8H080 zxI^YqM}D&W_^1(i$N;f(d?h5;&PPY!Q2jU!X}HZoXhRt2F$VmwHz4rS`31%cKKA=j zpSmNvA&8VDT`eOt#-c*p!)Ex%yYWwir_n?cC*YY0NkrXTaUGVAyT z``dD`s>C*t<;mxVd^6_Z$J8BFy7RLK6_Fyc>T4ubWU)n3#1byvx4^&jHD&rxa9(Gq z*pb#bH07y z#}LVmMC$GhK9I=z7bdCT`|zl3p`{{lijdc4NY=AxW!B!X1#cPXO|WPald3V+N|(hO zVovwL{!C%KX|V&WYoZOg=uopA9Ydj-d*v1%@2$stP}@@kuiny zr+5%_*};Yf2H@3*1KBn4rtm%9KbfxA)ZW#LwzZ{RYEXW?MLK^?>JMTo=G`SWHbpLO5|wo0@$cFad$(m+(^#$}5lHx> ziRV4F457TI8dJ8TD)F(ur>dh-M^`)wt|mRl)jA3y;N?9vH2FEE*5bZef)d;ZFCZNE zRJRA+)I)O$z7%_LV42+Men2Zjjvmm&9L3XPDw`vJRAqZm57s_8VUu`F3}Zjwg6Pb^XHt^Cdhy|hH_!7wK7Ie<_0v}` z-g^mAQv@om?WY>N{KTE~zc9AT@9u2HzxG?eVUT}n%$J`8MtU^^Y?WmOVdOW9uJWUy zRq~rY%8^g~(F=U`hAdc+mlEOai?_+|p8Sg#mBv%q+MuKSX0c9xlo`3q<9nWwV?wXX zniDhMBk%YpKF*Af&Hh+kD}%W56IHv=fuMjU_z!p2m(uaN2fYK$Ve11TJX)Rd?KMA` zIjXmygVKx8hkOpyP;!uPG+xDkDIKiq^PD59`tFm9E$?A9hu1`$-}~m*Kh$pUmI*zuf5(s>*ibO1!!0fK@DC_c z4*c~rdHIGK4me|=S?j0Ik$!po7`^t7N>el^&oO=jSR2l z@v(NlUXPfmQ#fjLv@JeEbAB;6x*j6PQ44Lt?2|B$;4)?AyGg$%?(SJa40YvZWcV!$ zjOq@oSJglbYWqs8*P;cfsA%@0#Ut}b2O!Cs+%jx!!3)SUAFj2-cp&*}I0nyh;w@v3 z1H=JNscaamZQBRV%Bu$xU*w){;Z%eGouR_3Z{8pC-P4(&T~p|SFY$!YUp*2RLiRx1 z%c%oM{-KDnj9!1@e)_M$6L0=DH_!0gh3&(GXq%43z1A?rf-ruF({9rMg%j}Y9kPWh zk)2p}l1z0@NgABKj!(WmoY<4u6FKYt;XX?;sFOuSGHd;i!?9_FqFEiR)x(>!$x0My zBCQ>=jmTJZA7Y7t4hRQsSog5Q9^1QZKwl%iNA5}Hd-I~jlyp{r3#H{aEBpyT5i5NA zr|+j20Kv@9B#Z`vyjBH99W_dMypZzBkWB0i9lAvsnG-gdF-?-pfX~M8$89E`asT+? zyXo(p2YQSlTV`^Rm36Zx-fL=(W9!qC3*PfBmzL)Jz;)sG6G&Wu#Tk|o95Vqdew;~E zkGP+rb~ob&jN0=|p|iThN{4KbraWqC40*ns$&*YTbUm`AKH;_m7lpTIw91UpVESjH z=X};6@BS*OrPt^+NlTwSC(3Aipn(G(dQnU>Pt4gA`?<=wu}RH^Pj$yqy7dJY*>n1t z+uxni{R8OBf2WSknDj2aw?=XV>3ik`=_G1u9|>jCYxaX-xUwN^*f<9h27`}KSb$FzTA*{J|vsoF@UT$QaIJ)g+u1X^)|t+Jwjp`a5-J>XCA z0s+1vo1T$JWvpa(?w-gFa$2K%wnu_$aJ< zt&L1|Gz1eJLWO=rhzuU_I+1eI(H+Ss4u8m@D0ckV!{`tXu9eCHc-D*@2Wkx}EF@Tv zZ3t4!8Aic2uf7avkv(gTj`t-QM&FhdRvUjVPCgu(%)y0naV*|+e5k)>KAr_BHX)Hw zIyg{5Tz%LENh?%jk3zt|W?F%vD>gBi)H;}&)KMOAKBNM!1xhKo8|YRdf7q&715{+m zJdk$1?pvu|%1h1f>|HcJ1J{j}u6^Npxcy9P@^8(D(M_O42&`n!$jbUa z|8GJ?X`c0P@q{0o3!Y9f31Mvf%(J?JPBCllzfAc_R{!Z2zTj1udtLli9w(j>*A9s) zDf__O?=&cSKFpfv=6m_{my56wJB8U4&L4c8W0Pi4mu=IwZQHh;S!vsT(zdhGwr!i0 zwr$&1b#>ghANq~x{tqYiUVEK6$H@D+87#hYJm=YnPWt;FP?KioT}Qot=5@4xNKL~3 zl@Vg*unN?XC|IDGAU5&AxZ#p%-6eYml@#0MEGV(}!z3`0|pl@G1`&&_WXwJ>HC^ z3u6^!O8rf?TyL_@87b44999V38#--@%DD+2wkHZ^%}a=4`#4iW7Zj`?j3%MbtPbl-u$ZRTxo{uZy4B@uqP7fi^QnY{$ZPayy=9rOR zPCXYd*KIcw1C5O_UxJikt$C&1Hd?X@r>?EH1TSCwHWmAk<>-xm=eLSg zdX8X>Yt-Fx#sZ5VzovsVQ|$Y)QA)a6#;}bMy#wdJ>&J%4)w#Ht5^}zc3uho|mlqX9 zt%u!+{>nDv&DmnCiKpMA#U-YuyVp0G7n*-~?1WaK>ycU63bK5iL1`Vf)6*Y*D86%m zSCtZ;q{zCT*dV43_wyln#MBEMtz+E_;h>$$r&?^yo3^G({{w*v0C)q^kP9hfGqM;1 zaBV>i(6yc=Hg^t^W5q~5>Xpjs#>-;isO+Qg2r*z z{b|ej(9+YJAeZgCJXAhl6P}Wt|lTawavr(i}+k@MPyC)-|A29;t}y9ytY{ zfX0zU(E2@XkDS>7YcPG+g9`>5Rve>}9ad9Fb2lA%-B>QZ`cMa~((;RPYhD%LFrjO> zmrS&O#Ck0sL@E*!iO+1{fw}*vrw$8Hdf!pTH4KJupX?(yCt(MPFV{m6&gROES zL0??zeMF;sI}Kb1xeGhmo+l2}&U7o5wQI#06?J`*p3Q%3svpLGKwbqPxOPUqIU2hj z)07yYTX`_Ox26_sY-@0nDTyL)N8@QAP!VDHWhFn^@$ol6GDq&1d^BGf9+3-=-#?(9 z>A6t+7rW4P`+J)5kFmTlOq+wnH=@PawiyU;8u|kjv$cQh!`h-Rlt_WS3oLD|90AS2 zK?grM^V9^lwyrccpe+y$0x$(RI$vwwngMQXuJEfk!DM&5efGoDkl?K8iBTBv8L|9V z3G?W%hefN5s;Y}sz14l-uBVIznr8DCmXnBF8-M4$+SMp8J;Kbc9>RlL;$p>PZmm?7 zVqb)5$b@7GU5$??(XJ$Kwb~8Kz)Phe0wSC~HQJGF1-5t6XVzyn_G^E|-{$m3{#NA@ z@OAY-Z@w7Fr04YqfK*X9-g(F0-LQ~T!mhfo_+S-iCnDGX>}WT4DH9PR00AY@{$FB8 zGY=OtduJ;L`+tX1kG9=^(|Uex3I>QU;GmH5R8;~p`Th~*> zC6oUFj30MzGl(lNb#`pEb>XizFvdL(r&w8wi>M}=Yc6C?In-ZMXsS7eEuU1ZZd^*M z%Q<9xaC4*C(pF}~?9?b|NSTP~pOqsgPt#^|Bi+7NOkHD_JCW4Yo)zZ0^~?^bq9lsc ztXfH=)m_!nXrx1SZsfF}wa`iv(N{L-#{^k9d^RQ<=Y}^rm;WJlQy#W785V^ODwRug zPpsLzNOtU-T|r=ds8XgLgPFj88r~*BFEQwCax9?5;)GnfQp7n>SD^GFYxEsxkTxcC zz}aD{q)zqp=84#B;7>6};A=D^Zm0COE1V!05-aMZrO+}&)31n9z35eqE}qRHwpjt- z_|Z`wOrw=42F46W%q1YiOq<6SxhGoXIhV`8AP(r!ir!@Obb^uUBVzEYB6F`?F{M*9 ztVGSK=u_mXTA2?d+vQIfFRrl6zs8y?*Uc{oqBgAfyn@6HPFJSJpwQ*bv4y+UeB6 zDkgCu&4mnu${!N5k+^Ol)OzTxiQ<(!JY0IARAgVq40O4eHh<;ncat>ju_hLSB}TDB zAdO)<8X zGN>qJa{_x`i=p+A9vtQg9eev$+2iPMD3Tg&AB55C>TQ?7+#E{*<-XwN*w^@ z>*xja*Q&-B6;M;xGBwWV7%yT6kU&B-L^tY<9j{d$QI#;St?=Js!8dwyqV;5*YTA{V zO5E4Zy&9WM)TC>A9G(nT=FpK3r7xJ=BJ->+6Jw;^v2ENQ%qh{y0ru$^(+v|vNkYWn z(b5yZSPLrIaz*wF6ElTa(m`|<;$bAv&EIjic18~5ZKPnwxRD+wGk_9jhq4Une#j{r z!f<%kf>qn=c~dS@6mSzuF0R0@5JF!Z^v7h^0!4D!|`fGbLhoumxmfN28_!uWX_d2aafU3|qaN zA=!+v8Jp?qRPq4_-HVf)O3>5}^>)`lk?_JcpW9jAxFHagc3k z6-%pO57NF2mVobTIYi_v3%7|-&FqHOGZY_Eb!*PbV7xMMLzFN$kVQ-6_=fn%XcV6U zF_S5GgVSnE;(qz1+m3`PKz0X`o8*a)014CJE{?2Z3%4a7r|In?5%jCBgv}~rHXmS( zV<~ylc&^GTmBL+!XDpmA?U8Fh4IJY>iG|Ejt#(`usk;Vu+|lZ~mUOPi($+}WB&XMn z`}1*Ds%Srg)vg?hF2T1)T*_X`Y`A$>{ZS~>y&F&3ewbvm*NeV*@hp5xbI} zX@c7ABxc7N$U>(t#E6o30Z+@$=R*5%g(uks#e0oG{>P9klv>r3rVjym$}W*F(gNx= zxB`b{3noFV93t!5`5G#8TN}NcXN(dCysQb0maJp3vtU6SR>P-d<_K&st5{iI=>UK- zD;{DDF5cgy!UMf;!W6oF>1you$77)r$~+!ytLWE$oO`YDQ`FR47%~ph>dMGRGGH&G zB8R3C^7_U%qQ3N&-N?=)Ul-#Nhy2Nke22mMM9!t!7|fRKPoKr?8N5RD2t7uh^%cqs zS8!IzwsPugXE4UKU;ImR0`Kaqg|MInU503J2{SiOYiraq)wyd@Z=RNPPLUoZ+(nfQ zr>-R`W{1nSF0PL~eS6X?SNC4&^PW-;59Zkre56ahdo#L^p%7tigaLKD*}rg2fN-r` znF98Px|#gBgM>sQ8J+L|gXo+Wif$cc+eN+P;+ThwFIlGgDBN*~6w1Q{Sw=I&ulLGn ztfP$XxNB0x@o)Rm-HExRtSl`^qzw?a8HQoj`Uu%3@V=Hs@8#0G*-)aI!Y_@F6iy3i zm&qzg$E#GlxZK)hlM@6r_BhQIT6zNhKG@ng{Ux#z$(l2eXHpC{$Cq+n3o6ek2#>FH z_w0RB(NdGq0! z&%^@OVO{f*)@D7$mM3E;q?}*AyIlQQ9!|jIv%J4oUzq{k^BX9v7yV`7?f`<^Z3`gU zFUiZTQ~g@E27(fyH8Oht24LvfXQ6}l>}Y}c#8Wt|l;z@PrSrt7un{mI-lOdS(8WWf zv$7cxFZ09Oa@MG(-)OWmq!al~czFIGHAM=AODz(kLg$2t{xl?S3 zR_UTAZGNlzkn2DFVL#c#W>u4Cf!1ThJ}%HqB?;a54BebWPC5%o2!6Q-zI#=`KA#Pd}DpjF+c5w3Mg z4@$mE2Pxj%@kWB!s9s<8Ca1`t_XoT8esXoU?~_Bs!sfYiZ(=KDiHsxc z)2U4z;r9;J8i@@~TZ@*mazJP#>Gn1t40N5F&}hxkM)hPxL9fERtqsMkxZ z{&fTqw}20(#c9CL(s?^0P`p*xpp`WeG?c@GaBeLbFuK98lj`bzhhZWcFnoOv1+Fv5 zg9m>UyZ7_v$$hsaZoydR?$rwpta{8g`d$!k<4Ni-pf;RUmEqDd(`KG((6gfs* zFm*xJ#@O)l;$gO$b4b6OfGTifM&p5jd>R0(K9#c2NSEMK;i>c7O~G;VMt zWYm8+J+opHBp&Q!L8>Xdhs^u|TzyBGtW4yHSpI!eq2pfDah~19&Em7O+7X!pJ0ZcE zm%Nd*@DfpKck5osS9r2Svb1>3TqO#zA&|DD>#?w;8A0oQu7g#;N**_M%^6PQrZl|5 z>mSup^C(j^d)>)N2E(#Os-5q=*HqmT<}v49kr}Ki@aw{Y-94ep5f_B=A|{A53`Z!h zeVuhk-JkO~_9Cp9Xv=I{PjJ~n{5>6V|2TGY@_o7@ctLBKnfp3B3G~i%ARef}o2g*7 z%>FBWOw>7iJ6FF|zvhWQg^#1Ol-S%KLVoo9<9PZ!v8U8;LF?;d_-pwb&3*OAqC0ap z`yn)e&j!mGW!Og*qwL47&H48A^=2EtU|8qXA6Gs+CW6AbD<||19h*+ZzJXf%q_0Mb zUKB6x;<=Ieje}R?=pG7b!sv$L{eeZ-e;zu$uo)SldUh@lej?aJU@FQFuy5w+ zv8tw^{VQW2K@HL$A`OoMmHEWeo*%VnHo_o?j}c&Bmr-TXE<~{_=AVrEwPode=nz`h z<1{-&%iM>Jv_DjNOR!dBzxv+M<%->q`c*~co{&9(iC-lNynrHk-Xx*yz?)f`*<4N%W zO2g&fu75R4poj7q)Z2;dxs0_)aQx^ShvL)1Zb?=zu_=-LhW?cEm7Ld5c9xQs2F~p5 z*P+aXbyKBtffB>`U34o)*lSPKngDSbjOB3Y`Bf}J02JQa+`aBWGB#%Eg1r>Ip6~um z@M~smn(Nzko-aYxDd6KPn}{qFb>v%){D_^nck=+<-#F>dY^FeXm${L>iKl^unTvztf5X6r`M&=dWg7Y}>jI8v;+x5*bG8*!$Yn-m?NItW z0W1xliJq%fVI;(n+=)tWfX0KJzdPSRLIK4$GdXU#6tUzWnshJTn6ipxi)>X{t>X_~ z4Kb5P%{$`~_?hEJWi$eKrsM}QEoL;CJ!r0|X;!)=L8lQSC3Xx(RQ5v!%57RSHks{e zp0%-B9a%$Gnd})fhq|_ATg@reX{f}xkzRF`5M(tcjD7~$&-NC3W z;>a454oTV|`tbd`&G6Zhc1G*OwKf?uP0Hodj4W0Zi{~yZ>*|$Am(Q)k&NZtfvZHDY?YktQgBGg-i21&F${B$ms}1dL(_ z)QKln;k=cA#mn>;4AxxmA4EwIr{RDXyG7N_ z;1@cSTuFO9KQ({?x&rs4_U@1~$inCkmk;+W`sy zo{xQ`t1Xj2u@JezFot1EnZ6jWspb&6J_j?evpO*RbNzH%&}P)^wcvf%MO|>G&x!;U zT}hEi!*GeGHnb>#HGYh6roI<%PP$M&-kMQu9e`%P&o)MQ)Lh2i0`k+;DTdE4ILAPX z_9c$IE49H5b}|!R6tG1$F1$XpTE0Y=E30fY*ZwJ|+>e7HG0?6hVhc4xfRQZ(VPw)^ zHyKi+vXbqQrPwUry+`mFqI4z=suM-VFbrX(HffwRsVa&Tgzm){4Ll{8LE2B zN)OV*BGj-k;z0+TqQg|GmBD`LsyXSyyEv-J$vkKMte(Q6z2U3b;u)5Y9pRqd ztOov&kw=oRg5U<*V6V;e!JZDQc>uIGS{R`ySDXf7>RGiRvo+#5nL9BBt`a<0e*$ko zNBV;4ri0?`oTK=fJ!ot%rR5g_yhqZjNZ#dkFUZHHzaz^5rx@5*@k+k-6@%ML2S zx4Y?2{|E3zj)xY|rF_;gH7FG6EmLtYFn25sxUk};?@*Lg`BTRmmTd}5D#_ZAnj<9) z6F39l)EP^HUbvzt#j^X81fv50cM1tnw^1D8AN&@o`SR7Y{e#jpWFm`XbEE+ zxCjAKO*DP(9jdv+w-S}$Ck!-1C@T;$7gKA@n*X z^W8jbR{Uolgrx1j(D{ll$AALuPd8j6@=CBau%UT-WaC;2>{V-tkchQ+S`7n0<6%Ao zhEYNQg{sjhveIX>A&3@%38Ai|#@FG+!N&&900OIHhYjXhgd~6PWx3WfMXVO}7HT4Y zPub3O^(dLeO~-VvH)Qa6X90n)rA?lIT6W+|j2TA(XB?E^vSHx&CZB93`CT4+r}Nx^ zBxGQh;f3&dIu&vJJ@fpfMqCV5VUv%!^YDzok;N%xlMYfh{QW(0x9!4k+H#sN3R=Zo0lt>@kFp0w*e4Q!AXIg8{%AdN?_8)R*H*) zKi!0r4iZX*x)Cr~YQU}ILHU0gcG(I%7>BiBpb*U!NLmE(iG*&p7=^-$!4oOu3N9gM zdg~oU7y(Ynb~#sOPDQK(Xr`Kr{Wy)J?MC4+XErpyubWVkO2xw5C~9A7P~a0<^55bq z8Tzd-U9sN({T`zCw2&(zR9E4-r7Wj0Vw;v>de)*5I4HOgVz-*)g>tQKyr2w$n1j?6 z;=vb+{wP0${_BrqQCPYSyf^vh%=<;}aoDVz@#U5%Llm%r%A2#C)p+66I0oTIq@zt# znhh=Ss2WA0yCX6H9p4FT+a#%$;SY&vNkRD-PtW!vbQrfRhgLsn1{Mo*f3uW%GJ2f)IBh}XvEi_AY|fHFIJLR`RJ;S z=YqZyo{x_rtt25%Ny9)kjotLwSMF|W%iDFaf}DCl`qxK!TR!I~qs`l&XUwy#-&vXe z6|4BxcO=2#Ot+LmUqxJ#M#T6qNy>*<|BJUL6t~zBD~h1#Y=TGEN_prOf31vB<6v0J zeV$hV%LxGttSlW@5rxNMsZcn)7=|D_F*iX>Ph!6BvDlsmLb4?9rTWhY`azVGpohAo zD>YefsSNiIo7sAo5j~5gShf_`5$KNCfs$e1pMb^$Y%j zuQDztMT;&XhsMwmaOQ6n9sIA}Q2T@>iw@N`!#MNxKhf$AN)5;bTl5kE_dUaC73?i( znDg;WS6@Ut`Y|2N;9M}%amY~{E!g3CR5*uV9M5gBJm%NWZy;Yjm6}d#OJt#9Y;=$>K1xkuuXg({1y(xM6}und;Nfhuafp9%y4WB zoIXS>umcMDT?#d#%byg$EH5{LYU#`|ktzHP8U2zk(lOcpp8{esN_9 z2scy7fiQdWG9MeP*0+pJ;^M}g^7<8{l2fKn+LVn`q_i95<$ik|Xq5tEWXvY@X+iZs zrQg2Vp_|uOf?14k2TO-|4DURH%jJ0Am=_5CXW_`&a1?3fxP|aDZuF6-ul3C5+_1@~ zU_Jx!5pD<5;-3@eGi+Nm!|NU`h!DOkOp4jDjO&<=*yafG7t?np)Cp(nZ&PF+%_G=c z+5p30gCOK!D-$kMF7Gm2X;FyL?!V>Vhp{oJ|G;Pnek0r)HSJgT;9KEl(a&8{-G0bm zf7l%Nc`)57^O-UAU_;MoGqE>oo6ZJ27Vli8(zYy-9u2^ceKvS9A80w4tTabr;Xp|E z&?ichT2aUEndZNdxlzF=4!7n!sJ@s{F_DiJ(smNPTu6Yp{Tn1O`zW@cVtyp`y~A4J z@YD8{df~6vaeE((L{HcOG0eLe*9HL{STDDE*Z2fwa;2CwY}{B@!ls$|2q}a1pbWE} z>|3Jb4i!&2E#%L7+I6r$pOS1@EpN!Yq%aIK9q?wc;SdSJ-&Y&xZbKHzm~p4K{SS7xe%}lk`VJxwjbo3m333y|m10?4dKb zPrOUFwxsyQa|WJeWc8Nm0`M&R4TxFYX&(7xj&eNGHZn78@I8dyG*7&01?HuKE z6mT<8MTLWijhG|x37&kAJfZ%uRxs$QVd)m&6p^?=<%5Xt z(dW0np0j=#UuS+rYc>81KSIk~$TNasD)m!H#kfaCkBGvAJPaQTNDfdPMKp3(SYa7g z%(8)$w7kdMV0b5p`AXFBYNkN?wW(a$BhLiYqs|RB%)3w8T?*lFU?J#cNmWM>5vBIl zHwu#bFIlV|EirHmveuBGg_)0^N9mF;|p?;Qme6+2ey79F8=D;2- zWs)7;DYZ2DvPITY2|Q^k%I}I0RJQuP3Dd~78Ib+ox^uBt!rrG83n;joB6LWxE-BI=`{=NLij_*8g{&=rJ_t zY$uW1(#^~hDI4+3w&u?Rd~q0wW5a)Yg*ik*4sq=NxJ8)nl$}f2|h>4bN-su&lCvxf7g}MWKX34bq#tbMW(CsK^Y%$gd7G?$* zPcs;kx2iCZ1*!g3aZHS_|K-#m14twJutT$5-<1yyW=e>E{f`Wen_N{CuUo1_>W z5;OQRl={;qNT7;s`z|V^x7%Ib56lo}KZu(?x{ge%(U9eb}mG!klqhR&2nV_ zKK$_6NQh55b@RExUJSK%ueb4IzdGO^@1=LJ_B(BmFb-8iiaPz9E*J;mCe`Dq0sZOw z2D?^4XkupWFP}m4_SBCAY%z8)mkk$5p}v??>TfN&7~lDa!mfqFbs*9OyP~3|4Gx!m zcZ?gO-wfw{lFTj2V2&hf=^|~7i=<_7^pzrZ(!`aQ(Nf>L}9|7VfR}oXR_{ z{<7gXS8ZU0st8fQzsDjxn+1pY-PFvRG;w&tpTmF%7=kF#yrCE%k5v%~c#@q~PIy5@ zaqWuWv3)3rcY1L6;sF!;2`5Cgk+1%@VYKUEDJYZK<0(S94ON!jbF6giKusqn50(dE zY`C*7hQasa>$1}CofMc(dK)G!(v0POMseVZ>#U1FBkTMFNOp3Wy^gb6JBdi+srXZ8 zd?ltA{taV{2$w=(IiStp3j9TD!6#Sh-*gbtgozK;WWu&mdlF(3Gr|oGk6Oi$^JI?x zJJQSuupXMHwb*UJ>rvx^nAq8a|FWw+R3<19#HA+?`eD*l!k zxa(x=fps@K5q({>GhD$x`YKGXMI;YpD|>aW8e(hci2d|ECuz-3Srz)SpsgNI_U>5S zxeOd1g#JJV9YvVerz*NkdWybrWz2)PgO9g*XRix6wCd4>-zaFa&y~V zp+yE)RiL$~c1k`hUltsVyKlS5`J6u66O9To&t}vG7iqtqU4QdW{}vU?cMl5}r>I+9 z5^LAT1gO`pE|%aj^_%-=b=Mtu#{QkI4;4~}Hf*jr&Fp*sTyT<`<}S7;=Am0m>Zhfir~14y2`V-@BE@ z55~_WarAwB4lDIx@6i`a4VQ{1O(gB^PLIUAJ0<^m{|@~_FM~v6HIMLdI4|$(ffVet zQY%b_BmO-;f0Xw>H^hRk6|0#rKtMGAl943;`-a%c*6jZa!&Wq_PEV$ib0<56mo3pNb8G8Q0Qc|JwDwA?J=udD$n?$6ARH-SPIk zxs1n&8!d6`z3+5-OsQQ8B9JV4eKYv%(PVesZ>h-9#HO86*E)CgC6=Sd6QCuxOvs8G zIlQ+l=F~CiVX|k_)@nk26gOiK!y7iiQ(M8gTzHEv{wGcJlCF~|LREJ!xG27rSuX*| zq>nh@GP=+=4%)uLE(nY_%?I^Kx2k1F&eqBQV>RH>&6bkr$m*Dv?W?GE_WnM!?<+9! zp_4Cx)l&O>c}|!j}E5njsKC@kc@I`9uTX@aiTjz@;>t-?m^pV=!a~^ z!=)ICc2zOpF;XYFwBt4rp6t<3kONA!rC}rx=$s-xZ$U<>;?z-m;Am7e9noGPe_^=n zF-S6VqD6)<<%n*)D6A-!4@3tX&Z6G0;0B<)oUNF4uBA5ya$!l;^B|rN49n79=WZPi zBM6B@gU<~cLpYC=*29}sOyt{wpCO5$3GeeNUHZSe`A;B18hBNWixP{mbWJ_DMSZAJ z<=>ys2h$g$TDRxsI1e!sBj(b<_x0|;0TpQEz?dQ<%P zHS&n&UzF&7lD`<`rfb$9tTlMfr@V*A1oPW%^b_w@E z2K)&x-E3qI-CKXX_M>Zo{-G8*N!Cfy$MB_1mR78Wau0?5CN{N1hvPNoH`R;1*&%;4mviHW{3Nk%vYW*9!cJp)%$a48~r!H*8) zm(O(^UZbY4PTL~1tZaeCxjND#F>u>s*-8#_e+8-4!6g#uDX~oD6O+@>R^0pKb~tTu~!cz3%`^2-q63=h3u+n%a{# zRD${_8&9+noNWO_z@%G)LM#J(u`&bmlN?6atw;8!g-dsZO#Z<g!>k4|7OL#p;@YD|5t!`o^(Wa^U=hkxl*aD;lF@Vv0waHeBu@Qt zkY^Vf97mqmGMwMD-r_3o8k-Rm%n-2K2#!r9pPhQ3NP_R~NHQOj z9%5<|j(gV+=-cxjVJBX!!0?l~Y#>G32X!4^B-C}e_HJ4!#0m+^>B(&{LR>1qnl}#` zlBgk(CHK0QC^VO}L83}Tz+vmkh=)W&r!rSQb3j9g(o>6T)3 zFIrRt(8%NSU;P;=r*_KKNSJ<)Drn3F>jmn^)uC?<<;x5yX#Yywsp$F6|-`@R7Fs-VK$Dv*lorSE2T38Z2LCJ_@svMSeFtY*s=d+IG)6RyM^Rf9fn)l zIb3jUM?KTbSO>nWNdHDEt{7hl9mBiLM+X5zjq~iECpkVmCrX!j&x+LPVbduvUeky- zQrEEf%W1ch>EDMYd?jn=Yw7SUwIrVMgMN4g-CEWUtr5S9`vi zsxwoqE5 z(SN3M5B!19T1SQC$D?~l=lTa;)8=12=@QCD^@L1WXZQ;wQhBzHy#7vh6oiQMiGy!w z%NL+^#^i6Ns^aggexRzPSe<>ukbg)$Cv^faGQsJUIEuQU+e(bvj%+H?RvQLU>hF8Y zZoy!>#uHGPA>D8&F|sa%0*-Pi%>@|Uxn6b=6#2ue6j3lO0ay2dLXdLX=5Si>VnajN zE=p}`p%T1igQ)9M@k39~c*vweJ~s1AEktQV9B5uO&SODIqj7&8cXw^@JGuuXIm2JGT+#~=)MxwKN^Mn#nDW9JcbyW%qP_4vj9Zf}Trj4) z))Q7ta9tQOcE?`BAV3ob*25d^JT}hUa=k#GC2Q%n@4vcw{H_EdyJsweC%h5P}(%A;#c1A&P)zL`nuG32F7STeygw*|_qlJpBq2_*S|9JB}M;n|&Idq7Irr(^$b?3zqScfl4EH}Qdgh*Zb9c39Px!PoB zrCi8h4v+}wIYU~WS%mIW!}9#ZU=SUli0$8vJzT9f0SCtMDQW3s#gdH3BP3Exj zHDe+Tb)5ynUSHSrTvn}#7qeEh?evwQ(z&}@&JeZ z?0q_*v%Y3?vGUbKJhaeQLrhnH?Hby1bUZD2uv^1dV^e)u_PGf_+-8J0zqL;ucA8}p zfD+8lj=D!CzK)|cVxN~*qxcJVa{7HT8XAF%X;25LSR%V=HEHX`Ik9hnlJOyTK~cb3 zIPXZhM+@FtH=7s%#}*jM^{i8L5!4m>h{3+pfS2rLtZ69iA{~v#wsIWQwQ|=RmhuoM zoKvvQAj)3{EBbUoXY*zK!Zk_F;?-wUJs5{pJS4BVoG1R0$CG{k#`}Ge(aQJnA8KHA z;OlaFBp{%3-v7&lYyNMtwf%n*v}Ify2VBmFET22`F)(uznue=;5{oA7 zN<3nu3ZvsKkl)_letoN1mrmoQi=KMR+Hz%b3>qc1Sd3AP2Pt&0iXl1lN;>FpYQP4840D^dqU~?iB^*%R!!2|+e+>1= z@YwVlpMx}PW4H_{h!%~vinJ7ARqjbp;R0H+ms6Nh*+!fywo&Plpcw7ob}{1xj)fE#ue5T&4|N*II9>- zY5P^}dT{IKMZMBP#N*Ptsbt_k7BHVtLIO~pB-p{ERj-sI&@Vis3HT4!$?3APpun9F zr>2V&)&3wRa=j#Gf{pTk9%ncGMZu4)eL;haOKh$$ySnN|4n$sFGI2FtYZ^mv(_sTX z$R7(JpvRHGH?LoW2GtdEuo_|Pf6N`1-5(DkX2K8PHYAV3EXZ0W(d$4@I~laGgH&e< z!-Seb36NFND4m#(Kg#y`5J_RJT`L;>>+=XITLZf;)M{D$b#`90>Yy*^`(kaqF9slY z)rxU zVN2aF{vgG=hh8|AlTL)fD9!pQ=J1Nl30Xi|5$&#qyI7EFlF)h1#yn^p;4s*+IPDii zHJo4weN#Tfn-Q4nIMHJc{T4>z<}RKetrplh(e+K9rrQsC8s7)C~=a{`0&h(G%t0}Y9^zQ)3Kb&l=4aC5NF33loh+PFx;CySx$!+)Fr>_xgF7Yh+|Qr5 zw4|2&){tt|8A0uQgB_|l*y&Sr7(mJ80mi^KZ;$9^kQ96*md9i8SRerbQT^DuJ#w~^ zMDuxH!i#4g14Fcvo<|_N>MLZ7q7ASGIZ}x)CtPKLsyUVl*0nCJp7~&UPoR}e(XggQ za^F4l=g%eMjpY6Zco^q|=oTHEZv8-`7kDH*8EJf(4Q&4LaG|kyPxinF@H@*m<%~=W zzdGEx?Lm8Cq(~$!LI%t<)}eHOro1DJhD~qeJdnGPv;Z&(%Ao@P#@cv$bs75Imz)I> zr~1JGa;|j&_{rFyNl0HiCk~(bKA7wfXL~GvFH}cp;XK$$z)&h7PJg8GqN9>R5&*wY z=7gg*2N%wcz9%B_^e;is&x(hiXf()&CpvRrQ1^k%i5Mvm{acD9dAwxT@~K%MY~n7e zaX1=+6U0mmbK-Bzl_#NW527Hj!gyJ!=(9b(2@H}y7Xu!nVc@*3YjtkLi~#LzcgO14 zp9z%IH8zTf-s=I?CPxA|=8n=dZLeX#NJ#=z6TBSwmINYMxv97tufm|ahM)R^Ed-uZTLzdB1dki9bU zrW6bf%?c&lKEhMZaR8&T;*mlCZgFo4%{Ijs$J;6EEG3>>;cijLx5J%tx>nQ8&W6Zf z^fb;Zs3~q-wyF-uz-=B!J1aQhg`VBr?5U}LanS3hq0%dTCWTEs#zUEs7w34N{A~{Q z)i?XsWt?yqQD}1OEqG7eIOUGN4W7Tk+a2+y+~a@Y3r|c-zO3skG^!h8e4X>ZvtW;J!HPYgBy+ zmmLC5&-F(BxEttrC#2S!W^=fnA9Z(xNf>|TuJzoH><@2B+RLlvksZpqi3~EgUH~GO zW*fs^XYuUkGDxftwjS%?K4f&hV*uS&M!EBcJuwvdJmn9^DcDiB+JZ!ce+aLbF}%Y5 zX6tI@B?nyBstbjK#I?GhQB$KT(kOFXY-v2tx44AM{X&2g>+*pJP<+=BUr{x%1NYg0 zh%!7-TDsx)+2TF0s#V}BEtd%To8svDy}TSr>!To~11IzIJ)I9s;qBlAURX{mdjIG5 z53LhHCnq%noU9SD3gf$+UFPRzU{9n>W}%NrNq9hbAuO2Eqyk7Jv$x8aJE@9YazE@T z-yx3$HN_PuV>q)(?BCM-5O5+mn7(BqpGsFOq5weWkp_sv+!;uYK6x!AvC$+;T}pcW z5m%&GM$^&e5UNJ<@L07NMThZvkWB=GNEUI^yTfkJg^3-6EN}YaZx9BZXcUiK8#GQ> zx*?+AUBe{M)L6@<)P=jB6;N=nxZFOTcP$Lh4vt+Uv}sW`!H3`c{_OT{C1uX3nygY^ z*F>@jdi8AjRn|;mCw9bRl**~%0s5&tomb>JD9hi6_tuMT8h={@_}oaU*0; zwT}!iCnDbs9Zikr_W!0am|!Il=^74i(ab!i){cHhS1np-cDBz2%K-!oId&`of&3JI%pX} z{j0B%N$TkO~goM%i&K{SVG>vwTvdaDtL{+rN#my}1Y&R~(v zwE2dtj>ZUAm{!7c@-K?y6gWGN#^JpdHt*VjxHZPUFAzRc1%g(akygGMUn}+(JMDu_ zrabkW4Zbn)q3d+GjSvleYj4F50RV28P?sw=o<-E-&q^|)@mj4yO<|@3V zk%kW_$%q|FSkZj{&d4)tvtCn@Mhx{d;<96W7KU7qV(hvb{%Pe6;~ z*zrrY2z8jXi1jH6${$IS9nCtNrT{Ir)1)i0JUhyDmU6^3X^?Zsr@P-bxAq6;5?|`x zGe!k@oryko4zss%w{AdoqCSRSxA+a5wR%xJX?8Zwab97LtV3a=6$Pvi*4E5W*S$}# z&~E!6QdO5|@;(X8dq}c+W4qSPf#3sk=&TJ;PjAK^RMXB1WSTaq`{g%*_47QGdg;9MlZ;d!f*wvnGnRdI5c#=W% z)`%G!d1TZesH?EBs_lXDs+YyE@iU|`+D++bMN{6GKC}AnDPlPzf6#7-bSsA1u>EMd z392AX3mb|KRb~uu;Fd#R9#&;f4sM5VE-8q`0NeWOJ`akRCQcS1ojq6@H}n7E>l~sp zftD@&$F^-d9ox2T+g3*%JL%ZAZQHhOJFoBX4(=PA`Kh&PtvYqSs=YI)4%THzwwmd1 z1m=H3IAs&K!V)Y7`AEnBT1`3%$QvtH;M1QKDi+znWY|nd4e-Y}u)zDZXP*@!4GhRs zqDDvPoI1f=`muCDIIk22GzXPP=xp!QE=qu$JNcgH zy-cR*wd27<;@b@?z-ggnE0D=wW$WV~t$SkkEj@^mu&ET_WTA!-IK1cUTGe!n{jof9 z^VEns@}rr#r|0)LV5!DQICEeK3Vn1faorpxgnCz@P>#699Dz?1cQK+Dmz3Lwiuk0j zZs=QS#)JEj`=(9VEG}x3gCemoR@y1<9F6ya=)wR;s2TDQTI)R(En zS?sL3NF6|eOVu4uP$Wv}LIG z#=0i{K?9@juP__l{KNuBEyaR~K?{mZ*O~Rq!^sRwhrANE+aGdIW-$Qg-5sbTW2<`c zBS+YV01`l3>Wq~~L&O%Saak@QRH9zH5d`+_sy^L!h3s!=$D-G%?_lVQu2zdW%#689 zc7p`@;vk;h-Wogv;@F$oX;}Xn9Z?mYUBcGARrh6X2^1+elodpY=Y6)Be$Usn@9trG z;Myw(*STH~1nr7sfy4&a)e;yERxr>dhr42!p9|W<#W@WfpFN=|d(g?sw8HZ{&d|&c z(0@}j^zLfMfBsN_+J7=5xc`%)+1Q)^TdxRF?u*!9MC_VThdm<;HCWG-Dw9@b(}p&5 zhobXdq`q548dZujVR-w9F&VjQPg)v#7=AdmJhrUU=mqbxnod{Pz{cUqE>!FmB%n{1 zN>U~KBfmkRA6S-SU3BNdy!{7mRN3D~st|9lG{N+y?Q`t)k`&kSG+KAHM9 z3>-pi{`flZ11`qAX76F|fJ!$CSnW=An<)rPfWN|($D~wV({vi7hYaFPy^<7IpARyL2i6gCRvi7-z$HEHuTj+^{%%2-j#nC9HmOo^oZ}? zjIaEun}}1`fF*q-jFa^2SilrHZY*goE{;{IL&Oh$mgv2GCp07d;`upo__M}4vRka zx~xbXmXP+fv^hMVJx15ylSC984+mA(-?(Rln)H<3laqCn;DBZCkYUx?FLJ*d83u26 zjx`*3N}fIMp9&7P=JIEf#hp;64}X`0aJs}qdS8rv#mR3@y3 znMYD;#|kP~zY?n2s7(qqb)6EWo49b#vo(j^12ueK#~V>h`gx$9?PZD}%$b$l<< zYT5*SOYDQPrkxkWsQI*PWq92x|^2YaRZb{Q%`Fa}rT$$-rULO?nNC4E?^n6pS~%+{^fG`HlqW*9+fj+0HET91UpO&irT zs)AimMdu_-;(Ygxxfh6Nt6@y8Koegw5zU4nqgJ-lB)3vfnl(AQBnj_1)~vz87;@2m9on3f_vDI?K#A&ROW@h({pzvkv)kOZ*N0?1-1|38xFmFI%%}q=gbTKvii>F(W2)#V24bQ_pN$q*c*rE_({Za|w;gQK2 zX;%V?F=bc#exv+^N&L)XR7baobKv#UxMogPxB^Axj&93n;*RW?UZK=y;E%lG-P@Br zrGa(-@j$(f&i^g1W z3{yRCs&rr(0iHm{`qwJhTT}vFDf0V>9Uf&XKWPQh*H;O z)>D}mOk1Tr$UL5@dM-y-Ii(i~y?4oAAJB%3vkX$7Y@tu3r;j~5?<2s(O2blrK%P{h z9g9*gvI&OtCABFsFUSubRxf6jvb*#M&KyD}I1hBmNfUMbWI=I(3=f5@Dw+G-{C7Tb z3)M`L{x%tNcDSif^IonJhQeT>sQKw$IHr#MaEi+&?iYH3Dka=@5_py%1q9UZ=Ryxp z^Fajd@dP?h>&9A;Uknxw@Jt>fHi2wl$eVfCV;YKa81(4-*(Jl_L7?sn8CNoPG{x*I zXfW|^Sk0li*qA1)ouIEmTpC(EGn}-mzvN2g1w;!RDO@=rlIfDIq%}RvCyYu~sB`_# zp+`qtCRC%|;BF@Jk=-&y@V?!uZb2=@w>ox=d8ZKZ8~hl8S(49oxex-w zRgx~nThbGsRmodoz_)4M!!t@SU#Jno3LD1jgiSfQ3b%oILwdV1U*$W?? z-n6V$Ec@9ITj|{IW-Q%=MSVfQ>R&m-cZ(*KqR<#%W3L1&Q0hRcV$>2I=$CC zG9#@zOT77P!x#ordQFFm3JsN<5+j9qd_Y#)JNLYVBw2i%=l*{2V4>(d6mDB*3RDc~ zxX|C24w0n5#d&o?qqt?006Hmm=Aec1Ftq!wN@G<)M1)jgLkk`e94K-cp zSo_we#(Vy`5fd4=Wn)0V-riiV+2~R`Z{pv|k+%Ft0-crT>+zMm{f^vPcqQ;5q;04T z_1Y%AB-EX&uyUnUgKfpwNX=I$4@k)gdU5?Y5j`$}K53x^?iFGSAtL_2dc`8PYL{J0 zqsMm5?M44p#K4h{Y!l+XI=*7@;HPxW4%;LTfpPwq4YGmIAPqG=uyLPUS8g?Q84wWX zoehpjdoSl+mBuzM_f@^sth+rLwq-eYoVtIavQ8gNB^(keY~M~eSFfB%@6A0r-nITs z97$bCacOCJ^RJneWYO1RKMkK%z@b&f=}YmUJKZTK3z=_aO|~s;e~9dNrhcKQf4b%W z@1n(g=NZk02>>9<{(tjB&YsT3_I75L|9Ssx@qGQ$S4;V~UfMgBMzl04lX!h4v*lc( zH)7e4L7vUA)qLXRfe0p9NtHk*v0=sb^U;M604g!==I#88y+qC`9Dt9Hzs-)%ktH`% z@h^O8qC^o(x^tn-n{(NC2rW|4ZmAVnvWv!1(e#=KE9o5)B0ig>*&?ZV)pVK981{Op zBVuK(QJgfhS5;coviqV{O3y?yZlEqJfPz`QSgz2qG(lL}o|qY2@7j2l2Bl(IyVO#& zR)ko|n^(P>>tumZ>aWQowW(vs$N=-QYSB8USxMa}JEb*{+yPCODz{hhu*4%~Q4y0+ zlU7B~@_UhR)kKO{OhQ$G1{sV*bQ6}ePmLniDyOJzMncRqXl;6H8IhqZ!hL7If_J8RU1*BC(T}R6Cvs_wXY-;<_O69#bv?cr%^NAgimaP~nG^H7Gk5cnACIr+jW&Gse?#HyE~fc7Cg8q1vG$F7X1hN$@i{S(IypG1AWAKqs6H9#G8 zuE%w8AqW*)LFV!$8@Q$pZ0l^8(MWIx;0ba87NR~WG)i%v5E?+;9=v@tsMq)hO2Xg~@r!WA!Sn=kFjN{PZ%>h%^JirZzqqTq zQ-|CB^knH|^3M7bHQj&k8zEyKPJxPW{83YpqMTb5q*W&L(^cUn1)_>Ce?`j{+1I7_ zlX)<8tY~vjmp%n+2kA9AixzrMo1U4Lk3XLBfn0&mm(equn1T7({i9DKLf3WS#|i&n zWyXQ{yKmA>I32r5Zp6#`7$}fPI^@gKm$3n>?~J^EM$n!Fp8!UohZPW*@GD0f#3NkDYq-G2X-lkObxKTYBm>1@?hNcZI?20!MxF~CrUU)YK zO3E14NN--Ijy|1Qk%^W=2RmLQB*K5ELBp>UJH+E4la&y`d*d#VRvJtV5Kj}yQgatM zY3Y$jP~28+nnKl4JsLDqUjZas;7($qBS7+wCVhA}e0PqoTk`S{K7FIW}G@o%+5 zAV=7B<{FR!ubwm7h^0Y+ypC2G3CayRb20DecI3(DsZ5VlD(4I{tb2w_vUZ+oVvrVN zkm54%Z?sQJClACWq$_&%sjWO)_S@(LxZ9CB$PM!?hnu+w7I;3hqQToP`m!F$hm^`u zG9rYu$g(+gWs;uPw5MoA_+>hl)jqKqa$~Epf})M#U7hFq_GUObYb?vdW6g@@^%xLi zPWpcMU5JnOS2=Qe=&;(@c~H2CvdIK+9!NET&4(5-v)x%>f=cGLWijQ;QSgEJc^$fF zYe+j}_yRk?BxWg#d_B0Y<*Z*Wh}KpD@C~YG=Ro_H9eI~f%$80-B*gCN z9Z^lJVkG$^g;}QneW0Z7mDYMc<6d1saRYpts&)d zsOu~N`viajWvi}sK|%~mG7kuQmSDErG<~CLbzDC@R~kr;z~|TUNN*ZS66^rNseWbD zMF4aBZ=`VpZH7a1JemP>@_T?i%qy^p*e%cdOF#hNa#!?`7<0+DVwYrobrRcn5*5iT zCqEus`i#u!GHZix!M4Acp=!LK^alLFHya@tyMG&(0t%x&pjW|%=Znv5(r6qz)awkc_yUOQVPh0a zo)-l-bb=UNPgnhj4$0v7WAN&XbN0nI)$@e}oR7K*g&0~t%g7>KL)4nmkzaO_3v<4# z^RvP9vfDM!wBf*1^hY^!Q_N&{akkKm6wYD^%TEBEa9u>6oCdwueDBf;ZLm9dkYEZo zCy?e@S2O2J_zG86>@lwQm-oKdj*7f~5VA0uTNXx3ZY8Qsnyay9zLSt*GXG`QT@LV`tfBi zF=wYzu)p>&uQE(x`PT<*j|Z^De|v?pNgRE3{sBMW_`q?^cWp3LAB1XA7&(gZpSQY& zOa!sv)s_|OqKjp%E4Mz(uT*Cj64)ne%!+PUau)L-@MR>kE=s7hQHeLO_Dzr|&8BR0M-^p+e9 zSV0S*gMq#F>t&PBWnA^ey_INwXxn2&9xOlC4y#(i(T^lEvq+apY&zamh zW&}@#S&DxZtWZtN^D=H%vO0)caL&yJ`gIup&9NU18Xl>j4Zn`sZYI}=J)@pFTn9G7 z@QW}bVqg5)jHPAbo^F0JlTHUCw{BhE|BIC)=dEcyo@OME0TJ$3iQ#TzD(~R;1+y+0 zChFx*8PjUKnyXfYt;9hA2U{^!%x`5M@<0t79Y)A$d)1{3b{2S@+se762GMl>3*+VI z)siDKAZIEK9HMxJAXx(nV`FcrK;UHkR?w{rOL4%6oDK{5 zmS3DLC3_aP^OZI(_WsB^kBUWs6v6`rt`t2qOW!esjPw~*7{TwpAv%gfi8VL3+#bj7 zKh8hE=6X9Nw)USnYDd{>8HP5oD8zp>*^-9yp9N#~6-7=Q@IVQ8n4LZ_pn)e*S$GtH zFHt8>4aeT1A${-*t*CP7ss`L|iJ8-&jCjH(g=b0Ml+>g@nMO3SUJU7zD(iJ;z%JY- z@Yp8bv?)o_7VDC^8su0@(KUpe=vJiU%?IKTqeO;l#sh5y-k~uj7CYsa4~uAt>R?VNU&0n z0|sg~Pv`pzGaN|nIA$taPNr}Bb6<(7M77VKMy=FZAUe96AkqlM&QVT&B%&sq z_8>KI(&V&UTSp+Tl6j3H&{NGL8NC_9a({~$w`uEZ5dhYM%mvoSIM%Fj-i1o&AD_^V zKt{ecI>!I`#>BpfzH)Tm2t-TtPGmwPq zggm>$N?9^ircHUkkC#r9>9(9e$gi~87YU^4CFi$v(Y0CFQSD5Y%p1@vbgiZi>;#@E z=c`Zk1WG8URtJ3DER1tduaLmF*<>CPR-38;5<7i}bCFhd4EwpHUv47C|h)(n;L*xSyf6JaMcY+8QD#@+9G2FanokBiNzwaqP^L&P=qMy4oFhkgwI`)Z?N;6Zd37J$SWNQN=ko~R~xt&smJW1y&Xf$N=R<9<6AvV z_SA-%$l(SS@Ig0HNA> z*-u&~OFwuoajA8EPpPvw$eG#T58H`3e%L%-PsfKFk$npbkvDSNIbE0h{tr$&&H&zR zm%FWu75_*EKM$~J#h$CfuUkn10`rZ4h-c8mmO^V_vw^3WrL9%@YP-Z>+itqXJ|cBJ zir@wMgvNM?_*j=I@{VL!N#+Ohff=Co^^y`wRt-A!qKTsMwJGu?s<++o%(f_4_6hoC z?Mw{TtRc9O?Q&Wi*!s~vAmpzfEUB0&Q zY8tyX9jhdw;9Xb8t~P)q#+^87M+Q$$1obU`<0?CPfJZ=#N}sH+Rc)Rz**T}63j-$Z zF`AKMe2_1-RvTlpBXeoXae*jk?oe*LrLxo&z%)IP=a}x( z7&ssYsdw1+QLcvT+%{siCftBHUbcx2M$u71v7Z4@97rdbA zvHT_W`OdqM=ZR7@VU&mOLpqHigTdCukE2VpUovFQGL&gP-*c2h;2o1eF}fM%$KC1C zOi)Om#uL@3*cTzV{6k}sF@C{^eXojn*c?Q~eod59y5JD-k3(F*%Ieej;c5WiaMLchWB%hL zG?T3bhZ#0u$~k9;-Dq?~7ZvP05U=VHYq^^qb?Xqc6mqw`cL|GO(dG|iB~FEQ*)AV1o8QH`5{XAjjgHo0 z!J<0sa%|=(oDRwI^Y;+*JPca805B$4Vs-_tsw}3jKJtrK&m=RsNjrA2;(1ln~zXigS|)tr;~Bd%soODdJl~JQYalo zmS3(#o_O1n;8VqzS2>glT}~|iUxeV$sjHfw7UB>G-|sn~Bnz?hg%Dw5ZUBjO948fG za#4qGXIu`jb30FYlWVfPVsuwSfnCnZ!SR!4KfFNd^mGtCkn(LnP$heeCgT7D<5r0J zRqZ^N?UaD~qO1a~xe;fS3C2-#X*Ch00()`NQSbKkX?sAuVTcI%PCgl&Ts)KdYHL(S zN8m_*_%NZD^}qY}?wF_eOym#1whF46_K8vhCvL=CVh>+DgH7QYCLg=#GO_S_$>Ccc z9mF^qh7pX}w^8>rDeuDP(_qg_aIHGh#qX^D&AJL3Y@O#dX;z&(#Ei?e!)7sZ#HgOp zZ(9Lrm>DTe79Pkn{)#Yty zZixVJx@)y?Ss+ml@A)1kI}qCgg2G4gXu-KStAx zp`|u$!XeJ1fq-Up^9|A@s+lTN7w9;+PZ_+`-a0y~fbYZd;0%s?D<}6(k9$=gVQnza z>CVh6)3-5)=)2&0@L+`*-o^fOQU9*%&{Q?46tk@~Nj@LAQ=jgSmX(Stk}TF&(&lSWoAFT(dfyJRsY556UU|6r08# zB_VgrmCL6LXDD~Am4UiX^GaXAwTXZ;$pdEBqmP^G3o(EiwWZYOs~Sew3vThCvUVhn zHUtn^ngkF)(6TtAp^$jQk6r7P&87do;xmyfci;6*TGAW1&dQi_%bE78)mc%~MlxGG zWeIWMnw$XrA>#xmjE#+cp&rHVmRa*o~-F>lmgP<3n!2Hzyqps6p2JQOCF1_X- z%-R@er2%wX&4}8&sf{l2g$HX%+eAghhL+&vyVGA?6$0e7#_)Ygp zeCu7sfADLpshru_d$;#Q02KX{X-BW3YVv}m?#S90=$3Cp{l?Yn$v57nRlz!p{QfA4 zM^AyqbEbcmZhT@UHkSn3lDc$+*7gM7*tM=36f-6QzkHhhwz-VCbKolTY<+)n>@edw z8mH@%W`)3@7$xKd`Ow0yoVyR8j|a7S2LV1|B@344d}l+?7o?x=hb#jtLI1Hk9I(j` z{~cvyK6E4w%lVK$g>u&X&woxbm28~(Cz&OC)3}*hnZ1|E1b8z8yty2h<6Aq~I5pao zb?`&S_}`a_ph;8ENGh!Lfqb>HqaK3L5JV=+@xBdhy*12T<*X}+!MuAja?tT6bvW_w zL{}YqJB*Gcv`kj)!jhi;)#2_F;vwyJj25)}+D%XggBmc(~R#!#i&c?BV8<1rr=>bjOZggu=^-;d9175BJ%HlTBnA3uFM1Z$%#Y}YTu*Ve6sj@h!EjIh z=!2V%SMZ)p6xR$Ht@PxBTp|aUWPdFe0yO6)_)_H|be8G^M`o7$hTT~c5)$WI z>iZkpoe8aTL|(~Y@%OJsDw^AH1aocr5WWcyE!Huo`2m4rUix(OvOq8BYQTMp_x zN!ejR^G3v=v)vHQXAp~j?K4xD(juSLADO`NmZBWPjDvIp_xh9lzo|oyjEJ+|KTIyl z@c&TABm3G&7Bk75xJY2cel+rv1PEHza0CitDPSot7OEQ=2cq2 zOMOP1@bA}+(pX+8Wg?jRA%5JGY)jKgZIyaaIFn(MgoP4fKNoY3icL%VXiI)MC?{*}l`V$68c4}9yvB3E7hHR?Ve4q4iltCy@7KGWnu7a<WCBYtZtAK${n^!E zzki7iBJC$SURA&%HXXWjBbPGEjlngDA4RB7;bf`Rfc^;BipsnlMY0k?C&v<(JOcTQvq0jUIl^hlBPfmit#6dNm0V^XKb>t(o}n7k19}n4hC5f z`b0Z5{3TZ_#hAa1oeEmkv>3v*2U`ol;#`oiu9Cc|X5~8&iR^1?u6glBj3T~-D+5Fe z1z#!^*)?CD5{=v#yviX2C!_6XQ&G$#Si@_TLI$or=UuP1(i^Uayu*Fq0>NE{YRQ24D8)w8W;ITd zWru$Vl+pRQg9A6a(Frt4wq~kY1`J;3SaXK)wVV#oOsMj!!1q~QT8l|@J=IYD8#h_hcgKCgw z#?`|JaE2esqpev7*0Ptl6V}cbb{8Z)70MXKQ!Y31Tv!l+N7@A^GzsCfo^uizm1guwDAFV5eWdv~oT?4WtBzRW}=9vXm4&sqBp{r1Y1>V(vBuGZHBEYR3!!xQbbA4f$= z9*9XyydiR{GWhteVFl&xQ*QuI99^>)O#H|zeoDlU!>vad(V;ML)Qz6QgOqNvi_{aQ5<5I>ld3JPX-?`-=>ZPJj66C#Li zVn_@XdJmltZowCV{Dy~|$O$2Q+RJQm^2*@iHz?>3$PzTEhuBhs#h=^)S56*VE~P5= zBttwuf42Lya1w+%fQeE*h(5%PVmbJ*aIo_2DyR8mvcwztp#Q$!OGyGVXrn1kT%v^* zQBJ7bgQ!D>XA06tN5mxmD6OE37+s8IBcRAE^^`Lg(K*H?=Lsc6t*bI?Y>h|4v4T2Y z7J0x34Q;Umwy{!tmyDUuWHO$QVuJhSiySTCQexwboB zq_Y}NYg9$;?Oxnw1DPzytU5JrB4mwlqwVtFb{G<-n7fgcH|$9^D3h#-K$;NAgeKjj<}d9dMZQWEKkZ7bjvnY zf)_Rz^BmL|-=X>#5rtr#!Jzg`+33K6?UqfALQ|;n`cC>56%RW&U=8)OLe2qIN;W!0 zuh0oIq~(FiKn@dSn-P(et!4hx+k65a>SgQbJ@b3Bsbqd|Uddut2hp2s!kkl(RvNEoh7gYeVUm);) zF5x=~CRx6JNMjOvbpXrp)<3j(zCUTkPQ$9Ag&w}Z8Coy@I&xx@0=Wz4x=#M)L4Ovc z>Z%Z9c(^oot}h0Ws&|MHrGIRz3nCI@sqq*kkh0u7vML1@dN*^k{rO6|Bqr9;d=*2& zDT=@>v9tTJi7y2!MMTzCM`J!iO%zmLUX&L@_yhkoO{R;=leEHdmoo_kRe876BlmXN zGTe*!<$YRTP@1hN@%HSC;e~bUw|X_Q%&xIJKrJjt6EXO(g8E3vZ=|W~JTo>&MC$pj zYaO8L(wMh5*74klvE5yIt}>;V-D-R$o%{E5@e5~$r5uOy&2*WqV=1RWG-rL5o~u{q zMipVl3mqq{%9;rb1s~q`5!BWZQ-Krp zI=465unB5uWZqtILlX;++r|)%yKE&cn{q&x2A-=65$GkaXxf9mP5<5czA!1@cq+IY zU0<3L9bc!uE8EI0>Wz=+V61o-mg{yrR#iveOaD%vMu}3XFpf1Yw3OT-x2h~D>aSGN zEI)Wnw29D)(cvbgon!sT_>lJJ2u4gu?nQ0}Sv%ZvY)D#4ZT*v+{hzK0k9W4#1Vhrh zN9w)}kJE;-)ns%RBF;Mf@~c1uM&ST+ z$gjrsQ&@W2AfcuMZ_fJsdSwyj@mtLt|-2Hpf?BOU22Jw>7jXZ+EZo02F(7?ws^owvcC>DZ8W%ss1M# z_Qfyfi*YS{YNuu1afog-1LNPg{GD%i)GxTF+T6i7b&<0=*6+sbKxmkz3aLx$t_JsZc8atkBQLzKGsG&kPWEt6uoFh&}s_~{$oAEV*QD=KqXRMfG zD~^SC_Ms-m=nHL>9AIomlR(2PCi%YR!k$QyQ+jZ0&+UJd31cJ#x-qds^D93;^b*^E z3Tb&XAj|H-kk{*<-<}%g+n4(e-z$>VHdVdK!$$~cq5fN?I=PM})p!@Qc5e<6*sj;E zuRy^R3uPn-v2-r+tI=tG5RdG+M%PIaTd_xp zXl(h^n9~KFMt&lQuBKhQHQyf7fZEt+`uhD3f0D=Th|VAAyN3{uWZKZ~ozG5uZQ6?! z6-HY-KFWRPTc46&{P%-B5+|B$Z7bp`+aER{86L5L2%saymzdsYd~XIR)J+F8laG9( zx-lW#?KCFWvy?k@9b-^{j=1Z$=OJATQ%k+^>Wx60u~!@peAa-P`@QusAzT$zth?*2 zC9sw`(JfPn&W@8B`$Z0u&}iJ8>dp|Ve6d1GHfPw|{k2u?vy+8fY4Zm#OBc5x!87p| zjv~7eap#-ywUEZ8*bRMVh{984u;^(dT*`6hUR)I*QO&{jjjN&=!_dF3J6suYl#sDJ zSLrfaLEbegAIfJNsfk;`HTGi8y`Y0dvTpq+h zvoq?NWuV6H&_*xxn#G2IGRx4^ag>>)W!Pfvut5)54$5w`B zg|n4A<4h;qk9XnJIRo=C-kW`-$Au+(Dj!*XKk97xjh7Y0Vyq=l=L1)clds-M;U!}$ z7H{PE@v9?j%syzMo+jIQIbCyGi$oFKv@K&Z7J4^4<>ovWr`t8Gxv>kDNdo1jq4QBH zw@lcd5~%%|mX!Zr) zL#zf}v68Qfv})DOtd^q$tMg&k2O{JJBH~9hnq?r#Kx}7M973RfYbEyVJVP&qPE*b9 zs%StAveNk;ag#w>KC{NpR5FxxA2Q5O&pRT9S-rNNUNpi|5qjb*s^87k#kw+1 zh0fS5(mfZNDWs4WtRvL<|*U();^)uTW<+Tzb@I7;vci3$q-7)W7y>VqPW{P9!N_0K!3ph zyE~^ay8{0=Q36=}cgX+eAiJxbrH8Sxy{*H)2Hojjhps9&1>MgG+kHX>%x%-50xHOm z{S2Eex&jM)rIe2Tcvao--0|3=x007?qj>N^%=3f9=I=1c`Z1w-V8Gkc({83Re z%4%#WQLU-ZAh*kCn3@Q0u=K`bkdBWVN+(tgjLLsH?%6Rn19VHebzCfrrB<>j)?$>%)rG1_gS^3657)GLZ5&MRr7(x+5(E-zfwbjrN&R;cNk zs6Mr->a|Qob+eN~)Q+H|UwiqJ+vQdIuk>uk8^@ZUdOVYC@)eexH;=)Jl^XkqrC|UJe!kLH%d%5x}z^xwYFxYI_!(9Yww@c8#dKU^JTeb zFF*7X>orVmZ?x>2D`;2h?+YiL>13b|O!rfMNWmXc+Oo^do2m~t9y~gVG(B~?N^Yt@ zGII2@MCH5rz3*Sl8M-sdM~1#Tt7+~1qANJ42HYOXw=$U&f!H?u43qxS3HrFo_g&j0 zpDnVYd6m#?lvb2iYDLIrA^SAcJS93c4AIY&jx<_(C67FnSF(L>Dlu(%sB;j1f)H!N zv9t0kzFye!&J$SaVlI=9gnX&>L|3p+rK~>18sSi^Q1H-u87~^m;(K@;%gV{gDRn8S zx5q3Ol@(O!>M<%dh?AP7{bh;f)3>(J%rmM0pDdMvkD3*%~{IbnEN z#G%d0;G?PM3QD<3{;s8iJG>}0!Y}Aet|ZLEI7hog1>u9~sSkei)-Balt5HoWsg>E- z>#7#P?OqAo9M%23kO1fOjgC#X@B zW<%P?YSAX?FLv{k^svY3`@27rdp$6FJq;YNDB=<~2 z%;3K-C~jcnY&2_jO5PbS;;Uygcdg^bj11s|ea}6;uKHCRKxYih*Fx!b&Xm^^qJw^K zJW$N!h^-1d9(S<6`rLQ%TZ(H|!@Z0g1aFHZFEMr9G<<6O${;sEYB!%;V5zQ!npg=F zX=;yPyX8I>2JPzI*N+qavzy4FY0ZXO!C=BkdxnHUNe)^<%87a z`-v^$^YM7LRzaZm{rPzb<~EG;ScNVkS$#mgFFC&r94Ki$x?{AXz`@L;ivGr@A}<|c z@H_d)^OW|Bn&Gdbp9zioO>Ie3vuvxZF7bLE-kSD|dz@_!9;6rM`@PQZo<V~h>5Adc5Ri$lL6*L`S7x@5S{>N@l^nZ z@FO>fah==rE+KU@UP!nw&EJ~2k5*{-r{AorFWSHwh!&F&Y;GzFb`WY4v6naSRL`X5 z1$~NpeayQM4JK7?T0_a`_V7>|CC{Nz0b*naU7RBW(8^oGaWXvq2Rmi0VG+^K1-iajHWQ!&LLIubFz>##%lOz?HVtl68g7RT%&NUE=`<`f4o zU3T6lXF6r#=9nE}Ie`qr*iB7C04>=(N`Z3M)C8~NX$Tu|Xag6wSnpVy}~<%<4W)N-bF_VFCnkRZr2{5)1^0KvqC*~JFaq1IA$Wp$og zh*#q-T!^nR9C*t5+2k%0VQj#kQ7P(EF1fJFp`eAb_se^Ca82H{(jQ(-Ml7$WDZceo z8jC##&&QLWF?D}Mao`30q#CxgwK!YiOafS)H?Z9pcVqRtxu5{6&+FzuamxYt^*5KV zCnTZo;@{6gsKQT}|K1LMs*a)8BX{nE8N2{Ew=CmF(;<>+N75utFw9V0&f@j}J^g0C zdr8e)0w-n@&!@>#I_3oI2xDR7B5#WVM-yq$2F*Zf93UFoWUO2%jHF91i_qLu2L{J@ zq8iFuVSlG^*6*qbdyBU4N}~KE`doiV`1|~&p0T~o{sMw}Yv11#e}!K)+R6w{I0q$u ztq;bD8{XS04+JJKf9HD8fWg952Yao_YSkdroM)2L$(JZ&T4q=1OIAJxGdgxvj~`n_ z*G?Hv*X>oA*d!Q~$QuKiRRFg7(c4d?fIv}l!0x7sJJ7s)E0qd=#y*(KQHXVWG< zUfkLg8`e)gD%mTUXuYQ}1be}t;JV%EdMFwHStORb7Ul&%OGrjr?O>)mNVA{4QTLUU z!HtsNC8}{zNj9Ye4|7TMpR!fsM*hpwnOQDZ?Eb`UI5!^ai2H&ErO#7xQ~bQQ^rSYh zS41>XyRyJ#DiLbRhc>p$|D~PS*W#2(nf#||HurXgnFd(gW8H2CW#%;j_!=IZn@qGc z44m^#)<-sUK4W=mq&g;u2`MFMF)&bTz>YnujHqB>a|T39xwc4)i}5z{URFljm?};Ad$F6m=LA z%pgINf&!NbxoXgU$=gcDp@RlFKM=VfO&mt#U^JjwU&T;}k;433>R!2B{VvDfb>Hhi zpQ1~W02rZ-mui9PxVOJ_6Kdt7BzY=dR2|h;U0i34VnjCy}1wJw1DoW2o$AzYz9J>d4#3<(X7)FhpMpx zWpfq2s1!gtSQ$ZTuVjmR(c}O2DKq7Pyi@85<=NphrU&*zS&slKoQcahkOS10Rygj# z{AX*JHKQymf}=a8!UaNs6?WK!E8y*SpWK(>RbTrccXas3N26bkEftMdY0E-o`#QqX zRl?UIO#A67cMXZekIT*Dkkjh7A38iL$~M(cFd>*i33Y-O88l&D+Lk z>P^{Eb0#mx*JlO8F?=7_ay(+YlExCc%M(PzMZ!Q#m|`!85NKf3g$GvRiZmx0g)D(4 zhC$;&vAk1fqOGr;!-;gpA6%7anFbctqrc<Ri37k~h?Yeg)oJK)enljmdDu8!^r@(`oH`(KQe-xO44Q%&XYJveZ zn9rS+I68~Q0MQ67;&28<)yD;n!y&Vv2*8%}Nr+ zA}Fb8I(9f`bUgo^ISSm~|HEX@!oZ&x1>P=93)7zWwOqTV)QkmcvZp*B6Y%n@ZZ#<< zLZi0Qj?+->BKkK|HZG=@_Fsdeq(M5yM1@6=u}8MJHrst#^h=wRao*Hial1i2k;F<5 zc}=I1dbJT9G+UO3R>)~Yg|w#W2l>3Owx!hrZ~+Y|>K)!I_@x&3?T$O|YguswH6$ES_l>twUeu@Q+nC3`pmQehHltf5iGF zUz(o4b_iB8&a8@KE8|miH5R(nc?nL{iYLY8Bi}X4g;sMao&T<_95`@GL4g#4TZok$ z-und>JY)MTzWy;sb1LSd6Ii1D9z+9ZKex!)Y!tU$Cm|O-m>Zm(5`iBJv^vuNe85vG zci)bd#RU34#@;DNlmN;SEY~aBwr$(CZQHhO+qP}nwq37`ecc43Fe(l#iMfnj@b$z{dxg8wy0t(@p6%x(Bbt-hWNt zx3pnnnY|?Awf*d8$GL1CGJ2b26R(qy5tbN&jFJKbyVae7> zpfCj{!(?M^XpV4G7G%mdUP zFxs%pszge0@}45;k(>(6&eUQ&ckS3&CedG%zMvX(wGpVhmT_B}y|xsj0BD=z$TWJn zS;Yy)?^b|hvx11MIy?B2+{e->o^RL@yVn&24S43-b;HhuLN^E2=^D|THe?aDeIARv zi3pQquz<~HG+l0&Kp;H)uA(vU!A9-8TD3_L$v9}3VJ6APKv+dX5{74kxJ~2Ly6vVd za395{K>&_FpPgA%-n`g3+ww$@ftG3*sI0-DrAVsBz`Fn~SuQD2RoPhOB7?!5qBltI zJfLsh4o-#*yr&Pf@0^IhXho9@FQZ!yC9pdVbbqAmIkX%Bhcg1>QTS6#5XKQ$j=nCH zTHpoM0!S?pB+xhs17$9@AbQ8D*{}^@YZ2d(estuh&Y-Qz*~@#8#DumSRb_2-#w-h; zIcSNG!>KNF`Il6V53^y(aXp%M-k^41OmY#(>S@KV9q^Qw#={rm)Q6zG5AZc3!J|z| za&bRm3`;0mAT(&?2Ee3+=Tg&h5v%K{ZVanva#xWoIo0;45|=EiIf3Zv6h?u&=w$yX zAX5)lqsQj@&On<3<9gz$hofflSf&MKYikay)K}hS7rM}>EC`?D_$=KgcP}e)=L|2C z@>@tL#!bQx$U%<{G$(>1eu@N`<5;_?vs@n|%1rquhWfRL?}3!esMZEH#+!W!KpIH_ z3jo*+(m$sY!+QViYCw2EQEvivBisOA#9lh#?3@fTfUXdp`a8pO{&GSAr6Cp&2M=~S zLlf^C;m*EFuYNtsi)r^#2yDf4?ITDwJ_nMtG;_JTXWiL>;MIw?knQxbai$&=d2|Iu zUrgrJIdrGp#i$lLy#-$;cZf6%0MMz0T59lTZ*(5TLbA$7nPsH?q8@-%{2WHZ*4pk$ z9tf*WHC-8YZK~At836Zqy%83HGA%Lj7XFbMs4!RiY7?v2D@i_wZDMn=`5|p~$Iuno zVF)ecd6zMuQX@2drS!FTC%jJ8BN~EZ+m@M76%vPTG5ZdG1W^d$@+5;8UT^5|0cyTt z!xogXS4n85y7G#`3i$}ZJp6XE_Gppmj3a3k5w=TOzXykI-E zIQeycVbEQgA{~tOC;v-Y%pRPT{LGQ+E>smq6yao5;rStS`2?xB&IdHV4jBZIHO!$em%M5BQ+=w*9abtosYCD=S+z(-Rn zs_LqeRsy&}aDKqj)A6`^x-(hQ>6qe}k!UQwQh;|PL$wH{QFpMBYSA1eTXy>!$TDdV z(kij9>?pH`^;zMG?_40yi?fs)9-4v4!v3jR>^a?YDAJicsUzZPl`a@pBH3qvof5%4> z+?Z;Mq;IHQTB>oFTv?qM5{^s2Pl?~Y6ZpnEu8I%L5)Butum&fR*4+!Oj)6q%C}<7(dKam@8@B_1k^ z8jREZ77G+O()S>?**5O}p@EcU9aacp@b;oNHX(FPX}M6Va0~#l9g%mpcVP1lq*gI> zXTi>0VL$=a*{tdoQwhj=b%&HGUBZTIAnnpPcCDpu4HaZ0ys!MVnx&3OGD7s#@6W+r z(RVy8hs6bwn7lzseLxn)8~kFpjSGa z(0u%Zg?}TB!@L@0=Qd(W)^m6tdOwqMP$oGVArDbX>b^lcDgqFf=Un|*wf~V{SN0{f zWMKXYTFsF)&;Rs#T^$laI9R1+xVS+T03+7S>kP)Q@7|N-H)HQC?4`)ia--lg3$mqp z&P9tnozrWt<|TsuiR0&^+OE)b1E1U}A`_&R%>?>TDOPq124erkZ_Ba9C?kPLKkiqf zzO1OQloG>nJRRFcP@hzjv%LQR)l%!tW6Yb*bcL|j*1Eayx`6OU)0-uD6Z?=1moZiQ zrb)8)hd6hZPhDVwY%b}HZQ7Q$wi*3L*0>DUiMhbftJb27?VNXXBBk2YK_c6)x`yG~ z1jIpbN|o&M7EB;PQ!iH~o&ro}%3PC!eajC&n$2uTT;4?~f$DqH6&u#YX#tC-wej!i z*MJ2Cv+x3fI7o0bq3Gim?1jcowh~Dd*tDVyB>elW1m zJGp^L^deqXrpuzj5RbeR1Z+4^uFbt}wu8zVx*fPKAqnak?2;?hRzCHgArX$k3ylG9xf@F!+F|CxV3(50lc5bb=*Ye-)k(VZJG20$QoG6 z0~`K?g9F0Ob%*hq!+kybg^h)Mr@mVaShX7W!bV43&`6Mi*^+?SRI+%XrUlcy$TKc` z{RDasJ_UMigg+#r<ykoA}pW%f$ILg*(C` zZTdYzzrnR506s?bVtMh*JJJ&27N*4BNygU)i+=qg63_8m#3Y!cK zL03;k=*S`t>94xXoi1F`{C7ULTN%kch_*DMUVUtml_sWMe(a<5MV3+XXux_ZPr4>h zcBkc^=4Iz%!8BTZzCWz*4E?I*7H+M7(gtYPi5Rd8MrWs+ z+?o%}r@C<JTAdaW^@n-)bjTf;wJBcH!B5p_fS)g2P58XTq+5fblq%jP@0I^X$OJH{;+)92TD(OIjA|Em%%+tB3g zJp=D*(Pf(znJzgdUj!+lf3D{)nTPwH?7h)AY&^Y%I?n@?&t+^i(LVADt^@Je&^@-2 zgX`rU6Scr~QGncdY(t^T#5H&UFi3lU6qlX&Ev5twQR;dQ=yky^AsGd}#KDvOB$4!- zSueWF8u$YnSs~`Npr>ZyGBTr@&Grm)+eu@|FF*yA(nW56o_BFauiY{8bS7fQbd;oV z-A+@yD`{envh9*5`SgdXv7hjA2}_L6B1yoJyp)xPgBC_Mro&eF#6^L)z)>3&XCANQ z#P8H_Cl5y3I}Wn$Io`+hMQh+kf2_ycuA;RXm?H;|ceICUg=6ViA(23MV6?ex*vs-aY=zR^~xK+x3eDSn!Q|m(a#*d;yHb1TwV^}&uLgw z8SnhVFYteblZp!L^>+VBl1niF0O0?RaFVNuqmzZ*e`q|7Xxb!gi8}uD_8G=rQ6{27 z&~+rzYI@|nIaY3$(xRFr}fTQ8v_8Qe}14;YwdvCy0Fxjgaten|v-Clh$QVUN$$nEBS^`URm2nX4$D| zAH$&s@o~0LNUGdGpZdC$yQ06~i6|Pa-ZcnYtg*x>`VDBdHjavPPolX^pt-lB$B6Ew zj0DL+o=4Bb`Amu#es8UUt1)bN7K%0zCDgJ}K?p1YT6yY!D6I!)g)D_(}>P{b_0 z@zqWt7B2-=;zFHRO8=G8TQ_|o?NHbY`fUGLKwA1YlRY_z!8{>3=V;4rh)sLi!?fjm+#CI9YFTHo-#?u?naC86dj@F?UK&`iom8=Hvm)!YtG=`oAR=myBvz3w!QkRVYneng2DIgnB{|W5@@;%F1p6x;HjsPAG62AO~75D2@rL zc|lQi;L_=HgTwsDTXTewc3{%ps0!Xgh4|^4`{nfGGeXc{a>A`=)gj`@_*~PvcwqI_ z84L%@TF2sBHqW`J4o<#Rq8c&x=GO>*Dx@!>Lc+ zN3hm8vZc|H-aFURQdz~~zo=s@!xqjOJc&34{18VzxYUV&80{apJZ}C9Y8nXNTAM0; z1EP}B3*wW9G>2i5j`~&_Vh&D$6)ZARPlr;$y=k_d;<$4IfAbRmSt6h-kRz`@Q|`7A z4biT~P7yG|4c!#Lo26?uHP$-AePR(du45972qfbxc8?P*XcSU#s~5cP5h3Zxv^eyW z(~5sQQur{_$46C3V8m@t6qi4-t`ys3bq*dJQITc*)#4{8<4{xY{a^XI`c-n)ZOFKY|<u7uyYtd*z?G|T>bz}%Q4=gmTkON*ty4}2wf4wAL@}xW zYY#>+oGU@3 z6$8+8lEU}Ker{|}_^+?F#v9169{74#CcOZ-X(L9?Y9Z$DOwzXtccv?5j`4?&!mP5X z(??4giRp3ouG`QVinz=G%M*f)g6|DkREFm4@>7Nkm^^H^mzG#~XYD3yo)-2_Nu(d2=%3*c$B)qS`!8k45^=2=40>R6ZfLEQ7Q zEjYfZd|!GqV4Rrr^QnwnD)V>cnfhVP$sQ!z4Bp2uZI=B0u#?`aqmv?IWTIrLMSiwn zY0ZLaj%Vf#EDN(9_QRV?o=7XtaZ)a<&}7+)1VfKUY{b8zg09GQc?YI2)trnXdTMON z>Nk?R$s}FpC4!mjM8{*0_(Oqsb`FFj>l# zSdjb7vgR!g<{o)Kj z!>kp}iu&VJfvV)cE&xkHq!n-5RLD%Td)?1NbjhG>#~LO9-?r zl8gK0eumK8FUISR$RrUg`fnl9O?g_fbNi-GR$z&n5mUti*0D@Cynph194L(hQdP>i zZ#)4|{p8gO37yP>17xd zxeFW(V+fZ3#fjyZl8PtM2j+~cIg@*HJ;qZGh7o}|^`%sH3o=ks4Tlo}FXyn5FaOj# zzxyn$RlABYrStHzia}Ww?|j|Bum2=-x|I1NJ#amnVJYV`)-{_K(af9p1siJ6g{y)tKieD+e!-`@nbRuEh zN5DVP4FHd2fW$jXt0?APP?o=570E*NL5@} zA~i=Xsr$y9BT0h%@Y-d;twF&j>i#7u~FE(Ik4Kw%`1V$&Q?j$}lo3?n$1v=?$SroE z`HiF+_EE=)?!E{#+^=+nCAzM2L=oyPv@|GqIDLX=1yOtS$5+GpHpn35L@FV#?lsZVy={vL^nRO zCzx3rND8LMTlF!wi`a~FIxIAyzCW=Ovopc)vzCn%cD<0rjTZyE7TgJGaNkBDsI8!Q z&y(2KI>(WEQiS8x(X`anWRZX6uV+e0l~(Z?0mrn0xOMAz_Ih=$@7kPuZ@u+ktlzjG z36u2CvXuGc;ZT)j>GrZO7=15_N~ZoD@cUPmx2*?E%!t;e;+OG*t*hL$Gf69FVAbbr zND97Wz*|tXo=fY$Zk@ zBr)wF*=n=0krLoQ%~cyS#-%ffAJS>RoxkN!xOvI}=m*4{NEe&H;%f4=y!MPPB)c>*XjU0{C z)j}9;FdEOkk#2qsm!F^KB~b-iYwN;UW@ysOWU)COeFwPnK!O38vfaoWF(%5|0Gb|K zVTZQ#%6HkwU7p80(}zW7eZr;By(>ut^G5;X#6+-*2wTWv%7|<@#RSVHRiJsm>9tv| z&Ii4vSS*&{y5sX(z+tSmhUbWM4GuR?>VAan1{R5#$7hY~X9S7UaWxB6$Y*X?+t)VD^ z(iBxca%R6sa`Qo6eBkuf6bZKb+v>km_!K&}Hdqoc#2s8~YprKNHW4#OKOK%mXbe@O zuL3SUpS1STVPb{T;Uh8^-YSORFFz51;1H?N_6`Uj0Lyo`9C5IdyRUn7#Ggo1uxt5KB*R43!=!Il2 z)!*pt?FYNX3wlty0lsN6Oo4)-DW`|nXvOx!6|et?~c{0HA^l z06_77+Kt#dniyLc894t3v$sZl%XW($-se?KdKoBH@t^$W%SgBm@(Akr5N)JQ8{C$$ zjbyLss~3WuB1BbTprSJ6#2Wr1db4uo zt`vd~iWP)bgpUZU3CR&LqGsSC3aos=nMaU7Vv0yf0l5i@v%<<=3O2N=N`kbVQsLtk8LObd*94`YH`PuVZ=z3du<<@ z6lG$`6pO;WLU;v|%5|#czVzL>(pR+Crq7~_Z)ePU#C4`lPXjf=d}as*O(*<$0cy4W z$6rdHMn`%K0ABN5xN|fJpm7e1tH|;J1X%a;JVm?8?eSKCm?Y7A1V5th4&52ZQ$1$n zs|7XM>EPH}7-Zb^YJ9r!vPt}-2|@w$Jdw_XzJGF{?^JdahoqL{5Y6-hDqY%9TM*j` z!$p^2X2po>2p7Td7-5iWDh(7Vb{}?#J~~y{l`;`cDVcsVQCZ%&QeT7m!?b+n0x0M7 zi7IoB$*(ht0V_Q%>*LczYV&5KlE@Bb(k=%wnd`7hJdHIbp=E;>`` z96@U>(L`k&W2bn*R49@GD#t=O5#Q_$8(!=#DKTjq)?ix7jdb&PsazV6IFTkGKi1f} zvHOSEdH34UV)o29eV%_QvFYZ54D^Y8I#cV%MM%-`nKivS27nY)M^Kl<0W(olvzSBL z=QT(9fg2Re#gVI%YNpAOU<>0v-kXAq)@g$YPQa71flz(Uqg+)27Bo%N4ST23&vfk= z6Y`W5hYdF*h>(Q=PH4nXTb&1z(|I@dGR+;Tfkvt4TcDUjFVaN}yem}*kKP^6z@f&A zdua{Bg<+*2utLBVK!+8lD5V@NfOOHj>XR|ednZ!S*UX!U;}&J2k&?0-s45ivBj@ke z+qWHzbS!LQ*F4I#wYbhhkLs8zVZF!{UEH^&yfGu<;A&v5p-h$0^BMn?Fj077Ni zQo5sueytj2<_AureoXw6KwQ7P*L6BJ3tglyVjzj9f;B_Vgs>8tlV?+p3XK%OPd@~M zp)B_=cBEMZJ{0xIg30{8hj7(G8JSR2?i*xOZ#Nb;X*r-&e(+D91_|w+M4AVP1@`ks z&5iMk=))CT(XpPdNAM-UWX>b=C_Ho0n{Z~S^w|X{z3JMy;R3mUm-yFC2+c9Mr;Rh; z#;2P-$9Z!Cj*Z*`AVva<14|Z?)mNMT;DTy;F519IC|YE|@uWK4U~CTB9o9|_ac87> zw#9{e)Hs{Ehkm(qw43;TPVa(`68Qm*lmur-s@LAWgxhHl@Em{57i-EKcyOfsyZW?d z`@L-ddl8yGER!X=vY^~Bzr1>4mXq8bAq`{cxHRPzY>U{Na2WOms7u@v z2SI&8*Yywcqf5spBsD_1JU|?Dtq5r+Z5Z<>-B(YPE4=r<2>C^ohz;)rU50m$D_=od zraZmAm(_5R!ax94Y@;=4M{_Dzm^OrF8~%{m(w;iQTEUbU=3dbH188AFM7@%^|)?fT+4z!{cg-Jc)CI}I*lhn+ckFEy0@oz-gai^RZGg{;5-jeMP89g z$aRi@$eYTXIQW9SAB&ZcDlIO0uIzlSsM*f)dY-`S1SIAb&Yk#D?kQxEc`n)?sI2J> z>*muFx>5Vp*}CQR&0;785=I6X_w>;36gvDmU=2oXl9ZC^1RGHn9i?Yr9{W;u+B!;Wg+ zR;p)aH&MS5pmvvq&9g(5biDwD^o;v7eR^DJ|0%8-(Xrl-69n3d;xd(nm9atc)#Sez z7qn3u{(a;5d-PGPbF>c{>ExwIgaMwN6ZHGvC@L5rL_oCv>LusDhva{(o1S(~|0~x< zifVFBWnOA_j%IvXf`)R{Np*ahW}J>vp?s3=^sw>~&eb(^l43n@_*q|@_P+(}7T;;E z`xht>@PAjR{}Fp^3~Y_*^zV};xk4USn+%Nfk1K$4%-O!Tq z-G%>ic#-(mqA>oCpZWiPYg0!98xuD>M=QPmU^p2#{|7xtS;lUQ9=hjK&AfLUP|-|C z4#!k~eO`!8rrtTs03BMR6p5zMSlp2p{L#q&l0wa&^2w!-NKRJap7P;k6*2>}&n5Wu0M@7`B z{%GRhZ=#h-xC%T-9(ba9Cg(|X9l>}3e!e;>K^!R_aA02C)vpr?Tu)6xCwn`~WLF%X zxPdwZmkn#$2bSErQ#tm><|aQ0<)E@`Ykup4hW|+1CwOA~xvwNH>2VSomfe7QfeNK^ z!Nd3#Z+9N^J%i~YKh*^GB&9?md?r&9QY33aHv`Um%w0Ys!GT(QVn+NQmQ*s`0I4mL zP7~5X^~Jq@oquJKwR>$(j8*=hBj(yIH>pkB=5<&zQgOGPo^iUy!G7(atr1l{TBGg#cUp7Su%Ls+nSF`lKd#nEf=OY#wF%vn zTa07%ah5p^TcP8N<_CuB%%ABDBQ<#(LqzEx7O{E;Z=jg1r0ulnG9i>@=Yk~E+WNs zsI(^7D05-h;hF&T`1KF=XH9RWPfDASUL+6R5n3ja@E34iNLD{vx4{~s&G*H=e>c>1 zB1=^;QbGSszrV&GlD8fl0ALsOf5+AP|Nd)32KG(8 z;g5q|wtp9j8<2pCi0omjsS?-vtgF}5kDDnrm%}Cqyz-{4hD5X$2{GxpEQdEdm(k5R{IimKp%b3n&r)f#ajJbj* z0-d?eQlTkM0tZ|F!7J~N=O?A#!%h|2UZ2masf6yb@>O1iavH)|A2D)>2clm4#X_i1wrR*XiRJBB1=P2Gc9h0J(g>SzKr&%mPmc6f0z^ zf$f#RmCPEn)H1^^eKHQlnv2|$9R4qIFfg>@(y_uqW3tuzHg)QFIlN;Ev5@kG2qQqf z9ZFj03P8*RF2OsBwo{VWL}KL|Ei~Lj;VP9j1cVLfpW6rHjo8S?>>xn>y&#VmE4ypO zupy{Ef-K#?#Ew;L2=CJh)Dv73>xt{))158bBSpK;eQjBEtrm2ktJri0hM9AVwl{3* zi|J0g`7r4~Ee z?5`~;pA|h{Y7l={&q;5huqmeBmS?Vdml}-as*R-a1>2FCxU#ltB*g!bJ$p}Zk!ExM zO~#T+gh96hi~f;zTl~<=+T2~W!Bb_|i|q;~w+n?8mpft zxmaSgmn%2FR^ocWxJlXp!ab2PJTfTJ5aJT4XD#sy+R{X&b_eb14CYD4z-Oxgj1F8? zG8I*VrgyMH&FnpzffP{&X4ekVv{qY4fcL+&hJ6b+2|+Ji7?`8N-?%%txVxV~YCne5 z_JX+ONpyh?VXq#)=pTlcOh&J)mIKx{sX|tE#cFeU8!fv{vCg8>c}eT+7HIq}jp8v7 z%&g0WNDD~qU+YIG+eWoqC7Q>`Xj7Wl9dNeXDy1`5zZw1 z;)Lrt@IBxy^%OO^T9xAp_K^vvIiSlMv557(@i?qK=}Q(1$3rJ+U_cr{MLg27bq8;J zo~@tu!7pCBIbN#ltdy!fWuVKWz^bxC4{;re(Vnln1&crw9T?uztOh%4BjD2@EKV+TXpB){+wKQL>_7nuRQR&zA8T$jtTe?K0sonp=)-1 z(t{<+$NB~*x>=~GE0XKm7_Yb?^M$9iK4L(|XbX-v>a`*0wV^VNR18>bapdSyC+j8cB{G0%tHr(D-B|Iw5BioHCuX=7ptDCrN8D2N z^!t+Xxna-rh(wuL$s?BgQxXL}2!{cfFz&ZuZ-WLU1^W1-x!ckdqbG0!b&S!ib$%x; z>N3|bjd#zwCP=lUEOkI9Ex;RnsJ8Hj-kRp5?AD{V9NcP~R5kfJv0k%d{<|CH1CGv( zVZmsL0atgJDDa|T9;)qRT52E_Gin}UEcn=0G8!&i718G%m@7OIXH{SZT(uJ+AfRQ} zOy_((ke5FVf*1t!8~Rm2TtY+1Hj3>1&?NmU>HLF#%f}7qrPg_g$WVmg_=Ifwd}A+>Ys%UR$OPh;PEz+cO2UsM={6jCC;wt;!9X8nd{def&*B%s zZQLbB8SiUy4w*7&qmS)Mq8){YZn5-+^CJzpV0Ah2C5^6$`%KCVGGB5dQ4Q3zPR%N` zZ2~|l+jQZK?FvoomwxeZZM8wH6{4JE&UAZ;*OosZskF-N9hg9ZK}`$0MPy_;7fE1r z{W%T)$-F;z_Uj~trM{V2Q!A19)Ip{FG^NCxjJ6!{Hu0Ew-4`P?aOJ~s_rUL@QT5s5 zY4C*yq|lf{V?LeH|9+?noDcdPJkT{p)ebe&llShG`AwS(YLlO!Jv_u^tbFVoi`yAe z!Y(mbc#nYDiO|ObPJ$O5F#Q4DA4lAVby2?70DA3(kCR<$2t8?dBxwkn98-X?od}pl z1421pTmz&twZ{5WsLKo6zRRK}bnOD`YQ|afy#NbL$$sb#rlM|zUE$Dr#fhtFv8Sig z*+mgcqMQQm3)kF~xw{>2FPfn?+qPOTH=FqJ!_9mp1!Cu>{!M`QvIvx5@CHm<`zcaIlF_C%nV3+8+wb2_vg+?-mdgpeMB~)x@WHt&OicRA4N|(hHNt0>y3@ z4o|+Q7ys9Fm$h6d_6+zut}^Ew(*9QX?F;n3_OeMg;IE;e003x+0030~$6oe7YrdYb zi;dxbF59H+j#;65|245YG}gdOw>Q(_rs+B!BB^ zwjcrvfr*K-ik}m{#FvsvEohoCqRm}AlGC8>E+6^ZDm~k=6OY%V_69*Yi!wNXKN-lI ztQRNK+l=280NMK4r6l4ThwwF%;!6?1nUit0?lQ;=wj1VxSfkydtBy3h0Zoay0I)i9 z@$m9TMPMR*u2Gl|BP3G1gUQ%cZ#&7Cm}L+hE=m}sFN+l(2T9c`RddtR4i_V4TF`df zKf_Y3zTwm&kW$Zk0~OA(AqzFjBUxb$&!6-273Up;=H31s^!f%&lKbleOG=akji9&p z%@-$JJ|aZ~)%IQ%U1Kr?as^a;RB=qzegH`tZ3kXrhps$+l1Mlb$E_!)ZF?zf6GQW{ zoEIVX$PCg*jVb#yc6=Nsjhs#l!Y7^N3#cq=S~B-Ev0Wr_p45Dz;o8t zwxBon7?st% zT#9`b;cP9!c z2-XWotKnc|04AD;17lixa19Dj=V#z!{Ko&_p;#348;7l;EAdhfdZ7gbp~kGn)jSpE z0nYUIQ!ARh2(CJWnGLDb6`sOH8Jo*BD?(W0H0NZx?)S8v&6L@;bgw$_2~La?-$cm^ zHG%vkW)btKUd-glEyzL~!B8A)^aVR)Cl7(yQT_W_pB)eS4LM~Iae}7!X{UY-fu(<| z1I#JD0dyw_*NKt!suQK%A=J6Or(~4rls6)sv3U9Vj83T+(RR&oRe~zF0n;f;A|{$s zAVuACZPF|#3Q6Qn)hh^)Y-NtMXL=!&cECW1d@FMtQL=16iB&~&@yC*e5RJ+ap@a`9 z&+s8DOi`F}Y?J2S&@+=mL&SDt{Ss&lokVlN7D$|Fk{#TtwJqH2cnG`(GKCWc!)dLn z=KFFVS6EF>7G$2MB3n#b!85&W@o2BCQ>rvCo z#*v=})!gA(dzeO11}syU22^&qR-}}{w(9m#kfUiHu7M79%V;2B%Y|VH+Y0|V!jEOC zhBURRP@BNEY^I6GPW47;E0brZkZHA}+!jAHXj3nvrbkJ%sC1&5C%bz=@3$6=JoPJ8 zN;1)wXm4F}p=!OoNy*o^%30MYOQ-^H*p;Gl%(POS)fw{R@1%r zQ195p(IB4wPa=IQId>3l_o~Lmyr=2=Cbv>7r!Sw@LoXED#nZZg5K6l)Q!~xQKD|2c z*vuzM4nUXCcSg#b;IeTcSdW$gIV%P#ckDZg^`0!Neyw5G{3IdSoVMD%ab;9>7^<@_ z{U>7^2o~_y|1$Wht))$@4^>{@Lhidm~r}=%qTF2T)nurW6SUS3Nrdbx) zHFYAN-TS*h&-J<_Vk^Hz!wS%u+Lf_2G2`J>DX>GO+mzdaH-qo>*i@Ki~ ztQ@_**s~EV5mvStcej~UqRsF@(!|4Rsohuq8T!26=Ef}6t(Xh)!wq9=lp}NWp^R_# z1iF7rd8?3|^^rL}m8IHVlg?sNBZJ>fTI7-?{HDS8q7J2o5mz0Y7GBj|CPV zIt*=my7kp}npXugd3Q@cwQu4Pc&E@BcqXhhg`9baiCxrESB9dre5kffHBut#zRJD) zUFf8AtuAAljD$%Vx>tc_`(&Ky8k=isul0+nB4TLw?@{T-LVm6+K+$X68AdDP^ZC+< zxv!+G)Pw;QgXYD7lC^Vpj0A9cdY@uGsy(xl+U~GA)vD%_lC$k6nKNhb`JN7h!e z82Tr2&+pGAkx;i3`vYTF=(uMc#oc+R0z-w1gF`Y7vznuhk3r8xz^U3TTbW#LB$q)3 z$1r<={Z}anSm>?wR7Dxy8wP3ioWK`|0QrEi_Fc(ub{Cl=j|uJt^*wj@dI81l1geTa7Zco zqSwdrx|Az9dDj6Bx_g5^I{*=E^K}pIebpNO6xuq!MBU0On$$;`7(boj3Xc(vD2W!` z;(jei8ms_inIC-gaxogrLT99GRh8kYf?Dq7*OL`_LdH5M<;D zN-0#v5i#YSVQLY?CK4Mo)P>e2={SeFAxE?RSAp8`3jo#mqmkJ{bRm4IrhnaK)0%9) z0IL=9P6m@4E|VdkL48#OEY%r#gBGl*I=_@H3ynaW2R&Jir&eWvpYFhh+!dcfB0In^ z#4m|e=(G8dL>zw(Nqv%8dJgfRy%5SD^~iI|sR8Q((-KP0Q8yH3FQvZL?_#O3W6yv0 za36PQMQ8&t``o()@PZR@Bs}7Mhom!IUf8^40Rv13t+vTs39on7-a)cCjToZd_W5O} zZeeO|&i6;Zf!{oJcL}{?u%0Oj=*nC&n&U9r8}>!JIv6%Me@Jo4|FR4I-!1grjT;E; znUrCZSk(~MoDVDi!*IZ11aJrmLKR9zvyFoG>b{NsGsaZ9Ks%M!u@q1&pJQmVjmQIU~}C7#_$u$I3QTtyBcRjoLw)X zUoWgk_{ChpaT4iwpw#2n--{+TJ#UHHem?EHzCiUK*N>l-so&?Tqo!D}S6l%;lXIrg z9wso&Cuczh;4w~ zd>P@O>mIwOAvf3~(o9qGeW(NlO;pg_B1y^|=Zh{TXUIAH;r4cVMww2NTRd3}k)6S3 zIG4FXKgzZ;rSvD5^mwXRV$0*2{x-qKQ|SeFEBw0&Zj1sCeNj2sJE6>uq=VAiSYuJR z>~-v~YIZ3;r;*kC(+$njulpdsNo_ilsoX^S^r8CxH+&h;u@Me(N9NfA7f&P^=%Sez za<2nSl!)jDhl7sjIAABDgq`@JA)X3<)0mrX-rGWtg zXMHhy=?3}s;fp#=nwr$(CZQHhO+qSE^Y}>YN+g4BCd7F8< z|KVhvtYm*X=XKo5SGzV=jks`yO-7-D)^~2>=G9`sjMj%GkY?`J;g$&zcZV#%{rE2m z7Ea2OoR4MwL@`7Z7c@Skd+jimva7uE9tNE%&S6h93;hyswFg$>Qk?gD`F!3FQTt%LUPeD>4y`6fQ+=AQ?JA+o|aSXI*=nlBY=OD9$mXfu* ztO+lC7EnnL90TjuNmpq2!SSI%;t&@yyM(!=TXX`k7LvQ4&lb;-IPC7i%A84Q%)s08 z_xk&N0?wR}IK)234A)X?P|#6^Xk+OT3zuedKAWyUs$21+o| zyVUxhdnvGivKj}h2kHm>{;%1 z6K`IhBtF|Jr*MQ!hOQMf$a(^~YMyW%`3pM6>+(vJ>?9YJ#BoxZXj!6K=VRd=RE=Sx zezUrbsqc?3305v|Cba$BZ%l~tqvBJ)M_F@fY+L}-p^ktKIaE(cYpJ6QpbsV%+DZF= zbVO4BH97=?JF}_g4C-aWC%3j5YZr_b&k8~>dsI;hWRhSFqb5{Au+HV}tuFvim#qYk zHsH}9bT``6$-YHk;#m|sEp`4N5{35ow1pgZuv#^D7RwM#d94bqJ(nwr8}ca*5@y-V%d#vC7X1%x@s;=WQJ&v z@kP)F^P=$P81*KVnJfAIm}`4Jf_8y=59XK&)FD}??`{c z<6!a8C2NnDTvL2^?0t*3ndDj&zrkY^z$rRde$zu3;YWAA=@$pxaRsRh&RKFZoj{o6K<{Rq2gpahE*f{W{zAV&3G47LP`20i01kLY;c`w&mMYDkqE^ z<{X_w#61LEa2twK=9oY9J}9hc{LgfN69E!y+_T0RJ3AWh7m*zB11KL<;TtDB(;Sir zA(f%}A1;~#5YgEj zNIi47TbHi;Fc?{`31AkOYG62jJ%jeCm4z?DXG! pMArijHAthGJpaQxz3=ukqe= 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

EWI0+TlCk@^0JYw&xncr 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 z zI|la_{Og{vZEMF)c5K_WZQHi(WXH}gwr%YgJGRZqy?3T&&cEu+SylaFy;)t=-L<-_ zzt8i0zD78S(cV9CzZDd+VrGr0^(My{x|2xCuioq`6y?Az=6wb6rImBG8`B!L8t>Yf?vSf>;X@w#B z>o6b@(Mz(CAHr zKRg$EyMqjlv2!4{>Yx3PrfG-w98R{aY*-s_A9zn0@8y4;ZiBgIqCoP)dq#<}NQcmS z-$?l!UZO2My{A#?6^lT?cpancTab?OE`9X&XO|izU7c(uyT=WWZKS0w1;9PcN}hjG~4%e4Cb^$bwZo%4vM@U29^&0 zyp&Q9TSW@-=MeUPqqE2Ni#H1`@cGcF@t5fvAB%6HaeV($ZwJVKSOF3>J-1@DGD8Dk_JrJ(cOue?W*a&#bOHw|}@fKZ7M$-uvd??3GkbU)qGqmB@ z;x~5x{p0bv_~s`Z)xv!#32(UW#a41OdKRRdZGp6|K3;Q2gfBAj)!8oxL)qhqR7O-8 zacf^#g!@<$pGxdGR10&XgrFI1kDF2tNGoPS>_LYo{b~o%qL2^%ft$?479E;RhV6~?dpn=MKdoT zQ^T?UU?5h;?wPNNEG@%wsx57;Bs``9zb<`5NDJ*uFllO`?l0aiVHPQ?zXb{%UtQfk zj4cNRv`Q7%)uHo{0wVa#{E@zy!AHsF58nX#)`Ai_ht65jWJv1(oyb#^Q^hdtfTZUi z!nrd>=hSaV2_G<9(NiovyV(ja;Lf$tQ zyHp@_LlXeIYR9~~_<(oS#sP1D*LvG>kzyazlyY2;{YW?BZ zI)g5Ry|IsjT01oD4l3y9QgmFMmb%u$awW??qTOg8C4KKJp*LFDqKIJ|(Z6>nRWUNX` zTf>urw9#%7C=S++WKKEua1M_9}dcXF>FS&BWYbbRB{yQwRW+cfm4W!|Y!+men`H6SkGeV~N$w1R8(R(0 zuto1;-7>crTz8?;2@a<#&%$Npu8$mAzu?a+lJg3CqP)~nZGR~OD`1-X=B1yBPQ0QL zZ=5(!C`}MdpYxqwN*BG9)+M}vl?)pof7{jPRce4KM@UYc51Cl36rWCfDJqb~YRwYH zj6ihIfN1!*n&YAQVAd?PJLEPvu^s(2-IC!+3);V+Xn*PhZEH~$56I9-9X zX9uy(Z6J3-P!j$Y-WTBN=}IOn3x5(2j}8FY5))g@fR)wE+E`Q>_-tdfvS1ds$b01U zRH-nYYV|t+DPh03>VXoq$;L^pdz`QC#?jw=3codtuJfpRVTtuy3$@(z=y&xs!P8C70zFR%B952ioJxpmf8{CaAZ8Q;Lwfh&=o7ZZPT zY4G!NX+f?B!Cn&*2oJ63^Kk~qc0!Tdzdy;73Z00z_4D>4<;0_yAZ#7>FSk`hB_A(6 zzeUG8z;_Cxv4@ofkrtxKq&hvfaU#Lz?$>@VQ?c$U$XUaH#vL@FzG^@+x6*umx*_Zv z=MGaXn<{`h`g?k3Th=OmvR;Ap_P~0dK!*T7Y57df_7#+r6hyjC5NyXrVb3x0)pbgv zl%-4pS~F);45xdwa$;uhr@2d{PVTLb+8nzl?ml}eo;}Dyv9`NVbxZFU=CJj!u9K<0 zG)eWPieDc6Fd_$1MZjQu4H_I%#x%;6r}jaL#5p#7O||~0jx}Mw zUdmt?w$*FS4_zyd`$fUfgs?oSA5em36^yqxe+}!H@;R9+$g*`|IW$u%N3HrgVp*k% z-Wj3L4Il3yE63E%I(~!qr&i_A^NbD*^h!74bEzJyC}*5y19v?-acAy>BTAWts{4{q@8F zP6jk+(xCR0v4?BbnEiP+8u+a>c7V|}Hm2(atE6{{Uw3Gj1GV$|Z0Q|Uy)XvG7*#@S zewedk6P;i{-h6VQMBmK5Yb+ap6w6h8;&+hKH8{*wpwjP4n;Y1HT~YNMhY@}yp{hQA z=Y$=qO=*_1L1CHexiVy|WOl=kAS#S$lD#3TisSuCN6v#h&vlk{rGb3|3BQ?CRX}o< z!wQC_1IHlvgnvb8IN^xHJI>UN73(-Hiry(Y7eG1^a5LyW20qc{Q;13d-M z4fNJ0cg#FR0qvV^WS^5e*~vrat#*y{M|CF&N#)NO!UX!Y{QN*yy6uUncz3MLJzgQX z_ei<_F@BL)!G%wTP|c+AJvMD&w{^KbEp32GZ=$#-&nWgC^7=7dckK`0x1V# z#TCOMxHiJ$h7kpWUx-$J^8&AI7*}%h43V*X2fo(PW;+= z<2N}2o5LXn?yB}f^zg>Q)H;7TA7r$yVZhucqgJ%FZUFA~YuZRzKDtE}!sUb@yfom{ zrj~6*gCWyUAmPC#vH##q1!Zx=K4zU9WlBT z;=y8oEV4C5#?jg-${Qf;i=g%3DOwA@$9SmFX_u@WlOj_2PVgzbi}E**M$89-Pe&s6 z*8WsS72-Da-Mo}`exunC(|{s@@+n88`^xykJp&Y+tjj1Tn_oL?0AKjT3bqBZ&FiW#awPow68I_7B)M7d^}^lRecl3XU|LeGO5>?6~BCvqe^(H z`UMJBpv~U+|Epz+W4Ufc_lL(O^`j+a{vTCLu695ACH?=ZzHn8Mj{XrO_q?k|u=?%_5U^i;M z$NlpUx+c0-%+;dbzBr71`COEYS&q)jx@C^#^F%ya7PJXJ=8>qGEe{m1ACfq89or71 zM<}s_a7g;5ojp|7F?dY6jkQL0n!UM4nu&loIy`=C=i^dS(qCpPKRcb4v;UiIqA608 z8GeB)PGyR6D?V6At?zNmHw86_Ce$we70an{&tMPCd!uqaaK_(de&ZicMw^S?nHTb>w`~NVr9{lw$IXAyBA^<&HRk zr1wFJO(MkkelMDT2vSYb4B4<`;C*xu<|J0ZU|zOdme1k=f~tNgur-Uj74|!LZ@VVW ztIj|6*HHA`W#s9AWcYX^hnb2hiX^h8uXcpS1k6wl6MvG?@qIX1FqYrCr4`h>%c>UR zq6@56O-pR^?1(RWKm`OEKS0usVT-CMh0<{zV!|}hJXhbGj*lrEwKiW3eY~ce{+uF= zbB^14EW^uiChr}Fi##frkr5d!7mv3-> zVVN9?qU2M3&DTd|r^_=@;;;Wm-Pi*@BrJmf0!l*z0wVvP7pse<&HoDA*wWaE-(*4Z z+vy!>V~}JJN#SEXQ|p@tDN-r75ee)0>9#?!Ilf9e`*Jt?_|@Q^Zk;F~bTOE_ z?_t)H`N~63ruZ4h_&3H(C5VZ{UAPYJpY95-oypGW@WAZ29AXjMKgH|EB0l_;m#dxX68J4K)50FN}5QMtf_ht`JRB+Egnh%EyE3gpx+A^n-Id{RHRj7N6xJmpZ#PtzHaS!ELvKQXhqyj4IxtB1w2vSP*_1JwWd2BPw2iXAk=0vOJ^ZA(6phvjUC^(;6$U zXA9}teh`mWj<}Xm8QTD3En7$ZAHDI|3khQ=ss{&o7d+$8LSzKKUkqY9_D#s{jG*aU@Mms9vY{nA@Bs9w zBnKMq{nzr*RJMhf9}SA@F4|H%X(sSl$}+35YSLgxd_wJHMeU;s zhUZZu|I5o{)ENRCoN!jNy$l^iIexNK!rv*y=?~Pt?$jINIEYA8^vxQy7!F6BpsJ1d zgq6ypTcR#V;$;kfBT=h}S);+PysCxR81ZkWLdyqiZCV@I@?2j41LErc`dIB^1~f?_ zP1UlPB#4@Yxh0+vbMIB|?dX`u0^l$HP@A`BEMXj|?nLrze<%qX8+oWl+bAgzm4ZTO zs7$U&+&PsHVbpHQf#dquB60KdP;*PXc9(BWjGg~lYD`&rvV~z&v$_;qtI)1x9ZZb2 zg65kb{u6T)?axAF2}Qeu_ioANrmpM1c(|k~Qv0^M%ZLCE)F7^^R8})gR>)9-9M$?z zPI*n_Gr$b`NTNIO%sZqLDQL==&7D*Me|KI$Pf*w+t}k`H7)7Cr@wAr2@~|gdd4IJ4 zIq*0>+6CUysqTh4MH^t?fgIt7wZ{bU_qPs2QLTExBIOy5gA?n-Ux8A~6t#S%x@)+u zk{e8Oe3^W4cEjh_>tErevO)Z8D4%D(hwE42^{0QWOZ1gEcq%mHG>1*2i^*)ZJG6WpxC3>ot1sThNsgDeYu5u&5>l&w;hm*7(&{iOILLa*JWc`=@D#QVeMq+DE zcj>P~1Wg`-YPHc-%rOd9+nJPt$F(PJD&YmHz^9?06y~f=fEiR$N#cDbYP81H7#4kt zvOg@zqbg0UXtw5u!@Tp&C7#GhVsBfxm}EBXTL!v6(Bxg0%__`;W@0v-4q-P@QS&bi zBs&5FUg-|wtWgAmWwfV8p(HlQbK2dg25?Xe!5k18P(J^|p2YTFM|6J34qn)cw)Rck zPiFHq`AnNSF{WBgvRZP#E+0-KD)R&}o9chf-Tn0kdqw@P=dz#O4d~}s_P?M1(Hr*v zV0c0QL!$11VYJBg^9k;sM*7p7OxD+VQDbnxnm_cm`=YY6w8!T?>p9gco_KSD2K} zfAn3#Z&w5QV{RM$Y5zmtwuY8=`uaaRwXQ;m6;$!OGdsbC7au#}CXm-XDrw>OcVhY2G)VC4p9*2UQgR?C?->{^d zFt!{;JthyIl9k@^wj2$My)nKZlH<%GBM1o>c->l;P=moF|TRx%|HJRCmW7+dPe5oM85P!^GefRQg+qLFEyUA?W{5#^4-B9 zuhGMMT3Ifo@!XP@IjO@;CGD5occYL07{t2K1~us)C~S|)e@jdLzbDYh#PY}B^}k=fKIYwD(BBm)3XMSiZkX+R@Wr+ukGzb z8(HcT4(p^QGec~;WbK(F&vqDV|5;!S?|Wwb2BHjhSLHh*gYn8S>62_zPkfSUD|`8# zNt#C$r$-e!!-1wx2;q)nKxBvgxs-RzolPF?QgmniRFaJ%w9X5)jFNxTJ-+B z{Lpd!e4Jm04~y?i+z5*-IVC<*a@GQ&d3>ZV8F6di-{OOfPQ#)Tu^qNn!wiq z`G#a*g562F>Rkx?^$5!eduE9y_dMnI0<7bCT(%27#5>z}v@_BK@1j^+A+WJhuH_u< zf09KY8erGOU`2(rDOS~eZc)2{V5Vj5@UEC%DB=EsLss~Xc&G&`G~T;*hqMEE1_}*_ zd_BO@=2dD)2^%n zr|TRSfEhTOIckiAl^Q@L2;iAotFdVg-*Haf*`SmTGf!oqGbEpA=Zj9(!~IIIo1+j| zOv1Fpy3N=kdqfEwWa+DzH#wjQIr&COQ#ZTG*Ju5`(*BnJXMAUHoREQh=XGT^m&uv@ z2GWr+jT*c^^&62PGeN__#ScFdPtPFF=MREaf{G6LjnAD_D5wMu`QDcW z6mSm!vhh4cVK-+S|CcAJQjhJ2R;md8;GXL|YP?A0O=gJRlnkoCR7-{6^s!0x7tui9 zCzS1sMZo<}g60hu5c6LP5M4{eVCGqC6s9NnC#qQw`2{}M6HjGk)(n%h*sC91tv`#| zAC~c~_ua3{^WDcPMT!qqP&fDWOr9M9fqEpD%#)Mg>NM9-?g>{l!xZ)BdLJbHQsbyM zSexI5>*LW&XDpNw$(gn6i8=67lz42N_phkYg>lv^qAP^ zNL%DVc5*9{HbW#-8uP_ea6r~t%NJ|?RoB~Cxmv|!#RrcYD{>pbjAzprdnjS0)-@@d zr(gXvRmX>Goxcqx&YtKp-fRc;{GWqldm-^b;G&Z=GSYtHYz)b;YwtG@CbCwz(S-674O2q#J%n$z$eBx=pjRtKoo^Yx2kmahUvgw<}* zPvMvSdieCOz3CP!@s7}<+#s*D-Ef`H$-cou>j09fuAz!avE#s=!`2H_64 zXrW6*ll3sxj2n!sU3#I!%-~i@cwxbo14b7QXoUT{G-mY+}>Bs_U z_y!fNJI@r*R($!3@(LO?NFrdx+25gwNRheS{o^rtGb@Xe`N8-4`~MBBUE$LE`5GF>Bhh=V9KR-dV83D78(YMERxl*#r(()57ts6R-Ddh_^?E&7Kx) za_0K9e7NPt$%Z1o{xP1w`H9V3u7HswcTNmM67i}#g!KLf_CG#+)X&?=@$y*T_F-_F zg#@Vq?cVR3gmVWHv6EJ5v~(KmSEv39Sw|syd(@|QG<54fn>=+X>(l0cshQg#Zms5H zsRjWzO&@}33N=5ZHTb3|g7768v8o)EnTJre#|Ea76m}W9qIIEk6|#^5vO*B(gPx+C1LJCTMQC#O(JKU~JF;x_KuKLqHt>o&zMrdBfL(2D24;k>3FTCU^+-KOn9g^p!V+>U z_(-F(`*Uckyn4Z4;A+ z48V+(p`vg}9qtq>6L?^Y^Dq}d zFs9CJ853(S=)Y4*edAt^K|7%b9G3vYQM25k@ZH=rQs0Y8Q4*`Z9mwpM&ht0PHd+D7 zC<1!e_hIjgrxk}n>=xqv0EQThUozZrbpyC63bxUe?5oAwJ(RMJYtpy|%@gs~e==aV zH2A`6I&YgY#ig2Kc}6iGWBmHWJemLL!&NHHV*TR{l^)2?37CC*@j8l&ftW|!qcI4a z1C9XUnMS#B`-`S-dG0l*tdl*zc#!clMrtiU^)%!eFvGu)hO#B;tsGSZ)XLi5CBIgR zXM5XZ2fiCxF-j46?u4FrHA733#Slk}Gf_&CcXx z=b3DyB{fcZNHvuxQf$?TwxY_M>1_JydTwY0N~&HmHx>TC{pe)c0|8pu?(cP7JVyrBXD09j?zN_QihP z>s-LD8i+0JjQ%`BJ>>2221hFqMxnd3995-Ptz-Ofm3RZI6nk$=e)*p`i}4$@Fh>!8 zsSv5d!r6(XbUOM#yIlbAXqI&pAKx3|fRszHL1U6tpN3>CcmtYcoF&DqZMW`0JyawR z2>gm-cQbRW*I_ikB)sQbQA>zaXgnKZP3S4Rd=o`W;$-Kk<;vsD&eeRj|I3h2J1q>$ zyyZkR%ueZ#92@B*%uIcEH=tABy$Kl{p#P#pQ(Y;KP2Ts?Ybchfwbg!4W?g15qUTkA zyvmVB8J8g~Xl2c%^D-+TJ%L5dOzBIH7C{ehl-cKcbHM2U2rdnAnCbz>KRrW@jkV;( z&8b}lqA2d&plBs_pR5lGXhk5Bu~pJ*PYK1p!Z6f?5^4qObRDs+YsHj}70ARBDLLRP z|FW15ekF1 zBGs;AbeD%|jNE-FgETrQ-!++t)RR>M(6FJfh=qnvSV>uH+NB=C<6H5JvIH*(=dr;ZLQ z@RpVHGbBb?YA30!(GUf%@dMnmujba}X;qMye9=tIuIiN<^ri=ZEm)^fH1f@CLzcuu zMro#oIX#GYl}N!raI-lp)o*1>7s+u$PmE?XdmcuqLzHmHmPjvNm-TUqSWM zYsb2QW%8KLLcL=fBDoU;WKX7a3a6x$Q)U0!$0^QB;3ZJi*^g;e(d)QIt4wyPzugP0 zKaQpfN&kW@`oB0FM$X!+Lv#7f5Euu4>MXq7R*U7sXHw)6+l`P%6sEf+&~*j{w_)a? z8=Lj0>e_0GMIH%V74Utu$&Ex-+oe|{sajowL@kY5Tk0U= zg&tHxdk9bi=9_yt8U!>#bc{2$L(iiU=aI*`x%VSbAU`m<`Lq!QqR9;+`&)(ACA?!; zthT0^2$G^aDDV_B#yz~CbjH%gji9&q2=McDKh$ro4n79g>ImiebRp8k^LO%f02`zx zdrR2pK5_hE>{)p%X_+ffl5{Y5$fVw_KEwdBvOl_-wG5^}c{-aJY@ml!q8BzO(>vpI zzmTmv4!85@nB()P=HINmvEbtzdrWBK6~>&ogi%cQsUR~(r3kX+@EI+Sl^I%OUg~%h z7W#c2494Rgam-~d_^#^Z6w`Y29oALtjlH_z`9TRz-9k&fTm;?oCHJ6!UsB$869_ou z=irUsM1n#ljsyH5TA=TMVBaim!`f_@)jeRbW0H@ou-IfR?tR~x4yl+$b?98h#xW_aF`->}aW-unl`?J-<4a9&*Ck;3OYawLaEdox8Ovi?aS=(&?#V>x^;x{Uc9YRu;0k zl-l{)<`i})#)DxA$+r)U4;=sGuQ2#FFs|fcW;@eId9buuniEk!$Pj>h(O6ege>1k| z;$SyW)&?@(nH;x@ihjNEU*dwQfcD1bd8nOY($ivD<(5RRmh`3TNV(ckBdr#%v3SK9*;|#B$@w+i2*TAMjrl<_}+ud4;b9BxEm=#S|G!D*m)Cm0VU zXPX>B<031q(jgposFt(G9!%5N-=x+Y?q%X#FT4rhh@t80{WTWEWYtC=i_DiW0@}9N z!Fj;xs1b2dB2}bkDDfv1&%wm-fc;3##_1tG+lQNDC39S>9Hb{uO%{Ch>^HWCOIJ-( z!GaM!H^)orD$WJ+PhIFQk2$>&=hg?`5ysB~Ty$+V^s5g7A{Bf?s01qKIdsH)f#9vW zY=sN-5Dtm-INI*dFw=(4lDuXL_LgqF6Ssl}q>~tLSNix`)tQ=e&?q&D(up<-k&9ms+c$VN%+nYP}|ouqN(^l)$P-?JPT=%s%(RqG&)IU=^;U-^C_ zE3q}`80@YMjQ!&^Hw3sv@?vbS^{e(FoOS6l-VJo9w>^a1Emh|2OzR95L!zI!*2Qm| zjc+T3tf};^!%(*MIh$xG?)-{_#gTK;zhJ}fvPkCRs8pO~yxDW!TBVt|eZzb?Icvy| z7ogkIID_(jEzF|ws09J&1$J#zWKo<~f4Oem&>yR2#|vI-qTtubtLi>oF}&hCJ7(rx zjZgxu*T|t-dWyijPf)eEscs>Is-k(g2uNp4IuN&2N^$F+iPBCqy7EPyC2X^UW3%k8 zE50WO=@&`wd0+gr1zBWCG#U!bmY2_$t|@ggL%G)N zv}?kM$5#t8%1Diy4W zN#H@s>#-h?E1EgVenJ0sb=5r^L?;U(t{HXbHZ=Ro* z{A~UVePJR&e2>nT&(rH3E-$jclY9UPDrMo5Ej5)qOJ7?ynw2(nCK|1218x4tHQVnf zwOD%H2k?Si&lFlfR}(uJ$Nj)jA(iyavF;tNrVIl1_PkZpJkeCoeiR`OWrE4H0K8VM zm;?EuM;V-wQlh!kh{ZegxZAY`Db)NES%*siTy?-6YV$7Y>IZ-0l$^ogGQ)B~tzmMd zrEM;WHLXyXdjX z!841-n{zA(LWu;S(ZapL0U{rt!P8}N3oMfH zJ=%wg6T$`~gE8t3(9v_;jFYg9-Wlgxh{vM+PD(-gi7 z>BlscF`(i~j?dWZ9_+of?nW02ks|;*=%&5;(!6Vv`8&~h#@~~l^YdXblg_~3EcDIg zZ$pWBj12vjnRb8eK?kLh7otBam<>oBB?fW+-vA<4f+0`q1utL81;^87oZ?G%*o#uj zwya^|PQu2=LbnEuZL=!Yw*iQC?ssTJ6+o3NJW^2CKJuPRtfegVM`sVg;ex`kHmtg@X6{o zZp8u21jRhsOOvP3g`iXT*Da>HiV1SPdCEoRjT&I1@)}YJ%Yl= zQ#A+2v(HVasDqE8nvCrd4_Zf4H0ABSr9Pi8?%%%O`q|DWUQZMKQjIE+WCUZpVhinH6LS0~Am1 z@}(a*^*!x=`HB~imd&5TTfP07o(LHCXFbew)q5k9t7+=_od)jec^;$9q!_{gv95oI zYInX~Y0=?ROO38Zn7b$Zy2mKSx|{FBfT=+%B{fhpY6$z3@Rc!0{`UBEzv!#9f$skw zjF;$BUcT?2$yNS=`usNl-TzZ<`>(~e)a3rC4MwD&6vrq-ZwXl94PJDZZWr8j1q)}W zl)NC4SYt&^<}Al9zxTM}wkp~Z1ynrk-#DN5pZjcK1Rey{{K1#uNZ1Jc&l~&u>6iN} zTdlLQ*(l`b-6nHMN!!`vTl(xoD_e!a8nXCB+WiF&46b7JV0;0vUoprUC{1_$+BG?} zR>N!_P_}W^aw8~&b>LT0viNU$o{bRg{U*7=s6s=0I&%S!1qJw?F_zPXB%6JF--8@j z!%%nu38R6+4ozs?UEZCZ0Z9FlQXv(M9iaF;H0P3XNdqy*l!Bm`F%RO!F3#vp?b^p$ z`3?~pZ!d~3DXvDqyEj2sgM%HOi!3eOrf+;@ruEsz%dbMMdP*w2Y$s5YZgI|@5-T(> z5jk&~OVOp?iQHq0<6M)CKnJyZir46A!vu!MjzNFFZD8@43UrO#b-l5oHHw< zQX-;H3CI**@#f&=bGFh(<5!!VT* zDZ*u6DA4D>$K?2O7xVv*TuwvbTh$LFAfPUR|CSTCeC6 z68GJHW90Strmn4_kEn*(nmdz;!xd;F71h`gWy$XE4C*SXXOiYUcoLw-I5enW!5OXb<8%m(?d2oSY! zNKI|OibsWEI-Gaa=&2Hkq5`FLU{H>k+HfL?(GmhO3t}p|ZsQqalKA z>7LbSo)PtoNGghfoF%P1<}e>sN0HP_R%I{n45g-l#M(xAhU5G*DByEhfQ%HcU}*Ws zs6`9bA;i3lWgMTY&Pq3WgWuT;>|yJDX?Kc|-~h9tNoA5Vr#iR!kTzs7G^I2@=9bxD zLCkB-PD;33 z-4-8Alo3oL-6aX3`hy6vTUouw^oQ_dfrn&N+G2XUo?OBwk6Nn>O^FO#9k5dN^?v%C zer&(_rRD8Tdj@8i)Or#tvQ#zDyu5z8ct z61)>yDn0TyCa``Wi%I(WrWw+WXG^69N{M37*~F%boj`694-GYfqH074W~;#nz8eGy zEv2F{6w3mq)SU(?l5dh#j%h1dauJ`%IQ2Y49o28RNtR%uHspRfi0CN;3G_a$1gp?Y zu<@iEm8xRp;Etmybw!3BiXvZQm zcY}bbg7wc0zp$RepeZDE_1L~muEOW;uu_3@Uo4UuDJfKQ>+xlh zv}d9L8R(+$nIdBwe=#x+8i~b@s(VHh_tQ-)DB&(Hgn)EI089!>3~5De&~rliaob#P zj%Xs;)H5cHOYrrdi4(CrA(Mo^CHv*pz^Z(oG*!KU+etDhmJiA3s*^V#m(V6vUD1y$ zx>gZY`P;Iei|UJuz4^VK(6ks&slszX4Fo)l;7_7m-kRvw93H0qEpL&B94T4N=`Bj$lo+4V`6i%Ci@_2orq*GVH|9zZ6XYRcwQ|x?%7HBOhLcQVyzQR1E+e0C$JSSorQ2*i;S9zJ4&;i5O zFr|R5_2XHYBC&YIuhwF=6$Tk)Sp8>NY$2RPRe~Vpkdw?zaMq*erhs$)vy2HUR)p7n z$OOIYs6>;}c@OBoe7x=oGYYij@C?Kc)Pp}33Ts^6bh0|!4rXI7p*L$cUZ+Cd$UV6b zUPneWaEY7mz?)9+An<}M;fF^=bb7$>ZKZYv8yXcYLBwG6u37dh?mF(r)&rtWQ08T! zvBC6nyRGqfO^Wj@NwlGpg>{6+mefZTjctoYA6#QeoL$?ramHKl>a4it^~Q8X6#WsIZQOz$qitu&XCMZs*$d zzQ6H1s|+#E_Myqqdv)>dD~y>72~HOM^Li9C<0X1O(7}Jy zPT*B}@}Y1qJjJ3yN#JmBO{bwCwQg8h^)b)l@_1`=OGzz2Z~_$;M9pq)V|7~)-X;Bp zk(N!NUAm=SkUem=RltTA#QLFYtg^5xdZB#Xg@4#KZpT2v_BF)U1Z#PBxa^(VS!3CF zL^M@F`ra)gP};;~!0DgU+nv$dQ6-|77;k{6B~qP`*8V<_fsO09T=%u`iXNob9$I&3{8Q%#D@k?SHh%?gE@1H1uc!<#N$NP=K>?GTDJ>XP-u z8yz}Z!#5_He%(D|(z%BpKv!kz=Dw$Y|4kON38d&~BD24WD9_cX8&)i?)r$nyp=u^!JHC;xIouo}Vp;=8Mv>TkK%8KshyuVkJDL&WQpmJIF%6VOiJl>*DA9 zAn_m3gBjQleZD?Qu!pIXSS#&#F~o}v`?aq|ruW}hTlw>TZ&z&)FRc)-*&(Z2sf@M} zm<%{j&_5S-YZBz<<|roB2YN86)Phr!3!^GUvNZZxf-v~v&V<@?hUb}aaBoTx+Df<{ zJ^d6@;txAR#J^RlcraEn;8v)~=pP$<*T5Rg7Nq8bpbD>%-s{yuY6+H^JyIj~6IFzU zN`N(liAJElB`B2%iI${f5VNcKDY}Vx?;gvgk<#0Kg_b21aW3 zDNHjuZ(DY9HiAMi@01mWEN?x!gw+xVd83b-&lZVOW$Kqod9=)D{&ZtGPvF!l)tc2e zlq3+!>$QaFIjqW>{LgQ;lIj0^2x67nBQ#n|y=$6lP1 z-j)FTQ{WRZK>cp+*9R>oMqfz#@FxDgnNp41%64+gV2di0to1<5*k5R#|9JU^c zzXTk^%^h|;>p5!;6g3xc%%_G12T`I?F8#?0brw5*DpmX~ntS35O8pqQWl(IY`+|Lm z$VRN$!TN!o~TO%3dUlOtH zGz8sfmEn#ZnKzS|D3txe!bCZervntHO=-{|vE;zKNNBU?X#$Uea<>_#A+7i)$FG<| zBMq+Dwfwl%ZAQfppJBftP3hd2@k;n(-6Ie}t@-ifEjpIN1}0yd;Z5J0)mP@}B~1j+ z4XzhJWaUF94{M0Bt}j=qE4`UJ31Q>b{Ub(HM9#m8_P+?wdCIh+G4CHb_;;R7DN0!O zuxR+(L$TmJNK+i4VP$QML@Bmc0?Ir(Evo>x519OjJ|krLu1|DArAlF^>|w?tBq6J& zR!CbQ?*5~Menf)Q=!H})uqNiEea@yvhvIY`eA-*2h%TdR$FD35QMCu@1mR1k^WvA>W7Y-fv8Dy1I1^XvV!qc958#o&Kb%H?&AyD0xJ#?C3owr<!k?rUmuCc$`qnE z)0omy#$Gat#)1w~$P93P%(5_9Q2gNjz?{Hgz$vxTWMp;R1;_=-qf=s!RHW6$XGO=~?YpP0xAWsiL)K#Rv*fhRF%ia+wwz zmnI5^N<0uo7?L4-SgbGLZY!k{ujvM;;yWZ2fi0(WvztQF0DO;k0VK*7?Zs2UL@sA8x)zw{8 z;iG2CQRLTGsE86OS5C1AahoHX7MjP%$y$M*Og${uTRylW5O(O;z^q!*icOz$Ww`5V zxSIQ&DyCYmj_a`>pk^TBtw6=|q`#wjg^(8CB^^14fXI`$t*W&%nPoXwH-t zhD~T*sv|kKw6DW~I9Wk2dLNdBrT&_MpelFU^T|$}EOI{d*`H`2p2*ZySTfIDALFP|x*6e$E{$&4jY#j9FVh`}aOIc5I_Py&B77 z>P9r9ekz>kIb`{8yP(>=HkT!(mAQYG(x#9kM=0*chUwxjt?SrWyRcJ@6v-q~sfd-@@ z(JMLnehB@Muu-s5+AA_M-y8p@O_ zH&2Ts!-980SXfy}?7?Mo7o~UJWMHc`M$O< z(r+~?!U<>9noCT7dN;dwD%Kp!c4uoKthV6H*~@KKI*cz*$ToEN-^QY`GN;#-d@5I4 zm&2#9^3IHcnz%(zmhU=I$6L8uurr!;MS<=Zq&f1doQZFhZMamfhm$HYwYmEH%6vJj z!?fxV>Uvkw5j+5aS^{mol6qG%7+qPHIoHdWl~nZ{Q%8VOD*fol0*RcxBpYqWF5t#C z+y(Ze`J~s2_2?{V8=?%gDs2k+kE@puXF0SWDiN!!1l#u#tYT_K7BtH1Y}pn|S;NVL z)?-a$ZIAmy7HMCR(50PeSk+;y5N8jBu6_2NeBGtz)K4ag?{1MTngE_D4M>XVX&=K- z=oCD^6W$hdzx#)SND@<&BX(D^O3k*MF@IIV>VAae)kRcFFYp)8fUi+&aKePSjbgZB z$8tq88zU^7{sb;PzK*}~tV(LJ00ZptGcDkfiCwKh*_`?4kVkT8R5mEju_+1-i}j_v zaM~^*R;oR;91}|-fSSS`u;(niL{C3$W%HHWjQVYBUZ)0T{ySNJeHmXz>HogJR#^|jTep)?sZrhoIe@Mnxo9p33wgV%7#1Q3NHIR3F#5Qr|ZW2j-ntQ z+T>o=m>dk!*yUzPg|!rG6IVj48K1e}W31TyvR-5?c><2<=G%}-#MV~7L)8lqdU{#J zAkjIMAC>SvWye0)rnDa#3cFB8WD;AJ!YG8{CnZ0ozhEfbDNz%$cjrFpuOHh)&o?t{ z(sbj`WReePN`+i#9CjwxbL3^$IW;PtrI@n}VQXu0P5@t&;y*2RJNc=?k(Q3xvxcR;=iz??GyQCmIiGfp~h=FZbK|aEEsEW!K&(h8f zvbT#sZ@$bOEsOXvl36tUu+IzP+>a`)SZUCu4mZ_Wa4t0gCz|93HY`*!!7%hb?${3a zw-=Jx25kwqvgMRUbcK*+S{V;&bb7!Ti|*x|lTm}*I@EVa)s5b(CnVx8ODC7T1sh+W zk$f+*`0~G<{&MLxZv*~f{PCeJZ9x*b1S#I!^8D}P3?FEo=C$~Oj$d;Da)ev6 zHJ;B`?Jk@`MZZ%)bwDZQ$4D{qF`&zB*_X(M70C*kLeDGGChgq^FN@Z1amHUJ+a9Vu z@dQ-rD)ez0gWM2DN8(^wIY-*bX3DL<1ygPX z0HyIdftq#h5YDk_Q4UnO4hBa?EGesV<`9P6ZrAtK0$&p3(8DV0A4dA^X(N^o)G7h& z9}8jLNTj9{&(2trcj+S1fQjiH$$5Z;&on=#o7mH?LwmKtihZXqa*-_Xv427u0Fx-O=VRdCm$9eK3U|A*mim*tePBOin)!14aEh7iyG5?UI92_U<})R z0GzhqG}{V)hh%EXff9f9h@0|D*93}>$k%?vPbS+-TL4U70I;ykQp~v3P-r#*bb19q zmjhzKF?XNZbOWypLq_T@oewp!F}P9%>>~b=rV6kY!}Gu#i*Srpjl&pEK!=f~as5E+ zU1}!0af>N|MR|6mCP@pj726r0)yK2zg-h$><8|LRy*wJ(y{368X1k z8U~Jzg7S=c_pzKv9j_eHO~1hH%Te6N8=H>j(@=^|Jdd*Is)8Hz3vXdH*qV&lZl&bd zpPTO`60Lo11Pr?d!#w{|@lkyxP81CK`?L^8<&}+XF!R)%Y5SBoPoKPzO%P^$nuW8R zU+8WAZ3!%HjU!W-b_6#Yis-y-H#YqotdW^e+_o0X05cm5bD(N8-3-+Yy3OFwOtEal zSSefY4EAE+An4Z033tijrBom!d}x%s%6!QAAvypjnObtZ3{|L>J&ty&_fQ4(W?Bx~ zNk;Ng+ytf0G}RLYrC#lCx;~zoC1YZLC2mv|szVteAzf`NsS7m&;hn?v?Lrg8>wW41 z1r*OU6jhK!eKJQS^tb5>DEBQiP^=w-{l|eJ0;f-=6UmWuW13`LjBT=o>HE{zbrVpF zV~^JgF1GK%Lbp@1<=>9v5em}FlABdRKQ+ijT}g8A0fca_#l+-n|}dfplbyX8Y;vG>xIcdl&L;K9jzcoE+9MO zJ7Hgu!jG6XVkBSa?Ba9+gA`9V8g>sZ+ek4TL$$EqDx2h;PT*7;5pPhh#r+1qB59+G zPh(9f=;x-|FRaa#{Y(Dt?7{IIwLS@6Ix5bwJZT1cRwAsJuDNpuy!^aqr7^4=O z(Gn!;KXxrn?5^*dhkoJq_iTH~{(|0)VR9Lzowue85Y=6Eg1a&(+XBCty!w@0g3Z=$ za1}M~>$G>TFp_0)2k7v^b~RPFvz1|~8|QSGM3H3^bm|dH9xw;g+P^cA-o!aJ*;vf& zW1%IYW#{{rE>$Sia)+bu;Lj4z+oh1x;H;&8Gv%Eev~Of77fu}BJpVO%&rm9r%O<49 zL_!l7w^>cZUH>-WVf3Qk`hv1`{`%^Q+u4%FhandSoMdhp*ZWo+Ud*q^yO~a_J$$gF zxs}6VwBbuttcM6^(emJ?g3cu)+RB>P!92~8dl+}%N(VNp<}zUhkRU;l%p(B1o}exO zjU-VVU?myAP;zS_-NLo|r~SfxouDo3vfksyv4As1o1oSam$R4dJ_kyPyTNIA8lFom z3GL(qqsQ0vEnQ;k>nz*r!I3Aws++4DB{Vb#7sV6d$Kofzlw z-N@FX;g;=!!o?d<4w3+{9hZXy`T){f+rnBG-K*x+!k%AtdMsu6Jg<`_ObLM0%I7lA zhmo5*Du0&eJ?kx}PrTb`t805{ZqE*S^i`@UO?P6Aj++A}TaUJ9UoWRhLri|lCVYVZ ztJqtWl*BypBl4E~XoC3vmDua_LmK!wgCxaHnGet-1YWw#0Ny=>E#7h%33LSNb;fNV}wCu2&xaD$N-VWQydq<=NJ0Hk*4&;<5 z3*nyTk2XV-ntoSTW?Hd43dgl^oVv$aI3VPZYFB(W5z77+Z9LFHRMz-yU)B%q7{T%| zY6sW{g>A4M@SWFpe3+i6^RJswe9=hq3IzbL@v{T}k3rP`6tw>1`9HO+Dm5#`O?IR& z-W~ylKS5p$+N01J8RNuBv17sO2;}$~?Ie{et9Ow|8+YQY1A%{gbXp8wy>^?gG$|N~ zBx#(U7q(kSH!{fLFCU?%qu}X$I(tNGoSx0d-T;8 z^4eDulH+YRCaNNbNbLRupFBdHXZ&^@LiW}1`W|C5eSEEX$2GAtAo;jQAMq+wqeep^ z-AS)!yo8RE3f(8{A)r<}I4SnhPf{dih@kjw{;eh?h&wlpFl7rdiRJ>&orT~kqQr&R zxl16cX+P{eJ4%wnME_CvdkY+SPAXq^a=gU|C!lRns`e0)-EZ>Y4q6QYohKjwZ_Onf zI3>m1Iu%6WY^xEXV_nV1;hcy=ao=s*ka3ML$F;cuAyLF49!$7Z2-FoujDTA-7X-46 zQVl7tQmUrZSKPv0SiFzJQnW^IP=vfJ&U1hLL+t-S|EX~Ir9_3 z?O?Q5s@~O?2Kpxm1cFKtKXM=tv-j4AYBiLCWGcyvy)3a|p&@0#UKdeQtgU-IEh_F~ z0Wzs-tO6LNYccgP!K1WPHT<0^0eATRCDi)+HwW*FZri1F(Az#m275FULK+xbRFx*9 zZII$C^qqdOFwK0mUslA{DKE&PkZ8jAuT-N8$_=Gvu$>+T$WS4rS_}FYmMZ;PRsRTo zA^TOzP<+zgoojA)o;pUV5RfoFBt?B+vL*Kce-~U3ToEBtm_KOu{;JcgOcVAI>ZS?# z*opHvK>T{;1t23&=!cI7Mc?g0v6L@zkfjA))sPkPedV1{-FxROyoqBD+j_&cW@8B} z!IUBRl$Lhxt_5KqR^Q9P{y>HGES{zwz%2=9y}D{tc!x4Yxp}$$V`#{f z`+7q{{ktRa!wOCEWU-wsQ%xqH_Gg^l{S&r$Q9{EtZ;aG$#~b z`0RzrE4;Z=>U=hWDd;;Yt%?(@IQGaMqEZgm&W-L9E>u;7y|YQdAqHqAQ5@?)B@#VM z74CycTr5D0WnEm}U+WeYVYG?+s8FeZ7Wm=P{RAc|YJ*&K)F${Nn7Dv=iv!BtH%4jg zAPjI%LVX4I6_zGwkWh1Pt* z0lMu?^Cuk1a-|C%%Uj>FMRd$B+Y3RfG+H>7rVwxdX5&zcIQ||D!RlvYAA#H+clSKO z7=L_HGo@rUD@6m^qZGlUGqeH}Tr8VWM`IZ#?1qEbVHp%&O@S%;?~Wbv%GXdhbpeY? z2|MBC3jrC%|I8_&*U(1QU?8r!fqLBrRa?X2pg2)ESVDft1+or8d%{tQn=i#?%jKRj z3H06Fl-(D{ftc3?A+Er3%Nt`pm6|Hb)?nm7>=u_>MYdZIHUp@`^7$}VP=&eQ>lod^ zLu@CC3m+>$lIVSep%j*FYB0$InGFxgsQYMYPYG#p-Cj6Wa z^|zzPr&6B{?kYn-iPr&&>I*Wy3qPrlq@F#=zolH)u!UpB3oOeZZgDCDK?LO4rxfen zFcvJCIhtAgByNS#24ZYgMeb6y-^%?~3wu%Ak>3-YGUIojMFukg4@c#9fT)bIor+@G zbrUl3=(N1$l$cAvJ`F_X%oTk*T$wb&1YRaXkcZJJQfF7TbN8>4hdhGF|HC^%r^; z1EvtwnFkTd-@e4{HnU?Z4T<5;4}sx{xR7W#b0q>>+!%|~)W2XuZc0B)1dNqUsALx;dyYu_4nZb2YE;EA76uRjZw z)hA5eBAO4TI4onkW|a>@%b&(!xDH5U0|z`1Vf4u9E^K(u);Xhs3h>$W!}s@@b#~6I zZ~fkeKJE2OL2PCD!0)B5__ipFO}XWh*+9KTV?kR?id%99JX4GQdfCe>r*4Att#0rz z51%VBfEEauDc${aVtB?_TOK-cYpkg4JU|8+dQ#xg4q26B#AG{5kC-dLRcmBZ>e3Q*pR)4mNQC%tG zLHV#vmRBaRkTt^&s5l-LUy6c%SxmB`Gsc9mH0;*gC3TEl{S6dyvRBXD>dAEw$q<2` z&XDjpXuidzen@P(JSz$4o1#f{>b$MZM5K1Q6ZJ@%#(DX>!=inzSB6KNgVsy>{cnas z7Nm8I(0{;sKOxQJ|5XEFU}^S$GFZA)w`?|85qws3_z^HDt>R|8z;1jEr<#9g(##Z3 zwXi@xH<^fGC}Nah3>Q8-zj}x{oX;-wyiLV%uf3m#Iuyx^)~>mxx#ml|P3md52skjS zP6D_7V1l|g&y|xobzxqXx@jTL!AofL7b+#IyS+r77wAkbrRwOEO($Q*=Cd+e0g&v0|z8yg1Gx_ z(B0a15L)3&h?ioFr$RCRPE#JdX98!dCadubk!3YBS_h%Urmar6_mi&FVG`1=-y-NJ zO4^{k7#z z71)C?&fRq?{J4-xu`A~n!S%+09O$W`{ZR^*i%Xd?ER1M`HTBF~m|qRpMeoRl$FD@k z>Cb0=vVnwEe5osO5aPv#;~!Pj+Wd;Q!A!4=cJS(A8GrR2XxTUTzIvAXlpn0ML?N9< zIl=4lw8wf(9(+y`|N6?kHv*3wm^q0@F2NyuJ-g})B4L^fO6GD(k@<9fRv;ClYUthT z*DEpsChe+0j+Pk=)+Tr}PviO2L`K@iD6Jysw6+MSQjhbO!2rqLM%Uk*!V<|#Ak-Jo5z9Lk>X}QzB*gkcx%ak&moY^6ZuN>GJM8SO88(9+B$7Vh1YC9 zs2WY$>_A1qV7d{p1hLsuS@HYG2CUw))5lL$cD6`s7O>um`wN-8fLCgwH_+QRq|!$9 zA&%j*LqOs@la%K{h+s%H%HMND<&05fFu*+{z1Oya17x|& za|I|aE>^3u$Ky(!As^6&rSOLZA4>}6m$`(>uLcX>(Ly#PeQVtyOv>fvCd`%W zwF9ngSSqaayf91z_sAX)%i%guzO%9if7hd$?xt2|2(|WImriO^W>gmSWK|u07@7^@ zi(ez7?X;2#E&!AmllWp(p@OWa+32>WXGY@9HXm0f7#(|Z7d(+;Yz3AobxXxAxAA|_ zcwhT-UJd)aa?dyC@%i{!#7BgzH1|D%ZhmEvz09tWh!vtYEI2nu(5lS-O5IYP`U$8a zafgtbhr})L19eG%C5#2fJm_epltRSUE~>2HDZ2g`BU>pG0b=exk`v-){qZ>=ox0hg z=S3+Fg{^|w&mSDqOW#~3>+T>ch?TDAE_2gM=7AHqIDkPUu5PP5yc55p6X%vfyjkge z%cf?T9YtdFn7XJ1GmP_mfWM%xBdQpl$(dJ(dT$;a6G%1ahURCCMu*tigK0}lcbjX# z;2%hUBM&Y5eU9Y4(>irzlmTxu6v3y zM%y5&=7zaI-^sktqY6lS&b|k9`eFZcK(9MdUPz>tAFFb&Ov@INtUL0%*IxRaqJn~x z(rvc4>B27{a~q|KgWUiG!jaL5l{Ii{Ce#1T4zqLmQD-lYLK-xHY{mUF3a{bc?cuK8ZoM}!008O?{~vq! zKh@m8#@gA)-tiw_FP4SF#_(;2Hz?SclplkeH}Pd*=6tomL^N4lWnGeMaXl1X7?B$y zL@qlY{vti??w5m$Dh@}Ou^ zp{d1Wm~qHn+xo(S;7O%5l`h$N>F;$;CkjbUZz?5pH+4(?reR&MhOszda^QUbi2hJT zVdP=`MPDu0HhK}cYZ@w+m}Q&PstWngO22>SVvTfYCb|R)+HUN&`n?)q3Kv&Tj6n!< zq2IE2b8P~W&I5foeq00=jaVS5#BCloovXGDEn3;yifCGm;eP68$Yf66d3LtpWVGm9 z8ac`ZZ}dFDR0 zWq|erGwdN3&ZNurzRQJ{r>JZihc-1E2p(_#-VGw~oeuC{(rF_~Mdj$*n`p$lz?6#0RbG%28{7{tpAt{(@52pxS7z*7 zxDB6?LGarqc#u1-O+oA1yC6^3$r8M4>2G&?QJX4R%K5@=hTexJyeq#a$?VY>!=*U+ zFE_<&mV4#4H*0StB>@|ci3KM@!o^h6uSl}-D4$TIv~UO|LoMs0XF1B5XZRl(q0o@Z2MV4d%Zaf`7R$glzLChhxfvYf!QG zx%bS`BMkZ+6`C)kx}9i6Tl9vdhzSFek=2&&`2%InLIK1-*g9lO{aD46vPY3bl@ZKo zZ$xz+-bMh*CIe=t#*)CT6OYb7Uji;u7^`|Gv2wvc=_H(JC!}}r-U-T%Eb3Hh@3^Nap1Wc2eb~Cd#h>p7A zh+_8{$b+!_fJPx+%UI60^)t13$yOpQ z;ah*B6@SZ4pKhopG@o!yx)E*$2fi716>I+%BK$;Uh_{fV^L-%O>+=Z^VRt$BE|f_7 zA!#v-I9 zaJU(T_IKx=aKdk9D_i9g6$CZ(Gcjo0lqTXOphOmh* z#NJI=p_w#3Vrr?hFKgeju*#udOoKN@b4u!cpfZgr#*+qOfD-EAB2dAcwcH9f(uZbE z#bs#TuU9K@$W?}@D}s?eTI&$J3*g$7LUW{7I5X;dxTJI@13?;of~w&?gj_1!qvc{C z;5&{7EG9w?8PF?Lzk47DvY;UJQv;9hQe+j$A5B>9J(Rt+w-7KD229IqiSqeK=|~&y zk+Ibeh}#wH4ZIUwp%j-HC!%OkZ0`y#3*zgILQwh%d=#_~7a>cadLrt^GHrfW=ujUr z%o`{V0K|XkAx+GAo0TiT-4=i*cP+mPpYu2x>t&kFG>B9d7=?UUiSH$bZp)G>Zp-I~ z{Ib8hRxxMAhNeL71g&|eH@63#qPH^ zZaBYutOt{b24i!0Bl>AO#ws=bhUqdVUEy<0DmUdK$1Op>L^W+lCX5tAocUy8U_h0r z{ZIyD68FSLSa~a~x6^s!l6K1joC0X(pW>HhddzU^U8N)*x0FDV3jeb4Slpr%(Z|Sw zY0!+BnWMhJgxHQ+gN^w1%qi;_fNyijP!MJi-zizV${{ObQG%sC5yFWBoNP@f%7rt1yMC}A68rT9P$#$I>B~_+H&}$(%mPP%APf~ zLA9z5@CqyuJB&H9W1DRRvxijAI)hbgX_0o0P{YJ}@3>X)O30&I1!Q<&Ak6jk<+>v} zg)xOs92FZSWVCEcwO2UPeIIP&l<^pDKh*5$6kJFiMRAZnkB6P?i>a9~jk?pL6I&|% z{q*Tk?BZFNK*30Hyv!PVrmG$pMoVYXpw76!iln0Ig?`-vpr% zv_xZT4rCe4fKf@o7VAebwv=;+YmGDV=P%>+JWy0%*f(7Z#LDD=x>u`IXw1fBil2=Zb37HO$AaCmk=Uzopjl%`WVUmHm9Qqk(e)4BWx8$V-^|}f`;v+ zp-m`ughZ{o68I7KMieJwN(<}oPc1*qff~hbc@Y-O!DNR)u#7NcMlGmSq1zryoHd4L)qL!%FsjMp_X_qb4XIIMRNG3&R^StBRs8`ofw+8(5v$`bUbWeZt_s-$RwIfFrJhdC3k%=SK zpVbG)VH~a#5uujdafNegUEzo`yJsxE0QaZ^mCHD~+@baLzHe2NrlI#^8!}J5u0?)C zQ)EKGPPejz1+k_MG(>!Dkdp{#pWly1(M>HfyCI_3R!GxsKzk22_+OTfC6(UcY z%T=Dl=$(GC!$VV`dOhIU$%SJvH9VDgb($<+TPXT-=HhQvz*#L~{V5eLKSu8h5>NX~5uoQ9<7{6*$@YMQ=HY5c)fX z=!7kUq!n-+{`}8q7wre2B}?a6J?-i%shMCisCZaN2MQMZ7r`U@N?AyUzTTZx@d_22GV|gzxQQ_4}sTIHOcn-Yw5=;RV;XSiO*o;;d_TRf*H3)MXcu&th z?Lxd!5iU-6dkUk=Q)Kg{M@t3o7u$TJzeH~|siakU^<9o1z#zbe4tl2R?cW5X;i)c+ zX#v=>y+@2k1^0vUYF$Wb(X3qpt@UQkp;!59-j5?_Pim&UF`g; zd`#__G~)3yPmA}{IQ^Fx;D4GXTRj7de{^&eNr#{7ukQOlxt8#TRtz;68fy!S&KYLs zhBz|^xve;2ni8hsvB176+|geiE`YoM!go?`v7UKe`S#%9m6ESlOdI)#Q@SEZ zmsE%@i^uctof#TXlNOrEDfc()Qi+|@DaJGMNP4UR4oBihK3ZzdxBw0SeYCMK;ls0*JTuN z4zm$Z2XY@GgR4e!fs2?KhWUzX&3Ald*@YjKhGYDlSP} zpaN2o5HUTi6hKyzdCH$B8qXtP%o8_xc`HG*XJi*PF$>pHq0dQHo0>9D*iCnG#e1Zs z!q@mclbF@;$HYK&CKp?nh*?(s>f1?+0o81ySsbv(mz*q( zK_Q|)w^j6(Z`*G{WVVLmFMyVTm<+ZWMK+HFdqRdK&*9KR=uE}ulSt2fb~cWha^mj4 z=P!vgS7Z@@shc_W*;+_FML~c&*~>x<*cK?b#D8kdGvZc_*}%vOi@#hh?(jF;v|hr3 z6OHbKm_+Mdd{<(f^M*$wV|Ky*elZhA-{iaIrl9`hIz@-X zqNrf5Q0{ML%C|Uy-D$_iCLPb=z7zo4IREhDKPkdO#6AuRIx=`3zg%3A%Rg=zwgckF z=<@#duDPEyLe-$K&uS?!UuepgJ~6Ld)l&~(0@SCDyy&Q8ZBJe+fiFm{Ca9X za$%!bw55*E!HhD_cDqGYSEiPZSZ-D?q)1RMXf*1qPC8U6U$0cz$}VlCP7q0A^my=? zhx$>SqITqrm2=0Xl8Q#f`1@PZRy@F6yJ>=eov_+i1v~~fM5!i4*VQ?c;<+z^$f4Ds z;NQZruk{R?)DQD@&5Fy5n+TgcO$3S#WtKY&mz;c#)>XU0)M#+q(QA#rYvIe$5If$I zF!dnqyYz2B_-{yO(&nWiiVxCs$rEd5tO7_elBqn5Krm*iMo&pXV@s*u1XGzEeHX%F z&~{I;;4{`StB&f!>TuxPW1aE9=mJi}M)rn;1Shp!=N>E0rS;Pl*6GFkPfk0pDnPeJ zp6aO9x$T5ZzQo+w&0+FwT}5B2!c~(CF!E!nV)3uq8V=^`G!uelRx%VV80u20 z(u+pN@kv{&Xr9Cr&el7?bot|oNgTO_iYqc{3y>=U!vXh0oYa_eBQ~M+_)Z=kcRxl7 zQ1V)&L>}HP9)v0DsPI?oE$X`Tw2M+xeBCWD!LoiH){uQSj?A0|y2Uda;6N$#wJ~_< zMHuXM^KgM;H-J)`x5`4ooCDiyB<}eo$m7F1N2fJalGssT5-SPn=&G|S05nKF%Rktv zHx!$<$!FDADHf~6QX@1OzDrM?qq>9@vA6#$r&IR;TA`i(P+U)oBG)d~xa+1$%n=yR zsu&64G3KAs{pIcPE{gEzpEX*_8&ZwF8Hp|EpOm_xqb*upZG(sl7a|fs;^k&%Gv_#4 zBjry@tYLk^x@TM0ho-qDIMa(g%iwm#KPFSr3>g5&*zOCxgoy|g3E~bc`oKlD!#(d; z;O|m7u6Un`+p9V2Puyh*=UeP97FXa730Ptcjzv#9CbOEDVuazj$0hdmIQ;88f&0?! zHxfiZOV)&@Pgyz=4JW`j9(0${=Y250h=-}Ip;jZ$&TSX1-=rC6GJt9w*Spj=IsE>bL?$9+>$F@W|} zFF(ibe1pHPHCQq@))oX-tY=LiE4%p0*0y->N2+cAE^vpAKF#GZ$Q@XKXvyk8yJ^Yn zH_nNxhu+T`=Z=BOfb^#-s~ywDRz2`EQuNiIdhPN9?Tm5tq*W^yvedgt@?v}N~O;acR*=<;9+95!6H z99lkSVl*&g5q3}Bkr*OJZGU4aFXhw&Fl8=O#MbXh!$iijrAw&t^f$~Aj@Bpizm|HB zu#6JKpTLdJAF$8=BUfSw1AMLq&!8! zs>r%&WYo{j7MitQ+O&PV$7Pr=xe^{@*k-%a%^{`M8LoRpyP@LIvFz*EE?xB&>dgW5mQMGI+NfzIZ$qDok<8Qcfg?#7&=7t_a>f)dfDP z863RFi!p36!2meilOe@thpqY)85!Kf8w@7Ffx}cIwAOy9ng+INzw%Nvzo-xjNo+^aR?6w&)BMZDx1~LU5S&m6?1p?s~K)QR% zeD!)CTZbsyt|X7qua5;0wb|gH`hJxJNj9O2Kad~TnUw-)eHl&D+-(EhemNMqqAO-I z33n6h-0=6*8-P3>!g|d05 zka^EW@|mVG1M*7ERBw`|l@#S}WLAX*FE?S!T6gL%*7d<2q9%qOv3_3>pSasTfh{-F z)-4+yn@JS=Ela`ILx!oUq&X4grkkN|7imF_b?YJR>9n#a|pl>Tn2#pzqQzZu1f3Z>PRIfH7z4GGb!29#KJhov-?+ao%?aX0D}&>@GEh_^NK;DZd{KkIiKE)2Aphj&JjU!tNPTZ z#YN{|H@>!|0QVI3CpYB>wReX<@g1~gtyc+etJ9Rj`ov(C8S*%$v&R@+4J!961PvqTR?@t{j=$9dR-?vj4 z1AFu-jZ>SeTN%ME_BPsk{qGx(*VuKcTcIv46&Z)0ch*ga*VoJYdJY<)kC!8lTT=sE zo|>~7Y!g$T?yvX2yaa$dlS5JEP9@-S(^EQbR>ogKOnXoDWE_h8c6-8m9ZAjsOB;we(5FH{8s!p}G5Q4(Xm_xqEykOdL-75J zH~a%0#W)&EuqE!89>uC=Zv{&opZJq`iYmSmwQ}&H1S5Jjm0$3e$A_Bg3>QDqa|>BX zwY3{_lG|`;K;~rFc9Qusi5Ohm_+~YM)Z!AeK$8I1?hIhsH&Et>t`_tsy3&deX38(K zvGbtWU^&rRpc4`c&Bgce9})YxAv_a%u*dsdb%mn8*SGlwi7P}x!11O!6ES}cg;9A; zNlx*v0d1P}NtOm)y*vxRh$JFqMqj&8e2~@qqE=lT$mHGI>aZny)Tk1`c?geyUoUjW z8y+Wfm`0Mu+86O7+sAC2OCMeU2 zfy8Mn`5{WcR%eRMJV?=g4@5HAfm+TWuS5*cbKCGlue_^(mN*C>!#@AD_tGXC5#Z%xz z3Y&n~A}( z=elFE9a)w0UR-Xx8L_A1n&l*h5E4GVk;ViH&u0EIpefY7k8Ra==cD_3kQ&R{H5|xT znECq%ju3DfAchQldJbCVZS>?)4(`~xf|6#)_Tt{ahw&0tc*;#_*?) zMYjV5my@l()H6|qn1$1uhQ6H=-l1B3g^Vv_2st+l!Zvy&QoWUjWDd>95&^ifTLb%g z&_>$8C+|*z%UY4ARA~hl`7H!*fNCv#)vw8Z;nX(M3y{ge2kSf8cqX4f#Sq@d%qtrR z1dWZ&>j!at#GTPeUui$iUl{vHVrh&e0i{~((jK5S?d2Uk0$6e;{)WE4=(+8VF}3YN zy!{8ZLcPquyIfm69HkNZSAnt$i|A;;6%(d4^bg;8eoRFRyu<>4P-7xKTqtg7)O3-l zXqIz)aqRpWVcaXK=j3()CzK-N5*_vgnRao$6dWq!B~ZWhPKsEJ(Gpu7)W=8Wl~6Jq zP=2R(S^6i5%^Hx4+ZAM+e@-|(!JQY?l&JrfNgc6M>hNmFJPsLE$Y>q+uUAEHDog6V z+ufbhlJ?|&nz1(8$~(kFtr$zJ?=k=e`bkigC3!R)+KQ79@9?ySjxO4P$e%Ge(~`a- zLxT5?{UHUEI;tJuFOn<(M5llV?A%0r8Y;CosI~%ZrE`0TkaR9eY66lav2U;rJSx0~ zq|qk(0(8tsseJqf)^%8RYYxuxyG8>^*Hg0tGJ#H|^hHJb{)b8e+&%K@l|xez@Uabv zcg(yQQfCzM6npm-#3w=W{Fu>NbRxt)^JX9q$++V)Pv`7@vJXyNF2h}P3R!Eu!I39J z#X4}KW~hBg@&ZC)@S=M!1tiYLV~E>@-R2@d4~I69{){3#;JFruTj+Vp%_l8xZ(S9A zCVRWbI`_J?v3Y7yBT!`pLog%=f?TFL_I*%3xV^;Q8+R68BY_vd2bLv%qLmY==XCDt z-NBbzmRj77f#ES6BPJZ36_q|R*uks0M2G{ErOjubR=tyEUC;y77(35kN66`SJ7|l( zaD-AXB7)h!$g^pZRRv_(ss~;gGWSGi$ZEVBw*nkwmU`fyEDp1xVY%-R18c7l#H?z|R9Jh|uimUMWib)C5gk8{PPXnw43VVur8*qjYEK`;$t`45!tv#wT}yz60@2> zA0U54&F4KGhWQ*;=fSdO-Jv4x!efXVKK@KG^Rpwkw05uize(K{kH6BuorKGsAL*+V zd>ham0F==e7bt>t8K54>?B_rpc>eWHf>JqHUS@wzhY21bp9=j5`yFsxWO~pS<=$Js zYCa+iK#i6to4E^ussqGc4q4fHU*#OHNP3JjQ0M4PV zx2yMVltc_$uK|%SdLQC{aQ2SPnT6ffXl&cIojbN|+qTiM?WAMdM#r{o+exRRll>l4 z?dRKb{=i!6s#RCjm}3rL!+-1!Me*AG?g$YGHWjlD9V4P{)-G9_Y_ZCxJoraYe>tjX zQFp|i=NclpS@5Tv;1zdI9bQ6oZlc2B3gQ0rDnIFB(2DXNHFduJlw&g)k~31;gfR4z z2hoi#45=Bha}cn9GjFy@Iy9Uhp1Ia}JBAf?EbGD0lv9I4)F0LNy9pz=6LjwE$#H$e zX%Gzw++4FVE;RlI#Hc}e>~~a*tYQQL_4dcQCzz8^o{Hpf-q02nUof0b@`~QP#HrOx{%aCHYe(#P;5dVqOb(jpOMO|u8=kY>v*sijz@)%j7sXj{KdCp9EU|PAWi@+&!ZUfkKnO&;Dv&XnP z**aPfwdrylz7CH4($7+*NPz(e2|3^(cF#D7UNL{p9r+>QSy5og3!r;;T`k5Ke|w(|l|j_a;l2QjnM5&_A(fD%L;a14sl= zpH%=mth%xkS?WTwL%e-)W%r|DUN<`jg^|h{ zLd~gR&$2-B5C)mFiV*@5wJdQ*Y>g|>lRbTViFLcd8$_RM%1H3e4$rgv zC~oI0@1R)e*lR|qDF(>{-$Ty{k|rZ{juCHn*VGLD8&B>rqsq4Fve|DLn-eRHicE00 zXu}Kb&`k;bUKUV>s5VYjgVwO$EV7&UEgqWUlI)zqT#l8>zQ`d~2^~WYLsvnN#fHOc zDA)OAMSTMQ;0oeo`6^g~JwbLXk*eGm9(oq6Zuf6Nz91B_pdv&xr#xNFptvAQx7K5k zaCQvJnf;(wu^NXvv`pCm&&n^z7XQ9T&yGwo3=o8H*$@(wHdL#=@LGEL27+$-C^Z+x zOBDagMXxTF!)s!DNT(o6nQw5Rm7{ zeX$_LwlAF;E2RQ4pREd!%`KcPQ(71{mMcPs_2}>Qz26E{Rj1FWal1OX%-GhuTYCe0 z|9<^%yDtCu;Pg2B_>&M#_%{8=6aoLs&v4(&SGT&~2$2xO=KkL3^WNTNayiWb7Pngq z=+kFSb+HC?d&*G*0FQZ>&EB&tIeXs6E{gd__&yzp^LP3~^E}s{sOJ{Y>IQNnkIKaw z1#9`ck9yX~31-ddG~MWqz{wp~1{$W{oO2a!C$E9=F}mQd&cG)_?7Q*;Bg!+V9Cr?D zXihC8K@onIv!&YkzCY4#dPAEx#w-)D$$5+;@93!D+2@?zG9hs(oBO;ZJ31lskRK31 zMVt4NZ~bRn)P#T(Xa*ZLyDLhNBklExZP&2)szD8qg>+Eex;uNmF2Ar45aUS=Xh}oQ z(r8R)eM>>h?2f=4Ky9n9MVuP%ighH1HE<`_JBKkP18zYY-G&K5y;gEJBU zD_n9;0n$ld9ItfpLTQoQ>bmz2F!o+7(x0upVHb!GCd_CQ8xU<`BF39;$xv>YCQN8WS#V;mGNI*9iyWF= z<=k;mxY~L#3W#fe#Rz5@raY0CCfh5a6qZRTVL z!I=&(2F`Rh(h{f=E-i;sW{O(QTc;G_fRhVoN6k~i%ZJ~#Jbj)_N%7AjFeEu*O}`Sv zOvG`E81VYqs&K2T(*HobUiAL&0Ww|@R-)^N4ifxf|2hAE4v@ksqB+V64!@budf!!e zua~5$7TvexlyG%{fZD zl+Lv*43%Fp2-!LM1=_1qW%hT^ZZ-x^#0a?JfaO29cH0WK`=hboM>?dIbEd+tF&9FLGYGKdsZet+blhBq1bpm32QMQ*3$|GSH|*wLckM@1 zNenLXB&eOTp$Ai7Jh$kBM~Viy0SsKI1$&5i(~2z$3>jJ9mG#h!Qqr8Ru{$?k$j~<- zYEceB=2Jo%GIk*qaN#W}R!~`%wXH!;pQz3iCfcbd(8Wu0k}*hF==vr-)y)wFkpqq; zl?zZ>TSbrfBZW{!fUFT$Gvpd$VdZO3%Q2#`ygr6Q&syPpW@;RgxeP2;PCKwSCP9}& zWSBoQ!&#UyknAk^S0N{_?X(3V!GOgZDrfb1=QZR?l7{X#*#ObSvz5+Bh4cLd2g2c>nh&Foo4o{&=0lMy38#{@wNZHm z1+}$_)|E3H-}ewffZPz#u;~#YVU`zrPN34@Gq6dDlM-=@#QgHoHx4;cCbJO*AqgBY zr3@eO%A!mF;g3q3rK}{P$izxcUFpE_;FhU%Z9no>qZ^=-;ThMi07UcHuHpajQP^kl z-;GC*81!%j;K5VXmyfQI6uy{f{KO_9$bzVFiPFfnQ{8B)M`T#%MlvN&B9K)#6~pp- zE=H;cBS{}SmMjiA&HErIPZv`VMY%T3E}54#(~(>=WtNE6xiF*^d$V@HjhWd`&5;pt zg8SDU0!qpfk3>K6q2qgct(5y3QZk=L5&Bb$`s5uS)<1kkJORl6u)b+KW5<*pSf&-rUuTh^>|HvQx-v&Cr{4D)u1e*uK||M2O)?h;~6=kocbP!m8vzTD03 zk2zzj-SSV2<#vUC{#*SGMI9I$J^igQMot9><-YNxk5vL_imCaf_n0+lmzsdvM#6%e)o~gVMQhVwyk*E8M7Tg2* z=yw-lzpg&2uySp_|D-*!?SWy~S}{$m;|S?{2m4hDgICDPwS=d-EjPiGJ4m(~OP%7_ zr~7MM0sbra`_%Dq?Gk>n&-=GDfBt#tFOsaqh)Z^7sv1P|kZuafOxXsqdfBXjh7`7% zg&0rRT5zi4a5*9*C5)f=OCWgqgvhB0w@|Rg)PeG7E_C#T#ns2GzU&h354NJaMBkkx z4WxA{AL-mt4=gE?ku{Z*87xd@{t>Wnpl{X@v@;;mC0_OtwNKk1%_t#sVXw!4y|bkE zux>4VP9nWuvb?W@=jTJ!i;F6pL^t75Eq8nP`HF%Ne+aQEKX-JV__?~i&~An=wYrNa zcIKl!Pkrva-TXB)G}!$$+|@j(IIHi(gDw6(Uwl}5O8oRB|K*eI5x;|&MtZV{cX?&h zB>WcMX_aK1GKE%ZMf;#^t+s3Qe1(5UqyG~^LM_`i(N)7DG zA-%kI79;Bi^9QX9n{Pri1Vb+26qcyCiKsj$1Yd`^17#k-VI(lYS@2MxFcA%f*$^`Y z^!l^}4G*9+`V!8QvG}v2{4oGOC-l7JlI+=8sBa?RaQ$d8r3;;AV-u^!37iHqT$!Gb zt?(9;jT^_BTC$`Z+uUU2d>kL1zF%bomO!)o|xJ4s{{MK z;vmcKG!I67s2jh-$EX)-dt~^pLZxYISYGr3G&0c50DuUPELPYi_tF*uR=$ksshBy! zP0ty&b?^EdARi%I2CIG#izbS9ngkYf7WBjCVcc*HsUVw zZ+2+0&zODaQ4(EpPE6bjCaYu!qVMz)#f+zFSqdX1Ox#Jo=tNNrr?)m5^K%&shlRxp zhxobxvF*wxy4?p^hqqTBx+qKQxCGUvLCF@UU*nscU*igx4)XS2!*T0J=O4bt{INb? zbrueYQ`6)9C)1DX%i}!Xr^#(~R~Y}UOeR-WSl#nzZnG|zW%(m~(u%X3TFK1Lax0SM`cT7t+06$LI_Iq8+=M0eGHw3pQP7JqS-87u>)@LL2k%)Rs?M1 zylz-dpbesYX@89L7Q7lkM{LDS5}u0u#+w351HJSmzLz0xkmSXY^}8MtI&g$Jb=Tm2 zaFJFND4<;C!M^-h$TiX4h<+=|qDB=#`^KjBQDuUrS@3oCGY{ElUkFnjlLlU@YnMu~ zrb-bQ5v%oNsf6Rjo)2CGbr>S|WI@7@US!rcCrqla_c1k|1on)DDK-$>4FQz444PDI zvic!YS(L?a!t2s^ibr&M8L8cpVesDiSF&s)y_;5`wInDDPiEqi`ecTyH{-|EzuDI2ktT7!B{j5|%+d&L^`w{y%?guc$ynbWA60Z9l~^@y4Na85>}bdkw&CdBner8bhRtG(qjgvDG_$aD z92obfL2Ut}-XgmNp(~^bGgqKTzYzk0)I!&I&Etc}LHec3u85@w*AHRlV}#MBq(Q0D zq7xkExG=Pw}Yw2%F3T6*UCR$A`bU5u>h=Q}?vB`nUBcKvjHBs}v@;|^ z7gR}j4M0c!Qq^pbUh$bhIZWo|Y;`{x%rf|MKkw@5>i6)mkPp|$Dyr{YB5*8FS@Zq{ zVi2eR#`s zZc0^xC-9voe&KC~L{oNf+|=REnt^O-vQ#~2$#qJGeycynZ+?NbTsea)yA`_GwP_mq zJqVYiFjR7<35XR?kDl|#K#gk$*w6AB0Zk41$k@=(ZvLdqS5QV;hf| z-?KpeFmc9gaj>M(OV;3Rqss5giD(ll*&=xv8|wx*3=fQ%B}cC5d0&KOsu>RPK(z7!}Kr5RuB$=$sRq> z9#yP@pd`e~K4wNyHGhmfjWU%SBnU}-#PnGFq?0Vtn?f*T>|@PxDCA(okwu&!5qTYg zF9#+<+OS~{TDH1bKslTl6*=vA3{N6brje+&C4%+Zj=Q*tJ`c{IAPOU8ri*{YRdw{q zBD*=XR~F_Mb@Ofvw&&1@Nzb3y>C;wFd)m$FLob;5Vqx-K>#MRL*!dci?uzqvl>k;t zO8;m>6765EZV!Cq`>%-r_ZjQia{GbJX9QccE1t_l;>GR)6tBG^rs+@Hs$@(#%s@Lo zDh4Iu<2sLZgg>vE>#>qY4Q|VNNUtaq^5`N&Veo$&tk$hFIqX{&Nh^68m-Yjy1f@-P za<4?*`O^dW)4g}NsW-R_*f4$UWv!B3l^={v;+FL?Z6^p$Vbw?y`19~(AZ#Z$Hn^`Y zhVM+Q*E*?c0i6^990IwiI0Nh;uy7QZGWRLW%5WrvVvI@6)WV9w;Mwl|0fbz_$OnQE zr4hmqK_Ofcws3rQtXN>2m}aR1=m=XC9Xt*)8b?Qr13jg)ni5P(osM6C~?Br zDUANDv)Zb!GrFRGY#s{(6&W^U!|_Oqq@x>*`___6?xAA*2oUL-;X#(#DE+iju@4VX z0Ju}%o|w~=5;aUi$D7$MId3wy|Gqw&rJmQ%(`yPo-&xbelc!jgt;`+DcnkR53rHw( ze_zk+D{pC><-oItDKo9xdG!O|GDCcBqDg){m(EWp)us{|DtklZijIeIwS!=;A*iX- zt!?wS8~2sj0;U!HvlR!07Qp7jB$C=TI-Z@N`ra`||WOq1J6= z2!)B*x|&=gw-#kDF*o~g`WB3+*34<@<#Rz4h$_cH@Aww)DJ`Ccxw4=lrkh`{d~1b$$4l@q1+1jXf>I zff&PJyZ-dbDSUYmoP+It|5yfAi)E$2m&raDuT{_;f9%l;)U*wjFG4U2v_ofUlz{SE~u-76(`I;nE&s~iKS2Zj%9DJS_#aq@|igUW=eFW8? zKDsT%E>;Nvww?OK0Vx+*No#JMi@{PRM0vs5QfTdEzd1!DjULOoJ-F@I5NU!#-iFII zSKs_oFUpMJX{E~g`pSW@y{jUrCy7-;Eq!x)9rN$&kKE>n|Dw!qp~_4IN9~z@mJ1X5 zi5-6t<>^|ZorEWM`U2&QT0(#^rR18t5x!O{`f{zG?)<{LGw}In)w|QHz0r~!uvt7# zD4Cy4g8M*uR169vK;k%3`X`sxN^pA#`slEGq@;xHd6BBw%OU+)EN+ln2*z&OW9W|& zVdX2t&GOKw`J;O()7DM_uDfHRf1IJja(}t{Z94R3n=6;u3ih9t+pa$$4u~7W83J$S zDZi%dbre0G|Auxta^^a;7}o#eRWs9B^xn1=g z6mOtkpCT}E=jrk7cw5i@{Wjk1v$qTjsYrK7VvpGvRSH@vS6@`&oTPVFJ!c=1R17Wm zOWT2gE+W&Zxt&o${M^J?x4Q(MhA6n4X%P>ExrntY z6^*hp^06@E(ui?paPq0aFDSX{cs$Ei7}&_C*-hISDtU9+s}B?MAMExOmCrukb*(gU z#82+*m~2rrgoFQE`#v{j{tJY2r^x-+X|5+Wofw+@i zxV*4!>!NHEWnX9fIk(HMfA_iY1An}wSR+{l_WnOcFaf|wcFPZG<@}S1_}`CUZZ5X} z?+6yFZudViFJH9=PK*4q1GYxcorX*&Mxa>;;GGL0K8yt2^29dO-}<_*aS*~^JjER(YIsu{cnU6B*s>R}Bbyn5G;=>&~vnFH5W z;Gu~rl~pPwY(9=@>83wNE^(^1*SjO}x5@JNF|FzCrcXdQ-?D{wQQ41CC%3;}~d0J#-SN_CkgTG>7 zg`lOUjM3D(rnTs?E|xoM>^|Gd_{du(`^^trrr63zt8~p=`YZg4_w8?p+U>CV{x>a9 z|D)T`H`vD;)CZ8Iw&(f*lwpXEHt zafX;H`-y@|B=uo27mL&dBrCPD_>yI25wJN?WGgZ>Ft)KPO}X*ZnI5piZxyXl|9t9O z_{R8B4R+gcrNzSCCU){$rv}MVny|UlE%gMxPiHm#o{6#|WK7IqGbgU=KLkNt4dxbN zu(v^P%)|TV*ZWY(66ddm+`KhwaLQeBE=UMjhtq<|S@m2@oHh?ZOhK)rmhcpJ6$3LO zFZ+irEnB$ui1-L~c_9~vK3^=-nS$(;(HxiK}5+lsy(?% za#YanUe4~1FDF@1feQX!Ha=6cfQMM~0cSDQWJJC)l>t_3X-0Pj)Ygrz;iVUKuwXaA z&o!#S8L$@trALl17*v#ZN<((V%vMq?Wppfi%3wnlPNH8z^2&aBnc$1rm3u1V)fe=g z#?ssL6?VlX^Zi=xJ_TrlOqwbq#Q1O{;E^zHrId3f?Q;AHapuar|Hc{uEP0c|YVA0P z2b`MCa^ve%ol-Wo@b)kM4vuKmg5Y%{#ks9A{zH-3jQ0_-i=o9`_AK@%YcWKi zLbCY?64!(KCaK~-Gf_gO*;ag@fq-m&;;lITTRO_o$l1lr`G<>|*4A;@1Yr2T*6o!e zrYSG?8xmqLUAX5g<=`A{U~gJsLV=_=4yLcu_N~bXPyIR0ECyM3{ zx_r(MlaD&_?|FVo4)RhmOEXVHiH$?lNJ$^CM`|cg$&Ax%g@l;*$Xj?x%yDa(als$z zG)qyDc)EoL1tddITB;0H@yZ%EOCR8cJB5i`EdG262|Tq{kxo>bjw~`cH92;q%iiL6 zpiR3RX=5m4M&c`X+@UM?9a90tt=q1*u7>pYsnxVIJ%Ea9~B z2(Scy5+Z|1`z;hg50S!+6y6u)Q;O|Q><%I$<)ooM7dCxBIo_=+cNLTcb{2)r!Y$F? znsEqM_(LW{5Khwh5eY;9jU-{981K^s{8a3*?u&_vw=V=vL?(VL;+!N$Hkv&;Ik}gR zNo=6}SWH{I^I`W_unHvbDhbpc4FoHAzen3-_#clR&bR~ET0|$XtTyHmBXPlgU)|2A zYKatz3B^1n$M(pGNxG9~#8bM|>57beX0YkJal1vgkTm(W15TN5ka>P%Zw0C|82;k| zH?@mv*uc8u6esysrf`XUHL+okC5h#0x6{yW?33|U;|f3oQm^w8*{L>i12fTq3Rp=I z1fGG(H7hyb_=Vaph>ij~DjCmAY+^AWNlmEfF0h`CbP7N>qjl0P=;4)X{<*lg$<#}0 z?_Fi;lf{4RA>}*anG>d^o^bV)_x-oSq#Oi#!zv0|p^i3QmuX9LcKjB+4>X!uqomOl z6;?af@S`?Av6I-qC93Jq-sIAu0_~78YMXA9e<-|$G#$kx?9)Z~N5FkI%Vm}+tzx~X z7DO&b#;4Ojy(cC>j@`#XN=O4Ih@F?Dn+TQIlBFC-rSSooo+Jq#PWM^!#PP|Rr5vfF zuFoT#s+tWdu@X3W$&Fd03mYo}vmX+&eg^2n0BS3|HKRAz7^{&|(<8bBts-MLqJ@$3hLscj#$T zBU}Epo1)K~wN24cd}5aj98_P61_M`OPh-&-CzcR`%SUiCcCq6YCNrwCtO&0Xv|4L{ zT|bnl%Y(#RXQ7y7S~z66XiI$(upU!xWIps85AARIVMUDPqA-}A^w_7N6l|eY@o>6L zyoX$5scwET!^)vvMl#$(j^ug97EUl`1pAy(HU`&5Seplz@jhcI&Bo5RaJt?&-H2|m z6FHyqQUIyR_&l?*YSp< zc3T>H>3k$Xil1aX3xOEqn38Uw8;`06zfKH)+^j|Ndb2BOer;r(A?pesXJJ{PZNFS= zBupda+NnQ0)Fxjh!zO?GoIYVB z9YUZ~BVh(TrIhwEN1|u4D$8S$Q4RD^Ui>z_f8E#f>(Vlkpy^384jftW54yt1p#i^Y zajNo*pbcq0x7dMxZA;u;_4GA?swb`rJ`XF0QjRXami;7ra5J~BE!a5!6k$5n)h7x~<&%%!4 zLnM3trV%fSkXf@Xyj>lR7O3dFL9q3N>LNzlGU1!9?8U8Rh`Xj zAWx>v6cCIrD=A_Jd4mq9d>8{D&jn&I4#97y%+$c3V)OojCf;o{YX^Rsle1#*p|~}- zWQ~~+(#@A?EyhET*l)J5)P_lQCe4DEK=>?{y^z0jE;9+JW)Baa>Ee;LPx0^ywC7a< z-xjNz+Ipvd9R|!_p-Jm|pHwXLbfMZaygJW~&41z+DHOdK^Ztgwe4Ky3?+rfo1?2K# z#%9%6+=7`6o1^-R(c%@6#pMDODgp;ZXMC|>`5MvBr01>erYL4pvQ<5^m#S6w#4^M8 z8P2icc}MzlK)VOKSY?b~`2wIR2Ak%O>RM;WwD!xbi%5M%3VV0u?Krse`|x$NC%P;` z9$XR+Zn;lW;W^Mar5k0lE53=OmWb=F4lYl-p8XQM>bs~5%Zq(i`fF$M(F5pN`br<} z;Ky=txMNNrLUIa=ZC}s)_)_u+CuR3!G31GHm*#h6*ds8Bm^p~s`l=v zi3|zH)=*eeB&=|hu;qW;RhZxYn}CQ+z@W=(vY%9oO{;Tyg5$B-w{LK>WAEx;k0$%w zADS~<(A$EjviV{Tsm`wX@AkJiM#$B)p2=h{ET|oO@p`P5%xM2LKBl=u^ysKZi`SJ# ztM@eC{0z*-MHc>hs?NWYs;QiHUnq~q@;QIm{v5Y6tx$}d)*={M2_Mr&ji!|`xM3Xw z`YZybWryS?7E8ws@*Wacl18f&COAA3IC7O7r6m4fMAXSRdWi`&0*sZV=hQxb6S>(7 zSrel!Mgb#hw`0&>qu*8lLd01L(twCv28+r0F2jcG__E%m*^Df{VzTCJ-eNW_p$#F_ z7ekoxHXg?uaZhmbb6{6M;9;tkkkIsI zY}(Iall-|HRm)zI*q74Q2ukUll}cUBc_01T#xH`L;xVkx; z{f`ZLI+@Srrwbv><2!0l3(ryZ9LutRkQz)lEu>bsVYW4jq+_8Y;g^Aye3ZaKziA}K z`a@5=>%l$TOG$kKY~|QDzB)EeK1$#m9{t%?Z?`J1+hQqw^ge1Vx;8kg zr!0Z-9?hd!UDvCO90-qSZ@X4+O>5V(m&oyI%*s;ZHZ+|22F$|Ak!QzGmX8T}A`%N3 zL$O>CktBH=#tIFl^qkeNN=NJfT}vvh7VLXcHpiG@&JrkWEC1| z-j8yJGUhGXyn5A?xnsPi!UvL$;uSzfMK=?n&#`6PFZE%l5$h<5tIk!#5P!F{NR#!w z?henl-Z`9O$=N-7z5);JLAia_mSMjQbsz6;Le8BKXDSk!ZM$0SyzmL~zyDjj3xNkU zBoI(F+W!wq!qvgX%-+iT{~+-kO*@wjaip)Ax^_!$)WA@RPfhIwLpn-X=JtR^c_DQK zfbH@@F<0c;5TLiG%Ogo1a?MSKMRQ|&v(3leBi=D*h=epc!{UlKdcccb6|LWz7P)Rb z$=nrrp3RJUT#H;v%5;~_Sj5t{ig*jZ; z$+M1{+&-&btQ{tkYB~m1x<>sy-)_QzCGr-_VHwI7px1;U#Y()(1oxRXWEG*VLw1CH zrWDOsA(@Hg;3ZDTOKfc9g9uBpT}7LdW+rEkEhYNFuG>g1br`G3RDtK*5}6hpFqqJjVEh0eE<@3Q-fRrG6 zfHvUF#9`2IS^P$zJfk&Pe|CVfkVY#fegk~>P}bu{bK`y^Dd)lojq1KlPg<=}1mO`F zf>SJVSNW->;xr0Y-n?fZ(#7d_nv81fCs9~aA@Hx*3Tj#DVuR)x8k*5~x_t}qdJ8yu zX~xM`i5(utzw6)M$n@ChxKIe1SKGyHiwNDzF|I}DSS~WWA)f}Cd)R*UgU$Y>%5?5? zn!MM@`T&uvu+z@jsx7L?x~be{6zfeWfWN;A`$yNG`@;3tBSjQDdpZtp?_O+Aw7$n0 zAp+UwY0wtFzOF0TIJIQ20X*f77E^AFcX0fe@ePSSrO^-@8MttoDu3%e1+_!js>A2V z=09Ky4=NkEI{IFt%*KE4)m*95E&@J9i~DPn_rKryx>KCiuHAhgzkAy}95%WPHo9K# zdVCB{Ze5|Y6bqX=@T2jMC;!>%*ZTx#qdUvt!9BXp>_gc+oX(W5a$Wn>$)T+AK=G}c zw=pCB5k?-Ia8Ibp?%fcbe-w7&b^|4h-XLb&mNrM3w?&VBMuz30tOt+AlEY>gU;8&U zf?ilYH`B9TbAG^aRDFj=<21AacTrp&J1cHFfghFm(KmAD-Z4$8T`&P-Jz&A{?Er*h z;Jv-$HH*vFlxODR6bw-6p+UL|<1@Stf>GH&^|yXuzBCPI?1>NFAG>sa={Y}2cvsB7 zwq8n4IcLu_`;vlQW2HhX*#w%Ww)4Ma`}SV_7+;OIpL@bZKk_beasWUvNbGps&Kj~@ zJf%Ej^4MI#K8J713!Hh#_xP(lfVC8*qQ0-LG-FTc_7*S6kpC+G$IkBi0%4K#4-_eP z0)zJ5*k4w2 zKCvr?Cuz+Ee%EFqH2l^Sl|TYY2W}t|`p)=C$wVbIXq+L!i5vfT`7`wmjX^Wl)VU_* zj`@rHV#ZS#%^3~Iv2IsqUfspzZpR&gb6k=@WK6AdoXGG+Q?Hd{4WJh=CE6MO zJJ8~~?pQX4Ct1LcMAP!RpeNO!8IED7VvR}Kaajnwri3=}pl69cgaY9yQ<^Bgm1Oe0 z{SzSTETbYt6D*8dOHe1@b_TvJni+#E_M^6%Htt_zaU+wXwyEZ{TIsVwD+=hXAN6LW zYUX+pto4v`5c^{YWgwX$e0GNP#)6FdQc{WH3D^^77CX(aO1rpOewsWI&zGyf47Il$ z+qQ;ihIRA_2;AY9?HWPLVq;SE7@N3Ij`J^HL+*^vDCayFCuSS4Y)g>t8#~P9NB}U! z(;{E}lPn0O^&dU-(HByyVlzI6H$sdIuu|^VvdJhct{XOO0fwf(kxsIc%^$fh*GQ~f zIjvwa%9`y!aatzNAdDQxY}!;uUsoR$&kEG~r={U+5&`Q+K}6Mlh{uN(HJtX3PJ_Yn zXCF!}LZ#Jb52Q{*oh||D9vp_OPkuXig>j_pB}5A6U@02(mtVH3%v5>U1{7sJfpQN3 z^{CpT3kwDg`}TEUlJ5&ujQBzm4?Op!Df(fGZ+dMhlz4j_d&b10r-u0o3y#+#Y?p!U zR{BY8*DM7Fl=#5le$h#788EJz!c%^A8G(rUx3{j}@i}-rI;wiX z`)=iTS0m;4-v{!J?$z>L@)$X+@oRyW z;l5PYQ*;=jZqmuXN=ywVFJ7|6`GZ1Rob=^Ny&7E#!s^!hDr@sXC@+7 z`u%9Ckq!kRn(bhSz=CYd!7w@&su?(#6nvTkc!P^PpQ9O!UOqWyA^$3EAewLk+%;O@ zB&AHf7(P#TM=Hy9LdHwh_!$n<`9}5U`IKJ;=vY-R(mLci5@k7;+@zy%Ibf+iz(H&%jLYI|Jhh+X_fZW`m+w8BKq%XFgqh> z8#Cws!Avz;1`b;sD8F_U`o|E{7;wq$<_nn)fI61IL7Jr$DX#~O3l>mTCpBeg$jp3f zz}jyecklSer|;rLt<6>eTl{M%T-n$N7XGXv_=)j4wc}xJF@4{qW%O2F(;?>Y=VF6s z%sS$5Dm-#WBY(PYEaAWXolIpOcvP}v8UQ4_X^d$hQ6ov8j~``%DoHLQS9^>OyV*TF z0*@e}_$tacqquB})yY}76)$}br05mLwKkbX#YSNd>gmzoyKc(;NpaLLDT7@I3VJf| z)!U&SJ*&~NNj%0c_?YQKh`ku^^AO0m5Q5CD=H)-=-W){1<9yi~&&DUCf1>BSv-TPLpUcwLs#ksO+RvHSR~c9Bh`h{VUY=r7+l)?m&~!Z5wKY(wQN44dts0 zrrQ9w3*W;zS-Agb$vW8GGWRF=vbdZA)>!uf5 z3>A2O%D(?v$YmP$#AwhtQl3^FCWSaPAixb_*gm!}bvtPWebI5|A6QWf(X9Udw`z-r z5pe@-4!vvU+#P%$?wLt>xz=8XH}IDs)1OcC`&IjA6x$v$>#F{6@r6K=W3zJ+B20`= z!*W+hXK!hiDpd}czTNuO5jzYp{1DYzkl!R)mrsq5sS0l~Db+c)Z;Eoy-%3q68clvF zSw&uf0FKXr=`8(7K{Pz@#2g&NUkJq8kC7th=*Bk_Vm@FK{9l~9RxT_+9}@?_P~YX= zt@Q{Ht}}2*)Nr7d&4uB*No^c5Sn0TiLP~U3Nu>cY5-qBW=R7CVf;vr)sW1Fm9>nz787P3IJ?eo>4pd6zKX$~QJDj-=M zfa(f!s;@AZzChX?FL#bj7C!S$IYw((YVFR{`trmGg7m3&M-hi%}@s~n40U;l`e?Zbdmbm5B6#1LoL@t5mDg5(memJ>0S zdA#q$)cef&Z1Xabux9pEeb_y2)_qH3xpPYK)OW{7r!nJYB|BKU{4KPLfy0|1%&>nni>M4HLF z+zQ1UE()c6TO9G6kEdu|i?b2aLe!DGjrxqXo9 zgrR}E!n_t;&b63(ypkcG$NOuiK|XzZ$F_v3q;u%if>ChAMd3B-qrq(8MSuvsGfk2t zgwr#LqSv4M8(?k~eJfuC3qcDZ9tY{}njx;sZ^R>s(7gh6T*J-|mQJhP+l1Q~Gk_da zV_pU3;0ep|W@P7h9dL}}#j}a%r5uDo!vMusYnc~dd$GhvCJELWR!LC-jn@YF@U|9K zDCUD1kdn~Tq@#nNtA?eR_o^^O3^)BPq#?h?0<7oXqFuL5Kxf)%Z`dctmv!>1{Ku{J z%`XwlU7PFI#9$S!pSEieW_mjO$$sEh0s1Ss5pWccaa7O=Ff$L0r%_w!8fknBz%ZvV zs2I6aMFV!Kul1wDqtI`!OaRp^P#d;VEESE3X-L-op2`Tab{M>w`F}x>)X-J5&|-k9Dj#~?^J2u z*lBZ+CRf)_qeT3<$pMQ0Y&RNhRuCkk9u)$Zk2p8Pe77XV?xX}S6{a&klgMvDqDv` zEUz{fPk*hw=iYC$=?23+D@OPzW}xI9=pouO=?Tq-6g4V;5>Ygro_i0DIscsF<;wuo zWufpOeOx-!hj?(rf$ph6Pg;yh|9ifkDwI?vWydv0MxL=~tV%@d*WevbkxGjYwLIQ- znSrcX3?mZ&@2%+RrfLv=Ns~U5bGhM*$UG^hcDx407=9zL(H)76T9FDAORRaf+k-UDXs7{bV>v7+7iVdop@ z?i;KrX1VBSfZO_zw3(g(s$leP(e<>)~=(dFRn+R10U&DeXsQP}Xr|Iq+%$pmUe;XssV$@gUt)-)X|gasBdeM<&WLR3x|%>uq(VqB21ETFPM^sl(u+P zql%IzHYL+=^bh?5W z>3uZ`)Y0mLdwVoG1N}?!cx^JP0lWT)I4*kKhXLrkjrg+OoO#((5&96`fNoN{_w}TI`kt04Roonv?N(jX}${nL?Scv|C zTr@U=QjLcAu1JzT7GvnJXPMx#nZqbCsU(z0=&l-(4zQ%yD&ItiLNmMwRjZ!g!?+od zxMtp1^h&!!i6978t>eF7aG(WhEzq#EhRiSv1zgO0!`=19rvi4`U!wuxs7{c1S!dA< zY3BcY0BL@!`6fehcd}_dpZhi6Ddh9b4-hC7%!k9Ua7FDXu+aT@+Zc*1X03)9j?Ef( zYrk~`3mk*nFmEtM?qGMEn;22O*S3R80qwj#-N1-_XM0IR{YA%klg9V|k-XFHl)q2; zBaU%{1p?yvFBihjW=?Ka&SrLI_OAZ}heOr>?-rz211CiS8u&^jI4mYlxu)uIkpMdA zq*fm&kiZ6cW{pf&DVjKqqn{q63dW6#6Ua%4&l`_j3Bvxe)MmwToWNW&QtR?#l+2*RlZFQeO9;hBT@g#z8(e?9%( zJa%wYzosm&K{&k(9gAvQH?AV;79i5Rdn)FMQ_e!FuYEbHV)TPI?TWoYI(94%_f)T) z+kfm;6(^28PDhF5E-Y8tp7o)k*q<^=RM;7#9T8Q4$T|n4XnR2$Lq~e`#yHVl^0kOv& z-0m=jE=V(g7NOW=Pn%C_YrFkW#GK2mjlRC&Ni>ln)XPOyGQ&zI7 z{Dlx}R(2)L$!#*CD2tCkJaLsfsVG_Hd{eU%?8!9S;I6 z0(u)o_U+{F((jWfFq||oqSl}-X5cV<&hqyxqj#fk2~{2HO7+E(__dG}Ix8mDS-+q| z#on~grUUH(Vxg}pNtXidAj^wP%jae9y9}F^9WfnWo8V_sUWN`U7b%=oK#Dt9PO4GKS(dkfSJ7rx8ol`Tf^iU{i?qboVqgs)-)R1?AW$#+qTV)ZQDu5wv&!++xTMJo?bI^b?(kDsGC}~s@{5@z0Xi6 zKU*zv8W^%1y07DIweWR!q>7&~R!%9`oa~4lJguNk>S;GNa7B_R9(r3;D@vYX%(xe( z_Z5R1sM~*f6dv9aa5o5~dHV;xu!|Z+AneDb5dFe$@BnO)NIBMCTMi~?88$H2G#=6>us2pziq$Mo1}N*G5c_C02hBQ zMGm*5*OoJ+++H>#BAKkv+`JP00zCH^CJOkl7h8vdFf?5&_x_*NUx7!q0wPZQ-!z1r z{J9=C+fV@!kqjmC|4Qot5T4kZ3jbJ9@uh4YzAu_6rApf?7Xd=}YKQ-E{y2Xl`=by8 z0U7`K?o?_XahHEMBsGPI(w=dZd3|<$c7_>Ow8~+Hu2+^B0r**` z9xtTLiRwaWvP_*7#aiYdt`uu8FWk@i_UMo;z1`*DttRlLn4qS+FFH6?@>z*b@(~~G z@p)>&MwO@w$q(P<{W)Kc)|fpE;o_r-E;L`O4^pO$rml&02EDC~X3oNhHY2x2o)*n1 z)SMT&gm3Vn}C`O)>pCeX@=O)ptnS?*!xj`5i}N*mhS5u}|5yNXl^U z0weiFDeipatP6`T;Xjiu1>H$cASwdkPxW&xp{A$*R%D39s7PPFY2~+ZCXo#CYI*9r z#G_o^tIOO0B*E9=LLr7E=H}|+!iu^&Z>b&OT-k6w@dKGCAmZXxP>(YgR=TQ$ke#M( zrN1o2kAAK)uNsN_F4CFFb6f}tImyP9tWzGu6t<(`C^<0F0w-gdr0=2ft zl(3VNlTk+Z=6IBC&Jv#ZzH6rJFc>HAIeiq2Ct5LjCIu*2f@+Ic9B16wOM=HR{|cxW zWYe6G%YaJWzp-qVZ>H)%wIi^y{A@U(+}$EW?>h-^0_>ZEPl_ziuBrB5-ydV~*+@F^ zTMyQAb>hy_ns9;Xd-UbV`({`43jALo*kx$wgt__IR$|5JE?FfGqN=jr)TOSn=gPsA z_EBo=(c}Zb&NL~dON@d9fB|C*A#Z?7vML9=vX#Dr1a;(X>M6#0+|mh#E`nan>mOZd z0cs$gH5CX^8WH5}7FJx6q%9=abqrFVK(TuD6nSpDmC(PY@G%x(h9wb0yMX2-+c*Kx zgdfv_m1k@qbhykNqWKQwzRPi5A$X(#e=UssxbtawYaoykAX93k%x$Ht_oT;Ek=PqO z+Px>0C%+UzyA=*W9AVhvI?d?J&|i4-`3s5jfFTiCQE9-)!RnE8%qWyQ@3(J~+AE~r zw;S}RhqlEZ$-{CLry<&!TMhVCVGsd%8;7L3SFuErKKg5z{G4j$HM>_+wXKM;<9cAKsu-^F`0QgrJ^HedU=a4~ zOEQnjmh-@ffO+sm*VuRE*MuXcIrcHL2i)EbNG%ZAUo<%6U{tb^tK&Fmp}Gu^u9Qgy z#KB4F^3vYOYTyKJVhEShCMz6gkK&*rOdCSnI!kj%a=x_*WyxHb*}o^Uk7=`6)f2lx z$g*ddm+S*V8cC}`lmEilt*PqNy*^|k+_sMef?)x|Dht8b($nfC0ebAZ=V(`;!jPm6 zp(F>VKT)aNagHgH=B*h9MR*3H7ll59?4pCMK=asD93Qgs3lPn|*l52Fq7R!t4|EIikT=`!WS`co3#Wl+e*sv)mtQWjTDQzoAKZ!kr_zr^IBWGZ}_LXpQAFz>d z&xp>1c76LYu>X|>sb7LHLa&?K7ETJl%FWWTH zmWx({-sFP?`coay`C!$W)$g3(kuS&Y^4h*+ZM^)oKaTVlqo7MP(*mTy6Ll>LB7mSW zJk}!J|E=J?Zf9Afr9g^HO2}(6wzQ2G?x&;j)>@_y)1rQxw>;vkoTa)JT`yiTqOau|Fl z_=!!xDp>dsDlnDMS|k0F@1(AiFSH2u?<{_%g&hhAE_X0`3d;a74PXX2pkx(>H{LvQ ziJ65Lcx8cvmRo$2eoFKD};sop()Dn-TKnUIgzV&)VG#5@+y~;zQCf(a%+RG|Wmes$YqX2y|8}ddF zc|0=H0fD4*@2X85yJKyaSPd4(S&V>te;K3DX+u2Toakb+-O|z7Y4~epdy!Wqsm>0m zJ1Qsi4bYAq^~&oH9&g8=oz%shZlcAN@P_Nq-3?4TH(1+I9K#&x<4%$5qhYtZZ63jg z+RJf?t|`W>tBVu{(1Uiv7#Cbr4SOAOm@$g<@w0eo-Crqw3nGrAbY5dR8(*r#9ZbpG zWP|E>nG4OJ%SsY$&sp|8O)j}Bsj8#EmM98}rVrq_K1MB1$yH5-X+Ad2RN=Ys96%E4wnhFNw2=Y16m+Zj*-?1-!Ze zxj|eszFhCPWs?LrXwYk-sLt9z{&O`Xv|M)PQ;OmUfHi1}CWSqk*Kx<%Nov>l!yIB4#ZwW)E}c#waDZ3}@O0q_25_`Q}#d-{)Wr3xHAUE9&W z;#*l!M>f^m03$E;pk-2H%ePWva<|Z3U^kzoCiFf)Z=DwY@ApSl7KXzE1hYT4`$u`X zzE6+KEclBLU0CSV$2nSO3WGC*=NTreL0ec07R7m&qll+W<%LQIMHG)p0=WoeQ9El=U1sgmT!7EWem3+yvroRu(dtiPD zf+|tALHmD+X_?DcA3+v}5Kz01=Mc{~%^yc$>ZAmIwX4L?fM3%iuqr;mY9opV30}Z- zX$*f;+q!fQ7d!ey&?Xa61tW6{rm8SxCV}<~GD#?SUG>SNoxW@E`vd-D+^SuB^(lTl zK{uGHVV;6z882mZ^L*cp%Uf**RwU9e#jV5nbWrmD_1(k>;?~4+UDOwPd{CY^Pi3c}VPrYhsm1G7;Lig{Y_aHneqnCCxtphju+3<~Vzxp;+Dcvzd8!;Bdy zQv!i5*VoswV9F6Je*N84!Y){hPJBt**6;PL-`bkfw4ocFufl$-K^AE;@vFgA_2gXN z%P(NLj-cqQxG-aTeN<`7+o~onHxkgZKmSAg~g17Pp*|U+XpB55^;eA(rUc`V=h|R85N}&n+%5S zu!i$0r_|N|K`^o<#2T$;#j`jsSBTflEZST!2kxm%gG33N#{YYb;}q`2AgAEI3Cb*x ziH0qLX3m#}X+Sbaj^F@krbvc&@!bXP>8$yD5qj-p@nE6^V>dZmL`En-TDc=eh;EQ6 zSy_?HZ!LYoNge2Gbs)Itaz&Xo;7^LO_*HBq?2MszKO~3aA>FACFQ=_T&rW^{@xK5kyjEP18a^70TPSAKL?kiC-2I~ZesiNo zePpDmdJM&%qseeI&F@#_?Szd24&ix^Y(YCU?=(oUujQ~xRRkLS%26**rolbWPs(q& zM>~=TM`qkyFhmlGE~mK3rmEJ|D%+A8SdA%{I@c=If3W*^!`;+8gLO9?Y_Uyet6Yd= z^GhGhD3AXBCh{_Yqz&Bue@h@4eUb5$FoZGC|eZJ4`U25OT-~<*e(7~9ta9sgdEsui7%zQ-9 zXc6*f8U-v9cK1yWD29<=pjlTi^zI8iH@nQ$*o@bPeLUnZnXp>vDOz5!56iQ|Nr2m) zmvFU-o20rCg%&5rxNO&8tLcDVSfmz}gp@gk>Hc0sO%JGOKd=2So0 z1h}i#zc{$x?^O08TRG3fXQAqYb= z@B=Ph_BrE(X`oKq=~(_2`czNK zEy9?@DYdpJH|s4)KPt_-HjXz3UHx_VObmJ`EYCCH=7Wfeo=V^;uid_K0I@+6PW5WA&FP!kn%ScZ zdy}ye2gclY0mT!*_S>@b=Ua-+uqxD^Z;LDWK~z zbk+-ay-?&0bm-iBT!A_*4gJ9-s<|LVUXBgD?l-T@9Z*_6BeUw?w@$z`}>)HG>b22)*Tp+!4$2)M*5sjuJj8a=-69=k!%s(PBlIiWYZtPvw8;0x`AF0rz1|ctc1E^3jsp2<;5Vq{?n9;w4b8`6 ziMjCUaP}!TC=AhggC8@lwaso(^E0!Bw5Z*3Hl>+I! zJO9HY5$BEB<~+&+UqXRDde*Ep5M1HJNc7f;7xk}&k87G0^m!4>d-*CM!**r%T1m5B zETdhdt#+W|M=Ug)Dg|=;yREsafZbCG8#|gNQZYoEq5hI-A9{0+rjgXJ%0mCbz;W_G zVlDaVML#0&Pk7koHeI?s1}pp@Z)*o#5xLAUhbrP9j%C3lupe6K(|yeRkm||;JngX{ zWUmV(Hq$`kP!}V#q@u7H<812caQr(99WhxF`v(P6|q_8%-SmyUGvFKw|EVU z%JB=^S{X|k6w0{wyTcEAj zrJz_P=kiTm(k%RI^jC$n=9v9PW6^?fET3h-S@FxbS~nDlNYTC{`zGDZwwNOCFc9j$ z1`MmA2s>X6*E9wZuH*JVSPT+>Y%^8WNQO4|#z=@Qz)2affyc#NRH4NsLRk^R<_fCA zwpBPpDsRcl*AG8WJlu6QVuOWsCX($>18EJJs{HKXpuVazNlo1x!HQw2lBJqptI+%$}6#8I1Gicu@sV<%GCW}ayvRM z!5MZDtGL9dpD9Wx^)sDck1Cw=85C#WlOceRY{5_;WLXal;#OEgyHiUAkBMAQ&o7pv z-s2P;bA;K>yZm4pHdD!@vcWQmgr8v9$NL182Umc+bPx67uBm{Gr+qohI%lroR6PjQ znXA5i>I9#(S1W#L=nwrk`U|#y@hJJ#!sA+Up(JY#dL|z64)_FTPkZO1lX44B3TwKq zA>*-2lIu;t^BRgfNL6y;+^<-!kv=JzuzQkMN9b0D3uPmdq zXml+O4-FpzreNJg!wI4zt;&}p=Gey{Z5{wl9%!P?2|kKm0G-UuR3>kUP+z9T+#JRs zptRQ4KfN85pwfL5>qX96Nv_ByzH*tP!tx;31bBUu1?p56@M=UCFRk)B-^Yw9GsZ}> zpJ)nn#2ma?ZJ9}9WQw*4=ZRiqB29Nfh8HWn^;SX1NpJ+6sR{#_x zS9u5f76TplrxiT5Ek2a>Z-o1aK0FmsSkr!fy%xJW{h)_123frHkC)D`#H`Ci4hyM- z#?+^czhq7@6|h%~&7pEVUiRBejlP_F(${!=|6#)rajyo>39K^3K2CLpck%@(yxZaO zZvRbBCYvY>!qsyGLgi*{r0l8^J+V(?>u_9*r_4f}6wXg_<~k&-5O5R&od@GXuayZ( zhZo(Q&|bMR=T-|_M!@IuYL4e2kDa^R}j&CF>7nN2(%B`v2y zUobT`n0kE40#Po{oDD3#DUL?gNx#*#KpYtD5*@iBYfjS987?w-`#cJ1IqrSQ=Zv$nB9bmWxdgNb>^twSsxY~1~H+KhCO z@jOiMU`nD<@z)|~38ZR2<0{U~@Nx97V`5~+4T+*u8KDtIEKM_*DI0M7HRe)TxGDd% zw*s(XZKx-TliZTkDj)7&JW`7$iu5?>z<+8=2Qs4!+ZQuyi(dVys2OD-!yH#4#iq^P z=gZ@ZtX1}K(gXZ+R!qHlPakOorPb!DC?9iJmbn2Pl&%>w$ZuRv@M67x0NYYN zD5!dOp*tUOh3(0YyQx~f1qrdKAdu8cJxRZb3AZ>Vvsjig$GwKKm@iP>DmF7Cb(#@S zKP`fOQdB-MLH{Iew(vJL>9RQ4$)H5YarajE)&ds9Y*N0}sOi84y(x%p2}m=1>9WZ$ zwq}Lsac#=)4XH64=<3FjhE3B%9U$j*>iNqt4KUsCi5`ogw5mE7I6(q$vBy-sO@I{W zncP==Ts$FI@pW5jlkn$xR>I%O+EY$EOPpC5^)xbFS3s1p=lR07fxg_(`M~5JTfJA} zg%cEtjqwqOIQL4hapwH5ICPN^&j9* zZ~2>~#bJ9$R>(pT`81ZF7we?wMY_5{kH84f7o%KK4i0q)hYeSGPK&NM_{LtJ2_Szl_UAE{od z53;VdXgovYKz_|wph^9&`m8T(gm>j%nJ%VXz)4Mm(^Z9HO#eD$^jNSM=YDGK;LqQ> zu3M@k{C)?OC(KddeEd(b+m{7yJnJM05~Z5bp?_BY26%S2$r^E8=1oAPChQyzYmY9_ z#z)D)eQdgd5b3HA2v@HwFXr+n^`u+g*EP6iKZZH2P^x8>>{@!SWT#{P;x+A4 z=TE=X5o$^*Ye{?xCHF(;sU@xfh6^g~AKR^RT?}tQ{_6vFepM1*|l%yMmo{f5w5M zm^qtZcML`_j#^&0ujjuc*xsFv4>1JN@>*Ny;=eYG; z(nX=H^_frS7*lSp%wn}Co#5lhkRZ`7Ymnlb;IOP zRaVU1PZzy^ME=fMIweOAY@nO44K8&7(6qb4l&xPAa_5duVVod5Ur0C%nM+@5ovi_xvvokboCe_Vtl5Xdgto@ z?3U?{frFIkvHcl}3rGcz>aA9rYK{B!2r^Z9+$~UqoBupojS4L})HgHf6x=QgvW-8w zLb(tz#8cF0t-ake%8o|6*T187`CMF%yA|k)R2yuMN^Tw#PL}Ty% zsdgIXqcZLw=|(zjIs*|k>{q+dbpyJ6pF#T|tiE|ljD>2OWm{NHxpNBytIeWqij}H+ zM&fi(*G3VVMp7Z?GX|2b7%p9vHuVF7JQMA@!C%bXi*ehSe#}Zq#P2E+K6# z4Y!xA^PDYVvu$8Py!B8{J(unL7Vdv)KVuBZm^9AX0es~tgRX-@s0MY$g!*f-mowQe z$VbWPAFp6pD%#F2aG=G?Inr z+o6Rw?M|HLVmCpb`{~FXrkxFL#;vyP1Pt!-*+bnXm$Y#~V4%XuvHjP|6{RrdYdX;IRi3r+a(-wMyWGA-}WKX@ACUeE8PIQe8UML(`Ah z$OP+S)1&oYRM%$u!ViZ$srWp&VwyWL;lq(INU0yrLoXwo3 zetT<@wy#0tROF$HoPHMg>;y(S*r@z>Bdzi-=hbt}X$Y}=IOhG(41(Rg28&@eEt^(7 zDx+~b`^+{}FP!t}WUQSknUm3AYMYM+SIfyQqvr2@M&%gu1{CAV$;Jxvw3OogWuP`v z8ZqWg#)XW@b2Jv`PzEj#5wb41m93@~Pn*~ZFwqV-+_`rW+e5v&YyFn zx1NT&{9XWaWcr&M9SlXp8W&0MHess#aksAXBoGT|iR5By8IOiZD8H<%Y55aA$sz{|Pf~Xaxe;?pMeX0ss zubZZE*OU9`{Dg^-J&SDX9b6{8cPCm|$ajJU;E*zApz^IVz1WWZTl>W8^#m>d@Q8Py zVK9)CYHlf{ic*h)xTZG_&_R#dfe)9EOn3%5yEBzSCVnLdNDse#& zaj*o|mrwTm@HmS7wQ0Qj+psqS^8T$0IOAkT5)s4@*Z4YuSO!PZ=-Qv@r3dJ}|H_aN zio$#E;$U%(7=0|U19hs%R6lCB{U*ml#LclUcOb$OC6$aPHthqY;$;4Xh}x-NCRZI9 zMt)NcolZ*)3IIptHdM>Nr68Cm^=zdj);E79OPlVlXnBSZ#-DeQd z7|ND9fc8Qo!E`l0W)OoH0Ll64KKPrD2Iy=O;Sj?^5J9GY7??v){S#)o3rrql(Q`n9 zQ1Vd?+0w@qVxQk)l~`&GE>~Yn3J=&F3-YIrx?vw<|D3tfP<5P!ag3BIvs~H@d8qI+ zsek`-4_wD-ZqNg)lF;yzTAM^23?d~R;4cI^ce5dB6o;ZlVRpnTt>VItJXu60vC*IJ zPbbygC-nov0j#Q;UhYknUpE&?7s&`)qOaa355q4WuA_kx!=g)QiBnDtT8Fh-{_j#+ zYD{G4=9s~C;*_(PjWNO52@y)~(M+sYs{z0cN^?m#AjJB-|B3^N!N!W$-p55KLHLHJ zobXhti51sAfF`lRo|~t{=Br!jkY%Q|06>9F6Q)Ja?_Buv%4!_KZ@m_vwdglhX%bUce!z?0TydYn)I;|dR4Vq9 zi9!1kq1a%!eXsi+Gv*I?(y=dxocn$%bV$3;Ay@ALvNNN12Bdr~Ufw zT5JT_?G;TQ3*mH3R8lwq+vm0SZi-xjs0=XDii)e8WC0}B8}tiZMfbt89&SynCpSjL z@zP|Bg+6So4la8!2&oVU3=y0&Q+-w$yq6$S_iP*~=Du&&I`EDG;ssRGa-^DeSXQx~ zWVf=28iFVDCEzA#OozG>NCS|dV1EmAQw3#s*Hi4F#@+x!WF)9~%vf|2%f|)gx}i=P z0mqI3kMEH-tkebRI!p=j`*FaMlg~F|E-Wop`3#UBKfi;34v2E zn*M<{G$wdToy6jM*aJ=q*4eQO4f@w}HHK7m4CW#Qs5GK!81=$0Ww5DObDO?nwOsnw zfHAQk>I)?BVp84O0Ore_I3AMJqCYw@{?kIjaN9Ex(+}g<2hB35w&{SM(!7PJa|Q#1xIb#TW=I`t_&ImU#hfQX?5|B zsfq&eD2HMy%98LXnX{UK%HN+TQkj$$*|<6N@`swlv3_jBF|A*6W9>ZgV&3o#|q-M4w8(Vkw$UH)ymF?jLvQ( z8XT`wCg@3>@F0&5QKez{v~YbeDCL-AbT*oBOaOVpKhVut`BB+9GT|1C*0KPc{pv== zysH~3GFDhH$9sLvvT6;^6I}WW&o{IlG!v#+L^jn;DQ}94+pGDD*e6KfCOPNC$HPA@w zk(|^7U?~hE@-z`lV@c?$h|q@!!lj5T2;7-U79@)G>1wIz9}2m&Mio!AK(Y$?h4HzE z#Qww+jmJmHBfv4;DW?7HM!Q#G3=0W2;P=mI`&^T)@#)^}lxC ztwwQn_eg;%t-{o4R|@Zp4`yiLRW`bL63@3OdT-))!yV$w!s~Vkx|fY@=QcY);>s+d z(pd*#ZQyD~l*?zT1qkxKk#axp_in#F6dZ7u8`e!2`v$ggq9p8vjpa^jcyV;X*Q99D zD^!?fjiCyWq&O_!PU1%$_`CUF%0_=YmkKne4+^;q6{K2os>gwwhT$PVkPoZ8ik-Y&VSi1S0nHLILRQf_(MDOa?4X{ug=2Cmo*|8^uojti7x(5vmj?hPRu z?DgWRV;3>j^g|+Cw)PFkcWOhJYxH(3;`ftw%x!r9vY+bDZ_9MWiQEz2RGiDm zJGY|35r(3Kw-kQ0utTa*Dh4XTHouiclhvjw%7H0Xv7oIa&ABVdAJL`g7t2p;#2K`0 z2&*g-q(COtGtJXu)HF zd^i&;Tp)j<0mexH514>T0VMRM9TL7M4C$)o^W+yWJxdGW%~v|2VdsQFsm)(`M?hUr z3>l}-sWn`qK(MV+ckcJZj%ZJ?>wN_m&J9LJ^X$7$^Q1$_hj`#|YV#S~GKhAkDXa@Cs((!i$d-}3 zM>md0-49421%BAoX5mAiyu8YR)L;d+?kWZ>k2Hy;I}_7vlsS_@nES z)0IH#l-*ZLCZ_+0g{fh;Add5hYMw2)J&Cd03Tt2`@aY}QfQOVCaLt-oFb&*m*$)uL z1yL-uE5=F`L;5DTza4*@4r zbqy+);*ooE`9s|szOl>(3i>e*pKqol6r{2dj1a?(5rtq4wvgw}aFz?@C>pvX%2Sc6 zkr{SXiSZwOhcMnyV3L;$a)L?tX-~#PJ!&c57^1l6BT%%F>&_ znnr(MOe7EWL248-i$zzhJM>VZUDPwRln9UC2Ckh%*52hPE)|22E4J!&>(^7r03_+Z zk$ThRmu@J$6sUVjz)l5*q&^Dj0K>k2qmLA#N?OO|B8G!R(v-}t3o`7!e(R1?KOo+a zP0c^=$A*1#2wU3mib9vQeQ3hc6|MoZJSX8 zn+Gngz|2X8C@ocil>BB$i?ELT$7L|kp5t3Cz@ld%gfZ@@j5Myc5*#WlL}213p{ZI~ zR(JR^X6dcKJ0;IScUG{Uqq}Ge1+lHf#ZljW8rNyfQnIHnMthyga$g$Qc6tAp#M2w$ ze3OcwcwQy1K_ ze7IxL?sPl0(H$&XSaTT{(A-(#-g#;NPvggP@)z;X3>tyk{5%!sh%fr6xwLOMiW(eT zg%Tl-YumCC^ljjeY29_;_dBxwE3$p7>L{Aj`Ki=V11+~%*(wF1lvoD^E5`=7$zjK} z`{>(yC;S)ds?x;+@-M|;Lcw_hO(4wE3SCIsy)dWUp0Y>Cugq>52<`Uq-<;dU{v+O4 z3UhR4Yz$r|i=|O&Mt1>aJAXCj@lq0_eaO5%UYo6rm4D7;yeGJOh9I4ixA z`j(Ka4S7a|59LM?{o2OO`TwGdbeF{bg> zTzU4FJ9L$6N@db)cWm#rg>a)ws-;|d{;8)TB`F~$p(FPcdihra$rMv4M6{IRH1o0a z9+8;P+=3s7Mb6S3FD7~p-R=in&*=yS$n{>@Dm-pRA< z%8mg<6R%Tl*|{PSOUB;xl8nqhF++rUIl~ZNqYF6KMkQ+Gpvbe@W8B8g7$*q2VOwg1 zFGWGr7I#%Xv&WC98oL(;SbDz8Kqu$UTom>N?`Pq#tH46nWH5jk`?Z za)-*GPT#R1?%J3lY>*>1Ru^uS9k{JbscVFzEbi6MzVW0CzNJVoK|NN9oPdnL47nxu!#b%WRS>NbM(9ohP2~nb zG)$j%9hNaw&zHeS(;LPyl%?}3ZoIdN;>>rBPv=;u1emKo@~QB_5! zBHPQfrqtjWKapRUA~ze{BG#~M8L5hDjgWx=X&z+dMu!~nE3t_?zM*7c0eh%N%ch<# zK(@oc23NHH@uydMQM+O(FGoPaA|?$$9wo~*|I)&P$%>VmUm)kWc&j{dh>OoNbh(PV zDe4b$t<|$GMq%XUen+;0GW$k+39*DB%m;m7P%zR|+GyxER(n^&y*|{`WIfE9dcM%m zI(ZsZwaRs9$ic9LBkRHm9hrJFKx8~jutl-}9V%R>AgQBS0c+agX_*%0g=gtyqgWG- zqkDbbg#71m4bBh$%PP43I(XBBZIQI_^D}dm5I&U`_f}$Gy1B%q47_Vn3Yu%tQ;KXJd)lyHRWzl+)anZ@lC85F zx1VtVCNwrl#l@3W7AlN5@8C+VpCn!udluB!n6F2nvFj6YV#Yw?OfCAZX(fTMQ2tJC z|LEy(Idbs9^Ll2_kUW(xpF-DzP71WEq{L;+dvMO6{&XG-)D{Gr+`qi!&wjtTJRMX9^`tQBx$wUs>vA0k(S^R9y9@=PjKc#IBb zqtTyI+nBevNTao*!!2h*Ae@*({g=ZGkLXn50bgrvc%k9FXG3oA*-_W2vh(BeaeDi~ zJV~V4p~f4j#iydD6H{OsXrfD8%!fv;W8n6N4^5-(Cqk|^L}M*F%A92?{m>vVp=_w0 zPI$~*mkxY^e!{T&If(|or6O({*8RS(E2li8aj(ucfoz^8b#1joNKgK(`{jncN5GW= zvPRaL1of&xPVo{q+uDunh`)rNe;F6ba^bkg4|uNPBE1s_w&rSXx-9;YCXd>Z=8wC? zZiB#%-4`WoGP^08L~PX> zOtIg#K>(U{6m6q4=f;=KG03{cr1uIdpy z?T$n0aJDfnx1}YUE3V((@^1m`|WG$G_r=`aS%pfHn1 za>NC?+?eEP>l#&q<~8m>mW5jMNRRKHf1x@A`6T8;Bpg7n>iTp-1z~e2nN;ZIr6%nf zjN9TzR?-&{saek~gVdiX&A4dQSN$z_0wYBxa%`3*sHvk5ySE5J=p8IIeTv7$|L$o6hI#J1ix#Mey^GXOpMROG5d*4aY)7uDObnB8-uP9R z^f(;ZVna=I%ovU%%NDMf$|}BR#k2J_`6v7N2lvrG^1G&IZIJ&M_R?g!ABe#nZdtJ0m6Dfr7hdVjaM+x_t#LkWPSI-TB zssbY}L7l@Qt0+FOb4gaf;n6)zgLYa(YXVxQdFljsfo9x;YM6ZWuxh*=!r(_X?z_GR zptJHGH6w9`%(Z7(Kx7#gm&-l=@72 znI58>D7}EPj`weTnMtZ%8u)Wbxl&@XX{(R_bG5P;L5f|Gl>+|gE39{S5suG^g)I?}ZXAQ(m$4g! z)ml}18i96|Rou*@)Q140Vr(v?QG9Y0Enk~)<_=w_jyKZ?lBRZ<-! zr#$fh4^pEBkI@2$yc!~1M@x1~gJ|gXX)OMY{>`c7q!+rHaPqpkJ#1!m z%X0QkDS#IT1Et6AX|qo@3u#WJArJbJ$9VG;y&uBOCUpzWCKr0EcE!GW6aD!%c`ZBa zpwBV`O9@&BICE46ZF6{N^U63%wW=N~GTd{&6 z>P`|HI-DR-vJX1NdQG@g9UzvWc9fyaEkG@5q!r}HqRFwFmfVv3(Q5dX*OmY5Y(Us9 zu4P!J0lTcO_@)dv02jiFduxqGbIcH^7Cd2-dfAUg?RKZk@YUwSdlFSI*ns2BaM8ft zwBJA;-rXhMeBiVuPPFQ|J$b)NJ-FFVmKZEYA6e?T09Uv(*^$j{=#8x01i_oQkG=m? zbPyfNna0%d?tRuyk4nQ}Dr>q$BtG8h$nZBZ+A?{kV{~pV4a`I6^0X}YhVLw}+SEB5RkRNe-FiT`&YuZabquA=|)Z^`r2_$tJU;Vn# z{>ST@ICAZO2z$pM(SmMSux;D6`?PJ_wr$&XpSEq=#%bHOZB2g@GZXjTdGX$ksQt4d zs`iRiwR5e^)VZWXi`oI&h3iZkjhxySW_XQOam}>wK7-=5NE8(D?}%zrO_757l_Y!EA}h%G z=739~WhIwDr?*qV>VX$v`hz}7R1ezlbpu# zFkKsJKjb-8L%%lKI4F4U!I~nMc+I;T=kje+lFx=)Nu^(|gdP+n@$MRafCsRig3B|Q z(L^+4+D9c`pFy|t4hnJ$0$U2lZfEy83um@_NkqGT{uC|Ii=6GKR@lqQEiUKt4m?Il z<5`t&#`9`K=?XLg%qfyse8jK{AQZNFYi_HUGFYzELy2I20(JM}Pj;6t)Q8Led0<6a zfms>5Te(k4mhOXV&OA=4TGJKfi7dxg&kOtWTv&rblh71qZ9TAnq4iGmp>jvsr~R=X z*S-y(Qnd(o3gxa2jP#JOjegJo!<}G!sF1xMGo8dLp1X?%>ut)D%j!AU`X{c613zbLX;`B86A=vCO9kH0i0K7_Bd6$Ixq+uV=D~;p96qKu4x!fJ4cktGBo3w_9kE>$$^+Oa-s!y6T^#qlF)#eO?dSCz z$}qlnWPd}iMh{L8W~cG86AQ+G3Tb8s6Uqq0e|*w6_HH{%);}AxVLO{2OVh^`hTUN| z2K3HDJcHRb(LH{$dtUu8IhIQj#;q~^M(md*$n(JN6CCDN-!?ikdaDo`R4r1xY-Qqf zM6J+o+0`w=6}+sTDYCo5Lv5cJYdu<16X$|<;+D#yqSMdydIv%0ksM@aCijv_*WW9U z8U%3Qvk(sKQ|Z?c3VGU3_UhKDAaZ}LfV)ch_(qC^Y|Yzs=+(PEyJ8qc&Hx;F)>Np_ zBBJ!G3^u*;jzZQ!90fl}6N1D|S{LQg*``6N{pSu~t_(uFCV83`Iw$M_jUb}VW8A5% zbpgm`J$cB9%y3dKE$WdKC?0?zD0enGU5utg(1-v)dG-mGptQkl_+9w14_(34@Hgrj-wK3g6*2y8_ndQELHM4t_xH#c8T z4_Wis+<08+ujq6y*Fb5v#(*8lVHS=zgKpoD*#kS0ms|MkXg5_#GeW0{4#1!Fs<~iq zsKS9k9C?z*bOZ)rr<6Z~Eaqw&g?|BW#jK^X4y=fuGuDP7aCP<-M{e3tj3vgDtXN7J zC5aP@|3tXE6iM%9Ua9GnAf-35}cbtFdeRX^GCar0bl=eC+Gv zg392Bq#N|mc+t`Xri&;BBUEU9@9CSgT3-G|L25g<^C~_)Vr6Geh9OKUH_I&d3Um}{ zl9pGoJmqc0+HHkN+yGKuB(8QvO)E`y3P-o~{aKq?7yzAOV85{BOHkhkMm>Au{u}^S zqcg&l5|6}t2Js6l<-BwQa5@zWjNwU-X_iSjlsB}rWgw+Jfbm*!%VZJ(W;u^p`(vu% z1QeLkV}%BA^^K*Z_{AnYD`24^bx;KF$GN629@=y5Jr|0XYX@)# zpvGhpLul{T?Vkgm6{9&QLpDmZsSmKN)XIv3p-6s%0U?uQitA^wu?{j=TY{6@T9VPT zXcs+j950?vqZVnB2jDjw>tvO%rk*$fXabmD0}A@($CVh%v%L`g>0Ucb!wp4zv< zRIojz2$6rR-INGtdysyiD}PcmxT_TQg-@}%&`~TENtR%a0qup>Il#ZBNKH=18W7az zffE(Gfax@41h*;dNld7)*v4gdpg5w(kb`2S90{>eCq?@Q=~{rSy-G=ZK80lZ^0_Ei z#)6C)^wVo(JZtZn?TSN=+O; zo1P@Qa4}?>@&jG$MfszL?i4vtZseB<1(`2l=~PAx=>EYzWw7MTrXPAWREU0cSSTY= zKq}_8W-8j;RO9NqREHA4+Qu^lJ*x-+$YrkeZh&$LUfWmassAL#yYaGC*>C#7vg&UJ z@#9j5DW;fgXG)KkU1ZP@36L$|fT5)FS{WbiRtm7m@O|%iNGh=Pj}GmF$uP+9`LlhJ z?Qi?Uq9tn-elS!EzHFuYsDB{Tk-jS)8f$2C#hdD2TQUS3c2GolMc&|ByTRp~I8(Aw z-GrMD>p7OdVLGcviQL^--8hL*>V-rru~+JZfv}c$Y^?2jJLkJ8^pS=y7>5>DMwwXX z1qUU_8=aV%2%0OFGp+!YGdl#?K+AXebo$ONo>}HC<<0I{)3HaaoqzQbOB)^U8K!2s zrgsn<6;_YLy6Tu&dl-q$RbHw$mSzdkYX6|C`pRu^PqI8U)6;t=qHQ~nq|00-?kLw* zj&4nf1di*0VMdnl;4P5?G7?{Nw8b&iZOBQdT<4R+vjt})sb>*fw zJ^#$og4wew0@~K%4NS=Krbe`l<*Td(Gbc;s2O3r6Ypq+?YTxDpc;fcz-7V%Kgx6jR zX=;AUOaa=?4Nqn0)y4kazoq9(uVAVEN`6sd1*Y{qAx8gXTVJFw`Diz1B68_UXSw(oX8=bg;8)80l(j!&X+(5Y;GqgE--Ce0Y!0&tI_n^3LGV6fP+c zJ+o>4;N#iHOuBlNmOH#BmrDj?<{W;f+6>1+Cw{^0gYDT{l}2}DH~bVS38ve)LQMj{ z@!di`YQ=Z0A0|=lJ@c;JJdalnkUJZ=4Jw5Bvbt7+?Ig<>ISFK1exGroSGeZmoouPt zW!eDECwEi;1m!wVQ>fH?R13@>6;tB59H@8$2slhXBIY%=BFw^AW1{Hd!Im}MSa z+n+#62OU$j}$06`pP6Y2=r97swLl{8YvO*0uzeLS7qdu@0$G_NiggM_wd_nnF) zyY*@C6dg~(ZehZZx4sR{ZIx64Oxt*nNeW@t#)E$sEDWW#rLWf)=9sGlqZG`S&h9lQ z30#W^nFz(=utGs|l-_Z-r@GOCLtk)eBjCbv+Qa zK&()qUVpPIPcSwsn=YPOPjZ-Lll!b7J;+qPe20%t^c&nlR05N7f8~qbtsv6UHl>w% z(&V+r@@JrZsSzgAgepjSK=lMeSfTE{5;o5&naf(KWIsOeQvBtI1li4r#3y5CSFM|x zaN$_K2taDq|*iOkgG zj3?OSy@j^Fp(1U!J_952*FHld?3RyY=o`&5u23V^VRcmmxZ+`5S$nO3KPY215EYi z9?;0W8e%y-Glc%ZyZ@S{_lUII#<@qkaoi=Me*7x}PiE(SaTfCLbT}zfdcb}THlLJ4 z;z4(Ic3X5HXx~5{V$dP-Pc~tx)fk{j>=4nyr5MVaYhv-Zr{Ylf9`ig^-XK40<43`S zI5kZlv@C(uG~tZA3KvrpBb?cuGc+00!XZ_EF;=bW?3X}(Ga0kN2s2|4(gI-2Fx~rk zA|*l;cs6(ia>g&w0%mzU;`W)pgVwnE1kU$V}Ga{q%z+%edsd7Dt210g5Mn zyLFdJZQ7v?X7XHDN?Xz15;oE;)@HVfs0KKB!`gb7j(e@C*`}RS&-0a{pY@BfYJqc0 z?S;bpU|9y|ixF-{wKM&HqtC-|=*CN?lvTrKqh_^w@Ig>(cwB-cRnYP5O#^?8Y`Pt+ zzJtwvh#A`WmrkN8JgXMD z@1|v28m(wE-PYm*58OjQs_6DaSRAp5m+g4Zba?vB@(4OQl=Voe(rED(gX_(3Gkw1Y%QGi^nL-7X0EHmjgf?(7fP~9 zd=L|;MN|5gW))}IxiBkg&T&#o36&-|R{)q4qW}On;Oj`o_H(=ez6Y{5hnqz7HZ^g# z88!&T`|LQ<2!fK%($db-UQ-e_oeg;D7IH;Cm+C}(PTs1ksJMnoW7Px;TF#W7NKV6lm9I_iKOnz-WT1Sx-jmj}dGE zDivDj1miBsV66A-l*hnq+SQ2`M5oGd_s<_OWH@T&VRhu#gaj%$$f?1ba^XVhGO`S# zs!S6RWFOC-&$N8^I^YZi3|~Q9a`{etIgbEiq>p^a78qs$j-NJ|1bD1-eE)r!ufnwnjB!M1_K*rTWKcmw+?4o$qmzwH6maWzTbX#pO`5dDGhj<*q-R;jDF(jbp|VWAm(kc21cgsE?U zNvo^`?VS0*2Gx7n+1Xpzti&1(F!F}B9IU;I7Yz{j+6@y}@Clq?QHmjlY;Q*dzO!M6 z!eA(XeN`V~3Nu!gHq8$~lwWv_HNpBO#lgny-lK4uot5fAq*|WUGkL*bTNtsBWVYOZ zUM8TiC*XGh^ATi%5Yh6|gkx^n(*f8y z{n=6*z7Ls9C6m$6Bo*XzxEWqki^Jn_cX)nUYtQ7`5!622(9#H0)EA2zMjUEhsjGP&;GJ=6(D7w@#k@eEpj-9!thCB^R?G-KWLVo3CpvTS2kc|GC5vhK%6N6i}>OKKl;lrcWU>dB=_4SIkc} z@H>7|<-YI=2W}`}pM!3$CS0W?W8JFD%!|R21Ky9 z63y`R^gV}uhAA~HgLQ_48*1nN%;N$|yvnBdy*~*xyfWs%i%B~I!IN44KyVv^Z4Gb+?=5Isql4j2GMK32CA_1Do_J-em);nuFT|c`#wxU zca(v0ppc|I19ib4GV5z4tARoF7aMI7xhH9h0Pr<60J3!oHlg^Da3V`Yd!uIzZ5sJI zGY?b~kjo+k*M$b^d7RcqV2A4#XGpS8<_}7Tx zu^d9ntAwmA*1lzg?koVF!D}H4uKNHCHKGaRR1Crt>P{M>N^~B#;^rM@VW40~C?+x? zxKUs4tUVGkS(;7=b4EdPq;vR76Zd%Tl{$GK4#7Bm^+Vq%YuM*^<2YLD0lq6V53w!O4(bwmkbrecqpUlb%sdnpg+L zfbylBJ6Yi@+mbPsGxA_DMxR!gC_7FeZFwgnI{p(7y*vx~hcyn?bYr;{$RbnOHMt~2 zW8LIv5*2}~kMzmj?4mOy`)`ZjVWbDh7}CG1cx{}>S$`75R%3eJJZbM=F^xm4 zfqv`3FhiTL7%jGPM7;u5n3zF6yS^vz)1LzQ?O^T`UOCy0)6j?9_)|-=EN2CcScM@FrPORuK=@d;pZwR zJqzCEID%YGM?~yzT@sO)BlXU!D53i%8C=*Q93Sn#bg%2GB(`lDuCeJ(T|-7}Pr;M@ z(q;gnBq+%XKy2cO9A`wtv}ro#LkaOP<4!p%PL!Cj0Bx87?nxRp! zW&C7GWCWS`eOBhguj)FN5urS2&ZfE8qCaJm$GGMBLa{*} zD?hvg7zjJpI0*)KO|qRG!sGIO(;#FkxIb%qx3aIw^fF3$+VDRq=Vy}`bEXO)rYH`j z;hYk`9E9RJ49l>8!N7|Gcv!6pl&nH)EeNeHC+5gtO))B1nX-M6f`rQB0&Bur0Zyj6 zaNUAaYfz%pgcsqo#3|vF7CQ4M;1pd*PS0^1o+k9fh9d8eEJbgINa!C7%Vui@yp1=_ z`lmNLJKW5>D!gw^ukY=K@zpcz0D9ZuB` zUICpqG)TMfIV-3DoK8)+_BM5<63MNL9Vk~PryS_s{vuDN2c!o3;j2Y(6a{@_*`4%u zGUNAiGV=rKhcSx7#RwigA#J3{#)*o|KZ7hbC)$!kBkw($thb*q?K8~wDu zItnE!+QTUTQbuxZN|^QHz+`I$yY3Rt7~ua#xa+MU?T;STKT({go;#swLs(@kV<%V_q`ip40Iv0*b_Pfvn?bm|~N*AX>bZq35p3kJ!#LD&S26 z(?!yzW&H;USUe6q`wz%;9N>v^F)=^{Y7zs*ohu-@$N71fhV4*vNbfde@lZ-d0w`G% zTx}m1xK<#mk5TM4zF8@TgC58Nw&ddh3yI`m)BvTDbne)drybV@+)_|2?C>fY3)!%| zG#ZiQqbg>4?^WwS3~5tEj4~81tpm0Y->4J8g@CYL-ZA)=lBI2+#ALc_aG z`{xAzXUe7~?}dS6E!cO$O5?l<${Ugr>&&L__+p*;pZj2r0!vu`Vibj|BE~s<_z_cQ zb<4uL>&hA#bx%ghVXdrs)XNHySKLPi2ld!>2jv{G`HL(`@0p(_!&eUctmy7CP=ZxumIE}b?5VO|;hUXUQ{ zmi!)_Sy-Fl39M|qZc9*`zF&VLe9V0E2of^umG^8H4kVN^wBEM?0kB5H5UqR76a%_- zBkpMmh_(84_S4^(IYD`nvI&B1*%bp>r#u5aisD7rzLf`zL}DC#L7m7S=M3wFQD9Eti+M!f z)oSA4cS$617Pea^!t+WLJlU?FXNgUW{=16bMP zksUQaBk-r55D9v{FVG4jofX`r!lLW@w)PUApJLC;c`hGSK_u6BeND_rXls<-ZJtkF zk~mQTrPZncqL@()gF!P(O=hLKFTa&oo^gw8K8^BCY3D;tsp+%#nMGBn66m9Sx3sAQ@P?eKt2N)XgUC>BS+TO2Qe!VJ2 zT3TM0h;y|@$%?vg+orv9F zMcMkGwv7QQVva4bB4!dr3o7W$s}->k$v63DN}T{CG3B0dxZ@?!O|itp2qX+Gm3;k> z8%PhG~LUoz?f!f=&Z1YSuCpg4{Sge!vuyrHe+j z5OjbmpmXAq?zquYNq^p?aWS)RgR3`R6HLpC&)+3ecym9vsp!2C)^Oxfu z0y8)5q6*4mL^s`3o6s?-P!}V$vlW3Zb7S6L_4LOFm4l~WNVSNP_Kie{`rMpp%_Wd* z9F-if)qar$5h~(KsWxrH2b?2CLr%^k-H**!bIwSQs+?*}q2Rv2WDK zp@X?q)@^)z{3r;cefHq`Sg1izNi)kglpiBR$F|t^jB?_ia|w(|$`qvzwp(LplAtDf z9{)Tb5)-EC=bI}VEoYl)(bUUHUQp%Ey$l|$l3d1i@pd6R0^+cd*l{<`I-M2_&J~-? zbXl)?ot_9X!gDWj+WvJwY(=c`{@8fBY~ZQjFG&G9|#8%hK5M_3`|SqxP&^_d;yq%cu;kvb8hHZ9N5!};)js} z>Jo|adOGg;CSU(O3=Z$ipS_+5Z~;z?_9JUu@=w7pt-vj!^YZg9-uB&~U9jq2b4`-{ zOy_dAE$)m-c&`zigd&6o;ws3Py$ai6?T+AGnEGg4-TgvKmFQZ#o=4H^K-Da=_%sp* z!ua_gnCEXE0RZ~X>-VAh9Zn|BF80oLcGgbxdafq6#&(YM|2O7oWM^Y<;QU{qPhf!m zo}RjdLdj!(XV~~1^+zm8Gh50PV5v^5I;=tjYnjFF3QmN7RUz`5mu_ajIkT_ z(^Y4Sdz}+j+TlTx1uH({GXH(0q445^KiNGOYgP7B0o{gF0K&#K$Nhkj54HU?^=|N5 ztF0wI6UB=GaD#} z?Djx*mYL)Q@c+Id3QyvDU%+217#IKm@4v6;cROqhY>ofxe*9JBksD+{*m|IrbvCCC z5${ti7gAxYV@$y=%|kI7$`T;9Ko;PL#tag39ft9cUVLy++uHn7SO*m@r9FXn1SkZ8 z$N)UVrPtSNR?~rgFaL*ZWwwP&jOr{jAiaXD^SIl^06}>sAL39Q@6x&=QV_mfB%BgW z1bg!q8+9@wslN^_<=1w;E>;MQ4s9K!FG`S8=U%fYeBjlQ8ys(Z7&4%0JX>EXf42Y8ZMxhBadz{oGKBUTLMa zE@a73vaC^OVCgdjHFMT7NbMp5A`Xd1;G6U14d3>==!gEJ_7C+QPn;vmU3HXTJ_l*a zf|TF?h{bI7eeyC;0D$0MH2>F+=)Xqw|M%toAHy>zXLsk{FsxRSa@t@;=y_1PoCQyy zl-XHwptmWVBAk*wI1^ctXJ}89PXd7TZ&;H4_ClwROui(M$rPdi{CIu7qgA!Y_>Q!R zgR)#BfWg9MKysy!XAG)aqwvOQ6V%BtYh3F~X)OzYX7B>pysBwml{6PGU{%DOAZg#| zEdcW>34omCS@xc=T9sU4Y?Su@))}1H@B52_3k8g;1YgDs@#uby=ve|YR{c;pj+qMd zJcdO*B76`HaN3Qt4i?OO!U-79WQPdAhP6Qp`0Woem8MH?XUm+C&!*VYaT&)$-QFeO zu%JlQ)NWB%%5di7w17XQ8ySg|H_?5zUWswTGMrgtkwKzG%2ifzr4JUO0@`&!+HVlp z#|W)QF2Q~C`lT_kO*4fG-U?>Bl&1se$Fqs<1m&Z&Q&1y{KDa_5{3<5aF1(GPiY8+Nj@1bD$#v z#fjHJe=r4JxLw%_!YonKCkn&TJdvafzxx}MRBSFFDwO5dyR1Mn^XGI^N#{Ooy)NsO z=QaL2MSU)bB*|iBW!{TOY3-(ag4oL~Dpi|~KzRUAx{SE=sf@kUHmCO_ZL*GS-+*Nn zn%d3U*wl4&U4GJ`5>5gLNt^c;d&S>>4_oN=QAt7S+^cD8l6 zILd8+`fjHvKoGfWQHmL_k8lwAa13FXvxn?t_}JG|4TYBqEoeIP(q?<511sM|b_vls z1)vH!yn1%Xep5x?=_U7c+8OuqsZ$L(MXz@HK&q)=9syE^G0WM!gw5s-J`Y418(Z;B z6_!KWs>Cr0TX~XrYMtaw`mgdFAKc)>%kA9g52cQsd5(-o>cNtB7|8@C8AqBgLrZHf zUBM*O>-{PkG*hF05Ueqtm;`X)f<_OF63Q-=Z=!8vBJeh_Yd41|atF>ZcZo`ydfdr! zeuFPgzZ6mS$R$I)UpdT(v(ouBTG7KhFT2m!%@x0Ot2s0;LecG z%ge_0*w0<|=W^@(d3T|+-k*bF$m381SnYM{ORFIA!)bY8KaNA$?A%vd>(_tT(~npy6-$1Hgwi)*WM-R+kCp_~FcN|~2YDP-5*zHa~Jc`81ytB4Uyy~O(b zPiWT*$!wXy(Gz4mtIHIQ;d^54*b^!=dZ!w-4``9Qaofk z1mSDMWk;S*30@EUZCN9-@nVeOhoww$IzCa-VtOjHj0ZvmfEj0m91K6+{4iU9&M5m8mV4GEz8tUYS?SqICG-ifCQs!a+u zY+_xDUEUENsy9wc5-P&|iUvXa;_I2NQQVd5yb!0^U((19vh^vtFE* zcct;snS}ImGYP8U^MP8f{peN1hNx@ra$K=b7m?PK>>#gZ)mD^7stfT}V*WLHnuGM! zR5FqL*)_G>#xajJTNuCPENIL>?Wr03EK&C2DX#$2Dw1oV(BN!N%Arzmx94KMehIj! zlc?W^iozs~ap))of; zVSjb2%Ghl%z;u78*>Cr=W?)>f@Polz#9lDyw=y%JXvsFOC6bTpG#32$EQZ-|IMihy z2PsAz62FNnR2Q2IC~UcDhKn8*n`;eJ;=~D;1~g~KV@dW~xzboJ&!Wyoo(*JI0sKwd z9S)$t5@`mkOq=gE4Hqd!QG~^C8+=2zx*vxqq^K~5J-;)fXO@ZJHqC^%jXHR(10=gl&%h^_wDf7N#1BvuDq@5z2L1Vv#Pz%+PQu)Brk&9~y_+CmA`~>#wpiI_dMS za+N&Q=VZY}H{3(UOPRG3C!Mi>lQ`We zKyEE+1fvcII<3yv?kR;>qXiP1{V_ZwDD?hUS=06GO2ZDhMsUD4|#E|OQ(8TW0>QhZE)fUIL@AP`hw}c@f zg+I{`J-U?kO!4PmHPGP36fblJtGkAd&Qy?9%@UOE2-(i-DO*tPiWhER**nj-Y4e*fwVCqtYp8En@gyT+xFU%_Oil{H23@YRbq#)`7+fdN zsmU)Zb)cMah9%9m2PEM}36cHp~cPszwyaUBS z_2$Lm?`UQhg3f?j=h|g$Siz(QOfP1Sb<|Wo^|>U707ItDhUZM8vDYaatsIi-WoE~f zx~7Ut>U`6hboWMSb(@sbmpb%U<*zLdUsy8XYruN9T%>Wk-=MIJq$=faqs{VRSqQKn*cFVnF6$kpz zW!Na(v%h)Gbp_+^0LrDCJmJomUC$N|52$8TwhCa=sM`a_lq&!y&0F zpk6MeLQIr&)*p89__0GQ08khXT=dM@!8R*?o@46p90G8*=L@NGt%A=v&)3Q zFkgto1n+rm@a&8SEsey{GH?M3 zYzxz(Kjly9YdQ^3I<h#OprL=sK3W3T$!=N zfzhgBf7&v^2kz9QuO+5tl-O5Bt`nEWi`c}Vkq*@#{i_6ark0rOlmLi3#;_}z77z&- z+GGeg3CpdAISsK-u&Zqu$e-zMRTd$rX$J3auO&k1O*?F){ z)$eV<=ot;~;3mbb#J_`m)(v4#Mbl_z{I4V9?kn`f9Kp2<2pjJCpE_ed5I*~Qmi*Ja z(Au6Y=*Q^@ut(#OP0aC`9I1)K;VFqV z+Os%+#uRn#7>nsmr)`>qC7+Sa7tA0gNEhN)!rD|3Qcm?O=Ft3F6ALZ;imYNSh5?^x z=^QM66SraDT*E+e3=XJJRB-m}BtM6lZ{xxL3HLoNb~2 zoB@jP(p$6O`(=lKV7_UCf41Pg$5bZO>qf#+%>~?n$(MHW^ z8%2ZAx5j>rh@s{e$R#b$;)~o#CNts161fyTGQ9tx6|sAzG2M(#-gD4LXf5J(cBeDi zwS0*%dI`xfSLX-k)c3zZzl%ecXc6NivAls)kvSMgMqLpz&rB>>U+(u7XYB#Ej~MMr zPme%=cv9C3#}greXO7PK;ijEG4S84vdc7|2o8(mfD5k3z1Im}aa^3S5FZ&y)AULk> z4)#xm-kXaBJMsA6YMMdj69{;TzsGJLgnl5e#&B@Ryz?d{Wyd!U{#pvLVc|zd9|@>v zJFEmDr|@859z*SeEKFM&YXMwtfrGXf{F^EAPezh#o9ZAAO$L1^KqM1&q4B!>uv>W_ zvoI(K5s({f=;BF3oIS52LC(3yz<&AD!1W8YI3muPz}Cctu*NfqT&J_-6W!|0{ACV~ zBQW1QE`*$0>Oh3JerTV0-jWl=s$yp}DTqvuwKPR|X!d9ZiT|Rc3G-kA6HEmxM+!Kq zJx>si*gGhDVd5VuzKaa4LiyCUKv;MvH}afa@!#-+%wjU)al0ByTUgfr1u2}>frHt8Q`vO6PqmnTYku|(J^lkB&dDdIp(6yucDVyIg)CSJq;w)b<67g%=I;aPmanO*OrGK0$S zQTE3-A#$0K8|?@|XxV~f%NG3tWtCLZtiNEpvCHZ9&F*URb$j|e{&3?Xow2pu?tWQw z@DolgZ3qw-ki(DW<7qNbZ94_mP2%_Pl3W)mCR{Acz77_nikR)Zap6N^; z^3rN5$Rx9->FU(yb0i6E=QSSO6X$Rm#@i&CQ5;)OD|?a5yykBX4l3vHHw)qi8eqgC z7qWDua|6^7XYDJj(E5i(IXV4?u7bYTgR!fbd)Cna;(}p?h9nXl=hIc6aWH(zd59%U ztwCf#FWsaF=qx8Mb{XWQnIDb8)Q1uab=DR$a0cxvy#e(`T)pTo<;mvxuv|t2W#Wkj z6GZp4d%xs?`}7C^O_qG1p*OoEZVc=??I+itpe>{Yr;GbcGM~^WJR@|L=h7b^TaQ#d za+>L)0e}Vj#mf4OO^>NWvYIu3C?L8V%4%8H$?ubq{d>bFU^NM^7ujSYt3gMnO@ zwz+<&F8QTJL|0@4a|*q+IvUbPXK|`gdF@DKF)AdctVEHK)snK6DeU(S4({0!+OmT` zU_&?>!9PelRko{umxZ0G&|rm+`l3oPccp-P0IU98iGmMNm9D+yhorjsiMD zzHmsmhMzFa=bP&S3FlWcsikS=Dm6DHP^&EdNCv!tOaqlkIKLV}(&7}@hr4KFnC0nU z=h>+W`<&KR%56oNXT2e9^>6ur!|e}kmWN~+>DDeT(v3e`BE2#SD*ktvT>wC0-B3}Y z1AGCYk>;mTI9Exnb|j&D-occog<%}pw+D@GcDH29VgUDm{iydQgZ1t?jZ@>@e&3wm z$8LOT{Rms|fnIxR6F&|B5h!u}p{)oNWo8l3L8uVd7QbfNtD|hH)>xG4v1vy$7D?ix zL}bzpoCr)!VFAKARANHGtPgk4eB14w#PD5Pi-8mOiJrG+6dGFXfhRNmZTy6*cU0{M zr~O-+DQ4EuFqxhWGZ-g$z6hbw3Hx_f8aajKaC+`Jh#|n(aC>F3Gc<RAeN7vZ-DdbL?36AMiRFfSvLs@=9gkv!y8+?To6 zx&^zS^zBbN3rA#DoJU!VK=J%x{vfNl#cQ~7iIjo}YKjZp;NLigDoLE%`!VL&qH;Um zbK;4!S7teP! z)%_5m;na%?erU3<;)ry6lFwGqVqFgzRzvtX05=_C_0; zpfEGPqOEh@LTd@9yeyih5M7ZeHW~MgcvJpU-cPzlKkmDml$HD*+a|wq_Q*!MuhSXru{svaxG#OU7{RM_LX#h`@dLPB{VC zy?f<8aln`;rJ?I&^6Ij36Pka1C4-ZlMUdR&wvyd9Vyt^9fx3E?&xba!jrH?s;(F!E zhwCB$_n&d939ln&MA7Cxrxn9_$wUnwYy_iQ!Fqn2$0(CdfZQ_UDPU+w_})}w=EYBs zWDW18cDIM$*H`9CxBL6%`B?m&x!`8(E2GeWrpNAH=v+PzT$sS;+r;wrQ#XyIZ~ZLQ ziG#EcSm#g&uJvqQ!&W^p_Vdk;{;}Z8v@8gb`Bv09gJV}%gYwI&t3|cACxTk|s1amo zkQ0b*>#2pXko#X`7)j^jLv1gV<0D6^#dlhdvV&%rZzeaNSgZ?#9e<60IXcGzQSQts zFd#oj$1Imt0{!`oLg)QVjKU%>UnGRBzf__a$FL$~TQekjR^;C!wOVy8{ZHH(Z7Vnz-^4|ES&lw2DBK+=2F zUxI!ho*d+;bdm+%N?sO)D?$|g3;JQZ+f+3YF|>fMe8Z#+oeO|Dtpbq{v#c*=OvHZQ ziVWve%X3~SOu8ZG7*YX0v4knDhX23lddK!qqD5IZww)Q%C@r6c<(AMrCN0)ds=HnKS%_iOf)qqo5t8h#8!NJmu0TQ zDa2uA!GWtKgNtN zM2yYI7cz79f?4bQN(eDEXA9S?wa)o&GX1GTvzzaB@MIfI>HXlY{KN z5u;+H8ZtABh(Is+yFZPo$t!miLmI{*JWEFl5NAGl9uC$>^X#tx3wmC+-Pt~l^?E)h zcA@0YJ|9AkoWSG#fC14vnF>;{hFp?Gj2@c89ik8fR`g|@SEk3Ze-Xi=#{C&|cV1Eh z^-!_(0fdqUPexwfqd)w1YNn0q;fEj~*IyWX6mav{FA`&x$cEt&B}FgL|Azw83i8=NAgc@>n(ExYnP3R+NqK#A zwG23BZY)Tr9ag_o?zlmyLif*8^NLa++L<3Hc_ue&*szlNL zig?^8b9|!(X#^`D{k|mtg+Ovt0qU-MdCiF+Cc`F%MH$Kqu>J@$<`mgbL1JJp?=!dS z6~sBeFN){-8ZDN;2))ZQp(61R_06)R>j_8q6PJ1{DE;AGB|b@hn@8r{>?)p6`lJbW zHhCCNfC6$`9*}{Iw&4qmcH_B%>dnha)W5>KHYeh}FZ{Qv8$!%jHfutpZWzz2my{!l z;F>X*ty>XN8odY0y{H`+NM6wIWf8FN+Yc}?fw<#LQ7UnPFj**b90Cg;ccDOtBu@yK zpr-%!=j)y3FBnYkG8O*oNc*{wn@pP=P3t3|B8{+;?-&Vt%JPvjvYZpFMh&C@Z6Ig5 zFo|yYk7^zIyWml!EO+4~l%J1i0*#q}JRLH#t--XTx!R#Bro{!Lh!PGfXP3fPaXgiX zqvv#mt=fFk?#V=I)ta*Nr0t?h>kzu-r46wS?@n&U;&39L@7IklV@v+(4k|Aa!S98r zVtLl=52u|tEhscmhm7$HV3j?(x6Wyd2IhQ3cvkX=7twa7Ei%gn zQL~t`c~RS%l`!at;!*8*3>=bAa@kmgE0fd4jC1MWw%@Tw1a)ucl5TNe5E)&KPLGd^ z;}a0%d}A5OcIFdq)%r5@sy-~l_a$M<3c-CWs`TgpiSg0rb1m)E->h<9td(~uED5N; znxDDdbJ-m?Kh2htwjiS^s8ZFIj0K;TETp`1zCp%eFDwlrf>V$V}K8MPnjZWMLSZ{hA%d_iwHEMT|ANIh6 zB4A+!gZP-_4)eB(HCi9aDsw-sITZv}3KCbikTGr_T>o`aKn?SZmXARu4Jt|yU;eVX zFEJ_`q%Ni2iMGdUtlXV8S7%00wx)912VM0d%Jc^V%_;SVk?%2&B zSwJHefhsw9hYc_Zo>gj-%_g_vxNqbBBopa!GmaFd2-rt~+_9NMT?Z3)t)i6>_7mc; zJ);o~twK#@;5~^L&_v0o+&*7hP-|R!YxqJ7d!cJ4`DJy?-W48l%2FP2TJ?35;LIM$p{ z6ywfyYjTsyC^3RKUXP85+0tV@T$d2B;6jiO4<>GJLLqH11>s{$O}o$RNQj*MO$6CEfspiYy^p`D%3DU^Eh@^qVT?841C4qeo?iewczUPFKZOK6#NgxeSKC%Gu{5W;ygj9B%%9Uaj zCli^uZlSih5AV63u0`kqD{{hao3{l}C$ceAtzMyN-G__RyfFW^$^s%^=|)IJX}-!e zWoA|h1`uD8|MskOTp+qWFaZFLRR918{{KCz|4&1AH8VE0`j3kKf0t^WsOoOP0U01C z6AZaF44zTM? z9F?Ir3aZ7`SfR$L&u*F7VxWIoG}%xFz&aaADxmc=6XT|`bgwvkSn2_YY;vxt*lb_e zQ*Ci&sCC%rM2oj2s+@EDkTHvgu0CRrK@(O|PrP5HugpBXHr8(Rv)RCHf}KTE$P9f< z9l9zlFMg}50$cfSycF9h2UYQncbM+ezL8Sj|*PU%XsAOmd~kSP@0tdtf4BnE4YL9LaFa3%BI0Do$|8RWITb zdqkZEEkYxTN?y@u*vr(EOrMERv}PUohfgT^A^UFj^=A+V{9CT_?q^gOj=yX??{%@M zyucz@j=UJGBy_fKv`iu??Nt(3T(O#6<j%w@7%&WzQVhg z?mOB3X-5#;P6`)> z9qiC_F*P+ZeBMF+$yll2KiO>p5&9aT;2-$#aom@jbAMh}i4bM<4SHr`67^&P*eCY? zq+Pe8*W5^v&QSSRYOz4$gRy3sp8H|&IZiGEr{(Y`L{Fg2O{^s3a99fH=?03-Bcm}^ zMHCP$JA5B`Q9GeWCeyesFqyS*V^X4xgh@ z-xV>1`Tqq#*XBg_13WcbUV1TayxlTJL4RV~1t_a7RE%K7tzwb)2(!>onfJoP84#?i zs&grZ&lulZ>-!_++!VLz zaGJ)nbmjLtL%p~_u}K{WxJz+9;_h;LUdjd*N2sAEq1)?yMF~+*#-E*ca z6{w1KxcoNGA!h0Rg7!ICqF?K~;Yi9CC=V!3YA&!a#Xp5s!#wKTb6QuW_Hc@1zr&W^ zS!fHDuRdXx2Wcm`y81jw4ZJs>)>&5IP48eLpCYZlFSt=cM6X5SW|eY#Lw}^a98f3% zug|?v-@qU=x ze=};k-m1a2=gJ*q+)JAwOdgp;O&YJj+yX;gAJy72j4@#{U4$hJYGw536c9bx%nJoo zGhsXKhE4^&(pwj@npRK7#vd;}TQV@X7Z^GuU}TJH?hVmMM5bTp(3N|)%zLiz>;Fk(E$wt~T%?31|t}Sm#-ikyOitY9C0e4JG11$1q z=c-(zrseOdnQfgLI+z4Z;!nTH+z@3VvoVde=Ruz90RL?`J-y=>k^X)sQ0Hs1Y-Jii zV;YKj^wYtlB|pYhJplp}etVV7ARG3!MiffRk>uvq!w~Gdy+4+nwUy!Fwzlo*#?q#i z74K_#vW|Wx*I#sz30JyTC8LxC0||f`6Tk$H3pvr^jio_SlGYvP0d7Aj%S33Vvm2o9 zHb&S>stjB`Xz+5>dvLh~>qR%eep2yl^F7e)gUE;R!H44Y0) zmL>4>6wSJBVql9&HQMHp*|fO>>_^O+#A2CfkaeuL-w;*0BR$N2jH*T$c4w;BNn@}b z9hF4U4`!h)r~>&_NDKl)wF$# zXvvm~^pwlVd2W9GRX+|YRt?sGxma(Qc>~gl2(K2y!ZgF~`*selH)2va$c5Jnjcxec zLiK!O&@%Lq8EA$0&2|c^5?|rTO3`y=V&{!M0RS&)G^jvHFI8f1GF8je89aaaiCHc6 zg2fOH+&+$B7*@uT-2pn-3~}Ep%cd&{$+pgHdN*w55PnN63E{HXnj{+kMXyRCmRs|T zfI%(!xvKexyj9i}O$A|1{j65K4-XBlI!B-U?7{wVF~x6nfwp7=1>JI>MC-s9i_#Ec zg6!h05uGfZs2829UQBzK$(9iXyC7p7h)sTq1g9KU9_DR__%>@yzL^DMsDwYF#vQ|i zpf~Z|I$F4wKfjQ-pD(Y^qxtmiD|}Ez2H`8PNq`j9N=lHocSOm|?Z6P~^&h!O`;&4M zUa}a6DJauJcy6>Q;Hpd!Rx;ro-KjeKMDZH{gSWdQEK~HjK)Fs2JBSN7aaA|$2Ft=h zgeI}i?+sfwF~4>0NCzIuxvyNq4)Ey|t_)PfHu}SWs9y2%8cKN&mPr#7+w^gEUN|=h zP^l$JC&n@+eDh`ivl5d>sBG?|5LrS3vAJk6KzCix6QD&S>Cj8ac%IvL)g|i4xoXuQ zKP9G<$#X&a$Li0o#I-v5Q02CU>a?;AZlGv|BM+u7h$DOOe+hIrLy|tGx6&t4VDc8v zTC}JQIubF)dY0h|n92YPG*!iVO>AxYUK%_q`E_ChYQ&JV8WO8QPt){o5}<-k@|b@V zAxm4pf{iq-VAMD0Uqb7v-wdgz^q_>JMuCFU{JMxthzP4fohq>Ob{S3pw1jc=qK3Pz z*&tlF($S@Sm>I#B+Eg6}`IgBu$|9E%@GZ;08 zoyU1w4j9KbVOH`~K)?kKuNC+OF$&ks;HfhQEl~~f2DRhxz}}`ijuZVQGkhS|;aDXu z#p>g)HbvsN4FNJXc3-l?C6Zz_!!?ByBedZze$H62jDaU24RGsH9TX1|}-j??FkQ2C){2oz(Ety)= zTyb0QtQZhlWS^!C0OSwSeAApsVL|q*!|uympG64_8Fh&(g?JCR1A!9la0QoDh>FQl zk*n6cXq*0Dki3B1+XZSO&4T)w12;WAqmy@H@Tt>pR0npJ8_UBz>=d>O(02-e&u zb6}GfQ!x#+{heI5k6&3oCaaa`L~j;v@0nvV6Us_z>H)>=%cASW#jDPA7vome10=6i zHM}#sa5=9A{_oFX1|!a_!U?5>zVJ^U@2a^nYBHGuorJ_=(e%GGE^#6mnn&poo5;1k z2o)*D@`855*h)13ee15<5Y5j*f+1N3JrfcKuWe@7!=6FpYS$|<14G?Y1JoWaPrRHg zZQQT>@5dolIv$62i3=YVbY^!9wIkh%C)F7Zb%Q!bt%dbk{+O1W+A_I<8xl^k5KQI8=jK|a&B~|dcT>#nYO^Q)*y0`2v!Kd3@dERQhZg2NTv7WUw%mKYW z@+!jP6(+?>GZAK-(uzlR1^mrG(F2Dtp4%u8^Yx>7$&X@m9OI3 z_piV@MFTRxJ^H?+dj9mG#S%z_GDEr(88{!tOMLUg_j}dLh}Y>1O9l#_5}L-(paVSb zoxXnF_YNd3M!uZ%db5AIq3Ymxd$@WW@k{OBrul8;j^Y()(i}6TK>mflkkS2{5MXta zGOLUpX?cqWwz{nVb{jq%ZKfz^8k`C!UGj8*sI4J#F(#Gpg`;Vl!~IdzZ{djNj2tFOcJW-+uLm$h>!;;MnQGA>T&noqL!~MFn=XL*|6;+lQfpz+wI_Y?I$==)# zwU_nz&e>Ns1U(y8Gg)F*kb&3^ewb!>#Bu-5U}FTqD#|p#kxB!nTaeS)ezJbfCc>fL z;{CA0L&FnWm{+a}FdN>A1uXwixn-eQRHVrP-}v!+Z~5&49`=-Rxm%?B06jX4mK_m2 z(Uu{sjOo^yLTngofW&pqecUp34dEHf3>%UO8;Bfc))~_`1${PXl|DF$6vsF+i-+OY z-RVj}J0ib@{t2OEg%Zxo*bY)Gi6->Uz1-E)09+1ffTmNYTU-7Xf%>F7I} z3d@n%%>(wthWJ~2f!j+!Kxu81=N{CBk^F-9 z>LAoQnPkFS2A=68Jr68&0xCYJM}MN8IWIdm?EaQ2DD?mBAh1{U6IBrf2e5B;m;mCU zRv5iJvMIeltJ_3QTF}=Jk_~+;AR5cRC{Y7qfNn7=@UNEfkIJHmB!W4?zfu=+MY$tI zNGFvNL}7_>|0Erzc8(u`4o4Gd_8@^OZ8xUE#iCJ5^6M?|=v0Hrsq9zaTRG@&H9b>G zIle2jy$KG9A%yw^Am;!uD?PwW)Yxv|ge3xzr+RQUWvvlBjAFHqgM6a+Gz~e{mKnn4 zzR0yK^V?M|%~H4o?*L>)0jy|=D{^!FjfxU9Mdk(R@?#SHvUHWr55a|S@hkp1W;|p9 zu&5=pn=gWsr(TRuvAyhn&V6yqCbL*wle>BIl`@T*ZXeFEav; zY@ntBGn$)p(OEpd?_`Ia574SJdkcEf`sSY+miTxRtekr33A--J=VAwGF zb|B<}^bib=0glqKbdrZCd=%&!-J>v#5z+Z&^+N7I9ZGd0=LW&rLR438bT7Z|Bi&h8 zyaA?N8O!w!#s!B23QdtG_Rjc~mlo6f8P7Tl+qA5#3quqRx~k=%CnpeH~)( zmw%dCbr+GkgniYQ{CA9@hIpCkpLfSfrt4Oq{*5iZWC&Bed$Vwp=>zgE&CJub!Tum>HsLmDN03w8Oqa1lFX8exN(_}|iz5|`65b_yA-2|VrmWjLI6?BmUY`2*o(gM2W<2%wjy z_vAk*YzWDwR7p0Fa4*R$KyqkO+QYr+tg z36*^_o}ImNvbE!|#2bD!1qGP&<6$Qvx#0FI4_ArNZVw+9@0Xjmm%HOT@bUEM33_j~ zx0h%xUyg3S4*mxY;Pc;P^*EwztS_AAAi{GWzYCP2RX{EECh1 z?Zl4!Rx%a$EtN=Ze+SIL>A$jG?K zr%hDc50I=WqNq3vh^F-9ENUB@eY}eklA|6gTJIK~E)Jvh_tGyYr2e6@CEUfNM#D50J4hb^8l8m2q#ravw%4DAdU0{ zg}P=5$Gn(@$w7F2*j-zdv`!jp{L-)XxL4m)GYIUX&}jc$R^M;AX&_4s(wfxG3fO5W zx9nV`rHb4hC(T>xn+KiWgqO~xLiU~Ii_cU-w9r^wUyw7%i{3X$9X96f^D@C2PlIx?iWqjb}&AcyoSrujpiLPgl1af8#$MJcKE-Y zB{A7^V?(3;q@Cxyu-=t%UhY31N)Hw%rEC8NkNjJ7h+?t3`0}V@dyrCB4FRPM%e#XR z^O59fPQnm7p}Rq+AWk(#aYdEz9LhtHw%OBWb3~vqB0s|!g43anh2DBcZnZW!Xyt~# z?l0)j=oK)evt{I_^qbTx+j=mNvX`N-n_jSJX@Y0pI?q2A(FP` z@dl2Tn(_dS86~M3Ae2;VULJCYvP`ji+)9V5*UK>`UQ$`a@U<73TKy3hmBl-IX~Db^ z$57Nzg0Hq(lC~&Vf+*80*p_Q zPpix5);`z&$Bwfnm+0YqsQx1kL@~dYO8{}YIvZ7KEttyaVbWPd9{4OxBKLP0|1WSV53gltWt3zmJS&ZF{{7p3v)Z8(3C@8K002ek|10fe;B0PX z^y?W7t8K;{up)Wi)Z%A_He>N*bOX54CjfFfu4fA$2b^INN8;C+mv0@nP*V~g;gXH@ z-joxP$lI{7c0#y((J!`)?UyT-(k`v6cWYF)XrE$Nt4v&KUIZ*i8wSFSuzv3wOj-`q zvrJq6e0DeXr?kkxM;0*hKEmtn+M2TB*vdk`GW4wWkYD-KW>%~@mDIfB+(kZHkoanA zRqc4YeXXwA*7j;|d%YjOb+Xyk{Of6x*R9kwY0z87HC3hj(q1uP@+q9v|J;`}-Pkql z)zQ*QYJTR;VU8W8PC4yBHikdUDkpdJ|DhzkxRzd{kj=LYY(7!Z8WYzo zTWAqQty%!Lq&#xnU?|rYov`^9Hx5hDxGIB`K$sNyXZ^b|1dm4eN%->=dK4QN%eH;& z{^`CmO3}`lIjzGs(Gh`Nc&knIsAwFpvs5H?5Lk18;l}bJWU;Q$spb22gtrPEENVa+ z0zC?!MoVY={d~RHN=Hk_QN(hE>AQwmyAhNphv(`P1Zi9UK3$p_+tsoj&-6DcLT2S6 zYWQ%-LtE&*XH&jVLevj@!6~s0_VmV8sQDdAEw{Rsv}DB+L35RoT|XDDZ~EE?prB~2fBA`QExRdI592qTw); z4`dlZ+3S#o<4at2?ECNMi=gO-(p5?1fQ44al9F79|lO>74!Y33x6HK^2#4d zAfL@GkEPV9;}65N{IVhKlJI+`=o+%(bBVu!6o@j&>`Wt>p4{oC!0m3=6xl3;`T>?D z$0d0J+e943kmtQT3>Mk#sB+MNUb?PL7b*A3zkvz&QTHm z@PtB7dH%$Qppt~P(drNDLQz%0I*r4q>^U|q!UDOoQ+N2~t{2=<1KbW)?uEbdYRlQe z)>UQIu2qWG+lWTqVxsh)}@6inZ_ zzigfzj$54HcmGIti5+6v3#skBN1H86nvCVD9C$PP3EEWH_)ZC1sT04V-LR?)oL{YfQ%49`4 z@*pO7zZn5!)U55up97I)u7%HRChEL83V{7gWZT#fnXXxDtq71Mn-BP8nJ}EtqWBtV2=(sFi0Um9 z<5g|D_QY6PAP9@zK~C*`zHaH0?XCe%tT5z#U17cStWqc9w)qP1SDSL=V02DG^)9=e6^YTzYxK3HGs@T)WfSZNP3**kcX)Ra<-v!4QlaFO4Q zA%}z$MOY^Uh%887u1iimOg4*tQ1AGQ%@u>FluQL2o!$PY7EB&`+5*kXAAYtI9wSQ;Ku@ zRVC)TllOG*no%j2Y9~1s?-Oxrp#Xy;5R)x-^nrMhvvJkT%=$#*Vzgy@waMuvG04h# zbJ^oGL`=#?g(A4P#PR!?Or!`lpU@pg7hY)9KpwAi3*yuxkK?9bE0PC3n34KLk+LGY zYt?=wq@DrY^x%yj^$Y)17uo;<_$8WMSNc!uvwcvwHk=~_7-)eBeVWBnUf$~tMeLNF ztCyItuVXTUSsNw+Zev%p>LDWKS&-~H(!0mnbcic&JUZqSSuL{=>KlN!NAx&d^cRl( z6udaJo3H5T=g3$Z9!|?<$3yAavTkAJHzRLdl4i6m+w1lS`}vq8s+QyZ3rdHGhKihf z)`aHK4 z|F=tJ+DScz{x_*rhX??G|G%VO{&RQHHMBDSPkYt0nq=$&D@@OcY8@s@3-qY4Crlg= z%{xE*Hjfj!Ck71Aa02qxSPH>76Bf$X4GBr_l~ai@bQSG^8nLj?r`WtAhPFV8y*bWU z!^%j1?5ovbxxIpFCgEjj#-sq$&bZRPdYa*Mk$Z{?_N)lolgXk@0>hFD>!h=KLxgFm z2&Ke-tNY1I5kO!mW#FM3V6Z$bC8Ng|aA4NU2f)6nUXQ7@p@ z=GDrN_eYQj3HmNAP#d{g;g%Sd%>F=5kqrzxtd?iJ09A3WvGa~?SF|nTYjagHx$u#n zzj<4cs7cN4?Kr2(N+)t-7$$miSzFmX)mo)Eb9+hn_xo3tBsMO`xcP zTGW)E-diw;@O9o(m}z(PxC6R*zm8KOJ5X>we5t_r`5%IT)YF5G_eLl5n|5I@$05|% z3N;FHe6R?&vPkFU`c=%XRyTGa)Sx zKuQRMQ*&uA$9FV=0^oTgp1V(AyyX=i1#TaxlP#tIQJ$yjBr!az5TN7@PzO# zY7bOmB%D}SK+5Q^(Fv%Fk_q80i6R6=G=)C1WU+)VAiKf3pDO2IHx{;GG~n=eF~YfK za2B}Q8icR;;2+bZqB`y7hAY9qhQA4Vm`fsR{~#%MdOP!fQBV~im-4tRzYPOAl&!!; z{bb_IaQc>oCrsr;voOm1372|(&04tJl|lxs zDa*c25#l}<{^gzDqhR`PPrf)sU`mH)tivPH0V(=0;5FW{(wi{5Y~-AC>Am-ifNb&I zo)c`VijYv+;>k9sCB#Rjf?Y{Wpf{^lVE@rrjIDx$uLXYFTyd`;1u>sQ_rRn$9_E`> z=IoEOpSp%mp1{5NgEw?z02wQ*k)tz0*7?VJfwePl`=Q;zBOILybHMX=Psy2##WG@V zi#>IszVyz~H!lRwR?_$oS!ii=V>We={79WLC+NdPQbBXkvfhQU%KUri)II$d*=51g z4d!vP?l7Z<`63`C$K*|_UF6I`V1n%`{?cL?OFZXP?7@mCz$4&FzkTet&T z95t)c!TAVIJ2SW~lNJ~I;i}?#x};Ei@un?RP&I4a zT~@Z`wdtkH7hQF-Q>y;Xo-o89XC(m&MyqL^WbRIXo~a>x(?-; ze}{GPz#8<$W2;OkWoS z+I^gsg#Vq5t>7@;(ER1}$3_45Z0!HMZH@n@{OIccCJcW$eWhXuQWPVu^~xa44XRNW z)|#hviph2MV&%X1^u&n%0BFEs`^ESF!|4N3aJ0I<94UoM!I^lxvwUTQtXOv&iZ@($ z_8f^1L})9v;z(9jTrbp=Q8A>K)fXICtF9K$0XM%9KN~cNh^cF*a2Gn;cT6z1t=2JL zovX+s1RB>zxSF@nQ0`nm4gHpkba{B*y>D+G?w8*5`hMLl58H|VLoLq9?sv%94H8=N z&a_W~N-=t>p^(TSvwY1~I*XCSklwKV-H-ny@=Av9@K#j9;`!c7#nUCB^KpCHx2^s6 z?M!G2vGW8wk9!h)`nZ&O+fJjxvw$UaXsy{QAp1p&`iUA7^l;6k1L>#dmIsb@DP3=| z=u%tPR~_#K+*HLDNM>JBX8V*C@y|S_X>c6BrK)BnIG^W2SoJ32=g?$4z}hW!OnuyX zBY?Ha8;ynx#z$r?(z_Lukl$8KKAaX*cnlM!%WayIma`tf{s{;(yXw7Q3K>7knMKpy z<*RE$7def9<;f;h#oY>>T<8iwV#_P@R>3Y@ZuR;IJbe41=Abi#^%8V-tmZ-`K%glK zA3zzOv0FNlQ9F5ypWyIt;P>?ZgPT4!lUU$GUO){+Y z?)1$TM9-n$`53|kNE*!10qg<&qs;HF5fF2PtlM00mYIsq$L;%be&hs)zY{>n@>N0I z-~fNh+6>@LNy%NUWwqDH19y7^6$A#3_^LX`>aK0S)Kg+>Ptw=9n`PZugE-mPC_Qie zfvVuLa+&!!g*)09+$k3q4ktGGUd*?C3WwU}5S;3Ocaq&jHY2=PM3mP1s@% z4MaEo2Te=O`o;y8>xCUg!1{T9YsT)%Z)o>mH1-l)hc%bs`46h`O1-rQ^h_ZTU(!R; zwl#;<=KhWzm7eViTBbGnXg&D&_OnAtva1UyhVYKiXSz*8RQ9(Pt<253c@KCiNCa#E z#}(Bapj|-ToC7n(#e0cu32xY1e70_vol8!~T&bvvc30vBVu zXjU?I%#;4oLC^SNj3~aaadOm~4?^TPh;a(PTpTd!zd)Wt^aT0vi3(?#dsHOtfQlV>E9oo2S)dg>l4wmcQCKm z27zJD@kudqro{kZD=N~towaB=UMp5bsyVOH%Kb{A#|vkByz=73v&U|&XjAk+`U{Qm zgA&w1<5Kde1~DqK!|>xe4G|!E`2pcVypdqI%~dNjqCU)-B+C@?fZU4xokbpL6zSh; z$qoyuxnT9J%Lh!r*Y>jecNuN~!RRi~Sm3B}3B7XX==i6|2%dIiX4-B9q}D210viyb zLw1Z$6wilie&Y~^ayG#U92lOB1CRj-8yOJ$Dxjm;%d{bA;*DozVz>4j&FcW}9wI@j zK_a6@<{6H*B$#~<2m}5VRKd;2dv+dWO*8X-Sy(H%LFkaqAvE}xi-GX1D>t6L^` zaJwuXtuMO}Z%5%t#EYSXRHtSMvOXuw72>ihD3O4fM^+jFdVD7|i5=Kmz+LKU-r4mt zU5ww-_*8rx{7%c-EPiMM*anISz&6k-*+;h^<^%B6(}5a8$tAa<)eRDSA1QCWU>`JS zc2{lYNK?mABqO%75Nc!YNZ;y6N(E@T)Kb~nfN;n$TvE#cNW>HQBHm5s3|AwBnfWY2 zyY|FJJN=w0eB%USij8A5wMjPb^p@W_J~|{i`XA^)Mpu^XwJ~Ayy?!SUUo|cabwMfE(y#!+x_=x-PW%d@>Xe5{exx8Dz;>kh`KRM7_+sS7oZg#28keGH zuThdRz-qg%4t(zkwCO`LH~r7yW61b{H1z1hL`rOo`%mqsxxQM5f7M{tw15OB95nz~ zJwU~W{e1lpY*Li6P6E1|+H%3dWqH)qG3S}M-*|x@?6(p-TTY3&#REEl#ohA{J{_Zs z1?Xee%>NiG?iM=ARpGP|At}KU3Za@H202+oRnQ^S+V)Gv_(zkY2cDc+19A>s2-TQ#@FFOwB{Y+&)GC8z{T?P92eCgo|F#0|PCH9dkL|)&QfA0LErkJDN$sV; zyhw-+sGEj}p+9BUHLar#TEsVF63Wbe*F72=a=wq(iMHf`(tqIUhn@ISH{4QL)q)#D zgjc6%!LJV{BE95>wee0Xvxdy>>FA~Hf^gM}B6!UlO&vKMFggwxvSi49TuEm@JLuR! zXhdb+@Yv_cE`cjJ(m*0?|N7TTcR5TexHp-BIM~W%I%hbe{4K`klwzDeiRSMfbhD_4 zD=g#^#_FONqjtSi6^ofuKqs{a!~26LAgd|_g_)#_=VY;pK&+JBbm6*5xEaGQC3*?! z0N*B8bg5x#30WpgXbo|9?E%wyvLI+j&vt&vW}R(MfCj_gZ$Dmh1tuo4;vk0U7~yY8 z`Fe|3rw$t<%wdMGz8qgqUj$eF^^uAu=JBK<0^o^YU}*v{&Hxp&*$y+c4j2Vf-zW2S1o%W_Y8WYE461W=^<@6u%#F%Bw}}$J0s1S6AlF! z50(8W?s>54j!@}b zzR^)K@5m=u+8R;}f?6f9H562N7E6PW|C2BVw3%~~q;Hp2gW@ZS_`r#3ULjl?0Z@-i zT4fR9vYINi2^?lNG$({w7lb0&SK%c77<+|c<{xe9$tF)Sl3-XR0-L;VHTQZF8U@tH zR%7|?I9Y|{DTEVj86}pXSAD0iHmiOUcXv);-kM7K2*tW8&_*`&a4GVkI+gu2f5F7B zf#6w=ywC`-JrwE}ZJjsxq>h2ae?EBJm^7aloKBa=L}eicS|7ofpiTD920DJaOCo z6-Dn~pv0k~vb310K-M~&Yxhg=J0m^5w5#*m=-BUGUGb^m~+9`EXUYIN4*PWM*X{I@DC>Vdzn&>d~+`c z$%=jU7?Hm49`gjli$V}5#ao~_R!yrjnBS`iW9) z@fK3?0T(>FZc|}+Coz=YR3Ob^@u~hU*>0ElM3i`GkfLIU3<%RWM6)}fyXx#o0{|bp zjT-I{Mbt3{GL?SFxsh2s@-KXAjl+Q;0Cj5cLJw#pf*7%$iH5zd@eieP`pmVhx}C(g zNN?(MOxFkE{#&u=nl>B=e-DjjTZtR)dF?EQI*H-4GP6kMYoZ+JLG1G?fgFI*)zhQJ z+$As@zU8cc2k3lKS`k8_8^+Tjd0)mf7Fl?Z_kKphI#h?`x7(JIk@&d@EOnM^$NJ)v zIk?I~Q5%-Y;T#p|W4nSjxVn6%oABVk0TPwHaPO%fPVfR>lh-d$*m+Ehiws6{+*6>> zeiG2Eu~#*i=6=>#!@xg)@4yd6(LC7}BXjgD5V=Uk0pfPZ5m=6io5F zWWGB!g@6i`X{hCTGE3gVptjkJ1u!5n)0qcpLuaxo{*@)S@OIS{zYF@pB!TDr^N3t`8Qj5^s7ziuJ>9?0}{ z@%8jb_uEq=1RLRKr=D0EooQnwzOhWDcaUGtQrMmkm}qbyZtESv+n-s=ab(H(n#Ak^ z%ic%2x#f{mT^K0|2)+IuJ6l*zxWD%4huDUH5vOYdBES2iZZ$?mK~T=oDMNL1iI6PT zpPIWshS3=5#kD28bt=1Qof6gAq)}=VDni9ISIu7Q)z#neTD^(UtVvA)Y_0jRTD6vr zXk4kY2f(=&cMs~PF!ALd zJc_tvt_LfvW_i&sV>0elvWj_zO5Gq%r-DiP%b^Gzy~p~KP%UXYij5dVkaHu=-TDpU z;PJ#cKkwRo1B)?=(hiWzJ!r%X=n1+M3aG0P#5;f}o^WDExWO4QROr^6LQ8?ce0ouQR0%bZB}tyrQDD72gRQ`{^$6396;jmyu1yJ8M3=b~X(tcD9)VYZ47$;^pN zF5^pAOoLLDRh2HQu-Z#haI7jkypw{uj)B7SQv7^jcj}M1+el*ljrOG2gaT!%$X~tUh$*G`8+n%+uvSI#F+NsDBBhb0f3$}lTNJ@iY~oIFR8LMS_>m$cqni(r#${RI@x(Gdafu; zGufrLVF8ccFNL_3kK{3y+uz=Y&yuN4NP#2cZ?G%5Q^wIs~keji-@H*8!;iN z6|_Y}5y0F}<#;#djJaM-S|Q+)VfYW)!Q_KB1i8GDsgOz~5a_dfsMdEl*k0N|D7%gm zO!Q3{Nn6>{-B3Z-DRRcrt+vy&7;D-cjV!h?>+XFm6q1q?;msd6QZ^!zN(gkS7-cix^5xk3 zjA_AqitgMhy9fM)=841z-%&%R%;?|$lAb7OzEGjP^bscy9>t+Z#A5*JZ7B>~&wv`U zLAtq?LD2$Jp-cO;*P-DX{tsQ})E!v3WzpESZQFKIv2EM7ZQHhOI~Ciuopg1-b&tM3 z;k=x&zrEL*b7t!Ni$OaEZ2meR`k7p*8ov)mPp&998I0nc*4*p44)7$erEe~79>Vdp z!#Ns1odn2CXJG#X|K&nUSiBz!`=o9Bg#rU1pD-q`+LwE6`Q26MjPVE^pB5wwfLSsdeL z`4Vt->)%vQXM~~vPA(SMX}})n)thqU7km|lYpUoCl?EC`G{YHlnpHE9h2hS_x zKndM)u6p|11`pS{`_!oq1!}3!^msJvk~7uv$IIc&1j2$UAJC)w`ng8(7Wf!AVq?d= zb9)PGD)ai0ep3wY>5nRX8cYnpB{yjfYJnyaSmCW3G7u@b%lQTS!kDRtQ%ynO9GLWJ zis98)NeyqByBwkgG4Z$FWOllKuRrgfdfv-W!S>P!y$oLt#8C|f6K1~?=_Fs6y#!xo zBw_o`VV^~DD39M}sjolnL!O@ivQaj-EaZWax2eEyLegD*MQueLwxkZb^d;|26Tf*G z1xr6}bI}(92g<+sf3k(1*P-u-o^yPwx-K<6PrqZFkfYK(V=-jst#?XnTvBW2X%nv~ z(ZfhkcI9gBZBZYHUuW);3K+zK?=VjCq8t69o5najnT{%4&Mx5P)N2N@>9P;n{MY*) z^Q)@fHW;?kW6Vzss~0s-zIrvae#?~bAoTFJ!?~=b7;F?4KhJr@WSxPKH_gQyhUz^) ztY%<~%-)FpcDDaEy>BA2o8;NOG{)i7!MKDm|0DzgrlYJi>pFQx$OPWk#1p~jB`f38 z_i5_N@NgqcsbL{m2*h(IutC6?X@mV?KF5oAKkEBGT=w(;^58He0DvZ*|47vTp9G7Z zy~n>BPz$$}-Qjq`?l-k3D};!wxu+{T_!i_HVG^dDzA=7K1G?bevjS}+DRZersLX~c z+vl#5$~PJ1jIk3V8n+^UWmV;+PL0j#l}4&fnD3cLYJ=l0o6elO4midL-m>}BXIN!3GXCCWM&>%ZK%wX+4aUWTUC{V&(pfP= zi*8maCJB&%yhDvfHd(_U*Y3&w_L~e%PiH^<+x7S9@Sy<|E&EsD;nvm)-L*!}OxS$9RiXzm#o_f&1) z=iT*l*X8Eb_vU8XI{WIb?)JZI9Iam2b%8dS^*Y|)pZQ2b%)Fc&RKuy4F9qW{o%KT% zQQlkE!npPK-^SZ4&FWcp)-%SW{N-34P6azxeoK_VwpHe%)Q&eMFCu z664Un5EISLV^Cj)4egcq?m1C`v@=_Y7IY0i0YfHB9;_%rje)P(Hd9c1d};P6v~&3H zvi<_Z<(uI9b$dDbji??Sw16*46PVy|<+zx&T7!w(_2{hO&O@n4w~Uc#lz?4`LhuXA ziOR*j95m~#=Mn%Kgr2c#Mf2`j6J&tHep7nBM7ckiU2Sbu@I1!$QXHcd==Y#Ix%RKA(+N@@e>7HTn^IjP%g z)_?iKDk=tF+=b)@#?54|BXqjO&DLkg4$7|;?vUk|>Aw+Weo0N-MYmcU8QO4HZCX1c0w_iGM@ z8!1QvX;Or#^CuW1v;4Bz-jIB6Tb{ZGt_SlcK<5is6R4Ed%J5r4hz(8h5f@UMRs)V9 zes6~`ku%@q`34P$8jZ5ZV1~7_!A{)RT3m=4lR=R2eHxrE5WBWa(q!ey8)fclg#u^W z3QgpmTs1!AAr8O>KsfrQTJ(J1gWo>@%p3IE&XY4;JZ!u z*4rIxL$z5TgO3h|LNQ#7sYMI(X&3|!({?goJ?$vcP)@oL#`V7Uwo&?afZkY;=eD8z zZTFAN$^55+7y)>DijFQAm{;hj?Ft8|2$9oJkrhKG`VB6%>3Eb!r}v<^c(W&f`!y9D45<7En(o)h5>Wt-3oUP>ki z55>kKo1?S^z8hYuB*k30g8n^ErBESLw59;jR)P}M^(kb><+IsBm#$S{P8{Iv?9&2F z_O~rLS7Yllpq=2A78D07e>{YVE<+}BdTA3!Z=gQ}3?4}JlbdqQ zKv?!a#l%ycjD~n!5%*l9kfGO{uT_3YV~y0FMQ8JHmC^uZS`Bx|y0ZuHL`;fU*FbFl zzMBC`fx@{+$gkMsx+Pz5C^#|>DvJ3fu!Gb7`}d zQdG1oK(QI<8`M+?21kJ>2~^^_!;~yf6#uk(Ocp==Q(U3}Gcf)XB3vY+A7XFlE8=2? ze=3YwIVj;od{6c=rl4TasCtr)x;{C&QNtE+9({JL8#|Dp3Pp5cc~;KzNynWjvPF$N z!4W6FFN_~vij*0B$@}F9U;@piaS^Tq@F&3G2SPE_$1Msflnz#-R{<5Q; z1@Zj>YU*Fe?~cYyz*#RI{OWK)&Lu2J2*t<0udCdxJN<;PP@N(jJXe=V8 zIg}=Rl|7zF4IzYIrV?}kmX44~1%<@%9#0Tf0Gt%(LJT6M;?DT+eu{Q2WY6+zs$dU9 z*1l-UZ0fcrR651_;Jjw|q7c9@Z(5*mUYo!eK-eijK|mA%v-bZcDdZ#s`Gq@$y+3;L zMZJ{cvh;$ST??6JZveh;dCGQdhxt_|^uQsV$;Ogv1&Zw?LArvizW$Q+5ic#T0rY5N zPI|?wBk#seff+Z*rNAO^6BF;6+#$Cal9uiGw;!3c2ZR%Cl!@UZ$qVK(h!<@m1%&|_ zKy_vjiWp_s;@M=h*UlpG5O-%g3{%TBX~nz2n}@C{P`A!dRg9s(duP+qye9z{CE-AY zhnags8QhUeFANUz_C44?^e5%AJ`|9c^$P!6nA9QReM_S$f0=R5+ywfnkBQb@p6u@J zGl<5{|Mhmiy)3iQ_Y|dG^H7CCO!t-*^@T*Gl%Ol6$nZNc%~Wj)pSQf>177p~wQ2ldkwyetsz=PW!oF)MOkSj( zi{%Y5SLY>AbhVjpN*L}GvusSX{AL8XPB#4FVx@rlrvBYKAgr@{Gb z{*#B{`LpDRF_1icX*;@-UIXrD8qoA9n>P4|TJ|Wgs&xZbIEt*gnK=#w-DC#v7 zaFZmJ0e{_UCm+3MuWnERSA8$HJ|F`)H)!pk|JY>PFkkfzn((|BFV!Wtc6lAwVTtj- zD+RVD)R~Z&_DS(7r#U_eUUf|d>HfXu`JR)kW z{?O1sbwgmW;0~r$U=T`sO9RumyWq}wzWDXGhJKSwH3zcOYNy2W-a55cAe7zrF;}|d zF>0>@TArp#yt?R0C)aqmkU<#21SWJzpcRNqHH#k&opnQ6q`^`{C^R0gMVh+Dy)H<+ z%|!JA)Kr+RHfk!uZUS(L2qMkxouL!=u~6SPkR@N;gBH^_pK*jvPb zEPQ)lh5Ll)W)pwJ5bDay7-!AX7Ow$F#@KQB=i_>F*!`QrR()wfEEJF|DGn5<{-n3D zt%h+|bklT3Ke>zhR)@%=9$=&=uot~e{xk}UcgNB*QWi5=u4$i%Izl?sJ=k=nm7g5$ zDdX^Yg)}f8#a|^zE!QlQX!stnSbX00a<57k#iff8Gl4-<&V;6t1&Jn?VC1pT**8c` z-lYP-e}@;L3)lN^^@A8WjfTAEar!p`e>oT80^e0bAZLY_hq8@w*w)}EuqX8Uxdu9x z0@}zveSE5b6CWs>4q82eNlCv-;_AN(Q{-9&L-Y7q2Esc2Y3j1U7OSRb^WD za?eCt?387o=rL$u;FC#pv8F9l(+t8RykJbh7$_P+;HtJ0+MOl`*ZpLllqU_on&KT} z&6~d7x5W%N<@93KIzfTeM}$v+cymLQvkDw*`|(N8mtVerr5${GON^zS0<_-5M(}^d z%qB?em48-b0GMB^x@4Vh$9!d8CkljgQmj{5h5t0f`4VPds7B9%vr08c65T^9{#3Lk zhF*4x=Phe|Y~?1#b)io=png9u6dOHJh){FMltI;6Vrwe&s$x4q%X=3J{A-Y?!dyS$ zg6Z6yfjSn;F_paR9{yQp3QOKApcH%Fn`k*Kn&)K1ZGkR&8y^BgjmB(0{gAY-P4uJo zOu$K2Amp3hv;y{jEBvr&xZrluw2uX}6^v7L;ARM((pWUxWyM3?NB}S_*`V*DOgugO zjO91W1ht z_OMxKZq<|Uzz!#BIUnlRlmiQpc{}LKXj$-+=(kgha@cLfUhc_s9J)v)22!Vu4h{ul zovsF6d&BMYgXLQ*{A>xHl;?cH*7xhCs}27bDD8H!F2RJqOKJfM`L@dSo!B+v`-V7yp z?bW5|+|m+-%+mW0LzW)LxQFh4I^ch?i{*{FrEgK5OSQOl8Dz#j^)wD}_S*GfjkK4zu@iY>$sFr^dKSK3Gh z7NaMOR86=ZHA8dDe`5GAR)Q{7=dz+2{_^2qydfcNRdj?uusYz`0188b1&+$W;wGH) zN_knt_PKUtNMTj>i*pN}I+S&j;%b^aFH7uRzlW%N-(7>NXQTo}rLk*?BKjZ^s#rfo zWxnt7_pwl=F1xHxUCg>Acr4duG7CVdsqKvzj?1WTvG{9hcD)}|C3?LXcmu0_UPY56 z`3FJC1|iGIuBKIZ8LHbbUEG7k`nues&+Xc`CRoA;=2RC#DOYIr2~p|QAhKIEYn&uU zo^N=83rNJm(T|;bm7iH&d!L?Pb5Ae@}5R3dd9nBf3?T{ym z4fI}h7H%HZe(d1vpn7;LUq6ulJIJh`^T=!TuY`4i1pt8i{|A|kO1WM0+E|M&HtuvSHe7PGzO0QXFFmi`b(^a(S>ukR#I)R<-)cMT zeJtSw8j3#2#g`pRl0I`dW@RnOO5*ni#ecu-fHfb;d9s!E+WPv?znPOZ*1FcMUvO0- zo3Aol3@OMSFXLVo;8!Tfs)olOe1-RtOxAZZ$PNYk&m!N8GdBL% zDwAj>di;7Y?IiIK5u4d--$;m$7!$)(0BxwpaB+Ty@?ZdcpP}K6#=Jet{RW>Q ztBFJ?++?T`QdfniN1(A4Zn$${Ms7Iw z`vTgUjnx~83T_+AyP86WmH z5!}~lGep(hemdq1Fe4+6pBH*I4MW%unnN^AP!ZlBt#U&b!IwqBvA?a)8xph7Fdg&&1_XbTGMFvPR4Qq6pJI7h4u(_QwB_;K9ZE3n7)^k|1WuRaE#u= zws|HkoAG8VOrq27>)$xpjx$2DANS9`!-pXSInA}YUz49lo{J-9QrXM65^3ki! zkc|^WlCUD=Fb4$3e!n5l(n3{1@^b1>=TVC!Kw#XX$HOE;cjC2w)uCr-z~Yq|s3?G3 zk{HsdrgbW5R{cG}XcNGaRT^6Jc?qLDG98VNoY` zPfchMy@dn%4n<@#x*17FUwT*!P6r=4ee;s)BolD@mH7un0)zO7^@7b z6!#Sh8X{{Sn#Ch31i|YaXNG?($b95J|BWgXO(<>vpvoNPpB8Kbpkl5y1e{hfznPUv zsd zV2=x5%&Xt_nH!B+XaZ6q^*%Ij1^pTvd;n&2n$`k-BQG5b`Fw+W;> zo@EjS7=zDlmD82ZKNaIK34q<3(AS5S8K1i{bs6G2}#U|FBr zZW@y1L}8zxod%c*+D$~bLA`|QeO_0)O?)ZR6+#wg9(&TLUQmg<&Q1yg{U3pN{xmh< z)r>b_d`%WrmEriQPsrZ?pjAK2Lamu54s5%c2{JrPD6adkPJ*l*wdn2U73NKu*S>9N z(ZXlnSD@;ZOH(En6O=c7^-b6M_ekeLNxf+F|v1g8x|=RuZm1!InoLeBU%!=f*0PisqKyA_|5y1_U4 zr$)5xf2|r=c2^ENR_e@O)Tq6=AJ^%3ee26SbM$dzpmw~M3VNAP3_RsTjqQx&0pne zkC>bNOf2+|!gAz-2JkTqEr)M^RQ&l7CExNQc2}CYsT6>MlE%U~1>Lz(|9ExBKp$#E6m$u<@xg zw&m*FB=@>j*IquC9)WP0Su<|@ysGvS=iC^-dn8_ zAt|L=Nk;=ABUf9zm4_Qw|LeO`*>`qpRM`F<<7;k%()uKhsZ^I1MKXOvw9UjeG@mPK zg0gFi`kCr=xytALW_qSapQjf*r5+rNx^*GkKW=^h3`>RMVBOV?odKAc6Lx!s%81#l ztq%=@q-d$(joWaD?J#$xi>rnX8}5d97dg{~GJ($Yfai9OZdYsg2NC4ekFwH5p`LX%tZVMpfl+Qp|Qf5uS4H;rYvTKt-qM+!EYne$4 zUH%hsuA7h=rY@7^8kFR@IE>iWfXAXs`-SY}FB)M<3ykbM!+U`WxID6W~A$Z%&WU z8$Kd!IC*doaonL`J4i?}b-SD9kAsO6c%RwnKrWA z(->P6d*J9M5p(27qLg+;1ndg9q7qZc;(q|Rh`pnp^n~$0 z6);H~HBYrnMA#)#Cp4)c0cuGR;jBw1B&VReft4LnwywOXb?r5fC-RYon~{z-VyuJ^ zTL$V9DUZ@$YE?_}Lz<9d#f~g74l_b2>@@Y;D`M<4*LWRUT+z(i+7hxvBG+%s9lQk~ z4a$4t>`dawNekvEX4;pt6G@>5bK)@kj)dR4SHWVt>PSN-6~jBB;B8PW<-DOpDNsWO z0;oJjDPUwur31<(2~3Mh(7|I;Mli-H3C6DajrwtMxLOD*M1KZxVEnv|r|R6e%vl?v zK%A>ZByWtNPqu5II&L@ zgQPq|{uWvWe_OzxXEjrQj5acvn3z)ZV#vu!kb~=bfqRNZ4SyV(kRRmCtmQJ%6HTdu z?8tYkavAV5E_K!hHO z_6brOS8dV?ZciFo&nq~n7L<8vay{aQKFk3e!%z=+A^@s30tCM|zqe}p^lfg{Fn;7^ zn=vjrcHDM~YGz0U0DG4!5ez;7>pnHd@)m=FBy zXN1nOSdXE@7&Z0LD4P+ShyShVl@dw0j)^T$et^6zWeiXUsyUFCjIhx*AcsGYBD@Dq zQ;5pV#w)Uh-@tEq;LR!zll%B~(JZ1q6RkZDLY`yv?DYdP<#r!t^}veZSuSWA+?6`&JX#MP|*LoFeI5flp{{e@rS|><6d`dt5MiO z%J}UIEVFFwB!aEMqXksy))UZK<`Q=wqJEfQq-{1B)++26&imJJpmgV7-=L{2^#068 zyD$Bc#T!IU5v2^1g|r>*dySV|H%ZGcZ1Yn+ERmWb+BTc%Zh<=mbsqP$rbcp> z&`K*X7JE#pKFBR+P$&t`2LeCuvcIS72#_7^<_+&|A+#`K6REDsD%%Q}yR7utTjcTe zZANF1Sf{ndPhR@R;mRddBvtj-)EuVZ!3CGxLhGx2jPvICihu1N_dQpM@~Oc-D`;aw z5co82=WZ&O%KOEf?g8w!T>LToBJ(YZp-ZzMiRs$Q)^lI*MQ*ZwG1A*xX7dk*FnNsc z|B(l%A)P07>jD7qsR8~cLA;59lZT##t&_8X^?y^?XMC*Ou*Vwq+`c0r-FpD60g{cm z9nu*(B1Oh{)KZ;AIb+jKV!`_Tvl03UnEffl7Jgo8)ZEng!HOR^+&qvnvd?SJ#a&!_ zs#Y&~IVd+aubj7SqmCa2)W3P$??^URo|&|kWZ3Geo=sG*TAwS|RGHsCO?#@N-VNS; zH&L3d_np;fPc?eRPgJ_LZK_PTbJ1)#=XAGi>n@&yWEi{X=%!o~O?HX89krDIxbxPT zb*Xgh04;g)PHr?#x2l<_PMbbkDplLvH*tty?i+vle(D4Aien0RsI@GoZuF_xG*)@4 zm}0+eZ1UvsxW}KaFS~YG#KFODI(BXD6>m(OxGG6ke$;E@(?!{=+rpn7mr|PDaZf|# zAE$UfSaxahxz7i+c-EU=sHC1oZM&`}1GH({T$pB|Ub@UMo8$Aun>Bg%l+&?`E~dtK zwuXResVY4dSI*eESovB-K6mS`_eni!d9~2&cJbRMTzE|L8_^8-E$~5#GtZr_?5=C@-JuX%yeVx0J%l%$Hjo^!~ zM}4qbdjO|D|NG$CJe7K^t!sMFJsaKujf~zlm5%JwDNCJBn=Ng<6PfjuF0hptFV8sX z-GLw&-`t$9^P~4EwfCmQCU#<9ps|9AQf{H!vxz9rPn#aCx%Zigrx=T|PnyS8(=8Las8iClD)wCjT;dL6X!Rbk!8a3+vv%BYFb5phDE+%mvHXApq zV&snQ$j)huWDDMrf|KLpV~QZZy>2?oVuDT2rkGBs?*OsKhaSMYq07^EOoFLba$__2tUk)7wc?%{GCc&1k&n zM$;A>DbM)FIxsjy3ol=`HoYIu;9ovZnxTfY01F`E0$6;LsShS|WZSy=-B?xRPKO8A z73Hj&kiK#(0Z5*~dJluSMvDtFo`#d_q>~4b-MFu@3Nw24cAX0Lx;?6ZOGnb%(0z!f zLSUJo6jLcS!%VUsafu28tXlxc00dg^Lf2vPwS-fewtVo-A2Otn^i28S@`K+`k!?vv#G0dHyG=Ii1yp}9?|4{d~LAU`?_w1a- z`j3y3tH+BcJ)1g~1*;jSecXpE%NX8=^@G3NgU1xRzylVOqR+jrRHWTCR%n`T0fwE+W3|rId>NCzN)tVmDSYV;v9RO2sWuh-zoqLbrs1sl zv^rVP1xtKSMMzcONv>9kR;Gf$~7!C27N z`j)-)D*`W#gALBk^>8J^WH`VL{A(t<4+%jr!8*4-&>+tUu2ZdMzVysrx^Y|+88d*V zTKcb=#ihe9EdW7k09n`mYLixh+nULPd4hR}`|WXRA6V^ux!!RUW}|jqLkX7)z+lGO zdcah#KPyhxGyU=DsG(-}+_ifk%7<>=afYj?-%8rTWizESwUKW9#VZ+Ga6yCsOf|d$ zT{9+1(|@kMfq#I_yi`peSg@N(cbxi#x;uSbq_o+X3&GU6p9UsiMg@f95Vv?(X)#ut z7XNWAb{ID6mcL=XrJsSlb#G3N)i?CpUf<@LJh$79)&=gm(5A8uT5;ztW@uk1?()j+ z#6x*kNb~Qu3L$(q0fHDqlR`h;yRA}Lo}hjOJAmqJli5Hvj~-^vUS0qRs+dECUAB>e zL1E4K>JaQANe2UnV}Vd7Bekl7alkIM%{&HlKy7WXRaMGhl6Apld z08h;~882X`CR-0TJ^r-pI%Vm_E(L$*~sH8S6zDTR&_rIzFv(tQZL`QdN==q~Lw ziJ$=6Qs`v?<@SS>7fWqij5>*HEa2ZzHa`lpVh*m1?}-q{?l4UhZ7Vb%hx%!@+Yfd0 z%-UVlXFlW9b@BzqIDbph9-f$UK7mn?2T`kzNeK&q#vb;``kwj(;vK74rvcK$ZrBKG z?|D9ZQp;SF5=FSYR4@HE(Sbs|nl0|E`zUm_SYMrrhIhp!_U%R=sAnpokgSW{rH9of zV^i7R|5L1jdf9F_ewxfyKuk|n$s(v+R2qV3M&N5_Ae~o51{^^j&xhdBlb4JD_zDhv z-Qa{|!~knj1@dyMc|blFkMH{+N_wY+`yYZFAw?{<_IH0`UDD0T`jmCds^1Q-94U`NR6%$0xE@R!SvOGrMMnh# zkxKY|KJ22ZRj~=kluX=|%)#HWb4e-3p2)rR^+uc%9I@vII@{0sWxptM8uIy8JCf}kfE3sd5RUeZAW%U2ebyIJGkG`H95y15dxFHERwH zT#?XdBWtM1r00A;7zTKO*-LWK+5iDcU530=TV{%rNq2xYaV~4Kd%a{LVqtTF%qr7h z=N-2jt-ND8Cfk-yHEv*#uSi60Y1tlJAZ^`s@6sQq0JD?ZTbJ%ne=X&f>#DsY7|g~O z=k#b_(R&9qE`(ac&ITNVxSzGy(jETbgC*wh(+k#s?{Ycbj_!a@gtMc5CyayFbXVnw z@a0a0M#ipuCPsDSj6iGT>G(yD{+DnIT4w-{!qIpZDRlx)O~ppGa_5+3p#2IsNTRz_vEabb_buyjrLVYj3`K|o2$(ITHJs~i zO@rB*IKjf6SZwTokmr}+66>1 zI9IjgM>@&CLo-_c;%@5+82RI%vC3D8EuSnBeI;wRJrG+ifT#?v81lZf?#Grh-FMA^y30ltqg=TH24#0=wKkcRlxr z>m~tF>=N`_55Kgp2#SrKtS{`9^8%JD28CUH=ZnV^>NAuJ>$0-nMG-MzF|sopc#>;d zzdK3)7X-DA}y7B@l_$+VsE=vQVJ7 z|NYO(4nWY<=7t(tj%2u;m-(j0E(F~-^bmL`(F|@*!nK?m89DOex;=LBD#I7h1Rptl z?$`diq^&3c{y==1px0sk>tWI={d_j0w-+Y$(#CWJYUPS~;c1dChZlnN&Z!h`BJj;$ zdnXWfh^(|*_VK+$JH-%o(TA39sNP}vARE=Z*zt_z@nAck5y_>6a9z#iT80;Az(&x> zSFzV4Cr;OWTmND8oTqWiO8?J=1LzpV=Z%$W>RA>q;}PW?^CxXE9 zsF7`b;romf3rniGKV2wK4jj70k?A|8nsNps^lVc8#sbp~LBQYoIB$OKRoTJjAnKd= z_r6-%NtyVnusJKPmo-2vcu}?C(Nso`(nETPP?F^|H#3>rJn>KRSerGX9yS>*y_sO~ zApvndd`xhxqdaxBQ`mcu6a1qUG~0X&B%c9fVs=`;Nuy@Vi}x7I74z1bBJzqJ^zsgB9P21)EL(BcS~X|~(0 zcmCS)%Mw8|T~sH}uwII|Gi6 zmh!%FOZ5%_CQZ0JIGpAYL!~V{sF%1#8&=5&r!@}j1(I#9#!!*tcHatqXQ>b=fVc#o zd9U<^3ZT87Ij@4{Z^qNgY{i0}IW$=g#!AMS2ecf;eyUN0=64tRDp);pb6di%vF zR?ZK`^`C0eKC_C9aaQe*RwLfEnPHdgUnoR&^cF_vLW*{N@DV=daKNeF8U-{dS?s`d zIzYVD5mOI`ki>FM#2R>UGv=PS zAZ_z~R;-fb-WwoyvB_-a#>|14xfEQ?+gg zMTaFx_gJIOV(Xa1di8VjS!IE~s%O9e?0H&iIftxu)1=oWFyX%jyw3yyLTwRs%O49m zC}y}u^kDBKJMQ341cW^AC4vVF+5Ktk=y9YZfbPQrAr264M6=$?hmv$E(Co1Bv@HR$ zqCAakac;oc;zKMVpG`R5m>O`q8Tn{JB>Jj+#0-Py5L(W4oH^9B z4UaQH6S#=Cvy+<>-t*}_@JV7WeDtmYU(P)@SUWY&-RVmA|Bi zkwA(CRQx5%o#`_q!_;x}Oh1{GrhXSGLb$jD!q2W<_0o;7lUco7+nn=?k!8Ht9_bY= zo0_3DZPJ2101);~n#<|iLVTx*^z${0Nhl4`F}%SNCU&EU7>>>=19;&Pu_i$1w9Cw& zu0{{)7dzVwNQ2%?jS!_|ik)Xk-IJtG-HwMHKy-$*4K>EDi`2Vvs0vrvqSgA|Gim9; zH5a&nUdWNn#*lXS z7PD}=Doa|hqA-wu+X#x4@z9%f-2|ycmRR&~TqJ;&!MmJD1#VFwX1)rBjn02Tz>?C& z#d|KFz4env;D13bGr-8xK3Z^c`>J-o%uJ*oczDrW%j05;gt6IhWUq6A5hhV`n_hLm zp~tbY;R}Q|Yl{ z1abDtnu&A~`vko_lcn>vgbr(3GELg_Tm?^5qFFeEg@t2ia0CXDf&Kl%oPh|srtDP3 z9J1Te8`5!){2?b|*?|upD|g3FfyQGzV6W0`C#k281zlugJKDFDk}V=<(Q#G~>Jsg2 zYE2Z!*YC#nvefFbFEQ5AO}Vk+0_Qy5KrD6U`Ecei_zbf0C}$G%FK=2d!UhNs_!D=i z(LlHjAp_Up1trvIVDlEVy+#)Qca184(qYU))$@`vUknGq0u2HDZ(PcCz;DJAY`9i1 z^_$Y+i!QU0S?45=)p#h)ax!)j?I>j`!=s zNENwVFE*jiuu&7~KHM7YM-U5W`%a%5vTG$`qK-#T3f{VfY@y0uA70S3(@)$^=I(O8 zII}($Qw5G26eBk$mBe${P)RCq>^mbMJjN@{pog&9Xo7H{+Bsd+b3rkM;Io2A*}BYD z3eN*OZWz_Bo6um#bf1LhcQH((P2VMMv*rg7#6f0YPj~j*0;MY8lwOybHa&2C%cg+M z&2F){FbhA94BCA<3=*tez&pLb&0iEpURvjNG_0cvPFwOHgS$K*2bQrDWIIPT#An@@ zf)Qi_Z$%NzOBeqS4Xgy!B6pD(@bput zU-cT~Q3rC&AdLb(=}GHko8W?~6~?^772uEx(HJz&$W7uA9p0>KEP{=*I{@Th;@I1H zBLhCUfK@mmi`7KpJm8<|XYl#`I3Sut)e$50p*1G-;Ji@-AbO;@ZO86*7hQ_B@mDy7 z1ax?E?d5%AN-Vo3W`8`PImd(Cn-<1l(N0JW8`hdzEIV`{&WT2~DI`p#b>VK_1r3l7 zlr+(nmlO7X+sa)q09~5N(NOkbXb$~DDA4MKS&u0*KqhRq=!{u+)JRNB8a_aFy_6Z1 z1`7vf?8Aoxv_Ks4cSWOl=QfWjF!YD>13ZpVLX@in&RRQ$@WoziM7o+tt%E}f@2<{a zCj*0j@T|nlx=Pl>0>dcqf(cKG=jZ8uau&2;*1GLCl`<*N0uW?P)TSyZVc{8h+sag- z?AtZD8Y`wI!Rj(;i8c(A@oE}xAQLWCi+RhgDFW&o#PvZhH5L?l7KbuGC!6zftuOq0 zUN#7FES^$xi0t8R5*>|(=h)3Yg0@sqT0Z7sl7$l+|AvkJVt_1DHHx3MVwC(5{o%W~ zXRx_K>^#tmf|R$_}U+&@mC zf@%qGI3%rtDlqs^+A)yF(etU;)FYy~VWkn!*$ zg`mUuYf6lo8ON#Q=O>y=siDNZSC1bb>CQ*zBhJ=!eTm8BRu305ZtoLOW%PMDy>CVE zdjy~ShYjw9zb{i0Mf5R$f;M0r%P75qi!jVI=$Y2@8NLAg=AQ#V{f97rU-#7dJfH3w ziZVY%Dt!);ukjU9i#aUrK=0iL(-!w0=PZ~ox{#v+Em)YQu0n)5EAr`9M7)tz*x(fp zu6wRLTz3^tF$my4rQ73*RW?Q(u`45K=J{?XU}Kx}ZhEbzvIWx-(;*<)m^G=B_4*lu-wf=oTT8TriEo7Emj^sN|96B*zcCrbMTI2tk_3p zHsonPhzkP7#)6!{u~nVD0~H_LLw^e&8vGb1@!$@79T3V30M9`k(8sfqn%LK?$p4vO z=VJGI1yW0f5m=8Tj)@%D)i2l<__;{a+ps7_yP~}k@C;rf-j_Od1jQB!xIwNlN^LM4 z`Q0q^vau0u2HNCg^QO?^X&w%av%GBm)6!=e8AELZ#)KPsBXdw1m~0$>5=QVIuIX(| zX^qu)4a?e!V8~*Sf;~cDVo1)@-8l1tC3)_&l`zhv9{f+PESK5Y4)iA`%ExC%$>26_ zW0^+z0GRT9c6mr@7krA1R^cVs3C_noX?7?v%{6q={yfpLEU4iMJa{{C1Fu6P-&Nkw zX18Bj9*WkS;YWv5t#qY-kOKkyMdQ&9OvoiC1_rgrOgYz0f-nwahlAR~ezHTVCiZp; zkASPRMp_y;kbLweYUUn=eK{6rH9bNRU@fK5(JlK(_vNb3{U~|;mysjqXn6MC)A9TA z!B+DtBVzC`QO;=RBDo7cthl#YOKyUD4Y?r*^Xkf@^DMJ++?|q^8oC1ZMN~f}POQ;L z33DrZmJ?+ihOf(Oql^PnMCCqRwP)uK(MC*vt~!7`iAtD!S@{~AEj@Wd1m?d1iM4UFOZr^0!I_Fd1A zsk3k(dBGV1-uL-#)tMLv0N>8n<0?SkPx*{*&tco37v4r>D@@=|COGt1ZP0C$p#uys z9D9umaUpc-oop6pV;{J=1h_vdLSwh^-{XZ_$uuL}8-Y9(&W1&|jHyp)Uaq>TMH~EE zifELEa|MNzC@g^;V{P;#C0npmysoeZ&0|W;Vf@i<6{&7T2aK3U)Qh9FWk$LO zT|gin>p?bwuZ}4ynRB|ag_3L_asnuWym#af^V54~!c{zc6Vgq*E+3lS52(cL#pgy2 zAhr!=mpV_K$JKq&=C<+A?J$wAuO$L>_uX`03Z<|i1O*sO z!z=lWQ5Yu+`P$qLbz{8Ci#6hl)ql_J?JAw_CIM{aaOT7A)Ru{_gZVMmYG)=Yh$MAwcv}u zcohVw6tHZ?mhFQZV1nTc-IUq&y1s#%kC+M0pF5$d74V8qDxYF*wFsbbH|ta=zMZ~^ zYtpq?Pbx)>&xu)zgjS4@VS@vi@!j|24=fPOe9OJ&ojc_ET=M zcEyqi&|!zA;i(M>3JVcPL|FHi7Vq#}yH}r6F&=lV`p6?ZDTO?4kYtmJ%X=F|We1Hk zVucisNZECH8xXp%-GtqqQeW4t;%|raB2d+(nyEagZg`Z(PT{A8Q5805wwrNnvsF;!iKP0=-9*MxUth z8g;eM2Im);XN@KrBk;Kbd@+iJ+&B)a1Bc~WN!5M3JEWTBM_iy{@^`Upg(m*lWQevOE2PR}Kpl3JN zilt!}y?Q6zDktSi-gLIO~1ZB#aH8wZDmSp?t;($7!hm*RSD>`g$UJm9Uq- z3~H3j*Wyh%wY2VaYEqJiLoZ_F{BHUSCASUt$Rz3uR=wbT10_fYd7pDu(2vvap`IA+ zGxdUl?N*y8GKP#YYvV6xtk_+P1p~&&QPYXW_b48gH<%cw4D6BpI!I7^{UPpNfAe#| zspCt|zzR$PJ9;-|WGE+R{c3BxzB*{z-MbnTha}8*+$&XxoXB}}6ZaY!$*tYS8oJPq zz1MrfT4Q~0hrO&FC4s=|B5(}Ps69DgG?QuB0@)_!6W=pio?vL%k4-H8pJUU6qETME z(FYy?@(0=nQIvkixx;XFX(y3Cmg}s#SkowXb`*-N3o#t6eP?YXEh zoQI(vV#T8f;ii^5;YGsJ>Yvb6%iM7D2Gg zaQ3IyU)labE5-9=f?l0{jY?)ceN2*GzB+_E!;WRR3q$@>A!bwD@D5|RvtNSPORXQO zc7#yb?vr_GYkrQu>FbbY54)HV(Ku!ZKa$~jc|?YJU=A=okF_h+N;6f&w+e7&OO?GpLjt&gbTle#rPjbm9N7;MM2=-nV z%y62;6fb;g4Nu7V2p++Vi2e0xrT;M{u5s{diigu{>zQez@gL8Oiny;ykg)S7_B671Zv8~~7jd!<+L&Tg_Q*`krgyYH@;^b}nNC8L; zt|f4ztasA`;)y+!%enc5JSqamQR9Q~vA7AkRFviiK6e>V;# za6Bllo|cN0zK$)OoQ+4E&DdSiaNx)#Z^`M8EGVW(E)Y<+6(R;U`Ks7pbs}%RY`8j# zlzF4D)%!AP^}Y;S-CQlhR#c-~rRKj=MV*UVfH&@QZ~ct7_xbp%qYIT_mGuDsRo*64 z)|7O?wv0G=S-EpPh(UVOR7sA5?8*9;c~k^Bq7W>s_{9}a>FOXsMjcbdkcQSr%`a2I zr9db!sy}}*lo4`s8w#F&Df#q_gd#~(5alniLV~u%{Ns|8=~hfzHcNUPJ?a!1L=BV) z>`1faDVlRbbqA4s!KJ?`t&J+spV$Q2fixL63M@R1((joY`(`V7{SHZ8;|&&!=F1M> z!^VXX)!552ubuK&-*mq_pN0mRniZwiUe?pldr^Jt^S<}RNVH_F3&KjcJ*0GRNtsQY zUeFdyJ}(8sG*>FRHtxr&gbz$o3g)2>v)`pe;7=Lv_voLG46q((`>z;jfCoHcuPPAc z_PD|PWf|rjL0op1fRtK}#ml5$t8aH1Vz#IkD!VS@T*i$rQj=3B?+m;H8Bb=@b-r;nWZR5c71++K=G z3rQL2#ec$Iyjb^;i~+`<$g#mLO{x}1`kVI5xwq-~nj}jc>xDEJ_i5$C7-6R!bokjp za&Od(n zWAk%gily3{76}%qJA@IAFP1u0noW?CzNab2*Af17`vu3~&>n`!03e^juSuA+@hsJ9 z`%gkYX?~c~y6!&BsR1dT7H=7(Y!rXoJ7Mm;V{Bb>P3~kAqiBBqzulb2`*!B;1GeZm zSZRvX(Rf9v#y@LCJ(AwW&$7XuOCYwpsLv+88-GI_Z)i&?jxOQ7fj@gG04+)NK3A8x z06UKH<*rffPj1*TW{>E0425G((kyO}y<2IJk_Sz5aIp_wbVGXF!~0U%4`R7mt3A%7 zjw*UbQe4@H%wfpWIEBmtl|v$G=Qq_FKa|7MP{=eL03h~Bmm0ViyT9mt@L+$mj?Fwu zm7nQHiv4agJWgF{9uBgnCaKnZw_4r$J}~tZjrJhH13Z***}lqbeX$gOfRGQ-e4;;o ziU#pjBFY=~7*>W;tr^}CWj!j$8Js5ZjY)Sfg*byg8%e=_j0O(##~@wI4;H0f1041W zvLW_sQDp$x)h^_QB804_b$Uk&5jIk30)1EQz~k3K^ge->SNDpbz$2&LC;vAJNH~t= z1!(Ds3%1whnmUDjEHLQx>c6615o0P~hJI#6^F2zv$Ly6qXVmn(tL9YXHxhkl%yu9{ zaoX}}1#AtPnoueTPP9Pqc9pgMW>kk}Vs~gQ%-sCb>J+{5zAHaVLc@zcHdBk4^H`jR zjqIvj{H`{RjV-XYz~GL_o8M|UZ0?KsZlg1W zCXF~d`D5VWvoFrWN zce3GYqNjL5Lc)mYoLS)C`@*I7$fJGhqvNZRuNjDOVLChf7w)dfqKs|D@6+ zd~Dsa*RUgd{H`=b1aWreQ5Vkuvlqd^ujI9+oOoEvASm&*tvC4+VeX6Q+ez*b8g(5p+ZYS5Je0e*qMpHo_SXq_dJ z!P5#NOc|q;M>_0C(v$E;vlq+f1ZPkD@0{-Bcn#DH=S$!6G4Bt_1K4{!UoN{Svz^ZQ z_!0V`J8D3g^72(Z-vKqSO}Z(s-Ewl0w}xd}A7D=f1dufN{~j<3rA%4}Smtzl4xC(u zwjb6O!KuL;=j`e0(^qfK4)@$O|Dut*Lt+L#4}HJQk8+jLkBv4C&`M7U^MuEvPZUq) zqy{F`lTGg7+C{N6_;X+c2(;Z5CJEl{{18!R{5yTjac$911GLUf*aF&bXtS6vLsa+j zb{CS61?8fpdry5o(&<4Hb4H?*K!jM5uJRX{V8XjN2-X~G1i0r=8%Glk1m{DMhIQW) zMSL{iZ4Bi$l}-Lm@)6@91BzSHn%vdMjbFTBpwqyt!YB*7u~p+18p~Q&sXnq(sa4Q$9X3C)^1I~cZ}$l3RhTSY>lZNmz7CZ zfiO7egY%JFV10px`WkbEww#`G%jI%W3)&Y)?!mC1VH)JDtya~tXb=_HIoRPe>;$F_ zEKqEvycWRyqcRJ#L{Ypk%avtYVak7-Jki%n>d{Qh$9ta*NC<3ubVG9PZnb~{>r)^d zAAdHGZU!tZC5(Tz{YL=)t^=T{`~cVXRdsn)HW8}C$?vQAr=r@6nle1LHK%CbcGo&E zHBVLjSj-I3pjLZTdU2gJe%L)s?lnP^(&TcIO>d#MN%pT$t#4gyRYZtwtkzQ3=fQj; zrU@Ka$(V13HNEAG>gW+Y8OG}t-RE9RGT-X3PbNG9Lq@G%^r=Cf9_dQpspk>>UAf#W zc1wQ|S0E?gF_R#`&9WfhLUa1Ch-0faMdGu5UVk>gZx2BZLZ%A1lVJw7A-V-PW4;Xzkz;6Ftv;m+g=p2#C}L72 zS_-g${imc`Y-cqYmWQfH$tjxH5R=->MGa6&`EBbtvyIH{U;|Y-^2!ZL<%H_7i#D96 z%UUX|5SegPf!x$~YuvV@uxQDE!*qtNbBz?(t|6KSt@9)g+Aq$4X9gRXVfAb=O?kc*E_bZeTxxy*G?hdcY?yTxw2dA0jT(6xex&?83-1iVB#3!k0n$!2pkUJdgSm~b1 zP=ST>`2NRghEj88(6?sJi7e~HN}hcsg%1A#K*ku+XQc^QpWMciJ>Zyp<_8?9X5NH_ zN`IF<_DRS)JhAWk+2@&8h%_5#k9@%r;Uc*L`3j2%_Q$y7?*b{n1M%DNTM>xA_T!Nz z<)Wykw&A#7lh=HB(1(JRU212~t#2!PPr~RQWk(knO(X&5sf9LQyR!QCJ3O|}BLMk-WsFoFsH1$mNLjOraqg#1+Vqrmf5 zT<$ib$-uh_U@o~!Ah#ehG*zLpO8~T{c%D>x6NGq>Do3F!+$@{hP3r{6h|XUHq?y50 ztiOuoDC^CwE^;gxS*vaBa^T7; zN+CZ~-B3()0erGe_e#|hkl)L>_Uv1weUfg+)N!D<@z@)J3T0eec4l|*;)1kb}WhoRq4%AnGbw_fn>GgM~Q03_bbO42DQ>PVAJE6zA8@|dAl?QIQy_0t67lt zs+hy+ibK>bSk=Npws7JAw0N1KQ|3G0gGljAKZ7V7r}sGM~#n%1x5o#VA{vU3I$}HU%H0SDnwvZ-d;jqK?th z>cmicH`;zyZ?@tfJNEnjq2>OZTDZo8Lc}^1ola)3AMAm<$cwF~&Di1>VXa{IvuO#0 z1#Yk1+TPs1P#`cb9mgcFHgaZtjYJ77801QMcPGgS?Q3c(QUG1eIWS>$MAo`+`9uV* zvu!$p+`rl0QEM$>?EIgoxJFAUhE*Ry$ThDimt<`YFfg{X1~JS=K8&5M4zgF$2Dfkv zs6h!vsJmtv@fgp`>F#pS`@y_)kqZiRsH#EtCrT-@ycMuxxFg2K2Y_>`b3i}cOK{u6 z(TQfFB3Gi&JI@#z>2A6_*YG)}GT^69=tbwWMVDV19!qqjPPiKxza8aoW-a)KDfgXw zcnh(6lm9_-`yk#;wWFZ~$ag+$GmOc1Dhhmok~=nn)xM|i zNb+7y(psuWL@9S<`Ux#Ms%C+CM=b(o0U9iZ=lI{r*_-_Pm(NZvhS}ee~L1rsV)&jsLjA)z`3ZOKgG~8 zEnGS>XUC{@F>1HxKh-!!+GlQQ6{nq8UV zaJJw$^14!P*W4;_MkbV|%N@7>@p|-5c>*)pQ_R~I@m3|}QDQ1!SRvHL{r#OuNruTm zkSU>0SmjEM#@&X*c(Evp9!`P$oOTZ_UH7g>Kgy~}IT`7a6o;)tde9xA71>^=^5vPnufeeX*Bq&EXh^cI01nh2N zv%3x2A@wU7J8V?34O7z-cm4e{G^Hy&#GYz#XV?svha=YZ7v(11@%Jbe#FWldj^M z=xVSx2PJ+N(}i*19*@BJ$!%ah2x8FtzddE2z*&>y1-kA4n~VwUq`o_AmvX?}cJi)R z?nALtIhTh zHLPPfgrSzz#a0{&oIbXZZb93LtDib z8BcQ?dVT!?(yFOP!>+dHeO#9Qg{^}YL*}v>ra^#F6oM;NyCsj8zlCuO5iYG&gER27 z7F?DKzJOh1XSg>fcI~WQM#D~YgO3jH#l^R~X}b+}&WxR`c!(Z>eZN?8Tvzbbh;OEu zcjXUvklYXk@L-g6?xuj%rPH~bNqSuvdV)IA;@p1~AJC~`ia-Qcc8|7_zaSg|hiSV@ z9xdN+JUG-t2GW)r{av>*lavTa@R$i3?7t@V;~MBcs1r^C6NZy4Fa{AF3B!E4Nn$ zijBeYyYVW~-Nj&%k$7@@rie_e_?NMnxOjv(=-2Jzaz!O^nyQ`EQ9WwAAgOA?hFk9e zaqhu?Jo?=8@>MbB!uAv5hQVUtvd6aj?gUT%f_5a^S>gcG5@nCK_yewux$ekmYgPQE zZaz*DFRC@X=zte2LPGeAAEg&{sTY;T*bDkz=oOCYH{ZFYT`P8v;n+Ry98idDsAZoQ z%}o^srN@YRzzU`Y$Q(N9vOi9neU9AQ=a{0x$Shcg7GTb^C!3A(VmrtzhdjxiTEUKV zq?@uCBN-e4)NO`bMN_b+pz413qTJ$_?duuo>Zhf3=dJZT`z*3d?1=lu4sySPDQs;> zNxysf{YgL4%3LDN@J@oGcb23;mB8OB56!F)Zv!sLc$5-%Va`NKIAD*A& zKb)Ps&d*NYoE&uyoSNHq0*3A%RreaDP*M;3CDDcnux8*07N&{+H4AC4DY;?EhcW2_ zZ6;-IT;>sY`h#kXb>D~oh~*sa-k-P(wsCpaLLg}_*tbSBEm^N#&d2Tc7A-xNve*l} zEZ2UFJG)smCR~t5n(~PaOI3Lb9DS9y^5q*99@*hHy!wVko_oiF=nJ*oJ#F>tw{>|BwbVRvoa`l{Lv=qe!^Cn z8q4YB8a1u%+l)dtC4IB0m)&Ctq^{Sfy!7S~?F(1T%2CZp&LkfFw2MzW6F9(Oxi#Z5 zOnW62*aYfu7>lQX(zLBwJw=_tveDm;2W*P z=GYTg(vSV|Mp9?Xg23cZTQk*`ubP`XN4K*ma~2BY)OV z$-5GPSy9F0B4Lqk*NEm3v$WDihB-=*|9_z5o0dvmU|*_;-b`K>H&5yBSM>MDngr#8 zOdgStpnX2b4bX3?V$JDs;YqZ#mJjtj+tw`WdRU|Q5ZaY|LF0T zUw!?{Up*OR_V+)et8`Jl0h*Uf?6Ⓢ^VQy$FnbFym}Pt3hCAdn;7i~MDJ0Y4GNw* zS4B>2w-3Pv2zQU~IOazH5V9^Zsua;h4LlnX7Qk`9MYLNk$#UqsX7E@f@q;K1rv0BE z!c-n%Zjb;pW~4@#(yby6jtY)2caM}YIx?_O?0ORbL+)OR^wb&GD}lBho99p7;G4}Y5SA|==e&Dg zKJIQAzp?3WR7MG-$366$K6~=!3F+4Z$;W|Y4F8*=#{aw8d`qdA^>W2j+*BFD6Yw{3 z2qZ=%*h%4wNhXU5l+g-GiWXuNJ94y0R(sBkg4&!fxrh>_TX}pz`W>52Fv*K>(?=IU zUHdV`V|CowR$b?<|i(u?oa6#$Jt|ZLhi{i@8f<|_wl;j zTwi`F5R)BgvcrmHgCVoig4KvUf}CA2rEK5dm`W1@8OoK8bJ?)5EWyRLUAse>F*jr< zBNosl7X0~V9Aa1YE~CKoEo>6Szc-~;pgtX7LPE1r9}hnO#E*FV@N5m!BJ|1PoD!DoCv${ zCOOE6B7VHIAI4scPE*67Hic3hzNWZgE50&K$ZV81r(NFSCKuT>%rCDLH6DJbc#wpE z?QgKR5=LT_ZhRl{f(%1Gl<1Eqga+-(1L5*qOMIouBYrfv_>s0&{x=SIw~$xnAx zkdIOHDVNd9%aw#b2}#>fvkd7V$$kCM$$*wmuWT~kd-8su*>1|Y$xbZJ3h@#cM+>h# zsy+1sj|d;!`;s~!KVwo{kUj%9Sq>-eA3CPsiKA+koUV25+|vR`=16jpnfZ=WaoAJI z;ch-xV@QZygbt#jN)Dl^R_ z=haP*33a_kN4!!2^*73EUm12bI~>ubVv+x+GV z??z@4u$cAgpfeKPa6*J&{XW!e+hyZj%hS`akAiD5;qiZI{FOL-xpy#kGANse_ zt;wn>N#6xHEncmwi#XJZN1!W%Ve7W z#HEoEY!jD*2Xe_=Y18d1rR&(%e{M6NR(@Dly&f&)I-cTJTr9H7Z%)p zA-JlznU_0ppmW(%CnT1F{38KGFp>+YaYt=*g>sBaN9vS_L4R#5 zzt{tLxKzlyW;CA6=h<6){nq8dSLy>eZJN!%ib6sJyMucj-S%npo^W812mQ77L!RJH zn$7CnnsxX6Nnf()RqN1EFa5n^qT83{c6Os#9p6k}rec!g3p|KIjv6PgVtTZu~)B|>6Y!1Y?%8uj9KrF826Ye#@Ee1HB|*e&DM{nDcoVasZdv-5vr2U)s_b)wuXHh`xJ4Dqt@B1CM8^MO?E`XC3>Fu^L*pH0zdJVH zk9{oo*c^LGu9(2m(3C^vY}Z5ybzD1Nji`UDV6staaKG8!HIBIhZ)DhMpM#;2$gDQ= zbxE{4Qfh_|1P%U^dhMZsvn9$h0U=~7{cK52waeYS4BpV8VKSNMQny>`L+=Cts-)l` z(np(gWS;RDzjZ*-?w98qg^4DsWCTG}!}9Ha>~kn699yTVbN6+nYMhtLJ6deC_xdfY zZa>G&NNiTY2oyh?9s$}MZ7#TeM=*EW5j9W1^w2RWZ<1L}AMpL{xj3Gky!qkPo0l)2 zpXFyio#jZ~^EW3?zk6}|-#?s?Q*v?BCnHn8VayVXQixCR{SMS4A@z1s9%U6d5}mKA zc6NbJU&6Vljva6@MFK}D8~E?xMBEbPY~=p% zZS*MV;W5V-Ro4@rHA*HnMNOj20>dC5xZ{qP=-h+w+PCq=UQ(^>$r_V3&93Att;vH8 zzzG~`85^zq$iyB;L9ae9L9Uon8(cYeaj45%X67kFa*;_j(rBd^0@h8Gk4Tb5s-@>T z5vWDIq*ZCMch!tDOTxLBqgij(sL|5yA-4i0_#= zhjdCJa#$yh*k!#_BfFd|pKbRPH6(JHxEE$}L5~|*0K2Jtwc*CvMFte9=2Q?Pll%?s z1oPw~K9&|-Q?foyR!5LAff6Gc%(7hIRswyQV`97VmK1Y~qFS=93r|)@YKQ z;MN000x-BU>Gkj|V?aZ8Eh8g+P`kU~4W_V2CQd#%xXC83yv!5WOXg+OHWO5`2k^N6 zx+`0A^eG_)JiUSI<o!UU2!g$f+uVjqQyDG6NoEgM`}@( z*1X;UP&gYSK0%z{7m0vD35upKvOY%rlSIw>->VXLzfbOcsMo|)<*i#Lg59E2ict;! zOH=4-E%iZRIU*EBV(Thlg!D=d+r6gFemKje^IqML7%V7KqJ3C0fo5L82-d6#HE9OX z>TJf)UKNrxksiw^yEjGRVF6>?>vBFang2QVc!}kR&5^Cpf8C{WrksD6#%V;Z#dvNc zK=P9&suUtuLe*+6`D9MpDx23g9N#Vm7S6c1rc~Bs6`qPgT8@3y@G{KRCh^F^jq>sH z%~b{b9gGA-YC<63wRbDyKyA)hCxLV&7%0u8z5v`Zlk*}jHO>>`)b6`dw9Kquo^vz} zMy5|ouGL2u7+w6ZMn$#m252sSgBhlDuZlB3L&Q~mL&>!ox~Pak-B5Z0FLy>ju@G?# z)9Wn_W7Vz|l0{cB^|hpcQa9mAop2Qc_E|UAy{!R8s6gG_JoyU1QCL(YXFcQ`kseSy z5-ef$&gSmkaRz@JGumSq?XfQ{Bf0Ve&bMY5Wp<59V`PM&9dRX`Lslwj@T??hMa%9{ z3$zUYvN7}FM$25A;QP5Ig~#-h!_!2ThXe@wl#F zxJxWaAV}-ZhrUPo^2NKxbAwRzAOw94k9hzm#b)%tu>v8k(Xd6!w3e)}Ee@LZCW(mx z%8>%I*7KV8kM@GM2fb4nH_@#cSBHHXQLHFhy7B}ZbZ+Y-`Q!8KkHiM+ymNj54~#y8 zRl!8M*`SzQPk_7XuRxH8Pslrg1ua0bqH9;eaQxNc{4e+(uT^sPlyySTgwiOORaEQiwuh3I8GmteqwH;FF_QW*RvL9 zv31adQq$RS*Dw9KWuNdB>J)Cp0d(8cm|@4#sJ1pqVh|W5nts2rmn|Q2I8ElH^P1f0 zPho{Cmh*Gi1tbEYE5WEsNE#YBH!T4=xi*)(DaE)qv* zI+ezd>|I@Kj=bU1t^jG2W%fz!in`DYP`Tf$?Ie3G*OaPNbRc`*#c+CCPM;e&nqC%| z&UvALQ7sK3Pm)rW4(?5zZLW$|uPUO#qe=FZ;E?Scb9*AL_?KZZqbiqrY-&UvIQYSI zPIzD2MchLw6_La%vYmLq9^+~m#z+QduVY?z94mZ_g?OM#9IsK%!$IBkbwL6+Nk~#8 z$qz;@1{-gH-NA9G9>{y)-M1{&ea;Ah^m|)wTvDyJ1pW_c?+i~eFvFM?uDKm4aCl*$ z3k4|;=rYDo2dTtWZK?FuoLES<(i18c4Put;i@Br*ECQT;0 zMB+~%52E6m)k`eh6%(Ia&Q46rm~thRfiYQNq`fY0Tg#tP5;nf&9c*@ZU~c3IX2RfV zm#n7(N=}KbTB%XR92_I5%N2$U6_(~{*w0Q3YPxL^0WLSt2iB?`>fu=-iy9RYTve@S zsIs0LrPFjwuMLgi99-PQc;{J8t`@^6=~miJy`Bqc@z}L;H7&_i2NN_A+vRCRd|nOU zVA34xoXY;xxzoWXZv=8sVP8bp9&Lh(Z?}zaE!=2-ln^-I6q|E3iX%OwFS17$;(=Sg(ydB9~AoNc1Ygvubq(RxRX`UJr@|0y4?It?y0O z4K-vDOmka27^`#RUKBaQRQ1;EDeK87%O7RVV+#vU7%zEZi6)8w;Xqz!1(%cX&E3xO z6kaydGE~!A5f?yASf7mWPB(($B~T zEkK+YK*V_kB)}f#f0m5%ZS!LChe@`%#bU`BXWP3=XBKFV@E%w2=th@>f*hTQ%_$Y$ zY)gE5vw+ptL-;&s}eQ(gSjj*$KleLl&p?yl6`B6q|Hl^ESH4h zj{RuM1h7SIrcX6Me6BWg85b`B6L(XzR~=%;MU{c^uxi^~*&aTA`1pW+74QEsJoxpa zFTZ;HunXn_4(jBp+-!?YJ)f*$T8K9M&-ic(qWR&YufCdnRsQnp+4PsdK2N_Xzy9^( z#h1mezW(N`viS0w$G`k~{^*-ueL3S!ENUKFbUP&M*f@NiY+Mpb;7fBp&BwfR=NsSd z8$Co%7t_O>uYy=z`hBn&IiQh?40r5IU}iqXW?^n^uc0n^P#?Zey{s(n_K$Oa{9(#t zNgWY{Kx6G8fs&gWG{G1jX9TS^05r7s(W5#HJxXZ8Q;yO~{it#Rm6)g;^8#gW5S(3>yie z7KsB(Cs-*~lo;q#=SwaR4xE~krizA`&2A~xC5zTb81y03$dYNUl3SE7D3NdAQhRW4 zPtkT+kh!5~P_;S=rYe+j1<nz&6J}C87~{JfvcO|!@mIQ5r9J_ zzcsZPwYo1FS_9WwVjH)tJ6-xI%@`houxG2N))pQ+qVO5CT3&k|eK6+Ztj3l)C}<~G zR@dlcaYF@!YcfW*h_s~38E~Z&T;mWPDQ|Xsp2>xNv8ZMhO~RC0MQLGud&B1t!&Ycn zt91a17hXbYoE<=D8)_y{6~+<0z~v%$Rl&E4bvVaqxdtIFNdFKMdi^#@4icXH4hrwb zU_a?RM+iZMLlG1#B1h0jJ+E{?^C+ z-!fYM&cq@1PFrSr`MFJ+BHPPJ7k=Lp3!92Lur(394~()tCfte^044 zM%kO&O_YR97T>vj`|4(S%^?+Lg6-A7Roiuf_A{*cu%a+mmV#V*eAk?%*GYSo4Xm-g z$PRu%9*#pJ6nXP?F#!&Y3I*t+1!O&b)aM9*jZel_#bt&0fEF8081UFzpOjnSYWzbCUe zX?fxI01vYPysbfa5^tFH3drW+wAdb&+u1~U`7PvDYTI<}4?~5HVC~vd;ZNNQcq(1`+ zFhFz-)NxPjsX!Vb`Zpi~>?<@!$Pkwmu z^t+SSz(0@d2llNWic2}_?N{s+Prn{zXIOOZ?|37^zo&4W;4pZ*{x0d$$G@Xi9qVq4 zXMi+%&BbGnzJj+Dsg$TLw&XS%IS&B~Y+8!GF7+kfubBXkGJm1f!qNNveSIHjf3GLB zT+AnM`{90IX6ZU1CZN!hz3K4mNEFWM$Z;xLZkde1qW8qyc4+J)2U^j;;X#FV*$4br z#`3XuC&K9O`fIIbc8@U~!bTTyI5X z&mAliRhfP}SDC;=l!OAQV1ma!gu?4wj075`Knj2qZZjNoUe}B|sIAFA(m`d?!EHJq-@uKyrQoAI9G&w1^!?4mD%B_zx{Z&)4F}(S0 zCyK+s+bf9nn9vK3Z*WWJHj7?QE}MF{89X8ut>4F$Wq6e8px+O%M!LuKbc#0T3p~tF z;pLoY(9?&TV2lk0Nb7AfIej|zhCa$HH{-6p+5Y>7mv5+EOuzTD8aJ{Rer9MdTd$Mzb0aujiWL&Jb!dFRyC^Co4xaq)!yFYG`h1RNEtXy@2W8qwDUy+9C)Mw=U|kFx*9l_o)k5T2I+U!W@tM+-^{m*mq)A2B6x3NFUrGv3 zt0HDY!ltg^R=Q$HzfpLZai80wT^mqQ@9b4`Vgl`z-LM9oV4^C#qSTN1f6v+xL9YpaO+|A)f;E z+EfCH5_zU8%fk5?$OMNINeAQ>n?vm2p2XspB9RRG;i7}M(!C=ZT!r}uabSq%$7nA= zk8hB70)_(wC?G`d&WYD($_bF~LDL_MnO6La>W<(4{bv-ihQI$0N|rj(orV##@;xw8 z5w6&ipSh!-nX^CCnR;FQyQOzw3-6gDoSaFcN0j%l%b5KZsV(!$RP9|PckOzkKaM|~ zA)X7?igs$~^kLiG##D7Kf&eCWDKd>UG8^F!&{_MpqRk!RGwdP{G%e@IyJM1caF)W( zs377EWfzCxahd^Od4&G4Fy!a_L+K{R!_$|cRmAu z!W5bsQ?-%P@+d(df~JDs5yF0kJvps38opwqt;;g{c?y4Mq#a0vIi|@EoU%Bzfo#F6q(Q~C*0Wse^JrFd6xate5w+sErDHLg&2VgB^MCN!yg z9G3Wv9owypw8ye*X`VXZAk1o$xR{1v_ zPA>(6d^HJ$kh1~4roN^NCN7!k*78^Yo!BSOi`^QStA@YoTT0gjEMu`e_Ha;VDoWe0$#tN7funWgxB|E2&dxb@?qok#Bi*(+{)FUTL9pOL1dCOn$pPBr(mjB++e_?R_3k}O; zg7T<`#~|j#9NkpvmbUU%Y&M^7P4>sv?X|r51M_JdzGsTYs!}gvUc(C=ne~ zkg5fvA*{ODV)u5)+FJ#6F9)55(Q!Ai`@N4{mbzPae186WCqlnmQ)15>adzPHBuE*T zH)z`j0263qoBtTLBj4|i&#)F314!!SJLSNRaZ1{q!=%4wplO7oq}Fb`+V*)%@RlVGO(*W4`~o z2um;qo^%X}uS1{XLu>+c&dsU~yyXij=k}-YNz8Cf(h&xCR%@(0yQ)=syM!$-|K(TH zDk}7IV2qN0b--Fz3*?AdLRBo});eTIJTU{|%ITD-nw^m|wo03{Ss<;l697h_k{5H7 z&(nTQ+(Wf?oOF5rIFO}y{H-vSY- z0gkyHyhBxAm>BTnJp~;TgM|7SZiJ}=7TLj<4}bZkkDWF}A_hYPC0qR&+qToOZQIE^@8ccg-ue?Ys@7a{68_ZH zAR+ukW@#u!K54Rwo>X%R^&Y~G%)3TO5`!miDf-PYgkqpX$38EUg}^$fxA3Je;Nz9j zo%9ve})I}PnXy9_T5cE{H>X0NB< z6&9}#)}9XQ?~K91I(v~}oZ)cWB{N=x14&uMed8obS=~0-dIu-$xA*<$(E{bHf&dIm z?35E2{1i1S&McmMWRltLBEy{w>Z|AT;LDDn*B9z}7CWbD&R$n4O1HxI<>7Sl!ihyu z(Xm9s>TqXA=0>Q5!_L0}5Yew^diwi~)_2^%u*H`YDI^;#YJoIgUWw06lN%=d7P+oa zF=u_Y2luI(HdvUcjQhsq|6VXNGqrLxGPX4{@US#9`~UX)qyOyp8(e9-9zU>6 z_x-BcCCQ7nFEt-+?pyVZM~zaKD((s$@uMA>IGheBkiT( zl1yv0l7&v!{H2~;GdUiuv#)NdoN3kNvXZ=Lt?ko_Dz?u~1JN&Y?qG|BCG)f+xaMU! z9+@oHGHJc?$wrx{Qo-)1rP&VMVimoctCfzm6sLAIR=-+}U+si9SHtxaG2(>ySJcG} z&ehS}Zq3UJW|e1V?h}hdAOXxVKylwt6*e7&NSotV`Nv>;qxaDj{jw z=~j>i7S?HM-s@;f7?l>R)guSv6gtx*CaK4)&E@spx*Rg>vmg{8)Np+uq@;F8b&$xF z57{I9^dJ)P2nwW??N;4?7{0Ep)y~>$Xt2bj!F_Y=2^!VXM2Hdk)%>fYVFBios8rbT z2i>4_PupEI%QRBQS>`HdWWus&YT$)XDJ7AW-VO@6|2_R%XfWV386FzTrdz$+tr}9_ zmQ?F3QHfx#&9p&|WMRy+rbG!sy~Ss@ov!_lcrl%e_Ae+5<24o|X8O2DRdX_t~~i0nV|bcg=cojY}t ziXkCi!4U*)ECn1XJLp(%3^ogou=6z^Nj{bg2r|qjy$I%X{}`P7{W+1;Q~#v`G9eC& z`{@F!`KQEntZdtg-`uB@VIAK>{hgErmZt-WbeB+igy@kWP?A;i?3i7`a&TiY-WfU=q( z{6_;NtEroF;*t%g-ae+Wk=QX{rvT?(VRTfg`WUA3R(vnHdoO^L@kv_!Cc~n)I1 z2|&MVt=6&cKIcj&3jxi1zXoNi6YRq9u7}21-BH}}eW`#uU~ZmPD5EpzbW8TuHZxOc zU0)s5*YOf#dT#KNbQ=;*mB@m*)IM{1x%^OF}uv+mn}2`DFZ${`9I+Xh4*twGNgYl0IQ%vO3=)w88gafh5z8+>i*fR z$~Ep81nRNUZTo=G+9e$IiHO{IcWdo2voW+zqZl?T3^B)^-JdkRP_9^%4z7}f_UP*U z_^QOD9pir$n)-o}~2*vi0%$6Mq-FnaG~>DKC}h z67={yTYWAXx!Q`B~Rq3x?bT1_|eV9dmcU4RX0@4z&dLMhPownX?f#NSkMA8wi2nK3n~G zgCw`k@B{vTO=9cyE#%NtDXl$cWxGi);KjE(8d5)dZB{`(x^6%AiWC`wR0z2MaU^<) zXktnGVzk7!h+KWlF@&(E!CnZF0tf5zws;=vx&uye150%DxXqAzsAI4l3Z)ALB2p;N zw3p~9y{>?d<^nkzrr-BSpVuH;nEN{xGjmty3G>5>)ox(Hzx?0}RwXZ1Ez%#W z`as?7>^fAZ;=HFcE|i>eDi1ZtYN-w|l3!YA-<5E-A;ECbb#)$%`hD%otXQf_TW)u+ z(tb2Fv3U}Gqb$2x(3qg*W*jDSw}<_Bx9s$0!<#6rX0X3_LNDwx1^gXFFC5`4BnJ4H z-kk3rEVv#LIzeLt00ao8N>+TP<^$O)XK5Ya`v|kaELq#a)pAmrj2v>nbWBiQWbwuG zcy6(3^)>u^X90Oc#TRUFc}>!P5$}ltz+h)SLfIG|@qF4e?}&f)vZMwjFOl@cCoT!n zW?as$I1`emU2$TOuA?UgQrgbe*=4~*Y1Hv8UXYPYCGen_4OI<3$|<{)c!3=q5}_h8 z|GpAvcYDO{n|ZT60JY3WPv&9tptFuWvHaph4LH_Oc}6+9xhvHN2=4UboStJ~xeAPn zCowip--ibhZr+Fz`%j|*fth!2@0TaDl!QyeC-FaAEY>;mfUDemY&cy!E#v<6J9`L!cIN?m}sZ+wr;{2Xg;&v>GSQ87Y`N&?XRD|$C4yF_+`XQIkh(i~UG%))o zP??7M>I=4RBrFuZfj$>EQ}nztzubP-xer4xKuCU5FZ*itAaqb_p?d!GXCV$mS3Ym( zEv3vb6QU5J`-+OB@^uZ4nc63q%BmK_2|M~~KC(71xF{&tMLTHHv(sg`adW&yc;M51 zM<#_D=m%kf%Ho|XxP;7-HTinn%0vJJ!rz|jA+(7urPlIOb*VC-0>y3WIvsvc1dv#8cWl!5mHi!BHRO^t~ts1j6maOb6L{v_$^%XkMK`xqw%$4Qul1KoqN~MlS zK-^MmnKn_=Ya}RW*Z#uV)A-r=(>O@q9z!i@yaU+?#V(x)gc({SGkm#i8ca6-!{xlr zr^fkBz|Tjb2x=^fTRNI4+ic<&Fm(P86}h5uCn%0WDUtM9dZXq1S7g67*T}N!OL>Sr zoDRpe%VuC{ix#(isbwG~BbA9L(L{&l)`!pJYQNK7@~k=E$uV`}^MfVcn$eR`tdIJL z0T)Y?)&laqPI-D($DX{AgLzp^o@A5!B=kFhSI%&Z z8gmq&b{@oiZo8YL@Z`Nd3Xcdw{j)q6g=H3_O-1U`X@X9Lh9vS5inMv$3388%DBRZ+ z{tqehesx3COO;kSm)J*WpLY=g%8}1PW1`*oC(AsUx`l1UU(8CiyTHENhaBtORs3^dYW3iPA5nNjE$}` zCMMCLQo$=znKML8=LzG+YpA-$9&n5S|6&^nVP%>Fz{b9<@tHh?bB1*|YSZpxhEqKl zqJjj&P!DF6^llPpfaCDhS%btgh)mlQdG2~@)?>T9>h=;0IYRg_p7^Mr2Z@qE>?c@T zNu96l)WF8+OIx8vY45DK#H!v8vYi3_vw?wrM+hnM^GS^ z!#s7&cbOJwcTFVG^ZDjk4O-I$FCm>f&_ri{)^XfnqiJJ&WWi6XzGlE~S`3)R3^&w3Qwiuq6Srm2eeaE)83`X(#E zN%zt1+>0gmO8vaM>cFvKq!xUX(IIx1y6)*(DpjydFi+t~E-n)cIkelJ@!MwvVUSuC?-A5lG3CQrnWFl9PHMb^$KUjH)oPWhcP zIW=pP2R(yXS-xR&r_NL(kpkmG^faJO9zu#E%NgCCDFFx*9=19+T8T0jDS) zfin&S=HW2>jYtHkL|M5t7G&TDAXwO|@BqWiU|l@epE+X}q2)dzN#tgZkVp0OJIMb10?9cvIBP=OX+U%WPtU+WeuHjm!#Ow$q|D zFqKayUF*VPn!RDacQQ$EVIB^X02l7x`*LIgN-~lxkx8vTn^@!2^i_A=n=DEHpNt;Yy1Lo|j`*OQN9sPyRSE2|pV#&k9YGc>DqPLR%@s>%0*PJ5%^ z6fwPOadzT{P<6Tl{5hJ|S&e5^?G?f{?ll1QY0|aBU@99tE3uDm4kcK!&Y@3>z1#KF zzS~%>=sCie!B~7Tv_)$$Iq$MrlF}&C28W|-jr3O^UFT`K*e~4|-U;#Nqh7_0mN(8A z()Q&npr5*9lR!j$@6Q&I6rg{O?bsD^?ctzUj?vAs3cI7V%SzA1cxnmH%*Bz|3y`@A zgsA{2Tqs^o>2y_k1{xeLmT1DE69z>ov*u6}@O|S7%DkHh7`LG;Te;ZI z5<3v(Rtce600kxS&hVBA`r{Yb%SdmmhD?#$H%HE`8#Vh>e*cB==2521f)tPaM2he| z3@eS;D(wRXoP-p<%;gC%;SEa1UGnav4OK>1W)hdyQ~J99>5>e@1@$D zLw&)MZAtud5TEgOi+z$%mnaY1dU)`n9Ib_wx{Jag4gPf~EJ252!kdy$nPI$*S$TiR z$AG6Y#*kj?laqPk0#8O9B46#s3k@oov4;sMo;OQS%{K;6`R~^jbetChcy2gL^V_$q z0lQu~q5Us>aBo~}zoRJ_91m00V_bOhfjA@8d7E+BuFpG~iim;+R9_|mrOS+HFS z-7}H7P%^9|>b*R#kA4Ni>eR2q3fEv^9RB_-uhO*!tBLp1+c`L1s#Y@`i{bnkAoKHo z0!zKJ_~vNk-wMDM0mr69YnEMHR1!MDmZ-r(Yc9e_AJTCpB<%cDIX*ocp#|3PCOiCY zO>kzdGFA|wz&lmV(_E}dOmWVy0IYnpHbNeGgX8j^is!T#Q&~1{({*Y*W8ar z+5)H=3E$mUpXh(*yM}%F5It~syutfrCJzZ54#pN<5-&Y4m4jbVK`cLy*R#O_XnqMH zSk(M6Y6mRtRvL7#f*x^Xdu8~PCc262l#l( z+5rj!v$q@vXR9~ZIG^B;LT}c;7V)EBq_=WWog0X&*=`_DfE;!rxRut80;+SH*(R{+ zyV7RA{WFo*3{{d9V!ohwBqfYAg|eV||E7(c)l>^EWMXlto^n@%&Ec}r$)3Ozf|oMf z#Nzt8BSiKZ3F_;43W*8Jgz7!EQx{EJdahT@RFQ|x2uL=0LlNjE%aZpor>HTdGf4yj zfT%99zq+jd88Vz)e9KH3`;k#;Y&8urv+9mG>sP$QzI3v0jDwMLJqzjb;!vYVp zSs>G{uqn>jD%?C&sM}xJqgwC-Opd(OhW)&~`CUOv2d{D3Na|2X@CK_cNvIlBF-Lu_ zRL40{uO3ytRt$TGy!iCGy-0@1nG-$hC$e~8;ZOeX>VB9Hx+(zDOA5a&Obs=h5QF*Jm*qwY1ya~`^?^pAT~T~M zT&D~N+QX6Aph0`4>R^8{UJ=o%lga5$^bpg6Siu1)XZ%}siMc7z;B9{Q%jprZrrp&~ z*NP4}H!Rz#%rs`LVjVQse79R77Q>Eu2GXTYM6H}U*y9sv*TgAmm~Z1iOWfuUtb(3T zHZ0#}Ty{K$5dhRzSmiUUi5a3Pg;qD7^E)ZZ2|tZ-*?1z*wxZ z*^+l&l~=~6neT5_exSI|yAIFO&M*M#Vb zRj5)LwPSLw0h2m)<MuKx7l+8p#QykswDqrwHdz!Q?0GrrcuE+Z?DG~S&vh8%6C@M@sSc7HZ6lt*rak+!3*-tj+W7}nu< zd}T1BI1i~17j1YCo=6s%T(~GT+=Io#|Hucj_Kkm$sr7>SYHYy)N3Pc+vW8bRrcMF& zMtvU<--rWlCpz7EE(^R$v)UFlav3ykVHohTZ24p7M2Xq>O4hm#-1^zEEj5LdL*AiA zUcc~XZ7|3>+i%SN$IJQc$Eg~=*IQ#&o~ z;C^S2gP`b*)K{zV7wMK(=wxMd&`#(W@hdsa^-t_yD7Fl{nXPVDZms;zHhyO47^0cIL7VGgTv=Jh12>C$^v{q%abN(^$YCBvj&p;nFbiKtQ zpJ0?grNiIP>`Ul+5sOLTZ=qWtX=pI$NJx-}YfYIhI${WKnx@zHnULbo z`mATK6@dDuCEQngPr3$cY$3wVaVt5X6X2*69!Oz2h_89FY8$szbV18XLgRZ} z;_I$Xb&-XE+6CQ!WPfWPv0kVSqaeNtdC$VVqbn?ypVm~{;4zkTa`Vy0Soqx%DpZas zya!0-d4?=}CUb@cITYqSPq$5T*v9BBNB2^U10r5=Q5U@JQE#W{&kQh}^P3g2F!Rcn0Uc4UuO>ygs7wrI4%e*(g^_g7J@9gRYx59E%`J8|Qs z!l3`OAAymmgyhk-aoP_ma8jUlb7~CKv(U}g(~L^?98pL#&tgy{=(hrwG!tS?&aQ6cnS~Z)f{ru!%&*_>*~oH8hEh5K>Qx6 z>xkDYAZ(=U26QW=tPg8HS*d7`?6o7!U+fXTm56}vhYik~!;Iz;i}uF2!3NqUMwIvG z`OHX8$a$uyP9kwo-eH{Qu_^NF`>$ZEdOD3&xftmW+SQ1%y%vOrS8TOpAiJq!q zTba&GLpB@wW0r?&u4ktMGto^89Ys%4Z~E&~qiFRZ#&Yb%cE4@w+P_S>D;4-a74xbs z$Z>*c@TxSVCXqH3cP9Vu_5Qu|@bcj!W+Bk)&*`rKxJac~rxrS<;SD;Ti0+4ZYMrT^ z=!56ej8=)X;?|gxO1Z0@^ghyfBmA2JPJ94uaa%q~=TRpH=@h&10N)#clKc+ME(rm@4?=On8YoIJ;8rrv_=2#xW5sKWHrzZH?eI` zeL8J%ZOED03OMS-Yn#!?LeuRo5o3cfl>#Po>oV9jiGo>iiIKSlLH`yBQ2zUwZbRXF zoQb6V{Cl#%PCV`%PF4RnL;VW5Zkuo8*jW&FZb_dBE;Iu;7Vsd-Nvo>0XDM0Qs za3wgG^Dm{Sz&yn9*gr4$WxOmHIu-%)K!801X@a3S0d3nPET-Ta z@T>Sl;?{z(l)bD)J0L`UHUbBa`2JsCBQ0>Q(RF9?F);pFiiCGJ0B)zeWTX}u{U7?i z59WJZqX*RMy{%d-SDU3gem)b&WX#yt)Q*8lGa>b?fwRb$LQH!DJ2#gm7wFzp-F4a* zJ}ORYQmHk~-qI6WO$d3NpTVmFmV|rqLm<8Rj?2;2pvMfN zi=ffj0CJ5>i6~vPVHExsvo95RaUL<&AQfa70%yrwQm8Fts@((+Z`_q5Sk;Oz&w$8l zCRMgUJmE6ZS*Jsz(JH%4)K$5ld%62L$^(;jC^CktKfx~%@uN)H%z8OCj07V8vDQmeV#UR~CAr>x?nJ_l6duuN zK<-#Uce0DZKrKwg)BJJ%?FhmTUK$12qjHTm#R^=MmQZ6RAD1FT^|F&K`tVk=0;!-* zL}1LSl=XzKF><*xlfQxkSQ^j_lMuF1jklmR!C`}-1Qb>KqR;HbFov;}Df>Ty1)CNQ8K2t54 zj84cCVc7Eda^w%RsJ|&CLfm^IOGWpH{hY4Mb!sZMy`+$B|Kk(V8Yj%>DCn3Xam{To zQ-?kuY=N8CfO+n>vWFi7qrn4aO_2o7Jm<_@t%SGYG=kyv-r2BejOJ}=IE*z zz~ecxEipIs&zA^BEU&p*jR_x>J|4!N09qsDTU~GYO;4pbUe4|wR4Kn@Qy!gi$SJu+ zzFR=pXo%BZoxjoXq}z~|NkR3RUltRngnJW4)Lw=omisb={x|`dLG*deb1lU|mp0}H z{p2y1MWgI*Z+Q#ln0(`Qh*eFRgNUuFHuYVl{WU0WE^)=I_UiH)#Nv1c=BKth_nMoU ztw_vdhgc_%u-dM&#$PkYzqi*5_+u8DWgfs1h-Sk+jfcT*oGmhKE2dMDOy;%4&te@` z%`K{WM%G7bEh-Fq%o1mPNu5jNY>V61b2DRP`xM0z89C8^$CNsr?td~T?lB}kWZlTK zGp_Cx>lHZh{<{Nn7=s2fx4(S7nuD+$Y?1i$x8aju5NImyMm!W4Itg1hHi7b#s`d>4 zu3k}UNW(iB*UhyX5zvlIql1QN37IH_$Jf0DdLnHbM(`~QBw=z>rNh53WR;IPzqB#W zUREu}Kjv@oFCyXEFAf+6Cplu;Pu5_k<9p_8#%Gh<$50a+A=IH${__iTp25r(WDa8{ z@`kWRG5pu$Ha&@ztn0@R+Bv_H?8Yt+Y0cppfOY6+g;58IJJ>1TnpRrwFs6d%y=3AC zu8vK6h-q}U#v1v)m87#*Mq+ynI$2sBOn-KJ_XP0>eBW&x?gUxKxkGX9(h*2ly)2@h|Yo=YqTlbaMl)k>axnoh&BvQllE9sL9#{8@2Gt77yg4 zu!#XGyfYR0GaDWlGTFHhVXMz*S$KacxgmvZXUIB6J3XyW6Oxd1X)8}4br`2x^UF~Z zzamWHB1nqJw65fg$Zmi|&To>NTBkPy@~MFg;7`Dx^=H#eUtJ7xE5aZ&BG9_vV?$pG z9f?J>tS%Mom7H$+H4G1gsK>QtiuZHEms5S=F`@k548lpt_QP5YzD>8*+_-h6ckS2- z2NZ*lz$U7IT)MD_D5Z#M<6dk|||I zlP=Lm9gTUcYb3Gxl9)jY`FIDEDU zv8qkic|8diIr~SNKRiw6e>#e*aYv$kEfw z?Go(dIgfoBKDW;@iJ45k_>0o_@zMB~L12I-14j|?$P$UVA2!}Vb)957=XX^UDzfk~ z6>Mu?5V)OAnB*C`y0=1DfV4Sr!GFBMeh1)cxe;@OZQA_a~W}t{KatO3cXH|cABw-Stk_=2?15dE0A_A971ZW$tV1{aFeHbxOY%$z)l)@pv|DV*JGE zI}5{t-MDAI0i8Wh6soO{C`2$iEu!R)bsZtpK97b$S7R);tU|PsUfD?XercRb&pK6e z%o_T`|L9Qlmg_3xHwqm^>vT7c54*FB&bQ{q?#NEW^3jAJ@!E|T-GJrumRF#tnP3Sl zn^B8{sQfW-E+-C++MqrQMoq2Th`f7!vcG>Q{fwpNYe@_0J+NxELLPTdMtfpY@myh0 z0?BX^^WPJ8FZdl8>=eB%8kYQ4~&7M+feS(K9-LG+p!%Tfn->Xpi# zYjHmtVrX8A3GoG-A2J;|&gVw2SSP>uT;32)oG-YZ4pR;Pz^sfC7&?SB*n;p>WMBBz zUWQ;eb^pO~FTBg->0CRjM)ZVWmBStT@)`p<SgQR&Jd7_X1F=uvg92*SI zZ!TkXuZjzoF)*!b89KSbXP8hYpmzdCv9k3cK4)cseRy6l8?%wp>B9Yq zmhRtdSbGrNC=RbWD6D-~z`gpzx)9DBH=4gY%V~j>ch}Y&e&BQ(<#?uVs*E0CLaX8v zx<@lw>B|47xPnjlXgX_yiKnDmWP*Lvbe;?GpI@BMKmWKbjr5B(gvOMWOq0bjLI*gN zH+(RuuNRaE#;V_&a{rwDnq0GWzpfTalmGq%z2B3^+!Op3BAB!a;WEUl6HDJ3hP1YG!T*EBo%1$vO3nlULSl zMCSe{MAO{Rp66O{lA)Q%(e{wJ%`hzGxohxux-hG02PY!bGXk_1oswfA=FzbRQ=_VS zV$2-pEqtalBWRBWa_xcI;jrJMj#w`t4}PbZRgr^cDmswz6|r-5l%B`$?A~0EbD;AE zr)Ar{fWL{qs4jsP9-Q*%URT{n*7QTB=C}mMJ-C#utDCiA?yA&#cw6g&qf-yNAn%ktsvEOpj`0fNw)1M9sQG4bd6@zSI}}T z9a9{?nz8vLwclFt4cmSdg*()mR)Tau9?lzUpjYywQ%|+2lBke)_Pw?rW3ZPP{xs)T zS+$5cmk14tBA|xR%euTXqpoOq{b{+kUTQwti)*OF-B3CHl@6w|JnUE|r^P78`{_SK zpESfL8)SJUV0R7{$%$&E*1Aze_nk{DA$yR5n;sU{oT`^*p=YkGYhedD)WY^|sN_kG zi$nnSg39^Nj}DOOSMjl^MM2CQ2@_?z(V;eYa#4^%{%LIa+#aO8oVAUMFwW*_misrD ztyN9EXJq9|z#MJi0^#!;F(9PwKeVuJIkbF5>&Q2Dauuq6hwMfeWM+U>OF3nm{A7N81jg2ZDJu(a{CqJbs28gPgckQT) zf+-EHEYgBtZ4|sxrxe=;j*wa!H*}+l1`^V7CJt#*=bYX-OWAbAcrHKhP)Y1L7M7wv% zF4L|~I3l^_0x^(dkTx`h+&S><_;Z%Su~E2oiw4s9MDeVlV47tdZY3IU|g_UJxvH<{71#G^EN}Uxz-32R66X{VTFk-IZeddM?L#R zYrHmbc0Q-e^+K7bygLf7psf|UN9>m0{oUl}xTfCcU`uclA3P@(@sULpF$Ck6+BnCr z7Br^R6H+7rT?_;^TAsr@OodsiA!H#NxXA-dUW+M0e;@fVf97CYApVyfeEBTy#b@IP zPJW+<+g6|l^gCvlx7sr7$~Sk-yIVJSZRO4{xa&d1&;9hm)A@sy2ho_}>648P(aG@S z#6X4alG;;?!KS&2JOrJ=f#ID$S+}5$wbpgFAjo>|>JfSDEX#eX4hzej<`jJqCz#?c ze`5z0NN#=uqc>dVTy-tZ-o)V#`>Snv2*E-sC6ggV`oDpPZwd5w6n1W;yi>m zm9;f<`w2)MleATx2MI1ucQeB*X0P>$29S0y2n~q{iKrntpz)%1R!&n*^L*D3gq$d=`fy$Rb8Z;&YzduV z*iUF89a7#!SZ{cQ0)khPlwJe9&ZCOTkQMBcN#0H*P4c< z(`;s;7EZ&E)e9P$W$hk@fc?p_m#<5zAJK=O7Fn@VD){<*a&ZmC{|J6)RFcG`<1N{2)PuvPUJfIIbc&cZh4#x#(Mb8vxRAJ#xu8S;TcGb2(KI#6 znDg#RkwVRS1FEMDKga;%?2eL~pqTS6VB}FhpABNjLsu!clR_+Y8tBW%=|jU>#Ki}A zh<+#T*SS{49xhqV!L(n2ekzjIH}Wyw#y+!gKZ^#kI??wVbtjcMrc7z?!muE2U}yAc zYtj%Lu{S@h!VvxE?q5|hr2!dv{YVi*#)X_5@R5}PivkzBw|b}_$?GcOL*AINN#S3_ zIwCl0zAg48CTh51V>WV@8SS(nm~)GDqLX!Mw|E;!C=(fjh1w_tG4ZXO_Fr~~zWEcm zvQ`{2CSQ-5Ub7W3aRXZE%;KhnOE3rw_Svzs`LxNrlh`&@$hr?iH zreaQuEkTkgyomm!x=dVy1(xvZhLNQ*NMU4|92dNIjS6IO8S29XhXpir`IS7E@B884 z>cE#pF`Oeza?6T%I!x)E8K#PzRJ?t7CU;79Lh(Y>FWbHcFXCEHA+t#U0I}Z_if4ow806=VE8M6GN4& zt^D41KRmOe)(OItSNu4y>+sg%o@5qsOLwty3jcmWtV%GoZQp3QHb(kHCA$=SK zPpTgelmU4c3O;ScM^-s?xZ+e1iP{emd<(p~g5JIsX_0nU5-QVC?b`Xw#Hu8~{@tb* zh~79w^H;`xq>FA%qC067u)VXocXdiRP>9mq<5nph5tU~!P2^`0xC}BuIuUp?48cfE zC-9im7I0du(>3@q3`hx=MSy<=mlsj1DjsO>8Sle5PuTz$;Ng^@7ENL#lc}honM%Qo z6fIJ7MWn$cag_|^C{9LT*ccqhNCtpt$(#n6-*E`;A>kM^s?kl|jp0VM~Y%6v5 z&;2!e>@?ipE+Rk-&NhNIrYto<|Ke*1kB8Aee22d8QF+7|sxgBx!GxP1 zK$qV)P{lJ|J-(zJOhIuhHgXCa9FAu=%56}^b}HQp-mR$742pVbRBL0XJqP1jVTh@j z7D8`M%CvM4ze0a+9hy2`YlC~QZDEZ{zl1GbAfLth6s1GE`&a|dEpZ~^6~Xo z@cVk}rPt?f&x26E*Pq?vcv}bAtNz^B#_M=wGf?HfTJ*r@pGLB63OU#Q#{yIh8-E*tYBH_wfVgrhR^CL6L|KO#QZI0@C8`e72CuqcH z7wT&suFQ8;1#=#;t3kwz$Oy7ZKvH2@vw?1>0uoC8odJ`H=V2;;! z4K8-HMI@PUGd{ym5W`7mb1g7QpQwy~M6h6JZX3%y#k)o|3tbwqWfi5f^*Ju|KX|$Z z#(x?0$_Ee!EVat{U|b}?OH!cRrx5yjel3aru#-%oz7dof6+FPuPKG8HYKy)F?}=jp zLyu%r(JGlEGS!)#8i4>ccXZ($rs9q33n^0rvq9SSydT1+g^WQ9N|M3H7MgdZy4HSq z{B$amSME$la`uY$a=Dm9*s_gz$8?D}9)Cq+2bkkt)Vl3eV?nUMZODK5BCTrDS1{Z& z_wOsKJI8+58R~mR2>rK)1_bF0^)AhRS*e{@8~)p%Lmp=uFS5T?o(bQ`uXMjJEt{M% zzLp1-_PE`YlO?C;3H^>dc*QNPmhl+~owpYBpGlM@m&H=dGcJuB`DSAuR_-F^rF2}< z-8gatA8u`Qj`$cm?V{zhucFC{L569Pnxy2!-A~wy)uK31ll5>B79O6bWh4aGgUDZG z%s8kU!!iN`@DB_V5*RT+*^1Dn(Q#2vHn9Xz!CA8fh)M%PE&OGp!g!UXF2@G9d!xSDYd+ls?5 zVwchDK?bppg#(z6eF;mq(NH;$FB0VVVJ0w(L@9)+@lAbk_w->3jv1U_2)FXmmN=cociy-6#hhQU#n!e zjU}HG3eby#p=c7o6X^u}2!!ZdG*#GB(D^|Zc{+&c=uwUD@qTW;lHMdCgr}`FwyF7&ZqDG~IZ3=@McEoZ z#=jFaUDw~AbDE{CZ`krUv!U6!zoK=|KyC;0+;svY@D8fi4aa8=$yvG=kWq_q-L|2> z@UmQD?1Sg+3;)w+%ubH*uszosXQ>pmin&qP>H5A!eyoOL z%5MB?tjsQV0Fs5>ZhW{9a+Nl@MPa9{DDplIoiD=pCopy_c%8j3%*t3|*eDp;TNT@4 zd`yLtbgqFYmgY)GL1ma~6=1S@d(lSS_Xvr(CY+3_2cwmTlLeE4pD(LlPS1gvqp{$o z*D5?TGUEnkDv~Y_#nKg}IZ;`4-Uh|Bpd|il7nytd$gSn2zfTn;`4b``GUZuZdpHLn z23aHU0rhQ;qGO<+pKI$E90s%v7Q5IhA*XEWHUs+ap@2QEjNTLx41T1JLkv9k-w#&W z$H*V+ud8Us-ZO4fc@_j9mXZOsHk}R%Fhv9lk$Upz%v_qD(=f%zvxfMywrihWb@_pR zB^~s|D77|%t zC3PMe6^KrwIx>udoN4oFsAKS}&mZQM4S@y%JXnQR(tHNnJ`wXmv|h|?B=;inV}?ss z4rK!?UaJ2a3s<~AKIPs4XP1pxu|wHC5nkc#R}Xjc426&jidel5e{c~TtR zgA50q>5-f}-x9 z*cJ7ET$$y1{&I}v_$5%-ws8|qY>)nys|r5U8?u#&w6R&`+9rHL=F#Hyei_{9Z=7eVBr)=;1G@C(&;;N%HZtF zKp6l79>UhIXMGt|(Z}fEJTFW&ijk6N;bZwCEn;#@v2tOoMihxtnY|v{vZUGXoi{g? zq5z`)d(o0TY8tB$L{>0;H$r_nL;jq&5rg3C2RoWlc9}2INqr9<`-O3i^@k2UnLPt# z5p~FnKZ&eZ4zAtJ0=3}pMx?@SFSWqgbc}!TnF0~usTrpXPj-JGz=CpgcGgd7nET@e zYAAH7E;njcKoRbfwWUz_LGE#?Eb&Ik)xPIdK9;-^AB~i!NOzwA01!D9;nbRcu1pq< z|056dEOcyB*ErHAvJF2k`PONlaEFDPu1`7c82A!_M!9-|Z`+%@JnaBQW1PTG`KYT2 z8PTGHfQBd6D^e#crsi3jxGo3}^oIi3VlJ+ECWE5{#M%@y<6#;)XnU(XAl>mv_&T-? zUz@PR@+(hRry5&^42PsJ3(taI5IP#*FZ83~8eqtQqh-7Xy-Vo0b!sbdgA9!{O~vI? z>Yesy#j@r&-dMXxL_;>nG?xL=OK-`ZX{O1qt{SJjk}5l6Y2uAv6EoUqbicFy;DA zyw_h%-2pd>zyXgOA{Dj(;xt5txokfV_}x|#V&m*q9%}Ia`1;1+(3)-A*tTuk&W>%{ zJGO1xwr$(CogF*bF<#Dnx9Yv`T&$|~v#YvhPmG=z{j19U^y>Vc<`hBWJ34(>d}yP? zZxcCnMi-)?!4-qy`R@V($v#D7XB;1w!ZR`StmQd#LIXDiggRS&e73GQ?)C3`0jFCT z+u)~XC&yOI-z~9mle&0z4&R6gz~}fVHWjg*Wj=dXY=ok_>MRn4vCVmk^xsiqb>lBz zrLAOqrTRO#6>X&~CB1~!?73od>G%VV@&k*BlYZhxU|Eq9(vfL3ca&T$G9bW?xm}j+ z`-U7di5^<_mjuV{e+<0TOhC|=ED^B=A!186XeYkF_~4Y#sO1|+R?k_*>qV$n_n=j& z(_`6*+zZqwHKu`8fWj*_2kDzfQ*Uk&F!H!Cisd%;v>Va1JX4&v0DOBumnK3eY-VKd zm-(5y?9An5&F8tb)eURts0v3>=fg@Q?CmGo^W9fIkM~bqHaMTkU|7?Phq{Ym8@IDq_YhfT$Msh@ZL<+ zxl9wN?U0Ss_BfIEC;nvc(tz}JBgTf7?P@rfFB;S2)CGG}Dv=K}Te`KYR+vTyQq*BHPZJSFJK_V_4aj|9o@xP^$zma-ITS zz=S0ks@(Bsn{??p14?nq+quO4tp6aAFwvAh{f9N>VWeug7{z4~c`2aeNqUT0!=3|2 zfz|~cCV6Gi!VH}s()%EN2Q*UxaQw)JXJz{RP}+CKeYt$SfY61ts1uq%f7IQPrs- z^S6qgU2uB~cu;sJcjSO1<>d)+NN5DmnXve9BXKZ|KAR=X&%6iZz&dr`+$?UAAO)U0 zUcTg;#>4(m#Nv}8CE`yo_JT+9gB%B?ZvvGn%?fg%8O9Z0aa)Vqt4pprxfx^D^P8o) zG*bl7O3PIDn)mBz1wadeWph7o)gjd=EmV5kN%-fccF_?n65i)+Vyj$}$|3c6=}GdO z3?10iueAPtiXYw_=3=naX0v5ef8&_-Z~?)&Ysr$M>!?v6m&;{q0K8}guc|1rVSjml zSzrBrT3*y06(ib}==lR_W==r8kn*nZnJiFtmX`oYNl#mEJ;@VgoP2p2pY%J~5C?>@+UYH~H& zecXieKW)S_shP#aK=|M>cQKPquz<_w(ZNk^`~wi~@f~U#E7$A$5MzU}M~hrU2ZHSt zRsaqb+3r@a;Leic@_?=G72<`(`68TSxGvnx{tzEHkbx0Wqf3ZtqsS3kJWExF=Sz>( zup7@QZT!9=Oo!!cwS1-2)kfFnBU-3#58@bie*d>gw*u=2ZOcR4EmE7Bwftl zek{1lbU+8n(&ev>R3(Hncbwn!o*Z0!i}x5b-_}e?NErcg;dkd1^hhJ%zCDj=MgPgB zB4%*gN-6m@YItnan!6cvPXRhbsK2o{d@<8hh%g1Es&0~mWNWpSYx|s@qE4= zUVcub7JlC6oUV6lL$#K!bL2qpL)-H;LOE4f_%lyV-hucr{I29lvdF*SGBovUYuDHK zwR3ekUKf1*OW8wU)u2KK6@b= z;&z0FlxKGgq0!}`&tiAeM-=mN&TVV`;|us{uGSuP@w07 zObJ<4C2$6T+Mh83&_?gTBQc^ZbI(S>ZUlQT1PMWRcklAlUkdy2tf8Ko)TiGWNz?1P zS8zxdZ(zU}c5l!F>{)LBSh8d1)KhE9nRaygYh)Td(9SV2gidJGLy6bhwiY{Y-?}aF z5c@%qeKm&-4~@0csFoeOE5sJ#*RZRIdd$=@ZpbatrG`T5lP=hjAr{}*Dc*D9sRv^= zoya-C@;ClJ>%jBp^lrdV001G_002MbU;sxGCuch+TU#qfdLvtFYXci&dJ7vzCj+a0 zk>X@)SUPUDp?+TL`6u(sJCIDswq`3UHOpid$#8EQyDZwc@L*vP5kg`k83WG6xNLs! zIQF9ptV@t##gZcSdFQ+bxOC8{CtUv4vWzE@MpG%LJ(NsN(bAxDbXnosJo(e|PLo{z z>vw0Z%FI>G((roBSg}ctQ9RtJSEdU~t$WUcHC=|xle$Fn&C1T_bx_B97U@TFe5p*NTG{^1IDvjwPsR$>xKw z+(B?zlX!LkKU?PU&hPg!(neCY@U>?lc=$HV9uf<_wJ1EHq^%(f4HRt?MjA5%2rk9n z>kD`-kkwMwUM5=)Cm!Dksse84SeDKsw_Ij7a7d#9%74UEvEvyzg6ta_)#Ct9XsA_| zHp8__EKN)Z3BVKC0;DNN1yf+z46^^=B#yuYWEw44%p*bP2r_?nHinKxo0H(u*R#IY zC?8y2(japa*wg!QS0)-)xDl$t>||Ao_e(x__p3=K1FU7w#b8wlZmxMuli@B)42J3E zIylilS(JXNddS#-+x>;R7~2`Z0P_dD)XGP7oj55#sDMOSn|4TVln}Uw%v=Muk}-3v zFC4RvQE*nk{kB8>&Pnee!X8Os@#M-(B-{aDL;Bb^%Q3NI zaWk52jjXI3{-YviyyCkcFG&sF3g=@Z;|S@>-zw7u0bX}zqnMBX2X{ROHDC~Yv;GtRhUBqB%siwv|IeI+ynjH?j~@0@2qQc4ni;|MO8Xq!0a9_`?{x~ z?MJp8QIi~6zf_TG0Bm!ia=Hv|yV>=lY`y`%l%^7l#;_Gzvc~>Dgb2jIKb2v?Y8Z9e zp;0i0SNnN{*#ZRG7^yImRNxjd)0mNnf=2qy6{cB%6cUMMfC2R;;kOtJhtxZPHKrIKs8sJs9>X+)PbueZr*t^XX~7ZsBjB`Hvh}a+rOhH8X4+)ml`>5HpfW5) zB3hLZ09XP&F3)7Ncv_vbcxOri?5Q1l+IGoc;-VpMIrrt%s!SZBZI0jUeyd%z-t6oY z)|u^%Cn2A!3OiP8b*1pdbi9hJ@xRr8L^3tEil}dkEKK*T92G!NZeJ>NMKbMRi8##+T*!liKdlA~Y z?Nl^XZVQ9%?7j-y*%88yYj|aZo9PB)?r&_5>^N7yD~9%Se`rS)F|Y6^^~(z!R9D7a zP1msE6j4fK8*vB;z$Q%4Jh>R}-Ha!pG%&9PVGiV%#8mU922}&a-()4_4Sdi9}A`b@AG9Ig@Fq ziHxPU$?bSrG*|;DJTJ1AXAl^41CY|X$Fy-s9r}G70!EaOf(&xh)=>MNJ8K!7-Vx~& zX|F1WbV-&Q1St7wG;tq&@>ZtQr?I(@JncZB0L!@GK5xBqc))t_jfxjU+7Jj1v)bp1 zr2+Ems1-$m#{AKue4uEQO(+jPFERTGm9&Bxjm)-pJKLYZ%e(aktPw$E6!IZKuX5lVRrYGCtUNkt>LCOK7)G-GJ!i3a2HL$zPG!7_T9`iL+68SJ|07WMyx+ayOx*4W!T(Hm)jN~`iB0N z5i75j2vrVA(4qDTolhPpFTIO6af)$j7fTMqe{6T^-{}j02nJ2k4PGZUlCJUWp{wC> zFf8)z9llO%eEBqg_+&@n`#@tskFav}?3OyW zOnCbXr9{EC79dY7J~c!0COWKYbKC%9ot#cxBFs&V+I9A{#l4<^)A@|^yW8eW&?nJ&D#3+X^EQZ1MJo1tq#s^|@vXhw8~RNL z!7lAa^)#%@*&_5zya3A)gmr1^!>l80qv+PTux-C1J0;G<`_TKe!PR}1FYYa-?jSwH z$>rz!vo4T83`&Rx1#wX4LqNaPJ2xc7oHt{$Gu>4-uanx^?xezbVTI(41Iq&_c$pvS z`t+ZXznl%Z7lw8Gn|0)$uHQRXm*4+Hj~}K{UxdH_0N4-!Kh%H!8+z0;`G4Jn$tp6o zSquo>57eT+5c3+2a;UnGmZadTm!wb#!NFFd>*Gg{3KhOQM``dwY>1PEQ8piTy6&)N zCu-PN{PQV(SDpFI!FDr?B91N2SD$g^U*I&I#(5|ZUI{}RR2F&-IM^vpw+T948gb&v zJc=kh4d9+&EU~E5SX$IOjP%GVG?Sc^qru`ZIGp53V~Z#hgWTnlktb2rTT7K=C)Pkk z)|N6iy=n`%-~#&EJ*ioJ?uce`A}yhIQp8vwBfvqey;@r1O(1Pb)Wn zfBf_jM}>2KvlrSIn-}p31BB8WZN-Fb(}Pbll-KJbIHDFOz*qU|IP&Q!w@oz!BbsP9 zvqEE)Yn9|QFPS(u&_|nP9-=-IKy;r)Ssk<@ODTAC!RN>=UV7V8?5G%kX}P}yWjk6G zX4OrND}+Gp_6*Y#J9qTXdc`MN@0;3v?ww+xJpaYz^t`Et?`jPEx~+NjY7a3^NvqN@ zZlp*Yl3WmQ9)Z;iWTS&nP~^8UT=6O_I%`Hn;~uQt&=_Li3Ek*Pp*c6*%0lzcq(TUy z*JkmKv*zhXuL29DECjpyV}BEb$?OBY+x@fUuzjV|$zwdP{QL^*n$IqY*##?<=*Wr3 z+AlP9RBDR@EnQDiTS4URck(whgr3p$*VfjUau%)3KG&-y%J)=D_XW*vixO0J%Xc+! z9X~bhnF427cfVDsg?$4og(d+WTHc8oVH;Ad`rRhitANsA2Xp_weP=*$$@4Q<%`k#L zBPQ4b=`jO>1Im!%K>0bt)K{kXSb?B-bRsHO8Gs&$3D^TtmnP48IIJ3(&0C=%S zvc}vwoW}yZstxhWW6D+qA6Cb29(WmXD33+8A66ZouZL9Ys+_E-(DZ9u7pEE>M(6pM z4B*sH)f|lWOS`9iJ@#uM@A0;Qur-iF;AFK~MmIT?7CC!vEdKtSk)wbxl*-wcTVx@LAOp zB%vT_jq$sl7gz+eTrF)LUbrTO0MRV4Vnd;fQ`Y`lyNgR1o@89Q4MS2l{yu!u!;NQI zjird#fd%cW4dyECm4dz)|sUEUL;ZDLaB+gfBVr!b!)W zBodN>ut7*qOj}{W?43b@t_J`S1}&Dj5tiZo$d%#O#-?dy>_^8b zUdjotiF@Fw58k69$<`$z%Npo(?uXCd2t<&&;Dk1NSuO_X{Hs9`$XC;03ut?!q^wa{ z7@P~_V9IBZ?>6ZyZbTdxI|k~ER9mm6$6jUKCd2yl*urZ;g?MSSYtM>K=JPK~p=Ai) zckjCZ*(pInPC|7HcdWgK{OYV)ufh^i?Z#LS;lG>^uh)%7-7DjOB#{UI2-X4Jhkilx zWTpV<=mGpa1uO!i@%BD4BNpyyZt?_LusIHqP}d0K zf?3W@h&FMew5e!~>k}Kg)fBKvGrb7wE^>Y1&rh?jNam{AOL=YSelCr=s&&7Bi6`*3 zt^ZDo4HjJrRX2?ky|!OZPvGR~DFyPFKy&U3T~XAm_?q+k5^hN^l0H zh5KX3)G<~Z zW_JG(1TDx2S3efnXHdG$j6HeU+F{e3=?-r{E{MMX3MgCgxF?i1M%IxddOLbEO9z_w z*31iaLJqf5Qz_kb`N~e0k195)&jS*&tK{-i@N!gef1i$@LbHB=>tPuVTZp>_cw)+q zC?@{NH16xqo9yNm+-ZGJ3~EeN!qKf6m?gRDfII*OTi|xRr*LodY4zUFl^+Djrq6}b zq0KFS`j)8+sjA;I$WN3DOsBUS7(g*YApn|c*EBhemC0b$B_=$XsiWGm>F2{#ncllVxY z>7(x=tK(T`W9oRsBu=P5ThEgL%t7r&-}98jP@l);mZhvOZxh>U(L}yXjI8!SpVm>2 zv8l~mn3~v$ClOmF(C7$yD7yqp2+QeXQoIiM(Q-_>oa^~vb4|_lZw-dY$Zn?ei?l%* z*uh`)zcOL32aqr*tycDXKeG5bx-(Uclo?=Z-6rHwSL@Y8Qbf)s4|!i*4`VIzi~v&g zqzIj_7Ph3ye1ZNm0sw?ccklkdESaA~9qIob0UV7SEbN>df1*IJs*LR+8$$QHnlvg6 zjWudPD+=J2lDs}VE!t^*C;@?ac*A zGJcJ5&N2g*mB6_TxBcXKn^vu-s7Pwp{_PkZ%)`RllT)&0;xtu_bvGmW`7)Imu}pMr zE$1u+!wTjHEmWcXbjew3xUY*#E1NhUV z;i>zOqCktR{^=v`WvSK!i6}y`*~^@KF|OLNQ&?gj@AnvM1Y&SEzm?O^4BE8U9bJ7E z$@erow~!%>3IL~R`1Ic4(iSfC*ipMbZDh8|O})-IL*i3-dm>K2yeEmE{y(kEZ}$uHQ^gTP^t`Sdb1ms{aQf2C1QxG8rcdoem%iAUSNgBaT?pyMOBG(?L}T`4xf~Q}U!OI>?_e zxGS@67HX_Z4{IvBa0`ObQfa_Y?FB}I9aVWN0-L)GX_jea>tvawLnvm~XT6(832F_{|WbaNf{cN1D3>@hR6shDtUp4BGjaVsTv>0s>ene<4 zIjn*MEeYV^$6%Ew5I35esExw3VTP2nt`3uoC|cNUhhy!Yq5WO2sbL*G;0qC0l)8u& z5ewu4({!HvL`X+Xyty!=iYTVp^WtOTk%9rKHyKeL$mGPvXs5$q>m>^veJL#uMnDvv=YM<}}a9aM= zE@pw&l@hMQ_R^z#Z#2lkH9B7RIeyV=*#3EI-S(ftXBx^UrTKBskw3R!oc~_<4z^AP zP9{HIIay`0A(I1P`vFx)xj2qY9pA1pYuY$v{IbEmodPDhx}AVALHxkWyJL7#+Y#Ns zAJ+Tr@oXjDJnJftB7+bHE)vLTRSP|jWh8{j+|nyS`ld<_OElE8cG_9=YddAfms*Dj zELjt%FME+$H!HFC2_Z2Yz8|Lc9MP``LNtG5r?#c7_6U`|;T+j60dsJ=JYYZKV zH>Qn5f-(&PPHUhc($D)gdDJqCvulO|b=J{z_nh9ChPx=zL=~cXoVb>QhzoDqQ8>DY zhgLlS{rIQAEFB`bq8a^#)K*1zLyi&juSgndA?Ger4r5#$#Uwm=#L9ID!UQ;9{N|y} z(k5SVV}IbAT^L)BwLSW%)LldI*_|NvL$u=MxwaDOPQ`C0Kuk~RmeQ<87Ph9@a(~&6 zOxfdwI@PxfFHFJq(DZOLCwUVEJfwH>%`#gHVB{qh3 zMc35?VC}#*jBwT^Sv9RwLWIg8wxX>}8(ClN2E95IKeb)R)c1?zdSi}3_l!!iyAn{< z!Es4V5>v4q^&9DN-J8*tg6h%4@g+hktAVD!6~9M?GX<|&lC_GzE=l2x<+e1a^mq`z zc{C3Qp>KIlezu`)fYkHUa=tVU@b$Ge;4-KNFAU;~^WYHqj!M#ti=m?rP8l|6nlw zoya=19ksIg)Py??!&&og$A`a^?7?CZDl{NA_BCS~Uz3i(Rdqu0o zots#^CBskKuG|Wpan8mRYO0OFXC!+1%Bkgku6H>{7233v!zIop_$E(@S1QiGgh<(? z4L=UgH=Qp+Ocbged~+VtTk7oC)rBa2ApDj0{pSoreR)W3jVM1}PTqmnd%CpQ;WIl2+`EqkMCJ(?*xW+rL>El0D3>=8ubO`f+jp z!^Enifs2Xl|3yD0iv2c&1TftXsJbrZM8&UulH25$3q~p;Nog?Q0znd`78|{@b0jh< za1iUWKi}ydI6>4RMtAEH1wYJ!Gi>MwMn6m;JYpSly~>Wo;%cA$p^j&AkPw6kDPmj~ z;b{35No61apNK*h2Q>4;4pt~LxZop!Aa-OF66OwJxxNEdj68{9o&`>Izl~N3E4$?L z9`MUcFyJ-NjT3Zw2t#|q+&R<}{_Pp=j>(^<{4p8D7Kt1E8l~TLKy4B%HKoA9Dm=^u zJ6$gw+HGIYa#g4q%Vqh%hfMDWX57@ikFukuQM`Bttr%^dqD6y7^w~v^ibjr2|EQ%&bW$%CPJfl`2ZH_A84e29hiCtYilQCVw|_M3_X7G^PdxG!5<| zwzyLG5IX6}GhZ#)%DEgDkI*CC!?wMuyGqEaXU73Ak}GrAQG7IYO-a81|LOa>OLJNw zeqia$kK_9(A^&&vV@G3)pJ4N^>anNo<{0AlZmyx6s321Ex-PtTpI<~tI5I}tTJRrj zXc9gTk(#1tO1k>arfR8&ZEohy$y5sRGQTw@PVSpgTQi!2kR*gs5unKJr?#2qOuE6*2z>d`boC!Ra@MhwI+t@EPe<}n~Ki^e|CyBadYi#Lub#%MmIJJCUPA)E< zZ}fD!HCjpt$T>7KSkyXkYa^Br9BtAT91byBe)*e~vyqc)5UzAlT9*tP**K4*Re=m7 zCL+7_=eWHh>U}JUX6{%U*BvL~zIxwRLeF>RDa@M5M9g>)% z2i$gy0wX3LIt4t5mA(2C69}A@c$uOV!UR(^zGK5!R#HAMQlMY?S;RS1#wcV~QPKp| z>ut3hNzC9pTCMTR(WRJ*(Tw>!C`(_t&`}T(QqYVs_XLP+F^t@f;|_b0D8 z7YuHba|Lr+==lqZ7h2poUWrbVZX(`10>R`pzm`}a=%X&{SbXU8%~%00v)jXKxQXLQ zwbJ$J;Y#)!`i98wEnRI|`r6tQbwxxJ`J~@QXRY2Yy{;lotK5M}d9X!iTn=(Q0!H6h z?I90VjP`JC2jN<_LGz{#rWY=Ys1aGt$Ynv_f^2r5-L@6P!t!P2d^$Wv2aXl8?{e|Vo z<}Y6Y$KKM{SD|vvY`SPL!j4vcL#b`>GrCTXA_^sVbxDCuC^dr&d&}7m31{0=`Dbwy z?E9^QeaF#Z#*H@@f~fmzbI%Lg2!9Hn-WtBpKRfp4$T~m8PDv|Vcb2^&oub|&9_dy^5d?@n7DkP(Eft{LzWo_`|)U_%0 z-Zmf#OIf-(GC`<3jZNy#Pyw$8IY6KwgwmA*fJGxIBnj5m%=4TSv|I-G7y$Dt?@{mv zNb5-G2U3ZV==)T*uq16JrX+apm_$B_r@D7h09Em1O!(CW#UwfjaGxt_e4B{6xh>gp zr3-7SA$2(|z!&4?Umq1x|FEEiAjm7q?V z!9{zC?a90FHw(@Ns;1d++6{f0l}3QUMOiMmH|+Qt8G<%Yxc4hkP&Tp5b)*Y0%>3=* zNawhU3Z*->*b*LGE%;`+m5%AKCL$H3bCOz$beRb=T`6x*9PyjAFTmpZolS-O0EhHG z_8ToNyFqVu24c%FJI`)mJVzp<$;=;pPy1AY6z&i2=0M^CE&k3*(}jR+2rRHRuvAE4Fn~@^xB89?#;1aVOO0yo z?2Ts~Th$Lh@BgSc!Xgt536$P_7T~iBKD|0JwOp6%QwL;_#U@tfPwQlc>l2n6(FAi| zng~!5#~$$cz1^R^0?tx!ebYbEK>17c;RCP)pVPv=5*u)2L{2N+mQlH79zYnR1y}v8 zvgbG7Mvh#6*WO+T$yLcpwc&@tctFYlCj|bw*X)!ktGt3fDOiG)fA+qFltaxWSX}0j zaiGyzcR=Cl!3I<(LskjIH{h{H`?obljuX?7W?Zp#@7Wfs4bq|ZIUdegf@)aSE02d zs>GPC<%c=IxU`&nUa;=rvHj#tZ(6!0RgbYU<;=(M3#y#I4tNGwNnpC9;U`#R04E>wyFGfz}xLGvvVNcQkbU>V!V8vGM%80FE0pS?*uLioZ3WTfK#Y?XlIKd5Tb$? zysT`%wOXhe=++d%xgtew_$!mid>Ox;Q(uID`( z+X6siSjUb{7Np;Og?2;_3cuv?cOYgvn<4ZixIK`0-7 z&o#D!t;{X*^xF5Ws6~LZPm+5Q^80Y7Krp!}I17H|EC(LDiq|a24q}wxzpauRu z|0_aYA@({LP+g#;R)0fRLuaGAt=mNyhII`^ND!f;v&*C){$3v8!EQy6_xF4#!x8v~ zQUWL%8Ts&$2m=RJ`oRJgZ{(<{BG$6r)AN2#pHRs|d@|NmTsAfgh`<4otIhSdTE)0Q zEO6AlS;!M%6Ltmu=ghB0PHfR8U~zvJdmgvImhO!~3Ws_D{Ts?rXRpoYgBnJ*DCMX9 zr>pf{#TJx+?61es+=c%BKt?&R4wy5L(%>-_Ko#*W(a8MVCw!`K7TYif!%Ohld2{vI z9)ex`N3{x2!%1<7ekt|rslci`{teS{Ky(&q)o2WN0UN5Wr90PzvS66MVz+^PB(m0>61_3=USM%*6-9&T{w=V|bg)U{V$UEPE0!S%V8V7PP z0Dw#6|MBwt)06mv>i?s#r4^gWf#Ne;Tc;lF$0mQJ#i~dZy>ZQVB~sn=&VYpoE`30& zZ!ElMwfyzOJBrSbT6A^nk7<=VyW9Tq;J;cOhdVB!bZ;b8Z0ImJohF|tLn$J&khGS+ zT2N!5)VANV-%g>v;@YKXBa@OkD$S(XXZOm){cxR>+*AR_@?B@Y2CA0b!X!Uf30yv8 z&z8tU8H|J0Wq;_~go{3{m6fHnw^$7h&d;=NLVG2fDU$-2PeaUokAbz?n4m_pl@N#8 zH93XR0|lFlTei>`;mm-Hm4xBR9}HrS%MRyJ zPZtSK(3)CHTtQ>R%uMHje+r03OipB#^Nn@Pv%G$if(?@Tw0{WD9MCz z$PQ;X`(RjgYBW~jLLIdFsPyZ&NG#BIuqBw*H+GfPv1hoTeJt@LV z47(Ubh>$MRI(w&{dGVUMsBM3gycT?Sk$O@`i8wig?R^D3#yWRecXir+wjJmS=GW_KLltY_&)mGLAdV~daK$nsbo~db(X_JC@g+A&< zT8;CHlQxr~Oe@xnm(V@3Eb5UUY`y>tgtbq>G@?0xV4%8Rr=KKuXzK4SaI>%)*sK(* zrfl6b*%)vLS3lBJRJxYXtv*z*#S-+P3b~_obvLyJ$@xOr?1UE-%7n=hZ9Su zKzvAVK_4go6L6sYiGAhy+JGT;c zAt+zq%%S&~jz+Fx1zpm`-A8|;4j;w`Epk;Uff(`5wCmrOpeWpe9mc@uZ1e1tlhdaR z_AKUg zQPeoP*s~jKZ43enw%Y5AY&}fV=|+A;@C!p-D|?i#7~Y6$yPD@f8zD~rGz@(htKZm= z47dvX^bMiDU}qaSZ|X=FXs_5PudB>CqaR=IN>hKOke6d?Z?e7nln~&MVYn(6vKA=VN`T5)sfCc=Zu%VrD~!z>PA>@haBrZS-mOzW#F zF;`S)E4U$><8-w!m)gz|!W9vDc9R=7PDolJ&!lF=#+PgiS%AdFPv*m&c%7TnhD)fn~SJ(%)0&v53lL#C{xlyeSI@Be}D+ zwTt=NHTUD)cHBy{E6_wztL&TTep76fY~&7T!SRn6fU`nXA8gK`wLiI&yAh2YxeM|y z5R8;>2<}BZxiggA*lXgVJrrKH&cadvYvZ(_C1vhZrn)`!~ zR8NJgX|mQ+$q`b;KBsC(e_{QGHzb8Q3d!%cdVv&oPb`K0)aK6ek;ldiz*8p1x?BHM z4$>Z41(9sqqAw+YNrybxsDfR6KEsmGod)T9HrzJNrcNZ#R?TrA8a zf&0Sb$?5Opt&_qt(#An%5e~lxdg!%)Nsjs=4HtwyU>uk`)`qV54(DI@rJ_tMpY^f2 zxvIRw)wZC{2#g|g6KG)Q$rag~Oucfz-;9iAzB}gsXMho(mostu$ry-U>(|ceVmB-7uL`(>x*H>t8s>f>THM*Yzq2KlxozOyTlKFx z8;C?w?+uV{c?H;=+9{Q0$W_9)S%6XktyrcNAFkVFt@M^u)Ggcjc89@o<3pZ=y*WB+_xse;{#p*@%c%kv_N4rL<1EI<|El&c5F>g#nRc zdkHq`=GaVFoV07%=9~5MKMFm#mpC7W{WZ(RyT4#wy{3UG1_*RG0H2z>v^N!FO z9&#k=wq5uc`58_JIU`JR>?U-|j#PfvAvG|N0Vx?4A&lGk7pN)@?xmX~%3B&BNb_$lt`pZ}Io`%zJVA9ekY@4rrD*cmv{>;2I3SvcwG{irN3z(18;4~F3N$;%ub`1z<*YpOu2%L=7yjsI1V)#{^2+oq)!zfY{YE0KB#PdKytJ#0yD4YsQvI>zIL0F)+1o1uL*H z<8P zrypLCv4@;vA&bjDoOzUPa>u}9edt_rR$W$eWKy*SB)ivyHixu79Fn?8}-?9s(G?{Ctg~ zK!ow2O!Pw;Pw|9YkLKPJCrf}uvE{b%jB&%EmFrPfNB*wOZe9!yqq3l4)m`#lu}t(6 zH0K8-uz1MVSkBw&i)nlI8Tqq`+q3I$%GYC}Z(q)yERGkCHK|7PhHQYDUcEP7tb3)A zRZidABwIKBb#VOd@(BXX0tQUJ^F5mtZZVb&*#w0#B~FtK@zbmt(MQHu{>ml=h z3LWF$g>GYNVMZ@2rz{~XViGTBImiGreDjKO(sv3y~j_x zU7EYl#BIg5rk;}HLNkPtWmp6X@AOg3MScZ{Pr-X z8mGda(Q-PoF`w61eR9FOHr`20?6*)GBpB!NZv9!IYct%pnDmgfQNjsjcb06hd&>7a z9?9qTmlR!-<&{A}(5&-)>u`)~3?7nHu^v-A<^fPcv@RLGZ7QyBq9fM(0`!`WMJJJs zohkxba4e!n{pFih%kBY@4cLtOW!O?G4YLl27E;mKJ#S{`4mvspZlCuGiCEnnW~{>no_PlkGQDFuCKV89spaO2~Pkh?^fm8n_JpG5D|G)4QtSn=@^n<8Q)S?FRGpUT{ zBb17XsOBVb;^GACq_f--SUFsbWN5OkyD2WqDEzX=5mNw= zbpZsexT+%N;GTayiwTiUH*J3*XhJII+Iut*{DK zt~8p#C-pImmPjWmmTFg?{J?gq+??mUK3XZZJZ{9_$Gw?BBm4a3=P^!l_)Ir{tGKAp z0&FfbUqcbhbyak&`vbz+XmiHdZ)5o8@s(a6NtY^-xXMV4CPvtaB4i*+(d0425{9)L z5d#hcoSTTy;wHItEo#qbY^DZ+3aD;ydrH@6Lhh=doHUi!bO5)}{B7#lJE-n`WKp5Y z4CH#VJq8j9pjHXHitBRM7n%%Pv;cGc8Pq0mAt)pZ@k%u$TyWH+&T(CZC&|ew^5VvI zCjMLm=bklm&`a50*}dyG?MPIOHS4o;pXS|i4Ut_QHZX8=YUV*oEBvOa=C=M@0D`ai z+@oPAfyD=0@px>;W+J7C4no`h)$4yG$c%nC&)0gU6*tchQOEEc;XwR7Nt^tcvc>@^ zaX}kUGNhKDLQ0X`W`d7ThPoGkPn4-s$S)M;nGf1&+o+D(a+a0z2DM&jaBy~T;HddD zTw|7x+}6?~@v)Ul`!;7z|Ap>e538!aVzB=fe+-mF`uY#oT{oIFS2!8~K%Eo- z0OfxjDS8I>_SPPH_8#_*c9tea&dzo={}Yp4=J}1uN;vMmQIpMq2hkxtZ1imZ(uvTk zhbAfONs~qGR)*^YAaD}u1pNDjkqQs}NuZ@%@c93U%J2gXXA!Qto|d(A~JT60P@!^U@sAsFYW!{Ssr3IDESh zN$fmQ`h%T1FcA9qwR(C!d_T{|Tn4#xvpwEfUB0}AtxePo;%Ic0t| z)_^=+dt@q@Tz{=#GK?PBg0;;%{qe@=+Ix@}s5|3!kOtBQ)%4&5W5y2X1|p~fKx-S# z&_4pAkdBQC3UJISl4I&hU9x*-3A&Kj?_e5LGe{E z>Pu%>Ow1&962zvIl1L!qVAaifXAX>G%|2~~1(K7bIKGSUDj>WHru3Dtd|T0Cnt7+* z$HTqzgUw=^^A&CP*#Dmp{EIfj0;B1#cN~LP`D_WuM zrRVp9If4Y3vFiUgiZ{F==GGItX4ndXP+6?qnbfXkdUs^_;!^(AKMb{RiwonE-(Ctwgi^tY2#v44W( za8IR=VZ$`F4Bfnuh4#}VcuLXLB%CPT=q= z6ImfgJO7yp$WJ3spM(Yfu2i2Pf{CO4&i5QD>5g*?z~8DCWTn{Pg-LH_KGt=c&P8*N_A-qaYwz6C-KI%8_-+^GoBK#euRl1<1lY zqUnO>B=Wt2f54o-axtg^rGmY3Pc$VgVdZ4%T8JnO&7(WF5?vp3WT2fJC{Yq)u-7U% zy$`p(8wM4|rN`btOW$-5LMALnYud*q#0(J4wIjR`Qizg!m1uERvSic7N8cF?M_S^4>!2e}3=P=Y%oO5U^(6%B`|c=Hd4A?8m?A%*YcBFNcxV2KDol)TP>+PD;j3^stC zPxrg`+il`vxu56T+vCyu#lwNMqXSnyvw{RTmQM%}9C4D^T=E%=W3(sh!P@E|_k z=~m8JklSCF)kujll9V3WNY>^r-9n0$n)sz51n^*($l2smD=E{EqmV$zN1XC9VI`F? z!$mZf>-g+YC5#e2LF}jcCqhd_MF1d$Z($erVk$` z7m6652e$%-V0HS`OCr-xuZ;3=irHnMkH&!=!m%1pCVU{kKh%G`OmE_YUtRlu-8uRB z2a=@+ddQ|2>eh(>0bbD$vfG96e^%Y|7+g@j{dBv|s9^H~Gsx4^^S>`9>hZ~a&L+yI z_U3qZwXl-X6CO=aeq&x<4cnx%DVv}AK=-qW^S2D7jd@3((~`*OyE9nmEdB0@&-iH8 zctuhjpgt>y1_y#P@4@tctza`E2ZTZ=o*NhIfhvXKXLig^4%t6aIJeksWNsy`@Lqv~ z)f&vV*f?x(LxzjPU_9q5aGmt{C|^`AWKc?0t!ult$xoQ50`Jpsh4qjUyb9tv-D(Uvb-sH$`sbb zJqA$pv12Y)W>i(f8a2`|O{|D&1Zj$crT=n4US~lJ4f-tF>n$QjSOpdN(TbTf7{P@E zPHM>)Tc(L$lMIL`%^UyR*#)PQlUm$$PE1qrLr!5_|FdoSHyOu|N0?v`VqBKs?32gz z05GJRwF4aYt~=u9fZ(`_IY7zPQ#jl}cJHD{auLBZo{4zU{vfgkXp7GVmw$AA^xm;? z=l>>|aq})aJsHwA_a+EI2RBz4=}HaL1%N^>io#?!f5>jD-qFQZpZaJ zI!*a5?7$t{xOMW0@2~CZeM8q=5WMhbkyvDyCCCih04KOh#U)aM2+t~z6E{tT!ie=a z<_Gz`8p16+1_2Y&wKuwWc;*QuV}qeChBZiMxB_@vI6`$`^X&*qa;%`r%|;%x07EKS z>VPcFp@3&dsupKlEBFcD4(@wwa`U=G5+G6$HF?aknkHndft@*@L+%tsy1_6ejzfnW zK{*(zI|&jT(+-Ml(uLH3PfZqm#Ddzm8xo`a6gp*R8u^S3G8jEB!5fJ%K^d$NiCqutM>t$0^K&mww1p zs}O8i9RmJMnHK>`l@KsS_#}CTBhYu6f2s+|t-)N8!n9&252>pwb zH*1Q)&23WG0yPpwxm;snU$w5DXGR+=M{cD3z5HJ~OARZa5PZ-CLqAV{BmT`u`CF|Q zJAp=k-xUV?0ZtMx5%EZ{%u=-P9i7d@=;&pCjZo>v$GGI=4;kq#B0YozgF56aV7XxY zHKM;*h8X=K1zk^a;QQ#!@XzJXb;rISuqJ(%R+-eAKkKwfe^;#cSZtUXb6zNLCtP5u zmzVZc-jd$2o3D07HmUmZIoErAVBQy}AVw>(8(q{9CrzNiKhbAq@vigfoqU2_vq=i~ z*VYt-QX7r;gnS;c((ZjhIQAQD{2eh?;F01n9%aIe*d^K^W_DWw7oeTE@+ma{nVc-f zjj)Q5qw@RKzji4e4>0>bX*grp=0{{8tzI1v*qa()AeV>Dk8Oz?vD<)NI$1Vv+*8gu z3+WRZVIg_S7Cz9m>(7f=#C=NEs zjOiGCK@zl?0yr~p2o1k-P>3m3$lKO--N{`;Nx4!;T^~-&VCfWyIMgBfqv=VJ;&JS{ zAkBDctc-x9gBaLXpc#tyFu!2YmkPEZ{i)r++2~wkOoLWRr;@miY$Ljsas0jVGbM#6 zs-ZM<31(blD;~nv`xebOOu+|B(|Y*#k-vES5`%PXDR*8h-x*M^&zGBC9&R3S@ofuR zRiM+l)JWi0tTMUOX!^+`&z(*)lr|f=u`Chs}z2#*Fd0zP)2 ziO04jl{2iX)(n0nXw|kFC(XcQ3C(e=Cs>ei0J-Z846Da%(>NVaIT2JyFgv(cR13e3 z>}x)Y^SlS35V;<;*d z!i@y#(gj^D-~JHqV`NLW2M{dVR0EiE2mmbguNDK{0c8jy?Q!nu}r&gwJe@v!X- zUcoVVUE&1a{-((61kj_ZCjTrjSu%;rr4zDh=10cPtqZbZfh#sk``wy=o$1&p;es84d8zS9r@)WP*a+x_2Wt69u*BUKMiS}jGz z9_d`t`tQ%PHG>L-T_sRNNld5dKn%B@*rT!)RH=0xptNNdDZ=$$D!4}=t-c}LS{+Aq3ttYp^s~w{g1EbcBA_1h}?SQYo&Hgn+UxT+d>l- zNFI^~L`4%y^G|7DalYQKK>?1Q9mMdinZhF}k z3;l5Oq{(L@=ri-y#L3J7jXB}sd+SMgl^FN@HZK?34b|(WJY4GLcfGZU@ z79RUS?mMCu=^x4NEhdGm*m3OvBs541K%wXDUBco#6#)ZMhu0tx3GOnw`0y()c9-cD z3NjW=W3<$zG!Rz1RV3wBg>Af(5TAqvpbNf8E~O>S|%i zqol`$HWN`{3fhU(TEDGsHM^Ze)&6ur0hq7J_4E(n2>Jt$*dP<+GA-K&Dyx!McQX;a zGdssA4C4eZbmRR4u^!B0>|hgv^;Yc_x0UVVuT&zesHe^hj&7g?8ZZ54wyYuATU%&@ z0-mHrk9Hm_OqFD+%W76nkQ1&v8Ie!%5Ct08mj`pEW|1EL-3^b=S9jtix9Z&L0;oo{ z3re^ee?HBwuVk|XJ94`Vw>I7O&4crECzs)wWoBploPZgL#G97d6QnVUqpG^`Lmh1z_)|0{N#G^L0YmJbPe? zW^l&QPF_-z!z-c4PRiTAin|!SR~a90l>|{3LrN5Zwk;%*k}Fq!bA^0G?pO#VV>o!} zRm`b`C$gGKsUArXA6=u4E*9!n)jgz&>Yo+8Va6a|-YPz}LgjMGzHklWNC1!@C;$VN z6hA4=@4(5D0-!$*@`)Dc-PLp=KV9zs#M9x3xz5hxI93%*9R8*m*rfJ_*y5s}mqtW~ zaI_6qZL2q+;HKF)zgp%U39U|Js}?ey0(Nmhf>g}X(}swi7!zu;4qq3uq91mCgPBYV zRTxOBf7nlMfs&?n42olu{8!IuM%VqA?t_SBN#PKeY9W?-NPx((u+c&D_AdNO=V?!A zn_~%y($Z$6b7IPU5yK1MemVRPT1IiTv>k&jWgDrKjzNE4u(~1WfpgfVHz5#g%_(r> z-Sjy+@!XvcQjTe>2`SX9Gu1gPhI+zFiMPY-;To3=(fUgjptA;}`srE?k>YVPr-d3K*=SFMfEGQ$e%Xz)4wMQELiR3T=yUL)$@gc^&t z+#^`4&>#Fu_G?>Uy>7FKn_x>A>a2!j69cw?3h`umUeT`?!^=3q6v){t7~=8&cv1ct z#?pCuAr-UHDKp@ztMHPuzRo4B)#?GIKgAM)VOJdY-bm-HRXvfPfDIF`GtAwX=Lnc{ zU?W>Rw}oOfY<#e2pJWRa04~8)5Y55*ypkZ7+&pTLYed$XYPjJQ-u%%DRClwhjwlNT z5@)yyWI<|#v$ICpJ;OL#{d~MDUvK7iF%a;|w!;b^8-3u0Kci-rF-Ys$!5o)X?y@R% zWH3K46nK-l#ziE(M_Na}>z6Cq`Gxp;(1bTkAoS)Fm3m1~<%y2R*ECl{OBS<9?2rAM zKxp!?{uDf!_CNGR4#9T7R%M5Xef*Kk75$b>_V_t`du|ez=I)cpz6>n4&_qvDqO$tD z`gGpN7t*m06>|<)LMer=qI=Xya-))jHe7;H)U55^J!HcYQJI{7PLdyN>MrF`G#yPbXgB4W!@m&3_Ph z5>5WgB zP+a zlSQ9w1pJs~(Izy-s?h;~?dp#Potq7xWPAjX+W`h3GTfAF!un-V#6i)-%Gd#5jdV36 z1W6j11Wh>(#OT+PWLN1b`bUIM3ZYK0(YdlRSz?rj!|YshH!SqeGage8nS zcPJYzKMq(Z?&0qDsEq-grIVd)wX&ksuPNr&bIH-gW36db{bYb3Ca$@NICe2tQpYG(EzwXd*Anb( z!ASb!L!MrwvwCeoFV@j{+bRE=jV}hU&Jixw%#$g!LeN2J)Y|VFCd|^>W4iDN1;5*E zCVZvp${-~`>y{@_mS=xBr-kge%AI31sWJ*9jv6m{{0po|k-6~4ULs>K=#*MbW}~vJ zV-=CEDw{7G(@yzIvzwYqN}#7qmmD*y1iIfWY{O0UBPR$YPu5sFxqkdPd1dI*ktVbE zELtCU@ag?+;oG6%9aMRF+|c%CdS*vvpNryOqx1oJMV)4;FW}q&fy*JPy`G)cvVs4E zXCVJFx%K9QT&VY`qPxyzEcPNk4|Q=`w>nZ$hw=#5@z1iUxO$4k7|SmC^q!oP`_Ycm z9S3IkA}sf3*w4wbq(`IQQ8H0GnFd9@qJB)UNgV8RiW8nZ=Z>|-HuNvA&x zl&hoi3FiD4h8nb2s%u}3U2aXCkJGWB6i#t_c8-7<3?j#*Gk z!G{|U_?EzwpCuhx4_y6!p!v-zSXoFN3kODQ8?nCs@mNMm1aetW0RYPR{!6Uu|Eqi^ z?)JZM&|kHq`B&B76ifJ?(PM=6BiJk4TkCXj8>S*Xl|71?j95kGrdQWOg#Z#pq(%^6 z@F&TU`}uk|(FL54tg(C+BmrmU<=y7pmVbpQpm=vEyRhVl++ITwUS^Ymsg8fu*-0ue z&(v|@ec|LoFjL^&)u9ioXwQ*puCz=8r1BHi@I@7=pyrhEeDzbc7m`SDGm{S%6l^h~6Nk zuC2rN)_=W=-PLfo%ic5xU(3DXsQOA!+36$b{9TxjLSg0c6yMkVWARcR{ov!5XME^} zKAT%Rd44#tZc@PIY{L)m zBg;LvWLeK(W67ZRR#?+glCOG#J+a}H_e*A^*yu>UBA8qRvYCL}A;f5SZiUu^Ug!%&hVFo}O#|?SH@!2(Mm#_0iJ$)ysJ8 zcRL3~cSpFkLigtr>$U7oBx&4h%{$UHJiC2|_7A_=X2D9{$Sl;MkD8ti{CVU3aK4+UB#P3a8?w~KX zSDTNUEkN`%wIHy0)$_8|6fM=f ziLyiMsRurr=4^NJ^1iW~EXCJIR>HFL2!pGwY0K_(hMqKmAWV=ZAAwIc(AsU%chk!*S2En*lN)j^RsJ;^3Yol8w3I0`8 z{IgPv=C%|CGAEvqgYI1CS<<^_dB6ZgI~%y)1f7o}0Iu&?0!#VWb4!(C#Q+7|TsLP55`0MoD$FQuYdEk}zJH^p`mKh|;qJyUj6;sd=lF$!D9c zV*ujSvUvzU(%e{|;5OD9e&P9Ly)LfkFoTHw82+dPSIGe107}R(f^KGO1`sI?>(z+W zcsL$$)30#cV1pHdYI2)~*4wL#_JMW0*99_biTLUQZhzGho24T<5p zQxhYAVL>yHv?xw{gaJw7$v5t2Wx%r*o^S!lnd!wQYrp`)tSLcW=cCMT*rHuAf?1=5 zj5?+Az!831=(Woghn?-2?g?iimZWBu;&&wy_^lY!&61%yWJs)1#I}FtHcEiNtYd-1 zEHlKf8_98-m5GW3BTy#sdJ_!(<&1KXZ$EkAqHq9TX87vUb;-z)SCTJqNCeFZuxI+Q zB&84$>Oz~ik&y;*0Nk}xwqAGBjNk4yPdoOp2DAqFuw4C>;fAZAViaVRv15~={jPK& z>sKa`iI(ezCpqxsgxkRbNHA&iCaJxBC@gSI+A_QlDbc=wEFI{WsZ!HFQtpgn!AtuW z9JZ!3Z`^D^^D|GAp2k`=l@Y>Lq|6PG3v3|)(D!Ue)@OQ@ChOfAs)%cxgm{-_yC1Tn z1RcX6KmSbQWEw|^!&-egoMa+CXf_Seq+~n1A8NWKw!X61>6>C-{Gb5qkGhk$>eDyx zcY$A@1=7wv(yPPGW@4OrZEU;t2YLcf-|Cz+q%%^AnAX~jTc6Yz*aLdXrw1o&d)dbsP5vWZdVjuzx9u(5XKpa82Hoxse32L>S_w%UDt; zsQ9*^R#VMGkhBk+8Du#8xON@QkHJYeZm3^l#J&`13&Rv@n}o}6mz^l-Fh-D|E2(0y zNFJC&Zb*`)gU)fBk`So0kGuv5|1S9OPyMO)HV*G>dg=r;*)`qJOGiOTV!5*KLrsO% z&;!J<9wpPh4i%4Exbhl|Ry|VMdkccl(^l;V3t8`fLO4dXLgSAnga5td@ zphBd_#*UR{jyXk@(S3r?1iFl7TkaXCxUw>NQo?@Lfn_kVy#tYP$XnzLmI;pIFK(~s zg+O1NDi?_lnlJZLbUu46R)uq(tb#|@+8|NUu|?xO8u0zyBL@$xf7`-(3S>erGH2mh zGGLZk93@pvRALew$bm_Rb2l?e+07L3V$C%RXAANo=!iqGlaIX1qaR?DwDO&1hB2(i zAx?7V$WC~&9l+vR=rS%hnxBlgRufv$R@eS)2p!;%ZwPgfKTBeAV$`O{>=1qLClI4@ zgz(O8{7Lf8eNBmO6S#K)Anbyuv<+bg%rfAh{rxga@Yw~Kt!NduvA`u76H=Wq|81`6 znlD`wF!E3ZH7)@r`9S0Bq$wsEtA=xA;<3I{Pam%C zL1n_=y_kr!{3OOCs1rW8C?};2k3&JK+9!f`bcM((x=Hh8G*NQqNGU==bu`W@8>f|+ z;rBT@hCh{}`i#iy=b944uAEwD%0esXdFf~fgajBW5ErN#mW5*WvxZK-JpsXmz4^#w zNl0y0;eWwnyfBsiQYd?F6j~z8tSwZ%(hMUxpQ!@H*R+P%b$2{2#aR%4>x>M)=Hfhc=96$q)h_kxOjEaV7PgVCO*T8--aIr5}ig*Z*<4dhtGpO7`+#tb+k?n zy=bp&jNp>P0>IvOAo%lUOs)k;;2lv9&Y5U+pcm*U_beBFbOK``sQdIF>StNc633{IeIvd}vZht`gA zc619bT)pLmZyfwb?|5{&U$cS6Z10g(-C>YmIc(JEc;#&aQtEs%QqW6xVp) z*Z>k5y@%p=1m$R;V!atS6iEYy`KQ!jtA-p_$dyD>e-3>gTBP=0AvEoMb!}(7#A(c*-l8*Pa z&B*Mj^=XyqwLs!x7v4~hxzrNAtcx;cAf*3+_&I{;j7c+r& zQe4yuGUQX-j{yHD5lSMrT>i=XeW1j(g?Ui3;PiW|tr(b%~ zo?6hY_Xr!vQZGS}N+O45w{p87QZp#za$>9zvd{toO+|@>nk3O(q!>_bX8J=&sBet` z5vHzJC<~w)a!^T2KwOoXZE%`(%*3a0dJs!0^X0gp@|`hbLH&40z;hQzv#|gk3p1;@u)Bzj%eZxBS=eK z|4vaqr{*67*$)!LDhQz?Z%R^(#kc+tPrxO~z%jm9R9l}(mN=~wYZswpogr96{t)Ef z9l5rc3JBI|B+%T?tcWmRiB`_)c!vs6g!&~ir?2C@n=X!LOK(}N2N%ON$|P!W|9vPA zc7s4d2_7iKs7AmO8kooOCg=<=5O3L%JXu(jB`Rf{PCQ?3`uatbV7FnQtgoQezl}C! zb*oSND-0!>d=6?r_2eb?8y{zs!Ew6734}rIFai^!KtSJogEfE?z#B#Y`w?nQ$^a(v z9fFW5n}@n2#~%yrUh6*Hyc)lY@O0f6OU3iZ#BH2L#R(2<+WFD3;%gN@>>hx&O z3#8|84uwqG7O{LJmD*`?d?29UwaW82MUy)yL=nBQ9F-+1-`lJpKOOc*9R(WkVED*!+i zP_vpHZv3?IJ#Bni0LSHh4Or(d$u-Sju@{+734K@Vi57yom{Yg3bd@|3n5Tt2p^k{2 z)Z@m~+V8_CdS{S)?5ETI<2Mc95SjbsPp2B>lm;+~*w@~f!!sVO>^9mCcAir!=SFCD-&EoSFVFM^gXOAIHtQ9 zMbkao`i>6o_r}vDZ70{lct*EAdJ1o9!Z^<{Bi#*6VBht;Y3vGJt0|summ2q*`1^ts zS}rk{dwk8YD#P{S%R?DeP&w#D$-N-MoTh>v0*yxg%=nDz1gnboB z<0Yl#rm)5p(MnL>IGW(LQwzPULg44;m|M69P1E9w4>n)ql(h5|nc9-iVknF(mgn+c z#W(L8nb_55xR3m6Z4K16gxooOC_oEpjtP+^XsIw7M z-lubfUAplh)RlC5K|$kW(Mu9vHi`?eU_so&?4L zfKLx#MexpNW8|N<_M5NdeO!EKImg9^anJT#$TpIDm;%%{z|=Tu(6TJ-3AM@N`Pd1m za_%HsHBV+8BGST)J!{J`x=%Q(BfZB*Gb2dH|XD zYoK?j`#yBaY6OO;KcsiiF}+wm{od5qU|$f{@$^8uSqb1gXSm=_RC8Vat;o;KFB0Tb ze23PV4ahu_)y7563L7Sze!R>;DZxQlJT(hn9YBIIc*M78M9frl=iiSAj+81}#C2OV zT$P6)+cHy)HM$T#zUtKha<9XSPhR7jEcs!ZX|Wchd5!I-@6flY&t#qELt(h5F|EOQ zLhc^_I4wOcfH2m26Un?5%vJRoaCw180Q8tRC$H@p*giNx9 zB+h_O0Tj+a#3SZB&Aaw(H4dZWhS(nC|2B6t7*V~SI?V_FAzLI&Wx)lGp1qTrO=Y=u zlpAmu3r@jHy}@~)l*e~WlYKatoTUC3DJb2oBrgO=9Hj0bKa+dEXg@h3k4Q4gt~Tz` z*_uP{5${!G=w4&oAG~ol-ExA=0bcPTzeOh`YJ33UtrC5ST_aAB^L$vMA#D!^ROjp0 zV)JDH;LcIpv&@tybw4i87((`HX7_SqEey#v8~TR--;oMN)Td<-6aavDaR30K|GJs~ zzYcR9yG@CN+jmsnsX?Uxk=vav$h+-hB(v2uNivBnOc)+8QAOG)?8qZl$<4sgpRbCF z-prx{>yzVXf|Rje(Dr-Hg{`BeD74=2rwTO@YrHhlb z$QON%o1{Aa#B;`VWJ2q>^~=L?SX^pvS4W5bt*`InTWM4(z53O1m^7LDii=o{?HSVxP0ih%1Hz;Jx3;4P?)zvyWFpKs3I>gnlxe|{rBFvpsYEx<*b1Va{e&2Cg(ir^4| ziWL#Csr)!=@2a=$ncKeI2kPKq;Q(g^>~8YWvx#ati??tx=}WAn>HtyoYqlU_&b6Kv za4cSGXFPZRySD=~lMrUgBgi?NGGC`w+fr-ZTlE2rY#L(SGJnAVH*EZbJR*7P!*|?q zhFhY4K-MP<5+%F?k7~Y#4};md`0YRO%_5|)GmQHrR1}c^iwSlWdRn#wxgmfoZl%zS zKciWchPX~i62~9z2@J#00CZJq@^jH2%cMZ1Yr8JEafkUSmb3{9j6QWE822LnaOU>M z`T|WPfg~bK9aE>_IP;rL&3Q)NA7EJ=HiEhWZJ324Y!0#XvA8c=LihFe#PEMDUKfUK6D>^e&%W=|_0EUKD;7gfE{H;|l zS#YZzqcae-BeZ=Oj+R*-rYHtBxI=B1KDw7k`73*jnZ~k&Kq&|bK-u9Y+m_?MjEz-v zn!oD^_1h>JrGLI+qZ5%sFP7SzM_J^a%qc;SfA|NWLrxR zHAdj=asYov>(PBYli<*e05WnyLA4N`e;2bL4CgTxXb}#d;nsV@nTNm;5c$j`A7s+p z6q=!H^yiXWLh5$TS1X904k@Ru#I}=EA^x$2G*YaM34eisjx4MI4NcSKEDB;_s^VX} z!je9mM=$*Y@6=jQrch~9KgIkCDcJ&}ye)Feb^9MuF5L;Nn8d0r(NS;SX_BgEMm-> z{K%C_#Oy3lFxP}?8Yz2jwg z`3x){Frkc+s0{N9Rzm$&1s%cj5mTiJsLa~5L|Za#Qo4vYFC8}MRej=iW5*2B39cKt zmn;-Yw935*Wxj0giqyk#E&JXZJWF-SJ{6qnmy02Q5z$T(*e125?@9zdjLn-AEP+ZZR)!GmAHc);aZfNcN8#4dE7U5x zu38oFB_8D*08cTjP_=N%8|)-N=YMP&-VFi*##xoJDCS%gQGWyxBMl5-pk#bv5I+~% zcM4;InqEb~JpvlWprav?4|!cuF|WEKwvq=Q#*fdN)(Xv8n}@mx1yu6+K4i zFbgFvao?;mlfg4G`+!*;a41=%Lrf#g3x=OiDj57j{B}VoGTRTPz{8Q-TD)^r_cGLD z>DyD(d=D|5_Rwl{8U@GV@hS%tvgqLzl9K;WIY7MGRW>LuaXVpjmjrUDmIqG}9@GyF zFkiE~Jg3H@_%&;htQxLkLk`p%=^Qmvg0-mj`EzCqV&nQY_FxP{9L{<1!Q4|jlru#0zqF#yYDbh*K%my* z`~+1-_RDeLL`A94O1cESBttBXopQ@Yl9carQs`Vt{w^7?0PgLEOLZHn z)QzP_#scxoW3jf1oQK`6R~ovkl&9_dY90U)FBzr-M-J-4`^s((9%{>|WqFy%m8?j2 z9!I1dGg-O?eAV5-!>fO;*@{o4#U$^XoP}#0Q;KW@loiidGT5Gw52bz35RTS~C3_c% z{rYudSlvyG-*@qV+OKxcR$`_ZKt|jrCxzVU)E<``S*0C!cmYH_1?l~45|c%xxWd!wQ%y@$DSVQh#=yA1`o`B9z#fEjrNohEOY@yoVsW#``}*INj@eMr zyBzQm!|>?OvC}`}-UkDSxEZHi;sUt9xU8_;$Z>i@BTj0N8HoiuDUMdw@+d1>ep|(- z3C8FT64c<_`{6-%gc58q1u-An`)N-{!@jp>c>XyG?$8Uw(AN-Om#BhXa@-*5TT2zi z24Cg*)7p~SMkDU|fEz9SgJV$rln&Rf6XY@A-9&@83KZjR zeO=PvRKo4X$6fUgkM7KaNLwb>zVDeb0g^cL@2r{3<{b_i^kgifzY30Fuy0G)n^d<` znO_0-74@=e(;dFibx?WiPm+2z2VG`ISqGc<>PdllXq?@LA?N*s?FziEjmKcVT=_#F z{Ej5?FE;NET$=WAbi#~<#ozU582_LP1N-zMf`873nN6uuiz~(u zg=FZsoFsIWRRXR6N1`y9*KRpE?%FtMY>;@< zUT#~PM^(5i;l$lEPB-AOOw&~zU3){M4IPwitGYzmCg1aXGIIN`r>|yTSOHdr+WbB6 zkycr@n{C+F_-mG}U~!Y8EVB{_Ti`CL%Wldj${7FtZz0IhiI61=*z!Inn`-mWi+fN5 zEwc={&>GvovdoAF{}jF!O{Wj`V6M)SZ{4HbYqHdSGQ9^$g)f5q_^m370HRMteNd^s z>Tbdxp0;|})EodjyY*kdL**RA!7eoy&Rhd99uiibkk4QwurnMFNb>xc*M_Z@)wC8l zpW$w+Bt19J3&dV z-Kp0?Z*2*U)DMHr@n5$D1ERRBDh8l;qo6Y_qTcjbaEzdbbgXu!TAb*Xw|Q>?o{Evx zaQw{3SVS&n#2jq-66M9%EU-nhiIq4potzcw7jrTI=qNDMR31uG7R8X8Q}=8;iMcIb z*|i#M>XN_)S93J6+(Jk%`66Wxs0E5bF*v{pN;p6rMj4FuRV4_X*2Hg~xJ$Btf5=J) z=vT^sYy%k$y&+tQGc866D#|o>&V71XUP9$xN-QeLt`HS+1n2Ij6Z1tg`81TqGCkQz zO>7X|F99gJb$T}TyAzB4=>Q3jytBP&*?C~MxO}2gju?Xm^c#X>FWPQ}k2) zn*H<2JxHy*w%-Ts|1u%FIVeq&FJ&b;IU2(PRk?w31kVkJjfdb`#W`&v2m?c5olfV^ zM9%hlh^9yRMZGNt42DKNVCKz}4W!twbYWyU3nmd%TZgX6`}Q(=@jQvSS@Dj9W;|Z% zP;Aga!*feqj)?1pEyvh7pk*hRQOBwO1Saioc85)&dLKW=#lk%+6dT3AgQ_+tV}2~x zrMfPhS6pDkfr0ljB{9de>;O%R1*F^7){+3m6tYsri(;sdeMUptiEgu3FOEDM1&(b= zL10LVe{cbAJxzNHy2#}`(~eTZD5MsOq9`o~=4lF}5?wiK7t|SvZj5`37N#uE! zC&*lhxdy^nK@a14h7tXx2rGv}wGasM&Sh~yUK};}W-YrQ_en$SQxU`cGdEv)`&WgZhQrdbj5Yjk4?&vLd*^+Wn`YK{$i4AylPSb(@53^dy(9c7o|Tw#^m&NJHxjf1!lkm) zgNZY2L(xMwqX1{8=u9ko-l-Dhi&>{SnvVDPuZ>t@d>LT_CK4E2D*eFt?B79(=@;}{ za4Ha6N1k>NpPWkWCIH4z_Xp2U9hC=8)HK^eeVQ1JigI>+wJ!Z6Jyd1KqQS+R|ZZQHhO zR&3jLQn8JSZQJS0tXZq4dv*VY^XWd%zV>dAi6(GAY2q03Q5Ju~P3}D8(lw18z)gUi=EP;gmo~(_Gl&Wwzli6deqde$ z61@XA9UX1Z452s-pBG#|`G#S0>NN8~Ae*fWcAP!V(){xM_-#zb9;>}_&etTZn-0MV zbn&3)r~c{(`4@nDn>>ugD}i?FoiLr%JA($-C4XXlBW>WcSR?UP<^b^k5~pakq;Ms$ zCA`rnULz3^@Bz^2&bJZn;7?xhSvoD8yOZeRYVn*Oi1Q$;qOaO!u|x^|qZD3M@>T}R z5++_%)gn1GkKI(aR$q7LHt@XIo$7vL#-ZFN%kSg+e6=*P-PPT1{#jNt%9FqVJ+n7) z1&MFuH-)_FKfy7%WB8QT|%hW}4Fqvwi9*W>K%8Qp}}2 zg{mug8cuPC0sKCZ?$aS1Fhh;L1=;b+AhYxR-+)aYubF$E9|!-8+5e_x`M(+>BU7^< zWb=^sM^GV_xcx|DdWrz5^DC>Z3C@0DRA0Q=sI8dLG<0ZUr1~Veit0qTDA^5K zieB$q`?PCDxG0RX@o{;+J-l4fIB5MTZi_ceI#RUG+#|m%qNJYFk*vS!>`9Qz#mDQe z6(upqwo&@lG0k2brJq`$&)#>mNth6Ouu;&#uU5tIQ8;qgMl-1rqi9uAOo_+=P>QJO zwA%bIh<0x4#5I`{$Cf8f8K2(h1U^da1$1odijizdDr=}N+!&o57#f-pP1aU(tlaf7 zINGUmAu$nM=0LLT+#PfsG@jmM6V+ua0$}8DvI$d`Xaq7pjb)L1(%o+I2!Gb|5)=!S zCA_&hUM|HMeP0TGXA_6~+^TnKl7@2#mkFT84q9M@tQw*t$LuGQR16JvOq5cLbo}Rv zBpFYIanA4MB{&3jtJ8qGgc^y7_BNaOYRy(aAPDDT_;YU$uZ*s*+uPl{8ow_$#>kQ_ zy>5xg*p6O`#rhYS&C#Bg##qVd(cY&m&8cKcOLoB&+f>cMZ+~pN`6ErX-B4tWO;i~j z8)GnIE3mk~rMn+uUup;!{Q=;km1pF1Q6CiVmh@TtLWt~jeB;gh?+hQ#^otD8_Z-Xn ziRd2brz2x*V7v0yTP{;tFG{^hGD_>bz5r&INV%iEDRd1{zmxh_3ngGx(+BRtPzpyF zM~S<8HTgJi^p#*<|uv93CtSU{l#Q5JMCJBMY8U4#3gC27~V^^q; zEmRC2ab>1L2gr((um0NPmY)v|AeDM;*s=yPAQ$Sa;|MPnNZ<=9cLL`4ljsL#z|Aj? z#vRa_7fuB&u4M*>jk!2My(zKy`=i^m?!|1}G5gVE%|CLsqM%aLQ>4%6Hb=P|xXDD4 zxgcQHt4_xszG{wnrv#||HWi7GrJEz^uJ3Y??jmK9aNeGczoYx_ z3P)u!Bg1x(JhwWIe{+L&5-=D2#K@d$G=j*Eb<#b0iXO4Pj5O#MpAEeUg#I5xaTH(X z^r(o5%Nx6?&#{Y>6Uc|GmTdQ?2=xqqkTPkQYB48wo?&pj0OddV$8I z1XVhYT6?G)xKiQ2!gB?kwnme?L#9* zkFh?#6FUl2p@0wsPrDN)_viqIXlozKCBkl=T zUC|_)SJ`&D3bQb91k!Pmhd6y9;RTr#NX8Wm7T|vJ`74{%T0u|055vnE zr+991GA7^v?d9EXR3rr1<^Ty2f{L0_-qg14op(42AB7;j69y$FyPW8_Z`^zsK)`0^ z)M9plu7`ZtWbqmM58MEgR~1}`m-o4P+g!S~9wM<2Y48*M zS|GVbWT|C$l^u4S2^3BSwj;0*60EapCQZG6ZzByRqD9q?n=)I}vi#Ic4%bJkqWg5E z(6oWuok*9T(;fInv&dVqKS=0QK`mhea^|Kh1z#M$UjVQGAVF{~5U#MQXd5J*4D-_E zRW63#vji;0Y`XSZHcsEonkvMe|ZlRk^vZjl_|bs+Sh-SK7cQshPr`#U-l zGwI`SRTwb0hiLAoA>av-Mrl>?0?hC`Y(6vrP(x!O4=!*mhXWiB9wN7>u#@D^^-?16 z!G`@jJ=DBG$Ecb{$Aj}oCrVtd?=ICX0Z<8sxIaRrke{r?CyvwbXQX#74W#(dR_L+1 z_(A0$Q&6}89pT?!RALjdtGY40Ut;QHUcQ;49tAB)agd{)mj|#HuV8cz3aPJkF{?tE zfbKi=X{%=-ahk$ArP4QmUI6{)3>cKP1M3Vj4SL@@1?k^_Av zS<`n@na;i<_&!N5lAH)SZ&{adY{rAo&1B{@z!7mo1GR2-u@P@fD*{QoEd)E?yv=~> zPJ^TW7d)7D1_n1VrQ}NmLbHg(b6|aOy#iUD?uZHsALMjSl8Vg=>!$c$WA-bdrgEXg zs?gr2-?W-Guc)Dyr<4M)TJYXN#U;*b3bk$nn5Eex+*;;n@nf|Af&`)yuQAd*gt47; zQK;|Mj|VxdXZ=|>_I8?psc)u7Rcu5snIvlz8w5@6$QM;aS@61dwG+Y(bvPKj#|JtA zXE2ux=N663?#!jLA60{C#^%GV;4uG_0CR~=V}8-)n0aXP)t_j~TB2{+ zUo3eni=?$}`)^6H9EQw${|P4plRG|iIP(dIyRCFaD8#lyNl?ZAxKc?6H;L_eR5sj4>> zRd>u)sb6b|wlr!t_yIv`g7H?B5pwoI42Wa>cpOZrqWiRk z^$Wxi=5W%BwF$tM7_kBB*>XCPaEuw=f{%L)-w$9b7QI>O<)K})c3?!};!sz+K_y_l z_K?|;xq=+Z1_Tisx=XEST&V>@7&4WGAK?!LSKMz-TCFy~3WT1JFWbWi|N7TtzEoup zk!VV8Xm->@rE}9?1{AyO@66}nOvCxoHMmL{=4eCHx|akCQrkf^45}Jcnuq@#ALoFD zIxrO@`KM|Quy>8!9)T$5U9GP?aDmsK5_YGU^pI|5l0=&TW&0eB{zG`542crbyrgw> zSSN}~U_jbuDLe`FbF0gGHMd>+9W5$sELg2o_XlCmYE-N^if9d~FSmYU3_(AanMdQE zC0-)CKHCBKYhw)ue>Q_fC7i$PPtv}tnB}1Ic>b8Lw6xgNVB|x%`2gX=j%tX@!P{u$ zDP(3z&y^N*wt$3PU)?JaBG^IJC#O)uD3F9}~+sPa?eKe5$`%W+3^nN1|$evZqf^Lc1SEn*pMFyjn#4@(1mELMo0b z4jN=MYfwpna{dF|@k)}B6(@;<9ZWo>_4M;}#Ec}DoneZgiN`^)zL>MTsHk_^QnbFH zX7x9-u_LO0P~578*}+{lgAAV&1h&ATFcl~{b} zQvg$t+1DRIXJ93LUd{dptGb@<;mA-|%A9GzG9k@Chi+>Lt_ft-D|UA_I~HV#MGf8% zon#g1NGKCzluV<+z3Q{3K6F@R#nOPn8fV@Sl$lydxOPJ0Lsrtp^wi|b4}S^G_Bk1F zv*~}ACkp%RdNAtGCgo>5XVMF#*$#7Ka2)vC;C4*fK*6m}`7}z{plUgkG!R`u+S}~? zJ4ieA2BCY9_>{Z~qWB{nw35 zOsU%H<1<)oeaC}&l?~&C`KvLRS~VmExsjuN$PUMB{F<`IO53I%!7&1K z#SFAn<@BvX=+(V-z#%*!Aic5#kp9n%{-Q-^Ep*S6{w6sFo`WZAz2Lv;)RI3-KwO>aTF@>D$Jrjtbju$Dvlz~k(WEf{Fy&$)k z<-d{U9O)WCct5pR50AnEEG;fezehy4i9`1{ZW+J&B@_Pjl2AkEH(pfK))+nEXIamp z1RQDTB$y{-)`^*(VB z_uG~W{KDsTVonhcN78!9Su+Y37e?5=PBk9na;KpmWbWN(tB`#V6PGcPL>MvU#f z51LCg+BvSl_Y0kt%xXcI&Lij@clB_LrbPL3R{`{JRgL3$d7oAc`)>$U<1BEihMe)D8 zm3MUNEhQ{Ko2?|ig@{i~u7AoIN4%?hiqCXHoXI&8396Qg2x}AT)>R`&+}dEyIyI`Y zjHhi625+!@I1gko9wBI(0|z>*D=ot2m~!Y;xJRk#ouL{KcDv9h7AAyLV6H0GuCmZ% z`HL`bKpn91NqxC^`8<5E*;iX`?>hGhdU-#0r&@2pG1u7sIpLzX5p)Es0z{v%aCGDq zQJZbs*BDl6uEjX9Lcv=~i4pzt@5O-tQApdz1w zixLOJfDLI~BzZJ>eN0SL#;2S#%pFy`oNztgR}&Nqr_X!KnRZf~d`ahf5O_kbHf zLV)OjoV~3-r}LsxzE?Z2tOL3E(eXG&aNumknoV8|dV<%)SuQg);}tV`W6>inyNip$ zOMc^2M3n(|Uk^f;#ToyR0ntZFpQpX_umnM>n*VlRH05|=dDNy#Dht?{aeE;>v!s`e zeuWogC{eZh@;6uc^jYY8_|b`oT2o=N7ip}Ut?l{In!k&H1t&hkLVIaKko{n?P$F&P z7_XxXqZrm03x|luc*Dg0`mMPMsj3BjQb$Mvlxuc@rlVE7W+MC@hq)w;B?GP)0e);Q zZdi0EQHeDorus+z0?HLLO@)sHEYNMZf$!x_%61I*YuLWI4v6xUK2(J~2op5RuQJaw zHpC&H-2O;G%gEE{9dMIz8ufVBTHA?-6pH`X^Nv+tFS?@b{mdHOc`%-d?o+vPA!yqvt{3EQMv+Z>~ke znks6v{)Shh*9@yEB2)xwK1)!R~f0lP-9TV$RS$R zEBq3jEFxUy2~9bH)9ieSY1;Hf@hIf!oF~GJ-L|A7C~p$8M4l3IGTbFr0+Qr4PAwd= znrwJuEwJIlYX} zHY%}a%B|uaYy?Q5NgY;r9w^=_z$mw}1pNori;m$Q#(;XY!nl>q+{eX*T5i9nXC)K` zStD9#vN2~i5eW8Oez=cbVV5{mOvkr^62H*H?m3XbgJbCAQd|4ZFCF4#nC1&1X|dy4 z3NyDF4ZX?PIA7N z=6E8#H{iQGQFd6`)g5y*)mo7D!V^#l5k))c#Z3NVG#J?pgkiw#iEp|>hrw=oaHo7@ z-4u(nFInI=@A#OS&NY-DcpuvSYj{V8zfi^!XAa__;Hoo@qak~=7ow*xB|B-`z8~rp zPySSY^$vvQXj>tp9T_KD*A@bt5#+zTtvdUG;u`aoiacDqakfmTCpFh`3ZGxT;vF=X zC1~lXVn+&jRGdXW*km_ezO-bqZpVgrxq~xVpAks9geTaBaW2HhG5WRC5!5HBTfTfo~# zuiRcf^&nI06->Mh|3Zjk#dy@>(GG{Z3>~@hx(c5P>x97${5Y~XX7EcJCKx`^^PBO7 z_d+d^VtbSJZG!B#yU1#%df36bp!Jtxx)&E3;zU1#(mB7)SA1z%TsxE*R%;n-csMy{ zg@={(hxPsW-<>kANySWJfnbYr8Ljk~5eFxEKu$Lv1TX+%g_{_-xu_HGYbvo}I&fn| zZ^&Hl7$aa&Zoc6$HTU_Uh}xb;qFrnr_xu;zXbJjY|8|#mWOC|6JMuGMKTqz@#R+UZ ziouBM3)i{0qD>$|wmB3>Gm141nu)e5yBDHSht*u+VWR6%w8<1IP?!URB# z?2ovf-9+CPUV=vW>lp2 zBONaAncIa9vcX;qSz(B;c1qR*{NDxgstwal`3cCsuAvb8jya~GX0z}Gd`_+HCR-7| zz(A-~lp3kXAkGI`pYSc$eot1H+~Q{&&@H%fd+7N3Wx&UY&CLQ4S@?NXH3as~=wHF~ z|4rWbtq@JbaIEXv_`H$zn0w=pEb-~3f}6BfBg_-GzYO`&2J_6l^T_G+R=oTAS+*Yx z&u%3#I9P9ZkA(4-HDJc)-+=Q`LV248*C18(M=h4lOxV_v)SJmh7&V-z_E%diuDP;J zyOiDPocv{w<@X{+7@vX6#BR354ZkCRYY%-Z&`2lxi6rCfX_<<+!pZ0?!zng~LXMop z&F}m50!x<rkvQFilj$JWvn^PDYDNg@S2l;jE-P*>B zLd)2{*>{r1{jm8MKh*UinU(oLjUJ8SAN%njCMj(TF6K364WV{X@Fc-xei;EggfJ

_17=+qDBPk4h~5q^!WT~EyS=&DG1nCaXpaHL8Gna z+=gU;Y?X9i4a20elWMt@{EdC=D}y#e%o%Yduhy+rXh>GFLxR!9K93N>OOHD6c_ zVQA%yjtJ{bEfWN0;M2f6%IvW#uZ@oS$PHxz2ZVZMRvxZbRbDQR(qJoE*s!Jw9VUY5 z4HC?mWdKs0GEoM$bTZc2WiylSEvUgi&dQ~$yP8nUTHT(lF>mA0czFb9%1C)Z`Aq@x zjD^@T-1>kMfRR+63mbOc9~%KY%z95qVuZ~vMB#`12eBH+wI{ei9D!_yI(7iu`x5w;u_pyj|!`p&ZZ@7p7!z!Xmm@(NXvhru`}WE zxj1u4Zp!+)TH8Y`#j^dBvLQjfTaBm3WpX)Q-)r+;`Aa%et?OqqM-uhyI^_JE7)5XE zfmL?-EEE-sNoapnj=aL*472=ur+ya5o~rBS~-z_sN$z_FsPP-6<)|KcuZP93e}h;r-WxPZvGj?+6p6B-e}f z2~9e8BMGDHOk*k8evnAW}`aT2#mZ-;H4;&Jw-cET2 zS{_7ybuW5$F;b2@+)i@MGJ7;vW2O`rTTN3vQK-gs5lQXps#G7Unh6czn#fxMb|&2_ zpeL>dlpt!-*`AyXj@`^F9VT6E-q>AR^9^?na45jKQX6X}NeB-12vtRS5MfT`5}Q=E zu>=a&+fz%Y0~{-W&Y>!%@wqb(@uJv6U>eL8^{0P+BqXffd)|LRF6p27!&n16?}@&~&%4)L^3Y?-t*d;K1Iapo3LxArhE;%^Trhd3R9F zZN@5ag_k$a$HZpL$tNwBS~vFGoR*5o#0wlSE>f$N{6GZP(Ai$K(*`)vaG7QCa|!3+ zK5?Zd`R5}{;&YHn2-)h)?90g?m>omBS)cq_v^jaJ%_yx^Whw@{^~OYxKxOoK41Z`8?`x~hX0{4j7t#J!kmcX=lcAKWkun*u^n`S+ zLnvd`woaYuY6kX?dautj*T;~Ozr`q6&eec*;mD!%p~67E<3h?11g@;+FYn+_(~Hn~ zA|sF~9X1eT^C=QHf;RmcmOlB28=sDj=y^x;5Y*nD&bXt!#x9fsNd>1;?Rc#y@&RRg zv|aoF8ws_2MRt}Do+$BYnQFv)3eh1oPOgLPF++TCd?wfSB-SGCiN3CG5I)k}h)GT`WVqFkgJ@7FqR}?@6xCgyu1A?rrQ?QbUbkcODng}!`Emj} zuArNPiUrmj=Iqz0XpWV*nnmp?2QG|bFl1*Cz1`$lv%4=rHj_zk*boLuuwom^x)VTK z-oDGwFF3|t7=E7<8TAbf|ADheF~EakROzkOvxI|_tCnYC!|u5|g0+`bg2*=e;_=u# z252dEfOphz;a<<+3b=QJ_L!JT9QQJ{!<*xec)c+WqzO0fN}V)QWL?X>?AxAwXFOeO z!{_|9wR?gs#ND`qYW;$;P}s$k_P+#|;`MmJO8#%t-;2z6X+wN?zCPh^R(3-$)AY=n zEaj-4&GbDLSt3>gl}ImR7G@doNNme9yyjEk56$YjmEk)CUNi9I>|Ly6 z2T{iCZMBRb6v(=fK6LI+xW^DzBT_WVe_+jOc7`gD*X#|PEZS4cC)|}oSb71mu&N8Q z7~{YP@WEsAckCt9B)<#iT(x?#(+eB8CbxShmr1`C(&!m@^?fYSzXEZnZfal>L-ea! zJ!K3IUpH*Q1F34MCP|$MOL&Bx-ty&$v_wF^2vhWC&j1~*=HJL0Ib-sSXNOj-ZC@U55Ax=B4cobK$%FuG zI%P;UPeo@=zJ`zc(J7cH|iR?V-Wj#CsRoU_vQx-%|(bG*7;-{p)VL96~LLUvi8!f{&Fb_b+j0_;E3uTOMwC>d`VkeG!In%jNbY8-8!K(Uz>9ffh>L4{ksh$iC zfW^I5UBFi6K+nEjFW&uyEz~SS33n)giiWGHqv1P_)9rf{~Nqxa>4An6}8 z@yYb5Q#ODV3n5*DPT{5cOpoMO=8C!zVba$B{Qh z)T|^58i}s?AX-t-thL(xehI7XaL$q9oJt1epcL&olD7Acj|YAVz3AJtzB+oVl!!pm zzP%Vc5DuOPJu%QOsRE{5JC06h)nMinPG0Zf0ZMBdmbpoj4nnVTz@u=Fa4FcELuBFJ z-ZuUI407@KLEmW!Vll>py8H{*3Ix!r4KMi1f}qN_giJyx9V>PDcBO=xp-KZDFY{I} zTG_7gxxXFS*@Fy%5dsTUcIZ!%+H=AwynH+(z7b!fqmu!EzMfz*PzOF_aWpOWzPt-k z2ETaMiY-uU{7_Vdez}L9oxy?Hs|?|}d?Y%7F3Z+To-@WZDQrBarNsu|IAHJQ?(Rq3 zj|p#6oC!J9R{K2_bo8$1nM7Tqbf`-0iD2S)>;tkA88KQxMFO}p{Hx;cr&Tf%D{J$xBP%A9a9L1^#Yd^VAsvIXS+ znWb&2=t+x1qyH>)i5d>rvIwhm6uHX~;Z$^6g=fajIv~#mvJGLv+HZn7%37Rfw z7t|3j1P%g6BNsFzM#R5TU>}fF)}PTlzo~H1`jrceHmaLp0W#1wa-=g+!)56c9*uG)Dxd^4vm33c}Y9(Bfn!GHva7zXy z>CLXBq-?Iu=SIAg?58EpAM)z*H)6daG0Q(G(J&a0qv?bF-IdcIdah1cHgxd@Cc5cn zcJVsVDeLqV{IL=r{whd=f}sHc0YL%9*QaO&s4O~>A^`z?umAxu{ERxAy0|*H*xTDU z1B~tM%q-0T`fi3cmL`TSruq(^4o>z~rp7J+GkYgnLl#KcGfH?b39uUDU!yuP4TocM9QmzS64 zPp;1Jp+o)0`DB`1MSt?0;~B{`}KK{!bz2iml2N z(4_aiW%~4I{O9GvNo-cQqT3`!izWWk*2EYSP4N1951QrPXqjEQzOB0C=zab}0)tdF zuCWXEv2yl3>CU=nn({zCls3i;=xsz zzJ_Z#d8;`;K}_fKU%amG_rBfd0sH~+o!9~VZ>77!*teusJ`^|pt4E%Etz`6Y0*4PIUbz z?SJWSwmrOm1{zpWFLQbvzJ9-UeusQ%H-FtnLK}b%+-78sYyet?fHEdgnSEvbW#1+H+Somm9kf4v zr*9n(_zF-Y)3TbZy;T+{5`A@u?nT;JrH&?47bkg$$Ln&)Sn4>WsycSvNNp72dT@n| z+c^9jk(ckj3J>J_bOnKVoh)EMcuBg`EFtd=p!o1fRfGQBrS|RxtII>N!vX){@x9^x zRV1sQU$t!jxv+kUg}T#!*cTy@Q}TTN@8o?}zEQgkr|_OOye&&R2M|sd!D~LA@y5x< z8vJDe5A{XiCaw?Zz|a*duQ|-z3ZhNNOnSUxKE;>O3oqr}J64j}KqNHi@Vk@a&$DV? znUUylfCV;qx5Kf0z336*Vrs$gO(TD@e`tJZ@&Mx~=Cf{B z;mee#<{`9o-6b(C7_+QH4zXf<@&$ve$8}lOQI1~c)#DB5_EulW?CxrxJ7hsZ@_Xa- zHs?2~?ba#5+4^JVINfKpeU(6iP=)gOy}kAFyKdRS0-2x~)ND~~r)=Lr9v6C^Tau7v z@9b-@Fbgzs_Uw<&$+uiv%_7a5vZ!?gE9vnWz(>4g_Q&B;8O_u+_q!?FNv`UW z7T;T5wkP*8IBg2Ket-(Y_?x=Q}-ZhHI7+toop!Bd`S z0j91DRS8|;f4;XyV)e~SX#-Rpge1iH=Lju?nP4f2#EX?zW9tO_CO=X>`+>bvcH%Y$ zgp--HWKMqIpy?Ka#f1%O7}|IgI_gG@N!2cEDbH}^&IU5X*E=KV!g^S8x`NXVF0Ne7 zz@d+^lqA>P*$a*KXn&T(Jacbj4tL*2vG>vY%XGHuY+2ly6pvxAJ0@X5EQNlAl^O_( z8=A42Sys@!#+q?Z0F;W?rebUE zrhkKrj9Q=AOeWPbx>wprw|oY>Fx)_`mPphKbZMhFH8(h6PYhbq#?)-`(0Fvq_q024 zU=_`foC0u5tn)DR$`~vo@DcsEEVF!p-@Wld?k{$R?bYM?KnA;#c zhT_0ay{o{Sahgd^M}F6@vflU3rMND;<|Cu4w^ngLHb{6-4_uy4;Rd8$4%DO^Oox-t zO}7$AjVs!$XVp1N8^}w=Fj1Z>kxn>4pA|?@YJ+luKy9U*tWFteWH0Kw1wa0Fz*+NQ zKM=%r(x#Kp;MHj7hg?d_AsHYERONKl8<0{zbb^A69`u1uKw>i6(u_{bXUtfW&I~Ko zlq@LbLYiCHD&&>5W6tXLBV?-6{ww$|$Sv)LXqYFBaB5B)L7f&S0$ZLYghx4?V{>aXlU4dIXYdr|ol-5T#Jr4$%=!*eFr?8$VCWtI znyk`kGq{pTkP3GNwwxmCr~nc3+5y-<=EihuKp0u z5@mfIBOd<40K)&5j$1hx2zEuh)|?TLnn(?Lt3$~UO4NZQ&zKm`G`$B8Izr%*)g8Fk zCEJ7nAk@E%^&-y0It472{E3~R{8B>Ccol`N{${o_ybSTOp)!3?Q329EV2Ob`w9Bi^ zRfNBS1ikif7Jk zrjAxsf%-92p}~qXa^8yuHaZw7=w}r{6zt8e9tU{QQ(ulQckUJPtxb~b_J>*=3h8a`>(K$L@ajG-Q8l{Xr_{6u31?#v?C0YH-YTnX!{2!}e2)BWS)wk3 zBtx~N*q2LiPB@6~`lH3l_VXpy%qt#G=@X)6rjCgYJdJS`QeLFpXBR&AQo83CPKJ2L zRGI&D%&@%Pa>LN0YOCz_J&QO?Q|d*8`&(sFD}`d0hM}@K9d(I&=t6VzltR6xd4=cCkuZCbP$xu63pG2fmEgD8^w0zsJjBKO4*5 zEH!0a5DF6>`7f_9lQyrS)qy0y`l8S`^SFjY93zcbDCd6@#p~%OJ0(Ig^{b4&xG2N;&V~`6cJhiYJzGSMBX#ie_ z*)bMM^mI*IT2N`%7fgfk)m6!q>Wc?ru6N-AZzoMrO|9H69Q9rM1V=CSK`1;HJ|c{= zR?x#{(S6EMTXwiYIAuRhtPYeKp_F4Qe-ytX%WpDbw+qrz1}_Nr;80^#7hCPAj_Mf* zPJ7qS_hdC+s2M4g1QP_fT-#ph3ooJ(AW^BLh-JD-KPvmj`18Ew!}v4{$O6K0_i!B= zJL=lF7h5$UVB#Z*6?mT8yPT*O%c|!H$9dIE(Lg4Wz!20S(&TU&-6X%u1k?hh=!nVk z(C1#IppPrQ$~Cx~x-nW%6X#q&Vb8Ee*bQ1&+Qa?%>+~?-Xnh*7e*6>^(ep`H0qe=%K^M7s-y!$cI$_?+ zo@EA^HC@vrazv>GWa-PXNFG#)ojTpp8sWTfGsE`@rK?A+H^%QKFEPSilBl7-u@I&^ z4JJ>7-hCGn#>!E#1qE4sZX<3+gT2b2od>xKD2^d|BvkUIwqFyD+efaYl>AP8q)lud zU3-(&M*x*&Gh}dlF>Ac-LTAc)+R%+}{f?F7lDzIu{PT!N@GPfMp0y100U^l`41NdB zQjyzdRcDYYkLptO4X>s#$s68?*BoE;0>HDep;h|245Cix2N zDUoLLK2+J3qsE}E!+#Eoh%>5&?kUV4{9(~DILlgOrJ2a-nH1^Tg4d=ivMGD$^2npZ}HPJSKuqoj%YTq~pi>%L#waH#7!9*AuC zYU-Isg!@wM6}cyssJdO(&3JtD0eraBcNy2sjpBI*3yJewV=W=}pE%IBOK$?=5JSv3nr)Gjw8K1&Xh(}=Pc4_>c;e5Q>N6#{;rDdBDinJkToXn0d(h5l~0@N^``?YTnIn$Tc zw0uOWKqbJB&MVm8m^va-q`l+m&AwyNX3<@P^|%vC(b|AENm@cyd>eLOz&k=;YZxNg zFV3fHMn1{Dzx}%uhvk5@wQJyE+}sGH`=_oxSw5N@-ru`SE-tWLc#^|6)e&=zo)^_QFdjl!)UL^o!t)Azyk9r- z`!!&H51(^yws zyBEzfh#9&B^hQ)htz z0j0wKf9%1J_Tpmd@m~r{mYTHvItNnsxw=D^@IkNYGcIATogW(1%q4yLlS(5TNtA6h zl_FUg>DM#&KnUfkoOLBVF=veZiz5f_>eCvB+WAypgz{ZotfA-`%N$$Og`0y?6!WP8 z?zK1>q-{?!Ot_J)%eY0UOP8_Y2mUdEgC5!t0ajD^fSQXDXt9OCB_8WsqN{U($omRZ zaD(-eqI<28x_XDXA)A}0F2jdJNDC2Rer+>J&@d zW&n9a0^Y=-MXaNqm|04e?tj3R37!lNNE0b7bTxJ8o)Aw1t@PT2{BgYBOUo6_YE28f zonh}6Ai=3nZ4a$I;7MlzvL5mqs8p6*HmTTAm-A3uEN zL4qZMpV^5Lk>%+l=wy*_7j-rp(GbFa>F#3Er&Syp!Ehld3IpQ6BqF( z?!%j0wMF7NWsRa|Zq6TDmMtV=D`v0ITTakZKO@D%2YWTu-YCRC>(JE_h}b zI9xJvPeSTByHPQAwLyd#=C*DIm46`_7&Bg_SzuX8og}HSL31(w(CH)n`hZo?i78bHT=_Im{L`XFmZ^LNwR&Cqs$Hl@16%KAyb7(H=}spRGP)6 z(ab$7WOf6~)ytmkg>%GBU0IwVi0LPxAV=?-`oM`9NGKh7Hesj1o>NyxUT5)Egq!u}MXl>Z^RXKH!J3@jM$aHZ1(F%Fw}(X1 z-mW3KIUPseKzxscW{7>$mZp}Na{HS>?O^e5Lb)T=3H7oId!2vXr3;^xYMoju2s#9B zMe&ilng0+MN7UE@{O4)^$#GGs>`_<{!>>SPn-FnE*bJ_QMadO9>%ui{r;Iye{o^aX z8TvoOWFH|71pXiIHGDWAAcFtf(QazzX6a;aXKQNb@}JXP&DMUM4e9GjUyzc3)--(1 zjZzKlCd6%Y#z?@JaI?0L0Zxlc+r~nPkc#`P_Pd)~slK$+00L#cbH9t{c_VIb#$rFU znHgEFd6P{n#b6}ss?#Qk(iI%t{dY6-8lI#~MjrY+ck@Lu z9yc^qNGr9W?s;G%9D3!xMZ-TyN>QrJ3omX#?NZ(bUu-#ROXp$LA)QSvazAxamvo=K zL(tN*2refoYKUPAV{e)g>;$`vF6gyqb7k=N1=hrhiXc<%(pN=`fo8mYaXSA~Y%iML zC{kdIc>5r<>U%#*-*6D9r@WiBBH$~70vvPX^u>=esnZI^pjWS>Md}sC`SJz-jl)VF z70!+WvluEpJ-$qfMdJ%weCm49c5oh{@E&#hrPcILk^IH=j7Lq@Owr4~@pmd@>=Oh` zS{<2Wxv?y%*me|E>$*M?V(#Bc`2Pj0mtPC5ueEY$aS1>B) zC`|E+eQugq(^)l;yGQOd<rG zTc$k%O9jrNevJV*8sHO+D8@p`BDsvm>3EonS!lG+Yjqi5>mg%6&pR#4yJJYW(7|^! zBL}?=PV`XuXL$LkCi1R%YlqO7)SXA9!pRCLn*JDYldcJ>SAnq{OlBg@@>-Ju{EAk7 z70lDJy@bT?P~l7{POMzLgrGx7I7pTqPAF%Ppva2LN>J<|M=)1esz}RBEl%_TN9Yep zB8FujOJ^3WAeTi$6Q5J22o%+=JrI>*j7H8Ct^Mj6x5*bW6H9^C6zVEVn_=PWQmyRF zwg>`J<11xtAG&VXAQX-aXQCRz9t(-G)17aB7{VWnlgMi4T+FXzPhv=Zcw1sI&_11k zT_S?VQnbX0mN0@bZXUu%KSI0WF~9dz(ofQLMQR4E$65bhpPn$6J&{fTsCV z^>Y*NBpKq&dHLDY_RgEJhv84>q3Wr;O6yNL`leHd!QP$5CW|3p$;DOoQjUH=t2KAo z0G-w2k$}3SH^VI#Klpc>PblmjUsLyN!0(9M?_ixs`~bVt0GHu!Seuq{$xEJZ6~~0W zmH9!8U;JC*+IDXoWkgy842HzpJrKrHvjGJ#p^Q(5vI_|{GNZT*6&u zEgtn^MV7ny=jJS;@{R6-OKScZR{cE&l!|aHuvvIQ`>AH7@>0Qk?sq-`hw)c<#kZr2 zn;^m)b76$i^kJ?ayHtFa8;e~UnEfj$&CP7SDi`v_-%#>@_3Ac}@F-q{@n%HVyJi;+bEO5&!BA2jOrpf#LJ$CX2|&SQU3{Y? zoR9>J)o-slqk@uEY9+)09DHdD+#gy-QOWvAZH}D1r5Qp|MQ;o!v1eJ*qDM$Rl~*+{ ziRUDp`_xNeEXuBl%nB?v>lWG+uUx@z-wVrM52|3GrwwQYEgpHXBrhElzL-0R$xbliT~hhSg@49xkp< zrvFLWEvjo|kG6G}qD_E`)a!TG$ft`JLG3tgkiNbRau zGpDAOc`b~-iM=XjC-K|%;5qts7Zl;RRQ-6V^~m{s|2QBt%7jD9+o?-GDEOXr6K)ju zM9E~UdKR@f2CZAqxEhR^^I|Z>_L52~z@83sn$EC1S4K|#25%IVbvNgYBP&;sG2u02 zS@J@~Zc^ZW52>TLR7Inel}(w`?(9?(FURD^S~G6T!s{y0Q=sPgG*;~y%_)hfxxfSQ z3E`y|R$@Wzxv*{LSZSZYxd8% zI7^=I`z~U%T;FPivtTkN%VOt@NBRByW^_zl0l)6Tid9FysyC0xxgTfd$Xr1}hTNn7 z+$u;YtPDY|LNno6u7q20v_M0fEJvigGNm1DEltNugnq#p9QES@d#Otu+yn3-mnXv$ zdIs0SB05MMB;($$^3trvZIfvXOaUvQS%I6jP;1EMZ_HQJuoXJzf$SS5=ohs$tg2`4 zsyV7;mlWl`Qe;M!UK{q$7h26wd~8H*tqE0pv6DLa9&h7p4MHGZU8|Vo+^Kz}l+uooHzvS1{+=u3=#tB_ zmLIFA>e0RsNX-2Bz_(K9itdjnRXdAvI>hdg#yc8Q7p`+5Eg$Csozd)Cuw!Kp$d&0x zoIi>cZ$Y;(v!2<*pJ#XdTfjc{)mNr8Kovb04Mm0|Q_>7~t;!3)qYR`{CZjeLEtPWf zk3aleJ;7xW{THJMl}2^HpPKKfeYcDj6?;)|xsb$8K7!SkJR6{q8k^RGOr?j}rtCkMw>`Hu~LvziauvdsbZAU@BKV`uo# z98?>7I;|8zKDssNhPOzOxq=ape5RZRW!_Ge-ttA`zOWEA?vdpPm-Re+qomy&MG{$| zOvqp^i>5m{{bY0-nG+QJdmHHgL)be-i5m6Vf@OP`ZQHhO+qP}(vW;D~ZQHhOyXw|| z`stiL-S=U=uJMhLFLNcC$&ApoXgHOEMjOo`EGKZ*p7a&Pl=@C2(l^DbduFB!^EdVL z{P2d}$U^!nZWK1QQKt$WIkxLt5p%{I29cax7XNv8h-vho^J0W{1h|GJJfVEsRwH)t zH|jH5hn|+$*6qT^dU`S(S-u$)^MSBg*F5F{AQwFk6LZe+IYvOcIl_U65Uk3t#-KmC$4Ag7 z@{jv*b7m>@WPBRE>~Ivim8PrG*FZzRtRI7#*%uw#O+)6ezzLD|mEI21P}o-; z3=^p)YuR5Ov`wzuh0j|CIyA9i=nzI4QTCuhun)!&V95|7J?1A^qX`*MWW_Q%k00hI zKkUt4GiEq4H&_HGRMP6J2k);tI>509{%8v<^)$3DX3sG(|SA%HkUznIe}& zCJbzdF5pP~s{n@VI}&Zs7I0JXY^6utWNjgA7X+}EmJ9l{4Lr1L-MWom5caq{+fd4} z6Q;Q_agqW8^U8LKvxj4Nu;d-lPAhLy!2uDyMY?B?juqW=rHodi5poL$i@!;yY=lob z)%WYqX>^B!z6epUyX82K-n@Tx_C@1b8{Ij>l}O7=#O+6`Rz0~261CO9)jPxg(cEw+ zSmoQ0Sm#CjRckDp2aFeXse6Rz2HMmV{4RX)#iZ26fZk_d1AC$iSOk4W(=T&&0aN%m zP8$El*X;rP?{DN+*VzU7Z(f4>dr1Db{KUr2SkKME*Yf4@3STFvNoxQ->$YburYD6H!%9I2e?U9#_oU> zq3c6UdM&81Qf;k~VixQ+pWJgtXikb1AKe0*DW5#KkktkF=Ve-cF%0Jh99139Ix-8R zJKf{Kc+0s8I?r4E%;4=*i>n&=8!IRKTvcm@b+)XQ=^b}|&&yN0N|nV{0Ni=L9L%SL zGHGieXr^Rj-xT!mED0KymHsNVn~C%AAF>GZYBL51&UV}eR%<&IQpSgFrYEGBid-aNXmLf8mKccDYxwlVvhASCn`|WqMFV26Hs_NYM3(S zqJu(bd~**~)1f*Rv{`e!`x!Ow2%$FcXU}v?@>SVQ7?U(NC7x6KDpa$wKwb}6`Ot>} z%I{N~N_=Zpd&R*v4+2bn(xYB{iid^3R2EfHjx41K6KyFCW2HQfI<33%DcwyPjpG8e zj6eQ9v&YoTq-{x!yaE_FkqKZxHaJ!Hb7`9HQHaqa9L-AWM4wgCv3{H@?cP-1^2^Nu z>$*+wDRO#*UvRgbLeR-VD2+bq!~#S}2Suj$Q7JAv&b+x+0@%$g>$PpT>ouJt28~h! z&I+RYVkVBwGyuLJjoohI1JLWh*{7J>Hv=)letB|7&LfT3D*hP*cMs`kg5GJk36#Rz z`8LtHvZ7%3H+!uSbG1wjH9YtR}qCp#)enFO1ROK-`MPQuA(J@8MK=?UV3p1>Mo^QbhC z0NHtI)!ieY0Zph4Ti9Q$euKM>4y$Twr!^}MpOmqm>2p?R)HagDeE?}Oo*oAk>bF)QMn&H`R1qb9D55A+u1;K@qci9iuyk7W1 zsDf15L9CSH<9b@l9e6;MQ$oP~6T#7!OjDi8YwvdEHEGSx{jhvO|Hw#E*txISImuA^ zea3TMp_I?{JKKPH%lF@*!hb8f{67tNNWX{Ze}f7qBS#B+XPW;25sjgp&2PX?B}qd& zMWOH?5E7@Rm6cKWf8o!@y<0-)FaGF20svtCua}xQx|%rt-t`~s`K>2+U8uoq1`#hx zakU}S>*sZd%%NU23+(ey2lDF|It+>uHW4o|li%-f5m(KWc2xlnF2*r=Gd&!SRVqLtt^eP*qEAHGy3*wOx4ClT!ajK2Bu|<8A+<`zDnz7jpTvXOB2} zMho;_*}e#)qq&C%2!NWXEVniXxjTFNnX_XcPIE8ttwM9CEoc?Th*13S$2g(Col=10 zdf=E=`;r&z`A^|l?LgGgdJOe17C+#uL-?=8?-uRWyr1(kTtE<%1101@~T!)HO+c4uL#xW)hwPs{``v z*&IqeH4$B!{S2UIrX@yGTw&f74sz;;0dBL1qEz zl%KD!4B$F)CwaVyETlwVmEM~V$2P`E9o*tb58g?Aj%VB#@~8!0HAu#C5aY2(%92ht zt}yrM@D3W~F6=0u?2a72qDq4N-*5P`CFel`9|CrTc{lGymd4!$pW2p*>t!Q@O?u`c zi!?5a3>1(|zux>#e038nYBk`YVw%oT4C`48d!MG{!H|+f&eFmK+0bjSsxTLy>)cY! zCE9Zvu9!h&mn#%8IzAY3(@I{|7|@Dsc~uBBTR+F-Vw|j7X_zWLSKFU3-L0gGLVPy; zNHH*0A4Mhl4Zbp$Q(}lVdlFo4XO760=caD1&QgJ$s7RBmw+PQ*K9^t3Ug%)6?dt_- z>)=th6ZhGB0X*%bO#1SF>magpV^$3jXDS=HF`{L$eyQ}Hx0=V;D-F41xEY5mIc_HS z=6wGnFd?q^q$v93!(@Iv_?i13tFQ7?ZHH|9xnVlCBm3g1L1PTRR`Emh zY*V|M^#|ULxQAh-iz`5lIp)~<_CSWHTJF=|a?*Joqf3SXlL|cx$4Hj)El$;jVkZ_1 zoefb3%wbWkyI%aPf8_Ho9ZKR*_EZ7un1VKqXT2HUG{%bVyjnHAyLtQ{$`Q5XW_!r1 zOohYUYOsR_kN!P7TqI%(WAM%aLpig4nzhO5#t-9opMhitDPV(Lz>Z}PZXFq~+ z($MEDO$fjv|5n1#-~ztEsTY*6pHZCvW?S%nZk;PR)LHZkS-aVSX17Wtm856ape#Wj zQTnRODQbkwpTF!lFY8G5B49DHB=wI7_RwKZ7PiAuZccPJ#{Q$W9NI7O&%0xH+SJP= z*vAV5HF;gO{+b+IGLa`fWBz`9zKYt$D75Tj7^^NDZD#y#@ijE(IpAXMR^$5QwYIT|y`zbdfwRSLDEez{AH_b~O?>3e2b58sRVthKdEI$& zRj*OJ$2cKm0wDJni-0h9_Ua$rTGvZk<>VmH$s>;D+=eKBJj9?eC~E*>$yak*;0v`nPr zA}X~f;^6|XP@Mz4b@7r)RpeiKP8wY6#)=ud!xa%XKbr}ugvbdq>lep4tcbyMrgr{^ zzqSZ|4s`}Xd5)S`3w%%+U%ySnv<-R6jWG(ID6i~x?C^smrWh)Py?_Rd>099b+cbG97rK)o&BE; z8`JC*u5dauBv0|CM+5$7G;KJjTJkRk;XztSL~{&uB1%m~)@T{uxgpi$SnI+H!7Edo zYqy_?f$_(A|JiJKI!_le@O@}pNBgugL4U-l9n~2T)CHh#W0)@IZ&k(Y2h9C2qbPKQ_R=Iz(HUb&~t6S_)JWNc6AcaDDblx-w>(E@sf_0 zK-!pmSheaW$c%!29KphUchXvmr*QI46ZX2Kwo^S80Teo_f~Jo!Qp1JJnAGRgu0vBj zYCxO5m|RV9_CNzA0LIvka7x>A9l<-8*W8?&ks!BLv0{t&Enm*!oE%thA7<9IS>WUCC``ye8juZA80uQF*hXHU%Pk&>C+Py)) zb$iAQsqrzdHwFBHRA%M-x!01~PK76O*dM_&^9)%dQHncK1@w|ZxIp?QB|4Y{RViL9 zSsuQp-GEQp?-MVfCw%G3QQ47mtHX^m@+h)%FUFkWoLnE^+qV<<^TQp}i03)Tpm?`A zOoE(S4imQ4!hWW7QH99_;ZE>F!ejEu@B=0;dm*t-?+X#48ME^vJt5~uMtjPf@uGj!E5tyHZUQHwZa&W1qsfO}WlcKBSXoJkt zt$*Zz$M>2wy@I~BQfFk(zWW;bpy$bMG(o;$+&I%G$)~SvHLQyO@0bFJ>Rgr`$@;v)R$BD zqcMBPP(O?uf;5-+gKdS2U8nJ1w`@A3G`Tw6mo^gPJ(zhzQe_S*AwlMP!{2dIkE;by zjd5tNd%3^8r6R`b#u&EYb@thM%32hxr(L6FxG?#)QXCkCN*!&EH@0;tW-6gt`MuY+4W$=*XFYr|GwTm`8y zOX^{RE+XMS7o5t*N2q{I|IV+`JI85yr$f9S)(6@}r~k~16XvkPe)~ls3snNSb~J+t zuEbi$(_`oUmNc^(J=Me|V}#BtyI@|jvLM=T29|r}uF52q)kJ-kOVR99ovSA1 z7*7E@bk~-55gOzdx87YN_v*>n#n6jN91m})z*bfd9ejMRlV>-3&${ar#GErn{us!7 z)>F|r%!kh)E|{x8un4lZ43`w-#Bymhxdzw?K=<=1x&$ zh=)asg*e!6eG`Wcf(|}DOyxyy+6>KgDP{~$Ec#-U37z@8E;S+?Sd&h<<9{A0x7=FU zI58&N-&a(G9yPAzuVa--<_ffp>OM327cWtH7HXIpN-Z_q)QObot!Nr^F{|9NIxb5e zJ`JI1R|aEIeh7K&+)x*z)n8qrN`@s?&c@R_vNMM;~MYh_Uq=Sjd)Hv>g-g)tzQuf zrAi@f`qa?EB8S5KBxHn~2tup;0!37UpszPfD+H}wD_CH8X25~p*ChVM@9CTG;N@rh z`@|0Behka~6{FfeXQl+_{%XhPW2)un>5A>p@8_y!%#g>DFyhLtPR_C zWmOtI=7#QIIrDDK#wu0eq{{4s>BD1mE=ruuem8%y^x25&q(p=r^na7=x45)d%1Au* zry!Y&dfy(DN|(ACJpgM&weGlol%3A(299Q;iFN;`B+uU$&U0T^#&Jmd1jw?Z(?2v8 z&BejS&4PPAy&eNxYh7F2US0R^QR@RftB8}6HPycYvidBmN6EW7?X-fBH`~v+xJX@b z^^R{0=?6xcDFKX6mv@fFHzt9!{U(PC$c3aw%~_+k+tm|9e@#VS2&Er+VJNr=&SN}< z6%V}y9oWN?|I+r=n4h0y1003L0hH}n~dGG9$P z=&#L3VF}@QR}f3jajnSggy&Ho6j((>@?_AOE@UI z&?;1I?Kd755ZHq%?g1q4%BT(KgKYsc8pBJbH40?0(fyJ#7k5J!5)|HHADnb{s2F-- z${9g^A0A^(27B<^$OF@MIxD-|84LJxPjBT2xhg`6H#uTS;rjPsV)7CbuHObZh;{oh zV5t~nlQxeG4=6G>;*dMeDJU75$)O*D$uv*8Rb?X!EI+R!EHYxKc7hhJ-QUi|dVV2vC%?o@l{!Eku5OT3SYc_XE= zGz}9sgY_<1(#xy#6irdly^*W%R?x?yg=RG+&$u1^54&EP)qX2t$zLan!qviKO*CJ2 z=4h7-&r7U42$V7>f)j~zQj7_e69+XpbvBy9q9Q))s()5XDOhkT1p=k_{KdtcZ8$uQ zbbXFMzvU=lcgu)TiowtmfwTl@RR1OrRIPDcLUj3L0Fx;`qJcN?-Iivg4B_PJJ!edI z0KKDiYSqw@`ru)}W}3>L*HH|afJ9&3JFB8^;w7DUJ+w!4YJPlEtUE+2 z0N`FaKS4R)I}RT9bv{*ScOv+EcWV(bdU29+ilmbQSd8k)!W0Xo$3 zPZA(;eRs*6vZrz0byfF4Re;$brhkH=WdQlt%H+4C)A%TH018Rq2S^ zR8~;!vRF22Ereq|M&7jIp@UAh~T|(7h zNZp)0hb+C>GHl#}==F8D(jp45WX_vR6({<5vA@Bd}(INwDY(%KY6gd4 z$la+tVIGb_=nQ9@dXS0)RQEtZC|Howkl2fvXKShtU8Rl8hny6B!`9Zu+i_K9$TF;l zD@Y`KH8gorRJn|13|d@u>P#wIu#d-{tOhG?g{0Ah^W0XWUiZPx4Hk7zuBEM?_75Y^ zWNQ2WtfahV&zUFG2cKVkX_|rxI>A&YzjU{vRN1seeH?efxm zn)xw-426W?@*ispl^zAo5Mz=t{3t-ks<}WzCEirVD!^P9VUVImLiUX_ix)Jbrsyad z6u~D^QJ;&LdKKEk(u@|1cEklykWXLLW90{0Zt0RYBUiJsMf~qq@ zh3)IKh`@^YMbQ9;uM7QvJGCTySL_OTS8!c7ebjzUY^Y`55gwpPlL?{zoDs#t&8^&E zX$@OyuW1I-X&6C+m7p9eKe{T=)mwt@;iu+i+jQ)I8pgm}m^b^S<{8xqvojS{{Pj7C z;Hb&ORuxkB8PJ@$?N^kI$;c-GNl9@9`9v&6DgrwUES<_^b^j>G7Fd~LuJ;wL$+-1LmY z;rVu{A@=PijLdb4~O94MVL=<7UcokCi+x)rnnE2vpFDo!-0DGwQsvw4RPZo2|Hkd)dZCDaON zoIrdc80_YUq%h3G@faTNx6zEI?ocfoC^JP%0dX>9#15F-6e#OS42*v_2f+*}eo=;_ z#8N{-HM{~FWy)>$mV*?oJ$LOiE#srKnJ`_wF=m9|bWsqtVr_6@P+ZsvmCOX_4W<85 z7@{qkSd@O{n}L@4F?1C=hPiDJGiZy!{Ljw?f%&_{OCT1!V?aejYzjpkmuyee>6{X( zqYL2f0DF|Ik{ z==*D08VmuuVZBM45QoyZPY?1L|5_*%`~YKS8@^KZIRTS#jDI>ugQq9$G9YaRBig4- z1~+dJsUqCfFi_K@L3aw+Y{Fu^>L@@x&K}VqOPopi$%`#xhN#4WbKPb>OVi0RB2}v# zEk;z)4tYpDGh{rc$g~kc9rbLQ{`r?5SBVbu{N;wtQvGQt;pIy1r}Rry$8G2S9b#ER ztn)Pz9hF7Bx*uEvt;uskzH%zQly2gD^SEHqvmA*V z>hPFaAxvdgAxpL|RCjYH?7>U&_NzIF3}aHml2erOw*CGqT~`6-9Q;uP2`x zDw{!uCr-gq?%$y-9Je_ZbhY%s(A~s|960hIUtUn+L5W_T#A@pewlN7x$3Z1=?sG)j+ufhra<;b~*t?qOl0FbD__&KrGoX#?3E_?qsTvXB;%n5Uha5NU zJsYyY3f>sq+GtFhT8w_bKJmVfdO4HeS>Aa!vOrnmQu2#2A-YulUGK(>oH zt+BYM6RDW*r0@qr_D0*~hF!?oHhiW;WLaQkq0IDlJI{ZbCmF9dT~bPDb>cf4O$}1t z9w4-#D(4C@=BP8~X5DV&SpI#;!wOq{D6mE1+R+ip!xVX+YEuHcb`d928N8(#t+`yG z3-g3Ms;IN&u8urJbcNe&#F|RYXRPI!M5O$_?Q9(znOIu@{p(cAT^{%0yq>Ee1CE{x zNsQV7e;ngQE=L{m{h?7>nHf!uKfVz%L2uNci~CFvbiwTr^eZE!-{@(``?UsvI5HyV zMnetZ6Ro#O+*9U=`{xA*+Ooh0g8W~OOycQ&%66&CJM$AC2)D8cn2kz9;CXvW`Y%!N`sd zgkf1h-U*F7H&kFjcBI+QfCor>_|0FeT^S|vVVCr<jixc-OC}7Faog--kNxj zf^X*geyvl{@BN3`WO*ds6InNZ#ORYZYIfGb8}Q+N*5Ig=cjntG>E`fCmvcJvSIs^$ z&KR2Gq_i{?*}zfTtCLGXSF7K*1qq=DdZ;Yjuy-(5JNBya9<__dy^SFZ)^h0lO0`eu z%Fzln#n=bnw8jk3w`n2g=9JzrfWj1gg~bH_dMRdDB-e!*b&Z&*BZo-MicS(_cLH1( z6I1yIZVV->f(;FHWIM%+|5CIKIwU9uR6EW82#!+{N5^UUtlWv0>~9WmVswT8rk4gp zWj}z8G70H1%@AAs>Y0C|sxws{iD&qc=qUxF>v2qq(yxNG3FaJk4Wu%!RRK;ER<_X3ym5{AT|zLej8OngRnLZ2xfxUqkdY=Nuqr5tReJwBv%C1b4$_d% zt8AB;9Bv=}zwHgz%cv(nSMD}Xm^zY~I<#Lomab8(h@~Y8n}8dj4s30Z&bpT%*^Icz`vm(PL>6Y9WqB@~Q+X(LwVz__gLV}uxD}B)>!G${> z)VErvN2uSnc#aMF${k6RqFMb_0K3T7Il1%;0lzc21NQLoC_9u%e$;@yNXJ;!`OQg# z2q5(b1M^?WOLCSFHAPXGi)dv&J=5>Nsn-O|?ILqjc>Kp|8V#HNKVc=tWX*p_+49VV zwjI)N{($d~$nL7gjn<)OQP~kfvF>Wi{2A_I<7?C*WM3!^K5yCK{7eNT_Q?xxXD>G~ znqC{MG_X;nrDal=6i>~)jXDn(VGO#py8U^|yKK)!bNM9c5EK#-BEOM!ds zA;D-OakG~g+d}NWaL{#tbDt&CPt~T-$~9=GX=cNR&H=?8AGDgXg-mt9*Bh?EBb#h# z5NrcP#CxEw4a&dv&T{NV@+^SbU}II76!~cHoA-H= z9s7vk=8H%k7uzJ_Y^c{`1-Ao538gc6FDLF9<*%&C)F~}z)Sj-6~f^iiQ zgVo?3jgrPIe9dxHq*Ajt9bP4KiJ6_il$6OE&pyc&MIdaRimzzF{Ajhgddz0 z2?=PO+ufR%&uO=t_tPr;U545hV93TkF;pwND#zl}nLSmx7aO*H{g^}VS0NU0Z)59u zUPCrUPc`I_09|?nDJMs|mRBfZG|+UHqMK)y4SjmaFVl<-!9s-;6WtZAS-L>C$OVv+ zh^$a#B0wC7w<*X7AoEKFIf`&(DCQPE#Cy_{I?ja(Ci;MkR3+6FL|~~B>dJELa@d=t zCz6;*ElnO34%a7dSxCvAQX1kmsoBAt&mn;h*BZ zmFJ!pKu-P4%@ftxSL)1^Ah!W!3d*YD(ADQ5j#@|gmf6ufbYOI_+DwvNm`e&9kR`n*xQ|bU0A-ky zfA5z1Lhn1cC28p36xUeDv-D;px<=vd;i)T=&9sVGDgq#jt1J+CC45mYN&h{_S|jU- zhCykPHQQ6=lgZ40KzW&Y*?Wd14^L(U5q4iNM4C+RsXBLob?durZk!Uk`}VnCU4ltd zy!q5|s@tfj%T>w4!TIBW7Y#uoeyQ@JD zzkJKr^L0C5x@GYnzT9vG3Gv@BB*GV*)S?Egh5NA&Tj^`nY0<+5cx936TXrVXvicWn z2U+xv>g*5Qc>BlLRBgOE2SH?3U$5o`3nK^TZ zO()2!Q58!=MG7-k1Y3RGFPtwH)$KvNei_h9mf9%tsk-0;%|${;DqbE)&5H&}`;76H zL>~aO46UM-4oY8I_R<{EY|JNPJOlozl0UWKGQI875^MP?~hH2 z-HF6ZMk;yEyLNXaz!jmE`3BAN+OpB&=#^O(F0+KKY4hCSv0z`KBywh%Vu$5J!;3X# zz<3MVs`NeIvw^hW?1Df+Oke;=@tmRjb9pjqsw-|K>^FQsfIm`U4rcqQ*ZwG^`BsL) z66ayBh5Q4W{*{#rc|2 z0G6gDZ@MHmzoqm8JFh#F{cVO!aHbc*glH6A%Q^*(k35)q@44wJ#4RN5n6?IAbqN^W zJGkeCo9QC{lj?h^lws0Vm~fJwCVk$l&Ac-xDs7lm>L>TwUqdXH zF-8EZM%@b-Fj~QA91MjN?4fxZEFu8&1CAl1LDjY@PFjfi22Nv<#`X`2Qti(cL0TnL z`bosCfU;!6sGc%+1kf{n=AO+!6m>exX&@f>zeuBR`Zz>~Iax3G9jWfp+3h}l)f5kk zjQpEMl#*3;?00+(Qhsr!_eP6So7+sdl&uHG8O4+((+?!l8Y#_2&aN-_B~FNB5%}12 zxb7*b?nT4)0E7bi8N+UCkbgqXQucBaV~3ZNt*@SU$&(RSAVbBgAOTYDp?xazBr}1` zvE`iV{y`FH@&uzSgXd`4xRqS#2(QA_r09JBD*)Nb;SLF8>PT`=_@C@C3%)&;U!sh| zgaXF0DW5sd`S3#AA~f*dQlJGJ?s9ny&?( z)OIe0YYXWRdzbd(&-d*gv`c9B~Wn~T$pTd{{5 z{@&W-LS)8o8;8RLkyk7oH`sK6# zOHUs;G$c~11&(2$rA4z;K?@?7kgR3}d0)3NU|-knA@?Bd+J<{BUXbAWw_)KVltBfR z@owFQd>ysf`Z@%^wSj>k9e0})Bn6~-`{triccu#<%$<}IS-B2q=(|wu&gBEeX_zw? z{PP#oBY$-fOL$Tqv(A$L93R4^0rtiJ0`VOf-gU2;;GlsT$%;1#U{^@8S ztYJrIz&529N*q~4&?GCB!f;yW;qtZVvKT_r}0|^u>`)v7jOkNaz_7u`aGY%M8 z3irw^YvFt2P;*|ce=)e~4fdS=wah7hr$Q}W*=sBW|oYCn9OfaRoCqNuApS<}J~##<7w*Um!W z{3W0_*rS5}c5fV{6Zi){W5=I>$sluJ^J4<_*8m_SF!Acd5rWVb%zLY&d32=RQ&yb(dL@Oz_$D(3U^{wIATfpPJ+1E&Ptm!OKb`> z88!NwR(KycW*uAWSQC(XjmQKUb2W|B;|;EwY*tn#jcUtaD;FP8Rka=E^1#ipA&di7!h6e0}2f!2apvkg((5zFKq-UmTrGG`tt-3C(Lsd zqmrDc(Yd460Wp%`!nlqWDGC~HyJ6o4^a7D5ps9mX2;wMnnJ4mX5{RPb#!EF&-u2ZZU&-#BL~wXk-(4 z!fPRQl)aFL&0p9PTEPX0l##fL0$ckd3pX1A$GmAufc81fMB7$fW_8Pr!mi%Q4a?Rv zAEfQT78&%%u?J6rlF&c71EXyRGQPUwt)*~YHfRWCV8QZz%0?OzH3E_O$M`|Vf*$SKzZWMi~- zCf?w@+>UiC(_(f`8$ihVr%a zO32ff)-1PmzBPZ)e+*Kc4huXSPr$eNt75FPGac~6m9yg)Z{f^-bB%auLU}8uy{jM! zOzk#}W~|_H3EE9r0>=49HUs;tfNgXSZD#LYGy)coqE;t(V-!k5uvf5!UB{u$mitCV zuR7FxP^BfNau0G9XuNO>W2dzn#|P1&o=X5Wz!dUywxTk6Zg0)_fHX`zvjojTT=r#7 zpzwN8r?S)0_H|u!c0P>pNv_wa70|0U6`FwLBfUb^kFPvc5m3d;L3!&%57R{npmp*p zkqV9nFHy;tV0*%Vz0aQfCER1&!)UdT{I1S0@b1z0n6^W{RGEi`5i36q%JM~RiMwtN z#Ai5)@T3=XpNtxrE2c$hG7I-qySnyh7~T-vBFER!suxY3?Nt@Ik4sy}DWR<}02MLm z8o>*;)G!$^!^p7lbi2ffwTMPQ!GUDI8g@(Cr>?)zuj`iz;oNL~*Yv;nRrhlIa@sc5 zxZ+jkGajED?t+%C*M`~tuIJvsaU>dS6@50Ou73iOQY;F?aP$bl!`Wa;#!6}Uk9-|3FsSu-i_gw`;{7j4eY3M?d%F zne~pE>#HfT+W|Mrov9APRXPtcnRZ_}p~K^gdFk{%560Uua_j4O<$I`UwZSxM)Q8Xf5Z9oOmV>QSL*!85?jd8HszVP(i+DNsfXBJyF$(q;cm{r zg$I$@;XuoL7{YtadelAu;h*m%a4)GA>g;KP$mqtXyTOkDe3$sE5=-dWB7hN0iyMxk z9)?FhW<@;x)H!G0Qtk6VrlSb1SM#K^)Xde=A_0*>IN|?Al|ZwsJGmqn+zLf!jo=wFClKnaN*L#q|dN z-y?1lQ;eBsEC2w=!2f%SYGdMT@EdYp@mM(>uqNz$QQ2ozAhA%*8F8)2be3$&nr1~? zov0X*mGO*-Eg%~+6pPbJL{~|__2{_);{zfglknAds2P+qq5?Ae`??+Una3A-jP)~9 zyxCxgrEpP;K+l#QHNA2GVy=8jq=s>qcflm zxKxknKTBkpG!rJ%CJv}cPUs<&9{_wJ0&U{};hsN?8qup2_%T6Jr{R>Jm?o`(S%ybo#Y}*dV$;!N-9hh@1udYoUMVjomoFd7)RcK1Juf0u}>X13O&0=j8qHE zwA?HbDsP39o>L83VFDOMV{-oztgi?lZSV1wY;=3Po$4E$9G%|>z<&;xMh@UhzQ@ks zcj_UXy!96EeB9(;IMF>H2XHvKxVTD2>|(cn?4gzl3Y{1FJN8NBc-vyAZ+m>+&h5l* zbU#YO$V#TeIlT0`>_dfya|D3Xpyf?9FR^o9@xH(JZU9VnKh`L<-oE|E)k56bqV^dj zKSelohBxH`LE~)9#PVDb4_9U-KW}eS)wIPln4R3vxmS7r205l2L$d}@YzdF`bq@p@ zj~nL_m8U)Ai6=%lah~*2|D7Nw*sOCN=B?zdT*X_JQ(%;_gL;*6X6?=v6@ldAJG2=6 zyAQm3e3}wA!8nyJ#GhTN-@x9gtKg@R1GGU6V0#+n8cH8R4;LBe1O1Tc3oYII=e1-v zY|(Yx4CXdhIQ%PcqyZ7yt~fD(4V|I)=1?c&7O?|)3WDNKv}kZ+S`f}8jHCq3t2p%ss63vJrDHOrilH;PT?EN%-^wXc|4?A7x@$q z7Rs!50;+NE(cv9s$-T*kPQ>$w2hSw>mYaZY20HaPJy8?fLn?G(B%~mbu@dD`i@sem z$WO6GCH7~^#nL(eg_ASo+=@`7{Uc@?gB<+7rN*7R^}T*NBA@vR9KrDq*yR_cT=Ak0JkcN+sxe35j# z2T(3Vfije)o<$x_fUq!F0v&S88>#kwyC9Fy66Qy<=AYP_2_Sbci6#7%0nC($6m1yg z&#jk&gNkiXln!jmJxaP!XK=23a9tWaTJ5xZK<2k%WHhvUBb-tTPC)eX6ZC`G7>zKM zY-*m+bg~!kn4Y~42>H7w=dMLxsZg6)n#Hz6C0=G^5Q@S+|M#?N z1l&k82E-@&asz=3NVV&qz*03#tqxdmGr2a$ESrUA*W|JxWUF?(J24LC&EacJi5T9_ zzIdMcsYb1u#2G!Dbdk!(#$6_x>jlA3v(O7~y=7H-J53Y4p|6leVQLRda_0q$fY6ai z@cIl6HXKaIUq@?mEu}O(l^)Ar$dd)~mQzV`ARP1~i+!2~3wcMiv!Ql4ck)wkijw8T z?L>R+b@O?92xYtd31bWmfZZYjN5FE>F%=@?3|a(e66B0Z^4j_o{IA~tVs{-lAs4yn)9{PYK~>9g;>=HHa!#7>U_f!L=~J6Z6GoNW>lEJXuq?_!%S-C`7G>_!-N zxA^rNV5FP}Q85N!z(3Oi^&p>_qf(W4hfcAhDZu(XoZE!kcM_{|>JpJRapJ)PV#=>g zM&_+&_Lsz6V(CyVA?o)^0g;Wa&Fgj-ASzkhQ#e{{a*ZeSRRw3A2p-VUCXcq0!4Jb) z{n}gD(l5RAyu5^J0Pmcqo$f2eE<=S5lk#S|Ku#`!>ijh*Z}IY0ff?U`ScihpL0%V+ zKz!lpF!cfJ&P*WJ6F|W@_pa!&YU9ul!uGuNZy^~4LOjnK0qLaCpi8xD(MjQCPf6f` zu}K;OexS3q{eL13k;ThO+ja#46@prfdWmz{rr6yr5O7A63NOQ9ZOPF9p+EyOA{wa8 z4r;Op#F;XiG@OsO4iVjDg0yCF^)BHI#WhJClC-UmJHiYh%+&CG@5(9GZoAbQ zXXrmbavbe-yAsZb@?@VR=YhA81T3rBrI_y5FP1S?#1|~qaDpKCfq2;6(~hrZ+i@`6 zj0*qJQ<1#=z_n#O-^>vFG=w|qhRl|9l2e%aLbE43GyrUhd$|&;UzDN*lItOlrXW9B z6eQ`!#Z$64w(1D%>lrT_qiz}I42QIGs3ZT090^tVx#||H)HX(#i$O0^k5t5hF##`& zpn~QB;sO$OJT(U@9z~f7V__Xb4R6tve?=f>8>@}{Ra4Hlz#`C$iv82yX*s0LyQ-@3 zWjfs^xr7eT*d17_4VE^?Lg{Yxe+WCrCQ+az*|u%lw$0PFZQHhO+dOUCwr$&XzjNo~ zjd(Nvp$mcgQuzk%=^F* z!Yy&7O+rIy9m4xlQuHM-UZiQIF2jqVIi;`E2r~A%9a9|cJOToT7WaUXNGkf-Lcm4$ zAGOK>%tN1`RPoasCPlPrv8S z0hBj`H+B=@jMhh_$e!giTvl>jAky#|L;TQM14r0SNE(ob;2LnTlXnHb=N zuk9p_5a}REOh+5GhD^YR7Nn9QY*dh_3@Pq`5R^1^5eyc5H+*kkigLtVGla~mS9>IA zKZtC%5^y276&EFEqsoaD&;`2NJQ1T$DBy*B>@gC#bgExGY{zXZw10%$kZ}#0UQJ-P z&hPHX6np3F9r6AaVQ$pG`$gZ-U1p&#;~en-OV zOc2NIHQEuuhuMNlU&W(^YCxY&>hPRBiC*;g*+i^cu=(iQ>^N1;7FFMJ_p``IqNye; z`{`nH5pXA!1}>6`Ylu}eD>}J}y)3bu4F@85M1+C37J>b`iAtOeAKL7s@i0F6q#$HM zr3LL`nkeyPmi;Uo+6BO7=60jHSRgi~fy-$JrPk|){_tNvpa0o?v zn|XK~5eFP}R`kAs22QXy9MY&07*xl?ruv$7T$RgOoujfpTA!*wh@}+#zsCl|W*8&= zAlUw&vIEzYs!&8}6LMtzq=|$$_@!+T`ly<0VD(R~R31F<}fvLK}Sb znT8uH#@f^kPnp&3E|LR!zqtthm@~?i*d|#B>Mg${f>@y-!8BNNtgZ=lSDX<`W2za~ zigHG5%;6$N84IE^%|iuOCOazA0fs788eKh1qvP}C?CJP2t&AQwyifVes;`pQ48-h_ zWMv?2I!Z@pbJZtBbvoZo<^8U;H#Tn9=e4J@i>*nA6)QnkteR_jO@$DC73I2x9g`)>jWw!W zWVyVyhN0Z2IeGr7vJA#ZjyFzE2eHydvoAq zreM9TAo8u%GI-P95QeAKGNt%fJ%k!KON+>)<1bCtz$Hst)wv8?20}6m?m{AVjlU2^ zEI6-cG(DYKdOhTmw<4%0mv<^0m6$B}%_PJGc{6RnLTKX(n~<2<+Gm_SGVV5>PulTW z_50$hn3&y1WwWxo9duC9N2MQ+=kw2@l^HCu%7|@)1drk->Qke@S3E#fz1^X4yE@4G zMZ8OCVx~dD*#W}mB?Y#hSQM+(*mK1M9d1Jpj68FEQUk9qY&f^^%ae3t>fn!nsU~;r zIh?X@$alv5CKEhFwIB|6kO8%)wLh~YZ9caWT@oazws}~>Mboy~ph`_SJw>^8Iil13 za=GXH3yE?>m{eZ|ch#!iHK9GV3T-#ea<05{sFuKDdgb!=V;*wCTm6wwMU#WV!Mvm% zECkE?$5Va%%m-WBNtOD^9Y2tgS3=WLRVOkBspc|x@ZkXX9a=@^ifxVA*@`4L<1m){ z!+qSRSM$_YZfT3L^lgSu&7$rM`X^hPDcK5Tq1L4Hr>e*5jeH{@f{&Z+)`;2k$+b*q zlWV2RYWz)a;|tT+r|ZfMuDYc)YyAez_epyqc<}Wr?0N}NG9l9@DkBqDz#;t$Z)CHd zi$MLshW&mEN_qD-hZb&h63hM}frbbBqdP3flBo=Z!0G*tpx>>I4LfFgHErw<;r9(L z&&U*hEI(shvwSX%S(1+4``PXI6yQN3Pe2{Y=PXFr(*C+U32Fxt0d!9rN|dwd&gh9C zM<5ugby&O*9VnnjgR+9%3OI&iRIwcIscVPDbeD~ev!Q=W!~8L!gh<22vZZmF%kPGV zSQ>mjV95S2#nTNTaHkm~BkXGIjre&GOcEo(E$)+GsuRgssaDH7-daygv16@{ir=@W zA|a_2TVf<3ud|!P_Hq2B_cM( zJjPQIjU)xYwP4DPr0COv?*hubRrJ-eV=C6;P5e9 z420>B)x%nS9`J33m7qRT23d@;fL9at_-BTNc-00*gNJ6+k@mj;E)t6pd`MnzUr(Na z@t~;IIk}>$kI`s4%%9KbMm5=u}9cUXqVsA_w}B6jU>D6 zjE3q(N0WHH?+zc$YI5d6CJ@%A*y!-&b^?udnU8`v*}&)-?(cxGB5Q2zDn&A9JbRs9 zZ(gI(i*UMT5v4z^%QBd_MNmtmIFxvY143>RCZD2MD7oPwP$p=MB6M;`>*8W&!Lz#A z{71>ty$J7AE>G&gLGcus4vvAKNUMu?;`({9!j@SYV=^jP;#yXMaRr*K3V(GfoU5U9 zD!K5BnVx0%v!t7m zzjKrKbUPr>z_mzXm93W?^Jzux8#6vd!vmk z$SS0WlnqOI!~?~S&KG)QraESWys3OKHC+DEnm@zdp13v3^78Z^a?R%5^0`@;dDT35Oni2*_8#PM6 zMmQA~teVB)6yNZHK1Ql@vWX)H>Q4n|&Xj+ZMcJU;;zlFOI>5be3KI!DvwM4-C2V?nbZT)##{K(f>aB&~m6=4~1`Xw326OPjP3@)+zVW7%1kH$!$Ioy;LI@4&mR%7deZyZYU(+T=DKq8>j!5}Zyx+bkH z<~Fz>44#bR(4%X>ylPGru*r+?*d@+WX_~EallFU`b8s^X~l1QrK4wNgbj$F)UnEyVstIi@4e;aauA#(ucKGp=50*3WN zxE?y%SUV>8B_|xa;h4MwU?$OsJ^X)uOcQpI%obzIdxvT5}I#<7_`3)djw1Pok&^ba2Lu%_jC~GcKS@##h6ofg^`9MQ5 z6>R({5Rol^7HBVRf#bBlp;GP#8lP5!b9V7~`3q2rXQ4lN=%dezf>_OQ*gn;mEt0tMZXE}+(phs z6Ba6-TTL8CIPWLl4*gXkXX+rXrYz8zr@zdKH)5HQSR(*I%&RFP{czLi0d*WOgeCM6{{3|(2Gx1r&2C&j=m7S*t11t-H4`tZz zf5D;+`s%qY|82YVbrz zGZ6kh*3UDc#!*tjVNZqDu1|Ap`eE{Z#5xU)6+&dN?nOHLfx|D`#(=ChcuRyyAFu25 z3aS$sMl>arxyYVYtx8>r9QX&s0f-txl_VKl0`17a8C})_A&dy7loH!*hzC5Az%#rv z2rXdpk>~x_g*9nP2yt3~hsZQ}k0sG~=ma#=G>Rn^nVD=X9Y`dZ&Jc^3Qg-nOWc)j| zxGR4#&=GDkVIUEbdTi&OU95*VT)BU4-A4?rykA_|&HcVy+!4`uKO1gSG0C^b@-@?Z zz;GgL_4=Mj;t(L*R-1~JG0_?$(F>oiWfRzOTNL8yb(38V)uL79+hi**%nc=ZnmaO; ze&nrpo3<5_fr@+o_UpXp}{PihB^oLKv<}uZ}IDzy0 z>ej6_F_@&n=dL+jt^)`P)}@%lyF1Qk9&2#GFdTcB@I(!m~c1aNX; z*%Ts{sfU6>a00lPB#9*(YXt${0Qwb57jaU}5o!c7h#A2q03+0gd+oCS1~4As-a!!A2m0<95TxA5)gvWOoy^0D(Aj(`}{P=kx) z%Y!V|AT?;c(}LfBFEN}T-mtcRsf1?LlH|~r1-OxwkyXAMBd5o;gp`-Ad$+e<&gyipuc@qO?%FxOe$39z@o{0qtKsnrlH`K zj+x`a!g*Nwj~CjX^b)D?HX;!DL41BZFG57Dk#-*!eC93P_^N}7tQU7 zc`DI`8}99C9gOYj6U#GGT4?zctpIvd99IZ9;J@4o5n6(+PpOB4N%ZSl02JU(A6{>f zx)!Zv$-!Y)n}dULiI|y*g3XGUALDwGcG%xMA7x1pbS1FTT{ZeDq(SquWp=Z>(SfgK zYQIoBxj*hN-)9d{@pt8W1=iRyr8@$uwPPlS^zh{}z zUXrXUMd}A&7J+Rb7lK@Yax7n}oe?|zHwSjT54l)OnrsrK3t%Pa zS>fQ(q{qpTOi*?AVXW@0?zk25XIHgY-ip>=I5IJ#a(7u%O^z>dRx?WxAM%lGVIM*)e^f4~l4 zw6#zSMv#q#9W_a4$N~kYOV^ZmtktK^kWl=ax6=O@$`Ih!L$=#IJZR-*%Yx7zS(IoN zfk$b5EfIpVu$a9%(k?Q_hmc7|&rTWoqGBaNimKFsOy6+t9G_j3JyyEi?r4MY>%<1G znx(1n>*RE|KPgZ5iNG+fVsp&2oR+8RYof)1_ybb%i#!I4B-X5)%m$CW=kMkP=Aaqj z6U?j8z#Dqj`n@2}peMh|!NtYC9N7uOO%L^t-&ZE}=6bt1pV_m*BI{M#+HM!q=1^sr zR^?kE3dc5Cfc-!(0_YDr2ULz%^*dpaCV|KzJWEvc;Cn*Y(=4@Fdx-O!=YuIv6QE+s z^v^O3U*J)HmQdL|sNlUw+3pGb!8Z(V2pw$H!d!Vn3jJUJErWDMnrmMv2navqq6~(+ z!}7ks4`cYl<+W{_}BcVHY^KsIW5H*1_OqA z&Xf)1fRii}RU&wN4Y2g8>Pd+5Km#`v9t>m&*IHNJJk0FNDYukt(DW#}YCBTr^sG&7X0h z6@35ugSc7(8U*peQtiJ8@;5$883qttCrV;5eJx-N(*WJLm{`*Qsh0g5M@^TLKP76Y zcDk#G+os3lIKL`_FjGVdW>fcxp68r|za^2BAtJ)C$6mp3;8S4EOa=B8^sVrRmZ6|- z_}-8XT2z0*M*~Doo^e`UcIchGA&M~Z3}Hy5{xa5uSwzwW!6{pkn_R_C)=_hhAu-6~ zO(&X*>PKliMEgwNcGGS2DV1B&ksN!-D+H$hTbo$ zcp>QP0T6Rfa$iegZ~*^~FhwQ;4-Lhd3C9LDW=`T?>8RSy-4tW;9tZZByn$gza1AF_ZSp2@);Efp~ zl3t$7HltRv1V*V!(YoLjTIxn--nzYX?TeJ7S$~UwaFQ6g!iZL;2RRAI%u{@Cv%wVmonK>WkpQ)jfrS}bR zcJcLnIfPd+1E;zU^zrZe=joAaZFOTYsqQTLB!>OQy**lMfzZu!UmK=*JPu$WKhJR`1JZdES)5y0ld3V0t zWcDH6mbxfX!Jc|_h?|qI154J7R$-I#$lONVuI|0W^VQT6y&!eS)pz7hp42>&thndI zZBsWu{d$i-x3{sB-w-qa{h`RJ=ObYf0qqw%k}PwpRGxP%1+(tj@==c(GDQ~fcg&Ey za-SVXNHNYkOGq0v4L?h$zA7jc<1qWTn%Dl|N6f#^;AtMiq0I*-@FLn5xCN~F=q;fR`)uzwL7is3 zDyVTeQc(c39q6M{MRqp7FIjJ;+~50W@_o3|FYd>~Gj$R3YQ5i!|Ckv2!1iMnJ@ zg+Aqu>3AWSF>Q+~J+><*x>NNeN`ox0)VA*wefGGizIady+Fd?X?*ep(M4HoQh0SFJ z76d~YccC}^+UEH1GKN!0=VYgcywV`tqwNzTN|%NY>DDyuB6sPaqfi}~MeZ(}Kne`5 za?$hIf=^5vRNjhaFIssif`rhr!9oBocJPnnrnZ4(qNA0Txhs05oYtjM$CLpxiz-4Q zD8fW^!)r`9Z+nU_rEE7(N|kakpocN{*{l5CNdIYt+=3vCR0wnG{`<(K;#VVlO zFv)2MNVbds8prIHWc8v(iUl>2dRI$P!Va-{9oz^mya`G8x1~*mLdFRGHC( zj0x)6tE{e%&zXx3-%9mQ-QP|f%-ow3f8Y#)o85Vpb#1EI^px&t-vj+Fs3u>o?9wE> z`hVVftjmnpgy)M&XA*Lwz2@GyDsSSc=3FnQ?XyrTJ-cNu=|l|`2c0yKl_{uu*s~G5 z4X}Vr?}CZ+XD2sG*Fh^|b+B3NV@@^(;FZgx3wuj^PFMpGwU_gYtoO~ji*=jP5Kwo= zZF$^nZ!mok1GH$1_wMOm!DkWw2|4%dJ9wILVX;|uJz^|ur+SRZ+a4L1s*D#6)2-z{ zbmuD-&Rv<*E?rfY-vFS;&*NEgGBT6Z!^TmJs&vmR*Q#}Lv!ioH!=AJuno+6Er;B-5 zBC_U*)zh$18+t%qdKshO75IM*p0d$OU0$ow4)Z>28~`)A$~R z>HWjT8rl7WeE(-*JLa)++7wOPeWsSb15bd19BE)nQNgX9F`J~keJ^M@nk+R(1qlR3 zLWE$zkVjH0`@Y`Xe(Zx`AfV)IUGG@56+xR@T-;tmM*Si{{XM|;jZYq4 zh>GHoIIS!{s(Lz_Tz$HV&V?**GBi;^!BiutY)G#@l$ITX}R zo~nehn?TU%(q|6n6a^%hG)bNXiY;35eiDO5!5bNdrDlhsk$BW-i#)hApZQblmXDW0 zQ@ezXj>o%*{pOY-8?Z%W2iVX+BdH4=FxRc1oPDqf%m}eIM(wI*vA4TInS=vE*_D}w zD2qs=-eSl)!&&*qkWIVc7)2N6Z2mg1zRFj*hSUad#z9MzTIQ$n_0-eAx*Z<}XGmkH z-3?Ic18vArR53w28^`^Q*(f568qZNaF`6>snPW0BK#f+7WVr`mB+rZv(92t)su8pM zfoLw=J6%j_WS_N$JJ8dT7Ns!v&rJ+ZUfR8Q97c7`X%SAiA#*Q2O|6;5x{(`2;en^O zLe09s!O@f2uLFgcLHdaWYG}VD@L8oHF1@GM;ToU%!&rAc`~9}@nDjgjUeQ<|CGHod8d-FC5y2y07bc-}p zjj$U|uB{Ub9MwmN&1fSr2!&VxG&NXp8h9xd{lFqIb3<}w=(l`6JYg!`>?{W|nIzFm z`RashHzJH{^_X~i45SA!i%h!o9;nCq>ZQ>;b1U=a-XiM=BJMy3%A>$l4Bs~4YTbu- z(r2s3gZZK^z)k<;1Dc!zbt%Z~a6|+IV0KRiTaZMF`p`y)bdfAW9Xk*wJ{iwMkrW9p za39vn0rw~oWZ-9{8w^dYzboFH=+O8X=Mi;GcF| zZ+G%GxQ?J5$(l&BPCWO{M<*$!@8?Sxi_{Mwe_UZz;&g;a^GHrk+G-eOZY=DP$BuAU zi$vB+62sW;2tDB~a#0pav$#j75aU&Hn`OtE*C4+RoyCH2*WEMa!}cVaH#Q z8+zLWE%&4dIA+O7U$NHl@IqV*z@kQfh1m-QnJDWLa}t2v&Hs#e_C!DpQJX>3tcSjS ze1}R2*`gkdK9)O-xJZtaiq$JdEn}%pI;m8x43m1QJ%Y962@aqNp1*)Rlh ze?GilExw;`(%BIWvWe3tB==!X)8Cvm=krURU<$5OVfpX?&nbwFIgpcn3nuxl<$uP*qlGkJ8?13xDKUqA z`~TF9QuU1V=uhG|=}Mi9@ZFyP#8*X=vxR+kb74Zo5uBmfLWGTTX4{@;wMpgzOki%U3<#sNV@J_q) z|3$?s8T4bNydn582_LqNypCo27CQ@o=yG0%ZfQjB_F+blu&S#Vd zcL%978Un-dcwNB7W@)$iFuQbAu}`h)SAsPFtd!YQsmv&?l5E<#=~)*HMdfwh1w#zoKQ}upQhrj*l=MY{TBFTBMl$mZCDt%%n!3%)?(8)6)Otc79ra6qkQThBQ zOhUiTZHE%a_!^CsqV{1`|5ZY=&LK{kkukl6nJlvucnR8M0;OX_-Cn!}!<#$=N@6wg z(;*$i|Ng-n_!~k?vMtkAkCX<8GlGEk3^BYma`gfoGmrLu&>rO)6B-F>gs6Vl3tJ*+ z^H=TW6m*g|wNTR2@pG|Pcm@TncE>(_CJF$f71~4!94r^=o&YV!Jg3mB@X^hMgm4=o zpQfUsA#7GVdSrPDKlT=3Pw>OL4<{RXOqRqy%_Qu#+AFUM-Mpm zFkbeT3G62@a<)o6mLjL%C6A-1#}75*;BueTDQ7B(NdF+P%Kj(gMe6U*)yM&yu2_zDk;tH9+E%TE7DXm|a0IZB2&4@pdb z94F9nJg-e&q!b}S2})oCiE&8$mCtp64IEdM|5iecusQOOZqaDX6wSnG70rtGF6#$* zEgUDPVyQ{8Bf*SDA)dQbRkx6Q@q|T3_#6Mi#Ors@b+|L_J6wW>#q~`?p%j;^sJ6SO zg)i_WJ9cE4GPHYx)@wCawVd#RW&5_OL$xBp)_GN*qNPE9&NUSLYq=aQ?#g*+?}XN?ku%>sjgmg`4f)gP@X0R z(_4wj*?WMUXL}hZD0wJ-)OusF`f2mzL-Lgo_kDSyapIxq!E{BiAC0h9vi{lh`KFrQ zj$ZJu&&T`Mc3GUuOJKzmO@x4-i=~h=jWm0xD})Zm_hlu#Sv3@~3qA(v2>Ns;ZZjzc zfSArH%AXzlZCJWLsv;02^y=`#0F4JqMg z!qb910^zjZT-BobEPUmXVN&w}oW6p9-uf$}weUQ_hFSuBR8vIP@9`Z$OGRU5RmJqy zik9KBC4Yeht09;rU4}Yt8gFY5E=J;8fINBP5!%q-jsjyhWx)|@*^`2a=hm0Ny#UK} zI)0bLw^yK}{&mg*(#*Mp(|Yb}7T>CGVP*$WSNw=J?lXqwDrQDu>1=UuSih_) zf8ooZ&o>BREBa&=&HKxV2#+13l|zuhOaZ>n%+I~dapgo&HT3c+A&o0ynIC|t5eZLr zR}|vj^_h4Ou6>ewBoyQQV3y2mOZai~Hc%ifnKoX`V0J_dvMNrs{t)+a?-?($7S~^# zYK9KrwG`_?#Yd=Jg*z9`TDU=yD5qt=`gq4di;E56OXU5n_G6M0!D2!S48k)j5av4R!RuF)Kd`pRzix4EI! z-^13UqSnlC2A49!OB^pBPqlg+hZUQxf|+nCVXAHtf>zddT^hxjEu^V^Sz_7sc4RvR z6>485tZBei&&cVIB5zzXni#Rwa~rM-$q`Zdz{9=Imjpwr8I-DbAUTa1Cu`X4OjJgE zgKweaQ5<>`O1lW3c0m_4&H&+}V~Fc|Bc!G2d7xMfRWRt&Nw^EuEW{qTgXM{PN#(+= z+ZKW?kY&?h(^xfg_BZiX0nQOWKA-24!GFmSC74~+`$Aashc;=o^3!P|D>0Pn6bAy` z4G}|m0bDA!6d=E+vldds2K&|fdox3NNh3m>bAzu089WMDzFYz4O~5pfcW>BD^Q+Na zpHqVT{e$&q=J_oBIzF}^9c(D2J~+SdgD=yWK5b&Njr@H~-7;!id28cT+pt!1a&_Y& zJ=(&F!Je%G2iUNV4_^E9QzuKB-jrr)xnp@4qe6!sJjEyasY;nWl+=Habd!4=w5_R|FG60B9I2m}aUw7H!GjKrtqsVg68N(>bG;N)x-KQN z{bkn)Kk%^C3w@6RMr##|znSMCN^V+Q{!FP+L-p?0~b4u!9(>{J#rkJVJ#8AcEEc50Tx04U}Xf5r{o8Wai zR6f|^dU*ZL2lD?+Bdkt%OC5rE6E+}9%<_ax;#RS|;mI$;Q3>^(o~DxhP6my(e?xQo z6XC0;ArID(>%O|9s5lxlTmo!T3<}AGT*DF_lpD)T5@1{Fv$!yKc zN{d~^5y`;PIsOQzqrLbz7D_hGU86mJ&|1v zW>C!%ZWb;7)^wU<=T^S(7Lf?DOL#=rv|H_7Wizg;-(4L?@&E3*ZftOhEVq$9M08ci zDHKh0Pp>k2Lx-s;+)|Z7*-abQPE?+Xv==XZvE3HjcJXt#(+s30)kEJ7{+?fISlayL zY0oHL@tydt1h4^8JtvLB#@D7H#A)GUnzz(8@meCNz84M#jkt;F++GP+*JZ}hq)+06 zzN=*9zSGt5rvo^qFFdOP2O32abtt!a_lWr#TG6%juuL9&3@%43=j{g@zEiJdQg$qVFbVB{s_Yl2J@8;572?FyHQm z3Ry>)xiJ#s51gfzwK<#=)FLz$A5tY^0RdUT*0IwlJ9Hsr)QuKGl`u;**AZaRK3KIB z)Z#opud?fA=+(KuJjjjuJt$gJ8B1kkw{`rvQd1M9144tX(uWHQ#$&RUw3W#2Gl0GzQ3>5Vlpy3sL{Ee)N(PGb0nT_q5>W-Tf=EJ@b7h;3`o%XPR{oUMe zyxLhb?kVD#VAF0Y0MfTY2PYHA89LG*m}2LxM0;;+V@IG@_l{AkgJ@6ijBZn~OSctf zi+5cMMATS&wAm}VCmoEl@4Ff6XOA$w$f7yXT$a^|YJFk%)(#cc$tO`C#L5?clEIYX zvv{Hl4i5Y~sh?1@uEa0B*ZWhyXV@|?!#`g$a)bWE9q}gJ=JuHh_M819Q81ez| z0%Fr+Nkm^`P=mp{%Z#G$pBJhW2C>o>=TUp9L4{OOf$99SD4tUq;HH~TC?f)!==6kv z@F`7*gt;FHNc*sGi~w=kld0mHXpZHukx)IkJh0Kk=l;sd?pj9i@GJxWXT>;eZTQ$o zD4XK%q<10Y`_J~}V%%vN#A}DTjleNl%4*c-&ipIA?^EloLZRj;DiNB%n0)b=aZM3b3HB%O z0vIak-q-iXxV&8c+FRikZN2-ym>-Mfu^D=b$yyTKu!ZX|5K+Tw_D5H~wAgDK!1su> z$?=i~0PIq>Sj_o&1R-e#t#2AD>zr1FrLZ@Eqe6#lC0I-rfjVH9sKPN-VoMonG6Cty zMc;!_O$X2Zk}N*#tjN=^#~|{02b92IQXuf326-h8&|WH`f9(C?;OqkE?t4{VSKU>< zeop$)iNFAh>><@7T7)3rBt_(MbEos0JdRzV(W2L2Wrs<(P$!p9>TeBpKVpSh)^zCq zk}pZgbnHqUl{OJ6lwm<5j1Fmil0K_e4q75M^T;)b?~)%u7M8l!Z)L5=$l2?J)_DIJC7=uhD3i)L&^-@@;9areGC-rszCyq!Gp z_kKCMAK>EgnSVIzhe+OMJ%$7}Bdwm*MQo%=KhmMDIT-e`ACHN%~J~ zZ#y2fIQFb6<(Eh8TeB=lQr8~e7Z`b{SzTs-D`+^fsU${D5;8J3aU9_UYFoq>MkybY z&Bk|j2~<45c_bH8*(Kb72>_9p6>m4|WKh+z3Kz|rvvl;@`ZLo$fIOH^pkwju0CgRdclH;6Z6 z2p&(nk3`i?LCgFW`RXfDl4YJ?;P)iVx~6{&7VCwE?6FQNeV8_%S|C-^xRyPMBavpP zKZwu9i%`Nha>N-QGSQqeoQHaFropPDCX=F7ZGVCN)Tq2!sRt|E5Q2Z3w-3-| zIyvLc2naIlGDQzERkfGj+H?f|HcLYF>}gQ4T+dE7(n`>~YtUMpv&aD84#Oc!ES25` zQh-aDL(5AS4Ss+7M^R`8PAsodCBbt^7kbwNq@&KGMVV!6H1FEg(dJj`lKkd8@lVy7*;V)mFZ+fGJs%2*4i+>|&dHF#7HUT8SJuY|@zA$THs4@^nfoZp<%XxSQ}K80Fjc zm`=Fs9}W4^H8t~Yn zspL%;0vR*xS;J;la~PQ{ zk=}83;#&SGzYthi6&bCwq~r$9#bi-C`qEVvggd;9i#2IO@344}pBZaJ#*5y;yXXUZ zMXESeZT5EVKG|Qi2J2Prl+!lN^Ez@R0!5uJjqLs+j#*S1<~MIqQXm`U4VHs$-d{Ut ztxEWS8Ml58wWSv7$7==qLaEbTyXxYbBg}&={Cjg1j7ysadKHM#+spY6M%Z5Vx@=cE zn?}XasORhZEgo{=jwi790{+;b9eIC4*9(h2nM{oEUbt)xTGIqIpZWDh_e^OAW9(PcO1TzpaRr-w-Uv00tnTX5;?{>||#c{s3q{i(uas zs;-3Fz+MPQVJrdx)ojyC&&P+u^rR0a5xO`sXV=i;W!@eEtv+oU&5XX-&G@v^WYZY` zk`L(EAPC9*EeJ_|@aDQ4;kcD1(@0X`qc^)33)eR`;MuOUIZ?vW58(3-TGYtp>0`-Z z_7rl@1P~zB`>*E=9kV?`h;gTKUClK_?^c6FKz{>2Sslj}M*XJdAz7yX9q!vIAb}-> zC@{-urC4TT%MM!Oh$+5VNrb>z1%R*A7MTwNf3l=_1h)-W35G!Y(Ax%K9a2QV*h8Us z4?05iB9!SN;qB~0&TkP>>+zP&{4eK7z-ZhCVVAnJ|IK2A!2E~ z<#6Vy9T~RqTTL=O4h(+couv}m85akhsh@!c8c>TM{s9XjL(4f=U{sD9hKovF@`KB+ z~x12048?H7DX~$(Af`QKczX_-);duIG_m4)^#r$wmK%WGQW3)lRI;;<7~6& zInhKxtuZEFC1%5SkV90)a<6xq@owe{gPblWREn0+RFjzF=jaOXnj6lhH}}9ttJlBY z;Z4d!_gSvWIovGF!F$9Q;RUkWQsZ-z?PxUP$v`E#Te8fsYvinm2bK>U`OSc$WFKk1 z!+Y1$?wET@i)Y}?Dy@_%K_XQ;rdeo34j3uCO$*o6*MwQszRz2$k}IWk9hYl!*QHuC zj?~ZQuMAk#vNkLW*XyNqqb&Lkv-zTx%Q9-=9XifY)!{5OWqaKpECpFraPPuLl=Pp< zYU~=gEY>#*muOKuOvnwfyeu)-$5r)|Pm+VfBybrR%xA%>w@`fuw1u6Y2DK6tZFLbc z|2+84q_3MOgD7*i`lCpbYZ=YpqtA5{oZ?KYYjetc=KNs4OG{AtQhg@?U8+yYFi)zz zNql}&nNZ|TOQC&b28tSl;qfHD@1%dq^spt;?Onuciu!FGQNC?-p}U0Film>rxh9c%^oy^_Ct;{42>0R&;Z6qbN_VgB!o z5I^BRLlb?J_c)nFy3V8{LCHpWV#$YW*}Xn9VXiyooWh@uyU-JIiEm9DIhw`d;xgML zcB=}MrlWyB1Y0GCidnP6?oEEI%1Hl@uXAkD1lX2!+qS1|+qP}nwr$&Fzr_?x%es&iPV5pdu<(M%K!FRuw=8oqG2T+Wl8&ii01Bb*;I=Gtbm;RFEBqvbtJ^ zOjKB?DEo~0XAZ2jN)#GP@4{vFmbW zqiB(zeQmuc@Zu48jS97MoyN;6t@hE9hEUBkQ$gUT#f&!g8V5ZAu@fo_I1yt&&Dh&S zUtgI!L6UDFwfREahlTC~B$Li_T$V?yU^)f|TAg)}{UF_3dU@e@XSwA^+`R)wlY)6V z9l_-)A}-ts7pwF6ycc*8oAiD;_%ivg@891n4tw#rUV{>b>Q0yo{byG!pKS zhR2^M?kedL`li`Hj2^2Z1xbHs9R5j-pW6H+>A&B{`>w0ctNw#;1ttqge8n!Q;yK?L0wUwqaAQ+E+_=AmEU1Sm&rL%jtD-X`nc-dGq-W zmcPVY2=vCiNE4m+c+P**J&9!xJTox(9+8S5SJyH0#~i+%2yH{t%F}V63)+kbt10{ z=8za+xM0sVlz9*4cwi;#AI%BlZG)+Nubl4{zudOj9?A<@eR#@DGO<#*2x&sbPmRN- z+Y=GBcBsKewqQwT-`QPgy_hUX+l0Khc?Fu6_|pX0@vQmYo@3To^}{?|qL0th@B&7s z8_@Yz0RP7~w$-OWh@+VjHM%PO2E^&+PbAy0OXY)b5zu$q<+@@6vn z;C(XK$MP3Vzk+;W-ga(@)q=w-X$7ZQHlu9T-0fhsy?&%CopD&7s1(}gz&Wc9WSUTSx^{0z zgn)P}qAf}VL0d>1+q4G^xZZ9pVGF+{yq)Q>N>|>z@S_RlT6@GN4PoX*3|?8C>xbjcG5OCQ_UL?fX+7%2^0tYq9QxJ|#1LjGSG z&0{BUZ;!~PxoB=yvl`Y7*I5r(t)mj^ABj$>eLidI#A;^cZ1=Y)m;d>kAjq`ZJT}iSfYZvsM z2*6-9u{IRv=BiI*~BTqZ7!$FSE=B6#EmQQUDWeo3J z&J(_*kU>sbynYobkWNfE7{(p6Xld8cZBH;B4UNmzt_B!fnvOh@z4`lS=?rqd6jOjl zfLp)|xySSEb!9R^Jq}QW?Ste*NVmXn)?%O7uG|C5w?=ctv`VqyczjW|QhHE3f6j)0 zi!{|$H^01v#~0UB5qXk$`WvBJdy%^rQn_cOgXzMgMuqXp3n7=AAyBjB59Kc?Xi4Fo zR=-aEuJNWN(r7!m(-Fn>XxlXwC{ZL^B%8I-3P&D8o=4vu5v-qgGqqlCjaL2^{pPyM zuiX4m)V%B1MxNPL|B#3avNrwg#(!B+c3S0m8*U6XVRf8G1V0YV{42{^-|f@-jz(;)E|Xj4%E4nJ{f?DxE4z`;K|xGa6;?A29p3a4jR*gh3T9Ko zU$-28HgTL3KI_4Np}@NnNeiXioz$v@eUVy6Cqfl{Y84~gHCoY+uoa~c{7{d2p*w+W zA^WRmb{7<7Cw@|UejKe#7*TeTwm_bU z6{cn{6lsYDXgt=naYz)MYNjb(4KZbHzCt!=LjH234Xj*{tPkd$3i>ZD}sLt~kO#W3` z3S?{|AUq}YminlOyf(SbpcT&3u$uSJ@Iq00VVp!hZZTM%9&sFAeZAt+U4P|14pN}b z*>IlSYj%eD6v#JQgkGYxAd@mUOLxvnq@>~fpX1Sot>z;N&*t;Wk#VG=*1sp@6^w>2v$2_zy zCSg^6{XuWSejl|Cc5=!<88ZLwhVDZmcSp>>3uu-LsOE5#_?*wb84Q^3|L%Gawl2D9 zSeZrj_O;GApQ7O$S%~zwQVxluY&$!gS0y275?b+bbj#XdBX{ZlZe`C7Y0`B-KGHh2 zWHo*cLDoj|1hn~EEn%=>p#a{Ht7ipQFn3DiJRNAawK9uQkY4yt+dWsw1H6iO&PfuVEk32U#39S-tqXK6 z+Qip|_Nt!uo!1ul_d9N{0QWhM^>NLNdD3^PEeCcD4KHnWb22;J^=sFYDGHvbk;GJK zd9FC(Sn71I>pY{ zRR49Nu-kEpcmnquL2pAy%uR2sq&Zvgi#Fcjn^^vc6*!wfd81QnLrhX&UWiTp2OWsI z`X>GM6^v@${A4@8THJ`Q{1=kyez&ThN|J*=F#5Oe_+smk2F-QzP zdHKoPtlPPq(WFd%r#c;`!AW*mS|;v=Tl2oBCqvjkuV|+Huyc=xIcFxc{JfoFA=w4H z<}Wp$K_&CQ;`*#K=%i$^L!L^ zWj&EATkWL)o%(uhdUKO6T)CDr!@CuoTd2n_+$X}i{B-V}B(__u1S7)TQkq^WD6So z*sEA8oihU(BxE^Z=puiA((I+~L!h2}K7#e5C*6Cvdlj^kWXXJ9YW*d=7oj&(AVlIz z>qiia<5={pG3{AIp-Gg849sS|{F`D-)DwE6DJpY)&4!&(kkg6m!MeWNcNPUJH8Nov z#HDc8mj;{#d;;H>O1=DFcrA?kgWntxKJ$HE{*+xBH zLr{zjMvLWRH)MSC;K{L46tT`|NGsc?0w zLrKMvm~>t0CNj4)z08+LIs1Ku2CP8c8`e?ek09!Z7KGvIq~~zdSC4K1Um}v(A=&;h z5<2XL!lBw@PaX_=aT^MmodfzoX=Am{-o8`$zj%k2Zc^Qev~vCApWoULHaQAgFjONY zen$uw{D$wQm#95l{|n99TWZn;=1s&18xc@P@G5$$rtrbs;5k7hb+L?Ze=bl%huP=( z$lUW?ZL>V=t>q}q(*StCvdD7R&GQ+2v3yL(CVJf>BES$%R@Jt-5xHyx?_Z|GKPGaB zVYumb_j_BqIGk{`5?4Sm?rIS>Cy5CgvNKv2K$Z=OT1*RTG(+VpugU8?xTD4fZ#l(G z(Z>92kX#cSh8_XuN=LSCjq+wCL=UUPO>;-_HBlm%Ickd>f-@!+_wdJc^0GFc-re>c zh>e`O{`nt{J5v*7>⁣z2YCKhwy)O+)d3K&FoEG{_R`GDK*v&F~V&>q5JEBpiDs$ zGW7y8^~CkU)XI=7c2wyh1te?Tj57Ed5y)Ql0R z)OJ)BTZEG|T`PT85RiN;8$U?3L9|%g3Zl^~e^G1>zfE%M#EyFQ=wlNl^pJXx;b6a<Ke~n+leH4ZAb@}{ z(Ejf$aW}JdaQt^At(tZYTkJ^xGQ)$N;H1E9+Za2r%vhF9=7N1KLMa%OUBN|UQ>V01 zAX1}F5npdRNz2vvtds11j--#bp7rziEP3)O{?jg6$iuA*OBb%p$YNw^M9Lfv=qx$9 zHJkIfS+Z5dAFD4_%FG8{W&od^=*m}0FXqWq+Q`$gy4cjtcq(bKUR2gJGVsed+J~n= zq9n0@gj0L8*P8FAKwk7sR#t}ty(l%iP}*K{r{;`_#e1F-i&9hYHFQuB@|5bQtAvc_ zM;0xV`^jG>7x2w`S+ds!NFNzO9w8u{*5sP7Acm-_`VOfhh1rUR7yC^ z;L(*^c|A__F)U6N)}s_u^Mu@DVm7mCniI~`Z~H~2sCnxy?jU#@naCt&)T&s>Wzfz5 z^rdYj{!ne$rIN974`aA=W)oE`Lao0Kmw!R|czU@J&3BX*Ofu`~>vYU@@tN?~`*o9D!K>B1}7IQC5?4A%yxI}AMzjbqZc);mn>u(te~2!r&Zs{IV##7-rR zi&&DL7%%^pW&(13jlE{)(~_x07v$y9+?1TJn-0yeu0gJzYfzI%dBFLha*kbMi_t8> zs}OLMqW4nUq_sLbUiBLJZ@e!(B^c#W^fo!hxpy5xeo}I+PXtJU4r$BN4v|_2tuGRkj8|Jy!L0FsNpEYDf2F%m`pMNautO4Nw9as? zM4_ZD6rFmpKMCDsg|@Erl#q?sX4d+flILeJjta@yY34sIR~b*vgx^8q&7m`(?oK6{ z^^Nm^yuVQ>z-iskMQe^JYx5^B!*sxPo=Ag!k4UgRLf5qy!$6WT_WJ+4enC@lDG2c# z(gb2jp+HQ^gCqn1XCZt98i%!QPtj=xAEdVuiNTXmPO$}XLXk~_u*UGr)%_(@Nso`9$l$*UB=aQZtua#1#w7ITkt z`M_|?f+WnAz*tM9@Um(A5ia@{BLL~jEGM*>bdC;z&?&7_ms7P1GX$ ziB0A;kr&sUrZl*PLpC*01n^X(4>RE0h|yrDHT zRB(!m9%7uT$WDu!G0WSflFcKq`xoc209xXcaR?p)ZXE|+;J}=NDI}B_(k(m#{uqn0 zpa-R<{3UZ33~Qhq;qK#f>^0JEh4W$MtiC|jU?QkUFqwFVl$or5O5!nqSg8SBQIfN6 zzXq(hW)3)obq#^pg{s}~Fdfwv>?uzbpyUdIT@|J(*-UfZ0TQ{y~0_+UO=WeUkOgnE|$tg%BV5C_Zm$PIo-eh zD9jx@GoqHdPW``+N7I!@34QgFF4Hl2kd)h=&hbYe&4gFVm(Bqjh?_@h-XE8v)j{%h zaV_mkm*p^*x4N19h*=Nxy3v}?o0IOWMBbo4p)rivR<@frLM)kQ-b)5Fm8Oi&gNp=p zR1;cJ(SkHRS@RDpXD$NkGezCbYQ!ga_EnDB-b-qA_+(s3_a)vWWvE_9&BqYSDQrMy za~pK+aJAmGl;>yRgAGx{8jIxB?eKpKYym=?k=!0TEcW(m&uG>y-H&6{+Q%0~Yg<=p ziq|rk1MOE!gg6siKE=7s`MC>v@C@8>N_tzq;cKE-ewGruA^(E#k061VV~&-td=^~K z-mK)aF-+G3lSgC>Ur-QQh!Q4Qr%sk`cuL;nFJ~{jG2@DM!%HA;3`JX(=(f{0qdBki zHF}zcly5&5p1F6)pb}hEb_ie6;xrb6y^dd+wEVZNa~O!j?*lElWgx(_tL-_UJAZ@w zhF`U55|=GuRc6!?oBGLw4t|m{Ae?_3`th)k<-URcyKkSlf%SzB8VG2V7zhaef0qST zF8_gB&1xyB9C4!f&({O8A-7Z&19vE*6{neJc9v(DRYtdO^v#?J$kb8j+D_8Y2!~Jl z4P?^oHV}wr23JpjX)9O$bm?5yn>DkZ2@}!#)Oez__-aR!8lmQ*5^ebfe|MHDzf9fw z{+4wi_lzc0o7Q#8mo$yoW2^+rp+!H^(t#Lf)P#}8boE9(G4&QXWsQy^_S>Rx;WlFt z57Ps?0w<9^rZ&z}C6$uKr|K+1ruCSXM3Ji9$zw9n(xq2CD9O@=fiZzjOS|vwLM5%2 z<@|B9%UGozL<^J;+b=fyebK)Uyr$j%}jG;wGdl%L7Yvig(Bg~kEohGE*nb0 z2PQE8H1h*%$0VH*W$Y!qfEJq~>GZtvLSkR}C;GOdLr9d^8?jHBbqxNuAxE!-h-yvb zzu*?`d)XSIO%xO{oU;8j6YKXrEv)ZNVanjZUHdEWM5UOxvNzlpupwM*9V48Nc=Kye zl&I_YGBI;Vko^^xpQ}t5SWFG(4463{IZzaGo8vT5R82n zOtwYa1nokq1q(X^N<9ft#qlIvoZQ^9ri8gXk5@EO(28W>c&t(wZd58-lM~ZPcwMZl z5VMvJOHNi26AnTg#Kc-gf5Fe6i(#|p>5mt)=iAF02~B=;6}3=Ril8EkkKwBv{v4F> zT_XMwiMABlmEf47w{(|hSme0f$g8U6TH&iC-C#;44hUPWO>`;Uc+15We`OsBx)4i~4tmcNv&>B?@Pl zck@5)*u7joz65@KJ>S0Y)##r#h+iqBGs+%$Vgxu5J8uetH~i{v($%};!qoSkD&|7} zoS{M0xt#~MFHi8#V#_c;*!^f4(b&BKdt!Av+SzT4cc3Cz^6U0e7|t)8c#;EN_B5Y9 z9hg6gC_;whEx?H9yz^S@5+_Br){GRZstQqD4|vqbk<=4=LO59{$it$)r4+pF1k+@8 z&Dy!C|L_vEPF=j-|67vk;Ny?~0Y!AhQKeI94NB_;H;xMC8i81j*foJVxJ$kbY}_PZ z$IBUH)B6}@+59E)Ia+-m92g!bA0&#(4f5_6%0y7|lSz3V|DeUAIK~M};meKD`kOHY z#18qljT7;J-ac(bPR1fev;sF#2M4KBJ@!y_s@K6!jD|doY^1_>;GuttkE{GoXGi2g zY9y{)W5b)#m2Zghwu_^s&`Zzes&xifc8fqrHv6y2 z8a#Wmz2@*S)iV2#UHEVAk~s3k1)D1LD;%NBP-Bx}vs%PTFqqh)LafZxW7hL^6!z-3 zJJ3sKhbw2D$7=#=b}u$X+cyYEfp_lK3z8aWjrz{uLt+{&xyz*HgM{~n1pmMQiEnRA zXB*JP!!P4mf2ro1u(3AT0Ei|fU^zsL{jvIZdqnv+8Lt&>} zO+AsUP0kU#4lF^p@M)A)lXlIdGm5I%jzu>E6n+B!iv7_Fp)qU>J3Dq3w=I zq6Rh*e&ARj^1^e?nrljm+G>}s+4F%7r`PG`-FTKN0wD=g1F7wT(_o`&joO>bOKL8* zz~60!TBwAKK|(-8VZ+eYoW$6J^5zRZt-;3({7)@pY+4n5!Ye{){g z>>BSVemrDqiretpRCgC(@E6j_=I5ZvfSC{2XUW~Uk5^Nb9IqzwzlG2S@zunV_&L<% zv48}3nCifLA5QU}lrM`j)Yh2X0qPbOnAT1b63xN14{Y97HU=NK7rcz?vAqOb_6L@g zmK5hE8pl~kzhub;g!G5V+3SC8oi?`H*TePNEqBp`z)PEWh;9^7&{j@@VRBPL>BY1y z#O|zy4d!H(@ngFU=f+e~`J{(!-=QHmu$lLwKu>}jkT*(!JEj?JuxgF5il?AAwY8m> zJ1>$GR6%t6sv`1@$;3w<@gzF)S($3XsgtR}w4$N~=vc;Hc|<3V4s;v;0pV?<3c2Nn z^9KD~Y+)Si(Om3R8#j;*o3|L+9C21~U~SNz%Y1LJ8gyG))%ZP?VK_wr`T@|h|0@3b zhq8RNvv!nyr{@Eo)y#6>IX%jZ;&fZ<9yxjk*hglEe&(%oGmmoCeXYh^j=l61GcgC$ z6H#xCO7CCSeBcvVnqxWRoov#_s-9nH9mjK_Q!s4C_tE9o%J^R0CjZGp(v>8^FOQN& zFKW({3dUmO7<=ePR>&NkCzi_gZ^9oNw-08>_Ksu8olNJBFHmqCfi!+>PUt0ngRQt0 zxpYr*FK)OC_@2XQvW!!co-a*}B=hw(Nl z%eN$v%?;)4IOTS0h&MhN{#$49&tq)`K>-2%{>L`@C$)fF%v{|ZT^$^3T^Rm@NB;lk ze**8W23Gdw4*$f~DE5~97U%1Z!C)0)vPw?W5(sJsz&J)q2W?$8M5^m^AM9U08T)uw zoFv(U>C>GLg4E6l=?$7@za-lHm;7c^jSM+y5fP^IREnB(=(bqwKKI$Q@FOeoa#PJh zVsM5jMfJ^I}pBFwQ<&o)j()P-}Q)PS~-&zx)k9OD@rMGmdvdLTTC8U z(dCKz&53NO7{W0t^q^@H70&W}2z?0!X~!ASW@rPcq)2{^UUg#iMHCxBoVd_2tPmiu+^~+pu=gW!fWdqb z(eJ`X#(=RfE*7sfM1a-&!{3~+{Kvc#NM(L9mO!JszgYklAzLBoZ*C;5a3lVjA4i?x z+|zS<1klri{^@rv?N|Tn!H*W&+sH=*Pf7EG&k$062Yn;(=&cmwpM#?UX$eKkl<-TD z33XGgd&*~Nx;#P@RAhaKs4zr{~AP^YBSTwm~9!G8qbx63M2M3V3+`USJ_lO#)vf z;ioX58*jVFd5Gy6r_-oF>B^Y&?Z_=0ZN9Voppic|XAnUkEGS%^F|S zB_p##m2?$tJG7UaD=N$GB{4@yLsYzeed2WC6(>v&zBAfXLQkR@E%e|`Zq!#iDmE;@ zsqGdXfPD?!*a)a*cHjsWZ3hXA6ZD~YWhuYmU!paJ;((b|46H|gaT+!g*#GdN$&F1C z)}A_|tk0+(^>zP(>#^1ua#LN)jz#}d9JZwD;pED9VaH$6uCjI4dwAM~!PtE5l(S4A z)fy;GOLpWoKFJ&OJti^_Cb8qoi^qraYCQ*yDV8qAGZAn|F``I$lnOTB09sdjNsM0# zywG}R0|jKGCQve!@;$m(Bz2hx8{tCC0!N4HN9YvT$P5L+T1Y86O44G3DU{Z zc~)d9UGa;*P4JiF=5Rb{*tBuP^FczCt`lN6Q{dlLyPP9c|N7#Sp?qa{w7u%gxxtGF#aU zQVbskW`)thXCWVsGR#Nypvfl_krdYEOE~-4diEo5#tL6em28*l7!xad%4Iq|3gO{ELS3GyWtcZ&J)j7;R)ZpxB<#A zbd*%hkb^@lb$c5Ll{=ak6Gm{KXngICF=UlAokvig`kDvr@-#d>`8>7%-Sgw-@0vov z+pdNyPAr!^-~N-5U*(t{=lRu|gq$~UP>f2E_%XCXF zMFVkLYt}g66~wmh>;vCyTpj zhk`zDgLwu9NtX#fIe!a7T#UKNe-H}_isP4uNd`9&4_aD6brWqj+Le}vr+wXs*I+-& zioFQY04N!fG!_Wo%ooYf>sP71h94771*aDag~Lz2P>##nODa-$r6WCoH9e-~M8lq( zD!|~tf`S_yg$0i{fJUC*WZ;iQ3N2*e8EXIT&R|9fPHnFJSb3;psr*p88xq%im?z3 ztu@~9<2d3q?m=hXLE-M%R^JK=dPU@FKV7?cbl+{8FTTWnj`y~*Ao0rEnUIN6jJ|a7 z+Bdjx^uI*Oe7uDoITc()Z45K`;M`Vn!Z^j#b3XROE0z%=<@xzjpYb-oho*!Weuc}V z_Rc_#8($ybubcz;=D&@+&9Z9p)a~wtB3?~Iyx~N9M3fsfcOi`^*>lj+-gXjMuOAAc z{ntm!OfH1F6^PjY13EP{)(F1)se0 zzu(56@VdV-Cka$q9mjxN0y9j6Pfpz`N| zIn`kcKS2L2V5nGNAOazQfK2|SHfa7&0psRsW$R*KWa{APYUN=6AKOUN&SmRg*Xo=> zP>Dd|6jg4|b_Bb}r7nHC&irV)L~?nC6c1MBzfCg9M9{!=wS*rZ58y;JvJEG1xzoQy zMFXS8ZyOc{f=a7t%%zJ#jtXvrvig-xU$q=NG{X+F+Kt@jrX1lms9@!=-VXY#gFmT*Nt#(KK-qP>JX@Nc~Rr-iB2Z z(CZ3@8kiwqK&(Bn5~d&kZwcZvqVJ%;;}sq>f6xnSTtTLV)tuj_?3K4!r4RWLbrHC& z$Xl#pDP4U*9mYn)G!%+&#aPGqQl6xRUGw=qTg)%DeJY4Ht+I@+`Dkwcet-6Z_6cWq zsH7-ngDR%R7lVD7ko&*ZR47A8B#5i$sJt*$(Fr=*Kl6qK>=1!c0Mttb;TUqgbZ7-> z+PT`yTHknUajYyS;Y-+9XzskPPU;-!UQ%K$1T#Beg>djPa%I}-Q9QWctVKhYc71#M%WIT=>}@THQ>}8-W-jwe{n-fkR;r< ztr$de<$s8EBp}&;NiDn533{1Suxk<3K~!`wC#TB#Nh_)!BN(*&KsZA_$xieT3&=|T z^_JD|!-?xvlhtrW;mRvrqhjr$sm7g{)D`-t-~ID?d|%cq@d}lGe3z+ZkXxz@9+_8v z@~BzTx|DM9m`fH6hrLJ1jM$dxpm14uv9HH!3I^(yM`A7>AC0+kdJO>6qp1W1^ywMX zOK&l&qZ8n86GK6k>?o30#55rt> z(L`%PNxQ14Up`obG(T?lja7WS_i(rb40gS}CQUl0fbU^W(6Pe5h$J5g&|Xo?TjYP~ zUgJ3*>Ck8`rQ-ZrT0LM{3LDvFgI@M;M3}g+Ks8D7@AMg5*O{1T4DBPvTrf*of`_Yp zk!UoK(dUbZ9%62{MSCVDqIVoK z=_9wY6H7Gq0)C7wTOm`ueJSJG#RHLDDV8DarYeE1%Fbe@KT`Aa^=3||!Tn(8&zi~j z`g%MA{@mU;d&3b@F8hndd(D!~xlR}!=kkEUnolmKCFf zvk8WyQHw!b6yxGC)+Y&NO$6m}E_eY|RsTGqwdpyfQc^>F`M|x1m(r9n7do(>gZ^8o zK-gp}ZXFYkQ2N3ZeTk&iP%L+?5`_;^S&5raOs>hAMY2>R-dlenQl{L*iNFggprN$Y z4HiTJYs0oXNi_it?aQn9RHCY#LfMyOrPmt1{|80vp^SxdcYLu2(nB7-f}HEHvATwQ z@L`0gfE55}jPv^hm#3D4^eDWx8B46|;HCIeP7NCz@8I9gh8A0ne|<4LGN9`2Y;+42+XSda)JS7e3Mm|()~EY>j6Dt}S` z3B9g~`fK{o_^TyXxf)Tn*^V=mM*0ey_bABw@tpTyg})3SKNe6WUKWVV5uj*=x9QC1 z_;P=>dp~>!Y3`Y1-~@GLoinVjB54m(0(Y~Nb9jCh{i6RqW=i2{(xe6`SWJ3V?dm&p z@e}6P+1=Eci6=_Q+e?VwxMf{~CnR?Zp$OPW%(Eo)#z*Wju7;sUvEzRM|NOd^xI+&c zJaU}0XZXs=zWRDxRwvU*Maal4?m{RehoK{trDZ;04~3>p$Xh+0DQ*ruGg`4a0eI8c z!%BB=rla`6^;^$Ey+NVerbnco7RZd1Q|vLa4NSf+LF?Z3k#c?K>UsvYDC{q^n>}}L ze1Q073{;|u9&B^27eF5}GI9jYT;azr~d}MzR0n*qbzPZ!EIv zS&JcNBv47_*YvJPDpGU(70x^1l;Naf@df*Csy-vi2{6MGr{Yirp`KTPs2F zus5#X@KOoX>Z<$#hcU4!ff}5BKVSybZbmO~oYUdF=JVBRx>6&m!|!K7NMXTNjz$p| zSjo6)JM$i}Zf3!XoHOJYO zPIf0NzA8w#QHqoV!~rC2-tz3~Z>)dj)OYlE(sxo-%>b}tnV3qlLxvI5r>dc` zR$E=IQ@d)UTceqbVaA)&sG^@LR=wi=NSeja)MHvgOZQRkoDR2=L%I}@#>>!bt6qWG zY|AaTSotUG0x_nehc$bm^G|gk`}5xP0r9+e4`3xa?YlYlNs4*NOorb;vp=Qt`;&Z5 zB~V!7iws$e!i!xak@4dO%Rd_N#2Pql`Lq2YH$fo-R?RMp+zh%yY42>~T>8keaY&(f zf8L28f!RE^o7$Z%&RO4d8gHy3Vq*FvI1wwp>4R5(oG1c1YB_WS{3r@+r>5T9GY`!y zA1=cz`M2i#d+$ft^dfD+c=ItP0THd9d&-Fh;~dl<^jdYG3UXK=<4iwifrun)nzA|9 z3+VY0w0pJcef&gWUoR^V?PcnO6qqgZNo^tMI_eyTrO3Wjm_yPFA^Ag73^OY<%o5TE zOnBIQgD2gUci^(Wp}VeD`X$UU=^xio6?n`(gA>4E?{9Jh49|-CeOwD@K_;O>LJ_?4 z%{`uv$CLd~#xN9<<<418?me{4W`mIBAITp1u^*vy$zovWM+dQ>%Hw}keDZ&5_<}RW z(epqmVDWYYKkSiQQpit(ju5FEosd}oWP5Ufy&Ji6Ou5*nrea6ew3y4X0}LL0!(fe( z6Y37BP;{l;#PA2WbWF(M z#h?YI#QKf(5VV0(Af8^Tv5%%o7(!}r1$aPI6S9sL$jp_8r3&W?h&qJk{WW*bU z3zai&aC^}j9A&gfz*LXToKjLV?0~Im4P|CzVX1Ugt1=v+&FzecliA1&;n8^KBr2qcdt(XG61$;eSyq{0cZ;Z~O z_?oRSW`PycaFtqtUnRt5B!0Gu=Wbim1P zI00V!xY|%;SR45ALOam^npnR7>vOPEO+6TV8+}bsJ#^x9xS*NTj>eB9*C6iU&K+`d zq7DStTWZhZebhd?9Nf_*G%fU@L$5@bmJIv1;gu0n^ipbq(u80&=&{6@?2IQiki@J8 zoE48wG56SJbTi_InQxxy$5}zpo;NPS(e+j=}jbr7F486Vd=N7siVUn(Y&fr z7)>IUfx~NZeROH!BMb2Ymp;Nv!{Ic0p#H;#2ZP^(?@IBivBtB?ty@Chl;~&`kJs1fpLWI0mmNm%Z`8m2JS>cPMdPpiQ|9 z1l2(Kk0nPOYpASr`tPAFtA;4plNt%2y(KVkYEC(Ec$cU60E(XFby=BxhAxkE?ZXZ(=a8S2ETK4 zR(nR#1#Ew8$^@Brfs|2?dx!1;GdW=hjMq4#rG@8_WZ`j&B`&dMRa)L0FW?2J5ve|-2b59qHe}^;Q$^VE$meuQ zrGGS^1(f3@%4rusn-R0%j{IQkN}3cV=2fI#us`AF8kAk4IzRXUJ7bM-gTp+CiU8Ey z9rZIb5j=bgTi(BL06<9_t7o^uMbkw_)UuZeEfH8g?R-cp5b9>1_8koiul#ZiIZ2S^ ziQSTUF%Vpu_xs}c)59UbAm2ZsOnoJ)7N&NA_M(}KG6w0EBrvkr62`xl(wa)?_+ULC zlGw%a4ONU)-slT-5}My2r>C%}Un0e%HhbOLttc-pxN-&Pz65D~5z2?nCYztzQJbr>myZ#ed*Q|O ze{h70FS2uAb0->~+ETj}J-usn)*JKz)fX9Qqnoep1T1@O;|_4+4m0VDRS2eSlsoM= zl;L3hoYLCVhFXQ%r<)V&>g4L$c_NMKtX=6L{&>wViI#gDtsd{T)GF<3XsTk-@w0R{ z_|-I&)&wR*+wy)|=VX0hqz@UoM^d^i_qT5&{&?G}4c|7|svO^ziA(26&1P+^*58uE zL-oy8e=X~)ymAe~M%9`m$; z{_LZgHIiS_ z%!QLMj%?=?Z$CDa07XfK)O#{@G@h^l;>xBg-q59t^wX(~Q;1Wd-Q~$_#57xoup((G zlE(|U4MjczFiPKpRjHOI+<^FVwQTxN;Qk+7?{t{ zVjqjMRII~4z6`fV$=uFWFl?jO0!gWb%O-+E9??QJ0fK=7148DYJ2?y95v)!-KJ2K( zKBE!1+B1qV(=v9$Jx9Z2{6!Hc9oVgj;FDqtG2kJ0Epe}Zoe3QqSv#x#@_Xe0ZJbU) z1_y9jy{;mhZj*|3LnP`$)MM{fBkjXKr5dM2_SR0LMfbwDS@oMvu}1%ko9B)@TiYfp z*qkWDZynxAR5MJBy&fb%)ilVnJ5VW&4zcx4krdfc+(kaxxAZ3m&>4aD&HIj8>S0;3seECyU*+^)S#1w$ROUIJfrcDW2W$r9c`76%?I z5#GirvXQVSkZrXeqAZHZAQ@VJ?w6?z2m~Nw@Yn$Z zSnbE6x7S5C+B}0TlajPvm2pPt2-G=t@^?}cI;gZVl~CB5O!QTAT-GY9&>duAnU+7Mn($7#Dg*PJ`>tYd5Kce zKu!V!d~Jw@iZCU%RdUR)jZRBa4@*7Jo!E5<4rv|1UeJW39LL_aeWG^t(y(EC#8C+< zL04xK-o2`fOdvWYvZbYJj`|=F?O{VLD+4@ZAM*y|??lOU_L&#+>A-lljP|f?mh=mx zWeJ8$M~KZ0&M1o*T9hCr4G0JuFdlaJ@9-H`6jM-R>xBXs8cdmFs0qM5n8>J=ZO_a@ zbCZxMD)A|NBxKrkjtt64VQz>&ME$@$va@uA40Ik4a6)t#lq@iP>;WnfwiG%v4tW>H z+{QOiaFwcUa1oO75|-9>x_geR)!=^02TH7sJ$MLpGkONzTrMLa8bauXypoX3Zw^E*E`7dPLv=jISdgoA0lJD>{H~iiv z^}H@Ty)H$7ACs_d6*4@x#~B58sK#u#wK(<)h7d_O5`XV}_pD}zQ@rZ?1wJjvN_oPAdy7Y>cVLsZO> z03XH}2^~c#^*p?0Ky*`T3zsC%zdWaK!qd4|Fb_AtTrXqCKs6moWORg;w1_yWzl__` z(jpR)t6rQmrZpBPa71du<)<~5m_Ei2OPZmXOK-53fe)`ZvlggC61Bv0G?q4jIoysH zabC0vw_xUS%JPYq!VIq{yOG-!(qRD}Cm%A5TY;#FZyLB_PE*F)?@~uo-^jso*^b3Z zgc+CXWu`ZiqJC+Ryd>G4hN0bMtA4`yQk}h84X^!4_qTyJkq}h*-oQ|!8%MIik@0;0 z4|_m_zj*Js98vuw+)_9WSex^TDM^fwhT0lw^-s1goNCyIbTDeTakEtpM=oah$vO)t zZSwcxXky{av>+|DWMwp55sf)|c7R$F9lH;zmUJpr)78Y&QIWW~5Sk8C)SzVZ1)XUb z2@@7p4n@>-(IThW#R#dI8vq|b?1ZB@FoA3G-=DsF{qBIh=krW>gfQ%iPX1=;h_j0t zOaxK}mIg05on*y|hPoq8yG41H$>=YkCH7fMerkEkO6M=Re534YP?^a@gc9wZR#mi* zx2(TaTNns?F_OTvO`zNzXL~OSHCz$!gv;J$@By41ka43wh?;L>d0V~j$2;Y zP;SFYTKB+|;;w7~P2U`Ur?E2VD-W(K4u%#FuCGpoKD0iDo<6wp0(j}K89eg1d)2QL zXfSesh;E-gwZgxm2#>VN6RqMDck94l9pN&cRY9F2jA*PJGp2Y4 zi?Hn?0L%dXbB8p}mgT_c>-gySuvP|4bhFIK>FakNPVG{t3V1?=T@k-#19!9t1>>ez z!LLB}RLm}4*;;|D5>)%*wEX)4D#W+BuxwGb(7uF$XFQXRobS%p938^08KJWz<+YdC zHh#e70dRrGuvpBJ5?>eXPOSqgsd3l$LGj*D|~hzI?M^$%r%Q z6R)c4buJYJs9>Ys&{v{)HeQO5$$91vRcVt@)4Ls`$u#JM@{lJI9fj$Mp>;fZalIYr zNbH%E*%FKuJK@0zcCuE*QNxNrCCTz4B2@y!Z6437(}`-)Y!grRW6fJ4|HU)9B9g== zb)g<&mH`ZVsCJia_`1dh9f3+)6GHJJXhA{AK@%+i39yQd=>xcXz*M`mY^nqN9lwgy zlyc+!;pzLM!xJ=Eb`7URQ}NTI9}a;tZd=?;=rKWOeBk)*w21}Sz*E5X8m!#lGDuOl z0xs!XG{$<3dzb^L(h!aK4&6nKUX-pRA|uHpBbx<{^*RsmTY^*ze`sAA-qtJE8!$6z z7+ylRDUYjAJw7GQeby;;?o(ItY9;Qe^wsKH-t@9DvcriJ5X`N5FL?$CUc zVBJ9n^)v_CtLL{p_#kol2qzzGIzke+DSZ0_e~m{FSnYtohSzOFu>wL0JjEQSY-q$r zlO`FlLYD-o+VU|z(u-5riU)SY*s#)}*YNfLu(k@jYbRIjBJSbc$HBylkbm~Gy>$wU) zhG2riNIQVE(N!I9gH9AykWVU0BAeJO)94D_Xz>hK_E-m$5zDjCr+-Y4naRCECtTU~ zQ|RS3k4sR0VXBqcPfH&+*oHB_Rx3K#4R@=pL_^1{5+72Kw1&WNpV{mkW7;AsJUl`% z#$DBPlUnFi2Z=JFaojbOa?U1GwH4xJ0jgbxSO?axTFp9BGS&y}tvD%}1c+TTZf%27 zEOmDrds~>nT^7l>2frS>!(Nj|od_@G!ai4am2tST%YI%OmZ;rCzD!dcAFLd_xKz+K zg9WmjOp;LoRO+o??O#~ey%5U<9NJVM}IvN$}uos5I z$1Q~=Fs3uaMFA=`s#bxTjNl7!#wab+F3)z{@J({X*~P{Exb4M-49Y|G6edo$c5z|1 z0R{CAPl1+jiF_y~*gi)(G@zp6P|3)VF0!6I~17!yUtBs}aIprP|nB3UU|smks#STxV}A)9Gdz$Qsf&ZIigxzCQPXeqWMZEMBq9@Fr7`t+ZC^G7XCXs@^p{EqUb!Kk|E zpP@qv5k~D9F`>R^CP+I}RY=h6uW>iYW6%J>Rwu_pA4G zwU2BbMY-~Rt%%oFpQu{d&CnVzOJU)O$GRm$S1OYxOa0d6D*1IZ?s;t|3(2hMw z=3dMkc>2!y?K)_>dL!Sga0bO+w8r&m4B=)Tk9!#4%-IWq$Tn)kek@-S0B zphWGbELMwzwo+z%8jn`}dkaf^{9^y}vCUtTIbS{Z<(~#K^laf1x7%|IdkTX6E_`^% z9s92F|^q%AM_;-7c_f>ZcA=aj!Qt_PeXQt95B{GU)}gNqpC+I14ol*Xpo6m=!1 zLIi=(tG9r~#nLnGzcL$%mp}`^C02ug_7g9&!j~xTb29K9b|msmv9|Qb!I&_|hQXTs zYyGiVzdfeG#K1877j`R=dFRs-KMULW4BTROi}eodJiCdI66(R0_I47UP`#kyJt}^o zZY)Ytk#BIhkUrZuUCm*RLzAH^tJqE=TzLTwJn4libR9e(dF0xwx_lJ`_8q#g$nL?p zGmX)pF7S}yY$1V-xdcoy4w4d%X!j{OF7>~DvT{RVXGd(mMJauR$spyL}VAOUk=g!B04EWZmq zACeS5JoUvCJs7Y19=z^ryn06A>0BLftrB~PC4E`WXMtVHJbhsS=9dyK3xEFA>vykC ze>y&7*!Bzi8@+mtaRM)%mq|I}FW%5)a1;^87tj0RLcG`#mWuy261zYeT@ z3#|S1V0o}VXn4Oi8M(aiz;~k0bV;~OOK7>j)?R$Ly<{ZH@#u@|4IXLiY-v)3^}aV4 zM3V2-J!Bf8uB)t5$I8P^qWhXdd`dZFW;NzcR}PmL8)NPF(!H2g?%Z}0gqVq`2+SPI zHMLFjPVwh_!B7OBZ`#%*bt-JKwZ3dnC$S~Qu%ZHukW>ax$OA+R^xi#aTHa3dU4 z`HQ)GzM!Gu@wj-k_$GIbu|9j>>}$_X*IBt@i^6Smt=pxkM{U4-77xUuIJI>YpDMql z-y4_q{oUTlWHbFX2eY`faMQjC_2NWC$8UIZSf%&t#^288_To3VMAUf$7-zG01fPu_ zlEL7crtm6!YQp5!ee+d*=XbJf8k$zBYJIpbw%tnjPrf~KtAEsQ@X}zLmq*`LznW`o zHAQQOslggFEk!EPw0mC%eX!mmO^|I#65A>zn#$dO){;DF<`GLHWk0295@&*{Ge;Lt_Kbi!zv#-d@tE2T4|0lTDswkW z*@cGxg0*ihvx1HRphLLUbi;x7FDUYN*eh{N-IoRc%~@kKb(Q(oDh#@9>lCT^K{;@? zP#4^xlsV^O2!7zvtXi*KVE=0bx@@3c!f@Dn(c!;05TETF{OVc9TegjN6VLWhZ{iW* z^kRN=Oc6PRHdLgEE!5f{+=`hN*AwYEKVK6a*TkFXdBB-fe5{R#pkWLRFiBUuq=jv^ z#&%dsM!mL>2J3`bF;)8*w(P=GS&`9F7#roq^@JWd=N2MLm)}gejcbSwjY>vZhxj+w z)W58f{g0`YR#L3#F1QU~ZL1XQhzrcZ&?7J!Y~SyW-oB3B9KHSTC+gZRwYBAm3%KWQ z$fkM?X8NXT4d%Lf9#1o>Qd!FUt;i!Epc9GA!BYJ(W9$RTl`AbM$BRc`t>m+nbM_jK ziCxFFBZ=zoe=N=A@iZ218_jucHXMrB5dW^8%}fTL-&#p9RJjf1wIZ>%g(6`_$(yRf z12u;YXj-d2togJ>>WiwV9)--p+f>>!#`|c)eDP3ym}>r^5qqBpYs5AoFGln6dLqW> z5wvJ(29?2xZZiY@AZR{P<$2D`bkro4ie@*9ukVG-nvee;h+k4Yxlc1ele#XR-^f;= zf;6VbAzb6Br2-cz-Dz#1`YdT&{q~=Ftv#}qCY~*;iL8b+(6;Jv&3W9~gZCM2SNZ|7 z^@qXshruSFKMc0DZ~rjZ*jj^4N@dLBst?3Ef$5u1!>WmTrEbS6@U|$fl7q(ikZ$8B zjof%-6U7M~3&+#rx)L>{R#&K-r$2m_jVIlnZJc=Su3uw>G%uD(J-5XMrE3d4Yny|% zo)flqA!EYtcjY}kYt;C2odB+)4KuIJJt*S`sB5H)l@vj2P#?WT8(f{06+01h6SpK3 zyfZ)$hqdQHDFv{$Xrj=~h2ol}GF$ZMDlj8&k@QOx$uy-KggxntI-v#r4tptLKPama z_Nk%P;Kdlwi`RByHqQmm6PaC89ncRcN4To@L=GjV@NEq=CGu`VSuv++p`ad!vS+*J zPfR3;JcwZ+Z_cSusgU)X6Lr&ErpXfWcK3U%1mY_B8RGQ^!t{x-=sQ~P>C z-$3IV50I;&4sO+7wIX`{B3=0GRwEA&nCs#lYhtC73iuF)!?ui!x`E}~2|D4JgG!k5 zNcYu_=+RUINLigGPhvQ zGV!?8#nZM@lPbc(i`T6F&JDRnLr}LEw^g&m7dkZ_>^Wp!F$#Yu?mCvyWlkN*Lw-}P z?@eg5+1TF3#1`vfO@f6jQ!BVJmAB3dZ?!G+`K;Z=rvcQaanLlKgQhrY-P>adss<8$ z6orLy!DDmO2!iiT=ok^F*u zPmNpvVx+!lsyC+`^>|ld*~ULZ{GVH#E=dGi6|1#%T|rFQ@aI9*qawa7qJk^Fr4~<> zJC$Br8Qi)R6SNXlSXz4566BFeTs3$LWs*`&BAGt$~9-ZuziAjPDu-U^a7Oh2Lm>wIQF zIkXPUV{_FvwWW8!D50@D$k4HyxZ7q@%T-JiBAd~mJ*wLWs@)-Lq z(mu_lxL4fQ8u19^Zpbs;F)y-3yUs5$L!oSx=34QXKx&2_*Ji;fZzkR@BLTIF_~T%! zN^;096iGp@qZIUK8Ukp=BoVtLM>`%k)JTK>D$N{vVGmDst29@nLL|Ebc1wlt45x@4 z;2B9FRd~}{x;l~Pz8`#U)oa@@uix%Nh2`73#-99Im#j7jKBf{6@{OY>DOikx>XlJa z;KQE7X3WU_lge$mMYQ;qcWtVBHXAy3R+>Dapc#N6>yTlhU^A~{b&E92v8~~+*Y0bY zy+{k{oyU72O;~>S!dZQQ|5W1#5`i*xtnUsCsUG_ocTe+eJDY{6kF<@(eXbw@fxH}3 zjoZC874O|lCSq#L3%$ezn+0OA9sC?$$0D05UgVpARe9OAFV&GG4(dUH$Nun^A7%HLgK?9$fHoV+ ztGdPgi0-wo#$D8tVCSn_z)o65e_v*xih>5DNe`!(gsOwJ&fUkf+9TC>V?#3n1wSIa z<)~FF0HBxtvdH_CT*MrB?Yvae?bgksLy(KdA0q30dgv&t@j)YxGDM4z+_7#0n#Gms za3UMggCEdkq*j*>m-1n_Ua5w{2M^y_8J`^iK`UTii#u7}Mfo*@kDJlay7kq*Yi)@S zgR8s}8`~%F$+;wN1PXsPk7n^Q9bKxJI2Sjjh&j2;ZW#U-Gf|frjWcnJ@sNvaMKI+% zU3<5J(mP)n*wM|&yOV*p7sfSr{&+YUZ3B0Ov7v)uxWSaz0bo|rb4&VMQdh_zb+gINozKL_JdJ{Fz?nJ5V(HnpZP zU>1?jS29b%MVHhnhOw+ZB2Cik3|A%rATIWa(UG!PmfJ5;lhOh|9Ci6-;&tEsh^$rS zopw;%yl6$Hok+3Ah!X7xoU+~vM~~mIX0a)Bny~C9Wv8nJrz|}}ze5L$@&E+|E8#1y zqh+cSSb2N#Bn8q9lQPafjmQ&dTdU2O&W3ceY^OuF076p-ETMz^X8>IfU!22-A33{; zQ``$DKnW=f?__aNUQcPF01Ab1j1$#*DF-ebaj7|K(ZO>elL7%<@WL=%y3B6T5@0_j z={UP7Ou_63GQXC3InD(=dK$2_t~v!mRcFEa%ixJMQR1UX$D(H}FcbM??FOpPx%_O^ z-eCeTMRJq@4S|v8?!b;HM)3mBO6sT!7Z!Kn|M-y@jzp?V0Dn?0hwZl@b;qaZ64Co# zKW90f`x9*;KTT1Y`FQs$m|7Vcf(%{9<{ zmak+>df5W3JYVFff#8y@>g)=~Ej85ZL8Ez%0p!hD6 zAX)dWh)EZWlbruZmg;Z@B$M(=y&Rx zQv5cA36$B*WWo?{u_7Ul##+V`Uv+}dmblgm%Rohcz=)W@9qOl?*8>) z@6QK&`yF=nudiM)D7PPqZ@YVYyXR(}CfbcM7v-}ql!yPitTjo}c-DgEs{VCA7G3@l zAU?vww=chaa~OR%0sZFW=oB-PQD%?EN>#F}z;K;xE-k_>y99Mpd6+R!#pTx2Wj5#J ztC7i6!yn*(=EfJ!FO3oiH$5}MB4sh64OX-*z89lQ zKF4I0MP}R+Kg5Yh#Qq@?3}S4K46jx2dD2@fhqGiPFSy-dxOb`|#>6MaQ?yvI(Hl;e zg%VN3V(Ot)bH4bB1|X&P69YxKoCJwSFxmpGP_-fhx*A&Y$Y9G#VDA9w7*)5fm}cWl zSHyab%lA`tiH_;woj-y+7@cK7zsz zG~nL;H{bmEPw>CJzmSJlYe0OQkWEH%EJ86UV*Qgs(t`A{O958Tu1Sov+B3gUzIA@PrAQ9ZQ5W1IPKQpx&n7H!(?K_lKD69s;cy_{#*Go4_U!~XGv0{e*<@tnQmzk1_UI?Lj*RI{kP3wJIqK-HVgT%;nOg2^cUz@EmQY;`|G8y^|>G^0Q= zym=EtYc6nAk***@78tEET3+A2JUJwBUA@8;SHXFjF*@glGAbE!mMwFJK1PJvw~Xo3 z04@%7yg_m?Oj{aaB^9FOtB3s`zkm1L(VGWVe|dcT`sL}%dsa0Vk4A~*GTcyJa;U02 zaeHaDP~vDR3)Ye{XH=N2Qj2v383?j~ImDBNsM4dW1S9IQtAWt)l|+lgc`T6^wi;;l zC2&e^(|Gf6QG|+S1$L`;RqU>iWnv@7%EfC1K36;$6gIKPEn?JSym8bx1NzN=w+hZa zJMVllng7;=$Fu8gh{2OMP0AG;QR-?b3-wJwMQMyRoX)agyY;ly zAt#s&tTD@0b6G2D$X(E=Wk1#F88fUKvpN0LVQg$JaCz-L zYjfL1lHc_!uuyFg)RK@TJD04sbak>VSA3V)E=$>DOHqLkIV70No}Qk5&ud&{b1#aq-~%;oPAimLC6tGvjDtW>4TWw|rPW`w55w~>0>05Qzw^Ee%aY?M^- z)r>{AQ^sZzT(IN8Cwj}S7Ly6kUQNY=agvUrG@i3EkD(>=DLlB2 zCoC$mtg>F*Bt^A|XYOjOh$=QmZe!xOBlx2rm ziVRuyJWT~r22lmvXqXD7#(0rN)b`HK&VO*u3CGDDig1tvZ_Uf&3m^M6to)wm&sdrO zd;6E&oe>*r0%dts1_k?N0rk5_M7(6|i3cG8@{W%^35l1$PXTQgsdwS``+)m6ne+jh z3cHWZN%(z`UUn=hU+$O{F9Gq>pH9C2^xdg(R~bmU?8^eEE1XwItG-_b1z zv@{4DCagJM=GPNnJ-%TDF$=tT86Lbk`0CI4-7Fab=U}ZbIM!I^`}gPmy(%zb<*-Qd zN&q;YCcsM|#NMZ6HHBwFf;7a@63|G7)X{>)qd7A=Ih@61nT!*_2n(p^M8>x$GFd(c z;(BMGx)-H)bmV>I^}SC8DBL09uzyBl==bcZ?}1Z6)JERPVgx)jWLQ(cgSIqw8kI#c zLma+*o6T7cJ;2^gt12&#_V*`AHCdRR0pL4^J&DG;RZ)A$ubgN~vI zHHkU||AHnc80QEmdwG9fAC~?&nF*fx?)@9EC8v8p%vOX#L3@+!Z~%ClGwR~!)04N~ zpN2(QHM^#znoTBAl8!SCsfs6kVnYNH=2=<2@J$Jz?_EAD{`V&z-oHCNI}el8Un4J* zS#}lA`bj#REk?}S3&9reh1FeEFTw6~i3`y12K7PjFj7giM}%(^$q;2f4=(f zj%EEIA`2Qt-9{Z4PnNrz-=qP5kzS|SZR)$|V?FEk>QtV`N%|lv*GQ>-8s+4~qX_k= zyQZ?NMpt^CBoR7BVBFB*bJqsDB>~(u2ZqM7IAj zEW3`bfF38Qe+PJUI&~eb(0~%(ve_*2e$0y5$SB73-+gZfG}F%6>G=)d%LPs4%IKAn4gksNju7p!1ssLSOC2!wy z9;DwhzXrn=aE>ry1n!muq6P3K!Ph$$b-J8Ehx3tT=Yh`XH5g;=_vhxXJju+@mGhfkhbg=#|r_1 z{I;<87=}8U#mj8bWO5gGOV??=*|i<~ZEZq}!!JK=jz91L1%iIF%GQy9LZuNA$WKdT zAV01Mp$Wa!{gofX#|5reJZ=?P_IdJz7lQAOLhCGz7{!{uY&`9u| zv)b?K;=$?y#Pivd#Zzd4--Y|PCftTxZzs?s&XgbW&OxBRW0QEe{Gbw#TuOJn89n!o z{~gtcP9KLO26YRj6ShGZhF$MZoHBuWEK|PaF}~-+sgy`sB2eUwC@G^wn*6e0LCI$0 zo~L!lJWH<`gbUSh+SQTjNT*YQJR)qs>}zQ&hE)kzfQ?X({cx{|uP4F}R)l+6D0G-6 zc#7afJP~EGZ&-itx7M`$ef+Syc%E}X4QleYOd+zmo}AQ!J^A9b^F`WTC#~1&YUKjl z&s9^BDxBu9lYsA3zT?;jAJh(g(Vm#2YcW75a^%3n5WNqpyJqSF z9)`my5)_bYM%9$`Kh~-9Ku3%{5ZWw{;xUZ*vClKwju@T`q&>Y5AXsP7aGa#^3_@6x zc$`NI9H|)zOV|OdHeppHtBxWuITQHU4rK}CL_UN#%xMZXS$xa*t?cb(oJIRBxufuE~L=i)wtm%im*S z6%{&Yu2BXZO0&wl&5CQy=)78YHAa4l&Oji9#qWvUY86&zb&)Ya%4(#!PUGH?Aqrp- zv7a>$iKC@C+ce7W`FP;WWObYA2%wzC1wEWHuS70Vc=fz2Wm4xbot{nOYEi(WrME!u z%>$F^fxHj;eeTTlg^2cX_Nz#-WFL4CS{n`YfP)8wO=j?4p&KEEA-aSX3UHcci`fWx zA--W$L*Zwr#VQ_F1o8^t>An>#9!?3=m7MAgHDi2&pU|S1&xH{g@o9)|=p-OlP~06v zm+DuYOL8HEtbDSq5*_g98{I6?4ef@B#uia1sj_iKJDYudpCW+e;77E(y zr-HT%e9VZ8!EY0sh+mP}s<;4s3c4SRN|hB$RSCt-ROa~m;^@+PAI5pkQ1?1r#xAzJ zjhm=EvSy-+yjH_oJ01=bRB-Vu z;z~;5xH{Y;M^V zV;=b=jJsy=FA~52+L~)R(89p8=ayD#EOAsp2R2Q-tzL^&jPI$0WAP%#%r~l1;uox5 zlCqJJE;0pl9VLW*#VNFAUt8Nk(*cm%C(Sui8g5ugN%BZ&KzD#7@jjJy$Kc3V@VPWY zSF}oZm;rBL(a-`QidwAS;SW2U`GG}~2_|olK78#oO2KUct<92a2GN;n9ArVLN~&67 z`cCT32uqmLx1#BN+SL+}K!ycF*|=d*mGR|+R7O)9=c4$AnAD^9xDQ4#%-HK-lYQA_ z2a8d0H1VVl0MViNgbDS^paERe2KECgjgny%WF_tsN(!9)`t-N+Z$EzcbaMXfamKq{vON5ImZZTv z&I1s$UK8K0J$bCLU*c{SHe3+zXcgM&&pXC17-sXQ=#}}X-P&HQ#QPghJZDSE#{4Hj^Upgs5HC9IY3|=!nebx9f^%ObCQ3_ z4C+(Gs{}oZ&Ka!|MJ^kwO>iOiq`1cG{0Qzsb_*rg42(wy>UH=a-=X)Q1OVafD`nOg98?q_49RdYiwny0Gu#Biz3LJ8 zX35Me(1GU~EGAK?M7EH0pQ8FGgS|Cj@ zD}@}UcCN^M#(gRyoP%PZiaoLaV5GH{ieKdPCf^$-q7(kZh)QM|Zi@M$y#r+YfEVlG zTl%kSmcLL2^fIW&OH#aQ>l2IyTO9ea7#_4Z^4Amh8+yB;?}z*%*5?5Y56rcBhxPmh ze%ezW65B%w?9c@E(to7OrK*71qOFy#kO%33TEXV~CsiRg>xE@}!>HvlfExB=Pzhp99K5t06_gIzV32wN z?|6?BAQS^?|HRD*RD^e{x$FToyJ#xRvm6%#^}G*RYR>FaIpE%~CP1?H_R0io5lCch z!3~-?H7Xr8D{-W4JP5=}_%+vL!O8p zx-lb9!Yf0Si%Vm7+XDnl!|XnzoY7hiySKlJC^7j<2bMzNwo?9gH{=U_A3IK))#jMQBul@UHx1n-^9r*i0;VyY ztQKx0H03}Ic(+jNCW#fSfi=-Qz;&qQG`meH<51t#c=+lYJL$iH4&tg~DFY7qlaFRy z1+{wbs{Wx3V5LGb0&}PgB}~oqRwg;m@MTc5(^Nw(&vlFkep(b=ja#a!Xi|oEkK*n~ zxGDZ0Kb?N^zB>5&kB->LH%6&F#!^wKg30&cDdM*TU3R@t#b6@zFo?fd{#$Y$s?a)8 zy|(7+8FA~ESm$h>YZ4G;ZDMoPgG$#vLxmPJbG0osuU!$(e8Jgf9PJy@m)AY-28Dow zqy@}&av4@jyeA?&ujJlgTe4(j(`LqffX|PGmR2x1eiyLWg=h$l)+p2Yg zIrlBCvPR1jFv{0b{1x$C81c;r>oWv>ojtTPZIvC17&o`a^Z!zrj!`UbpfoC|Q^@KXhh z!*&?eZDznYY=`l31&nFIOiXGX^J8Z14DYwyAyr5+3tvpKp}QCt$+%mzP_O?a;3&FI*_@oPd^vw#Ddj{>RRXks<*Rd^2 zGU|x{`;Zw*+P3A^pDkhDmahBFCcf*6!{^EFN5#mid0%bwB*Ls01f6+Sj8-P|WEL0X zf^D5w8fV!??Bt4K@c58N%n&{8`quRf$-7!-NPPM)#E^)tWXN>|Lw>%?ufe_IFq!oX zEDP3iC~*mNz;i3BzbYSt$IA-Dz;aEWp5a>?+aMBudgt_?Asn*$^UKEbvU}&< z$B&&2xE+es9hFU6fX@8&X5g`(l?v8`@CqSVXQ_ArWRxRZ#bS;MTL19@kZ&dNr$;`T7lAN2#J^8^C z^47u(xu@lTi-Ix~t5hDOEkB%XqAWk0H9GmA*3uFTf`SC!K@0!<^ClQrBzc52ZeXB{ z)C2GkHml(TCm@BzoQ;y$STVcVB;xs$$>|cYz8Y71i{O7S7%yfsY(Kb(=x5dzr9(W^2ecizS#Z~P1*Kzn|Omk*e&h=(jVz0W};WyW1`y&^C5l^;wK6Ach zVw%tZGp2%kJ?hj|X*`)WCl~oXM0Xd44OHWFRq?y!KN>Zbtcq6Dnx|R*i&^PGhf(Tf zBeekvuR=7hqLaPaoA54NhW{Ys(?*GTn1+Bs_zS)_dtu^fp5|-vh22u4VJ9YS=vka% z{s_&bE-?J&k8o1^8BX1uGxy??4gZS(2qh)pG-Rc2V`DRkrUt8C zic`H&QVm`MYTB$Za+DicCC08apH;$ShQ^CQ|PctM;00N|yqs@e_+P}DS8al5V8T!K)6HiL!D zu34#v7v`>|8w$A_NhR9jq*p_(&4W4pL%kN8x8hEe$<^w#j!15{kPp3=e_k=eYl*6$ zyY0HDaw>wT>#6B{xFgWmXXD#f|IM&E<$aVuH>I|;%U45~mm**eh_H4n1 zf+ob-#TggsefxP18p&25=5V-qP1&#Cv=nWfs(B!Fb-Sh}-G;U;7jaS^{a&Kc*&8*ymuhtsLU8E}!IOa;ixfLwCaJok>W2ol)~ z-r!}gwy#}*vX0|A<)>+a-H(?dij3j6PDEmTRem@h$@`lahU}XjyrGy^y=)%4f%_g< z?f7ke_y#jhZFtxz8y3>8BR4)|;+QA$#YSF7VcgNjZZxQdfxbK)Dpt2>HYkP**TF?7 z>(nw&CFzO7Nl@J6cP%%x-NxV;I-FmX4*_^f9enj@;z|5ngnM;}eos4zj9X*rmpFL2 zK_BH2t)P+7IraVt?yXhKyKawU9SPbn+4S#flje{&njOR}xc4t*^g1 zSXs?R#jLMs{q-v+h-Z>N(${=w>m?4prv5ZW{<%181u7PG^l3)Mogv~DW^M)Ez31_c zywW>xh4tF*nSqW#k|12v{j*E&PseNS0B&{dcRf57B`e}$X`IP(#1BK3CldCRw;osV zfKCh=*pGM~zBIs!d~d<#*$oEqcxQyS`||MLUSRTgoZQKjFMeF@0HMb5tTYcNv|fJk z_k0RO=yF#7a>c#4GTPvW)alOGuDdmjxjMye&rp!&S$693R&!tojNvIt1_^{h*bqKUbzMzD&s)2}$$Iil$p1O6^k#Fnd!Q(=Sdoi~y6`9T*vrV^_?d zqBg`Yn*mVX#)E1*T#h=N?Ep3Onj4!KP50bOPiSPK|H5^T-o_aYi$)BOvi&r*`*MrP}2DD31(AhR%J`z*Zk+ zsc!CW1}DFz=9PiZkJ>&8 z_i$lHT!ni_#(=N7ZO=Du)8=)RrgZ8^@ZCSY{dnYkCLh}y@HzwGYrkc~{lgcBhuq8F zMwfCpLYK;w7;9n`d**^y-B<;S+Gb=f2Wu>1Dns;8CfwX%I3=Eh9j1!fM?uICHiXH+A8SHbpW)cg|C1x#D~$ z9$9KFNAv4hNl6OzpljXrX_U4~x=Il>Ez-(TrlF?6s#C6g@Sq2m%}ep@kub+-6}?e8 z14rXg_COlqLb)E0>&n`B%9h$(W%d5DW9YOMCePMnQj2YWV)E8j%~&3GRnWTKNx{!n z*yMU)KvWn*Ry!!Uq(p#N+;&y;jN|=ZtXQA5(V_c|iN_U(?5|&{fS+w|mNN&5*w~y| zmKR-ah~syefsZ%F&TMxpz(v=3u{uT;nZ)Ep(?_pp**Z^9Rx&K6+l%6+cD zlYfvNlb56vc#wLQ9`T+Wgx+8v^AUpq_s$n7I#Bffu=YNl-Nwt(!-Om5GPvfdGF1pX z<-vf1e!Eb&(N#rE+tDBd$Qju3k>Rx)mzdnpbtzc|e-g0M6snYDRtVsf4uYU{vCw_G z5+N%YQgkurm)+J5Z?3s?w6sb1a}nOiEAD!%SYBd?EwZcOj!r^aN_lT@joW6fx4C?T zCX^>U03QN9n~|kKJZWh0$*O{($f<=Y-zI9R-(ZN*L#iz+Cw81KBUvoKGLE&_XgyIB zF#dPE#!tC`u@AZ=G0jVf@k6V=y}!n@1taGm@Xt@r&ZEyK@0*)nxlsU%PbeOL(Rson z6W{x_dj7tiRf)%JvWN>9sR)V4Bj)<1okn^^djKSdf_Fi#g%_piOcArV?gxl2Lmd;f zT~7otN;>e&KhGHAs3(-r12Qwsux@1>)fU<5aXRvhajF;Jg|1ijO-lN{2nZ(e==t$t z_UmVf+`9HhOqS|s z2TWA#16*K3vlIhdssaLD;Vm5|85%W{G3P~9)x0aipU1+*54g}U`c;4})u7*N%%{FPmR6F@vvdYOq&76THtlhGSFO<NZcIyBrW+rC8PU)| zML5)nz%H=L)r2gEvh6Y=D@q9w5FP99%IhRg#>RE@=1ROj03Xegba7`a#KU9vp@y+>0p%nD58aC>h~66-?|C8Z*|?j7PAXiH4Xl&i#Zg~-s(8&g)?r^Nb5N0F7n zP5!4+g9ZW+^zlN&yzH8*M_0;W?d;U&6h@mP|CNPO36RLAT;%*7Cpz!afGbCa1ul@lIaDW_T`+?;hQkoES6TaI%Ilf5GrvDHl4d_8$Kb%xbKtxU*hcF;Rbg(=2cyoTT@72dg5K4=o6E_ zIB1nqQcKvDh+)Na_-|l%8N*r#0`JtZX(`RP;Kjgw_Cr;OS-xx-$^q@IYCFMWE^5Z{ z(b*$=XA7L7t0b(WScsiuDm!Zwby3UajWrTXFymvbrWPj-E9vAr?EAabt#>>@4%@yN zO*&g=R!66EtX;Y(&&smq7yA%guX!xC^vA-8doyd+NMJnXJ%J09#SSV!I6?K9nsG5$Im^y>encGf(f+1cP1PK>bPUAN zVd`$#njm8k9N*OPtOi?aJaD&IyBHqlC3cb=`@wLf=_-EJYEb`Z6n4JmIU zU2 zqa!GYGB|*{*;`L&q>$23OPP^_pK;NWC>%eLc7BJg`2FkR=_e0~E7`A*Tv-}BZJK+P zeGZtpp-{#B<2BQOQI~fRnvtVLks(INo@U?Q2&Ra|R}O?2*3q*S<(`WI`V`~`R1dZH z=k^YTd4Z@#^8&bs|H}p!$d{bzA`=^Pt>vJD7(cS=_GMz(OM}^83$IKKJB-gW$zsx3 zspX^3|MceO<8$Hn56FM+Q{|SUt=r)M0g2QD0TKWAeJTr!|4nJR;nc-R>6+j4Wmk_ojPL#v#QFrmEo>0|Xw1cEv@<84)aXlaxJ z0-Zqxok`3zKZ||+ZlRlseHVx_e_*4PN+h#FMpMo{($TpG=uZ?-I`O3UFX?_yReYw2 zZl1rGsTW0>yV^wa{|-4|iyv$1UUQs=og$-w$;NClO;`9`g*?O97^&$?XgWxiwt}>S z!8l>1{5ys%&IKUVOgHP?yqr_g3`h(m;OD;kWvV@x{K5}1x`5{WXfcRA>wP4OfxcWW zoh04@%@})bBp9*_U}H!MHrCCj%PEl-W{mEUv~(sgvtj{_EJncaoe1^eq%IP~XNqEzrT)i#f?;IgYvRTbdXgZfawM(^$)fU!0m4yq?7c6Lc7kI1%KQ$6#{QTRm28R z0=NG!;04;fzuphBXxNF!Xk%wlyoQ(8Y}N;VxfBlH8dH~*e@UUhQ$0$5_gOE46cQ+z zVxZ;a@^x6F+cA80qrV(fHF_&*a;# zpoF&bG$z)HdSsW`EBXlZKMIpgXC900LB$;|<3GX0qyH%tgsh~#->1YxBUdyzmvzEJ zvcw!L1eh?=qI$TNioRk2+YhE91i&5Su9|H_MC-+a(o_geS^`m+<(J-2LpduPT6r|+ z4s;9!0}W0z#FZ~<5MqvJXtu^=$hSB;2AWA?$4bn@L9HN{*iOYjH*Ttmhktzo&x){R zEgZ1H+js}MfixpPyle@U0}xi5Sp^S8uE&){9)On?O_&0HF`Nlf1iH&57_srspBSql zYO=i)QQXQyI`?qi-+SQ!!;D-J+^oH`!tRt8J~vZIH)7*}Lg)lh=maP65%A%xtXm9( z&@Bip=Sno6%ld%}bC;tC--R+G%{V(vmJ~|Dm=}LdMkHnthoRDV1@M+aQ!7}p+XV5+ zIj#~TV>yCJ0-PuWYdEbv*!(KkTDxJOvS@G;{eKLBm;whKbg9(|{Jz61y57HGarz`uq!Q)6% zaN{>dbqeN^<8J^mk_>`tO9^guvAMV=@e@yxXeHsg%ZefzE`2hT8O1hItkyd8XNFuT zDw@-Rd~#J)elgWrgK4SO<`3I6Ebz?~Agwu!vG=0HcUoiRF|Z}m2wN#;QREVjUMk!> zfD;K_gl9{lmu4zhB5GbhC7?&LOVn2i2II$d7)Mv3k5E-~baD*-{ZGn@+0Q4)0>_}0L58N+FV~SmK-4zaD4PmBxbg_wJ#80OIYO4@PCiXQB z15Z2X3PF7xeXTdtOJkMuIzza3<&~Pw?XEG6)|M;5oOVq8s~M<6nIdJ0h`VT?9y@1M zx+A#fd}QOGo*HM%lYw1>R*SE9ssK^qzTSw;rZl6pf{gn;)#%^HD(EMExcQbQwuRBH z0A$&dc<2_2(-NwX%xXLy+m3ayaXidTKd^oG9Ja9Yah?VJpBKfi;S9k(mZ-Pq6-I}A zf}W@7m?1hC>IogFd$*{*9e{oHFYk{wXUB~9y}6f#0*71Xybosn^2P;6Qy5hQ8;nAM zaZZgE7;yj-u#vO|!1E#gxPf-t7! zGqf>Y->2t5_(^UaPoE>Ii`_ri8xpuw)5fEHCn`UC1BEu%62BPW49cKetzesJVF>u` zsJhURW5&Hp>D@zGWSa5HBWAp&o_nRZEhK<=2d-lN)P&eTj4N-ew1~ zsie8dP$A^eRUKL}XcQFZ2U$RxYzCuY=k83xREfbvrv6GdW=R`+O-@f(s^} zk_gX~lKfhFO5K|1HBbMJA9F`wDcBZ+o}*?N1L*5+RlTBQ(9a)}lb8 z11QKLR3y9vYXWs%%8S`=tdV0r8F#}>C*Mxc;{0YQVPT8T)!Ok5w}F<+t6eON!`23* zSk0*0_9B?sJx_G@0ljCeKS1f(+zq(i#!VgD6uqAJ_qURnd4moHHaUpqEhx4x+~6gY zPS`3aWhuFro&fz8ZRwt_vNiBo-3tbfEJ#YUec{H3GPy_}oX+^2&#A7!KcVC5@>Cp{ zzI74%U?J1$7FJn7MvsG)Hgss{nf%)?m*)}*jBeIa)wM1qM#4DPt4AV-B$xuub9V_N z>K+LzZ{y?0ftg?3V1nXx7k}*#DS`U6L+|*{!+jiMT$-#Ow;z24W1Np4gYqVRucJ7h57>Iaz*_A~`Q7+~sGe3% z+hl6ig^qX1=Z|zih7!;eL#819_I@LmGr-KRg%zy85wfV;!G6oUi5DH z{W3gm;P=?g`z>!3VW+328ikEk&c~;oM-Q81eYJ5F^ zA4Ae;22)_X%K*lueqF{I&1fP)G8I+jY0@tUP-WC>;9(EDhggHx_NFka77=`5+ou8F-i5=aidiDX>5;_%xsNEQ>#-RH)W zdL~6M;fL8Wc@r;MOKY!oysNRT;xb6A5xp*Oe0^767QZnMaF5c|(4it4imB;PwR!L! zt0DUA;3Li}`&3R+Qn{$xx&aSG-wBH%J1&4DnUlRq!*02%(I`zXD<4y(#)x~ zF2R`MRCNqyS-!;amINT$QiRFeu0%K0sYDMumtR^Zc#wZ1A^_xQcCp7VO=6!ll#(KG zn{qP_6`a{`q+Z=^ZfJaT7!UJEveAM9e>Zfk*ZRvonu;65kUF%k;u7#Oap4FOTzl>E z@ETO~It#Y~;*)f(yM9%pQ;WwZ$^ObANdRg$8&pr#g)|{t zy}p1B#!3jY;f%eTof@rbIY`NuS9W$0{WO0R`{xCo-eNrLnJasWE7gTy_6{6WLHgND;9M@6-tzPw@0x8rdv9^ zYk&tgq1u*kk3))p4Xv7}4Yv?vUrsmy)SJxeJ*koTwyR+tWfhW*XIbcM)-X);j`*%^ zN{yI(%f6s%>cf#6wQ<%^B(&2-=8u59rBuq?xQLmR4to}ko@9T45^iRXpniEP-CcKU z78V=y*Nq{yFMxB|!3GIpC49`!W;DCUJf$m?^VkN0oPolRDS;=CRU*mRLgZ+pp#$`+UWupVi_(1R6aiTp@m$Dbu^f2zZw|o$ie6`e>&Spxw`4mQfWOHU(m7PAjcsUKb&S8!s!S|At zEX4>XfGMM)jhWXz16Yv5WEftvQ*Oi=DKn{=+CJ=)enqPnfx==C+VFRi&fN-;%AjpB zWdz{>D)=;yRQ|#X2|7m5AR6ucq~Wvt`#XvrhxvZW7O4X@`R1hs<&hx+t#<@ zZ#J%9UseOjw&5LJ&je9!Uy>(Nda|T(16xD3p*Cg2RBs-$Q6ik}vH)oEj@}#XuOxTQ z5%$O!!=tkHh7&k?Ken>4E?Bf)c{`oog!@o>A@X+q$p-}dOg8A|L9N^gB1}=S#kA%M zbVM>Cnh0`fOz~wrPpJUKp2Ep;Ij2MU#W%C^i@P2?zwwU~O-6+9*^&Dy1yB|qo6Mt= z>G?zi#N5E5E)5O?n_>I#;0^9PmtaCpY6RBYp}y;h^{+8L!5OlWLE8?8J}eIbG?Uii z#!5+*Zm5{tB$}W0?&62S{*%%!_*JADxn58DlG=cQRiw~q2-(4TVh-&-B&*~v1Fu(K zAp)=6b)55qxQq3p<5JyU;BsB*y{*0kGZTVQSx&dDFNAfhiz&qL0KfTQpK|cmw)Pbd zdt~2?tJ;d87$mVo8!NPi!zNC|*NZI@No>!lha%mu4eypdY&P4nr2W#r>n;7%aT)Y$ z^8MQPIXM|4p}k&gWOIlG1Ua0*C_A1@IWMZp0#Zji)7wTvd5P0xD_(eLpXeAHDI4O!O;Tnu z)~_ciSN7Pj{C_ah@%UUcU00LQop@_fD}QIk#jBT6yKj8Y|Uic!E%>-R&OW0c(%ju2E_zGVoqv-pZj-o$2m`Womy zAlDy0tezxV8t$|SHxOX?Xe^5~jzF=^$6-&KSzR-_+kFVB)SGJ)owGPK^p`KXF3 z8kUn1`TwpO(>lnjxi0JP_jYf?`Gk6A)H~ac$G$%C+)%S&BdL{T)Uv9Jf z{blgu!)uN(9#n?#P1oS64T}Za7ASb}XUwE_E+4_sv{F#}SnA|OQ$>yYl)nKmTw=_+ zdD$~A*Qe~_xXa~W@$FjcPl=Yl`Y~I6>ir!;ywo97J}7^ApUbJZ#lKf1);QHxW25@Qn` z)luKVwOqc5Uc2TDQ6qHsmrOsXf>y~1`cL*~Om5~kNO!M1PaO)skRI}D(=HzKv#Mc( z$%mW}m+OcJtDl?2E?Q=eMs>HtqXRWwb9gUOab<;h^|Qk34Q!8-&=@HK3P;uQoszf zt&N*P3e)WE1;E;)zo}e30b@a{!~0aaqdR65bu3axtnyt3tC@}yRhnb>!zzy2MwsvO zHO6_m+Xsqy*F5qM7!9gT!d=8?>Oa!P0Vu2WbhAAmM?upIkg!r%?i@_W)+!Eq_l_s>Lv-49!Ju&4P7}L$-MB<%}N>k1W$#1I92NS;g(8ymqM;*24^K+D&WJsVyvwR)a>PYIC?d?kqTKpgve z{A;e=T1V$#CE(jNK=5VCrq?qv>uc25yt>(6&xdMg%|pD#0NZbZBrk+=JX7)LWRNZJx5;S>^b>u1dFl? zn#iD9m7+x|9`My76b#RC-;+lCS-zjKmLL2LF5Q81mmmFRfrg+pNtCIahh;x8sP(kKqbU z&;+SUlIkT>+#NYGE#mZN(;lcnyi318{?lk?y4^jjAX>-dA~iDPd`|F0=cK?D2XGKi zy_ob(PNm6-a)R#?*BPh0f??PcIq;l$%1JP0#zVS;6BS6O{JI7yEB;%qLh%{7;MU`| zNXN}>$T{J_>fTz3bP~<&?c;)n+{{jrRaQg20V>d4^$ED zUM4g~d23$^oUf#(WnO{X`}Kzs_x`&RIU!BvxuLnD*SY}EvT*zdR=>RtRh&YMaXzBE zhoaEY6f=QFZwyS+v|##LL36?dNU-1heocUi1o3uWhEvbS>E_K$v-H}l%&v$#oSy5U z2QxCY+&}2_%|5Q5!7AswMzi2bL}k_@T+}ATE@<&|j8&?uQ2TR`tZEr&s|LKzQ#`k- ztXw9DIi z*!k$t;b@*qm~0b3Op++o<9@bn5E_4`b53r!I52iE%krpdRfip_dP=A0?>h>PO0%p5 z?l%4|52oVtJ)3$Yo3}9XpKaR-*9s_AGcrpMUf>9%O$8SH)w(}Xc80KVKSzk9MJ~b| zg!4A{H^`pXo5l_7*=`^T%9JQ2t zs+Im72P|w)XJ8+R&!1TkjNT?uGCz@sBxl!SHbE+!LegidGq^_^_|KQpm!uCVy@pyn zoRwaD=GN|<(NrJJwA199D7!$NZPUyJ8CTP(?XO=>eOC!LX2u9iSnK$miwd^++N1ZI z719c~T5`jhU=oNPGydzmAD{2h*YIY*$a52cZ_)dElOHj4>wUxHw+#grsLJH9g(I!0 z2=V6tXn_D20L1d$i@RVru}Ua`l2s(uBD26^3MRAD*8dBR4u{_Q- znsRF3z9NEBamau*wno(XFY=W=yqcAfsng6oYDo@W8T+otI1XAme8Z4-auH|)PI@jgflHXFDyl_!P;yGDOSASRNVoGTo})rnAVO zvs)G;&#o;=aYA$Ck2-7A2HG0eVT{FY;Sj^(ng*HJhqlt?Nmy%1T2Yo?)_y7^j^8$T04n^YoCyOa4xClRKKRcp;e^o3g%72d4Br77KxGr1}QKxzg{Uv zJehgT&THipPp|wTax^=G6ObrdnmH%0@b)3nlVB}~caqAF_}Xfrr*AKB%*T(O5!G*| zER2YfS#|VSmAf~EXa(js$MD6yr%s1{o|E;NN`UO;o?m2%*lw)*MHTA3z*hVRXu96I zdh`-M#8m`#(oYb-|As4>HQtO6wl|J=GItHY6Mq&PwtVc#!F%VPo#)LP&j9f*or;E7 zIMb{7aLzVGFc*ST^~HR>1bY~1bOcSx%TyXNmdZsVjdz$&xv_DHp=LtOTjO4^0?`jM zw_^sm+Z5*)QQqnuXdulGM{W#}$^hdbj^sMWf~9C(33TGZgdmW;Ls>WwKbQ-_6&9yj zk?QZ7O6_XW*h!HgR-s&`q*GOw%1w{$2RDNCm1NoSN@~=D&d;6$WY-WSgZ*pr2lPK@ zRUI&&pgI3$R8+t~K!pE&R^{nx=KMbtUU@}cgkd7MZ9f$axMMAV*isMn1rOhAUiUBxLXOIzp|@ z!})>u6LvvXftYvh9i(q`$lLZ;9P=&sf1XQkB1v-*0thIE00@ZUzn;t7%HGt#(a6Nc z$imF!e;~sES%*Uoq`nVLr0c=^p`@6$3<$>s@kUs*b$X0-el#eM;PlhXR zrCAfQ04tKTpR(sxj4VQ{{0!ctJBWYE=G**P_q3U)gD!^F)GU&Juk_KW7<4BvBTnb0 zRcU&+!^Cb|r$=u+bD7@*@lC4*bhAE%Cuu3Tlq`%{@zaMdg30EPu5-;O%*|vbGRSNo z$=nF%?h<<`_jze@E_E*~acFbyw8oIKF@uN-zx1=qib-S^K;#YxQq(x%D}a@9<}I;Px4y_T1XhUv&)8}TLfwi19ybjo=89QN*Ngh_tf zSXo$El)tb)$(q)KMtvR%hp?oqyyUc+PEzABVbMzl6&aR@15A>=d8I+@k9i7h9QZqA zDyc%tT&Wg9Ar#!{xuIZ~5@2CY-auoE!GAMqnC`U@P_Hy8JTDCWX%gm*2W8{oB7!WN zi*lNXr{oJebbGS08Y?9>#)ZxAAT<-w3wgN> zzX0n%Wyp}dZDf1aI3dIRYsV3o+BV*g&Io7!kVEHrA{V1)an}}r6odvxq8__@AXq|G zj9~+4*9MuHx>D3EbWq2#l@#sHstXsA2JPB^^r}T-Uo1ZoH}zu7Z;LU@HC4GgjWySb zv>YWok5%t$fm_<+ri{lsl|`%ArUeY^UfyAGX<^_t!FRe)qWz3Al-xjCKb(+NFa5}D zqdVPmQ22~EU{wDF*U41EvCUsjLrEwqUh)mF7%286ni03t343n(t)e1z^+YnMKOpxD zMD-}j0>GCSYG7Pat#GfN-aApJ%1jJnqd%^!T=~+&V|HlZF@lU-6iMSn;vQU1YfPmx z`Fl_2x>ep_vWX71%$PJ^U#UE}7;h<~d+3j`%bN1ucdQ`KL=Z2NGCy>0c_r8`RTV-p zofi*JQ*V52#yP}!(al<2j?-urDn0cQoffLuZxh`fiXQ$Y=!P>3U{o>vENHt>PMI84 zhUoCk)TA$-3dKPQE<}!(eQ%ZpG#DK1;upCbH4_)ONK>Nj4y$B@ORR^fws5&-3Bpiq zAufsXgh*n_OiZWLK$x``LG&5j)?3sA>x$Wv~6>?wGgT$RGYCk$0Dpy7eEiVbrZX z+2_C4)AikgG}UE6?;E{sId}Ww92pnexN)R=xqf9N-j4f?E6suEe2t>hv2wR7n6a4o z2^?BH8XrHoFqTN!#HYL*ntR;#pBfJ`_%6v_7C7h?Lfdu~Ch4EG@BG0_ey~bgHwpEx4t|Itw zW@{%*=U^`&cZbwjtgbw?u6LOt(XG{+Zo0e7FgiRW!$KP$hC$cu$>!tyb)*1uudFa# zKJOoV8@6yycg(|{uX1J^pS4^Fe^G(8O$rUa&8a3zceV9i5skh5Sx;z-)GHxHyGylH zPV?)8@}6M9o2`zBs3VlMOSgRTd#5<)6YjBvWr^RH>a1>D7yPh8WR`LM2>m8HuDhiT zG1vw-Z!v9A0}oXN&fUm|kn!pRCqG~eG33=mhi5XMqoe_7fhmlncR_^eEqGfH7Y9-3 z>x-|nS%Ir{iCzC(`jGQ7mId&5f9D;<@wnqqZ&cQo8G6HC<5_J+I6jmZDpW;lBb#q& zZ0FU)5UfQtVVkfgq}JhR97>Ax3_rfb)p1n#O2t?+Q}6cr@a^~vVFg%>;i%EXN!g?krK_qrimvc4;j56NR9V`X zGmR;ai2rI4pNgF>YX(9RFVz6SHs2hhQO~grh6>VTZ(J);t?j)F)YQe0E4S=}Wx`f3 zCg!l|N+_7uV%cr%3d$51QJo2>WM|Y!hMIE-5pU&wst!LkiZe@muMaI;gN7)51S|~9a zsqF0MzS%EQX4j)=`CR=s8{GnuAGXrgIX!n^awrlFayIvA-V(-dsPpu*k}-VKuT~OQ zYDUWk|>wS=v z$LKeOOX9QaBJaM*&vw5;$134}_Kh5-dbj%jdSMSxARzqz8X*?84#xl5#TC_Ug<&GB z-V^Exd!VITAldYenJU>WhFp)a%FD9B%KNT=7 zm^9N}AdxL<9ZiTaC7zM=<=ON4)wI`ozQOwe^%tv8R~UPioN+Gp1BJGoSM_-&U~6v8 z<1AdKzD1Ug4)e@AWz3M>fYD)T7Bxd&E^}3@7ZcoZ9(2Tnryo#i5qh0*Tf7l3x>Ot_ z<2)Fr8z$|XRbo0qd&(OS607ztJNR}J{os)N(9ix6Qlz5B^?Jd+9!Il{CuS2IfC4v_ zy7<1tOE=itkFwPrBhQ<-0LJLvS1`uBsva`!<)7*%Er@foRoRYR*D^{x&~(<&$=V7w zC2%NroO}CRUkZBuDT+jX)$s&|W#Y-vO@$bX_5Ob>)vZ-mucZC^DOT|Rx779j!^RC< zOq{J8UH_-0|C{a*v&n@twtJ!~=f{Y*Bu1@CMQ%u?d$YHbDst0-nPH6PlY6R>t}DG$ zQ-A4kBNhKsy7>zpR+=bNIVz9`KBf5B0M6Hzr!PX|? zqwC3l5u*0eYEJrCSfqsuK5-c`L@$pGOlp z>6-L)Ok9V32g1a~-BrlGenL@)1+6O9xPnFti&3&qht!VgTZGC$&n2y9>g+1(wLYJV z?CwK}^W5oK{R;S}#|m}6@ITonmKF>CgjG73RdPcLBK$v{h_7bdo4wB;A|8A6W}8<# zIDSk$!MmYivV@{cd=`xE-3pP)u02rJ;OU835@whtI@+m!t+C9BXJDvk9PoB!<#sK@ zCfzONGp%@VEszYqY?J?JtddWD27Xe5;Mdq68fogIKaQHo3vgsz8>PHce4H&c$fl-r z32P~B1ThBG>=^hYBxv##PRkT^p#2ypqqFeMoW&S_lL_=SWUpoHXrwCZfzv#}Av&M- zXlhpjpw&-Pz-vu?1R^X3V$8@A<3=qkUG$e8SrpH)p)^;5!epv(ZKJL~xu}=B_D%=Q>M0b>tn%iVy_(UC55Ci~`3HC+TcFo91?ayjurE>lRyc-qGTKP^l~9^BaCX`_9L+id31~;yzkI#@p4T`Gew0|mN>W9RAoyN1 z$#c&+i$!h*((5UZP~G8mg?Cz>ah6Y|y3&~QCWys8@J`u8j!^nv>dWHz?jh>ClA6Js zRG;-nwcVaK^Sjs~YaLsm!U322=%3>49}pluvN_oJ>=)@+k3{j15jqUGmpHrXOnpss zwT2A;@L24tF5UVDH$I1ou~}dID3YoXc&uMieH{ByIl}>O8|^{MCwO{x5=stMc%Ke4 zkisx+9O4!$}0R&edw*9qj%W z+N@AOKt%sF=h`}0SXkLx{0r$gb!CT54kZ7zdZJTE*}$1j*}%3zIM?CaBB2~G%NL~< z@M$T^hz2ohvNQ?&=Uopna)9$QQ|~(8jvt@@%eFE{El0ds9!>6QtGp?jl$j3IR!7a# zKGBMi?#2$q;ZK`~p98AeJ-nq16tHXF2rX9Oz|*UM2RuCKzGxoWn*36Hpo$nK8ru1bp! zYrb1Oz30Qt$L{xMh`GVt2nr1fO3dcs6EeLUyuNxzL@t{)e$n_3IK$^3Za{vnUf2lJ z+sgseh&x2250Hwb$$XsLJXr^-6~81|+N!&d2IgjrW>$YEWR?pSfSk7Y(cmCj*TCN3 zb;Sj<{6sogMSG1q>O50aK_lUA8iJiUjK=I~tZM|w zh(6c#dVA;^o+2qf$jmgtySMY})#xW_Oh=4(b7*39A7D~BsTdkgzxw=`Mfzc10(!kS zS!zOAh@w^`D`zCht7)gdkeOeO9z^MmEdz`dojhKbd3Zd+vwc7C6E8!)4Hqg-$S^ZV zvUfryX#GX-Md;a+a48c!A4htTS{?%WiSXZ>picEJZh#a6% zDSmlf6VY(xM|cMR0*p2O#e(nqbs`s_{mRHAN8w2W z6!T*Wou}=4IW)H!NiquD*W`kM|s%VKU7TEO5*XQr?|NC zH>QA*^_|W+(?c;W(M@}yANh<>!ah#SUHpnx*L4+9MzBy5ld+xg;r1bW!4}v&v_-TW zCpA+!2}@8Fb0&XGe*(#7LKZ%q_R5o(CsoDf(GNZjpIa}f7bfINXC62GAxASw!%Yw_ zW-;YgA&^bj8RoAD#kc`NF(c2~yNN(JqCsqPO$TTI6^npNOhbDt#X{zz+Q&`az9ao09O0xCmfq3*4H`{vmUY#W&{ zlR=caa7ZB8bS`>uC}2#C3MKtcK{7#{4%L-@(bQZ5MMD5u8){6&9zvx}i|o%(PZP?( zjV6iobh|t>l55snN^1J&J$!Vf_>o$T^WbR!!WTRs2ri1h}Ox z5N&@Uk%}09Ci60vpCMac=f@*)M_2a;7sQ)?nu|A;_zn75nAZ+&6|v#?U@X`BVt|#z z1CuutX6o>*n6Y9+-Y%SS+ z&N!xDUSvtDZ#IQaP8GH#O|-ki`wLu0{Dt6}SQqh^p+d8>t(3Q7v;J;^%2;K@^jq-O zwrUHlkxQ^;Zs?h6$Nnifh>&5Yse+RxIUu#p*cAs!x7*K!weKON z1|j+T;3Vgri_4Ju<#c$rzrVxp<@igXZzK@eHi(L#cOoY1unh!N@E% zsdp!rdHoXg?FNm|+z1%-HC}P)yFmnxdcDLqUz_pvgjL~D61}XrO1g(=x7-$DnxWWd z1~vLp$g2MI)FM?_ApH@(1_jdBUTt=i(?*~V2j{)D7}DyuM7T%su{!;r`x z(C;R!=EwwRD0<9*+Vu>RfVx+;kO0p!N0o4)`&_XXqqf4MGXb=YS`Jg`;aDnzA;0!1 z0ECjZ5;y{E)e+T~@P-AG5=Q-VqBaJR5q+3fJ^WNy%`9B4@-Oc^vlM)9;07g)R8gCZ z#|dm3lsaTR(kw@exK-RUon15BpuR&Sx2RsCwHG5~iNrVQ)bL!Y zYE&>LcB&+ni>#>=EBH+jpkNLe)mESEGTVE27Jp-L9Nt`V$J%lBYl&V3T+f+Nm)*cV z)_Up*(`c6E;NK;h_W6}wg&P@mWUMY3HgbJQc-nZ*OUE`#Q<#7!DLu&CDX{=?rWvCh znI>QG*4bS2lC_FgZ>50=W+ zls!W(*-DjXmt-GwoDLB9u<|!i;=;~K93X(T5;V3p%Vh4kgfsSbzv@$ipyH}lwU11n ztj>BL9VpwKkVsGPcT=!c7tZ_&e%X%q7&*>TMoZ$aq_dJTswch#cHN|Z#$y8}wj>vH zSkN*>2LtRK4`qN<9Fa@|p<1^yhUAgK)6nt4uAlU=2XsGi<9a%Ywf6!4D7IpHG}Flw zC<;;fLDAWZk`7SldQk;*gH%Ae$zXD{4~9=}nTwX=+hRM7A=dMq6qb-T#M#W*yn(>T zYYpr<3VO``DfJji)&~&;pAHoqJ_eK*0sv_xlFU+T=zhX!j|UeZUT!LJt*nz>|c(wyEnVN^WE#UW3U zp-CP5!@3I-$e-!}EaAJ!7RdEr`pm+J1vv3M9Y*4DLQqi3&@>;?oKAnO(k$fk`^%(p zGOb5I_Vz^I?;|{hq^r@%CxbBnzQis-Dp}kOOx^}({x&Rfb#Ub$9S3URBB~T9Z&miv z{%3$&6gcZX)QI3HnV_(3f4Pns)E`;rcyFJXz?;mXT0P z(|bpiU5IzK!}aI1uks_-(bThn7H`dyZTG*P>fCo^w4?sR41_D+@*d}uR;H^BqDGV znD&JCo>0pz%wh$jEf3^|XO?%9dPS4g&-84S8AAF1$>fsW zbK3suUKVTU#l;-E#TqjHR4FMBzyh$)A_EgjV>W!e_j{pFVo*wjUX%w<+fJl+H;-lx``E?Y^uAeQfM z9!c!}fr9{`ML08l0iow>>k9r*yGT}s$fizJ4(ZBNq4Dh9tQ&$$y+qZZ)@wXHu3QS> zRrk}z4~B^5i9U433=DDXY!St$*Y=U=`Ub>2<;t+onPfCqr!KE#%*5NYUc4H$>z%AtOdWXh=6rP_Ke6U0; zq(^@GA9@|%R2t9iMeproX-onG?Vh6@=SgaKKQqYdknsh&=e?D4LM@6xe|9_!7(_~7 zm%X5DV8YMxRi_0pc};2O-~ooP=BL%KC~0ClZ9_80QIg4Aw7%R7BU*{G@p5$DR?3}5 z!eJ87?hZbB4ey&&ov}#EP|-3$eLr8yZF82Zol-aoHn@u0*0Gtb@=|S6m>-#B`As12cgsM08izLq&@x2IXp26uI^pa z{V>sITPh#raNnTs??pNfPUfS>7a z4e`xw%AT9Zm+eo>zKTJ8q!f0k8KSTv-eD;H=RoKq!nMPDu~a4ff}BVK%6A^R9Uvi; zxa_fhgIFT)=ooT3^^U?#9n~?k3XSTszFgCOuUE-k`TV=-mq3fE#+b1`3k^-d5kR2B-VRh4{Rp`oPi(zKl)s7DzFQsIR_*-aim2OQjZ6Ix-XO51QVZXr^F1CDmW?nI7#e>@e+|Xp z$lu5+vV~K3Wuuh@h1-$6;K8M)zIdnJf+qc1_dvD~ALx?~pwE4aBr+XkNTqPRe*T($ zgLE-PCB(&!9#2dEZjO&tpam#~xj*gY>nPoATg*;I*0qLK`oCd#({;#wmHeUyvqiHy zdJXh<_AE_t^@Mw&i63Y=iB`;zB$J=FjJGi>O{-k4CUeq$bE?i_K2SEPYxBjp41>Ks zsRo?pO~&MG#<>Ru-utwU@p3I)Crjh6m(h1@mT(VYQ;_qn5z77gvHhN}z|0<3T+=*9 z$s5(gQ32!^Umx4Ip8JfSFJ^D-PTE6>z%OX@?eLs)nl*da_X4{gN>$A)*Ya5MVU4-{xLa-a^sYf&d7E0K)`wWgAgoxh~`y{= z=$B`Cln&h=QMi1{mrKKuHyfcqnA$gDHx+$FD_$%C?d8g(LcqGz{uKA4wflLX%vU^z z_`mpi$KcSSHd{BgZQHi(?AW&L9ox2T+qSb~+qQG^-BYJ-bys!&S-;n+xn|XiF`l6? z?W*z@s?`#?HR5_EscQ6nuD^kePRPO8DAPtKTu z_M|Vdc!zs)z?&ecDq$j!YLH(fP~BaGxn?!1MV;<1ri3Oop$uNNOBOORz-L9~q*#p>^*u+tPguR$&R8!H9A(*&hD+?XRDWo_%I7WyG ziBqLTU?*@Lanc6@luA_`o0v@d8&aXU>4>S-(|By7}0E8)TB8_`MFe>^FOkLf1F@+zw~HzmN-W zuUb2#@nm1|-fnN3(a~>qdzE_e9J#|;0wX71r$rX%xDH z#ST8WUZ$b4{6^T8UWY@H&wfx&9e&_OV2GkcHg-~lZm8C+`^4NcoDrRr15A%kEnk^< z&vF{mdOTjnYh`@hRX0r0gf*;iPmL2*^;zD0ogl?3M#MAe&RA%VPAhxNJI;e=j6S=G zUC|(0CqyeP>V%c4MDnIpBW>amn>h7UR9g-pg5+|I4Iz2gqAF-*&aPKuu=`&2-W~YN z%EYjkW3dL=P+dG&jA5^NAkXgJ-S`f67G4gA(bOBRNIAu=cvZBn3EjH4X7s0{ zc1CEn%vp%XI)*lc3Jf&eAgH}>5|2-gXt%c~KKAvahudAcLzl0+=7-%z+nd`DYF4D2(5LS{`jc3${H3kbeqAWHd4b`k|b7%kQK674wrtPW}Y_(UQ)Hkgel^6(?Ahw4I}Z=Yb5C! zRB8-3A`|Ub7lDl5aH_O zBNOqnyM!c(ChZB@G${;o;k5Tn#OT>mWM{F@#&1Pi+&kvMbQS*KgkHbHXap}2Vkc0<|(yJ~Q7`2i2*`da_$L9tJ9gg5u2>THG@f*IaxO zb;)I=S8TEQ2O;Ca6p2&Xz4?SS0bAK^R4?hDSxgj4=|3XCDuI+uqo_fFCE=r&M|EgI zGxB=9x_WyXbj@cjR2fuWfEX$xV|SGk2!)ncSX>GqQQouW1-SAgSo<8uA?jHEreoSn zo~2`19xO^xVSawxfXP(vLH5-W5Iz#$Pj7?tO)3;gs>~aj)v9ovp$rii3@BQIf`rz? zQMYaGoS22IsA7Fgt?I z#qP#{l7;HefXUEHahqk8ny^{Eia(KicfbwigoUc02dc6%K((azxQJRNmlnrV5G(Ch z4GOIl-tp7K$5iyj4$|{Wi=Xg7(sMsfEfnKw34clbf#OAd^2wy=CZS9&FP#nlf-$GG z>oaZVpEQ){i&VQ%BqUK~k1nzHM5|h)r8p=R%sGD5c}{Yg(kxQV%jpm!kPgW#B92=6 zL(MOkdi+%;kL+u(3VdxO7$w)3B6)#8LxomC%d-Ro(y{7idGa1l6o~V?4}0_KuRq?0 zj?;CFjTbR@Obt_6Xs?*50~wsC9zE%~OJfcv^aj3B^L!pb(=;1hQ)^z zz~03n#x3=#G)dossSgl$hZ$E(uP=e2Opa)GdIWbHMwW??^@kbb^XIa+_QvCCiX!>} zn`j{s#t%FFG~dtS0Qep}q|lMiq6~}0w7e)=O=Q9+t*x#P83MBZo3=#TFCEbc8$<5! z0K&&7i0XyM7Gr+dn}Ic)#V5;H8f@qYGuyvJuQ-Riw)R@u`IuC>5O(maf41*4Ow!C- z4B&q@00NxZJU(j01^jP(A&_7V4q@I(dZoPUON zBGFNLH_16JGOB`Anc7{>!Z}u*hG0%qSh5w{qQjy&Q_F2hg~1T|~^o69j+NwY6pZ;7AO{RMj9z#l(<1W>SX`FEBu}nAQl1(#NM!Wvk~GEsZhi zW;zpS8IE%CmduQGJ%Cw3BnH8Y$LCTke*%(RJO}U(_=-d!E7sZjd4^2s!AvkhjV^@Y z=HjEd#^C$QogNi&D=Ml;T|A?wIROScW1nlSQ5jp?aHiCme&8lxs$ zPjvNFlwClB;DrCYt;k44HE2sP=L!}Kc46omPDeOQF-dZ)j=gwnkdxb>4W|xMc^o)mR$h(Gpw;GC&?K?aR+W3z&eGyaA;6yM|JgpWQfv;k){%p+rcKTW+UZH1x<@x zUxTnwG@<3UYuM%{oF;UfPd|7xSCBBqYBMctV{y@ww%;@a6n=l>R>C!m)^9GGqkjM; zb9M6T#VTs@jkrI;Z%WWRkdT`yCij3$qqRY0jX; zzUP5N-?e3by~aO6)hZi8d&VzV$v9Oe*?0znegh4^UEZlDA$eQ4|+|-HKiByKuSJh2xm3=u<#NgTR$JEx%#Y2a#vJ3DX6@)wt{K=<0*? zD#-9sN`7#6=Fas@Zq4-+_|SNEwzfvHTPu}K@NR? zcR%E)z7L6yWm~~BQ~j(W5{_)go)x!s;>OS-iN1W0ZmoYXp|cRy`f>DWXl^#Dy-Ge> zD0QcN>_Nb!P)8`i5;hJTx45~O7b`efpx^b=B{rx z!7w)6V|1C^$$7F zY#A`Cve=WoyT=C}4)0c!*sWB?bN49^Na91PI_Qxa6sdN5^BqyDL$3WXLbV`k)OBFo ztP0H7$8pcnb!W9YDfR9uTjIeG%a=Xus)aSGLrELDJtvtI8}LpyA2ic{X3oKMr>rQ) zd^+`hR)qM*AyS#6Py42?^Ay_ zir(dbl9WlmeY#}*NC?U~_2mvlzY@`SDgpKqR+FC&r}=Do^>#U>zy*a^G5Y$l)n?AD zJ-TK84q!SC4vsun_kVXkbW79XwSoWuoFn{i$^$0@TVq4J|I}yGYO??F2Xud^)t&nz z?Nabw=0ofjY}r7enAeH2cXrbd%q^^v5hYSdz5xqK_D{g^4VhU|LbIOB+6`^sDT@te%WbUk4mKs?`v% zD`sQY*0WLBRslPGsLxuo?2#Ia55tzJpEp5f(rhm0HXDSTcrkEmIKP6ip)z;&)$7m~ z{nkBzxTYHZ_F~dPvCfhLhYh=ioe`Fe?eC0I5dVysS(;|ap7_qroC{!iJ}3?a!s(6; z#|n%nDreH8v%9^0PhMf{*Kk^IdW|Yv8|a`Lz!W8`-#S1HN&Jxyn*j%S4lG=r%?u7{ zOLDu^Kq(wY7Y9XKL&|9}8n7aWZFX=n1Fi^Xm_Fi7#-e6WYp0T<9h+0UqQcBgv9Xnka98WJvbq=w1_N)zHeLBxku;X5uV^9U zVVF)TVSB6Q4ohP;ur7khrxz`uKtCQ;4Yzc6pw4%H1LE;?R7WS<3Nd?RcFR?KloDN5J3G}Qwj~mc7q)|CB zl`2yHYxPo4q$f1IlGg?M7TthjD*`Se!5pyZKw~V>*s1OOEI-|!bfSh(1v{5(fBlHt zrm4*1OW|*zcWMyh@P@sIi+n8K`r$L`vgwb7YHzG2*=o54S*TAgbk8uj`DYdz{pt&u z7A!~GOw2@;YLbV<@hK7YwJDX2*GV{(0DnuwZ%e?pH0P9h%y~#s$?WlMa!+VC7Et&% zRYiCaz1H6x?3yWDwFl0tAG#JNibu55HGoP>tNJPpuxCRxDOEo+ggtm;3d!tB% zOMp|+ee_e@{~`9u?|}+_=5&U02zxgw6ia>~JXdXLB5Scaq@g>r;js;zJ6cvYP6y&& zj?Cm7>TuG?ci5e-T`I*$KkPY`bCv$)spd*>5lQ0N6!8?CcFNQXb7WE4{CRLW?bOLm znPVm+PE7X>V|et;pRuAhrTcQ&O}%cPYt=f+yBp~1Dik;mDg-dijt_Y8hr--E2J_hf z0(4|vVtwi0OxUekUO98;*lvr+`BJ~}_e+>~G9s%&#!+?lIXvEsi1FEq75)2*=p^TB zy;)_&!R<$;`q(`mY_1=u9~Evj!G(YibG^KdI;xbzJ#&6NMMm>Osu8lV81(K$R7^8l zruC*f)k8+hM69hivTAu=F}1vZV$9Dcd#j#8fp>;1{>al;e~W0bJcY}Ig>yT$^Z@>A zXKLK%Ychoc0ATJ10PyRM`X4$|Cu58ME4lbj#w*8v*n|v^s*?9($_e8-(;X8kiPBlP zE2Q<*j>ifyk@*r5`)TmBf@{kqfBCI``o8NgY90+ zJ{*Xsm2G7~Cm!6~k}VVy5u&2HcbQbOWbdg(bGZwKY|{7X-nSmsWhgw_z z8|;#-?M9vIAz*ZU85KLTodqU8Gou^3M(EW$xLX@a;00bKJ_%aacNwjqmR+ z|6DKKz$sIf10a9yMmF8%3E%?cdROa)ekyNohyQYG1t%MHTp* zgySo!&wU#_e0TpaKuE_@CFd7v3KOWmS7*%I;{I9g_x83IH1tkVYalMta-rT#jBa$S zye=S%a2=m4BBJx@Qsr%FPF5#HipkKhfl-Z%bk^fyY?uFl>=Zgr?9C-ZDO{kZT>tCg z^hK$-1j|Eat(e)4W^Xy5tE;O|*-}g`A5bZ<&O~;a{Mt?`WbJz(aE5C9h=19>p$6=e znQjULMj5Ui|DW7Fe{HW5;?qQwZ;T+~T-@A8aN!r~r>co$9JC)*NO_}oHraay*=lhs z@zWf-Di8EW88{he&3gUc>h3^KqJKUBJy^o_{Z)mQE4`r$U=1mcppqe$gEuI@!tsxM z0Z0j@6VO5G-}sO@xG?h9ffG)ub)VS7W^n23M={=ZksPQV*eJq6I*teY+}JgX4isxj zzY<39X%MAjMlYx5zdv4@0lxyGsH+rxOfY`>2^yJ1-@Z#dUf*Txg-x!qKz`r>i(?Kh zIyVj3mtM7trgdw`9!M49`H%@aqMoujBz|63!gir?n z7*v@#rl(RVH>xgea)w|a<6#~E#iYb-zu*8_blO8%bUvU1SbGV4u#B!XaK+uOzD$2;sT>qK#>~Bw37CvYn z4_BC_h}?D|ts(re`o&S8tWWOm#|Og3nyQDaL(B{;__aKg9lj1f90wQ~j9x z;o~pBCtl@}hrtX}z5D;2#i^_T^{6AK@aGLpz!M+SPVBSa<^?uTP}R;O9Af$dWwsF^I<2h`l_an%ZmGKQBq$XDC~wXF{MQECR9sjqqCyIt(2M4gVHEzR+2m5}+WSXU4#j40~EQa>DlYW4l-OV)Mzdw2Kek z&EV$9sET7BG=cht8A54WL^Tg$0Ud&&MbJMV%v#zLRUMj4k`4uYH#YBlB&c(|f_615 zJkoTf(`@wDY_JeImx2_a2sq`shSvr-Tq!5x7W))%?dOKJjseI3$*7ScE2L@3!z{qx z2gawB%!tbq9@E#99GkkU%nlR#=jB(q!f^A?``%%T)D9Nqs1*d!NPbvau2wTYKLRoi z&))z>s4!S?jjXByDSG3ho0l3u=#?D-7nxcC#FXP;zlE%=VcvCfqqPq%E2JlE!dL-x z;;DQo&jK`P==3+n;f^2p15(`!5Q`_z4CXp}@2dCTzC&jPkiiHX-2OO0$u|vKD_$ZC04-sz zj%Yo9TWN{4T2Cnzb6WF0R+bz)c~9mgnswiNWnrdQHw&mm#LO_PV~4yyU98R1C&Zdk zfuL0{hMr0L7?ZG({0+nj{K|8g`Lx00nGl7 zxr`w6qdLMq7rL=StvKX!y-Za!lMNSxWl2x=SPBwiI4+luUEtdyFKaU?$#H`)pVrL0 zG=)~%OmZTb6hEkGGaDeAf=Q0?1Qwd{rwtjMD`tPioO`6dmIBR1hT4a`f6Cayqc2k( z*($;hg1L$!Xv+<}_jkMWaSpImL&A6O@-hQn`t)!oCt=2W`O#Unuv3wh0mcqyXQ2sF z!SG5Z-7$=mc_!F-0>p;8+x}DT_ zRHp4~hZI@alXL+e8ZOiEekkv$@e&7r?5jDVQ**oLG;dR1m zMJgih!ecKK%iTval|Vpla9&w3v};|n^LdeW&za2Cn<1MjRM1He51*?$z7c_2;r2K(w=7# zUE`rRqWF<10vlx6*hK)IV8%qxszF9+`lL?bZPKf%V%GPEDIo}eE}e|aQba7iWXgp* zuepo@5rp9aGMrTAPuLmKmUKE7*o?gyn(D|DsuB!=gBU~1pXbRKGMifGu^P?;4Ml?* zNVkQ_sOQsu98(6evMEB#Y~v0^0LCBX1wS~RV3zWdnsLO;s)wk~>fB?nW*w1uO%+%4i$&>qG}er|*4cI?cz=Iw(b-;eS0; z`ggHxT`PtVFwU@brjM*g$4G_zNe()8qnAy~3`{!;a>`jwR>bh{4wBBVK3Z`;< z#fJ(CBY^rlIe6csO3TZqNCezG#&~PsBd%ID44XxO_36>qF44F&1YnCy*wa=xdMtNo z_gy3tVMOiujK=HBzOT&ghC+14Iif;qrSM#F>4hq@hT#w6e{k9J)r^FN#mEk zw;yK}ro*Je!QVd!FYe2?^OB7`GKl$Gc*r6(9Xi#a+n;)f;zMRiq^L(ui-VLRp8WU(7P_-KSv;9Y~Q33 ztvWAF+#$b#rTSUZjhOoA3~|DH^bCu7|=UWe~Xfo{;yV%DCz2!KFD1X&OAA~WkZ6{lDN}IcdQ$t7b zQKH!rD29w9lcPj66`vI(e=HO}5h-`PG~P7b*-SE65|r=npofv%LAi3279StayZSq{ z^Op`E3O-^Eb*EDos_pi>=sNCo`8-@cKh6IhlVf0euH>k|>?tq?i5IJv{6#N4u9@^8 zxuAZ}!OGCUcwdnCwZBTKU_;DF1^{mnyp|8OoY-L+c=77CP;Sp7WBCz{d6 ze)1BSp}qmPVkQf0s)USq!sZXBPn#Os*Cl(WR2z~^3c@` zXVA*06PKg=4P&Ucr#iI%^wbkN1meq@b;BEE>M(V>0AK-^lXbyR!{yTrflHF6`x}N! znE_p1gHFO2BC2AjNGFGo@m&&Y&`JP{t(gebL663qw%Uux`F>qybwk%rvC_vAW2lRf zS;33?)5)Mze7PE9Rf5;UuX;`kWH@M7wKvpnRJ7i87Ewc^d#7g_v4dZZl#VO(Z6I|D zN?+4}SxIf%+Z2y_T68zUJD!Hf)zncZLkRhzqIh%w<;NatFHwcdBH6hPUcxjCohh`) z_}4er?TZ|%G$Vi)&cn3nhqIonnb&Iu6|T!-D`=C7#r}Ns3xnqv!DX~7STaaQN~T>` zV%DRN%E#(v^W*B_T-BG7m-gcIzHi{!miNtiU5;lq;>+vCzHA?FXZxMf2sIiIn{-U( zB=!MC66X5Ha_*t%q4k9yp%%}ZB;^=0*=riEM?u5Yla`pgyg5ezssU3myxXPGFS9{I zG{u4#iV&1Q3@LjeJOFoq)g7u&>!P~$lU2h(^4-_E`>+CS1s2yn5dF;jTtH|(cvnk{ zC)REHiuAnlnZAla8IAS%`P9b4J&0gNe|EJ2y2Gu8#$NkXN(6FT2VkQEv;wtFgthhI zv-t&rZu?pTlH+0Ey8vvP(7P`wg)iHq89Mis7(|rM3R%LL=mc%5gJc!HD`*A7W+8FC z)bEm0mXH006~Nb+@B_hD4;i7&ubcW!ESVd1YH@2dt3p7!iC|<9Q+8P8lzgOmIM}?J z`Ddl6$?|cL@<^*%_KE6_5TV6rTVL6omp7^LQh7`Wa{5w1dG>;LdP}v}L5P?n_pu9m z%+ZrqfBM+T8(ED#KR1zeKN)n%uuK!h(SSf4)!dMP9LRz%u3t0#p4SipfKHUT5~;+sdX3peUem6xJk`Mm6Qu8Y18 zkp^jq^`v-{HSQUkrrX_(c0_3MoJcZ7fHqhb>MWmN3CyWT$(gZ_A{RS}j7-;@Y2ojz zDX&pC_L0+Qh-#WCn7O`L+1MU( zExi?S&7-LUpT-{BV?CX1LE?S{SK!#e-Ze`s)C&fJCCzw(xk(pBk_~|d)hMD&$CvS& zIGo7EXSOk^Mm%$rwrIsP7HcS8-h*wWBVFZR*Qiuo}07VCm1dM>mj#a-Va$Vt#W znJ2CNEXazcx^<-&H*F1@TTK6FcH9R70y4G-Z1*j5-}YT@Zrm5%#+S}W?tFyNY>rz5$ipGI|C|4|q2NP)d6rca>M32X)!&{yF@8++QX z>yo?A*;vW2^)^u+Jyr5QR9yKx?JgQ6i2jCwe-X21aCw8rDi-RB#n2Rn29N)x));OK z(hLkbz?A54sp+&_Uzb8t=D!YFw6VtC8S3S=!WL+jDDn4k?#kdMld2<@vNT{a3K-Im zcs`~~v>bzJv!;O>C~I=$b`K$lz5;8i@^=fPcfSM(%uxfQ7krj5NsDLBVEmZ<{0AUs zo1k-xgRG{+OXlywsA0@;oh5CxRmd(Qpp9*{wX(0u6NHpYn2&GB1mSlL3)3|nPN$II&qRnFWR{rwgP9K-Vs7skB?2wTpqNo^TW%aP}PT zlpNxge-F-DSw`hqc01fOo!V9TTV7Dn2)cnj+rwAhb=c&je+vtwJkSj+aw`Pnz9$gY z2Ts1`%3~65)#<}j_DED#iwWIzlPhNn4o_Nn^ocJ9m*nsad+%B?QT<-5Be=D-@k#UupYS?!>iA8LVZ4!r*}75H21f z%K+AhJ^w%5f)@p&uEd!c*P^N0z(03|HHo+vc}rvkfaWv*n))(Z7G5(y09Y1o!*@BE z9xU`VL2S{^EQoAr7B8R>w!J!v5Em!kem`Fr$P)At6l^CW$7%h3TOrWrFMX@_o0_el zd*oRQ#2I1DfUVumcA;M9Os{DvcgHy#U9a_;#p1@UI2(4acV~HXWm;4`)SN_nYoU$9 z>B(7^cq#@8OOy^#mWcHUFPf50$R=d_1x*aMgk4?y9{&kjpEriak7KWqc66~F$JkO{ znbWPURfHm-WMGMcdg@{ZHYGnM9Jmdjft(|DziJbsRO6lvve z>Atcu#gQowIMKLl$T>fnjQUhmv(qP})AZbk`ux#5g1gM%*ldV+zXnL#rLI~{4L-)& zwsU$IKT1C+PZ{G5xKl_n&1AWsiyx$#cm`SaOPhi*`u~$PL)9(J<55iJX>eJyGp_ z!GaWC-|gNY_&4w-r(t44AM=R2 zZ_GAkTMqermX2djU*$psOGyysH6e-;kde-x1$K}lcZweM0l&*ADTn&PiA0E!7|aZ< zy$pM0ZE;osNjkd@>BkNB$7(xN`ci|TGaYF6nJhngpUC2_p#U+rd2hc65V&;u+MAIp`NRUYqV3HD;WNy&2O(Mo~RAT`ic#RY8 zAr(9kwE#tutxfy6!;)o*gj8uM<@?}VwESbc;^pJ~z6-ziIgrf_0>9TD7$DCRr7x<) z_Y9A{hDRlRnvSZ(S2vVt;XZ>Z+fa5j=@Fg;5ZW|re=YH`rd!ey>6jz$*aSqs68EUu zscFzwQ;BkGlbr5)RvKhYrLH{1EfvT;qmP^sDlL%vF|1D{^_$B0-xz>_8m5IrLEOcL z7`#(GMLCWh{!;6+>dI*>y{8S$j(*H&cYWQURX&>--+!j6%RHw(qgm}%_GP;4D@$rU z{xuJX-{X;x%#Qor&&}EI@|2On@QGGdS{iOJcsaE;!M;{Cl9%s^zh|MY7f;~}7`5$= zenDRNGX)!Q!|L@{X;(AVWrlwNi;;2a;K?(S!BH-B@XqsEaPAfEI z<+#b}s-XTLUp%Y&m`7xZ703XCQV@WD>uc-fY7|(So7HaTIz{{J$yn59Tn)PGC1N|e zNjJ@HZ3k%zI!FvQo)x}Yx{{G)2!gZ-bdMsZgIH4+7i)i`>HN#4druhh0)B-ZEaxdj z(SsRZ1=0^oHwj0tJ0uyuqgRD>-UGeXHncX73951=?kS}#o-pzxXy7orD^U&%4Bn7p zc2(;xvq!hbHP3@>&p8{ndoU5FN)AfnK+7SObDZA0vF3d*L%l|ckCd9Q)?9>tOa)d- z{EFX)rQ*n>twxglCu13QR(?&^%RV^dYAUe=Ztn;<3S}nQRdentABt;jXxsKJGUPTj zKG=3E0e9V*kiop}POnNW4^i``?Cl>#&D%#t6pNMbRKIs_SpF&5-S4FtyFf(s;Byc zFjO5ah}aW-0uyr5JVul+lxB2J16Zs}snb8!apUFrufB}$GyIn>_}d8o+qp+>i*1}B zN)w>XeBVFSDsbjAWCGpsK|WyeLSl1Yg1j&PycT>x2b~+)4E!_jkFV})=L}dar3J^s z8Sw&>l@?uEvVUAKkYOvh<(Q@bO0y>y zsX?}#G?U6HI!i>?3`hmDf*diKN?NTI?Bkj;4)8NFqcF#Pa{cXs#kXL?z(oHSxkPe+L9-wMoU6#(P zsV(}Y!qcD~e1w9b<~2@%0)>v1G->K|clLAw&)W}z=GWI(pJu5U-F{48S;Dr=f3yV9 z4p2ILTUVTxGP404y=Yb>1+Ptrb{i?}@VfadE3L$_h?&(m;CD|yKajiJtT{9O#rJ`U z?d^q)9h}`;kr{zEyRPJ(-jFFz2v{Ic4N{YZ%?RMlbHSaV1835o9(j?m<+||!aqb;) z=x~*wgqBP*iXEGIEdY21`OF0YI$+!ma=Ls(XJn{4+V-%#7{fkpaiHUC)Lruzi~S3wo?QVZ^$2H#-(sTFibkjmqf zyD|%YzDy;~X=_(P4{HDulFQQfy6}xtCXZ16i6v@N8MZ`$WTba6DWY)^pvvBy(rVGA zMuO5)m&3$&cqmqREO>Q~CDV+X=^l0I57ques__q4MQUBaULFbHTF}`Z$jeyJi3UDq zdfCKt`IJyQj8Ks=$(E7ba)9?`!kX3byw+YCO{uZ`aMlcj~fS+{D*Qf3~R8_}FtrIQ1|$8xmGWSFv*UDzZkRVNOD>G~|VtNRFtO zPeA!3Jn}{A(n5SH2*~)~)0j)%q!r`wd5JAmKQrjh=+UMf$=<=%0lv3xay_u$_D5_w zH6}L`T8}FFA-9qwn@9?JFH^^D|ABQdlq6XnqsnhLh{#GT7N=^{$GZs9mO#W%WVjFr zNrp8E_){l$1A#7vq#X7gDJoQBm?Y(k zappB3pb4Go1o+BZL|pU{in7ZK-LGDf2#djxj2Y-+H`Q*+8@?q z&v$(&U?!m2$BHUDjlD^TAacdEp6XZw(KIx_Y9o41mHEh>V-!cBny3qKtE3G)gUMb0{WU!TO zdcBM5o?#Prfad`b9ZaakCENCZmBX(Mwt!xwCDn|9YZZTDAHTiu53$ahZXP8LZo`U7s8d0i$43YY}I6xwsl|0#~I z*(h9)Qa}@Le%`;iL>l8CBsp2y@9PzRv7a?_rF_YhjJ^96p$__(r^e28*inwWt6mbT zcf4VI9-_LT`6By9!mB3Ei#+(D8T}z-hO>YhfV45-;{gHowbF<02ran)vfptC&WSaL zL$m^7gNpIlS#X;3&_RK8#1W&NwG#Kf+u_82q$E@nyYMVy@SGY)$bX}_$@;q^e>5n= z3I^KuddV@Txe4j<6SvhS$FL=|8QK^Td}C3a^zj{6s6F=dm6Gwa;WBQ1!1S6}eY5gc zhi~dM|H_Mj7!9u7wE#zrb}K$MH?B1xE9>j(Bb8e14`TSHaA=Nj?LyMJB2?oNUH|?n zp=dq+Vh@rh#Oe3#_%)3|rTCFMF1aF|OVvf|5C6y;EEG>#m#A5W2+BzX zR1NsDymq^JTRccy>C5`pJAptt3~-z^*8Um~ddw-7beBz32YO!ck)yP1z>6yqbTI6S zI-`1KFuqa_0~p2}c*X^HyZje8SBNNKP(L@|^}OTPdaYbNaFoKV5$qq}rrrJ?z@;Sw ztD6hFeVcuny69a92xIenA^NilL;}s}djG)Vw;NyQodN^3W9PrkwPZ&#nc-I4$>EED z>U(MjguFec(*=dNbZ!<3W|!!rv+k)WWEf)uq&as8P3(8+a2@kZp5HE&|1gDR&un8# zBT4YVY-Er9+(P}lN*d>kE^tRZJy|aF(r+@qA9tC!sYws5ub5f?X3s?a{3YOotlI2q`n|>3dCkeTH+uP2K?H2V8(xapC5zwN^hJ@V&NEC_~}2vgpjPH&mw3ztld`#EGpUNbzUIpBF!=fwT~nw0M5wt#%B_z~_re~hw!SSd@a!W&`u zB?24bjWWrQg<8W}|1YNS8#W^Ngkj;;X^Iu3B3!2+ zH$f#l;C5pZw$~o%5ot+JDA_ng-(xE$HR5op;@7IMcRwvq;xDqEI}adUa|N$O$0fQB z-Koi3`v;fK>s(t*37x7v1oOpJ_ux8e67*rw&9mYRv&VPYH_K`I4Wg-$IN#MB44q9a zqPY*6(U9aWd{U?R`*#T7ez$TnHr_g`=kpIeRXCR5M%1y_q)LKm+!@HT<@K@UXEA)B-CWeCK z%fOo9aA6ojp8XaTUI0aOY!voE$_+yXNjY^i?fBbo|~hAy}gO!Z+T{;AS*r0 zfY5cKM#EWr8|I0+EjSklP7kFkY~9MzGM-|5(fG4nq{oAIjF56UHDkQ|7EdH>I5}YR zlqG^aK8S+DOtzeJj@NRnRWrOUKlX1-M0h=u%mukq5Mo{*I@qeJ!F7^T0Vc& zqeFa z$WAk*e<&Q%yna^FZJszZttj0yRb%CCs%u_xsJn3p*XZ9&t4)N=(#PjWS7|d^1MA1r zukq6n@BOc$kZN{nkQZGAQBjtK*#h6vNu|QJPQ4dZ7fM<(m43324-IZ1HCGImy*xXg z9~lT?v0XY$7ee3vO>oMe_we5QdkQ?iC-OhD_+4x*jO>g}^!{@u{{eT}#4$n*GQfzs z%958IZuc zsxd${iW-GP5xqkBMGaMrHslT&PAEwGE=; zE9)VscnIZkaI@RVHTQb>Z_HA|ZEQir-|jL2?tkkLTrC`(T@0*EY+Zl9dP_~~mu-mh z%QobQ`<>f0@Nmm!8b8$KPx~2arJB+{|1pN5#a$C2Q6w%Wxvt$I6kSuyvh}vbLy6dL z_dXez?XoRmB5>*JOtxK#PT)KroRI@BPGD`SsCeZPEiMB3xjT5WOMUis7TV*dwfvWx zud|j1;Tb$VGq*wj;d8EPF^(@=$zWI=Up=w}zGR4lqW)gE#;aChf*SF0DOkacyY-K$ z2g2`rfWi4EHb!XLm7oZaIsu5@ADe>2l93hilzZE6tw4<2#>556muSGap2sUb##zfr zqc9F9IN3{Dg}OkV5OYfkkqlwR@qE8MU&`PaIs+rMZ3 z+jP||zGDXmp03U?5MRiG$1P5+bKJj|H<;Rp3Oe z0heNECgKJ`W`A*ctrDrq*ZFi0f7;qZIFji{?>-p{QtR)+0IlsxMPLPE7G|t~=5qqo zx6FpH!%fOz(n2)=&*1UGki7Q6@Q7&-eg8<`hp#|5`Vz#8#O6iK)thZ2NRFYCD z)e&Pp4}r4TPTx}$si(cWwRCHSE)7?kYC@^3i**rBYrSAeefpH-oy}~iIG$`wEk&+z ziF6*K%E|8K;pO&l@bSSS6a1{QVpA)8B=>4GjD=&J0Qt_D-c5jlD(efvMO!pf%~+6M zkh$s`>lrHVfYwbZ8-t`0Dy9^GKBq;!p*?eK-%{jl?xO0GSFWH;SRCP#Ev<=zFG#5J z(^6Y%ZFw>W$)?fm0wRM-qnVx6Ejj4JDp8MSk>E3e=dokD2@-$)$5(t))OUEs;y_iP zuwI>jpR!XNX~DGKO7>)F{!?$gwr1Z$6NO;m3_E0P4R{9_=D~&nX?u~cG zYDDxson0?HFxE)w;c)5fakUVeo9{)Yei-b- zjzO|T&AM)LwQXayZQHhO+qP}nwr$(Ct<}cuZ^t=t`KYgA^;8Z~ESzWF}* zBAgjFgh*VH6jXC5(r%A}3HxLwr{ig2QhU|86Q~5XGxYG6JKrRDWf5s5fVaWS-o*sg zqTKfA{%WsBHN{Lr+-i$z3UqXMx9y>&fwO#dybFrMrBx(Z-c7QnH1dNvn$UaKwD_C@ zaI7DXPmk^=OY7*yR9VwW-CT0DsNL$9-nwxS_R$<1pRi)OoD#0f-Rv0%cwOmzw`OZo z5gQ*>Cay`0gpYzv-sqk#ue|wDX-_(l|CULw0y}fPC$u%N!_I#MzPaL~x;JvmeI;qm z&T4vdJ}9^-x_@M@q{h@|9;jy^YdUah&(|PUp;p2H710dd5( znHx)x?t1HUp5{JM6yetSg+iub-<$I6*z4W)|4NfVB8Ft(ybuou7xlB))c98xC0=Z6 zmednT^oXM{LwMVnzI;2+Np%(>I%acF4?$gfuxuJEOW7ih;YBQ~`C@-U{eNqAW(}q_ zRlooMNWU)71pmhyyQ{u~&97DLf9@jJD*xLnw(a4U!m@5ZABsUIM>}5!AdWo*+d&8e zqQWt%K_-t+KAN-t(^F_Psjz_wywWU84-`Q3a5L6<+j%&hJcU2)>|gKCL|McD4|U{D zXgInqjcggX!8Wr6X~|@)Uxduzc0eKf2-dBd#Ug2{Mi$jBZv0(?`k-0aNj@4rz&nz< zinfm|QfKe?N78OaQhyCTfr-V2*&Iw2&f}XxM$R3 zoU?nz#iOi&)k5fKE2b}gyZTh)d=}cBC_!Z9IoF>I14{CDM2%Vf_O;~Q=YGH(1UJrB}RGPv?^wZCd^7F1F>+3XKOE&FFi$lWO+ltfdBdfYVu_P@*qRu7!{WPe2PM6|}e>^GWVoY4c0(><5W1oQLsQjtH(#%o;w__0jmWy$O z9um`p1rGVW4BUUde0uZ2&Mk(%SfnPQ3j=ud>gdwhGJ1J&-Ay8uSef!5A)X|sL=on)DWgM#UUai2Y^-*u{>aEls3 z-DT}OL7zJ8L)q_{`3Dsdzj?g2IAR`KdUAmR-ss8N4lpp2zk_K8QD({Vr1<{s+R_e;!y^g;eSA5~7O+8T1n3Y~qPk_&V66 z!Ky*xS+-VVJiOshF3Ljt=@;y=2@mX1k4bOA@99SuG^HxB?n)%=HCf(l9@^vw=(?RhbOss2{17Vc>jGu-D1PH1-s^` z9;(lfJuUgiZ6Qv)91y{J(?aqxV0t2vu&3!{Ek+%WGXcM&-0kz8Wup0Vvt1;1)6q8E zSw%o!Cko;p!%%khQF769n+`KkClF0&6X1vMlYSDU+7bUqK7nQy@u!W;V|Pq-DMjCO zyLnTY{;A{G4TgdSr7EmD+GeLLJIlxGs)CL2bsl4Sh>%Z^W?}Jp*Z2Kl_Wt*L4CvkJ z`+1WF9p25Kx2}Tj`&(kKiyym4#OpSjcy6Q`&*}l#^Q~9^rM;l<-tFak`rhfq&ER&N zTbTk#nE=z~KoA{Ax&PV`1h#wvQH&nzU1PpBiDN$jPdfZoKl>&A%{Q8(gR(v6@g&TY zu_z~~f<4R#U<(&uJra-FVLunb&<+`*scIz+?DcrEa_z6B+<~l|AB=0NJwGxAYrD6> zMYx>U3%8RCLQ6@iIbel&8JJ+teCu%Ga=G-5{O}2WI-S--`R459mIA9;nbHAj$)n5G z;?7l?%AO^Pt{x)l8bG+}?aTZ<5Ta%!N<4tI+2={IudE+d*P{T`FAv`(u}gsVs4CXDQ$ zaT&-D#D}BPYum0Upakp0=jgb-I@+KUIh-I7o5kiS4{fP9nzoI-udS0}*N z1~`xz!}w4dD=b(?z` zpyvl8m@aCs4Jr5*w+Xkt(b4wQ@ThfDjQvM|a=FP(3*;8)pe35y(23ErPG6+(XNM@T z8t{l3aOhr}fm$Tcbj`4D>7W+UwQCe$35>0bexmcby1JHO^sF|~D8@$^1Z_fQ)R=y5 z(kfEXQv6VtEV!J$Q6Z>dGP@*BwT@$R_*cAZw4=v~??b473fW7kl}6F7739L+Tg$KF zS*F0UipTg9AYg)=?odx~YPXK^SQ_k#>VHI;Kk&Gx`so?6FJ1iU2UAauLlO)?1 zCk!~tqy1?}+CFLiO$=e!nE{a|y!Cel)zvo!cm-->Qkuyy3W(e#g(!GQpl00OUn1R4r`fJdc+H~!aTw*3W2+VL_<1HS~03GlAgV8>wRPMZGLKkQLs!guN zUVyRII%W%71zc9>Bc->~47AjgU*GBOr9h8|Q45q%dJ%Kk0bUjn=$Pv38OhIdte8pY zR`t)LKcL)6bil)O2Rv*G$qSA-NE|EHYS9liEl_a5O)5r0-~->v@q(?zKqMXV#|E5Y zWSl~7Zv4FAaQpl`99QO2hXV9o9FBsegiO*KxfdggbqLhkQeekGHh8_bU`Q&}p-Aob z@SVa--4&Yqxm20*;LG)c(9u0#G6f;wP}fAjj8E2(P0Y-aOTdJ{ykSi0c7TT@T9Hpn zssg&P2lH$0+c!Psi8GYZpsV#i%1tLR$u-5JR+|m0)MscH)73jgV&0=fO`$nS#dY!_ z_hI<;(XUJW2~$=(5yExThxz){wCS*YsJUo)?)~xcBTpfI3*o`3J2SOFD`Znvm?x=J z4!!6f^Mp^Vqy#Wwd%yitw*(iI7%QRDF_0G~buWfZ#u{S^O{hw=o|17npe1>cv`@}5 z&W~CbB~?=A3||E&&qY8rQ;HFo?0=+p5o)TBjLO*BQ^t=0KLf!Bd_~w79oJYMUCuZw zIgap{NCp37cL@9i0VQ#rCH!|P3JU_q6^!m|fet`Fn7`UJhyc60UCNJXsSPiDgCOt} zB&^&{!k4At1o8u@p{70NNvFk*3g0`iYxbUbKGH2@>w$-ePa@;U9o!wfN6^3svye_c zu)n6S958Zzb&s-0dhZcb=4Ff%0OlE=hitJkNXRZK4mb1|0ZA8nnq-`57uJw1na(-| zOaO>ePo2$Q?MT?qQw!O=r?cA|7GfP)@ob#s=aHOg_IlVK5?{X$wvC^gJu|pmHq!fiF$K(eII4qppAB0Bq$@W1HTl3w!jA!Z znG5NWNG%_0kpRu-{_nYZnCk8euzN2&WV82N-%DEr73vt&9U7;^eelt@`p%Pc$W417 znZ#MA?PR2^kvA_K699*sb^cl_8KMxPxC3$F`|`(7l@n4EciPpWR(XYZoRWRG}M}l?pfP+-OIZ;zy&{TIePhbLS z15bDal1~W3!8&2o3UkIYxDVRrKEoc5RU?=7Vu#&so4nRr-QFAAZr{c5T$d*Lw$~D2 zx;`0bg|VWq!otA6C~E8>f7s|=^x*W*CtqxP19NNVC{w7K=$kmGEA>ztzYnCva2w`< zYwMM;g9=4l6?Hq58(_&VEN5xDjvJ7K)Gs#=(~Lk`(c3uGS7{lsGfdgkX2EkP`S#Oeyf>){Cq9 zjZL@qAIXfwZbWojm0si_FD zGR6xB`m9kAiQDiDJ}$s%_RTnTiS@l)_UdNwZ$Kf z^@FP=X2dw{S(W0{v^WfKyR?wS?mdfL9sbwvm}a=p zvacMa&iFkC)x|}+vFf(1f7`d02|JNony$Y|u!;{jfu$K3f3!8}odnq*c~WM3Wom5- zxvpW$NYu~#iF0`?oPC0tb9q@f@=ps>d3+U4SGCPeqANJ~oi%LA##?u9H{Sbg8H#dd z%e)?YWiTmw8JPqr;CH?O3!X8bNX&Ht7gB7I*QVsr#NU6%x^YB5*koyEwwqoHaVs-` z)DpVxFkBtr2Uq%W-~3B60kq*d&$A@EtG&rB@(rm2YV9425}VTIGdJ>@T1tR2PFBA? zs=)X@8L*36jJLd6v03$ej*P1crb<536-wQKm}E0tEjF1Chg*>MBAs3NKk2JAH;%2L zKWV0ueGj1yIU$n&!MA@V8h}Nr_}nO0Ur$F{Nb>&F2<$PP*MNOsy#^j5?0jZG18k0< z8Fw3gyxhCDYkTD0GtVVlB3^^5VrjgnY9DU8kt#v758N`rU}|n|R>*KyU3od{|8hqi z#N{Traq0UmAwkZ`8-!nm%|m>XHP_0kF3|Q%wh+rKL&TaRqX0Bo}3BN0pD(?Gfhy& zTR-i`ZN=;O=LM#GVKR3>Rrp^DZb19o_c$ndcnkf_#K2Tlh}L%&Rn=Bx{)2UT?-2^2 z!)cVstI5}l7*j#f2e7%Z;9A=*Ma4vBdz`W@f)n7Qs-mXmd|LZaQEOqT^`?DWVk_QI zfoQgdCao@kx4T=vNj>F}PV}TX#(lSNvj0s0C|78~gkOM(Ps$Aq6<6|PF)})R#X6s{ z2}xp-`YY7wa-zc9jw(hl`6d~~YHl29D^yOtL)3Ro@Y2&bPU=Z>gxQ`uZN`#nXI{=A z_MtShJ$_-xqJBpqO-k*zn`ymN$-)PzmAhQQ@RyPp%D7}h4Rm)uGoz-gZn80sk5enX z-ssfvQw@Q5dJb}J97?7=3+nP=;1&LqYx#1mrBL1{gjpRq}2vRJ;BW7=;%#zMv z-p@cmqZW47h<9k@Fl-KCR#$iwEI6tj{Mpl+>~A~wb+{l!>Wf)Ty%tB*ty7Os?a#i@ zVd)VZDRAi?E0G5bn5NmHqftv4AOnVkTIGbg$NTqZgDX81G}bt|E1Ee=w(jX^yhz=u z!dpOR?D7Yt+lEFGrrKk@Q}SNJgGc)Q7zpZB7oPuuE$QY`YR4stiy{%|TzW!_DI3}yKnmBf?V-mOCId?^)x4u>$XuMp#6&i8# zK|#z)`#4V9h$SDhI3ByIn+s7hxsaAJ@kmpBL{x-oj7rgPydl#7W?f7b8t8S zecGDCDD!Dz_MD^C8vA~0a3Y?>d zHu(^HH&u%R-xdW~Y&zI2e>m~+gCPfr+L7*KZNzE<8_lxQNogQt3 z;O!RRpF5{T`@0;U*wM$G5#RrYbxJZ*Xixn`I*tFDjFA7IYjwZgm$t5s|GNwKUo|`R zpI;YDlpk$h(8~jSth#36Rh$r@KPpc)g8rSwY6~cWNa#&Mn*#r7f^h8A{dnh!Dj*tj zn&0^0Kwbs0#E*@Rj=mf};_8wlia&hoxk@x#YNbXS<1yP}=C6yGKi4M(Z)6(16_bib z)ma-RS3px5>of<`S>38*j8a}1Y|(_Q08ZC?eUMjieb_s(XKlOt|WsX9*^*Oy6~U7P;A#~OS{Ov6^lijnh5{rF71LYomf0U zbt{cV!KP34Y!zbgk(5;ts?B5n4`e+-shNsS=3-X>mWKSIkHHDr{9@agTK_4&AyrS} zj3}tCs=-RFUum<(bJrScm4clJW3XRrM8cui9w%an%^q7knbFS8S9PIj7KT~Ng%=QP z?b1N6=`_Ll_zz^hw~;6;$fOxamm|{*&)9>${uSWVLMpAo3E`66>6B;CevK(AfFT>F zd!9`(TrAC5KbniEn%?Asn@iqipAnJJEVXJz!C{gm3y0eeLXa*H;~tJ7l*zX<=(2L7 zemLj;(q-aTjBpULN~09BfXt#dM6rtyj^Kgd_*O9cNnYqm;h>LtgL#m2I` zhhf%5z&ewX+%UQxyaZ`U{UGoK*vTkr35e8#VJ8+g3Eq$?jFyi zy#uG2YbNM{mP%!Um1~z~OwKk^+2RDK<+TQyAL0ln7l#FPmH1v(oVQ#ej3LWcn(?!3jy+t$Bb{M>79ih zJ0g0+q*ig;j_RQB7OgtV&}@>5=`?dU8ZT{A4Vb#b;MyC=WrZF`VZU($n%z-GqI?Le z;ywjcK}LOt9XAoaTrFhpe6bqD;DLCT#e{O-9lfMPXW)AT=G4+#2XmRRExBM^srQ8S`qC7rE+gTXk1zyYMqp~ zCcrh_Z8T-vM;!Sv`M#&?4^#qS+uenzaWRj)h~3prZn}aZ zywg->Rpc6VS)!6 za|oWl^EFgQF?ypd8ON-Jt-Gx?2i2VF9Z}3y$;SllMJiW%uvnHnDol}sU`YRU4-GGK zF~OHvT`Wftmf8)^6kyvIlmu^Lxq{~BuRl1tmEz{kt>g1xyGdV^ZBC?+k$duNf0xl| z0#6!23qQ(T@>0McbGb2Cvx6V7ntBQH3N~XgYo1*336!@`XvrGcE9bOcFi_h|QwaMwo`t%*^6xV`SsU>R^Q$$Lio@o0Mmo(VVHkn#%gh zpyU0sj-mOUwZf0rKlDkqRLEAEh%IWXB^rMPL0Ht<_456+9YS=AqcOkb5odJY`-th}-e~qiqIUCmFzJ>fft*s}qx;oGOJ zBja=)Kvf!@m@6*H@Bh|`3@ev@RQY|X@%&xr|MSz0+keIeBxl5_XjJSbr^Kfvr=+P{ z4io?a7P_X6K9iI{H9`L!Yz=NkBt*d4O3qW3DS=I%5TzKM3}XQq=U+T2Jn|)laHxr_ z%4s7RNQi56fubt&*=s}PVg5IPjeJTxhum)r2EXgq*zJFEbaQk1kId;ep2;cdF`S5(=h(i0icE zEyT^`akV;eg~;V9bzs$g4Ghr!TO_|94)CkF`mfjjsEIeUwYJlDqSe(kw=s9p)%^_! z_`hwD=Ssz`^}9jn@51|C|F3}le;KF$uPyA{8QFi|Zal>g>4y)`H{V^Fmi=cQhGP=H zz^wT1Vbk3+F+Yg)Pas|zgH=j(p|Drt@G?z2WRX@l=)g4_z^1xrI)B??p@jV4S?mSI ziY)8!)K*uU*-H!uJI>i;>n z|JvfeayB$E)irk0x3>EagWbu^sXBSwW=jM<+yQjD zek}CqxnepxPhUN1}^T^X*bNssJ(hAO{UyFKeO(X&D4 z!{3vweJc+;Xv(%&_w-k{Wxwl5UsEU6TYk!x%4?tTVRe7)qr)3#^zdID7xxZciBDI{ zhdIVa!%j8t%K6RmbQzeh1ZFMG&vWpkxsoW|&HRgTdh0#V?&w*aieV#?NUo#A^k|Yo z%{r*x0uh#8qS+_uQ9T&Gr~O2JB(J*oc78_QwC*2 ziAeNneg&&$8bKg1YVrG+@UY?KCxVCE#g3aLD`@w!an(WQ%oGjj8n)daf>czZ#^JL^ z7-_u~zSetv4&JV_IC4%Kbu|r=B?VzkZ)x?eWP^g`%_R7VkFD>qQUt)*Pg_g2{Dk}v zm9bQ#a&1Dm8gKS>n@mSrFT{^j)a`Gy~Kv{MM(HgYw}eqq2Ef{Nry z8F3H@7Is4?Gei;CJ08~$!w_|1o-a4c8xOZy`b`CCGe|2fGWTQ>uu}_|_@+n9p-ja! z_e<9y*+ULpxKK0~u@qu=RcduS$>F{kptXZIE2rh*DU_+`wcQ3+M^i%^EhNCugmg^y z@cLn+%vNd?cs|Lw*O9U+MkjR|(hQnSU)|?Fw(UZu+62n|M`kG2%6KGy@TA3}p-JL3 znYodyXzy=NW^BwF;dCnC_PFyOQK3%q)P*T{gmJ{QBUukh!PxLpR!3{tJLSeT#IO@v za_pd{%UH_QjG!sn-R@W~+X=#`7`vHYP=?i4vGT)}nZEX3eXEE#+VIvk zWEYEd^-mrbWl}1jWCov$27&#x_*0wEij!ZXSfVq&>F!|O{u@^m?qFKZ{P%}2)b1B5 z{C_!S{{b=R{)#`X+;#2T?Hp_^j18TfY_0#RpUf@ocqroH`W>M&<-bP-;XSoxX*nI< zj2T@m#8`+qZduBt1y7VoLZ6?r zk!O1*Rlh3sL2j_eo?i2!k1ecyqjO#`L#5-@|ID1?eF4HPp^CA2<6c<{jDop5k%82G<5~ z7&?wkQmF?Gm~V#+Gd$UzDg*4Br-R!sdrt`UguxF&z&aa4wmUx6gWc?n6HN9=x{yo^-Lr4t49hl@+q0`3 zl-EscDo1!9Lw653L~##G5g!ZGx2*S&M64+vb3*ngP-0M@DHG5hnKG#2uh*?`+Kncu;9gOW-U@4BxdLCD}q^I>T;jCSR7IeY;h7>~X=A7`!RdUg7z+c%O^lVJlp@uj3ww`xP&V>TT9b44CAOklj5AxeAR&HZd)%rPNw!pE5 zw-TWJ?f`ynu`jC7O=RGm0UFiXC9BnYg8v}&=|XZ|QlcyI5@gaCbMEAHOW56}kqQkYao6HKqg~gI2k9){p|0=x$2)LxY{n(;U8ps?3&6mb<&xbwzJ2MpL4X?X;3-5HH; zuxvE@R4S*mGTl1+p=nw)NRZ9I_ma=nQ+Oc?(=>}!)CZ7QkIsdWa}jj!l+{<$$b;e^jQr5zfh`;Hpch!!8E+Fm5$6XG$84 z`>Rp_3(#KYh2i#6f+hTAKM`Et+@O&zRwQ!KB|dwr;PU>4^z3g5OGZn$D=KgZL-CC} zRjQbWCFz#Tm2(vdf()dxz=h6(udOvVIBmU&0r9ETFx6})$kuDhQ25$yqL))A<9szX z8$rg@T`;wJw&ot_Y-rnsO4s{o?IFOM~{j4^N%*E}-r z_aXNP&OxfaiE`$OPg+sgm6=1)3MtZxvvv3&d94Bv(A|x~S5sYuCmAjU_u&Pcq80=L zeGo#rM@kKYOAGs}aFU8kUZkIqa~wC!i*5J}hfOw>t`Ot&8BLY78F{Z(P+|7?7M}qZ$iGvE6!HdKQ*^VL2 zv%qrMExi)oH-I={_5y|D3lYEsi9;iT_pFU4jWYTo53AJf8~bTW>G{przD+&K!&a65 zoO3;kL{dr7iMP~jylNoa97(UR49SwJ^?nZ89`b%7{nYl(bNa&A@&@;v6=vk@es8z-en0Q_ zeldUK`hHtJ@BZ9bcJB^lx{LJc7^3?by&3lMZF@l0o5D}ikD83;*{Tn?xU3nj=<^<$ z-PTXbV@u<^rxD=5cH1QYj@K_ENRI*a;R`#$HXy=!=LF`IoPR3trM>>0J27WrD-P`@ zjTL`A+di`dz~EkYY4MpL6vMK!;Js)tNn??OVTt^sy$&#{a&RL=G^{f>Uw_U__a?^{ zP8IAuz(WQPKg%(A_K9pIKN}Rzu24%KJmnkHcoSiMZDm!Khf0K!M22Z1CLmiLZx!?o zG)~kd69$z>@kn-0kfdW)O~R8P!WYm)p9LUF(tOKm;sxyNZU`?F7@Z!f&{O~Nb`v@K z45#aJG9{7{(lKY*hwJGS;$KVE4;xVj1^2X)ICY!Gy%)X?jC;mSws$t>7ns5S%z7WH z18`BYG#?a4Szgv5{^)#nVDz&bxt$o8O6vl*{dL+27W%?qz8hMh`*Y~(>-?2@`P22i z{O@-C-|hkL>W{})=I!^xyJ`ob&JS$P=W}Oo?|16uuJ+Hx;^oilP3mgzZQoB`A@EP1 zWqt++_tDn&VYe!nC}{vlgAw7YNoyTI0@1Lbp7g)`0+(L}@zMNf4b#xg__r#luNH+Q zssD~5N%hKOAd;JM=*_pPXzg=sEWeTunV2Rc@32A?7~n~gYoC<**=mA$66oJ2bAps( zpdXl0F=E;bmFRl~$4|gCL%M-NtFUKdU>#^6nUPDA%Pgo=vbgCtY+)%4TGa)aB<_ce zfGUyv+S`kq9&L%Mcx`P(vrQNb=i_QU2S~cjG9~JvzJO~Iy>O6YAtfLYK=Tz5{AnDP zm7OjW-P)=W@N&98kp@*vMC$;OP5FST!@9yvWcU5P4^>jYauv4d&M+0hRODuBm^dI2 z)ZMkr$Ncd0VJ-pj%3wv%gWx4na@jJWQUjevn*m;FSXJi2Lj7BGtpb`4SKi=q*@ z%K*~%jgraSlUf=FQYgKChR7H;wgm1oh|2WK!B**Jr-nz(xgV>EQ3p{IW)H+E8HD4S zfvT#N5sd-FDe&-7CZXpBGu0__J&Rgm)|V*E1Yzg^z=bS)DqnKV%Ygb;d6@fgYOk!D z5azOwYNffppXJN)WoY2MlEPf7R2d}EZY+J%?~JDgYF1@;Gs zXrkssGD`)QC>UOdi$ucc1{_$!I&(3L?DXASNmgd_K)FO!KV`s;zVM3%WXpi+LvS-? zrVBLw(7*-Wne7uV`wkNZFR*DIqDS-fMV7vjf9QN=uOsqGbMqSKCN(aQUT-KRc^eqz z%~hxeYsm<$lZw2*4P7lT(Ek(NCed30w76q?GC~xsS6`sYu;{ia8jDC8!0v1(j5E!x zLg?W&&j65zO{k$kPBlyuOUBYWOdz|`@FbM8B&m{BbScsMbi;mxsNti|+MXvn5nnKs zN2lszp0Uqn(N{@^sLi>qhr_kDZRIaeXZi!dZK|1R{b&D{<#7OE&>2e(iyq(e$jX{M zllbXA5K4Ej$rI7E%NuFe!cE0kebNq>3_0VfGp_uo5)Yj{aDB|}bUqZ1r(lM)Oj28F zG{XugZI};0yoET;Vo~EK4Vtvy;%jREG zEmaiBaT@F9trvxKVrNDit8ts^BY5{;3fejq-%2eAvW#!GC`ek3u8v|(vf^iAOR#Wf z@%{PeWK0~$BUiQNW{=hNS<;rl4CTf=$X;TFeNpr=#lv)#>JIBa$t|z(o^USM$syzP zILHFA*_AYE52q2J1fdq#p+yS;jcefw$$1}nbf^IYbu3JE7G3tzI`2<7Lf+0 zrv*u^gDu&kA#W=hmI}>9!s5?&t>=&w0=h|OT15$(#E$I6@yUh7EP#ksVyOz79??)j zo7IccJ0I~k^}@qTm8%5b9@gTOe!=~zqhNYG02k~;(VPPXW2?1*wK>I5KUDe2;LF;y z8VjhKfTsk1itqel0e1BAgW^$?B$&h*kv2NIV+ren{l)iQkMFckYC62CMUfEOf}Yc0 za3BdZEXO30iZ3Ifmh10by6=+DKkRX)#HAa*E6!`m$T zYQAYLmi0i{3IgEOb(7=e93RNfp+h7T`M~=*!39Dqxwg-y-zg#84wXclP=k~btZ=MB z%!%@Nha}6>n=cjlgIOf})x9Iy?{DiKxN$Fxr&W;MAEKTHqPP|WI*Es)2zv@M5q@rt z2&$L_kc@W!Yy0MhYtNW!r~UK!ZP1;1cl4IJ#-UZ@}TN0U9FhJT)Kv{ z(-?JfOg@e?BI1Rv>Ed7dSQm^tVU~xqMBAo<(X4!yRPQAcI0o9zG!dd` zjbtJ%sG&cOW$}C@slkmZIodDa2Cad3`HSN_#roc~iYvYfXoQNDQP$rJa>;%x|ARWv z2soN-pxolRCdnZ->_@$aQqmNu6EYOu5(d*i#A054|6wesDZ$`$sbOP5r7RJ9H<82| z5t~|7cpxeaH;s6joiNYz$+MAO?;11NZ@5A=4BZe~3}^qc2gF+pASkx=6%f@qu(qOS zHX)Sp2D_;Fxn-f;G#AiP!?|o-Hi>w0eBemhsc75G9KqdZsN6oLD6fr{>nts~E{Pnu zbh}tJW^@z06&C;&hl~0|2~k5}l00ZEobc>ZbRJ6}VstUjtjI%XRK?B-a(uHmb+%3KYWDLf{E1g_y6iwlQcRkm;Ee>FyPxRNDA0 zUr3!pD{!)MZG+`dQqpxh1o0?*ZO3QSv4orm*poK)`*UyD6vviM=-RkbH_Pk$VcGuF z;Xb6>%^fIulaGB8A(;t=?#3NmaUSHei#}ppWLrX6_3%Jk84zr9q#o z&6*M&RZLjq-+H$Iv9dLV`f@9PDEtq(On`8YFCW~`<6@an`v z{4go`=%$ugn-zbUDiu#El1hUcIH-RC_op*&J7xrBrWovSH^5v(bYYAUs0t zpdAM`ykV6`whWQGwu9@_#G!PCkEqPduI3vNCC6mc3PM2qHGe|^g7*%d!()_WVAmx} zrk3_7yZ70e=GEF%9Dq5faVPUCma{s=l4O;gbVg=5r zeombq(2$rBe(~}}I-jKBT1rc^e~cyuuWWVU8ZX%2kWI|z2UYl~r2eFvrurj&i`VxR zpc)c{A-0Qk*ghQ5`M$``13V5~n4W8qvJHCc0O($rGT_^3R&|&4xvxa!r_)m?iba3LTxt6OX1h)5-g-lt8I#Guqz_q#%JcHaEkmB9 zFKcx?*`=OJW-)3{WZM(qj+2j$gkT1-KP}(}#7P1qC;+}@f1J6ctLR0JKOCwyCh6yb zl&oj6lYem57CeHxOB-rR4VT`0B3xYeeP|o1fiD@P`%*-9_&Y*s;Gt!xq6)g`(<&-zU@cko<4pD;THrV`H?yl_H{H{# z+BRC)2HN@uVeH4O18?@RKMIc0td5Fq@MT$WmullZ+z`wPbb~WR(9J+`nD#_+dG>ww zsncRZ@X(Myol5BxWY_xBGSZawy7alY@X#9A9rVtA zTodg^!6(`k(HOWuM*0ET<};6%?d)^!nKO^D3-WshS;c}abUO+bw0X~Pg1j{p6Ux?z z@3f9S%86M?(@LFqhpXO6z`?k4sC424CQ7aZ`uxz0Pv%5BatP9jT^oSu2$7BhOs^c) zsjJ<9C)$6XdzBTv2b1w#(QkXG#OhSk85X zj2qK}d-bF_;GRX*jPNro-lTfIOv68G4+{~8NJ2j*p}y&k6b)IUprKQ_i0UQggQ*~D z;pTLrL+r2IDpR_?y#U*jpubRCq#&CBA&Z%xm$gwwk&)#W>c_=LDxa~Z&HE&0c{8CK zoV(Qd{h4Tc`mo*t5jRz|Lo!O!Rn(o6&}QAeUHu~r;2FfKN`Qc}IAlSzqa5?HM+14U z&Vh!HV)~zdB0M(Bn^jnGY@O~vBZzQFiq z>}5x`ty2SB>}O^@q)`(DMg*cdQTm?Y0t(A2wG*)2?hRrRAIj{M*4l1&Zjbk}O(dl4 zwE_w8g$2e?sQg#5GcLDxe6RiV9>Zo@M{sG7VJoc?06T{hrxVmLtlI0gP93WS#BNp{ z#Il(5tC6Gy6kF$I%dpmOPd1c^Y$9J9MCABsT3pbS+bG}Sxb>R%i85gAkP!vCQDLD< zZEc?DyD0vM;3(h3IS6~CN0C`FN&aj@F3XeftY>tsSEqc6f=99EaC2e8^$AIZ{wcnB zBgBoW&9$z&>AtZ7sy&XXRbv*E(Id;_2O9kc#&76%U1ZbeFYu^1DYZT#z)p$fLjzdQ z|7_gImhhsQ*B6CktPed(-C)vPs>fbjwKHHFzCj6h8xbz-X-< zssgRJPBLt@-Ti~js=^b!_Dh<8mAXXGB>z!z^4e=#=?N1(e(20w&ijyBl+?7?m90pq zqOHFJSU2P4E;4Hw36Qt+F@FzOOyse$Kl0-k@I2GHHw^L#0{cX)R_?8=;fg#~7vO9h z1%*exY9gM(Dtw;zH=14zPpZzk!okIyUod&lWR`uf+GBE!^a7y*<1v03TJHcmpeUl1^J+s4CWOtE9YkARwKPl0xzIJC>K=E?H1Q+^(tIgdYp_G15*DEw$JK?F z!-vimV%T`4-5DF=jYHm})xx@#F|*hl%DJyjpgTx%z2kNftc(_I$F!5t38~E&GvZQn zWV7Fem>Q6lRpcL(Y#5($QPbdEU4>f%W+x6~+nG@k9lfEw|CNhE*F0!Ej23b6c?Xh$ z+6x#_NmGwPtH=1-CzF03nui3+(~Z20EpG;I5QZ zD&L#7y7)G%5=0hTbOD=m=n1LYL7mo&{sH~}&bO26zfl|gdI}yB{vWJT3~m2QAn;$E zOJg^?Uoz>h24Y!b)8>#3(fdUeej<>#wy-I2M5@*wU^DF+5OXaXL+WKp5}PbZiZjSL zP>l5c&v)Ke%vXK&fp7uUcsOS_93@DLlnTnFLq$ZvKx0E05&2NqC1*WSp|Y}3QPBg> zF+KfZZ~tsOF@MeBq54E5m7Q%J(pIRKdipO5oJ${(^YxVTZ3TJ5g|FQ zT63clWe#m&0tr@!iF`S-0v8`yYXUG-WkmsE%J;3y=c>X3;ZIOv;k?+BNJXo@wB3C8qGl{j>rBtl3)9Ea?oQSQGm=zz1Dx@aMeVGOd(Mj8` z@gswRe?&Vl(YAvSJN;)c(l*N$tr6q*I%!*m;F|*F@X(E+;<|@^*8k8lk zGh@8xAqe%==79l6 zG?oNd#fTbJO6cZ}Y<&sfa|hQkEZ4^SSHDX3;xj8&kQOAb7#S4qV;!>ZDFH}ScM#Tc z)7y8*N`+%y3z4k+XP-)a`Y$21-c)=K!px+Hzci<{9JE{Vnq`46l6~I!uFW8FZ|3_=yv+*X`VNHx`f?7A4b zr@K2~!cy3JaMX4X-M_ar`85qd!ltQo+SckF-LzLR!egaJ$S`#*+0N|$TMQ{#!8Pd~*+x6Jp_fVgd|_F_z8d67SN zPbUxXSEYlxD1F0%em029HFx`zZJIoDJ<;fGj(~6V$g_j|ybfnG0ixG5CFpORM2mC- zpp?{P$`@Vqj;D0mc-3xV)8r>2Ek~RRHkjgQZyP9_D3K<7Ij|C@zq`WL_5%Bwp%dgSNo=>I*^7r)o<71NDT@I~j*7u@K z6D;WOrVQG`TeCJ@2Yq0*fvTx=Q!=J+S$7Bn&C+jFJ{kx`{7Mv9a4ty>x))ysmT3un zZXyECvB|<^)deHnt)PqUT_SAdVqwH=c@mtXde#wh--l%6+7Vc^Q<(0^dsCkwCXiJ>9P~MW@Lp>P;LGf@N8zx)HHqfhs;8| z+Pza{QXw-erWREqz~ab?{q{!iRzk8`zlmX}9hS2|qd(iUM119y7ec9Y5OvO!HG0sX*=P}uH?OEX)2J&mCFz1G$sbM4RlgZqs7%Tpw5mT@ygyr z7I^oB9Y?YzII(Urgyhi=^pY4}d6~WJla8JIEiiOUm{1uV?_Ca3S%%7BGVCvsw{LrTw%oH>FGO5!3}bfa3MCSLEv*J+2S8-G7wE z!BugF!8d@zYbp^6RUj7e^%55r9GXT9w+9_?8&KIC1&P-Lqot(=&SJvKmQsA$U;6HO zMj+Rm#Kja5i(!5{=TI^Y=AfcEZ5u=fo(swxEvnFDE+1=QTo6Eu9^2rdwc&zW8n(!I zPNUyCADM`D$Hqrz1K~N%LTGD$Kh zrbVUKCq^;-*f+ivuPR#F+6Dsk&RXKzWCw`%Gx^hA6?HysPN>Gv+XCzfs@A0JExKa8 z;;bT3r2n=wK(nWsDg?Asr!N!B_PwLe3tw5r<|i0^L7sP8*Pjl-G=CXHt_=U@LeBr(r~UWZvMtRE zrwulwpXnYTa9@VAyj?T0GEI}}6|RM87K>uZjSdA?mOnyBR^3toO)5D*FB_l&NQLIo zl3rcYZ^A-=5krPAE$}@1jib_%QhPKLJfsHMv?M|lUS!8smd1DUAW)fvTx#G<4bdQ5T&9IZ>6v6Ti-Xdx`hNwF@34?JZMoCF%* z&^hsH=;S50MrkQ|1Pa(d~qG>-c^93+djn%@t z^zJeLZrDY?U7j^1@Epdxmj)t;)-_Nu=|w~!K5 zmmr==ztSCCV2RrzhSyI{loNh63*<{%Te+;LMIC5=KRm41^|X;N3sJfpi-ALvAC&bD zE0+avu^T#Nq$q&eIB6c0zMurQE-d9jir{Z)t3<>nuOWjH1rsSvl z&8>I@U+PUyuR7&?+PF^3uEu?H*zirK$2=S#1mG%=BL$5T#Rb*L?^8VCowL zi>zxoGYFbfXBN1vD2CjIR(9U4&*8q8yD+p?eo*y=fVL9}=cEp+*GHkg$Gnc-0yWVZ zOOV@&Y;LEyS*eat$TJMwSaeIY0f5B}i+E>bERJ0)W7~n;ycR3v6s9 zsPzPDu4No$Q+c7o0SW_TL9U?P-|Ho{bjwr-KW*p0SoVta`zL8~M6ouK@NCQuv5A{v zs9Iy1&GiE!O6Q4!7aIZhvp^y1`qL!cs}&@%#H^ybfGQl}vc*8mMD8#E=L8KD(QsF# z9~0>FYx}%hU)b&N3<+~@^lJ=U6-KbZuCR{}AVI~y&`e$K=j=p1I8CuLh%P#ZExU!f-w zodsfqMxbQypLh@P*&`>Z^Dh>M9N{NERFlQCOJKH6^=h@!89nl-U3@QIog%ZJMTHedUswj7#Sd6x(~t1et6DY>H4(nP3D(qkvJM$WTnXu(CQwXbhluttSpRxL^$&bh%2q9= zU+|m-x1`l(0rAsWLP3F86Flt_V43ZHz##&@G_SE#G6EhMZnz@NK?kO*`>>Zlw0oOH zm$(`=ALdj3vWWnnN*%6Un?g?XRx)EN*)ZP9q8Cm1LO#=jJ6|cqBysISKCFQx#0p!Nipnx9O%H+`J@f#%hc|gS12nUQMG}o6UOvfk`OegVyT_% z=>l+ifJHS>Bc@phmKTQ&f?(cfb8?73vW9w^Yu$ns+~`TuBsnYwJN_X+vOGiCM~+xmQCGMFJaP3SxBGJ-sw_jO-g?L|R+;H{GF zjlhMytDEkmvnqE1ArcgCN`49~(Tnqi8gBQ&dg~NXwO60o&+uXy59>)LJB)W2W9B;e zv}(t_bxX7D;8Q2%491EL;)wJRK_avVvP;gz;$T%>c_2AIKRdX_h7K(21wPx3mGGlV zY;|AX=tL=1w-!SczZ@^PrY&DfAe}J~z181lYOAQJ_tw9dsWs||tCtGh*WV{MIVv3#d0mK}ttP6Nax6x7m*Dv^dO^ke&9 z6Q#cMOCzQg>>|YuHv1s?_GU{i!$)*`z&TK9ka|N#MjD3Qebw~nL^%MC`b&__bls|H z9jt)^Pp#1mlk}^jKYIlSi=3;xtR%U|$2bsDpng$x@imcbbw2C+!XOrDM8jY>Hy?vs zNTe9P%}IW!b_3-glgXH1j0c8F^X>IuZ1wfAyebXstWMa&?(ra!kxu}@l*B6s5WR40 zKxxU1!hO-{st)Iodm0NicdG^CO#yO1`s;MO*bk@gXU-g_VSrBKzxSqX7d{E=t*c)9 z+qs+|ezx})@M)bf{oZCbG3gl`!1#HjBLws2e|XdTEwI#3-EDyB?GaxXZ|*wz_+-2Q zTy|wM-;EjO!~t8lFS>SChVV!KZNeY3MF;y@1f98snL#*!;Q@Z!P(_1`r5u*$)YlYW znnxgBflC*dG`W;%Uy$G=$Dji?DAqE_L5C92Ofi9#A&|V-kBNmfo2{Q6LVt#* zFrol0bkDg4#{u6*#fBtNxL0m_%_S-T=KvyEqcrKX$k|Pi=%>h|a zK9{wk>WtvhENx!5eoY}4b?ww3?ABU!5iU@AfWp{%ZrM%z1x1nSwsUVTcMyk6)B_~6 zcE>l_f?S#iP5wuP?*42LxWwr?I7`~Zdoip^i?7A?{AFH&mDvJm&zNeJ3E`f5)A1r+ zJa$9*kI#Avt1+Knf^VS|# zdQ$mMufln3hvtb3(jFq=j06gOO6|sIA?PyP=@hP(oAP3+*-s=-r8VttB%ZQ*@;Rl& zrJv!3MzA@LJwMVSt8Dz1C z2I?sYvO>Sb|MRI*f!b#-da!1A9w57hE9UNuj6fe6P6|`OhUHL5hnsu-7~kJqaGO(i zqg#bZL!Lo{-d<{)lc>wZw>8LUwjOCJ*Dw0R_P2UD}B*m75dAh%S<&5<`~$O zkq^$P82Y6pKUc)NwkItYo3ZKRGsf_4i#7aUoR5iF8m1W~8Dafk%T?}U@=#B@p{V)p zuJ8E=Fdpq<%jjm!6q;N$ZrvC)^_?h!SxH2DtygPYLM&F;ZB^?*nI#Gfx89A3JOnjw zu!-d7^)+FP@@u7yNO7gA@Cnlsp?-pg!>?9BVwciJ_?Uc<2#3)Ov*Z)p@DW-Szbda* zfM-Es@L{>+myJ?)C`_Z%+K)?eqL3l6uEe)QmK#=}uXtm&0{&tBfQ@jx_#ydr!LG%b zdCN4Veypqb#$bqOs(}Stx>~z&@tC%QKS{~NS`-WW393WBd`SLH zC?>`G(j8|yio}lqHnmN*qTc%A%2$zk3BDJw_519KBwv1b8x?Vrg{qYs3c$S4YDS_i z1Oeu(kYpj>td*{JO3R=yJXjV}`P(q+!whlXF-(4Ikbe_@Zf+J^W!zWQc&qro6EVqo z7G>Qbq~^2P2mXF5iFwz6>$~Wz>C6jgAZgp>QfS zW?zlOQTQ8Qnkws*0khVmhl87$OyT&3cRjBU$85j-U4*S|5Powgj`%iY?j-oamAaAf z{|6KSHuQ8ZB{LE}wUp$c_aF;VpIl#BJTgL^THZZ;>HxEFcVJsJB9FI{&&9|8b@=B0 z4wjFSla`gu^K*E3`8gCJ=NY_tvb6*b*mkf$1IP*D@3Vyoa&16S%Mk$8QfUpG0f4>8rxE;CPIT0Gn;uG3|yiVa2w@4V(k7|319vqIgbSt6smaO zR5Bi`@?t;9!?U;kNA3W`(skT->-v6J*PJS4F1wgf%+U)S&^PdZJ*0_>PART+w=U&6sZFg{_S8b=`E6u?Kn+&Auga9uazJN=Em zQGl?RO?w-AkG^=`4C%OW!=0me^b%_H53n=C#R$+8*rNg3rHIGzzaNbtD|8XrhM$%9 z(q9JJU$QD0RElu1C3I}=bt;k3z)gbO^D3Tmfp zkI?oc746XD7Fw0OaC~8IqFiLNfYt&BZB;-Ocd)gIsM5v~{vmgGM5-kyt~cck)?u$|fwP2SW&fD(A&>q>iJZ3*O-_@0Qd@t4iT4gM6>uF~Q2DJO4SRrQ zaTgT^MHi)DDkODW{}jomDZ73-Y`1n5~P!xT#z85_tUK zYsiiD3F(I5&Jd`S&+rnpH|>8(yt^v3y0D6YU0-6=3y1>75zQQT@Y#*e|LExH{V1?# zaPtOz@ZW%}Tf)W$Zs-622>}2A6#uty`hOLyzrl0Gd*yh*;rR1~!XZ;cA!F`oePesv zIkzl(d|<2ipkgE!UtSj>tq?oJDoO!jqQ#x&Hn#IARYYZ1OP_4}Hx>X=Dv@SpT6fun zT{>E1L*9rx83CqivMxx$)e~6~YfqXvA zeW`@{G69FIdtMn&;0X^VEy65$-dlse8}bpRa-s2e>0lq zPk6@r7edAT6`}hk>g?ZSJ~h^Oa>*XY*oOv70UxHQ_UEJIa0q>VqS6p@=fbXlOkkKC za&p9s1RzWDt}ig3Ttfa0t~*qZ!|Urv!~Z=r_ND8Zdz-N6HryTAEE(WX3)WMb0pUQ*ifax%dr+KWBq4sRwBo#~+F zps04!%y&88yF3riFt$nNgW`nfq@Xft<3gGgiF*P8o}Ofhq5yng`SXo67ksy4Fh1~I z8Hl@(BEm0!D`2tHEy5?%HYtdnTxmoPza}1DO6YgG0X@Hjb8y}c)g|&iZNKDu_b$kP zKA(AWahd@QKLYcDS6EK;b-((;p4Q~^Vn+DhhRJu{3)zcTx}eSW%F&>P;zJXuOE+Uw zY7k0CG!g-|yGz;x>6cc*-9hS+? zfDz#jG8}g5Ep8WuCnR5NG=ZVnb{(=Oq0eXpe5FZg@<3`pIyL=kk)$dZ0DMb?L>5LxMZnso&KDq4;c+=HB=8S`CCqulc-RBJWXp z&n-NI2XfiwzEXMxGE;n&tpRq2(8V2(FPv-oCCC`omN_e0dJn0ds)1R0k$jb)^n0Fo z|H~%A5*Ib5EPO3=##;JOu*bk*1E@Z|4?Z%2L&)DBg0<>p(fwTljVT|C$YjKMnh%kDt>8kf6BSOx@1IIg;D5h=lJ;9E19cQTCq3tHOOUC!)2yg; z`cXJp$VD+Hsn+F2FQtCo5n4!~Yn7*210M-zdYPX8!n(O?1=^kOn(38_Nzp(GKMQ+sAgmX~DGn5CCQBdz+HC=UsNzy^f- zIjK{FHGyEqG*b0I-*OMJ{Z3TI!x8wbnp0pC;u9Sb7DS4Jc3?_b|?j z2*j~(#*9yXnKFbv-IDW(krW#C7&H!IKJ!aIVVi!WyAqb!VvI0);k}fMH~-v!oZlmH zZun)0bA@!vUR0v^Zq{sz5Yy5k;d-$|R}DKbs;&$v0teimyk-IvgDd&Liby%y>Croj ziu{z=qn1{!EHF`+pVB48CgLJDySV zxUv`xx>U>&Qp&lU5t+|k zikn?hiM#f`#YFv0|G0`D3_U};|L~;st6`**?3Qx0#`mzVEvQUxH<#r*3W?Xwe- z^Awep9c{?zqe^#RBD`^Qh4f>FkQk-*)6jPT*xNz^iQD_-wAp0rtOZ@L`=7(D+eVqP zcS5~X8we0%uxwb$j22n$oiE_h>)Co?{%6*+UvIQG03w{zkUwxaH-#OuVhq&=VD ze1dgF*~4kO!^T=IWKdT&U@r%NPTAjLmj&tUz2o@l>#1Ra-`lvjHnmneW8*Ei2fwr} z?|Rmi7ER$!@LiCJsmTSWLx(RFmYOw6zd;!7GnA9X#DX`WL3h2F`?z3=iG7mg>RgmJ zsVs6`Mn=&x7};E(&h5GJaSoEU-+<{xHOxs(i#5%QVcWAgI+ufeY&-SW3TzIf$BON67670Gt z4d>`I6()AMF$e5p9Sf#kMZRETr*;Sh{AI_uoWQ~s9Cu?1S&gqD@nAOX`En$f|nd=|%%8t|?Xfq@uOIp^UXQhU%SPXD^9zT(CyB zGuazQ(o%Y+f*gWIFt8hIx_&i&Xeo z8YZ=Ide^r(ED~48v&%w+4mk$545Mv?8u~{{f@cy)3MxxZ1Izk+lmFqBXm!uU48@83 z3ZNePc*cfQKnfXg4e~B;$0oy|9alT&Xl`71Y7kKiFVCi^i|<#B-p|9z&-atB?-%}M zZPe$RDC3%Mr|ln0r_T6|gX_&~Gx*W(0dFD=PQtKF)CQl>PC)LRO9G~bHWVQ4nfCB? zA{F!x+!L3qJ9+P`GQ$!)41Z{7|CkXQA|@pcW7DQn$<|7+$0DkgyMeWcuMs9(Yb4S# zWM5(jJ@N)*XVm;wMN>$#j~IO4kCU$fyRoq>?B3jE)`%zJWc{ujg-)cEN2p^$|HcJ$adt|$DA=KZ``f0U5WTihG*i>9@L?s z^<0e>Io^b{ZOAi?%}2fs3RWoUbbx%9^by1o#dkY~hO3sOcWl<~GE%>@o~5uJw**GC zsQMwCD%UXG(Gfvj1_U6LvTYpp6CHBvz6BEw)eYpcR;0wU^IYu3<|1G#JvX8H(s5Un zX=*ge=D9-_)aC*<$as!%26WY8r%n;7i9%zz_;(iTHmX zP6nt1Yv#}^20cn%2EKASdn{K<;L+3v7Yt;9<-^QnKZ^9h1CIgFA4{%D0vQIQggh{w zH^9h-(IAPfx+@VU@>+7wR!1Bzgf7#RWiWSBb6%_>cmITAU;)bJG9`Us?c&B=aV(>Qh=Nd$x7@#({2jfa&gyXqI7u_d!vd0<7J-A)lVW4J&J*F)Y#4)dD?7E3 z9fD23&awsx=lGqMWY{J8LM_r;tNh-?*}H>2AnN3%UtRO`3fv{KQY`j zWBLGQcpN47#li!P!QHh4MJGI3$g9DN)^tQKG#hr^UL1N%KcBB?qm|s;Aqrl8o`>3H zN>$aLFb+OU2swk(XJ#sZ&NjMv;4&tD;EQzL_cr-+je;xKYoeOIm{;uF7$9I_;`x%< zh~DKZt2qhvS)30ixf4fvxU8$TE)fB=q7;}ZtHr9fmAOTuF?_ZUVnQCJvRa;?(SA9t zzQt=jrn(T(n)`3H{bAW2oS~QnH-I@y!9X<|FiCYEuCl8W-pM#bST%? zsZ(cVk5j&Iui1OGR>_)hVY5(t*)_A8D}`+PzRu~SU3fnlZdI_CDH6>>-E`0IY|+Y2 z!F7R5C#*!L!xQ~L_Gb;S_WZ|JT;~x#>l{DJQhc~OSa#`pYjO}UlX6Snu;+Z@a+(^+ zi`xf2qS17+b6}g7;f$^0J%pejBz5R$z;}?Z^}RH-m=alld3)1uJ6#5GcH>ySSBFRS zT)y5fMx08iItrGcF_4y7Hit6!W%`fpZ6-c1A-S`tkm7dG0)W2^D9nBNKx2n)N~`Lf zA{6W!jPmH7*)Dw!cK->}f!J>$?pJF5g66jv6t)o!Mv!Zgh1CNg6QcR z==ZXj4%W-=w^%tU-nkg6W^W*08I=n90`_K!}GHWP8;DDqjp8_MIu z?r<><1J6fZj~}}mtXNJ|>U{3><@KZ1zG|b0fq?Mv)s1SZW(4JB%o?L0P$`?!txUjE zrgn2vlyl(-Eo+^7OhfA(z2l3cNLOzI)gHx^Bbcs4Dl9ivGj_c4@N zf%8xOgce28LmK{RjmV#GCO#Q0dE$kn#*scT+u!3|E14n3X%H3L4iEAb^APhtT_kCqucDG$IEqS&80RC~rySBCi(5^cgTi|SC(O%LD-mOt6kbr8-r22e zIqz*T-ChrXnH5}dINODnakKP%QAyfK7|$_-XzJ*;L-}&djvAcV z*x0y9nK%koR%L9|L0>htFybOry5EaF?uwe+sOsz(WH&X%ZfOc@RAXKgTuw%Ah%|{W z1f6>F#-L*TI2>%HHcBFWv9x70(&85Dydf65l;JUMP7ONJ!4r<3udFGn#HU>33BM(K z2R|NG%!jkzBMGPrxDDkq?M)`Vr|c+GR8x7hW%74uA2a$2%?Xub>_ES*K678l(=m!3 zvq=1ioAg8DA5@@Jf0QMA!wsaAOQcBQ#t58)yA!F~hef;8VF7E@h@&6il+L|be^S^j z(vM_H2i};-q&*HvMfU>;p;BXxh2$kq5Jh80f(HilG!DdrT$PXX;ot9}6~#TvkH!^U zN>kw+dgXpFm{)_b=0wr?2fvcDdUN`e)*!1%LaCzu8{nr$-W+ zooj(e(F2Qnu_CSk>r_B{fZ5^=2S0KvXUPZDlc6u)+tGtzOX2mzRFT`>>cJvN7UK`Rf???W+ zZ9rC*^zz*?Fz(A*phiZ?9{fPTF8;IXTpbyzX~S@JL_5 z?w%SYdlT~G1yV-ag1eLdnl$N*oe|aeW*=gp4xDy+)DS|oOfC}&s<@y3!eE8-1Y{~+ zHUt{Id!Ol^d0e5ps^(2)BdMtLehRD0$(0eQS+oeE8g$^j=wc_sn`z4Ihl$P_~khRs)Y?4w>BN zF;hXvV~8B0B7^P>j9@?vTWSpm0HNVf`(8Vr1O9Xtwy0VXZa-1zOI=$>gln}LyUIP; zv)I@7Q7U3q{0VSn6amitO!Em>o;9P_5O3OOqP}^9IQ>2DCqh)=Mmfy?#3U;OQSc{VwyuvjFL5!m{@xg5IAep zCGh}}KxE50SAHVGN|crJj2sXa#*O|<%L6+gl`971UV}H|D5l1Ek5$87Azo#J} zV*SvK$#JXud-LfDLmn+S711G9;W2Cfk#PGUe*7bM3ww_X>^w?zSfyQf1g4VvMo1O9 z_B{ElW{o65DS01=orwaf*1br}^+oo)$UK@7<;)zUe>1w-+1?ypZ|5wZx5tkI9sl9< z%Ik~P?c?)&xR%gBP8yz-(sWHXx=ekI2XRG;fd)}PdJbkAz)sYHV!ER%pK-i#ydNM# zPfOu6X}`r6d%oS0h!d>S{$v0VJO}T}B(`Z-{A+Xz+t#SPl*){z7c@T!f@?-#8VJIJ zDcr-HHY#0gY}L+cIulP*rXx<9X!JVPjs{HfFg9S++aDKO+4;}q())X})5V2&Nj(o+ z*0G|d`jx5b$M?VH!bGyi_Yr={lf?fQ^5idz-PYR9z}V@3#aLFUD9HV$zkFtDIMTq8 zstV;2ilzPt0a+yI6AU+p^ha)Jmx^L}H8;o;et%qN7Rszmki~@za_+dfeSc=@a5aEY zPLQ#{6@hd$4};3P*aSeWFY-xxW(*jhJYprp$EgOX6rlrOhWq?Vb5)FI<~R?6*W}pQ z)y%9{og7{(1b;)S9xK8nUZSi#oQf;Bjic-H?&$OWP&wPD8x0u&E^y%$+oX)zIM5=% zrvEu5zOflCQ9P&(~Mm9r9JvUkU07{2n8QWY< zePA7>eKQuYU&?E@e!Du6gtGP@VjpE4H!%Jt2-L!|b`nivhNx<2CZifAj(6m<2GFBO zzh#q{V*v#x=p5TrJ0}tpqNGr%L8AulEvq{>+1siVEu+aP2poI^q4^JQ=Co@wM#SJk zYx~B)9S|8y!iQ-b7vqnI7m3}&#&dJ4*9cea-9^?Fo zkfafvNQdsfUlKPg{e>Pp-43v_xe47dzH54Ia$cWw-VEMCbq{d14bq&l8?@gojXx8Q5OT}UB2boyq0S7cD}xZ$*NM( z@m#p^+!Ae^;sKymw z`dTiWut*LZ9|#4Rj(ez2JTe^hv(zCPZAj{_l&8yMwMD|8=5Ld6BghiP@dxn$oitr9 zRlXG~p7R{-sBv4hkq9f|uVcE0${4++M=Yt}L|j)A*o!?*vD!gBuesVWST?~gQ~dJ> zYzk#jQF1`*oGq_$DTp}mnru4|YWz*Mxo{bd0C@j+#C(CG`L8t>#Pk2x&~lrhR)_qx zw(kFJH2;UZ*#8VIdq=IvsTH>e?&2Y569CJoPi2p%sF+UyK8Yd=zx$a5xxIWKiK*iK*QJ{S?gV?U0@++2Ulqv~aR9O`hCZvwkt3*uof)%yuEgCrzxOG= zw3I0;g`swQF*0$oH#=HEejPBe#h)hh_TQ`{($DX~lz)fvKRCeu+sys1X@rTpiPL`) zf%U8{Y^|L1>>Vxa94(wZ=uO<6O&o0vtm(|1ZLAx{D8{Dh8YIW%${G*qC6z}hq+}XY z=H(`4OVrE3(YWf+@SBV**G{)B-u9UL4aUHVxEeP;e5kS9B>vA;xe zeY|Tq+1~Yq+x{D`IK+ks)aAFI)xS;jxBdTk#s8D$)Q zXzLd|Joz>KudVSz($K8!gE7RmD1wH z*p$LiB*vqST6=hz7YFLylyXby$T$aL9$rm{g+h1gUg{SiWbDdwEAI$oV3U%!3qppu zN>vteg^>wxxVai&DUj_iNG^m0@c(9$*h!-Hj5ZV3P93AGT`l6VW0w~CBjvrQ3)jkD z)xKl*!{ycgGZX@;OoEq9@)n`-DiU^-Hvq>I-T}30(i81q!(PD#;-#n(JP+jYgt#8< zeGGoA6<5>5Psw`Ht+VPQi>gdi6$JZ-HR02tVwyoN6iT9CW2OUeQId2sF}()$^%tqo zDsi~bp*cu4I7^i@vi_wJ7er5GL^EMmZmH`?T2j#n*w|IG=?QA~a-~NjaLowlkyD)> zZ&HIc`(flgCF)Y>rnc7m^u6|b*RvA>*r$zGNNFv-Q#*o><%K_@$he1ZYfB?%L5kB# z)!r>-9*ZKd#L2b0v^${Z`i63P9j{l>99|Z*zwq%@DR`N=x5UNi!ktU4a zn3eO~5#Lg`8|=;) zkf0+4s#bR(CdR2yoTQy$R)~t|E&#)h0#dyP-?fL!Os8E$OSFCxP>l0o(G zmV`0qFCkS${KYjI62dRCxl&LeZ8|i%>7-l^6ji(XiwaEwokh!sEVqutX(LaP{2waB%jgT8_6j|} zK-0V3sS%9nE@|$_g_m&ocWnCzA$=5w%*(XA*5fg_B~|FvVo|G-eU*%^t|&_`HgS7x zOy;U2y|De6H5q3G1#vppNfkA58pcI_j@-IM^49#drjx-e+HV|1Jhtsi=U4=eAqDf(a8$TaW3f! zD8U|gk;Fcjm4YA3XG2E@=MJ(wGtc=$%_1iY1|!VQF!nePYD<3=(k{|QIM1~6hu5?B zpW;UB){X0&?tbEb!65d=f789Oz#|5dkI^}-EX>tqvK-7CO3Og6>r!tO?+ET^R2AgL zxX%K9UKb8ZKs^^H;ziYEr$jx0N2*RbLkA5)N>3zUIyNN!WSJ3~oQ(XS%^ZV%@k@zo zkc=wt?EJThb_>{_IO|t)BR~KE!1jNeNdAZL#=`!WGw7GDlVGimN}CTTr}S@B1TW^kOj> zRk*dvmfe#>%8O0qC1FxS2C)vjHqA*8VuA=bDN*VxopI&HQL|B|)GExh>CKjj_RT11G)kS6jrLMSrbU+O1*xr5 z?iJW=h;PZHX%f|#GfqgRN%NZh>xkM~{$i-BG!~FnVOHmZHg&XCHO&SwN z^~X&lf^#Iu>Kl;+*)HG?~@ud4+E_Bh<3O|5XnC}`B%Fw zt(ZKQjv?NgO0kNeMZAR|DUHKY36w0X92oZ5JR+^Ckj5l6`X~`d>5&1ET?T|Mh&)V< z$2>A_ZFiI_jVUQi>zjKCeUb857i|o_d$b^Po>4G6BZ>Qu%}Jo8Xg?|ql0K>$e~KEK z<2+tgRAW?|s8R(;QnfM5(^G5Phrm>w`YxCc4g#aN6<5!A9NmW`X3luBczJ%3?EdDTNyg>9NUS%fOWtj@uk>$<= zQq;G@euI@qu>mz3lz^k4I%cs-&_kXy?Vh29#{x7VqHWN|hHuGdgDjS#h4pb>DMmyo z=W?s*4WDKsDA>xo91GeS8Ax9P!1pshAk}4wRJn@)Ct+(;#3S?~=^|lr?HfM8KRn1q zceK|_goWhAyaH(Z*&`C~x3!Am#>a3Oj*xowSWgsZbs6=nYn+2+0)hn=H~sJu zo(^KG;Wb=1OU$}@3Jt9OSMOTIwh~uCQub@OSiIC6uSCyH3_Ja=$Wl2#3MX|hTYCb# zOa<=qI?dcBP(xR=DqZ^;5-}6UH=mzn@s$FBQp*-Mh4;d+`AD=IA57`<)`uK#wL!0+ zU=BcClbiR~?^+={Ky6m?utsmy=3anM9RIo4zKp_l8^t|8`O1(uZDCJLLWJ$+Cq##3RP(KIYl{WOE1??q+z81gc zjS3ETao7QMNi@3*EuuRL9vOvx=)332TrW~vJ0>lRSEq2=vldn|l(J~y^n>52L?Q-^ zJXiFtr-=^vP~=cpaVT-AX_*itsBz0d9WBr&CJ!c|ulZB>F+n1jr>X7f&A77UC4{(M z9t!<*%zn-`eokk&-qcJf$xWma9xHh0v2n>ERapKAyHxSyWCj{F zyx>u9(g8nQj@XX4TvxH;l?JSh_AyIe^tJeO_uB^iT}Z)0NM#4~G@mz@rxVi8U(atx zyq-_zyZ1-h<-n4@uS538HfY`M;NdsX&*#U_skyPcp5v3d54VTcsgedcdTvmLz1OL= zGtR;2?MeJq{@SJ^Ro>3JZdJ>yh*EK$Gc0coz8-g9XP}T=Y7j!PeN_@0;qUw?IcUpc zb8MLjr^gt5>)wZjWHM^YQDm%NMAZCiJ+J12fT0IPqDmeTd(t^apx`40f;5C+^ZB!O4#5JYkB(Kh;%UeI=EVv} zIkukKV_H~HVt#OninfN_w?Q%rLmc+T2fzpT#WT164}0$zq+7IY38rn^wtdpJZRbhb zHcr~MZQFU$wrxAJ^H$gESNB#`zxOJl|8;yZ|9*4rh_&_}YtAvoyt9Xw#;HkfM>b?e z6UGJ}8d@2BC(iJerU-FERmm-=$INIjHsMN`N0`09)g)~gu{ra?iK=SG0w|MWUl9W< zDxNS)NJ~Bb~4t@w#gJHEY)IU`x*gY*I0RF#S8(dtQohg z8wwl@9Wop0fTUgma$F&U(ark0dpdYHQSowdy5(B=li~}XhE-09mWcq@zVNU7f8ljBAE_E1)BBHdRU}Pv;LTi`Ic{ZL?e7o zAEQydw5F<|QjiwZ=ld1o+viq#DmX(K8CAz!;o-SM0;L-)`nw)}4lY@C418j6Kw*oT z(WD`eaAeCR5mvPcp(jG0aj=AQ8Ze|20RzVel`z)^*}{B#&J>{e3Hi?LSp}A`oJ&Od27g2H33Hozf{fiE3%rjT3)TN=I>QknfD1HQvp^Cq{c4z^1 z`7X-UYV@YvW{w17&{DFZ)sSw@?w7v$`s3y&hS&)(r<^c`^w{Q#(dw1gsH%TQR$JhY z*8^uRKkDRLoF)DVZ zR_)|ytIQPgRZ=}LW2qwCYxR9OSt2|M*?SvJ=qAo%vpK<|QidUrn`&%&dPKxdL8!m; z(*hZA-LrtHyV_@{exLKGOTTS?f{DEG$R4KC1)*`z8b1e<~8QT(L$ifblOJ z_wM3}`s}zA;vPRaED+Q+WH~ouySPMb=x#j@fHyzPPxX^do#9EoO{j}=?+)1x zHu%EF&C6PwJ6O|A0EUV#7)(W)jB9xG&PnlkrO_KfySR-45y$kqdR}KMoTLdUhss-t z8U?m-p6#3vG$x3B^yHAI6sQQdEapXGE^Dq_BN6_>7fWkx(i~t+Te)UBV`t=6rw+(C zME6q{VP{Ppq}o_D?ZFsxJKj?RZ!~Di5B-p)sbrYgm>Em%NVcvab&38PgndO#TyF|u z>lPkIxec~S5!3>GL2~Ngew=v$dcn8~5-Tc!>=!z{9@jFf;zZ@}rmxl-6S_%fI!Lx8 z-aEy?sElb4*R5_+UzZXX@+f_NAe;6I;FlKpME-nwC#hLZR@avl{@DxuKjy#hJ0iQXsEiU_`$Q%nZBc z?28@NG%>WZY5U2tst12HrL5b2ZW;KxIH-Tzp>FO`C%#UV@^Cn`yu^PHy9vvi5q!## z|K+d9y27ZIX}tPbOGVFqw9EREkq!SS63>fXVAYGYyl@n-)vLd&|J6jG=|vvV?T0A7BVU?Dhla7Iok?tRNI$8@GQ(Jl~3nLR7 z$DfLzw1kj|tdfY1f>cZf148$^T9m_Y(#EdmBM`GXNd~5=VooH6KGZ%$wm(nXt|7*H zNwG6L-OH_w^FzTq9}sl>w&b23!S~8Bv(1oB2~9iP=5HQc>md1|9$vu}SwKJ^luI^F z`hSGhPix)Wzn`H}rwMBD;cB9{DS~6Cf)!qPoJHb$4qIS!iUni`1-8bss!81}&dGjcq18GqWfT*Qti!C~Q(YdYC>^?1qb z0i7RN2upP6YsQdkp?t59DTXd_^TB+H&SLR3M@|uD%rji3?TL#Lyb*8&g#Q2@y*d|4A z$o)lwmMDleX?}SH2bY$Mc545@CR!d~Xr*kxk_=hAaD?j-nzpZTaTK=5mMjoVmgVRP8vNJs)cY*)!75{&1%yaSI$!I^5jT12e07(9+ z67fH8Oc@bn0bv1UfhG;DAJY#spW_<-A4^P|5fWOBa_dE>B}?wjzwP1Jr0;X590=~h zL|~9$0Hlt^d_L2Flw<884+ExTal)DBt~MNhZS~#zX`jEL*C14))cG*nuHE5?O?y>~ zR3x!%-90?;aKL}rY9DegKZb2O5F1Y-vC(&P)s;)L%I$>R5*et> z@|rz2O)B?E;_=;~M7gT$+FlAx1>?}lUT>CBC@$eZYc)g)V`ym*{;?H)o1RkulPpQu z>QdDsZ2mfqaV2I`O^Q~qHnp}8HAscZP)Q(Abyr^^h0oz^8Fnq$qYCYJi2SNvQ9A&v z0~ZWF_gcoJSB$0lq=QExeB+BlFg*#GbBGb`DtMxWUATtR> znS2k#H!66o>0mUd>?hh_5Hc?1F*R)NWwK&E81mQ5+(dEcK!(GmeEDSy6)ge;Ntgps zevsd)iXrM1i&nB<{fHCv0IsWQG<%jw^X2lO=;2{HRB*}CD@U2qkPEY-EXxtra%=aV zn{iefnqj)|GelRC!4qV0SWtGm+9@*}Q$x5i@`Gz|(Tn+lO2CYPZp~!Grg|EK*}YAk94tzpm&=pZ<)0I|`{JFT6)6B|2hT)LUS#Z|oQKGLI@?x=m~W zuux$1xA0={?n!y>0b9XImW%u@*A70l{I~En;=(|CF@|N?o|xhSsH*f(w@TXw@ICBT zq4@(Ccv_gi%l&XMT%P4kJrTIdYW2YiS8)%6Qbwqv1Gfs7Sxqcy^?VzJZim7Q>~kUI z#{`7`$i=JY{1v|GBu9UMVNG2jR`~nEXF+85(v9>XKR;ZD$M159G8LF%ryYuQ|2WD= zb6o=vICSAi;4B@{0Ua0Xry${>3_A zUkUFliWUjehmG-z)}qO@Km;PnNRh!p-Nw0!3=BVxG~^J1_5p=DCE_emJ{7d;mn|WXJU2@ym<4a zxZcuCw)Zaiw2O45jSP=gnK7DMHs?^m(qoVacbjNO!{gWf`R%ILF?j0k5^JWBA-6Oc z5(q;?pVX`5ieuIV zT3eip*iVnlf_tBF`29n6)rQsnXA9S#gch`T0-5IqtATXBl*Mq}!mer)LRv)-fpHJ2ZCjjaI{# zAbjooUUlsXibysX88QK~kQNbQXv6V)L0S=>k(F_qY+p4-czp79t@X(C@|x=PL}RkE zlnvV=w>@1xa&}=Yt<84`u0xA&Izr0ENSU%D@?N$ch%=oKxmAsCs7d#$s(*Ds@BzMT zwa>}(m~>!{ELlqPYNDoiDctjGEg)p|a8W;ng%&<0Xp`tYLN5_~t7HXeNIgvCUn54G9hsMy=#N3QPDEIcev9VWtvBLrg& zVsh7xZBLImN`_dhzP;yVai_iK*aF`b{4JX>=wY)tohwz2coLYk3=$sn*Km+Jsb5xA z0Vu6AMhk%Lp<=XoAQ{0{t|n>|4BuRgC23Oh5oUB=R!DPWA^BwU7A>pFyspf1S_g+~ zG#6IG$Q=9d5>h^;s7(o@WxO4`Mz;mT#K?k5>$kMJ$vyb~R)Pq&7~h;i^zt-S42An$ z^Pk zXMLqW!lwAf{*&PCFH7}Gz<@0ev@rYkkVFNS>-;RcKD4^XP?wji(rglT1* zTf~@1^|zAo$Dg`{J;X$xKk@}e#)L9SGAp1;4Os%hEBYR&_v_z#H#Je%1Y07ZblH_B zv)#S#YDrA6EZf*k`jTOwDfKS2I7vwxdsU;gnak!up%U59Qs93$g-fv-$ta*R%%eOl z$hCqPPwJTm?yDIWq-9o{s7@Zs(5t?0JGG=sG*Nr)c>RhklnY-r%XA@4F7<~?74D>4 z0UZZ3i-WC?MhC1~q^XlKj(%~5gcBp`GK`bPmh+p<&`UoDxxn#OFB9N6!4~yX0MWN# z^4z+Y=jcD9>!pw$4{j)CC2`x z^Gf%N3!s5-^WI)}$sLEX4YfF8gnbC=mr6CW3G)fju=eu&Yaza$#cN7fLw0>tI>PSg z22OjJ^4dwJpd>yUc>@2>OT$<-DpUNy;&DG+-2VuRo7gxx{7lre{b>;Yx52eZDt*!G z1PERCsPDj&d0m8$s!iooO`b7S!WR+&f__XK8rRFNQT`msw`AzdPoB1>4AcA86%0-S z78@*2UdhEnJ<`8PZx7F*1y8hVpTKO02KSRL!K(ZQ4AM09b@NGXuR6&HQ&T7;s>J}h z$Ra>^!&j@7+N;FD!bZ-;k^iW6pxO++(n?dgL0b|9xQE9NK3YO8-{aJx(dAZY6_dx7 zBn^2}H?*L7gt&)eh+k0l6F47E%3^wX4CJZ^T(MMUP5cVo-ez(bknF)c}PTrc581yL9buo%rx%UYiB1A z%)`&4i`ge!b3-&d5?ps_9W7kXA>iBOlS)R*!9bf@T%u+q+dyW-84ke<0i&A+Tvy6A z3hcHEXnl7*KT*Vr*XB0ot$HAxAHGB8CP_v<_{ z)T2q9ScpfN{L(^9E+SMh_$>r!7iJ(NiU(LRNwK_|`DEq({fohxjA1#nfEE8a1`ex9 zZD-Xy>zHkWFl@TdM?;_5F^^A$nyUa%;i5=X|9TuU@x2H+quaZAQc2(Ok;;#pyyxrf z_VoDr+Lt%=hm*TO2^-h;&7SwPOo4d;`jIN~Ht?b3rpw3p0lcTc5r#tZdV2cy%7b6+ z?b^+v7lila06!st2uk!MOU@a0kwhrfnUJ!`z~RDDKbp_dZ=Q!drDWNMvrQK8hr<1; z1lJokyPfVv5YIlP^gx2P_WPgrCU?#35Y^9dq5SXh?+lFp8IC*I+UZ%DxR_Y|W1=!> zp8ym90R%{IM@}L`nY_IAr!R9SzQ-Hq3mu~*w{}D-5~zS@Ju%M1ztB@g@KgH1ev$#ZN7~49~8#p`J{tT+N{xN8#Wup7X@89pL60%Cl0@BhViqxYtJ3#-^ z<*YcA$f^MVroE9JyctD@dG~%VybmTfQHaS2p-~Hhg8zE%>&4 zI@@LM)4pBrb_F-rPx}Mbd9vRh126hI6XB+-@DKE@yW~ z5%}ekUn0 zJ@>IQIIOu8coT4iW_xqJu^oZE#9fB=ld?HtFrD&=YsXsb6@#zy&SN{0h5A%Aq`%0r z9Rw{CgNcMor*`?d{i&R(wbpFFwl4T0c;z;hEsdakA6_K59)<1FOahOOqTa2D{Te!S zb9%CFt@X3(cD$=70=362_0q;_((+gA#Ow{VabHl^z1~8|mw7^ps1jQ603qJA6Li-C zE6#7JDZWFV!i`o_6N`O5i72>oA~_iz_s#N1t8LJtKQ{0V3?Q!MvwASxiBbKOXn^;Y zI#^BjSF@_cRE7`vr125&RkSUXkQ&on!BI)Yqx^kV1NE^GM7FGRhAK;3cD#M?ALqGG z2RKN&n8xzK!G9{G{b{}R8A}#2tmRRrJQYp`D!{_t9SnF(R=XxS;I51bQ-eDI|oSR7s*Hsbe#eDiivfuOT-$9 z%*nF~EbOq~%BU@%3hsxmekP^T_a@@TDp0V9)y!NQ(27pfqPTCWe3#6$m;X^7pg^wy z{Kcr+ervF1eo|sd;-=^pz}Vp)XXifXx$0s(L5|K8xLbkvS>bEm$9_P zS~+=uK~*>V+IYST5Nl2BDm}3x`z$W6_b%CZU3w&Gqt#R6d{9cxa}!+Js9?!~bni z1y_jIg|av!P%<=>Ca92?q?;^C4%SKShk%rdvjNB4iK8!D)3)$!$<7l60hCmu=4L-LJ7?qpkHg6tTSl}TNUHR_e zZ?y8UX#N$e3=MdaT&ni4iP^rpL_++WIcIoQ&Xx#$@e2ybY&WCmO~fyNAvjFWSSquX zp;rs1*Cdl`c{_^ia%*)h2(Z$jkq~~+C&M$Xp1{cHg*!ploB=Pnqq%7?iqq6-vypjn zoa@1yH=n-`O8A*^*3ptO+u?jCS_6%cR@kLjO$8=G1V~=%Mmw*K_HKJ1H=SkvPoJQ0 z_5@603q3c9wt{8m3gpOw38dtaIVvE-nkMX`)HP5MNqQaToHQE%m@8|-;>48QS=Hp0 z-8P%Db~)Q+@p^t|DG=2H+c-;``DGXbv6B#CS4)gE5tfzM-T;o9K>V0_D{aj%PY8~m zIyk0@!Eec`>PT%xRamJL!Xi#ax-7_~OkouPyOr6kLGN_Nie67CH!RmIwr*B?OsS6$)G{G$=qLxVz=S@& z)i@1={LReF6 zF~gR3f`-EZc!KcrcR-sqx&Srh&y&DxHLYqYOA;eH%Sy&l1b%tgro@#v4o}n6R4>TM znq-+IV?Ny9QQcwc-k=Cfdo0U~JGsR>u|)DtI#7DExF(ViJT&j?hJ+H4(rZPhY)I2M zz!z=WWe=(`x)xcO%LXdpkvhc7@Jfs`T9yzDrcrNmWpEU>CnSot&?3%cQJOdpQ~9QX z$ITT>Co)NJp!yvmG^(kcF{><{)YmR5YdcGK4bNmn!oo_)B^Y3Vj)WC;d;XHdY9Xqj za9z1g5#9_F-*ep_P+x~Jlx5N;NDx~unFSi#h9gE4x#TWyF}AthvnMOwr#y}HIU;F2;mHDZLXn73rJZ)#I~7Np|O5e$W+w|K}aYMd0s zTNZ2!8}(dXUUX~fJrS+E;p=)7WF=BSRBtJ$JY8WTfs()&C3+2&LpBFsb3V{!dlb)J zFtrRTtrf!2ZNQ}wvzyGM3v;?dVY#0ORkpi*6Og3qN5Aya-OWl*SUd!$6L%orz8RH% zY4)H@@}-6)Y_u!XioJLm2Xn2qMD}M4>f_o!jhrIjKv7dqNujfml`kbdA zT6-H_GTode(;Nayl<6-3LN$H!AeiA7-!eQ3Y(iQcPY?@ZC#gV%1UfAR76Y@cOQe93 zFAP_4A^&R7T%?6qHf1no;mH(!mEc2vRMDW>Q3`krY{~uo&w1J(K~-(o0oW^lktDty zLL?qsZfqfHs+Za@^Wb3-oQtFgP$K(^oRg1Vg>ny+*_;!yh%xNzDsLx&X*5)BpId8q zH^qzLz)ctW>@P**3Vcvq;;+51Ji~^3e&NWA`aVK&6wg>=#^hRNX)I1RyO~j4{34QQ{pr&P{RbqwX)KfBy^0U5im>xkLs4$o^qq zv;Wso?!OhjyR5iVQ)&*= zMzt!SyL68idBSxEz7N`iM<%bNJJE{LS#K27X(L3BR3o%oH4+q66c_&Jvb}23V0DGK zMmAH?#7ce@E;}e5OQ~C!SX)oY%B74~uW3Nd%h7A)nkLc68L{cMxD;hD-_o^IbRyvw z)r}QV8JqZxA5N>KWT=g)6nxV_bHCo4AExz+*!{VkUp|@qZgHWsN$+e;n4Fi$q%zX9 zh_7I3f!?g#rKr%kEB^aO0h-kR8~;!=xm|Z+Ji;<#eJQMg`Bu)5$j6`l&joCEX=UY+ z1D$S*BWZ=;p1K&|;2icj9I#ktPd7r1CC4YMbnrD_&pQEK5o0|f--INaJLS)c%OV&o z?j>Pf5yXDwtI_l|l_&2p)g;s6P$+h;`LmSQU_|8TdSAM&NvS-+--s^va&)#WL3d1I zW8$CAi|yS@De99>+Ge8Fy+>xZkRrUPY_*ifnf6al58_Yj6@6!ZP{_=dVm0~Ri54hd z9+;{Ho@u_WW(SCDPCho@Vf}iQNGe@Ix83UQ@W~*_@p(S|?dnX(W);~YR1?$S)tz{n zZ|zbxSMo@=G_+uqTu=gwOFAYoeUgyvyr1E0qY{^LC`%&|JhN8OX1*qg(Nrz z322g^^T(j!Sron?8P%dy!k`Ix=0`}VD;wr_bcv2W4lzV8;LT2j-c&vvPcl(|fPd5A zyS|UHO|%LR_E0JZk5(le#K(Zo?u=5QXm7A90!nWErNMyr6#RIRoO&UkyGFu)kc~ZmCu^X@ic9CXJYAM~rvk~L4!{iy+C+rXzbnfo z1I`21JPwtE7eVfH;WwMA=VnghXG))mrxg8PRS52YV3CASCA8v;mIE`YRvM#IOanm;Exs zWEyv;$9a3pR?dnuq3wat3It#ltF;J^P|1Za+Qn|FEoOR}REVLwDi`$RL4hKrv*gZM zCGZwNq9g-+BTd$nHf5zA4dqTpfy!mmi<3pDc&m*?8fPml0t5#{_wBLjda%rY@2Ya! zfaEvuV>BKm0plB}LwTUzhf0!1F7*>y^&RhNJL+qqtqv7+wdcD@rxNt?4fGo(q+IqP zMnP&YH84|70Rcx664OueUesmnD#S8$X+{mX6h)v)7=_r?&bnrwTGfvS+3c`esJg%) zjAGm*ssjczTie^7^e=8C0I#{15mgZofJTCf8O9&zq=Cm%X!6s zIB}6Mw2+S;-4nEtxJJP+io>>WL-pb$_vH-C3Ji>ufgi#1PnTS?P!KFM4h4|*ON6j9 zD|LkPm}7BfLxJ?HkTmWMKw;*YXrX{e2q#nPZBM-vw?FYavi6NUIos(|_iG2qusX*A z$q1b%E}LeJ(vV`DfE7mf2arf41u<;~CAbv~UX;>4cLsr0nP>}{Qhqoi_7Sl27ATFuP8pQ{QT?NhReu~z2Y~`8 z(_<^S8NR;IVT2!zGPzyTw9$u+aTN*}RqmtAoMBlhaRbH4BrjR0=eceN{5bHNSI0bS zuf-o;#&6$F-*2^YdV{P zLRvvy5>;OgWaD_d=R_nbu;=UZqQ)1$Fjf8dwzq&RNcbJ-UM{ooxBcVSa)y?_6J9cM z&*w1HUXeYM$j=n(*jvRw4)fQW(~EcXm?)w4?H)hE+ZXz&)TGx0+0ia&|KD~732%wd z_Gm;XL)lfJ@xXMA$H|ULspp4KTbA)R;k#EOwU*ht%5of zBZfP6@5MDz(sYlZ|xD z^-OEMFhuW7EVEOXa~^&nR1bbtJJjQ)815kmr_+sb$Ag3F8!eKWUrc8_)8BA&Y%lv? zPvK}cAdA~OAR`<;JtNnHuLkcYmn1#)}~pq_zAm$oH}y2d~8e>*fnF~9e@m6<#+-9K{k+-%@; ziU_m{fvX;b6#`?c*voszGAQ|VfoV|FbiIy-Ih3>mdiFAapC)nU^1NAAAM|qHT&PPt z0fZ%qiuM45=8yVG0c2hYq|Y1L*>Kphxm0VN%TQNC%ipN|_BNv+c`Dmo@g|OUwoy~I z&ajl2;aN^}42iNMjpXLxpXBT&-$feBGb4?1ES4M;5FJiOT&W3WFe9syijf=(>6aj~ zY5_1K5MR=)^mERXI>jfBWCEd~RH5$sM2OJdx6gbv93N;#o+Wtk;s)>DJT ziHv?6>;g+8hbJ@0g((;0J8@wcMmB+Ur+HW>s-uL#Du*h`;vP~=YViFrO4Z$9nCjsQ zIw95ltUNa#4yKpo5F_>OtEY}np)o-5EMt5faR6P#;u#VsT(zm;sjaF6QyGjU3ZN*L z-Y3y>gu%+~%FQ_-;3 zr_bNB-OB^x`#d|4M!1B(`f|EfX801RM<|ic0{MM%&KjcU*9$8VG_{zN>Bh)gg-0Jl z0L>9&TJwPn>(qf0+2_JP;B5QCi&qxd@&bQcXuKFCBNoYTr~bJ!%xB;C2MIlPMX!5k zTC?-pPDn5QU_JA$Mb_h2%RXGs3t%_Q#lDtn@0UV6k3zecKD*$iU8cVecuuI94?x}f zeYfZM;Pq2q+rUwUeDfa~Zo0cU)SxEyjVSA!J($=GpkB}*^ZWV=0Bm0Z9au6{c7GTH ziPG^Y$|l&Lk*<6?*dh-&VZ=#owoF$g`)6tfLrn>8lFef!PGn#wZk#RQTdZNI`gHhV zmSjnEp7?TC(NZZML-ISi?lC zn@C$hK@N#8CV3H491JLFMo@&=f|=&tf5CJZBXjE?E3AInWONn}|M4+hs_3R=p_o{D zse_n;;?JNiaKeuJ_~P$q*M4X5tbz~fhLbQK|R7%`x?)6wcglN zdX#LkBXAPqhvZm6$L)# zI%x3~77l*vxnzr?!$$LPbb)@$v7udXqPZRg^!TRs^U&kuxt^{3=ra3t4S``*L_I6Q z>W)}!N#z*5_I78N1K5zb#}p2n9oi{SG{hKTtS2W1Dc=K$P(9O~k9lP^pcGB(#C?j9 z=D~-F({#SwDOpXjgLt4_SoIJe6gvkODG{ym1(rc@x0DlnrWi}7(TAIi^fuBX$t#-J zgI!$e^2_ko+$E^lyuhuR*MQbOdc8~MFB)pzo)+wOnv#PyGuKr7d00JHaIxF6`GLQu zMFZK{Gi`JetpEjBWo&o;OM%Tm`UhO9U`!JpDT!BWC2G?SY86*p53mP11T@h}2?xYU zBl*@a`BE^;ekcQg&I~72R%q5^zmQNL=MlfV0Tg1H`El)erWXwXU-20lVmI0kY)idM z5-LFC;rwH@Bsg6`CHPEmYF9!ZKw3St+<7Tu2*NlhYH=mjKiA;s-!%s3^%tu=yV>12 z&RqQo=e1KH(`CSVa|HkHjuxq}-_K#g?ab@LMB~|Ad)?Fkll*}cv(&nCxNBf$7F#Eo z5N%<-K~cCbC^2|Q6a_4J@cc1g15?EbO2_dUlNgu+&*lD;xmKJM2wuE(uS_M($8rnE z?}C$aPao|)leHJTug+nr?cD8}<9zWM$I^={4*%GSHwePcSEe!e{=X< zl2`Tqh%Ht*3{@waOq;Nyf)?#;c1yf>+{v4d3GF_Mks-PON6|Tw?QV zrra^jn0SB5y;(YWTBt|)GvdN5(e8+rqpQm1ia@Z6q$zpFgrgtCnTx}2?>5Xck2#PU z=`%oj8F!BwG%tSE7PZg@0q)Ttn4;+Brgiu{)ilb>&Bt#mI>Ws-WYYOqxpO|~u+0+w zcNq+v7h@3f_9t(BZ4lV#rpP*SwR*sm$e7Fqk}JmjQzS=8HNRlqx%A#5uEE&K z)=(}NBP*w_2H(ek76ssKe6F2!@%EzILrDyIi6PK~U`I)u7x^?a+4G(H`II|@z2+J` zWu!~RSn)WgeUe$~s7E8ezx%sKI5PGJT)U;S%g(I>U#4^T64SacD#mCVHDW=vok4@V zT|6DQCZ1J+KF%{sqLZ3ds}t&J+JMLsLBDyZ=%b%xChr5=wP!FkON#_YFYsZgxoEIR zE~kVPT$)CqRj8zPWeUknO2{d)VgrlzU~&9=*w4`Xn^Mf9+SXy-=$LU#bQ%h5E7xq} zA7nhOM8N_DLkT>e@c%At^Ojfd%iTVLPtoA`>YS5ovs~Vys*@+=sd5>Ad4LBk+1hJT zY+1>Jmhl`K@-9P3WF=H6E%Uf{l1JS&t~Ul;SZ?F4Fx zH4(s=aqz{rIV+2GwUm}i-{n2kn5A6veNb$5^|bnUep)34DbN9W#SL1|8KCEA`w3Me z@_Bt5+IcoUlKvIQnaF;4N0(?czY8$axo82+$OaWf031&oG8KCX%*y&@Lhd`Vgr4V# zbK2n}AMFl%(Q>o~Km)iWZz*yID)0S{X-fcvlk&R?hlhL>mKG6sj&-fw}z!dVZAQ zJvCf`qs)*>Hg0f8$zX7(a~4wAEvcemo^ z=`juzj>m`I;6VUHw_>noUMo zmhV#eoGj5_mQFO0QP=YHXP}aY)a;N8Yreb-k6#2@M)=s&`AXb1lK$T9O-I#NKG93? zS3|K^?3V3wh2!eW4V;ogi|^-=--T<7S-YG13IIEJ+?Vnt&98KyK;U9b9aH|qsU0lW z_Eg1LZM5?wFK1t=6yke^wZ%It&S0%{4m!;=2p2u{>@W4be5dik#Xvk!zzhft<%vS! za7~Ht6NN9=V;;e40W?{AeB+@+_L`d_hWuV3-`kKu=@}>|O&hc@=_e zaGM0(B;={+D>3pK2)H%;9oqUsAhAd@nT^Q1oANroQ<>2Ndcs3de);2^eq`D7AWL^v z(^78f*%8<9D#NUS?|d{suD-_=f?z;N0TnmJ6BK8P*DGMCOCin;1&zVZ*q&T&s&)PK z_^xFv4*pd~C%@Y*m9|&}$G-oim+c_8Pv`za8o>bt0AT&ER}d9Ngya;3lj0?83;0oj zu8Al4`XKe$@Q?f^O=~S&r0F1pup!<}8G3x;)7*F@NW>Jjv)jANtC#y`)C^Qm1TblD zLYI+?bb7=mlpb4EWFj{&-GEQy0m#er?ipZWN5{{I=r0uJ;TRnQ8YH+go(RrqD`uIM zLvLwL7zIB*5H}32o3V9SU#Z)I<#i_P@p*!~`q=s99cc5K8&CmvYhyT(S@=ye+}s5a z(=~knx9`y=>d-ukfRjD3qDXV+oOR*Vp{m@TLNc+!0XeKgexG2)CR5Zqgy@q*C_dkU z^#Fo}5 z%@=Bx%J-n;%c_5@ghU-Nc7w>>-fRLTjVL6>hv(NaUM{Vvl(rgW#GDK9w6!z)JkNCL z{hinSvF@ZAC+c2(cW-ZipUs{S(@Hs`wnX`E$d_7HGf?$dh^_uI&yINaf$| z=^J>PBUfot|3R(_FL~Xr>C5gjI^#DT`7N)mCL*!P+lvbV>wVW@`s=FJ&q;ev59>@T z-cA*sY>)#L4_C(%(rV3f6`De^-fsm9=YSA zq}J`SIptl*>CuUqwsE7)BISh*MNWwW5-48NUP~Ik1^2|riX;sAf_a~(N3d3y6zysoR*IHt>hE-eBEtgnj)UB!6s;u9 z7=68>xa1fOT{t~>nZgXi40H1g^XviC_%!|OBh3;l1+C=dm~_1&1tpctA(Vu4iy}oS z%hKe;wA7-^--?61e`S%}yPRFZ{j3t^CwTtrtNkaIj1&#+nB3I#q|9Hb-G9yVKQdu> z|JQj84U8=9Y#shV1v79kwXiawW&B~oF#KovF#n?nAH(u{-#2So`&VdKCVxX_lm-qx9pr=Jj0D>QX$9S)AO<559pV37j>Tmq=*6 zjrwjFIhq`I;P>q<*Q`WPNb2r@b9r^?ee!{uH5!DxYs;c~Mimlv36hdOh(Sh_wYbJH zk;$M`#_$5YW`MX3H!Snh=o-Fp=MTv4KPh((uGZh-FKY-XC5QdA6r>4OX6*bTqcNz) zjB2DKe`59OL=-K2Q~K5~ql*ckF0f^A-mN*0#OOXlvW(m3rpTZD_MsDBY;Ni)cJAq0U!VW}uqX3v#Mb2~*HsuF^k$&xlmB_fAC zSV|yQi_SDO?h=N-@5oVRS#T^|^n88Hhdv@lfx^R{Ph*la5L|i}694d$7yV%As^o>< zt!Qocr{xce`M?)LNP%$*E=Y;F60v2D9mgE+FZ3@f%d@rt&yi~(BOL}e*dBRY%DVg6 z)x(#+&&G7jiaZ^zqJy!g3%o&`NlQce=Yp!wI+flp04H&=BsWWe+jIq?(aQ_maqorE z@j}h-Offu6q!pN0h8ML1DVX!aCn*YD@3DoOP&W==a8<)dw;Xl3n-?}YL2lvP9nXB%20Q}O|obBM<*0|$$Lc;MlUC&qS%-~y)Jw?`y&7NF2W$v@-PSXWV?=`)n+fa+2h_HwtKrax zZ=B8N%0a3IRi`#M?4qR5QHZXU&kO1-7Cnkwo)&~r`CTITP+E5L(j_|4nNOHp>m_5y zh#h&?{;v1i+QCJ$3cCag1KiKcUyB)fyW6>=+Wuc$zp_Kcy0n!m*&TUUxE1=*hbpS; zoBo={>^nK{bph~QIiWjPCbm8sn3c}VhD0|PbjhnK$%s7AqW(UVmQI{n9XP(H=X?2= z>0L;h?q$-PGg~lkVkX#o=xuXuS9!u4B%+lLE<%KwWE#g7iawb0aFL;sC0Isnp>EoX$N;k` zWC^8beBhk5hgf-tbV+(tS=h*V!GN2!r=@5uQi(Wn@psu^uW}(?tj7%S!KO2Tw*%vS zFpCyr$o_uctCSAcbbGgMWC6?C2eGN=K@-tDhfrqsgn{@i&0IsV)Qd_wi;5pT?@ryp z9+4NL1A&t0OxmEW@cNYuEpdIAM|Mrj|h|`t7 z%Iytw8ePXm(Uu`}6%g5PP}FY}QBw>MUw#GoBW6ydm=WDl`Sg|^->G*Runm7sGfD;< z>H?V-Gi&mrEx?9>oe{`JB7W5+VYqhnZpl8UxvA!62i`NZ`2b#PV$o_-MNRmJG*ID2 zAIa`pb3mukAVz-BlyJeq{fOT{$azA2e5a<(V$f5V>tVu$x{oZ$Z$1O1l~DB}|I%>Ow8bwR-S z`w#T_LuCdK{E7bsNBTcr^FQNIN$DxNQFpu?y>&owI!#Yz@n#aS#C z*vH{@2O(8L7Q$^dWC@|9wFE6-wWD+d5ucC0Qo8&Lja}bVWr|rPE>;_k@3O8lhoEoU zdPL|avXHKw%eYE(dF~JR5}nta_Re3=1wUk53L3$0AJ3P!dD_g`VLmg|YM$OdwddFz zjaq83w#K_W{GqRFu6;Bkht4EByEJrYuT$77n%rC+$$2YlpnoLfp019MyR<$!GfHX` zkDr@&q3|!TkT1XKs+Fs_fB8AseDzy0-;<|9wj{_9Ym#y^_DX=8XptMMh0i&m)*v<2 z89J3Cs6H+!8-?qk>!R>#S+J{jVcvQYW|i7Wsf14oRH}>#b_2aGMXhJO$fjcPn!8OU z8dbFKs?!_Ep-{{C)_Qbc2d%n}L&-y;o$V4Fa9l(!eiwgsW;#-bWZ{5VwKpN5xoCNe zt`jQpTbmG9OFV=l8HIwATqebmN2)N)z39nopzFZ=dPR^j5xRrw7HqloX*BHvm-E$mCx?>i80fgh>F?o3F_S`HmEfN zJZsz_QRQ)exe|gAOG3(y9CxC8h12MZP;%9%Z8Leo zDu681HlGRF*mr3clW3~$OYf>Djp%-Pdc3B$;08W27yuX8-vO)TdIQ)nPJ%`fI1qK# z4^j-ICZA*el`!GUwlA&StND?VrE{bk`aMGi-;Sk_jOy;?26%?pxCn;|*rj!e1Ijam zNeH^tsbT0BG7NDxzT3f~br z4#3Q>h8%oq7FzbL_vBI@?%1Y+igw8EAd6GmNYR8T;Reh&yP z7tdsUV5$}&7q>qXeJ>%hTdnqiTbamc8`CTn>6-UYp>tf_kzcXuX> zx_|f5Om)$g-ytS`cm=V3O8^+?C&7QLD59UxmYs!pho`jk^wADP)yCw_OZtk83Ew;R zRuxg|sJDT?q__Zy&cM*w`6&f7RqKgR9l+U3mktr&SY4IX1^+Mh-YH6xcFopK+qSJr z+qO|@+pJ`zZQIUD+qP}nHve2>ckjQ~>i&AK-ec^ubr7fVM#OkV#5bwL|od?vK1Tl-%upj)qO{7&Bq_4X=V`Gb6NtD-p$HD?4s|?F8kn`Ju)b@(kWU&XTfU z4Z)U!QVZrhMh5UlZ~07WS>Sr@g^Fx89We75FvK3j<4z?w_M*8=<1>M8NP=v90bgl# zGcLfV=uJDo_!9a0MPcVj&D^OoT>)x?>vO&2gdBhdmPE5UM{>i5`B(t_e(?rWh`?QD zKm_Mjcr1_6Bg=r1>E?xM8|}2M-eAUM&n%BvK2V{?F74TTw{l^xCW0KPz_zdRKX;bZ zkXcdJ7+YIw+%CR zd2k!1o_y1vcr762#8#|8!wNWr`MzIlHAHf2CUiS;1nRLGgpA@)6{?hv4%S%5q0OD( z=kCa-=Yj3}@lgoon)B!jd%uC#%XUH_G&mk2HwZs7ag-Ik*e^*siy5&V*Nr@Gx5*UD z&-1Ijk8-015d0nmeqoTW^R!xd1T=;o$;&Lrj+?!v7m__- zj98?&jY=;Gn?EF0p!y+Ly^!Y<1cr>xjB+KETTGpwdBN`_Vh-GRaQPv%$uoN5T4YeK zl{;$&N)wH@-B%LEp1@4YmGO-E+=|8*NOUI4Pt5!gwB4As~P zEVgf&_Zw;mTcxno!V1_rQ&<`R?efcyE1Dq^1}6wZFQ{%rc#i5gH3fY6hW&GyUT#A( zgGJ$#Dx`6?It#LUW}Dg(6zb+^SY50yU}}+jK;@kRgi9Fm<^U#~BDY*i&jEsta;khoy#} z4IQ7eBrhtX2v0v>(eO}g5scPIOeD?x&AvtoV2xiVj>H;?jQE?(t9xkH{MHfUL%ZTmy58w9~U!YwCG$uT4Ouk)q|*FQNA!80v=twKoCk7y?8DReBKO8Z9e>zY zg2wP^mJc}aAoI~Ijycm`4jp_%v*BM33=M2-7&v#e>x8Gby=#0PX8tVyjpnUeS?P0W z)(QB#|D;5Ud?tWeg_TSOQ`k`v)9w^NhAuS>i^v12MsNJ>;N$HKsJzp?MXRGtv+~pZ zWA5R!6X83#*zti_uk~K-+137)DtNqc``fwWa0whuXYgVd4(D;_V^?^u)IEZd4`KKG`bhZrv&rdMVh3RT! zrvOt76*2ZJM1rh6ZLp+J-O*d2s|Vx!jfc!*cD zy-C2GzT3wx@1DQu-DO9?;f(#puw<~`LIGM-8y&bZclJ)NCHdVsx*wpf7VFkb#VH6H zJrjLEf=pXo36BE70tQpDk0bXi#(o|C95AdH)ZE(>O)lpUjlg`t{@!dcX-<=aq08z( zhS%7s!R`ZWV<}oaBg@A9?U!fusk7l;Ez6qdA*@UBUuh<7UiyrEjX6Rzztd`akO8XX;Rs}W+CDVBrk?FTB+A&wcru#Q#Wmp*G`wjiRdTVS>Scyg7-Hh0R8&aPQN0Ym?r{1rp;{t6>zj1?y2 zj7c%udM6ndY$T4RF0@?2$}cQm<{pX@DQWVJJQCsW>BclL@6s3^WZF!;hP{rR@z$qi z>5>c6mDf;ugA1uME%mg73It0tVU(F-mUGsr**L&tLTXVHRPYMnb1gTA+asc!(}1)w zR#+qNyy1fptfJcNK2>UJKh>r>z{I+K*x2sG9`J4DK$~j-gt%W<{n)R%+_5bf<=rIh z+4?}Iqj4$6e9`z*PrDfqdQKPg|}`HBwI`E0(3GY1_3lK&>_IE(9vNYv1R|gM%juqI+Tp5UtMlXYOg&jThF090pP40(K(j z4-=s|Mh?uCK>j2lh^MIzhu7~WYYVRrB;_Jf-QMZ!UA)gsCY1QddD$(yUMc@~-(rmC zFYzwm8?w5J#IzCwzYH_jei)pJwI~ETe#jgery<+N7juc-v&(y}JM@1HwN#xgO8I#++N=}YB8MB1VWhFr>P(ZFDq06tm*O zsZiU+o0Tjh2=S$A*@$)mZ;-sVwBy5T=PiI#xOE9o4WsB8xs21A@}y1y7-wmwL>P$J z$}v>LGv}=1Lcbn$sjqTT9MINpZ4;;010KdwG9I2z?CnNm@Xi8)cl;S{0g)xCz-9iZPvYZq=L9;*(&r>AYnqN``pXdyY@ zqNC{IaJHJ1e@wk9KS=pf>Mi|r{049Okxwdgd4Tn-K@M^iPu}h1d{vS2m7A4jMVme~ zy0X~YZr7yc@eOh{I3*fqZ6C*KQaCHG_5L2ray8LO8aGOmNRtS`w~bs|Sl;7ZAzMze zt6Vny=(8SOVA(`4?UccuQu@`kY16t*&UFPrJ^jx5kZ=H#&aZ9Lk`=~jpXsCewmoX#FISY?81`8=*fOf^>T5)aWYE}|~Z*o@-5(Kosp4*2RA7#3DJ15|y;)ks3| zr(Uo!W_@aE3Ru6l8u|-j1{HO`a9O2eW}4K-y2+H=$J#RSQyS=YCH3p&-2BX{Io990 z7q4LME*E08qL>wyGc^+jD|4Z+D^X?o72Pj?oLhz~!eQ4En>P5l!r8BEgc*gXUwpWe zD~$FtMOO1+`A*H?FksUb$Ac(J0IE_bG7B@lqdT)>NVO|>q7BcNZLo;l=HPckl3=}(NM?Za}J{UEamfpu$LOr^BKI6eS_95FTG1a|y|vt}fX#pgPNwin#0Eq|*J-_*ptaAVi^q z8|$?Qk}8Na)8g<17MV zhp(rLA6Q+-`WnOPXYQ%P7i^|SJfwY?l~YII-BhHoZqTMs;`RA?XN6HC6^mSwm9*0W zUGm=(;!k(D>}{HNiuVw;f6ujM7$}yGy2wE4vuxE_=Zv8*ia?ImGa4Rr_UJy;KwoHH zQZtl`Hs4llAO{3=%O^f!nwITv-|)A$SfM)za6Url{HJvG`3#Hk^LN zKF;DGe2NW2{&aCPv(dYrOI>dtIS~z6jzhr@sJi$HwNxbjAtVzXB~WSIYCMA2kLq+e z%tH4boFsx`LwCg=KQIHnH|}R#S%-L?H}FSNC&v9eHnq&ocEi)XeMcIdMD5!B^(#tc z-HS%)lSUtd%0T)4%zdwQZkzA#O^5;($(a7{uFS>nj-J0jCH{$T+yAo63X>$_^Tcrm zu8P+lZUdPu%X& zt5E?wS8A-;+-L03CYy=e^Z6q7x`pN_w2+kbXik7v;2FmG$hR0g`Yv8CM zpRwhREj0}))AWrI5TGwqE|rE^H)|MvL@v3+3YQQCX%WOjt_^>$E>}1;bJo7gB|wMW z^hL`SrNlo%&+U-|SxrgpGJqhfeZ_5wTQ}X48;M$)xtKG&byvjb7<(XSUmCR$&Z*8% zla9)ya>!gQ2$9!EYv(c&_P4c@~ zq=SIu8U;U}YJ3Ov(6+e`pZ7G3L$;uE>cAMw=mDq(-K!HWT#CLJJCUh)5z_xR@!LKz zpZWNaCPtBQSl@NUDPM_eld^c4gfQuaVyTMdkwa3W7?mHDL|O^QeMF5TEI1WP54|5V z)-tfxgFOm4Jq&n!^Qpa`WE|jqA3a|5)z<8gVxay;dtRhMRv$)jiKl{&GU9I$H>)uW z;)E`vbl%3d+UM*AH75+0s`L98(PAW)YxNV;G@%U(;+1E%gVw~J-?RlJY&?~~qU~lq z{qRN8d%HYKsmms{K{=QN-`OZpo@$kNjh8Z;3^v9FrbRpt5o$FX$yH-*P8Sr|wFy}! z>XkJlps{zTj}9B5fYE@Xed53);f6#BRT#bB@}L#DlDn>Hdvfgd`wV>QRPv~Kq?9;g2Wv7Vpmb)AT<)2?Qi9Cd~tNoVmiaZ z+O7o@0noN(=7iNb<54TEtlpog zVB$#v?jUR>xa#^C+w))}P`3&I#A0PS@%>@rcWy{EnW$tunr=!hObR?GP13J+(LP^l zmLqA7L)~9J--7{|iWc-XVrAX@LP)RgYN;pZ%1oYNQ zBI(hYHLjkMnVsaioxY{fjhj%;!u8(BuXyl{A*Zlvp{{Nqk-U_-Nqn?D@VBbshD%rN zxc0b>^w`WyWSDujv2Ao0Ocb^Sw}``Z`$-xlTny-D_$9_2}156jx!g5c>f2 zGj5*41f|Xy1GL-ITfC@7Z|Gq7X$|kd=sP+Ks8V+%?Z1Cvo>zTopnhNPcgP(;===CD zJj#FKn*YwE92=+owkStNq{F6UXeFd2Xe9RkJGb&LNW$+*V=H4rCt5Z-roU@B{%@P3 z{v#>J_!oEc7w7#KANMal?*BC(cU=SHh=l_Hpr!}_!1O;`p8qbl7q~4Q*VyB>UZ|_r zSL+huj+ax%HtI<9<(7;{R;XR*Uiw#P=O$OVirD&DPAr+>1z0!PjfMHx4qg^n?g zk)pf@^N|$qCi?(g3gv_W?t(%Y(O48?9cI zhlxGz-1G{<>kgUXrZGtaTypNH`>rHU8O26F)yM#IvRkf575QdZB>)K805`Z|mSA7s zX0v`AHNk}7eYi+TTuZM$7zTaL;uCAeA7Q2F8z+j_beSaHUis!P_KcOoRfH7J#1VF| z&Jaz=@mOlCFLUq$x$D!3r^n)xcr!I@Ha}G0L<>W(5h|#M)r))y`MNE za&}x?IWT0vQTwS=a>;j@m9*vkXyoR^fuH$6ZT*+tIvjjpjmFWYcY?QB%(nEw`CFmm z`x1N-(Y(>H1z+xn|5Q9nJlp!=dnJ4g*#Q!RZ{12i_7i1Z3C*QDr+DT!bb|6jafO9{ zw<&nZJ#t}htPa1ZwFp9Wwmmbr-tE%o$Rh-fs3rFJ)NK!;gVTi5c-0^qZi05d)Px-I zM^I=T1`NUn=Rck#Qg2`2Q4p|jqjmvbW`b66UeSI2^!JYhpSw%B^sG44)hF8jc-efP ziju;QzQn#_ZOiBS17fWxdYpmJLq(k=g>lOX&}BcKgGyCyb9@5o3t?D|krT2=6?{WA zeI_pgjT>-s0v7ObKcjKT&sWJ!DEuRNMe<;Q;vB7D<I|avMZ*MfSrG#0{uSj#j+zS;`;Ws! zIF}bUe5a3`s8yDB?mPjg!VvIi&3J$wrO8)xOm(y>yCcv znIDOJ#%<1AHe?d0JqV?n&4-Ez6*#*6=j(eg-FO^OO^h?6MgJb5q?!O~S;8K+X}w9f z`>f|{f_1gQNyF?Wvyyc+9iF7EOY2K(-i|iY`w>wzpJiAa-TKj&-;`kkMEEn%2^#ej z_5&YWJM{CBTT+4Euhy@2fSV~G5Cn|0@PZJ0pKr$yRK(c#NX8pGl20aBsn@3lpmt&| z@Dxb@NY{gB50M9{ihi>Iprju1*@~F%+@GIxdtCX?1Z@{OK6qXF%|gBDl^f{c5tD&! z6<`mz2Q9kpD5*m+iO4=I0>KxUKgOLD&pq-l0LgTH#o_)uKv7iY+dAYx2Y{HC=7lp#5K z6b&1|S*^qDBOmWefQOyjwS8%R0?c(3+y48xZ3kDLc9iVb_!Z@ds+*DR+ri8Tx)WBr zen2}P2~pFFNqsKG+5@Jb2_Bz8TE0#FH#a4&DLsN^JdZQ}S?S<>_+L#QkOq@yNclG# zX71gQiiqhi3dp;0wA?FBc3jOFGfwzm`F{XqRAt}I8F^Fe@w4DfejPQfL0pgoQ1~p3 z`YqokL*2qpi*|X@z;vjRlQL9-RiEkx*J1B;@$ztemzurFK1+-))k%0WcU#wO=xR%t?OgLd{#@8R*c|Ein3~V?7U}!yE*_aY^{mNgde+szvDQgrp<#+D# z-J_6|3rCXhs7UqUA?Cacc|;x)#c0P9Eel0@Ln$#o6Pr%+P5IzN>xv>*Q`(8XR04Xvqda{^7c4#%zy@Y`7FMi|Vcd-cXGfbFMwwu;^=gtiW#aC# z?GD7MC;hUG%?w$Y7k<@QGaY&{R7S5|)-wJSVGmN!D7CN#-~uI9$41gTOxNs#Pi^>>WKv6@~D5i%+2QQL51kW4!>75I-z)Gbikwi>%;EKTQ?nU4gJ|P2({X6e9E==z|V!Yju(8w20 z$@xMvc@P5gA;y{?ZbYH0e6>;7wUeBDppca^T1UXfT9Ykbwm_}&Z3pqEl<`Smb|p1V zY{WRhTN6^4lMMQgxLkcA0EZp)7@PB~^e|4(BP0I1?cgSFr$dbrjsnM-K=_usC7x5@ z2?-03##q<6d4z1QKk@Gto-d!5LeUV^s?#D&Hw^^fa9!cbFclh`aHeJkJ$mn8s`lJ| z*O+q+rFzlas7?Nq%ewN))pW^v32%Lf>#|^T={FY(h)MfmsuCTt`2zzhmQa13P^&sb zQ)fl7Es{Dou8Yjq=jR8Ix@@v;i@so6Nat01JHzb6w^3G(ksD#Z%;+FFO%yAVez3*3 z`+m1;G5UU8ThI{_jDCzBD`iJ*;}rz<-gBUx;?V%P}mz)TKdeB&ecOch~`0Z zoxm5Am&1$l+XO&a=|*QH!MhNSk0&&Gu;Yz9O){h88N%og>`w#;bm(G7rWi18J2omA zr$DIttSaM_)F3bMB3co9Po$rL^P3?idfW>BS|oP}ICijX<(&eJt^lLlwgmBkib7@Q zn=qm(8bBt7^e@aoKH$s)B=*+xpltAt`0|Peyxl|q8%s>uu13%T>ol-9<4dcZ$I+GD zsVU9KmkbY}h9(9B8c;Qm(l4f6BzId?U*L}3{*RFBS@j=2)G1^iwtTQdv}73K52T*( z{8R@B7~vCIhvc}f@_=?09CRq;FAdwHV$V~x2S&r!0bBFq5gdRtS!WBJvD2Q9BnUce zz#NY9LlNFay$bJ3yy7FeTA;VM2xkyU^A-v}ejMk3_i%FgkY)#{RGfJ%I(6&T1-V3y zTzuEi7wK+0YQHm*TAT&!&(qUM{EgFm>K{V0&I()+H#UVrf1k;*q- z3aSc{)zyW)IoZDt2Q<{!Kp2M`I{idkj)a>5B=&Cbgerfi8}YONgt)h z4#OGD?UFo1ZYUp#e?g|oScng}9dMZgW02gJ#l-__F?~d#I>QF8ys^%y>|RhOa32V- zXJBkUj^3~BU# zZLh8LSXUP~pOQ-q--RVbM>Z&7JeiBCzSQrLM>U32&=vL&cOXowsJZKiC;rU?k&LKF z2>rTx70NFN*0u=yqkc`eIlZL`mBd1(^IonLuPD(i;i zN+gjyRGY$mXeN#YU>g_lZn(uQWGA*|=a2SPS zpnYW|p63n44xx=h6N1`<%pf*5#4aB{4J>gD)J0EZuQZ9k<&Rkvnih7Mj`Gss*Z2ed^{CS&Bf8WRwSu zo4}8QRxTto05k+9#G5vF#Xlm*NA%_N&4}o+pO0n;j&Wvk?cE<;@`6ec8Ng>?{%+2c zF)x7h<8uz+(5@ABY6|s}oJW~#-y_xELa!sjQk6#n^!x)rS1pP9S$-%A51&ck6CxKk zz#_M~g^FqEjc6>0ILS>Xn+K$S6yWXIR*wgo7Pmr~M~u4^MS!hSnhCK2A_6b8$Tr1} z$};0=DXJ?X9m$TN4lk1_NnKNc!SC|fxk&Yfz*h%UL*)M0#>4P3HU`<{LD2_^A_E@u zcRlg}$f9!3222HH=}=G*);Y{2(D;!6r}mw#;7D>|m9*;flyL^rGr~WFRdAJ*5TgZ# z@J$g#%Jqz0p^^#7#)I^dxB|69Z90?C(Iqwigfg;04!lEn3hkNr6EOg++U*`R24vm? ztdw-S4qTw?)mNSz5W0`O7zTq%%Z&~?3PDhKbV>$Cd@8jwBy9Y9kynpIDIMcvX5Hil zq{lZp;fH`PN<+<06w2!G$#ygDbAdV*gfju(Uci|nS1RkBhI*jz(9$C#lHaMECnbA9 zR|+`x-tZgM6RiyUO?ZZ!k@b`!z z16-i(f_uVsQ6=~$oKxWc0$`QtV-IK{D#t)7bQ)Hsvc3k)gsHV+*SYDhY6sR*uQ)4c z8Y6uZp9fozX7k(zIltl-abpX(a9wkTV21LIE3xJ##C`Z44q6Q0>5|#WRb^JVFJ#oa z8ZpJq2nkbh{*;6B36Qx9lp2OsiW@=*OhZnQifbWdj{HIMkB}oJ>lbrYrm*~wRmg=L7L=Q3p zx*y<=3JEFTs&pAWI9sz-S~mz49j!1{uv=8a6OR#(DK`ZEEsGJISeqhac?o8Xpxd+= z?wNtlC72A^Na;_>4pC#l3_%m{*#*)Vf%u<-30p&TDArg9mGjud-i^7cmwu;~v51(z ze}d8i<3bCU+qA2v9x<+hBnE)=-<5MJo1$>~#SMfbV(q_qK!pA|R+(=Ta+Dw$lKkl{ z!eE}}#%kdJgZzBHt||${EjjM{a~O>p@RTHOAF=70;n@Ejzz@oW@6IjP+OnK|wig%E z&NmHM)(&NaNbpt?j-T>TfE>u*3MM$eW>I!;j%W_0UKu6#{nRzgj`7&%GF-4*Xl zswS>v*NL-41dV;ZKHDGyDA3AP)XLP{fTrNO#qY>N82UQvq<4Bi@p1YPyQT&?{^A!} z*vkCPSpm=*^jLsM8H_G-j?!`l13P99!!S`nH}Z!C23J`*2`D>!pGnRPHx*h6g*^Az zjU-DxcsZ*C5EZ%V4LTGM5;wnu!#+W#jAO(zB2Z;s993MAHlFrf(eWP~AQT&v}BzGGQq(mvZWd$%GNvaz^9? z!+;*jJfH#+lKK$g9X7@*!iP_sR{J>8#m_N3nYwF|m^?^U zxCPH<`g#izR(-!Y10M{&fEeBbu3eZQFDnk`8jYP1Q$?1T6e_+|L*nT>%Q%~O*$u)R zk3TCyo<45U6YhF6;L_pryNH2g4=m?|y};g9Pj4Qq%E9C(I`R=6yIp_q$#}UprPP1^ zcrLAAL^D|4FNQLNoPPD}$|kz%-#Zb|cCX9-}vWzs; z7d2TZh;kLG+VLB3HteRrO_Izh(o($pceD0Vz#@!Q@%Cy+Ft+Rnlu41^)8}S}v=y?_ zStiIObU`9tQ0b1)Y1uz>&`w|M1Bb@A!2NUvmD?y_)GYOOl4Mw>N z!-2C(?+JTYs9OU{Y*K1{Dfb zQV7JSF)AaDWvL!&0w{^X8iz))CaA>{gUQcupp3GE8qAP&;04j&&dvNH_5u1vOF9ht zcV$pPbIEkMw3}Q=b;UMtg*Xy@_L)#Z;I)*6_BCT6;ihqXmx@9AIetG`NP!e4$Mze2 z8v#>_v$#;sL*rq8Xddd(K)9^Ox$6jf#@9nAtcnk7@`GGM+o0&C0+JM%l38Zb*w^_` zsrhhG%x)Ip!bGxUCG}3gNhKrGm!~XbPNkDa}6ex&8Qc=JbLGL9xB*dYWg5P@FW)2(hm+lODFh`M6Xm2e{(tgel z{&SwM+wM)myI4oYBnnbw?k!;0<_1RC5#;h20hAI(5wIi$g;3w>GE_{#Ff@aD>s*Oc z^)QlEzZRl&ec5_0TlJJMDtFML;-%IuImM+eAEG7>lB;RA-2MOot8Gn{iX7`5uDmOT z1??R>q8GEKp^9!0ynS1yhYuSwlUflx=c-;bg6dD&MJjG9V8t%HE=NImN9Q zAB5H}eBYcZQZ)~JV`YjSX?Cs6044t2roAseQZew;hN>2?XD3U_vxsvA=>!=R)tjts(bM%@6)!ZA=8> z8I`EK0NY)2PjC6>!7VJ8H@s4ih0LG8kx|<%h=D;-%CNf1z~%q|-2#4*0tl~qp7<7B zvn$Fv^@UVXEHp^mYH+;3rui4utI9@y4BYEJ0iVds!iDKc*+)i}3Vxdq;ksIVG#ZiT z1u{67DXi(np&ZdhycT`=PS-ze$iK)A_M~I?wzuwJbC_{CHq5XZF{mPQO;T+z=jjxH zV~VfD0c6KK{PcbFvG|>$$6wnm-8?VmJ8+vnpWk@zt!Lf6ogpnF-%*;Yphd6gNj~|w zHC}>i`*Jk9!~watvD}OpY9J{^YVQhN!pS*kN?XQ+vA|LJ);MI*b5c|=A#c>nJ2t#L zfc>*>(te4aA{?kFTkYg!4jZgUp7t({H}T-L>>8a8R>ved09iaxa_|j?5+hrjnpiW+ z=wb(w6|x-kdE9A*z-i0Y`s|9SgHBiL^@~c3b^Q`3^ZE~bLZ<&r9OCvvMq9bM$5S~# z;;-3~oZDpkkZF)ORFD1G8(i{wuXatiXtW(9qbe$AB9U9?AY&SG3;tiL&j#qhYE$hU zptxl^7J@{+EQ)4* zNaRhqL5k3ft(^nrz5Gy#6U|;Jo`ZTV`&b!F&D_Ys58lW-H65`FSq#O9A%PPnfM4ji zxu}Hry0BlOlziIt8*E*k55K-%K7Xc_M^1N4)Cu*z+z2K7hN_a0V}m8!{t6}pFR|7U zuF&U^gK;pizz9fqd~IC^a<1)7;k(ke2!A7l58ijk%70e4S6Qs2foQu^J8eJ?^k*(K zD`#J1b_4BVHk`;Fz7FTZke(#GRtv||*Ict=cFRjVr46Y?-vtl08!GIhu{l*vEHPUO z5ZO|hixP{-XYOI1Q)gs~K+U9>Oa;MT8tF5#VSxl1)>ot>UC*bl@(Ba;h{}`|S9v%l znJ85aqs-!xXaV>Rx(zn0TcK!rRfp=RkF4>%b(=a9Fi-Q-&^%z=`LQUkPUpGPtE}Ax8So+`X~qvvEs+UUyMh`8ci4|&>KfmQLT znajOA!Hr2R2hy&XSL%~-1O}l~sm#WaKpg|ln3EB356K3Ie-n44zFO38b94*gr|X(| zW550b2cSTl52)7K-XECaE-)I)3t`{irlPu2A=V2IMgndhYOBZ|hX-$5fYzWqW2jO- z*@t0vFtshVB)QmcJG`wJ%d2;N&Qp-$oqC6S--?7gS+{+{FRCw#yS`=k5k~d2s17^} z(nHbmM8fEc3AJxneh!Szb#HWBLQX{#*g=QHdnu-{f=pgp#6POF{e|NUSgY_t$Rs#~ zmoWjeTnLR8@Riv7g{o`{mHH_6Nd4oT5_6n7>$861>O!r$I@$O7@{=h9mL7#ARfgy{ zb0iZR?|JYDSW?6^v%Q~OR5olFo#1OFZfcXX^G!Ja4KUdR02+Ko%A8O;ZB&U;h6(%g zT>O@;gL@`W)T1H&q0x`T(J?=iAlxM+V$wW zYLf9eG0tf%^?npsHt+o4O;QT8v!_qh5t^N_9l5QWoou=I8(qRR?&s@hH=VC*cpIUy zkgSQGhB3QDt!vXv9|7N|uD|#7v5YAO4gmoGs=i?;hX0w>_-};)(_>M2@(5ttA0&5V zKI=ULGzd{Z#5q2Kb%YSX;Sf0K>DTv$WcE%oWbvD7USGN@^CzH#xO50G0*DebF0Km2 z6M{^*YO6NBkqvIbj_*}1S02Mj&P*rI`s)|(($YKU5MnQLOHbLevMISM(S7dO`sY*l z1K7X*jC4D73r)0{kc)WQW@m131+L-%@u^m<>^GAR{ecuTwq?!fSS9JEh%?#j1G6I) zaVQMNF$#JSa97l8dKSR(-xcQ)b&qbTgmlxO!J}!Cu5ItL=Thg6j4NHYmt$A z>>>SrTP3otOz~O^N==~`J(<_)K*a6TFxtnV9rS^>YCMn%Hrt`sw=dhptGAE7&E~3`GvY>o z-tENOKbDp8vesq)EoA>UfQ9~lwrKtnwemfSPnHYhZ?pIUrYGqrl?vHI@`#D6t@?5_Zle_&Ol#w29u{@YTgzaWJy^d0mKZRrect^UTTnA_So z(z1M$D*tqx{y(xRe-WquCw}5z*vh}KmH*e+O5~uB8P#`SMEkq^;V%Ls|HMlDcY>uu z?Yq}W6vb<)YMC*9)}=aue^z20;}l53oNxrd8h=EiH3_|{e)MuQ=lwOSEiM9AT1Y32 z&~VJ-s%6WxYI1l?+MVM$y6m?NWBpPwKPetW+ii@>c}Yfn&$U|N}##*gj%4+ z6ga?RwDUSKriP4{-QJ5=#g97?Qn_Uzi7Qzl&GSlMXYwaNbz3GZtX$zv(z#TqUL_k{ck4zC0QHV?tQNpuKJiF$;b67C4XS3qf`sSG< zd1x|HRIqtGBb?6N+4hwDicS0IgiPYklU%k|ZO#WT(ZWz8NCHG6g)l%{wb_ z5l)Sc7CQq>v{ffAkXjawNZy!3!O2v8&>NX>DR$_R#T{9ufxa27?2naqU(=m(G5OyI+c>PDRV@g zFZ&RbfN3pOSPg}d$_?zk=`3_=k+Q%~s4=2m=>fr0tj@X)4i*5|KXcA!w7_*dewaHRZW#u-3C*LUdT}?K z7?B(2@|-}7#l}lcESoF=dP^ypAMx#D8pSY3JS2DFZ`vL%-A>G9`MYnaoZ+Yi#snpgJ#m zVa9_-kG1kEw9Y=ReU{nFk~u3Tfa0d~7KK)0WTc{4h3Y`SF7OD-St14q<`jb%tRvbo z&`2{aYqc$fHA3qPonk{e5@NhVsFe@B)9$GhwfcchQ@yMv^=PE;m9mIn zM+>T#xH+}jZ80^OdON@yFmG2u76y!*`2`?D( zg)oI{Ts$~_CNHX{P}UlBMP98^ME@=?rghGm{6uzqhtTCa%y3DrsUG+gGPBx6f%W6s z1yxXpPi5BKC5E{3Jh?s`wFr3gKw6+Wb0;P+22Z(^HpB8e-&!!7wIjf ziTLTYCX{3%)eo_Yjn1zds;JL@=$D$&Fl%W4Ry5|{ismn(9sk5v=-W^ zI=?;5k9K2rf<+kGMKq%;5|O3mUoxZYSEZclaqc3ZmXs)SCt`H$P_6npv}^@=AS$l# zkiM6JU?LqHI3xsNzBL&nRA3NxZLDkx0!KHWakFuS`3(F!FGMYHjk9~QCHeO0^5>eu z7C?GG+={J~x9rI6`ui>S=zOtC6UP}F_wAVY!n$vUE<;$|k>#nLRI_2&ucm>WVcg;d z^eZ+|&y`HV9(lQqS#hntheCDu-5*1C{|+NmJKUg$^A)0mu6vDCKX994BboYcz)-+< zaB{Mq8%v{>oHRYbC4A;~Ysqx+x*=2>an|k;n&A5xmTVmRA%!tj?ZYy9CkL{dIxi## zjq?Q^kiGS0h&lz6%0TsXr_T33NBaM%Rq+?KV|XyDF#oQI{sQU$Cr&_n`7` zr2kk|(A*T9SLS~q{he1{@HwO4o3eA`j%j9r|2C*RP~DoFJBhi_tzd#?e`S9G_hFqW z_}6sXJEQIskp&c`JMzarJd^X~KOEorCxGu3&c9;%|A~wKdz~an^Ibi5^xtv(|EI@e z<7DjW^evH?{_i9b$M?Hj5<{w0t8U(2If(~lnMZ+TSzEsy@9n(+Uy zO8ez_-CLAIp0X~$WLS;o#ME!tbtgY%0zzo0;kZBkv;rgn{11yIk zNN427AF3n>X?$JpIdpF!a>u86POV;aec{|WLF(Wqx)sTGNx9j* z{lQV<4?H1>rNSP-eTvCl2oFb=21d>H` zSF*;Sudu9>uK!eP^bDbFT5cS;c=Gkh4!iWeOjXzoj{8*7(2x@J&Rw35tJZFHD4DBs}TH&9AwYZUa?fqsTJ6bPN!}@_Q+=| zkeuq$_DYMwFO9id4XKPT3$o0$kQ=Xl&jt=GdR`l88U=qU!;Oo6PEH29puwZt**GQP zkrBnH%Qluuw)1-UynRX^1K)w4Z6)2$Yrbf(XLasXCV}J=z#sSA><95_y8e*h)b-3p zD2k+!c(~oftO-y=ciGCiz%IQ6_ER&0Qb$sycJ1l?@mM&-bq*O&!ZH8rPu=~#Igg?C zs-$yx5cA>5${bcA+pBqL$Zyf2jk+GLyn@6;XmK4p`L*JSTxXQCP7Z@ixb3zDb56M< zu`AUo{1vQ*&mf|*i7>Em%||hMG!ObL#f5ZR#H*m+&uDJST9#zn8lK^KH!30;LzE3A zM<)t+O0f7zGP5W~Lm$Vvgd>%**2Fcm)>O`m@Ezl<9x{{->70N*qzht^mjbDUO4g?l zVPDW_HeIPaOZt2ffhO>7yhaD5ylJ7+2Uae{6V?l-TU5)6U08FDwAo(= zGcbQfKHH=;4npL=Wcxx%waiY63Mp{sd<{njcVnXzTE_gbW&K4+^X3z-N7mleLDuwGF9saZgvl(%OsKQJtOmY zLda~LGkK+#Dn13Gm7bvEda@@BxQ0?TJyh(6cZjhUj~eM~;T>DMD*O{y45^PcowF+* zi@Fzl_wZku`xIYo28>)%NWS~zZ8Tox1uGl9B^>-Agk4gM5kw^+6KAwtF?RuD=j^}@ zXxsBQf|K#Z{)F)Ot#g@8#~^soRB+1^Uf|5fi~qdlWaf{`q0Tuc<4Dt#VDx`@z3uEq z*dr%?eZYFP7x(eOLUj;*+`La~jNppPK_wyDzT&2^pN&34CsgVlkPu_8#VmP@dhSp7 z|JZw{AlequzFRV9NuT~s1kt@v1SVe3aI+HAP5NewMGJdi@Oj6CBu!?ia2x&2Zk!qnJkz4`;#tInUr#C&0L(SCt^FF4Xsw)d1Fs}et z_zw#~nRqDEg-zL1?!J&lq7}vQAoSY zUpogS?ZIUD5SrE1cQ3kRXYFmYSCFEKbA4;hNr%)&t%N) zl+I;*Sc8SS$kA=2Fm6cD>${bz&`247WaA7S1<(n!k39q>5M#7<;0*;*3+}&XS4Uk3 z`&F01Qep1i{SLm1HV@;TB!nvs7yb<(6k0pEsNcGqbxE8UG>8PscgtDRgB}qBR`t zQJ1$o!rE811ARc2%_kFiJ0%NE9+ZPI$wA!W@1d&%UnFZ*(EXe|-7f@Vnn}dgxGW}Z zi>p9%&z@czK(QGZxB03t6+tbRb@ z{Dm5mK!i{j9w4ZfG7-ZB5keTS_pfhfLkwM3D=*8OFS-q0$;^~ytR+0?0fmzF&h0;1 z-OHfaoc_3*5rNd*QZR$k_(s`mC{~rP?f!hHgIRdc5*ShEQ-4{Eyf!V3S+wgoq2hvG zpTS?bxf^ZD#UFswbwo>F%O6^CM_zw;`sA6)(tiYG=kxbEz+#eH2Mtv^@7^>=TBjBzf zjRP^qVsv$usXXS$>WshU0ZhTH-FzcouP?;!ma!9OuARBmcSGDKuUpS)H5$cxCbQ3z zgHx~ePDQ0CCPYW4NFrLHZ{uN<*a=Q0+!cC4?r20C*&N7AX&THTlzl`>YWyrmel*o9 zr5bsz9ml&+)9{5o#h}4xJbHCS1+W2xr!gYYzdqc;aFN33em_21Y z_YWBVKlVfZi)8A513LbHZif6HNy)!+RQ@@Z|IZSVe{HImy4V^y+u2z=(XxCK62|{Z z7RSG4Jb!(I|0X5>O-lZ6lM;Y2FG73}001fIf2WrH^*sGQv_w@)>3|L8yH_`diK!H~ z(gwn+?aGjR=tLr1#{|F?b;asPbU2L#Dd(==4lySgiLsPBB{Sy!>3g@+UE3)#mnzf9 zzTd2RwJ}(MG5gyj*5qzBrfO-SrO93*5}RlLB{_F+yG2H58ePx>AYh;z;-;`huw+v` z2wh*h;;n=8=KKxWaHvCE-?H8$732zJCdJaPD>R(PX za+O1Liqv`b?it(|Xr8NO04yQT~Lk^7EN zU>RK#VrrPnmrG@~VkFjWE;|cgpmf`I$3C&vEt7FI#pyBLz=h*%(3D{fh2 zPwP(B4o;s#05FY6N7)Lzyb7mrE&WOL=KRCPTHc|-w|?b$apA0s-%SZkyi^Q7E=`?> zXDvCPN2WtO&q!_cVQZXWh3{iE=lolLZ)^LmV{VjuYyK;DD)@rixWIWfcO@eIsg#un zpCOcoq)VzwMClQj*{e z3;z-Ylxv;4AZyNdvdFG{R!F+?$&5xhi{=_c?xP7UYz#BDFI(na#os5d=QP%ZEJfLx z+jl*wEPGjiTz84nQpd?m#R_V21<3c&A53bStxe7%NWobJi zKYKQegkXMWfg4(dj(^%tJv;7L5OmBz zQ20i5Bz!jVBbpdx9S$)gntX>ZNfPix+;_!}oV(Sc`-hNQ=C!E2V*iEI17B~Q^S44j zrUrc9q>ME{w*DL26+1^R?QE)=qCDUD7^9^ilK+cY8#qG8=>xeR52rE8brvljZJq;` zjlA8$5#e$Wa+_K`KFkogq?CJ_om{t%R3<}#~&0Sh&+1?W4FWW?Z0^B=E38DCp3OI3=~DVr1TI_)f=UfIIqTw5AHj>kl}Aw2*)mP;=ua( zhwv1BtC5#VgXci(+-Rvn#!)F?N*0A$0_f$&y32VYdFFjSGtQi(jwZFib;Q<(okh>i zC*#kddHxAf3i_7tT}0&vYotW7DXMh59npYssl3<&&9~&z&G$4yL^jFKFqxtS;sFe_ zNpY-|I6v0cj%P8qyob?S+ZAz*w=wdNT$4W+UiNe`LI3x2=0Aejf35TW^_=;C2xkTU zC87H3NB++O+JF6O{-?bD-7AObzXoRiHHrA=ga0>>{cj-qe;dgD?5ljM{La6U{LXUx z*UH5|pQHa9#5SqvDPglC`1Q!unWV=%bLcQgkUJPr02@n!$HUC>x{$@)JbOpD%!|yg zZA$ZbtHsY?7=^#f_t?*0e?Hf^rbsz=?namR<<$vR5W=kb-UqC31lMfdYBPtm&}qcO zuXdlb+Fqn**g~YVo0AHb!Mcla48ZLS73TX&Z%Uupw4kj%2e&Dz25Mk;iLMO9q%i zd?VPh46LLes}UV51CHR8SfDbB^-Zl*L<5?;?9DdEQYf0;R$7p9oUT=%U-|YiFM@}q z*1NCnK5!#4MvuRQ&%8Z+7ZPrM#@_$jbD>=Xp=(WVJj_1%T2!b^{Nj48`4H%e*KrKl z(KMJXj^hX(m;>}m$jqOLGVdHX`}us`_E>C1F~n=WW@A|?h1Ohr6GGBI^m}YA?4p5t zP|0Aco@8A0NY=^%Kkq~r5_Mqj6m@AF3*IiP_)#Gb<$@$nQld?-r@E<6nS%w}C8B+9 zSb|aZOtytg47x`UndsI7?E-DPZyM06Zl zMwEVvyBoek>)!7~IqwKKF}t-sw;_sMX((0^A)xomD>u;b<dBJA(fH54^iT4mM{BF? zcB}TPG=<;3`hlE5RYlTT$t3sj3Ratf;T;jaARK18A8YF-Kghr-7wz3M%OxC6A$y_(RqMWrmh;ayTdMHfUR5TfN21Hp4dQ$sr9di%rv`2kwX-zPOkgtfFmDe25o21XBFqnHknE_jSQdL*hetSGm-1yq-~Q(9-|7QlSk zpmDi{apCazttSKDUsH+g&9(#4Fz>(ss}48V#rhLHX7d@rZzE9QZoKLn+M*piGblA9SQtv z>T(sj!~5f}Z%X;5=>D_$T1MwlQI{QwRw^EPTY5yhIzePp&g{#ZC=!EU zC1d(B$euMt6N4gYTJ+lvfm9mC3!6& z1S4k-fv1HqwexoBMuT|G-S}pN5y>k`BxfiIHUlCIQDTY(Y`v#B5);!3NY1j}?hu8n zi%!mvcF!+gAMbyqt%iB(=R|GwpvnxsB>rZ$tS`x@c!M#JH!5G z+o|h5RO1W4$PUKkAk8PIxLgj)an3-C8Kk@Pn<0k->H@&o4qV1Gy#Ndut3 zPvGWEh zUQK@5eyOh$7Z3C9BnSc6UcOhl_gT9oIwAr`mI7VKHh-zQ>WIuRXRoy;_UrTW>XA=d zjdacV2wdmUp1P36QTE!jgJjtk$0UL^4Q7D3($PE}Doy^ndRlupoKvD}7IqCeIgtE^ zy8#qDIvYilw}nlf&7r4y!h%BMR@1TR0ctAAjS0msAk+eN)4M=7<1xo%jAzHmXOoqL zlo-3&O~vMn1wq6u6uBQyNc+FY0F8l5+8K)H%ndq=q0pzJEHCmq1(b_9KqP_v!7l*>kO)w$cj$l1TV@H z#JFs0Dw_&rTrso~D|alyI7rOGw@Rptqe@11DnY&t{M0=A>I`_Fm6y^Rzeb zB&DMg92%(}LcNlXJV*!5({DKAV|P%qfU-7&f7ES&7@Bw<8#d0@*q6WlvNL-o;hMTlhAH=wvubg zUbZNNfD@EwG_dVL=Q#w2!B_Gn_+@?leS94O6q=TSP)m=w5qWbHqSd(2q%{@h-7~rJ zNv0H+6@ww2fG=Z4HHTm?eQo>7wtPc)4sqqep@wQ4&g~<~qy>fBM6!uE-QES4A#nE# zkPpi*5cdkZSJe_eS3v!EwSm2-m{s0{AQR-&3mZb@G2&o&HyN z!(TQz{f}JjF9PlVm+|n=SL6Rfp7yT|fq(wE|5=Xq7nO-Nc8(@`7S1M)f3qAI8U8b6 zP_Vsg7gpaJAnv!G`xlbRzZ?IbFtzoh+2$=HJ2(#P3J~H0>vUp3@>Wug{^GgM_vzTn zXF@{4naP)Qi9bg+&LwpHLJtr#IXypuFt+4odDVv{xwCa3rr~#ioLw8k*g2w}5NtAf z9U9R-5YwyievDQ7@Y3UZzo2JX%M%%z}xICec2AIh|J0%`o%Ul$2u6dE59H|>)~$q za(#IX1(H($PGX|e3v$RD&|tR}_vsq#k_OiWtpij=d_I6uhf%t^8X%u~kbqCP+D#5- z20T#pab=rLc!92I-dPjwytn@Qfv=(Q?0^N1!op1YLgTh!AAUo)eD-zjrK?A=aTva; zKzNVZ2u$m#@2lP^dBX%8f9`w=2zU4Xr2Iwo)bi1{_9Y~*8XG|B5)GVU%NYU@{>J_V zop0LnR&SDgQ4hLwas09sjewe>$ulW!RKJhfn;vOZqCr&)m?O`_;5k>dH6p8NqX0hs=U2z~9k{jTg`IzCTKqKOy_DC&mphy90Ay?l=KT=Cpmt$2fEP8Jlly^=oNPrLIuCi!sLHlp%<*phX#c43SU&D#yNvvdGtXE+ zO%83arj1=JWx(M*l}d*}beo3c+G}c96`6R&@I|{!ZKKG&_Q!5_f!ih>p1_eI1&SA5 zk}&xbz8^MTM1|&}hbSsr**0%V{!qId@_l=h)T^vYe+S~#e0QNGEl#9fTzC>w8uB zrXXcshcv2o0NrX7b`g)yljDu*O?Gu~6iEa==e%DadGEpnQe}Uz2wM*>%4JSzs4B^c zax&P5oGy6Y1P*eIdPK)$&A7i?U+B62v{4el0){?!HOu=XJS!rRr%aJDLRh~U492n} z;hEe&B(TA(bCfFkci|3tTDH0rpNRNS;87m4U6~s&>$)#$$L`mv} zi6{Gj+$vescS(zd?_d|Q=I})3N(SJ6;CtWs{r1NCGwlnoPwIHZ+tXmd99?T?TaMG2 zSP(L?q{k@KIWY^sn>`FH`u+hY%=;K2L&}M~hMsislSR>pK^oMf!Cje2a0R&zYFNAf zx%AFczOsN82n{lmoB`+ijnHZM1Lj$!R_wOqy7R;-2U2iWWad(T!6h`BL?ia)1;m5w zXYn^#mlZ~~5nqzR8tPZ5L}vZuY-b-dwp^4~t*GwdObqB$KuE0B;SCL^$t*q&NCo8f zqIGV{#es861^hW#ZcGW$;Th!yRH1@;V^Ajm@+G70TATXgUR)ph*kjz zBU^Yl{JRYYH;I;nG40}~znq#nA1WDfeFz9q@DKk{-1Kwa1Thq_Vgh=p-xnC2x^U@ zkMyXvTL1>vdvBuLQe|PSK!LhW6~eFB5PLtj0idLU|;1DuYiKq zpfyPhF|@IU|Ai~MGsUFx#7U}+pnU+QRFguvv!XTsXpb<6D~iI!ltm6%v84_ed(d(U z)h*hwg&!E>$2>%|P8=5O0BIh2&k)S2m}Dk5N}|&i7K3}Fw!O+v4h{QpGK&B(7+wtW zwup(hT(CRKcevDE>~5uPJ0DN+?wtoe2V=EFGUz!^tz6z_Zt-qlAJ=paqQxOt#KGfM z2%*y+uTEqeeKd(9l2%tLOnd%G4@_)8uz2|{*YA1R zxl|u6;3(0txE%ny&k`Y(&CwJMX>*IlREkY+^D6*oK7j#yG*`G)aS^$K*4UotX;);T za2Up-$vEyoN-*;1PL_dSx~PuMHMCyjdps===QevsSq#v10x-BV$sJlqu~@L%8S{@Y zd_H_c_rPa)7Tvckw7OR7>2u_0GF*Kik@xUv+07X_N6i3N&J=gt7cyb-6!>>_&JTB@ zL_l|+_tbZM(!dS=vaKCl(@|XKR=b$w0gGrdN<*@8J}R-}%R%AEvhc;0yWZBM@ObAR z7JXo8=Z3ld=|gbL`et_JaK;Jj;QFE3*`jgHR8qt=Mev)Uu=$!HT7&Bdc0a5Fz_jo! z0;lQ;b|SlCysad~qFdhv9vJBr&xNkuM|qGbDT2B|9U8x%3P(>dTM3{MnKLR{s(%$H zKg)GUa(781&=J)ZzV)TdmiWfTM2B9Otv4WCKIIEU)<`o+lFS`d3;x^%ptGkV$hH1- z=>ovrPB{LJx0lsJ-1qxeyz|yyu) z-n%~+f)~oZR}-D&w2m$!*FEbtWO$-x6?E3Y^+4O4hjpWC@LXchZ5K>9twb5aeIWNd z;T?hZj_@Gpk!Uo}?zRstHK+-+dusv>^(s}&(~ssGDqC~8lNJYH>uKk8iTR*B*xej# zFy4>L=$k!w)X_m#O%S_4Q2dRdA_fnYoqpL;1MoesI!!GJiGe0^BAZv;aFThFqO{S2H(4lQ7e_9!OA%kP_kuE`rB*B&Y>w?rXrEFR*ek=XkDn-H66 zlAcW5m%KCTOZ}S3ahc-K+!S%tWZiP6YQZ`}Pp`#T_i*z-u+3$|6PE=+mw+$4_yQhX z&aphZv%;R21S2H4Q5~CQ6yr6;yc_OKBCVQtO;4y|UaZe}_BZ8Aqs+c$ftNw!aP23* z#Z@@gn^y(Fc*4TPnkq6Kp?AR;YhNyG!br9zTSH*HR9j+Tk7(ul^`EntdiV}tEuX2x zHkE@mVkz9_2(~Gsb>x{DyA(J*Qz+jc3SUx*^zDg2H6_yq8B|6g&YZtHE=pHPi_jc9 z32@?D1cY`-V)+n`^Uec+sZ}Wp4q<*ttp`siV^8M32$pOn%n!pNzEE*>4c(l@cqBF6lx>%e?{QA?Mg1B~Ct7hB0GU(3B;cqzhJ52zH6Yz=qsv4vY5}CPvbLny@!2 zr|);p@Xu4dgxYl})b0qO6J0kx2s4ae^L;DqW#6h+fbTsV^d z37w}tmOwf)2uRh`n?7+c5_93`>JmTJxj0;RT)$;AZ$t8k_AH8hEiKD61 zfi!Un8>(dkzI*p}W7N`vU)}&eFX82Y8BKswvL~`(*nwTPfPwPa+HZR+W7@RwDtCLO zTq3)U9q;<;86v-X9Pz|Hn-JqJ9Kc({t+xSGs?6@s4ZQ0xrRGt7q|x85cv-hpElYr~ z1S|&8`zxUQfsP12pl`QC{48@GmbAe&UzAphmMPIuSfHd>BDa3idYnHb1Z#Z{=n3cN-oIf7et4yEq70QF86Jxy5zaBvPdbrrC~B zbYKLVnjSXOBLe7P_X%XjN5SO-LyF3@J;COVWmRU6Q2 zQaCH-n8~AqOM;XzWzrF#eSbL}&4-Fwy#N*aGi=*~3jODTUjCqGmkPRZH)i&T;3}XF z!E;k2!CZmd5R!fsMlxU}s-X9b*2^f!PD|(B@9$}1Sjvf!!8kfhRnCOW;mm+Psx zY`GFpdCorBXz9q~XvIQX$DyR)(WW>0Y@Id_Sfm>hrhJIcPTV{&!1z;TxGE~RvHT)1 zQe@bmmOBMny5ulo1@^(Hv0@e~O|g3-_qHJxZ>QZl6D+qTlfT=N4vSVx8gDC}pWQ8H zWb$YPhoNOxwqbI_B;pWF$br5j2}9-#>!7~9;EBANQ(l0c+3agk>8Lwv%VAz}nF2nu zCo!0f5Im;{iAJRM`e8uVO72xfyh4HtF)52COiDk!)L!Hv7X(L;C&WRhpOSZ&Gdv%h zH&iL71y*u@P=?A^_PC`kN9>DZKA_cq_dcXd?=~!`RXF9L{byAbakM&)?%f!aYg#JI zRME;L2>TVUZDY}dAv-vx;}gnT)~kU&BFOHh(yvcA47qA{UYaibRl!L0ur~&T{0Wxg zW9WW=!ojHDkCb|Qji=-g@n4GL@OF4o?*wBHEGb=;TGJwZg&7Hzo=r!;Rt>CB*P9_d z6jW=Jk%>Tr8fJ978Tgi6^SR;-I%Vgl2~;-tqMXcY&kMauH}k0{#P__?pELEgJR1hp z=y!Yp{(d6=NBQ-?C<*?h0$anuUj7{*1EBDI{e>g`?-t4b7ZG-1j7EWBxd9*o?C2!r z)D!IjED^2f*qBU%G7%-I*fE5pSc?)x8SCQs=%nbJ>{!JS@E_iRh}28@_d5QcGW1_4 z#r6h9Rt9Dkwq~?UEObo&v<90N_#cMdxt{Oz`yO`a`}&IzoxdFRf61UWvJ+7K3<#Yk z)W@4bUwO-cf#LFg1a;W;v8IF_OFQgcr$sFujLb8GP)X+adXTU+zJ#wWCznEG)+TvS zQXmEGR3!Ptwwh8b$KXd}}0z%>D zre>Fzq7off*UdhPMkOljNzCzGX}p3Y6b3>_H2j*&pKm$T<3QZ`*`;bF;i};G`A)bT#s{H8sHP ztv##7J~8#j^Y;TVFCpO0Z#2IT*)TuJuLmC6z*Y z^}b3zo9ZuF)c*4AbW55$hdq(KjwIKhr47U!=riFEt#V207{dZdw0qdm7SqkEA^85q zTY&+uVjQg{*b+}nuVQtJw}PdPKLnF`N~->nwes*{gd+xam7nleCr8?vjF(3Q`U_b} zwY3{_QrmE8K$hg#4pR9ui5T2J2rO!XX(S|PfhGZNJQ=}sZ=o!Y+^rZ+^<|VGEL2`+ zW9LD$!E&N?Kqn*@nu{OeKO*;Y!+0k4U{ChD>I%j9*0=cwNh(Cc!11R#6ESgzep35P zNlgi?0d1P~NtFg)zrF~#-+$)u; z8=oX|n*Sn;bu1De7SoQUkc{f#?GAVj*|-FU3fQG} zg#-Fy2$LMJ?TBWPtyT+?65?zrTU66drK&RI=y4^T<`c8UT&8HxJieZaL*31UIi8XL zQp5~w?o{>QdRNTse9wRH$Xv)?5_B29O)Rhz!#5A&x8B8;`Pt2GvZ%?eL(3ob;QqGr zdhUB>`;k?7-^Jy|+Yv{4?pZDp2w{XP`9O#6cT>wayHOP98pUVuziepvs>#&d-PYQ~5@7GAkv zAZToC-T;W3;~yEF43&=K0)??pq}HZbl2Gc!ZtX!j(>}h@BY-965^w1Hi{9Iw7*pGB zB-?7R6`Ey6zU8`_5h#t&xCJVztYV`<*UXr<&~t$f^JA(y;3ZZ7L|PN^5yEjxqvngu zMYCMvi(?lz2;)9cJ*RgIIN_8TSLm>($aIVIrQlE*ufc|`_tGTdOxD<%pniU`zlo$G z02Ow6mu38I*lhv1f4GBe3(Se6CwTIrniKcmF>4}rN*`ShS;irw3Y)Cs9r#rArn08) zd)(iFJ{#imHvto0s$z z856#D><=lT)KTvMf0AYaAi4xaV&^6j&{Au}LA4cND_=N5gr##+(GZd@iGP80;8Wu_ zB#kyX7NBGPlFlb+U|Wafu;t_`zi%{>azC>;Bp2*dPG3}F=zpvv#M`5&UO6%s1s~gx ze8K)iA){gVFU zW_R%QjbMgDM0wa=`Y&{Zq16CfWQSg4 zlNl&T)BdqpkjsMFipzJf4Vi_2g|YE)9o?q-IIPR58`Cgg>iF2rc0_jWQQecmrNpcz z&__tzsQJ9-qo009)p@XNS@)=jyYLti#!rox&H@|=Zmr$xfp60H#pAze;Z7suFOCg0 z3cif!4*|*;iVKv$x{Oc{W%qL+54{h3lb}=&mzOzS(qTeJD5k)GS(^XtKy9-+#lay%>=< zt2t!NaSj&W$m=e}d%@mSg%uN?9WS%Hgu6Sr%#A-EupqxfPMWJdVcSRmXa6Z_gdg&U z8{UN`6rmBmeSl-{dd_5ncyK6IG;Ov0W)vghNZO6AA*%|Dpf92~qX8|u9eDQi(SB{% zVE_dI)KtAZIwa;A$e>PfG$Z1dw0t-m<<`fVJBWioj*|FL&fq2nPY{e^HIuM;-uN!# zTxO<&t9VGHoTF}eiD*SNVq^Z7&p}vqcGdND%6C3gthXHS8ur5UO+X^|2Kh`i3=p01 zc$&$WURvp_r{ry3W$B2tKnYzss({3PCOh(hKvZl1yr39G_i%Mu>I7Q1xf&E(>>R~e zZPs|nqp!rc>UYt`S&V=Trn7XjELys7l9xhQjw3@3h(`9x0$}2-OFt$}T8A>=%n`O$ zrj`a+b&5=zx1D{T9$FJ@)dtPu@W+Z6RJjkvcC-l%)7u(~> z9bvrIq-Y`DQ$odu2pE&{=ownU$^&CI{E5&?s*f$ho&+&tGSbp(+DFzj`PwI34kA9} zCnb*UQmQ2tBW&{v5<{R``!DoOj|dCnU4}sbW^HM*bX9?w!5_WRC3hpC9@pFZ`9Bra z1R9eS?ZS|C%Srf7tC+^lMo}vt|O#<1Tu({HMqSZMOwx((dmp%u6Ezs9vJHE>Iwz&_Ub}OR8kDz8mCSreSsh!Tm8F5g zMCzYwhHQxG_Amj{MYOOh>otdFFiCIVHo2*binFo{a@v~il zU~TJ_GWtNSVAKz_X&AF`JSjdSn!SAzpB@^e>cI(Mv%tm0uPax4;xzT}^aoz|lB>^; z70JKKM6N89!m4Asi$Q`vR)@=g%UaGHHxOn7?s&7d$^)$``sGAoUzNJ_OKoU+1ARie zat@P(m)pi{_9~u4(W(Xl-V?FXTR^Fjg#F%hlUSl&L=6Kgp|$*Ei>4=^?iy}GwatKRF6J`h$-mc$4x{u>tGnqrYe3tzFq?0FRGmb$4g*=g!t?Vky}U8oN^i z;E&g;@=^t1x=E;9Uv=dq&EA;~eL%u=^&!$~s~_x6=74 z8FT5omumX(F?!X>6wSys-|;PHDhj&KtYZas`wuV#^sz&b2AStc^le4?Ps~qLd9%h zz^!I6E(x&zzPx2dFC*y$+1|wKtaC)* zzz7qcl>>Ls6~!r@I9FI8wYci(2E^QrLg?Py9dZKuU_g&VvI5d1B%r_U6c6E&ijeav zyk2nT>7wzzYp1Q9b+m(#6R?=)Y_l#&qrPp{M9c%&M#axRVJsEv`Syq{xj!r= zu)^y!GIBc@HiqT$!dp58Rt1hH)M)MTCee5F&ir%h`d*iCYLWLnKm+q7`^A$`jhVx^$GJ1c=F%urWNEJE z4$5$F-hZmSo*YXNdtpA5I91)7|UQm>Bmo98H`p%J?%@ z#7GpofDWg(r2@OMBBdMb<-F(bwT2jZXt9oO(TxAQKJqV4ar|2XDX1izr6^~Y!GO~9 zuJq$-QIcZ8byGIlA&@jM%_ja>Jb)z`qPn;7yk(~$q4Z4EVxEHp{=I9b2i$4KuE2-O zv(@UF&_!sgCHIdvhkI!l+U4vz`o^>id=&PFIlkG=u!Vs6ju1|!{$;VP_t`oqVY$_n z^L4##IK;yuA(XT&UItzEkM%sfo!#Bxg~Z;9Coy}Y54Q$#@|E=5|l_ntQ;>3tXC{r#F<5t2r=bE#m=US#l;M;^Dy8yW0~P|!EE%q zZ9Sxjr*o1eLT;B1*`EaAzCj&0l-JAlr{hG<+l9xOl5diuOHKbOuZ5(SkYsm`+P?lo zguD(`iLeVaofJ@$vI#DO32REUfJis5ZVq(#gX~!LZ2%ra7B0?;|3bh()ivs>Yz)u) z*>7J|J`bU>S@3{6oDWgJkv{Bff>>oJsCWfzK1vXp(@S^YUM-l*NQp%>n~K5AZUge0 zf#2x>5vqH7C>{M5I4e``W$^J!D|Mbw5XZtbg`?`6<0@h~QC;V6!-Q4|1nS%~Rgkq5 z)6Fillp7M!VJ41|E`ondc@OhVaUc2?o`PWj0RAFe{4dwT|1L7hQth+d;6UhlpaxqV zXy&JFvVgvXvZ*agXa<`f49^1fj4=P%ye6J6sVMvB#Yaf0IjL~V-A7#5()96oDub|x zg5K%uXjkuBJ5do7o3nBtluDAPZP03)T@t+1?fw}(SV$>lu>uJjrLyR*V_uU~&{oHy z`(9M0sdh9d5!8fRs(4P6r=&DSRPxjA*h%i@zV>4z#H4Vlu2pdd+a?LydY{DkSm5NEk*1G;~u9;Ugvr3!311IL3gpO82AoVi&s9%}6 z;qj+zV8QZZ*)={{NlZJyd;fU7u*9jhY9ts+!=KPfVCmmyPkGOP7AlqSPeOm-o?hSe zW7O#3-8@`cC+7C(!tQ@iT?p3MlSU@Gg??3Z!yo)wIq zqC1sd^$xA+sn`lh5Rgooqpj2XlzEg=y-jhpXzLnv!(b@U0Xo{?u$QN%kbln zTOFJBGK||ZgEt1~3d0-sOvztvftKB39MUN&PEqySoK?#M6EV2fkDax25(MB+LSOZm zV~qZT4m2`E%NM&f=@uH8lmh*+r3it+GxxLavqmK8Myrs$L-G_Vo`M|`NEpDK2@f>y zu@8zd_gT1LhE~^FrrE#lwO-&?Nd?cSx@-XES2g#n;4dnOW(qt(wEcBZJQrB7F2F(k zG5gKzWMl%O5B(O_HLHZMp;P5mj>D`mpriy>h6K1GTj)M2_vB)|9#M1)irX?*jRS3b z;@%kx8?1VnAxl;kt5%ZEq_K!&EG(dPB>JBrsg@^L)*87AY>z`L%c^M8v+TeFEPsNu zaQOvp#VV9d9EW~Oxk>F{AF-W_>7YfRHZg(1@dg{BZf{oD-*=25U%to2e`Wu0xaB_@ zj15TRq+>$WgL(`6HuCsS(!H1(>j%34JBdWl7TI)HaQyNyAVysP!g$ox%~Ij}v6S}H zp2paTTGzi98C6&s-33|>v~bfxJ5pKE%v|>x3x7Oiv#l>L%UU1QgE0^A z%;tbI`xi7aPq?)RNh5rlv_96~Ci`#?azVM_BtSU#_JcWTd(>wmK$ZthdqWbh5b1C= zK9%#YAmD+)@+8$N{rSC+Q1scpE@zOQMw13NgJOTuG&FP)6BTq>IDaAHuZn5H z5Noq&yqA4@X<0M-pjg+4qKaR!FCC)l!nho89SX|wa_~ z+{0=y)rgdMue2I5lM20E)@T7}Nb5ItCPhH24gp6Obl8V)CF9EM<=jl!+%^ftDvhs9 zn$!fItc!CpG|L3|Wh9IPl^u5QP6Vxpr3)oaDL+L{A|N6)lyp95nXU3l_Fl00(-CaEE;*hs(eRWWD!y z%U9}3Bo)7P#||a7>88mAPg_C@uxP(S*)VJZJs!Y3km&2aI$8lG>mAurMP|x{bG8xt z>0B#xL$r|`v>@J^`XL38wH}s~?GF(%g6}BqEHzX}b1;SvOXJz3#aU4pJc;h`I}c$+ zzqQ&j??(z^l#gLzA$$!v)MvEtLWTR3%h{s$=|=4++X4^m^1Nd?|7s4~R3RZoCTy7> z28Kp|Z)#|~F5=D&~4}hA&Sv8>m=77OOi8f0=H0urYMse#{!|S+yts)b{%fRh{ z$XuP&dingSfh+uS!uNjO8$Ua=*mU^IINefy(wpS&sIW*XA)q5x7;3Y@^Q7~J z2OJTie&;U7DDj5!^YwBmY6EF0Gj0Q7WLkATaoZ`=XeAtPo|3)21; zoO3?8?&Q)r@(0(Lqym`*XREI%EE2X&-q5}K@p8ht7%VZ^h@I?-$b5eB^nl=Lz%QpK z$+!Xk&ztrzesbTpwJ5+pbkx5TMA%yDIU3v8TN#?!|HI3Q@h>{+2+hvFy7#KEAJTk% z`vVoy{8dQ)KTiIihviH4b^Co*B(GzYoDY32QaJ;Y_z4Q)OZFy{y@6&du2JR6W}47I z5fCr6Zj?+SgicSa~E$iCV*JWveo0JYljKbjSSn9J^M8q>zofqZoed(s)*` zhhNWhZZvB&Q7yO>xFPb`Hd|k7zaHQ_(`lkxr`9h*Q&U6`+ovu?NofvyQ{X{?@u7oI zIg`R9gCmPel}nR0!do|r#DoaI8FdSCdjo$xV48GJKrk4{E^OU*T2y+Y`B{D5oOVn~ zE5;5I)o$oB!?M<~vRo>cb1LgVrb{RZ=k4ZcJ#h*o4S_P&*F*O+2sW8-`@^UBegKwBTk%Xfi z?}p?@FGW%=s2CN}KF*!Cf{XeosKzo^G9ZQE{R81*9}1tNoimrs7XdNI`s?cL>EUGQ zbLU90cu0{H;r<>_cN|RkE1W9>5-f9ng7|TiC@eM#-Im<|p*um4aA;^)sG_)BpEUwS zs@^5*yFUx&5y1pd`~ttoFC><(`hjo~6hf{se#&@W1Pw}6IK?){}NO<{n7_muhg$h8z*|q7${j>e1PUAH()l_vdet8)YaAD`cjaE0oK5 zXaiQ^&0R<8Pzs1%e!9`V;MoK8n2deQ5l^h=&Zb)oHDNHbo^QG=HcbBgpqF9HO_N}$ z%9~TW;G-n;nL6glg!P~}s}F{Q~?4Sg#{h@{buId_d;ZBzjz_+~H*Bq+dHLd@bc!7_{m z94u_T%a`PafZEz_~n+#>MqjsmQ>nOQu~Hlsl%c^H_UI3s+rj^8DRLfr-ql_M}+*e%t0-){-z73P5& zzzu%Iew(E`-JVnQ`E# zp_S&vs13rPVf7xdgbgPk{D_A+V3-G;EfAILdGBbrT|EUcFNI=6zzsX92vN*m@}r~e zNI9;^4uIyFPQ|Jr*;jReN!TF;Wu{47MHRT`z6LIc0GB?Q)3xt$Olnbw76^B5n}w ztry2ke6~6~Gowv)I_-qhd~KYVrgu<>t!D-{vDuElGj^q=^a%Ok?gH1J2Zad{&A5YU zN-f|&N*Ru{8gqACu1=h8TwaXoA{(uWpTqFuVw2`VlLK=c00AP_d6zuiIJjkApGe6# zEg?@>SQ4GiLTo)SW7WjLU-6)l;N@(bUZ>lwo$XW-me-|wz1_r%ZSAx)F3zhziWjQ}TXCh+LvT2p@~!bu zfkB9tsn1^_)1~0yPu*{!x5+Z+W7&Q91_UGZS8@2b_UKX6=f)T^MgZ5t7)u;C?P?XN zWo;W)PhRENFsgxy7ZFGT2>kwzFtP#TI0|$ zdGH_yo!{^0qWmStnP}8NwAg0GoreRhROlg15)Mck7r<{b4Z;$}4LPq>HH5jX1%6~Rn< zzcd?+*IbUN%*F1&shSehr>Ct%yd@)zGgla4n^E1Xf-QuW7@chjQ(os@LmT6}&Q&G8 zGmOm{tB*S9D3dVITRYP(ft;?0i`xIqKmehQL2Lwl*jxRdesbMXY`MmpOy(38(um{ryMoUEP5SZAI~34HkX z!8qC*5>)tjaEO-^!gwn-XQLbs^8RqkxE4yaeyrXEl`esSg%&v}9F2Xl0ftsRqp zg3XJcez8j*KmN%kyw@N`jlgVMe1f0W@n_1{pifxf1;tQHwdX498&V&&TCSH~ABymt zM33HmwRorM&I8gE4J7P8{4rJjp1QM0>|nUQ2%!YHo(SPIMN1V(wSNT4pJ>6$7_)=;I9%jn$Mpu$&ClUWhSHoz!Q&m+bTNN|A~&4o&&ClIH5mDlEG zC|{|!T7>)}R zb_%G7Y(I5M?NxSc`-zi4T~MLfxQx_p=zMJx0+K+taRAuZElO8fdAp;cHPA9pPdsK{ z#~Nqq&oO(#!ZjNzr-WUug^~e?c@T{@ff3TQu4aOO-C6V?Hs|$H1}9-_B{>iPYZky+ zk?eP6RDH|BkvMJUs@vZsa9FgyIHKj99e5Z+u$S~`UiI_KPC)@?QlfI|9d^3ks(1LH z{l5fKYWNrG?esD$tZMZ#GrsowvjOnKNAJ53M}#l0c9|4@LQqseCttlscEO6TH(br_TdFCO!CchWYr>F}LqPXfJ( zKl{i)05B+samd_cX9pObTTX)F9xD{7t;k@|+EitH=Pu%xh|DLmJFv&ueixoMx4k!L zf!UUj6^#PjkTZu8B(*ww&Juo`#8%d>s0!+DuBOW5k#J{Ph!3Af{eW{TLPU{GC~T zg3qXCFq31=nv!582xdrn!bP7!Hyg#TS&~X%g*@AR>TC`yb%(kD9315FK!g+@Ts>OVQh{JSosN1me7| zfS8PEi3cHt{Af*BDJ@#V9ZA^8B zbZuO-)#;4@pyuq*#9B)#q7l@t=YQ$+jl!1CbWBRX}HiQ&14aOV4?P(F(UDxdjixqPv6e~&Jv`U$GWp48eNpUxP_y>Z`qMiBx&<5n+ee`6vH4}4C!CMHh(;N zBtK(YMx4m&J}Bi9p$zg&zL{`)IVxn5ef4j>WbXB+A^=adJAx$A zJhJAah6E&4taOMd>FB6hAxc2%RO?Z{tXhSd-MR5zVg3q*t=B)HtC3DEos{AZra}X2 zYhBcGG+`=*L5)i#jhbFT&iHAsRpW*-i>D!{ z>1HYDqqB*#*PYZS^Urwy8|MEP%klpQ@BfwQaJyU_R{ow`<$L^%KKVZp3ID-%z>fZa z^CkFZJ2F*n{E&pmd2}}-e{klT`35!=_4HgnF0~Ws2O}4fkQIwCm4HGHs-P9;SIbO< z_2E0SDql{ZE&*vEPnQr>^!WK6Oe((XnFnf6Zr3p$6sh_M;j|kGg`*~iEX!* zs*}IV_pZe7O{DxK+4kSh`k%%b@}t)44F91Aa*+DQHgP-&OE?NDVpDKd#ncIESQ^bD zlw!TUPmM^=p)e5GCN)PnloHlgVs`4$I3F*M;=KbrUjqO1@_GYn!<^J5%=Gzc%E!bk zpKz?((qQv{oJk+O(Ai~YF<{UF2^qz(1vh;+=1ha|4>0x&s!#n%*WzV*VLD9AJVS)u zKRQDk=@MdVMi$VI%e>XT#2N~oX>pu@DS#Y$tqZ=t%kuzsb+$gV+0Q?hYOL!Fa@N_n zqc3oM1dwp6S4XQSs5|VO!ZmlZgm*Ezg<?1l`T$^-+0NOPv$;a#F(Ec%uT1lt_?Q;DvV%gb4xn`>e|^tbWq zDUNUx!zZIv4pbEW215(9n#Jadn*BjHQ&H{W#U_5eYwMPaBVeI6=?vf$zDj4K=Qa%R z#Uw4m!Juop&xNGmH89>5D{#hU!_6O?d&E}-K&RFvjVh24EGnh05kV5$VGGY~p_)Wa zS~e_O{#*X*98F+)=NkbuF_1lzLP0Ho(v*pENvMiZqAAuenk>B_51}w0{ZxsloHh`m z4n8qCWK}xFjJCt0F0d?cBo;~p##;&n}L@232%w#tZ$m!R5)h2O4 zxyYfaGu?D3Dn$HYoeCqh*r*nohCY)(xo{{+O6?m`@HJ}btUT2KoKp46*>FySZH)x(zhFZVe zm6dScdg%Cd25r5^%xd?7tfD@mKhl)Pcf&aK8jG1~LS%~jVCSfL=vI*=&@{{#x2Z>7 zt&LO1sGL^51Ky=6nrI$^b)jTTr!;b}YAE20g#Hux!p+H<3`)C3#Nqu>3RDsoRsNPy zLzqeLXpuzR@RQwja$AYt4i}G>4+7~Yo|OoEfL9eVsW@xw&_%`z|+}_?`2|P{PEAi$B2N0w-=zX^7!(VQ2n9k_sM8lx8R{n% z>)I6~No<1woHpUL4hE5%*ak3fY=Rsk8>$<9C{HX;zsA>#?I*!BO||YXOBPQ_Ud}Rdp8NG?uxmS0LdQDNN%}qeDL3SD4(~X zjw`&tFfVyXk2)v$mYt-ST-~k%E%H4Q*fVcMv!}Cok%buXIZoS_6~d9{+P&|p_RKlT zPPewU>I1Ji>{U7apL1UNbk&sIw@6!&Q||<`MEF@d(mJ;+y~Ui8v@3tU2y@s3UVB49 zj-&F}_e^PJS;h+TL_BET&@2Y3_HA@J#4Pg8I+H9`sBl^=8U|?GFk3_qpUYd>K3|Fb z9A!^XI7-O*ZtkpYXVR9n=d;$}9ll_eDV9I1g^ZH?l>D-4x!NRfeNe)vdm@`BQw|Qw zohn-$LF}Q5*)}=&$_H;i2-2|ByJd&qcXpS>05#Y^dT2j)=A|6B3|tK`Hk`}3*IJ)65X=sV?vpF7%=V9kF84hwK!octHv3va; zJ+|nUYx?T=H9oa2SJbhiu=?kA(0jH$FKg!L!U|#A*tMN2yL?je_S~wcG8Spa!kJUNPuCopz1P23_`eme6 z^=i=EcroO)S>S~PjcX3uPrC#0b+avbdIojET|xUSeyu>-$XT>qQ&fH` zd36R}Fu6Ls^(B7N7Geu*qLHBSK1ZPTgu(m#^r) z#sU{1e=?gw&c<=8!|pWYvoWP^eqNew=NehtT_4fvIzj%0sj$G_Q6kW9_2i>(T*}mv z3W|hxqdYcPO2$R-eKPfqnbz==pZmxj)@HKBS1cxU5>NY>qA$*)fF!uw!(zu}dW3JUEX%cx4V zBy=Mn%)3Kko2i2gFy@4CaS4>u8iprutPJjn+@jLO=E;1czIN?zY8KUcRLtsLt-M@C ztbTG-XO+E?qz++r%gUq^TRYXeiqINkxNIP<@ru61n8Ak6bcTjU>JqNQd`Y*Mp{-?- zbR1-zQFfKTqnL7G*8ty!aD({Af7Hgx)eH~)8dN*v*S}zdI3+_%%WxYTq6Lm3WF;#G zp%F~aEw?}Ut;`eRU8-(F#hU#)LZ4zDTZN_uA)E(~f8 z7&~dt%uW#2govZ({FdFG><%6N)%)j}RSr1DJ3GEV6VhRl9UxTO1^Gl{c3I`Nn(s<( zoS0fc`KC-k(L=KOVQ-(=EnsT&2yF)I;k`MP&P_l4w@#x{ zby2GtmxpQ=CfTO86xD7g60EFE2q!P?=rHN4$kgJ55&c|yhO`h-EK*Ni??c9DExoha zvMq^ns8nfCoVl44^(?^j(a5-9e6fIw85`Z+S}0|K{;_%XtTP+!YE=65nx4+sh|--M z^)t&P>y-hOk}MYA6ra)ateFE$P*7k}I;EYz-F9eRv@w*%yU{%yWrnyAR=MNid>g03 zg67J$z_L@iD!C~`D1fOO6K8FYvIP1YPh-VZGbV3OWP;Cne&IPUo#tFoig9r(%VCfJ z<@842j`|V)fcx_N3HOYg-o-QGQsKdV<&L(`Otfiz!IA$R@qzjnRA9v zR|7{0>vJjX^zQU>S59CAU6_RUJu@_ylp<)A`W!%vhT8YD)0hsrT&Z}`uDv2dcNF&y zMvJVj$QEBoz90@KDVLvyZp@1$Cqg!E6|zQGy` zzNs(Y)enZt0)8|?L34ZPXCl5JT#jN|C!!XS$UathBp}L{p6%ICl_;embLaC zvkw)sX*>Jn*1plrWBR>@Vnt6v=U;C}e~?a+9qg+Y(K-Hbub$-O3+wHp68Tpt54lh zQfsMHEM-GBV8dY_xFc15=mxN!aYJh$5KbcOQ@CoO3Q2YIfj@eR*_*wu@ahvp|Hh}^ z5GARXr1Rx$FyQV4B{&69pzKZ^Fn`(B9{YpO_u0F|2v0|MZX#yhM`{0P%DfWQl>ccQ zA9i5L?2ZAOS1I+|X@*83L)fcgd)A)$OhZ(O9#3D+2OeSqik}_W$6t5`&{``Am=oFz zD@WREB-)$9?epyLzH|S0cslDc=E{=0rQ6}_9K8L{CYMhjdjmHkC!W`u5Ad@?CImFH z^KiH2!jDgxFL6i0c0u8yTl@U=PGrl+W-z@ky_Uv5kR4!F+POYEkR8>QB3uu{OXsDQ zjU~*{Fg+!jU?SdqxwUgV;&tzTytCs@+%Y`0q4)8|H)x_{53hgNV5x|$+117==!7Gr z*}4~=Hnna-f0@>1Vb^=_Aqjj(n!u+F0U;rip{9vHd?n|tNy_*(rqfX{I|>sO?6(?zR`l8^Tn-vJ50#?|Ff z;xaklx}(Hl7WAd4O1H+00YM&WcKXSzicRxldGI_B=c_vSV>V?6sO#B1ux}htiR3&m z?>kFDW+ZEXq&k^18Q^sy5gQs69gBziwf1r}$P|M2GfR=^Tl>)0zqDJ=%KE1=erCsZ}Q;Zl#Ts+(DA>Byr_9Gn{-|bupN)WPvjxU^xhCW2wgN_V{iw)6x<2@rJm}T z@}b)AMx3epv)8RxVJ7B9Qt*)@Ct#br4t!p1^2vxC3^G!&3b;o#-<@@xbN1*T%1e_( zKo-hMY@`XUED-5ensVn9iK+%h8O5DR{43M={lApNa?}QOua86vrK?r%zqNz}w1w60=P)apAo8EtICZ|4Ctu&l=A_+xlQDbqLF7Y9_^i@akP zAU3V=<=X@KQU<%eWc!^l>x#fAA~(nab)et4Ho>&;UAAFdZrK9*=Z60H)MYE+qszGX zDglWz_F!YO9F`UiFRp>3AKPnWL&EHmpvRlAZxRSV)CtYfpDmsC3LX;;o=z|!J|J8y z;r8Qil3+zDXI7Gk8#U9398v6P`X7C?bC zDZ``OlA;oUp2S%s0O|HAOm<%VNXp|9C>z}C_1iS9w3Yz|RM}_NzNLnSa=1Cuc}f2V2?jkaDm6I^ zD?v*&H8EW;&#=I>V=pr)K`T!4oj58l`oC-wn1KI(+a~;X0_-mYx}%$|k%^O@z2SGg z&;KA!n<5!`E$tuF{5J{qm%{kJ&-`CRm{jx{1IpGricE!Z0m@T7(}3?{tY9?e7$Zq= z3zVH<`dq6j{Y=E;YC&eIcwB6D_wygjRWtCHSLxnpi4&)Muww0;dv8xQnycscjYxJz zw)K+M+T=kHyNL29ouYov3t@g`W6%1xIX74HEWf8Sa?6Wr9RZU!YQn-gmCFoP0fdX+ zO2up~EABUCQahH3Kn}r0<(ERFJFpA=*pXwLnhfMR=X~(!CAoZwz7M9E><&-QYg8c3 zvRo!uTEzGbMXg$kI~Mp4Q*A16S^OhvELIa)`J&}#cn=8BFCIcyYc5Aey;R4Q)<7cu zIYr%#z5Uzg+6JKTK{a^!?Y!;|DFiQ)JLc+MaNSe`MEjQu7R#hFXS`l*Zh~zLk=$g2 z!+ol@W!Jy~!rMR2+uK7SsSGFQIau`tE7`fRoqt=>ZY_kM1A_u}r-K ztAENM>u!tpmO()Qda4*_6hiL4Qmq&N-0neozT@W~x{Zk`sPiRYm$Op_t;7Ac#KSv{ zH_^mHR&_`PpJIuZrznl}Y@9A3IMn_vG$_j>w3cyf;YVdTY|jCnwGXpGjhC}FM-w6M z7;`1`&`dR!zmr}WXcboo6Cx2jb;4*sBCc1WB>YO}_xtK3c(4*kO)yaE3Qhw}q(tOa zLrnXCaHwpgu*blkGH5^vNd>+`A5ylrTy{hqMHK&RAc`n{M|%p7CfEGsfd~Tf*lY09 zjgT@Q(_TsMmH4~5`(OUH?h4vN%f1_;g1%!z{=#?veXai|UskDdX}!*h@~67EW!!g? zj;PeEzS1GRNi7W)aE{Kj?hhtddXi}btdKd0kqCVg`bxxQ?C)+QUi*LZuXl?EKHlBJk3;2c}Km! z!^6>=H)bTU+s-r#mF(B{lEvI6194_c4_qE1UXN{*%+%(wgQr(FPHt{H_gtzBwOEmD z5+vkoje2+Y2c_(d9?-R09-60Xz=$lW8oxLXTqREAcs}@ToD^(@6w=B1@fHusKZUamQm9Lp8pKzL#d$P?s z1JYGk5tis_j}P-7^m(ivNHSUsO47K{CyFJN=hBjLES?oB98kg=V+DH3Sb9;K3ZSpb7o##DCXzB>0G6YJ70eOQFV z)pSzX?2S#K!uKUt3sw)HbH9me4+~NI8TT9;sjLtvyzA0Bh5*iETD{x4FQeq)5(EADwPP!v-xmD@M{rACw6& zYpD~cwZZa-Nt5UWaoqs_>(W9z-EC_iVqohytNT3Unnc^+y@Y4)&I7%ti2-Ol6PrWK z+LjIJ$+s8UFRklq^rt7~OX-tLkXpGuY3Tt|uB`_Rp(0}|XLsJd%J75nplz$Bajl6! z9jVUutyFx)@q`$CIx~_PqOQ#JxK!^}v6y4zkx5eGs;%|UtV=r8WJ961oSDtgm~!0=cfHKhFs6MBLf?tE?)hzg~)w}%fyo*zb`%@`h_q!_q}oDbsnV~JK?qj*mUg$?#<;Ev&m@by>w8CqOl zvMPZl?wUdcF}P3VLYD}`lh#A+`ONtfNb5@ak003Yh|gb|K?XXD+M3$JaPq)NN5U`U z=~1&E0F$kK!M0ra%nTUDqmnV}rTVTdMDvhzlDGv|1>x} zIO#yVekIR6aNHJ(>8PM0RG)g7Sjud13(hKFM}wd0d1G`9=hl9B%$D1l6=nqx0d1r` zMOP`scnn=~C39~{U;BDR_LIcwt(~jGf9hCg`CiEd*+=@)bvq0`3M@$fH8I4866;>@`;em#`B#gmzi3t%3j{`}F zf%DlyG2NylPWH>!YQ8ZXRd3?@yZS(U%M{w}d_-qse6+wJ@yJC(h9Qrzw7o2GjNQy= zwRNp|KNr!{zO-@jh?o!^P${?>P86?@tv-V<~MNrcf5ANXdm_gjrmmw zUxiJSSkC(j++@Osi=~G4#RX}wSMyQ9*xgb2i}lSG(J68mR2TPVRaJ`^t5X zccw;5O39erdjmt<8ys|a+l3ZLLSB{eWBq5@4e!SiQKwWUdz>CO0eWVgZICq&L<;>hq=q76YqI}TU)CiBvS8(<2P)gkUfhTS5X?3a~HEYzDL|Hlv zp_>Dg5clnSgPk_|hL|_bU{L9V1=XQ!7~?Vwd~c|+Gh|4bK9VcZa$x3iiq8bIU>aXF`ux0Ai}drC}v_5+D- zJ!5Vnqg#|Yzgf%2==5^7FH3J)(k$dF-OrH?!21{rQk7vQ{Tq8k9zqQ@E_X5@A&2V(fyZEPQE5A&mUek+^&q{cq2%TtsvFHMe5ODsxFlppNtM z>wh%ZdgcD@;YQI9PHLiuBfxcB zW+IU<1r{e4FJU#1u31q}kt%e~kfawTl1kc-;WHjm7zrhsF?gtXXpc&!!6A`r#7rAO z0U9BcRAne$cF=^N%^$3zE)3^|MM_#gpRZ{>cM%ad9eKV3UYDP2e9jCVmabWligD9D zGoZqu3EAu_rYPw81_3MW4*V5s6Himel!$Jo|0~!zH5;mB^cXzp1H%Yj4k^eUn1!!o z$5%Co<6&$ue~bWTIpfmrIwx^tUZ@t5snDtozWHwN8B(!L|~~155;!a8SSu0%}c>2%^}LDg$ONS+Th=jkokN7u@orxL=dng}Mixc_CPI9w? zwfz0^`lhVh-8Du8h4|(rjG;6gk}jykMxr(X{3~l$=aq%(ODfAzrV;J<3H%)g&9Lxd z4ZR&?1Zk5=9Q6f%waU9INbh6ITSZPA$toQ%4)GE^Pl(f1{M}n6$+-7d+Th|nt5rPX zX^>Z!$XbngLQ|2WP`J$H)qTNZId|0I=wK=0W|6Vdr~IwuJaPQF4OXQ~XbfRx257BE z!)CYQM|5uIN@1m&-_xE&!}XIXeRNUh4hoKR+)LKjWfo})CAjIJQyN-I7}b{?MBwR` zWx~DU;b=NgDRBW-1W==#4Mn4Y{CRjthyEQnQvR4Kc3OyT;OMqEy&bc_k*sKs5t4|G z;$tGIfzpVTI;$kA17sY{(MV8AVG-J>lpra_S)h(6LntGMMqUN6ImHjLgwz5YdX@xk z4=U2ofIR*#FrZyF(8Z%gX7RHOnZ6Y8WqY;7UzfoYHdJKT+Nt@-zv;eg@iRhT0z;Fg zV1(?X%>{nPAfXsL^xzfn>Ozc5qSX|3w3Igvs>}9LImmNXPw9xREG6) z1X-mCr~T85Ad^q$DL9~u?fZjQ8{X^m!QC3cH2^y-oe-e%YZO|OV^<`9dVhU)2h`AG zp*pzRDP_hnqqshPCx&k1VIfkJ+H?RKVSg&}UIh6Iy#}Q}>9)Zj27Axt3I54LqD-f6 zk+X;cY+A_TEo5N^@TbJ?2gNSct70ujZdmb}-H>ZuaLLZrG?m1(JC5BQ)rks>=%iyI zmWgbIsbQZM;LtopwC-FT!#uigo^SXfP9vgVC1!bNGBR*e#-tiS;3#@xm)@*A-$`y| zJNiJM;fxCf-9hHE19+%kOcnmjpCERRL77mxz9-P=$kDt~Dpn9JZLrwA z0^nn3=}N4h9-}|qBV9>m>R~iqWV0M62uh|(VOlE*+xaM69oZ0ta}G>EPnA6I_*Dbo zn#DMOA-Nc~F|3tQ@K0qCy zA^(^XC_=fdWsfacO|c#nKqC|S6E|0*`o}+s3YmZ!DFuUHdbwOR}z<{IHRs&s#dl&C(LYnSa2IO&EisXO5X=Uqn_aQd&xp*TpVmT zeT6hfc5KRLuvZYbujE()FUW zlF)%r?Yb;ff(=l|G-}vDY+fe!G?8~uyh_nF=W6GC^hm3AK&PO%fNVmom52wO_bzO) z0l5#Gao1oY?$LBj6xE^)T}&7r*1BTP*$-)W(AVCM$E{urEpL%Fu12GOiUW$PNUO;V zY_W+`w>;7k8>zurv>rNhT#j8ecYj@rSwk96Jy&hF4@Nm0Zn`~;Brk8d0%(Ek12SN6 ze6E~JUE%0NlYN?o;zYCUYnV(>Ya_RHv+nSE-(6ja3hcsGdHI4Rt3}#ycTY#=#JD(c z!R_ozT<-5I&63=W1!+w!W>OFLmuc3}==bUd2EOtUS1rwAKXmY{Ld4=kZe)pk?cnZ6 z?R*;ev$+zzPjx!%*WF(S-!vgZfPX@gxfnbR)?91(1kWV}LJxO=+uU!~tBxQEdv8B> z#@OD7Bzx|)_gh4sintJTTAV)o@~_gVMCkMLGwHDE3S!z)QMwcBTNnIMT~XLDXQIp{ zD=YgOie#N-5YHbaCXWGaUNR`u?Le?hJ_HKh(932V@CYM_r5YR$C7qrS>Jd``72Y*S zw7O$v0;!{q{~~ENEg1%1kul%juQgluZm(A8bw`JiAhJMG!MLpIiTOfN?7V{{n<=Y} zYUouHqExps?bsvrV%(ePhO!?8TGy=SLUo8M{@O&o! zXXN+SZ2pEoj6OPd)8<1u!$Csg9cTYdo*-~Jg(_9p5+c#z{mIyfMQnc+OLY&t@!lUq z1bv-_>|mlb4b$pgq#lc*V4DEs7UasKXEt9bKRL?)s9#`)4G4s%CY-$Xg)susOA^&ThE)Slnu-*Ulp~2!I~oI%QHZi_JoK^iPl@Hl zD%(Pw&%*>TLKkHC%W})vaqi%9O!(UHapv)H-lfMzuh80B-dXfUyFxj`G#}qccY(VO zK>Lc|LAwfK--w>hyd+#Nrgh{se{yn(H4*PW5Kd~e($To?+n8*_+t^cm*t{Bes8#3( zZYB-o7O*l|(gP`YTW`c!fZNfl(>BWF?gq^5Jh=)Ez2`e^eQDWZ+N^ejjy$;E`uK2L z**It_r|<909JxX}j-s8s?e-VqoOas1nqd-JzhMf5xO;5uqQ=n8z^b`#U}du7c_{^H z(7!dyrm&+0{egpaG%R(6G{mjtC)Wf z8p%!MbTciKzImdt!R0~k1dnn4AW2_jx?Nmk!j+iDrqvd7YfuXD&9xUJV9Wc%sbudo zw}jVo=;SCHJU(uwEeHx09AFbSWY@N9S>Wx5KBuIJLdFs1liaZH`j&rjae|jhT23~2 zG%eU*D#Wa~sZ@(uSw_LqAHov>4|zJ-iF-;WHGE1_kx(;7$}J~E-MCZ~#u)Y~1-qX1 zGaa1-p!YXQgIVfm0zG8r)(&kO>F;P%nxYK@9nFr*ESGJ}N37&JxTAvr$ztQs*9Jb( z+_&+h2&~NAl|^Zs8ENT{cKpIp*>N{Z3tUUj&!$SDV;AjTi(7wu)Pxp~Of9E`ZbtRX zQbVdu!&0X4XAv%;-s;hn8uPWA8^+K+iW-$@A7!-l7Ms4NtLnRo==U-TjvNbQ1uqjU zEFA1BH<6@dKNd7ygj)ZM%@fJ6g+hK&GAV+SuV<=1!jP6MHjH)_N#&RjGx=^LxFu9FJ;pK? z9ZOkl%+?~DbisQ$W}%*HcP^{7h_!cMBq_`t9Voz7no;m`E|=k*`r-iP_R4dasDdH8 z2uY#5xZief`R(!$OIhvKq3Md@?6fnF8So2WZR++^n1-HV*rmsHLx%ep*v@d>; zuIZzMJElRQ?Pu*0i}B`;*{;^H6oQhTVTDC`pvxIrjy`qbRA4{v90CfKF`eu;sKEaT zmZvdqlwCYB&caB-;3pOU76!O0 z!Ovg-ywsIv8ntguq89MOX6D(mnavw9@ki)f^B(eyF+-s3HZOC$`{g_wksh0NM{t~% z$pymc2tL0J#h=q9qaKLEOg~$9EFhhPnc5f)3Al}PuInM(hYhUNUJu@me#oC2^BC*5 z++K6GjA%J{i~=_pEosEJc)MQ{CE2VFon+p&3Y#gzt(VQM2B8IqD~2uf=^@z&z+^`!yQaZPN}I~XuaB0G=X zi~P5GGH)ZPJ)U2OzUWRp*Y2+xds?x&$k$JPt~RF&4-_)`8X`@kWf(rKkEWolo~$E- z)c%^@EF*)<0h)M-2YZJEU;pBX!U?HM+5gS%k9}L0@O<+_|E_%F|IXX|m&U8SZoMvy z;I&OI|F4VJ8Wm!99^j{trMB2Z6~rPd_dRUUNFJaxIJTY7V{v%DR8F$ z=FiIu*dITV%49OVnz;&(Q4r4u=goKA$U?jl6TGK+#O^PPzBb|0zwR5n?N$r<#))+$ z@~$s`)cXI?_Ks1ub=#I;7$jgURK_$`&D~cE$hen zJKNfA&pFo^eRMcILTrs0$*ZBs`7{5DvVn5q6h$%uQ+4*Y%agvLuZJCS7OV7Eoo%KY zkX56rRP+&29|FT@k??H;Iwzwbg>t$ilRur7h_{pCC1LmJ$?lRBs(o#SJit3od3+{~ z!u=B%hu#8sq+m+q=Ava`oEry5Tls9f6^b`LDA0#d)a#MD%JXrr+8Xl(#~yD&`+mbu zRyq(tc!SX~Rikk-MrZNapFxm6aAZ3`@}*-D>N8X23S!$;OcL&Tf>aEpGq5v*slW=Y zhOdzB6eT_xTqj8c%XHeAW$r}e7K&ohq{pEzK$wUtLi7R#M(?7M7)i*`nPRds%Eg%I zvc&A3z<1cuiRLf{joj)YM;S@V5399ERLxau%9-muBcvrIvztSnQAwWYQ_Y>ZqOK+p zdb5=z!eUcZ&nbo$M6-&OvcRLaC4h(#3_8z{izHK?!IgTP(F2>{5an7n*R0w;#D*fR za-Aj$dzE&(Ifc=m++GarD!aPLS051!k8ZkzH^H&Rp^jrR zCPeN<9(&qZY@7lwxy7t(0FG0p;#4T_Ism6=0PfRo8ljYsl>3H`MZMnb|;H@1Pbw>CNcd2@M8BnowB>UuV%ZTdz7VFF365PbA zm)gmf3OuttDX<5Et@GY)-!Gb~;l~#zC^>jOqfKsFjZIv_AtfJMI9f`l#GyiXRf@hz z;7J?&Vz3MXw=h9Cee+&|Wfi%EC@(%2lpUQqP*E+u4>=^NY!LvF( z%%k!^VbK_~_ZG4v1jX+ac@o_z8@0*=*V$f4z9W-Z0UJ8ri zWAi^d24i2Ia_sXVHDaBjg9dly4imHUpJLP`{_Z;*x}y=H)HICGuJkrp__7ur_P=wX z{^-}0YbqZ@xKD_Go5Vj6=4_HVy#9Ua8AG1q92ZleOky{J`jcAdzob?B0wnyY_6pe9 zHNz`GWFfkHFZe2gS0vgxghgK4~okbNqO}j@G{= ztxMeswlUMcM6F<5lnIxP0&+z;Ku3jg>2+u832mV{$0LBk5_;1RN#JP4D=(Uc-^a3F z`knu~Mn|DW0t2i-`0dC*pxDW|A^s`pNR!da)G-YfMVDBUG8FAtHM!W9S0^*;UhLGV zNqa~Et%xi|-6Gjas;+8n<>3#9&=v2$!rJb?WP7Cme`1z@m|p)7J@j9Iwf}>?8O!Au zz=s_CK5>xkM~A8&hD=Un0PhZIf@Ly@0@(tu^Q)VOSw)=Rd)jL|)v>?kgb;Dl3|;2D zN=yxV&Qw}G@t`72NLY2-VF>De&}hVd<9$6F9*(r%nW2?fd6MiRP1bq-5xJiaR&{ek zK+ESrdR{K3u4DanIMg`Pz^$;8>h=7tp#q#$C|X$-B8phz{0n;Thqrn8XY)?9}%z9G!RV9=LS4OuVu#Ql06II}o*h;0C;}?$wB1$|NIgAuZ)HMBgYBke+wb;9|D(L&5Vt$ zXy|E~S!fylZHm*snT=p}W9>)(>`}#^Px(JEwEkh)|CopTx7m`mg64lSt@e4YW*<|e z%C$P*XO?#Cp;&^z-)A1r2PSnRWbd2G`+CRWI&G5{sPm%Tp0sKS>TB?DNQ+s>(lvF& z=H_mQzInKvM>4Wt^a$FB^6{hFgrBnQboeTmXt|Kp<(I~3aJj4J(CWy^8Id$rbg4S+N`MOv>(%4H>(l0RAp1O4qEbSBG1;MH~dq-rarHc?cG~2KR=Was@F| z{#C~W?s0s(3uEHj0pF0| zS+c}8{@d%=I0@ig9%_kM4ziEUxff%}EfsY=B6+%|ud}Z2JHxg`NKTy}zeCV|38V%F z$vjH6Z4Wg0J*c9U8u+LUkuSGM;Lb@?4)9TBLAgJMM{p%B+0vids$Zg2NTmYMvloh) zgC^`G(nh`$;tjv@taE3&HK>DSAncDJn{&;$ zIyC{_AYEbyWng3jWnqtH0BtaIkiGLTRuVj=8xoLu_^(~TAHl9L{07x342clTcLzjN z(0h~qwFaXI)PP0b-FCm^9oD~|aoc!NLWrN;4f#Vu`G<+`KkV@TEKU=ZcNO+o;lHYj zSGKaJ=+$8D9yFfyXyfgE7ugw!ooox$vetUk9Y{C?=vL6ppq-Dg2K6|TL&vDCUC&=N zZ;vgb2O;wl`{vw_r9nr%LcUW|D#fBBU=FQ)tEa!Y4J(5tIsjNmn4K zEF}^Sz@;$f)`WaUO)hbsdY1LmDx|VtEg|n9QU}jrL+Od9Rqpm7oCvX|NU|F^|~(S28jpy`@XUMa-S^lnLjL~dV!;Tu-oErhD=)pBO^QW zCVLg#`$`F=q{+xgnhz;XT~U71zvt9@&r6SprbCq~II3;{#zHd@8Dbt^;F4f`2N^Mi zo$8#Mv9O_bp(V6EVgr!`+Ol-!0A2q#hIu<0SAF8x{#U~-;P{L6$(?|;Si=jwyV4EC znZ%))*SIHBXOO-!M|&>t>Fb-rQ%xs$Zo;xmg7HBoqKWn}>=1C<)aUoi%Voijbmpj% z-J)L;p}Paap4pA-;N&u9KXZ0yEEKwoGzaeBryhfQ0q_C6OGL7B0lVItYCi^u&aDb) zTx-smzs4tz=VgE!-Mmx=%MWlMK&ynyg~+{APit}97bpe+ z7KwOCc!_9+T$=RKW#%B>kV)_zUpEkh<}V`td1pZRsV9j%66M7-i2~B6NJ~h)LgqI? zcwFKt2qJ!rrW7ryozL6Q!Hj-m{Hdq6TRsW+%q15rca=u-VI4D+w1`^ZoOGi=+8%>m z`%WEdzJ~qqgmg$&T3e`2HMxV#zsvgB{esAq+k4N;p$b5YyEO%?A^1zNanJ%we@nT; zO*8sCstMMeo!SqTHMyE?G9@+&P*ixnB*BnV8J~h*Dim91^sM-jF1{ZGRJGx9pNP>G z7tiO%^2a3h^D}#@))q5cj|=(~<{0i-O%e02R|q=g1TKL2vnKkE`u*^50)8dHn@J|Q z>cJP1Wo7v%;D=gDj}GXB25brTGJ3pv?bB0PSlAj`-UL@RdO;M?mmTc%%ndr*0az>! zl{_^?vB+CnAGIsKaj)jr`o~I*kg~=hW{3O8bhv+O+*hS?H_Vo9_P`GiN=x)77;e<(M*1%xtdr~eaa`( zC#DUKHe7(^2)YuLiLM1#SQie@2OQVm%`*SM+B5gr1Ho(7%ZM zbf9~mrV4U}I#pOVmKaWVa$@RA+4jncw&{wx7Gctn82SSJ*L&q5kBPM6$Hkc#=^xE3 z{^4Hv4|AOq@$|eE#E|DtP5O9uwFe-04CGYhjT->QXvUG7^J-=7A{FPt)I@Jr9*4b1 z{JjCR=4Yj~BTtY@@ zlfaeQN&Y!Z4U!|hb-3h2RKOF@g~$;H%|Or7p{t=pV}zquK2zn!KEoTY2GKebpfLod zZaFbFNXx_oDAp+!fB?R|-whL~4{@xgSj}qF#FX%$u}%5Rll~{^T*9(aa2sWJu(?ZU z!VQKzlnR)B2oUbk<&G73bbvdb$7zEYq|F^1B7aGW7bz&{L)z`6YFFs!|H63C%+v#| zk2N;%-icW~P(cA~=U;(Ft#ACXvaVf1he#{7We8tp>0K%;lc(p@GK)w0Rk!_$*~;vO zfWP}3!@tTBuc#`%+87OLCWU3tA%(IRkqo6IsG>(Rg~)WhD}_siTb4!)P51E^3=rZ& zeob{)e5}ARBiX}a+>L4M@X#q|wacFM9sjO^eu&V@;(7Qeh$u-Z4w+r+#Tra+0jt<9 z{*G*lHNLWrO?^wXZUju&1c~7y${9WD`8kZI&yumx9XNmnmXF~AMoG5oeAs_~Bk=yb zG`!rwpKoT--?w~^F5Fk>rgU(tIf!!`&uc0NrmgF$jM6rBoErivv1b$&5O-*ou>SRMJWOMBke;pgU;!x6?9bTpwFL{ zLIweeB&!G*$Z-JZ-Gz0T59yHW1<)oFGdko6e)ym4>bk$Mz-A&1Po(R#9 zfyRt{F*84r=tLK1F>BBI2IMhYjt)V}7SthNvb2XB`q8i&4;LQ6iWjXOF=eW}vOdeS z>u(WUr<{PXiqvl&>TaKuxU{1(nBbrnR#gXkonE7JR4WQPSxa2OYU6|?nHcx%hT=eF=k`X4sC@YF?)cum z(_9p2A%jDkY^na@@s3;X{Xo@`%qwFrLdD~E9jFMf@lx@P00}+4;B(Q$8|qC&2#OqK zB8?iUqy&Ds*zQtY`)T?bIxMr{VF2-|K0_nrxc$vE@8JL&?ikF7)S*@jGOM@8$lJKg zCCu)g#clpu1MRDOnT;MIN5)Wm>>?YiYw6q@@`&L}LTQ%6xKGsaOFQ%}M#s{a9N^rP z&GERl5?Zy$CHH{Xgf_X}%TZi|oA5S!kb&01xw2RHl-qiny9t*wh3oEfZphr^SV4(G zy78dfbv}?|ii;q{+l_N*B3DG$aA~KLXPUDcwi=FH^BV@KoV#_!P0Y+cle2YHH96$1 zB_BPS;;s7LI=` zv~{6Mw$jZj@v7JHf*>9Y!6R;6Tu#BP$2K%k-m2I zM(Uv3z$0j*gc>vY@_-LsH{!oP1W4Q2Ad{8@*^X+`HEM9)A=AIZ%X8y==c)N5jn!rC zk{Mww9ph!}w*K(+9MY*JO(gLl7Ql)QEkI@;*A>_jdT|^g(`+Y;XwkCA$N+?h*q7aT zu4>s=&ij3uB8ZKSRlv%%u40&*B2z~k7U;3k%%E|I;jiHx281jUH+^GE<2XDmCz*zV zZMjoOn>jDwk95I(2i#2h44Q4rd2rD4614)x3a~$Ibh{)A*SDBzCWp@9#i|{?l8OnA zqg585pl+;5nxdJGQCw+djkXN=i|_uV=TLbTI;d)--X#l1 zs&TSEOowS0ARQCgPY%SRue*7#zh`oF3JbQjE-xI#cL2khp{A&|%t<5QcyJ+%V3)m) zf@3)xL#p?B-`3m1l&dH=3?50cNr0Y3c-n-(EZ+^aWwV!M-T*`K!vKG}UPAY}Xawm= zR@%$eE{u|NWifKCpEcCdXf!l1FO9jMEN3MFIYg27CSavA)XunfiT zLjkEVXDPKxr;IlE5E1+3fIV4*F7aW(>?-1A9$Hb-v?1ySLqLWGz~75=&!0|pZEK>Q2hu;O%%W+YMVj5Qgvh)j508L`@^?DY;8J+4xJP!bH%Mx zci`7P(OxZtm{!Zyj^=j#wf*?(I+aV@u}S+wBI=OxzbQFuLE}9}IZ{;jJ~$E{&lC| zD-_4?bu)qyBUBNyqNV+R;gJ4Ai|1i(XGgd(v+8sPN#?OdRL8bupnvZd_Dp(t(Bl>oGRi#r0+%PMPQA5+I9^w(oKw+;qF>) zX_y}j-uZx_<+CO8^a#FJik@wPbc%1>;WB&k;9LjE3-#~{F3$u4`k+{{aoWeQF#C8+ z+%>s~C4HgYIvd?{fO3ZDvS+#DKTm%(7*-RVJKxX)_6qL|R(|BG=!%E1wEpdfawobC-%mYLF z0ND^#BI`I{l4D{hDxe>Q=VQ)j&ykn~*{vp@bPa>)S z>ZIA_6)bFO4(h4>2dhX~fPs~gK64Ue(ZUgqM@*uapQU=Pa?|3k)6r9@dgxgq+%Q1E6FKIYpQ-(jBRKgHm<56Uq6{-}?aw%+K(B^xOqaW9F z%DbE40L*^JDJSS+tI;4Ol5*+|H495%duUsHkcZ60(m`*L7QY+3l}yznWsLEif+_ZL zg`){h)09l`HZ*SI#9Pc{wqv`zgd|Dk!EL;0vd=qUBxoHojie6Bod6Z#$ajY~RT$Dz z2ig4V#phkBS&-!m%Sjd{{dV+zc}7C;UJ9ntqcLlCY{}-|STdNwEX@}`zP`DvtS2=g z*R@71wfDEH#pz6{Atci?`>UCY%pp=m=j6p3E9env_h(a~9>Cpzj$tvxH$cw1+$vWR z6*cK*yG>3B*m1Mc?NoN*qd)doxI^c~D$)1G z7)4L9REz8sA2HZ5fbOqn#%C6d3Jr8FsmS62zE%bi5S6)aGFM1wHs`o=S#$HT{7r$MzZ zV%EAAwc@$UCnSf*q9WxA6_%IsI7>F3>o$!EjeSE<3GBb55{sTK~(N zmo+8b5a$sK)yjyV!-`@qfiP*NQu8hJY3)0#Ygv?D@ccG^a!|ZGV?4Rj{45%5E!(Wi!9qg%{_A=7l9cHIqq{BfXJjcJc8rwEp(s$-`l7m;LHj3dET@ zT&J~QgT5GR=lg|FrM1(A_EVONm=#nnb&4LUZR_sWjq?ZFY&>pz3Cw85s$N$ z=55T{VxQ{M{nV1qVYT(XuI=VvcRlm7)lX~eQsG@|K~qny^}D9u{iB^XrexIWL+6b4 zrB>b75#tOl%u~FIu)wK#1Fh* z)`Xq!_7{P}{?{qBlG1un=qbm9b4W;>eqKGmC2_=hYG@BgbCRV0@ZuzzPnV!8hO9PI zuf^0n$Ai{<5zzhvg&M=aZdA@u{Td(&pmkYDoKxd0f zV{5y~@0gODT&`N{ehwX1={kWVEpP{tVKQn~n*hLeaF}#cv`oeD$~_OD68PUbUg9M= zzXxYVFk|KsE*}EqwO<(wz3aYsb$Y&j?1k$R_G0_}iOfs4@dkVW{}nqrVYo`h{3$Kq zesJl3DEI#T?foCJ$l39_G`^n*Fz>QE(w$vG@^ET+QCdm|6k|aGSTqFF1b$dp*NBKmw$-$U8LPGTzNwPiWx7vA=R&F;r(V4y5OoJ?)~Qi#4M&y7e(gMK z4@G!l{)H&?)=)3NBujuMzB_1X%l;&b!&qXp0Unos<6_atQlB# zttSk05<>RPVr=cY3vG?^q22BN(V~f5Jg45oZPDHr8(M5+0kuAZksp*%$V&{b4o`aH zp4CJ~Ne&R2g>VHnWaCU{h~SjL;gE-{)P~ap1NBgE#o(qaYpZ6l-cG+&DpK>AaOfuA zAhD0@C-!hhGhg`i zd9)m?<{7NvzrJaaTk`0M;5n~|n(3XnmwGyE+Xeiu+w7+<`0tI%|59E2kA2GjD4+WW z)0Tg~asMkb(*KRpIRRPck{@gq;HN_RC;#(*|DOM%S}XtQP}z%Yv62 z0080e&-1hV|6O^}uyOoPZpi=Fah>QCt)}4DSQ(d(WUv==eSSh^RR4|i;Iq^IWSAfa{Gx3}b>6uvJLx1R|-PHx6D@8l_ zZq=%zJf}* zh*)EV$|p6U_7(eIn8w_Wg~y*BKrNHpZ6X}P|_27sU z>^mw5nZ%SvMKJ>o*P>?N4o3g?FBlQyJ>vX=2xmKY9B#;_J#g3P+ml^(($}}n$$oY+ zdwHKNUDf2rusKG;}n@R(g!L*qo_TO-gQ*5UzYv-PNByRUJusnzt z%an?<{DMbZfaF4(JX_z{A)T8FoJEH+V`w6y<(VF&zA&(bOv32dc-+KvAU!7+&nu@q zia?kk!)`ySPS)tP<3c}YDzX82V?)9!ut{`jWjFvEQTz2z;kpP)(eW(V4<;}hJ>)ly zhjF?r0QyEz?@=)n5rS%?o}3qZA-zifOMtqH_CC*ebVt;X%R3k$z8N}vp1;ZbhebBg zUWS-Tjt>+V*ru^N`8F3&h7BMwe3E{FvLf=fG0GIawH%6@N9u`MZ6&@y0UK!I{Hhj%e~*H5D4x>UT9V_mmw#73MY0B+TN~%ItsU-RPebtZikUwe{YN{H+C`=m@s~l~Em8D_6 zoEhSIQoaET@axS9{h-aD*%LNC00uhld-E77pR^1&*R=~-J+XTrqH-I;D~6mWCOvaN zbv|nRJp(;yXff5#1p?Xbq$W|;tNGRH?R(Ae$=Q)2kAd>b_sy%^9v~A<^!rd$Q$KCn zy`~$tzwuXMx2`l#ygJQvlO3O)>NdRIIrY;lE=awLD2ZXs%VUU8?Dykj?}M|p7xc7d zz9NyMs}A{44tEldyZGDH?*(FPwifdrNOL#|)O}-??I^H|VWnT5!UAYMX$5KnLc)MDQs=!sWd2#yI=~pnMw*&>s5O;OSH`Yg zZCc<|%Z;VjG4x_Y%G;ItSU%nqt7G(CpD)DM9<2K^R#+kA$89<>3BS`+!>)&m(l^AK zkkoGO;v^d_J9`=Sgmsjg3ssHA*--C^nYNR>He{A@P<4)D!cmNq;w^2Mx*P!Mu0C6q z0h{E)6r5PmA@k`3u&QOVjX>WKf<@pF)C_hqu!xobTp`_% zk?P)DHYqJl=5oZ3XPJ$sv5n&o{*9|W`x~_l{?n}aulh2pI1QB9Lv+NutS&3}Y59_M z^XPZt-lKr5<>=<{58^4yakjrYR$84_;XCzpeBj&QMvRAlz$0t zs~DBO5feT>?m!3z9*`*}i9U}>Jp2vDSR+VB12U*wBR#e+5ZZ7W=vV8n$|~Ij=|wtK z)DFLu$oO+B%$qbDskXlXz}LAE?&eT)JG&O2jaoNH)*dwwKkg|lLAUyhDk6e+^?S}D z#bX4SIF_eE*MLkonWkbnLc}flo?X!b9a|l~?woV|x$;(WuAUM%DT}p;;i2Gvb~;Cb z9n5hHAiyr0)SRk^6gj}uBYt=yvd8cW`9T7^1J18JqP!|rxC20 zp+AA;g~_4U4P6mTe{$GEOBQ_5J&%I>6=TYa$I@M=jp3c3-+K03Ci`S%&T0JNYU=+R4HV@DXN&-`&lj}!QQ`e|N%|w&~ zTG1OnsEUbqOYU;;Pbi=U_a4_bz{6?(g9eo-GOmx zqN>Q|n6NIQSIV(1m3JOXlH_6*Y>rBe2q9gZ0#~a3miR1&Tn;+Gk&X1tUo?MT`7NtS znJZaFSv3ue&grR`zi47^IV_fP(GM%lL)z=x@pGw{EgTyVXD$5vDS27JE;=Z0kM%8T zosa%JMv*!DMIEvPp+gpg1!B}n2WdZ<-hmO7=*INVIRMoeO;BqUr{B-twQ)<|_t9Ne zVR&EnE$C2&*uUGe1qxk*>i5G%N53-JJ?w7eN!>nG66O}EZ!NyAZb zNP+wwuigAHN`fy8&^HXm&rWxoggUIx*x|~(lUSA1+mL)K+puKH+r z@C~UE0r2_LMxXk?xoUX!g#%DShtmmkS?wdaY++xi8D{NMY(d&89aqAPe$C{+V={`h z(?p~Kn8X}~)T=}ffqAe*k6M;5u4H9by7UMJ3BO%pYP)QBHO94Zg5^xgiZ6$$#U#jO z$qLqL6)6|$s;?%9%rg169k2UHNf@kX);k6yua`3Sm`fgw6-*a_O3H)NtYZ)#*a+@b zf7xg#S7)_JvN6LUeiJ)$=I7=Abm54!1s+>9q1T6BoYOrC$s)#~_hzoWtVMg~f<4s@ zx1#*7iXqO&nFPOOf59Oln&lvSm$jVx3>Bxs2zW&hCnj3uwV>9w@zbAC^_GKsBBlWb zo2dJH<5%8MYamXYQS&~TTJ?G?o_mhs-y~RVw%2MveRO>ADkGt@Cm1jtK05FLcaVx&(Q>4&ZeZv<6cC`Fq85o)Kek@HxWc zhoi?dH(IT(&Hbk7uosbT$bkhCl0zq4t#fs|vTE6o`xN?uzad(RNGaG0*9_Vi7(BwQ z(O}ood&DezW&yfs>UZoON$h;;GhF;i6<=YG~TfUNEes=p5FB1I{R-WLc1=1bA) zwRN^|^?M!(BCyIp8qIcN;`NS#E$S?Q73PEyUXO2E zaefC8@&~cN(-vJTA^v(+G+iWD7OVJ=iIQ&#m=|a6eDV2+7q%o$914=>?0wqf z@|Q`-PY{hh#m8v*zk{N&Vfwo2&R0B@gyKu8(~oIZbp>&5RGGl)@0>U+{Kg!SjQc)t zmzbigg38TE3LBkcSY{B29}8cUW4Cql>X{NG`w2@K?MO+&&;Y>|l3Ljbrx%@ZLr#!` z$70k??F2iLdv%1-s_7I8^8+ao-}z`)V=FA#II!h8nVx9Li|55qlP$*#a(;tTc@M$| zy$C#5s&Py@g=+r=1vT1eVvlN?GvtG;Ut91DzK#rGvi%B}9BjDrMF;7IZ;Kl`eTAXb z9w<;6Hoo2PBXp5~_!Gx$?@2DD9gx4!g^?)7{9C-Tlggies9#gVqAuTw52}gtRrb!~_IC39-0M1%VQ>}okq_jc3tc-b9=n$N z)TsC$ozeG+!QT@QqO}e5eugT;z=Dkxeogh`6^q3d^}iJO>t{4NJMmA(V!Mr{bUE62 zN!YBBP5hU6uU{g0uwIO`3DiuFyP72IVe{3TcFLn+VHbjZ_TVHy|ID3b>D^E%YMoup z`?wmWtkPzL_7XS8VHXcG-}4)h#s}ZHsp2|l@(J~gs_F!K7m}B6I83thF#P7(vPR49 zI-I%mftTZaIH*X1GqXrT3D0|I%3zNiKzCBRZ_)+X;04@sne4`up2^0*OljTacR=0Z zW}0Y8sh9vHh0rpwJ&)jG2|DU~+8wD4H6l6QQMKKpBmbt6(3Zmr1ke0uF~9i1XN1EBPH3~21#W#n2sggd zppe*H_Kmhj*Q;sB-`U#BVB+P?rFBJ9Q&`8OCl^IU4Kq$v)Myg(*Fx;DcLo0Z+Xu?< z(RtdY%DUYj@xp>*x9jp{ZikW4BSoglBe5LF+O0&yi>BqdS7HqEgsS>GY?fP_f7 zzOkf2nYY&vUdYU{3wuB0p}H+SiHpJ7h~@hrr;CEj>Iv87q_%vD;V73; zyxGhb&e<2Fo#}vW$4-qfN=$NaIBDP&WV)Wz4f%Hq-&lan^-~b=D-xY9Dwc6;~dfb@z>fgY$8UCGrswf^FJ*%xng3FP7` z#kkAUg$3-2XG95+AwpX_ z=SrbAlj0{w!yAU8D;X3E#`H!{;{<0T7Wule@M#i6v3k-!+BECi8&5BUT@E?UmjWDI z53VKrD!Y1XG8#8mkC%o2C|-23y``WZ#G!qVqukx9IB%cFF5&WsR9a3G$d_z)d~#J9 z8L@6dWIAIIeJT84sq*=ChzRY$>YFIGAtiUxr8wY-kdZo2m?sBAVu9Vi7$s(~fklPz zI(W}}pep#f2NT_15Vr9*jhv7lSnue(vSfPfvm_e^e=FS}ZOB4Y>tUCH_X#Tb3w~}RJFCWQN!Jzl)$rt(*{tOQK5?CF{&{Gq^9U*n0#aT^)>sV^d0WU<>@?& zBh6ZXW)j=uh;>7vZw4n_c-apvdXsjA?V~gEDz;P1(OEqoR|2v7CV&^eD;YSee%p`% z101ivgDN-lCpcGMlkxCxyTg|D1Ti&A6ldSO=Vk1DZ1-I-FQ3ntI+r>xO`NN8bRp`{ zy^^BnqXD`X*>ymU-n`C0MZl<)vid9jZTQV6@LuH`E((-v)Q>74qE&T4T))rI4PA@a z0U@4HcDR<30(@(J!i5B%{DrIBjTmVfqX=L^$-%tp;p$Ah9v_2ad=|`JP#qmvAD>-P zBSp$DekodgK=-x;51z`~r|2^gH4|liQjh^oyC?Njf}nnea|keBb*HV0sc0V24I&B) z`yHk76Yz%l;JHZ4I^`E&$b0E#a*B#MvNwDYDxBS!o2F8&Yh+={jx}0YFBoahH^GXm zg^QHT4F$)V>H#yd|1{W6EYI);b*(G%ReZk_U!w5HmDnlwj*sbmRxwvSWonjnJVmj4 z)_|blQaumeM9wcd)i7L4*FP_!fvE14xRogfCsm#-=S632^@-)yQTwEQR zSZo~79*AAs72Fhp)O@W_O|3V2YMM^_0KA5cW@AdnEh}dQBnm;ZXNmj`w)138d<5%C zA-%|7EXk?MeZvJf_azzSl&<(sbx(ff^yAPUyEmT}^Q8k6nklM!cWrdA8E09n%|hg# z(oDG9=x+P)TkGSxI&*&Vxwh6w$7-|?l}n++mUY^+WxE_Nw>q-H0lE?_H~*a zuZskMQd_A?KERTXEL;78noj))<5-56j}4$ zg`YH3;hFM0?GP(Sz^77mTYf8!UEG1beReY^gkc#ly{a3Hk2J2FGV^)IF0wZ5;lala zFOq!^kNMd)=$TullEC#iAKIzTT=aO@fgk8$`ktV{UwePGTJy+`3O71|C?GtIj zlG60T>Fg+D?w%t7><|0}K&N7n28B)RkzOqdqXaukB=5?}n*#mAvdbOFeV=XvE$w7e z*BOwt<=Lf`&KQ{FOs4_KasmmXt80Ye*5=>chFj=%7 z_FlKxnXJxpYVeZZQbgFI81OU^^|eLUx~P^} zhrW{7+j<+Yyz4pW+pD$M=zr>xcMFu^KCLOh#h`#{n@Bu!Se#%IUy=vzK#vOR3*ei) z*0z50X}{KBSvR(of~AH@w}xdf^P)>L#x^m4wTnJNf5G5=PYO-6*l>a3E7np|XDBW){H{y@J6^`lg?FaFtl~Q9640Oy2 zd*(pKSmA>UY;;Zzq<{K}M5d{fQ`EpiOl4#x{xa4-F^zbh8zA3?PbGAqFKkZC!Z%Wu z(LzT89L~}xYm{L^5_gV$Igxms-DZfdwgh{hUotQ0NBt<2)Gu?e+^30lE7SMSJI>mbvOw#muRsXf)Y9?OIZNk=Ist<8QR0Fj0udhxKTjb zcdn$SW#S<^LBqe&gT#@4r%ai29hEQGP;bLUHoh+=)~jQ1Q}?D}DnLuHGW_cm%>=h# z@`cxk1JZq_s~ANQbaJKVq-O*QO9you#=3~Sv2XmIAr0O96m%to<}n*t35{ zZat3gy~?`}^J7zy&==5>-P^kzMRG^UqbyzezO}kzJA5D<=!R4jliQkP8OU-gY)sch zs$g)yD@SDzRZzKwQ8zEZ>XR5IM(3PpqJEPFusuA|wIxaF7@Dvw4W0mtzK?Ct-85}# zexwoO8c*2C2{Q|=GW@k}rrooS*#REdH?2-DKQ z@lO}t;P;|5hT&xCxn;nC`hp$G=H=zuVmy`z^b6MMttsG4E2b(})xJRX`hya78I4ML zPaa&o4r*w-xlbo2ab2j_5bVuJM6c8dj`rkHFx8HVXAA2|I|m9)SvLX3NZfkmL&!!i z=H<124IjDOwa_zF?072i-lH2;*D2bd=DUUOhg*GqObAUJEInzVTJh2G~wSL6lo zT6!byhC8=JK_X=RM>TH)J;3FtZEqs>F>;5gmjegoKQfOZww_V?UcM6ZY<)SKF6LUm#5!^w*EP46iG?QMWEiDBlL+q&hGv zmBsQbU`xB2%{gk3{@ER@XP(9{w1wz-JKiZk9}W3y3uXW!3USlt2+WkF7HtYLu+Be# z0Hs%b7zo;m%!HU5_#>EvzGxPos`K4*dn!`XOG438sCTkLV0!H@MnngZ_3GnW!r;_5 zwe1Vnu^pZnNFOINJp7=}@8rpmIw~1g5zH^4mWcHza#hQM_)e~Y z1_e>P-m9}<${qCptk+p~Qi?~6a?)&b0z%T_atYOmMkS$Ns98=P(*`0&GJ2faI~=jZ|5p6Yvwtm=ShF_D zma1=F^N}eP+n#G|^)2v)|C(*|@q=WdD?Ljlmp=qooIEQfzgMzX;J`@@k_fBo=7U$< z2CPy-o$BUTY8E8K)1Cv*`F2$e_`n_VoxS#sY~tjB@Zh1grvk)4KXd95!2hKQbSAIm ziBOx?V;_JMN~q@_)H)D)2?p+~dd@RHpqE`v1nZ|x0Gz|DUC{w_9aTLZhm~V0&Kl$a z1iT_JE6NAXJ9J+WhBtL=SpCK$7+}<%DrwLPPqEHSHw=2LM76Pb0s#?PD=LSC_PB%P zpU-2tnGNvW1@=Ml(*3%oORyE7Rry~0qLgWUmdBRJW2XR`$fetsJ!j5ZTXM@JS+Bly zj!QEa4WL^681Tj*e{XV4s!Fw&Iq57F$x)p3Hr|Sh8OdZ z`YVx0KN&(Y@{(2*2qo!q#cZ`@{VLUqj-Lm5Lh4VyzE))^uecZUL$=?SrstDf)i=2_ zR^m68Wm~RS7yAD}+j+oKz5kD&WK*^>Lq=JVnQRgj8bVga!NK9!do@I|LUt08l$|}Y z3t44k@4ff#|JJ?ry~jNV_y2hOKKJqIbnktf=lk`3y0(Avkco{X*?=e<)5dBFjY?r zD&20YNxY`@hHy86QxwAnDvGYcN3OnNb#o+@@;l#*wv9@*iHC66| z<_$}Yh(T!QtEM!Yb!r72i~+UWUTbH(OU9n=<5w1%I_!?IwNCp!=tmp1Orqv|!0j0* zqHNvULl-xsAL9Nn?*==z^X#bY_)UKQR=3U%&%CR#!aQ-Ej4^v~q6_dQ9EhWWABbYH zmp)*lZ}-?fV;_5meL7dn{bNUF!C9zXC5?!pksVf)jYdaOdd*^akKRHv#p%3`y=R_*{b(lTUec{APl=_f<4&H(6u;H4kB816cJH&%GkV|8W+DYEY{@-Lib(l+d73pp!9 zekA?Wj;1?1G)h;qv#R;tg63!Rl&g4Xw8|H`s;@We3ZFq7p3nM!!di&-_Yw$VHHf7d zF9_k^dV$8lkP=?b+O8^n-eUrKeFe+p@{&7lKGfnQ z%RU;U`twY-&d1n_O|)YM=BM9OANv;JB)KGli>D^?!^$8w4b!%*5<@5FZIqhd$E2rB z3ka?#lEQ2m3?WMVf@Le zEX3lfO^d=$j)mG#{p!D;?dh15!?lnM{*j(HXlL%E>(y zI8RK913m3}h9S<`+S8AdN$FBtH>SR?k)ue8oL&a2U-$hB3(c}SgbPbEp6K%Gol>Oc zpGBbB0n8;jwohXp7itNyDha1~hh4MKtTXUq5tQr8$S#5H#Kaid%VmNZ#7SG-txH^^#nGaJIIlB z!vCbtXF9)>WdAiyk6eYj8i{_(DU@k~7as3EtQn7NWv%)#rP1$b+-Yj^$|K|agply; z1%v0U@OOi35DcZ`_Y_Rux01eB#69lge&I>4v#>(rwE2f@voN$Aez5|%5Ab|7kB2_r z{Dih*^z9?cBWgEhFArj@=aVewF0W}E|3u4}5K=;2pK#;DBl`eMmWp}vN716)g_zlz z{W3My=eo=<%wNRb*4S)>y)W<{V<=5`7U#;=lw#eaS;;{sM)&L`eo-yK_U!r4)%PcC z6L({;jk{xzsS~O6jA^LGiM){_AJcqjFzAwqE$w`-pP@8|=!;=}xrf%&X+c~eCF*A$ zVz)+>u4<8riIh5NNL?%KyCJK}Yb8V01ryY^xfyU$8E%MO+dh7o3N3F^fc@OMW|tXW zTEJ8eLDuTfJ)zLC&l+dLS*kduQ<<$XCvpuydJ-zE8YPy`;4u7QDc4fu)NWC@YGCZU zZmCvU!niNS+*z}?l@*ghwdRUk9a_43i7E)5Hh^mzCl|vr6T}LWj0n>3x-L1-Hg{`v z#U&6`H9Fbn_A{NhFO{_N9^boqh7Mly;x67>nXUW!j2b7NC%n_2ma7@|JwA=uZ-imtL+iPg>Ue{|5gN56dz?GfWPFmigWlUa z?f45dq0>Q7_}8ama<7aYiKk6FNDpBCvLL|pyVdxS+J=%YYN0xjVLTl>FN%ba3qRpcpI!Am>Sh_a>t6^vk_A?wD zK5T}h$D-*To#-A;b@p_$Ndb=6b;4vj$mgzpIgo_D}%vCvocEihdmajgY#$MZ!Yw}UL$HVcrs#T)VUAva2 zkNdm0%(>&afAN<38F~NubdVujJd;Upw z2e&#ji-q|Khq|b(gx(j63GpI&t0Nc4c3}Kwm?B(_SMEbT*PhD=xH(%mY>&^h*8byh z(QO#tD0CY4Elu@+SC3G?$xOEWQ^;b7c+aoaf8>6^ z#3+2nKjHXWt-?ai(XKo;KRkF@%#ty&JD&%uhXqDT+O`yLmrTP0Pzcp|2>)ktwipJG67jp1yU*dfv#P zMP+%Kw6M;k{DRN!H@e3+m28Oz3x@1 z`S6@4KPOaN*wIQP4cx3cYU$dR^>AfbKJq&TihZxMW1X?@Txf?`*|#U zA;BK|9ja8xcI3&G#rMipqV0OrkkIT^Q8WI(BUL8(!qb7Hv*d zV9_&0G)s8i2$NTi9>?rwuazs<*3VqsWNo8{TX%1<9oMH(yVLz@eC+ej<4Z!78KV{! zWn%`Fb)w-i?h=`&p81f*)SV9+^)F#|f(hQFvs9T#y%Su6wmr7nMS8W*NoCEOB(0qQ zm(G%>1^zBT#jfldkB!Io6>Kh(U?-A$H#}=(iHco5!fg`e>8-t4L-?<@w^FJNVLsM4 z_UUn1_JDM8)I0TKLS{PMGoxg*iNyv|9`FS7C;V*;I(cQRZv}G#aFb7b+`{u^Adjuv z&K|i^&gF}VXV#B58Jb9u1uy(Kn)_&({_>a721tq4&dQfBFJ`npR5$hVN;}_dxH(8a zM0PDbU!)M`%_Nu1cn(hG$oW%`Ziu^CJm@~BYC!9K9+y{RJ{V84pEuR}Shpa z40$M3J{d2&ov*gYTHaThzwJ`N;PHh(`3`Y7`Q`#gX2#3DqEUy@)|jXhnH5ia*$Phk zjZAv2U-nv}9bLM#$?~Q_I-1tzGmQ{`*`(|FYV4N=t<3t}`1ejcE2=m*dRM$4lHRYs zaNB}rYd8Fo8@vBVQ_OuKnICJLKBZcgu&<=-zV?dF+`L`O*)$uzMWqb*TX_O#Okxz& zyOrztAq+gm(wg`cwD*4+!w7}^tGC~12K)N4h6+R_-x}Dw`DF9{L+MzI75f2;Z?9fT zIYe+@Z{YF4uo_W&{9wf{JXX1F%5`EEPb@^KT(MJ(%V=v{skClqwJ}#zm@d4e9S^!V z=o}#FpVjr{vgL3=yX7ZZ zm;8Rn7bsoo5YenkG_p-NHnA`h+}JZ&ihkET;|)$D=j}@vDKE2J-(^01kRQub$JfNJ zJR_2z(SWVkK($-W8X8F*U}_MkGyW5!Kj@XXkx-Y`nMcb|wrWLq!fU*dV8zexuF4B` zT2XS;eJXY-y|_6C}*=_CiH`OrZ+s? zK)rrb&!@3y%3`2zF2OzZgVtHS)Y+}puX7x}M4!}dxD*Ik&hTZWh%5&4mfvwwOP5=j zy{NUpwYx?CI$(QQ$Ep^r-u#^JerKbrSlFWM#Sf$5JWKkniNP!g&3=xlJJL|=t>3K{ zmGlvT*r9Fe%0AifTo<$1#|zh?8pe{3F=oNyS03 zm1_~-)w`MXu$9`c>-77h<8NFf<`9mBHb~I4nAD8S4+&Kp6|D02CK-NyIW9$7NF*@h z5n^Q(?Rq}M`mMrip_v)$u-CRhq~n$&rPPxzw*xpzs>zABbkfgRmEw$ z!X;AH;IZf2A#}Lh7u*=L$1UNr?r$%MCv6&A#NBjc>yH|t^$CW+17~jX+o-Hvmy^3T z*0iR6JAi5)7QJQdxn)}P^WNC7>1QQBvb6h7?wPk;b2;sce7|8`;AzuUjnV*HZt_X> z2d!>$PGl~Mhi}RTZhEX+>x}5pt}@CbJ&z}~k%m#GkUcAOAk7P&QEz{K;~ZW1=dR2? z^Q)&eV#{2c-{J&E`YYZjq`&{9f-rM%NWM>_{R8v*8+%JfNX|D})jN3^p2>~4i(%pP zTNHyEO%I>k6`sZw@@trQ!4QCVY9YRzIL8>LUBj+c(K0_QpHPZ8_6elqk&ADlB^jRG z8+>8ccFnp}XXmrn&N}Z3nMPzin7>u^@uIhyH%11p7mB~Rur^s?A>&DJ~<1TYc^_;bTK9)Bfqd4kJCz9+z^9;AR ziiVlDKL2CwdwL6^OH8{D^U7m|8-^+_#9q$k@m4-Gn@+M!%t?$;Dvfw9iFh1NH)D zt#qD0Fx-5JBgWHGMewJY?~LS}_5MI>wk4w6jKyYtfB2*Lf>W4w$Ys{xtCc4_J*#zx z-^RSYy%pk5d$krrMP5rJIcM;3=VOr%Lv+#pPB*IDY~^El`(#2Ql^4&9^{3K2@{hj* z$%w1CuR~sblfO@kw5?0gWjR`VtW$fWg!L^Y^C)KIeZ?@{)CU@I8rThkyh8ae!=1QK zPHESVrj`(2Uo(yZ7VzMs_7@Ax)+&+8ds)n|a%eB2^@>U!5pNmXiH z4qZi(Q{%F{i5yO}!6!&i8TtniYdC(*{cmQG6mjfy*eyZr)W_W|6Y1;L+k?D5Usm?- zwxnMRCP+xsW~K);QoQPrEA%ch--YMU*fhJS!jI?s^}{$i{B7;+|s`%Y(q;Ez=Sn!;Yr$<+-r0Q$xQ}Lez zBDa?e-zHXV8Q?}3aCl03%)GytRq=$nNiy}0zu)a+osTChqFK#x=Ci);42p@#Bvgtr z5pi6hy@j@>*l;Xio$u=!^@WkOyDx{2CwC|p*anQ&%2_OIgovm}hB5ibNPUW^tt!kO zl9k6~QJRs$waD_NlPY+LHc0`G3N3fLGnUr)ag8-5pOH9OLocemwrb23-*mYMf6m-& z{V{w&ni#V64cY^v*)V?8SNeqH<4@E(6=Z7`dg@LyJRke^IkOppy2+Q}m%r zZ%fSug;s9P^`5M}_VQ-A#Bdj5bb$3W9cD+RhRDpd^s6haY*SaCXFyxeEfRVX(Zdp) zOHa9c)ax)fW^*loh8(xt?uYIfqOqyx#h%YFmK-iEFkBh^Z23^KUoOinTk0i#WTj{x zF~@-Q&B*TJwpfq(XJ%W^?QNHG0?Snd7HGK{+P5WpdcRbb-gsBif78G|s-cV#3sxed zA-Vy_!RfmCL&r4A)FO-GOC#)vF|M-KCSI+!)sp;aBSYMRbxY)o$n#IUWSSh9&)ji8&YN?AJ96elLX68R>)U9C5Jg&%KIE?^sX z03Q9pr=#3$P6|^r8bX+I^VIz;$Aon6TwXb;d#Y*c6}c(=y7L&70ToN`HYug&i^Ws5 zw06Qf2&s-Yd_5my6CBHR=i!nj*7GWIMTYl+^|JQH?gMomV&-nIgZe7v)<##la}!`o zy#@u20?=3dlTCf9^+X}H8x%rA<>>@pHpr(*0#4!#_7_&Wk#4rS2~B#;Nb5Y8sn?*o zEXeKlK?2v*F$Zl>4fTe<_Nmwi6J+at*A3@`Nn;aB_Ad=BbkhcbB{@`@dkHi zx*7(wC$N!O&CyhM8jRMgk!Gw(tmbxDxGzCtrNacCV1TVS?d?ggpDcc>47R;5YxtIw z55HG#hXemqfqVUr;;#(biR?p>+K)_?*EO_yv%7P0I=u@yp4eskM;9uvcxB;6J7j-r z)}&yCH!m)7oi$m^n;jQTclB9>k`$W2eZ5zmKUY%Rp+BWZSw2u(RGC9LWF}m#UvbU! zMUF=Rcc@!Sw9?hS6E$hLo$)ifo#O2Aj0QgZDtJ@nSLNs=f+#Lsk+6DnUP>_cx%{C1 z)9{&a@!2zv#X9F%E1mnC+;;|H(P;evm+jCzz>QvG9jnsLI{27kN-l!dBIl=nu)I6* zF`M#vO~>VpsKAW&m6IdusjRV2m|2b`CJeI}87MauN~~3UvIpnBy5m0D@Qe%F#$n9S z+jkCKr&uoB+QQm#{87jO;Sp3`@H#VIjQ^Uz{Tgv(l;XW&RmhjnoTW4T8>={}XDO@t zevG$|2Ux_Fq@VQ7n$&y^IbTd60atjXBIWuuQ9i%Md5|Q*$G~_bGx6MFv$A_}+(g~h zxm`lZ;e`cdK|ya+O%g==_Qsr4yh(j$fzus|*a$8=;nf#L zh804>1mr(>S=Y&N#?0m#dd7PLhZI`ZYS@=g(L2HBUBjr2*49Waz!`Zdl9*16N?4ay zd^4W7?$b}ARPaLOquo2tA_unT0_tB{OxZ^Zn`~pk&P{!*5o%G0*P;7S@>!MS4v*P` z%@zrLJvc3%)rSwa;*(u6;fi^zinKH;`WWsG{Q)|eq^q9ZcUa!=7H0K0Xxt5Xn3JUP-sm~vbU7)?57uAd^f`*nJD7Y*i%p?QhO+48z~vq{uKSuoKTf{N3#^?e z7OuINGlW4QxJ~BUenG)4kmlpiwKD4N408f3>?N8mX2 zifaigpGha%`j<-F`Vt18+r%M^PnVi3W#{m3P@rD@~zz zea`+^GekZsRb*5KaAP^W7j|VajFcSj-&XwW>59lc_m0!l!Oh)wTDp(mrV~_#su}mB zC{7Dgd0Oecj}|f4NwXHGH5^UuJUp-c$XkuL?4FJ>NwVg-w|UNdkBgo7>TV>=%Q!wV zENIIvt%ZF2($CrW)J;6;z1!Wup(c8ADskBB4F(qU68h_#KGWlD%(Vnb@e&t3>zO4c zvWI#VpZH&Gq`aiJk?=v*wR@hq*y(x2dd&B?bN2(YlJsqwa4d=PHfd&RO|EIH9a9-d zNMXzw@HnqnUBe1r|0d#Q!Xi8VCC|2|hboHN*p%7mpaoZOGk)}Gc#P7$ z{k_6@9o?R z7c2M@$KjhkzA)^Kii+{)Wce45X9YiF{HWVE*D>y0_` zlk}+zw~T0^?r#Rp6%Cl{uXD1rLuJ0PpE}z&H|Ch4x}j^lkdPLmu%=QRm`PWN6XYE)Te6E!X5T;k8<9ZJdYhI&1l%xj{x_GRMTQI--RgFbbqupAe2v8$1pM zYrlhSSW#a{q*qJMzQWgamcJi7)(}h}C+b?*iPu78Qk&t@e@czFrya+6HR@AegYA&% z#?ypPVF9{va^53TI}wvlY>jf83q;D((Q1NT=RbFwe5N3B?nTPg405di_G4$YF`w&< z1WY|v+2MQC7=vd_N$YV% zGm~yVh`;%CIEmy!gQVE|bHZ<}VClpUi0P%OxiJQ%td;ueLlZ|d$u+lK_~12+LW{2& zG+uPizt1eCZU1^#Q7?s%j`ZDvs`h5nEz>?)`M0?g+OP*j)Le_F^bNB)!}a=YnzHrW z1tT+O+jBik1>5l6Xln^H)H!wi*&JGy8{Ms-n(JDR387&Dz;6|i5p9s$tu7-X|91E=QKH7;y zh7xl}JI@Y&#v;-i4Ch*^P!vV%{~qbg$Mc{uj*e2{%X2? zYphroT`XTX;AH{Ng{rFt?Oww#oV}0huBQZk3^%7+h)ZKMW5w~C=9L}v`x-fTHEEW^ z(7xDTIf?ydK4vqHX56Xe^B9|5Xt8o^g4600Ci)(vmja##&1|YS-H<4hc*sT9$R{i} zS%2v`{~h*4TelUxOGLyiG}Wh5-!yOb6JEZhEV53<`TW-s^D1p^i2);Jv>Iutee~ZvFHk(N+nK1w(98E~ z#}17;{;JTV#QI^3ggmM2i`i&iwSn=J#FIS!<-Ii#^|m9ftzj&xCD*Dh5jo(-OT74K>t7Z|2V+efLF9QU=2 zj_p$%X=@F=wX09qmCOr5PIPPYG$B#Oq&$9Xv3T-b&1Dy69zD%p#7J0)bgegFW^Ho5 z#6XJsSDe4jpBK1X!hufGw|SIR+%Z5d~W=@jn2a1u7~~3 z1DfnPj#@)Bu1#eFX3sO;8f7{J;`wu(F&*NeAZ1OVgzkKB?;h`ZucFdgt->1+YJN7K zT|}p)YG=mEEAkpiQ)jj1YE1R?S#m8-6^6d~n}Zk2T0ckPgkj9j^nR~|p=-uFJ5a%i z4Wz%}w6ii}s#9|)63q*}%j`CEY`*u?y>G7Y?wlz00@sTEY4#`1lmG8;)TsBS0LC7Lf$sG!=4&)t2#m=*FfJ3yzO+1G1WGN=2q&~%nZy5BpW z995GT;wVz?K!MM0GZ@@+;DXae`aq)`Aq{Nv|XOjXNTcfT^Yh3)6V(ZGAaWnB;(}MK-M~`oW ziTUf6S3c+~k>y(6RZ=ByLJOCFN3vK>l}2`%{pth}3k$y_8QokOp{d#_cHe+NtWr1r zwWiw?p_E)XXOpMz@|p>KNo_ZGHT8dT{i*Zj^^I+qNHE?AIhFmb2EobQ>CfCZCk33g zM__hm7Pphd7|UdnLwSPz*Tj#VG+L0aO}kZocjx6)R$MxRZ$a`(iYQgItOUUI_DPq)0YMIYZ7=x)2mu-T^#h%`sfMm| zo*!RRXpUdJA13Ous$;3Lp}n=FQ=)&7;J&#=d1h0D7=HP)&gPlVZ?Qz@bYFX?UctkZ z@Q;>?(2~4HSn+;3K4GVsddut7^6Dh?&gBnspA~TkYCD=FE_b$6b(U#+Cl|D>k$jD0 zeDgx3zqeDNVv+nAC-8FGKa<2EE2a1zNyi)ex2NF4-60Xqno}aQLygh*dbvV}ub2_* zOzDT+Am#3n=6C#g(gKbqxr4cj={j)Pu2WT{v*O9lkB-?>-noj3XNXLck>uQZX zyqBSsh5duR;6khvSzAJcGuJZvdiVR(;-t-8ZB{b%^y8GqMB?U8PX(F9-B33!Av+%H zyQn{ZTEWd};o+yu#WW-9W^*~;w??*w=Eg(st{u0wBYKi6gvWJHOvS|@pisTdJH@{C zDt#JtsH?LM<}9wwPxB8EO9@I-9V3?&KB~W~&i~*eBwRHe*Af4)_%YU6`MWHiCH)gs zB2^y(0-c}EPz3BYXh>bX!7HWJgVC&+L!gi2VDwrH|D{&`Lox&Gfxv23U3S!Dp4{)eSArQNODS{9e)yvHJ11I_HAI!oO$RK`o_UvxI;!cuIycQMAZKCmQLL=ZmuH1Jiw>RsMP z@ASu9NuyGbVB3Y&Y^N4K3URV&JgkAL8{MTzC4y=bYE=)6W!~#AgbgZ7(BjFe$_O^T znUrY>$G9SgbAwHsWQ@`YTSs@2!Ze&?>8G}TX@kqy`68KRb2l~XZ{yBiD16T=ynlaF ztiLruc69YM#vK-Tj?*`tyxKWW-xH+sMoM?HO^To7ae`&I{MeJLRSospjAymG3(Ch0 z%?O0rdz@s53Z{>z|p=y-^Rw=k)bipGH>p1_5DYmtcA#kg&-{9 zLBr|U)e}AVpSdT++&_tl_^F<_G(d9hyktpPgC(4zUAv^N|9+;e9%F+e1AfmXl2zH7ef$~#~G$9Yp5QXB_1|w1YD!j=8*Ng$I@6y~;PF|PP)H(5QxOwJ{3sDpLoHmKX zyZ1G^NacJsu|ZccpFe>;_4UCV`I9%WJCDz=~=QM0d~lly94`nw`2 z(ZZ^8H@`DEyi(dak^Xrh5&d%^pjhOy0*2)!6 zZK8yfSp?1%SN2lMc$BIvGsJ?nc7hcSB_o=CmlNu)zJAU8e$OYaC_p3 zZTw8EIR38YQG8?l82@r{8C}vF8uh1qpDjhrUr(VI;)w>DDgdp~FCwRVvR4QG2*bI87uHLRoZfskxfK}<$Glrq0b zbGE9{|K$}`9Sm0)Jr^Hso9B)6k$D}I)#ffTV$M~O-`JEf^whtaX(czxFjIAOH~JW; zZ{qSmkN4_6@OZmcKwUNfY5m$K+2?%a>dQK<@|2+y$7*{i1&0{g)uZCAJUifeyklKI zf=_D8o@2++Zll>2T~HFKne(38j^}?~9ctnkucv#GT#x`-UHKSI?d2rtlt9>hxJ#D_ zTZju!&Qcf)9$I{?dqY{v9ZB0|E4~{h)hqlZL#DoBfldCZ`KujX{#IAmtE!4!RyGJc zjlRc*;cU@PikvNXtJ|(1(dBk*OjS)h=40>RuT`m5N~`+~ZRC?Tl%+-Jq?S<>VSk^Z zfO1NJpL1PSsCM~FUX~CjEoL{JWP#t<_aya`&jY&6FQ35{&}BOPRltuFo$uBA`$U>H z`AW*}o$cDMdxd|z$q{JdB(ZT%)1xSDe>|l9?jB*hzC>)dn(_m&Ci&|As@BZPhNRjN zcQ@g6vNbc>rNlZ$qCtj4`_=w%Z`J9z+omQm6gm9^a6j%`Sgt6+_E7yB zmEGyO;5HH&(uB5r1t(AD99(}+9*F3DTEc7Mu}-C2^wj0r^+8*NvjI_GjPmT?@MbBb zTO`Bq83o@)Zt#?)hP+G5Wqux}>sNOLLS}a?`nIVomoI_%@h0^3F4Gc7!|k(lFJ$zx1T7!_@-A<%^38X0f=PvW z3f^{%ktXhKHQdt4dRaf~(7`~7&cN5o6l&10nNipRi_}tY6PlUZ zsF%PZV!}NYa=B!~mCJGmeYI@4{{qoC6<^JA-T=olpIeJw5{ZIYvb@nBA|`iqvD?PB z2pKCEB{JKy*G7%ZK71=vT=dS!Z0bBOgB^FAL?kSA`9i(84)6APbsOd$Q|a}`x8_XS z=(%FFA8NyNO-!U(8I)!(GTmH$Qywf$ytH!s>FbNCPi=f`Zl{iU6^`Aki@mOMkrser}VRY3$}$QbE%X%XRD0nMdnL(e+qfru@m2H9*Km>N}L{*B!{$MMh7ap zkQmB$rrHll8%}W;I&xl$=VqJ;Si(bFr+n(VRJ(I**5rN7mocNh zEnI1v^0unFoM)U6R<+`Phbt<(Nxa?p=T+yN;5s{&o1S?@W3wzbtj}V-`7m?w8+L1q z9@F>LcRn>CN?ZY7eQ}oWZ7wrS7Q-aQ7TfT?O}ympP`&n2@8z|NebP$d*Ymd;pvm(T zZ`~$o=NP>1ncEj1-|wJe!~{&Y^)4X66iE2kx8 zg-6!Pnv}-?v$##bJYW(c;lQ9MTjQA3R&Fs3p=1uxQJesc1+~N~msnd?V5JUgHqy zM@vY&d7kCUDyD0<)XtiS`cC6d@OyXY$H*|qz&(XHXi?zyLN8ZcS~u`-Vc3FHc&HbPPi3BA7TJ8*N1SyZH)g(c@8KXapHWG;Pb2lv_s!Y7Vv0(cywS?g#mtS+3c;N0I})h@j(m{{~suL9DEx20)~Lff_H> z>|A(=hPE}0j&>Trem@DGrT_#tHPW$ySX%E`A+h4K0^1%hKzaEOeKleToJQ1 zpqhybm5PmkAEeC2Mnl`v`0ppd^92gn9#Y2;YHskWJ*}X6z%ne%t&u2SVT`-D9MskZ z>#y53|HrO*xxcc13Hpl-HMfBnS?TUkb(}2V5MU-Y5GzC7J)8Si1J9tBo{b0K1@JQc zE&zNcc=p5{PzFrb$`oRSOlXxUOTQBE+fD6KXlUmUfMu@_03w(Y5;+x^TZ#06>5w_0 zp)n(1t8)&({;^IZNULM|5lKL`kql^PtO!W6mIILX7FMQEb0ZyaK`9dG9_P1#3L`+( zejtY$=-TLV!yGyH+Kv+rI<~G6g!8XH#SO}^g>&f}8tu2?zVVL@DgX}!uMk2p8UNqK z{M$GZVfJ*NKy8SDaZ4bSqS^T$Dg&;B-Csd2@Za{=2}8R&&;gWE2wn(6X-+*yFHOhR zd{2oG15SvM5h(VTFVXp}S01hloN&jBT!LJC|5sK{j#FOoiW0Z-wZHzdUnCzE=M6(3 zN)veZex&2~li+FF`+qFbZ6%dk(z3UaIl*A8c3$w~8EC-mpjrMF34A7a>h}E~OJx9o zL(KQaFGn3SsJSUpAN^19Oc2Cd`o9(r;hN$7jrmWx5bevY|NmHRz}h*jbqyi=Me4nM zqBIvY<2BHnHxX2IeBeKb1n^*-JKW&+7e8bm?|%9u8rmB+G&ID~^!(62Z(u%X05rWa zX;BV__F6Fd(I8a8ei4zOZ(#+|fkA9^_dFgF*EvCzo0$$2fdwKU#2~>M9sM8oL38UM z{QAwm-Im(iF>5RUdI&%y2qI}>B7&eWI1)ipro?%i!S{f*?hEFglxZh8peGt`KgNH~lx3Gfgnn9g@ z4g5&t(UIXP*#TN{1%VqFpZ`1wo|PnsB)G1PF%p3H+j-{%P??vQ`vHp-hyW{yC9=Bo zJqjY30)P$xApcYTp5LlDjR>$d-V2wJ$e?@IfzAu4V-2(`B4UJQAHoJGLLUjjrkHK} z3l17u5l|^=3pF$NW%x*> z;lK6fo!XvxfbN4>Aqg1-ioO|?^CB-7#Nl5~5B2F;3C4WVsBf`bjj+!{psNU9Oa zc+8y|s52E15St?#>)1d2sgaolG6&X9@<>MmscHTsMEt%jD%14HW9wN!YMOp5|3V-) zsvi8Mc1fhBnIZWv1WMEVXI&6=!KSvKg3L6tGtX+kfi72p=R|CfEI7jVq2|^$x@Kk& zDh?%afxjr%%Ov97&Rt$L18DL$AUHzOBBEiDQ2qY;; zg!uTWj(ipvr4YdEsrC1h;2G(HfPh&TKnAlXLj-=r@6!Huv1&&ES?ih`=vg2`d?TSx zi3g3*30}m3UTPc>p>G9++nixDfY_bmvVprE$F*#pxe-b zy8M0;JXz2%{^?BZfsI($TkBZc!r>q`L}D!KqQN$U;QK}Z2gLdFBzU%=BcT2_;df8~ zem*rmfC)g(gBMY<8L7NOz=E&95oZQ@A=kGy4nD~uLQJ*DB}zcY98<+dlEvCJ7&dz z`1aGlIAmPFO;wYBGLFFagILV{l{eY_pi_pR6Il7y-6k`2$<4A zR$uY8=&!|Ws;IT)R5%4ZJ`UwChLUd!l($*SK zvK&F8WNrvW*1Jlt%UEjzxnw~Mh1dwNF;qMoh{N9UhZ!<|!fH5!o30sb*? zqYi6em}V*qpkY-&DsT>gCc$(F4Xh#>K=pNPkm>OBai(Kj00$z`ePfx!c~n>@3+sba zSah`~I}P9-04Itl=>$q77;Rx7);%b2;bIY=^9FrH9w>nqK>>a2s3iS^HM9bO{oYax z>d51R2cB3u$k}>9(1>VFxm<_w_4d|Uk@V}}8SFJL5Q|cRBoa8sKTm>ZiolVWItI2d zWXZPATcXtoAn0k8zaqKcjp-%H5l|Lc(;s zBE(1zMnPRLIwLCKw#Jc|I$$yjnG(cc3f&AK0IPQ2uY?SPqk(~QK_YPY87`Md&|*T8 z`wf`t>Cw1gE&1Q`%o6n#S2BU|?t)2yy)O6rN$}i?J{s2gS1yMvVUZb=HPHaA3IiI&i-0poj~9;Co6Q96l=E z7@}){e8s-dOSq5@n8Y)Nzc!5gE+D~^ZS1IYd(&_$w# z#QRg2M8F6NfW(Lvo8f&FEQtM(P$QFHoXY~{^&I#GJ_Pf^4m=9=AKQ^o*abmj!}bGE zf*&YB0HFkekpEZ$N)yRdhY;QY=3xf(!;etLVAy{w0|q7*?U5+T;JamcI|v8`L0*8U zD4)pxS_E>t5xT&hQ3{mAE4;si%vb-h1QaH8nj~l<0I2CJs0S;8nkrIImGD;sAFu@x z-&0nm5Ckw7Krx8HMp){>P}DQ0#Z3=nLO|e&4}1yYc=F=o!OUM9;|`g!`!+uD8Z3;V z*#I>l_IroYBhXQ$47qI$i@w0nYjE~QiB)As#iGg@On;n8rv^cRCUCHbAx(7Ek=e)- zhieN#XYGMD8bBK&YU5b#QP3!Ihu-(g%J+c8Q@|`hEB<*BJU`VR6^kl;FxzHl^92C` zvD$uZ7=mLZzxLAo+GfH1kNo_fj_TJsJ@O2~&`D{|5_pk8Fq6RXN9eub_VU<~(MSh! zChr&5_JVSWe}#@~vqvVQ4wCLSv3oLuCcFiveGr>5XYmMlRH?-368>T*h?6=%QzBv+ zH;#fukxiJgtQSInGIGEyCZZ>j-Z}~v>4f+BKyS`T05t|q6w%~nc8>!6m8IzDBW3F> zJm=B`I#@ZefAnR-z&hOI_O?VJ8B`<}Xgom7g%(1*-`c9Mk3cm*J>%?_mi9RUbUmp2 zetFLj9*K-}R)kvBcmT{&pxp&pKy*VtX^%h!T@K|Y1_?*V*xshFa>D&+FUBLF_a+)q zP5F{<^9ET1r+ni4el2^l91)FT_IIr=5vGp|7HdF8g&3NOaUX$ z6?96(fh9}k2+#vIXta7fq=^hG|f z3Wb<_mBWsJMzOQ7?1|<5y?DF?3|NTn!_EE(RMbljdmRpNe?5rF%3Y_UgAZDL@Le-I z{TcB0mI{Dd{qrPvzB{CZz1#%#T>c*Dx8%S={yYhu1PmP7OxG_HtTJFutT!ipQ5R@ysQ@@F_imYB;&8CP%sO%VP| zSnn02P+eOai$AL-s85XDBdlv12ZP}Sum*u>bK7f&a`(#6wRSW|wo|JqL(s6CU4*fj@Qv z@*?V@3-?G|xULn_xv*O$1CLfg&#(hRBf1NIszbpj=c`6F_=FUI{oVvGBGr-xDOC@g zkJ;Osbl{P_(;Uu%yhdnfbxG*^LJf4+{+-)r1H07uQ4AFMuHuf{w}iTSMS8!BaZr$arv; z0}f6vAoEl!sma(Lz}Uxteh??$&ZQlhuWteN((0o+AbCs4GT#*789}au7!GfPh2JA; z=&zPWqW^$P7En{*xs>qtH}J=-BhwARPF_@JJ0dT2w08sX$$?m8BNqTY;P8mn zhk~)Ypa9^t_GP{=3y)mD-iaL~+Cg{tO1ZbTV+Vvs9FZkTj?CVRt&p&pgw`!Pfhjrz z^Fdrr^D8?l+W@L-WNv}f4euE{(8{j|QS_YhqvDTpxEL&^{CN^QkAq`8M>EO8eb2y+ z2y6$Soco{}h!S$Q|zepzdq$9W&M%@DieWtF4iQ8nL_tvQ@dGGG&u`)3@U9?1UVwht0K_tm}C>!5qE zfZ2D%;U~l7AihU{!|;_28jOQ3Kk@%=HmtJOhL|6(7ooRoKEy5$aVN8ohGzRn`>z-mKmKuLz}+6zn`eF;vyTU_KEyA_Fv)x zE&TBsh&eH~z)`rUdj)kJ0#AbJN<^xr&S6xD znIVe3&mndf6E6{hU07h)Mr^nz28U6>PFqLdglv)R51Ht=_@)d<>xH+Ui)(c#*Vft$ z6}RQC-Wh(7I&OohEyUR^68porzvf4foshf`zxez;ELbT5zT+~7)E?5euxV1kx zmK{EXYom+wWMM&%RpfK93?LE-?`LGr3aRN&r0q$DhLK1va#WR zZVUC?|NRf5Rm-3U+y)c8h&eR@`5{m%q<$#NiARY!pW_V*&rCc7L7zL_*yY4utBz9z^N`wZo~X9FB;50;L}qbb|lo zZflP~MX~)2HzY%690(c(ECI2xqV*0J6?KpM5FWVj6$tCcx}RIGe>nH=bHqqg`@W4@ zYcCCw1dS%~mxh1+ncz79JCw`G$Hfc&+1viixwqS#`%mY-E-x*scw2UV8v5oP^A07@ z%ro$wMJTNtjt7~=OQLNha!KN*+f!M=oe|Aj+b^s%* z^#7>8wum7gZSM}d-MxFvLM00!Ot>Vc%(N5AqGP2;c}29ZX^IVc=hL zMJVovM5{FzH3W9B0s!k;BGF0^B8l)(Lch7>0grKa9P% z*%+h@D6XF9)FTn)Vn9RlN!w4R?mCQYW2I|uy%&WbO&o+;)7rH_KUM%bL0o+c-ad>B z#xZcU=fV6z^bz{K%Pn7kgatXATs4-@yK) zU+ch#qYE54(thuLj)O**L~-*E7{GSHO-=j8tLcM@D1*7QnvA>GfO9Olv)|?N;2^E` z-!lH%5U%sV>!b?)c-kS9k8Pe#U($!6QW{20UR9h+~;^> znGS#-EU~Wig33177EvUz-;E719{~LoAv%ETBK1(rd~~**(YXS;E;Vpth&I3~cn}wL zs{0`{l&=O@71+9fb_RU@eiA&>#19~%*pBsPI-7X|3^-k9_Itx@uxa~$?ZK_{_d5Jv z?f>gf6jkOY={yBa=b-TdZwLm3KTm?^{Ivs+;MnXzrtje9_-P(YO5Fqb9pZ3Xa`O-> zSo{HjEV8v&4x7-Ly$y==0F2l=OWKD(_tH`%mM@` zG}9GmiwxfG%Y_>!cH!={rUpNHqTsJCAwNna2v!4Ic6{hnhkVyp;1~1T*3V>2pXo}| z9R&D=#fFU>;0*5B5WaJdO2=(0kZ;^r**l;^gsTb?XcJVp#IJYc#kfX8UlKXOTi7a=GN;q5D7B z4qdQJlZe^BRH+!5)bgU*pi?D?muJ8=hS+prS&kFS6^aqA8SOL8l*nt>D3l?acrMHJ zD(ux1_jD15(UF3 zOvcOGl*w?C{5AaX^gNHQ8cp&_X`iKhp-hl$E_mjf*&b_<^m=NX)L9-rt4xewS}Lnw zci;kI@OLrxLDt^FUz2h7FUkanGR3E7WhMR#(?|w(+o`48_@6Qf;xyJ)pIzES$Vi0> zSaRDwld)?HH=YAy)u`pdl$tNy4n7MW;a^MAHG4h9fPx~WtT|R6-rLqT*2nD;v2fh0 zxpmGD=jcC*|iKAH2z7<6~>;6N)fAmIz-CYMe}69u-tb%Yx8o&?V%~?ub(`n zmAE38aj{V0RG*p6tI)pN^UH@<;X-C1=ApE>@;)C5`EF5lmB}7;%XTGdG{Yu8Cb6Ve zYq(%~+;sq$xzI01&#;fb)t^A<;`@elebf9QA1^gMFDV%p6-7)NKPdU!VI07ltCx;K zC*M}~PR{B#WNOJAulFy)E~4f&7&#zqpUK#LG#5*(Xv4HO|L6jj(E)=>mt85ba!@*% z=8e5I+Wi~Y!UecBYNOvylEvg-3e=QF@4kR0j)%tGZMrhAc4(?BocJ1G!W+H92f&Hv z!7X8wZ66$O0vFO!%~@~ohD%SMhF4zvie@?#?d|vXwcY!op*XxHDHxcpikH@jMiC%QEl z72Ma*6QR&G?2@zH5bI|${w<3SBr5zk=6H-A)v`6iK@vC;+h;O1&F3Pq+%d;W**^q~ z`J;BQqfm;2%C>R=jjsLB(K7(V;sx5IJ6tGHc<}aw?|+|-k!uegP@M82UXvIO@96gO z2i2GEqAgj#qBv1-m~_;bY7D0zr;8HYU!6S+7}xQ`v}yZH#-b7d4zI9K#h${;{TpGB z;nFk{bgIUHoyEA*@p@q{4@G84zPo+qpvBmL+%itO z+cm<-g-XiF$t>386z3SSGjIT%?f7xJ%RRRK^v}@qFoutYjICGjU~SL2`Q>5{ZDx{R zMs9YNRPFtL8?ibFiwB|Dw9^Z(wkO!b6^{A=;5<*!n)yVYR?9>Y_ zr-HVDW2ITPN&n@c>Ix9iqQ0B{(Lf8R_lPz4On*`{lE}N# zMWwt(Vh9vm7*2@aMe0M)VCl02Ld=(ee$*Lu)vJg{htoOO!RF!Nr#lTum>W~2qo17uS`_OU98 zzb4}gKZqbLX#hO!g9F{>H>`Nn+Y#PS^A)_w`lixZ%8<;bvA)y}} zelFsZt^_dnH+evQdO$zzxud^gVbFYmw1JgRw6>oKE#ApY9ogrwlJ$3DQJo`(kt~7!NDCM3S}k2gbzf$owwNpxv%3#qVH!vTW*-{qU;o;c+Nh+XOM1DBaR;OVC{{ zyi5%^Lu2n}QiW*z0@}llK6}=PHST(_hB9#8OgSh~@}^h+7EjHGGf2U3P){?WKn~BG zQP(AThlefPC}rcRkA;xd+>kg8 z^FigGewc{~ARLF3sLk~FhY(g=Z20pQ4NX>pfW9Ci&8?ldE`&n`F9WX^6TV$K`2=|Q z23%ne!s@CUA~5sY$d+wS?up94_%5s2wRv2Nkt`SX+?B^H)?w1m&*Y+pq$b=`DA{)25T}0VW-ejIL)&?u(!rhgT=gJ5AX#c<)Z2_J{RQlYZe35vurv z(2~nhaqY3lSTIIPnwZDi2;7Y=f906l)jjj^@X=`8he%=jka0Glk_z8@$wo`XN3N|% z#sEjcNzkCAPdgD{BWFa9bnTAPdhI{}T#sO#cHl8D5v2INuGBsA^oJ3!rp=em^EEy~ zG(pwr@ODezZvsaD89_BQx!MszEK!2&?gqcQc=TKV#(@q+tC2Eb`8lF872}R>*gO`N zVMGfNH`qRtaYM8a(ENa>uF!zg2{-5*zUKD*?SL8rVCon`o)>}Q+@tJUW63{u2xDav zmm!xxHTRDZVPa{Lm!Fi0H40}ANZUGn^G8kaVi*j$i%oA9#v0>9fHIGey*K{Sh*x2A zRZ&vPWKI#n3Em@nH*Mk<{B7)xX-e>;xw@9G zaACQ7i4&GD9TW?MWc=uwu3Ut|+QbyfU@WCwUAaND0?fXJ!b_C&y5YcEEdgEs@j>zSzYX-EY){q!U!h3+2-mt|h zO(eFyR2!R)!6?moyp)d`u+M_PsIU=*&prlv_l+Z^E{4Qhvwllzc#PSF{Ec3 z(({V_u^dIfnUv|5mgl&aRywomllhRb8NUepte?sFr|%TX$;CYhR5F{Eot>Oy@V7)3 zJdsQU$YlO#$ajq|GEYAp7g4bEklR=qSeY>P2}CSsU{-YI4LZzKLvxNf0ks%bdX9DTTgTh(Te-D7-fL3C>oMq$hoC6sP!=B0dStiS0R#O4 z`~qFPj>eF$j>%Zw9`oM2$n1A(>~Qu6+kY43k;g~Gg^db}3$u5O46*6<$S}sp7xAN$ zysl0jaq^gmh-fL?v!_!wviA^|V$7)O_np9zSM4PpoHcZC_3@hCXe68N+0Xi!jLWad zHxzH^=`z`23f{zUpmX^?tk5*znPj`6>2-Tp+wpe0qKsO0m(cz;;n?RV#f3#jM@+Qu zmSt%Crur9;gdmH00&A-fYs>N1WK2;mMQ}LzOj%seb!;%Nf^$Ls$o65Q@t$H$cywWk z7-BB_6=QrmBG3%BiC~KOyFG13ZI`-hV#LUpi4NzI*RBt|!5GPYK}5vw?utb?kd!HL zyP*C$f@BTmKFts!VPk8>5?p52Fe2&cHe5-z;*@@kWhAwMU|4{iix6_m-s@1Ua@EQb zFfC@_#K-V%u;$;GeuomRI<{4*m56U1=ZmMaH%wx|x4}eIW_y*Q_^e~tkYoPnNImzQ zYC#kJ95E>_!hzup?j=!HhSNx(P^~O3&kNdgJj^%_$0ON%?Bl6YCzshi+|rfi|DwP- zU!G&hPHia2?h@U%ZM*aua;BO@Of!^(_f-oM@V)x#si^swBz$ockaA;RZ*D)jpqGg7 zrr>c){*Jie6edt=#j66<%5u(lI$FDe)k_jkzHZ&#U>0JqAGPBSA!>zjIe)GCVSGCX z!9*z806(f)kjtc}(^bR(zGjx1i`oHx{YmWSl4I`&wXC1XSfNaUfZjJham)4WsL@QG zC|w&58Kg)95vM)%zBNB#Jyi`}2U=T?zb0el3yK8ri99Ui)78wKGHzj|Z2L^cb5j(- za|w0h@iO*2paxO5BtU#^sv>lkSwxNTC%zO**-AgV3c=c7(8(p&X&)(2*Eu$xtC9=C z1l)RS|H!o7aGlt8leo^?FR2wJqS^8IwC%e5T} zgV*?(jG3wg3CO;6`h&gfpw~8#oyK(@8EW(q5nbmWAIpkEvn>gFMDDX3HFEeQw~tck zda~4@Mv7C;QA-NVRU^b@zEQuoN>-SyVlB!C(R__db2g>+LzSWgoYNdE>Czd=3tXI+ zIK9dOxegTTBw}4%LqPpGur3Pjm9pErM4cq(yrae%H%FZur_2kpIt2VhDs8ShF)nkD zYLzW3j#UQMB2et7JXWMedsu}S0q6d*_oMqCVL?6)NfhdTa*T3a$Ja%~x6k9;LodR^ zor5HrGc%Mbm%`^78+YqOPDiEkbu}^sQ@Fh=rCBr08_LBvr8D(y)ha}|Ol2BpwSAMT z4K3cQ#pO^J=j~Q|PG4m&Ty%niqb?^`og@LFJ@WG^j$l)u<}K;!G^9+GMj}p^mj5Tk z0EO7?J<7L)3RQxf)0&P@k2NaA2$)?RI-m;6M)T`C)DlMuBQ)vp#plW-I5!bWsiNIV@ddWg zeeulE%r@AMrafvEir^ZB{mvEjC0SqbHL$B(>QD$%Xc&`2uk?M0;Br0ktklB4Qz1q) z+;4TLUR@9K#*>7S$=>@r<;pl8a@t{uN7Tp=414kQlLy!#qR9@i`%a&8U5*1Yh6@gJ#z~-UE(fQ&*{7l$=n|dJ&b5$1eD5 z5E}m%l=U=&~2wXs(t(o`EF@G@uKR;{B@z-ShMhU)C*3f`D z=@P%~)-tE?7A{6?zU6Jtu*X$0z#KX;>{Kd4z@A&Cor|lmBzP5z59+upuPD?)#GrY- zJa3PJ0_+4Xts`c-s!#-v^z^b7zx4oXnC;TVz!jzVa>p{*UV6?i ztGdCK&3lYAM4$GHLRDN|bkIc6A)y@loG3_)RkTaN;E{P{uitN@(d58PX#Z(%iIj3C zOhD&B&0p^I4Y+m)Q!L$ET+qaW7p^%xlj^9Kh}Xp}V)jNOgEJTgN|yy|o2iw>CwQ%< z^L(otqv@~67HTDNnQk8hQB%Fe7i7M;rVa25V!I_StP4E9?r@pCM$Br7Qg<`9C07%H z0BbNUIoZTaDEpnruFU}79PWmCgFfy`M2Lp{&bt0RK0+AX4SS+=HecwW1fM_Duzgz! zg^s`+Pi5$WDbYTRrt|qRMFP0|sqNs=CK4K?;YZz9b_XS`cjhMTc}mCFy(3o+E7ZmE zegj?eSoT|bbc|D#;0v78k|EosWMj^~ha3rA$voCsLF+{X{WPk))!N@rV$B90fX{6!jvf|!;Q#&)^st&T literal 0 HcmV?d00001 diff --git a/Lib/test/win_console_handler.py b/Lib/test/win_console_handler.py new file mode 100644 index 00000000000..e7779b93635 --- /dev/null +++ b/Lib/test/win_console_handler.py @@ -0,0 +1,49 @@ +"""Script used to test os.kill on Windows, for issue #1220212 + +This script is started as a subprocess in test_os and is used to test the +CTRL_C_EVENT and CTRL_BREAK_EVENT signals, which requires a custom handler +to be written into the kill target. + +See http://msdn.microsoft.com/en-us/library/ms685049%28v=VS.85%29.aspx for a +similar example in C. +""" + +from ctypes import wintypes, WINFUNCTYPE +import signal +import ctypes +import mmap +import sys + +# Function prototype for the handler function. Returns BOOL, takes a DWORD. +HandlerRoutine = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) + +def _ctrl_handler(sig): + """Handle a sig event and return 0 to terminate the process""" + if sig == signal.CTRL_C_EVENT: + pass + elif sig == signal.CTRL_BREAK_EVENT: + pass + else: + print("UNKNOWN EVENT") + return 0 + +ctrl_handler = HandlerRoutine(_ctrl_handler) + + +SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler +SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL) +SetConsoleCtrlHandler.restype = wintypes.BOOL + +if __name__ == "__main__": + # Add our console control handling function with value 1 + if not SetConsoleCtrlHandler(ctrl_handler, 1): + print("Unable to add SetConsoleCtrlHandler") + exit(-1) + + # Awake main process + m = mmap.mmap(-1, 1, sys.argv[1]) + m[0] = 1 + + # Do nothing but wait for the signal + while True: + pass From 492f03135c32492d0f82d51b411568f8c1dfe859 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Wed, 14 Jan 2026 18:51:58 -0500 Subject: [PATCH 2/4] Annotated/removing failing tests with expectedFailures/skips --- Lib/test/test__locale.py | 3 + Lib/test/test_codecencodings_cn.py | 4 + Lib/test/test_codecencodings_hk.py | 1 + Lib/test/test_codecencodings_iso2022.py | 5 ++ Lib/test/test_codecencodings_jp.py | 7 ++ Lib/test/test_codecencodings_kr.py | 3 + Lib/test/test_codecencodings_tw.py | 1 + Lib/test/test_codecmaps_cn.py | 3 + Lib/test/test_codecmaps_hk.py | 1 + Lib/test/test_codecmaps_jp.py | 5 ++ Lib/test/test_codecmaps_kr.py | 3 + Lib/test/test_codecmaps_tw.py | 2 + Lib/test/test_context.py | 1 + Lib/test/test_coroutines.py | 27 ++++++ Lib/test/test_flufl.py | 1 + Lib/test/test_fork1.py | 1 + Lib/test/test_frame.py | 21 +++++ Lib/test/test_free_threading/test_dict.py | 4 + Lib/test/test_free_threading/test_gc.py | 2 + Lib/test/test_free_threading/test_tokenize.py | 1 + Lib/test/test_frozen.py | 2 + Lib/test/test_generator_stop.py | 1 + Lib/test/test_module/__init__.py | 1 + Lib/test/test_os.py | 12 +-- Lib/test/test_peepholer.py | 67 ++++++++------- Lib/test/test_regrtest.py | 1 - Lib/test/test_sax.py | 43 ++++++++++ Lib/test/test_select.py | 3 + Lib/test/test_socket.py | 2 + Lib/test/test_source_encoding.py | 83 +++++++++++++++++++ Lib/test/test_strptime.py | 9 ++ Lib/test/test_termios.py | 15 ++++ Lib/test/test_traceback.py | 2 - Lib/test/test_type_annotations.py | 9 ++ Lib/test/test_zipimport_support.py | 3 + Lib/test/test_zoneinfo/test_zoneinfo.py | 4 + 36 files changed, 311 insertions(+), 42 deletions(-) diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index 606dd297452..18a185d3826 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -161,6 +161,7 @@ def test_lc_numeric_nl_langinfo(self): support.is_emscripten or support.is_wasi, "musl libc issue on Emscripten, bpo-46390" ) + @unittest.skipIf(sys.platform != 'linux', 'TODO: RUSTPYTHON; Crashes - localeconv always return decodable string: Utf8Error { valid_up_to: 0, error_len: Some(1) }') def test_lc_numeric_localeconv(self): # Test localeconv against known values tested = False @@ -179,6 +180,7 @@ def test_lc_numeric_localeconv(self): self.skipTest('no suitable locales') @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available") + @unittest.skipIf(sys.platform != 'linux', 'TODO: RUSTPYTHON; Crashes - localeconv always return decodable string: Utf8Error { valid_up_to: 0, error_len: Some(1) }') def test_lc_numeric_basic(self): # Test nl_langinfo against localeconv tested = False @@ -279,6 +281,7 @@ def test_era_nl_langinfo(self): if not tested: self.skipTest('no suitable locales') + @unittest.skipIf(sys.platform != 'linux', 'TODO: RUSTPYTHON; Crashes - localeconv always return decodable string: Utf8Error { valid_up_to: 0, error_len: Some(1) }') def test_float_parsing(self): # Bug #1391872: Test whether float parsing is okay on European # locales. diff --git a/Lib/test/test_codecencodings_cn.py b/Lib/test/test_codecencodings_cn.py index 2a450718d5a..42e0d8b17f5 100644 --- a/Lib/test/test_codecencodings_cn.py +++ b/Lib/test/test_codecencodings_cn.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: gb2312") class Test_GB2312(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'gb2312' tstring = multibytecodec_support.load_teststring('gb2312') @@ -19,6 +20,7 @@ class Test_GB2312(multibytecodec_support.TestBase, unittest.TestCase): (b"\xc1\x64", "strict", None), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: gbk") class Test_GBK(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'gbk' tstring = multibytecodec_support.load_teststring('gbk') @@ -33,6 +35,7 @@ class Test_GBK(multibytecodec_support.TestBase, unittest.TestCase): ("\u30fb", "strict", None), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: gb18030") class Test_GB18030(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'gb18030' tstring = multibytecodec_support.load_teststring('gb18030') @@ -57,6 +60,7 @@ class Test_GB18030(multibytecodec_support.TestBase, unittest.TestCase): ) has_iso10646 = True +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: hz") class Test_HZ(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'hz' tstring = multibytecodec_support.load_teststring('hz') diff --git a/Lib/test/test_codecencodings_hk.py b/Lib/test/test_codecencodings_hk.py index c5e2f999efc..522c03f6d1a 100644 --- a/Lib/test/test_codecencodings_hk.py +++ b/Lib/test/test_codecencodings_hk.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: big5hkscs") class Test_Big5HKSCS(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'big5hkscs' tstring = multibytecodec_support.load_teststring('big5hkscs') diff --git a/Lib/test/test_codecencodings_iso2022.py b/Lib/test/test_codecencodings_iso2022.py index 027dbecc613..169c08ceb8f 100644 --- a/Lib/test/test_codecencodings_iso2022.py +++ b/Lib/test/test_codecencodings_iso2022.py @@ -10,6 +10,7 @@ (b'ab\x1B$def', 'replace', 'ab\uFFFD'), ) +@unittest.skip('TODO: RUSTPYTHON; LookupError: unknown encoding: iso2022_jp') class Test_ISO2022_JP(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_jp' tstring = multibytecodec_support.load_teststring('iso2022_jp') @@ -17,6 +18,7 @@ class Test_ISO2022_JP(multibytecodec_support.TestBase, unittest.TestCase): (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), ) +@unittest.skip('TODO: RUSTPYTHON; LookupError: unknown encoding: iso2022_jp_2') class Test_ISO2022_JP2(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_jp_2' tstring = multibytecodec_support.load_teststring('iso2022_jp') @@ -24,6 +26,7 @@ class Test_ISO2022_JP2(multibytecodec_support.TestBase, unittest.TestCase): (b'ab\x1BNdef', 'replace', 'abdef'), ) +@unittest.skip('TODO: RUSTPYTHON; LookupError: unknown encoding: iso2022_jp_3') class Test_ISO2022_JP3(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_jp_3' tstring = multibytecodec_support.load_teststring('iso2022_jp') @@ -47,6 +50,7 @@ class Test_ISO2022_JP3(multibytecodec_support.TestBase, unittest.TestCase): b'\x1B$(O\x29\x28\x1B(Bℜ\x1B$(O\x29\x32\x1B(B = ⟨ሴ⟩' ) +@unittest.skip('TODO: RUSTPYTHON; LookupError: unknown encoding: iso2022_jp_2004') class Test_ISO2022_JP2004(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_jp_2004' tstring = multibytecodec_support.load_teststring('iso2022_jp') @@ -70,6 +74,7 @@ class Test_ISO2022_JP2004(multibytecodec_support.TestBase, unittest.TestCase): b'\x1B$(Q\x29\x28\x1B(Bℜ\x1B$(Q\x29\x32\x1B(B = ⟨ሴ⟩' ) +@unittest.skip('TODO: RUSTPYTHON; LookupError: unknown encoding: iso2022_kr') class Test_ISO2022_KR(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_kr' tstring = multibytecodec_support.load_teststring('iso2022_kr') diff --git a/Lib/test/test_codecencodings_jp.py b/Lib/test/test_codecencodings_jp.py index 94378d124f7..ff617d39691 100644 --- a/Lib/test/test_codecencodings_jp.py +++ b/Lib/test/test_codecencodings_jp.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: cp932") class Test_CP932(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'cp932' tstring = multibytecodec_support.load_teststring('shift_jis') @@ -39,6 +40,7 @@ class Test_CP932(multibytecodec_support.TestBase, unittest.TestCase): (b"\x8eXY", "replace", "\ufffdXY"), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: iso2022_jp_2004") class Test_EUC_JIS_2004(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'euc_jis_2004' @@ -49,6 +51,7 @@ class Test_EUC_JIS_2004(multibytecodec_support.TestBase, b"\xa9\xa8ℜ\xa9\xb2 = ⟨ሴ⟩" ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: euc_jisx0213") class Test_EUC_JISX0213(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'euc_jisx0213' @@ -59,6 +62,7 @@ class Test_EUC_JISX0213(multibytecodec_support.TestBase, b"\xa9\xa8ℜ\xa9\xb2 = ⟨ሴ⟩" ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: euc_jp") class Test_EUC_JP_COMPAT(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'euc_jp' @@ -74,6 +78,7 @@ class Test_EUC_JP_COMPAT(multibytecodec_support.TestBase, (b"abc\x80\x80\x82\x84def", "ignore", "abc\uff44def"), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: shift_jis") class Test_SJIS_COMPAT(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'shift_jis' tstring = multibytecodec_support.load_teststring('shift_jis') @@ -88,6 +93,7 @@ class Test_SJIS_COMPAT(multibytecodec_support.TestBase, unittest.TestCase): (b"abc\xFF\x58", "replace", "abc\ufffdX"), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: shift_jis_2004") class Test_SJIS_2004(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'shift_jis_2004' tstring = multibytecodec_support.load_teststring('shift_jis') @@ -106,6 +112,7 @@ class Test_SJIS_2004(multibytecodec_support.TestBase, unittest.TestCase): b"\x85Gℜ\x85Q = ⟨ሴ⟩" ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: shift_jisx0213") class Test_SJISX0213(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'shift_jisx0213' tstring = multibytecodec_support.load_teststring('shift_jisx0213') diff --git a/Lib/test/test_codecencodings_kr.py b/Lib/test/test_codecencodings_kr.py index 863d16b8fd6..e38f78d6cd5 100644 --- a/Lib/test/test_codecencodings_kr.py +++ b/Lib/test/test_codecencodings_kr.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: cp949") class Test_CP949(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'cp949' tstring = multibytecodec_support.load_teststring('cp949') @@ -18,6 +19,7 @@ class Test_CP949(multibytecodec_support.TestBase, unittest.TestCase): (b"abc\x80\x80\xc1\xc4", "ignore", "abc\uc894"), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: euc_kr") class Test_EUCKR(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'euc_kr' tstring = multibytecodec_support.load_teststring('euc_kr') @@ -49,6 +51,7 @@ class Test_EUCKR(multibytecodec_support.TestBase, unittest.TestCase): (b"\xc1\xc4", "strict", "\uc894"), ) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: johab") class Test_JOHAB(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'johab' tstring = multibytecodec_support.load_teststring('johab') diff --git a/Lib/test/test_codecencodings_tw.py b/Lib/test/test_codecencodings_tw.py index bb1d133cad1..101f2615a05 100644 --- a/Lib/test/test_codecencodings_tw.py +++ b/Lib/test/test_codecencodings_tw.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: big5") class Test_Big5(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'big5' tstring = multibytecodec_support.load_teststring('big5') diff --git a/Lib/test/test_codecmaps_cn.py b/Lib/test/test_codecmaps_cn.py index 89e51c6e2a1..b0c3c87c1a9 100644 --- a/Lib/test/test_codecmaps_cn.py +++ b/Lib/test/test_codecmaps_cn.py @@ -6,16 +6,19 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: gb2312") class TestGB2312Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'gb2312' mapfileurl = 'http://www.pythontest.net/unicode/EUC-CN.TXT' +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: gbk") class TestGBKMap(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'gbk' mapfileurl = 'http://www.pythontest.net/unicode/CP936.TXT' +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: gb18030") class TestGB18030Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'gb18030' diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py index 7a48d24fddb..01f16863921 100644 --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: big5hkscs") class TestBig5HKSCSMap(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'big5hkscs' diff --git a/Lib/test/test_codecmaps_jp.py b/Lib/test/test_codecmaps_jp.py index fdfec8085b2..db498c8516e 100644 --- a/Lib/test/test_codecmaps_jp.py +++ b/Lib/test/test_codecmaps_jp.py @@ -6,6 +6,7 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: cp932") class TestCP932Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'cp932' @@ -21,6 +22,7 @@ class TestCP932Map(multibytecodec_support.TestBase_Mapping, supmaps.append((bytes([i]), chr(i+0xfec0))) +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: euc_jp") class TestEUCJPCOMPATMap(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'euc_jp' @@ -28,6 +30,7 @@ class TestEUCJPCOMPATMap(multibytecodec_support.TestBase_Mapping, mapfileurl = 'http://www.pythontest.net/unicode/EUC-JP.TXT' +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: shift_jis") class TestSJISCOMPATMap(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'shift_jis' @@ -42,6 +45,7 @@ class TestSJISCOMPATMap(multibytecodec_support.TestBase_Mapping, (b'\x81_', '\\'), ] +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: euc_jisx0213") class TestEUCJISX0213Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'euc_jisx0213' @@ -49,6 +53,7 @@ class TestEUCJISX0213Map(multibytecodec_support.TestBase_Mapping, mapfileurl = 'http://www.pythontest.net/unicode/EUC-JISX0213.TXT' +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: shift_jisx0213") class TestSJISX0213Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'shift_jisx0213' diff --git a/Lib/test/test_codecmaps_kr.py b/Lib/test/test_codecmaps_kr.py index 1b6f5ca3080..81f328d81ed 100644 --- a/Lib/test/test_codecmaps_kr.py +++ b/Lib/test/test_codecmaps_kr.py @@ -6,12 +6,14 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: cp949") class TestCP949Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'cp949' mapfileurl = 'http://www.pythontest.net/unicode/CP949.TXT' +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: euc_kr") class TestEUCKRMap(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'euc_kr' @@ -22,6 +24,7 @@ class TestEUCKRMap(multibytecodec_support.TestBase_Mapping, pass_dectest = [(b'\xa4\xd4', '\u3164')] +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: johab") class TestJOHABMap(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'johab' diff --git a/Lib/test/test_codecmaps_tw.py b/Lib/test/test_codecmaps_tw.py index 145a97d511e..62bbffcc039 100644 --- a/Lib/test/test_codecmaps_tw.py +++ b/Lib/test/test_codecmaps_tw.py @@ -6,11 +6,13 @@ from test import multibytecodec_support import unittest +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: big5") class TestBIG5Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'big5' mapfileurl = 'http://www.pythontest.net/unicode/BIG5.TXT' +@unittest.skip("TODO: RUSTPYTHON; LookupError: unknown encoding: cp950") class TestCP950Map(multibytecodec_support.TestBase_Mapping, unittest.TestCase): encoding = 'cp950' diff --git a/Lib/test/test_context.py b/Lib/test/test_context.py index 6083b532223..9caa35a7e74 100644 --- a/Lib/test/test_context.py +++ b/Lib/test/test_context.py @@ -9,6 +9,7 @@ import weakref from test import support from test.support import threading_helper +import sys # TODO: RUSTPYTHON; Needed for skip try: from _testinternalcapi import hamt diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index f705f4f5bfb..306711a4464 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -563,6 +563,7 @@ async def foo(): self.assertRegex(repr(coro), '^$') coro.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_4(self): async def foo(): raise StopIteration @@ -592,6 +593,7 @@ async def foo(): coro.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_5(self): @types.coroutine def bar(): @@ -647,6 +649,7 @@ def foo(): coro.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_8(self): @types.coroutine def bar(): @@ -659,6 +662,7 @@ async def foo(): self.assertEqual(run_async(bar()), ([], 'spam')) coro.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_9(self): async def foo(): pass @@ -769,6 +773,7 @@ async def coro(): "coroutine ignored GeneratorExit"): c.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_15(self): # See http://bugs.python.org/issue25887 for details @@ -786,6 +791,7 @@ async def reader(coro): 'cannot reuse already awaited coroutine'): reader(spammer_coro).send(None) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_16(self): # See http://bugs.python.org/issue25887 for details @@ -817,6 +823,7 @@ async def read(coro): 'cannot reuse already awaited coroutine'): reader.throw(Exception('wat')) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_func_17(self): # See http://bugs.python.org/issue25887 for details @@ -925,6 +932,7 @@ async def foo(): self.assertIsInstance(result[1], StopIteration) self.assertEqual(result[1].value, 10) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_cr_await(self): @types.coroutine def a(): @@ -955,6 +963,7 @@ async def b(): self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_CLOSED) self.assertIsNone(coro_b.cr_await) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_corotype_1(self): ct = types.CoroutineType if not support.MISSING_C_DOCSTRINGS: @@ -1093,6 +1102,7 @@ async def foo2(): self.assertEqual(run_async(foo2()), ([], ('spam', 'ham'))) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_await_12(self): async def coro(): return 'spam' @@ -1297,6 +1307,7 @@ async def func(): with self.assertRaises(AssertionError): run_async(func()) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_with_6(self): class CM: def __aenter__(self): @@ -1316,6 +1327,7 @@ async def foo(): # it's important that __aexit__ wasn't called run_async(foo()) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_with_7(self): class CM: async def __aenter__(self): @@ -1342,6 +1354,7 @@ async def foo(): self.fail('invalid asynchronous context manager did not fail') + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_with_8(self): CNT = 0 @@ -1585,6 +1598,7 @@ async def test3(): self.assertEqual(buffer, [i for i in range(1, 21)] + ['what?', 'end']) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_for_2(self): tup = (1, 2, 3) refs_before = sys.getrefcount(tup) @@ -1600,6 +1614,7 @@ async def foo(): self.assertEqual(sys.getrefcount(tup), refs_before) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_for_3(self): class I: def __aiter__(self): @@ -1620,6 +1635,7 @@ async def foo(): self.assertEqual(sys.getrefcount(aiter), refs_before) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_for_4(self): class I: def __aiter__(self): @@ -1767,6 +1783,7 @@ async def foo(): run_async(foo()) self.assertEqual(CNT, 0) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_for_11(self): class F: def __aiter__(self): @@ -2042,6 +2059,7 @@ async def f(): run_async(f()), ([], {1: 1, 2: 2, 3: 3})) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_nested_comp(self): async def run_list_inside_list(): return [[i + j async for i in asynciter([1, 2])] for j in [10, 20]] @@ -2124,6 +2142,7 @@ async def func(): pass finally: aw.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_fatal_coro_warning(self): # Issue 27811 async def func(): pass @@ -2139,6 +2158,7 @@ async def func(): pass self.assertIn("was never awaited", str(cm.unraisable.exc_value)) self.assertEqual(repr(cm.unraisable.object), coro_repr) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_for_assign_raising_stop_async_iteration(self): class BadTarget: def __setitem__(self, key, value): @@ -2172,6 +2192,7 @@ async def run_gen(): return 'end' self.assertEqual(run_async(run_gen()), ([], 'end')) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_for_assign_raising_stop_async_iteration_2(self): class BadIterable: def __iter__(self): @@ -2204,6 +2225,7 @@ async def run_gen(): return 'end' self.assertEqual(run_async(run_gen()), ([], 'end')) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_bpo_45813_1(self): 'This would crash the interpreter in 3.11a2' async def f(): @@ -2212,6 +2234,7 @@ async def f(): frame = f().cr_frame frame.clear() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_bpo_45813_2(self): 'This would crash the interpreter in 3.11a2' async def f(): @@ -2221,6 +2244,7 @@ async def f(): gen.cr_frame.clear() gen.close() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_cr_frame_after_close(self): async def f(): pass @@ -2304,6 +2328,7 @@ def here(self): info = inspect.getframeinfo(inspect.currentframe().f_back) return (info.filename, info.lineno) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_origin_tracking(self): orig_depth = sys.get_coroutine_origin_tracking_depth() try: @@ -2350,6 +2375,7 @@ def nested(): finally: sys.set_coroutine_origin_tracking_depth(orig_depth) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_origin_tracking_warning(self): async def corofn(): pass @@ -2392,6 +2418,7 @@ def check(depth, msg): finally: sys.set_coroutine_origin_tracking_depth(orig_depth) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_unawaited_warning_when_module_broken(self): # Make sure we don't blow up too bad if # warnings._warn_unawaited_coroutine is broken somehow (e.g. because diff --git a/Lib/test/test_flufl.py b/Lib/test/test_flufl.py index d77e481c81d..06c0018c721 100644 --- a/Lib/test/test_flufl.py +++ b/Lib/test/test_flufl.py @@ -2,6 +2,7 @@ import unittest +@unittest.expectedFailure # TODO: RUSTPYTHON; barry_as_FLUFL does not exist in RustPython class FLUFLTests(unittest.TestCase): def test_barry_as_bdfl(self): diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index a6523bbc518..892f6a629ea 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -19,6 +19,7 @@ class ForkTest(ForkWait): + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: process 846604 exited with code 1, but exit code 42 is expected def test_threaded_import_lock_fork(self): """Check fork() in main thread works while a subthread is doing an import""" import_started = threading.Event() diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 2f802179cb3..72f875a2d51 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -43,6 +43,7 @@ def clear_traceback_frames(self, tb): tb.tb_frame.clear() tb = tb.tb_next + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not <__main__.ClearTest.test_clear_locals..C object at 0x56044c5f5d10> def test_clear_locals(self): class C: pass @@ -58,6 +59,7 @@ class C: # The reference was released by .clear() self.assertIs(None, wr()) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: <__main__.ClearTest.test_clear_locals_after_f_locals_access..C object at 0x55813425b0f0> is not None def test_clear_locals_after_f_locals_access(self): # see gh-113939 class C: @@ -92,6 +94,7 @@ class C: self.assertIsNot(f.f_builtins, None) self.assertIsNot(f.f_globals, None) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised def test_clear_generator(self): endly = False def g(): @@ -110,6 +113,7 @@ def g(): gen.gi_frame.clear() self.assertFalse(endly) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised def test_clear_executing(self): # Attempting to clear an executing frame is forbidden. try: @@ -121,6 +125,7 @@ def test_clear_executing(self): with self.assertRaises(RuntimeError): f.f_back.clear() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised def test_clear_executing_generator(self): # Attempting to clear an executing generator frame is forbidden. endly = False @@ -201,6 +206,7 @@ def inner(): tb = tb.tb_next return frames + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: {'y': 6, 'inner': .outer..inner at 0x56398a5d31b0>,\n- 'x': 5,\n- 'y': 6} def test_clear_locals(self): # Test f_locals after clear() (issue #21897) f, outer, inner = self.make_frames() @@ -209,6 +215,7 @@ def test_clear_locals(self): self.assertEqual(outer.f_locals, {}) self.assertEqual(inner.f_locals, {}) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: {'y': 6, 'inner': .outer..inner at 0x56398a5d31b0>,\n- 'x': 5,\n- 'y': 6} def test_locals_clear_locals(self): # Test f_locals before and after clear() (to exercise caching) f, outer, inner = self.make_frames() @@ -230,6 +237,7 @@ class ReprTest(unittest.TestCase): Tests for repr(frame). """ + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Regex didn't match: "^$" not found in '' def test_repr(self): def outer(): x = 5 @@ -265,6 +273,7 @@ def inner(): % (file_repr, offset + 5)) class TestFrameLocals(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertEqual(x, 2)\nAssertionError: 1 != 2 def test_scope(self): class A: x = 1 @@ -282,6 +291,7 @@ def f(): self.assertEqual(locals()['y'], 2) f() + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertEqual(x, 2)\nAssertionError: 1 != 2 def test_closure(self): x = 1 y = 2 @@ -304,6 +314,7 @@ def test_closure_with_inline_comprehension(self): lst = [locals() for k in [0]] self.assertEqual(lst[0]['k'], 0) + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertEqual(len(d), 4)\nAssertionError: 3 != 4 def test_as_dict(self): x = 1 y = 2 @@ -361,6 +372,7 @@ def test_non_string_key(self): d[1] = 2 self.assertEqual(d[1], 2) + @unittest.expectedFailure # TODO: RUSTPYTHON; UnboundLocalError: local variable 'b' referenced before assignment def test_write_with_hidden(self): def f(): f_locals = [sys._getframe().f_locals for b in [0]][0] @@ -372,6 +384,7 @@ def f(): c = 0 f() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: != 'a.b.c' def test_local_objects(self): o = object() k = '.'.join(['a', 'b', 'c']) @@ -402,6 +415,7 @@ def test_repr(self): frame = sys._getframe() self.assertEqual(repr(frame.f_locals), repr(dict(frame.f_locals))) + @unittest.expectedFailure # TODO: RUSTPYTHON; del d['x']\nAssertionError: ValueError not raised def test_delete(self): x = 1 d = sys._getframe().f_locals @@ -445,6 +459,7 @@ def test_sizeof(self): proxy = sys._getframe().f_locals support.check_sizeof(self, proxy, support.calcobjsize("P")) + @unittest.expectedFailure # TODO: RUSTPYTHON; copy.copy(d)\nAssertionError: TypeError not raised def test_unsupport(self): x = 1 d = sys._getframe().f_locals @@ -479,6 +494,7 @@ def __eq__(self, other): return StringSubclass('x'), ImpostorX(), 'x' + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: dict_keys(['obj', 'x']) != ['obj', 'x', 'proxy'] def test_proxy_key_stringlikes_overwrite(self): def f(obj): x = 1 @@ -501,6 +517,7 @@ def f(obj): self.assertEqual(keys_snapshot, expected_keys) self.assertEqual(proxy_snapshot, expected_dict) + @unittest.expectedFailure # TODO: RUSTPYTHON; UnboundLocalError: local variable 'x' referenced before assignment def test_proxy_key_stringlikes_ftrst_write(self): def f(obj): proxy = sys._getframe().f_locals @@ -528,6 +545,7 @@ class ObjectSubclass: with self.assertRaises(TypeError): proxy[obj] = 0 + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'dict' != 'FrameLocalsProxy' def test_constructor(self): FrameLocalsProxy = type([sys._getframe().f_locals for x in range(1)][0]) @@ -614,6 +632,7 @@ def test_eq(self): class TestFrameCApi(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: dlsym failed def test_basic(self): x = 1 ctypes = import_helper.import_module('ctypes') @@ -648,6 +667,7 @@ def test_basic(self): class TestIncompleteFrameAreInvisible(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Process return code is 1 def test_issue95818(self): # See GH-95818 for details code = textwrap.dedent(f""" @@ -711,6 +731,7 @@ def run(self): ) sneaky_frame_object = sneaky_frame_object.f_back + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertIs(catcher.unraisable.exc_type, TypeError)\nAttributeError: 'NoneType' object has no attribute 'exc_type' def test_entry_frames_are_invisible_during_teardown(self): class C: """A weakref'able class.""" diff --git a/Lib/test/test_free_threading/test_dict.py b/Lib/test/test_free_threading/test_dict.py index af6c8b69743..b15e9a10344 100644 --- a/Lib/test/test_free_threading/test_dict.py +++ b/Lib/test/test_free_threading/test_dict.py @@ -18,6 +18,7 @@ @threading_helper.requires_working_threading() class TestDict(TestCase): + @unittest.skip("TODO: RUSTPYTHON; Flaky") def test_racing_creation_shared_keys(self): """Verify that creating dictionaries is thread safe when we have a type with shared keys""" @@ -26,11 +27,13 @@ class C(int): self.racing_creation(C) + @unittest.skip("TODO: RUSTPYTHON; Flaky") def test_racing_creation_no_shared_keys(self): """Verify that creating dictionaries is thread safe when we have a type with an ordinary dict""" self.racing_creation(Or) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: a04 missing at 1 def test_racing_creation_inline_values_invalid(self): """Verify that re-creating a dict after we have invalid inline values is thread safe""" @@ -46,6 +49,7 @@ def make_obj(): self.racing_creation(make_obj) + @unittest.skip("TODO: RUSTPYTHON; Flaky") def test_racing_creation_nonmanaged_dict(self): """Verify that explicit creation of an unmanaged dict is thread safe outside of the normal attribute setting code path""" diff --git a/Lib/test/test_free_threading/test_gc.py b/Lib/test/test_free_threading/test_gc.py index 401067fe9c6..726e84e2178 100644 --- a/Lib/test/test_free_threading/test_gc.py +++ b/Lib/test/test_free_threading/test_gc.py @@ -14,6 +14,7 @@ class MyObj: @threading_helper.requires_working_threading() class TestGC(TestCase): + @unittest.skip("TODO: RUSTPYTHON; o = gc.get_objects() - Not ImplementedError") def test_get_objects(self): event = threading.Event() @@ -34,6 +35,7 @@ def mutator_thread(): with threading_helper.start_threads(gcs + mutators): pass + @unittest.skip("TODO: RUSTPYTHON; o = gc.get_referrers() - Not ImplementedError") def test_get_referrers(self): event = threading.Event() diff --git a/Lib/test/test_free_threading/test_tokenize.py b/Lib/test/test_free_threading/test_tokenize.py index 860cfec4d71..e4db31b37e9 100644 --- a/Lib/test/test_free_threading/test_tokenize.py +++ b/Lib/test/test_free_threading/test_tokenize.py @@ -10,6 +10,7 @@ @threading_helper.requires_working_threading() class TestTokenize(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'function' object has no attribute 'TokenizerIter' def test_tokenizer_iter(self): source = io.StringIO("for _ in a:\n pass") it = tokenize._tokenize.TokenizerIter(source.readline, extra_tokens=False) diff --git a/Lib/test/test_frozen.py b/Lib/test/test_frozen.py index 0b4a12bcf40..95f6f5d6386 100644 --- a/Lib/test/test_frozen.py +++ b/Lib/test/test_frozen.py @@ -27,6 +27,7 @@ def test_frozen(self): __hello__.main() self.assertEqual(out.getvalue(), 'Hello world!\n') + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: unexpectedly identical: def test_frozen_submodule_in_unfrozen_package(self): with import_helper.CleanImport('__phello__', '__phello__.spam'): with import_helper.frozen_modules(enabled=False): @@ -39,6 +40,7 @@ def test_frozen_submodule_in_unfrozen_package(self): self.assertIs(spam.__spec__.loader, importlib.machinery.FrozenImporter) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: unexpectedly identical: def test_unfrozen_submodule_in_frozen_package(self): with import_helper.CleanImport('__phello__', '__phello__.spam'): with import_helper.frozen_modules(enabled=True): diff --git a/Lib/test/test_generator_stop.py b/Lib/test/test_generator_stop.py index bc235ceb00e..6df7d5dbb7a 100644 --- a/Lib/test/test_generator_stop.py +++ b/Lib/test/test_generator_stop.py @@ -13,6 +13,7 @@ def g(): "generator raised StopIteration"): next(g()) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: is not def test_stopiteration_wrapping_context(self): def f(): raise StopIteration diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py index c0361518e29..a97284322e4 100644 --- a/Lib/test/test_module/__init__.py +++ b/Lib/test/test_module/__init__.py @@ -362,6 +362,7 @@ def test_annotations_getset_raises(self): with self.assertRaises(AttributeError): del foo.__annotations__ + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertTrue("__annotations__" in ann_module4.__dict__) - AssertionError: False is not true def test_annotations_are_created_correctly(self): ann_module4 = import_helper.import_fresh_module( 'test.typinganndata.ann_module4', diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 4755aef080e..18f2eab5133 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2350,7 +2350,7 @@ def check_bool(self, f, *args, **kwargs): with self.assertRaises(RuntimeWarning): f(fd, *args, **kwargs) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.skip("TODO: RUSTPYTHON; Causes a Segmentation Fault") def test_fdopen(self): self.check(os.fdopen, encoding="utf-8") self.check_bool(os.fdopen, encoding="utf-8") @@ -2407,12 +2407,12 @@ def test_fchmod(self): def test_fchown(self): self.check(os.fchown, -1, -1) - @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipUnless(hasattr(os, 'fpathconf'), 'test needs os.fpathconf()') @unittest.skipIf( support.is_emscripten or support.is_wasi, "musl libc issue on Emscripten/WASI, bpo-46390" ) + @unittest.expectedFailure # TODO: RUSTPYTHON; f(fd, *args, **kwargs)\nAssertionError: RuntimeWarning not raised def test_fpathconf(self): self.assertIn("PC_NAME_MAX", os.pathconf_names) self.check(os.pathconf, "PC_NAME_MAX") @@ -2420,7 +2420,7 @@ def test_fpathconf(self): self.check_bool(os.pathconf, "PC_NAME_MAX") self.check_bool(os.fpathconf, "PC_NAME_MAX") - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; f(fd, *args, **kwargs)\nOSError: [Errno 22] Invalid argument @unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()') def test_ftruncate(self): self.check(os.truncate, 0) @@ -2463,11 +2463,11 @@ def test_blocking(self): self.check(os.get_blocking) self.check(os.set_blocking, True) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'super' object has no attribute 'test_fchdir' def test_fchdir(self): return super().test_fchdir() - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'super' object has no attribute 'test_fsync' def test_fsync(self): return super().test_fsync() @@ -3315,6 +3315,7 @@ def setUp(self): def tearDown(self): shutil.rmtree('base') + @unittest.skip("") def test_directory_link_nonlocal(self): """ The symlink target should resolve relative to the link, not relative @@ -3988,6 +3989,7 @@ def listxattr(path, *args): @unittest.skipUnless(hasattr(os, 'get_terminal_size'), "requires os.get_terminal_size") class TermsizeTests(unittest.TestCase): + @unittest.skip("L") def test_does_not_crash(self): """Check if get_terminal_size() returns a meaningful value. diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 50ac8aef2f3..3d5f3b107de 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -69,7 +69,7 @@ def check_lnotab(self, code): # aren't very many tests of lnotab), if peepholer wasn't scheduled # to be replaced anyway. - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: POP_JUMP_IF_TRUE not found in bytecode def test_unot(self): # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE' def unot(x): @@ -80,7 +80,7 @@ def unot(x): self.assertInBytecode(unot, 'POP_JUMP_IF_TRUE') self.check_lnotab(unot) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (CONTAINS_OP,0) not found in bytecode def test_elim_inversion_of_is_or_in(self): for line, cmp_op, invert in ( ('not a is b', 'IS_OP', 1,), @@ -120,7 +120,7 @@ def f(): self.assertInBytecode(f, 'RETURN_CONST', None) self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_CONST occurs in bytecode def test_while_one(self): # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx def f(): @@ -133,7 +133,7 @@ def f(): self.assertInBytecode(f, elem) self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SWAP occurs in bytecode def test_pack_unpack(self): for line, elem in ( ('a, = a,', 'RETURN_CONST',), @@ -147,7 +147,7 @@ def test_pack_unpack(self): self.assertNotInBytecode(code, 'UNPACK_SEQUENCE') self.check_lnotab(code) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (LOAD_CONST,(1, 2, 3)) not found in bytecode def test_folding_of_tuples_of_constants(self): for line, elem in ( ('a = 1,2,3', (1, 2, 3)), @@ -189,7 +189,7 @@ def crater(): ],) self.check_lnotab(crater) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (LOAD_CONST,((1, 2), 3, 4)) not found in bytecode def test_folding_of_lists_of_constants(self): for line, elem in ( # in/not in constants with BUILD_LIST should be folded to a tuple: @@ -204,7 +204,7 @@ def test_folding_of_lists_of_constants(self): self.assertNotInBytecode(code, 'BUILD_LIST') self.check_lnotab(code) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: BUILD_SET occurs in bytecode def test_folding_of_sets_of_constants(self): for line, elem in ( # in/not in constants with BUILD_SET should be folded to a frozenset: @@ -235,8 +235,7 @@ def g(a): self.assertTrue(g(4)) self.check_lnotab(g) - - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (LOAD_CONST,15) not found in bytecode def test_folding_of_binops_on_constants(self): for line, elem in ( ('a = 2+3+4', 9), # chained fold @@ -282,7 +281,7 @@ def test_folding_of_binops_on_constants(self): self.assertNotIn(2**1000, code.co_consts) self.check_lnotab(code) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (LOAD_CONST,'f') not found in bytecode def test_binary_subscr_on_unicode(self): # valid code get optimized code = compile('"foo"[0]', '', 'single') @@ -306,7 +305,7 @@ def test_binary_subscr_on_unicode(self): self.assertInBytecode(code, 'BINARY_SUBSCR') self.check_lnotab(code) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (LOAD_CONST,1) not found in bytecode def test_folding_of_unaryops_on_constants(self): for line, elem in ( ('-0.5', -0.5), # unary negative @@ -352,7 +351,7 @@ def f(x): self.assertEqual(len(returns), 1) self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; tgt = targets[instr.argval]\nKeyError: 18 def test_elim_jump_to_return(self): # JUMP_FORWARD to RETURN --> RETURN def f(cond, true_value, false_value): @@ -367,7 +366,7 @@ def f(cond, true_value, false_value): self.assertEqual(len(returns), 2) self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; tgt = targets[instr.argval]\nKeyError: 38 def test_elim_jump_to_uncond_jump(self): # POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump def f(): @@ -381,7 +380,7 @@ def f(): self.check_jump_targets(f) self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; tgt = targets[instr.argval]\nKeyError: 32 def test_elim_jump_to_uncond_jump2(self): # POP_JUMP_IF_FALSE to JUMP_BACKWARD --> POP_JUMP_IF_FALSE to non-jump def f(): @@ -393,7 +392,7 @@ def f(): self.check_jump_targets(f) self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; tgt = targets[instr.argval]\nKeyError: 22 def test_elim_jump_to_uncond_jump3(self): # Intentionally use two-line expressions to test issue37213. # POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump @@ -427,7 +426,7 @@ def f(a, b, c): self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1) self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; tgt = targets[instr.argval]\nKeyError: 6 def test_elim_jump_to_uncond_jump4(self): def f(): for i in range(5): @@ -435,7 +434,7 @@ def f(): print(i) self.check_jump_targets(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: JUMP_BACKWARD occurs in bytecode def test_elim_jump_after_return1(self): # Eliminate dead code: jumps immediately after returns can't be reached def f(cond1, cond2): @@ -462,7 +461,7 @@ def g()->1+1: self.assertNotInBytecode(f, 'BINARY_OP') self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertFalse(instr.opname.startswith('BINARY_'))\nAssertionError: True is not false def test_constant_folding(self): # Issue #11244: aggressive constant folding. exprs = [ @@ -527,7 +526,7 @@ def f(x): return 6 self.check_lnotab(f) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertEqual(count_instr_recursively(listcomp, 'FOR_ITER'), 1)\nAssertionError: 2 != 1 def test_assignment_idiom_in_comprehensions(self): def listcomp(): return [y for x in a for y in [f(x)]] @@ -587,7 +586,7 @@ def format(fmt, *values): self.assertEqual(format('x = %s!', '%% %s'), 'x = %% %s!') self.assertEqual(format('x = %s, y = %d', 12, 34), 'x = 12, y = 34') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; eval("'%s%z' % (x,)", {'x': 1234})\nValueError: unsupported format character 'z' (0x7a) at index 3 def test_format_errors(self): with self.assertRaisesRegex(TypeError, 'not enough arguments for format string'): @@ -635,7 +634,7 @@ def f(a, b, c): c, b, a = a, b, c self.assertNotInBytecode(f, "SWAP") - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SWAP occurs in bytecode def test_static_swaps_match_mapping(self): for a, b, c in product("_a", "_b", "_c"): pattern = f"{{'a': {a}, 'b': {b}, 'c': {c}}}" @@ -643,7 +642,7 @@ def test_static_swaps_match_mapping(self): code = compile_pattern_with_fast_locals(pattern) self.assertNotInBytecode(code, "SWAP") - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SWAP occurs in bytecode def test_static_swaps_match_class(self): forms = [ "C({}, {}, {})", @@ -658,7 +657,7 @@ def test_static_swaps_match_class(self): code = compile_pattern_with_fast_locals(pattern) self.assertNotInBytecode(code, "SWAP") - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SWAP occurs in bytecode def test_static_swaps_match_sequence(self): swaps = {"*_, b, c", "a, *_, c", "a, b, *_"} forms = ["{}, {}, {}", "{}, {}, *{}", "{}, *{}, {}", "*{}, {}, {}"] @@ -707,14 +706,14 @@ def setUp(self): self.addCleanup(sys.settrace, sys.gettrace()) sys.settrace(None) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_LOAD_FAST not found in bytecode def test_load_fast_known_simple(self): def f(): x = 1 y = x + x self.assertInBytecode(f, 'LOAD_FAST_LOAD_FAST') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_unknown_simple(self): def f(): if condition(): @@ -723,7 +722,7 @@ def f(): self.assertInBytecode(f, 'LOAD_FAST_CHECK') self.assertNotInBytecode(f, 'LOAD_FAST') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_unknown_because_del(self): def f(): x = 1 @@ -732,7 +731,7 @@ def f(): self.assertInBytecode(f, 'LOAD_FAST_CHECK') self.assertNotInBytecode(f, 'LOAD_FAST') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_known_because_parameter(self): def f1(x): print(x) @@ -759,7 +758,7 @@ def f5(x=0): self.assertInBytecode(f5, 'LOAD_FAST') self.assertNotInBytecode(f5, 'LOAD_FAST_CHECK') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_known_because_already_loaded(self): def f(): if condition(): @@ -769,7 +768,7 @@ def f(): self.assertInBytecode(f, 'LOAD_FAST_CHECK') self.assertInBytecode(f, 'LOAD_FAST') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_known_multiple_branches(self): def f(): if condition(): @@ -780,7 +779,7 @@ def f(): self.assertInBytecode(f, 'LOAD_FAST') self.assertNotInBytecode(f, 'LOAD_FAST_CHECK') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_unknown_after_error(self): def f(): try: @@ -792,7 +791,7 @@ def f(): # Assert that it doesn't occur in the LOAD_FAST_CHECK branch. self.assertInBytecode(f, 'LOAD_FAST_CHECK') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: LOAD_FAST_CHECK not found in bytecode def test_load_fast_unknown_after_error_2(self): def f(): try: @@ -803,7 +802,7 @@ def f(): self.assertInBytecode(f, 'LOAD_FAST_CHECK') self.assertNotInBytecode(f, 'LOAD_FAST') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: (LOAD_FAST_LOAD_FAST,('a00', 'a01')) not found in bytecode def test_load_fast_too_many_locals(self): # When there get to be too many locals to analyze completely, # later locals are all converted to LOAD_FAST_CHECK, except @@ -878,7 +877,7 @@ def trace(frame, event, arg): self.assertNotInBytecode(f, "LOAD_FAST_CHECK") self.assertEqual(f.__code__.co_code, co_code) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_setting_lineno_one_undefined(self): code = textwrap.dedent("""\ def f(): @@ -913,7 +912,7 @@ def trace(frame, event, arg): self.assertNotInBytecode(f, "LOAD_FAST_CHECK") self.assertEqual(f.__code__.co_code, co_code) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered def test_setting_lineno_two_undefined(self): code = textwrap.dedent("""\ def f(): diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 9f09413f59c..0cbfbd0b11f 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -2182,7 +2182,6 @@ def test_dev_mode(self): self.check_executed_tests(output, tests, stats=len(tests), parallel=True) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_unload_tests(self): # Test that unloading test modules does not break tests # that import from other tests. diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 9b3014a94a0..92b67ae7977 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -133,6 +133,7 @@ def check_parse(self, f): parse(f, XMLGenerator(result, 'utf-8')) self.assertEqual(result.getvalue(), xml_str(self.data, 'utf-8')) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parse_text(self): encodings = ('us-ascii', 'iso-8859-1', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be') @@ -146,6 +147,7 @@ def test_parse_text(self): with open(TESTFN, 'r', encoding=encoding) as f: self.check_parse(f) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parse_bytes(self): # UTF-8 is default encoding, US-ASCII is compatible with UTF-8, # UTF-16 is autodetected @@ -188,10 +190,12 @@ def test_parse_bytes(self): with self.assertRaises(SAXException): self.check_parse(f) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parse_path_object(self): make_xml_file(self.data, 'utf-8', None) self.check_parse(FakePath(TESTFN)) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parse_InputSource(self): # accept data without declared but with explicitly specified encoding make_xml_file(self.data, 'iso-8859-1', None) @@ -201,6 +205,7 @@ def test_parse_InputSource(self): input.setEncoding('iso-8859-1') self.check_parse(input) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parse_close_source(self): builtin_open = open fileobj = None @@ -222,6 +227,7 @@ def check_parseString(self, s): parseString(s, XMLGenerator(result, 'utf-8')) self.assertEqual(result.getvalue(), xml_str(self.data, 'utf-8')) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parseString_text(self): encodings = ('us-ascii', 'iso-8859-1', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be') @@ -229,6 +235,7 @@ def test_parseString_text(self): self.check_parseString(xml_str(self.data, encoding)) self.check_parseString(self.data) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_parseString_bytes(self): # UTF-8 is default encoding, US-ASCII is compatible with UTF-8, # UTF-16 is autodetected @@ -578,6 +585,7 @@ def test_xmlgen_ignorable_empty(self): self.assertEqual(result.getvalue(), self.xml(" ")) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_xmlgen_encoding_bytes(self): encodings = ('iso-8859-15', 'utf-8', 'utf-8-sig', 'utf-16', 'utf-16be', 'utf-16le', @@ -708,6 +716,7 @@ def test_1463026_3_empty(self): self.assertEqual(result.getvalue(), self.xml('')) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_5027_1(self): # The xml prefix (as in xml:lang below) is reserved and bound by # definition to http://www.w3.org/XML/1998/namespace. XMLGenerator had @@ -801,6 +810,10 @@ def xml(self, doc, encoding='iso-8859-1'): return ('\n%s' % (encoding, doc)).encode(encoding, 'xmlcharrefreplace') + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? + def test_xmlgen_encoding(self): + super().test_xmlgen_encoding() + class WriterXmlgenTest(BytesXmlgenTest): class ioclass(list): write = list.append @@ -881,6 +894,7 @@ class ExpatReaderTest(XmlTestBase): # ===== XMLReader support + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_binary_file(self): parser = create_parser() result = BytesIO() @@ -892,6 +906,7 @@ def test_expat_binary_file(self): self.assertEqual(result.getvalue(), xml_test_out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_expat_text_file(self): parser = create_parser() result = BytesIO() @@ -904,6 +919,7 @@ def test_expat_text_file(self): self.assertEqual(result.getvalue(), xml_test_out) @requires_nonascii_filenames + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_binary_file_nonascii(self): fname = os_helper.TESTFN_UNICODE shutil.copyfile(TEST_XMLFILE, fname) @@ -918,6 +934,7 @@ def test_expat_binary_file_nonascii(self): self.assertEqual(result.getvalue(), xml_test_out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_binary_file_bytes_name(self): fname = os.fsencode(TEST_XMLFILE) parser = create_parser() @@ -930,6 +947,7 @@ def test_expat_binary_file_bytes_name(self): self.assertEqual(result.getvalue(), xml_test_out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_binary_file_int_name(self): parser = create_parser() result = BytesIO() @@ -968,6 +986,7 @@ def resolveEntity(self, publicId, systemId): source.setSystemId(systemId) return source + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_dtdhandler(self): parser = create_parser() handler = self.TestDTDHandler() @@ -984,6 +1003,7 @@ def test_expat_dtdhandler(self): [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)]) self.assertEqual(handler._entities, [("img", None, "expat.gif", "GIF")]) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_external_dtd_enabled(self): # clear _opener global variable self.addCleanup(urllib.request.urlcleanup) @@ -1001,6 +1021,7 @@ def test_expat_external_dtd_enabled(self): resolver.entities, [(None, 'unsupported://non-existing')] ) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_external_dtd_default(self): parser = create_parser() resolver = self.TestEntityRecorder() @@ -1022,6 +1043,7 @@ def resolveEntity(self, publicId, systemId): inpsrc.setByteStream(BytesIO(b"")) return inpsrc + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_entityresolver_enabled(self): parser = create_parser() parser.setFeature(feature_external_ges, True) @@ -1038,6 +1060,7 @@ def test_expat_entityresolver_enabled(self): self.assertEqual(result.getvalue(), start + b"") + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_entityresolver_default(self): parser = create_parser() self.assertEqual(parser.getFeature(feature_external_ges), False) @@ -1064,6 +1087,7 @@ def startElement(self, name, attrs): def startElementNS(self, name, qname, attrs): self._attrs = attrs + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_attrs_empty(self): parser = create_parser() gather = self.AttrGatherer() @@ -1074,6 +1098,7 @@ def test_expat_attrs_empty(self): self.verify_empty_attrs(gather._attrs) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_attrs_wattr(self): parser = create_parser() gather = self.AttrGatherer() @@ -1084,6 +1109,7 @@ def test_expat_attrs_wattr(self): self.verify_attrs_wattr(gather._attrs) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_nsattrs_empty(self): parser = create_parser(1) gather = self.AttrGatherer() @@ -1094,6 +1120,7 @@ def test_expat_nsattrs_empty(self): self.verify_empty_nsattrs(gather._attrs) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_expat_nsattrs_wattr(self): parser = create_parser(1) gather = self.AttrGatherer() @@ -1119,6 +1146,7 @@ def test_expat_nsattrs_wattr(self): # ===== InputSource support + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_inpsource_filename(self): parser = create_parser() result = BytesIO() @@ -1129,6 +1157,7 @@ def test_expat_inpsource_filename(self): self.assertEqual(result.getvalue(), xml_test_out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_inpsource_sysid(self): parser = create_parser() result = BytesIO() @@ -1140,6 +1169,7 @@ def test_expat_inpsource_sysid(self): self.assertEqual(result.getvalue(), xml_test_out) @requires_nonascii_filenames + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_inpsource_sysid_nonascii(self): fname = os_helper.TESTFN_UNICODE shutil.copyfile(TEST_XMLFILE, fname) @@ -1154,6 +1184,7 @@ def test_expat_inpsource_sysid_nonascii(self): self.assertEqual(result.getvalue(), xml_test_out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_inpsource_byte_stream(self): parser = create_parser() result = BytesIO() @@ -1167,6 +1198,7 @@ def test_expat_inpsource_byte_stream(self): self.assertEqual(result.getvalue(), xml_test_out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_inpsource_character_stream(self): parser = create_parser() result = BytesIO() @@ -1182,6 +1214,7 @@ def test_expat_inpsource_character_stream(self): # ===== IncrementalParser support + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_incremental(self): result = BytesIO() xmlgen = XMLGenerator(result) @@ -1194,6 +1227,7 @@ def test_expat_incremental(self): self.assertEqual(result.getvalue(), start + b"") + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_incremental_reset(self): result = BytesIO() xmlgen = XMLGenerator(result) @@ -1218,6 +1252,7 @@ def test_expat_incremental_reset(self): @unittest.skipIf(pyexpat.version_info < (2, 6, 0), f'Expat {pyexpat.version_info} does not ' 'support reparse deferral') + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_flush_reparse_deferral_enabled(self): result = BytesIO() xmlgen = XMLGenerator(result) @@ -1240,6 +1275,7 @@ def test_flush_reparse_deferral_enabled(self): self.assertEqual(result.getvalue(), start + b"") + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'codecs' has no attribute 'utf_32_encode'. Did you mean: 'utf_16_encode'? def test_flush_reparse_deferral_disabled(self): result = BytesIO() xmlgen = XMLGenerator(result) @@ -1267,6 +1303,7 @@ def test_flush_reparse_deferral_disabled(self): # ===== Locator support + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_locator_noinfo(self): result = BytesIO() xmlgen = XMLGenerator(result) @@ -1281,6 +1318,7 @@ def test_expat_locator_noinfo(self): self.assertEqual(parser.getPublicId(), None) self.assertEqual(parser.getLineNumber(), 1) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_locator_withinfo(self): result = BytesIO() xmlgen = XMLGenerator(result) @@ -1292,6 +1330,7 @@ def test_expat_locator_withinfo(self): self.assertEqual(parser.getPublicId(), None) @requires_nonascii_filenames + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_locator_withinfo_nonascii(self): fname = os_helper.TESTFN_UNICODE shutil.copyfile(TEST_XMLFILE, fname) @@ -1314,6 +1353,7 @@ def test_expat_locator_withinfo_nonascii(self): # =========================================================================== class ErrorReportingTest(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_inpsource_location(self): parser = create_parser() parser.setContentHandler(ContentHandler()) # do nothing @@ -1327,6 +1367,7 @@ def test_expat_inpsource_location(self): except SAXException as e: self.assertEqual(e.getSystemId(), name) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_expat_incomplete(self): parser = create_parser() parser.setContentHandler(ContentHandler()) # do nothing @@ -1463,6 +1504,7 @@ def setUp(self): self.end_of_dtd = False self.comments = [] + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_handlers(self): class TestLexicalHandler(LexicalHandler): def __init__(self, test_harness, *args, **kwargs): @@ -1519,6 +1561,7 @@ def setUp(self): self.chardata = [] self.in_cdata = False + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetParamEntityParsing' def test_handlers(self): class TestLexicalHandler(LexicalHandler): def __init__(self, test_harness, *args, **kwargs): diff --git a/Lib/test/test_select.py b/Lib/test/test_select.py index a82584d6904..ce8a6279395 100644 --- a/Lib/test/test_select.py +++ b/Lib/test/test_select.py @@ -40,6 +40,7 @@ def test_errno(self): else: self.fail("exception not raised") + @unittest.expectedFailure # TODO: RUSTPYTHON; def test_returned_list_identity(self): # See issue #8329 r, w, x = select.select([], [], [], 1) @@ -82,6 +83,7 @@ def test_select(self): @unittest.skipIf( support.is_emscripten, "Emscripten cannot select a fd multiple times." ) + @unittest.skip("TODO: RUSTPYTHON; Hangs forever") def test_select_mutated(self): a = [] class F: @@ -91,6 +93,7 @@ def fileno(self): a[:] = [F()] * 10 self.assertEqual(select.select([], a, []), ([], a[:5], [])) + @unittest.expectedFailure # TODO: RUSTPYTHON; def test_disallow_instantiation(self): support.check_disallow_instantiation(self, type(select.poll())) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 57539a3862a..792ea347242 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -2367,12 +2367,14 @@ def testCreateISOTPSocket(self): with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: pass + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: AF_CAN address must be a tuple (interface,) or (interface, addr) def testTooLongInterfaceName(self): # most systems limit IFNAMSIZ to 16, take 1024 to be sure with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: with self.assertRaisesRegex(OSError, 'interface name too long'): s.bind(('x' * 1024, 1, 2)) + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: AF_CAN address must be a tuple (interface,) or (interface, addr) def testBind(self): try: with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index c5280673ab8..af99237e648 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -12,6 +12,7 @@ class MiscSourceEncodingTest(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_import_encoded_module(self): from test.encoded_modules import test_strings # Make sure we're actually testing something @@ -28,6 +29,7 @@ def test_compilestring(self): exec(c, d) self.assertEqual(d['u'], '\xf3') + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_issue2301(self): try: compile(b"# coding: cp932\nprint '\x94\x4e'", "dummy", "exec") @@ -75,6 +77,7 @@ def test_20731(self): self.assertEqual(sub.returncode, 0) self.assertNotIn(b'SyntaxError', err) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_error_message(self): compile(b'# -*- coding: iso-8859-15 -*-\n', 'dummy', 'exec') compile(b'\xef\xbb\xbf\n', 'dummy', 'exec') @@ -92,10 +95,12 @@ def test_error_message(self): with self.assertRaisesRegex(SyntaxError, 'BOM'): compile(b'\xef\xbb\xbf# -*- coding: fake -*-\n', 'dummy', 'exec') + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_bad_coding(self): module_name = 'bad_coding' self.verify_bad_module(module_name) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_bad_coding2(self): module_name = 'bad_coding2' self.verify_bad_module(module_name) @@ -109,6 +114,7 @@ def verify_bad_module(self, module_name): bytes = fp.read() self.assertRaises(SyntaxError, compile, bytes, filename, 'exec') + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_exec_valid_coding(self): d = {} exec(b'# coding: cp949\na = "\xaa\xa7"\n', d) @@ -138,6 +144,7 @@ def test_file_parse(self): unload(TESTFN) rmtree('__pycache__') + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_error_from_string(self): # See http://bugs.python.org/issue6289 input = "# coding: ascii\n\N{SNOWMAN}".encode('utf-8') @@ -148,6 +155,7 @@ def test_error_from_string(self): self.assertTrue(c.exception.args[0].startswith(expected), msg=c.exception.args[0]) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_file_parse_error_multiline(self): # gh96611: with open(TESTFN, "wb") as fd: @@ -161,6 +169,7 @@ def test_file_parse_error_multiline(self): finally: os.unlink(TESTFN) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'Non-UTF-8 code starting with' not found in b'OSError: stream did not contain valid UTF-8' def test_tokenizer_fstring_warning_in_first_line(self): source = "0b1and 2" with open(TESTFN, "w") as fd: @@ -328,6 +337,7 @@ class UTF8ValidatorTest(unittest.TestCase): @unittest.skipIf(not sys.platform.startswith("linux"), "Too slow to run on non-Linux platforms") @requires_resource('cpu') + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported def test_invalid_utf8(self): # This is a port of test_utf8_decode_invalid_sequences in # test_unicode.py to exercise the separate utf8 validator in @@ -393,6 +403,7 @@ def check(content): check(b'\xF4'+cb+b'\xBF\xBF') +@unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported class BytesSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): def check_script_output(self, src, expected): @@ -424,6 +435,78 @@ def check_script_error(self, src, expected): res = script_helper.assert_python_failure(fn) self.assertRegex(res.err.rstrip().splitlines()[-1], b'SyntaxError.*?' + expected) + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_utf8_non_utf8_comment_line_error(self): + super().test_utf8_non_utf8_comment_line_error() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_second_non_utf8_coding_line(self): + super().test_second_non_utf8_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_second_coding_line_empty_first_line(self): + super().test_second_coding_line_empty_first_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_second_coding_line(self): + super().test_second_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_nul_in_second_coding_line(self): + super().test_nul_in_second_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_nul_in_first_coding_line(self): + super().test_nul_in_first_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_long_second_coding_line(self): + super().test_long_second_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_long_first_coding_line(self): + super().test_long_first_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_long_coding_name(self): + super().test_long_coding_name() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_long_coding_line(self): + super().test_long_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_first_non_utf8_coding_line(self): + super().test_first_non_utf8_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_first_coding_line(self): + super().test_first_coding_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_double_coding_line(self): + super().test_double_coding_line() + + @unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: b""\\r\\n"" != b""\\n""') + def test_crlf(self): + super().test_crlf() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_crcrlf(self): + super().test_crcrlf() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_crcrcrlf2(self): + super().test_crcrcrlf2() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_crcrcrlf(self): + super().test_double_coding_same_line() + + @unittest.expectedFailure # TODO: RUSTPYTHON; Only UTF-8 supported + def test_double_coding_same_line(self): + super().test_double_coding_same_line() + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 5e6cd68b8db..0bf2f8c5387 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -82,6 +82,7 @@ def test_am_pm(self): @unittest.skipIf( support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" ) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_timezone(self): # Make sure timezone is correct timezone = time.strftime("%Z", self.time_tuple).lower() @@ -91,6 +92,7 @@ def test_timezone(self): "timezone %s not found in %s" % (timezone, self.LT_ins.timezone)) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_date_time(self): # Check that LC_date_time, LC_date, and LC_time are correct # the magic date is used so as to not have issues with %c when day of @@ -149,6 +151,7 @@ def test_pattern_escaping(self): pattern_string) @skip_if_buggy_ucrt_strfptime + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_compile(self): # Check that compiled regex is correct found = self.time_re.compile(r"%A").match(self.locale_time.f_weekday[6]) @@ -445,6 +448,7 @@ def test_bad_offset(self): @unittest.skipIf( support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" ) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_timezone(self): # Test timezone directives. # When gmtime() is used with %Z, entire result of strftime() is empty. @@ -502,6 +506,7 @@ def test_bad_timezone(self): 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN', 'my_MM', 'or_IN', 'shn_MM', 'az_IR', 'byn_ER', 'wal_ET', 'lzh_TW') + @unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: time data 'Tue Jan 13 17:46:01 2026' does not match format '%c' def test_date_time_locale(self): # Test %c directive loc = locale.getlocale(locale.LC_TIME)[0] @@ -563,6 +568,7 @@ def test_date_locale(self): ) @run_with_locales('LC_TIME', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', 'eu_ES', 'ar_AE', 'my_MM', 'shn_MM', 'lzh_TW') + @unittest.expectedFailureIf(sys.platform != 'linux', 'TODO: RUSTPYTHON; AssertionError: Tuples differ: (2000, 1, 1) != (1900, 1, 1)') def test_date_locale2(self): # Test %x directive loc = locale.getlocale(locale.LC_TIME)[0] @@ -672,6 +678,7 @@ def setUp(self): self.time_tuple = time.gmtime() @skip_if_buggy_ucrt_strfptime + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_julian_calculation(self): # Make sure that when Julian is missing that it is calculated format_string = "%Y %m %d %H %M %S %w %Z" @@ -682,6 +689,7 @@ def test_julian_calculation(self): (result.tm_yday, self.time_tuple.tm_yday)) @skip_if_buggy_ucrt_strfptime + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_gregorian_calculation(self): # Test that Gregorian date can be calculated from Julian day format_string = "%Y %H %M %S %w %j %Z" @@ -697,6 +705,7 @@ def test_gregorian_calculation(self): self.time_tuple.tm_mday)) @skip_if_buggy_ucrt_strfptime + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not true : Matching failed on '%Z' using '(?Putc|gmt|est|edt)' regex def test_day_of_week_calculation(self): # Test that the day of the week is calculated as needed format_string = "%Y %m %d %H %S %j %Z" diff --git a/Lib/test/test_termios.py b/Lib/test/test_termios.py index 5fd62b30263..21950b1bee7 100644 --- a/Lib/test/test_termios.py +++ b/Lib/test/test_termios.py @@ -26,6 +26,7 @@ def assertRaisesTermiosError(self, errno, callable, *args): callable(*args) self.assertEqual(cm.exception.args[0], errno) + @unittest.expectedFailure # TODO: RUSTPYTHON; termios.tcsetattr(self.stream, termios.TCSANOW, attrs)\nTypeError: Expected type 'int' but 'FileIO' found. def test_tcgetattr(self): attrs = termios.tcgetattr(self.fd) self.assertIsInstance(attrs, list) @@ -44,6 +45,7 @@ def test_tcgetattr(self): self.assertEqual(len(x), 1) self.assertEqual(termios.tcgetattr(self.stream), attrs) + @unittest.skip("TODO: RUSTPYTHON; Segmentation Fault") def test_tcgetattr_errors(self): self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetattr, self.bad_fd) self.assertRaises(ValueError, termios.tcgetattr, -1) @@ -51,6 +53,7 @@ def test_tcgetattr_errors(self): self.assertRaises(TypeError, termios.tcgetattr, object()) self.assertRaises(TypeError, termios.tcgetattr) + @unittest.expectedFailure # TODO: RUSTPYTHON; termios.tcsetattr(self.stream, termios.TCSANOW, attrs)\nTypeError: Expected type 'int' but 'FileIO' found. def test_tcsetattr(self): attrs = termios.tcgetattr(self.fd) termios.tcsetattr(self.fd, termios.TCSANOW, attrs) @@ -58,6 +61,7 @@ def test_tcsetattr(self): termios.tcsetattr(self.fd, termios.TCSAFLUSH, attrs) termios.tcsetattr(self.stream, termios.TCSANOW, attrs) + @unittest.skip("TODO: RUSTPYTHON; Segmentation Fault") def test_tcsetattr_errors(self): attrs = termios.tcgetattr(self.fd) self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, tuple(attrs)) @@ -93,6 +97,7 @@ def test_tcsetattr_errors(self): self.assertRaises(TypeError, termios.tcsetattr, object(), termios.TCSANOW, attrs) self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW) + @unittest.expectedFailure # TODO: RUSTPYTHON; termios.tcsetattr(self.stream, termios.TCSANOW, attrs)\nTypeError: Expected type 'int' but 'FileIO' found. def test_tcsendbreak(self): try: termios.tcsendbreak(self.fd, 1) @@ -103,6 +108,7 @@ def test_tcsendbreak(self): raise termios.tcsendbreak(self.stream, 1) + @unittest.skip("TODO: RUSTPYTHON; Segmentation Fault") def test_tcsendbreak_errors(self): self.assertRaises(OverflowError, termios.tcsendbreak, self.fd, 2**1000) self.assertRaises(TypeError, termios.tcsendbreak, self.fd, 0.0) @@ -113,10 +119,12 @@ def test_tcsendbreak_errors(self): self.assertRaises(TypeError, termios.tcsendbreak, object(), 0) self.assertRaises(TypeError, termios.tcsendbreak, self.fd) + @unittest.expectedFailure # TODO: RUSTPYTHON; termios.tcsetattr(self.stream, termios.TCSANOW, attrs)\nTypeError: Expected type 'int' but 'FileIO' found. def test_tcdrain(self): termios.tcdrain(self.fd) termios.tcdrain(self.stream) + @unittest.skip("TODO: RUSTPYTHON; Segmentation Fault") def test_tcdrain_errors(self): self.assertRaisesTermiosError(errno.ENOTTY, termios.tcdrain, self.bad_fd) self.assertRaises(ValueError, termios.tcdrain, -1) @@ -129,6 +137,7 @@ def test_tcflush(self): termios.tcflush(self.fd, termios.TCOFLUSH) termios.tcflush(self.fd, termios.TCIOFLUSH) + @unittest.skip("TODO: RUSTPYTHON; Segmentation Fault") def test_tcflush_errors(self): self.assertRaisesTermiosError(errno.EINVAL, termios.tcflush, self.fd, -1) self.assertRaises(OverflowError, termios.tcflush, self.fd, 2**1000) @@ -170,6 +179,7 @@ def test_tcflow(self): termios.tcflow(self.fd, termios.TCIOFF) termios.tcflow(self.fd, termios.TCION) + @unittest.skip("TODO: RUSTPYTHON; Segmentation Fault") def test_tcflow_errors(self): self.assertRaisesTermiosError(errno.EINVAL, termios.tcflow, self.fd, -1) self.assertRaises(OverflowError, termios.tcflow, self.fd, 2**1000) @@ -208,6 +218,7 @@ def writer(): 'output was not resumed') self.assertEqual(os.read(rfd, 1024), b'def') + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize' def test_tcgetwinsize(self): size = termios.tcgetwinsize(self.fd) self.assertIsInstance(size, tuple) @@ -216,6 +227,7 @@ def test_tcgetwinsize(self): self.assertIsInstance(size[1], int) self.assertEqual(termios.tcgetwinsize(self.stream), size) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize' def test_tcgetwinsize_errors(self): self.assertRaisesTermiosError(errno.ENOTTY, termios.tcgetwinsize, self.bad_fd) self.assertRaises(ValueError, termios.tcgetwinsize, -1) @@ -223,12 +235,14 @@ def test_tcgetwinsize_errors(self): self.assertRaises(TypeError, termios.tcgetwinsize, object()) self.assertRaises(TypeError, termios.tcgetwinsize) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize' def test_tcsetwinsize(self): size = termios.tcgetwinsize(self.fd) termios.tcsetwinsize(self.fd, size) termios.tcsetwinsize(self.fd, list(size)) termios.tcsetwinsize(self.stream, size) + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'termios' has no attribute 'tcgetwinsize' def test_tcsetwinsize_errors(self): size = termios.tcgetwinsize(self.fd) self.assertRaises(TypeError, termios.tcsetwinsize, self.fd, size[:-1]) @@ -267,6 +281,7 @@ def test_constants(self): self.assertLess(termios.VTIME, termios.NCCS) self.assertLess(termios.VMIN, termios.NCCS) + @unittest.expectedFailure # TODO: RUSTPYTHON; self.assertFalse(issubclass(termios.error, OSError))\nAssertionError: True is not false def test_exception(self): self.assertTrue(issubclass(termios.error, Exception)) self.assertFalse(issubclass(termios.error, OSError)) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2ba7fbda5c3..26d98a6645b 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -4817,8 +4817,6 @@ def CHECK(a, b, expected): CHECK("AttributeError", "AttributeErrorTests", 10) CHECK("ABA", "AAB", 4) - # TODO: RUSTPYTHON - @unittest.expectedFailure @support.requires_resource('cpu') def test_levenshtein_distance_short_circuit(self): if not LEVENSHTEIN_DATA_FILE.is_file(): diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index 3dbb35afcb6..fea4a7f4410 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -4,6 +4,7 @@ class TypeAnnotationTests(unittest.TestCase): + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: can't delete attribute def test_lazy_create_annotations(self): # type objects lazy create their __annotations__ dict on demand. # the annotations dict is stored in type.__dict__. @@ -17,6 +18,7 @@ def test_lazy_create_annotations(self): self.assertEqual(foo.__dict__['__annotations__'], d) del foo.__annotations__ + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: can't delete attribute def test_setting_annotations(self): foo = type("Foo", (), {}) for i in range(3): @@ -28,6 +30,7 @@ def test_setting_annotations(self): self.assertEqual(foo.__dict__['__annotations__'], d) del foo.__annotations__ + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: can't delete attribute def test_annotations_getset_raises(self): # builtin types don't have __annotations__ (yet!) with self.assertRaises(AttributeError): @@ -44,6 +47,7 @@ def test_annotations_getset_raises(self): with self.assertRaises(AttributeError): del foo.__annotations__ + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: can't delete attribute def test_annotations_are_created_correctly(self): class C: a:int=3 @@ -119,9 +123,11 @@ def check(self, code: str): annotations = ns["__annotations__"] self.assertEqual(annotations, {"x": int}) + @unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: '__annotations__' def test_top_level(self): self.check("x: int = 1") + @unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: '__annotations__' def test_blocks(self): self.check("if True:\n x: int = 1") self.check(""" @@ -146,6 +152,7 @@ def test_blocks(self): x: int = 1 """) + @unittest.expectedFailure # TODO: RUSTPYTHON; ZeroDivisionError: division by zero\nDuring handling of the above exception, another exception occurred: NameError: name '__annotations__' is not defined def test_try(self): self.check(""" try: @@ -176,6 +183,7 @@ def test_try(self): x: int = 1 """) + @unittest.skip("TODO: RUSTPYTHON; Segmentation fault") def test_try_star(self): self.check(""" try: @@ -206,6 +214,7 @@ def test_try_star(self): x: int = 1 """) + @unittest.expectedFailure # TODO: RUSTPYTHON; exec(textwrap.dedent(code), ns)\nNameError: name '__annotations__' is not defined def test_match(self): self.check(""" match 0: diff --git a/Lib/test/test_zipimport_support.py b/Lib/test/test_zipimport_support.py index ae8a8c99762..0b14bf10c69 100644 --- a/Lib/test/test_zipimport_support.py +++ b/Lib/test/test_zipimport_support.py @@ -93,6 +93,7 @@ def test_inspect_getsource_issue4223(self): finally: del sys.modules["zip_pkg"] + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'test_zipped_doctest' has no attribute 'test_debug' def test_doctest_issue4197(self): # To avoid having to keep two copies of the doctest module's # unit tests in sync, this test works by taking the source of @@ -184,6 +185,7 @@ def test_doctest_issue4197(self): finally: del sys.modules["test_zipped_doctest"] + @unittest.skip("TODO: RUSTPYTHON; Crashes") def test_doctest_main_issue4197(self): test_src = textwrap.dedent("""\ class Test: @@ -213,6 +215,7 @@ class Test: print (ascii(out)) self.assertIn(expected.encode('utf-8'), out) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'/tmp/tmp9wflv3j5/script.py' not found in b'--Return--\n> ... def test_pdb_issue4201(self): test_src = textwrap.dedent("""\ def f(): diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 02b63b07479..5d9f633555a 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -12,6 +12,7 @@ import re import shutil import struct +import sys # TODO: RUSTPYTHON; only here for a test import tempfile import unittest from datetime import date, datetime, time, timedelta, timezone @@ -1793,6 +1794,7 @@ def test_reset_tzpath_kwarg(self): self.assertSequenceEqual(self.module.TZPATH, (f"{DRIVE}/a/b/c",)) + @unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Expected type 'str' but 'FakePath' found. def test_reset_tzpath_relative_paths(self): bad_values = [ ("path/to/somewhere",), @@ -1808,6 +1810,7 @@ def test_reset_tzpath_relative_paths(self): with self.assertRaises(ValueError): self.module.reset_tzpath(to=input_paths) + @unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: TypeError not raised') def test_tzpath_type_error(self): bad_values = [ "/etc/zoneinfo:/usr/share/zoneinfo", @@ -1823,6 +1826,7 @@ def test_tzpath_type_error(self): with self.assertRaises(TypeError): self.module.reset_tzpath(bad_value) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Sequences differ: ('/usr/share/zoneinfo',) != (,) def test_tzpath_attribute(self): tzpath_0 = [f"{DRIVE}/one", f"{DRIVE}/two"] tzpath_1 = [f"{DRIVE}/three"] From 4e9fb55c830c8ad54794d6bc3e3705a519ea1d19 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Thu, 15 Jan 2026 19:03:51 -0500 Subject: [PATCH 3/4] Added skips for tests missing vital modules/attributes --- .../test_free_threading/test_monitoring.py | 7 +++++++ Lib/test/test_unparse.py | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Lib/test/test_free_threading/test_monitoring.py b/Lib/test/test_free_threading/test_monitoring.py index 4e8ee2e2ac0..592ec4af569 100644 --- a/Lib/test/test_free_threading/test_monitoring.py +++ b/Lib/test/test_free_threading/test_monitoring.py @@ -1,3 +1,10 @@ +# TODO: RUSTPYTHON +try: + import unittest + from sys import monitoring +except ImportError: + raise unittest.SkipTest('TODO: RUSTPYTHON; cannot import name "monitoring" from sys') + """Tests monitoring, sys.settrace, and sys.setprofile in a multi-threaded environment to verify things are thread-safe in a free-threaded build""" diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index 971fdb2ba32..88d8b1a1cf3 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -1,3 +1,22 @@ +# TODO: RUSTPYTHON +try: + nonlocal_ex = """\ +def f(): + x = 1 + def g(): + nonlocal x + x = 2 + y = 7 + def h(): + nonlocal x, y +""" + import ast + import unittest + ast1 = ast.parse(nonlocal_ex) + code2 = ast.unparse(ast1) +except AttributeError: + raise unittest.SkipTest('TODO: RUSTPYTHON; type_comment attribute not implemented. FunctionDef, AsyncFunctionDef, For, AsyncFor, With and AsyncWith should have attribute') + """Tests for ast.unparse.""" import unittest @@ -512,6 +531,7 @@ def test_class_bases_and_keywords(self): self.check_src_roundtrip("class X(*args):\n pass") self.check_src_roundtrip("class X(*args, **kwargs):\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; f"{f'{x!ÿ}\n'!ÿ}\n" def test_fstrings(self): self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') self.check_src_roundtrip('''f\'-{f\'\'\'*{f"""+{f".{f'{x}'}."}+"""}*\'\'\'}-\'''') @@ -651,6 +671,7 @@ def test_multiquote_joined_string(self): self.check_ast_roundtrip("""f'''""\"''\\'{"\\n\\"'"}''' """) self.check_ast_roundtrip("""f'''""\"''\\'{""\"\\n\\"'''""\" '''\\n'''}''' """) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxWarning not triggered def test_backslash_in_format_spec(self): import re msg = re.escape("invalid escape sequence '\\ '") From d3940a752d554af8ca90be18a0bc13e6ec8cf894 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 18 Jan 2026 20:46:32 -0500 Subject: [PATCH 4/4] Fixed test_unparse --- Lib/test/test_unparse.py | 62 ++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index 88d8b1a1cf3..6aa7fb0c5f6 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -1,22 +1,3 @@ -# TODO: RUSTPYTHON -try: - nonlocal_ex = """\ -def f(): - x = 1 - def g(): - nonlocal x - x = 2 - y = 7 - def h(): - nonlocal x, y -""" - import ast - import unittest - ast1 = ast.parse(nonlocal_ex) - code2 = ast.unparse(ast1) -except AttributeError: - raise unittest.SkipTest('TODO: RUSTPYTHON; type_comment attribute not implemented. FunctionDef, AsyncFunctionDef, For, AsyncFor, With and AsyncWith should have attribute') - """Tests for ast.unparse.""" import unittest @@ -178,6 +159,7 @@ def check_src_dont_roundtrip(self, code1, code2=None): class UnparseTestCase(ASTTestCase): # Tests for specific bugs found in earlier versions of unparse + @unittest.expectedFailure # TODO: RUSTPYTHON; f'{a!ÿ}' - SyntaxError: invalid syntax: f-string: invalid conversion character def test_fstrings(self): self.check_ast_roundtrip("f'a'") self.check_ast_roundtrip("f'{{}}'") @@ -199,6 +181,7 @@ def test_fstrings(self): "{j!s:{a}b}{k!s:a{b}c}{l!a:{b}c{d}}{x+y=}'" ) + @unittest.expectedFailure # TODO: RUSTPYTHON; f"{f'{y!ÿ}' * 3!ÿ}" - SyntaxError: invalid syntax: f-string: invalid conversion character def test_fstrings_special_chars(self): # See issue 25180 self.check_ast_roundtrip(r"""f'{f"{0}"*3}'""") @@ -206,6 +189,7 @@ def test_fstrings_special_chars(self): self.check_ast_roundtrip("""f''""") self.check_ast_roundtrip('''f"""'end' "quote\\""""''') + @unittest.expectedFailure # TODO: RUSTPYTHON; f"""{"'"!ÿ}""" - SyntaxError: invalid syntax: f-string: invalid conversion character def test_fstrings_complicated(self): # See issue 28002 self.check_ast_roundtrip("""f'''{"'"}'''""") @@ -216,6 +200,7 @@ def test_fstrings_complicated(self): self.check_ast_roundtrip('''f"a\\r\\nb"''') self.check_ast_roundtrip('''f"\\u2028{'x'}"''') + @unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError: invalid syntax: f-string: invalid conversion character def test_fstrings_pep701(self): self.check_ast_roundtrip('f" something { my_dict["key"] } something else "') self.check_ast_roundtrip('f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"') @@ -232,9 +217,11 @@ def test_shifts(self): self.check_ast_roundtrip("45 << 2") self.check_ast_roundtrip("13 >> 7") + @unittest.expectedFailure # TODO: RUSTPYTHON; type_comment attribute not implemented. FunctionDef should have attribute def test_for_else(self): self.check_ast_roundtrip(for_else) + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'FunctionDef' object has no attribute 'type_comment' def test_while_else(self): self.check_ast_roundtrip(while_else) @@ -254,6 +241,7 @@ def test_huge_float(self): self.check_ast_roundtrip("1e1000j") self.check_ast_roundtrip("-1e1000j") + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.kind == "u": - AttributeError: 'Constant' object has no attribute 'kind' def test_nan(self): self.assertASTEqual( ast.parse(ast.unparse(ast.Constant(value=float('nan')))), @@ -270,6 +258,7 @@ def test_imaginary_literals(self): self.check_ast_roundtrip("0j") self.check_ast_roundtrip("-0j") + @unittest.expectedFailure # TODO: RUSTPYTHON; for field in node._fields: - AttributeError: 'NoneType' object has no attribute '_fields' def test_lambda_parentheses(self): self.check_ast_roundtrip("(lambda: int)()") @@ -277,6 +266,7 @@ def test_chained_comparisons(self): self.check_ast_roundtrip("1 < 4 <= 5") self.check_ast_roundtrip("a is b is c is not d") + @unittest.expectedFailure # TODO: RUSTPYTHON; type_comment attribute not implemented. FunctionDef should have attribute def test_function_arguments(self): self.check_ast_roundtrip("def f(): pass") self.check_ast_roundtrip("def f(a): pass") @@ -294,6 +284,7 @@ def test_function_arguments(self): def test_relative_import(self): self.check_ast_roundtrip(relative_import) + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'FunctionDef' object has no attribute 'type_comment' def test_nonlocal(self): self.check_ast_roundtrip(nonlocal_ex) @@ -303,6 +294,7 @@ def test_raise_from(self): def test_bytes(self): self.check_ast_roundtrip("b'123'") + @unittest.expectedFailure # TODO: RUSTPYTHON; type_comment attribute not implemented. FunctionDef should have attribute def test_annotations(self): self.check_ast_roundtrip("def f(a : int): pass") self.check_ast_roundtrip("def f(a: int = 5): pass") @@ -325,6 +317,7 @@ def test_set_comprehension(self): def test_dict_comprehension(self): self.check_ast_roundtrip("{x: x*x for x in range(10)}") + @unittest.expectedFailure # TODO: RUSTPYTHON; for e in node.bases: - TypeError: 'NoneType' object is not iterable def test_class_decorators(self): self.check_ast_roundtrip(class_decorator) @@ -347,12 +340,15 @@ def test_starred_assignment(self): self.check_ast_roundtrip("a, *b[0], c = seq") self.check_ast_roundtrip("a, *(b, c) = seq") + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'With' object has no attribute 'type_comment' def test_with_simple(self): self.check_ast_roundtrip(with_simple) + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'With' object has no attribute 'type_comment' def test_with_as(self): self.check_ast_roundtrip(with_as) + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'With' object has no attribute 'type_comment' def test_with_two_items(self): self.check_ast_roundtrip(with_two_items) @@ -401,6 +397,7 @@ def test_invalid_fstring_value(self): ) ) + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.kind == "u": - AttributeError: 'Constant' object has no attribute 'kind' def test_fstring_backslash(self): # valid since Python 3.12 self.assertEqual(ast.unparse( @@ -414,6 +411,7 @@ def test_fstring_backslash(self): def test_invalid_yield_from(self): self.check_invalid(ast.YieldFrom(value=None)) + @unittest.expectedFailure # TODO: RUSTPYTHON; self.write("." * (node.level or 0)) - AttributeError: 'ImportFrom' object has no attribute 'level' def test_import_from_level_none(self): tree = ast.ImportFrom(module='mod', names=[ast.alias(name='x')]) self.assertEqual(ast.unparse(tree), "from mod import x") @@ -440,12 +438,14 @@ def test_docstrings(self): # check as Module docstrings for easy testing self.check_ast_roundtrip(f"'''{docstring}'''") + @unittest.expectedFailure # TODO: RUSTPYTHON; _feature_version=feature_version, optimize=optimize) - TypeError: expected some sort of mod, but got <_ast.Constant object at 0x55810cf9a2a0> def test_constant_tuples(self): self.check_src_roundtrip(ast.Constant(value=(1,), kind=None), "(1,)") self.check_src_roundtrip( ast.Constant(value=(1, 2, 3), kind=None), "(1, 2, 3)" ) + @unittest.expectedFailure # TODO: RUSTPYTHON; _feature_version=feature_version, optimize=optimize) - ValueError: mode must be "exec", "eval", "ipython", or "single" def test_function_type(self): for function_type in ( "() -> int", @@ -454,6 +454,7 @@ def test_function_type(self): ): self.check_ast_roundtrip(function_type, mode="func_type") + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'FunctionDef' object has no attribute 'type_comment' def test_type_comments(self): for statement in ( "a = 5 # type:", @@ -470,6 +471,7 @@ def test_type_comments(self): ): self.check_ast_roundtrip(statement, type_comments=True) + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'FunctionDef' object has no attribute 'type_comment' def test_type_ignore(self): for statement in ( "a = 5 # type: ignore", @@ -519,6 +521,7 @@ def test_simple_expressions_parens(self): self.check_src_roundtrip("call((yield x))") self.check_src_roundtrip("return x + (yield x)") + @unittest.skip('TODO: RUSTPYTHON; self.write("." * (node.level or 0)) - AttributeError: "ImportFrom" object has no attribute "level"') def test_class_bases_and_keywords(self): self.check_src_roundtrip("class X:\n pass") self.check_src_roundtrip("class X(A):\n pass") @@ -531,7 +534,7 @@ def test_class_bases_and_keywords(self): self.check_src_roundtrip("class X(*args):\n pass") self.check_src_roundtrip("class X(*args, **kwargs):\n pass") - @unittest.expectedFailure # TODO: RUSTPYTHON; f"{f'{x!ÿ}\n'!ÿ}\n" + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'f\'\'\'-{f"""*{f"+{f\'.{x}.\'}+"}*"""}-\'\'\'' != 'f\'\'\'-{f"""*{f"+{f\'.{x!ÿ}.\'!ÿ}+"!ÿ}*"""!ÿ}-\'\'\'' def test_fstrings(self): self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') self.check_src_roundtrip('''f\'-{f\'\'\'*{f"""+{f".{f'{x}'}."}+"""}*\'\'\'}-\'''') @@ -541,6 +544,7 @@ def test_fstrings(self): self.check_src_roundtrip('''f"{'\\n'}\\n"''') self.check_src_roundtrip('''f"{f'{x}\\n'}\\n"''') + @unittest.expectedFailure # TODO: RUSTPYTHON; for e in node.bases: - TypeError: 'NoneType' object is not iterable def test_docstrings(self): docstrings = ( '"""simple doc string"""', @@ -565,6 +569,7 @@ def test_docstrings(self): for docstring in docstrings: self.check_src_roundtrip(f"{prefix}{docstring}") + @unittest.expectedFailure # TODO: RUSTPYTHON; for e in node.bases: - TypeError: 'NoneType' object is not iterable def test_docstrings_negative_cases(self): # Test some cases that involve strings in the children of the # first node but aren't docstrings to make sure we don't have @@ -608,6 +613,7 @@ def test_slices(self): self.check_src_roundtrip("a[1:2, *a]") self.check_src_roundtrip("a[*a, 1:2]") + @unittest.expectedFailure # TODO: RUSTPYTHON; for field in node._fields: - AttributeError: 'NoneType' object has no attribute '_fields' def test_lambda_parameters(self): self.check_src_roundtrip("lambda: something") self.check_src_roundtrip("four = lambda: 2 + 2") @@ -618,6 +624,7 @@ def test_lambda_parameters(self): self.check_src_roundtrip("lambda x, y, /, z, q, *, u: None") self.check_src_roundtrip("lambda x, *y, **z: None") + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'For' object has no attribute 'type_comment' def test_star_expr_assign_target(self): for source_type, source in [ ("single assignment", "{target} = foo"), @@ -657,6 +664,7 @@ def test_star_expr_assign_target_multiple(self): self.check_src_roundtrip("[a, b] = [c, d] = [e, f] = g") self.check_src_roundtrip("a, b = [c, d] = e, f = g") + @unittest.expectedFailure # TODO: RUSTPYTHON; f"""'''{1!ÿ}\"""" - SyntaxError: invalid syntax: f-string: invalid conversion character def test_multiquote_joined_string(self): self.check_ast_roundtrip("f\"'''{1}\\\"\\\"\\\"\" ") self.check_ast_roundtrip("""f"'''{1}""\\"" """) @@ -671,7 +679,7 @@ def test_multiquote_joined_string(self): self.check_ast_roundtrip("""f'''""\"''\\'{"\\n\\"'"}''' """) self.check_ast_roundtrip("""f'''""\"''\\'{""\"\\n\\"'''""\" '''\\n'''}''' """) - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxWarning not triggered + @unittest.expectedFailure # TODO: RUSTPYTHON; f'{x!ÿ:\\ }' - SyntaxError: invalid syntax: f-string: invalid conversion character def test_backslash_in_format_spec(self): import re msg = re.escape("invalid escape sequence '\\ '") @@ -687,6 +695,7 @@ def test_backslash_in_format_spec(self): self.check_ast_roundtrip("""f"{x:\\\\\\\\ }" """) + @unittest.expectedFailure # TODO: RUSTPYTHON; f"{x!ÿ:\'}" - SyntaxError: invalid syntax: f-string: invalid conversion character def test_quote_in_format_spec(self): self.check_ast_roundtrip("""f"{x:'}" """) self.check_ast_roundtrip("""f"{x:\\'}" """) @@ -696,6 +705,7 @@ def test_quote_in_format_spec(self): self.check_ast_roundtrip("""f'\\'{x:\\"}' """) self.check_ast_roundtrip("""f'\\'{x:\\\\"}' """) + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.default_value: - AttributeError: 'TypeVar' object has no attribute 'default_value' def test_type_params(self): self.check_ast_roundtrip("type A = int") self.check_ast_roundtrip("type A[T] = int") @@ -719,12 +729,14 @@ def test_class(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "class X:\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.bound: - AttributeError: 'TypeVar' object has no attribute 'bound' def test_class_with_type_params(self): node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[], type_params=[ast.TypeVar("T")]) ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "class X[T]:\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'FunctionDef' object has no attribute 'type_comment' def test_function(self): node = ast.FunctionDef( name="f", @@ -736,6 +748,7 @@ def test_function(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "def f():\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.default_value: - if node.bound: - AttributeError: 'TypeVar' object has no attribute 'bound' def test_function_with_type_params(self): node = ast.FunctionDef( name="f", @@ -748,6 +761,7 @@ def test_function_with_type_params(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "def f[T]():\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.default_value: - AttributeError: 'TypeVar' object has no attribute 'default_value' def test_function_with_type_params_and_bound(self): node = ast.FunctionDef( name="f", @@ -760,6 +774,7 @@ def test_function_with_type_params_and_bound(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "def f[T: int]():\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; for deco in node.decorator_list: - AttributeError: 'FunctionDef' object has no attribute 'decorator_list' def test_function_with_type_params_and_default(self): node = ast.FunctionDef( name="f", @@ -774,6 +789,7 @@ def test_function_with_type_params_and_default(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "def f[T = 1, *Ts = *1, **P = 1]():\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.bound: - comment = self._type_ignores.get(node.lineno) or node.type_comment - AttributeError: 'AsyncFunctionDef' object has no attribute 'type_comment' def test_async_function(self): node = ast.AsyncFunctionDef( name="f", @@ -785,6 +801,7 @@ def test_async_function(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "async def f():\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; if node.bound: - AttributeError: 'TypeVar' object has no attribute 'bound' def test_async_function_with_type_params(self): node = ast.AsyncFunctionDef( name="f", @@ -797,6 +814,7 @@ def test_async_function_with_type_params(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "async def f[T]():\n pass") + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'AsyncFunctionDef' object has no attribute 'decorator_list' def test_async_function_with_type_params_and_default(self): node = ast.AsyncFunctionDef( name="f", @@ -811,7 +829,7 @@ def test_async_function_with_type_params_and_default(self): ast.fix_missing_locations(node) self.assertEqual(ast.unparse(node), "async def f[T = 1, *Ts = *1, **P = 1]():\n pass") - +@unittest.expectedFailure # TODO: RUSTPYTHON; type_comment attribute not implemented. FunctionDef, AsyncFunctionDef, For, AsyncFor, With and AsyncWith should have attribute class DirectoryTestCase(ASTTestCase): """Test roundtrip behaviour on all files in Lib and Lib/test."""

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